diff --git a/CHANGES.rst b/CHANGES.rst index 5efd62cd..6493a5cb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,14 @@ +Version 0.13.0 +-------------- + +Unreleased + +- default ``hashlib.md5`` may not be available in FIPS builds. We + now do not access it at import time on ``FileSystemCache``so developers + have time to change the default. + ``hashlib.md5`` will be lazy loaded when a new default is not provided + + Version 0.12.0 -------------- diff --git a/src/cachelib/file.py b/src/cachelib/file.py index 02a3b0e1..43748b33 100644 --- a/src/cachelib/file.py +++ b/src/cachelib/file.py @@ -1,4 +1,5 @@ import errno +import hashlib import logging import os import platform @@ -7,7 +8,6 @@ import tempfile import typing as _t from contextlib import contextmanager -from hashlib import md5 from pathlib import Path from time import sleep from time import time @@ -16,6 +16,14 @@ from cachelib.serializers import FileSystemSerializer +def _lazy_md5(string: bytes = b"") -> _t.Any: + """Don't access ``hashlib.md5`` until runtime. FIPS builds may not include + md5, in which case the import and use as a default would fail before the + developer can configure something else. + """ + return hashlib.md5(string) + + class FileSystemCache(BaseCache): """A cache that stores the items on the file system. This cache depends on being the only user of the `cache_dir`. Make absolutely sure that @@ -38,6 +46,8 @@ class FileSystemCache(BaseCache): _fs_transaction_suffix = ".__wz_cache" #: keep amount of files in a cache element _fs_count_file = "__wz_cache_count" + #: default file name hashing method + _default_hash_method = staticmethod(_lazy_md5) serializer = FileSystemSerializer() @@ -47,12 +57,15 @@ def __init__( threshold: int = 500, default_timeout: int = 300, mode: _t.Optional[int] = None, - hash_method: _t.Any = md5, + hash_method: _t.Any = None, ): BaseCache.__init__(self, default_timeout) self._path = cache_dir self._threshold = threshold - self._hash_method = hash_method + + self._hash_method = self._default_hash_method + if hash_method is not None: + self._hash_method = hash_method # Mode set by user takes precedence. If no mode has # been given, we need to set the correct default based diff --git a/tests/test_file_system_cache.py b/tests/test_file_system_cache.py index ddf20442..9eda8c25 100644 --- a/tests/test_file_system_cache.py +++ b/tests/test_file_system_cache.py @@ -42,9 +42,21 @@ def __init__(self, *args, **kwargs): super().__init__(*args, hash_method=hashlib.sha256, **kwargs) +class CustomDefaultHashingMethodCache(FileSystemCache): + _default_hash_method = hashlib.sha256 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + @pytest.fixture( autouse=True, - params=[FileSystemCache, CustomSerializerCache, CustomHashingMethodCache], + params=[ + FileSystemCache, + CustomSerializerCache, + CustomHashingMethodCache, + CustomDefaultHashingMethodCache, + ], ) def cache_factory(request, tmpdir): def _factory(self, *args, **kwargs):