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

Support Django 5.0 and Python 3.12 #74

Merged
merged 2 commits into from
Oct 24, 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: 10 additions & 10 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,27 @@ jobs:
python-version:
- "3.8"
- "3.9"
django-version:
- "3.2"
- "4.0"
- "4.1"
- "4.2"
- "3.10"
- "3.11"
- "3.12"
Comment on lines 8 to +13
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve moved the GitHub setup to only use a per-Python matrix, using tox to run the relevant environments per version. This is what I use on my ~30 open source projects and it works really well. It requires less maintenance and is actually faster than splitting per Django version unless you have a long test suite.


steps:
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies (django ${{ matrix.django-version }})

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pyyaml coveralls pytest-django django==${{ matrix.django-version }}.*
pip install -e .
pip install flake8 tox

- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics

- name: Test with pytest
run: |
coverage run $(which pytest) --ds=test_settings typedmodels/tests.py
tox run -f py$(echo ${{ matrix.python-version }} | tr -d .)
7 changes: 6 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

Backward-incompatible changes for released versions are listed here (for 0.5 onwards.)

* Added support for Django 4.2.
* Added support for Django 4.2 and 5.0.

* Added support for Python 3.12.

* Dropped the `VERSION` and `__version__` attributes. To check the version of the package, use `importlib.metadata.version("django-typed-models")` ([docs](https://docs.python.org/3/library/importlib.metadata.html#distribution-versions) /
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pkg_resources is gone from Python 3.12, so it’s necessary to at least change this. I think it’s best to drop the version attributes and recommend using the new method in the standard library, importlib.metadata.version(). Loading the version proactively slows down startup unnecessarily for the 99% of cases where it’s not used.

[backport](https://pypi.org/project/importlib-metadata/)).

## 0.13

Expand Down
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def read_relative_file(filename):
"Framework :: Django :: 4.0",
"Framework :: Django :: 4.1",
"Framework :: Django :: 4.2",
"Framework :: Django :: 5.0",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
Expand All @@ -39,6 +40,9 @@ def read_relative_file(filename):
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Utilities",
],
)
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ envlist = {py36,py39}-{dj32}
{py38,py310}-{dj40}
{py38,py310}-{dj41}
{py38,py310}-{dj42}
{py310,py311,py312}-{dj50}

[testenv]
changedir = {toxinidir}
commands =
pip --disable-pip-version-check install -e .
coverage run {envbindir}/pytest --ds=test_settings typedmodels/tests.py {posargs}
coverage report --omit=typedmodels/test*

Expand All @@ -25,3 +25,4 @@ deps =
dj40: Django~=4.0.0
dj41: Django~=4.1.0
dj42: Django~=4.2.0
dj50: Django~=5.0a1
6 changes: 0 additions & 6 deletions typedmodels/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@


pkg_resources = __import__('pkg_resources')
distribution = pkg_resources.get_distribution('django-typed-models')

VERSION = __version__ = distribution.version
107 changes: 73 additions & 34 deletions typedmodels/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import inspect
from functools import partial
import types

import django
from django.core.exceptions import FieldDoesNotExist, FieldError
from django.core.serializers.python import Serializer as _PythonSerializer
from django.core.serializers.xml_serializer import Serializer as _XmlSerializer
Expand Down Expand Up @@ -245,40 +245,79 @@ def _model_has_field(cls, base_class, field_name):
def _patch_fields_cache(cls, base_class):
orig_get_fields = cls._meta._get_fields

def _get_fields(
self,
forward=True,
reverse=True,
include_parents=True,
include_hidden=False,
seen_models=None,
):
cache_key = (
forward,
reverse,
include_parents,
include_hidden,
seen_models is None,
)
if django.VERSION >= (5, 0):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change necessary due to the change in signature of Model._meta._get_fields. It seems the simplest to have two versions split by version. If you use django-upgrade it can automatically drop the versioned block when you stop supporting Django < 5.0: https://github.com/adamchainz/django-upgrade/#versioned-blocks


def _get_fields(
self,
forward=True,
reverse=True,
include_parents=True,
include_hidden=False,
topmost_call=True,
):
cache_key = (
forward,
reverse,
include_parents,
include_hidden,
topmost_call,
)

was_cached = cache_key in self._get_fields_cache
fields = orig_get_fields(
forward=forward,
reverse=reverse,
include_parents=include_parents,
include_hidden=include_hidden,
seen_models=seen_models,
)
# If it was cached already, it's because we've already filtered this, skip it
if not was_cached:
fields = [
f
for f in fields
if TypedModelMetaclass._model_has_field(cls, base_class, f.name)
]
fields = make_immutable_fields_list("get_fields()", fields)
self._get_fields_cache[cache_key] = fields
return fields
was_cached = cache_key in self._get_fields_cache
fields = orig_get_fields(
forward=forward,
reverse=reverse,
include_parents=include_parents,
include_hidden=include_hidden,
topmost_call=topmost_call,
)
# If it was cached already, it's because we've already filtered this, skip it
if not was_cached:
fields = [
f
for f in fields
if TypedModelMetaclass._model_has_field(cls, base_class, f.name)
]
fields = make_immutable_fields_list("get_fields()", fields)
self._get_fields_cache[cache_key] = fields
return fields

else:

def _get_fields(
self,
forward=True,
reverse=True,
include_parents=True,
include_hidden=False,
seen_models=None,
):
cache_key = (
forward,
reverse,
include_parents,
include_hidden,
seen_models is None,
)

was_cached = cache_key in self._get_fields_cache
fields = orig_get_fields(
forward=forward,
reverse=reverse,
include_parents=include_parents,
include_hidden=include_hidden,
seen_models=seen_models,
)
# If it was cached already, it's because we've already filtered this, skip it
if not was_cached:
fields = [
f
for f in fields
if TypedModelMetaclass._model_has_field(cls, base_class, f.name)
]
fields = make_immutable_fields_list("get_fields()", fields)
self._get_fields_cache[cache_key] = fields
return fields

cls._meta._get_fields = partial(_get_fields, cls._meta)

Expand Down