cachier 3.0.0__tar.gz → 3.1.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.
- {cachier-3.0.0 → cachier-3.1.0}/MANIFEST.in +3 -3
- {cachier-3.0.0/cachier.egg-info → cachier-3.1.0}/PKG-INFO +33 -16
- {cachier-3.0.0 → cachier-3.1.0}/README.rst +2 -2
- cachier-3.1.0/pyproject.toml +174 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/__init__.py +4 -4
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/__main__.py +1 -1
- cachier-3.1.0/src/cachier/config.py +139 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/core.py +33 -28
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/cores/base.py +33 -24
- cachier-3.1.0/src/cachier/cores/memory.py +97 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/cores/mongo.py +28 -30
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/cores/pickle.py +68 -66
- cachier-3.1.0/src/cachier/version.info +1 -0
- {cachier-3.0.0 → cachier-3.1.0/src/cachier.egg-info}/PKG-INFO +33 -16
- cachier-3.1.0/src/cachier.egg-info/SOURCES.txt +23 -0
- cachier-3.1.0/src/cachier.egg-info/entry_points.txt +2 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier.egg-info/requires.txt +1 -2
- cachier-3.0.0/cachier/config.py +0 -101
- cachier-3.0.0/cachier/cores/memory.py +0 -89
- cachier-3.0.0/cachier/version.info +0 -1
- cachier-3.0.0/cachier.egg-info/SOURCES.txt +0 -25
- cachier-3.0.0/cachier.egg-info/entry_points.txt +0 -2
- cachier-3.0.0/pyproject.toml +0 -78
- cachier-3.0.0/requirements.txt +0 -3
- cachier-3.0.0/setup.py +0 -84
- {cachier-3.0.0 → cachier-3.1.0}/LICENSE +0 -0
- {cachier-3.0.0 → cachier-3.1.0}/setup.cfg +0 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/_types.py +0 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/_version.py +0 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/cores/__init__.py +0 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier/py.typed +0 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier.egg-info/dependency_links.txt +0 -0
- {cachier-3.0.0 → cachier-3.1.0/src}/cachier.egg-info/top_level.txt +0 -0
@@ -1,9 +1,9 @@
|
|
1
1
|
# Manifest syntax https://docs.python.org/2/distutils/sourcedist.html
|
2
2
|
graft wheelhouse
|
3
3
|
|
4
|
-
recursive-
|
5
|
-
include cachier/version.info
|
4
|
+
recursive-include src *.info
|
6
5
|
include README.rst
|
7
|
-
include
|
6
|
+
include LICENSE
|
8
7
|
|
8
|
+
prune __pycache__
|
9
9
|
prune tests
|
@@ -1,33 +1,50 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: cachier
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.1.0
|
4
4
|
Summary: Persistent, stale-free, local and cross-machine caching for Python functions.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
Author-email: Shay Palachy Affek <shay.palachy@gmail.com>
|
6
|
+
License: MIT License
|
7
|
+
|
8
|
+
Copyright (c) 2016 Shay Palachy
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
18
|
+
copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
SOFTWARE.
|
27
|
+
|
28
|
+
Project-URL: Source, https://github.com/python-cachier/cachier
|
29
|
+
Keywords: cache,caching,cross-machine,decorator,local,memoization,mongo,persistent
|
13
30
|
Classifier: Development Status :: 4 - Beta
|
31
|
+
Classifier: Intended Audience :: Developers
|
14
32
|
Classifier: License :: OSI Approved :: MIT License
|
15
33
|
Classifier: Programming Language :: Python
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
34
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
17
35
|
Classifier: Programming Language :: Python :: 3.8
|
18
36
|
Classifier: Programming Language :: Python :: 3.9
|
19
37
|
Classifier: Programming Language :: Python :: 3.10
|
20
38
|
Classifier: Programming Language :: Python :: 3.11
|
21
39
|
Classifier: Programming Language :: Python :: 3.12
|
40
|
+
Classifier: Topic :: Other/Nonlisted Topic
|
22
41
|
Classifier: Topic :: Software Development :: Libraries
|
23
42
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
24
43
|
Classifier: Topic :: Utilities
|
25
|
-
|
26
|
-
Classifier: Intended Audience :: Developers
|
44
|
+
Description-Content-Type: text/x-rst
|
27
45
|
License-File: LICENSE
|
28
|
-
Requires-Dist: watchdog>=2.3.1
|
29
46
|
Requires-Dist: portalocker>=2.3.2
|
30
|
-
Requires-Dist:
|
47
|
+
Requires-Dist: watchdog>=2.3.1
|
31
48
|
|
32
49
|
Cachier
|
33
50
|
#######
|
@@ -492,8 +509,8 @@ Notable bugfixers:
|
|
492
509
|
.. |PyPI-Versions| image:: https://img.shields.io/pypi/pyversions/cachier.svg
|
493
510
|
:target: https://pypi.python.org/pypi/cachier
|
494
511
|
|
495
|
-
.. |Build-Status| image:: https://github.com/python-cachier/cachier/actions/workflows/test.yml/badge.svg
|
496
|
-
:target: https://github.com/python-cachier/cachier/actions/workflows/test.yml
|
512
|
+
.. |Build-Status| image:: https://github.com/python-cachier/cachier/actions/workflows/ci-test.yml/badge.svg
|
513
|
+
:target: https://github.com/python-cachier/cachier/actions/workflows/ci-test.yml
|
497
514
|
|
498
515
|
.. |LICENCE| image:: https://img.shields.io/pypi/l/cachier.svg
|
499
516
|
:target: https://pypi.python.org/pypi/cachier
|
@@ -461,8 +461,8 @@ Notable bugfixers:
|
|
461
461
|
.. |PyPI-Versions| image:: https://img.shields.io/pypi/pyversions/cachier.svg
|
462
462
|
:target: https://pypi.python.org/pypi/cachier
|
463
463
|
|
464
|
-
.. |Build-Status| image:: https://github.com/python-cachier/cachier/actions/workflows/test.yml/badge.svg
|
465
|
-
:target: https://github.com/python-cachier/cachier/actions/workflows/test.yml
|
464
|
+
.. |Build-Status| image:: https://github.com/python-cachier/cachier/actions/workflows/ci-test.yml/badge.svg
|
465
|
+
:target: https://github.com/python-cachier/cachier/actions/workflows/ci-test.yml
|
466
466
|
|
467
467
|
.. |LICENCE| image:: https://img.shields.io/pypi/l/cachier.svg
|
468
468
|
:target: https://pypi.python.org/pypi/cachier
|
@@ -0,0 +1,174 @@
|
|
1
|
+
[build-system]
|
2
|
+
requires = [
|
3
|
+
"setuptools",
|
4
|
+
"wheel",
|
5
|
+
]
|
6
|
+
|
7
|
+
[project]
|
8
|
+
name = "cachier"
|
9
|
+
description = "Persistent, stale-free, local and cross-machine caching for Python functions."
|
10
|
+
readme = "README.rst"
|
11
|
+
keywords = [
|
12
|
+
"cache",
|
13
|
+
"caching",
|
14
|
+
"cross-machine",
|
15
|
+
"decorator",
|
16
|
+
"local",
|
17
|
+
"memoization",
|
18
|
+
"mongo",
|
19
|
+
"persistent",
|
20
|
+
]
|
21
|
+
license = { file = "LICENSE" }
|
22
|
+
authors = [
|
23
|
+
{ name = "Shay Palachy Affek", email = 'shay.palachy@gmail.com' },
|
24
|
+
]
|
25
|
+
classifiers = [
|
26
|
+
"Development Status :: 4 - Beta",
|
27
|
+
"Intended Audience :: Developers",
|
28
|
+
"License :: OSI Approved :: MIT License",
|
29
|
+
"Programming Language :: Python",
|
30
|
+
"Programming Language :: Python :: 3 :: Only",
|
31
|
+
"Programming Language :: Python :: 3.8",
|
32
|
+
"Programming Language :: Python :: 3.9",
|
33
|
+
"Programming Language :: Python :: 3.10",
|
34
|
+
"Programming Language :: Python :: 3.11",
|
35
|
+
"Programming Language :: Python :: 3.12",
|
36
|
+
"Topic :: Other/Nonlisted Topic",
|
37
|
+
"Topic :: Software Development :: Libraries",
|
38
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
39
|
+
"Topic :: Utilities",
|
40
|
+
]
|
41
|
+
dynamic = [
|
42
|
+
"version",
|
43
|
+
]
|
44
|
+
dependencies = [
|
45
|
+
"portalocker>=2.3.2",
|
46
|
+
"watchdog>=2.3.1",
|
47
|
+
]
|
48
|
+
urls.Source = "https://github.com/python-cachier/cachier"
|
49
|
+
scripts.cachier = "cachier.__main__:cli"
|
50
|
+
|
51
|
+
[tool.setuptools]
|
52
|
+
include-package-data = true
|
53
|
+
|
54
|
+
[tool.setuptools.dynamic]
|
55
|
+
version = { attr = "cachier._version.__version__" }
|
56
|
+
|
57
|
+
[tool.setuptools.packages.find]
|
58
|
+
where = [
|
59
|
+
"src",
|
60
|
+
] # list of folders that contain the packages (["."] by default)
|
61
|
+
include = [
|
62
|
+
"cachier*",
|
63
|
+
] # package names should match these glob patterns (["*"] by default)
|
64
|
+
namespaces = false # to disable scanning PEP 420 namespaces (true by default)
|
65
|
+
|
66
|
+
[tool.ruff]
|
67
|
+
target-version = "py38"
|
68
|
+
line-length = 79
|
69
|
+
# Exclude a variety of commonly ignored directories.
|
70
|
+
exclude = [
|
71
|
+
".eggs",
|
72
|
+
".git",
|
73
|
+
".ruff_cache",
|
74
|
+
"__pypackages__",
|
75
|
+
"_build",
|
76
|
+
"build",
|
77
|
+
"dist",
|
78
|
+
]
|
79
|
+
# Enable Pyflakes `E` and `F` codes by default.
|
80
|
+
lint.select = [
|
81
|
+
"D", # see: https://pypi.org/project/pydocstyle
|
82
|
+
"E",
|
83
|
+
"F", # see: https://pypi.org/project/pyflakes
|
84
|
+
"I", #see: https://pypi.org/project/isort/
|
85
|
+
"RUF100", # alternative to yesqa
|
86
|
+
#"N", # see: https://pypi.org/project/pep8-naming
|
87
|
+
"S", # see: https://pypi.org/project/flake8-bandit
|
88
|
+
"SIM",
|
89
|
+
"W", # see: https://pypi.org/project/pycodestyle
|
90
|
+
]
|
91
|
+
lint.extend-select = [
|
92
|
+
"A", # see: https://pypi.org/project/flake8-builtins
|
93
|
+
"B", # see: https://pypi.org/project/flake8-bugbear
|
94
|
+
"C4", # see: https://pypi.org/project/flake8-comprehensions
|
95
|
+
"PT", # see: https://pypi.org/project/flake8-pytest-style
|
96
|
+
]
|
97
|
+
lint.ignore = [
|
98
|
+
"C901",
|
99
|
+
"E203",
|
100
|
+
]
|
101
|
+
lint.per-file-ignores."src/**/__init__.py" = [
|
102
|
+
"D104",
|
103
|
+
]
|
104
|
+
lint.per-file-ignores."src/cachier/config.py" = [
|
105
|
+
"D100",
|
106
|
+
]
|
107
|
+
lint.per-file-ignores."tests/**" = [
|
108
|
+
"D100",
|
109
|
+
"D101",
|
110
|
+
"D103",
|
111
|
+
"D104",
|
112
|
+
"D401",
|
113
|
+
"S101",
|
114
|
+
"S105",
|
115
|
+
"S311",
|
116
|
+
"S603",
|
117
|
+
]
|
118
|
+
lint.unfixable = [
|
119
|
+
"F401",
|
120
|
+
]
|
121
|
+
|
122
|
+
#[tool.ruff.pydocstyle]
|
123
|
+
## Use Google-style docstrings.
|
124
|
+
#convention = "google"
|
125
|
+
#[tool.ruff.pycodestyle]
|
126
|
+
#ignore-overlong-task-comments = true
|
127
|
+
# Unlike Flake8, default to a complexity level of 10.
|
128
|
+
lint.mccabe.max-complexity = 10
|
129
|
+
|
130
|
+
[tool.docformatter]
|
131
|
+
recursive = true
|
132
|
+
# some docstring start with r"""
|
133
|
+
wrap-summaries = 79
|
134
|
+
wrap-descriptions = 79
|
135
|
+
blank = true
|
136
|
+
|
137
|
+
[tool.pytest.ini_options]
|
138
|
+
testpaths = [
|
139
|
+
"cachier",
|
140
|
+
"tests",
|
141
|
+
]
|
142
|
+
norecursedirs = [
|
143
|
+
"dist",
|
144
|
+
"build",
|
145
|
+
]
|
146
|
+
addopts = [
|
147
|
+
"--color=yes",
|
148
|
+
"-r a",
|
149
|
+
"-v",
|
150
|
+
"-s",
|
151
|
+
]
|
152
|
+
markers = [
|
153
|
+
"mongo: test the MongoDB core",
|
154
|
+
"memory: test the memory core",
|
155
|
+
"pickle: test the pickle core",
|
156
|
+
]
|
157
|
+
|
158
|
+
[tool.coverage.run]
|
159
|
+
branch = true
|
160
|
+
dynamic_context = "test_function"
|
161
|
+
omit = [
|
162
|
+
"tests/*",
|
163
|
+
"cachier/_version.py",
|
164
|
+
"cachier/__init__.py",
|
165
|
+
"**/scripts/**",
|
166
|
+
]
|
167
|
+
[tool.coverage.report]
|
168
|
+
show_missing = true
|
169
|
+
# Regexes for lines to exclude from consideration
|
170
|
+
exclude_lines = [
|
171
|
+
"pragma: no cover", # Have to re-enable the standard pragma
|
172
|
+
"raise NotImplementedError", # Don't complain if tests don't hit defensive assertion code:
|
173
|
+
"if TYPE_CHECKING:", # Is only true when running mypy, not tests
|
174
|
+
]
|
@@ -2,15 +2,15 @@ from ._version import * # noqa: F403
|
|
2
2
|
from .config import (
|
3
3
|
disable_caching,
|
4
4
|
enable_caching,
|
5
|
-
|
6
|
-
|
5
|
+
get_global_params,
|
6
|
+
set_global_params,
|
7
7
|
)
|
8
8
|
from .core import cachier
|
9
9
|
|
10
10
|
__all__ = [
|
11
11
|
"cachier",
|
12
|
-
"
|
13
|
-
"
|
12
|
+
"set_global_params",
|
13
|
+
"get_global_params",
|
14
14
|
"enable_caching",
|
15
15
|
"disable_caching",
|
16
16
|
]
|
@@ -0,0 +1,139 @@
|
|
1
|
+
import datetime
|
2
|
+
import hashlib
|
3
|
+
import os
|
4
|
+
import pickle
|
5
|
+
import threading
|
6
|
+
from collections.abc import Mapping
|
7
|
+
from dataclasses import dataclass, replace
|
8
|
+
from typing import Any, Optional, Union
|
9
|
+
|
10
|
+
from ._types import Backend, HashFunc, Mongetter
|
11
|
+
|
12
|
+
|
13
|
+
def _default_hash_func(args, kwds):
|
14
|
+
# Sort the kwargs to ensure consistent ordering
|
15
|
+
sorted_kwargs = sorted(kwds.items())
|
16
|
+
# Serialize args and sorted_kwargs using pickle or similar
|
17
|
+
serialized = pickle.dumps((args, sorted_kwargs))
|
18
|
+
# Create a hash of the serialized data
|
19
|
+
return hashlib.sha256(serialized).hexdigest()
|
20
|
+
|
21
|
+
|
22
|
+
@dataclass
|
23
|
+
class Params:
|
24
|
+
"""Default definition for cachier parameters."""
|
25
|
+
|
26
|
+
caching_enabled: bool = True
|
27
|
+
hash_func: HashFunc = _default_hash_func
|
28
|
+
backend: Backend = "pickle"
|
29
|
+
mongetter: Optional[Mongetter] = None
|
30
|
+
stale_after: datetime.timedelta = datetime.timedelta.max
|
31
|
+
next_time: bool = False
|
32
|
+
cache_dir: Union[str, os.PathLike] = "~/.cachier/"
|
33
|
+
pickle_reload: bool = True
|
34
|
+
separate_files: bool = False
|
35
|
+
wait_for_calc_timeout: int = 0
|
36
|
+
allow_none: bool = False
|
37
|
+
|
38
|
+
|
39
|
+
_global_params = Params()
|
40
|
+
|
41
|
+
|
42
|
+
@dataclass
|
43
|
+
class CacheEntry:
|
44
|
+
"""Data class for cache entries."""
|
45
|
+
|
46
|
+
value: Any
|
47
|
+
time: datetime
|
48
|
+
stale: bool
|
49
|
+
_processing: bool
|
50
|
+
_condition: Optional[threading.Condition] = None
|
51
|
+
_completed: bool = False
|
52
|
+
|
53
|
+
|
54
|
+
def _update_with_defaults(
|
55
|
+
param, name: str, func_kwargs: Optional[dict] = None
|
56
|
+
):
|
57
|
+
import cachier
|
58
|
+
|
59
|
+
if func_kwargs:
|
60
|
+
kw_name = f"cachier__{name}"
|
61
|
+
if kw_name in func_kwargs:
|
62
|
+
return func_kwargs.pop(kw_name)
|
63
|
+
if param is None:
|
64
|
+
return getattr(cachier.config._global_params, name)
|
65
|
+
return param
|
66
|
+
|
67
|
+
|
68
|
+
def set_default_params(**params: Mapping) -> None:
|
69
|
+
"""Configure default parameters applicable to all memoized functions."""
|
70
|
+
# It is kept for backwards compatibility with desperation warning
|
71
|
+
import warnings
|
72
|
+
|
73
|
+
warnings.warn(
|
74
|
+
"Called `set_default_params` is deprecated and will be removed."
|
75
|
+
" Please use `set_global_params` instead.",
|
76
|
+
DeprecationWarning,
|
77
|
+
stacklevel=2,
|
78
|
+
)
|
79
|
+
set_global_params(**params)
|
80
|
+
|
81
|
+
|
82
|
+
def set_global_params(**params: Mapping) -> None:
|
83
|
+
"""Configure global parameters applicable to all memoized functions.
|
84
|
+
|
85
|
+
This function takes the same keyword parameters as the ones defined in the
|
86
|
+
decorator, which can be passed all at once or with multiple calls.
|
87
|
+
Parameters given directly to a decorator take precedence over any values
|
88
|
+
set by this function.
|
89
|
+
|
90
|
+
Only 'stale_after', 'next_time', and 'wait_for_calc_timeout' can be changed
|
91
|
+
after the memoization decorator has been applied. Other parameters will
|
92
|
+
only have an effect on decorators applied after this function is run.
|
93
|
+
|
94
|
+
"""
|
95
|
+
import cachier
|
96
|
+
|
97
|
+
valid_params = {
|
98
|
+
k: v
|
99
|
+
for k, v in params.items()
|
100
|
+
if hasattr(cachier.config._global_params, k)
|
101
|
+
}
|
102
|
+
cachier.config._global_params = replace(
|
103
|
+
cachier.config._global_params, **valid_params
|
104
|
+
)
|
105
|
+
|
106
|
+
|
107
|
+
def get_default_params() -> Params:
|
108
|
+
"""Get current set of default parameters."""
|
109
|
+
# It is kept for backwards compatibility with desperation warning
|
110
|
+
import warnings
|
111
|
+
|
112
|
+
warnings.warn(
|
113
|
+
"Called `get_default_params` is deprecated and will be removed."
|
114
|
+
" Please use `get_global_params` instead.",
|
115
|
+
DeprecationWarning,
|
116
|
+
stacklevel=2,
|
117
|
+
)
|
118
|
+
return get_global_params()
|
119
|
+
|
120
|
+
|
121
|
+
def get_global_params() -> Params:
|
122
|
+
"""Get current set of default parameters."""
|
123
|
+
import cachier
|
124
|
+
|
125
|
+
return cachier.config._global_params
|
126
|
+
|
127
|
+
|
128
|
+
def enable_caching():
|
129
|
+
"""Enable caching globally."""
|
130
|
+
import cachier
|
131
|
+
|
132
|
+
cachier.config._global_params.caching_enabled = True
|
133
|
+
|
134
|
+
|
135
|
+
def disable_caching():
|
136
|
+
"""Disable caching globally."""
|
137
|
+
import cachier
|
138
|
+
|
139
|
+
cachier.config._global_params.caching_enabled = False
|
@@ -14,14 +14,13 @@ import warnings
|
|
14
14
|
from collections import OrderedDict
|
15
15
|
from concurrent.futures import ThreadPoolExecutor
|
16
16
|
from functools import wraps
|
17
|
-
from typing import Optional, Union
|
17
|
+
from typing import Any, Optional, Union
|
18
18
|
from warnings import warn
|
19
19
|
|
20
20
|
from .config import (
|
21
21
|
Backend,
|
22
22
|
HashFunc,
|
23
23
|
Mongetter,
|
24
|
-
_default_params,
|
25
24
|
_update_with_defaults,
|
26
25
|
)
|
27
26
|
from .cores.base import RecalculationNeeded, _BaseCore
|
@@ -56,13 +55,11 @@ def _function_thread(core, key, func, args, kwds):
|
|
56
55
|
print(f"Function call failed with the following exception:\n{exc}")
|
57
56
|
|
58
57
|
|
59
|
-
def _calc_entry(core, key, func, args, kwds):
|
58
|
+
def _calc_entry(core, key, func, args, kwds) -> Optional[Any]:
|
59
|
+
core.mark_entry_being_calculated(key)
|
60
60
|
try:
|
61
|
-
core.mark_entry_being_calculated(key)
|
62
|
-
# _get_executor().submit(core.mark_entry_being_calculated, key)
|
63
61
|
func_res = func(*args, **kwds)
|
64
62
|
core.set_entry(key, func_res)
|
65
|
-
# _get_executor().submit(core.set_entry, key, func_res)
|
66
63
|
return func_res
|
67
64
|
finally:
|
68
65
|
core.mark_entry_not_calculated(key)
|
@@ -118,7 +115,7 @@ def cachier(
|
|
118
115
|
wait_for_calc_timeout: Optional[int] = None,
|
119
116
|
allow_none: Optional[bool] = None,
|
120
117
|
):
|
121
|
-
"""
|
118
|
+
"""Wrap as a persistent, stale-free memoization decorator.
|
122
119
|
|
123
120
|
The positional and keyword arguments to the wrapped function must be
|
124
121
|
hashable (i.e. Python's immutable built-in objects, not mutable
|
@@ -127,13 +124,14 @@ def cachier(
|
|
127
124
|
value is their id), equal objects across different sessions will not yield
|
128
125
|
identical keys.
|
129
126
|
|
130
|
-
Arguments
|
127
|
+
Arguments:
|
131
128
|
---------
|
132
129
|
hash_func : callable, optional
|
133
130
|
A callable that gets the args and kwargs from the decorated function
|
134
131
|
and returns a hash key for them. This parameter can be used to enable
|
135
132
|
the use of cachier with functions that get arguments that are not
|
136
133
|
automatically hashable by Python.
|
134
|
+
hash_params : callable, optional
|
137
135
|
backend : str, optional
|
138
136
|
The name of the backend to use. Valid options currently include
|
139
137
|
'pickle', 'mongo' and 'memory'. If not provided, defaults to
|
@@ -175,6 +173,8 @@ def cachier(
|
|
175
173
|
None will not be cached and are recalculated every call.
|
176
174
|
|
177
175
|
"""
|
176
|
+
from .config import _global_params
|
177
|
+
|
178
178
|
# Check for deprecated parameters
|
179
179
|
if hash_params is not None:
|
180
180
|
message = (
|
@@ -240,29 +240,34 @@ def cachier(
|
|
240
240
|
func, _is_method=core.func_is_method, args=args, kwds=kwds
|
241
241
|
)
|
242
242
|
|
243
|
-
_print = lambda x: None
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
243
|
+
_print = print if verbose else lambda x: None
|
244
|
+
|
245
|
+
if ignore_cache or not _global_params.caching_enabled:
|
246
|
+
return (
|
247
|
+
func(args[0], **kwargs)
|
248
|
+
if core.func_is_method
|
249
|
+
else func(**kwargs)
|
250
|
+
)
|
251
|
+
key, entry = core.get_entry((), kwargs)
|
249
252
|
if overwrite_cache:
|
250
253
|
return _calc_entry(core, key, func, args, kwds)
|
251
|
-
if entry is None
|
254
|
+
if entry is None or (
|
255
|
+
not entry._completed and not entry._processing
|
256
|
+
):
|
252
257
|
_print("No entry found. No current calc. Calling like a boss.")
|
253
258
|
return _calc_entry(core, key, func, args, kwds)
|
254
259
|
_print("Entry found.")
|
255
|
-
if _allow_none or entry.
|
260
|
+
if _allow_none or entry.value is not None:
|
256
261
|
_print("Cached result found.")
|
257
262
|
now = datetime.datetime.now()
|
258
|
-
if now - entry
|
263
|
+
if now - entry.time <= _stale_after:
|
259
264
|
_print("And it is fresh!")
|
260
|
-
return entry
|
265
|
+
return entry.value
|
261
266
|
_print("But it is stale... :(")
|
262
|
-
if entry
|
267
|
+
if entry._processing:
|
263
268
|
if _next_time:
|
264
269
|
_print("Returning stale.")
|
265
|
-
return entry
|
270
|
+
return entry.value # return stale val
|
266
271
|
_print("Already calc. Waiting on change.")
|
267
272
|
try:
|
268
273
|
return core.wait_on_entry_calc(key)
|
@@ -270,17 +275,17 @@ def cachier(
|
|
270
275
|
return _calc_entry(core, key, func, args, kwds)
|
271
276
|
if _next_time:
|
272
277
|
_print("Async calc and return stale")
|
278
|
+
core.mark_entry_being_calculated(key)
|
273
279
|
try:
|
274
|
-
core.mark_entry_being_calculated(key)
|
275
280
|
_get_executor().submit(
|
276
281
|
_function_thread, core, key, func, args, kwds
|
277
282
|
)
|
278
283
|
finally:
|
279
284
|
core.mark_entry_not_calculated(key)
|
280
|
-
return entry
|
285
|
+
return entry.value
|
281
286
|
_print("Calling decorated function and waiting")
|
282
287
|
return _calc_entry(core, key, func, args, kwds)
|
283
|
-
if entry
|
288
|
+
if entry._processing:
|
284
289
|
_print("No value but being calculated. Waiting.")
|
285
290
|
try:
|
286
291
|
return core.wait_on_entry_calc(key)
|
@@ -294,17 +299,17 @@ def cachier(
|
|
294
299
|
core.clear_cache()
|
295
300
|
|
296
301
|
def _clear_being_calculated():
|
297
|
-
"""
|
302
|
+
"""Mark all entries in this cache as not being calculated."""
|
298
303
|
core.clear_being_calculated()
|
299
304
|
|
300
305
|
def _cache_dpath():
|
301
|
-
"""
|
306
|
+
"""Return the path to the cache dir, if exists; None if not."""
|
302
307
|
return getattr(core, "cache_dir", None)
|
303
308
|
|
304
|
-
def _precache_value(*args, value_to_cache, **kwds):
|
309
|
+
def _precache_value(*args, value_to_cache, **kwds): # noqa: D417
|
305
310
|
"""Add an initial value to the cache.
|
306
311
|
|
307
|
-
Arguments
|
312
|
+
Arguments:
|
308
313
|
---------
|
309
314
|
value_to_cache : any
|
310
315
|
entry to be written into the cache
|
@@ -314,7 +319,7 @@ def cachier(
|
|
314
319
|
kwargs = _convert_args_kwargs(
|
315
320
|
func, _is_method=core.func_is_method, args=args, kwds=kwds
|
316
321
|
)
|
317
|
-
return core.precache_value(
|
322
|
+
return core.precache_value((), kwargs, value_to_cache)
|
318
323
|
|
319
324
|
func_wrapper.clear_cache = _clear_cache
|
320
325
|
func_wrapper.clear_being_calculated = _clear_being_calculated
|