Equip Django projects with cache-busted static files#3
Open
audreyfeldroy wants to merge 7 commits intomainfrom
Open
Equip Django projects with cache-busted static files#3audreyfeldroy wants to merge 7 commits intomainfrom
audreyfeldroy wants to merge 7 commits intomainfrom
Conversation
Django projects can now add "staticware.contrib.django" to
INSTALLED_APPS, set STATICWARE_DIRECTORY in settings, and get two
things: a {% hashed_static "path" %} template tag for cache-busted
URLs, and a get_asgi_application() that serves hashed files and
rewrites HTML in one call.
Key design decisions:
- get_static() is a lazy singleton that reads Django settings on
first call, so the HashedStatic instance is created once and
reused across template renders and ASGI requests.
- Django's ASGI handler spawns a background task that calls
receive() a second time to listen for client disconnect. The
combined app blocks that second call with asyncio.Future() so
Django can cancel it cleanly after the response is sent.
- STATICWARE_DIRECTORY is required (raises ImproperlyConfigured).
STATICWARE_PREFIX falls back to STATIC_URL then "/static".
default_auto_field configures implicit primary keys for models, but this app has no models. default_app_config was the pre-3.2 mechanism for AppConfig discovery, deprecated in 3.2 and removed in 5.1. Since staticware requires django>=4.2, both lines were dead code. Tests assert neither attribute reappears, so this stays clean if future tooling regenerates the scaffolding.
…pper The ASGI spec says header names should be lowercase bytes, but Django sends Content-Type and Content-Length with capital letters. The middleware now compares with .lower() at both header-reading sites: content-type detection and content-length update after rewriting. This made the _normalized_django wrapper in the Django integration redundant. The wrapper existed solely to lowercase Django's headers before they reached the middleware. With the fix in the middleware itself, django_app passes directly to StaticRewriteMiddleware and every ASGI framework benefits, not just Django.
The ty type checker runs without Django installed (it's an optional dependency), so the Django contrib imports fail resolution. Excluding that directory from ty's src scope lets CI pass without pulling Django into the typecheck group. The ruff formatter also needed two files reformatted (string concatenation and dict literal style).
Django is an optional dependency, so the default test job doesn't have it installed. The tests/conftest.py now skips the django/ directory when Django isn't importable, and a new test-django CI job runs with --group django to cover the integration tests.
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
Both directories import Django, which is an optional dependency not available in the typecheck environment. Tests are covered by pytest at runtime; ty only needs to check the core library.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Django projects can now use staticware by adding
staticware.contrib.djangoto INSTALLED_APPS and setting
STATICWARE_DIRECTORY. The integration providesa
{% hashed_static "path" %}template tag and aget_asgi_application()thatcombines Django's ASGI handler with static file serving and automatic HTML
rewriting in one call.
The middleware also gained case-insensitive header matching. The ASGI spec says
header names should be lowercase, but Django sends mixed-case headers like
Content-Type. Making the middleware tolerant at both comparison sites(content-type detection and content-length update after rewriting) means it
works with any ASGI framework regardless of header casing, and the Django
integration doesn't need a normalization layer.
Test plan
get_static()singleton, settings fallback,ImproperlyConfigured, template tag rendering, ASGI static serving, ASGI HTMLrewriting, AppConfig attribute guardrails
verifying both HTML rewriting and content-length accuracy
uv run --group django pytest tests/ -q