Skip to content

Commit 37dcfb5

Browse files
committed
Re-format code using black and introduce poetry for setup.
1 parent 32ded86 commit 37dcfb5

26 files changed

+312
-248
lines changed

.editorconfig

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
indent_style = tab
6+
end_of_line = lf
7+
trim_trailing_whitespace = true
8+
insert_final_newline = true
9+
10+
[*.py]
11+
indent_style = space
12+
indent_size = 4
13+
multi_line_output = 3
14+
lines_after_imports = 2
15+
include_trailing_comma = True
16+
ensure_newline_before_comments = True
17+
force_grid_wrap = 0
18+
use_parentheses = True
19+
line_length = 96
20+
21+
[*.rst]
22+
indent_style = space
23+
indent_size = 4

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ docs/_build
44
dist/*
55
build/*
66
.coverage
7+
htmlcov/
78
TODO
89
.tox
910
__pycache__
1011
*.egg-info
1112
.pytest_cache
13+
*.lock

AUTHORS

-4
This file was deleted.

MANIFEST.in

-1
This file was deleted.

Makefile

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.PHONY: correct tests
2+
3+
correct:
4+
poetry run isort cacheback tests
5+
poetry run black -q cacheback tests
6+
7+
pytests:
8+
@PYTHONPATH=$(CURDIR):${PYTHONPATH} poetry run pytest
9+
10+
tests:
11+
@PYTHONPATH=$(CURDIR):${PYTHONPATH} poetry run pytest --cov --isort --flake8 --black
12+
13+
coverage-html: tests
14+
poetry run coverage html

cacheback/__init__.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1-
__version__ = '2.1.0'
1+
try:
2+
import importlib.metadata as importlib_metadata
3+
except ModuleNotFoundError:
4+
# This is required for Python versions < 3.8
5+
import importlib_metadata
6+
7+
try:
8+
__version__ = importlib_metadata.version('django-cacheback')
9+
except Exception:
10+
__version__ = 'HEAD'
211

312
default_app_config = 'cacheback.apps.CachebackConfig'

cacheback/apps.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from django.apps import AppConfig
2-
from django.utils.translation import ugettext_lazy as _
2+
from django.utils.translation import gettext_lazy as _
33

44

55
class CachebackConfig(AppConfig):

cacheback/base.py

+78-50
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class Job(metaclass=JobBase):
5555
This is the core class for the package which is intended to be subclassed
5656
to allow the caching behaviour to be customised.
5757
"""
58+
5859
# All items are stored in memcache as a tuple (expiry, data). We don't use
5960
# the TTL functionality within memcache but implement on own. If the
6061
# expiry value is None, this indicates that there is already a job created
@@ -104,8 +105,9 @@ def class_path(self):
104105
return '%s.%s' % (self.__module__, self.__class__.__name__)
105106

106107
def __init__(self):
107-
self.cache_alias = (self.cache_alias or
108-
getattr(settings, 'CACHEBACK_CACHE_ALIAS', DEFAULT_CACHE_ALIAS))
108+
self.cache_alias = self.cache_alias or getattr(
109+
settings, 'CACHEBACK_CACHE_ALIAS', DEFAULT_CACHE_ALIAS
110+
)
109111
self.cache = caches[self.cache_alias]
110112
self.task_options = self.task_options or {}
111113

@@ -150,17 +152,25 @@ def get(self, *raw_args, **raw_kwargs):
150152
# the fetch has finished, or
151153
# b) trigger an async refresh and return an empty result
152154
if self.should_missing_item_be_fetched_synchronously(*args, **kwargs):
153-
logger.debug(("Job %s with key '%s' - cache MISS - running "
154-
"synchronous refresh"),
155-
self.class_path, key)
155+
logger.debug(
156+
("Job %s with key '%s' - cache MISS - running " "synchronous refresh"),
157+
self.class_path,
158+
key,
159+
)
156160
result = self.refresh(*args, **kwargs)
157161
return self.process_result(
158-
result, call=call, cache_status=self.MISS, sync_fetch=True)
162+
result, call=call, cache_status=self.MISS, sync_fetch=True
163+
)
159164

160165
else:
161-
logger.debug(("Job %s with key '%s' - cache MISS - triggering "
162-
"async refresh and returning empty result"),
163-
self.class_path, key)
166+
logger.debug(
167+
(
168+
"Job %s with key '%s' - cache MISS - triggering "
169+
"async refresh and returning empty result"
170+
),
171+
self.class_path,
172+
key,
173+
)
164174
# To avoid cache hammering (ie lots of identical tasks
165175
# to refresh the same cache item), we reset the cache with an
166176
# empty result which will be returned until the cache is
@@ -169,8 +179,8 @@ def get(self, *raw_args, **raw_kwargs):
169179
self.store(key, self.timeout(*args, **kwargs), result)
170180
self.async_refresh(*args, **kwargs)
171181
return self.process_result(
172-
result, call=call, cache_status=self.MISS,
173-
sync_fetch=False)
182+
result, call=call, cache_status=self.MISS, sync_fetch=False
183+
)
174184

175185
expiry, data = item
176186
delta = time.time() - expiry
@@ -180,30 +190,35 @@ def get(self, *raw_args, **raw_kwargs):
180190
# the fetch has finished, or
181191
# b) trigger a refresh but allow the stale result to be
182192
# returned this time. This is normally acceptable.
183-
if self.should_stale_item_be_fetched_synchronously(
184-
delta, *args, **kwargs):
193+
if self.should_stale_item_be_fetched_synchronously(delta, *args, **kwargs):
185194
logger.debug(
186-
("Job %s with key '%s' - STALE cache hit - running "
187-
"synchronous refresh"),
188-
self.class_path, key)
195+
("Job %s with key '%s' - STALE cache hit - running " "synchronous refresh"),
196+
self.class_path,
197+
key,
198+
)
189199
result = self.refresh(*args, **kwargs)
190200
return self.process_result(
191-
result, call=call, cache_status=self.STALE,
192-
sync_fetch=True)
201+
result, call=call, cache_status=self.STALE, sync_fetch=True
202+
)
193203

194204
else:
195205
logger.debug(
196-
("Job %s with key '%s' - STALE cache hit - triggering "
197-
"async refresh and returning stale result"),
198-
self.class_path, key)
206+
(
207+
"Job %s with key '%s' - STALE cache hit - triggering "
208+
"async refresh and returning stale result"
209+
),
210+
self.class_path,
211+
key,
212+
)
199213
# We replace the item in the cache with a 'timeout' expiry - this
200214
# prevents cache hammering but guards against a 'limbo' situation
201215
# where the refresh task fails for some reason.
202216
timeout = self.timeout(*args, **kwargs)
203217
self.store(key, timeout, data)
204218
self.async_refresh(*args, **kwargs)
205219
return self.process_result(
206-
data, call=call, cache_status=self.STALE, sync_fetch=False)
220+
data, call=call, cache_status=self.STALE, sync_fetch=False
221+
)
207222
else:
208223
logger.debug("Job %s with key '%s' - cache HIT", self.class_path, key)
209224
return self.process_result(data, call=call, cache_status=self.HIT)
@@ -261,8 +276,14 @@ def set(self, *raw_args, **raw_kwargs):
261276

262277
expiry = self.expiry(*args, **kwargs)
263278

264-
logger.debug("Setting %s cache with key '%s', args '%r', kwargs '%r', expiry '%r'",
265-
self.class_path, key, args, kwargs, expiry)
279+
logger.debug(
280+
"Setting %s cache with key '%s', args '%r', kwargs '%r', expiry '%r'",
281+
self.class_path,
282+
key,
283+
args,
284+
kwargs,
285+
expiry,
286+
)
266287

267288
self.store(key, expiry, data)
268289

@@ -292,9 +313,7 @@ def store(self, key, expiry, data):
292313
# without warning.
293314
__, cached_data = self.cache.get(key, (None, None))
294315
if data is not None and cached_data is None:
295-
raise RuntimeError(
296-
"Unable to save data of type %s to cache" % (
297-
type(data)))
316+
raise RuntimeError("Unable to save data of type %s to cache" % (type(data)))
298317

299318
def refresh(self, *args, **kwargs):
300319
"""
@@ -319,22 +338,24 @@ def async_refresh(self, *args, **kwargs):
319338
obj_args=self.get_init_args(),
320339
obj_kwargs=self.get_init_kwargs(),
321340
call_args=args,
322-
call_kwargs=kwargs
341+
call_kwargs=kwargs,
323342
),
324-
task_options=self.task_options
343+
task_options=self.task_options,
325344
)
326345
except Exception:
327346
# Handle exceptions from talking to RabbitMQ - eg connection
328347
# refused. When this happens, we try to run the task
329348
# synchronously.
330-
logger.error("Unable to trigger task asynchronously - failing "
331-
"over to synchronous refresh", exc_info=True)
349+
logger.error(
350+
"Unable to trigger task asynchronously - failing "
351+
"over to synchronous refresh",
352+
exc_info=True,
353+
)
332354
try:
333355
return self.refresh(*args, **kwargs)
334356
except Exception as e:
335357
# Something went wrong while running the task
336-
logger.error("Unable to refresh data synchronously: %s", e,
337-
exc_info=True)
358+
logger.error("Unable to refresh data synchronously: %s", e, exc_info=True)
338359
else:
339360
logger.debug("Failover synchronous refresh completed successfully")
340361

@@ -368,13 +389,14 @@ def should_missing_item_be_fetched_synchronously(self, *args, **kwargs):
368389

369390
def should_item_be_fetched_synchronously(self, *args, **kwargs):
370391
import warnings
392+
371393
warnings.warn(
372394
"The method 'should_item_be_fetched_synchronously' is deprecated "
373395
"and will be removed in 0.5. Use "
374396
"'should_missing_item_be_fetched_synchronously' instead.",
375-
DeprecationWarning)
376-
return self.should_missing_item_be_fetched_synchronously(
377-
*args, **kwargs)
397+
DeprecationWarning,
398+
)
399+
return self.should_missing_item_be_fetched_synchronously(*args, **kwargs)
378400

379401
def should_stale_item_be_fetched_synchronously(self, delta, *args, **kwargs):
380402
"""
@@ -400,15 +422,18 @@ def key(self, *args, **kwargs):
400422
# The line might break if your passed values are un-hashable. If
401423
# it does, you need to override this method and implement your own
402424
# key algorithm.
403-
return "%s:%s:%s:%s" % (self.class_path,
404-
self.hash(args),
405-
self.hash([k for k in sorted(kwargs)]),
406-
self.hash([kwargs[k] for k in sorted(kwargs)]))
425+
return "%s:%s:%s:%s" % (
426+
self.class_path,
427+
self.hash(args),
428+
self.hash([k for k in sorted(kwargs)]),
429+
self.hash([kwargs[k] for k in sorted(kwargs)]),
430+
)
407431
except TypeError:
408432
raise RuntimeError(
409433
"Unable to generate cache key due to unhashable"
410434
"args or kwargs - you need to implement your own"
411-
"key generation method to avoid this problem")
435+
"key generation method to avoid this problem"
436+
)
412437

413438
def hash(self, value):
414439
"""
@@ -450,7 +475,7 @@ def process_result(self, result, call, cache_status, sync_fetch=None):
450475
def job_refresh(cls, *args, **kwargs):
451476
warnings.warn(
452477
'`Job.job_refresh` is deprecated, use `perform_async_refresh` instead.',
453-
RemovedInCacheback13Warning
478+
RemovedInCacheback13Warning,
454479
)
455480
return cls.perform_async_refresh(*args, **kwargs)
456481

@@ -473,18 +498,21 @@ def perform_async_refresh(cls, klass_str, obj_args, obj_kwargs, call_args, call_
473498
"""
474499
klass = get_job_class(klass_str)
475500
if klass is None:
476-
logger.error("Unable to construct %s with args %r and kwargs %r",
477-
klass_str, obj_args, obj_kwargs)
501+
logger.error(
502+
"Unable to construct %s with args %r and kwargs %r",
503+
klass_str,
504+
obj_args,
505+
obj_kwargs,
506+
)
478507
return
479508

480-
logger.info("Using %s with constructor args %r and kwargs %r",
481-
klass_str, obj_args, obj_kwargs)
482-
logger.info("Calling refresh with args %r and kwargs %r", call_args,
483-
call_kwargs)
509+
logger.info(
510+
"Using %s with constructor args %r and kwargs %r", klass_str, obj_args, obj_kwargs
511+
)
512+
logger.info("Calling refresh with args %r and kwargs %r", call_args, call_kwargs)
484513
start = time.time()
485514
try:
486-
klass(*obj_args, **obj_kwargs).refresh(
487-
*call_args, **call_kwargs)
515+
klass(*obj_args, **obj_kwargs).refresh(*call_args, **call_kwargs)
488516
except Exception as e:
489517
logger.exception("Error running job: '%s'", e)
490518
else:

cacheback/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
warnings.warn(
88
'`cacheback.conf.CachebackConfig` is deprecated, '
99
'use `cacheback.apps.CachebackConfig` instead.',
10-
RemovedInCacheback13Warning
10+
RemovedInCacheback13Warning,
1111
)

cacheback/decorators.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33
from .jobs import FunctionJob
44

55

6-
def cacheback(lifetime=None, fetch_on_miss=None, cache_alias=None,
7-
job_class=None, task_options=None, **job_class_kwargs):
6+
def cacheback(
7+
lifetime=None,
8+
fetch_on_miss=None,
9+
cache_alias=None,
10+
job_class=None,
11+
task_options=None,
12+
**job_class_kwargs
13+
):
814
"""
915
Decorate function to cache its return value.
1016
@@ -19,15 +25,20 @@ def cacheback(lifetime=None, fetch_on_miss=None, cache_alias=None,
1925
"""
2026
if job_class is None:
2127
job_class = FunctionJob
22-
job = job_class(lifetime=lifetime, fetch_on_miss=fetch_on_miss,
23-
cache_alias=cache_alias, task_options=task_options,
24-
**job_class_kwargs)
28+
job = job_class(
29+
lifetime=lifetime,
30+
fetch_on_miss=fetch_on_miss,
31+
cache_alias=cache_alias,
32+
task_options=task_options,
33+
**job_class_kwargs
34+
)
2535

2636
def _wrapper(fn):
2737
# using available_attrs to work around http://bugs.python.org/issue3445
2838
@wraps(fn, assigned=WRAPPER_ASSIGNMENTS)
2939
def __wrapper(*args, **kwargs):
3040
return job.get(fn, *args, **kwargs)
41+
3142
# Assign reference to unwrapped function so that we can access it
3243
# later without descending into infinite regress.
3344
__wrapper.fn = fn

cacheback/function.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66

77
warnings.warn(
88
'`cacheback.function` is deprecated, use `cacheback.jobs` instead.',
9-
RemovedInCacheback13Warning
9+
RemovedInCacheback13Warning,
1010
)

0 commit comments

Comments
 (0)