A X-Clacks-Overhead package for Django! Middleware, Mixin, Decorator
Project description
Django-X-Clacks-Overhead
X-Clacks-Overhead package for Django!
GNU Clacks Overhead
A Django/Django REST Framework middleware system for managing the X-Clacks-Overhead HTTP header, honoring the legacy of Sir Terry Pratchett and the Discworld Clacks semaphore network.
"A man is not dead while his name is still spoken." — Going Postal, Chapter 4 prologue
📖 Background
In Terry Pratchett's Discworld, the Clacks is a semaphore tower network. When Robert Dearheart's son John died, he encoded his name into the Clacks overhead with the code GNU:
- G — Send the message on
- N — Do not log the message
- U — Turn the message around at the end of the line and send it back again
This ensures the name travels the Clacks forever. This package brings that tradition to the modern Internet (the "Roundworld Clacks").
✨ Features
| Feature | Description |
|---|---|
| Three-Layer Precedence | Middleware → Mixin → Decorator (each overrides the previous) |
| DRF Compatible | Works seamlessly with Django REST Framework ViewSets |
| RFC Compliant | Supports comma-separated multiple tributes |
| Security Hardened | Sanitizes header values to prevent injection attacks |
| Tested | Full test suite with 100% coverage of precedence logic |
🚀 Installation
1. Install package as par of your Django project
PIP
pip3 install django-x-clacks-overhead
UV
uv add django-x-clacks-overhead
optional:
### With DRF support
uv add django-x-clacks-overhead[drf]
2. Add Middleware to settings.py
# settings.py
MIDDLEWARE = [
# ... other middleware
'clacks.ClacksMiddleware',
]
# Global default tribute (optional)
CLACKS_OVERHEAD = "Terry Pratchett"
3. Configure Tributes
The default X-Clacks-Overhead value is GNU Terry Pratchett, on all 3 applications: Middleware, Mixin, Decorator.
Unless you configure and spécify a tribute. (See more details bellow in Usage section)
Tribute configuration on Middleware level
# settings.py
# Single tribute
CLACKS_OVERHEAD = "Terry Pratchett"
# Or
# Multiple tributes (comma-separated in header)
CLACKS_OVERHEAD = ["Terry Pratchett", "Alan Turing", "Ada Lovelace"]
# No setting = defaults to "GNU Terry Pratchett"
Tribute configuration on Mixin level
# views.py
from rest_framework import viewsets
from clacks import ClacksMixin
class TributeViewSet(ClacksMixin, viewsets.ModelViewSet):
clacks_tribute = ["Grace Hopper", "John Warner Backus"]
queryset = MyModel.objects.all()
serializer_class = MySerializer
Tribute configuration on Decorator level
# views.py
from rest_framework.decorators import action
from clacks import clacks_overhead
class TributeViewSet(ClacksMixin, viewsets.ModelViewSet):
clacks_tribute = ["Terry Pratchett", "Alan Turing"]
@action(detail=False, methods=['get'])
@clacks_overhead("Dennis Ritchie")
def ada(self, request):
return Response({"tribute": "Dennis Ritchie"})
📖 Usage
Layer 1: Middleware (Global Default)
Applied to all endpoints automatically. Set in settings.py.
By default the X-Clacks-Overhead will deliver "GNU Terry Pratchett" if no settings specified.
Single Tribute
# settings.py
CLACKS_OVERHEAD = "John Dearheart"
# All responses will include:
# X-Clacks-Overhead: GNU John Dearheart
Multiple Tribute
# settings.py
# Multiple tributes (comma-separated in header)
CLACKS_OVERHEAD = ["John von Neumann", "Alan Turing", "Ada Lovelace"]
# All responses will include:
# X-Clacks-Overhead: GNU John von Neumann, GNU Alan Turing, GNU Ada Lovelace
Layer 2: Mixin (ViewSet-Level Override)
Override the Middleware tribute for an entire ViewSet.
# views.py
from rest_framework import viewsets
from clacks import ClacksMixin
class TributeViewSet(ClacksMixin, viewsets.ModelViewSet):
clacks_tribute = ["Grace Hopper", "John Warner Backus"]
queryset = MyModel.objects.all()
serializer_class = MySerializer
# All endpoints in this ViewSet will include:
# X-Clacks-Overhead: GNU Grace Hopper, GNU John Warner Backus
Layer 3: Decorator (Method-Level Override)
Override both Middleware and Mixin for a specific endpoint.
# views.py
from rest_framework.decorators import action
from clacks import clacks_overhead
class TributeViewSet(ClacksMixin, viewsets.ModelViewSet):
clacks_tribute = ["Terry Pratchett", "Alan Turing"]
@action(detail=False, methods=['get'])
@clacks_overhead("Dennis Ritchie")
def ada(self, request):
return Response({"tribute": "Dennis Ritchie"})
# This endpoint will include:
# X-Clacks-Overhead: GNU Dennis Ritchie
# (Overrides the Mixin's tributes)
🎯 Precedence Table
| Layer | Scope | Overrides | Example |
|---|---|---|---|
| Decorator | Single endpoint | Middleware + Mixin | @clacks_overhead("Dennis Ritchie") |
| Mixin | Entire ViewSet | Middleware | clacks_tribute = ["Grace Hopper", "John Warner Backus"] |
| Middleware | All endpoints | (none) | CLACKS_OVERHEAD = "John Dearheart" |
Rule: If a higher-priority layer sets the header, lower-priority layers skip automatically.
🧪 Complete Example
# settings.py
CLACKS_OVERHEAD = "John Dearheart" # Global default
# views.py
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from clacks import ClacksMixin, clacks_overhead
# Endpoint: /api/default/
# Header: X-Clacks-Overhead: GNU John Dearheart
class DefaultViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
# Endpoint: /api/tributes/
# Header: X-Clacks-Overhead: GNU Terry Pratchett, GNU Alan Turing
class TributesViewSet(ClacksMixin, viewsets.ModelViewSet):
clacks_tribute = ["Terry Pratchett", "Alan Turing"]
queryset = MyModel.objects.all()
# Endpoint: /api/tributes/ada/
# Header: X-Clacks-Overhead: GNU Ada Lovelace
@action(detail=False, methods=['get'])
@clacks_overhead("Ada Lovelace")
def ada(self, request):
return Response({"message": "Honoring Ada Lovelace"})
🧪 Testing
# tests.py
from django.test import override_settings
from rest_framework.test import APITestCase
@override_settings(CLACKS_OVERHEAD="John Dearheart")
class TestClacksHeader(APITestCase):
def test_middleware_default(self):
response = self.client.get('/api/default/')
self.assertEqual(
response.get("X-Clacks-Overhead"),
"GNU John Dearheart"
)
def test_decorator_override(self):
response = self.client.get('/api/tributes/ada/')
self.assertEqual(
response.get("X-Clacks-Overhead"),
"GNU Ada Lovelace"
)
🔧 Configuration Reference
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
CLACKS_OVERHEAD |
str or list[str] |
"GNU Terry Pratchett" |
Global default tribute(s) [Optional] |
Mixin Attributes
| Attribute | Type | Description |
|---|---|---|
clacks_tribute |
str or list[str] |
Tribute(s) for this ViewSet |
Decorator Arguments
| Argument | Type | Description |
|---|---|---|
value |
str or list[str] |
Tribute(s) for this endpoint |
🛡️ Security
All tribute values are sanitized to prevent HTTP header injection:
- Newlines (
\r,\n) are stripped - Null bytes (
\x00) are removed - Empty values fall back to default
# Unsafe input is sanitized automatically
CLACKS_OVERHEAD = "Evil\nHeader"
# Result: X-Clacks-Overhead: GNU EvilHeader
📜 RFC Compliance
This implementation follows the Clacks-over-HTTP Draft RFC:
- Header name:
X-Clacks-Overhead - Format:
GNU <name>(prefix enforced) - Multiple values: Comma-separated (
GNU Name1, GNU Name2)
🤝 Contributing
- Fork the repository
- Create a feature branch
- Run tests:
uv run pytest tests/ -v - Submit a pull request
📄 License
GNU General Public License v3 (GPLv3).
Acknowledgments
- Sir Terry Pratchett — For the Discworld series and the Clacks.
- GNU Terry Pratchett Project — For keeping the legacy alive.
- Discworld Fandom — For maintaining the Clacks network.
"Go well, and fare you well, and may the light of your going be a beacon to those who follow."
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_x_clacks_overhead-1.0.0.tar.gz.
File metadata
- Download URL: django_x_clacks_overhead-1.0.0.tar.gz
- Upload date:
- Size: 49.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52f67570cdafc63e63fbe41cfb626054ff0c4fc83c2ba54ade75b44354274966
|
|
| MD5 |
a727c9841f05972f1c6b91d196d8b747
|
|
| BLAKE2b-256 |
7583c9e5fffe1f959878478ae349d9cc671d1844e216b3e7889abc483055ac69
|
File details
Details for the file django_x_clacks_overhead-1.0.0-py3-none-any.whl.
File metadata
- Download URL: django_x_clacks_overhead-1.0.0-py3-none-any.whl
- Upload date:
- Size: 31.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfa40f2b9d05afcf2e501640349b3a5a910086f7d8ca4e94668c528c36bbe2e5
|
|
| MD5 |
a3cd2caccedd8882f350f06460503302
|
|
| BLAKE2b-256 |
e3c6c298c5014f075fe3d884e2b4e6c38f18de97b88c74e4300a082fcca2cac6
|