Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Django 4.1-4.2 and Python 3.11 support #229

Merged
merged 8 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,27 @@ on:
pull_request:
branches: [ master ]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10']
django-version: ['2.2', '3.2', '4.0', '4.1']
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
django-version: ['3.2', '4.1', '4.2']
exclude:
- python-version: '3.10'
django-version: '2.2'
- python-version: '3.7'
django-version: '4.0'
- python-version: '3.11'
django-version: '3.2'
- python-version: '3.11'
django-version: '4.1'
- python-version: '3.7'
django-version: '4.1'
- python-version: '3.7'
django-version: '4.2'
services:
redis:
image: redis:6
Expand Down Expand Up @@ -71,7 +77,7 @@ jobs:
${{ matrix.python-version }}-v1-
- name: Install dependencies
run: |
sudo apt-get install -y libmemcached-dev zlib1g-dev
sudo apt-get install -y libmemcached-dev zlib1g-dev libpq-dev
python -m pip install --upgrade pip wheel
python -m pip install tox tox-gh-actions coveralls
- name: Tox Test
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.9']
python-version: ['3.10']

services:
redis:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ coverage.xml
# Django stuff:
*.log
local_settings.py
*.sqlite3
db.sqlite3
db.sqlite3-journal

Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
What’s new in django-cachalot?
==============================

2.6.0
-----

- Dropped Django 2.2 and 4.0 support
- Added Django 4.2 and Python 3.11 support
- Added psycopg support (#229)
- Updated tests to account for the `BEGIN` and `COMMIT` query changes in Django 4.2
- Standardized django version comparisons in tests

2.5.3
-----

- Verify get_meta isn't none before requesting db_table (#225 #226)

2.5.2
-----

- Added Django 4.1 support (#217)

2.5.1
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Table of Contents:
Quickstart
----------

Cachalot officially supports Python 3.7-3.10 and Django 2.2, 3.2, and 4.0-4.1 with the databases PostgreSQL, SQLite, and MySQL.
Cachalot officially supports Python 3.7-3.11 and Django 3.2, 4.1, 4.2 with the databases PostgreSQL, SQLite, and MySQL.

Note: an upper limit on Django version is set for your safety. Please do not ignore it.

Expand Down
10 changes: 2 additions & 8 deletions cachalot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
VERSION = (2, 5, 3)
VERSION = (2, 6, 0)
__version__ = ".".join(map(str, VERSION))

try:
from django import VERSION as DJANGO_VERSION

if DJANGO_VERSION < (3, 2):
default_app_config = "cachalot.apps.CachalotConfig"
except ImportError: # pragma: no cover
default_app_config = "cachalot.apps.CachalotConfig"
default_app_config = "cachalot.apps.CachalotConfig"
36 changes: 12 additions & 24 deletions cachalot/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from contextlib import contextmanager
from typing import Any, Optional, Tuple, Union

from django.apps import apps
from django.conf import settings
Expand Down Expand Up @@ -45,7 +46,11 @@ def _get_tables(tables_or_models):
else table_or_model._meta.db_table)


def invalidate(*tables_or_models, **kwargs):
def invalidate(
*tables_or_models: Tuple[Union[str, Any], ...],
cache_alias: Optional[str] = None,
db_alias: Optional[str] = None,
) -> None:
"""
Clears what was cached by django-cachalot implying one or more SQL tables
or models from ``tables_or_models``.
Expand All @@ -62,19 +67,9 @@ def invalidate(*tables_or_models, **kwargs):
(or a combination)
:type tables_or_models: tuple of strings or models
:arg cache_alias: Alias from the Django ``CACHES`` setting
:type cache_alias: string or NoneType
:arg db_alias: Alias from the Django ``DATABASES`` setting
:type db_alias: string or NoneType
: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(
Expand All @@ -90,7 +85,11 @@ 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: Tuple[Union[str, Any], ...],
cache_alias: Optional[str] = None,
db_alias: Optional[str] = None,
) -> float:
"""
Returns the timestamp of the most recent invalidation of the given
``tables_or_models``. If ``tables_or_models`` is not specified,
Expand All @@ -106,19 +105,9 @@ def get_last_invalidation(*tables_or_models, **kwargs):
(or a combination)
:type tables_or_models: tuple of strings or models
:arg cache_alias: Alias from the Django ``CACHES`` setting
:type cache_alias: string or NoneType
:arg db_alias: Alias from the Django ``DATABASES`` setting
:type db_alias: string or NoneType
: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):
Expand All @@ -134,7 +123,7 @@ def get_last_invalidation(*tables_or_models, **kwargs):


@contextmanager
def cachalot_disabled(all_queries=False):
def cachalot_disabled(all_queries: bool = False):
"""
Context manager for temporarily disabling cachalot.
If you evaluate the same queryset a second time,
Expand All @@ -158,7 +147,6 @@ def cachalot_disabled(all_queries=False):
the original and duplicate query.

: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)
LOCAL_STORAGE.cachalot_enabled = False
Expand Down
4 changes: 0 additions & 4 deletions cachalot/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
'django.db.backends.sqlite3',
'django.db.backends.postgresql',
'django.db.backends.mysql',
# TODO: Remove when we drop Django 2.x support.
'django.db.backends.postgresql_psycopg2',

# GeoDjango
'django.contrib.gis.db.backends.spatialite',
Expand All @@ -20,8 +18,6 @@
# django-transaction-hooks
'transaction_hooks.backends.sqlite3',
'transaction_hooks.backends.postgis',
# TODO: Remove when we drop Django 2.x support.
'transaction_hooks.backends.postgresql_psycopg2',
'transaction_hooks.backends.mysql',

# django-prometheus wrapped engines
Expand Down
16 changes: 2 additions & 14 deletions cachalot/tests/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.conf import settings
from django.contrib.postgres.fields import (
ArrayField, HStoreField, IntegerRangeField,
DateRangeField, DateTimeRangeField)
DateRangeField, DateTimeRangeField, DecimalRangeField)
from django.contrib.postgres.operations import (
HStoreExtension, UnaccentExtension)
from django.db import models, migrations
Expand All @@ -21,19 +21,6 @@ def extra_regular_available_fields():

def extra_postgres_available_fields():
fields = []
try:
# TODO Remove when Dj31 support is dropped
from django.contrib.postgres.fields import FloatRangeField
fields.append(('float_range', FloatRangeField(null=True, blank=True)))
except ImportError:
pass

try:
# TODO Add to module import when Dj31 is dropped
from django.contrib.postgres.fields import DecimalRangeField
fields.append(('decimal_range', DecimalRangeField(null=True, blank=True)))
except ImportError:
pass

# Future proofing with Django 40 deprecation
if DJANGO_VERSION[0] < 4:
Expand Down Expand Up @@ -103,6 +90,7 @@ class Migration(migrations.Migration):
('int_range', IntegerRangeField(null=True, blank=True)),
('date_range', DateRangeField(null=True, blank=True)),
('datetime_range', DateTimeRangeField(null=True, blank=True)),
('decimal_range', DecimalRangeField(null=True, blank=True))
] + extra_postgres_available_fields(),
),
migrations.RunSQL('CREATE TABLE cachalot_unmanagedmodel '
Expand Down
2 changes: 1 addition & 1 deletion cachalot/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class PostgresModel(Model):
null=True, blank=True)

hstore = HStoreField(null=True, blank=True)
if DJANGO_VERSION[0] < 4:
if DJANGO_VERSION < (4, 0):
from django.contrib.postgres.fields import JSONField
json = JSONField(null=True, blank=True)

Expand Down
25 changes: 2 additions & 23 deletions cachalot/tests/multi_db.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from unittest import skipIf

from django import VERSION as DJANGO_VERSION
from django.conf import settings
from django.db import DEFAULT_DB_ALIAS, connections, transaction
from django.test import TransactionTestCase
Expand All @@ -27,24 +26,6 @@ def setUp(self):
# will execute an extra SQL request below.
connection2.cursor()

def is_django_21_below_and_sqlite2(self):
"""
Note: See test_utils.py with this function name
Checks if Django 2.1 or below and SQLite2
"""
django_version = DJANGO_VERSION
if not self.is_sqlite2:
# Immediately know if SQLite
return False
if django_version[0] < 2:
# Takes Django 0 and 1 out of the picture
return True
else:
if django_version[0] == 2 and django_version[1] < 2:
# Takes Django 2.0-2.1 out
return True
return False

def test_read(self):
with self.assertNumQueries(1):
data1 = list(Test.objects.all())
Expand All @@ -66,8 +47,7 @@ def test_invalidate_other_db(self):
data1 = list(Test.objects.using(self.db_alias2))
self.assertListEqual(data1, [])

with self.assertNumQueries(2 if self.is_django_21_below_and_sqlite2() else 1,
using=self.db_alias2):
with self.assertNumQueries(1, using=self.db_alias2):
t3 = Test.objects.using(self.db_alias2).create(name='test3')

with self.assertNumQueries(1, using=self.db_alias2):
Expand All @@ -82,8 +62,7 @@ def test_invalidation_independence(self):
data1 = list(Test.objects.all())
self.assertListEqual(data1, [self.t1, self.t2])

with self.assertNumQueries(2 if self.is_django_21_below_and_sqlite2() else 1,
using=self.db_alias2):
with self.assertNumQueries(1, using=self.db_alias2):
Test.objects.using(self.db_alias2).create(name='test3')

with self.assertNumQueries(0):
Expand Down
13 changes: 12 additions & 1 deletion cachalot/tests/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@
from decimal import Decimal
from unittest import skipUnless

from django import VERSION
from django.contrib.postgres.functions import TransactionNow
from django.db import connection
from django.test import TransactionTestCase, override_settings
from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange

# If we are using Django 4.2 or higher, we need to use:
if VERSION >= (4, 2):
from django.db.backends.postgresql.psycopg_any import (
DateRange,
DateTimeTZRange,
NumericRange,
)
else:
from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange

from pytz import timezone

from ..utils import UncachableQuery
Expand Down
Loading