|
7 | 7 | except ImportError: |
8 | 8 | _hashlib = None |
9 | 9 |
|
| 10 | +try: |
| 11 | + import _hmac |
| 12 | +except: |
| 13 | + _hmac = None |
| 14 | + |
10 | 15 |
|
11 | 16 | def requires_hashlib(): |
12 | 17 | return unittest.skipIf(_hashlib is None, "requires _hashlib") |
13 | 18 |
|
14 | 19 |
|
| 20 | +def requires_builtin_hmac(): |
| 21 | + return unittest.skipIf(_hmac is None, "requires _hmac") |
| 22 | + |
| 23 | + |
| 24 | +def _decorate_func_or_class(func_or_class, decorator_func): |
| 25 | + if not isinstance(func_or_class, type): |
| 26 | + return decorator_func(func_or_class) |
| 27 | + |
| 28 | + decorated_class = func_or_class |
| 29 | + setUpClass = decorated_class.__dict__.get('setUpClass') |
| 30 | + if setUpClass is None: |
| 31 | + def setUpClass(cls): |
| 32 | + super(decorated_class, cls).setUpClass() |
| 33 | + setUpClass.__qualname__ = decorated_class.__qualname__ + '.setUpClass' |
| 34 | + setUpClass.__module__ = decorated_class.__module__ |
| 35 | + else: |
| 36 | + setUpClass = setUpClass.__func__ |
| 37 | + setUpClass = classmethod(decorator_func(setUpClass)) |
| 38 | + decorated_class.setUpClass = setUpClass |
| 39 | + return decorated_class |
| 40 | + |
| 41 | + |
15 | 42 | def requires_hashdigest(digestname, openssl=None, usedforsecurity=True): |
16 | | - """Decorator raising SkipTest if a hashing algorithm is not available |
| 43 | + """Decorator raising SkipTest if a hashing algorithm is not available. |
17 | 44 |
|
18 | | - The hashing algorithm could be missing or blocked by a strict crypto |
19 | | - policy. |
| 45 | + The hashing algorithm may be missing, blocked by a strict crypto policy, |
| 46 | + or Python may be configured with `--with-builtin-hashlib-hashes=no`. |
20 | 47 |
|
21 | 48 | 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. |
| 49 | + the algorithm. Otherwise the check falls back to (optional) built-in |
| 50 | + HACL* implementations. |
| 51 | +
|
| 52 | + The usedforsecurity flag is passed to the constructor but has no effect |
| 53 | + for HACL* implementations. |
24 | 54 |
|
| 55 | + Examples of exceptions being suppressed: |
25 | 56 | ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS |
26 | 57 | ValueError: unsupported hash type md4 |
27 | 58 | """ |
28 | | - 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) |
| 59 | + if openssl and _hashlib is not None: |
| 60 | + def test_availability(): |
| 61 | + _hashlib.new(digestname, usedforsecurity=usedforsecurity) |
| 62 | + else: |
| 63 | + def test_availability(): |
| 64 | + hashlib.new(digestname, usedforsecurity=usedforsecurity) |
| 65 | + |
| 66 | + def decorator_func(func): |
| 67 | + @functools.wraps(func) |
43 | 68 | def wrapper(*args, **kwargs): |
44 | 69 | try: |
45 | | - if openssl and _hashlib is not None: |
46 | | - _hashlib.new(digestname, usedforsecurity=usedforsecurity) |
47 | | - else: |
48 | | - hashlib.new(digestname, usedforsecurity=usedforsecurity) |
49 | | - except ValueError: |
50 | | - raise unittest.SkipTest( |
51 | | - f"hash digest {digestname!r} is not available." |
52 | | - ) |
53 | | - return func_or_class(*args, **kwargs) |
| 70 | + test_availability() |
| 71 | + except ValueError as exc: |
| 72 | + msg = f"missing hash algorithm: {digestname!r}" |
| 73 | + raise unittest.SkipTest(msg) from exc |
| 74 | + return func(*args, **kwargs) |
54 | 75 | return wrapper |
| 76 | + |
| 77 | + def decorator(func_or_class): |
| 78 | + return _decorate_func_or_class(func_or_class, decorator_func) |
55 | 79 | return decorator |
0 commit comments