mirror of
https://github.com/Hopiu/django-cachalot.git
synced 2026-03-16 21:30:23 +00:00
Use f-strings
This commit is contained in:
parent
70e970e46f
commit
431be11fc8
10 changed files with 46 additions and 79 deletions
|
|
@ -45,7 +45,7 @@ def _get_tables(tables_or_models):
|
|||
else table_or_model._meta.db_table)
|
||||
|
||||
|
||||
def invalidate(*tables_or_models, **kwargs):
|
||||
def invalidate(*tables_or_models, cache_alias=None, db_alias=None) -> None:
|
||||
"""
|
||||
Clears what was cached by django-cachalot implying one or more SQL tables
|
||||
or models from ``tables_or_models``.
|
||||
|
|
@ -68,13 +68,6 @@ def invalidate(*tables_or_models, **kwargs):
|
|||
:returns: Nothing
|
||||
:rtype: NoneType
|
||||
"""
|
||||
# TODO: Replace with positional arguments when we drop Python 2 support.
|
||||
cache_alias = kwargs.pop('cache_alias', None)
|
||||
db_alias = kwargs.pop('db_alias', None)
|
||||
for k in kwargs:
|
||||
raise TypeError(
|
||||
"invalidate() got an unexpected keyword argument '%s'" % k)
|
||||
|
||||
send_signal = False
|
||||
invalidated = set()
|
||||
for cache_alias, db_alias, tables in _cache_db_tables_iterator(
|
||||
|
|
@ -90,7 +83,7 @@ def invalidate(*tables_or_models, **kwargs):
|
|||
post_invalidation.send(table, db_alias=db_alias)
|
||||
|
||||
|
||||
def get_last_invalidation(*tables_or_models, **kwargs):
|
||||
def get_last_invalidation(*tables_or_models, cache_alias=None, db_alias=None):
|
||||
"""
|
||||
Returns the timestamp of the most recent invalidation of the given
|
||||
``tables_or_models``. If ``tables_or_models`` is not specified,
|
||||
|
|
@ -112,13 +105,6 @@ def get_last_invalidation(*tables_or_models, **kwargs):
|
|||
:returns: The timestamp of the most recent invalidation
|
||||
:rtype: float
|
||||
"""
|
||||
# TODO: Replace with positional arguments when we drop Python 2 support.
|
||||
cache_alias = kwargs.pop('cache_alias', None)
|
||||
db_alias = kwargs.pop('db_alias', None)
|
||||
for k in kwargs:
|
||||
raise TypeError("get_last_invalidation() got an unexpected "
|
||||
"keyword argument '%s'" % k)
|
||||
|
||||
last_invalidation = 0.0
|
||||
for cache_alias, db_alias, tables in _cache_db_tables_iterator(
|
||||
list(_get_tables(tables_or_models)), cache_alias, db_alias):
|
||||
|
|
@ -160,7 +146,9 @@ def cachalot_disabled(all_queries=False):
|
|||
:arg all_queries: Any query, including already evaluated queries, are re-evaluated.
|
||||
:type all_queries: bool
|
||||
"""
|
||||
was_enabled = getattr(LOCAL_STORAGE, "cachalot_enabled", cachalot_settings.CACHALOT_ENABLED)
|
||||
was_enabled = getattr(
|
||||
LOCAL_STORAGE, "cachalot_enabled", cachalot_settings.CACHALOT_ENABLED
|
||||
)
|
||||
LOCAL_STORAGE.cachalot_enabled = False
|
||||
LOCAL_STORAGE.disable_on_all = all_queries
|
||||
yield
|
||||
|
|
|
|||
|
|
@ -15,10 +15,8 @@ def check_cache_compatibility(app_configs, **kwargs):
|
|||
cache_backend = cache['BACKEND']
|
||||
if cache_backend not in SUPPORTED_CACHE_BACKENDS:
|
||||
return [Warning(
|
||||
'Cache backend %r is not supported by django-cachalot.'
|
||||
% cache_backend,
|
||||
hint='Switch to a supported cache backend '
|
||||
'like Redis or Memcached.',
|
||||
f'Cache backend {cache_backend} is not supported by django-cachalot.',
|
||||
hint='Switch to a supported cache backend like Redis or Memcached.',
|
||||
id='cachalot.W001')]
|
||||
return []
|
||||
|
||||
|
|
@ -27,14 +25,12 @@ def check_cache_compatibility(app_configs, **kwargs):
|
|||
def check_databases_compatibility(app_configs, **kwargs):
|
||||
errors = []
|
||||
databases = settings.DATABASES
|
||||
original_enabled_databases = getattr(settings, 'CACHALOT_DATABASES',
|
||||
SUPPORTED_ONLY)
|
||||
original_enabled_databases = getattr(settings, 'CACHALOT_DATABASES', SUPPORTED_ONLY)
|
||||
enabled_databases = cachalot_settings.CACHALOT_DATABASES
|
||||
if original_enabled_databases == SUPPORTED_ONLY:
|
||||
if not cachalot_settings.CACHALOT_DATABASES:
|
||||
errors.append(Warning(
|
||||
'None of the configured databases are supported '
|
||||
'by django-cachalot.',
|
||||
'None of the configured databases are supported by django-cachalot.',
|
||||
hint='Use a supported database, or remove django-cachalot, or '
|
||||
'put at least one database alias in `CACHALOT_DATABASES` '
|
||||
'to force django-cachalot to use it.',
|
||||
|
|
@ -46,15 +42,15 @@ def check_databases_compatibility(app_configs, **kwargs):
|
|||
engine = databases[db_alias]['ENGINE']
|
||||
if engine not in SUPPORTED_DATABASE_ENGINES:
|
||||
errors.append(Warning(
|
||||
'Database engine %r is not supported '
|
||||
'by django-cachalot.' % engine,
|
||||
f'Database engine {engine} is not supported '
|
||||
'by django-cachalot.',
|
||||
hint='Switch to a supported database engine.',
|
||||
id='cachalot.W003'
|
||||
id='cachalot.W003',
|
||||
))
|
||||
else:
|
||||
errors.append(Error(
|
||||
'Database alias %r from `CACHALOT_DATABASES` '
|
||||
'is not defined in `DATABASES`.' % db_alias,
|
||||
f'Database alias {db_alias} from `CACHALOT_DATABASES` '
|
||||
'is not defined in `DATABASES`.',
|
||||
hint='Change `CACHALOT_DATABASES` to be compliant with'
|
||||
'`CACHALOT_DATABASES`',
|
||||
id='cachalot.E001',
|
||||
|
|
@ -69,8 +65,8 @@ def check_databases_compatibility(app_configs, **kwargs):
|
|||
))
|
||||
else:
|
||||
errors.append(Error(
|
||||
"`CACHALOT_DATABASES` must be either %r or a list, tuple, "
|
||||
"frozenset or set of database aliases." % SUPPORTED_ONLY,
|
||||
f"`CACHALOT_DATABASES` must be either {SUPPORTED_ONLY} or a list, tuple, "
|
||||
"frozenset or set of database aliases.",
|
||||
hint='Remove `CACHALOT_DATABASES` or change it.',
|
||||
id='cachalot.E002',
|
||||
))
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ class CachalotExtension(Extension):
|
|||
|
||||
def __init__(self, environment):
|
||||
super(CachalotExtension, self).__init__(environment)
|
||||
|
||||
self.environment.globals.update(
|
||||
get_last_invalidation=get_last_invalidation)
|
||||
self.environment.globals.update(get_last_invalidation=get_last_invalidation)
|
||||
|
||||
def parse_args(self, parser):
|
||||
args = []
|
||||
|
|
@ -23,14 +21,11 @@ class CachalotExtension(Extension):
|
|||
stream = parser.stream
|
||||
|
||||
while stream.current.type != 'block_end':
|
||||
if stream.current.type == 'name' \
|
||||
and stream.look().type == 'assign':
|
||||
if stream.current.type == 'name' and stream.look().type == 'assign':
|
||||
key = stream.current.value
|
||||
if key not in self.allowed_kwargs:
|
||||
parser.fail(
|
||||
"'%s' is not a valid keyword argument "
|
||||
"for {%% cache %%}" % key,
|
||||
stream.current.lineno)
|
||||
parser.fail(f"'{key}' is not a valid keyword argument "
|
||||
"for {%% cache %%}", stream.current.lineno)
|
||||
stream.skip(2)
|
||||
value = parser.parse_expression()
|
||||
kwargs.append(Keyword(key, value, lineno=value.lineno))
|
||||
|
|
@ -49,7 +44,7 @@ class CachalotExtension(Extension):
|
|||
lineno = next(parser.stream).lineno
|
||||
args, kwargs = self.parse_args(parser)
|
||||
default_cache_key = (None if parser.filename is None
|
||||
else '%s:%d' % (parser.filename, lineno))
|
||||
else f'{parser.filename}:{lineno}')
|
||||
kwargs.append(Keyword('default_cache_key', Const(default_cache_key),
|
||||
lineno=lineno))
|
||||
body = parser.parse_statements(['name:end' + tag], drop_needle=True)
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ class Command(BaseCommand):
|
|||
model_name = label.split('.')[-1]
|
||||
models.append(apps.get_model(app_label, model_name))
|
||||
|
||||
cache_str = '' if cache_alias is None else "on cache '%s'" % cache_alias
|
||||
db_str = '' if db_alias is None else "for database '%s'" % db_alias
|
||||
keys_str = 'keys for %s models' % len(models) if labels else 'all keys'
|
||||
cache_str = '' if cache_alias is None else f"on cache '{cache_alias}'"
|
||||
db_str = '' if db_alias is None else f"for database '{db_alias}'"
|
||||
keys_str = f'keys for {len(models)} models' if labels else 'all keys'
|
||||
|
||||
if verbosity > 0:
|
||||
self.stdout.write(' '.join(filter(bool, ['Invalidating', keys_str,
|
||||
|
|
|
|||
|
|
@ -49,8 +49,7 @@ class CachalotPanel(Panel):
|
|||
model_cache_keys = {
|
||||
get_table_cache_key(db_alias, model._meta.db_table): model
|
||||
for model in models}
|
||||
for cache_key, timestamp in cache.get_many(
|
||||
model_cache_keys.keys()).items():
|
||||
for cache_key, timestamp in cache.get_many(model_cache_keys.keys()).items():
|
||||
invalidation = datetime.fromtimestamp(timestamp)
|
||||
model = model_cache_keys[cache_key]
|
||||
data[db_alias].append(
|
||||
|
|
@ -64,6 +63,5 @@ class CachalotPanel(Panel):
|
|||
@property
|
||||
def nav_subtitle(self):
|
||||
if self.enabled and self.last_invalidation is not None:
|
||||
return (_('Last invalidation: %s')
|
||||
% timesince(self.last_invalidation))
|
||||
return _('Last invalidation: %s') % timesince(self.last_invalidation)
|
||||
return ''
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ class DebugToolbarTestCase(LiveServerTestCase):
|
|||
UUID(store_id)
|
||||
render_panel_url = toolbar.attrs['data-render-panel-url']
|
||||
panel_id = soup.find(title='Cachalot')['class'][0]
|
||||
panel_url = ('%s?store_id=%s&panel_id=%s'
|
||||
% (render_panel_url, store_id, panel_id))
|
||||
panel_url = f'{render_panel_url}?store_id={store_id}&panel_id={panel_id}'
|
||||
|
||||
#
|
||||
# Rendering panel
|
||||
|
|
|
|||
|
|
@ -697,24 +697,22 @@ class ReadTestCase(TestUtilsMixin, TransactionTestCase):
|
|||
r'(?:None )?ALL None None None None 2 100\.0 Using filesort')
|
||||
else:
|
||||
expected = (
|
||||
r'-> Sort row IDs: cachalot_test.`name` \(cost=[\d\.]+ rows=\d\)\n '
|
||||
r'-> Table scan on cachalot_test \(cost=[\d\.]+ rows=\d\)\n'
|
||||
r'-> Sort row IDs: cachalot_test.`name` \(cost=[\d\.]+ rows=\d\)\n'
|
||||
r' -> Table scan on cachalot_test \(cost=[\d\.]+ rows=\d\)\n'
|
||||
)
|
||||
else:
|
||||
explain_kwargs.update(
|
||||
analyze=True,
|
||||
costs=False,
|
||||
)
|
||||
operation_detail = (r'\(actual time=[\d\.]+..[\d\.]+\ '
|
||||
r'rows=\d+ loops=\d+\)')
|
||||
operation_detail = r'\(actual time=[\d\.]+..[\d\.]+\ rows=\d+ loops=\d+\)'
|
||||
expected = (
|
||||
r'^Sort %s\n'
|
||||
rf'^Sort {operation_detail}\n'
|
||||
r' Sort Key: name\n'
|
||||
r' Sort Method: quicksort Memory: \d+kB\n'
|
||||
r' -> Seq Scan on cachalot_test %s\n'
|
||||
rf' -> Seq Scan on cachalot_test {operation_detail}\n'
|
||||
r'Planning Time: [\d\.]+ ms\n'
|
||||
r'Execution Time: [\d\.]+ ms$') % (operation_detail,
|
||||
operation_detail)
|
||||
r'Execution Time: [\d\.]+ ms$')
|
||||
with self.assertNumQueries(
|
||||
2 if self.is_mysql and django_version[0] < 3
|
||||
else 1):
|
||||
|
|
@ -729,7 +727,7 @@ class ReadTestCase(TestUtilsMixin, TransactionTestCase):
|
|||
Tests if ``Model.objects.raw`` queries are not cached.
|
||||
"""
|
||||
|
||||
sql = 'SELECT * FROM %s;' % Test._meta.db_table
|
||||
sql = f'SELECT * FROM {Test._meta.db_table};'
|
||||
|
||||
with self.assertNumQueries(1):
|
||||
data1 = list(Test.objects.raw(sql))
|
||||
|
|
@ -752,13 +750,10 @@ class ReadTestCase(TestUtilsMixin, TransactionTestCase):
|
|||
"""
|
||||
Tests if queries executed from a DB cursor are not cached.
|
||||
"""
|
||||
|
||||
attname_column_list = [f.get_attname_column()
|
||||
for f in Test._meta.fields]
|
||||
attname_column_list = [f.get_attname_column() for f in Test._meta.fields]
|
||||
attnames = [t[0] for t in attname_column_list]
|
||||
columns = [t[1] for t in attname_column_list]
|
||||
sql = "SELECT CAST('é' AS CHAR), %s FROM %s;" % (', '.join(columns),
|
||||
Test._meta.db_table)
|
||||
sql = f"SELECT CAST('é' AS CHAR), {', '.join(columns)} FROM {Test._meta.db_table};"
|
||||
|
||||
with self.assertNumQueries(1):
|
||||
with connection.cursor() as cursor:
|
||||
|
|
@ -780,9 +775,8 @@ class ReadTestCase(TestUtilsMixin, TransactionTestCase):
|
|||
for f in Test._meta.fields]
|
||||
attnames = [t[0] for t in attname_column_list]
|
||||
columns = [t[1] for t in attname_column_list]
|
||||
sql = "SELECT CAST('é' AS CHAR), %s FROM %s;" % (', '.join(columns),
|
||||
Test._meta.db_table)
|
||||
sql = sql.encode('utf-8')
|
||||
sql = f"SELECT CAST('é' AS CHAR), {', '.join(columns)} " \
|
||||
f"FROM {Test._meta.db_table};".encode('utf-8')
|
||||
|
||||
with self.assertNumQueries(1):
|
||||
with connection.cursor() as cursor:
|
||||
|
|
@ -855,18 +849,18 @@ class ReadTestCase(TestUtilsMixin, TransactionTestCase):
|
|||
"""
|
||||
table_name = 'Clémentine'
|
||||
if self.is_postgresql:
|
||||
table_name = '"%s"' % table_name
|
||||
table_name = f'"{table_name}"'
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute('CREATE TABLE %s (taste VARCHAR(20));' % table_name)
|
||||
cursor.execute(f'CREATE TABLE {table_name} (taste VARCHAR(20));')
|
||||
qs = Test.objects.extra(tables=['Clémentine'],
|
||||
select={'taste': '%s.taste' % table_name})
|
||||
select={'taste': f'{table_name}.taste'})
|
||||
# Here the table `Clémentine` is not detected because it is not
|
||||
# registered by Django, and we only check for registered tables
|
||||
# to avoid creating an extra SQL query fetching table names.
|
||||
self.assert_tables(qs, Test)
|
||||
self.assert_query_cached(qs)
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute('DROP TABLE %s;' % table_name)
|
||||
cursor.execute(f'DROP TABLE {table_name};')
|
||||
|
||||
def test_unmanaged_model(self):
|
||||
qs = UnmanagedModel.objects.all()
|
||||
|
|
|
|||
|
|
@ -125,8 +125,7 @@ class SettingsTestCase(TestUtilsMixin, TransactionTestCase):
|
|||
with self.settings(CACHALOT_INVALIDATE_RAW=False):
|
||||
with self.assertNumQueries(1):
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("UPDATE %s SET name = 'new name';"
|
||||
% Test._meta.db_table)
|
||||
cursor.execute(f"UPDATE {Test._meta.db_table} SET name = 'new name';")
|
||||
with self.assertNumQueries(0):
|
||||
list(Test.objects.all())
|
||||
|
||||
|
|
|
|||
|
|
@ -161,8 +161,7 @@ class WriteTestCase(TestUtilsMixin, TransactionTestCase):
|
|||
self.assertListEqual(data2, [t1.name])
|
||||
|
||||
with self.assertNumQueries(2 if self.is_sqlite else 1):
|
||||
Test.objects.bulk_create([Test(name='test%s' % i)
|
||||
for i in range(2, 11)])
|
||||
Test.objects.bulk_create([Test(name=f'test{i}') for i in range(2, 11)])
|
||||
with self.assertNumQueries(1):
|
||||
self.assertEqual(Test.objects.count(), 10)
|
||||
with self.assertNumQueries(2 if self.is_sqlite else 1):
|
||||
|
|
|
|||
|
|
@ -75,8 +75,7 @@ def get_query_cache_key(compiler):
|
|||
"""
|
||||
sql, params = compiler.as_sql()
|
||||
check_parameter_types(params)
|
||||
cache_key = '%s:%s:%s' % (compiler.using, sql,
|
||||
[str(p) for p in params])
|
||||
cache_key = f'{compiler.using}:{sql}:{[str(p) for p in params]}'
|
||||
return sha1(cache_key.encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
|
|
@ -91,7 +90,7 @@ def get_table_cache_key(db_alias, table):
|
|||
:return: A cache key
|
||||
:rtype: int
|
||||
"""
|
||||
cache_key = '%s:%s' % (db_alias, table)
|
||||
cache_key = f'{db_alias}:{table}'
|
||||
return sha1(cache_key.encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue