]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-136929: ensure that `hashlib.<name>` does not raise `AttributeError` (#136933)
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>
Fri, 25 Jul 2025 14:49:09 +0000 (16:49 +0200)
committerGitHub <noreply@github.com>
Fri, 25 Jul 2025 14:49:09 +0000 (14:49 +0000)
Previously, if OpenSSL was not present and built-in cryptographic extension modules
were disabled, requesting `hashlib.<name>` raised `AttributeError` and an ERROR log
message with the exception traceback is emitted when importing `hashlib`.

Now, the named constructor function will always be available but raises a `ValueError`
at runtime indicating that the algorithm is not supported. The log message has also
been reworded to be less verbose.

Doc/whatsnew/3.15.rst
Lib/hashlib.py
Misc/NEWS.d/next/Library/2025-07-21-16-13-20.gh-issue-136929.obKZ2S.rst [new file with mode: 0644]

index e8e2c1ed6047bfee4564412834538a8dc0d99165..1128da875a88474b05662ce53692a7e21542d23a 100644 (file)
@@ -230,6 +230,17 @@ difflib
   (Contributed by Jiahao Li in :gh:`134580`.)
 
 
+hashlib
+-------
+
+* Ensure that hash functions guaranteed to be always *available* exist as
+  attributes of :mod:`hashlib` even if they will not work at runtime due to
+  missing backend implementations. For instance, ``hashlib.md5`` will no
+  longer raise :exc:`AttributeError` if OpenSSL is not available and Python
+  has been built without MD5 support.
+  (Contributed by Bénédikt Tran in :gh:`136929`.)
+
+
 http.client
 -----------
 
index a7db778b7165373aa1ded16f7c256e8313dbc807..8e7083ba692348c6d513b4b30c56b653b6fa491e 100644 (file)
@@ -261,16 +261,39 @@ def file_digest(fileobj, digest, /, *, _bufsize=2**18):
     return digestobj
 
 
+__logging = None
 for __func_name in __always_supported:
     # try them all, some may not work due to the OpenSSL
     # version not supporting that algorithm.
     try:
         globals()[__func_name] = __get_hash(__func_name)
-    except ValueError:
-        import logging
-        logging.exception('code for hash %s was not found.', __func_name)
-
+    except ValueError as __exc:
+        import logging as __logging
+        __logging.error('hash algorithm %s will not be supported at runtime '
+                        '[reason: %s]', __func_name, __exc)
+        # The following code can be simplified in Python 3.19
+        # once "string" is removed from the signature.
+        __code = f'''\
+def {__func_name}(data=__UNSET, *, usedforsecurity=True, string=__UNSET):
+    if data is __UNSET and string is not __UNSET:
+        import warnings
+        warnings.warn(
+            "the 'string' keyword parameter is deprecated since "
+            "Python 3.15 and slated for removal in Python 3.19; "
+            "use the 'data' keyword parameter or pass the data "
+            "to hash as a positional argument instead",
+            DeprecationWarning, stacklevel=2)
+    if data is not __UNSET and string is not __UNSET:
+        raise TypeError("'data' and 'string' are mutually exclusive "
+                        "and support for 'string' keyword parameter "
+                        "is slated for removal in a future version.")
+    raise ValueError("unsupported hash algorithm {__func_name}")
+'''
+        exec(__code, {"__UNSET": object()}, __locals := {})
+        globals()[__func_name] = __locals[__func_name]
+        del __exc, __code, __locals
 
 # Cleanup locals()
 del __always_supported, __func_name, __get_hash
 del __py_new, __hash_new, __get_openssl_constructor
+del __logging
diff --git a/Misc/NEWS.d/next/Library/2025-07-21-16-13-20.gh-issue-136929.obKZ2S.rst b/Misc/NEWS.d/next/Library/2025-07-21-16-13-20.gh-issue-136929.obKZ2S.rst
new file mode 100644 (file)
index 0000000..31b8563
--- /dev/null
@@ -0,0 +1,5 @@
+Ensure that hash functions guaranteed to be always *available* exist as
+attributes of :mod:`hashlib` even if they will not work at runtime due to
+missing backend implementations. For instance, ``hashlib.md5`` will no
+longer raise :exc:`AttributeError` if OpenSSL is not available and Python
+has been built without MD5 support. Patch by Bénédikt Tran.