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.
Files changed (33) hide show
  1. {cachier-3.0.0 → cachier-3.1.0}/MANIFEST.in +3 -3
  2. {cachier-3.0.0/cachier.egg-info → cachier-3.1.0}/PKG-INFO +33 -16
  3. {cachier-3.0.0 → cachier-3.1.0}/README.rst +2 -2
  4. cachier-3.1.0/pyproject.toml +174 -0
  5. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/__init__.py +4 -4
  6. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/__main__.py +1 -1
  7. cachier-3.1.0/src/cachier/config.py +139 -0
  8. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/core.py +33 -28
  9. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/cores/base.py +33 -24
  10. cachier-3.1.0/src/cachier/cores/memory.py +97 -0
  11. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/cores/mongo.py +28 -30
  12. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/cores/pickle.py +68 -66
  13. cachier-3.1.0/src/cachier/version.info +1 -0
  14. {cachier-3.0.0 → cachier-3.1.0/src/cachier.egg-info}/PKG-INFO +33 -16
  15. cachier-3.1.0/src/cachier.egg-info/SOURCES.txt +23 -0
  16. cachier-3.1.0/src/cachier.egg-info/entry_points.txt +2 -0
  17. {cachier-3.0.0 → cachier-3.1.0/src}/cachier.egg-info/requires.txt +1 -2
  18. cachier-3.0.0/cachier/config.py +0 -101
  19. cachier-3.0.0/cachier/cores/memory.py +0 -89
  20. cachier-3.0.0/cachier/version.info +0 -1
  21. cachier-3.0.0/cachier.egg-info/SOURCES.txt +0 -25
  22. cachier-3.0.0/cachier.egg-info/entry_points.txt +0 -2
  23. cachier-3.0.0/pyproject.toml +0 -78
  24. cachier-3.0.0/requirements.txt +0 -3
  25. cachier-3.0.0/setup.py +0 -84
  26. {cachier-3.0.0 → cachier-3.1.0}/LICENSE +0 -0
  27. {cachier-3.0.0 → cachier-3.1.0}/setup.cfg +0 -0
  28. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/_types.py +0 -0
  29. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/_version.py +0 -0
  30. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/cores/__init__.py +0 -0
  31. {cachier-3.0.0 → cachier-3.1.0/src}/cachier/py.typed +0 -0
  32. {cachier-3.0.0 → cachier-3.1.0/src}/cachier.egg-info/dependency_links.txt +0 -0
  33. {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-exclude __pycache__ *.py[cod] *.orig
5
- include cachier/version.info
4
+ recursive-include src *.info
6
5
  include README.rst
7
- include requirements.txt
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.0.0
3
+ Version: 3.1.0
4
4
  Summary: Persistent, stale-free, local and cross-machine caching for Python functions.
5
- Home-page: https://github.com/python-cachier/cachier
6
- Author: Shay Palachy & al.
7
- Author-email: shay.palachy@gmail.com
8
- License: MIT
9
- Keywords: cache,persistence,mongo,memoization,decorator
10
- Platform: linux
11
- Platform: osx
12
- Platform: windows
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
- Classifier: Topic :: Other/Nonlisted Topic
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: setuptools>=67.6.0
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
- get_default_params,
6
- set_default_params,
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
- "set_default_params",
13
- "get_default_params",
12
+ "set_global_params",
13
+ "get_global_params",
14
14
  "enable_caching",
15
15
  "disable_caching",
16
16
  ]
@@ -7,7 +7,7 @@ from cachier.core import _set_max_workers
7
7
 
8
8
  @click.group()
9
9
  def cli():
10
- """A command-line interface for cachier."""
10
+ """A command-line interface for cachier.""" # noqa: D401
11
11
 
12
12
 
13
13
  @cli.command("Limits the number of worker threads used by cachier.")
@@ -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
- """A persistent, stale-free memoization decorator.
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 # noqa: E731
244
- if verbose:
245
- _print = print
246
- if ignore_cache or not _default_params["caching_enabled"]:
247
- return func(**kwargs)
248
- key, entry = core.get_entry(tuple(), kwargs)
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.get("value", None) is not None:
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["time"] <= _stale_after:
263
+ if now - entry.time <= _stale_after:
259
264
  _print("And it is fresh!")
260
- return entry["value"]
265
+ return entry.value
261
266
  _print("But it is stale... :(")
262
- if entry["being_calculated"]:
267
+ if entry._processing:
263
268
  if _next_time:
264
269
  _print("Returning stale.")
265
- return entry["value"] # return stale val
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["value"]
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["being_calculated"]:
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
- """Marks all entries in this cache as not being calculated."""
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
- """Returns the path to the cache dir, if exists; None if not."""
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(tuple(), kwargs, value_to_cache)
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