persistent-function-cache 0.2.2__tar.gz → 0.3.0__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.
Files changed (33) hide show
  1. {persistent_function_cache-0.2.2/src/persistent_function_cache.egg-info → persistent_function_cache-0.3.0}/PKG-INFO +10 -9
  2. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/README.md +1 -1
  3. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/pyproject.toml +14 -7
  4. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/cli/clear_cache.py +2 -2
  5. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/main/cache_slot.py +4 -2
  6. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/main/decorator.py +1 -1
  7. persistent_function_cache-0.3.0/src/persistent_cache/main/hashing.py +54 -0
  8. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/models/path.py +4 -4
  9. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/reducers/speedup_deep_learning.py +0 -4
  10. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0/src/persistent_function_cache.egg-info}/PKG-INFO +10 -9
  11. persistent_function_cache-0.3.0/src/persistent_function_cache.egg-info/requires.txt +9 -0
  12. persistent_function_cache-0.2.2/src/persistent_cache/main/hashing.py +0 -80
  13. persistent_function_cache-0.2.2/src/persistent_function_cache.egg-info/requires.txt +0 -9
  14. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/LICENSE +0 -0
  15. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/setup.cfg +0 -0
  16. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/__init__.py +0 -0
  17. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/caches/__init__.py +0 -0
  18. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/caches/deep_learning.py +0 -0
  19. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/caches/speedup_deep_learning.py +0 -0
  20. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/cli/__init__.py +0 -0
  21. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/main/__init__.py +0 -0
  22. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/models/__init__.py +0 -0
  23. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/py.typed +0 -0
  24. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/reducers/__init__.py +0 -0
  25. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/reducers/base.py +0 -0
  26. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_cache/reducers/deep_learning.py +0 -0
  27. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_function_cache.egg-info/SOURCES.txt +0 -0
  28. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_function_cache.egg-info/dependency_links.txt +0 -0
  29. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_function_cache.egg-info/entry_points.txt +0 -0
  30. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/src/persistent_function_cache.egg-info/top_level.txt +0 -0
  31. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/tests/test_cache.py +0 -0
  32. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/tests/test_clear_cache.py +0 -0
  33. {persistent_function_cache-0.2.2 → persistent_function_cache-0.3.0}/tests/test_deep_learning_cache.py +0 -0
@@ -1,26 +1,27 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: persistent-function-cache
3
- Version: 0.2.2
3
+ Version: 0.3.0
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: <3.13,>=3.10
8
+ Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: package-utils<1,>=0.6.7
12
- Requires-Dist: powercli<1,>=0.3.2
13
- Requires-Dist: superpathlib<3,>=2.0.9
11
+ Requires-Dist: package-utils<1,>=0.8.5
12
+ Requires-Dist: powercli<1,>=0.4.0
13
+ Requires-Dist: superpathlib<3,>=2.0.13
14
14
  Provides-Extra: dev
15
- Requires-Dist: package-dev-tools<1,>=0.5.11; extra == "dev"
15
+ Requires-Dist: package-dev-tools<1,>=0.8.1; 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
  [![PyPI version](https://badge.fury.io/py/persistent-function-cache.svg)](https://badge.fury.io/py/persistent-function-cache)
22
23
  ![PyPI downloads](https://img.shields.io/pypi/dm/persistent-function-cache)
23
- ![Python version](https://img.shields.io/badge/python-3.10--3.12-brightgreen)
24
+ ![Python version](https://img.shields.io/badge/python-3.11+-brightgreen)
24
25
  ![Operating system](https://img.shields.io/badge/os-linux%20%7c%20macOS%20%7c%20windows-brightgreen)
25
26
  ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
26
27
 
@@ -1,7 +1,7 @@
1
1
  # Cache
2
2
  [![PyPI version](https://badge.fury.io/py/persistent-function-cache.svg)](https://badge.fury.io/py/persistent-function-cache)
3
3
  ![PyPI downloads](https://img.shields.io/pypi/dm/persistent-function-cache)
4
- ![Python version](https://img.shields.io/badge/python-3.10--3.12-brightgreen)
4
+ ![Python version](https://img.shields.io/badge/python-3.11+-brightgreen)
5
5
  ![Operating system](https://img.shields.io/badge/os-linux%20%7c%20macOS%20%7c%20windows-brightgreen)
6
6
  ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
7
7
 
@@ -1,20 +1,20 @@
1
1
  [project]
2
2
  name = "persistent-function-cache"
3
- version = "0.2.2"
3
+ version = "0.3.0"
4
4
  description = "Persistent cache for expensive functions"
5
5
  authors = [{name = "Quinten Roets", email = "qdr2104@columbia.edu"}]
6
- license = {text = "MIT"}
6
+ license = "MIT"
7
7
  readme = "README.md"
8
- requires-python = ">=3.10, <3.13"
8
+ requires-python = ">=3.11"
9
9
  dependencies = [
10
- "package-utils >=0.6.7, <1",
11
- "powercli >=0.3.2, <1",
12
- "superpathlib >=2.0.9, <3",
10
+ "package-utils >=0.8.5, <1",
11
+ "powercli >=0.4.0, <1",
12
+ "superpathlib >=2.0.13, <3",
13
13
  ]
14
14
 
15
15
  [project.optional-dependencies]
16
16
  dev = [
17
- "package-dev-tools >=0.5.11, <1",
17
+ "package-dev-tools >=0.8.1, <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,14 @@ fix = true
53
53
  select = ["ALL"]
54
54
  ignore = [
55
55
  "ANN401", # annotated with Any
56
+ "CPY001", # missing copyright notice
56
57
  "D", # docstrings
58
+ "D1", # missing docstrings
59
+ "D200", # one-line docstring
60
+ "D203", # conflicts with D211
61
+ "D205", # blank line between summary and description
62
+ "D212", # conflicts with D213
63
+ "D401", # imperative first line
57
64
  "G004", # logging f-string
58
65
  ]
59
66
 
@@ -1,6 +1,6 @@
1
1
  import time
2
2
  from dataclasses import dataclass
3
- from datetime import datetime, timezone
3
+ from datetime import UTC, datetime
4
4
  from functools import cached_property
5
5
  from typing import Annotated
6
6
 
@@ -43,7 +43,7 @@ def main(options: Options) -> None:
43
43
  if options.verbose:
44
44
  relative_path = path.relative_to(Options.cache_path)
45
45
  timestamp = datetime.fromtimestamp(path.mtime).astimezone(
46
- tz=timezone.utc,
46
+ tz=UTC,
47
47
  )
48
48
  message = f"{relative_path} ({timestamp})"
49
49
  cli.console.print(message)
@@ -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 Reducer as Reducer_
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
 
@@ -84,7 +84,7 @@ def cache( # noqa: PLR0913
84
84
  cache_slot.value = result
85
85
  return result
86
86
 
87
- return cast(F, wrapped_function)
87
+ return cast("F", wrapped_function)
88
88
 
89
89
  if function is not None:
90
90
  cache_decorator = cache_decorator(function)
@@ -0,0 +1,54 @@
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
+ """The goal of this pickler is to create hashes of complex objects, not to
34
+ reconstruct complex objects.
35
+
36
+ So mapping does not need to be reversible.
37
+ """
38
+ reducer = next(self.determine_reducer(obj), None)
39
+ return NotImplemented if reducer is None else (tuple, (reducer(obj),))
40
+
41
+ def determine_reducer(self, obj: Any) -> Iterator[Callable[[Any], Any]]:
42
+ if obj is not tuple:
43
+ for obj_type, reducer in self.reducers.items():
44
+ if isinstance(obj, obj_type):
45
+ yield reducer
46
+
47
+
48
+ @cache
49
+ def load_reducers(reducer: type[Reducer]) -> dict[type, Callable[[Any], Any]]:
50
+ return {
51
+ parameter_type: method
52
+ for _, method in inspect.getmembers(reducer, predicate=inspect.ismethod)
53
+ for parameter_type in first_parameter_types(method)
54
+ }
@@ -1,4 +1,4 @@
1
- from typing import TypeVar, cast
1
+ from typing import Self, TypeVar, cast
2
2
 
3
3
  import superpathlib
4
4
  from simple_classproperty import classproperty
@@ -9,11 +9,11 @@ T = TypeVar("T", bound="Path")
9
9
  class Path(superpathlib.Path):
10
10
  @classmethod
11
11
  @classproperty
12
- def source_root(cls: type[T]) -> T:
12
+ def source_root(cls) -> Self:
13
13
  return cls(__file__).parent.parent
14
14
 
15
15
  @classmethod
16
16
  @classproperty
17
- def cache(cls: type[T]) -> T:
17
+ def cache(cls) -> Self:
18
18
  path = cls.script_assets / cls.source_root.name
19
- return cast(T, path)
19
+ return cast("Self", path)
@@ -57,7 +57,3 @@ class Reducer(deep_learning.Reducer):
57
57
  label = None
58
58
 
59
59
  return length, data, label
60
-
61
- @classmethod
62
- def reduce_tensor(cls, tensor: torch.Tensor) -> NDArray[Any]:
63
- return tensor.detach().cpu().numpy()
@@ -1,26 +1,27 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: persistent-function-cache
3
- Version: 0.2.2
3
+ Version: 0.3.0
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: <3.13,>=3.10
8
+ Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: package-utils<1,>=0.6.7
12
- Requires-Dist: powercli<1,>=0.3.2
13
- Requires-Dist: superpathlib<3,>=2.0.9
11
+ Requires-Dist: package-utils<1,>=0.8.5
12
+ Requires-Dist: powercli<1,>=0.4.0
13
+ Requires-Dist: superpathlib<3,>=2.0.13
14
14
  Provides-Extra: dev
15
- Requires-Dist: package-dev-tools<1,>=0.5.11; extra == "dev"
15
+ Requires-Dist: package-dev-tools<1,>=0.8.1; 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
  [![PyPI version](https://badge.fury.io/py/persistent-function-cache.svg)](https://badge.fury.io/py/persistent-function-cache)
22
23
  ![PyPI downloads](https://img.shields.io/pypi/dm/persistent-function-cache)
23
- ![Python version](https://img.shields.io/badge/python-3.10--3.12-brightgreen)
24
+ ![Python version](https://img.shields.io/badge/python-3.11+-brightgreen)
24
25
  ![Operating system](https://img.shields.io/badge/os-linux%20%7c%20macOS%20%7c%20windows-brightgreen)
25
26
  ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
26
27
 
@@ -0,0 +1,9 @@
1
+ package-utils<1,>=0.8.5
2
+ powercli<1,>=0.4.0
3
+ superpathlib<3,>=2.0.13
4
+
5
+ [dev]
6
+ package-dev-tools<1,>=0.8.1
7
+ package-dev-utils<1,>=0.1.6
8
+ numpy<3,>=1.26.0
9
+ torch<3,>=1.26.0
@@ -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()
@@ -1,9 +0,0 @@
1
- package-utils<1,>=0.6.7
2
- powercli<1,>=0.3.2
3
- superpathlib<3,>=2.0.9
4
-
5
- [dev]
6
- package-dev-tools<1,>=0.5.11
7
- package-dev-utils<1,>=0.1.6
8
- numpy<3,>=1.26.0
9
- torch<3,>=1.26.0