cachier 3.4.0__tar.gz → 3.4.1__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 (28) hide show
  1. {cachier-3.4.0/src/cachier.egg-info → cachier-3.4.1}/PKG-INFO +1 -1
  2. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/cores/pickle.py +78 -10
  3. cachier-3.4.1/src/cachier/version.info +1 -0
  4. {cachier-3.4.0 → cachier-3.4.1/src/cachier.egg-info}/PKG-INFO +1 -1
  5. cachier-3.4.0/src/cachier/version.info +0 -1
  6. {cachier-3.4.0 → cachier-3.4.1}/LICENSE +0 -0
  7. {cachier-3.4.0 → cachier-3.4.1}/MANIFEST.in +0 -0
  8. {cachier-3.4.0 → cachier-3.4.1}/README.rst +0 -0
  9. {cachier-3.4.0 → cachier-3.4.1}/pyproject.toml +0 -0
  10. {cachier-3.4.0 → cachier-3.4.1}/setup.cfg +0 -0
  11. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/__init__.py +0 -0
  12. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/__main__.py +0 -0
  13. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/_types.py +0 -0
  14. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/_version.py +0 -0
  15. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/config.py +0 -0
  16. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/core.py +0 -0
  17. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/cores/__init__.py +0 -0
  18. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/cores/base.py +0 -0
  19. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/cores/memory.py +0 -0
  20. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/cores/mongo.py +0 -0
  21. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/cores/redis.py +0 -0
  22. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/cores/sql.py +0 -0
  23. {cachier-3.4.0 → cachier-3.4.1}/src/cachier/py.typed +0 -0
  24. {cachier-3.4.0 → cachier-3.4.1}/src/cachier.egg-info/SOURCES.txt +0 -0
  25. {cachier-3.4.0 → cachier-3.4.1}/src/cachier.egg-info/dependency_links.txt +0 -0
  26. {cachier-3.4.0 → cachier-3.4.1}/src/cachier.egg-info/entry_points.txt +0 -0
  27. {cachier-3.4.0 → cachier-3.4.1}/src/cachier.egg-info/requires.txt +0 -0
  28. {cachier-3.4.0 → cachier-3.4.1}/src/cachier.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cachier
3
- Version: 3.4.0
3
+ Version: 3.4.1
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
@@ -6,8 +6,10 @@
6
6
  # Licensed under the MIT license:
7
7
  # http://www.opensource.org/licenses/MIT-license
8
8
  # Copyright (c) 2016, Shay Palachy <shaypal5@gmail.com>
9
+ import logging
9
10
  import os
10
11
  import pickle # for local caching
12
+ import time
11
13
  from datetime import datetime
12
14
  from typing import Any, Dict, Optional, Tuple, Union
13
15
 
@@ -51,12 +53,14 @@ class _PickleCore(_BaseCore):
51
53
  if not entry._processing:
52
54
  # print('stopping observer!')
53
55
  self.value = entry.value
54
- self.observer.stop()
56
+ if self.observer is not None:
57
+ self.observer.stop()
55
58
  # else:
56
59
  # print('NOT stopping observer... :(')
57
60
  except AttributeError: # catching entry being None
58
61
  self.value = None
59
- self.observer.stop()
62
+ if self.observer is not None:
63
+ self.observer.stop()
60
64
 
61
65
  def on_created(self, event) -> None:
62
66
  """A Watchdog Event Handler method.""" # noqa: D401
@@ -256,29 +260,93 @@ class _PickleCore(_BaseCore):
256
260
  cache[key]._processing = False
257
261
  self._save_cache(cache)
258
262
 
263
+ def _create_observer(self) -> Observer:
264
+ """Create a new observer instance."""
265
+ return Observer()
266
+
267
+ def _cleanup_observer(self, observer: Observer) -> None:
268
+ """Clean up observer properly."""
269
+ try:
270
+ if observer.is_alive():
271
+ observer.stop()
272
+ observer.join(timeout=1.0)
273
+ except Exception as e:
274
+ logging.debug("Observer cleanup failed: %s", e)
275
+
259
276
  def wait_on_entry_calc(self, key: str) -> Any:
277
+ """Wait for entry calculation to complete with inotify protection."""
260
278
  if self.separate_files:
261
279
  entry = self._load_cache_by_key(key)
262
280
  filename = f"{self.cache_fname}_{key}"
263
281
  else:
264
282
  with self.lock:
265
- entry = self.get_cache_dict()[key]
283
+ entry = self.get_cache_dict().get(key)
266
284
  filename = self.cache_fname
285
+
267
286
  if entry and not entry._processing:
268
287
  return entry.value
288
+
289
+ # Try to use inotify-based waiting
290
+ try:
291
+ return self._wait_with_inotify(key, filename)
292
+ except OSError as e:
293
+ if "inotify instance limit reached" in str(e):
294
+ # Fall back to polling if inotify limit is reached
295
+ return self._wait_with_polling(key)
296
+ else:
297
+ raise
298
+
299
+ def _wait_with_inotify(self, key: str, filename: str) -> Any:
300
+ """Wait for calculation using inotify with proper cleanup."""
269
301
  event_handler = _PickleCore.CacheChangeHandler(
270
302
  filename=filename, core=self, key=key
271
303
  )
272
- observer = Observer()
304
+
305
+ observer = self._create_observer()
273
306
  event_handler.inject_observer(observer)
274
- observer.schedule(event_handler, path=self.cache_dir, recursive=True)
275
- observer.start()
307
+
308
+ try:
309
+ observer.schedule(
310
+ event_handler, path=self.cache_dir, recursive=True
311
+ )
312
+ observer.start()
313
+
314
+ time_spent = 0
315
+ while observer.is_alive():
316
+ observer.join(timeout=1.0)
317
+ time_spent += 1
318
+ self.check_calc_timeout(time_spent)
319
+
320
+ # Check if calculation is complete
321
+ if event_handler.value is not None:
322
+ break
323
+
324
+ return event_handler.value
325
+ finally:
326
+ # Always cleanup the observer
327
+ self._cleanup_observer(observer)
328
+
329
+ def _wait_with_polling(self, key: str) -> Any:
330
+ """Fallback method using polling instead of inotify."""
276
331
  time_spent = 0
277
- while observer.is_alive():
278
- observer.join(timeout=1.0)
332
+ while True:
333
+ time.sleep(1) # Poll every 1 second (matching other cores)
279
334
  time_spent += 1
280
- self.check_calc_timeout(time_spent)
281
- return event_handler.value
335
+
336
+ try:
337
+ if self.separate_files:
338
+ entry = self._load_cache_by_key(key)
339
+ else:
340
+ with self.lock:
341
+ entry = self.get_cache_dict().get(key)
342
+
343
+ if entry and not entry._processing:
344
+ return entry.value
345
+
346
+ self.check_calc_timeout(time_spent)
347
+ except (FileNotFoundError, EOFError):
348
+ # Continue polling even if there are file errors
349
+ pass
282
350
 
283
351
  def clear_cache(self) -> None:
284
352
  if self.separate_files:
@@ -0,0 +1 @@
1
+ 3.4.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cachier
3
- Version: 3.4.0
3
+ Version: 3.4.1
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
@@ -1 +0,0 @@
1
- 3.4.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes