11import functools
22import hashlib
33import unittest
4+ from test .support .import_helper import import_module
45
56try :
67 import _hashlib
@@ -12,44 +13,81 @@ def requires_hashlib():
1213 return unittest .skipIf (_hashlib is None , "requires _hashlib" )
1314
1415
16+ def _decorate_func_or_class (func_or_class , decorator_func ):
17+ if not isinstance (func_or_class , type ):
18+ return decorator_func (func_or_class )
19+
20+ decorated_class = func_or_class
21+ setUpClass = decorated_class .__dict__ .get ('setUpClass' )
22+ if setUpClass is None :
23+ def setUpClass (cls ):
24+ super (decorated_class , cls ).setUpClass ()
25+ setUpClass .__qualname__ = decorated_class .__qualname__ + '.setUpClass'
26+ setUpClass .__module__ = decorated_class .__module__
27+ else :
28+ setUpClass = setUpClass .__func__
29+ setUpClass = classmethod (decorator_func (setUpClass ))
30+ decorated_class .setUpClass = setUpClass
31+ return decorated_class
32+
33+
1534def requires_hashdigest (digestname , openssl = None , usedforsecurity = True ):
16- """Decorator raising SkipTest if a hashing algorithm is not available
35+ """Decorator raising SkipTest if a hashing algorithm is not available.
1736
18- The hashing algorithm could be missing or blocked by a strict crypto
19- policy .
37+ The hashing algorithm may be missing, blocked by a strict crypto policy,
38+ or Python may be configured with `--with-builtin-hashlib-hashes=no` .
2039
2140 If 'openssl' is True, then the decorator checks that OpenSSL provides
22- the algorithm. Otherwise the check falls back to built-in
23- implementations. The usedforsecurity flag is passed to the constructor.
41+ the algorithm. Otherwise the check falls back to (optional) built-in
42+ HACL* implementations.
43+
44+ The usedforsecurity flag is passed to the constructor but has no effect
45+ on HACL* implementations.
2446
47+ Examples of exceptions being suppressed:
2548 ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS
2649 ValueError: unsupported hash type md4
2750 """
51+ if openssl and _hashlib is not None :
52+ def test_availability ():
53+ _hashlib .new (digestname , usedforsecurity = usedforsecurity )
54+ else :
55+ def test_availability ():
56+ hashlib .new (digestname , usedforsecurity = usedforsecurity )
57+
58+ def decorator_func (func ):
59+ @functools .wraps (func )
60+ def wrapper (* args , ** kwargs ):
61+ try :
62+ test_availability ()
63+ except ValueError as exc :
64+ msg = f"missing hash algorithm: { digestname !r} "
65+ raise unittest .SkipTest (msg ) from exc
66+ return func (* args , ** kwargs )
67+ return wrapper
68+
2869 def decorator (func_or_class ):
29- if isinstance (func_or_class , type ):
30- setUpClass = func_or_class .__dict__ .get ('setUpClass' )
31- if setUpClass is None :
32- def setUpClass (cls ):
33- super (func_or_class , cls ).setUpClass ()
34- setUpClass .__qualname__ = func_or_class .__qualname__ + '.setUpClass'
35- setUpClass .__module__ = func_or_class .__module__
36- else :
37- setUpClass = setUpClass .__func__
38- setUpClass = classmethod (decorator (setUpClass ))
39- func_or_class .setUpClass = setUpClass
40- return func_or_class
41-
42- @functools .wraps (func_or_class )
70+ return _decorate_func_or_class (func_or_class , decorator_func )
71+ return decorator
72+
73+
74+ def requires_openssl_hashdigest (digestname , * , usedforsecurity = True ):
75+ """Decorator raising SkipTest if an OpenSSL hashing algorithm is missing.
76+
77+ The hashing algorithm may be missing or blocked by a strict crypto policy.
78+ """
79+ def decorator_func (func ):
80+ @requires_hashlib ()
81+ @functools .wraps (func )
4382 def wrapper (* args , ** kwargs ):
4483 try :
45- if openssl and _hashlib is not None :
46- _hashlib .new (digestname , usedforsecurity = usedforsecurity )
47- else :
48- hashlib .new (digestname , usedforsecurity = usedforsecurity )
84+ _hashlib .new (digestname , usedforsecurity = usedforsecurity )
4985 except ValueError :
50- raise unittest .SkipTest (
51- f"hash digest { digestname !r} is not available."
52- )
53- return func_or_class (* args , ** kwargs )
86+ msg = f"missing OpenSSL hash algorithm: { digestname !r} "
87+ raise unittest .SkipTest (msg )
88+ return func (* args , ** kwargs )
5489 return wrapper
90+
91+ def decorator (func_or_class ):
92+ return _decorate_func_or_class (func_or_class , decorator_func )
5593 return decorator
0 commit comments