Skip to content

Commit

Permalink
django 5.1 adaptions
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiasertl committed Jun 27, 2024
1 parent 2fb4e4b commit c152e87
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Install program
run: pip install -e .

- name: Run black, isort and flake8
- name: Run ruff
run: python dev.py code-quality

# Will show outdated dependencies, etc
Expand Down
56 changes: 13 additions & 43 deletions ca/django_ca/tests/test_acme.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import typing
from collections.abc import Iterable, Iterator
from contextlib import contextmanager
from importlib import reload
from typing import Any, Optional
from unittest import mock

Expand All @@ -26,12 +25,13 @@
from dns import resolver
from dns.rdtypes.txtbase import TXTBase

from django.test import TestCase, override_settings
from django.test import TestCase
from django.urls import include, path, reverse
from django.urls.exceptions import NoReverseMatch
from django.utils.crypto import get_random_string

from django_ca import urls
import pytest

from django_ca.acme import validation
from django_ca.acme.constants import IdentifierType, Status
from django_ca.models import AcmeAccount, AcmeAuthorization, AcmeChallenge, AcmeOrder
Expand All @@ -42,47 +42,17 @@
]


class URLPatternTestCase(TestCase):
"""Test that URL patterns are not enabled when CA_ENABLE_ACME."""
def assert_no_reverse_match(
name: str, args: Optional[typing.Sequence[Any]] = None, kwargs: Optional[dict[str, Any]] = None
) -> None:
"""Context manager asserting that the given URL pattern is **not** found."""
urlname = name
if ":" in urlname:
_namespace, urlname = name.split(":", 1)

@contextmanager
def reload_urlconf(self) -> Iterator[None]:
"""Context manager to reload the current URL configuration."""
reload(urls)
try:
with self.settings(ROOT_URLCONF=__name__):
yield
finally:
reload(urls)

def assertNoReverseMatch( # pylint: disable=invalid-name
self,
name: str,
args: Optional[typing.Sequence[Any]] = None,
kwargs: Optional[dict[str, Any]] = None,
) -> None:
"""Context manager asserting that the given URL pattern is **not** found."""
urlname = name
if ":" in name:
_namespace, urlname = name.split(":", 1)

msg = f"Reverse for '{urlname}' not found. '{urlname}' is not a valid view function or pattern name."
with self.assertRaisesRegex(NoReverseMatch, msg):
reverse(name, args=args, kwargs=kwargs)

@override_settings(CA_ENABLE_ACME=False, CA_ENABLE_REST_API=False)
def test_disabled(self) -> None:
"""Test that resolving URLs does **NOT** work if disabled."""
with self.reload_urlconf():
self.assertNoReverseMatch("django_ca:acme-directory")
self.assertNoReverseMatch("django_ca:acme-directory", kwargs={"serial": "AB:CD"})
self.assertNoReverseMatch("django_ca:acme-new-nonce", kwargs={"serial": "AB:CD"})

def test_enabled(self) -> None:
"""Test that resolving URLs work if enabled."""
reverse("django_ca:acme-directory")
reverse("django_ca:acme-directory", kwargs={"serial": "AB:CD"})
reverse("django_ca:acme-new-nonce", kwargs={"serial": "AB:CD"})
msg = f"Reverse for '{urlname}' not found. '{urlname}' is not a valid view function or pattern name."
with pytest.raises(NoReverseMatch, match=msg):
reverse(name, args=args, kwargs=kwargs)


class TestConstantsTestCase(TestCase):
Expand Down
2 changes: 2 additions & 0 deletions ca/django_ca/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from cryptography.x509.name import _ASN1Type
from cryptography.x509.oid import NameOID

import django
from django.test import TestCase, override_settings

import pytest
Expand Down Expand Up @@ -81,6 +82,7 @@ def test_read_file(tmpcadir: Path) -> None:
assert read_file(path) == data


@pytest.mark.skipif(django.VERSION >= (5, 1), reason="get_storage_class() is removed in Django 5.1")
def test_deprecated_storage_configuration(settings: SettingsWrapper) -> None:
"""Test that using a deprecated storage configuration emits a warning."""
settings.STORAGES = {
Expand Down
4 changes: 2 additions & 2 deletions ca/django_ca/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@
path("crl/ca/<hex:serial>/", views.CertificateRevocationListView.as_view(scope="ca"), name="ca-crl"),
]

if model_settings.CA_ENABLE_REST_API is True:
if model_settings.CA_ENABLE_REST_API is True: # pragma: no branch
from django_ca.api.endpoints import api

urlpatterns.append(path("api/", api.urls))


if model_settings.CA_ENABLE_ACME:
if model_settings.CA_ENABLE_ACME: # pragma: no branch
from django_ca.acme import views as acme_views

# NOTE: Some functions depend on the fact that ALL ACME urls have a "serial" kwarg
Expand Down
12 changes: 7 additions & 5 deletions ca/django_ca/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from cryptography.x509.oid import NameOID

from django.conf import global_settings, settings
from django.core.files.storage import InvalidStorageError, Storage, get_storage_class, storages
from django.core.files.storage import InvalidStorageError, Storage, storages
from django.utils import timezone

from django_ca import constants
Expand Down Expand Up @@ -973,6 +973,10 @@ def get_storage() -> Storage:
RemovedInDjangoCA200Warning,
stacklevel=2,
)

# Lazy import as this function is removed in Django 5.0.
from django.core.files.storage import get_storage_class

ca_file_storage = getattr(settings, "CA_FILE_STORAGE", global_settings.DEFAULT_FILE_STORAGE)
ca_file_storage_kwargs = getattr(
settings,
Expand All @@ -983,6 +987,7 @@ def get_storage() -> Storage:
"directory_permissions_mode": 0o700,
},
)

ca_storage_cls = get_storage_class(ca_file_storage)
return ca_storage_cls(**ca_file_storage_kwargs)

Expand All @@ -1000,10 +1005,7 @@ def read_file(path: str) -> bytes:


def split_str(val: str, sep: str) -> Iterator[str]:
"""Split a character on the given set of characters.
This function is now only used in a migration and will be moved there in 2.0.
"""
"""Split a character on the given set of characters."""
lex = shlex.shlex(val, posix=True)
lex.commenters = ""
lex.whitespace = sep
Expand Down
10 changes: 8 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,15 +471,21 @@
}

qualname_overrides = {
"BytesIO": "python:io.BytesIO",
"StringIO": "python:io.StringIO",
"_io.BytesIO": "python:io.BytesIO",
"_io.StringIO": "python:io.StringIO",
"mappingproxy": "python:types.MappingProxyType",
"cryptography.hazmat.primitives._serialization.Encoding.PEM": "cg:cryptography.hazmat.primitives.serialization.Encoding.PEM", # noqa: E501
"cryptography.hazmat.primitives._serialization.Encoding.DER": "cg:cryptography.hazmat.primitives.serialization.Encoding.DER", # noqa: E501
"cryptography.hazmat.bindings._rust.ObjectIdentifier": "cg:cryptography.x509.ObjectIdentifier",
"cryptography.x509.extensions.ExtendedKeyUsage": "cg:cryptography.x509.ExtendedKeyUsage",
# These only happen when building on GitHub actions (2024-06-27)
"BytesIO": "python:io.BytesIO",
"StringIO": "python:io.StringIO",
"ec.EllipticCurve": "cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve",
"dsa.DSAPrivateKey": "cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey",
"rsa.RSAPrivateKey": "cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey",
"ed25519.Ed25519PrivateKey": "cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey",
"ed448.Ed448PrivateKey": "cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey",
# Django documents these classes under re-exported path names:
"django.http.request.HttpRequest": "django:django.http.HttpRequest",
"django.http.response.HttpResponse": "django:django.http.HttpResponse",
Expand Down

0 comments on commit c152e87

Please sign in to comment.