persistent-function-cache 0.2.2__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {persistent_function_cache-0.2.2/src/persistent_function_cache.egg-info → persistent_function_cache-0.3.1}/PKG-INFO +10 -9
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/README.md +1 -1
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/pyproject.toml +14 -8
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/cli/clear_cache.py +2 -4
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/main/cache_slot.py +6 -4
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/main/decorator.py +3 -2
- persistent_function_cache-0.3.1/src/persistent_cache/main/hashing.py +55 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/models/path.py +4 -6
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/reducers/base.py +8 -10
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/reducers/deep_learning.py +5 -7
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/reducers/speedup_deep_learning.py +0 -4
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1/src/persistent_function_cache.egg-info}/PKG-INFO +10 -9
- persistent_function_cache-0.3.1/src/persistent_function_cache.egg-info/requires.txt +9 -0
- persistent_function_cache-0.2.2/src/persistent_cache/main/hashing.py +0 -80
- persistent_function_cache-0.2.2/src/persistent_function_cache.egg-info/requires.txt +0 -9
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/LICENSE +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/setup.cfg +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/__init__.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/caches/__init__.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/caches/deep_learning.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/caches/speedup_deep_learning.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/cli/__init__.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/main/__init__.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/models/__init__.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/py.typed +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/reducers/__init__.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_function_cache.egg-info/SOURCES.txt +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_function_cache.egg-info/dependency_links.txt +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_function_cache.egg-info/entry_points.txt +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_function_cache.egg-info/top_level.txt +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/tests/test_cache.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/tests/test_clear_cache.py +0 -0
- {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/tests/test_deep_learning_cache.py +0 -0
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: persistent-function-cache
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Persistent cache for expensive functions
|
|
5
5
|
Author-email: Quinten Roets <qdr2104@columbia.edu>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Source Code, https://github.com/quintenroets/persistent-cache
|
|
8
|
-
Requires-Python:
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Requires-Dist: package-utils<1,>=0.
|
|
12
|
-
Requires-Dist: powercli<1,>=0.
|
|
13
|
-
Requires-Dist: superpathlib<3,>=2.0
|
|
11
|
+
Requires-Dist: package-utils<1,>=0.9.1
|
|
12
|
+
Requires-Dist: powercli<1,>=0.4.0
|
|
13
|
+
Requires-Dist: superpathlib<3,>=2.1.0
|
|
14
14
|
Provides-Extra: dev
|
|
15
|
-
Requires-Dist: package-dev-tools<1,>=0.
|
|
15
|
+
Requires-Dist: package-dev-tools<1,>=0.8.7; extra == "dev"
|
|
16
16
|
Requires-Dist: package-dev-utils<1,>=0.1.6; extra == "dev"
|
|
17
17
|
Requires-Dist: numpy<3,>=1.26.0; extra == "dev"
|
|
18
18
|
Requires-Dist: torch<3,>=1.26.0; extra == "dev"
|
|
19
|
+
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
# Cache
|
|
21
22
|
[](https://badge.fury.io/py/persistent-function-cache)
|
|
22
23
|

|
|
23
|
-

|
|
24
25
|

|
|
25
26
|

|
|
26
27
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Cache
|
|
2
2
|
[](https://badge.fury.io/py/persistent-function-cache)
|
|
3
3
|

|
|
4
|
-

|
|
5
5
|

|
|
6
6
|

|
|
7
7
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "persistent-function-cache"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "Persistent cache for expensive functions"
|
|
5
5
|
authors = [{name = "Quinten Roets", email = "qdr2104@columbia.edu"}]
|
|
6
|
-
license =
|
|
6
|
+
license = "MIT"
|
|
7
7
|
readme = "README.md"
|
|
8
|
-
requires-python = ">=3.
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
9
|
dependencies = [
|
|
10
|
-
"package-utils >=0.
|
|
11
|
-
"powercli >=0.
|
|
12
|
-
"superpathlib >=2.0
|
|
10
|
+
"package-utils >=0.9.1, <1",
|
|
11
|
+
"powercli >=0.4.0, <1",
|
|
12
|
+
"superpathlib >=2.1.0, <3",
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
[project.optional-dependencies]
|
|
16
16
|
dev = [
|
|
17
|
-
"package-dev-tools >=0.
|
|
17
|
+
"package-dev-tools >=0.8.7, <1",
|
|
18
18
|
"package-dev-utils >=0.1.6, <1",
|
|
19
19
|
"numpy >=1.26.0, <3",
|
|
20
20
|
"torch >=1.26.0, <3",
|
|
@@ -53,7 +53,13 @@ fix = true
|
|
|
53
53
|
select = ["ALL"]
|
|
54
54
|
ignore = [
|
|
55
55
|
"ANN401", # annotated with Any
|
|
56
|
-
"
|
|
56
|
+
"CPY001", # missing copyright notice
|
|
57
|
+
"D1", # missing docstrings
|
|
58
|
+
"D200", # one-line docstring
|
|
59
|
+
"D203", # conflicts with D211
|
|
60
|
+
"D205", # blank line between summary and description
|
|
61
|
+
"D212", # conflicts with D213
|
|
62
|
+
"D401", # imperative first line
|
|
57
63
|
"G004", # logging f-string
|
|
58
64
|
]
|
|
59
65
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from datetime import
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
4
|
from functools import cached_property
|
|
5
5
|
from typing import Annotated
|
|
6
6
|
|
|
@@ -42,9 +42,7 @@ def main(options: Options) -> None:
|
|
|
42
42
|
for path in Options.cache_path.find(should_remove, recurse_on_match=True):
|
|
43
43
|
if options.verbose:
|
|
44
44
|
relative_path = path.relative_to(Options.cache_path)
|
|
45
|
-
timestamp = datetime.fromtimestamp(path.mtime).astimezone(
|
|
46
|
-
tz=timezone.utc,
|
|
47
|
-
)
|
|
45
|
+
timestamp = datetime.fromtimestamp(path.mtime).astimezone(tz=UTC)
|
|
48
46
|
message = f"{relative_path} ({timestamp})"
|
|
49
47
|
cli.console.print(message)
|
|
50
48
|
path.unlink()
|
|
@@ -30,8 +30,8 @@ class CacheSlot:
|
|
|
30
30
|
try:
|
|
31
31
|
with self.path.open("rb") as fp:
|
|
32
32
|
return pickle.Unpickler(fp).load() # noqa: S301
|
|
33
|
-
except (pickle.UnpicklingError, EOFError):
|
|
34
|
-
# discard values of corrupted or empty slots
|
|
33
|
+
except (FileNotFoundError, pickle.UnpicklingError, EOFError):
|
|
34
|
+
# discard values of missing, corrupted or empty slots
|
|
35
35
|
raise KeyError from None
|
|
36
36
|
|
|
37
37
|
@value.setter
|
|
@@ -87,11 +87,13 @@ class CacheSlot:
|
|
|
87
87
|
if self.key_reducer is not None:
|
|
88
88
|
return self.key_reducer
|
|
89
89
|
if self.deep_learning:
|
|
90
|
-
from persistent_cache.reducers.deep_learning import
|
|
90
|
+
from persistent_cache.reducers.deep_learning import ( # noqa: PLC0415
|
|
91
|
+
Reducer as Reducer_,
|
|
92
|
+
)
|
|
91
93
|
|
|
92
94
|
return Reducer_
|
|
93
95
|
if self.speedup_deep_learning:
|
|
94
|
-
from persistent_cache.reducers.speedup_deep_learning import (
|
|
96
|
+
from persistent_cache.reducers.speedup_deep_learning import ( # noqa: PLC0415
|
|
95
97
|
Reducer as Reducer_,
|
|
96
98
|
)
|
|
97
99
|
|
|
@@ -49,7 +49,8 @@ def cache( # noqa: PLR0913
|
|
|
49
49
|
deep_learning: bool = False,
|
|
50
50
|
speedup_deep_learning: bool = False,
|
|
51
51
|
) -> F | Callable[[F], F]:
|
|
52
|
-
"""
|
|
52
|
+
"""
|
|
53
|
+
A decorator to cache function results. Decorated functions are only executed if
|
|
53
54
|
result is not present in cache. The arguments of the function can be any nested
|
|
54
55
|
complex object.
|
|
55
56
|
|
|
@@ -84,7 +85,7 @@ def cache( # noqa: PLR0913
|
|
|
84
85
|
cache_slot.value = result
|
|
85
86
|
return result
|
|
86
87
|
|
|
87
|
-
return cast(F, wrapped_function)
|
|
88
|
+
return cast("F", wrapped_function)
|
|
88
89
|
|
|
89
90
|
if function is not None:
|
|
90
91
|
cache_decorator = cache_decorator(function)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import inspect
|
|
5
|
+
import io
|
|
6
|
+
import pickle
|
|
7
|
+
from functools import cache
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
from package_utils.annotations import first_parameter_types
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import Callable, Iterator # pragma: nocover
|
|
14
|
+
from typing import BinaryIO # pragma: nocover
|
|
15
|
+
|
|
16
|
+
from persistent_cache.reducers.base import Reducer # pragma: nocover
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def compute_hash(key_reducer: type[Reducer], items: Iterator[Any]) -> str:
|
|
20
|
+
with io.BytesIO() as fp:
|
|
21
|
+
HashPickler(fp, key_reducer).dump(tuple(items))
|
|
22
|
+
data = fp.getvalue()
|
|
23
|
+
# use fast hash function because it is not used for security
|
|
24
|
+
return hashlib.new("sha1", data=data, usedforsecurity=False).hexdigest()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class HashPickler(pickle.Pickler):
|
|
28
|
+
def __init__(self, file_pointer: BinaryIO, reducer: type[Reducer]) -> None:
|
|
29
|
+
super().__init__(file_pointer)
|
|
30
|
+
self.reducers = load_reducers(reducer) # type: ignore[arg-type]
|
|
31
|
+
|
|
32
|
+
def reducer_override(self, obj: Any) -> Any:
|
|
33
|
+
"""
|
|
34
|
+
The goal of this pickler is to create hashes of complex objects, not to
|
|
35
|
+
reconstruct complex objects.
|
|
36
|
+
|
|
37
|
+
So mapping does not need to be reversible.
|
|
38
|
+
"""
|
|
39
|
+
reducer = next(self.determine_reducer(obj), None)
|
|
40
|
+
return NotImplemented if reducer is None else (tuple, (reducer(obj),))
|
|
41
|
+
|
|
42
|
+
def determine_reducer(self, obj: Any) -> Iterator[Callable[[Any], Any]]:
|
|
43
|
+
if obj is not tuple:
|
|
44
|
+
for obj_type, reducer in self.reducers.items():
|
|
45
|
+
if isinstance(obj, obj_type):
|
|
46
|
+
yield reducer
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@cache
|
|
50
|
+
def load_reducers(reducer: type[Reducer]) -> dict[type, Callable[[Any], Any]]:
|
|
51
|
+
return {
|
|
52
|
+
parameter_type: method
|
|
53
|
+
for _, method in inspect.getmembers(reducer, predicate=inspect.ismethod)
|
|
54
|
+
for parameter_type in first_parameter_types(method)
|
|
55
|
+
}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Self, cast
|
|
2
2
|
|
|
3
3
|
import superpathlib
|
|
4
4
|
from simple_classproperty import classproperty
|
|
5
5
|
|
|
6
|
-
T = TypeVar("T", bound="Path")
|
|
7
|
-
|
|
8
6
|
|
|
9
7
|
class Path(superpathlib.Path):
|
|
10
8
|
@classmethod
|
|
11
9
|
@classproperty
|
|
12
|
-
def source_root(cls
|
|
10
|
+
def source_root(cls) -> Self:
|
|
13
11
|
return cls(__file__).parent.parent
|
|
14
12
|
|
|
15
13
|
@classmethod
|
|
16
14
|
@classproperty
|
|
17
|
-
def cache(cls
|
|
15
|
+
def cache(cls) -> Self:
|
|
18
16
|
path = cls.script_assets / cls.source_root.name
|
|
19
|
-
return cast(
|
|
17
|
+
return cast("Self", path)
|
|
@@ -4,7 +4,8 @@ from types import FunctionType, ModuleType
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class Reducer:
|
|
7
|
-
"""
|
|
7
|
+
"""
|
|
8
|
+
Inherit from this class to implement own custom pickler.
|
|
8
9
|
|
|
9
10
|
The result of each function are pickled further with their custom
|
|
10
11
|
pickling function, so make sure to reduce each object to a new
|
|
@@ -14,15 +15,12 @@ class Reducer:
|
|
|
14
15
|
|
|
15
16
|
@classmethod
|
|
16
17
|
def reduce_code(cls, code_object: FunctionType | ModuleType | type) -> str:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
cache result can change when function/module/class implementation changes
|
|
24
|
-
"""
|
|
25
|
-
|
|
18
|
+
# name reduction for function/module/class is not enough because we assume
|
|
19
|
+
# cache result can change when function/module/class implementation changes
|
|
20
|
+
# custom lambda reduction needed:
|
|
21
|
+
# https://www.pythonpool.com/cant-pickle-local-object/
|
|
22
|
+
# custom module reduction needed:
|
|
23
|
+
# https://stackoverflow.com/questions/2790828/python-cant-pickle-module-objects-error
|
|
26
24
|
try:
|
|
27
25
|
reduction = inspect.getsource(code_object)
|
|
28
26
|
except (TypeError, OSError):
|
|
@@ -9,13 +9,11 @@ from . import base
|
|
|
9
9
|
class Reducer(base.Reducer):
|
|
10
10
|
@classmethod
|
|
11
11
|
def reduce_model(cls, model: torch.nn.Module) -> tuple[dict[str, Any], Any]:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- class implementation (forward method)
|
|
18
|
-
"""
|
|
12
|
+
# avoid pickling _forward_hooks of model:
|
|
13
|
+
# implemented as OrderedDict with nondeterministic keys
|
|
14
|
+
# model outputs determined by:
|
|
15
|
+
# - model weights
|
|
16
|
+
# - class implementation (forward method)
|
|
19
17
|
return model.state_dict(), model.__class__
|
|
20
18
|
|
|
21
19
|
@classmethod
|
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: persistent-function-cache
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Persistent cache for expensive functions
|
|
5
5
|
Author-email: Quinten Roets <qdr2104@columbia.edu>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Source Code, https://github.com/quintenroets/persistent-cache
|
|
8
|
-
Requires-Python:
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Requires-Dist: package-utils<1,>=0.
|
|
12
|
-
Requires-Dist: powercli<1,>=0.
|
|
13
|
-
Requires-Dist: superpathlib<3,>=2.0
|
|
11
|
+
Requires-Dist: package-utils<1,>=0.9.1
|
|
12
|
+
Requires-Dist: powercli<1,>=0.4.0
|
|
13
|
+
Requires-Dist: superpathlib<3,>=2.1.0
|
|
14
14
|
Provides-Extra: dev
|
|
15
|
-
Requires-Dist: package-dev-tools<1,>=0.
|
|
15
|
+
Requires-Dist: package-dev-tools<1,>=0.8.7; extra == "dev"
|
|
16
16
|
Requires-Dist: package-dev-utils<1,>=0.1.6; extra == "dev"
|
|
17
17
|
Requires-Dist: numpy<3,>=1.26.0; extra == "dev"
|
|
18
18
|
Requires-Dist: torch<3,>=1.26.0; extra == "dev"
|
|
19
|
+
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
# Cache
|
|
21
22
|
[](https://badge.fury.io/py/persistent-function-cache)
|
|
22
23
|

|
|
23
|
-

|
|
24
25
|

|
|
25
26
|

|
|
26
27
|
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import hashlib
|
|
4
|
-
import inspect
|
|
5
|
-
import io
|
|
6
|
-
import pickle
|
|
7
|
-
from types import UnionType
|
|
8
|
-
from typing import TYPE_CHECKING, Any, get_args, get_origin, get_type_hints
|
|
9
|
-
|
|
10
|
-
from persistent_cache.reducers.base import Reducer
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from collections.abc import Callable, Iterator # pragma: nocover
|
|
14
|
-
from typing import BinaryIO # pragma: nocover
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def extract_types(method: Callable[[Any], Any]) -> Iterator[type]:
|
|
18
|
-
type_hints = get_type_hints(method).values()
|
|
19
|
-
if type_hints:
|
|
20
|
-
argument_type = next(iter(type_hints))
|
|
21
|
-
origin = get_origin(argument_type)
|
|
22
|
-
arguments = get_args(argument_type)
|
|
23
|
-
if origin is UnionType:
|
|
24
|
-
yield from arguments
|
|
25
|
-
elif origin is not None:
|
|
26
|
-
yield origin
|
|
27
|
-
else:
|
|
28
|
-
yield argument_type
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class HashPickler(pickle.Pickler):
|
|
32
|
-
def __init__(
|
|
33
|
-
self,
|
|
34
|
-
file_pointer: BinaryIO,
|
|
35
|
-
reducer: type[Reducer] = Reducer,
|
|
36
|
-
) -> None:
|
|
37
|
-
super().__init__(file_pointer)
|
|
38
|
-
self.reducer = reducer
|
|
39
|
-
self.reducers = {}
|
|
40
|
-
for _, method in inspect.getmembers(reducer, predicate=inspect.ismethod):
|
|
41
|
-
argument_types = extract_types(method)
|
|
42
|
-
for argument_type in argument_types:
|
|
43
|
-
self.reducers[argument_type] = method
|
|
44
|
-
|
|
45
|
-
def reducer_override(self, obj: Any) -> Any:
|
|
46
|
-
"""The goal of this pickler is to create hashes of complex objects, not to
|
|
47
|
-
reconstruct complex objects.
|
|
48
|
-
|
|
49
|
-
So mapping does not need to be reversible.
|
|
50
|
-
"""
|
|
51
|
-
reducer = next(self.determine_reducer(obj), None)
|
|
52
|
-
reduction: Any
|
|
53
|
-
if reducer is None:
|
|
54
|
-
reduction = NotImplemented
|
|
55
|
-
else:
|
|
56
|
-
mapping = reducer(obj)
|
|
57
|
-
str_mapping = str(item_to_bytes(self.reducer, mapping))
|
|
58
|
-
reduction = str, (str_mapping,)
|
|
59
|
-
return reduction
|
|
60
|
-
|
|
61
|
-
def determine_reducer(self, obj: Any) -> Iterator[Callable[[Any], Any]]:
|
|
62
|
-
if obj is not str:
|
|
63
|
-
for obj_type, reducer in self.reducers.items():
|
|
64
|
-
if isinstance(obj, obj_type):
|
|
65
|
-
yield reducer
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def compute_hash(key_reducer: type[Reducer], items: Iterator[Any]) -> str:
|
|
69
|
-
data = item_to_bytes(key_reducer, tuple(items))
|
|
70
|
-
# use fast hash function because it is not used for security
|
|
71
|
-
return hashlib.new("sha1", data=data, usedforsecurity=False).hexdigest()
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def item_to_bytes(key_reducer: type[Reducer], item: Any) -> bytes:
|
|
75
|
-
with io.BytesIO() as fp:
|
|
76
|
-
# Use custom pickler to generate bytes from complex structures
|
|
77
|
-
pickler = HashPickler(fp, key_reducer)
|
|
78
|
-
pickler.dump(item)
|
|
79
|
-
fp.seek(0)
|
|
80
|
-
return fp.read()
|
|
File without changes
|
|
File without changes
|
{persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/src/persistent_cache/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persistent_function_cache-0.2.2 → persistent_function_cache-0.3.1}/tests/test_clear_cache.py
RENAMED
|
File without changes
|
|
File without changes
|