persistent-function-cache 0.2.2__py3-none-any.whl → 0.3.0__py3-none-any.whl

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.
@@ -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)
@@ -4,43 +4,30 @@ import hashlib
4
4
  import inspect
5
5
  import io
6
6
  import pickle
7
- from types import UnionType
8
- from typing import TYPE_CHECKING, Any, get_args, get_origin, get_type_hints
7
+ from functools import cache
8
+ from typing import TYPE_CHECKING, Any
9
9
 
10
- from persistent_cache.reducers.base import Reducer
10
+ from package_utils.annotations import first_parameter_types
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from collections.abc import Callable, Iterator # pragma: nocover
14
14
  from typing import BinaryIO # pragma: nocover
15
15
 
16
+ from persistent_cache.reducers.base import Reducer # pragma: nocover
16
17
 
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
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()
29
25
 
30
26
 
31
27
  class HashPickler(pickle.Pickler):
32
- def __init__(
33
- self,
34
- file_pointer: BinaryIO,
35
- reducer: type[Reducer] = Reducer,
36
- ) -> None:
28
+ def __init__(self, file_pointer: BinaryIO, reducer: type[Reducer]) -> None:
37
29
  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
30
+ self.reducers = load_reducers(reducer) # type: ignore[arg-type]
44
31
 
45
32
  def reducer_override(self, obj: Any) -> Any:
46
33
  """The goal of this pickler is to create hashes of complex objects, not to
@@ -49,32 +36,19 @@ class HashPickler(pickle.Pickler):
49
36
  So mapping does not need to be reversible.
50
37
  """
51
38
  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
39
+ return NotImplemented if reducer is None else (tuple, (reducer(obj),))
60
40
 
61
41
  def determine_reducer(self, obj: Any) -> Iterator[Callable[[Any], Any]]:
62
- if obj is not str:
42
+ if obj is not tuple:
63
43
  for obj_type, reducer in self.reducers.items():
64
44
  if isinstance(obj, obj_type):
65
45
  yield reducer
66
46
 
67
47
 
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()
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
 
@@ -4,20 +4,20 @@ persistent_cache/caches/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
4
4
  persistent_cache/caches/deep_learning.py,sha256=tJQrRJK2Sbv7tIGwnrnFAn8nP-Fh7ZgugocKcXeiMnU,126
5
5
  persistent_cache/caches/speedup_deep_learning.py,sha256=bywNXeEZsg8Rs2A1_w-ZrJi9Ew2TvNCz59YryGyV0NI,134
6
6
  persistent_cache/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- persistent_cache/cli/clear_cache.py,sha256=YbKfqzUB9IyEpU-2n0h5BrK9kZKjbayqiMTzjPbDl3s,1541
7
+ persistent_cache/cli/clear_cache.py,sha256=WlxwPo_ZUKMf45tNTFkwv92xVp777WXyGDq57OVIVTI,1527
8
8
  persistent_cache/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- persistent_cache/main/cache_slot.py,sha256=MmWBcz798QA8N6CPXXNxYmlGb3HwbG21NbbFcavH6eU,3265
10
- persistent_cache/main/decorator.py,sha256=lE_0q-KEavCe8IaK8hwuMUxH-tkN1_v-Jgy_dM_8Uxw,2684
11
- persistent_cache/main/hashing.py,sha256=6GYzUJcqjfjHPWMqPOCEY9FUSH2k2zUGrUza2fKaMTQ,2688
9
+ persistent_cache/main/cache_slot.py,sha256=jL9lwbkVC-JqIDwYkMG9-6ZfXYYMQYhN7KGL4w7ldCM,3332
10
+ persistent_cache/main/decorator.py,sha256=du8fn7MgJS4jqbfLK6Q0T2huHzpRCfNovky_WFh5DZs,2686
11
+ persistent_cache/main/hashing.py,sha256=dzeCfLlg5Rk27XKnOIPBkJFGMu8hwM_cZvHEWePuKiE,1891
12
12
  persistent_cache/models/__init__.py,sha256=1OeLZ6FZ5gAjlerAjc69Vx0AqWLSx6OSqNCpuzkCZQg,23
13
- persistent_cache/models/path.py,sha256=-HRJ4yqqmBPlax4H0KS8guhndBXNwCnl7WZXhtaWmGQ,441
13
+ persistent_cache/models/path.py,sha256=9HAYSB6sv7ac41Rpl1-9BYSDZrpoS_q5eNjUkivtr-w,440
14
14
  persistent_cache/reducers/__init__.py,sha256=hCozFuvrAb_gXLcqMMq92v4KdwBbQugwGwCmyLGt5RE,26
15
15
  persistent_cache/reducers/base.py,sha256=HsodcuE8Id5oV93_b_RN18I69UWsJ21yazXCwT1f6YU,1428
16
16
  persistent_cache/reducers/deep_learning.py,sha256=cyVolwmwm_ZN2gRxPtfJFqiNQlNuq_CrehO7ZSVWeVw,800
17
- persistent_cache/reducers/speedup_deep_learning.py,sha256=kMkr1467zUsQUPOi_N8tWKWAtKUcFgsk3rh94QhETg0,1970
18
- persistent_function_cache-0.2.2.dist-info/LICENSE,sha256=ENpNaBSvIV7ifD7iNz_-lBhImbiYowqPzBc5V5R8IhM,1070
19
- persistent_function_cache-0.2.2.dist-info/METADATA,sha256=PoI9EMIFqiZ21RHpHKNctkbdhu7h0LdHWD7TS-8_Krc,2164
20
- persistent_function_cache-0.2.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
21
- persistent_function_cache-0.2.2.dist-info/entry_points.txt,sha256=fSZfuL6Wn42mZldojgGyg7dzSPCRVAuZox3w6dLd2ZI,88
22
- persistent_function_cache-0.2.2.dist-info/top_level.txt,sha256=Jj-Z9wxHRlRyDUxrfC9GeaV5-8hAf8R4LclQFZesxi8,17
23
- persistent_function_cache-0.2.2.dist-info/RECORD,,
17
+ persistent_cache/reducers/speedup_deep_learning.py,sha256=-kBazhXzPuDpu2CLzw8F2kFyunA1z_H0qU9lW6771y0,1841
18
+ persistent_function_cache-0.3.0.dist-info/licenses/LICENSE,sha256=ENpNaBSvIV7ifD7iNz_-lBhImbiYowqPzBc5V5R8IhM,1070
19
+ persistent_function_cache-0.3.0.dist-info/METADATA,sha256=RVklXJbtkZe5MCd1Xe6CDX7WnpuPbUE4k6PYNptX8I4,2186
20
+ persistent_function_cache-0.3.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
21
+ persistent_function_cache-0.3.0.dist-info/entry_points.txt,sha256=fSZfuL6Wn42mZldojgGyg7dzSPCRVAuZox3w6dLd2ZI,88
22
+ persistent_function_cache-0.3.0.dist-info/top_level.txt,sha256=Jj-Z9wxHRlRyDUxrfC9GeaV5-8hAf8R4LclQFZesxi8,17
23
+ persistent_function_cache-0.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5