https://github.com/python/cpython/commit/7ce2f101c4b1c123860c90bf67ccc20a7805ea48
commit: 7ce2f101c4b1c123860c90bf67ccc20a7805ea48
branch: main
author: Bénédikt Tran <10796600+picn...@users.noreply.github.com>
committer: picnixz <10796600+picn...@users.noreply.github.com>
date: 2025-07-25T14:49:09Z
summary:

gh-136929: ensure that `hashlib.<name>` does not raise `AttributeError` 
(#136933)

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.

files:
A Misc/NEWS.d/next/Library/2025-07-21-16-13-20.gh-issue-136929.obKZ2S.rst
M Doc/whatsnew/3.15.rst
M Lib/hashlib.py

diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index e8e2c1ed6047bf..1128da875a8847 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -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
 -----------
 
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
index a7db778b716537..8e7083ba692348 100644
--- a/Lib/hashlib.py
+++ b/Lib/hashlib.py
@@ -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
index 00000000000000..31b8563f9d8523
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-07-21-16-13-20.gh-issue-136929.obKZ2S.rst
@@ -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.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to