cachier 2.0.1__tar.gz → 2.1.0__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 (46) hide show
  1. {cachier-2.0.1 → cachier-2.1.0}/.github/workflows/test.yml +0 -5
  2. {cachier-2.0.1 → cachier-2.1.0}/PKG-INFO +53 -7
  3. {cachier-2.0.1 → cachier-2.1.0}/README.rst +52 -6
  4. {cachier-2.0.1 → cachier-2.1.0}/cachier/_version.py +2 -2
  5. {cachier-2.0.1 → cachier-2.1.0}/cachier/base_core.py +22 -8
  6. {cachier-2.0.1 → cachier-2.1.0}/cachier/core.py +27 -11
  7. {cachier-2.0.1 → cachier-2.1.0}/cachier/memory_core.py +2 -2
  8. {cachier-2.0.1 → cachier-2.1.0}/cachier/mongo_core.py +2 -4
  9. {cachier-2.0.1 → cachier-2.1.0}/cachier/pickle_core.py +3 -7
  10. {cachier-2.0.1 → cachier-2.1.0}/cachier.egg-info/PKG-INFO +53 -7
  11. {cachier-2.0.1 → cachier-2.1.0}/cachier.egg-info/SOURCES.txt +4 -2
  12. {cachier-2.0.1 → cachier-2.1.0}/cachier.egg-info/requires.txt +6 -1
  13. {cachier-2.0.1 → cachier-2.1.0}/setup.py +5 -4
  14. {cachier-2.0.1 → cachier-2.1.0}/tests/speed_eval.py +1 -1
  15. cachier-2.1.0/tests/standalone_script.py +11 -0
  16. {cachier-2.0.1 → cachier-2.1.0}/tests/test_general.py +105 -26
  17. {cachier-2.0.1 → cachier-2.1.0}/tests/test_memory_core.py +38 -14
  18. {cachier-2.0.1 → cachier-2.1.0}/tests/test_mongo_core.py +67 -59
  19. {cachier-2.0.1 → cachier-2.1.0}/tests/test_pickle_core.py +53 -31
  20. cachier-2.1.0/tests/test_quality.py +17 -0
  21. cachier-2.1.0/tests/test_security.py +20 -0
  22. cachier-2.0.1/tests/conftest.py +0 -22
  23. {cachier-2.0.1 → cachier-2.1.0}/.codecov.yml +0 -0
  24. {cachier-2.0.1 → cachier-2.1.0}/.coveragerc +0 -0
  25. {cachier-2.0.1 → cachier-2.1.0}/.deepsource.toml +0 -0
  26. {cachier-2.0.1 → cachier-2.1.0}/.fdignore +0 -0
  27. {cachier-2.0.1 → cachier-2.1.0}/.flake8 +0 -0
  28. {cachier-2.0.1 → cachier-2.1.0}/.gitattributes +0 -0
  29. {cachier-2.0.1 → cachier-2.1.0}/.github/workflows/checkdocs.yml +0 -0
  30. {cachier-2.0.1 → cachier-2.1.0}/.github/workflows/lint.yml +0 -0
  31. {cachier-2.0.1 → cachier-2.1.0}/.gitignore +0 -0
  32. {cachier-2.0.1 → cachier-2.1.0}/.ignore +0 -0
  33. {cachier-2.0.1 → cachier-2.1.0}/.pylintrc +0 -0
  34. {cachier-2.0.1 → cachier-2.1.0}/LICENSE +0 -0
  35. {cachier-2.0.1 → cachier-2.1.0}/MANIFEST.in +0 -0
  36. {cachier-2.0.1 → cachier-2.1.0}/cachier/__init__.py +0 -0
  37. {cachier-2.0.1 → cachier-2.1.0}/cachier/scripts/__init__.py +0 -0
  38. {cachier-2.0.1 → cachier-2.1.0}/cachier/scripts/cli.py +0 -0
  39. {cachier-2.0.1 → cachier-2.1.0}/cachier.egg-info/dependency_links.txt +0 -0
  40. {cachier-2.0.1 → cachier-2.1.0}/cachier.egg-info/entry_points.txt +0 -0
  41. {cachier-2.0.1 → cachier-2.1.0}/cachier.egg-info/top_level.txt +0 -0
  42. {cachier-2.0.1 → cachier-2.1.0}/pytest.ini +0 -0
  43. {cachier-2.0.1 → cachier-2.1.0}/setup.cfg +0 -0
  44. {cachier-2.0.1 → cachier-2.1.0}/tests/__init__.py +0 -0
  45. {cachier-2.0.1 → cachier-2.1.0}/tests/test_core_lookup.py +0 -0
  46. {cachier-2.0.1 → cachier-2.1.0}/versioneer.py +0 -0
@@ -32,11 +32,6 @@ jobs:
32
32
  python -m pip install --upgrade pip
33
33
  python -m pip install -e ".[test]"
34
34
  - name: Unit tests
35
- env:
36
- CACHIER_TEST_HOST: ${{ secrets.CACHIER_TEST_HOST }}
37
- CACHIER_TEST_DB: ${{ secrets.CACHIER_TEST_DB }}
38
- CACHIER_TEST_USERNAME: ${{ secrets.CACHIER_TEST_USERNAME }}
39
- CACHIER_TEST_PASSWORD: ${{ secrets.CACHIER_TEST_PASSWORD }}
40
35
  run: |
41
36
  pytest
42
37
  - name: "Upload coverage to Codecov"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cachier
3
- Version: 2.0.1
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
@@ -111,6 +111,33 @@ You can add a default, pickle-based, persistent cache to your function - meaning
111
111
  """Your function now has a persistent cache mapped by argument values!"""
112
112
  return {'arg1': arg1, 'arg2': arg2}
113
113
 
114
+ 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.**
115
+
116
+ .. code-block:: python
117
+
118
+ from cachier import cachier
119
+
120
+ class Foo():
121
+ @staticmethod
122
+ @cachier()
123
+ def good_static_usage(arg_1, arg_2):
124
+ return arg_1 + arg_2
125
+
126
+ # Instance method does not depend on object's internal state, so good to cache
127
+ @cachier()
128
+ def good_usage_1(self, arg_1, arg_2)
129
+ return arg_1 + arg_2
130
+
131
+ # Instance method is calling external service, probably okay to cache
132
+ @cachier()
133
+ def good_usage_2(self, arg_1, arg_2)
134
+ result = self.call_api(arg_1, arg_2)
135
+ return result
136
+
137
+ # Instance method relies on object attribute, NOT good to cache
138
+ @cachier()
139
+ def bad_usage(self, arg_1, arg_2)
140
+ return arg_1 + arg_2 + self.arg_3
114
141
 
115
142
 
116
143
  Resetting a Cache
@@ -121,7 +148,7 @@ The Cachier wrapper adds a ``clear_cache()`` function to each wrapped function.
121
148
 
122
149
  foo.clear_cache()
123
150
 
124
- Genereal Configuration
151
+ General Configuration
125
152
  ----------------------
126
153
 
127
154
  Threads Limit
@@ -163,11 +190,15 @@ Further function calls made while the calculation is being performed will not tr
163
190
  Working with unhashable arguments
164
191
  ---------------------------------
165
192
 
166
- 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.
193
+ 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.
167
194
 
168
195
  .. code-block:: python
169
196
 
170
- @cachier(hash_params=hash_my_custom_class)
197
+ def calculate_hash(args, kwds):
198
+ key = ... # compute a hash key here based on arguments
199
+ return key
200
+
201
+ @cachier(hash_func=calculate_hash)
171
202
  def calculate_super_complex_stuff(custom_obj):
172
203
  # amazing code goes here
173
204
 
@@ -176,6 +207,23 @@ See here for an example:
176
207
  `Question: How to work with unhashable arguments <https://github.com/python-cachier/cachier/issues/91>`_
177
208
 
178
209
 
210
+ Precaching values
211
+ ---------------------------------
212
+
213
+ If you want to load a value into the cache without calling the underlying function, this can be done with the `precache_value` function.
214
+
215
+ .. code-block:: python
216
+
217
+ @cachier()
218
+ def add(arg1, arg2):
219
+ return arg1 + arg2
220
+
221
+ add.precache_value(2, 2, value_to_cache=5)
222
+
223
+ result = add(2, 2)
224
+ print(result) # prints 5
225
+
226
+
179
227
  Per-function call arguments
180
228
  ---------------------------
181
229
 
@@ -365,7 +413,7 @@ Other major contributors:
365
413
 
366
414
  * `cthoyt <https://github.com/cthoyt>`_ - Base memory core implementation.
367
415
 
368
- * `amarczew <https://github.com/amarczew>`_ - The ``hash_params`` kwarg.
416
+ * `amarczew <https://github.com/amarczew>`_ - The ``hash_func`` kwarg.
369
417
 
370
418
  * `non-senses <https://github.com/non-senses>`_ - The ``wait_for_calc_timeout`` kwarg.
371
419
 
@@ -411,5 +459,3 @@ Notable bugfixers:
411
459
  .. _watchdog: https://github.com/gorakhargosh/watchdog
412
460
 
413
461
 
414
-
415
-
@@ -83,6 +83,33 @@ You can add a default, pickle-based, persistent cache to your function - meaning
83
83
  """Your function now has a persistent cache mapped by argument values!"""
84
84
  return {'arg1': arg1, 'arg2': arg2}
85
85
 
86
+ 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.**
87
+
88
+ .. code-block:: python
89
+
90
+ from cachier import cachier
91
+
92
+ class Foo():
93
+ @staticmethod
94
+ @cachier()
95
+ def good_static_usage(arg_1, arg_2):
96
+ return arg_1 + arg_2
97
+
98
+ # Instance method does not depend on object's internal state, so good to cache
99
+ @cachier()
100
+ def good_usage_1(self, arg_1, arg_2)
101
+ return arg_1 + arg_2
102
+
103
+ # Instance method is calling external service, probably okay to cache
104
+ @cachier()
105
+ def good_usage_2(self, arg_1, arg_2)
106
+ result = self.call_api(arg_1, arg_2)
107
+ return result
108
+
109
+ # Instance method relies on object attribute, NOT good to cache
110
+ @cachier()
111
+ def bad_usage(self, arg_1, arg_2)
112
+ return arg_1 + arg_2 + self.arg_3
86
113
 
87
114
 
88
115
  Resetting a Cache
@@ -93,7 +120,7 @@ The Cachier wrapper adds a ``clear_cache()`` function to each wrapped function.
93
120
 
94
121
  foo.clear_cache()
95
122
 
96
- Genereal Configuration
123
+ General Configuration
97
124
  ----------------------
98
125
 
99
126
  Threads Limit
@@ -135,11 +162,15 @@ Further function calls made while the calculation is being performed will not tr
135
162
  Working with unhashable arguments
136
163
  ---------------------------------
137
164
 
138
- 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.
165
+ 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.
139
166
 
140
167
  .. code-block:: python
141
168
 
142
- @cachier(hash_params=hash_my_custom_class)
169
+ def calculate_hash(args, kwds):
170
+ key = ... # compute a hash key here based on arguments
171
+ return key
172
+
173
+ @cachier(hash_func=calculate_hash)
143
174
  def calculate_super_complex_stuff(custom_obj):
144
175
  # amazing code goes here
145
176
 
@@ -148,6 +179,23 @@ See here for an example:
148
179
  `Question: How to work with unhashable arguments <https://github.com/python-cachier/cachier/issues/91>`_
149
180
 
150
181
 
182
+ Precaching values
183
+ ---------------------------------
184
+
185
+ If you want to load a value into the cache without calling the underlying function, this can be done with the `precache_value` function.
186
+
187
+ .. code-block:: python
188
+
189
+ @cachier()
190
+ def add(arg1, arg2):
191
+ return arg1 + arg2
192
+
193
+ add.precache_value(2, 2, value_to_cache=5)
194
+
195
+ result = add(2, 2)
196
+ print(result) # prints 5
197
+
198
+
151
199
  Per-function call arguments
152
200
  ---------------------------
153
201
 
@@ -337,7 +385,7 @@ Other major contributors:
337
385
 
338
386
  * `cthoyt <https://github.com/cthoyt>`_ - Base memory core implementation.
339
387
 
340
- * `amarczew <https://github.com/amarczew>`_ - The ``hash_params`` kwarg.
388
+ * `amarczew <https://github.com/amarczew>`_ - The ``hash_func`` kwarg.
341
389
 
342
390
  * `non-senses <https://github.com/non-senses>`_ - The ``wait_for_calc_timeout`` kwarg.
343
391
 
@@ -381,5 +429,3 @@ Notable bugfixers:
381
429
  .. links:
382
430
  .. _pymongo: https://api.mongodb.com/python/current/
383
431
  .. _watchdog: https://github.com/gorakhargosh/watchdog
384
-
385
-
@@ -11,8 +11,8 @@ version_json = '''
11
11
  {
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "15ce5f36320f15e8058baf29015fa27ea493d211",
15
- "version": "2.0.1"
14
+ "full-revisionid": "7be769b78acf3a0e0aff8d528476913571d79749",
15
+ "version": "2.1.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -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
- # pylint: disable-next=protected-access
14
- _default_hash_params = functools.partial(functools._make_key, typed=False)
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, stale_after, next_time, hash_params):
21
- self.stale_after = stale_after
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
- 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'
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,
@@ -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
@@ -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
 
@@ -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
@@ -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.1
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
@@ -111,6 +111,33 @@ You can add a default, pickle-based, persistent cache to your function - meaning
111
111
  """Your function now has a persistent cache mapped by argument values!"""
112
112
  return {'arg1': arg1, 'arg2': arg2}
113
113
 
114
+ 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.**
115
+
116
+ .. code-block:: python
117
+
118
+ from cachier import cachier
119
+
120
+ class Foo():
121
+ @staticmethod
122
+ @cachier()
123
+ def good_static_usage(arg_1, arg_2):
124
+ return arg_1 + arg_2
125
+
126
+ # Instance method does not depend on object's internal state, so good to cache
127
+ @cachier()
128
+ def good_usage_1(self, arg_1, arg_2)
129
+ return arg_1 + arg_2
130
+
131
+ # Instance method is calling external service, probably okay to cache
132
+ @cachier()
133
+ def good_usage_2(self, arg_1, arg_2)
134
+ result = self.call_api(arg_1, arg_2)
135
+ return result
136
+
137
+ # Instance method relies on object attribute, NOT good to cache
138
+ @cachier()
139
+ def bad_usage(self, arg_1, arg_2)
140
+ return arg_1 + arg_2 + self.arg_3
114
141
 
115
142
 
116
143
  Resetting a Cache
@@ -121,7 +148,7 @@ The Cachier wrapper adds a ``clear_cache()`` function to each wrapped function.
121
148
 
122
149
  foo.clear_cache()
123
150
 
124
- Genereal Configuration
151
+ General Configuration
125
152
  ----------------------
126
153
 
127
154
  Threads Limit
@@ -163,11 +190,15 @@ Further function calls made while the calculation is being performed will not tr
163
190
  Working with unhashable arguments
164
191
  ---------------------------------
165
192
 
166
- 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.
193
+ 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.
167
194
 
168
195
  .. code-block:: python
169
196
 
170
- @cachier(hash_params=hash_my_custom_class)
197
+ def calculate_hash(args, kwds):
198
+ key = ... # compute a hash key here based on arguments
199
+ return key
200
+
201
+ @cachier(hash_func=calculate_hash)
171
202
  def calculate_super_complex_stuff(custom_obj):
172
203
  # amazing code goes here
173
204
 
@@ -176,6 +207,23 @@ See here for an example:
176
207
  `Question: How to work with unhashable arguments <https://github.com/python-cachier/cachier/issues/91>`_
177
208
 
178
209
 
210
+ Precaching values
211
+ ---------------------------------
212
+
213
+ If you want to load a value into the cache without calling the underlying function, this can be done with the `precache_value` function.
214
+
215
+ .. code-block:: python
216
+
217
+ @cachier()
218
+ def add(arg1, arg2):
219
+ return arg1 + arg2
220
+
221
+ add.precache_value(2, 2, value_to_cache=5)
222
+
223
+ result = add(2, 2)
224
+ print(result) # prints 5
225
+
226
+
179
227
  Per-function call arguments
180
228
  ---------------------------
181
229
 
@@ -365,7 +413,7 @@ Other major contributors:
365
413
 
366
414
  * `cthoyt <https://github.com/cthoyt>`_ - Base memory core implementation.
367
415
 
368
- * `amarczew <https://github.com/amarczew>`_ - The ``hash_params`` kwarg.
416
+ * `amarczew <https://github.com/amarczew>`_ - The ``hash_func`` kwarg.
369
417
 
370
418
  * `non-senses <https://github.com/non-senses>`_ - The ``wait_for_calc_timeout`` kwarg.
371
419
 
@@ -411,5 +459,3 @@ Notable bugfixers:
411
459
  .. _watchdog: https://github.com/gorakhargosh/watchdog
412
460
 
413
461
 
414
-
415
-
@@ -33,10 +33,12 @@ cachier.egg-info/top_level.txt
33
33
  cachier/scripts/__init__.py
34
34
  cachier/scripts/cli.py
35
35
  tests/__init__.py
36
- tests/conftest.py
37
36
  tests/speed_eval.py
37
+ tests/standalone_script.py
38
38
  tests/test_core_lookup.py
39
39
  tests/test_general.py
40
40
  tests/test_memory_core.py
41
41
  tests/test_mongo_core.py
42
- tests/test_pickle_core.py
42
+ tests/test_pickle_core.py
43
+ tests/test_quality.py
44
+ tests/test_security.py
@@ -1,14 +1,19 @@
1
1
  watchdog
2
2
  portalocker
3
3
  pathtools
4
+ setuptools>=67.6.0
4
5
 
5
6
  [test]
6
7
  pytest
7
8
  coverage
8
9
  pytest-cov
9
- birch
10
+ bandit
11
+ flake8
12
+ pylint
13
+ safety
10
14
  pymongo
11
15
  dnspython
16
+ pymongo-inmemory
12
17
  pandas
13
18
  collective.checkdocs
14
19
  pygments
@@ -18,10 +18,10 @@ import versioneer
18
18
  TEST_REQUIRES = [
19
19
  # tests and coverages
20
20
  'pytest', 'coverage', 'pytest-cov',
21
- # for reading configfutation
22
- 'birch',
21
+ # linting and code quality
22
+ 'bandit', 'flake8', 'pylint', 'safety',
23
23
  # to connect to the test mongodb server
24
- 'pymongo', 'dnspython',
24
+ 'pymongo', 'dnspython', 'pymongo-inmemory',
25
25
  # to test pandas dataframe as-param hashing with mongodb core
26
26
  'pandas',
27
27
  # to be able to run `python setup.py checkdocs`
@@ -44,7 +44,7 @@ setup(
44
44
  author='Shay Palachy',
45
45
  author_email='shay.palachy@gmail.com',
46
46
  url='https://github.com/python-cachier/cachier',
47
- packages=['cachier'],
47
+ packages=['cachier', 'cachier.scripts'],
48
48
  entry_points='''
49
49
  [console_scripts]
50
50
  cachier=cachier.scripts.cli:cli
@@ -52,6 +52,7 @@ setup(
52
52
  install_requires=[
53
53
  'watchdog', 'portalocker',
54
54
  'pathtools', # for watchdog, who has dependency spec problem
55
+ 'setuptools>=67.6.0', # to avoid vulnerability in 56.0.0
55
56
  ],
56
57
  extras_require={
57
58
  'test': TEST_REQUIRES,
@@ -18,7 +18,7 @@ def _test_int_pickling_compare(int_1, int_2):
18
18
  def test_pickle_speed():
19
19
  """Test speeds"""
20
20
  print("Comparing speeds of decorated vs non-decorated functions...")
21
- num_of_vals = 100
21
+ num_of_vals = 1000
22
22
  times = []
23
23
  for i in range(1, num_of_vals):
24
24
  tic = time()
@@ -0,0 +1,11 @@
1
+ import cachier
2
+ import time
3
+
4
+
5
+ @cachier.cachier()
6
+ def _takes_3_seconds(label, value):
7
+ time.sleep(3)
8
+ return f'{label} {value}'
9
+
10
+
11
+ print(_takes_3_seconds('two', 2))