cachier 2.0.2__py2.py3-none-any.whl → 2.1.0__py2.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/_version.py CHANGED
@@ -11,8 +11,8 @@ version_json = '''
11
11
  {
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "d1578e6853ade5428bbd9e7b969d224a1eaf053a",
15
- "version": "2.0.2"
14
+ "full-revisionid": "7be769b78acf3a0e0aff8d528476913571d79749",
15
+ "version": "2.1.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
cachier/base_core.py CHANGED
@@ -9,26 +9,31 @@
9
9
  import abc # for the _BaseCore abstract base class
10
10
  import functools
11
11
  import hashlib
12
+ import inspect
13
+ import pickle # nosec: B403
12
14
 
13
15
 
14
- def _default_hash_params(args, kwds):
16
+ def _default_hash_func(args, kwds):
15
17
  # pylint: disable-next=protected-access
16
- key = functools._make_key(args, kwds, typed=False)
17
- return hashlib.sha256(str(hash(key)).encode()).hexdigest()
18
+ key = functools._make_key(args, kwds, typed=True)
19
+ hash = hashlib.sha256()
20
+ for item in key:
21
+ hash.update(pickle.dumps(item))
22
+ return hash.hexdigest()
18
23
 
19
24
 
20
25
  class _BaseCore():
21
26
  __metaclass__ = abc.ABCMeta
22
27
 
23
- def __init__(self, stale_after, next_time, hash_params):
24
- self.stale_after = stale_after
25
- self.next_time = next_time
26
- self.hash_func = hash_params if hash_params else _default_hash_params
28
+ def __init__(self, hash_func):
29
+ self.hash_func = hash_func if hash_func else _default_hash_func
27
30
  self.func = None
28
31
 
29
32
  def set_func(self, func):
30
- """Sets the function this core will use. This has to be set before
31
- any method is called"""
33
+ """Sets the function this core will use. This has to be set before any
34
+ method is called. Also determine if the funtion is an object method."""
35
+ func_params = list(inspect.signature(func).parameters)
36
+ self.func_is_method = func_params and func_params[0] == 'self'
32
37
  self.func = func
33
38
 
34
39
  def get_entry(self, args, kwds):
@@ -37,6 +42,12 @@ class _BaseCore():
37
42
  key = self.hash_func(args, kwds)
38
43
  return self.get_entry_by_key(key)
39
44
 
45
+ def precache_value(self, args, kwds, value_to_cache):
46
+ """Writes a precomputed value into the cache."""
47
+ key = self.hash_func(args, kwds)
48
+ self.set_entry(key, value_to_cache)
49
+ return value_to_cache
50
+
40
51
  @abc.abstractmethod
41
52
  def get_entry_by_key(self, key):
42
53
  """Returns the result mapped to the given key in this core's cache,
cachier/core.py CHANGED
@@ -14,6 +14,7 @@ from __future__ import print_function
14
14
 
15
15
  import os
16
16
  from functools import wraps
17
+ from warnings import warn
17
18
 
18
19
  import datetime
19
20
  from concurrent.futures import ThreadPoolExecutor
@@ -85,6 +86,7 @@ def cachier(
85
86
  backend=None,
86
87
  mongetter=None,
87
88
  cache_dir=None,
89
+ hash_func=None,
88
90
  hash_params=None,
89
91
  wait_for_calc_timeout=0,
90
92
  separate_files=False,
@@ -125,7 +127,7 @@ def cachier(
125
127
  A fully qualified path to a file directory to be used for cache files.
126
128
  The running process must have running permissions to this folder. If
127
129
  not provided, a default directory at `~/.cachier/` is used.
128
- hash_params : callable, optional
130
+ hash_func : callable, optional
129
131
  A callable that gets the args and kwargs from the decorated function
130
132
  and returns a hash key for them. This parameter can be used to enable
131
133
  the use of cachier with functions that get arguments that are not
@@ -141,15 +143,19 @@ def cachier(
141
143
  split between several files, one for each argument set. This can help
142
144
  if you per-function cache files become too large.
143
145
  """
146
+ # Check for deprecated parameters
147
+ if hash_params is not None:
148
+ message = 'hash_params will be removed in a future release, ' \
149
+ 'please use hash_func instead'
150
+ warn(message, DeprecationWarning, stacklevel=2)
151
+ hash_func = hash_params
144
152
  # The default is calculated dynamically to maintain previous behavior
145
153
  # to default to pickle unless the ``mongetter`` argument is given.
146
154
  if backend is None:
147
155
  backend = 'pickle' if mongetter is None else 'mongo'
148
156
  if backend == 'pickle':
149
157
  core = _PickleCore( # pylint: disable=R0204
150
- stale_after=stale_after,
151
- next_time=next_time,
152
- hash_params=hash_params,
158
+ hash_func=hash_func,
153
159
  reload=pickle_reload,
154
160
  cache_dir=cache_dir,
155
161
  separate_files=separate_files,
@@ -161,16 +167,12 @@ def cachier(
161
167
  'must specify ``mongetter`` when using the mongo core')
162
168
  core = _MongoCore(
163
169
  mongetter=mongetter,
164
- stale_after=stale_after,
165
- next_time=next_time,
166
- hash_params=hash_params,
170
+ hash_func=hash_func,
167
171
  wait_for_calc_timeout=wait_for_calc_timeout,
168
172
  )
169
173
  elif backend == 'memory':
170
174
  core = _MemoryCore(
171
- stale_after=stale_after,
172
- next_time=next_time,
173
- hash_params=hash_params,
175
+ hash_func=hash_func,
174
176
  )
175
177
  elif backend == 'redis':
176
178
  raise NotImplementedError(
@@ -194,7 +196,10 @@ def cachier(
194
196
  _print = print
195
197
  if ignore_cache:
196
198
  return func(*args, **kwds)
197
- key, entry = core.get_entry(args, kwds)
199
+ if core.func_is_method:
200
+ key, entry = core.get_entry(args[1:], kwds)
201
+ else:
202
+ key, entry = core.get_entry(args, kwds)
198
203
  if overwrite_cache:
199
204
  return _calc_entry(core, key, func, args, kwds)
200
205
  if entry is not None: # pylint: disable=R0101
@@ -259,9 +264,20 @@ def cachier(
259
264
  except AttributeError:
260
265
  return None
261
266
 
267
+ def precache_value(*args, value_to_cache, **kwds):
268
+ """Add an initial value to the cache.
269
+
270
+ Arguments
271
+ ---------
272
+ value : any
273
+ entry to be written into the cache
274
+ """
275
+ return core.precache_value(args, kwds, value_to_cache)
276
+
262
277
  func_wrapper.clear_cache = clear_cache
263
278
  func_wrapper.clear_being_calculated = clear_being_calculated
264
279
  func_wrapper.cache_dpath = cache_dpath
280
+ func_wrapper.precache_value = precache_value
265
281
  return func_wrapper
266
282
 
267
283
  return _cachier_decorator
cachier/memory_core.py CHANGED
@@ -17,8 +17,8 @@ class _MemoryCore(_BaseCore):
17
17
  See :class:`_BaseCore` documentation.
18
18
  """
19
19
 
20
- def __init__(self, stale_after, next_time, hash_params):
21
- super().__init__(stale_after, next_time, hash_params)
20
+ def __init__(self, hash_func):
21
+ super().__init__(hash_func)
22
22
  self.cache = {}
23
23
  self.lock = threading.RLock()
24
24
 
cachier/mongo_core.py CHANGED
@@ -37,14 +37,12 @@ class _MongoCore(_BaseCore):
37
37
 
38
38
  _INDEX_NAME = 'func_1_key_1'
39
39
 
40
- def __init__(
41
- self, mongetter, stale_after, next_time,
42
- hash_params, wait_for_calc_timeout):
40
+ def __init__(self, mongetter, hash_func, wait_for_calc_timeout):
43
41
  if 'pymongo' not in sys.modules:
44
42
  warnings.warn((
45
43
  "Cachier warning: pymongo was not found. "
46
44
  "MongoDB cores will not function."))
47
- super().__init__(stale_after, next_time, hash_params)
45
+ super().__init__(hash_func)
48
46
  self.mongetter = mongetter
49
47
  self.mongo_collection = self.mongetter()
50
48
  self.wait_for_calc_timeout = wait_for_calc_timeout
cachier/pickle_core.py CHANGED
@@ -29,10 +29,6 @@ class _PickleCore(_BaseCore):
29
29
 
30
30
  Parameters
31
31
  ----------
32
- stale_after : datetime.timedelta, optional
33
- See _BaseCore documentation.
34
- next_time : bool, optional
35
- See _BaseCore documentation.
36
32
  pickle_reload : bool, optional
37
33
  See core.cachier() documentation.
38
34
  cache_dir : str, optional.
@@ -84,10 +80,10 @@ class _PickleCore(_BaseCore):
84
80
  self._check_calculation()
85
81
 
86
82
  def __init__(
87
- self, stale_after, next_time, hash_params, reload,
88
- cache_dir, separate_files, wait_for_calc_timeout,
83
+ self, hash_func, reload, cache_dir,
84
+ separate_files, wait_for_calc_timeout,
89
85
  ):
90
- super().__init__(stale_after, next_time, hash_params)
86
+ super().__init__(hash_func)
91
87
  self.cache = None
92
88
  self.reload = reload
93
89
  self.cache_dir = DEF_CACHIER_DIR
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cachier
3
- Version: 2.0.2
3
+ Version: 2.1.0
4
4
  Summary: Persistent, stale-free, local and cross-machine caching for Python functions.
5
5
  Home-page: https://github.com/python-cachier/cachier
6
6
  Author: Shay Palachy
@@ -128,6 +128,33 @@ You can add a default, pickle-based, persistent cache to your function - meaning
128
128
  """Your function now has a persistent cache mapped by argument values!"""
129
129
  return {'arg1': arg1, 'arg2': arg2}
130
130
 
131
+ Class and object methods can also be cached. Cachier will automatically ignore the `self` parameter when determining the cache key for an object method. **This means that methods will be cached across all instances of an object, which may not be what you want.**
132
+
133
+ .. code-block:: python
134
+
135
+ from cachier import cachier
136
+
137
+ class Foo():
138
+ @staticmethod
139
+ @cachier()
140
+ def good_static_usage(arg_1, arg_2):
141
+ return arg_1 + arg_2
142
+
143
+ # Instance method does not depend on object's internal state, so good to cache
144
+ @cachier()
145
+ def good_usage_1(self, arg_1, arg_2)
146
+ return arg_1 + arg_2
147
+
148
+ # Instance method is calling external service, probably okay to cache
149
+ @cachier()
150
+ def good_usage_2(self, arg_1, arg_2)
151
+ result = self.call_api(arg_1, arg_2)
152
+ return result
153
+
154
+ # Instance method relies on object attribute, NOT good to cache
155
+ @cachier()
156
+ def bad_usage(self, arg_1, arg_2)
157
+ return arg_1 + arg_2 + self.arg_3
131
158
 
132
159
 
133
160
  Resetting a Cache
@@ -138,7 +165,7 @@ The Cachier wrapper adds a ``clear_cache()`` function to each wrapped function.
138
165
 
139
166
  foo.clear_cache()
140
167
 
141
- Genereal Configuration
168
+ General Configuration
142
169
  ----------------------
143
170
 
144
171
  Threads Limit
@@ -180,11 +207,15 @@ Further function calls made while the calculation is being performed will not tr
180
207
  Working with unhashable arguments
181
208
  ---------------------------------
182
209
 
183
- As mentioned above, the positional and keyword arguments to the wrapped function must be hashable (i.e. Python's immutable built-in objects, not mutable containers). To get around this limitation the ``hash_params`` parameter of the ``cachier`` decorator can be provided with a callable that gets the args and kwargs from the decorated function and returns a hash key for them.
210
+ As mentioned above, the positional and keyword arguments to the wrapped function must be hashable (i.e. Python's immutable built-in objects, not mutable containers). To get around this limitation the ``hash_func`` parameter of the ``cachier`` decorator can be provided with a callable that gets the args and kwargs from the decorated function and returns a hash key for them.
184
211
 
185
212
  .. code-block:: python
186
213
 
187
- @cachier(hash_params=hash_my_custom_class)
214
+ def calculate_hash(args, kwds):
215
+ key = ... # compute a hash key here based on arguments
216
+ return key
217
+
218
+ @cachier(hash_func=calculate_hash)
188
219
  def calculate_super_complex_stuff(custom_obj):
189
220
  # amazing code goes here
190
221
 
@@ -193,6 +224,23 @@ See here for an example:
193
224
  `Question: How to work with unhashable arguments <https://github.com/python-cachier/cachier/issues/91>`_
194
225
 
195
226
 
227
+ Precaching values
228
+ ---------------------------------
229
+
230
+ If you want to load a value into the cache without calling the underlying function, this can be done with the `precache_value` function.
231
+
232
+ .. code-block:: python
233
+
234
+ @cachier()
235
+ def add(arg1, arg2):
236
+ return arg1 + arg2
237
+
238
+ add.precache_value(2, 2, value_to_cache=5)
239
+
240
+ result = add(2, 2)
241
+ print(result) # prints 5
242
+
243
+
196
244
  Per-function call arguments
197
245
  ---------------------------
198
246
 
@@ -382,7 +430,7 @@ Other major contributors:
382
430
 
383
431
  * `cthoyt <https://github.com/cthoyt>`_ - Base memory core implementation.
384
432
 
385
- * `amarczew <https://github.com/amarczew>`_ - The ``hash_params`` kwarg.
433
+ * `amarczew <https://github.com/amarczew>`_ - The ``hash_func`` kwarg.
386
434
 
387
435
  * `non-senses <https://github.com/non-senses>`_ - The ``wait_for_calc_timeout`` kwarg.
388
436
 
@@ -428,5 +476,3 @@ Notable bugfixers:
428
476
  .. _watchdog: https://github.com/gorakhargosh/watchdog
429
477
 
430
478
 
431
-
432
-
@@ -0,0 +1,15 @@
1
+ cachier/__init__.py,sha256=znE73J-Sg_4tjDmfKaJ5Cabg_kWhus9yhq1LJmM8B6Q,133
2
+ cachier/_version.py,sha256=Gk55atXZn9Zmgvi_28RCi_UXaHw85U1VAatxQ7cEXK0,471
3
+ cachier/base_core.py,sha256=3TJOmzCwtoGAGsMj-4zo91C6eP69cSB-40Lru3MFgBg,2663
4
+ cachier/core.py,sha256=L0dWlbERd-8RiGPP3Iw1sbNzgclO7wSSflS4QbkaQmY,11454
5
+ cachier/memory_core.py,sha256=do4aKTxcNaoaCpLx6w2bhhLIAze8tEo9gPNAekwWrHo,3020
6
+ cachier/mongo_core.py,sha256=Tbe-fpWyvlYK_WQq02-K2WDGqtoz90rwcTS_DFT146I,5114
7
+ cachier/pickle_core.py,sha256=4-sji-yQNfTvutpxHBHzZbourHyby_7ykGQFLf8dC58,10150
8
+ cachier/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ cachier/scripts/cli.py,sha256=tMwq1IaD_lxhcc8WMgWh4P0gpi5l6-twCS2YeAKAM98,429
10
+ cachier-2.1.0.dist-info/LICENSE,sha256=-2WrMJkIa0gVP6YQHXXDT7ws-S3M2NEVEF4XF3K8qrY,1069
11
+ cachier-2.1.0.dist-info/METADATA,sha256=ysQu-4vNtUdDT2wQjGAsrgfzZNOWaEWKPKdTinfOM_8,16470
12
+ cachier-2.1.0.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
13
+ cachier-2.1.0.dist-info/entry_points.txt,sha256=HZPL3TEoT-aieCOvQvsLEDBAuPBSbDNrIjMiDA8h6zE,71
14
+ cachier-2.1.0.dist-info/top_level.txt,sha256=_rW_HiJumDCch67YT-WAgzcyvKg5RiYDMZq9d-0ZpaE,8
15
+ cachier-2.1.0.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- cachier/__init__.py,sha256=znE73J-Sg_4tjDmfKaJ5Cabg_kWhus9yhq1LJmM8B6Q,133
2
- cachier/_version.py,sha256=_r_v0NgI9oOYWtcdA8kJvbdmbzJ6jVUStmVJ8q6JQNA,471
3
- cachier/base_core.py,sha256=gS2BrCv87Xr6lVzaXPAqtgYHNJGUYMjWIelp7eXblow,2257
4
- cachier/core.py,sha256=h3dCDlBdUYSsJ2AOUVl-IbNjE6H5NH9y-nlzlxXWYoI,10869
5
- cachier/memory_core.py,sha256=5PGm1avW_nHlctvJrYWlerLWgttKpP69epOt2e5r1O0,3072
6
- cachier/mongo_core.py,sha256=-YOjOOMVCu1bVb26nge2b4vatxFwafAyA52RKzhL9xc,5191
7
- cachier/pickle_core.py,sha256=b7QtVjuRvcUnC2ecabMmH7Zu8JYXF251cWWTFv4Iv48,10354
8
- cachier/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- cachier/scripts/cli.py,sha256=tMwq1IaD_lxhcc8WMgWh4P0gpi5l6-twCS2YeAKAM98,429
10
- cachier-2.0.2.dist-info/LICENSE,sha256=-2WrMJkIa0gVP6YQHXXDT7ws-S3M2NEVEF4XF3K8qrY,1069
11
- cachier-2.0.2.dist-info/METADATA,sha256=KyH95H8-1mKmsgc1VaykLYGOIQpZQX-4OoCFQrNC72s,15045
12
- cachier-2.0.2.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
13
- cachier-2.0.2.dist-info/entry_points.txt,sha256=HZPL3TEoT-aieCOvQvsLEDBAuPBSbDNrIjMiDA8h6zE,71
14
- cachier-2.0.2.dist-info/top_level.txt,sha256=_rW_HiJumDCch67YT-WAgzcyvKg5RiYDMZq9d-0ZpaE,8
15
- cachier-2.0.2.dist-info/RECORD,,