cachier 2.3.0__py3-none-any.whl → 3.0.1__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.
cachier/__init__.py CHANGED
@@ -1,11 +1,11 @@
1
1
  from ._version import * # noqa: F403
2
- from .core import (
3
- cachier,
2
+ from .config import (
4
3
  disable_caching,
5
4
  enable_caching,
6
5
  get_default_params,
7
6
  set_default_params,
8
7
  )
8
+ from .core import cachier
9
9
 
10
10
  __all__ = [
11
11
  "cachier",
cachier/__main__.py CHANGED
@@ -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.")
cachier/_types.py ADDED
@@ -0,0 +1,9 @@
1
+ from typing import TYPE_CHECKING, Callable, Literal
2
+
3
+ if TYPE_CHECKING:
4
+ import pymongo.collection
5
+
6
+
7
+ HashFunc = Callable[..., str]
8
+ Mongetter = Callable[[], "pymongo.collection.Collection"]
9
+ Backend = Literal["pickle", "mongo", "memory"]
cachier/config.py ADDED
@@ -0,0 +1,103 @@
1
+ import datetime
2
+ import hashlib
3
+ import os
4
+ import pickle
5
+ from typing import Optional, TypedDict, Union
6
+
7
+ from ._types import Backend, HashFunc, Mongetter
8
+
9
+
10
+ def _default_hash_func(args, kwds):
11
+ # Sort the kwargs to ensure consistent ordering
12
+ sorted_kwargs = sorted(kwds.items())
13
+ # Serialize args and sorted_kwargs using pickle or similar
14
+ serialized = pickle.dumps((args, sorted_kwargs))
15
+ # Create a hash of the serialized data
16
+ return hashlib.sha256(serialized).hexdigest()
17
+
18
+
19
+ class Params(TypedDict):
20
+ """Type definition for cachier parameters."""
21
+
22
+ caching_enabled: bool
23
+ hash_func: HashFunc
24
+ backend: Backend
25
+ mongetter: Optional[Mongetter]
26
+ stale_after: datetime.timedelta
27
+ next_time: bool
28
+ cache_dir: Union[str, os.PathLike]
29
+ pickle_reload: bool
30
+ separate_files: bool
31
+ wait_for_calc_timeout: int
32
+ allow_none: bool
33
+
34
+
35
+ _default_params: Params = {
36
+ "caching_enabled": True,
37
+ "hash_func": _default_hash_func,
38
+ "backend": "pickle",
39
+ "mongetter": None,
40
+ "stale_after": datetime.timedelta.max,
41
+ "next_time": False,
42
+ "cache_dir": "~/.cachier/",
43
+ "pickle_reload": True,
44
+ "separate_files": False,
45
+ "wait_for_calc_timeout": 0,
46
+ "allow_none": False,
47
+ }
48
+
49
+
50
+ def _update_with_defaults(
51
+ param, name: str, func_kwargs: Optional[dict] = None
52
+ ):
53
+ import cachier
54
+
55
+ if func_kwargs:
56
+ kw_name = f"cachier__{name}"
57
+ if kw_name in func_kwargs:
58
+ return func_kwargs.pop(kw_name)
59
+ if param is None:
60
+ return cachier.config._default_params[name]
61
+ return param
62
+
63
+
64
+ def set_default_params(**params):
65
+ """Configure global parameters applicable to all memoized functions.
66
+
67
+ This function takes the same keyword parameters as the ones defined in the
68
+ decorator, which can be passed all at once or with multiple calls.
69
+ Parameters given directly to a decorator take precedence over any values
70
+ set by this function.
71
+
72
+ Only 'stale_after', 'next_time', and 'wait_for_calc_timeout' can be changed
73
+ after the memoization decorator has been applied. Other parameters will
74
+ only have an effect on decorators applied after this function is run.
75
+
76
+ """
77
+ import cachier
78
+
79
+ valid_params = (
80
+ p for p in params.items() if p[0] in cachier.config._default_params
81
+ )
82
+ _default_params.update(valid_params)
83
+
84
+
85
+ def get_default_params():
86
+ """Get current set of default parameters."""
87
+ import cachier
88
+
89
+ return cachier.config._default_params
90
+
91
+
92
+ def enable_caching():
93
+ """Enable caching globally."""
94
+ import cachier
95
+
96
+ cachier.config._default_params["caching_enabled"] = True
97
+
98
+
99
+ def disable_caching():
100
+ """Disable caching globally."""
101
+ import cachier
102
+
103
+ cachier.config._default_params["caching_enabled"] = False
cachier/core.py CHANGED
@@ -7,28 +7,28 @@
7
7
  # http://www.opensource.org/licenses/MIT-license
8
8
  # Copyright (c) 2016, Shay Palachy <shaypal5@gmail.com>
9
9
 
10
- # python 2 compatibility
11
-
12
10
  import datetime
13
- import hashlib
14
11
  import inspect
15
12
  import os
16
- import pickle
13
+ import warnings
17
14
  from collections import OrderedDict
18
15
  from concurrent.futures import ThreadPoolExecutor
19
16
  from functools import wraps
20
- from typing import TYPE_CHECKING, Callable, Literal, Optional, TypedDict, Union
17
+ from typing import Optional, Union
21
18
  from warnings import warn
22
19
 
20
+ from .config import (
21
+ Backend,
22
+ HashFunc,
23
+ Mongetter,
24
+ _default_params,
25
+ _update_with_defaults,
26
+ )
23
27
  from .cores.base import RecalculationNeeded, _BaseCore
24
28
  from .cores.memory import _MemoryCore
25
29
  from .cores.mongo import _MongoCore
26
30
  from .cores.pickle import _PickleCore
27
31
 
28
- if TYPE_CHECKING:
29
- import pymongo.collection
30
-
31
-
32
32
  MAX_WORKERS_ENVAR_NAME = "CACHIER_MAX_WORKERS"
33
33
  DEFAULT_MAX_WORKERS = 8
34
34
 
@@ -68,15 +68,6 @@ def _calc_entry(core, key, func, args, kwds):
68
68
  core.mark_entry_not_calculated(key)
69
69
 
70
70
 
71
- def _default_hash_func(args, kwds):
72
- # Sort the kwargs to ensure consistent ordering
73
- sorted_kwargs = sorted(kwds.items())
74
- # Serialize args and sorted_kwargs using pickle or similar
75
- serialized = pickle.dumps((args, sorted_kwargs))
76
- # Create a hash of the serialized data
77
- return hashlib.sha256(serialized).hexdigest()
78
-
79
-
80
71
  def _convert_args_kwargs(
81
72
  func, _is_method: bool, args: tuple, kwds: dict
82
73
  ) -> dict:
@@ -84,7 +75,7 @@ def _convert_args_kwargs(
84
75
  # unwrap if the function is functools.partial
85
76
  if hasattr(func, "func"):
86
77
  args = func.args + args
87
- kwds = dict(**func.keywords, **kwds)
78
+ kwds.update({k: v for k, v in func.keywords.items() if k not in kwds})
88
79
  func = func.func
89
80
  func_params = list(inspect.signature(func).parameters)
90
81
  args_as_kw = dict(
@@ -103,42 +94,15 @@ def _convert_args_kwargs(
103
94
  return OrderedDict(sorted(kwargs.items()))
104
95
 
105
96
 
106
- class MissingMongetter(ValueError):
107
- """Thrown when the mongetter keyword argument is missing."""
108
-
109
-
110
- HashFunc = Callable[..., str]
111
- Mongetter = Callable[[], "pymongo.collection.Collection"]
112
- Backend = Literal["pickle", "mongo", "memory"]
113
-
114
-
115
- class Params(TypedDict):
116
- caching_enabled: bool
117
- hash_func: HashFunc
118
- backend: Backend
119
- mongetter: Optional[Mongetter]
120
- stale_after: datetime.timedelta
121
- next_time: bool
122
- cache_dir: Union[str, os.PathLike]
123
- pickle_reload: bool
124
- separate_files: bool
125
- wait_for_calc_timeout: int
126
- allow_none: bool
127
-
128
-
129
- _default_params: Params = {
130
- "caching_enabled": True,
131
- "hash_func": _default_hash_func,
132
- "backend": "pickle",
133
- "mongetter": None,
134
- "stale_after": datetime.timedelta.max,
135
- "next_time": False,
136
- "cache_dir": "~/.cachier/",
137
- "pickle_reload": True,
138
- "separate_files": False,
139
- "wait_for_calc_timeout": 0,
140
- "allow_none": False,
141
- }
97
+ def _pop_kwds_with_deprecation(kwds, name: str, default_value: bool):
98
+ if name in kwds:
99
+ warnings.warn(
100
+ f"`{name}` is deprecated and will be removed in a future release,"
101
+ " use `cachier__` alternative instead.",
102
+ DeprecationWarning,
103
+ stacklevel=2,
104
+ )
105
+ return kwds.pop(name, default_value)
142
106
 
143
107
 
144
108
  def cachier(
@@ -154,7 +118,7 @@ def cachier(
154
118
  wait_for_calc_timeout: Optional[int] = None,
155
119
  allow_none: Optional[bool] = None,
156
120
  ):
157
- """A persistent, stale-free memoization decorator.
121
+ """Wrap as a persistent, stale-free memoization decorator.
158
122
 
159
123
  The positional and keyword arguments to the wrapped function must be
160
124
  hashable (i.e. Python's immutable built-in objects, not mutable
@@ -163,13 +127,14 @@ def cachier(
163
127
  value is their id), equal objects across different sessions will not yield
164
128
  identical keys.
165
129
 
166
- Arguments
130
+ Arguments:
167
131
  ---------
168
132
  hash_func : callable, optional
169
133
  A callable that gets the args and kwargs from the decorated function
170
134
  and returns a hash key for them. This parameter can be used to enable
171
135
  the use of cachier with functions that get arguments that are not
172
136
  automatically hashable by Python.
137
+ hash_params : callable, optional
173
138
  backend : str, optional
174
139
  The name of the backend to use. Valid options currently include
175
140
  'pickle', 'mongo' and 'memory'. If not provided, defaults to
@@ -219,13 +184,12 @@ def cachier(
219
184
  )
220
185
  warn(message, DeprecationWarning, stacklevel=2)
221
186
  hash_func = hash_params
187
+ # Update parameters with defaults if input is None
188
+ backend = _update_with_defaults(backend, "backend")
189
+ mongetter = _update_with_defaults(mongetter, "mongetter")
222
190
  # Override the backend parameter if a mongetter is provided.
223
- if mongetter is None:
224
- mongetter = _default_params["mongetter"]
225
191
  if callable(mongetter):
226
192
  backend = "mongo"
227
- if backend is None:
228
- backend = _default_params["backend"]
229
193
  core: _BaseCore
230
194
  if backend == "pickle":
231
195
  core = _PickleCore(
@@ -234,23 +198,16 @@ def cachier(
234
198
  cache_dir=cache_dir,
235
199
  separate_files=separate_files,
236
200
  wait_for_calc_timeout=wait_for_calc_timeout,
237
- default_params=_default_params,
238
201
  )
239
202
  elif backend == "mongo":
240
- if mongetter is None:
241
- raise MissingMongetter(
242
- "must specify ``mongetter`` when using the mongo core"
243
- )
244
203
  core = _MongoCore(
245
- mongetter=mongetter,
246
204
  hash_func=hash_func,
205
+ mongetter=mongetter,
247
206
  wait_for_calc_timeout=wait_for_calc_timeout,
248
- default_params=_default_params,
249
207
  )
250
208
  elif backend == "memory":
251
209
  core = _MemoryCore(
252
- hash_func=hash_func,
253
- default_params=_default_params,
210
+ hash_func=hash_func, wait_for_calc_timeout=wait_for_calc_timeout
254
211
  )
255
212
  else:
256
213
  raise ValueError("specified an invalid core: %s" % backend)
@@ -261,26 +218,39 @@ def cachier(
261
218
  @wraps(func)
262
219
  def func_wrapper(*args, **kwds):
263
220
  nonlocal allow_none
264
- _allow_none = (
265
- allow_none
266
- if allow_none is not None
267
- else _default_params["allow_none"]
268
- )
221
+ _allow_none = _update_with_defaults(allow_none, "allow_none", kwds)
269
222
  # print('Inside general wrapper for {}.'.format(func.__name__))
270
- ignore_cache = kwds.pop("ignore_cache", False)
271
- overwrite_cache = kwds.pop("overwrite_cache", False)
272
- verbose_cache = kwds.pop("verbose_cache", False)
223
+ ignore_cache = _pop_kwds_with_deprecation(
224
+ kwds, "ignore_cache", False
225
+ )
226
+ overwrite_cache = _pop_kwds_with_deprecation(
227
+ kwds, "overwrite_cache", False
228
+ )
229
+ verbose = _pop_kwds_with_deprecation(kwds, "verbose_cache", False)
230
+ ignore_cache = kwds.pop("cachier__skip_cache", ignore_cache)
231
+ overwrite_cache = kwds.pop(
232
+ "cachier__overwrite_cache", overwrite_cache
233
+ )
234
+ verbose = kwds.pop("cachier__verbose", verbose)
235
+ _stale_after = _update_with_defaults(
236
+ stale_after, "stale_after", kwds
237
+ )
238
+ _next_time = _update_with_defaults(next_time, "next_time", kwds)
273
239
  # merge args expanded as kwargs and the original kwds
274
240
  kwargs = _convert_args_kwargs(
275
241
  func, _is_method=core.func_is_method, args=args, kwds=kwds
276
242
  )
277
243
 
278
244
  _print = lambda x: None # noqa: E731
279
- if verbose_cache:
245
+ if verbose:
280
246
  _print = print
281
247
  if ignore_cache or not _default_params["caching_enabled"]:
282
- return func(**kwargs)
283
- key, entry = core.get_entry(tuple(), kwargs)
248
+ return (
249
+ func(args[0], **kwargs)
250
+ if core.func_is_method
251
+ else func(**kwargs)
252
+ )
253
+ key, entry = core.get_entry((), kwargs)
284
254
  if overwrite_cache:
285
255
  return _calc_entry(core, key, func, args, kwds)
286
256
  if entry is None:
@@ -289,17 +259,13 @@ def cachier(
289
259
  _print("Entry found.")
290
260
  if _allow_none or entry.get("value", None) is not None:
291
261
  _print("Cached result found.")
292
- local_stale_after = (
293
- stale_after or _default_params["stale_after"]
294
- )
295
- local_next_time = next_time or _default_params["next_time"] # noqa: E501
296
262
  now = datetime.datetime.now()
297
- if now - entry["time"] <= local_stale_after:
263
+ if now - entry["time"] <= _stale_after:
298
264
  _print("And it is fresh!")
299
265
  return entry["value"]
300
266
  _print("But it is stale... :(")
301
267
  if entry["being_calculated"]:
302
- if local_next_time:
268
+ if _next_time:
303
269
  _print("Returning stale.")
304
270
  return entry["value"] # return stale val
305
271
  _print("Already calc. Waiting on change.")
@@ -307,7 +273,7 @@ def cachier(
307
273
  return core.wait_on_entry_calc(key)
308
274
  except RecalculationNeeded:
309
275
  return _calc_entry(core, key, func, args, kwds)
310
- if local_next_time:
276
+ if _next_time:
311
277
  _print("Async calc and return stale")
312
278
  try:
313
279
  core.mark_entry_being_calculated(key)
@@ -328,22 +294,22 @@ def cachier(
328
294
  _print("No entry found. No current calc. Calling like a boss.")
329
295
  return _calc_entry(core, key, func, args, kwds)
330
296
 
331
- def clear_cache():
297
+ def _clear_cache():
332
298
  """Clear the cache."""
333
299
  core.clear_cache()
334
300
 
335
- def clear_being_calculated():
336
- """Marks all entries in this cache as not being calculated."""
301
+ def _clear_being_calculated():
302
+ """Mark all entries in this cache as not being calculated."""
337
303
  core.clear_being_calculated()
338
304
 
339
- def cache_dpath():
340
- """Returns the path to the cache dir, if exists; None if not."""
305
+ def _cache_dpath():
306
+ """Return the path to the cache dir, if exists; None if not."""
341
307
  return getattr(core, "cache_dir", None)
342
308
 
343
- def precache_value(*args, value_to_cache, **kwds):
309
+ def _precache_value(*args, value_to_cache, **kwds): # noqa: D417
344
310
  """Add an initial value to the cache.
345
311
 
346
- Arguments
312
+ Arguments:
347
313
  ---------
348
314
  value_to_cache : any
349
315
  entry to be written into the cache
@@ -353,44 +319,12 @@ def cachier(
353
319
  kwargs = _convert_args_kwargs(
354
320
  func, _is_method=core.func_is_method, args=args, kwds=kwds
355
321
  )
356
- return core.precache_value(tuple(), kwargs, value_to_cache)
322
+ return core.precache_value((), kwargs, value_to_cache)
357
323
 
358
- func_wrapper.clear_cache = clear_cache
359
- func_wrapper.clear_being_calculated = clear_being_calculated
360
- func_wrapper.cache_dpath = cache_dpath
361
- func_wrapper.precache_value = precache_value
324
+ func_wrapper.clear_cache = _clear_cache
325
+ func_wrapper.clear_being_calculated = _clear_being_calculated
326
+ func_wrapper.cache_dpath = _cache_dpath
327
+ func_wrapper.precache_value = _precache_value
362
328
  return func_wrapper
363
329
 
364
330
  return _cachier_decorator
365
-
366
-
367
- def set_default_params(**params):
368
- """Configure global parameters applicable to all memoized functions.
369
-
370
- This function takes the same keyword parameters as the ones defined in the
371
- decorator, which can be passed all at once or with multiple calls.
372
- Parameters given directly to a decorator take precedence over any values
373
- set by this function.
374
-
375
- Only 'stale_after', 'next_time', and 'wait_for_calc_timeout' can be changed
376
- after the memoization decorator has been applied. Other parameters will
377
- only have an effect on decorators applied after this function is run.
378
-
379
- """
380
- valid_params = (p for p in params.items() if p[0] in _default_params)
381
- _default_params.update(valid_params)
382
-
383
-
384
- def get_default_params():
385
- """Get current set of default parameters."""
386
- return _default_params
387
-
388
-
389
- def enable_caching():
390
- """Enable caching globally."""
391
- _default_params["caching_enabled"] = True
392
-
393
-
394
- def disable_caching():
395
- """Disable caching globally."""
396
- _default_params["caching_enabled"] = False
cachier/cores/base.py CHANGED
@@ -8,21 +8,33 @@
8
8
 
9
9
  import abc # for the _BaseCore abstract base class
10
10
  import inspect
11
+ import threading
12
+ from typing import Callable
13
+
14
+ from .._types import HashFunc
15
+ from ..config import _update_with_defaults
11
16
 
12
17
 
13
18
  class RecalculationNeeded(Exception):
19
+ """Exception raised when a recalculation is needed."""
20
+
14
21
  pass
15
22
 
16
23
 
24
+ def _get_func_str(func: Callable) -> str:
25
+ return f".{func.__module__}.{func.__name__}"
26
+
27
+
17
28
  class _BaseCore:
18
29
  __metaclass__ = abc.ABCMeta
19
30
 
20
- def __init__(self, hash_func, default_params):
21
- self.default_params = default_params
22
- self.hash_func = hash_func
31
+ def __init__(self, hash_func: HashFunc, wait_for_calc_timeout: int):
32
+ self.hash_func = _update_with_defaults(hash_func, "hash_func")
33
+ self.wait_for_calc_timeout = wait_for_calc_timeout
34
+ self.lock = threading.RLock()
23
35
 
24
36
  def set_func(self, func):
25
- """Sets the function this core will use.
37
+ """Set the function this core will use.
26
38
 
27
39
  This has to be set before any method is called. Also determine if the
28
40
  function is an object method.
@@ -36,59 +48,62 @@ class _BaseCore:
36
48
  self.func = func
37
49
 
38
50
  def get_key(self, args, kwds):
39
- """Returns a unique key based on the arguments provided."""
40
- if self.hash_func is not None:
41
- return self.hash_func(args, kwds)
42
- else:
43
- return self.default_params["hash_func"](args, kwds)
51
+ """Return a unique key based on the arguments provided."""
52
+ return self.hash_func(args, kwds)
44
53
 
45
54
  def get_entry(self, args, kwds):
46
- """Returns the result mapped to the given arguments in this core's
47
- cache, if such a mapping exists."""
55
+ """Get entry based on given arguments.
56
+
57
+ Return the result mapped to the given arguments in this core's cache,
58
+ if such a mapping exists.
59
+
60
+ """
48
61
  key = self.get_key(args, kwds)
49
62
  return self.get_entry_by_key(key)
50
63
 
51
64
  def precache_value(self, args, kwds, value_to_cache):
52
- """Writes a precomputed value into the cache."""
65
+ """Write a precomputed value into the cache."""
53
66
  key = self.get_key(args, kwds)
54
67
  self.set_entry(key, value_to_cache)
55
68
  return value_to_cache
56
69
 
57
70
  def check_calc_timeout(self, time_spent):
58
71
  """Raise an exception if a recalculation is needed."""
59
- if self.wait_for_calc_timeout is not None:
60
- calc_timeout = self.wait_for_calc_timeout
61
- else:
62
- calc_timeout = self.default_params["wait_for_calc_timeout"]
72
+ calc_timeout = _update_with_defaults(
73
+ self.wait_for_calc_timeout, "wait_for_calc_timeout"
74
+ )
63
75
  if calc_timeout > 0 and (time_spent >= calc_timeout):
64
76
  raise RecalculationNeeded()
65
77
 
66
78
  @abc.abstractmethod
67
79
  def get_entry_by_key(self, key):
68
- """Returns the result mapped to the given key in this core's cache, if
69
- such a mapping exists."""
80
+ """Get entry based on given key.
81
+
82
+ Return the result mapped to the given key in this core's cache, if such
83
+ a mapping exists.
84
+
85
+ """
70
86
 
71
87
  @abc.abstractmethod
72
88
  def set_entry(self, key, func_res):
73
- """Maps the given result to the given key in this core's cache."""
89
+ """Map the given result to the given key in this core's cache."""
74
90
 
75
91
  @abc.abstractmethod
76
92
  def mark_entry_being_calculated(self, key):
77
- """Marks the entry mapped by the given key as being calculated."""
93
+ """Mark the entry mapped by the given key as being calculated."""
78
94
 
79
95
  @abc.abstractmethod
80
96
  def mark_entry_not_calculated(self, key):
81
- """Marks the entry mapped by the given key as not being calculated."""
97
+ """Mark the entry mapped by the given key as not being calculated."""
82
98
 
83
99
  @abc.abstractmethod
84
100
  def wait_on_entry_calc(self, key):
85
- """Waits on the entry mapped by key being calculated and returns
86
- result."""
101
+ """Wait on the entry with keys being calculated and returns result."""
87
102
 
88
103
  @abc.abstractmethod
89
104
  def clear_cache(self):
90
- """Clears the cache of this core."""
105
+ """Clear the cache of this core."""
91
106
 
92
107
  @abc.abstractmethod
93
108
  def clear_being_calculated(self):
94
- """Marks all entries in this cache as not being calculated."""
109
+ """Mark all entries in this cache as not being calculated."""
cachier/cores/memory.py CHANGED
@@ -3,20 +3,23 @@
3
3
  import threading
4
4
  from datetime import datetime
5
5
 
6
- from .base import _BaseCore
6
+ from .._types import HashFunc
7
+ from .base import _BaseCore, _get_func_str
7
8
 
8
9
 
9
10
  class _MemoryCore(_BaseCore):
10
11
  """The memory core class for cachier."""
11
12
 
12
- def __init__(self, hash_func, default_params):
13
- super().__init__(hash_func, default_params)
13
+ def __init__(self, hash_func: HashFunc, wait_for_calc_timeout: int):
14
+ super().__init__(hash_func, wait_for_calc_timeout)
14
15
  self.cache = {}
15
- self.lock = threading.RLock()
16
+
17
+ def _hash_func_key(self, key):
18
+ return f"{_get_func_str(self.func)}:{key}"
16
19
 
17
20
  def get_entry_by_key(self, key, reload=False):
18
21
  with self.lock:
19
- return key, self.cache.get(key, None)
22
+ return key, self.cache.get(self._hash_func_key(key), None)
20
23
 
21
24
  def set_entry(self, key, func_res):
22
25
  with self.lock:
@@ -24,10 +27,10 @@ class _MemoryCore(_BaseCore):
24
27
  # we need to retain the existing condition so that
25
28
  # mark_entry_not_calculated can notify all possibly-waiting
26
29
  # threads about it
27
- cond = self.cache[key]["condition"]
30
+ cond = self.cache[self._hash_func_key(key)]["condition"]
28
31
  except KeyError: # pragma: no cover
29
32
  cond = None
30
- self.cache[key] = {
33
+ self.cache[self._hash_func_key(key)] = {
31
34
  "value": func_res,
32
35
  "time": datetime.now(),
33
36
  "stale": False,
@@ -40,10 +43,10 @@ class _MemoryCore(_BaseCore):
40
43
  condition = threading.Condition()
41
44
  # condition.acquire()
42
45
  try:
43
- self.cache[key]["being_calculated"] = True
44
- self.cache[key]["condition"] = condition
46
+ self.cache[self._hash_func_key(key)]["being_calculated"] = True
47
+ self.cache[self._hash_func_key(key)]["condition"] = condition
45
48
  except KeyError:
46
- self.cache[key] = {
49
+ self.cache[self._hash_func_key(key)] = {
47
50
  "value": None,
48
51
  "time": datetime.now(),
49
52
  "stale": False,
@@ -54,7 +57,7 @@ class _MemoryCore(_BaseCore):
54
57
  def mark_entry_not_calculated(self, key):
55
58
  with self.lock:
56
59
  try:
57
- entry = self.cache[key]
60
+ entry = self.cache[self._hash_func_key(key)]
58
61
  except KeyError: # pragma: no cover
59
62
  return # that's ok, we don't need an entry in that case
60
63
  entry["being_calculated"] = False
@@ -67,13 +70,13 @@ class _MemoryCore(_BaseCore):
67
70
 
68
71
  def wait_on_entry_calc(self, key):
69
72
  with self.lock: # pragma: no cover
70
- entry = self.cache[key]
73
+ entry = self.cache[self._hash_func_key(key)]
71
74
  if not entry["being_calculated"]:
72
75
  return entry["value"]
73
76
  entry["condition"].acquire()
74
77
  entry["condition"].wait()
75
78
  entry["condition"].release()
76
- return self.cache[key]["value"]
79
+ return self.cache[self._hash_func_key(key)]["value"]
77
80
 
78
81
  def clear_cache(self):
79
82
  with self.lock:
cachier/cores/mongo.py CHANGED
@@ -14,31 +14,45 @@ import warnings # to warn if pymongo is missing
14
14
  from contextlib import suppress
15
15
  from datetime import datetime
16
16
 
17
+ from .._types import HashFunc, Mongetter
18
+
17
19
  with suppress(ImportError):
18
20
  from bson.binary import Binary # to save binary data to mongodb
19
21
  from pymongo import ASCENDING, IndexModel
20
22
  from pymongo.errors import OperationFailure
21
23
 
22
- from .base import RecalculationNeeded, _BaseCore
24
+ from .base import RecalculationNeeded, _BaseCore, _get_func_str
23
25
 
24
26
  MONGO_SLEEP_DURATION_IN_SEC = 1
25
27
 
26
28
 
29
+ class MissingMongetter(ValueError):
30
+ """Thrown when the mongetter keyword argument is missing."""
31
+
32
+
27
33
  class _MongoCore(_BaseCore):
28
34
  _INDEX_NAME = "func_1_key_1"
29
35
 
30
36
  def __init__(
31
- self, mongetter, hash_func, wait_for_calc_timeout, default_params
37
+ self,
38
+ hash_func: HashFunc,
39
+ mongetter: Mongetter,
40
+ wait_for_calc_timeout: int,
32
41
  ):
33
42
  if "pymongo" not in sys.modules:
34
43
  warnings.warn(
35
- "Cachier warning: pymongo was not found. "
36
- "MongoDB cores will not function."
44
+ "`pymongo` was not found. MongoDB cores will not function.",
45
+ ImportWarning,
46
+ stacklevel=2,
37
47
  ) # pragma: no cover
38
- super().__init__(hash_func, default_params)
48
+
49
+ super().__init__(hash_func, wait_for_calc_timeout)
50
+ if mongetter is None:
51
+ raise MissingMongetter(
52
+ "must specify ``mongetter`` when using the mongo core"
53
+ )
39
54
  self.mongetter = mongetter
40
55
  self.mongo_collection = self.mongetter()
41
- self.wait_for_calc_timeout = wait_for_calc_timeout
42
56
  index_inf = self.mongo_collection.index_information()
43
57
  if _MongoCore._INDEX_NAME not in index_inf:
44
58
  func1key1 = IndexModel(
@@ -47,39 +61,39 @@ class _MongoCore(_BaseCore):
47
61
  )
48
62
  self.mongo_collection.create_indexes([func1key1])
49
63
 
50
- @staticmethod
51
- def _get_func_str(func):
52
- return f".{func.__module__}.{func.__name__}"
64
+ @property
65
+ def _func_str(self) -> str:
66
+ return _get_func_str(self.func)
53
67
 
54
68
  def get_entry_by_key(self, key):
55
69
  res = self.mongo_collection.find_one(
56
- {"func": _MongoCore._get_func_str(self.func), "key": key}
70
+ {"func": self._func_str, "key": key}
57
71
  )
58
- if res:
59
- try:
60
- entry = {
61
- "value": pickle.loads(res["value"]), # noqa: S301
62
- "time": res.get("time", None),
63
- "stale": res.get("stale", False),
64
- "being_calculated": res.get("being_calculated", False),
65
- }
66
- except KeyError:
67
- entry = {
68
- "value": None,
69
- "time": res.get("time", None),
70
- "stale": res.get("stale", False),
71
- "being_calculated": res.get("being_calculated", False),
72
- }
73
- return key, entry
74
- return key, None
72
+ if not res:
73
+ return key, None
74
+ try:
75
+ entry = {
76
+ "value": pickle.loads(res["value"]), # noqa: S301
77
+ "time": res.get("time", None),
78
+ "stale": res.get("stale", False),
79
+ "being_calculated": res.get("being_calculated", False),
80
+ }
81
+ except KeyError:
82
+ entry = {
83
+ "value": None,
84
+ "time": res.get("time", None),
85
+ "stale": res.get("stale", False),
86
+ "being_calculated": res.get("being_calculated", False),
87
+ }
88
+ return key, entry
75
89
 
76
90
  def set_entry(self, key, func_res):
77
91
  thebytes = pickle.dumps(func_res)
78
92
  self.mongo_collection.update_one(
79
- filter={"func": _MongoCore._get_func_str(self.func), "key": key},
93
+ filter={"func": self._func_str, "key": key},
80
94
  update={
81
95
  "$set": {
82
- "func": _MongoCore._get_func_str(self.func),
96
+ "func": self._func_str,
83
97
  "key": key,
84
98
  "value": Binary(thebytes),
85
99
  "time": datetime.now(),
@@ -92,7 +106,7 @@ class _MongoCore(_BaseCore):
92
106
 
93
107
  def mark_entry_being_calculated(self, key):
94
108
  self.mongo_collection.update_one(
95
- filter={"func": _MongoCore._get_func_str(self.func), "key": key},
109
+ filter={"func": self._func_str, "key": key},
96
110
  update={"$set": {"being_calculated": True}},
97
111
  upsert=True,
98
112
  )
@@ -101,7 +115,7 @@ class _MongoCore(_BaseCore):
101
115
  with suppress(OperationFailure): # don't care in this case
102
116
  self.mongo_collection.update_one(
103
117
  filter={
104
- "func": _MongoCore._get_func_str(self.func),
118
+ "func": self._func_str,
105
119
  "key": key,
106
120
  },
107
121
  update={"$set": {"being_calculated": False}},
@@ -121,14 +135,12 @@ class _MongoCore(_BaseCore):
121
135
  self.check_calc_timeout(time_spent)
122
136
 
123
137
  def clear_cache(self):
124
- self.mongo_collection.delete_many(
125
- filter={"func": _MongoCore._get_func_str(self.func)}
126
- )
138
+ self.mongo_collection.delete_many(filter={"func": self._func_str})
127
139
 
128
140
  def clear_being_calculated(self):
129
141
  self.mongo_collection.update_many(
130
142
  filter={
131
- "func": _MongoCore._get_func_str(self.func),
143
+ "func": self._func_str,
132
144
  "being_calculated": True,
133
145
  },
134
146
  update={"$set": {"being_calculated": False}},
cachier/cores/pickle.py CHANGED
@@ -8,7 +8,6 @@
8
8
  # Copyright (c) 2016, Shay Palachy <shaypal5@gmail.com>
9
9
  import os
10
10
  import pickle # for local caching
11
- import threading
12
11
  from contextlib import suppress
13
12
  from datetime import datetime
14
13
 
@@ -16,21 +15,15 @@ import portalocker # to lock on pickle cache IO
16
15
  from watchdog.events import PatternMatchingEventHandler
17
16
  from watchdog.observers import Observer
18
17
 
18
+ from .._types import HashFunc
19
+ from ..config import _update_with_defaults
20
+
19
21
  # Alternative: https://github.com/WoLpH/portalocker
20
22
  from .base import _BaseCore
21
23
 
22
24
 
23
25
  class _PickleCore(_BaseCore):
24
- """The pickle core class for cachier.
25
-
26
- Parameters
27
- ----------
28
- pickle_reload : bool, optional
29
- See core.cachier() documentation.
30
- cache_dir : str, optional.
31
- See core.cachier() documentation.
32
-
33
- """
26
+ """The pickle core class for cachier."""
34
27
 
35
28
  class CacheChangeHandler(PatternMatchingEventHandler):
36
29
  """Handles cache-file modification events."""
@@ -69,75 +62,51 @@ class _PickleCore(_BaseCore):
69
62
  self.observer.stop()
70
63
 
71
64
  def on_created(self, event):
72
- """A Watchdog Event Handler method."""
65
+ """A Watchdog Event Handler method.""" # noqa: D401
73
66
  self._check_calculation() # pragma: no cover
74
67
 
75
68
  def on_modified(self, event):
76
- """A Watchdog Event Handler method."""
69
+ """A Watchdog Event Handler method.""" # noqa: D401
77
70
  self._check_calculation()
78
71
 
79
72
  def __init__(
80
73
  self,
81
- hash_func,
82
- pickle_reload,
83
- cache_dir,
84
- separate_files,
85
- wait_for_calc_timeout,
86
- default_params,
74
+ hash_func: HashFunc,
75
+ pickle_reload: bool,
76
+ cache_dir: str,
77
+ separate_files: bool,
78
+ wait_for_calc_timeout: int,
87
79
  ):
88
- super().__init__(hash_func, default_params)
80
+ super().__init__(hash_func, wait_for_calc_timeout)
89
81
  self.cache = None
90
- if pickle_reload is not None:
91
- self.reload = pickle_reload
92
- else:
93
- self.reload = self.default_params["pickle_reload"]
94
- if cache_dir is not None:
95
- self.cache_dir = os.path.expanduser(cache_dir)
96
- else:
97
- self.cache_dir = os.path.expanduser(
98
- self.default_params["cache_dir"]
99
- )
100
- if separate_files is not None:
101
- self.separate_files = separate_files
102
- else:
103
- self.separate_files = self.default_params["separate_files"]
104
- self.wait_for_calc_timeout = wait_for_calc_timeout
105
- self.cache_fname = None
106
- self.cache_fpath = None
107
- self.lock = threading.RLock()
108
-
109
- def _cache_fname(self):
110
- if self.cache_fname is None:
111
- self.cache_fname = (
112
- f".{self.func.__module__}.{self.func.__qualname__}"
113
- )
114
- self.cache_fname = self.cache_fname.replace("<", "_").replace(
115
- ">", "_"
116
- )
117
- return self.cache_fname
118
-
119
- def _cache_fpath(self):
120
- if self.cache_fpath is None:
121
- if not os.path.exists(self.cache_dir):
122
- os.makedirs(self.cache_dir)
123
- self.cache_fpath = os.path.abspath(
124
- os.path.join(
125
- os.path.realpath(self.cache_dir),
126
- self._cache_fname(),
127
- )
128
- )
129
- return self.cache_fpath
82
+ self.reload = _update_with_defaults(pickle_reload, "pickle_reload")
83
+ self.cache_dir = os.path.expanduser(
84
+ _update_with_defaults(cache_dir, "cache_dir")
85
+ )
86
+ self.separate_files = _update_with_defaults(
87
+ separate_files, "separate_files"
88
+ )
89
+
90
+ @property
91
+ def cache_fname(self) -> str:
92
+ fname = f".{self.func.__module__}.{self.func.__qualname__}"
93
+ return fname.replace("<", "_").replace(">", "_")
94
+
95
+ @property
96
+ def cache_fpath(self) -> str:
97
+ os.makedirs(self.cache_dir, exist_ok=True)
98
+ return os.path.abspath(
99
+ os.path.join(os.path.realpath(self.cache_dir), self.cache_fname)
100
+ )
130
101
 
131
102
  def _reload_cache(self):
132
103
  with self.lock:
133
- fpath = self._cache_fpath()
134
104
  try:
135
- with portalocker.Lock(fpath, mode="rb") as cache_file:
136
- try:
137
- self.cache = pickle.load(cache_file) # noqa: S301
138
- except EOFError:
139
- self.cache = {}
140
- except FileNotFoundError:
105
+ with portalocker.Lock(
106
+ self.cache_fpath, mode="rb"
107
+ ) as cache_file:
108
+ self.cache = pickle.load(cache_file) # noqa: S301
109
+ except (FileNotFoundError, EOFError):
141
110
  self.cache = {}
142
111
 
143
112
  def _get_cache(self):
@@ -146,9 +115,9 @@ class _PickleCore(_BaseCore):
146
115
  self._reload_cache()
147
116
  return self.cache
148
117
 
149
- def _get_cache_by_key(self, key=None, hash=None):
150
- fpath = self._cache_fpath()
151
- fpath += f"_{key}" if hash is None else f"_{hash}"
118
+ def _get_cache_by_key(self, key=None, hash_str=None):
119
+ fpath = self.cache_fpath
120
+ fpath += f"_{hash_str or key}"
152
121
  try:
153
122
  with portalocker.Lock(fpath, mode="rb") as cache_file:
154
123
  return pickle.load(cache_file) # noqa: S301
@@ -156,30 +125,28 @@ class _PickleCore(_BaseCore):
156
125
  return None
157
126
 
158
127
  def _clear_all_cache_files(self):
159
- fpath = self._cache_fpath()
160
- path, name = os.path.split(fpath)
128
+ path, name = os.path.split(self.cache_fpath)
161
129
  for subpath in os.listdir(path):
162
130
  if subpath.startswith(f"{name}_"):
163
131
  os.remove(os.path.join(path, subpath))
164
132
 
165
133
  def _clear_being_calculated_all_cache_files(self):
166
- fpath = self._cache_fpath()
167
- path, name = os.path.split(fpath)
134
+ path, name = os.path.split(self.cache_fpath)
168
135
  for subpath in os.listdir(path):
169
136
  if subpath.startswith(name):
170
- entry = self._get_cache_by_key(hash=subpath.split("_")[-1])
137
+ entry = self._get_cache_by_key(hash_str=subpath.split("_")[-1])
171
138
  if entry is not None:
172
139
  entry["being_calculated"] = False
173
- self._save_cache(entry, hash=subpath.split("_")[-1])
174
-
175
- def _save_cache(self, cache, key=None, hash=None):
140
+ self._save_cache(entry, hash_str=subpath.split("_")[-1])
141
+
142
+ def _save_cache(self, cache, key=None, hash_str=None):
143
+ fpath = self.cache_fpath
144
+ if key is not None:
145
+ fpath += f"_{key}"
146
+ elif hash_str is not None:
147
+ fpath += f"_{hash_str}"
176
148
  with self.lock:
177
149
  self.cache = cache
178
- fpath = self._cache_fpath()
179
- if key is not None:
180
- fpath += f"_{key}"
181
- elif hash is not None:
182
- fpath += f"_{hash}"
183
150
  with portalocker.Lock(fpath, mode="wb") as cache_file:
184
151
  pickle.dump(cache, cache_file, protocol=4)
185
152
  if key is None:
@@ -202,11 +169,12 @@ class _PickleCore(_BaseCore):
202
169
  }
203
170
  if self.separate_files:
204
171
  self._save_cache(key_data, key)
205
- else:
206
- with self.lock:
207
- cache = self._get_cache()
208
- cache[key] = key_data
209
- self._save_cache(cache)
172
+ return # pragma: no cover
173
+
174
+ with self.lock:
175
+ cache = self._get_cache()
176
+ cache[key] = key_data
177
+ self._save_cache(cache)
210
178
 
211
179
  def mark_entry_being_calculated_separate_files(self, key):
212
180
  self._save_cache(
@@ -227,19 +195,20 @@ class _PickleCore(_BaseCore):
227
195
  def mark_entry_being_calculated(self, key):
228
196
  if self.separate_files:
229
197
  self.mark_entry_being_calculated_separate_files(key)
230
- else:
231
- with self.lock:
232
- cache = self._get_cache()
233
- try:
234
- cache[key]["being_calculated"] = True
235
- except KeyError:
236
- cache[key] = {
237
- "value": None,
238
- "time": datetime.now(),
239
- "stale": False,
240
- "being_calculated": True,
241
- }
242
- self._save_cache(cache)
198
+ return # pragma: no cover
199
+
200
+ with self.lock:
201
+ cache = self._get_cache()
202
+ try:
203
+ cache[key]["being_calculated"] = True
204
+ except KeyError:
205
+ cache[key] = {
206
+ "value": None,
207
+ "time": datetime.now(),
208
+ "stale": False,
209
+ "being_calculated": True,
210
+ }
211
+ self._save_cache(cache)
243
212
 
244
213
  def mark_entry_not_calculated(self, key):
245
214
  if self.separate_files:
@@ -254,12 +223,12 @@ class _PickleCore(_BaseCore):
254
223
  def wait_on_entry_calc(self, key):
255
224
  if self.separate_files:
256
225
  entry = self._get_cache_by_key(key)
257
- filename = f"{self._cache_fname()}_{key}"
226
+ filename = f"{self.cache_fname}_{key}"
258
227
  else:
259
228
  with self.lock:
260
229
  self._reload_cache()
261
230
  entry = self._get_cache()[key]
262
- filename = self._cache_fname()
231
+ filename = self.cache_fname
263
232
  if not entry["being_calculated"]:
264
233
  return entry["value"]
265
234
  event_handler = _PickleCore.CacheChangeHandler(
@@ -285,9 +254,10 @@ class _PickleCore(_BaseCore):
285
254
  def clear_being_calculated(self):
286
255
  if self.separate_files:
287
256
  self._clear_being_calculated_all_cache_files()
288
- else:
289
- with self.lock:
290
- cache = self._get_cache()
291
- for key in cache:
292
- cache[key]["being_calculated"] = False
293
- self._save_cache(cache)
257
+ return # pragma: no cover
258
+
259
+ with self.lock:
260
+ cache = self._get_cache()
261
+ for key in cache:
262
+ cache[key]["being_calculated"] = False
263
+ self._save_cache(cache)
cachier/version.info CHANGED
@@ -1 +1 @@
1
- v2.3.0
1
+ 3.0.1
@@ -1,33 +1,50 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cachier
3
- Version: 2.3.0
3
+ Version: 3.0.1
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
- Requires-Dist: portalocker >=2.3.2
30
- Requires-Dist: setuptools >=67.6.0
46
+ Requires-Dist: portalocker>=2.3.2
47
+ Requires-Dist: watchdog>=2.3.1
31
48
 
32
49
  Cachier
33
50
  #######
@@ -267,7 +284,7 @@ Cachier also accepts several keyword arguments in the calls of the function it w
267
284
  Ignore Cache
268
285
  ~~~~~~~~~~~~
269
286
 
270
- You can have ``cachier`` ignore any existing cache for a specific function call by passing ``ignore_cache=True`` to the function call. The cache will neither be checked nor updated with the new return value.
287
+ You can have ``cachier`` ignore any existing cache for a specific function call by passing ``cachier__skip_cache=True`` to the function call. The cache will neither be checked nor updated with the new return value.
271
288
 
272
289
  .. code-block:: python
273
290
 
@@ -276,17 +293,17 @@ You can have ``cachier`` ignore any existing cache for a specific function call
276
293
  return first_num + second_num
277
294
 
278
295
  def main():
279
- print(sum(5, 3, ignore_cache=True))
296
+ print(sum(5, 3, cachier__skip_cache=True))
280
297
 
281
298
  Overwrite Cache
282
299
  ~~~~~~~~~~~~~~~
283
300
 
284
- You can have ``cachier`` overwrite an existing cache entry - if one exists - for a specific function call by passing ``overwrite_cache=True`` to the function call. The cache will not be checked but will be updated with the new return value.
301
+ You can have ``cachier`` overwrite an existing cache entry - if one exists - for a specific function call by passing ``cachier__overwrite_cache=True`` to the function call. The cache will not be checked but will be updated with the new return value.
285
302
 
286
303
  Verbose Cache Call
287
304
  ~~~~~~~~~~~~~~~~~~
288
305
 
289
- You can have ``cachier`` print out a detailed explanation of the logic of a specific call by passing ``verbose_cache=True`` to the function call. This can be useful if you are not sure why a certain function result is, or is not, returned.
306
+ You can have ``cachier`` print out a detailed explanation of the logic of a specific call by passing ``cachier__verbose=True`` to the function call. This can be useful if you are not sure why a certain function result is, or is not, returned.
290
307
 
291
308
  Cache `None` Values
292
309
  ~~~~~~~~~~~~~~~~~~~
@@ -435,7 +452,7 @@ To run all tests EXCEPT memory core AND MongoDB core related tests, use:
435
452
  Running MongoDB tests against a live MongoDB instance
436
453
  -----------------------------------------------------
437
454
 
438
- **Note to developers:** By default, all MongoDB tests are run against a mocked MongoDB instance, provided by the ``pymongo_inmemory`` package. To run them against a live MongoDB instance, the ``CACHIER_TEST_VS_LIVE_MONGO`` environment variable is set to ``True`` in the ``test`` environment of this repository (and additional environment variables are populated with the appropriate credentials), used by the GitHub Action running tests on every commit and pull request.
455
+ **Note to developers:** By default, all MongoDB tests are run against a mocked MongoDB instance, provided by the ``pymongo_inmemory`` package. To run them against a live MongoDB instance, the ``CACHIER_TEST_VS_DOCKERIZED_MONGO`` environment variable is set to ``True`` in the ``test`` environment of this repository (and additional environment variables are populated with the appropriate credentials), used by the GitHub Action running tests on every commit and pull request.
439
456
 
440
457
  Contributors are not expected to run these tests against a live MongoDB instance when developing, as credentials for the testing instance used will NOT be shared, but rather use the testing against the in-memory MongoDB instance as a good proxy.
441
458
 
@@ -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
@@ -0,0 +1,19 @@
1
+ cachier/__init__.py,sha256=6i43oFM_ZTp47b6R8Ws2vvf2JXLvbn-TKzWESwBDb-M,304
2
+ cachier/__main__.py,sha256=upg-TlHs1vngKYvkjoPpl3Pvl6xOx4ut-M1mElMiAo0,443
3
+ cachier/_types.py,sha256=EGJMiw-oCIC_cDLyzw7YC40lfo8jnD3zMmoJpA9Y8Iw,238
4
+ cachier/_version.py,sha256=yE6UYwvdoIRpw3HmNifiGwV3fqVea5PwZj_EvyosiZ8,1079
5
+ cachier/config.py,sha256=JJutAeOlNjDobNWKteTvcvdj_Pfemk6_dMKIGFtW6UM,2754
6
+ cachier/core.py,sha256=x0I-Ot2dt7wThu2j8iLXPYvv8exwp4xXMUQIQyImr64,13261
7
+ cachier/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ cachier/version.info,sha256=BuP-SnoYhNINgcxwZcqM_Uw5TsYCxNGA3cObvU99pSE,6
9
+ cachier/cores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ cachier/cores/base.py,sha256=i6xlR3Em_s6rVER5Uyf1kK86KcaWtb84iKwS9bgDZGI,3465
11
+ cachier/cores/memory.py,sha256=JFXnXBAeP0nOXOf_vNgpcEuRNjxre8WQjiUBa7yEYmY,3128
12
+ cachier/cores/mongo.py,sha256=Ez3SNZzAzELO9c_SrGlOX855-5UEPBViabWPjkQnFc0,4917
13
+ cachier/cores/pickle.py,sha256=fVajLzlhUkQ9ochLWAZnDQQZJPXj0lWl7mgalI-z2x0,8903
14
+ cachier-3.0.1.dist-info/LICENSE,sha256=-2WrMJkIa0gVP6YQHXXDT7ws-S3M2NEVEF4XF3K8qrY,1069
15
+ cachier-3.0.1.dist-info/METADATA,sha256=JZCfPStwVwwhYrdvT_Vkmm2FrfqYc2rUGtExGh86DGI,20102
16
+ cachier-3.0.1.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
17
+ cachier-3.0.1.dist-info/entry_points.txt,sha256=x4Y7t6Y0Qev_3fgG-Jv7TrsvVdJty3FnGAdkT8-_5mY,49
18
+ cachier-3.0.1.dist-info/top_level.txt,sha256=_rW_HiJumDCch67YT-WAgzcyvKg5RiYDMZq9d-0ZpaE,8
19
+ cachier-3.0.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (72.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cachier = cachier.__main__:cli
@@ -1,17 +0,0 @@
1
- cachier/__init__.py,sha256=OvhjKkhTHuE9ESuBWLks0g6eGto9jKsJOr6FMFKQzXM,289
2
- cachier/__main__.py,sha256=g8dgovKdDgOMgNIpnmzVRuI5Dj5xODTav45TpZlH_iA,429
3
- cachier/_version.py,sha256=yE6UYwvdoIRpw3HmNifiGwV3fqVea5PwZj_EvyosiZ8,1079
4
- cachier/core.py,sha256=xB0kWRey5wL5XeTMTRe4lMpIukJmc2xWsOgUQGr8Q_c,14965
5
- cachier/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- cachier/version.info,sha256=B-GHcA-4RPUOEZ4BIL3emPJuZ5J9E1aQPTmIXKecyHE,7
7
- cachier/cores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- cachier/cores/base.py,sha256=Sy0Tzi2We-op31bsickPiSM1U0neND1hvgjAkjKEUKg,3194
9
- cachier/cores/memory.py,sha256=sbit0EQNqZLqIZMKLJ7sNXfTkG63s68vKJpTl49E6Kc,2816
10
- cachier/cores/mongo.py,sha256=LMfLawErv7WJQVMwgca1jg6Qu7rK7tO7_9Zi1BHrodM,4829
11
- cachier/cores/pickle.py,sha256=tNrOQ61n2i6KC-xAw5K3Ewz_6Cegz4n3NtvRl_lqLnM,9903
12
- cachier-2.3.0.dist-info/LICENSE,sha256=-2WrMJkIa0gVP6YQHXXDT7ws-S3M2NEVEF4XF3K8qrY,1069
13
- cachier-2.3.0.dist-info/METADATA,sha256=06wPKvqCL4K1ujV0dFz6GLU4t75GFpVq8QMZHm2X2PQ,18839
14
- cachier-2.3.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
15
- cachier-2.3.0.dist-info/entry_points.txt,sha256=16JU8wc6a62BLuOXVNjAiXJRp0AB5LtEwiKGYMjZjv0,49
16
- cachier-2.3.0.dist-info/top_level.txt,sha256=_rW_HiJumDCch67YT-WAgzcyvKg5RiYDMZq9d-0ZpaE,8
17
- cachier-2.3.0.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- cachier = cachier.__naim__:cli