Skip to content

Commit

Permalink
Drop Django 3.2 and 4.1 (#60)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
Archmonger and pre-commit-ci[bot] authored Oct 28, 2024
1 parent db9d198 commit 53f2a1d
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 80 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ repos:
rev: "1.21.0"
hooks:
- id: django-upgrade
args: [--target-version, "3.2"]
args: [--target-version, "4.2"]
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Using the following categories, list your changes in this order:

### Changed

- Drop Django 3.2 and 4.1 support.
- Any errors from threads in the `servestatic.compress` command are now raised.

## [2.1.1] - 2024-10-27
Expand Down
24 changes: 0 additions & 24 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,6 @@ extra-dependencies = ["pytest-sugar", "requests", "brotli"]
randomize = true
matrix-name-format = "{variable}-{value}"

# Django 3.2
[[tool.hatch.envs.hatch-test.matrix]]
python = ["3.9", "3.10"]
django = ["3.2"]

# Django 4.0
[[tool.hatch.envs.hatch-test.matrix]]
python = ["3.9", "3.10"]
django = ["4.0"]

# Django 4.1
[[tool.hatch.envs.hatch-test.matrix]]
python = ["3.9", "3.10", "3.11"]
django = ["4.1"]

# Django 4.2
[[tool.hatch.envs.hatch-test.matrix]]
python = ["3.9", "3.10", "3.11", "3.12"]
Expand All @@ -83,15 +68,6 @@ django = ["5.1"]

[tool.hatch.envs.hatch-test.overrides]
matrix.django.dependencies = [
{ if = [
"3.2",
], value = "django~=3.2" },
{ if = [
"4.0",
], value = "django~=4.0" },
{ if = [
"4.1",
], value = "django~=4.1" },
{ if = [
"4.2",
], value = "django~=4.2" },
Expand Down
38 changes: 10 additions & 28 deletions src/servestatic/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from urllib.parse import urlparse
from urllib.request import url2pathname

import django
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
from django.conf import settings as django_settings
from django.contrib.staticfiles import finders
Expand All @@ -17,12 +16,7 @@
)
from django.http import FileResponse, HttpRequest

from servestatic.responders import (
AsyncSlicedFile,
MissingFileError,
SlicedFile,
StaticFile,
)
from servestatic.responders import AsyncSlicedFile, MissingFileError, StaticFile
from servestatic.utils import (
AsyncFile,
AsyncFileIterator,
Expand Down Expand Up @@ -253,31 +247,19 @@ def set_headers(self, *args, **kwargs):
pass

def _set_streaming_content(self, value):
# Django < 4.2 doesn't support async file responses, so we must perform
# some conversions to ensure compatibility.
if django.VERSION < (4, 2):
if isinstance(value, AsyncFile):
value = value.open_raw()
elif isinstance(value, EmptyAsyncIterator):
value = ()
elif isinstance(value, AsyncSlicedFile):
value = SlicedFile(value.fileobj.open_raw(), value.start, value.end)

# Django 4.2+ supports async file responses, but they need to be converted from
# a file-like object to an iterator, otherwise Django will assume the content is
# a traditional (sync) file object.
elif isinstance(value, (AsyncFile, AsyncSlicedFile)):
if isinstance(value, (AsyncFile, AsyncSlicedFile)):
value = AsyncFileIterator(value)

super()._set_streaming_content(value)

if django.VERSION >= (4, 2):

def __iter__(self):
"""The way that Django 4.2+ converts async to sync is inefficient, so
we override it with a better implementation. Django only uses this method
when running via WSGI."""
try:
return iter(self.streaming_content)
except TypeError:
return iter(AsyncToSyncIterator(self.streaming_content))
def __iter__(self):
"""The way that Django 4.2+ converts async to sync is inefficient, so
we override it with a better implementation. Django only uses this method
when running via WSGI."""
try:
return iter(self.streaming_content)
except TypeError:
return iter(AsyncToSyncIterator(self.streaming_content))
8 changes: 8 additions & 0 deletions src/servestatic/responders.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,15 @@ async def aget_response(self, method, request_headers):

def get_range_response(self, range_header, base_headers, file_handle):
headers = []
size: int | None = None
for item in base_headers:
if item[0] == "Content-Length":
size = int(item[1])
else:
headers.append(item)
if size is None:
msg = "Content-Length header is required for range requests"
raise ValueError(msg)
start, end = self.get_byte_range(range_header, size)
if start >= end:
return self.get_range_not_satisfiable_response(file_handle, size)
Expand All @@ -171,11 +175,15 @@ def get_range_response(self, range_header, base_headers, file_handle):
async def aget_range_response(self, range_header, base_headers, file_handle):
"""Variant of `get_range_response` that works with async file objects."""
headers = []
size: int | None = None
for item in base_headers:
if item[0] == "Content-Length":
size = int(item[1])
else:
headers.append(item)
if size is None:
msg = "Content-Length header is required for range requests"
raise ValueError(msg)
start, end = self.get_byte_range(range_header, size)
if start >= end:
return await self.aget_range_not_satisfiable_response(file_handle, size)
Expand Down
16 changes: 6 additions & 10 deletions tests/django_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import os.path

import django

from .utils import TEST_FILE_PATH, AppServer

ALLOWED_HOSTS = ["*"]
Expand All @@ -19,14 +17,12 @@

STATIC_ROOT = os.path.join(TEST_FILE_PATH, "root")

if django.VERSION >= (4, 2):
STORAGES = {
"staticfiles": {
"BACKEND": "servestatic.storage.CompressedManifestStaticFilesStorage",
},
}
else:
STATICFILES_STORAGE = "servestatic.storage.CompressedManifestStaticFilesStorage"
STORAGES = {
"staticfiles": {
"BACKEND": "servestatic.storage.CompressedManifestStaticFilesStorage",
},
}


MIDDLEWARE = [
"tests.middleware.sync_middleware_1",
Expand Down
27 changes: 10 additions & 17 deletions tests/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import tempfile
from posixpath import basename

import django
import pytest
from django.conf import settings
from django.contrib.staticfiles.storage import HashedFilesMixin, staticfiles_storage
Expand Down Expand Up @@ -36,15 +35,12 @@ def setup():
@pytest.fixture
def _compressed_storage(setup):
backend = "servestatic.storage.CompressedStaticFilesStorage"
if django.VERSION >= (4, 2):
storages = {
"STORAGES": {
**settings.STORAGES,
"staticfiles": {"BACKEND": backend},
}
storages = {
"STORAGES": {
**settings.STORAGES,
"staticfiles": {"BACKEND": backend},
}
else:
storages = {"STATICFILES_STORAGE": backend}
}

with override_settings(**storages):
yield
Expand All @@ -53,15 +49,12 @@ def _compressed_storage(setup):
@pytest.fixture
def _compressed_manifest_storage(setup):
backend = "servestatic.storage.CompressedManifestStaticFilesStorage"
if django.VERSION >= (4, 2):
storages = {
"STORAGES": {
**settings.STORAGES,
"staticfiles": {"BACKEND": backend},
}
storages = {
"STORAGES": {
**settings.STORAGES,
"staticfiles": {"BACKEND": backend},
}
else:
storages = {"STATICFILES_STORAGE": backend}
}

with override_settings(**storages, SERVESTATIC_KEEP_ONLY_HASHED_FILES=True):
call_command("collectstatic", verbosity=0, interactive=False)
Expand Down

0 comments on commit 53f2a1d

Please sign in to comment.