Skip to content

Commit 49ac4af

Browse files
committed
ensure that hashlib.<name> does not raise AttributeError
1 parent 1e67293 commit 49ac4af

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

Doc/whatsnew/3.15.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,17 @@ difflib
230230
(Contributed by Jiahao Li in :gh:`134580`.)
231231

232232

233+
hashlib
234+
-------
235+
236+
* Ensure that hash functions guaranteed to be always *available* exist as
237+
attributes of :mod:`hashlib` even if they will not work at runtime due to
238+
missing backend implementations. For instance, ``hashlib.md5`` will no
239+
longer raise :exc:`AttributeError` if OpenSSL is not available and Python
240+
has been built without MD5 support.
241+
(Contributed by Bénédikt Tran in :gh:`136929`.)
242+
243+
233244
http.client
234245
-----------
235246

Lib/hashlib.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,16 +261,42 @@ def file_digest(fileobj, digest, /, *, _bufsize=2**18):
261261
return digestobj
262262

263263

264+
__logging = None
265+
__logger = None
264266
for __func_name in __always_supported:
265267
# try them all, some may not work due to the OpenSSL
266268
# version not supporting that algorithm.
267269
try:
268270
globals()[__func_name] = __get_hash(__func_name)
269271
except ValueError:
270-
import logging
271-
logging.exception('code for hash %s was not found.', __func_name)
272-
272+
import logging as __logging
273+
if __logger is None:
274+
__logger = __logging.getLogger(__name__)
275+
__logger.warning('hash algorithm %s will not be supported at runtime',
276+
__func_name)
277+
# The following code can be simplified in Python 3.19
278+
# once "string" is removed from the signature.
279+
__code = f'''\
280+
def {__func_name}(data=__UNSET, *, usedforsecurity=True, string=__UNSET):
281+
if data is __UNSET and string is not __UNSET:
282+
import warnings
283+
warnings.warn(
284+
"the 'string' keyword parameter is deprecated since "
285+
"Python 3.15 and slated for removal in Python 3.19; "
286+
"use the 'data' keyword parameter or pass the data "
287+
"to hash as a positional argument instead",
288+
DeprecationWarning, stacklevel=2)
289+
if data is not __UNSET and string is not __UNSET:
290+
raise TypeError("'data' and 'string' are mutually exclusive "
291+
"and support for 'string' keyword parameter "
292+
"is slated for removal in a future version.")
293+
raise ValueError("unsupported hash algorithm {__func_name}")
294+
'''
295+
exec(__code, {"__UNSET": object()}, __locals := {})
296+
globals()[__func_name] = __locals[__func_name]
297+
del __code, __locals
273298

274299
# Cleanup locals()
275300
del __always_supported, __func_name, __get_hash
276301
del __py_new, __hash_new, __get_openssl_constructor
302+
del __logger, __logging
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Ensure that hash functions guaranteed to be always *available* exist as
2+
attributes of :mod:`hashlib` even if they will not work at runtime due to
3+
missing backend implementations. For instance, ``hashlib.md5`` will no
4+
longer raise :exc:`AttributeError` if OpenSSL is not available and Python
5+
has been built without MD5 support. Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy