cachier 3.2.1__py3-none-any.whl → 3.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cachier/core.py CHANGED
@@ -31,6 +31,7 @@ from .cores.sql import _SQLCore
31
31
 
32
32
  MAX_WORKERS_ENVAR_NAME = "CACHIER_MAX_WORKERS"
33
33
  DEFAULT_MAX_WORKERS = 8
34
+ ZERO_TIMEDELTA = timedelta(seconds=0)
34
35
 
35
36
 
36
37
  def _max_workers():
@@ -225,8 +226,31 @@ def cachier(
225
226
  def _cachier_decorator(func):
226
227
  core.set_func(func)
227
228
 
228
- @wraps(func)
229
- def func_wrapper(*args, **kwds):
229
+ # ---
230
+ # MAINTAINER NOTE: max_age parameter
231
+ #
232
+ # The _call function below supports a per-call 'max_age' parameter,
233
+ # allowing users to specify a maximum allowed age for a cached value.
234
+ # If the cached value is older than 'max_age',
235
+ # a recalculation is triggered. This is in addition to the
236
+ # per-decorator 'stale_after' parameter.
237
+ #
238
+ # The effective staleness threshold is the minimum of 'stale_after'
239
+ # and 'max_age' (if provided).
240
+ # This ensures that the strictest max age requirement is enforced.
241
+ #
242
+ # The main function wrapper is a standard function that passes
243
+ # *args and **kwargs to _call. By default, max_age is None,
244
+ # so only 'stale_after' is considered unless overridden.
245
+ #
246
+ # The user-facing API exposes:
247
+ # - Per-call: myfunc(..., max_age=timedelta(...))
248
+ #
249
+ # This design allows both one-off (per-call) and default
250
+ # (per-decorator) max age constraints.
251
+ # ---
252
+
253
+ def _call(*args, max_age: Optional[timedelta] = None, **kwds):
230
254
  nonlocal allow_none
231
255
  _allow_none = _update_with_defaults(allow_none, "allow_none", kwds)
232
256
  # print('Inside general wrapper for {}.'.format(func.__name__))
@@ -271,7 +295,23 @@ def cachier(
271
295
  if _allow_none or entry.value is not None:
272
296
  _print("Cached result found.")
273
297
  now = datetime.now()
274
- if now - entry.time <= _stale_after:
298
+ max_allowed_age = _stale_after
299
+ nonneg_max_age = True
300
+ if max_age is not None:
301
+ if max_age < ZERO_TIMEDELTA:
302
+ _print(
303
+ "max_age is negative. "
304
+ "Cached result considered stale."
305
+ )
306
+ nonneg_max_age = False
307
+ else:
308
+ max_allowed_age = (
309
+ min(_stale_after, max_age)
310
+ if max_age is not None
311
+ else _stale_after
312
+ )
313
+ # note: if max_age < 0, we always consider a value stale
314
+ if nonneg_max_age and (now - entry.time <= max_allowed_age):
275
315
  _print("And it is fresh!")
276
316
  return entry.value
277
317
  _print("But it is stale... :(")
@@ -305,6 +345,14 @@ def cachier(
305
345
  _print("No entry found. No current calc. Calling like a boss.")
306
346
  return _calc_entry(core, key, func, args, kwds)
307
347
 
348
+ # MAINTAINER NOTE: The main function wrapper is now a standard function
349
+ # that passes *args and **kwargs to _call. This ensures that user
350
+ # arguments are not shifted, and max_age is only settable via keyword
351
+ # argument.
352
+ @wraps(func)
353
+ def func_wrapper(*args, **kwargs):
354
+ return _call(*args, **kwargs)
355
+
308
356
  def _clear_cache():
309
357
  """Clear the cache."""
310
358
  core.clear_cache()
cachier/version.info CHANGED
@@ -1 +1 @@
1
- 3.2.1
1
+ 3.3.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cachier
3
- Version: 3.2.1
3
+ Version: 3.3.0
4
4
  Summary: Persistent, stale-free, local and cross-machine caching for Python functions.
5
5
  Author-email: Shay Palachy Affek <shay.palachy@gmail.com>
6
6
  License: MIT License
@@ -100,6 +100,7 @@ Features
100
100
  * Local caching using pickle files.
101
101
  * Cross-machine caching using MongoDB.
102
102
  * Thread-safety.
103
+ * **Per-call max age:** Specify a maximum age for cached values per call.
103
104
 
104
105
  Cachier is **NOT**:
105
106
 
@@ -282,6 +283,27 @@ Per-function call arguments
282
283
 
283
284
  Cachier also accepts several keyword arguments in the calls of the function it wraps rather than in the decorator call, allowing you to modify its behaviour for a specific function call.
284
285
 
286
+ **Max Age (max_age)**
287
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
288
+ You can specify a maximum allowed age for a cached value on a per-call basis using the `max_age` keyword argument. If the cached value is older than this threshold, a recalculation is triggered. This is in addition to the `stale_after` parameter set at the decorator level; the strictest (smallest) threshold is enforced.
289
+
290
+ .. code-block:: python
291
+
292
+ from datetime import timedelta
293
+ from cachier import cachier
294
+
295
+ @cachier(stale_after=timedelta(days=3))
296
+ def add(a, b):
297
+ return a + b
298
+
299
+ # Use a per-call max age:
300
+ result = add(1, 2, max_age=timedelta(seconds=10)) # Only use cache if value is <10s old
301
+
302
+ **How it works:**
303
+ - The effective max age threshold is the minimum of `stale_after` (from the decorator) and `max_age` (from the call).
304
+ - If the cached value is older than this threshold, a new calculation is triggered and the cache is updated.
305
+ - If not, the cached value is returned as usual.
306
+
285
307
  Ignore Cache
286
308
  ~~~~~~~~~~~~
287
309
 
@@ -3,18 +3,18 @@ cachier/__main__.py,sha256=upg-TlHs1vngKYvkjoPpl3Pvl6xOx4ut-M1mElMiAo0,443
3
3
  cachier/_types.py,sha256=EGJMiw-oCIC_cDLyzw7YC40lfo8jnD3zMmoJpA9Y8Iw,238
4
4
  cachier/_version.py,sha256=jnPPRn_qmjNi-qmQjlHnzNGf3LSBTYkMmJdGjxMTOBM,1089
5
5
  cachier/config.py,sha256=6hyQtn9T6UXu2UQhKJltWT0Nu4OBS4ION1x7Lt1i8Og,3838
6
- cachier/core.py,sha256=7pqf_EGvGXu5WWtC5MeY0tVW4M59XljZI9_2R4RVfRU,13627
6
+ cachier/core.py,sha256=PHLDA6Mabih-mi4y3CBsb8_vBIpvYQt547_Y57fo8uI,15825
7
7
  cachier/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- cachier/version.info,sha256=OCJyh4MEn8LyKlbR7nTWI1LEn7-BxrVLUIMNb2HDryI,6
8
+ cachier/version.info,sha256=Xu5sDFxpAfXlI_CkNLykEG4f9xNt60UfmCsLsaexNxY,6
9
9
  cachier/cores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  cachier/cores/base.py,sha256=s7qgmDJA4LGub6ydGfMk9vVJW4fgeU0EXl-9gmpuh28,3683
11
11
  cachier/cores/memory.py,sha256=fsvqq9rwwmAaMBvYo-oUNAxB6UOyfBpuf8ACW_XTaU0,3572
12
12
  cachier/cores/mongo.py,sha256=pCBrxLsmGr68Q50JVD_CUPAYwhaLDrJUQs_6A-_GYLA,4993
13
13
  cachier/cores/pickle.py,sha256=FgfvZWAFdWQPOo3G-L57iEV2ujEkIDH8TyGzbarsZeE,10678
14
14
  cachier/cores/sql.py,sha256=nuf2-Szo7VTPRa7IC3JGWEtGsBtdkIrx0bhOm3U0mfE,9895
15
- cachier-3.2.1.dist-info/licenses/LICENSE,sha256=-2WrMJkIa0gVP6YQHXXDT7ws-S3M2NEVEF4XF3K8qrY,1069
16
- cachier-3.2.1.dist-info/METADATA,sha256=xowxZuYhc4r5f4pKxrhROtHGT7FYObRJ4ULWFDXwA-Q,22101
17
- cachier-3.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
- cachier-3.2.1.dist-info/entry_points.txt,sha256=x4Y7t6Y0Qev_3fgG-Jv7TrsvVdJty3FnGAdkT8-_5mY,49
19
- cachier-3.2.1.dist-info/top_level.txt,sha256=_rW_HiJumDCch67YT-WAgzcyvKg5RiYDMZq9d-0ZpaE,8
20
- cachier-3.2.1.dist-info/RECORD,,
15
+ cachier-3.3.0.dist-info/licenses/LICENSE,sha256=-2WrMJkIa0gVP6YQHXXDT7ws-S3M2NEVEF4XF3K8qrY,1069
16
+ cachier-3.3.0.dist-info/METADATA,sha256=P8GoF2LSZKA5Tp8NunrJvS_f1XlytO4HsCmDsUy4oxQ,23136
17
+ cachier-3.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
+ cachier-3.3.0.dist-info/entry_points.txt,sha256=x4Y7t6Y0Qev_3fgG-Jv7TrsvVdJty3FnGAdkT8-_5mY,49
19
+ cachier-3.3.0.dist-info/top_level.txt,sha256=_rW_HiJumDCch67YT-WAgzcyvKg5RiYDMZq9d-0ZpaE,8
20
+ cachier-3.3.0.dist-info/RECORD,,