cachier 2.0.1__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 +2 -2
- cachier/base_core.py +22 -8
- cachier/core.py +27 -11
- cachier/memory_core.py +2 -2
- cachier/mongo_core.py +2 -4
- cachier/pickle_core.py +3 -7
- cachier/scripts/__init__.py +0 -0
- cachier/scripts/cli.py +17 -0
- {cachier-2.0.1.dist-info → cachier-2.1.0.dist-info}/METADATA +59 -8
- cachier-2.1.0.dist-info/RECORD +15 -0
- cachier-2.0.1.dist-info/RECORD +0 -13
- {cachier-2.0.1.dist-info → cachier-2.1.0.dist-info}/LICENSE +0 -0
- {cachier-2.0.1.dist-info → cachier-2.1.0.dist-info}/WHEEL +0 -0
- {cachier-2.0.1.dist-info → cachier-2.1.0.dist-info}/entry_points.txt +0 -0
- {cachier-2.0.1.dist-info → cachier-2.1.0.dist-info}/top_level.txt +0 -0
cachier/_version.py
CHANGED
@@ -11,8 +11,8 @@ version_json = '''
|
|
11
11
|
{
|
12
12
|
"dirty": false,
|
13
13
|
"error": null,
|
14
|
-
"full-revisionid": "
|
15
|
-
"version": "2.0
|
14
|
+
"full-revisionid": "7be769b78acf3a0e0aff8d528476913571d79749",
|
15
|
+
"version": "2.1.0"
|
16
16
|
}
|
17
17
|
''' # END VERSION_JSON
|
18
18
|
|
cachier/base_core.py
CHANGED
@@ -8,24 +8,32 @@
|
|
8
8
|
|
9
9
|
import abc # for the _BaseCore abstract base class
|
10
10
|
import functools
|
11
|
+
import hashlib
|
12
|
+
import inspect
|
13
|
+
import pickle # nosec: B403
|
11
14
|
|
12
15
|
|
13
|
-
|
14
|
-
|
16
|
+
def _default_hash_func(args, kwds):
|
17
|
+
# pylint: disable-next=protected-access
|
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()
|
15
23
|
|
16
24
|
|
17
25
|
class _BaseCore():
|
18
26
|
__metaclass__ = abc.ABCMeta
|
19
27
|
|
20
|
-
def __init__(self,
|
21
|
-
self.
|
22
|
-
self.next_time = next_time
|
23
|
-
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
|
24
30
|
self.func = None
|
25
31
|
|
26
32
|
def set_func(self, func):
|
27
|
-
"""Sets the function this core will use. This has to be set before
|
28
|
-
|
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'
|
29
37
|
self.func = func
|
30
38
|
|
31
39
|
def get_entry(self, args, kwds):
|
@@ -34,6 +42,12 @@ class _BaseCore():
|
|
34
42
|
key = self.hash_func(args, kwds)
|
35
43
|
return self.get_entry_by_key(key)
|
36
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
|
+
|
37
51
|
@abc.abstractmethod
|
38
52
|
def get_entry_by_key(self, key):
|
39
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
21
|
-
super().__init__(
|
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__(
|
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,
|
88
|
-
|
83
|
+
self, hash_func, reload, cache_dir,
|
84
|
+
separate_files, wait_for_calc_timeout,
|
89
85
|
):
|
90
|
-
super().__init__(
|
86
|
+
super().__init__(hash_func)
|
91
87
|
self.cache = None
|
92
88
|
self.reload = reload
|
93
89
|
self.cache_dir = DEF_CACHIER_DIR
|
File without changes
|
cachier/scripts/cli.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
"""A command-line interface for cachier."""
|
2
|
+
|
3
|
+
import click
|
4
|
+
|
5
|
+
from cachier.core import _set_max_workers
|
6
|
+
|
7
|
+
|
8
|
+
@click.group()
|
9
|
+
def cli():
|
10
|
+
"""A command-line interface for cachier."""
|
11
|
+
|
12
|
+
|
13
|
+
@cli.command("Limits the number of worker threads used by cachier.")
|
14
|
+
@click.argument('max_workers', nargs=1, type=int)
|
15
|
+
def set_max_workers(max_workers):
|
16
|
+
"""Limits the number of worker threads used by cachier."""
|
17
|
+
_set_max_workers(max_workers)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: cachier
|
3
|
-
Version: 2.0
|
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
|
@@ -27,13 +27,18 @@ License-File: LICENSE
|
|
27
27
|
Requires-Dist: watchdog
|
28
28
|
Requires-Dist: portalocker
|
29
29
|
Requires-Dist: pathtools
|
30
|
+
Requires-Dist: setuptools (>=67.6.0)
|
30
31
|
Provides-Extra: test
|
31
32
|
Requires-Dist: pytest ; extra == 'test'
|
32
33
|
Requires-Dist: coverage ; extra == 'test'
|
33
34
|
Requires-Dist: pytest-cov ; extra == 'test'
|
34
|
-
Requires-Dist:
|
35
|
+
Requires-Dist: bandit ; extra == 'test'
|
36
|
+
Requires-Dist: flake8 ; extra == 'test'
|
37
|
+
Requires-Dist: pylint ; extra == 'test'
|
38
|
+
Requires-Dist: safety ; extra == 'test'
|
35
39
|
Requires-Dist: pymongo ; extra == 'test'
|
36
40
|
Requires-Dist: dnspython ; extra == 'test'
|
41
|
+
Requires-Dist: pymongo-inmemory ; extra == 'test'
|
37
42
|
Requires-Dist: pandas ; extra == 'test'
|
38
43
|
Requires-Dist: collective.checkdocs ; extra == 'test'
|
39
44
|
Requires-Dist: pygments ; extra == 'test'
|
@@ -123,6 +128,33 @@ You can add a default, pickle-based, persistent cache to your function - meaning
|
|
123
128
|
"""Your function now has a persistent cache mapped by argument values!"""
|
124
129
|
return {'arg1': arg1, 'arg2': arg2}
|
125
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
|
126
158
|
|
127
159
|
|
128
160
|
Resetting a Cache
|
@@ -133,7 +165,7 @@ The Cachier wrapper adds a ``clear_cache()`` function to each wrapped function.
|
|
133
165
|
|
134
166
|
foo.clear_cache()
|
135
167
|
|
136
|
-
|
168
|
+
General Configuration
|
137
169
|
----------------------
|
138
170
|
|
139
171
|
Threads Limit
|
@@ -175,11 +207,15 @@ Further function calls made while the calculation is being performed will not tr
|
|
175
207
|
Working with unhashable arguments
|
176
208
|
---------------------------------
|
177
209
|
|
178
|
-
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 ``
|
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.
|
179
211
|
|
180
212
|
.. code-block:: python
|
181
213
|
|
182
|
-
|
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)
|
183
219
|
def calculate_super_complex_stuff(custom_obj):
|
184
220
|
# amazing code goes here
|
185
221
|
|
@@ -188,6 +224,23 @@ See here for an example:
|
|
188
224
|
`Question: How to work with unhashable arguments <https://github.com/python-cachier/cachier/issues/91>`_
|
189
225
|
|
190
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
|
+
|
191
244
|
Per-function call arguments
|
192
245
|
---------------------------
|
193
246
|
|
@@ -377,7 +430,7 @@ Other major contributors:
|
|
377
430
|
|
378
431
|
* `cthoyt <https://github.com/cthoyt>`_ - Base memory core implementation.
|
379
432
|
|
380
|
-
* `amarczew <https://github.com/amarczew>`_ - The ``
|
433
|
+
* `amarczew <https://github.com/amarczew>`_ - The ``hash_func`` kwarg.
|
381
434
|
|
382
435
|
* `non-senses <https://github.com/non-senses>`_ - The ``wait_for_calc_timeout`` kwarg.
|
383
436
|
|
@@ -423,5 +476,3 @@ Notable bugfixers:
|
|
423
476
|
.. _watchdog: https://github.com/gorakhargosh/watchdog
|
424
477
|
|
425
478
|
|
426
|
-
|
427
|
-
|
@@ -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,,
|
cachier-2.0.1.dist-info/RECORD
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
cachier/__init__.py,sha256=znE73J-Sg_4tjDmfKaJ5Cabg_kWhus9yhq1LJmM8B6Q,133
|
2
|
-
cachier/_version.py,sha256=a5msFbwD6NYMINT1KG0orJkvdk7TUYcBzgZ2eQgM_Ts,471
|
3
|
-
cachier/base_core.py,sha256=PT2fhBy4P8PyaBy_iR6FFbTStDBMeNtF_FVMY7ax7ww,2157
|
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-2.0.1.dist-info/LICENSE,sha256=-2WrMJkIa0gVP6YQHXXDT7ws-S3M2NEVEF4XF3K8qrY,1069
|
9
|
-
cachier-2.0.1.dist-info/METADATA,sha256=0wGUUQ0pfQEq3uh8gIDpkmU0Gjil3NX_K8xPJm5jQSg,14837
|
10
|
-
cachier-2.0.1.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
|
11
|
-
cachier-2.0.1.dist-info/entry_points.txt,sha256=HZPL3TEoT-aieCOvQvsLEDBAuPBSbDNrIjMiDA8h6zE,71
|
12
|
-
cachier-2.0.1.dist-info/top_level.txt,sha256=_rW_HiJumDCch67YT-WAgzcyvKg5RiYDMZq9d-0ZpaE,8
|
13
|
-
cachier-2.0.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|