Merge pull request #3426 from benoitc/website-2025

Migrate docs build to MkDocs
This commit is contained in:
Benoit Chesneau 2026-01-23 01:20:42 +01:00 committed by GitHub
commit 3960372b82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 8190 additions and 42 deletions

80
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,80 @@
name: Docs
on:
push:
branches: [ master ]
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'scripts/build_settings_doc.py'
- 'gunicorn/config.py'
- 'requirements_dev.txt'
- '.github/workflows/docs.yml'
pull_request:
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'scripts/build_settings_doc.py'
- 'gunicorn/config.py'
- 'requirements_dev.txt'
- '.github/workflows/docs.yml'
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install -r requirements_dev.txt
- name: Build documentation
run: mkdocs build
- name: Upload site artifact
uses: actions/upload-artifact@v4
with:
name: gunicorn-site
path: site
retention-days: 7
deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install -r requirements_dev.txt
- name: Build documentation
run: mkdocs build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: site
publish_branch: gh-pages
cname: gunicorn.org
commit_message: "docs: deploy ${{ github.sha }}"

2
.gitignore vendored
View File

@ -16,3 +16,5 @@ examples/frameworks/pylonstest/pylonstest.egg-info/
MANIFEST
nohup.out
setuptools-*
site/
docs/site/

View File

@ -9,9 +9,15 @@ test:
coverage:
venv/bin/python setup.py test --cov
docs:
mkdocs build
docs-serve:
mkdocs serve
clean:
@rm -rf .Python MANIFEST build dist venv* *.egg-info *.egg
@find . -type f -name "*.py[co]" -delete
@find . -type d -name "__pycache__" -delete
.PHONY: build clean coverage test
.PHONY: build clean coverage docs docs-serve test

View File

@ -1,5 +1,5 @@
Gunicorn
--------
========
.. image:: https://img.shields.io/pypi/v/gunicorn.svg?style=flat
:alt: PyPI version
@ -13,60 +13,58 @@ Gunicorn
:alt: Build Status
:target: https://github.com/benoitc/gunicorn/actions/workflows/tox.yml
.. image:: https://github.com/benoitc/gunicorn/actions/workflows/lint.yml/badge.svg
:alt: Lint Status
:target: https://github.com/benoitc/gunicorn/actions/workflows/lint.yml
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork
worker model ported from Ruby's Unicorn_ project. The Gunicorn server is broadly
compatible with various web frameworks, simply implemented, light on server
resource usage, and fairly speedy.
Feel free to join us in `#gunicorn`_ on `Libera.chat`_.
**New in v24**: Native ASGI support (beta) for async frameworks like FastAPI!
Quick Start
-----------
.. code-block:: bash
pip install gunicorn
gunicorn myapp:app --workers 4
For ASGI applications (FastAPI, Starlette):
.. code-block:: bash
gunicorn myapp:app --worker-class asgi
Features
--------
- WSGI support for Django, Flask, Pyramid, and any WSGI framework
- **ASGI support** (beta) for FastAPI, Starlette, Quart
- uWSGI binary protocol for nginx integration
- Multiple worker types: sync, gthread, gevent, eventlet, asgi
- Graceful worker process management
- Compatible with Python 3.12+
Documentation
-------------
The documentation is hosted at https://docs.gunicorn.org.
Full documentation at https://gunicorn.org
Installation
------------
- `Quickstart <https://gunicorn.org/quickstart/>`_
- `Configuration <https://gunicorn.org/configure/>`_
- `Deployment <https://gunicorn.org/deploy/>`_
- `Settings Reference <https://gunicorn.org/reference/settings/>`_
Gunicorn requires **Python 3.x >= 3.10**.
Install from PyPI::
$ pip install gunicorn
Usage
-----
Basic usage::
$ gunicorn [OPTIONS] APP_MODULE
Where ``APP_MODULE`` is of the pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``. The
module name can be a full dotted path. The variable name refers to a WSGI
callable that should be found in the specified module.
Example with test app::
$ cd examples
$ gunicorn --workers=2 test:app
Contributing
------------
See `our complete contributor's guide <CONTRIBUTING.md>`_ for more details.
Community
---------
- Report bugs on `GitHub Issues <https://github.com/benoitc/gunicorn/issues>`_
- Chat in `#gunicorn`_ on `Libera.chat`_
- See `CONTRIBUTING.md <CONTRIBUTING.md>`_ for contribution guidelines
License
-------
Gunicorn is released under the MIT License. See the LICENSE_ file for more
details.
Gunicorn is released under the MIT License. See the LICENSE_ file for details.
.. _Unicorn: https://bogomips.org/unicorn/
.. _`#gunicorn`: https://web.libera.chat/?channels=#gunicorn

8
benchmarks/baseline.json Normal file
View File

@ -0,0 +1,8 @@
{
"gthread": {
"simple": {},
"simple_high_concurrency": {},
"slow_io": {},
"large_response": {}
}
}

40
benchmarks/quick_bench.sh Executable file
View File

@ -0,0 +1,40 @@
#!/bin/bash
# Quick benchmark for gthread worker
set -e
cd "$(dirname "$0")"
echo "Starting gunicorn with gthread worker..."
../.venv/bin/python -m gunicorn \
--worker-class gthread \
--workers 2 \
--threads 4 \
--worker-connections 1000 \
--bind 127.0.0.1:8765 \
--access-logfile /dev/null \
--error-logfile /dev/null \
--log-level warning \
simple_app:application &
GUNICORN_PID=$!
sleep 3
echo ""
echo "=== Benchmark: Simple requests (10000 requests, 100 concurrent) ==="
ab -n 10000 -c 100 -k http://127.0.0.1:8765/ 2>&1 | grep -E "(Requests per second|Time per request|Failed requests)"
echo ""
echo "=== Benchmark: High concurrency (5000 requests, 500 concurrent) ==="
ab -n 5000 -c 500 -k http://127.0.0.1:8765/ 2>&1 | grep -E "(Requests per second|Time per request|Failed requests)"
echo ""
echo "=== Benchmark: Large response (1000 requests, 50 concurrent) ==="
ab -n 1000 -c 50 -k http://127.0.0.1:8765/large 2>&1 | grep -E "(Requests per second|Time per request|Failed requests)"
echo ""
echo "Stopping gunicorn..."
kill $GUNICORN_PID 2>/dev/null || true
wait $GUNICORN_PID 2>/dev/null || true
echo "Done!"

238
benchmarks/run_benchmark.py Executable file
View File

@ -0,0 +1,238 @@
#!/usr/bin/env python3
"""
Benchmark script for gunicorn gthread worker.
This script runs various benchmarks against gunicorn and reports performance metrics.
Requires: gunicorn, requests (for warmup), and wrk or ab for load testing.
"""
import argparse
import json
import os
import signal
import subprocess
import sys
import time
from pathlib import Path
BENCHMARK_DIR = Path(__file__).parent
APP_MODULE = "simple_app:application"
def check_dependencies():
"""Check if required tools are available."""
# Check for wrk (preferred) or ab
for tool in ['wrk', 'ab']:
try:
subprocess.run([tool, '--version'], capture_output=True, check=False)
return tool
except FileNotFoundError:
continue
print("Error: Neither 'wrk' nor 'ab' found. Install one of them.")
print(" macOS: brew install wrk")
print(" Linux: apt-get install wrk (or apache2-utils for ab)")
sys.exit(1)
def start_gunicorn(worker_class, workers, threads, connections, bind, extra_args=None):
"""Start gunicorn server and return the process."""
cmd = [
sys.executable, '-m', 'gunicorn',
'--worker-class', worker_class,
'--workers', str(workers),
'--threads', str(threads),
'--worker-connections', str(connections),
'--bind', bind,
'--access-logfile', '-',
'--error-logfile', '-',
'--log-level', 'warning',
APP_MODULE,
]
if extra_args:
cmd.extend(extra_args)
env = os.environ.copy()
env['PYTHONPATH'] = str(BENCHMARK_DIR.parent)
proc = subprocess.Popen(
cmd,
cwd=BENCHMARK_DIR,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
# Wait for server to be ready
time.sleep(2)
return proc
def stop_gunicorn(proc):
"""Stop the gunicorn server."""
proc.send_signal(signal.SIGTERM)
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait()
def run_wrk_benchmark(url, duration, threads, connections):
"""Run wrk benchmark and return results."""
cmd = [
'wrk',
'-t', str(threads),
'-c', str(connections),
'-d', f'{duration}s',
'--latency',
url,
]
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
return parse_wrk_output(result.stdout)
def run_ab_benchmark(url, requests, concurrency):
"""Run Apache Bench benchmark and return results."""
cmd = [
'ab',
'-n', str(requests),
'-c', str(concurrency),
'-k', # keepalive
url,
]
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
return parse_ab_output(result.stdout)
def parse_wrk_output(output):
"""Parse wrk output to extract metrics."""
metrics = {}
for line in output.split('\n'):
if 'Requests/sec' in line:
metrics['requests_per_sec'] = float(line.split(':')[1].strip())
elif 'Transfer/sec' in line:
metrics['transfer_per_sec'] = line.split(':')[1].strip()
elif 'Latency' in line and 'Distribution' not in line:
parts = line.split()
if len(parts) >= 2:
metrics['latency_avg'] = parts[1]
elif '50%' in line:
metrics['latency_p50'] = line.split()[1]
elif '99%' in line:
metrics['latency_p99'] = line.split()[1]
return metrics
def parse_ab_output(output):
"""Parse ab output to extract metrics."""
metrics = {}
for line in output.split('\n'):
if 'Requests per second' in line:
metrics['requests_per_sec'] = float(line.split(':')[1].split()[0])
elif 'Time per request' in line and 'mean' in line:
metrics['latency_avg'] = line.split(':')[1].strip()
elif 'Transfer rate' in line:
metrics['transfer_per_sec'] = line.split(':')[1].strip()
return metrics
def run_benchmark_suite(tool, bind_addr):
"""Run a suite of benchmarks."""
results = {}
# Test configurations
configs = [
{'name': 'simple', 'path': '/', 'connections': 100},
{'name': 'simple_high_concurrency', 'path': '/', 'connections': 500},
{'name': 'slow_io', 'path': '/slow', 'connections': 50},
{'name': 'large_response', 'path': '/large', 'connections': 100},
]
for config in configs:
url = f'http://{bind_addr}{config["path"]}'
print(f" Running {config['name']}...")
if tool == 'wrk':
metrics = run_wrk_benchmark(
url,
duration=10,
threads=4,
connections=config['connections'],
)
else:
metrics = run_ab_benchmark(
url,
requests=10000,
concurrency=config['connections'],
)
results[config['name']] = metrics
print(f" Requests/sec: {metrics.get('requests_per_sec', 'N/A')}")
return results
def main():
parser = argparse.ArgumentParser(description='Benchmark gunicorn gthread worker')
parser.add_argument('--workers', type=int, default=2, help='Number of workers')
parser.add_argument('--threads', type=int, default=4, help='Threads per worker')
parser.add_argument('--connections', type=int, default=1000, help='Worker connections')
parser.add_argument('--bind', default='127.0.0.1:8000', help='Bind address')
parser.add_argument('--compare', action='store_true', help='Compare sync vs gthread')
parser.add_argument('--output', help='Output JSON file for results')
args = parser.parse_args()
tool = check_dependencies()
print(f"Using benchmark tool: {tool}")
all_results = {}
if args.compare:
# Compare sync and gthread workers
for worker_class in ['sync', 'gthread']:
print(f"\nBenchmarking {worker_class} worker...")
proc = start_gunicorn(
worker_class=worker_class,
workers=args.workers,
threads=args.threads,
connections=args.connections,
bind=args.bind,
)
try:
all_results[worker_class] = run_benchmark_suite(tool, args.bind)
finally:
stop_gunicorn(proc)
else:
# Just benchmark gthread
print("\nBenchmarking gthread worker...")
proc = start_gunicorn(
worker_class='gthread',
workers=args.workers,
threads=args.threads,
connections=args.connections,
bind=args.bind,
)
try:
all_results['gthread'] = run_benchmark_suite(tool, args.bind)
finally:
stop_gunicorn(proc)
# Print summary
print("\n" + "=" * 60)
print("BENCHMARK SUMMARY")
print("=" * 60)
for worker, results in all_results.items():
print(f"\n{worker.upper()} Worker:")
for test, metrics in results.items():
rps = metrics.get('requests_per_sec', 'N/A')
print(f" {test}: {rps} req/s")
if args.output:
with open(args.output, 'w') as f:
json.dump(all_results, f, indent=2)
print(f"\nResults saved to {args.output}")
if __name__ == '__main__':
main()

18
benchmarks/simple_app.py Normal file
View File

@ -0,0 +1,18 @@
# Simple WSGI app for benchmarking
def application(environ, start_response):
"""Basic hello world response."""
path = environ.get('PATH_INFO', '/')
if path == '/large':
body = b'X' * 65536 # 64KB
else:
body = b'Hello, World!'
status = '200 OK'
headers = [
('Content-Type', 'text/plain'),
('Content-Length', str(len(body))),
]
start_response(status, headers)
return [body]

29
docs/README.rst Normal file
View File

@ -0,0 +1,29 @@
Generate Documentation
======================
Requirements
------------
Install the documentation dependencies with::
pip install -r requirements_dev.txt
This provides MkDocs with the Material theme and supporting plugins.
Build static HTML
-----------------
::
mkdocs build
The rendered site is emitted into the ``site/`` directory.
Preview locally
---------------
::
mkdocs serve
This serves the documentation at http://127.0.0.1:8000/ with live reload.

190
docs/content/2010-news.md Normal file
View File

@ -0,0 +1,190 @@
<span id="news-2010"></span>
# Changelog - 2010
## 0.12.0 / 2010-12-22
- Add support for logging configuration using a ini file.
It uses the standard Python logging's module Configuration
file format and allows anyone to use his custom file handler
- Add IPV6 support
- Add multidomain application example
- Improve gunicorn_django command when importing settings module
using DJANGO_SETTINGS_MODULE environment variable
- Send appropriate error status on http parsing
- Fix pidfile, set permissions so other user can read
it and use it.
- Fix temporary file leaking
- Fix setpgrp issue, can now be launched via ubuntu upstart
- Set the number of workers to zero on WINCH
## 0.11.2 / 2010-10-30
* Add SERVER_SOFTWARE to the os.environ
* Add support for django settings environment variable
* Add support for logging configuration in Paster ini-files
* Improve arbiter notification in asynchronous workers
* Display the right error when a worker can't be used
* Fix Django support
* Fix HUP with Paster applications
* Fix readline in wsgi.input
## 0.11.1 / 2010-09-02
* Implement max-requests feature to prevent memory leaks.
* Added 'worker_exit' server hook.
* Reseed the random number generator after fork().
* Improve Eventlet worker.
* Fix Django command `run_gunicorn`.
* Fix the default proc name internal setting.
* Workaround to prevent Gevent worker to segfault on MacOSX.
## 0.11.0 / 2010-08-12
* Improve dramatically performances of Gevent and Eventlet workers
* Optimize HTTP parsing
* Drop Server and Date headers in start_response when provided.
* Fix latency issue in async workers
## 0.10.1 / 2010-08-06
* Improve gevent's workers. Add "egg:gunicorn#gevent_wsgi" worker using
`gevent.wsgi <http://www.gevent.org/gevent.wsgi.html>`_ and
"egg:gunicorn#gevent_pywsgi" worker using `gevent.pywsgi
<http://www.gevent.org/gevent.pywsgi.html>`_ .
**"egg:gunicorn#gevent"** using our own HTTP parser is still here and
is **recommended** for normal uses. Use the "gevent.wsgi" parser if you
need really fast connections and don't need streaming, keepalive or ssl.
* Add pre/post request hooks
* Exit more quietly
* Fix gevent dns issue
## 0.10.0 / 2010-07-08
* New HTTP parser.
* New HUP behaviour. Re-reads the configuration and then reloads all
worker processes without changing the master process id. Helpful for
code reloading and monitoring applications like supervisord and runit.
* Added a preload configuration parameter. By default, application code
is now loaded after a worker forks. This couple with the new HUP
handling can be used for dev servers to do hot code reloading. Using
the preload flag can help a bit in small memory VM's.
* Allow people to pass command line arguments to WSGI applications. See:
`examples/alt_spec.py
<http://github.com/benoitc/gunicorn/raw/master/examples/alt_spec.py>`_
* Added an example gevent reloader configuration:
`examples/example_gevent_reloader.py
<http://github.com/benoitc/gunicorn/blob/master/examples/example_gevent_reloader.py>`_.
* New gevent worker "egg:gunicorn#gevent2", working with gevent.wsgi.
* Internal refactoring and various bug fixes.
* New documentation website.
## 0.9.1 / 2010-05-26
* Support https via X-Forwarded-Protocol or X-Forwarded-Ssl headers
* Fix configuration
* Remove -d options which was used instead of -D for daemon.
* Fix umask in unix socket
## 0.9.0 / 2010-05-24
* Added *when_ready* hook. Called just after the server is started
* Added *preload* setting. Load application code before the worker processes
are forked.
* Refactored Config
* Fix pidfile
* Fix QUIT/HUP in async workers
* Fix reexec
* Documentation improvements
## 0.8.1 / 2010-04-29
* Fix builtins import in config
* Fix installation with pip
* Fix Tornado WSGI support
* Delay application loading until after processing all configuration
## 0.8.0 / 2010-04-22
* Refactored Worker management for better async support. Now use the -k option
to set the type of request processing to use
* Added support for Tornado_
## 0.7.2 / 2010-04-15
* Added --spew option to help debugging (installs a system trace hook)
* Some fixes in async arbiters
* Fix a bug in start_response on error
## 0.7.1 / 2010-04-01
* Fix bug when responses have no body.
## 0.7.0 / 2010-03-26
* Added support for Eventlet_ and Gevent_ based workers.
* Added Websockets_ support
* Fix Chunked Encoding
* Fix SIGWINCH on OpenBSD_
* Fix `PEP 333`_ compliance for the write callable.
## 0.6.5 / 2010-03-11
* Fix pidfile handling
* Fix Exception Error
## 0.6.4 / 2010-03-08
* Use cStringIO for performance when possible.
* Fix worker freeze when a remote connection closes unexpectedly.
## 0.6.3 / 2010-03-07
* Make HTTP parsing faster.
* Various bug fixes
## 0.6.2 / 2010-03-01
* Added support for chunked response.
* Added proc_name option to the config file.
* Improved the HTTP parser. It now uses buffers instead of strings to store
temporary data.
* Improved performance when sending responses.
* Workers are now murdered by age (the oldest is killed first).
## 0.6.1 / 2010-02-24
* Added gunicorn config file support for Django admin command
* Fix gunicorn config file. -c was broken.
* Removed TTIN/TTOU from workers which blocked other signals.
## 0.6.0 / 2010-02-22
* Added setproctitle support
* Change privilege switch behavior. We now work like NGINX, master keeps the
permissions, new uid/gid permissions are only set for workers.
## 0.5.1 / 2010-02-22
* Fix umask
* Added Debian packaging
## 0.5.0 / 2010-02-20
* Added `configuration file <configuration.html>`_ handler.
* Added support for pre/post fork hooks
* Added support for before_exec hook
* Added support for unix sockets
* Added launch of workers processes under different user/group
* Added umask option
* Added SCRIPT_NAME support
* Better support of some exotic settings for Django projects
* Better support of Paste-compatible applications
* Some refactoring to make the code easier to hack
* Allow multiple keys in request and response headers
.. _Tornado: http://www.tornadoweb.org/
.. _`PEP 333`: https://www.python.org/dev/peps/pep-0333/
.. _Eventlet: http://eventlet.net/
.. _Gevent: http://www.gevent.org/
.. _OpenBSD: https://www.openbsd.org/
.. _Websockets: https://html.spec.whatwg.org/multipage/web-sockets.html

66
docs/content/2011-news.md Normal file
View File

@ -0,0 +1,66 @@
<span id="news-2011"></span>
# Changelog - 2011
## 0.13.4 / 2011-09-23
- fix util.closerange function used to prevent leaking fds on python 2.5
(typo.md)
## 0.13.3 / 2011-09-19
- refactor gevent worker
- prevent leaking fds on reexec
- fix inverted request_time computation
## 0.13.2 / 2011-09-17
- Add support for Tornado 2.0 in tornado worker
- Improve access logs: allows customisation of the log format & add
request time
- Logger module is now pluggable
- Improve graceful shutdown in Python versions >= 2.6
- Fix post_request root arity for compatibility
- Fix sendfile support
- Fix Django reloading
## 0.13.1 / 2011-08-22
- Fix unix socket. log argument was missing.
## 0.13.0 / 2011-08-22
- Improve logging: allows file-reopening and add access log file
compatible with the `apache combined log format <http://httpd.apache.org/docs/2.0/logs.html#combined>`_
- Add the possibility to set custom SSL headers. X-Forwarded-Protocol
and X-Forwarded-SSL are still the default
- New `on_reload` hook to customize how gunicorn spawn new workers on
SIGHUP
- Handle projects with relative path in django_gunicorn command
- Preserve path parameters in PATH_INFO
- post_request hook now accepts the environ as argument.
- When stopping the arbiter, close the listener asap.
- Fix Django command `run_gunicorn` in settings reloading
- Fix Tornado_ worker exiting
- Fix the use of sendfile in wsgi.file_wrapper
## 0.12.2 / 2011-05-18
- Add wsgi.file_wrapper optimised for FreeBSD, Linux & MacOSX (use
sendfile if available)
- Fix django run_gunicorn command. Make sure we reload the application
code.
- Fix django localisation
- Compatible with gevent 0.14dev
## 0.12.1 / 2011-03-23
- Add "on_starting" hook. This hook can be used to set anything before
the arbiter really start
- Support bdist_rpm in setup
- Improve content-length handling (pep 3333)
- Improve Django support
- Fix daemonizing (#142)
- Fix ipv6 handling
.. _Tornado: http://www.tornadoweb.org/

117
docs/content/2012-news.md Normal file
View File

@ -0,0 +1,117 @@
<span id="news-2012"></span>
# Changelog - 2012
## 0.17.0 / 2012-12-25
- allows gunicorn to bind to multiple address
- add SSL support
- add syslog support
- add nworkers_changed hook
- add response arg for post_request hook
- parse command line with argparse (replace deprecated optparse)
- fix PWD detection in arbiter
- miscellaneous PEP8 fixes
## 0.16.1 / 2012-11-19
- Fix packaging
## 0.16.0 / 2012-11-19
- **Added support for Python 3.2 & 3.3**
- Expose --pythonpath command to all gunicorn commands
- Honor $PORT environment variable, useful for deployment on heroku
- Removed support for Python 2.5
- Make sure we reopen the logs on the console
- Fix django settings module detection from path
- Reverted timeout for client socket. Fix issue on blocking issues.
- Fixed gevent worker
## 0.15.0 / 2012-10-18
- new documentation site on http://docs.gunicorn.org
- new website on http://gunicorn.org
- add `haproxy PROXY protocol <http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt>`_ support
- add ForwardedAllowIPS option: allows to filter Front-end's IPs
allowed to handle X-Forwarded-* headers.
- add callable hooks for paster config
- add x-forwarded-proto as secure scheme default (Heroku is using this)
- allows gunicorn to load a pre-compiled application
- support file reopening & reexec for all loggers
- initialize the logging config file with defaults.
- set timeout for client socket (slow client DoS).
- NoMoreData, ChunkMissingTerminator, InvalidChunkSize are now
IOError exceptions
- fix graceful shutdown in gevent
- fix limit request line check
## 0.14.6 / 2012-07-26
- fix gevent & subproces
- fix request line length check
- fix keepalive = 0
- fix tornado worker
## 0.14.5 / 2012-06-24
- fix logging during daemonisation
## 0.14.4 / 2012-06-24
- new --graceful-timeout option
- fix multiple issues with request limit
- more fixes in django settings resolutions
- fix gevent.core import
- fix keepalive=0 in eventlet worker
- fix handle_error display with the unix worker
- fix tornado.wsgi.WSGIApplication calling error
- **breaking change**: take the control on graceful reload back.
graceful can't be overridden anymore using the on_reload function.
## 0.14.3 / 2012-05-15
- improvement: performance of http.body.Body.readline()
- improvement: log HTTP errors in access log like Apache
- improvement: display traceback when the worker fails to boot
- improvement: makes gunicorn work with gevent 1.0
- examples: websocket example now supports hybi13
- fix: reopen log files after initialization
- fix: websockets support
- fix: django1.4 support
- fix: only load the paster application 1 time
## 0.14.2 / 2012-03-16
- add validate_class validator: allows to use a class or a method to
initialize the app during in-code configuration
- add support for max_requests in tornado worker
- add support for disabling x_forwarded_for_header in tornado worker
- gevent_wsgi is now an alias of gevent_pywsgi
- Fix gevent_pywsgi worker
## 0.14.1 / 2012-03-02
- fixing source archive, reducing its size
## 0.14.0 / 2012-02-27
- check if Request line is too large: You can now pass the parameter
``--limit-request-line`` or set the ``limit_request_line`` in your
configuration file to set the max size of the request line in bytes.
- limit the number of headers fields and their size. Add
``--limit-request-field`` and ``limit-request-field-size`` settings
- add ``p`` variable to the log access format to log pidfile
- add ``{HeaderName}o`` variable to the logo access format to log the
response header HeaderName
- request header is now logged with the variable ``{HeaderName}i`` in the
access log file
- improve error logging
- support logging.configFile
- support django 1.4 in both gunicorn_django & run_gunicorn command
- improve reload in django run_gunicorn command (should just work now)
- allows people to set the ``X-Forwarded-For`` header key and disable it by
setting an empty string.
- fix support of Tornado
- many other fixes.

83
docs/content/2013-news.md Normal file
View File

@ -0,0 +1,83 @@
<span id="news-2013"></span>
# Changelog - 2013
## 18.0 / 2013-08-26
- new: add ``-e/--env`` command line argument to pass an environment variables to
gunicorn
- new: add ``--chdir`` command line argument to specified directory
before apps loading. - new: add wsgi.file_wrapper support in async workers
- new: add ``--paste`` command line argument to set the paster config file
- deprecated: the command ``gunicorn_django`` is now deprecated. You should now
run your application with the WSGI interface installed with your project (see
https://docs.djangoproject.com/en/1.4/howto/deployment/wsgi/gunicorn/) for
more infos.
- deprecated: the command ``gunicorn_paste`` is deprecated. You now should use
the new ``--paste`` argument to set the configuration file of your paster
application.
- fix: Removes unmatched leading quote from the beginning of the default access
log format string
- fix: null timeout
- fix: gevent worker
- fix: don't reload the paster app when using pserve
- fix: after closing for error do not keep alive the connection
- fix: responses 1xx, 204 and 304 should not force the connection to be closed
## 17.5 / 2013-07-03
- new: add signals documentation
- new: add post_worker_init hook for workers
- new: try to use gunicorn.conf.py in current folder as the default
config file.
- fix graceful timeout with the Eventlet worker
- fix: don't raise an error when closing the socket if already closed
- fix: fix --settings parameter for django application and try to find
the django settings when using the ``gunicorn`` command.
- fix: give the initial global_conf to paster application
- fix: fix 'Expect: 100-continue' support on Python 3
### New versioning:
With this release, the versioning of Gunicorn is changing. Gunicorn is
stable since a long time and there is no point to release a "1.0" now.
It should have been done since a long time. 0.17 really meant it was the
17th stable version. From the beginning we have only 2 kind of
releases:
major release: releases with major changes or huge features added
services releases: fixes and minor features added So from now we will
apply the following versioning ``<major>.<service>``. For example ``17.5`` is a
service release.
## 0.17.4 / 2013-04-24
- fix unix socket address parsing
## 0.17.3 / 2013-04-23
- add systemd sockets support
- add ``python -m gunicorn.app.wsgiapp`` support
- improve logger class inheritance
- exit when the config file isn't found
- add the -R option to enable stdio inheritance in daemon mode
- don't close file descriptors > 3 in daemon mode
- improve STDOUT/STDERR logging
- fix pythonpath option
- fix pidfile creation on Python 3
- fix gevent worker exit
- fix ipv6 detection when the platform isn't supporting it
## 0.17.2 / 2013-01-07
- optimize readline
- make imports errors more visible when loading an app or a logging
class
- fix tornado worker: don't pass ssl options if there are none
- fix PEP3333: accept only bytetrings in the response body
- fix support on CYGWIN platforms
## 0.17.1 / 2013-01-05
- add syslog facility name setting
- fix ``--version`` command line argument
- fix wsgi url_scheme for https

202
docs/content/2014-news.md Normal file
View File

@ -0,0 +1,202 @@
<span id="news-2014"></span>
# Changelog - 2014
!!! note
Please see [news](news.md) for the latest changes.
## 19.1.1 / 2014-08-16
### Changes
### Core
- fix [Issue #835](https://github.com/benoitc/gunicorn/issues/835): display correct pid of already running instance
- fix [PR #833](https://github.com/benoitc/gunicorn/pull/833): fix `PyTest` class in setup.py.
### Logging
- fix [Issue #838](https://github.com/benoitc/gunicorn/issues/838): statsd logger, send statsd timing metrics in milliseconds
- fix [Issue #839](https://github.com/benoitc/gunicorn/issues/839): statsd logger, allows for empty log message while pushing
metrics and restore worker number in DEBUG logs
- fix [Issue #850](https://github.com/benoitc/gunicorn/issues/850): add timezone to logging
- fix [Issue #853](https://github.com/benoitc/gunicorn/issues/853): Respect logger_class setting unless statsd is on
### AioHttp worker
- fix [Issue #830](https://github.com/benoitc/gunicorn/issues/830) make sure gaiohttp worker is shipped with gunicorn.
## 19.1 / 2014-07-26
### Changes
### Core
- fix [Issue #785](https://github.com/benoitc/gunicorn/issues/785): handle binary type address given to a client socket address
- fix graceful shutdown. make sure QUIT and TERMS signals are switched everywhere.
- [Issue #799](https://github.com/benoitc/gunicorn/issues/799): fix support loading config from module
- [Issue #805](https://github.com/benoitc/gunicorn/issues/805): fix check for file-like objects
- fix [Issue #815](https://github.com/benoitc/gunicorn/issues/815): args validation in WSGIApplication.init
- fix [Issue #787](https://github.com/benoitc/gunicorn/issues/787): check if we load a pyc file or not.
### Tornado worker
- fix [Issue #771](https://github.com/benoitc/gunicorn/issues/771): support tornado 4.0
- fix [Issue #783](https://github.com/benoitc/gunicorn/issues/783): x_headers error. The x-forwarded-headers option has been removed
in `c4873681299212d6082cd9902740eef18c2f14f1
<https://github.com/benoitc/gunicorn/commit/c4873681299212d6082cd9902740eef18c2f14f1>`_.
The discussion is available on [PR #633](https://github.com/benoitc/gunicorn/pull/633).
### AioHttp worker
- fix: fetch all body in input. fix [Issue #803](https://github.com/benoitc/gunicorn/issues/803)
- fix: don't install the worker if python < 3.3
- fix [Issue #822](https://github.com/benoitc/gunicorn/issues/822): Support UNIX sockets in gaiohttp worker
### Async worker
- fix [Issue #790](https://github.com/benoitc/gunicorn/issues/790): StopIteration shouldn't be caught at this level.
### Logging
- add statsd logging handler fix [Issue #748](https://github.com/benoitc/gunicorn/issues/748)
### Paster
- fix [Issue #809](https://github.com/benoitc/gunicorn/issues/809): Set global logging configuration from a Paste config.
### Extra
- fix RuntimeError in gunicorn.reloader ([Issue #807](https://github.com/benoitc/gunicorn/issues/807))
### Documentation
- update faq: put a note on how `watch logs in the console
<http://docs.gunicorn.org/en/latest/faq.html#why-i-don-t-see-any-logs-in-the-console>`_
since many people asked for it.
## 19.0 / 2014-06-12
Gunicorn 19.0 is a major release with new features and fixes. This
version improve a lot the usage of Gunicorn with python 3 by adding `two
new workers <http://docs.gunicorn.org/en/latest/design.html#asyncio-workers>`_
to it: `gthread` a fully threaded async worker using futures and `gaiohttp` a
worker using asyncio.
### Breaking Changes
### Switch QUIT and TERM signals
With this change, when gunicorn receives a QUIT all the workers are
killed immediately and exit and TERM is used for the graceful shutdown.
Note: the old behaviour was based on the NGINX but the new one is more
correct according the following doc:
https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html
also it is complying with the way the signals are sent by heroku:
https://devcenter.heroku.com/articles/python-faq#what-constraints-exist-when-developing-applications-on-heroku
### Deprecations
`run_gunicorn`, `gunicorn_django` and `gunicorn_paster` are now
completely deprecated and will be removed in the next release. Use the
`gunicorn` command instead.
### Changes
### core
- add aiohttp worker named `gaiohttp` using asyncio. Full async worker
on python 3.
- fix HTTP-violating excess whitespace in write_error output
- fix: try to log what happened in the worker after a timeout, add a
`worker_abort` hook on SIGABRT signal.
- fix: save listener socket name in workers so we can handle buffered
keep-alive requests after the listener has closed.
- add on_exit hook called just before exiting gunicorn.
- add support for python 3.4
- fix: do not swallow unexpected errors when reaping
- fix: remove incompatible SSL option with python 2.6
- add new async gthread worker and `--threads` options allows to set multiple
threads to listen on connection
- deprecate `gunicorn_django` and `gunicorn_paster`
- switch QUIT and TERM signal
- reap workers in SIGCHLD handler
- add universal wheel support
- use `email.utils.formatdate` in gunicorn.util.http_date
- deprecate the `--debug` option
- fix: log exceptions that occur after response start …
- allows loading of applications from `.pyc` files (#693)
- fix: issue #691, raw_env config file parsing
- use a dynamic timeout to wait for the optimal time. (Reduce power
usage)
- fix python3 support when notifying the arbiter
- add: honor $WEB_CONCURRENCY environment variable. Useful for heroku
setups.
- add: include tz offset in access log
- add: include access logs in the syslog handler.
- add --reload option for code reloading
- add the capability to load `gunicorn.base.Application` without the loading of
the arguments of the command line. It allows you to [embed gunicorn in your own application](custom.md).
- improve: set wsgi.multithread to True for async workers
- fix logging: make sure to redirect wsgi.errors when needed
- add: syslog logging can now be done to a unix socket
- fix logging: don't try to redirect stdout/stderr to the logfile.
- fix logging: don't propagate log
- improve logging: file option can be overridden by the gunicorn options
`--error-logfile` and `--access-logfile` if they are given.
- fix: don't override SERVER_* by the Host header
- fix: handle_error
- add more option to configure SSL
- fix: sendfile with SSL
- add: worker_int callback (to react on SIGTERM)
- fix: don't depend on entry point for internal classes, now absolute
modules path can be given.
- fix: Error messages are now encoded in latin1
- fix: request line length check
- improvement: proxy_allow_ips: Allow proxy protocol if "*" specified
- fix: run worker's `setup` method before setting num_workers
- fix: FileWrapper inherit from `object` now
- fix: Error messages are now encoded in latin1
- fix: don't spam the console on SIGWINCH.
- fix: logging -don't stringify T and D logging atoms (#621)
- add support for the latest django version
- deprecate `run_gunicorn` django option
- fix: sys imported twice
### gevent worker
- fix: make sure to stop all listeners
- fix: monkey patching is now done in the worker
- fix: "global name 'hub' is not defined"
- fix: reinit `hub` on old versions of gevent
- support gevent 1.0
- fix: add subprocess in monkey patching
- fix: add support for multiple listener
### eventlet worker
- fix: merge duplicate EventletWorker.init_process method (fixes #657)
- fix: missing errno import for eventlet sendfile patch
- fix: add support for multiple listener
### tornado worker
- add graceful stop support

187
docs/content/2015-news.md Normal file
View File

@ -0,0 +1,187 @@
<span id="news-2015"></span>
# Changelog - 2015
!!! note
Please see [news](news.md) for the latest changes.
## 19.4.3 / 2015/12/30
- fix: don't check if a file is writable using os.stat with SELINUX ([Issue #1171](https://github.com/benoitc/gunicorn/issues/1171))
## 19.4.2 / 2015/12/29
### Core
- improvement: handle HaltServer in manage_workers ([Issue #1095](https://github.com/benoitc/gunicorn/issues/1095))
- fix: Do not rely on sendfile sending requested count ([Issue #1155](https://github.com/benoitc/gunicorn/issues/1155))
- fix: claridy --no-sendfile default ([Issue #1156](https://github.com/benoitc/gunicorn/issues/1156))
- fix: LoggingCatch sendfile failure from no file descriptor ([Issue #1160](https://github.com/benoitc/gunicorn/issues/1160))
### Logging
- fix: Always send access log to syslog if syslog is on
- fix: check auth before trying to own a file ([Issue #1157](https://github.com/benoitc/gunicorn/issues/1157))
### Documentation
- fix: Fix Slowloris broken link. ([Issue #1142](https://github.com/benoitc/gunicorn/issues/1142))
- Tweak markup in faq.rst
### Testing
- fix: gaiohttp test ([Issue #1164](https://github.com/benoitc/gunicorn/issues/1164))
## 19.4.1 / 2015/11/25
- fix tornado worker ([Issue #1154](https://github.com/benoitc/gunicorn/issues/1154))
## 19.4.0 / 2015/11/20
### Core
- fix: make sure that a user is able to access to the logs after dropping a
privilege ([Issue #1116](https://github.com/benoitc/gunicorn/issues/1116))
- improvement: inherit the `Exception` class where it needs to be ([Issue #997](https://github.com/benoitc/gunicorn/issues/997))
- fix: make sure headers are always encoded as latin1 RFC 2616 ([Issue #1102](https://github.com/benoitc/gunicorn/issues/1102))
- improvement: reduce arbiter noise ([Issue #1078](https://github.com/benoitc/gunicorn/issues/1078))
- fix: don't close the unix socket when the worker exit ([Issue #1088](https://github.com/benoitc/gunicorn/issues/1088))
- improvement: Make last logged worker count an explicit instance var ([Issue #1078](https://github.com/benoitc/gunicorn/issues/1078))
- improvement: prefix config file with its type ([Issue #836](https://github.com/benoitc/gunicorn/issues/836))
- improvement: pidfile handing ([Issue #1042](https://github.com/benoitc/gunicorn/issues/1042))
- fix: catch OSError as well as ValueError on race condition ([Issue #1052](https://github.com/benoitc/gunicorn/issues/1052))
- improve support of ipv6 by backporting urlparse.urlsplit from Python 2.7 to
Python 2.6.
- fix: raise InvalidRequestLine when the line contains malicious data
([Issue #1023](https://github.com/benoitc/gunicorn/issues/1023))
- fix: fix argument to disable sendfile
- fix: add gthread to the list of supported workers ([Issue #1011](https://github.com/benoitc/gunicorn/issues/1011))
- improvement: retry socket binding up to five times upon EADDRNOTAVAIL
([Issue #1004](https://github.com/benoitc/gunicorn/issues/1004))
- **breaking change**: only honor headers that can be encoded in ascii to comply to
the RFC 7230 (See [Issue #1151](https://github.com/benoitc/gunicorn/issues/1151)).
### Logging
- add new parameters to access log ([Issue #1132](https://github.com/benoitc/gunicorn/issues/1132))
- fix: make sure that files handles are correctly reopened on HUP
([Issue #627](https://github.com/benoitc/gunicorn/issues/627))
- include request URL in error message ([Issue #1071](https://github.com/benoitc/gunicorn/issues/1071))
- get username in access logs ([Issue #1069](https://github.com/benoitc/gunicorn/issues/1069))
- fix statsd logging support on Python 3 ([Issue #1010](https://github.com/benoitc/gunicorn/issues/1010))
### Testing
- use last version of mock.
- many fixes in Travis CI support
- miscellaneous improvements in tests
### Thread worker
- fix: Fix self.nr usage in ThreadedWorker so that auto restart works as
expected ([Issue #1031](https://github.com/benoitc/gunicorn/issues/1031))
### Gevent worker
- fix quit signal handling ([Issue #1128](https://github.com/benoitc/gunicorn/issues/1128))
- add support for Python 3 ([Issue #1066](https://github.com/benoitc/gunicorn/issues/1066))
- fix: make graceful shutdown thread-safe ([Issue #1032](https://github.com/benoitc/gunicorn/issues/1032))
### Tornado worker
- fix ssl options ([Issue #1146](https://github.com/benoitc/gunicorn/issues/1146), [Issue #1135](https://github.com/benoitc/gunicorn/issues/1135))
- don't check timeout when stopping gracefully ([Issue #1106](https://github.com/benoitc/gunicorn/issues/1106))
### AIOHttp worker
- add SSL support ([Issue #1105](https://github.com/benoitc/gunicorn/issues/1105))
### Documentation
- fix link to proc name setting ([Issue #1144](https://github.com/benoitc/gunicorn/issues/1144))
- fix worker class documentation ([Issue #1141](https://github.com/benoitc/gunicorn/issues/1141), [Issue #1104](https://github.com/benoitc/gunicorn/issues/1104))
- clarify graceful timeout documentation ([Issue #1137](https://github.com/benoitc/gunicorn/issues/1137))
- don't duplicate NGINX config files examples ([Issue #1050](https://github.com/benoitc/gunicorn/issues/1050), [Issue #1048](https://github.com/benoitc/gunicorn/issues/1048))
- add `web.py` framework example ([Issue #1117](https://github.com/benoitc/gunicorn/issues/1117))
- update Debian/Ubuntu installations instructions ([Issue #1112](https://github.com/benoitc/gunicorn/issues/1112))
- clarify `pythonpath` setting description ([Issue #1080](https://github.com/benoitc/gunicorn/issues/1080))
- tweak some example for python3
- clarify `sendfile` documentation
- miscellaneous typos in source code comments (thanks!)
- clarify why REMOTE_ADD may not be the user's IP address ([Issue #1037](https://github.com/benoitc/gunicorn/issues/1037))
### Misc
- fix: reloader should survive SyntaxError ([Issue #994](https://github.com/benoitc/gunicorn/issues/994))
- fix: expose the reloader class to the worker.
## 19.3.0 / 2015/03/06
### Core
- fix: [Issue #978](https://github.com/benoitc/gunicorn/issues/978) make sure a listener is inheritable
- add `check_config` class method to workers
- fix: [Issue #983](https://github.com/benoitc/gunicorn/issues/983) fix select timeout in sync worker with multiple
connections
- allows workers to access to the reloader. close [Issue #984](https://github.com/benoitc/gunicorn/issues/984)
- raise TypeError instead of AssertionError
### Logging
- make Logger.loglevel a class attribute
### Documentation
- fix: [Issue #988](https://github.com/benoitc/gunicorn/issues/988) fix syntax errors in examples/gunicorn_rc
## 19.2.1 / 2015/02/4
### Logging
- expose loglevel in the Logger class
### AsyncIO worker (gaiohttp.md)
- fix [Issue #977](https://github.com/benoitc/gunicorn/issues/977) fix initial crash
### Documentation
- document security mailing-list in the contributing page.
## 19.2 / 2015/01/30
### Core
- optimize the sync workers when listening on a single interface
- add `--sendfile` settings to enable/disable sendfile. fix [Issue #856](https://github.com/benoitc/gunicorn/issues/856) .
- add the selectors module to the code base. [Issue #886](https://github.com/benoitc/gunicorn/issues/886)
- add `--max-requests-jitter` setting to set the maximum jitter to add to the
max-requests setting.
- fix [Issue #899](https://github.com/benoitc/gunicorn/issues/899) propagate proxy_protocol_info to keep-alive requests
- fix [Issue #863](https://github.com/benoitc/gunicorn/issues/863) worker timeout: dynamic timeout has been removed
- fix: Avoid world writable file
### Logging
- fix [Issue #941](https://github.com/benoitc/gunicorn/issues/941) set logconfig default to paster more trivially
- add statsd-prefix config setting: set the prefix to use when emitting statsd
metrics
- [Issue #832](https://github.com/benoitc/gunicorn/issues/832) log to console by default
### Thread Worker
- fix [Issue #908](https://github.com/benoitc/gunicorn/issues/908) make sure the worker can continue to accept requests
### Eventlet Worker
- fix [Issue #867](https://github.com/benoitc/gunicorn/issues/867) Fix eventlet shutdown to actively shut down the workers.
### Documentation
Many improvements and fixes have been done, see the detailed changelog for
more information.

79
docs/content/2016-news.md Normal file
View File

@ -0,0 +1,79 @@
<span id="news-2016"></span>
# Changelog - 2016
!!! note
Please see [news](news.md) for the latest changes
## 19.6.0 / 2016/05/21
### Core & Logging
- improvement of the binary upgrade behaviour using USR2: remove file locking ([Issue #1270](https://github.com/benoitc/gunicorn/issues/1270))
- add the ``--capture-output`` setting to capture stdout/stderr tot the log
file ([Issue #1271](https://github.com/benoitc/gunicorn/issues/1271))
- Allow disabling ``sendfile()`` via the ``SENDFILE`` environment variable
([Issue #1252](https://github.com/benoitc/gunicorn/issues/1252))
- fix reload under pycharm ([Issue #1129](https://github.com/benoitc/gunicorn/issues/1129))
### Workers
- fix: make sure to remove the signal from the worker pipe ([Issue #1269](https://github.com/benoitc/gunicorn/issues/1269))
- fix: **gthread** worker, handle removed socket in the select loop
([Issue #1258](https://github.com/benoitc/gunicorn/issues/1258))
## 19.5.0 / 2016/05/10
### Core
- fix: Ensure response to HEAD request won't have message body
- fix: lock domain socket and remove on last arbiter exit ([Issue #1220](https://github.com/benoitc/gunicorn/issues/1220))
- improvement: use EnvironmentError instead of socket.error ([Issue #939](https://github.com/benoitc/gunicorn/issues/939))
- add: new ``FORWARDED_ALLOW_IPS`` environment variable ([Issue #1205](https://github.com/benoitc/gunicorn/issues/1205))
- fix: infinite recursion when destroying sockets ([Issue #1219](https://github.com/benoitc/gunicorn/issues/1219))
- fix: close sockets on shutdown ([Issue #922](https://github.com/benoitc/gunicorn/issues/922))
- fix: clean up sys.exc_info calls to drop circular refs ([Issue #1228](https://github.com/benoitc/gunicorn/issues/1228))
- fix: do post_worker_init after load_wsgi ([Issue #1248](https://github.com/benoitc/gunicorn/issues/1248))
### Workers
- fix access logging in gaiohttp worker ([Issue #1193](https://github.com/benoitc/gunicorn/issues/1193))
- eventlet: handle QUIT in a new coroutine ([Issue #1217](https://github.com/benoitc/gunicorn/issues/1217))
- gevent: remove obsolete exception clauses in run ([Issue #1218](https://github.com/benoitc/gunicorn/issues/1218))
- tornado: fix extra "Server" response header ([Issue #1246](https://github.com/benoitc/gunicorn/issues/1246))
- fix: unblock the wait loop under python 3.5 in sync worker ([Issue #1256](https://github.com/benoitc/gunicorn/issues/1256))
### Logging
- fix: log message for listener reloading ([Issue #1181](https://github.com/benoitc/gunicorn/issues/1181))
- Let logging module handle traceback printing ([Issue #1201](https://github.com/benoitc/gunicorn/issues/1201))
- improvement: Allow configuring logger_class with statsd_host ([Issue #1188](https://github.com/benoitc/gunicorn/issues/1188))
- fix: traceback formatting ([Issue #1235](https://github.com/benoitc/gunicorn/issues/1235))
- fix: print error logs on stderr and access logs on stdout ([Issue #1184](https://github.com/benoitc/gunicorn/issues/1184))
### Documentation
- Simplify installation instructions in gunicorn.org ([Issue #1072](https://github.com/benoitc/gunicorn/issues/1072))
- Fix URL and default worker type in example_config ([Issue #1209](https://github.com/benoitc/gunicorn/issues/1209))
- update django doc url to 1.8 lts ([Issue #1213](https://github.com/benoitc/gunicorn/issues/1213))
- fix: miscellaneous wording corrections ([Issue #1216](https://github.com/benoitc/gunicorn/issues/1216))
- Add PSF License Agreement of selectors.py to NOTICE (:issue: `1226`)
- document LOGGING overriding ([Issue #1051](https://github.com/benoitc/gunicorn/issues/1051))
- put a note that error logs are only errors from Gunicorn ([Issue #1124](https://github.com/benoitc/gunicorn/issues/1124))
- add a note about the requirements of the threads workers under python 2.x ([Issue #1200](https://github.com/benoitc/gunicorn/issues/1200))
- add access_log_format to config example ([Issue #1251](https://github.com/benoitc/gunicorn/issues/1251))
### Tests
- Use more pytest.raises() in test_http.py
## 19.4.5 / 2016/01/05
- fix: NameError fileno in gunicorn.http.wsgi ([Issue #1178](https://github.com/benoitc/gunicorn/issues/1178))
## 19.4.4 / 2016/01/04
- fix: check if a fileobject can be used with sendfile(2.md) ([Issue #1174](https://github.com/benoitc/gunicorn/issues/1174))
- doc: be more descriptive in errorlog option ([Issue #1173](https://github.com/benoitc/gunicorn/issues/1173))

42
docs/content/2017-news.md Normal file
View File

@ -0,0 +1,42 @@
<span id="news-2017"></span>
# Changelog - 2017
!!! note
Please see [news](news.md) for the latest changes
## 19.7.1 / 2017/03/21
- fix: continue if SO_REUSEPORT seems to be available but fails ([Issue #1480](https://github.com/benoitc/gunicorn/issues/1480))
- fix: support non-decimal values for the umask command line option ([Issue #1325](https://github.com/benoitc/gunicorn/issues/1325))
## 19.7.0 / 2017/03/01
- The previously deprecated ``gunicorn_django`` command has been removed.
Use the [gunicorn-cmd](run.md#gunicorn) command-line interface instead.
- The previously deprecated ``django_settings`` setting has been removed.
Use the [raw-env](reference/settings.md#raw_env) setting instead.
- The default value of [ssl-version](reference/settings.md#ssl_version) has been changed from
``ssl.PROTOCOL_TLSv1`` to ``ssl.PROTOCOL_SSLv23``.
- fix: initialize the group access list when initgroups is set ([Issue #1297](https://github.com/benoitc/gunicorn/issues/1297))
- add environment variables to gunicorn access log format ([Issue #1291](https://github.com/benoitc/gunicorn/issues/1291))
- add --paste-global-conf option ([Issue #1304](https://github.com/benoitc/gunicorn/issues/1304))
- fix: print access logs to STDOUT ([Issue #1184](https://github.com/benoitc/gunicorn/issues/1184))
- remove upper limit on max header size config ([Issue #1313](https://github.com/benoitc/gunicorn/issues/1313))
- fix: print original exception on AppImportError ([Issue #1334](https://github.com/benoitc/gunicorn/issues/1334))
- use SO_REUSEPORT if available ([Issue #1344](https://github.com/benoitc/gunicorn/issues/1344))
- `fix leak <https://github.com/benoitc/gunicorn/commit/b4c41481e2d5ef127199a4601417a6819053c3fd>`_ of duplicate file descriptor for bound sockets.
- add --reload-engine option, support inotify and other backends ([Issue #1368](https://github.com/benoitc/gunicorn/issues/1368), [Issue #1459](https://github.com/benoitc/gunicorn/issues/1459))
- fix: reject request with invalid HTTP versions
- add ``child_exit`` callback ([Issue #1394](https://github.com/benoitc/gunicorn/issues/1394))
- add support for eventlets _AlreadyHandled object ([Issue #1406](https://github.com/benoitc/gunicorn/issues/1406))
- format boot tracebacks properly with reloader ([Issue #1408](https://github.com/benoitc/gunicorn/issues/1408))
- refactor socket activation and fd inheritance for better support of SystemD ([Issue #1310](https://github.com/benoitc/gunicorn/issues/1310))
- fix: o fds are given by default in gunicorn ([Issue #1423](https://github.com/benoitc/gunicorn/issues/1423))
- add ability to pass settings to GUNICORN_CMD_ARGS environment variable which helps in container world ([Issue #1385](https://github.com/benoitc/gunicorn/issues/1385))
- fix: catch access denied to pid file ([Issue #1091](https://github.com/benoitc/gunicorn/issues/1091))
- many additions and improvements to the documentation
### Breaking Change
- **Python 2.6.0** is the last supported version

64
docs/content/2018-news.md Normal file
View File

@ -0,0 +1,64 @@
<span id="news-2018"></span>
# Changelog - 2018
!!! note
Please see [news](news.md) for the latest changes
## 19.9.0 / 2018/07/03
- fix: address a regression that prevented syslog support from working
([Issue #1668](https://github.com/benoitc/gunicorn/issues/1668), [PR #1773](https://github.com/benoitc/gunicorn/pull/1773))
- fix: correctly set `REMOTE_ADDR` on versions of Python 3 affected by
`Python Issue 30205 <https://bugs.python.org/issue30205>`_
([Issue #1755](https://github.com/benoitc/gunicorn/issues/1755), [PR #1796](https://github.com/benoitc/gunicorn/pull/1796))
- fix: show zero response length correctly in access log ([PR #1787](https://github.com/benoitc/gunicorn/pull/1787))
- fix: prevent raising `AttributeError` when ``--reload`` is not passed
in case of a `SyntaxError` raised from the WSGI application.
([Issue #1805](https://github.com/benoitc/gunicorn/issues/1805), [PR #1806](https://github.com/benoitc/gunicorn/pull/1806))
- The internal module ``gunicorn.workers.async`` was renamed to ``gunicorn.workers.base_async``
since ``async`` is now a reserved word in Python 3.7.
([PR #1527](https://github.com/benoitc/gunicorn/pull/1527))
## 19.8.1 / 2018/04/30
- fix: secure scheme headers when bound to a unix socket
([Issue #1766](https://github.com/benoitc/gunicorn/issues/1766), [PR #1767](https://github.com/benoitc/gunicorn/pull/1767))
## 19.8.0 / 2018/04/28
- Eventlet 0.21.0 support ([Issue #1584](https://github.com/benoitc/gunicorn/issues/1584))
- Tornado 5 support ([Issue #1728](https://github.com/benoitc/gunicorn/issues/1728), [PR #1752](https://github.com/benoitc/gunicorn/pull/1752))
- support watching additional files with ``--reload-extra-file``
([PR #1527](https://github.com/benoitc/gunicorn/pull/1527))
- support configuring logging with a dictionary with ``--logging-config-dict``
([Issue #1087](https://github.com/benoitc/gunicorn/issues/1087), [PR #1110](https://github.com/benoitc/gunicorn/pull/1110), [PR #1602](https://github.com/benoitc/gunicorn/pull/1602))
- add support for the ``--config`` flag in the ``GUNICORN_CMD_ARGS`` environment
variable ([Issue #1576](https://github.com/benoitc/gunicorn/issues/1576), [PR #1581](https://github.com/benoitc/gunicorn/pull/1581))
- disable ``SO_REUSEPORT`` by default and add the ``--reuse-port`` setting
([Issue #1553](https://github.com/benoitc/gunicorn/issues/1553), [Issue #1603](https://github.com/benoitc/gunicorn/issues/1603), [PR #1669](https://github.com/benoitc/gunicorn/pull/1669))
- fix: installing `inotify` on MacOS no longer breaks the reloader
([Issue #1540](https://github.com/benoitc/gunicorn/issues/1540), [PR #1541](https://github.com/benoitc/gunicorn/pull/1541))
- fix: do not throw ``TypeError`` when ``SO_REUSEPORT`` is not available
([Issue #1501](https://github.com/benoitc/gunicorn/issues/1501), [PR #1491](https://github.com/benoitc/gunicorn/pull/1491))
- fix: properly decode HTTP paths containing certain non-ASCII characters
([Issue #1577](https://github.com/benoitc/gunicorn/issues/1577), [PR #1578](https://github.com/benoitc/gunicorn/pull/1578))
- fix: remove whitespace when logging header values under gevent ([PR #1607](https://github.com/benoitc/gunicorn/pull/1607))
- fix: close unlinked temporary files ([Issue #1327](https://github.com/benoitc/gunicorn/issues/1327), [PR #1428](https://github.com/benoitc/gunicorn/pull/1428))
- fix: parse ``--umask=0`` correctly ([Issue #1622](https://github.com/benoitc/gunicorn/issues/1622), [PR #1632](https://github.com/benoitc/gunicorn/pull/1632))
- fix: allow loading applications using relative file paths
([Issue #1349](https://github.com/benoitc/gunicorn/issues/1349), [PR #1481](https://github.com/benoitc/gunicorn/pull/1481))
- fix: force blocking mode on the gevent sockets ([Issue #880](https://github.com/benoitc/gunicorn/issues/880), [PR #1616](https://github.com/benoitc/gunicorn/pull/1616))
- fix: preserve leading `/` in request path ([Issue #1512](https://github.com/benoitc/gunicorn/issues/1512), [PR #1511](https://github.com/benoitc/gunicorn/pull/1511))
- fix: forbid contradictory secure scheme headers
- fix: handle malformed basic authentication headers in access log
([Issue #1683](https://github.com/benoitc/gunicorn/issues/1683), [PR #1684](https://github.com/benoitc/gunicorn/pull/1684))
- fix: defer handling of ``USR1`` signal to a new greenlet under gevent
([Issue #1645](https://github.com/benoitc/gunicorn/issues/1645), [PR #1651](https://github.com/benoitc/gunicorn/pull/1651))
- fix: the threaded worker would sometimes close the wrong keep-alive
connection under Python 2 ([Issue #1698](https://github.com/benoitc/gunicorn/issues/1698), [PR #1699](https://github.com/benoitc/gunicorn/pull/1699))
- fix: re-open log files on ``USR1`` signal using ``handler._open`` to
support subclasses of ``FileHandler`` ([Issue #1739](https://github.com/benoitc/gunicorn/issues/1739), [PR #1742](https://github.com/benoitc/gunicorn/pull/1742))
- deprecation: the ``gaiohttp`` worker is deprecated, see the
[worker-class](reference/settings.md#worker_class) documentation for more information
([Issue #1338](https://github.com/benoitc/gunicorn/issues/1338), [PR #1418](https://github.com/benoitc/gunicorn/pull/1418), [PR #1569](https://github.com/benoitc/gunicorn/pull/1569))

112
docs/content/2019-news.md Normal file
View File

@ -0,0 +1,112 @@
<span id="news-2019"></span>
# Changelog - 2019
!!! note
Please see [news](news.md) for the latest changes
## 20.0.4 / 2019/11/26
- fix binding a socket using the file descriptor
- remove support for the `bdist_rpm` build
## 20.0.3 / 2019/11/24
- fixed load of a config file without a Python extension
- fixed `socketfromfd.fromfd` when defaults are not set
!!! note
```
## 20.0.2 / 2019/11/23
- fix changelog
## 20.0.1 / 2019/11/23
- fixed the way the config module is loaded. `__file__` is now available
- fixed `wsgi.input_terminated`. It is always true.
- use the highest protocol version of openssl by default
- only support Python >= 3.5
- added `__repr__` method to `Config` instance
- fixed support of AIX platform and musl libc in `socketfromfd.fromfd` function
- fixed support of applications loaded from a factory function
- fixed chunked encoding support to prevent any `request smuggling <https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn>`_
- Capture os.sendfile before patching in gevent and eventlet workers.
fix `RecursionError`.
- removed locking in reloader when adding new files
- load the WSGI application before the loader to pick up all files
{note}
as documented in Flask and other places.
```
## 19.10.0 / 2019/11/23
- unblock select loop during reload of a sync worker
- security fix: http desync attack
- handle `wsgi.input_terminated`
- added support for str and bytes in unix socket addresses
- fixed `max_requests` setting
- headers values are now encoded as LATN1, not ASCII
- fixed `InotifyReloadeder`: handle `module.__file__` is None
- fixed compatibility with tornado 6
- fixed root logging
- Prevent removalof unix sockets from `reuse_port`
- Clear tornado ioloop before os.fork
- Miscellaneous fixes and improvement for linting using Pylint
## 20.0 / 2019/10/30
- Fixed `fdopen` `RuntimeWarning` in Python 3.8
- Added check and exception for str type on value in Response process_headers method.
- Ensure WSGI header value is string before conducting regex search on it.
- Added pypy3 to list of tested environments
- Grouped `StopIteration` and `KeyboardInterrupt` exceptions with same body together in Arbiter.run()
- Added `setproctitle` module to `extras_require` in setup.py
- Avoid unnecessary chown of temporary files
- Logging: Handle auth type case insensitively
- Removed `util.import_module`
- Removed fallback for `types.SimpleNamespace` in tests utils
- Use `SourceFileLoader` instead instead of `execfile_`
- Use `importlib` instead of `__import__` and eval`
- Fixed eventlet patching
- Added optional `datadog <https://www.datadoghq.com>`_ tags for statsd metrics
- Header values now are encoded using latin-1, not ascii.
- Rewritten `parse_address` util added test
- Removed redundant super() arguments
- Simplify `futures` import in gthread module
- Fixed worker_connections` setting to also affects the Gthread worker type
- Fixed setting max_requests
- Bump minimum Eventlet and Gevent versions to 0.24 and 1.4
- Use Python default SSL cipher list by default
- handle `wsgi.input_terminated` extension
- Simplify Paste Deployment documentation
- Fix root logging: root and logger are same level.
- Fixed typo in ssl_version documentation
- Documented systemd deployment unit examples
- Added systemd sd_notify support
- Fixed typo in gthread.py
- Added `tornado <https://www.tornadoweb.org/>`_ 5 and 6 support
- Declare our setuptools dependency
- Added support to `--bind` to open file descriptors
- Document how to serve WSGI app modules from Gunicorn
- Provide guidance on X-Forwarded-For access log in documentation
- Add support for named constants in the `--ssl-version` flag
- Clarify log format usage of header & environment in documentation
- Fixed systemd documentation to properly setup gunicorn unix socket
- Prevent removal unix socket for reuse_port
- Fix `ResourceWarning` when reading a Python config module
- Remove unnecessary call to dict keys method
- Support str and bytes for UNIX socket addresses
- fixed `InotifyReloadeder`: handle `module.__file__` is None
- `/dev/shm` as a convenient alternative to making your own tmpfs mount in fchmod FAQ
- fix examples to work on python3
- Fix typo in `--max-requests` documentation
- Clear tornado ioloop before os.fork
- Miscellaneous fixes and improvement for linting using Pylint
### Breaking Change
- Removed gaiohttp worker
- Drop support for Python 2.x
- Drop support for EOL Python 3.2 and 3.3
- Drop support for Paste Deploy server blocks

View File

@ -0,0 +1,7 @@
<span id="news-2020"></span>
# Changelog - 2020
!!! note
Please see [news](news.md) for the latest changes

51
docs/content/2021-news.md Normal file
View File

@ -0,0 +1,51 @@
<span id="news-2021"></span>
# Changelog - 2021
!!! note
Please see [news](news.md) for the latest changes
## 20.1.0 - 2021-02-12
- document WEB_CONCURRENCY is set by, at least, Heroku
- capture peername from accept: Avoid calls to getpeername by capturing the peer name returned by
accept
- log a warning when a worker was terminated due to a signal
- fix tornado usage with latest versions of Django
- add support for python -m gunicorn
- fix systemd socket activation example
- allows to set wsgi application in config file using `wsgi_app`
- document `--timeout = 0`
- always close a connection when the number of requests exceeds the max requests
- Disable keepalive during graceful shutdown
- kill tasks in the gthread workers during upgrade
- fix latency in gevent worker when accepting new requests
- fix file watcher: handle errors when new worker reboot and ensure the list of files is kept
- document the default name and path of the configuration file
- document how variable impact configuration
- document the `$PORT` environment variable
- added milliseconds option to request_time in access_log
- added PIP requirements to be used for example
- remove version from the Server header
- fix sendfile: use `socket.sendfile` instead of `os.sendfile`
- reloader: use absolute path to prevent empty to prevent0 `InotifyError` when a file
is added to the working directory
- Add --print-config option to print the resolved settings at startup.
- remove the `--log-dict-config` CLI flag because it never had a working format
(the `logconfig_dict` setting in configuration files continues to work)
### Breaking changes
- minimum version is Python 3.5
- remove version from the Server header
** Documentation **
** Others **
- miscellaneous changes in the code base to be a better citizen with Python 3
- remove dead code
- fix documentation generation

36
docs/content/2023-news.md Normal file
View File

@ -0,0 +1,36 @@
<span id="news-2023"></span>
# Changelog - 2023
## 21.2.0 - 2023-07-19
- fix thread worker: revert change considering connection as idle .
!!! note
This is fixing the bad file description error.
21.1.0 - 2023-07-18
===================
- fix thread worker: fix socket removal from the queue
## 21.0.1 - 2023-07-17
- fix documentation build
## 21.0.0 - 2023-07-17
- support python 3.11
- fix gevent and eventlet workers
- fix threads support (gththread.md): improve performance and unblock requests
- SSL: now use SSLContext object
- HTTP parser: miscellaneous fixes
- remove unnecessary setuid calls
- fix testing
- improve logging
- miscellaneous fixes to core engine
*** RELEASE NOTE ***
We made this release major to start our new release cycle. More info will be provided on our discussion forum.

58
docs/content/2024-news.md Normal file
View File

@ -0,0 +1,58 @@
<span id="news-2024"></span>
# Changelog - 2024
## 23.0.0 - 2024-08-10
- minor docs fixes ([PR #3217](https://github.com/benoitc/gunicorn/pull/3217), [PR #3089](https://github.com/benoitc/gunicorn/pull/3089), [PR #3167](https://github.com/benoitc/gunicorn/pull/3167))
- worker_class parameter accepts a class ([PR #3079](https://github.com/benoitc/gunicorn/pull/3079))
- fix deadlock if request terminated during chunked parsing ([PR #2688](https://github.com/benoitc/gunicorn/pull/2688))
- permit receiving Transfer-Encodings: compress, deflate, gzip ([PR #3261](https://github.com/benoitc/gunicorn/pull/3261))
- permit Transfer-Encoding headers specifying multiple encodings. note: no parameters, still ([PR #3261](https://github.com/benoitc/gunicorn/pull/3261))
- sdist generation now explicitly excludes sphinx build folder ([PR #3257](https://github.com/benoitc/gunicorn/pull/3257))
- decode bytes-typed status (as can be passed by gevent) as utf-8 instead of raising `TypeError` ([PR #2336](https://github.com/benoitc/gunicorn/pull/2336))
- raise correct Exception when encounting invalid chunked requests ([PR #3258](https://github.com/benoitc/gunicorn/pull/3258))
- the SCRIPT_NAME and PATH_INFO headers, when received from allowed forwarders, are no longer restricted for containing an underscore ([PR #3192](https://github.com/benoitc/gunicorn/pull/3192))
- include IPv6 loopback address ``[::1]`` in default for [forwarded-allow-ips](reference/settings.md#forwarded_allow_ips) and [proxy-allow-ips](reference/settings.md#proxy_allow_ips) ([PR #3192](https://github.com/benoitc/gunicorn/pull/3192))
!!! note
- The SCRIPT_NAME change mitigates a regression that appeared first in the 22.0.0 release
- Review your [forwarded-allow-ips](reference/settings.md#forwarded_allow_ips) setting if you are still not seeing the SCRIPT_NAME transmitted
- Review your [forwarder-headers](reference/settings.md#forwarder_headers) setting if you are missing headers after upgrading from a version prior to 22.0.0
### Breaking changes
- refuse requests where the uri field is empty ([PR #3255](https://github.com/benoitc/gunicorn/pull/3255))
- refuse requests with invalid CR/LR/NUL in heade field values ([PR #3253](https://github.com/benoitc/gunicorn/pull/3253))
- remove temporary ``--tolerate-dangerous-framing`` switch from 22.0 ([PR #3260](https://github.com/benoitc/gunicorn/pull/3260))
- If any of the breaking changes affect you, be aware that now refused requests can post a security problem, especially so in setups involving request pipe-lining and/or proxies.
## 22.0.0 - 2024-04-17
- use `utime` to notify workers liveness
- migrate setup to pyproject.toml
- fix numerous security vulnerabilities in HTTP parser (closing some request smuggling vectors)
- parsing additional requests is no longer attempted past unsupported request framing
- on HTTP versions < 1.1 support for chunked transfer is refused (only used in exploits)
- requests conflicting configured or passed SCRIPT_NAME now produce a verbose error
- Trailer fields are no longer inspected for headers indicating secure scheme
- support Python 3.12
### Breaking changes
- minimum version is Python 3.7
- the limitations on valid characters in the HTTP method have been bounded to Internet Standards
- requests specifying unsupported transfer coding (order.md) are refused by default (rare.md)
- HTTP methods are no longer casefolded by default (IANA method registry contains none affected)
- HTTP methods containing the number sign (#) are no longer accepted by default (rare.md)
- HTTP versions < 1.0 or >= 2.0 are no longer accepted by default (rare, only HTTP/1.1 is supported)
- HTTP versions consisting of multiple digits or containing a prefix/suffix are no longer accepted
- HTTP header field names Gunicorn cannot safely map to variables are silently dropped, as in other software
- HTTP headers with empty field name are refused by default (no legitimate use cases, used in exploits)
- requests with both Transfer-Encoding and Content-Length are refused by default (such a message might indicate an attempt to perform request smuggling)
- empty transfer codings are no longer permitted (reportedly seen with really old & broken proxies)
### Security
- fix CVE-2024-1135

37
docs/content/2026-news.md Normal file
View File

@ -0,0 +1,37 @@
<span id="news-2026"></span>
# Changelog - 2026
## 24.0.0 - 2026-01-23
### New Features
- **ASGI Worker (Beta)**: Native asyncio-based ASGI support for running async Python
frameworks like FastAPI, Starlette, and Quart without external dependencies
([PR #3444](https://github.com/benoitc/gunicorn/pull/3444))
- HTTP/1.1 with keepalive connections
- WebSocket support
- Lifespan protocol for startup/shutdown hooks
- Optional uvloop for improved performance
- New settings: `--asgi-loop`, `--asgi-lifespan`, `--root-path`
- **uWSGI Binary Protocol**: Support for receiving requests from nginx via
`uwsgi_pass` directive, enabling efficient binary protocol communication
([PR #3444](https://github.com/benoitc/gunicorn/pull/3444))
- New settings: `--protocol uwsgi`, `--uwsgi-allow-from`
- **Documentation Migration**: Migrated documentation from Sphinx to MkDocs
with Material theme for improved navigation and mobile experience
([PR #3426](https://github.com/benoitc/gunicorn/pull/3426))
### Changes
- Minimum Python version is now 3.12
- Documentation now hosted at https://gunicorn.org
### Breaking changes
- Dropped support for Python versions before 3.12
!!! warning "ASGI Worker Beta"
The ASGI worker is a beta feature. While tested, the API and behavior
may change in future releases. Please report any issues on GitHub.

22
docs/content/404.md Normal file
View File

@ -0,0 +1,22 @@
# Page Not Found
The page you're looking for doesn't exist or has moved.
<div class="quick-links" style="margin-top: 2rem;">
<a href="/" class="quick-link">
<strong>Home</strong>
<span>Return to the homepage</span>
</a>
<a href="/quickstart/" class="quick-link">
<strong>Quickstart</strong>
<span>Get started with Gunicorn</span>
</a>
<a href="/reference/settings/" class="quick-link">
<strong>Settings</strong>
<span>Configuration reference</span>
</a>
<a href="https://github.com/benoitc/gunicorn/issues" class="quick-link">
<strong>Report Issue</strong>
<span>Let us know about broken links</span>
</a>
</div>

241
docs/content/asgi.md Normal file
View File

@ -0,0 +1,241 @@
# ASGI Worker
!!! warning "Beta Feature"
The ASGI worker is a beta feature introduced in Gunicorn 24.0.0. While it has been tested,
the API and behavior may change in future releases. Please report any issues on
[GitHub](https://github.com/benoitc/gunicorn/issues).
Gunicorn includes a native ASGI worker that enables running async Python web frameworks
like FastAPI, Starlette, and Quart without external dependencies like Uvicorn.
## Quick Start
```bash
# Install gunicorn
pip install gunicorn
# Run an ASGI application
gunicorn myapp:app --worker-class asgi --workers 4
```
For FastAPI applications:
```bash
gunicorn main:app --worker-class asgi --bind 0.0.0.0:8000
```
## Features
The ASGI worker provides:
- **HTTP/1.1** with keepalive connections
- **WebSocket** support for real-time applications
- **Lifespan protocol** for startup/shutdown hooks
- **Optional uvloop** for improved performance
- **SSL/TLS** support
## Configuration
### Worker Class
Set the worker class to `asgi`:
```bash
gunicorn myapp:app --worker-class asgi
```
Or in a configuration file:
```python
# gunicorn.conf.py
worker_class = "asgi"
```
### Event Loop
Control which asyncio event loop implementation to use:
| Value | Description |
|----------|-------------|
| `auto` | Use uvloop if available, otherwise asyncio (default) |
| `asyncio`| Use Python's built-in asyncio event loop |
| `uvloop` | Use uvloop (must be installed separately) |
```bash
gunicorn myapp:app --worker-class asgi --asgi-loop uvloop
```
To use uvloop, install it first:
```bash
pip install uvloop
```
### Lifespan Protocol
The lifespan protocol lets your application run code at startup and shutdown.
This is essential for frameworks that need to initialize database connections,
caches, or background tasks.
| Value | Description |
|--------|-------------|
| `auto` | Detect if app supports lifespan, enable if so (default) |
| `on` | Always run lifespan protocol (fail if unsupported) |
| `off` | Never run lifespan protocol |
```bash
gunicorn myapp:app --worker-class asgi --asgi-lifespan on
```
Example FastAPI application using lifespan:
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup: initialize resources
print("Starting up...")
yield
# Shutdown: cleanup resources
print("Shutting down...")
app = FastAPI(lifespan=lifespan)
```
### Root Path
When running behind a reverse proxy that mounts your application at a subpath,
set `root_path` so your application knows its mount point:
```bash
gunicorn myapp:app --worker-class asgi --root-path /api
```
This is equivalent to the `SCRIPT_NAME` in WSGI applications.
### Worker Connections
Control the maximum number of concurrent connections per worker:
```bash
gunicorn myapp:app --worker-class asgi --worker-connections 1000
```
!!! note
Unlike sync workers, the `--threads` option has no effect on ASGI workers.
Use `--worker-connections` to control concurrency.
## WebSocket Support
The ASGI worker supports WebSocket connections out of the box. No additional
configuration is required.
Example with Starlette:
```python
from starlette.applications import Starlette
from starlette.routing import WebSocketRoute
async def websocket_endpoint(websocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Echo: {data}")
app = Starlette(routes=[
WebSocketRoute("/ws", websocket_endpoint),
])
```
## Production Deployment
### With Nginx
```nginx
upstream gunicorn {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://gunicorn;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket support
location /ws {
proxy_pass http://gunicorn;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
```
### Recommended Settings
For production ASGI deployments:
```python
# gunicorn.conf.py
worker_class = "asgi"
workers = 4 # Number of worker processes
worker_connections = 1000 # Max connections per worker
keepalive = 5 # Keepalive timeout
timeout = 120 # Worker timeout
graceful_timeout = 30 # Graceful shutdown timeout
# Performance tuning
asgi_loop = "auto" # Use uvloop if available
asgi_lifespan = "auto" # Auto-detect lifespan support
```
## Comparison with Other ASGI Servers
| Feature | Gunicorn ASGI | Uvicorn | Hypercorn |
|---------|---------------|---------|-----------|
| Process management | Built-in | External | Built-in |
| HTTP/2 | No | No | Yes |
| WebSocket | Yes | Yes | Yes |
| Lifespan | Yes | Yes | Yes |
| uvloop support | Yes | Yes | Yes |
Gunicorn's ASGI worker provides the same process management, logging, and
configuration capabilities you're familiar with from WSGI deployments.
## Troubleshooting
### Lifespan startup failed
If you see "ASGI lifespan startup failed", your application may not properly
implement the lifespan protocol. Either fix the application or set
`--asgi-lifespan off`.
### Connection limits
If you're hitting connection limits, increase `--worker-connections` or add
more workers with `--workers`.
### Slow responses under load
Try using uvloop for better performance:
```bash
pip install uvloop
gunicorn myapp:app --worker-class asgi --asgi-loop uvloop
```
## See Also
- [Settings Reference](reference/settings.md#asgi_loop) - All ASGI-related settings
- [Deploy](deploy.md) - General deployment guidance
- [Design](design.md) - Worker architecture overview

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
width="740.18402"
height="161.62476"
id="svg4375">
<defs
id="defs4377">
<linearGradient
x1="-403.07309"
y1="-40.681377"
x2="-560.61346"
y2="-32.881535"
id="linearGradient4343"
xlink:href="#linearGradient3354-9"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3354-9">
<stop
id="stop3356-9"
style="stop-color:#959595;stop-opacity:1"
offset="0" />
<stop
id="stop3358-9"
style="stop-color:#cccccc;stop-opacity:1"
offset="1" />
</linearGradient>
<linearGradient
x1="-403.07309"
y1="-40.681377"
x2="-560.61346"
y2="-32.881535"
id="linearGradient3269"
xlink:href="#linearGradient3354-9"
gradientUnits="userSpaceOnUse" />
</defs>
<metadata
id="metadata4380">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(215.61577,-659.72777)"
id="layer1">
<g
transform="matrix(0.95410088,0,0,1.0481072,336.12082,766.65951)"
id="text3109-8-2-3-2"
style="font-size:118.26729584px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#4d4d4d;fill-opacity:1;stroke:none;font-family:Century Gothic;-inkscape-font-specification:Century Gothic Bold">
<path
d="m -192.30737,-56.190303 15.93837,0 0,30.259797 c -2e-5,5.890292 0.40421,9.990375 1.2127,12.300261 0.80844,2.271427 2.09814,4.042355 3.8691,5.3127882 1.8094,1.2704632 4.02306,1.9056873 6.64098,1.9056742 2.61786,1.31e-5 4.83152,-0.6159618 6.64099,-1.8479265 1.84788,-1.2704333 3.21458,-3.1183579 4.10009,-5.5437799 0.65442,-1.809405 0.98166,-5.678498 0.98171,-11.607288 l 0,-30.779526 15.82287,0 0,26.621691 c -6e-5,10.972089 -0.86628,18.479283 -2.59865,22.5216047 -2.11747,4.9278127 -5.23585,8.7199081 -9.35513,11.3762975 -4.11938,2.6178955 -9.35517,3.9268422 -15.70737,3.9268438 -6.89126,-1.6e-6 -12.47353,-1.5399388 -16.74684,-4.6198163 -4.23484,-3.07987137 -7.21847,-7.3724463 -8.95089,-12.8777377 -1.23196,-3.811329 -1.84793,-10.741046 -1.84793,-20.789173 l 0,-26.15971"
id="path2910-3"
style="fill:#4d4d4d" />
<path
d="m -130.62894,-56.190303 15.70737,0 0,6.409995 c 3.58033,-3.002821 6.8142,-5.081736 9.70161,-6.236752 2.92585,-1.193388 5.909485,-1.790114 8.950903,-1.790178 6.236704,6.4e-5 11.530238,2.175225 15.880619,6.52549 3.657292,3.695907 5.485968,9.162684 5.486031,16.400348 l 0,41.5205983 -15.591879,0 0,-27.5456543 c -4.9e-5,-7.507166 -0.346534,-12.492713 -1.039459,-14.956655 -0.65452,-2.463857 -1.828723,-4.331031 -3.52261,-5.601527 -1.655476,-1.308899 -3.71514,-1.963372 -6.179015,-1.963422 -3.1954,5e-5 -5.94804,1.078006 -8.25791,3.233871 -2.27144,2.117461 -3.84987,5.062591 -4.73531,8.835399 -0.46201,1.963458 -0.693,6.217534 -0.69298,12.762242 l 0,25.2357463 -15.70737,0 0,-62.8295013"
id="path2912-9"
style="fill:#4d4d4d" />
<path
d="m -58.382674,-82.061274 c 2.771873,8.9e-5 5.139526,1.001048 7.102968,3.002881 2.001897,2.002004 3.002856,4.427405 3.00288,7.276211 -2.4e-5,2.810463 -0.981734,5.216615 -2.945133,7.218462 -1.963441,1.963492 -4.311845,2.945202 -7.04522,2.945133 -2.810399,6.9e-5 -5.216551,-1.000891 -7.218462,-3.00288 -1.963427,-2.040346 -2.945137,-4.504245 -2.945133,-7.391706 -4e-6,-2.771809 0.981706,-5.139462 2.945133,-7.102968 1.963413,-1.963334 4.331066,-2.945044 7.102967,-2.945133 m -7.911435,25.870971 15.82287,0 0,62.8295013 -15.82287,0 0,-62.8295013"
id="path2914-9"
style="fill:#4d4d4d" />
<path
d="m 22.756753,-43.485808 -13.1087298,7.218463 c -2.4639564,-2.579352 -4.9086068,-4.369529 -7.3339592,-5.370536 -2.38695243,-1.000911 -5.1973376,-1.501391 -8.4311656,-1.501441 -5.8902994,5e-5 -10.6641044,1.770978 -14.3214284,5.312789 -3.618878,3.503402 -5.428304,8.007718 -5.428283,13.512962 -2.1e-5,5.351313 1.751657,9.720885 5.25504,13.108729 3.503331,3.3878798 8.103892,5.0818107 13.8016994,5.0817979 7.04517317,1.28e-5 12.5312,-2.4061391 16.4580968,-7.2184629 L 22.06378,-4.8525946 C 15.326486,3.8865605 5.8173728,8.2561324 -6.4635876,8.256134 -17.512676,8.2561324 -26.174822,4.9837658 -32.450051,-1.5609755 c -6.23676,-6.544725 -9.355133,-14.2059125 -9.355128,-22.9835855 -5e-6,-6.082721 1.520683,-11.684243 4.562068,-16.804582 3.041367,-5.120243 7.276194,-9.143329 12.704495,-12.06927 5.466754,-2.925821 11.568754,-4.388761 18.3060194,-4.388825 6.23670517,6.4e-5 11.838228,1.251263 16.8045846,3.7536 4.96624,2.463961 9.027824,5.986567 12.184765,10.56783"
id="path2916-7"
style="fill:#4d4d4d" />
<path
d="m 61.798097,-57.807238 c 5.928721,6.4e-5 11.491744,1.482254 16.689087,4.446573 5.235731,2.964439 9.316569,6.987525 12.242523,12.06927 2.925814,5.08184 4.388768,10.567867 4.388838,16.458095 -7e-5,5.92879 -1.482264,11.472564 -4.446588,16.6313386 C 87.746013,-3.0431569 83.742165,0.99917828 78.660426,3.9250563 73.57858,6.8124412 67.977058,8.2561324 61.855845,8.256134 52.847175,8.2561324 45.147489,5.0607627 38.756764,-1.3299847 32.404509,-7.7592146 29.228388,-15.555147 29.228393,-24.717804 c -5e-6,-9.817069 3.599598,-17.997985 10.79882,-24.542774 6.313727,-5.697712 13.570681,-8.546596 21.770884,-8.54666 m 0.230991,14.841159 c -4.889338,5e-5 -8.970172,1.71323 -12.242513,5.139546 -3.233893,3.387906 -4.850827,7.738229 -4.850807,13.050981 -2e-5,5.466808 1.597664,9.894128 4.79306,13.281972 3.233842,3.3878795 7.314676,5.0818105 12.242513,5.0817974 4.927761,1.31e-5 9.027844,-1.7131671 12.30026,-5.1395454 3.272317,-3.426342 4.908505,-7.834413 4.908555,-13.224224 -5e-5,-5.389749 -1.616989,-9.759321 -4.850807,-13.108729 -3.19542,-3.387817 -7.295502,-5.081748 -12.300261,-5.081798"
id="path2918-0"
style="fill:#4d4d4d" />
<path
d="m 101.59025,-56.190303 13.51297,0 0,7.911436 c 1.46292,-3.118318 3.40709,-5.485972 5.83251,-7.102968 2.42538,-1.616872 5.08177,-2.425339 7.96919,-2.425403 2.04038,6.4e-5 4.17705,0.539042 6.40999,1.616935 l -4.90855,13.570711 c -1.84796,-0.923913 -3.36865,-1.385895 -4.56207,-1.385945 -2.42543,5e-5 -4.4851,1.501489 -6.179,4.504321 -1.65546,3.002923 -2.48318,8.893183 -2.48316,17.670797 l 0.0577,3.060628 0,25.4089893 -15.64963,0 0,-62.8295013"
id="path2920-3"
style="fill:#4d4d4d" />
<path
d="m 141.78656,-56.190303 15.70737,0 0,6.409995 c 3.58033,-3.002821 6.8142,-5.081736 9.70162,-6.236752 2.92584,-1.193388 5.90947,-1.790114 8.95089,-1.790178 6.2367,6.4e-5 11.53024,2.175225 15.88062,6.52549 3.65729,3.695907 5.48597,9.162684 5.48603,16.400348 l 0,41.5205983 -15.59188,0 0,-27.5456543 c -5e-5,-7.507166 -0.34653,-12.492713 -1.03946,-14.956655 -0.65452,-2.463857 -1.82872,-4.331031 -3.52261,-5.601527 -1.65547,-1.308899 -3.71514,-1.963372 -6.179,-1.963422 -3.19541,5e-5 -5.94805,1.078006 -8.25792,3.233871 -2.27144,2.117461 -3.84988,5.062591 -4.73532,8.835399 -0.462,1.963458 -0.69299,6.217534 -0.69297,12.762242 l 0,25.2357463 -15.70737,0 0,-62.8295013"
id="path2922-9"
style="fill:#4d4d4d" />
</g>
<g
transform="matrix(1.4868765,0,0,1.4868765,477.80324,740.33267)"
id="g2886-8"
style="fill:#4d4d4d;fill-opacity:1">
<text
x="-299.4765"
y="15.69857"
transform="scale(0.95410088,1.0481072)"
id="text3109-8-2-3-7-6"
xml:space="preserve"
style="font-size:127.35551453px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#4d4d4d;fill-opacity:1;stroke:none;font-family:Palatino;-inkscape-font-specification:Palatino Bold"><tspan
x="-299.4765"
y="15.69857"
id="tspan2884-5"
style="font-size:127.35551453px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#4d4d4d;fill-opacity:1;font-family:Palatino;-inkscape-font-specification:Palatino Bold">g</tspan></text>
</g>
<path
d="m -339.89735,-32.881535 a 120.17799,8.4974337 0 1 1 -240.35599,0 120.17799,8.4974337 0 1 1 240.35599,0 z"
transform="matrix(1.1060606,0,0,1.1060606,426.17959,848.32285)"
id="path3423-1"
style="opacity:0.26353838;fill:url(#linearGradient3269);fill-opacity:1;stroke:none" />
<path
d="m -100.65527,815.73848 c -2.57334,-0.81915 -2.58247,-1.18087 -0.21166,-8.37918 2.367628,-7.18862 1.282568,-13.86445 -3.10637,-19.11195 -5.96822,-7.13575 -8.84216,-13.22467 -9.75823,-20.67444 -0.78646,-6.3958 -1.03575,-6.90285 -3.6397,-7.40279 -1.74646,-0.33531 -4.15929,0.32877 -9.90965,2.72745 -8.36615,3.4898 -10.35876,5.02218 -12.33291,9.4843 -1.03707,2.34406 -1.16172,3.7482 -0.57348,6.45967 0.7183,3.31089 3.66212,8.64054 4.77653,8.64768 0.29081,0.002 1.89748,1.1023 3.57036,2.44541 2.77589,2.22869 2.96643,2.61592 2.18093,4.43265 -1.70673,3.94745 -5.76177,8.73342 -7.39962,8.73342 -0.91894,0 -4.67154,-2.62848 -8.80223,-6.16546 -8.11348,-6.94729 -8.45354,-7.64379 -9.29155,-19.03126 -0.99988,-13.58711 2.34128,-19.92818 17.22216,-32.68537 l 9.33328,-8.0013 0.5961,-4.8534 c 0.61452,-5.00328 -0.25183,-16.91684 -1.70841,-23.49298 -0.86051,-3.885 -3.7003,-7.58525 -6.39519,-8.33292 -2.93605,-0.8146 -4.65723,0.86613 -6.89082,6.72892 -2.40994,6.32567 -4.71315,8.53245 -8.90528,8.53245 -2.61108,0 -5.04774,-1.67029 -6.73027,-4.61349 -1.20562,-2.10894 -1.90545,-14.77861 -1.18473,-21.44783 0.34669,-3.20813 0.36656,-6.45629 0.0442,-7.21814 -0.52459,-1.23963 -5.04199,-3.92118 -15.69218,-9.31491 -1.93608,-0.98052 -4.19318,-2.30073 -5.01577,-2.93381 -0.82258,-0.63307 -2.95712,-1.79702 -4.74342,-2.58654 -4.0461,-1.78831 -14.59582,-7.95686 -13.60818,-7.95686 1.86847,0 9.78678,2.4371 14.00212,4.30958 2.57327,1.14308 5.0365,2.07831 5.47385,2.07831 0.43736,0 5.39838,1.59698 11.0245,3.54885 5.62613,1.95185 10.76835,3.54883 11.42716,3.54883 1.56481,0 1.81019,-1.35379 1.30876,-7.22052 -0.22777,-2.66503 -0.21772,-4.84552 0.0224,-4.84552 0.24005,0 1.27682,0.51453 2.3039,1.14339 3.13787,1.92125 9.88743,3.09794 17.82053,3.10678 7.58555,0.009 17.48687,1.51346 21.27112,3.23319 5.42864,2.46702 11.72565,10.17235 19.713658,24.12261 5.55341,9.69849 11.5205,13.80519 20.07216,13.81419 3.45705,0.004 12.6805,-1.42134 25.17062,-3.88871 10.29868,-2.03446 23.894192,-2.49173 29.171452,-0.98115 6.43791,1.8428 14.95039,6.76935 21.98861,12.72577 6.58741,5.57491 7.60197,6.81262 11.64852,14.21045 4.82928,8.82878 10.15926,21.37645 14.899,35.07479 0.67537,1.95185 2.10182,5.23512 3.1699,7.29614 2.84984,5.49914 2.70779,5.83455 -2.47093,5.83455 -5.62088,0 -8.25697,-0.88636 -11.94164,-4.01528 -3.534,-3.00098 -4.45715,-4.83696 -9.98855,-19.86556 -2.83329,-7.6979 -5.59135,-12.31728 -7.35421,-12.31728 -1.20786,0 -3.65266,8.24914 -3.65266,12.32476 0,2.89469 1.814,6.8052 6.50386,14.02068 7.61871,11.72159 11.62643,28.29168 10.02221,41.4373 l -0.69293,5.67814 -5.07747,0.21136 c -7.99385,0.33278 -7.91382,0.44389 -7.91382,-10.98681 0,-12.16922 -0.82877,-14.80464 -6.19901,-19.71247 -2.2306,-2.03853 -7.23508,-7.83031 -11.12107,-12.87062 -3.88602,-5.04032 -7.60749,-9.16422 -8.26995,-9.16422 -1.52225,0 -2.28133,4.14488 -2.36225,12.8988 -0.0532,5.76495 -0.28025,6.74074 -1.98112,8.5172 -4.611042,4.81598 -7.847772,13.58519 -9.843172,26.668 -0.44167,2.89579 -0.82033,3.43751 -2.6974,3.85899 -2.76002,0.61973 -10.40844,0.72118 -11.06755,0.14681 -0.56842,-0.49534 0.42766,-8.35904 2.68721,-21.21493 0.85765,-4.87965 1.79377,-11.42725 2.08028,-14.55022 0.51005,-5.55953 -0.62987,-15.97557 -1.97899,-18.08289 -0.56095,-0.8762 -3.4173,-1.06606 -16.77722,-1.11512 -8.85564,-0.0325 -18.56749,-0.32384 -21.58186,-0.64737 l -5.48072,-0.58825 0,2.43214 c 0,2.60447 0.66758,4.90008 5.1317,17.6466 1.57224,4.48928 3.85053,11.33318 5.06287,15.20868 l 2.20425,7.04637 -1.86007,5.72944 c -1.02305,3.15119 -2.54137,6.92717 -3.37407,8.39107 -1.46872,2.58204 -1.64095,2.65994 -5.76025,2.60558 -2.33545,-0.0308 -5.28634,-0.38711 -6.557538,-0.79175 z"
id="path3046-2-3-0"
style="fill:#499848;fill-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,79 @@
// Collapsible TOC for settings page
(function() {
function initCollapsibleTOC() {
// Only apply to pages with many TOC items (like settings)
var tocNav = document.querySelector('.md-nav--secondary');
if (!tocNav) return;
// Skip if already initialized
if (tocNav.dataset.tocCollapse === 'true') return;
tocNav.dataset.tocCollapse = 'true';
var tocItems = tocNav.querySelectorAll('.md-nav__item');
if (tocItems.length < 20) return;
// Find all top-level TOC items that have nested lists
var topList = tocNav.querySelector('.md-nav__list');
if (!topList) return;
var sections = topList.children;
for (var i = 0; i < sections.length; i++) {
(function(section) {
var nestedNav = section.querySelector('.md-nav');
if (!nestedNav) return;
var link = section.querySelector('.md-nav__link');
if (!link) return;
// Skip if already has toggle
if (link.querySelector('.toc-toggle')) return;
// Collapse by default
nestedNav.style.display = 'none';
// Create toggle button
var toggle = document.createElement('span');
toggle.className = 'toc-toggle';
toggle.innerHTML = '+';
toggle.style.float = 'right';
toggle.style.marginRight = '0.5rem';
toggle.style.fontWeight = 'bold';
toggle.style.cursor = 'pointer';
toggle.style.userSelect = 'none';
link.appendChild(toggle);
// Toggle function for this specific section
function toggleSection(e) {
if (e) {
e.preventDefault();
e.stopPropagation();
}
if (nestedNav.style.display === 'none') {
nestedNav.style.display = 'block';
toggle.innerHTML = '';
} else {
nestedNav.style.display = 'none';
toggle.innerHTML = '+';
}
}
// Click on toggle button
toggle.onclick = toggleSection;
})(sections[i]);
}
}
// Run on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initCollapsibleTOC);
} else {
initCollapsibleTOC();
}
// Re-run on instant navigation (MkDocs Material)
if (typeof document$ !== 'undefined') {
document$.subscribe(initCollapsibleTOC);
}
})();

View File

@ -0,0 +1,439 @@
/* ============================================
Gunicorn Landing Page
Inspired by Caddy: minimal, spacious, clean
============================================ */
.home {
--accent: #00a650;
--accent-hover: #00c853;
--accent-dark: #008542;
--teal: #00bfa5;
--text: #1a1a2e;
--text-muted: #555;
--bg: #fff;
--bg-alt: #f8faf8;
--border: #e0e6e0;
--code-bg: #0d1117;
--max-width: 900px;
width: 100%;
max-width: none;
margin: 0;
padding: 0;
font-size: 1.0625rem;
line-height: 1.7;
color: var(--text);
}
[data-md-color-scheme="slate"] .home {
--text: #e6e6e6;
--text-muted: #aaa;
--bg: #0d1117;
--bg-alt: #161b22;
--border: #30363d;
}
/* Remove MkDocs constraints */
.md-main__inner { margin: 0; max-width: none; }
.md-content { max-width: none; }
.md-content__inner { margin: 0; padding: 0; }
/* ============================================
Sections - Caddy-style vertical flow
============================================ */
.home section {
padding: 5rem 2rem;
}
.home section:nth-child(even) {
background: var(--bg-alt);
}
.home .container {
max-width: var(--max-width);
margin: 0 auto;
}
/* ============================================
Hero
============================================ */
.hero {
text-align: center;
padding: 6rem 2rem 5rem;
}
.hero .container {
max-width: 700px;
}
.hero__logo {
width: 350px !important;
max-width: 350px !important;
min-width: 350px;
height: auto;
margin-bottom: 2rem;
}
.hero h1 {
font-size: 3rem;
font-weight: 700;
line-height: 1.15;
margin: 0 0 1.5rem 0;
letter-spacing: -0.02em;
white-space: nowrap;
}
.hero__tagline {
font-size: 1.25rem;
color: var(--text-muted);
margin: 0 0 2.5rem 0;
max-width: 550px;
margin-left: auto;
margin-right: auto;
}
.hero__buttons {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 3rem;
}
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.875rem 1.75rem;
font-size: 1rem;
font-weight: 500;
text-decoration: none;
border-radius: 6px;
transition: all 0.15s ease;
}
.btn--primary {
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 100%);
color: #fff;
box-shadow: 0 4px 12px rgba(0, 166, 80, 0.3);
}
.btn--primary:hover {
box-shadow: 0 6px 20px rgba(0, 166, 80, 0.4);
transform: translateY(-2px);
}
.btn--secondary {
background: transparent;
color: var(--text);
border: 1px solid var(--border);
}
.btn--secondary:hover {
border-color: var(--accent);
color: var(--accent);
}
/* Terminal */
.terminal {
background: var(--code-bg);
border-radius: 8px;
overflow: hidden;
text-align: left;
max-width: 500px;
margin: 0 auto;
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
}
.terminal__header {
background: #161b22;
padding: 0.75rem 1rem;
display: flex;
gap: 6px;
}
.terminal__dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.terminal__dot--red { background: #ff5f56; }
.terminal__dot--yellow { background: #ffbd2e; }
.terminal__dot--green { background: #27c93f; }
.terminal__body {
padding: 1.25rem 1.5rem;
font-family: 'SF Mono', Monaco, Consolas, monospace;
font-size: 0.9rem;
line-height: 1.8;
color: #c9d1d9;
}
.terminal__line {
display: block;
}
.terminal__prompt {
color: var(--accent-hover);
user-select: none;
}
.terminal__comment {
color: #6e7681;
}
/* ============================================
Why Gunicorn - 3 pillars
============================================ */
.why h2 {
text-align: center;
font-size: 2rem;
margin: 0 0 3rem 0;
}
.pillars {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
.pillar h3 {
font-size: 1.125rem;
margin: 0 0 0.5rem 0;
}
.pillar p {
color: var(--text-muted);
margin: 0;
font-size: 0.9375rem;
}
/* ============================================
Frameworks
============================================ */
.frameworks h2 {
text-align: center;
font-size: 1.75rem;
margin: 0 0 0.5rem 0;
}
.frameworks__subtitle {
text-align: center;
color: var(--text-muted);
margin: 0 0 2rem 0;
}
.frameworks__list {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.75rem;
}
.framework-tag {
padding: 0.5rem 1rem;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 100px;
font-size: 0.875rem;
font-weight: 500;
transition: all 0.15s ease;
}
[data-md-color-scheme="slate"] .framework-tag {
background: var(--bg-alt);
}
.framework-tag:hover {
border-color: var(--accent);
color: var(--accent);
}
.framework-tag--new {
background: var(--accent);
color: #fff;
border-color: var(--accent);
}
/* ============================================
Workers
============================================ */
.workers h2 {
font-size: 1.75rem;
margin: 0 0 2rem 0;
}
.workers__grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.worker {
padding: 1.5rem;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
text-decoration: none;
color: inherit;
transition: border-color 0.15s ease;
}
[data-md-color-scheme="slate"] .worker {
background: var(--bg-alt);
}
.worker:hover {
border-color: var(--accent);
}
.worker h3 {
font-size: 1rem;
margin: 0 0 0.25rem 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.worker p {
color: var(--text-muted);
font-size: 0.875rem;
margin: 0;
}
.badge {
font-size: 0.625rem;
font-weight: 700;
padding: 0.125rem 0.375rem;
background: var(--accent);
color: #fff;
border-radius: 3px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* ============================================
Quick Links
============================================ */
.quick-links {
text-align: center;
}
.quick-links h2 {
font-size: 1.75rem;
margin: 0 0 2rem 0;
}
.quick-links__grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
text-align: left;
}
.quick-link {
padding: 1.25rem;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
text-decoration: none;
color: inherit;
transition: border-color 0.15s ease;
}
[data-md-color-scheme="slate"] .quick-link {
background: var(--bg-alt);
}
.quick-link:hover {
border-color: var(--accent);
}
.quick-link strong {
display: block;
margin-bottom: 0.25rem;
}
.quick-link span {
font-size: 0.875rem;
color: var(--text-muted);
}
/* ============================================
Footer CTA
============================================ */
.home-footer {
text-align: center;
}
.home-footer h2 {
font-size: 1.75rem;
margin: 0 0 1rem 0;
}
.home-footer p {
color: var(--text-muted);
margin: 0 0 2rem 0;
}
.home-footer__links {
display: flex;
justify-content: center;
gap: 2rem;
}
.home-footer__links a {
color: var(--text-muted);
text-decoration: none;
font-size: 0.9375rem;
}
.home-footer__links a:hover {
color: var(--accent);
}
/* ============================================
Responsive
============================================ */
@media (max-width: 768px) {
.home section {
padding: 3.5rem 1.5rem;
}
.hero h1 {
font-size: 2.25rem;
}
.pillars {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.workers__grid {
grid-template-columns: 1fr;
}
.quick-links__grid {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 480px) {
.hero h1 {
font-size: 1.875rem;
}
.hero__buttons {
flex-direction: column;
}
.btn {
width: 100%;
justify-content: center;
}
.quick-links__grid {
grid-template-columns: 1fr;
}
}

40
docs/content/community.md Normal file
View File

@ -0,0 +1,40 @@
# Community
Connect with the project through these channels.
## Project management & discussions
Project maintenance guidelines live on the
[wiki](https://github.com/benoitc/gunicorn/wiki/Project-management).
GitHub is used for:
- [Bug reports](https://github.com/benoitc/gunicorn/issues) — search before
opening a new issue.
- [Discussions](https://github.com/benoitc/gunicorn/discussions) — Q&A and usage
tips.
- [Feature planning](https://github.com/benoitc/gunicorn/issues) — development
and project management topics.
## IRC
Join the Gunicorn channel on [Libera Chat](https://libera.chat/) at
[`#gunicorn`](https://web.libera.chat/?channels=#gunicorn).
## Issue tracking
File bugs, enhancements, and tasks in the
[GitHub issue tracker](https://github.com/benoitc/gunicorn/issues).
## Security issues
Report security vulnerabilities privately to
[`security@gunicorn.org`](mailto:security@gunicorn.org); only core developers
subscribe to this list.
## Contributing
Start with the
[contributing guide](https://github.com/benoitc/gunicorn/blob/master/CONTRIBUTING.md)
for development workflow, code style, and review expectations. New contributors
are welcome—open a draft pull request early to gather feedback.

78
docs/content/configure.md Normal file
View File

@ -0,0 +1,78 @@
<span id="configuration"></span>
# Configuration Overview
Gunicorn reads configuration from five places, in increasing order of priority:
1. Environment variables, for settings that support them.
2. Framework-specific configuration (currently Paste Deploy only).
3. A Python configuration file `gunicorn.conf.py` (default in the working directory).
4. The `GUNICORN_CMD_ARGS` environment variable.
5. Command-line arguments.
If a configuration file is provided both via `GUNICORN_CMD_ARGS` and the CLI,
only the file specified on the command line is used.
!!! note
Print the fully resolved configuration:
bash
gunicorn --print-config APP_MODULE
```
Validate configuration and exit:
```bash
gunicorn --check-config APP_MODULE
```
This is also a quick way to confirm that your application can start.
```
## Command line
Options set on the command line override framework settings and values from the
configuration file. Not every setting has a command-line flag; run
```bash
gunicorn -h
```
for the complete list. The CLI also exposes `--version`, which is not part of
the main [settings reference](reference/settings.md).
<span id="configuration_file"></span>
## Configuration file
Provide a Python file (for example `gunicorn.conf.py`). Gunicorn executes the
file on every start or reload, so any valid Python is allowed:
```python
import multiprocessing
bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1
```
Every configuration key is documented in the [settings reference](reference/settings.md).
## Framework settings
At present only Paste Deploy applications expose framework-specific settings.
If you have ideas for Django or other frameworks, open an
[issue](https://github.com/benoitc/gunicorn/issues).
### Paste applications
Reference Gunicorn as the server in your INI file:
```ini
[server:main]
use = egg:gunicorn#main
host = 192.168.0.1
port = 80
workers = 2
proc_name = brim
```
Gunicorn merges any recognised parameters into the base configuration. Values
from the configuration file and command line still override these defaults.

62
docs/content/custom.md Normal file
View File

@ -0,0 +1,62 @@
<span id="custom"></span>
# Custom Application
!!! info "Added in 19.0"
Use Gunicorn as part of your own WSGI application by subclassing
`gunicorn.app.base.BaseApplication`.
Example: create a tiny WSGI app and load it with a custom application:
```text
--8<-- "examples/standalone_app.py"
```
## Using server hooks
Provide hooks through configuration, just like a standard Gunicorn deployment.
For example, a `pre_fork` hook:
```python
def pre_fork(server, worker):
print(f"pre-fork server {server} worker {worker}", file=sys.stderr)
if __name__ == "__main__":
options = {
"bind": "127.0.0.1:8080",
"workers": number_of_workers(),
"pre_fork": pre_fork,
}
```
## Direct usage of existing WSGI apps
Run Gunicorn from Python to serve a WSGI application instance at runtime—useful
for rolling deploys or packaging with PEX. Gunicorn exposes
`gunicorn.app.wsgiapp`, which accepts any WSGI app (for example a Flask or
Django instance). Assuming your package is `exampleapi` and the application is
`app`:
```bash
python -m gunicorn.app.wsgiapp exampleapi:app
```
All CLI flags and configuration files still apply:
```bash
# Custom parameters
python -m gunicorn.app.wsgiapp exampleapi:app --bind=0.0.0.0:8081 --workers=4
# Using a config file
python -m gunicorn.app.wsgiapp exampleapi:app -c config.py
```
For PEX builds use `-c gunicorn` at build time so the packaged app accepts the
entry point at runtime:
```bash
pex . -v -c gunicorn -o compiledapp.pex
./compiledapp.pex exampleapi:app -c gunicorn_config.py
```

322
docs/content/deploy.md Normal file
View File

@ -0,0 +1,322 @@
# Deploying Gunicorn
We strongly recommend running Gunicorn behind a proxy server.
## Nginx configuration
Although many HTTP proxies exist, we recommend [Nginx](https://nginx.org/).
When using the default synchronous workers you must ensure the proxy buffers
slow clients; otherwise Gunicorn becomes vulnerable to denial-of-service
attacks. Use [Hey](https://github.com/rakyll/hey) to verify proxy behaviour.
An example configuration for fast clients with Nginx
([source](https://github.com/benoitc/gunicorn/blob/master/examples/nginx.conf)):
```nginx title="nginx.conf"
--8<-- "examples/nginx.conf"
```
To support streaming requests/responses or patterns such as Comet, long
polling, or WebSockets, disable proxy buffering and run Gunicorn with an async
worker class:
```nginx
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://app_server;
}
```
To ignore aborted requests (for example, health checks that close connections
prematurely) enable
[`proxy_ignore_client_abort`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_client_abort):
```nginx
proxy_ignore_client_abort on;
```
!!! note
The default value for `proxy_ignore_client_abort` is `off`. If it remains off
Nginx logs will report error 499 and Gunicorn may log `Ignoring EPIPE` when the
log level is `debug`.
Pass protocol information to Gunicorn so applications can generate correct
URLs. Add this header to your `location` block:
```nginx
proxy_set_header X-Forwarded-Proto $scheme;
```
If Nginx runs on a different host, tell Gunicorn which proxies are trusted so it
accepts the `X-Forwarded-*` headers:
```bash
gunicorn -w 3 --forwarded-allow-ips="10.170.3.217,10.170.3.220" test:app
```
When all traffic comes from trusted proxies (for example Heroku) you can set
`--forwarded-allow-ips='*'`. This is **dangerous** if untrusted clients can
reach Gunicorn directly, because forged headers could make your application
serve secure content over plain HTTP.
Gunicorn 19 changed the handling of `REMOTE_ADDR` to conform to
[RFC 3875](https://www.rfc-editor.org/rfc/rfc3875), meaning it now records the
proxy IP rather than the upstream client. To log the real client address, set
[`access_log_format`](reference/settings.md#access_log_format) to include `X-Forwarded-For`:
```text
%({x-forwarded-for}i)s %(l.md)s %(u.md)s %(t.md)s "%(r.md)s" %(s.md)s %(b.md)s "%(f.md)s" "%(a.md)s"
```
When binding Gunicorn to a UNIX socket `REMOTE_ADDR` will be empty.
## Using virtual environments
Install Gunicorn inside your project
[virtual environment](https://pypi.python.org/pypi/virtualenv) to keep versions
isolated:
```bash
mkdir ~/venvs/
virtualenv ~/venvs/webapp
source ~/venvs/webapp/bin/activate
pip install gunicorn
deactivate
```
Force installation into the active virtual environment with `--ignore-installed`:
```bash
source ~/venvs/webapp/bin/activate
pip install -I gunicorn
```
## Monitoring
!!! note
Do not enable Gunicorn's daemon mode when using process monitors. These
supervisors expect to manage the direct child process.
### Gaffer
Use [Gaffer](https://gaffer.readthedocs.io/) with *gafferd* to manage Gunicorn:
```ini
[process:gunicorn]
cmd = gunicorn -w 3 test:app
cwd = /path/to/project
```
Create a `Procfile` if you prefer:
```procfile
gunicorn = gunicorn -w 3 test:app
```
Start Gunicorn via Gaffer:
```bash
gaffer start
```
Or load it into a running *gafferd* instance:
```bash
gaffer load
```
### runit
[runit](http://smarden.org/runit/) is a popular supervisor. A sample service
script (see the
[full example](https://github.com/benoitc/gunicorn/blob/master/examples/gunicorn_rc)):
```bash
#!/bin/sh
GUNICORN=/usr/local/bin/gunicorn
ROOT=/path/to/project
PID=/var/run/gunicorn.pid
APP=main:application
if [ -f $PID ]; then rm $PID; fi
cd $ROOT
exec $GUNICORN -c $ROOT/gunicorn.conf.py --pid=$PID $APP
```
Save as `/etc/sv/<app_name>/run`, make it executable, and symlink into
`/etc/service/<app_name>`. runit will then supervise Gunicorn.
### Supervisor
[Supervisor](http://supervisord.org/) configuration example (adapted from
[examples/supervisor.conf](https://github.com/benoitc/gunicorn/blob/master/examples/supervisor.conf)):
```ini
[program:gunicorn]
command=/path/to/gunicorn main:application -c /path/to/gunicorn.conf.py
directory=/path/to/project
user=nobody
autostart=true
autorestart=true
redirect_stderr=true
```
### Upstart
Sample Upstart config (logs go to `/var/log/upstart/myapp.log`):
```upstart
# /etc/init/myapp.conf
description "myapp"
start on (filesystem.md)
stop on runlevel [016]
respawn
setuid nobody
setgid nogroup
chdir /path/to/app/directory
exec /path/to/virtualenv/bin/gunicorn myapp:app
```
### systemd
[systemd](https://www.freedesktop.org/wiki/Software/systemd/) can create a UNIX
socket and launch Gunicorn on demand.
Service file:
```ini
# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
Type=notify
NotifyAccess=main
User=someuser
Group=someuser
RuntimeDirectory=gunicorn
WorkingDirectory=/home/someuser/applicationroot
ExecStart=/usr/bin/gunicorn applicationname.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
```
`Type=notify` lets Gunicorn report readiness to systemd. If the service should
run under a transient user consider adding `DynamicUser=true`. Tighten
permissions further with `ProtectSystem=strict` if the app permits.
Socket activation file:
```ini
# /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
SocketUser=www-data
SocketGroup=www-data
SocketMode=0660
[Install]
WantedBy=sockets.target
```
Enable and start the socket so it begins listening immediately and on reboot:
```bash
systemctl enable --now gunicorn.socket
```
Test connectivity from the nginx user (Debian defaults to `www-data`):
```bash
sudo -u www-data curl --unix-socket /run/gunicorn.sock http
```
!!! note
Use `systemctl show --value -p MainPID gunicorn.service` to retrieve the main
process ID or `systemctl kill -s HUP gunicorn.service` to send signals.
Configure Nginx to proxy to the new socket:
```nginx
user www-data;
...
http {
server {
listen 8000;
server_name 127.0.0.1;
location / {
proxy_pass http://unix:/run/gunicorn.sock;
}
}
}
...
```
!!! note
Adjust `listen` and `server_name` for production (typically port 80 and your
site's domain).
Ensure nginx starts automatically:
```bash
systemctl enable nginx.service
systemctl start nginx
```
Browse to <http://127.0.0.1:8000/> to verify Gunicorn + Nginx + systemd.
## Logging
Configure logging through the CLI flags described in the
[settings documentation](reference/settings.md#logging) or via a
[logging configuration file](https://github.com/benoitc/gunicorn/blob/master/examples/logging.conf).
Rotate logs with `logrotate` by sending `SIGUSR1`:
```bash
kill -USR1 $(cat /var/run/gunicorn.pid)
```
!!! note
If you override the `LOGGING` dictionary, set `disable_existing_loggers` to
`False` so Gunicorn's loggers remain active.
!!! warning
Gunicorn's error log should capture Gunicorn-related messages only. Route your
application logs separately.

216
docs/content/design.md Normal file
View File

@ -0,0 +1,216 @@
<span id="design"></span>
# Design
A brief look at Gunicorn's architecture.
## Server Model
Gunicorn uses a **pre-fork worker model**: an arbiter process manages worker
processes, while the workers handle requests and responses. The arbiter never
touches individual client sockets.
<div class="pillars" markdown>
<div class="pillar" markdown>
<div class="pillar__icon">⚖️</div>
### Arbiter
Orchestrates the worker pool. Listens for signals (`TTIN`, `TTOU`, `CHLD`,
`HUP`) to adjust workers, restart them on failure, or reload configuration.
</div>
<div class="pillar" markdown>
<div class="pillar__icon">⚙️</div>
### Worker Pool
Each worker handles requests independently. Worker types determine
concurrency model: sync, threaded, or async via greenlets/asyncio.
</div>
<div class="pillar" markdown>
<div class="pillar__icon">📡</div>
### Signal Communication
`TTIN`/`TTOU` adjust worker count. `CHLD` triggers restart of crashed
workers. `HUP` reloads configuration. See [Signals](signals.md).
</div>
</div>
## Worker Types
Choose a worker type based on your application's needs.
=== "Sync"
The **default** worker. Handles one request at a time per worker.
- Simple and predictable
- Errors affect only the current request
- No keep-alive support (connections close after response)
- Requires a buffering proxy (nginx, HAProxy) for production
```bash
gunicorn myapp:app
```
=== "Gthread"
Threaded worker with a **thread pool** per worker process.
- Supports keep-alive connections
- Good balance of concurrency and simplicity
- Threads share memory (lower footprint than workers)
- Idle connections close after keepalive timeout
```bash
gunicorn myapp:app -k gthread --threads 4
```
=== "ASGI"
Native **asyncio** support for modern async frameworks.
- For FastAPI, Starlette, Quart, and other ASGI apps
- Full async/await support
- See the [ASGI Guide](asgi.md) for details
```bash
gunicorn myapp:app -k uvicorn.workers.UvicornWorker
```
=== "Gevent"
**Greenlet-based** async worker using [Gevent](http://www.gevent.org/).
- Handles thousands of concurrent connections
- Supports keep-alive, WebSockets, long-polling
- May require patches for some libraries (e.g., `psycogreen` for Psycopg)
- Not compatible with code that relies on blocking behavior
```bash
gunicorn myapp:app -k gevent --worker-connections 1000
```
=== "Eventlet"
**Greenlet-based** async worker using [Eventlet](http://eventlet.net/).
- Similar capabilities to Gevent
- Handles high concurrency for I/O-bound apps
- Some libraries may need compatibility patches
```bash
gunicorn myapp:app -k eventlet --worker-connections 1000
```
=== "Tornado"
Worker for [Tornado](https://www.tornadoweb.org/) applications.
- Designed for Tornado's async framework
- Can serve WSGI apps, but not recommended for that use case
- Use when running native Tornado applications
```bash
gunicorn myapp:app -k tornado
```
## Comparison
| Worker | Concurrency Model | Keep-Alive | Best For |
|--------|-------------------|------------|----------|
| `sync` | 1 request/worker | ❌ | CPU-bound apps behind a proxy |
| `gthread` | Thread pool | ✅ | Mixed workloads, moderate concurrency |
| ASGI workers | AsyncIO | ✅ | Modern async frameworks (FastAPI, etc.) |
| `gevent` | Greenlets | ✅ | I/O-bound, WebSockets, streaming |
| `eventlet` | Greenlets | ✅ | I/O-bound, long-polling |
| `tornado` | Tornado IOLoop | ✅ | Native Tornado applications |
!!! tip "Quick Decision Guide"
- **Simple app behind nginx?**`sync` (default)
- **Need keep-alive or moderate concurrency?**`gthread`
- **WebSockets, streaming, long-polling?**`gevent` or `eventlet`
- **FastAPI, Starlette, or async framework?** → ASGI worker
## When to Use Async Workers
Synchronous workers assume your app is CPU or network bound and avoids
indefinite blocking operations. Use async workers when you have:
- Long blocking calls (external APIs, slow databases)
- Direct internet traffic without a buffering proxy
- Streaming request/response bodies
- Long polling or Comet patterns
- WebSockets
!!! info "Testing Slow Clients"
Tools like [Hey](https://github.com/rakyll/hey) can simulate slow responses
to test how your configuration handles them.
## Scaling
### How Many Workers?
!!! warning "Don't Over-Scale"
Workers ≠ clients. Gunicorn typically needs only **412 workers** to handle
heavy traffic. Too many workers waste resources and can reduce throughput.
Start with this formula and adjust under load:
```
workers = (2 × CPU cores) + 1
```
Use `TTIN`/`TTOU` signals to adjust the worker count at runtime.
### How Many Threads?
With the `gthread` worker, you can combine workers and threads:
```bash
gunicorn myapp:app -k gthread --workers 4 --threads 2
```
!!! info "Threads vs Workers"
- **Threads** share memory → lower footprint
- **Workers** isolate failures → better fault tolerance
- Combine both for the best of both worlds
Threads can extend request time beyond the worker timeout while still
notifying the arbiter. The optimal mix depends on your runtime (CPython vs
PyPy) and workload.
## Configuration Examples
```bash
# Sync (default) - simple apps behind nginx
gunicorn myapp:app
# Gthread - keep-alive and thread concurrency
gunicorn myapp:app -k gthread --workers 4 --threads 4
# Gevent - high concurrency for I/O-bound apps
gunicorn myapp:app -k gevent --workers 4 --worker-connections 1000
# Eventlet - alternative async worker
gunicorn myapp:app -k eventlet --workers 4 --worker-connections 1000
# ASGI - FastAPI/Starlette with Uvicorn worker
gunicorn myapp:app -k uvicorn.workers.UvicornWorker --workers 4
```
<span id="asyncio-workers"></span>
!!! note "Third-Party AsyncIO Workers"
For asyncio frameworks, you can also use third-party workers. See the
[aiohttp deployment guide](https://docs.aiohttp.org/en/stable/deployment.html#nginx-gunicorn)
for examples.

160
docs/content/faq.md Normal file
View File

@ -0,0 +1,160 @@
<span id="faq"></span>
# FAQ
## WSGI bits
### How do I set `SCRIPT_NAME`?
By default `SCRIPT_NAME` is an empty string. Set it via an environment variable
or HTTP header. Because the header contains an underscore it is only accepted
from trusted forwarders listed in [`forwarded_allow_ips`](reference/settings.md#forwarded_allow_ips).
!!! note
If your application should appear under a subfolder, `SCRIPT_NAME` typically
starts with a single leading slash and no trailing slash.
## Server stuff
### How do I reload my application in Gunicorn?
Send `HUP` to the master process for a graceful reload:
```bash
kill -HUP masterpid
```
### How might I test a proxy configuration?
Use [Hey](https://github.com/rakyll/hey) to confirm that your proxy buffers
responses correctly for synchronous workers:
```bash
hey -n 10000 -c 100 http://127.0.0.1:5000/
```
That benchmark issues 10,000 requests with a concurrency of 100.
### How can I name processes?
Install [setproctitle](https://pypi.python.org/pypi/setproctitle) to give
Gunicorn processes meaningful names in tools such as `ps` and `top`. This helps
when running multiple Gunicorn instances. See the
[`proc_name`](reference/settings.md#proc_name) setting for details.
### Why is there no HTTP keep-alive?
The default sync workers target Nginx, which uses HTTP/1.0 for upstream
connections. If you need to serve unbuffered internet traffic directly, pick an
async worker instead.
## Worker processes
### How do I know which type of worker to use?
Read the [design guide](design.md) for guidance on worker types.
### What types of workers are available?
See the [`worker_class`](reference/settings.md#worker_class) configuration reference.
### How can I figure out the best number of worker processes?
Follow the recommendations for tuning the [`number of workers`](design.md#how-many-workers).
### How can I change the number of workers dynamically?
Send `TTIN` or `TTOU` to the master process:
```bash
kill -TTIN $masterpid # increment workers
kill -TTOU $masterpid # decrement workers
```
### Does Gunicorn suffer from the thundering herd problem?
Potentially, when many sleeping handlers wake simultaneously but only one takes
the request. There is ongoing work to mitigate this
([issue #792](https://github.com/benoitc/gunicorn/issues/792)). Monitor load if
you use large numbers of workers or threads.
### Why don't I see logs in the console?
Gunicorn 19.0 disabled console logging by default. Use `--log-file=-` to stream
logs to stdout. Console logging returned in 19.2.
## Kernel parameters
High-concurrency deployments may need kernel tuning. These Linux-oriented tips
apply to any network service.
### How can I increase the maximum number of file descriptors?
Raise the per-process limit (remember sockets count as files). Running `sudo
ulimit` is ineffective—switch to root, adjust the limit, then launch Gunicorn.
Consider managing limits via systemd service units or init scripts.
### How can I increase the maximum socket backlog?
Increase the queue of pending connections:
```bash
sudo sysctl -w net.core.somaxconn="2048"
```
### How can I disable the use of `sendfile()`?
Pass `--no-sendfile` or set the `SENDFILE=0` environment variable.
## Troubleshooting
### Django reports `ImproperlyConfigured`
Asynchronous workers may break `django.core.urlresolvers.reverse`. Use
`reverse_lazy` instead.
### How do I avoid blocking in `os.fchmod`?
Gunicorn's heartbeat touches temporary files. On disk-backed filesystems (for
example `/tmp` on some distributions) `os.fchmod` can block if I/O stalls or the
filesystem fills up. Mount a `tmpfs` and point `--worker-tmp-dir` to it.
Check whether `/tmp` is RAM-backed:
```bash
df /tmp
```
If not, create a new `tmpfs` mount:
```bash
sudo cp /etc/fstab /etc/fstab.orig
sudo mkdir /mem
echo 'tmpfs /mem tmpfs defaults,size=64m,mode=1777,noatime,comment=for-gunicorn 0 0' | sudo tee -a /etc/fstab
sudo mount /mem
```
Verify the result:
```bash
df /mem
```
Then start Gunicorn with `--worker-tmp-dir /mem`.
### Why are workers silently killed?
If a worker vanishes without logs, check for `SIGKILL`. Reverse proxies may show
`502` responses while Gunicorn logs only new worker startups (for example,
`[INFO] Booting worker`). A common culprit is the OOM killer in cgroups-limited
environments.
Inspect kernel logs:
```bash
dmesg | grep gunicorn
```
If you see messages similar to `Memory cgroup out of memory ... Killed process
(gunicorn.md)`, raise memory limits or adjust OOM behaviour.

View File

@ -0,0 +1,339 @@
# Docker Deployment
Running Gunicorn in Docker containers is the most common deployment pattern
for modern Python applications. This guide covers best practices for
containerizing Gunicorn applications.
## Basic Dockerfile
```dockerfile
FROM python:3.12-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Run gunicorn
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000"]
```
Build and run:
```bash
docker build -t myapp .
docker run -p 8000:8000 myapp
```
## Production Configuration
### Environment Variables
Use environment variables for configuration:
```dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Configuration via environment
ENV GUNICORN_WORKERS=4
ENV GUNICORN_BIND=0.0.0.0:8000
CMD gunicorn app:app \
--workers ${GUNICORN_WORKERS} \
--bind ${GUNICORN_BIND}
```
Or use `GUNICORN_CMD_ARGS`:
```dockerfile
ENV GUNICORN_CMD_ARGS="--workers=4 --bind=0.0.0.0:8000"
CMD ["gunicorn", "app:app"]
```
### Worker Count
In containers, determine workers based on available CPU:
```python
# gunicorn.conf.py
import multiprocessing
workers = multiprocessing.cpu_count() * 2 + 1
bind = "0.0.0.0:8000"
```
Or let Kubernetes/Docker limit CPU and calculate accordingly:
```bash
# At runtime
gunicorn app:app --workers $(( 2 * $(nproc) + 1 ))
```
### Non-Root User
Run as a non-root user for security:
```dockerfile
FROM python:3.12-slim
# Create non-root user
RUN useradd --create-home appuser
WORKDIR /home/appuser/app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=appuser:appuser . .
USER appuser
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000"]
```
### Health Checks
Add a health check endpoint and Docker health check:
```dockerfile
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
```
## Multi-Stage Build
Reduce image size with multi-stage builds:
```dockerfile
# Build stage
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
# Runtime stage
FROM python:3.12-slim
WORKDIR /app
# Copy wheels and install
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
COPY . .
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000", "--workers", "4"]
```
## Docker Compose
Example `docker-compose.yml`:
```yaml
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgres://db:5432/myapp
depends_on:
- db
deploy:
resources:
limits:
cpus: '2'
memory: 512M
db:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_PASSWORD=secret
volumes:
- postgres_data:/var/lib/postgresql/data
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
volumes:
postgres_data:
```
## Kubernetes Deployment
Example Kubernetes deployment:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8000
env:
- name: GUNICORN_WORKERS
value: "4"
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "500m"
memory: "256Mi"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8000
```
## Graceful Shutdown
Gunicorn handles `SIGTERM` gracefully by default. Configure the timeout:
```dockerfile
CMD ["gunicorn", "app:app", \
"--bind", "0.0.0.0:8000", \
"--graceful-timeout", "30", \
"--timeout", "120"]
```
Match Docker's stop timeout:
```yaml
# docker-compose.yml
services:
web:
stop_grace_period: 30s
```
## Logging
Log to stdout/stderr for Docker log collection:
```python
# gunicorn.conf.py
accesslog = "-"
errorlog = "-"
loglevel = "info"
```
Use JSON logging for log aggregation:
```python
# gunicorn.conf.py
import json
import datetime
class JsonFormatter:
def format(self, record):
return json.dumps({
"timestamp": datetime.datetime.utcnow().isoformat(),
"level": record.levelname,
"message": record.getMessage(),
})
logconfig_dict = {
"version": 1,
"formatters": {
"json": {"()": JsonFormatter}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "json",
"stream": "ext://sys.stdout"
}
},
"root": {
"handlers": ["console"],
"level": "INFO"
}
}
```
## Troubleshooting
### Worker Timeout
If workers are killed with `[CRITICAL] WORKER TIMEOUT`, increase the timeout:
```bash
gunicorn app:app --timeout 120
```
Or investigate slow requests in your application.
### Out of Memory
If containers are OOM-killed:
1. Reduce worker count
2. Use `--max-requests` to restart workers periodically
3. Increase container memory limits
```bash
gunicorn app:app --workers 2 --max-requests 1000 --max-requests-jitter 100
```
### Connection Reset
If you see connection resets, ensure:
1. Load balancer health checks match your `/health` endpoint
2. Graceful timeout is sufficient for in-flight requests
3. Keepalive settings match between Gunicorn and upstream proxy
## See Also
- [Deploy](../deploy.md) - General deployment patterns
- [Settings](../reference/settings.md) - All configuration options

127
docs/content/index.md Normal file
View File

@ -0,0 +1,127 @@
---
template: home.html
title: Gunicorn - Python WSGI HTTP Server
---
<section class="hero">
<div class="container">
<img class="hero__logo" src="assets/gunicorn.svg" alt="Gunicorn" style="width: 350px;" />
<h1>The Python WSGI Server</h1>
<p class="hero__tagline">
Battle-tested. Production-ready. One command to serve your Python apps.
</p>
<div class="hero__buttons">
<a class="btn btn--primary" href="quickstart/">Get Started</a>
<a class="btn btn--secondary" href="https://github.com/benoitc/gunicorn">View on GitHub</a>
</div>
<div class="terminal">
<div class="terminal__header">
<span class="terminal__dot terminal__dot--red"></span>
<span class="terminal__dot terminal__dot--yellow"></span>
<span class="terminal__dot terminal__dot--green"></span>
</div>
<div class="terminal__body">
<span class="terminal__line"><span class="terminal__prompt">$ </span>pip install gunicorn</span>
<span class="terminal__line"><span class="terminal__prompt">$ </span>gunicorn myapp:app</span>
<span class="terminal__line terminal__comment"># Listening at http://127.0.0.1:8000</span>
</div>
</div>
</div>
</section>
<section class="why">
<div class="container">
<h2>Why Gunicorn?</h2>
<div class="pillars">
<div class="pillar">
<h3>Production-Proven</h3>
<p>Trusted by thousands of companies. The pre-fork worker model handles traffic spikes gracefully.</p>
</div>
<div class="pillar">
<h3>Lightweight</h3>
<p>Minimal dependencies, simple configuration. Efficient from containers to bare metal.</p>
</div>
<div class="pillar">
<h3>Compatible</h3>
<p>Works with any WSGI or ASGI framework. Django, Flask, FastAPI—it just runs.</p>
</div>
</div>
</div>
</section>
<section class="frameworks">
<div class="container">
<h2>Works With Your Stack</h2>
<p class="frameworks__subtitle">WSGI and ASGI frameworks, no changes needed</p>
<div class="frameworks__list">
<span class="framework-tag">Django</span>
<span class="framework-tag">Flask</span>
<span class="framework-tag framework-tag--new">FastAPI</span>
<span class="framework-tag">Pyramid</span>
<span class="framework-tag framework-tag--new">Starlette</span>
<span class="framework-tag">Falcon</span>
<span class="framework-tag">Bottle</span>
<span class="framework-tag framework-tag--new">Quart</span>
</div>
</div>
</section>
<section class="workers">
<div class="container">
<h2>Choose Your Worker</h2>
<div class="workers__grid">
<a class="worker" href="design/#sync-workers">
<h3>Sync</h3>
<p>The default. One request per worker. Simple and predictable.</p>
</a>
<a class="worker" href="design/#async-workers">
<h3>Async (Gevent/Eventlet)</h3>
<p>Thousands of concurrent connections for I/O-bound workloads.</p>
</a>
<a class="worker" href="reference/settings/#threads">
<h3>Threads</h3>
<p>Multiple threads per worker. Balance concurrency and simplicity.</p>
</a>
<a class="worker" href="asgi/">
<h3>ASGI <span class="badge">Beta</span></h3>
<p>Native asyncio for FastAPI, Starlette, and async frameworks.</p>
</a>
</div>
</div>
</section>
<section class="quick-links">
<div class="container">
<h2>Documentation</h2>
<div class="quick-links__grid">
<a class="quick-link" href="quickstart/">
<strong>Quickstart</strong>
<span>Get running in 5 minutes</span>
</a>
<a class="quick-link" href="deploy/">
<strong>Deployment</strong>
<span>Nginx, systemd, Docker</span>
</a>
<a class="quick-link" href="reference/settings/">
<strong>Settings</strong>
<span>All configuration options</span>
</a>
<a class="quick-link" href="faq/">
<strong>FAQ</strong>
<span>Common questions</span>
</a>
</div>
</div>
</section>
<section class="home-footer">
<div class="container">
<h2>Join the Community</h2>
<p>Questions? Bugs? Ideas? We're here to help.</p>
<div class="home-footer__links">
<a href="https://github.com/benoitc/gunicorn/issues">GitHub Issues</a>
<a href="https://web.libera.chat/#gunicorn">#gunicorn on Libera</a>
<a href="community/">Contributing</a>
</div>
</div>
</section>

176
docs/content/install.md Normal file
View File

@ -0,0 +1,176 @@
# Installation
!!! note
Gunicorn requires **Python 3.12 or newer**.
## Quick Install
=== "pip"
```bash
pip install gunicorn
```
=== "pipx"
```bash
pipx install gunicorn
```
=== "Docker"
```bash
docker run -p 8000:8000 -v $(pwd):/app -w /app \
python:3.12-slim sh -c "pip install gunicorn && gunicorn app:app"
```
See the [Docker guide](guides/docker.md) for production configurations.
=== "System Packages"
**Debian/Ubuntu:**
```bash
sudo apt-get update
sudo apt-get install gunicorn
```
**Fedora:**
```bash
sudo dnf install python3-gunicorn
```
**Arch Linux:**
```bash
sudo pacman -S gunicorn
```
!!! warning
System packages may lag behind the latest release. For production,
prefer pip installation in a virtual environment.
## Virtual Environment (Recommended)
Always install Gunicorn inside a virtual environment to isolate dependencies:
```bash
# Create virtual environment
python -m venv venv
# Activate it
source venv/bin/activate # Linux/macOS
# or: venv\Scripts\activate # Windows
# Install gunicorn
pip install gunicorn
```
## From Source
Install the latest development version from GitHub:
```bash
pip install git+https://github.com/benoitc/gunicorn.git
```
Upgrade to the latest commit:
```bash
pip install -U git+https://github.com/benoitc/gunicorn.git
```
## Extra Packages
Gunicorn provides optional extras for additional worker types and features.
Install them with pip's bracket syntax:
```bash
pip install gunicorn[gevent,setproctitle]
```
### Worker Types
| Extra | Description |
|-------|-------------|
| `gunicorn[eventlet]` | Eventlet-based greenlet workers |
| `gunicorn[gevent]` | Gevent-based greenlet workers |
| `gunicorn[gthread]` | Threaded workers |
| `gunicorn[tornado]` | Tornado-based workers (not recommended) |
See the [design docs](design.md) for guidance on choosing worker types.
### Utilities
| Extra | Description |
|-------|-------------|
| `gunicorn[setproctitle]` | Set process name in `ps`/`top` output |
!!! tip
If running multiple Gunicorn instances, use `setproctitle` with the
[`proc_name`](reference/settings.md#proc_name) setting to distinguish them.
## Async Workers
For applications using async I/O patterns, install the appropriate greenlet
library:
=== "Gevent"
```bash
pip install gunicorn[gevent]
```
Run with:
```bash
gunicorn app:app --worker-class gevent
```
=== "Eventlet"
```bash
pip install gunicorn[eventlet]
```
Run with:
```bash
gunicorn app:app --worker-class eventlet
```
=== "ASGI (asyncio)"
No extra installation required:
```bash
gunicorn app:app --worker-class asgi
```
For better performance, install uvloop:
```bash
pip install uvloop
gunicorn app:app --worker-class asgi --asgi-loop uvloop
```
!!! note
Greenlet-based workers require the Python development headers. On Ubuntu:
`sudo apt-get install python3-dev`
## Verify Installation
Check the installed version:
```bash
gunicorn --version
```
Test with a simple application:
```bash
echo 'def app(e, s): s("200 OK", []); return [b"OK"]' > test_app.py
gunicorn test_app:app
# Visit http://127.0.0.1:8000
```
## Next Steps
- [Quickstart](quickstart.md) - Get running in 5 minutes
- [Run](run.md) - CLI usage and framework integration
- [Configure](configure.md) - Configuration options

View File

@ -0,0 +1,32 @@
<span id="instrumentation"></span>
# Instrumentation
!!! info "Added in 19.1"
Gunicorn exposes optional instrumentation for the arbiter and workers using the
statsD protocol over UDP. The `gunicorn.instrument.statsd` module turns
Gunicorn into a statsD client.
UDP keeps Gunicorn isolated from slow statsD consumers, so metrics collection
does not impact request handling.
Tell Gunicorn where the statsD server is located:
```bash
gunicorn --statsd-host=localhost:8125 --statsd-prefix=service.app ...
```
The `Statsd` logger subclasses `gunicorn.glogging.Logger` and tracks:
- `gunicorn.requests` &mdash; request rate per second
- `gunicorn.request.duration` &mdash; request duration histogram (milliseconds.md)
- `gunicorn.workers` &mdash; number of workers managed by the arbiter (gauge.md)
- `gunicorn.log.critical` &mdash; rate of critical log messages
- `gunicorn.log.error` &mdash; rate of error log messages
- `gunicorn.log.warning` &mdash; rate of warning log messages
- `gunicorn.log.exception` &mdash; rate of exceptional log messages
See the [`statsd_host`](reference/settings.md#statsd_host) setting for additional options.
[statsD](https://github.com/etsy/statsd)

98
docs/content/news.md Normal file
View File

@ -0,0 +1,98 @@
<span id="news"></span>
# Changelog
## 24.0.0 - 2026-01-23
### New Features
- **ASGI Worker (Beta)**: Native asyncio-based ASGI support for running async Python
frameworks like FastAPI, Starlette, and Quart without external dependencies
- HTTP/1.1 with keepalive connections
- WebSocket support
- Lifespan protocol for startup/shutdown hooks
- Optional uvloop for improved performance
- **uWSGI Binary Protocol**: Support for receiving requests from nginx via
`uwsgi_pass` directive
- **Documentation Migration**: Migrated to MkDocs with Material theme
### Breaking changes
- Minimum Python version is now 3.12
---
## 23.0.0 - 2024-08-10
- minor docs fixes ([PR #3217](https://github.com/benoitc/gunicorn/pull/3217), [PR #3089](https://github.com/benoitc/gunicorn/pull/3089), [PR #3167](https://github.com/benoitc/gunicorn/pull/3167))
- worker_class parameter accepts a class ([PR #3079](https://github.com/benoitc/gunicorn/pull/3079))
- fix deadlock if request terminated during chunked parsing ([PR #2688](https://github.com/benoitc/gunicorn/pull/2688))
- permit receiving Transfer-Encodings: compress, deflate, gzip ([PR #3261](https://github.com/benoitc/gunicorn/pull/3261))
- permit Transfer-Encoding headers specifying multiple encodings. note: no parameters, still ([PR #3261](https://github.com/benoitc/gunicorn/pull/3261))
- sdist generation now explicitly excludes sphinx build folder ([PR #3257](https://github.com/benoitc/gunicorn/pull/3257))
- decode bytes-typed status (as can be passed by gevent) as utf-8 instead of raising `TypeError` ([PR #2336](https://github.com/benoitc/gunicorn/pull/2336))
- raise correct Exception when encounting invalid chunked requests ([PR #3258](https://github.com/benoitc/gunicorn/pull/3258))
- the SCRIPT_NAME and PATH_INFO headers, when received from allowed forwarders, are no longer restricted for containing an underscore ([PR #3192](https://github.com/benoitc/gunicorn/pull/3192))
- include IPv6 loopback address ``[::1]`` in default for [forwarded-allow-ips](reference/settings.md#forwarded_allow_ips) and [proxy-allow-ips](reference/settings.md#proxy_allow_ips) ([PR #3192](https://github.com/benoitc/gunicorn/pull/3192))
!!! note
- The SCRIPT_NAME change mitigates a regression that appeared first in the 22.0.0 release
- Review your [forwarded-allow-ips](reference/settings.md#forwarded_allow_ips) setting if you are still not seeing the SCRIPT_NAME transmitted
- Review your [forwarder-headers](reference/settings.md#forwarder_headers) setting if you are missing headers after upgrading from a version prior to 22.0.0
### Breaking changes
- refuse requests where the uri field is empty ([PR #3255](https://github.com/benoitc/gunicorn/pull/3255))
- refuse requests with invalid CR/LR/NUL in heade field values ([PR #3253](https://github.com/benoitc/gunicorn/pull/3253))
- remove temporary ``--tolerate-dangerous-framing`` switch from 22.0 ([PR #3260](https://github.com/benoitc/gunicorn/pull/3260))
- If any of the breaking changes affect you, be aware that now refused requests can post a security problem, especially so in setups involving request pipe-lining and/or proxies.
## 22.0.0 - 2024-04-17
- use `utime` to notify workers liveness
- migrate setup to pyproject.toml
- fix numerous security vulnerabilities in HTTP parser (closing some request smuggling vectors)
- parsing additional requests is no longer attempted past unsupported request framing
- on HTTP versions < 1.1 support for chunked transfer is refused (only used in exploits)
- requests conflicting configured or passed SCRIPT_NAME now produce a verbose error
- Trailer fields are no longer inspected for headers indicating secure scheme
- support Python 3.12
### Breaking changes
- minimum version is Python 3.7
- the limitations on valid characters in the HTTP method have been bounded to Internet Standards
- requests specifying unsupported transfer coding (order.md) are refused by default (rare.md)
- HTTP methods are no longer casefolded by default (IANA method registry contains none affected)
- HTTP methods containing the number sign (#) are no longer accepted by default (rare.md)
- HTTP versions < 1.0 or >= 2.0 are no longer accepted by default (rare, only HTTP/1.1 is supported)
- HTTP versions consisting of multiple digits or containing a prefix/suffix are no longer accepted
- HTTP header field names Gunicorn cannot safely map to variables are silently dropped, as in other software
- HTTP headers with empty field name are refused by default (no legitimate use cases, used in exploits)
- requests with both Transfer-Encoding and Content-Length are refused by default (such a message might indicate an attempt to perform request smuggling)
- empty transfer codings are no longer permitted (reportedly seen with really old & broken proxies)
### Security
- fix CVE-2024-1135
## History
- [2026](2026-news.md)
- [2024](2024-news.md)
- [2023](2023-news.md)
- [2021](2021-news.md)
- [2020](2020-news.md)
- [2019](2019-news.md)
- [2018](2018-news.md)
- [2017](2017-news.md)
- [2016](2016-news.md)
- [2015](2015-news.md)
- [2014](2014-news.md)
- [2013](2013-news.md)
- [2012](2012-news.md)
- [2011](2011-news.md)
- [2010](2010-news.md)

115
docs/content/quickstart.md Normal file
View File

@ -0,0 +1,115 @@
# Quickstart
Get a Python web application running with Gunicorn in 5 minutes.
## Install
```bash
pip install gunicorn
```
## Create an Application
Create `app.py`:
=== "Flask"
```python
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
```
=== "FastAPI"
```python
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def hello():
return {"message": "Hello, World!"}
```
=== "Django"
Django projects already have a WSGI application at `myproject/wsgi.py`.
No additional code is needed.
=== "Plain WSGI"
```python
def app(environ, start_response):
data = b"Hello, World!"
start_response("200 OK", [
("Content-Type", "text/plain"),
("Content-Length", str(len(data)))
])
return [data]
```
## Run
```bash
gunicorn app:app
```
For Django:
```bash
gunicorn myproject.wsgi
```
For FastAPI (ASGI):
```bash
gunicorn app:app --worker-class asgi
```
## Add Workers
Use multiple workers to handle concurrent requests:
```bash
gunicorn app:app --workers 4
```
A good starting point is `2 * CPU_CORES + 1` workers.
## Bind to a Port
By default Gunicorn binds to `127.0.0.1:8000`. Change it with:
```bash
gunicorn app:app --bind 0.0.0.0:8080
```
## Configuration File
Create `gunicorn.conf.py` for reusable settings:
```python
bind = "0.0.0.0:8000"
workers = 4
accesslog = "-"
```
Then run:
```bash
gunicorn app:app
```
Gunicorn automatically loads `gunicorn.conf.py` from the current directory.
## Next Steps
- [Run](run.md) - Full CLI reference and framework integration
- [Configure](configure.md) - Configuration file options
- [Deploy](deploy.md) - Production deployment with nginx and process managers
- [Settings](reference/settings.md) - Complete settings reference

File diff suppressed because it is too large Load Diff

154
docs/content/run.md Normal file
View File

@ -0,0 +1,154 @@
# Running Gunicorn
You can run Gunicorn directly from the command line or integrate it with
popular frameworks like Django, Pyramid, or TurboGears. For deployment
patterns see the [deployment guide](deploy.md).
## Commands
After installation you have access to the `gunicorn` executable.
<span id="gunicorn-cmd"></span>
### `gunicorn`
Basic usage:
```bash
gunicorn [OPTIONS] [WSGI_APP]
```
`WSGI_APP` follows the pattern `MODULE_NAME:VARIABLE_NAME`. The module can be a
full dotted path. The variable refers to a WSGI callable defined in that
module.
!!! info "Changed in 20.1.0"
`WSGI_APP` can be omitted when defined in a [configuration file](configure.md).
Example test application:
```python
def app(environ, start_response):
"""Simplest possible application object"""
data = b"Hello, World!\n"
status = "200 OK"
response_headers = [
("Content-type", "text/plain"),
("Content-Length", str(len(data.md)))
]
start_response(status, response_headers)
return iter([data])
```
Run it with:
```bash
gunicorn --workers=2 test:app
```
You can also expose a factory function that returns the application:
```python
def create_app():
app = FrameworkApp()
...
return app
```
```bash
gunicorn --workers=2 'test:create_app()'
```
Passing positional and keyword arguments is supported but prefer
configuration files or environment variables for anything beyond quick tests.
#### Commonly used arguments
- `-c CONFIG`, `--config CONFIG` &mdash; configuration file (`PATH`, `file:PATH`, or
`python:MODULE_NAME`).
- `-b BIND`, `--bind BIND` &mdash; socket to bind (host, host:port, `fd://FD`,
or `unix:PATH`).
- `-w WORKERS`, `--workers WORKERS` &mdash; number of worker processes, typically
two to four per CPU core. See the [FAQ](faq.md) for tuning tips.
- `-k WORKERCLASS`, `--worker-class WORKERCLASS` &mdash; worker type (`sync`,
`eventlet`, `gevent`, `tornado`, `gthread`). Read the
[settings entry](reference/settings.md#worker_class) before switching classes.
- `-n APP_NAME`, `--name APP_NAME` &mdash; set the process name (requires
[`setproctitle`](https://pypi.python.org/pypi/setproctitle)).
You can pass any setting via the environment variable
`GUNICORN_CMD_ARGS`. See the [configuration guide](configure.md) and
[settings reference](reference/settings.md) for details.
## Integration
Gunicorn integrates cleanly with Django and Paste Deploy applications.
### Django
Gunicorn looks for a WSGI callable named `application`. A typical invocation is:
```bash
gunicorn myproject.wsgi
```
!!! note
Ensure your project is on `PYTHONPATH`. The easiest way is to run this command
from the directory containing `manage.py`.
Set environment variables with `--env` and add your project to `PYTHONPATH`
if needed:
```bash
gunicorn --env DJANGO_SETTINGS_MODULE=myproject.settings myproject.wsgi
```
See [`raw_env`](reference/settings.md#raw_env) and [`pythonpath`](reference/settings.md#pythonpath) for
more options.
### Paste Deployment
Frameworks such as Pyramid and TurboGears often rely on Paste Deployment
configuration. You can use Gunicorn in two ways.
#### As a Paste server runner
Let your framework command (for example `pserve` or `gearbox`) load Gunicorn by
configuring it as the server:
```ini
[server:main]
use = egg:gunicorn#main
host = 127.0.0.1
port = 8080
workers = 3
```
This approach is quick to set up but Gunicorn cannot control how the
application loads. Options like [`reload`](reference/settings.md#reload) will be ignored and
hot upgrades are unavailable. Features such as daemon mode may conflict with
what your framework already provides. Prefer running those features through the
framework (for example `pserve --reload`). Advanced configuration is still
possible by pointing the `config` key at a Gunicorn configuration file.
#### Using Gunicorn's Paste support
Use the [`paste`](reference/settings.md#paste) option to load a Paste configuration directly
with the Gunicorn CLI. This unlocks Gunicorn's reloader and hot code upgrades,
while still letting Paste define the application object.
```bash
gunicorn --paste development.ini -b :8080 --chdir /path/to/project
```
Select a different application section by appending the name:
```bash
gunicorn --paste development.ini#admin -b :8080 --chdir /path/to/project
```
In both modes Gunicorn will honor any Paste `loggers` configuration unless you
override it with Gunicorn-specific [logging settings](reference/settings.md#logging).

97
docs/content/signals.md Normal file
View File

@ -0,0 +1,97 @@
<span id="signals"></span>
# Signal Handling
A quick reference to the signals handled by Gunicorn. This includes the signals
used internally to coordinate with worker processes.
## Master process
- `QUIT`, `INT` &mdash; quick shutdown.
- `TERM` &mdash; graceful shutdown; waits for workers to finish requests up to
[`graceful_timeout`](reference/settings.md#graceful_timeout).
- `HUP` &mdash; reload configuration, spawn new workers, and gracefully stop old
ones. If the app is not preloaded (see [`preload_app`](reference/settings.md#preload_app))
the application code is reloaded too.
- `TTIN` &mdash; increase worker count by one.
- `TTOU` &mdash; decrease worker count by one.
- `USR1` &mdash; reopen log files.
- `USR2` &mdash; perform a binary upgrade. Send `TERM` to the old master afterwards
to stop it. This also reloads preloaded applications (see
[binary upgrades](#binary-upgrade)).
- `WINCH` &mdash; gracefully stop workers when Gunicorn runs as a daemon.
## Worker process
Workers rarely need direct signalling—if the master stays alive it will respawn
workers automatically.
- `QUIT`, `INT` &mdash; quick shutdown.
- `TERM` &mdash; graceful shutdown.
- `USR1` &mdash; reopen log files.
## Reload the configuration
Use `HUP` to reload Gunicorn on the fly:
```text
2013-06-29 06:26:55 [20682] [INFO] Handling signal: hup
2013-06-29 06:26:55 [20682] [INFO] Hang up: Master
2013-06-29 06:26:55 [20703] [INFO] Booting worker with pid: 20703
2013-06-29 06:26:55 [20702] [INFO] Booting worker with pid: 20702
2013-06-29 06:26:55 [20688] [INFO] Worker exiting (pid: 20688)
2013-06-29 06:26:55 [20687] [INFO] Worker exiting (pid: 20687)
2013-06-29 06:26:55 [20689] [INFO] Worker exiting (pid: 20689)
2013-06-29 06:26:55 [20704] [INFO] Booting worker with pid: 20704
```
Gunicorn reloads its settings, starts new workers, and gracefully shuts down the
previous ones. If the app is not preloaded it reloads the application module as
well.
<span id="binary-upgrade"></span>
## Upgrading to a new binary on the fly
!!! info "Changed in 19.6.0"
PID files now follow the pattern `<name>.pid.2` instead of `<name>.pid.oldbin`.
You can replace the Gunicorn binary without downtime. Incoming requests remain
served and preloaded applications reload.
1. Replace the old binary and send `USR2` to the master. Gunicorn starts a new
master whose PID file ends with `.2` and spawns new workers.
```text
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20844 benoitc 20 0 54808 11m 3352 S 0.0 0.1 0:00.36 gunicorn: master [test:app]
20849 benoitc 20 0 54808 9.9m 1500 S 0.0 0.1 0:00.02 gunicorn: worker [test:app]
20850 benoitc 20 0 54808 9.9m 1500 S 0.0 0.1 0:00.01 gunicorn: worker [test:app]
20851 benoitc 20 0 54808 9.9m 1500 S 0.0 0.1 0:00.01 gunicorn: worker [test:app]
20854 benoitc 20 0 55748 12m 3348 S 0.0 0.2 0:00.35 gunicorn: master [test:app]
20859 benoitc 20 0 55748 11m 1500 S 0.0 0.1 0:00.01 gunicorn: worker [test:app]
20860 benoitc 20 0 55748 11m 1500 S 0.0 0.1 0:00.00 gunicorn: worker [test:app]
20861 benoitc 20 0 55748 11m 1500 S 0.0 0.1 0:00.01 gunicorn: worker [test:app]
```
2. Send `WINCH` to the old master to gracefully stop its workers.
You can still roll back while the old master keeps its listen sockets:
1. Send `HUP` to the old master to restart its workers without reloading the
config file.
2. Send `TERM` to the new master to shut down its workers gracefully.
3. Send `QUIT` to the new master to force it to exit.
If the new workers linger, send `KILL` after the new master quits.
To complete the upgrade, send `TERM` to the old master so only the new server
continues running:
```text
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20854 benoitc 20 0 55748 12m 3348 S 0.0 0.2 0:00.45 gunicorn: master [test:app]
20859 benoitc 20 0 55748 11m 1500 S 0.0 0.1 0:00.02 gunicorn: worker [test:app]
20860 benoitc 20 0 55748 11m 1500 S 0.0 0.1 0:00.02 gunicorn: worker [test:app]
20861 benoitc 20 0 55748 11m 1500 S 0.0 0.1 0:00.01 gunicorn: worker [test:app]
```

View File

@ -0,0 +1,504 @@
/* Gunicorn Punchy Theme */
:root {
--gunicorn-green: #00a650;
--gunicorn-green-dark: #008542;
--gunicorn-green-light: #00c853;
--gunicorn-teal: #00bfa5;
--gunicorn-bg: #fafafa;
--gunicorn-card: #ffffff;
--md-primary-fg-color: var(--gunicorn-green);
--md-primary-fg-color--light: var(--gunicorn-green-light);
--md-primary-fg-color--dark: var(--gunicorn-green-dark);
--md-accent-fg-color: var(--gunicorn-teal);
--md-typeset-a-color: var(--gunicorn-green);
}
[data-md-color-scheme="slate"] {
--gunicorn-bg: #0d1117;
--gunicorn-card: #161b22;
--md-default-bg-color: #0d1117;
--md-default-bg-color--light: #161b22;
}
/* Header - punchy gradient */
.md-header {
background: linear-gradient(135deg, var(--gunicorn-green-dark) 0%, var(--gunicorn-green) 100%);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.md-tabs {
background: linear-gradient(135deg, var(--gunicorn-green) 0%, var(--gunicorn-green-light) 100%);
}
/* Logo bigger */
.md-header__button.md-logo img,
.md-header__button.md-logo svg {
height: 2rem;
}
/* Navigation styling */
.md-nav__link:hover {
color: var(--gunicorn-green);
}
.md-nav__link--active {
color: var(--gunicorn-green);
font-weight: 600;
}
/* Code blocks - punchy */
.md-typeset code {
background: rgba(0, 166, 80, 0.08);
color: var(--gunicorn-green-dark);
border-radius: 4px;
}
[data-md-color-scheme="slate"] .md-typeset code {
background: rgba(0, 200, 83, 0.12);
color: var(--gunicorn-green-light);
}
.md-typeset pre {
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
[data-md-color-scheme="slate"] .md-typeset pre {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}
/* Admonitions - punchy colors */
.md-typeset .admonition,
.md-typeset details {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.md-typeset .admonition.note,
.md-typeset details.note {
border-color: var(--gunicorn-teal);
}
.md-typeset .note > .admonition-title,
.md-typeset .note > summary {
background-color: rgba(0, 191, 165, 0.1);
}
.md-typeset .admonition.tip,
.md-typeset details.tip {
border-color: var(--gunicorn-green);
}
.md-typeset .tip > .admonition-title,
.md-typeset .tip > summary {
background-color: rgba(0, 166, 80, 0.1);
}
/* Tables - cleaner */
.md-typeset table:not([class]) {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.md-typeset table:not([class]) th {
background: var(--gunicorn-green);
color: white;
font-weight: 600;
}
/* Buttons - punchy */
.md-typeset .md-button {
border-radius: 8px;
font-weight: 600;
text-transform: none;
letter-spacing: 0;
transition: all 0.2s ease;
}
.md-typeset .md-button--primary {
background: linear-gradient(135deg, var(--gunicorn-green) 0%, var(--gunicorn-green-light) 100%);
border: none;
box-shadow: 0 4px 12px rgba(0, 166, 80, 0.3);
}
.md-typeset .md-button--primary:hover {
box-shadow: 0 6px 20px rgba(0, 166, 80, 0.4);
transform: translateY(-2px);
}
/* Search */
.md-search__form {
border-radius: 8px;
}
/* Footer */
.md-footer {
background: linear-gradient(135deg, var(--gunicorn-green-dark) 0%, #1a1a2e 100%);
}
.md-footer-meta {
background: rgba(0, 0, 0, 0.2);
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
background: var(--gunicorn-green);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--gunicorn-green-light);
}
/* Selection */
::selection {
background: rgba(0, 166, 80, 0.3);
}
/* ================================
Homepage Specific Styles
================================ */
/* These are for the non-custom template pages */
.md-typeset .hero {
margin: 2rem 0 3rem;
padding: 3.5rem;
background: linear-gradient(135deg, var(--gunicorn-green-dark) 0%, var(--gunicorn-green) 50%, var(--gunicorn-teal) 100%);
color: #fff;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 166, 80, 0.25);
}
[data-md-color-scheme="slate"] .md-typeset .hero {
background: linear-gradient(135deg, #0d1117 0%, var(--gunicorn-green-dark) 50%, var(--gunicorn-green) 100%);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.md-typeset .hero__inner {
display: flex;
flex-wrap: wrap;
gap: 2.5rem;
align-items: center;
justify-content: space-between;
}
.md-typeset .hero__copy {
flex: 1 1 320px;
max-width: 520px;
font-size: 1.05rem;
line-height: 1.6;
}
.md-typeset .hero__copy h1 {
margin: 0 0 1rem;
font-size: 2.6rem;
font-weight: 800;
line-height: 1.15;
letter-spacing: -0.02em;
}
.md-typeset .hero__tagline {
font-size: 1.15rem;
opacity: 0.95;
margin-bottom: 0;
}
.md-typeset .hero__cta {
margin-top: 2rem;
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.md-typeset .hero__code {
flex: 1 1 260px;
max-width: 400px;
background: rgba(0, 0, 0, 0.25);
border-radius: 12px;
padding: 1.5rem;
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.md-typeset .hero__code pre {
margin: 0 0 1rem;
border: none;
background: rgba(0, 0, 0, 0.4);
color: #e8f5ea;
box-shadow: none;
}
.md-typeset .hero__version {
font-weight: 700;
font-size: 0.9rem;
opacity: 0.9;
}
.md-typeset .hero__logo {
height: 72px;
margin-bottom: 1.5rem;
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.2));
}
/* Pillars */
.md-typeset .pillars {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 2rem;
margin: 3rem 0;
}
.md-typeset .pillar {
text-align: center;
padding: 2rem;
background: var(--gunicorn-card);
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
[data-md-color-scheme="slate"] .md-typeset .pillar {
background: var(--gunicorn-card);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.md-typeset .pillar:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(0, 166, 80, 0.15);
}
.md-typeset .pillar__icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.md-typeset .pillar h3 {
margin: 0 0 0.5rem;
font-size: 1.3rem;
font-weight: 700;
color: var(--gunicorn-green-dark);
}
[data-md-color-scheme="slate"] .md-typeset .pillar h3 {
color: var(--gunicorn-green-light);
}
.md-typeset .pillar p {
margin: 0;
font-size: 0.95rem;
opacity: 0.8;
}
/* Frameworks */
.md-typeset .frameworks {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
margin: 2rem 0 3rem;
}
.md-typeset .framework {
background: var(--gunicorn-card);
border: 2px solid transparent;
border-radius: 50px;
padding: 0.75rem 1.75rem;
font-weight: 600;
font-size: 0.95rem;
color: var(--gunicorn-green-dark);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;
}
[data-md-color-scheme="slate"] .md-typeset .framework {
background: var(--gunicorn-card);
color: #e8f5ea;
}
.md-typeset .framework:hover {
border-color: var(--gunicorn-green);
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 166, 80, 0.2);
}
/* Feature Grid */
.md-typeset .feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.5rem;
margin: 2.5rem 0 3rem;
}
.md-typeset .feature-card {
background: var(--gunicorn-card);
border-radius: 12px;
padding: 1.75rem;
border: 1px solid rgba(0, 166, 80, 0.1);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;
}
[data-md-color-scheme="slate"] .md-typeset .feature-card {
background: var(--gunicorn-card);
border-color: rgba(0, 200, 83, 0.15);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.md-typeset .feature-card:hover {
transform: translateY(-4px);
border-color: var(--gunicorn-green);
box-shadow: 0 12px 32px rgba(0, 166, 80, 0.15);
}
.md-typeset .feature-card h3 {
margin-top: 0;
font-size: 1.2rem;
font-weight: 700;
color: var(--gunicorn-green-dark);
display: flex;
align-items: center;
gap: 0.5rem;
}
[data-md-color-scheme="slate"] .md-typeset .feature-card h3 {
color: var(--gunicorn-green-light);
}
.md-typeset .feature-card p {
font-size: 0.95rem;
opacity: 0.8;
margin-bottom: 1rem;
}
.md-typeset .feature-card a {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-weight: 600;
color: var(--gunicorn-green);
}
.md-typeset .feature-card a:hover {
color: var(--gunicorn-green-light);
}
/* Badge */
.md-typeset .badge {
display: inline-block;
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
padding: 0.2rem 0.6rem;
border-radius: 50px;
vertical-align: middle;
letter-spacing: 0.05em;
}
.md-typeset .badge--new {
background: linear-gradient(135deg, var(--gunicorn-green) 0%, var(--gunicorn-teal) 100%);
color: #fff;
}
/* Quick Links */
.md-typeset .quick-links {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin: 2rem 0;
}
.md-typeset .quick-link {
display: block;
padding: 1.5rem;
background: var(--gunicorn-card);
border-radius: 12px;
border: 2px solid transparent;
text-decoration: none;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;
}
[data-md-color-scheme="slate"] .md-typeset .quick-link {
background: var(--gunicorn-card);
}
.md-typeset .quick-link:hover {
border-color: var(--gunicorn-green);
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 166, 80, 0.15);
}
.md-typeset .quick-link strong {
display: block;
font-size: 1.1rem;
font-weight: 700;
color: var(--gunicorn-green-dark);
margin-bottom: 0.25rem;
}
[data-md-color-scheme="slate"] .md-typeset .quick-link strong {
color: var(--gunicorn-green-light);
}
.md-typeset .quick-link span {
font-size: 0.9rem;
opacity: 0.7;
}
/* Community Links */
.md-typeset .community-links {
margin: 1.5rem 0;
}
.md-typeset .community-links ul {
list-style: none;
padding: 0;
margin: 0;
}
.md-typeset .community-links li {
margin-bottom: 0.75rem;
}
/* Footer */
.md-footer-meta__inner {
flex-wrap: wrap;
}
/* Responsive */
@media (max-width: 960px) {
.md-typeset .hero {
padding: 2.5rem;
}
.md-typeset .hero__copy h1 {
font-size: 2rem;
}
}
@media (max-width: 720px) {
.md-typeset .hero {
margin-top: 1.5rem;
padding: 2rem;
}
.md-typeset .hero__cta {
flex-direction: column;
align-items: stretch;
}
.md-typeset .hero__code {
width: 100%;
}
.md-typeset .pillars {
grid-template-columns: 1fr;
}
}

266
docs/content/uwsgi.md Normal file
View File

@ -0,0 +1,266 @@
# uWSGI Protocol
Gunicorn supports the uWSGI binary protocol, allowing it to receive requests from
nginx using the `uwsgi_pass` directive. This provides efficient communication
between nginx and Gunicorn without HTTP overhead.
!!! note
This is the **uWSGI binary protocol**, not the uWSGI server. Gunicorn
implements the protocol to receive requests from nginx, similar to how
the uWSGI server would.
## Quick Start
Enable uWSGI protocol support:
```bash
gunicorn myapp:app --protocol uwsgi --bind 127.0.0.1:8000
```
Configure nginx to forward requests:
```nginx
upstream gunicorn {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name example.com;
location / {
uwsgi_pass gunicorn;
include uwsgi_params;
}
}
```
## Why Use uWSGI Protocol?
The uWSGI binary protocol offers several advantages over HTTP proxying:
- **Lower overhead** - Binary format is more compact than HTTP headers
- **Better integration** - nginx's native uwsgi module is highly optimized
- **Simpler configuration** - No need to reconstruct HTTP headers
## Configuration
### Protocol Setting
Switch from HTTP to uWSGI protocol:
```bash
gunicorn myapp:app --protocol uwsgi
```
Or in a configuration file:
```python
# gunicorn.conf.py
protocol = "uwsgi"
```
### Allowed IPs
By default, uWSGI protocol requests are only accepted from localhost
(`127.0.0.1` and `::1`). This prevents unauthorized hosts from sending
requests directly to Gunicorn.
To allow additional IPs:
```bash
gunicorn myapp:app --protocol uwsgi --uwsgi-allow-from 10.0.0.1,10.0.0.2
```
To allow all IPs (not recommended for production):
```bash
gunicorn myapp:app --protocol uwsgi --uwsgi-allow-from '*'
```
!!! warning
Only allow IPs from trusted sources. The uWSGI protocol does not provide
authentication, so anyone who can connect can send requests.
!!! note
UNIX socket connections are always allowed regardless of this setting.
### Using UNIX Sockets
For better performance and security, use UNIX sockets instead of TCP:
```bash
gunicorn myapp:app --protocol uwsgi --bind unix:/run/gunicorn.sock
```
Nginx configuration:
```nginx
upstream gunicorn {
server unix:/run/gunicorn.sock;
}
server {
listen 80;
location / {
uwsgi_pass gunicorn;
include uwsgi_params;
}
}
```
## Nginx Configuration
### Basic Setup
Create or verify the `uwsgi_params` file exists (usually at `/etc/nginx/uwsgi_params`):
```nginx
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
```
### With SSL Termination
When nginx handles SSL and forwards to Gunicorn:
```nginx
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
uwsgi_pass gunicorn;
include uwsgi_params;
uwsgi_param HTTPS on;
}
}
```
### Load Balancing
Distribute requests across multiple Gunicorn instances:
```nginx
upstream gunicorn {
least_conn;
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
}
server {
listen 80;
location / {
uwsgi_pass gunicorn;
include uwsgi_params;
}
}
```
### Static Files
Serve static files directly from nginx:
```nginx
server {
listen 80;
location /static/ {
alias /path/to/static/;
}
location / {
uwsgi_pass gunicorn;
include uwsgi_params;
}
}
```
## Protocol Details
The uWSGI protocol uses a compact binary format:
| Bytes | Field | Description |
|-------|-------|-------------|
| 0 | modifier1 | Packet type (0 = WSGI request) |
| 1-2 | datasize | Size of vars block (little-endian) |
| 3 | modifier2 | Additional flags (usually 0) |
After the header, the vars block contains CGI-style key-value pairs:
```
[2-byte key_size][key][2-byte val_size][value]...
```
Standard CGI variables like `REQUEST_METHOD`, `PATH_INFO`, and `QUERY_STRING`
are extracted from this block to construct the WSGI environ.
## Combining with HTTP
You can run Gunicorn with both HTTP and uWSGI protocol support by running
separate instances:
```bash
# HTTP for direct access
gunicorn myapp:app --bind 127.0.0.1:8080
# uWSGI for nginx
gunicorn myapp:app --protocol uwsgi --bind 127.0.0.1:8000
```
## Troubleshooting
### ForbiddenUWSGIRequest Error
If you see "Forbidden uWSGI request from IP", the connecting IP is not in
the allowed list. Either:
1. Add the IP to `--uwsgi-allow-from`
2. Use UNIX sockets instead
3. Ensure nginx is connecting from an allowed IP
### Invalid uWSGI Header
This usually means:
1. HTTP traffic is being sent to a uWSGI endpoint
2. The packet is malformed or truncated
3. Network issues caused data corruption
Verify that nginx is using `uwsgi_pass` (not `proxy_pass`) and that the
`uwsgi_params` file is being included.
### Headers Missing
If certain headers aren't reaching your application, verify they're included
in `uwsgi_params`. Custom headers should be passed as:
```nginx
uwsgi_param HTTP_X_CUSTOM_HEADER $http_x_custom_header;
```
## See Also
- [Settings Reference](reference/settings.md#protocol) - Protocol and uWSGI settings
- [Deploy](deploy.md) - General deployment guidance
- [Design](design.md) - Worker architecture overview

11
docs/macros.py Normal file
View File

@ -0,0 +1,11 @@
from importlib import import_module
def define_env(env):
"""Register template variables for MkDocs macros."""
gunicorn = import_module("gunicorn")
env.variables.update(
release=gunicorn.__version__,
version=gunicorn.__version__,
github_repo="https://github.com/benoitc/gunicorn",
pypi_url=f"https://pypi.org/project/gunicorn/{gunicorn.__version__}/",
)

View File

@ -0,0 +1,35 @@
# Website Modernization Plan
## Goals
- Serve a single, canonical domain backed by a static MkDocs build.
- Keep the documentation authoring experience entirely in Markdown.
- Modernize the marketing home page with a refreshed visual identity.
- Preserve the generated settings reference sourced from Python code.
## Architecture Overview
- **Static site generator:** MkDocs with the Material theme.
- **Content layout:** Markdown files in `docs/content/`, grouped by guides, reference, and news archives.
- **Styling:** Lightweight CSS overrides in `docs/content/styles/overrides.css` for hero, feature cards, and color palette.
- **Dynamic data:** `docs/macros.py` exposes the Gunicorn version, while `scripts/build_settings_doc.py` renders the settings reference into Markdown during every build.
- **Assets:** SVG mascot and hero art live under `docs/content/assets/` so both the homepage and docs share the same branding.
## Completed Work
- Removed Sphinx configuration, themes, and the legacy static snapshot under `docs/site/`.
- Converted the entire content library (guides, FAQ, design notes, yearly news) from MyST/RST to MkDocs-friendly Markdown.
- Rebuilt the homepage using Materials layout primitives with responsive hero, CTAs, and feature cards.
- Added CSS overrides that mirror Gunicorns brand colors and support light/dark modes.
- Replaced the Sphinx extension with a standalone Markdown generator for the settings reference.
- Introduced an automated MkDocs workflow (`.github/workflows/docs.yml`) that builds on every push and deploys to `gh-pages` from the `main` branch.
## Remaining Enhancements
1. **Visual polish:** produce updated screenshots/asciicasts for quickstart and deployment examples; add Open Graph imagery.
2. **Content review:** prune outdated news entries, tighten FAQs, and add framework-specific quickstarts (FastAPI, Flask, Django).
3. **Accessibility & internationalization:** run axe audits, ensure color contrast, and consider adding minimal localization support.
4. **Performance extras:** enable MkDocs search index minification and gzip the GitHub Pages output (served automatically once deployed).
5. **Contributor docs:** extend `CONTRIBUTING.md` with MkDocs authoring tips, link to preview artifacts, and describe the `mkdocs serve` workflow.
## Deployment Checklist
- [x] Update DNS to point away from ReadTheDocs once `gh-pages` is published.
- [x] Verify `site_url` in `mkdocs.yml` for canonical URLs and sitemap generation.
- [x] Ensure `CNAME` (if required) is checked into `gh-pages` during deployment.
- [ ] Announce the migration to end-users and update links in READMEs and PyPI metadata.

131
mkdocs.yml Normal file
View File

@ -0,0 +1,131 @@
site_name: Gunicorn
site_url: https://gunicorn.org
repo_url: https://github.com/benoitc/gunicorn
repo_name: benoitc/gunicorn
docs_dir: docs/content
use_directory_urls: true
nav:
- Home: index.md
- Getting Started:
- Quickstart: quickstart.md
- Install: install.md
- Run: run.md
- Configure: configure.md
- Guides:
- Deploy: deploy.md
- Docker: guides/docker.md
- ASGI Worker: asgi.md
- uWSGI Protocol: uwsgi.md
- Signals: signals.md
- Instrumentation: instrumentation.md
- Custom: custom.md
- Design: design.md
- Community:
- Overview: community.md
- FAQ: faq.md
- Reference:
- Settings: reference/settings.md
- News:
- Latest: news.md
- '2026': 2026-news.md
- '2024': 2024-news.md
- '2023': 2023-news.md
- '2021': 2021-news.md
- '2020': 2020-news.md
- '2019': 2019-news.md
- '2018': 2018-news.md
- '2017': 2017-news.md
- '2016': 2016-news.md
- '2015': 2015-news.md
- '2014': 2014-news.md
- '2013': 2013-news.md
- '2012': 2012-news.md
- '2011': 2011-news.md
- '2010': 2010-news.md
theme:
name: material
custom_dir: overrides
language: en
logo: assets/gunicorn.svg
favicon: assets/gunicorn.svg
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
primary: green
accent: teal
toggle:
icon: material/brightness-7
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: green
accent: teal
toggle:
icon: material/brightness-4
name: Switch to light mode
font:
text: Inter
code: JetBrains Mono
features:
- content.code.copy
- content.code.annotate
- navigation.instant
- navigation.instant.progress
- navigation.tracking
- navigation.sections
- navigation.tabs
- navigation.tabs.sticky
- navigation.top
- navigation.path
- search.highlight
- search.suggest
- search.share
- toc.follow
- toc.integrate
- header.autohide
plugins:
- search
- macros
- gen-files:
scripts:
- scripts/build_settings_doc.py
markdown_extensions:
- admonition
- attr_list
- def_list
- footnotes
- md_in_html
- tables
- toc:
permalink: true
- pymdownx.details
- pymdownx.highlight
- pymdownx.inlinehilite
- pymdownx.magiclink
- pymdownx.superfences
- pymdownx.snippets:
base_path:
- .
check_paths: true
- pymdownx.tabbed:
alternate_style: true
- pymdownx.tasklist:
custom_checkbox: true
extra_css:
- styles/overrides.css
- assets/stylesheets/home.css
extra_javascript:
- assets/javascripts/toc-collapse.js
extra:
social:
- icon: fontawesome/brands/github
link: https://github.com/benoitc/gunicorn
- icon: fontawesome/brands/python
link: https://pypi.org/project/gunicorn/

30
overrides/home.html Normal file
View File

@ -0,0 +1,30 @@
{% extends "main.html" %}
{% block tabs %}
{{ super() }}
{% endblock %}
{% block htmltitle %}
<title>Gunicorn - Python WSGI HTTP Server for UNIX</title>
{% endblock %}
{% block styles %}
{{ super() }}
<link rel="stylesheet" href="{{ 'assets/stylesheets/home.css' | url }}">
{% endblock %}
{% block hero %}{% endblock %}
{% block content %}{% endblock %}
{% block site_nav %}{% endblock %}
{% block container %}
<main class="home">
{{ page.content }}
</main>
{% endblock %}
{% block footer %}
{{ super() }}
{% endblock %}

View File

@ -5,5 +5,8 @@
# otherwise, oldest known-working version is 61.2
setuptools>=68.0
sphinx
sphinx_rtd_theme
mkdocs>=1.6
mkdocs-material>=9.5
mkdocs-gen-files>=0.5
mkdocs-macros-plugin>=1.0
pymdown-extensions>=10.0

View File

@ -0,0 +1,254 @@
"""Generate the Markdown settings reference for MkDocs."""
from __future__ import annotations
import inspect
import textwrap
from pathlib import Path
from typing import List
import re
import gunicorn.config as guncfg
HEAD = """\
> **Generated file** update `gunicorn/config.py` instead.
# Settings
This reference is built directly from `gunicorn.config.KNOWN_SETTINGS` and is
regenerated during every documentation build.
!!! note
Settings can be provided through the `GUNICORN_CMD_ARGS` environment
variable. For example:
```console
$ GUNICORN_CMD_ARGS="--bind=127.0.0.1 --workers=3" gunicorn app:app
```
_Added in 19.7._
"""
def _format_default(setting: guncfg.Setting) -> tuple[str, bool]:
if hasattr(setting, "default_doc"):
text = textwrap.dedent(setting.default_doc).strip("\n")
return text, True
default = setting.default
if callable(default):
source = textwrap.dedent(inspect.getsource(default)).strip("\n")
return f"```python\n{source}\n```", True
if default == "":
return "`''`", False
return f"`{default!r}`", False
def _format_cli(setting: guncfg.Setting) -> str | None:
if not setting.cli:
return None
if setting.meta:
variants = [f"`{opt} {setting.meta}`" for opt in setting.cli]
else:
variants = [f"`{opt}`" for opt in setting.cli]
return ", ".join(variants)
REF_MAP = {
"forwarded-allow-ips": ("reference/settings.md", "forwarded_allow_ips"),
"forwarder-headers": ("reference/settings.md", "forwarder_headers"),
"proxy-allow-ips": ("reference/settings.md", "proxy_allow_ips"),
"worker-class": ("reference/settings.md", "worker_class"),
"reload": ("reference/settings.md", "reload"),
"raw-env": ("reference/settings.md", "raw_env"),
"check-config": ("reference/settings.md", "check_config"),
"errorlog": ("reference/settings.md", "errorlog"),
"logconfig": ("reference/settings.md", "logconfig"),
"logconfig-json": ("reference/settings.md", "logconfig_json"),
"ssl-context": ("reference/settings.md", "ssl_context"),
"ssl-version": ("reference/settings.md", "ssl_version"),
"blocking-os-fchmod": ("reference/settings.md", "blocking_os_fchmod"),
"configuration_file": ("../configure.md", "configuration-file"),
}
REF_PATTERN = re.compile(r":ref:`([^`]+)`")
def _convert_refs(text: str) -> str:
def repl(match: re.Match[str]) -> str:
raw = match.group(1)
if "<" in raw and raw.endswith(">"):
label, target = raw.split("<", 1)
target = target[:-1]
label = label.replace("\n", " ").strip()
else:
label, target = None, raw.strip()
info = REF_MAP.get(target)
if not info:
return (label or target).replace("\n", " ").strip()
path, anchor = info
if path.endswith(".md"):
if path == "reference/settings.md" and anchor:
href = f"#{anchor}"
else:
href = path + (f"#{anchor}" if anchor else "")
else:
href = path + (f"#{anchor}" if anchor else "")
text = (label or target).replace("\n", " ").strip()
return f"[{text}]({href})"
return REF_PATTERN.sub(repl, text)
def _consume_indented(lines: List[str], start: int) -> tuple[str, int]:
body: List[str] = []
i = start
while i < len(lines):
line = lines[i]
if line.startswith(" ") or not line.strip():
body.append(line)
i += 1
else:
break
text = textwrap.dedent("\n".join(body)).strip("\n")
return text, i
def _convert_desc(desc: str) -> str:
raw_lines = textwrap.dedent(desc).splitlines()
output: List[str] = []
i = 0
while i < len(raw_lines):
line = raw_lines[i]
stripped = line.strip()
if stripped.startswith(".. note::"):
body, i = _consume_indented(raw_lines, i + 1)
output.append("!!! note")
if body:
for body_line in body.splitlines():
output.append(f" {body_line}" if body_line else "")
output.append("")
continue
if stripped.startswith(".. warning::"):
body, i = _consume_indented(raw_lines, i + 1)
output.append("!!! warning")
if body:
for body_line in body.splitlines():
output.append(f" {body_line}" if body_line else "")
output.append("")
continue
if stripped.startswith(".. deprecated::"):
version = stripped.split("::", 1)[1].strip()
body, i = _consume_indented(raw_lines, i + 1)
title = f"Deprecated in {version}" if version else "Deprecated"
output.append(f"!!! danger \"{title}\"")
if body:
for body_line in body.splitlines():
output.append(f" {body_line}" if body_line else "")
output.append("")
continue
if stripped.startswith(".. versionadded::"):
version = stripped.split("::", 1)[1].strip()
body, i = _consume_indented(raw_lines, i + 1)
title = f"Added in {version}" if version else "Added"
output.append(f"!!! info \"{title}\"")
if body:
for body_line in body.splitlines():
output.append(f" {body_line}" if body_line else "")
output.append("")
continue
if stripped.startswith(".. versionchanged::"):
version = stripped.split("::", 1)[1].strip()
body, i = _consume_indented(raw_lines, i + 1)
title = f"Changed in {version}" if version else "Changed"
output.append(f"!!! info \"{title}\"")
if body:
for body_line in body.splitlines():
output.append(f" {body_line}" if body_line else "")
output.append("")
continue
if stripped.startswith(".. code::") or stripped.startswith(".. code-block::"):
language = stripped.split("::", 1)[1].strip()
body, i = _consume_indented(raw_lines, i + 1)
fence = language or "text"
output.append(f"```{fence}")
if body:
output.append(body)
output.append("```")
output.append("")
continue
output.append(line)
i += 1
text = "\n".join(output)
text = _convert_refs(text)
# Collapse excessive blank lines
text = re.sub(r"\n{3,}", "\n\n", text)
return text.strip("\n")
def _format_setting(setting: guncfg.Setting) -> str:
lines: list[str] = [f"### `{setting.name}`", ""]
cli = _format_cli(setting)
if cli:
lines.extend((f"**Command line:** {cli}", ""))
default_text, is_block = _format_default(setting)
if is_block:
lines.append("**Default:**")
lines.append("")
lines.append(default_text)
else:
lines.append(f"**Default:** {default_text}")
lines.append("")
desc = _convert_desc(setting.desc)
if desc:
lines.append(desc)
lines.append("")
return "\n".join(lines)
def render_settings() -> str:
sections: list[str] = [HEAD, '<span id="blocking_os_fchmod"></span>', ""]
known_settings = sorted(guncfg.KNOWN_SETTINGS, key=lambda s: s.section)
current_section: str | None = None
for setting in known_settings:
if setting.section != current_section:
current_section = setting.section
sections.append(f"## {current_section}\n")
sections.append(_format_setting(setting))
return "\n".join(sections).strip() + "\n"
def _write_output(markdown: str) -> None:
try:
import mkdocs_gen_files # type: ignore
except ImportError:
mkdocs_gen_files = None
if mkdocs_gen_files is not None:
try:
with mkdocs_gen_files.open("reference/settings.md", "w") as fh:
fh.write(markdown)
return
except Exception:
pass
output = Path(__file__).resolve().parents[1] / "docs" / "content" / "reference" / "settings.md"
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(markdown, encoding="utf-8")
def main() -> None:
markdown = render_settings()
_write_output(markdown)
if __name__ == "__main__":
main()

643
uv.lock generated Normal file
View File

@ -0,0 +1,643 @@
version = 1
revision = 3
requires-python = ">=3.10"
[[package]]
name = "backports-asyncio-runner"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
]
[[package]]
name = "cffi"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" },
{ url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" },
{ url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
{ url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
{ url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
{ url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
{ url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
{ url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
{ url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
{ url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
{ url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "coverage"
version = "7.13.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/23/f9/e92df5e07f3fc8d4c7f9a0f146ef75446bf870351cd37b788cf5897f8079/coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd", size = 825862, upload-time = "2025-12-28T15:42:56.969Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2d/9a/3742e58fd04b233df95c012ee9f3dfe04708a5e1d32613bd2d47d4e1be0d/coverage-7.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1fa280b3ad78eea5be86f94f461c04943d942697e0dac889fa18fff8f5f9147", size = 218633, upload-time = "2025-12-28T15:40:10.165Z" },
{ url = "https://files.pythonhosted.org/packages/7e/45/7e6bdc94d89cd7c8017ce735cf50478ddfe765d4fbf0c24d71d30ea33d7a/coverage-7.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c3d8c679607220979434f494b139dfb00131ebf70bb406553d69c1ff01a5c33d", size = 219147, upload-time = "2025-12-28T15:40:12.069Z" },
{ url = "https://files.pythonhosted.org/packages/f7/38/0d6a258625fd7f10773fe94097dc16937a5f0e3e0cdf3adef67d3ac6baef/coverage-7.13.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:339dc63b3eba969067b00f41f15ad161bf2946613156fb131266d8debc8e44d0", size = 245894, upload-time = "2025-12-28T15:40:13.556Z" },
{ url = "https://files.pythonhosted.org/packages/27/58/409d15ea487986994cbd4d06376e9860e9b157cfbfd402b1236770ab8dd2/coverage-7.13.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db622b999ffe49cb891f2fff3b340cdc2f9797d01a0a202a0973ba2562501d90", size = 247721, upload-time = "2025-12-28T15:40:15.37Z" },
{ url = "https://files.pythonhosted.org/packages/da/bf/6e8056a83fd7a96c93341f1ffe10df636dd89f26d5e7b9ca511ce3bcf0df/coverage-7.13.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1443ba9acbb593fa7c1c29e011d7c9761545fe35e7652e85ce7f51a16f7e08d", size = 249585, upload-time = "2025-12-28T15:40:17.226Z" },
{ url = "https://files.pythonhosted.org/packages/f4/15/e1daff723f9f5959acb63cbe35b11203a9df77ee4b95b45fffd38b318390/coverage-7.13.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c832ec92c4499ac463186af72f9ed4d8daec15499b16f0a879b0d1c8e5cf4a3b", size = 246597, upload-time = "2025-12-28T15:40:19.028Z" },
{ url = "https://files.pythonhosted.org/packages/74/a6/1efd31c5433743a6ddbc9d37ac30c196bb07c7eab3d74fbb99b924c93174/coverage-7.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:562ec27dfa3f311e0db1ba243ec6e5f6ab96b1edfcfc6cf86f28038bc4961ce6", size = 247626, upload-time = "2025-12-28T15:40:20.846Z" },
{ url = "https://files.pythonhosted.org/packages/6d/9f/1609267dd3e749f57fdd66ca6752567d1c13b58a20a809dc409b263d0b5f/coverage-7.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4de84e71173d4dada2897e5a0e1b7877e5eefbfe0d6a44edee6ce31d9b8ec09e", size = 245629, upload-time = "2025-12-28T15:40:22.397Z" },
{ url = "https://files.pythonhosted.org/packages/e2/f6/6815a220d5ec2466383d7cc36131b9fa6ecbe95c50ec52a631ba733f306a/coverage-7.13.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a5a68357f686f8c4d527a2dc04f52e669c2fc1cbde38f6f7eb6a0e58cbd17cae", size = 245901, upload-time = "2025-12-28T15:40:23.836Z" },
{ url = "https://files.pythonhosted.org/packages/ac/58/40576554cd12e0872faf6d2c0eb3bc85f71d78427946ddd19ad65201e2c0/coverage-7.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:77cc258aeb29a3417062758975521eae60af6f79e930d6993555eeac6a8eac29", size = 246505, upload-time = "2025-12-28T15:40:25.421Z" },
{ url = "https://files.pythonhosted.org/packages/3b/77/9233a90253fba576b0eee81707b5781d0e21d97478e5377b226c5b096c0f/coverage-7.13.1-cp310-cp310-win32.whl", hash = "sha256:bb4f8c3c9a9f34423dba193f241f617b08ffc63e27f67159f60ae6baf2dcfe0f", size = 221257, upload-time = "2025-12-28T15:40:27.217Z" },
{ url = "https://files.pythonhosted.org/packages/e0/43/e842ff30c1a0a623ec80db89befb84a3a7aad7bfe44a6ea77d5a3e61fedd/coverage-7.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:c8e2706ceb622bc63bac98ebb10ef5da80ed70fbd8a7999a5076de3afaef0fb1", size = 222191, upload-time = "2025-12-28T15:40:28.916Z" },
{ url = "https://files.pythonhosted.org/packages/b4/9b/77baf488516e9ced25fc215a6f75d803493fc3f6a1a1227ac35697910c2a/coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a55d509a1dc5a5b708b5dad3b5334e07a16ad4c2185e27b40e4dba796ab7f88", size = 218755, upload-time = "2025-12-28T15:40:30.812Z" },
{ url = "https://files.pythonhosted.org/packages/d7/cd/7ab01154e6eb79ee2fab76bf4d89e94c6648116557307ee4ebbb85e5c1bf/coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d010d080c4888371033baab27e47c9df7d6fb28d0b7b7adf85a4a49be9298b3", size = 219257, upload-time = "2025-12-28T15:40:32.333Z" },
{ url = "https://files.pythonhosted.org/packages/01/d5/b11ef7863ffbbdb509da0023fad1e9eda1c0eaea61a6d2ea5b17d4ac706e/coverage-7.13.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d938b4a840fb1523b9dfbbb454f652967f18e197569c32266d4d13f37244c3d9", size = 249657, upload-time = "2025-12-28T15:40:34.1Z" },
{ url = "https://files.pythonhosted.org/packages/f7/7c/347280982982383621d29b8c544cf497ae07ac41e44b1ca4903024131f55/coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf100a3288f9bb7f919b87eb84f87101e197535b9bd0e2c2b5b3179633324fee", size = 251581, upload-time = "2025-12-28T15:40:36.131Z" },
{ url = "https://files.pythonhosted.org/packages/82/f6/ebcfed11036ade4c0d75fa4453a6282bdd225bc073862766eec184a4c643/coverage-7.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef6688db9bf91ba111ae734ba6ef1a063304a881749726e0d3575f5c10a9facf", size = 253691, upload-time = "2025-12-28T15:40:37.626Z" },
{ url = "https://files.pythonhosted.org/packages/02/92/af8f5582787f5d1a8b130b2dcba785fa5e9a7a8e121a0bb2220a6fdbdb8a/coverage-7.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b609fc9cdbd1f02e51f67f51e5aee60a841ef58a68d00d5ee2c0faf357481a3", size = 249799, upload-time = "2025-12-28T15:40:39.47Z" },
{ url = "https://files.pythonhosted.org/packages/24/aa/0e39a2a3b16eebf7f193863323edbff38b6daba711abaaf807d4290cf61a/coverage-7.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c43257717611ff5e9a1d79dce8e47566235ebda63328718d9b65dd640bc832ef", size = 251389, upload-time = "2025-12-28T15:40:40.954Z" },
{ url = "https://files.pythonhosted.org/packages/73/46/7f0c13111154dc5b978900c0ccee2e2ca239b910890e674a77f1363d483e/coverage-7.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e09fbecc007f7b6afdfb3b07ce5bd9f8494b6856dd4f577d26c66c391b829851", size = 249450, upload-time = "2025-12-28T15:40:42.489Z" },
{ url = "https://files.pythonhosted.org/packages/ac/ca/e80da6769e8b669ec3695598c58eef7ad98b0e26e66333996aee6316db23/coverage-7.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a03a4f3a19a189919c7055098790285cc5c5b0b3976f8d227aea39dbf9f8bfdb", size = 249170, upload-time = "2025-12-28T15:40:44.279Z" },
{ url = "https://files.pythonhosted.org/packages/af/18/9e29baabdec1a8644157f572541079b4658199cfd372a578f84228e860de/coverage-7.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3820778ea1387c2b6a818caec01c63adc5b3750211af6447e8dcfb9b6f08dbba", size = 250081, upload-time = "2025-12-28T15:40:45.748Z" },
{ url = "https://files.pythonhosted.org/packages/00/f8/c3021625a71c3b2f516464d322e41636aea381018319050a8114105872ee/coverage-7.13.1-cp311-cp311-win32.whl", hash = "sha256:ff10896fa55167371960c5908150b434b71c876dfab97b69478f22c8b445ea19", size = 221281, upload-time = "2025-12-28T15:40:47.232Z" },
{ url = "https://files.pythonhosted.org/packages/27/56/c216625f453df6e0559ed666d246fcbaaa93f3aa99eaa5080cea1229aa3d/coverage-7.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:a998cc0aeeea4c6d5622a3754da5a493055d2d95186bad877b0a34ea6e6dbe0a", size = 222215, upload-time = "2025-12-28T15:40:49.19Z" },
{ url = "https://files.pythonhosted.org/packages/5c/9a/be342e76f6e531cae6406dc46af0d350586f24d9b67fdfa6daee02df71af/coverage-7.13.1-cp311-cp311-win_arm64.whl", hash = "sha256:fea07c1a39a22614acb762e3fbbb4011f65eedafcb2948feeef641ac78b4ee5c", size = 220886, upload-time = "2025-12-28T15:40:51.067Z" },
{ url = "https://files.pythonhosted.org/packages/ce/8a/87af46cccdfa78f53db747b09f5f9a21d5fc38d796834adac09b30a8ce74/coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3", size = 218927, upload-time = "2025-12-28T15:40:52.814Z" },
{ url = "https://files.pythonhosted.org/packages/82/a8/6e22fdc67242a4a5a153f9438d05944553121c8f4ba70cb072af4c41362e/coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e", size = 219288, upload-time = "2025-12-28T15:40:54.262Z" },
{ url = "https://files.pythonhosted.org/packages/d0/0a/853a76e03b0f7c4375e2ca025df45c918beb367f3e20a0a8e91967f6e96c/coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c", size = 250786, upload-time = "2025-12-28T15:40:56.059Z" },
{ url = "https://files.pythonhosted.org/packages/ea/b4/694159c15c52b9f7ec7adf49d50e5f8ee71d3e9ef38adb4445d13dd56c20/coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62", size = 253543, upload-time = "2025-12-28T15:40:57.585Z" },
{ url = "https://files.pythonhosted.org/packages/96/b2/7f1f0437a5c855f87e17cf5d0dc35920b6440ff2b58b1ba9788c059c26c8/coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968", size = 254635, upload-time = "2025-12-28T15:40:59.443Z" },
{ url = "https://files.pythonhosted.org/packages/e9/d1/73c3fdb8d7d3bddd9473c9c6a2e0682f09fc3dfbcb9c3f36412a7368bcab/coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e", size = 251202, upload-time = "2025-12-28T15:41:01.328Z" },
{ url = "https://files.pythonhosted.org/packages/66/3c/f0edf75dcc152f145d5598329e864bbbe04ab78660fe3e8e395f9fff010f/coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f", size = 252566, upload-time = "2025-12-28T15:41:03.319Z" },
{ url = "https://files.pythonhosted.org/packages/17/b3/e64206d3c5f7dcbceafd14941345a754d3dbc78a823a6ed526e23b9cdaab/coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee", size = 250711, upload-time = "2025-12-28T15:41:06.411Z" },
{ url = "https://files.pythonhosted.org/packages/dc/ad/28a3eb970a8ef5b479ee7f0c484a19c34e277479a5b70269dc652b730733/coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf", size = 250278, upload-time = "2025-12-28T15:41:08.285Z" },
{ url = "https://files.pythonhosted.org/packages/54/e3/c8f0f1a93133e3e1291ca76cbb63565bd4b5c5df63b141f539d747fff348/coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c", size = 252154, upload-time = "2025-12-28T15:41:09.969Z" },
{ url = "https://files.pythonhosted.org/packages/d0/bf/9939c5d6859c380e405b19e736321f1c7d402728792f4c752ad1adcce005/coverage-7.13.1-cp312-cp312-win32.whl", hash = "sha256:ff86d4e85188bba72cfb876df3e11fa243439882c55957184af44a35bd5880b7", size = 221487, upload-time = "2025-12-28T15:41:11.468Z" },
{ url = "https://files.pythonhosted.org/packages/fa/dc/7282856a407c621c2aad74021680a01b23010bb8ebf427cf5eacda2e876f/coverage-7.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:16cc1da46c04fb0fb128b4dc430b78fa2aba8a6c0c9f8eb391fd5103409a6ac6", size = 222299, upload-time = "2025-12-28T15:41:13.386Z" },
{ url = "https://files.pythonhosted.org/packages/10/79/176a11203412c350b3e9578620013af35bcdb79b651eb976f4a4b32044fa/coverage-7.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d9bc218650022a768f3775dd7fdac1886437325d8d295d923ebcfef4892ad5c", size = 220941, upload-time = "2025-12-28T15:41:14.975Z" },
{ url = "https://files.pythonhosted.org/packages/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78", size = 218951, upload-time = "2025-12-28T15:41:16.653Z" },
{ url = "https://files.pythonhosted.org/packages/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b", size = 219325, upload-time = "2025-12-28T15:41:18.609Z" },
{ url = "https://files.pythonhosted.org/packages/59/f6/efdabdb4929487baeb7cb2a9f7dac457d9356f6ad1b255be283d58b16316/coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd", size = 250309, upload-time = "2025-12-28T15:41:20.629Z" },
{ url = "https://files.pythonhosted.org/packages/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992", size = 252907, upload-time = "2025-12-28T15:41:22.257Z" },
{ url = "https://files.pythonhosted.org/packages/75/38/f1ea837e3dc1231e086db1638947e00d264e7e8c41aa8ecacf6e1e0c05f4/coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4", size = 254148, upload-time = "2025-12-28T15:41:23.87Z" },
{ url = "https://files.pythonhosted.org/packages/7f/43/f4f16b881aaa34954ba446318dea6b9ed5405dd725dd8daac2358eda869a/coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a", size = 250515, upload-time = "2025-12-28T15:41:25.437Z" },
{ url = "https://files.pythonhosted.org/packages/84/34/8cba7f00078bd468ea914134e0144263194ce849ec3baad187ffb6203d1c/coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766", size = 252292, upload-time = "2025-12-28T15:41:28.459Z" },
{ url = "https://files.pythonhosted.org/packages/8c/a4/cffac66c7652d84ee4ac52d3ccb94c015687d3b513f9db04bfcac2ac800d/coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4", size = 250242, upload-time = "2025-12-28T15:41:30.02Z" },
{ url = "https://files.pythonhosted.org/packages/f4/78/9a64d462263dde416f3c0067efade7b52b52796f489b1037a95b0dc389c9/coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398", size = 250068, upload-time = "2025-12-28T15:41:32.007Z" },
{ url = "https://files.pythonhosted.org/packages/69/c8/a8994f5fece06db7c4a97c8fc1973684e178599b42e66280dded0524ef00/coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784", size = 251846, upload-time = "2025-12-28T15:41:33.946Z" },
{ url = "https://files.pythonhosted.org/packages/cc/f7/91fa73c4b80305c86598a2d4e54ba22df6bf7d0d97500944af7ef155d9f7/coverage-7.13.1-cp313-cp313-win32.whl", hash = "sha256:549d195116a1ba1e1ae2f5ca143f9777800f6636eab917d4f02b5310d6d73461", size = 221512, upload-time = "2025-12-28T15:41:35.519Z" },
{ url = "https://files.pythonhosted.org/packages/45/0b/0768b4231d5a044da8f75e097a8714ae1041246bb765d6b5563bab456735/coverage-7.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500", size = 222321, upload-time = "2025-12-28T15:41:37.371Z" },
{ url = "https://files.pythonhosted.org/packages/9b/b8/bdcb7253b7e85157282450262008f1366aa04663f3e3e4c30436f596c3e2/coverage-7.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:868a2fae76dfb06e87291bcbd4dcbcc778a8500510b618d50496e520bd94d9b9", size = 220949, upload-time = "2025-12-28T15:41:39.553Z" },
{ url = "https://files.pythonhosted.org/packages/70/52/f2be52cc445ff75ea8397948c96c1b4ee14f7f9086ea62fc929c5ae7b717/coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc", size = 219643, upload-time = "2025-12-28T15:41:41.567Z" },
{ url = "https://files.pythonhosted.org/packages/47/79/c85e378eaa239e2edec0c5523f71542c7793fe3340954eafb0bc3904d32d/coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a", size = 219997, upload-time = "2025-12-28T15:41:43.418Z" },
{ url = "https://files.pythonhosted.org/packages/fe/9b/b1ade8bfb653c0bbce2d6d6e90cc6c254cbb99b7248531cc76253cb4da6d/coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4", size = 261296, upload-time = "2025-12-28T15:41:45.207Z" },
{ url = "https://files.pythonhosted.org/packages/1f/af/ebf91e3e1a2473d523e87e87fd8581e0aa08741b96265730e2d79ce78d8d/coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6", size = 263363, upload-time = "2025-12-28T15:41:47.163Z" },
{ url = "https://files.pythonhosted.org/packages/c4/8b/fb2423526d446596624ac7fde12ea4262e66f86f5120114c3cfd0bb2befa/coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1", size = 265783, upload-time = "2025-12-28T15:41:49.03Z" },
{ url = "https://files.pythonhosted.org/packages/9b/26/ef2adb1e22674913b89f0fe7490ecadcef4a71fa96f5ced90c60ec358789/coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd", size = 260508, upload-time = "2025-12-28T15:41:51.035Z" },
{ url = "https://files.pythonhosted.org/packages/ce/7d/f0f59b3404caf662e7b5346247883887687c074ce67ba453ea08c612b1d5/coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c", size = 263357, upload-time = "2025-12-28T15:41:52.631Z" },
{ url = "https://files.pythonhosted.org/packages/1a/b1/29896492b0b1a047604d35d6fa804f12818fa30cdad660763a5f3159e158/coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0", size = 260978, upload-time = "2025-12-28T15:41:54.589Z" },
{ url = "https://files.pythonhosted.org/packages/48/f2/971de1238a62e6f0a4128d37adadc8bb882ee96afbe03ff1570291754629/coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e", size = 259877, upload-time = "2025-12-28T15:41:56.263Z" },
{ url = "https://files.pythonhosted.org/packages/6a/fc/0474efcbb590ff8628830e9aaec5f1831594874360e3251f1fdec31d07a3/coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53", size = 262069, upload-time = "2025-12-28T15:41:58.093Z" },
{ url = "https://files.pythonhosted.org/packages/88/4f/3c159b7953db37a7b44c0eab8a95c37d1aa4257c47b4602c04022d5cb975/coverage-7.13.1-cp313-cp313t-win32.whl", hash = "sha256:75a6f4aa904301dab8022397a22c0039edc1f51e90b83dbd4464b8a38dc87842", size = 222184, upload-time = "2025-12-28T15:41:59.763Z" },
{ url = "https://files.pythonhosted.org/packages/58/a5/6b57d28f81417f9335774f20679d9d13b9a8fb90cd6160957aa3b54a2379/coverage-7.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:309ef5706e95e62578cda256b97f5e097916a2c26247c287bbe74794e7150df2", size = 223250, upload-time = "2025-12-28T15:42:01.52Z" },
{ url = "https://files.pythonhosted.org/packages/81/7c/160796f3b035acfbb58be80e02e484548595aa67e16a6345e7910ace0a38/coverage-7.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:92f980729e79b5d16d221038dbf2e8f9a9136afa072f9d5d6ed4cb984b126a09", size = 221521, upload-time = "2025-12-28T15:42:03.275Z" },
{ url = "https://files.pythonhosted.org/packages/aa/8e/ba0e597560c6563fc0adb902fda6526df5d4aa73bb10adf0574d03bd2206/coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894", size = 218996, upload-time = "2025-12-28T15:42:04.978Z" },
{ url = "https://files.pythonhosted.org/packages/6b/8e/764c6e116f4221dc7aa26c4061181ff92edb9c799adae6433d18eeba7a14/coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a", size = 219326, upload-time = "2025-12-28T15:42:06.691Z" },
{ url = "https://files.pythonhosted.org/packages/4f/a6/6130dc6d8da28cdcbb0f2bf8865aeca9b157622f7c0031e48c6cf9a0e591/coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f", size = 250374, upload-time = "2025-12-28T15:42:08.786Z" },
{ url = "https://files.pythonhosted.org/packages/82/2b/783ded568f7cd6b677762f780ad338bf4b4750205860c17c25f7c708995e/coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909", size = 252882, upload-time = "2025-12-28T15:42:10.515Z" },
{ url = "https://files.pythonhosted.org/packages/cd/b2/9808766d082e6a4d59eb0cc881a57fc1600eb2c5882813eefff8254f71b5/coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4", size = 254218, upload-time = "2025-12-28T15:42:12.208Z" },
{ url = "https://files.pythonhosted.org/packages/44/ea/52a985bb447c871cb4d2e376e401116520991b597c85afdde1ea9ef54f2c/coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75", size = 250391, upload-time = "2025-12-28T15:42:14.21Z" },
{ url = "https://files.pythonhosted.org/packages/7f/1d/125b36cc12310718873cfc8209ecfbc1008f14f4f5fa0662aa608e579353/coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9", size = 252239, upload-time = "2025-12-28T15:42:16.292Z" },
{ url = "https://files.pythonhosted.org/packages/6a/16/10c1c164950cade470107f9f14bbac8485f8fb8515f515fca53d337e4a7f/coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465", size = 250196, upload-time = "2025-12-28T15:42:18.54Z" },
{ url = "https://files.pythonhosted.org/packages/2a/c6/cd860fac08780c6fd659732f6ced1b40b79c35977c1356344e44d72ba6c4/coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864", size = 250008, upload-time = "2025-12-28T15:42:20.365Z" },
{ url = "https://files.pythonhosted.org/packages/f0/3a/a8c58d3d38f82a5711e1e0a67268362af48e1a03df27c03072ac30feefcf/coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9", size = 251671, upload-time = "2025-12-28T15:42:22.114Z" },
{ url = "https://files.pythonhosted.org/packages/f0/bc/fd4c1da651d037a1e3d53e8cb3f8182f4b53271ffa9a95a2e211bacc0349/coverage-7.13.1-cp314-cp314-win32.whl", hash = "sha256:6e73ebb44dca5f708dc871fe0b90cf4cff1a13f9956f747cc87b535a840386f5", size = 221777, upload-time = "2025-12-28T15:42:23.919Z" },
{ url = "https://files.pythonhosted.org/packages/4b/50/71acabdc8948464c17e90b5ffd92358579bd0910732c2a1c9537d7536aa6/coverage-7.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:be753b225d159feb397bd0bf91ae86f689bad0da09d3b301478cd39b878ab31a", size = 222592, upload-time = "2025-12-28T15:42:25.619Z" },
{ url = "https://files.pythonhosted.org/packages/f7/c8/a6fb943081bb0cc926499c7907731a6dc9efc2cbdc76d738c0ab752f1a32/coverage-7.13.1-cp314-cp314-win_arm64.whl", hash = "sha256:228b90f613b25ba0019361e4ab81520b343b622fc657daf7e501c4ed6a2366c0", size = 221169, upload-time = "2025-12-28T15:42:27.629Z" },
{ url = "https://files.pythonhosted.org/packages/16/61/d5b7a0a0e0e40d62e59bc8c7aa1afbd86280d82728ba97f0673b746b78e2/coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a", size = 219730, upload-time = "2025-12-28T15:42:29.306Z" },
{ url = "https://files.pythonhosted.org/packages/a3/2c/8881326445fd071bb49514d1ce97d18a46a980712b51fee84f9ab42845b4/coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6", size = 220001, upload-time = "2025-12-28T15:42:31.319Z" },
{ url = "https://files.pythonhosted.org/packages/b5/d7/50de63af51dfa3a7f91cc37ad8fcc1e244b734232fbc8b9ab0f3c834a5cd/coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673", size = 261370, upload-time = "2025-12-28T15:42:32.992Z" },
{ url = "https://files.pythonhosted.org/packages/e1/2c/d31722f0ec918fd7453b2758312729f645978d212b410cd0f7c2aed88a94/coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5", size = 263485, upload-time = "2025-12-28T15:42:34.759Z" },
{ url = "https://files.pythonhosted.org/packages/fa/7a/2c114fa5c5fc08ba0777e4aec4c97e0b4a1afcb69c75f1f54cff78b073ab/coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d", size = 265890, upload-time = "2025-12-28T15:42:36.517Z" },
{ url = "https://files.pythonhosted.org/packages/65/d9/f0794aa1c74ceabc780fe17f6c338456bbc4e96bd950f2e969f48ac6fb20/coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8", size = 260445, upload-time = "2025-12-28T15:42:38.646Z" },
{ url = "https://files.pythonhosted.org/packages/49/23/184b22a00d9bb97488863ced9454068c79e413cb23f472da6cbddc6cfc52/coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486", size = 263357, upload-time = "2025-12-28T15:42:40.788Z" },
{ url = "https://files.pythonhosted.org/packages/7d/bd/58af54c0c9199ea4190284f389005779d7daf7bf3ce40dcd2d2b2f96da69/coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564", size = 260959, upload-time = "2025-12-28T15:42:42.808Z" },
{ url = "https://files.pythonhosted.org/packages/4b/2a/6839294e8f78a4891bf1df79d69c536880ba2f970d0ff09e7513d6e352e9/coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7", size = 259792, upload-time = "2025-12-28T15:42:44.818Z" },
{ url = "https://files.pythonhosted.org/packages/ba/c3/528674d4623283310ad676c5af7414b9850ab6d55c2300e8aa4b945ec554/coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416", size = 262123, upload-time = "2025-12-28T15:42:47.108Z" },
{ url = "https://files.pythonhosted.org/packages/06/c5/8c0515692fb4c73ac379d8dc09b18eaf0214ecb76ea6e62467ba7a1556ff/coverage-7.13.1-cp314-cp314t-win32.whl", hash = "sha256:18be793c4c87de2965e1c0f060f03d9e5aff66cfeae8e1dbe6e5b88056ec153f", size = 222562, upload-time = "2025-12-28T15:42:49.144Z" },
{ url = "https://files.pythonhosted.org/packages/05/0e/c0a0c4678cb30dac735811db529b321d7e1c9120b79bd728d4f4d6b010e9/coverage-7.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0e42e0ec0cd3e0d851cb3c91f770c9301f48647cb2877cb78f74bdaa07639a79", size = 223670, upload-time = "2025-12-28T15:42:51.218Z" },
{ url = "https://files.pythonhosted.org/packages/f5/5f/b177aa0011f354abf03a8f30a85032686d290fdeed4222b27d36b4372a50/coverage-7.13.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eaecf47ef10c72ece9a2a92118257da87e460e113b83cc0d2905cbbe931792b4", size = 221707, upload-time = "2025-12-28T15:42:53.034Z" },
{ url = "https://files.pythonhosted.org/packages/cc/48/d9f421cb8da5afaa1a64570d9989e00fb7955e6acddc5a12979f7666ef60/coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573", size = 210722, upload-time = "2025-12-28T15:42:54.901Z" },
]
[package.optional-dependencies]
toml = [
{ name = "tomli", marker = "python_full_version <= '3.11'" },
]
[[package]]
name = "dnspython"
version = "2.8.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
]
[[package]]
name = "eventlet"
version = "0.40.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "dnspython" },
{ name = "greenlet" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/d8/f72d8583db7c559445e0e9500a9b9787332370c16980802204a403634585/eventlet-0.40.4.tar.gz", hash = "sha256:69bef712b1be18b4930df6f0c495d2a882bf7b63aa111e7b6eeff461cfcaf26f", size = 565920, upload-time = "2025-11-26T13:57:31.126Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/22/6d/8e1fa901f6a8307f90e7bd932064e27a0062a4a7a16af38966a9c3293c52/eventlet-0.40.4-py3-none-any.whl", hash = "sha256:6326c6d0bf55810bece151f7a5750207c610f389ba110ffd1541ed6e5215485b", size = 364588, upload-time = "2025-11-26T13:57:29.09Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
]
[[package]]
name = "gevent"
version = "25.9.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation == 'CPython' and sys_platform == 'win32'" },
{ name = "greenlet", marker = "platform_python_implementation == 'CPython'" },
{ name = "zope-event" },
{ name = "zope-interface" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9e/48/b3ef2673ffb940f980966694e40d6d32560f3ffa284ecaeb5ea3a90a6d3f/gevent-25.9.1.tar.gz", hash = "sha256:adf9cd552de44a4e6754c51ff2e78d9193b7fa6eab123db9578a210e657235dd", size = 5059025, upload-time = "2025-09-17T16:15:34.528Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/c7/2c60fc4e5c9144f2b91e23af8d87c626870ad3183cfd09d2b3ba6d699178/gevent-25.9.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:856b990be5590e44c3a3dc6c8d48a40eaccbb42e99d2b791d11d1e7711a4297e", size = 1831980, upload-time = "2025-09-17T15:41:22.597Z" },
{ url = "https://files.pythonhosted.org/packages/2e/ae/49bf0a01f95a1c92c001d7b3f482a2301626b8a0617f448c4cd14ca9b5d4/gevent-25.9.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fe1599d0b30e6093eb3213551751b24feeb43db79f07e89d98dd2f3330c9063e", size = 1918777, upload-time = "2025-09-17T15:48:57.223Z" },
{ url = "https://files.pythonhosted.org/packages/88/3f/266d2eb9f5d75c184a55a39e886b53a4ea7f42ff31f195220a363f0e3f9e/gevent-25.9.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:f0d8b64057b4bf1529b9ef9bd2259495747fba93d1f836c77bfeaacfec373fd0", size = 1869235, upload-time = "2025-09-17T15:49:18.255Z" },
{ url = "https://files.pythonhosted.org/packages/76/24/c0c7c7db70ca74c7b1918388ebda7c8c2a3c3bff0bbfbaa9280ed04b3340/gevent-25.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b56cbc820e3136ba52cd690bdf77e47a4c239964d5f80dc657c1068e0fe9521c", size = 2177334, upload-time = "2025-09-17T15:15:10.073Z" },
{ url = "https://files.pythonhosted.org/packages/4c/1e/de96bd033c03955f54c455b51a5127b1d540afcfc97838d1801fafce6d2e/gevent-25.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c5fa9ce5122c085983e33e0dc058f81f5264cebe746de5c401654ab96dddfca8", size = 1847708, upload-time = "2025-09-17T15:52:38.475Z" },
{ url = "https://files.pythonhosted.org/packages/26/8b/6851e9cd3e4f322fa15c1d196cbf1a8a123da69788b078227dd13dd4208f/gevent-25.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:03c74fec58eda4b4edc043311fca8ba4f8744ad1632eb0a41d5ec25413581975", size = 2234274, upload-time = "2025-09-17T15:24:07.797Z" },
{ url = "https://files.pythonhosted.org/packages/0f/d8/b1178b70538c91493bec283018b47c16eab4bac9ddf5a3d4b7dd905dab60/gevent-25.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a8ae9f895e8651d10b0a8328a61c9c53da11ea51b666388aa99b0ce90f9fdc27", size = 1695326, upload-time = "2025-09-17T20:10:25.455Z" },
{ url = "https://files.pythonhosted.org/packages/81/86/03f8db0704fed41b0fa830425845f1eb4e20c92efa3f18751ee17809e9c6/gevent-25.9.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5aff9e8342dc954adb9c9c524db56c2f3557999463445ba3d9cbe3dada7b7", size = 1792418, upload-time = "2025-09-17T15:41:24.384Z" },
{ url = "https://files.pythonhosted.org/packages/5f/35/f6b3a31f0849a62cfa2c64574bcc68a781d5499c3195e296e892a121a3cf/gevent-25.9.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1cdf6db28f050ee103441caa8b0448ace545364f775059d5e2de089da975c457", size = 1875700, upload-time = "2025-09-17T15:48:59.652Z" },
{ url = "https://files.pythonhosted.org/packages/66/1e/75055950aa9b48f553e061afa9e3728061b5ccecca358cef19166e4ab74a/gevent-25.9.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:812debe235a8295be3b2a63b136c2474241fa5c58af55e6a0f8cfc29d4936235", size = 1831365, upload-time = "2025-09-17T15:49:19.426Z" },
{ url = "https://files.pythonhosted.org/packages/31/e8/5c1f6968e5547e501cfa03dcb0239dff55e44c3660a37ec534e32a0c008f/gevent-25.9.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b28b61ff9216a3d73fe8f35669eefcafa957f143ac534faf77e8a19eb9e6883a", size = 2122087, upload-time = "2025-09-17T15:15:12.329Z" },
{ url = "https://files.pythonhosted.org/packages/c0/2c/ebc5d38a7542af9fb7657bfe10932a558bb98c8a94e4748e827d3823fced/gevent-25.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5e4b6278b37373306fc6b1e5f0f1cf56339a1377f67c35972775143d8d7776ff", size = 1808776, upload-time = "2025-09-17T15:52:40.16Z" },
{ url = "https://files.pythonhosted.org/packages/e6/26/e1d7d6c8ffbf76fe1fbb4e77bdb7f47d419206adc391ec40a8ace6ebbbf0/gevent-25.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d99f0cb2ce43c2e8305bf75bee61a8bde06619d21b9d0316ea190fc7a0620a56", size = 2179141, upload-time = "2025-09-17T15:24:09.895Z" },
{ url = "https://files.pythonhosted.org/packages/1d/6c/bb21fd9c095506aeeaa616579a356aa50935165cc0f1e250e1e0575620a7/gevent-25.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:72152517ecf548e2f838c61b4be76637d99279dbaa7e01b3924df040aa996586", size = 1677941, upload-time = "2025-09-17T19:59:50.185Z" },
{ url = "https://files.pythonhosted.org/packages/f7/49/e55930ba5259629eb28ac7ee1abbca971996a9165f902f0249b561602f24/gevent-25.9.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:46b188248c84ffdec18a686fcac5dbb32365d76912e14fda350db5dc0bfd4f86", size = 2955991, upload-time = "2025-09-17T14:52:30.568Z" },
{ url = "https://files.pythonhosted.org/packages/aa/88/63dc9e903980e1da1e16541ec5c70f2b224ec0a8e34088cb42794f1c7f52/gevent-25.9.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f2b54ea3ca6f0c763281cd3f96010ac7e98c2e267feb1221b5a26e2ca0b9a692", size = 1808503, upload-time = "2025-09-17T15:41:25.59Z" },
{ url = "https://files.pythonhosted.org/packages/7a/8d/7236c3a8f6ef7e94c22e658397009596fa90f24c7d19da11ad7ab3a9248e/gevent-25.9.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7a834804ac00ed8a92a69d3826342c677be651b1c3cd66cc35df8bc711057aa2", size = 1890001, upload-time = "2025-09-17T15:49:01.227Z" },
{ url = "https://files.pythonhosted.org/packages/4f/63/0d7f38c4a2085ecce26b50492fc6161aa67250d381e26d6a7322c309b00f/gevent-25.9.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:323a27192ec4da6b22a9e51c3d9d896ff20bc53fdc9e45e56eaab76d1c39dd74", size = 1855335, upload-time = "2025-09-17T15:49:20.582Z" },
{ url = "https://files.pythonhosted.org/packages/95/18/da5211dfc54c7a57e7432fd9a6ffeae1ce36fe5a313fa782b1c96529ea3d/gevent-25.9.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6ea78b39a2c51d47ff0f130f4c755a9a4bbb2dd9721149420ad4712743911a51", size = 2109046, upload-time = "2025-09-17T15:15:13.817Z" },
{ url = "https://files.pythonhosted.org/packages/a6/5a/7bb5ec8e43a2c6444853c4a9f955f3e72f479d7c24ea86c95fb264a2de65/gevent-25.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:dc45cd3e1cc07514a419960af932a62eb8515552ed004e56755e4bf20bad30c5", size = 1827099, upload-time = "2025-09-17T15:52:41.384Z" },
{ url = "https://files.pythonhosted.org/packages/ca/d4/b63a0a60635470d7d986ef19897e893c15326dd69e8fb342c76a4f07fe9e/gevent-25.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34e01e50c71eaf67e92c186ee0196a039d6e4f4b35670396baed4a2d8f1b347f", size = 2172623, upload-time = "2025-09-17T15:24:12.03Z" },
{ url = "https://files.pythonhosted.org/packages/d5/98/caf06d5d22a7c129c1fb2fc1477306902a2c8ddfd399cd26bbbd4caf2141/gevent-25.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acd6bcd5feabf22c7c5174bd3b9535ee9f088d2bbce789f740ad8d6554b18f3", size = 1682837, upload-time = "2025-09-17T19:48:47.318Z" },
{ url = "https://files.pythonhosted.org/packages/5a/77/b97f086388f87f8ad3e01364f845004aef0123d4430241c7c9b1f9bde742/gevent-25.9.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:4f84591d13845ee31c13f44bdf6bd6c3dbf385b5af98b2f25ec328213775f2ed", size = 2973739, upload-time = "2025-09-17T14:53:30.279Z" },
{ url = "https://files.pythonhosted.org/packages/3c/2e/9d5f204ead343e5b27bbb2fedaec7cd0009d50696b2266f590ae845d0331/gevent-25.9.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9cdbb24c276a2d0110ad5c978e49daf620b153719ac8a548ce1250a7eb1b9245", size = 1809165, upload-time = "2025-09-17T15:41:27.193Z" },
{ url = "https://files.pythonhosted.org/packages/10/3e/791d1bf1eb47748606d5f2c2aa66571f474d63e0176228b1f1fd7b77ab37/gevent-25.9.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:88b6c07169468af631dcf0fdd3658f9246d6822cc51461d43f7c44f28b0abb82", size = 1890638, upload-time = "2025-09-17T15:49:02.45Z" },
{ url = "https://files.pythonhosted.org/packages/f2/5c/9ad0229b2b4d81249ca41e4f91dd8057deaa0da6d4fbe40bf13cdc5f7a47/gevent-25.9.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b7bb0e29a7b3e6ca9bed2394aa820244069982c36dc30b70eb1004dd67851a48", size = 1857118, upload-time = "2025-09-17T15:49:22.125Z" },
{ url = "https://files.pythonhosted.org/packages/49/2a/3010ed6c44179a3a5c5c152e6de43a30ff8bc2c8de3115ad8733533a018f/gevent-25.9.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2951bb070c0ee37b632ac9134e4fdaad70d2e660c931bb792983a0837fe5b7d7", size = 2111598, upload-time = "2025-09-17T15:15:15.226Z" },
{ url = "https://files.pythonhosted.org/packages/08/75/6bbe57c19a7aa4527cc0f9afcdf5a5f2aed2603b08aadbccb5bf7f607ff4/gevent-25.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4e17c2d57e9a42e25f2a73d297b22b60b2470a74be5a515b36c984e1a246d47", size = 1829059, upload-time = "2025-09-17T15:52:42.596Z" },
{ url = "https://files.pythonhosted.org/packages/06/6e/19a9bee9092be45679cb69e4dd2e0bf5f897b7140b4b39c57cc123d24829/gevent-25.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d94936f8f8b23d9de2251798fcb603b84f083fdf0d7f427183c1828fb64f117", size = 2173529, upload-time = "2025-09-17T15:24:13.897Z" },
{ url = "https://files.pythonhosted.org/packages/ca/4f/50de9afd879440e25737e63f5ba6ee764b75a3abe17376496ab57f432546/gevent-25.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:eb51c5f9537b07da673258b4832f6635014fee31690c3f0944d34741b69f92fa", size = 1681518, upload-time = "2025-09-17T19:39:47.488Z" },
{ url = "https://files.pythonhosted.org/packages/15/1a/948f8167b2cdce573cf01cec07afc64d0456dc134b07900b26ac7018b37e/gevent-25.9.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:1a3fe4ea1c312dbf6b375b416925036fe79a40054e6bf6248ee46526ea628be1", size = 2982934, upload-time = "2025-09-17T14:54:11.302Z" },
{ url = "https://files.pythonhosted.org/packages/9b/ec/726b146d1d3aad82e03d2e1e1507048ab6072f906e83f97f40667866e582/gevent-25.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0adb937f13e5fb90cca2edf66d8d7e99d62a299687400ce2edee3f3504009356", size = 1813982, upload-time = "2025-09-17T15:41:28.506Z" },
{ url = "https://files.pythonhosted.org/packages/35/5d/5f83f17162301662bd1ce702f8a736a8a8cac7b7a35e1d8b9866938d1f9d/gevent-25.9.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:427f869a2050a4202d93cf7fd6ab5cffb06d3e9113c10c967b6e2a0d45237cb8", size = 1894902, upload-time = "2025-09-17T15:49:03.702Z" },
{ url = "https://files.pythonhosted.org/packages/83/cd/cf5e74e353f60dab357829069ffc300a7bb414c761f52cf8c0c6e9728b8d/gevent-25.9.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c049880175e8c93124188f9d926af0a62826a3b81aa6d3074928345f8238279e", size = 1861792, upload-time = "2025-09-17T15:49:23.279Z" },
{ url = "https://files.pythonhosted.org/packages/dd/65/b9a4526d4a4edce26fe4b3b993914ec9dc64baabad625a3101e51adb17f3/gevent-25.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b5a67a0974ad9f24721034d1e008856111e0535f1541499f72a733a73d658d1c", size = 2113215, upload-time = "2025-09-17T15:15:16.34Z" },
{ url = "https://files.pythonhosted.org/packages/e5/be/7d35731dfaf8370795b606e515d964a0967e129db76ea7873f552045dd39/gevent-25.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1d0f5d8d73f97e24ea8d24d8be0f51e0cf7c54b8021c1fddb580bf239474690f", size = 1833449, upload-time = "2025-09-17T15:52:43.75Z" },
{ url = "https://files.pythonhosted.org/packages/65/58/7bc52544ea5e63af88c4a26c90776feb42551b7555a1c89c20069c168a3f/gevent-25.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ddd3ff26e5c4240d3fbf5516c2d9d5f2a998ef87cfb73e1429cfaeaaec860fa6", size = 2176034, upload-time = "2025-09-17T15:24:15.676Z" },
{ url = "https://files.pythonhosted.org/packages/c2/69/a7c4ba2ffbc7c7dbf6d8b4f5d0f0a421f7815d229f4909854266c445a3d4/gevent-25.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:bb63c0d6cb9950cc94036a4995b9cc4667b8915366613449236970f4394f94d7", size = 1703019, upload-time = "2025-09-17T19:30:55.272Z" },
]
[[package]]
name = "greenlet"
version = "3.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658, upload-time = "2025-12-04T14:23:37.494Z" },
{ url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810, upload-time = "2025-12-04T14:50:04.154Z" },
{ url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248, upload-time = "2025-12-04T14:57:39.35Z" },
{ url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403, upload-time = "2025-12-04T15:07:10.831Z" },
{ url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910, upload-time = "2025-12-04T14:25:59.705Z" },
{ url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206, upload-time = "2025-12-04T15:04:21.027Z" },
{ url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359, upload-time = "2025-12-04T14:27:26.548Z" },
{ url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740, upload-time = "2025-12-04T14:47:52.773Z" },
{ url = "https://files.pythonhosted.org/packages/1f/cb/48e964c452ca2b92175a9b2dca037a553036cb053ba69e284650ce755f13/greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e", size = 274908, upload-time = "2025-12-04T14:23:26.435Z" },
{ url = "https://files.pythonhosted.org/packages/28/da/38d7bff4d0277b594ec557f479d65272a893f1f2a716cad91efeb8680953/greenlet-3.3.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a687205fb22794e838f947e2194c0566d3812966b41c78709554aa883183fb62", size = 577113, upload-time = "2025-12-04T14:50:05.493Z" },
{ url = "https://files.pythonhosted.org/packages/3c/f2/89c5eb0faddc3ff014f1c04467d67dee0d1d334ab81fadbf3744847f8a8a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4243050a88ba61842186cb9e63c7dfa677ec146160b0efd73b855a3d9c7fcf32", size = 590338, upload-time = "2025-12-04T14:57:41.136Z" },
{ url = "https://files.pythonhosted.org/packages/80/d7/db0a5085035d05134f8c089643da2b44cc9b80647c39e93129c5ef170d8f/greenlet-3.3.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:670d0f94cd302d81796e37299bcd04b95d62403883b24225c6b5271466612f45", size = 601098, upload-time = "2025-12-04T15:07:11.898Z" },
{ url = "https://files.pythonhosted.org/packages/dc/a6/e959a127b630a58e23529972dbc868c107f9d583b5a9f878fb858c46bc1a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948", size = 590206, upload-time = "2025-12-04T14:26:01.254Z" },
{ url = "https://files.pythonhosted.org/packages/48/60/29035719feb91798693023608447283b266b12efc576ed013dd9442364bb/greenlet-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2de5a0b09eab81fc6a382791b995b1ccf2b172a9fec934747a7a23d2ff291794", size = 1550668, upload-time = "2025-12-04T15:04:22.439Z" },
{ url = "https://files.pythonhosted.org/packages/0a/5f/783a23754b691bfa86bd72c3033aa107490deac9b2ef190837b860996c9f/greenlet-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4449a736606bd30f27f8e1ff4678ee193bc47f6ca810d705981cfffd6ce0d8c5", size = 1615483, upload-time = "2025-12-04T14:27:28.083Z" },
{ url = "https://files.pythonhosted.org/packages/1d/d5/c339b3b4bc8198b7caa4f2bd9fd685ac9f29795816d8db112da3d04175bb/greenlet-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:7652ee180d16d447a683c04e4c5f6441bae7ba7b17ffd9f6b3aff4605e9e6f71", size = 301164, upload-time = "2025-12-04T14:42:51.577Z" },
{ url = "https://files.pythonhosted.org/packages/f8/0a/a3871375c7b9727edaeeea994bfff7c63ff7804c9829c19309ba2e058807/greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb", size = 276379, upload-time = "2025-12-04T14:23:30.498Z" },
{ url = "https://files.pythonhosted.org/packages/43/ab/7ebfe34dce8b87be0d11dae91acbf76f7b8246bf9d6b319c741f99fa59c6/greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3", size = 597294, upload-time = "2025-12-04T14:50:06.847Z" },
{ url = "https://files.pythonhosted.org/packages/a4/39/f1c8da50024feecd0793dbd5e08f526809b8ab5609224a2da40aad3a7641/greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655", size = 607742, upload-time = "2025-12-04T14:57:42.349Z" },
{ url = "https://files.pythonhosted.org/packages/77/cb/43692bcd5f7a0da6ec0ec6d58ee7cddb606d055ce94a62ac9b1aa481e969/greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7", size = 622297, upload-time = "2025-12-04T15:07:13.552Z" },
{ url = "https://files.pythonhosted.org/packages/75/b0/6bde0b1011a60782108c01de5913c588cf51a839174538d266de15e4bf4d/greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b", size = 609885, upload-time = "2025-12-04T14:26:02.368Z" },
{ url = "https://files.pythonhosted.org/packages/49/0e/49b46ac39f931f59f987b7cd9f34bfec8ef81d2a1e6e00682f55be5de9f4/greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53", size = 1567424, upload-time = "2025-12-04T15:04:23.757Z" },
{ url = "https://files.pythonhosted.org/packages/05/f5/49a9ac2dff7f10091935def9165c90236d8f175afb27cbed38fb1d61ab6b/greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614", size = 1636017, upload-time = "2025-12-04T14:27:29.688Z" },
{ url = "https://files.pythonhosted.org/packages/6c/79/3912a94cf27ec503e51ba493692d6db1e3cd8ac7ac52b0b47c8e33d7f4f9/greenlet-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7a34b13d43a6b78abf828a6d0e87d3385680eaf830cd60d20d52f249faabf39", size = 301964, upload-time = "2025-12-04T14:36:58.316Z" },
{ url = "https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739", size = 275140, upload-time = "2025-12-04T14:23:01.282Z" },
{ url = "https://files.pythonhosted.org/packages/2c/80/fbe937bf81e9fca98c981fe499e59a3f45df2a04da0baa5c2be0dca0d329/greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808", size = 599219, upload-time = "2025-12-04T14:50:08.309Z" },
{ url = "https://files.pythonhosted.org/packages/c2/ff/7c985128f0514271b8268476af89aee6866df5eec04ac17dcfbc676213df/greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54", size = 610211, upload-time = "2025-12-04T14:57:43.968Z" },
{ url = "https://files.pythonhosted.org/packages/79/07/c47a82d881319ec18a4510bb30463ed6891f2ad2c1901ed5ec23d3de351f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492", size = 624311, upload-time = "2025-12-04T15:07:14.697Z" },
{ url = "https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527", size = 612833, upload-time = "2025-12-04T14:26:03.669Z" },
{ url = "https://files.pythonhosted.org/packages/b5/ba/56699ff9b7c76ca12f1cdc27a886d0f81f2189c3455ff9f65246780f713d/greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39", size = 1567256, upload-time = "2025-12-04T15:04:25.276Z" },
{ url = "https://files.pythonhosted.org/packages/1e/37/f31136132967982d698c71a281a8901daf1a8fbab935dce7c0cf15f942cc/greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8", size = 1636483, upload-time = "2025-12-04T14:27:30.804Z" },
{ url = "https://files.pythonhosted.org/packages/7e/71/ba21c3fb8c5dce83b8c01f458a42e99ffdb1963aeec08fff5a18588d8fd7/greenlet-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38", size = 301833, upload-time = "2025-12-04T14:32:23.929Z" },
{ url = "https://files.pythonhosted.org/packages/d7/7c/f0a6d0ede2c7bf092d00bc83ad5bafb7e6ec9b4aab2fbdfa6f134dc73327/greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f", size = 275671, upload-time = "2025-12-04T14:23:05.267Z" },
{ url = "https://files.pythonhosted.org/packages/44/06/dac639ae1a50f5969d82d2e3dd9767d30d6dbdbab0e1a54010c8fe90263c/greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365", size = 646360, upload-time = "2025-12-04T14:50:10.026Z" },
{ url = "https://files.pythonhosted.org/packages/e0/94/0fb76fe6c5369fba9bf98529ada6f4c3a1adf19e406a47332245ef0eb357/greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3", size = 658160, upload-time = "2025-12-04T14:57:45.41Z" },
{ url = "https://files.pythonhosted.org/packages/93/79/d2c70cae6e823fac36c3bbc9077962105052b7ef81db2f01ec3b9bf17e2b/greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45", size = 671388, upload-time = "2025-12-04T15:07:15.789Z" },
{ url = "https://files.pythonhosted.org/packages/b8/14/bab308fc2c1b5228c3224ec2bf928ce2e4d21d8046c161e44a2012b5203e/greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955", size = 660166, upload-time = "2025-12-04T14:26:05.099Z" },
{ url = "https://files.pythonhosted.org/packages/4b/d2/91465d39164eaa0085177f61983d80ffe746c5a1860f009811d498e7259c/greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55", size = 1615193, upload-time = "2025-12-04T15:04:27.041Z" },
{ url = "https://files.pythonhosted.org/packages/42/1b/83d110a37044b92423084d52d5d5a3b3a73cafb51b547e6d7366ff62eff1/greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc", size = 1683653, upload-time = "2025-12-04T14:27:32.366Z" },
{ url = "https://files.pythonhosted.org/packages/7c/9a/9030e6f9aa8fd7808e9c31ba4c38f87c4f8ec324ee67431d181fe396d705/greenlet-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:73f51dd0e0bdb596fb0417e475fa3c5e32d4c83638296e560086b8d7da7c4170", size = 305387, upload-time = "2025-12-04T14:26:51.063Z" },
{ url = "https://files.pythonhosted.org/packages/a0/66/bd6317bc5932accf351fc19f177ffba53712a202f9df10587da8df257c7e/greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931", size = 282638, upload-time = "2025-12-04T14:25:20.941Z" },
{ url = "https://files.pythonhosted.org/packages/30/cf/cc81cb030b40e738d6e69502ccbd0dd1bced0588e958f9e757945de24404/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388", size = 651145, upload-time = "2025-12-04T14:50:11.039Z" },
{ url = "https://files.pythonhosted.org/packages/9c/ea/1020037b5ecfe95ca7df8d8549959baceb8186031da83d5ecceff8b08cd2/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3", size = 654236, upload-time = "2025-12-04T14:57:47.007Z" },
{ url = "https://files.pythonhosted.org/packages/69/cc/1e4bae2e45ca2fa55299f4e85854606a78ecc37fead20d69322f96000504/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221", size = 662506, upload-time = "2025-12-04T15:07:16.906Z" },
{ url = "https://files.pythonhosted.org/packages/57/b9/f8025d71a6085c441a7eaff0fd928bbb275a6633773667023d19179fe815/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b", size = 653783, upload-time = "2025-12-04T14:26:06.225Z" },
{ url = "https://files.pythonhosted.org/packages/f6/c7/876a8c7a7485d5d6b5c6821201d542ef28be645aa024cfe1145b35c120c1/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd", size = 1614857, upload-time = "2025-12-04T15:04:28.484Z" },
{ url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" },
]
[[package]]
name = "gunicorn"
source = { editable = "." }
dependencies = [
{ name = "packaging" },
]
[package.optional-dependencies]
eventlet = [
{ name = "eventlet" },
]
gevent = [
{ name = "gevent" },
]
setproctitle = [
{ name = "setproctitle" },
]
testing = [
{ name = "coverage" },
{ name = "eventlet" },
{ name = "gevent" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
]
tornado = [
{ name = "tornado" },
]
[package.metadata]
requires-dist = [
{ name = "coverage", marker = "extra == 'testing'" },
{ name = "eventlet", marker = "extra == 'eventlet'", specifier = ">=0.40.3" },
{ name = "eventlet", marker = "extra == 'testing'", specifier = ">=0.40.3" },
{ name = "gevent", marker = "extra == 'gevent'", specifier = ">=23.9.0" },
{ name = "gevent", marker = "extra == 'testing'", specifier = ">=23.9.0" },
{ name = "packaging" },
{ name = "pytest", marker = "extra == 'testing'" },
{ name = "pytest-asyncio", marker = "extra == 'testing'" },
{ name = "pytest-cov", marker = "extra == 'testing'" },
{ name = "setproctitle", marker = "extra == 'setproctitle'" },
{ name = "tornado", marker = "extra == 'tornado'", specifier = ">=6.5.0" },
]
provides-extras = ["gevent", "eventlet", "tornado", "gthread", "setproctitle", "testing"]
[[package]]
name = "iniconfig"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
]
[[package]]
name = "packaging"
version = "26.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
]
[[package]]
name = "pluggy"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
[[package]]
name = "pytest"
version = "9.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
]
[[package]]
name = "pytest-asyncio"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
{ name = "pytest" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
]
[[package]]
name = "pytest-cov"
version = "7.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "coverage", extra = ["toml"] },
{ name = "pluggy" },
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" },
]
[[package]]
name = "setproctitle"
version = "1.3.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8d/48/49393a96a2eef1ab418b17475fb92b8fcfad83d099e678751b05472e69de/setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e", size = 27002, upload-time = "2025-09-05T12:51:25.278Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/48/fb401ec8c4953d519d05c87feca816ad668b8258448ff60579ac7a1c1386/setproctitle-1.3.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cf555b6299f10a6eb44e4f96d2f5a3884c70ce25dc5c8796aaa2f7b40e72cb1b", size = 18079, upload-time = "2025-09-05T12:49:07.732Z" },
{ url = "https://files.pythonhosted.org/packages/cc/a3/c2b0333c2716fb3b4c9a973dd113366ac51b4f8d56b500f4f8f704b4817a/setproctitle-1.3.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:690b4776f9c15aaf1023bb07d7c5b797681a17af98a4a69e76a1d504e41108b7", size = 13099, upload-time = "2025-09-05T12:49:09.222Z" },
{ url = "https://files.pythonhosted.org/packages/0e/f8/17bda581c517678260e6541b600eeb67745f53596dc077174141ba2f6702/setproctitle-1.3.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:00afa6fc507967d8c9d592a887cdc6c1f5742ceac6a4354d111ca0214847732c", size = 31793, upload-time = "2025-09-05T12:49:10.297Z" },
{ url = "https://files.pythonhosted.org/packages/27/d1/76a33ae80d4e788ecab9eb9b53db03e81cfc95367ec7e3fbf4989962fedd/setproctitle-1.3.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e02667f6b9fc1238ba753c0f4b0a37ae184ce8f3bbbc38e115d99646b3f4cd3", size = 32779, upload-time = "2025-09-05T12:49:12.157Z" },
{ url = "https://files.pythonhosted.org/packages/59/27/1a07c38121967061564f5e0884414a5ab11a783260450172d4fc68c15621/setproctitle-1.3.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83fcd271567d133eb9532d3b067c8a75be175b2b3b271e2812921a05303a693f", size = 34578, upload-time = "2025-09-05T12:49:13.393Z" },
{ url = "https://files.pythonhosted.org/packages/d8/d4/725e6353935962d8bb12cbf7e7abba1d0d738c7f6935f90239d8e1ccf913/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13fe37951dda1a45c35d77d06e3da5d90e4f875c4918a7312b3b4556cfa7ff64", size = 32030, upload-time = "2025-09-05T12:49:15.362Z" },
{ url = "https://files.pythonhosted.org/packages/67/24/e4677ae8e1cb0d549ab558b12db10c175a889be0974c589c428fece5433e/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a05509cfb2059e5d2ddff701d38e474169e9ce2a298cf1b6fd5f3a213a553fe5", size = 33363, upload-time = "2025-09-05T12:49:16.829Z" },
{ url = "https://files.pythonhosted.org/packages/55/d4/69ce66e4373a48fdbb37489f3ded476bb393e27f514968c3a69a67343ae0/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6da835e76ae18574859224a75db6e15c4c2aaa66d300a57efeaa4c97ca4c7381", size = 31508, upload-time = "2025-09-05T12:49:18.032Z" },
{ url = "https://files.pythonhosted.org/packages/4b/5a/42c1ed0e9665d068146a68326529b5686a1881c8b9197c2664db4baf6aeb/setproctitle-1.3.7-cp310-cp310-win32.whl", hash = "sha256:9e803d1b1e20240a93bac0bc1025363f7f80cb7eab67dfe21efc0686cc59ad7c", size = 12558, upload-time = "2025-09-05T12:49:19.742Z" },
{ url = "https://files.pythonhosted.org/packages/dc/fe/dd206cc19a25561921456f6cb12b405635319299b6f366e0bebe872abc18/setproctitle-1.3.7-cp310-cp310-win_amd64.whl", hash = "sha256:a97200acc6b64ec4cada52c2ecaf1fba1ef9429ce9c542f8a7db5bcaa9dcbd95", size = 13245, upload-time = "2025-09-05T12:49:21.023Z" },
{ url = "https://files.pythonhosted.org/packages/04/cd/1b7ba5cad635510720ce19d7122154df96a2387d2a74217be552887c93e5/setproctitle-1.3.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a600eeb4145fb0ee6c287cb82a2884bd4ec5bbb076921e287039dcc7b7cc6dd0", size = 18085, upload-time = "2025-09-05T12:49:22.183Z" },
{ url = "https://files.pythonhosted.org/packages/8f/1a/b2da0a620490aae355f9d72072ac13e901a9fec809a6a24fc6493a8f3c35/setproctitle-1.3.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97a090fed480471bb175689859532709e28c085087e344bca45cf318034f70c4", size = 13097, upload-time = "2025-09-05T12:49:23.322Z" },
{ url = "https://files.pythonhosted.org/packages/18/2e/bd03ff02432a181c1787f6fc2a678f53b7dacdd5ded69c318fe1619556e8/setproctitle-1.3.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1607b963e7b53e24ec8a2cb4e0ab3ae591d7c6bf0a160feef0551da63452b37f", size = 32191, upload-time = "2025-09-05T12:49:24.567Z" },
{ url = "https://files.pythonhosted.org/packages/28/78/1e62fc0937a8549f2220445ed2175daacee9b6764c7963b16148119b016d/setproctitle-1.3.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a20fb1a3974e2dab857870cf874b325b8705605cb7e7e8bcbb915bca896f52a9", size = 33203, upload-time = "2025-09-05T12:49:25.871Z" },
{ url = "https://files.pythonhosted.org/packages/a0/3c/65edc65db3fa3df400cf13b05e9d41a3c77517b4839ce873aa6b4043184f/setproctitle-1.3.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8d961bba676e07d77665204f36cffaa260f526e7b32d07ab3df6a2c1dfb44ba", size = 34963, upload-time = "2025-09-05T12:49:27.044Z" },
{ url = "https://files.pythonhosted.org/packages/a1/32/89157e3de997973e306e44152522385f428e16f92f3cf113461489e1e2ee/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:db0fd964fbd3a9f8999b502f65bd2e20883fdb5b1fae3a424e66db9a793ed307", size = 32398, upload-time = "2025-09-05T12:49:28.909Z" },
{ url = "https://files.pythonhosted.org/packages/4a/18/77a765a339ddf046844cb4513353d8e9dcd8183da9cdba6e078713e6b0b2/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:db116850fcf7cca19492030f8d3b4b6e231278e8fe097a043957d22ce1bdf3ee", size = 33657, upload-time = "2025-09-05T12:49:30.323Z" },
{ url = "https://files.pythonhosted.org/packages/6b/63/f0b6205c64d74d2a24a58644a38ec77bdbaa6afc13747e75973bf8904932/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:316664d8b24a5c91ee244460bdaf7a74a707adaa9e14fbe0dc0a53168bb9aba1", size = 31836, upload-time = "2025-09-05T12:49:32.309Z" },
{ url = "https://files.pythonhosted.org/packages/ba/51/e1277f9ba302f1a250bbd3eedbbee747a244b3cc682eb58fb9733968f6d8/setproctitle-1.3.7-cp311-cp311-win32.whl", hash = "sha256:b74774ca471c86c09b9d5037c8451fff06bb82cd320d26ae5a01c758088c0d5d", size = 12556, upload-time = "2025-09-05T12:49:33.529Z" },
{ url = "https://files.pythonhosted.org/packages/b6/7b/822a23f17e9003dfdee92cd72758441ca2a3680388da813a371b716fb07f/setproctitle-1.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:acb9097213a8dd3410ed9f0dc147840e45ca9797785272928d4be3f0e69e3be4", size = 13243, upload-time = "2025-09-05T12:49:34.553Z" },
{ url = "https://files.pythonhosted.org/packages/fb/f0/2dc88e842077719d7384d86cc47403e5102810492b33680e7dadcee64cd8/setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e", size = 18049, upload-time = "2025-09-05T12:49:36.241Z" },
{ url = "https://files.pythonhosted.org/packages/f0/b4/50940504466689cda65680c9e9a1e518e5750c10490639fa687489ac7013/setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798", size = 13079, upload-time = "2025-09-05T12:49:38.088Z" },
{ url = "https://files.pythonhosted.org/packages/d0/99/71630546b9395b095f4082be41165d1078204d1696c2d9baade3de3202d0/setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629", size = 32932, upload-time = "2025-09-05T12:49:39.271Z" },
{ url = "https://files.pythonhosted.org/packages/50/22/cee06af4ffcfb0e8aba047bd44f5262e644199ae7527ae2c1f672b86495c/setproctitle-1.3.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6915964a6dda07920a1159321dcd6d94fc7fc526f815ca08a8063aeca3c204f1", size = 33736, upload-time = "2025-09-05T12:49:40.565Z" },
{ url = "https://files.pythonhosted.org/packages/5c/00/a5949a8bb06ef5e7df214fc393bb2fb6aedf0479b17214e57750dfdd0f24/setproctitle-1.3.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cff72899861c765bd4021d1ff1c68d60edc129711a2fdba77f9cb69ef726a8b6", size = 35605, upload-time = "2025-09-05T12:49:42.362Z" },
{ url = "https://files.pythonhosted.org/packages/b0/3a/50caca532a9343828e3bf5778c7a84d6c737a249b1796d50dd680290594d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b7cb05bd446687ff816a3aaaf831047fc4c364feff7ada94a66024f1367b448c", size = 33143, upload-time = "2025-09-05T12:49:43.515Z" },
{ url = "https://files.pythonhosted.org/packages/ca/14/b843a251296ce55e2e17c017d6b9f11ce0d3d070e9265de4ecad948b913d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3a57b9a00de8cae7e2a1f7b9f0c2ac7b69372159e16a7708aa2f38f9e5cc987a", size = 34434, upload-time = "2025-09-05T12:49:45.31Z" },
{ url = "https://files.pythonhosted.org/packages/c8/b7/06145c238c0a6d2c4bc881f8be230bb9f36d2bf51aff7bddcb796d5eed67/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d8828b356114f6b308b04afe398ed93803d7fca4a955dd3abe84430e28d33739", size = 32795, upload-time = "2025-09-05T12:49:46.419Z" },
{ url = "https://files.pythonhosted.org/packages/ef/dc/ef76a81fac9bf27b84ed23df19c1f67391a753eed6e3c2254ebcb5133f56/setproctitle-1.3.7-cp312-cp312-win32.whl", hash = "sha256:b0304f905efc845829ac2bc791ddebb976db2885f6171f4a3de678d7ee3f7c9f", size = 12552, upload-time = "2025-09-05T12:49:47.635Z" },
{ url = "https://files.pythonhosted.org/packages/e2/5b/a9fe517912cd6e28cf43a212b80cb679ff179a91b623138a99796d7d18a0/setproctitle-1.3.7-cp312-cp312-win_amd64.whl", hash = "sha256:9888ceb4faea3116cf02a920ff00bfbc8cc899743e4b4ac914b03625bdc3c300", size = 13247, upload-time = "2025-09-05T12:49:49.16Z" },
{ url = "https://files.pythonhosted.org/packages/5d/2f/fcedcade3b307a391b6e17c774c6261a7166aed641aee00ed2aad96c63ce/setproctitle-1.3.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c3736b2a423146b5e62230502e47e08e68282ff3b69bcfe08a322bee73407922", size = 18047, upload-time = "2025-09-05T12:49:50.271Z" },
{ url = "https://files.pythonhosted.org/packages/23/ae/afc141ca9631350d0a80b8f287aac79a76f26b6af28fd8bf92dae70dc2c5/setproctitle-1.3.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3384e682b158d569e85a51cfbde2afd1ab57ecf93ea6651fe198d0ba451196ee", size = 13073, upload-time = "2025-09-05T12:49:51.46Z" },
{ url = "https://files.pythonhosted.org/packages/87/ed/0a4f00315bc02510395b95eec3d4aa77c07192ee79f0baae77ea7b9603d8/setproctitle-1.3.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0564a936ea687cd24dffcea35903e2a20962aa6ac20e61dd3a207652401492dd", size = 33284, upload-time = "2025-09-05T12:49:52.741Z" },
{ url = "https://files.pythonhosted.org/packages/fc/e4/adf3c4c0a2173cb7920dc9df710bcc67e9bcdbf377e243b7a962dc31a51a/setproctitle-1.3.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5d1cb3f81531f0eb40e13246b679a1bdb58762b170303463cb06ecc296f26d0", size = 34104, upload-time = "2025-09-05T12:49:54.416Z" },
{ url = "https://files.pythonhosted.org/packages/52/4f/6daf66394152756664257180439d37047aa9a1cfaa5e4f5ed35e93d1dc06/setproctitle-1.3.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a7d159e7345f343b44330cbba9194169b8590cb13dae940da47aa36a72aa9929", size = 35982, upload-time = "2025-09-05T12:49:56.295Z" },
{ url = "https://files.pythonhosted.org/packages/1b/62/f2c0595403cf915db031f346b0e3b2c0096050e90e0be658a64f44f4278a/setproctitle-1.3.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0b5074649797fd07c72ca1f6bff0406f4a42e1194faac03ecaab765ce605866f", size = 33150, upload-time = "2025-09-05T12:49:58.025Z" },
{ url = "https://files.pythonhosted.org/packages/a0/29/10dd41cde849fb2f9b626c846b7ea30c99c81a18a5037a45cc4ba33c19a7/setproctitle-1.3.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:61e96febced3f61b766115381d97a21a6265a0f29188a791f6df7ed777aef698", size = 34463, upload-time = "2025-09-05T12:49:59.424Z" },
{ url = "https://files.pythonhosted.org/packages/71/3c/cedd8eccfaf15fb73a2c20525b68c9477518917c9437737fa0fda91e378f/setproctitle-1.3.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:047138279f9463f06b858e579cc79580fbf7a04554d24e6bddf8fe5dddbe3d4c", size = 32848, upload-time = "2025-09-05T12:50:01.107Z" },
{ url = "https://files.pythonhosted.org/packages/d1/3e/0a0e27d1c9926fecccfd1f91796c244416c70bf6bca448d988638faea81d/setproctitle-1.3.7-cp313-cp313-win32.whl", hash = "sha256:7f47accafac7fe6535ba8ba9efd59df9d84a6214565108d0ebb1199119c9cbbd", size = 12544, upload-time = "2025-09-05T12:50:15.81Z" },
{ url = "https://files.pythonhosted.org/packages/36/1b/6bf4cb7acbbd5c846ede1c3f4d6b4ee52744d402e43546826da065ff2ab7/setproctitle-1.3.7-cp313-cp313-win_amd64.whl", hash = "sha256:fe5ca35aeec6dc50cabab9bf2d12fbc9067eede7ff4fe92b8f5b99d92e21263f", size = 13235, upload-time = "2025-09-05T12:50:16.89Z" },
{ url = "https://files.pythonhosted.org/packages/e6/a4/d588d3497d4714750e3eaf269e9e8985449203d82b16b933c39bd3fc52a1/setproctitle-1.3.7-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:10e92915c4b3086b1586933a36faf4f92f903c5554f3c34102d18c7d3f5378e9", size = 18058, upload-time = "2025-09-05T12:50:02.501Z" },
{ url = "https://files.pythonhosted.org/packages/05/77/7637f7682322a7244e07c373881c7e982567e2cb1dd2f31bd31481e45500/setproctitle-1.3.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:de879e9c2eab637f34b1a14c4da1e030c12658cdc69ee1b3e5be81b380163ce5", size = 13072, upload-time = "2025-09-05T12:50:03.601Z" },
{ url = "https://files.pythonhosted.org/packages/52/09/f366eca0973cfbac1470068d1313fa3fe3de4a594683385204ec7f1c4101/setproctitle-1.3.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c18246d88e227a5b16248687514f95642505000442165f4b7db354d39d0e4c29", size = 34490, upload-time = "2025-09-05T12:50:04.948Z" },
{ url = "https://files.pythonhosted.org/packages/71/36/611fc2ed149fdea17c3677e1d0df30d8186eef9562acc248682b91312706/setproctitle-1.3.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7081f193dab22df2c36f9fc6d113f3793f83c27891af8fe30c64d89d9a37e152", size = 35267, upload-time = "2025-09-05T12:50:06.015Z" },
{ url = "https://files.pythonhosted.org/packages/88/a4/64e77d0671446bd5a5554387b69e1efd915274686844bea733714c828813/setproctitle-1.3.7-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9cc9b901ce129350637426a89cfd650066a4adc6899e47822e2478a74023ff7c", size = 37376, upload-time = "2025-09-05T12:50:07.484Z" },
{ url = "https://files.pythonhosted.org/packages/89/bc/ad9c664fe524fb4a4b2d3663661a5c63453ce851736171e454fa2cdec35c/setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:80e177eff2d1ec172188d0d7fd9694f8e43d3aab76a6f5f929bee7bf7894e98b", size = 33963, upload-time = "2025-09-05T12:50:09.056Z" },
{ url = "https://files.pythonhosted.org/packages/ab/01/a36de7caf2d90c4c28678da1466b47495cbbad43badb4e982d8db8167ed4/setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:23e520776c445478a67ee71b2a3c1ffdafbe1f9f677239e03d7e2cc635954e18", size = 35550, upload-time = "2025-09-05T12:50:10.791Z" },
{ url = "https://files.pythonhosted.org/packages/dd/68/17e8aea0ed5ebc17fbf03ed2562bfab277c280e3625850c38d92a7b5fcd9/setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5fa1953126a3b9bd47049d58c51b9dac72e78ed120459bd3aceb1bacee72357c", size = 33727, upload-time = "2025-09-05T12:50:12.032Z" },
{ url = "https://files.pythonhosted.org/packages/b2/33/90a3bf43fe3a2242b4618aa799c672270250b5780667898f30663fd94993/setproctitle-1.3.7-cp313-cp313t-win32.whl", hash = "sha256:4a5e212bf438a4dbeece763f4962ad472c6008ff6702e230b4f16a037e2f6f29", size = 12549, upload-time = "2025-09-05T12:50:13.074Z" },
{ url = "https://files.pythonhosted.org/packages/0b/0e/50d1f07f3032e1f23d814ad6462bc0a138f369967c72494286b8a5228e40/setproctitle-1.3.7-cp313-cp313t-win_amd64.whl", hash = "sha256:cf2727b733e90b4f874bac53e3092aa0413fe1ea6d4f153f01207e6ce65034d9", size = 13243, upload-time = "2025-09-05T12:50:14.146Z" },
{ url = "https://files.pythonhosted.org/packages/89/c7/43ac3a98414f91d1b86a276bc2f799ad0b4b010e08497a95750d5bc42803/setproctitle-1.3.7-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:80c36c6a87ff72eabf621d0c79b66f3bdd0ecc79e873c1e9f0651ee8bf215c63", size = 18052, upload-time = "2025-09-05T12:50:17.928Z" },
{ url = "https://files.pythonhosted.org/packages/cd/2c/dc258600a25e1a1f04948073826bebc55e18dbd99dc65a576277a82146fa/setproctitle-1.3.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b53602371a52b91c80aaf578b5ada29d311d12b8a69c0c17fbc35b76a1fd4f2e", size = 13071, upload-time = "2025-09-05T12:50:19.061Z" },
{ url = "https://files.pythonhosted.org/packages/ab/26/8e3bb082992f19823d831f3d62a89409deb6092e72fc6940962983ffc94f/setproctitle-1.3.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fcb966a6c57cf07cc9448321a08f3be6b11b7635be502669bc1d8745115d7e7f", size = 33180, upload-time = "2025-09-05T12:50:20.395Z" },
{ url = "https://files.pythonhosted.org/packages/f1/af/ae692a20276d1159dd0cf77b0bcf92cbb954b965655eb4a69672099bb214/setproctitle-1.3.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46178672599b940368d769474fe13ecef1b587d58bb438ea72b9987f74c56ea5", size = 34043, upload-time = "2025-09-05T12:50:22.454Z" },
{ url = "https://files.pythonhosted.org/packages/34/b2/6a092076324dd4dac1a6d38482bedebbff5cf34ef29f58585ec76e47bc9d/setproctitle-1.3.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7f9e9e3ff135cbcc3edd2f4cf29b139f4aca040d931573102742db70ff428c17", size = 35892, upload-time = "2025-09-05T12:50:23.937Z" },
{ url = "https://files.pythonhosted.org/packages/1c/1a/8836b9f28cee32859ac36c3df85aa03e1ff4598d23ea17ca2e96b5845a8f/setproctitle-1.3.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14c7eba8d90c93b0e79c01f0bd92a37b61983c27d6d7d5a3b5defd599113d60e", size = 32898, upload-time = "2025-09-05T12:50:25.617Z" },
{ url = "https://files.pythonhosted.org/packages/ef/22/8fabdc24baf42defb599714799d8445fe3ae987ec425a26ec8e80ea38f8e/setproctitle-1.3.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9e64e98077fb30b6cf98073d6c439cd91deb8ebbf8fc62d9dbf52bd38b0c6ac0", size = 34308, upload-time = "2025-09-05T12:50:26.827Z" },
{ url = "https://files.pythonhosted.org/packages/15/1b/b9bee9de6c8cdcb3b3a6cb0b3e773afdb86bbbc1665a3bfa424a4294fda2/setproctitle-1.3.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b91387cc0f02a00ac95dcd93f066242d3cca10ff9e6153de7ee07069c6f0f7c8", size = 32536, upload-time = "2025-09-05T12:50:28.5Z" },
{ url = "https://files.pythonhosted.org/packages/37/0c/75e5f2685a5e3eda0b39a8b158d6d8895d6daf3ba86dec9e3ba021510272/setproctitle-1.3.7-cp314-cp314-win32.whl", hash = "sha256:52b054a61c99d1b72fba58b7f5486e04b20fefc6961cd76722b424c187f362ed", size = 12731, upload-time = "2025-09-05T12:50:43.955Z" },
{ url = "https://files.pythonhosted.org/packages/d2/ae/acddbce90d1361e1786e1fb421bc25baeb0c22ef244ee5d0176511769ec8/setproctitle-1.3.7-cp314-cp314-win_amd64.whl", hash = "sha256:5818e4080ac04da1851b3ec71e8a0f64e3748bf9849045180566d8b736702416", size = 13464, upload-time = "2025-09-05T12:50:45.057Z" },
{ url = "https://files.pythonhosted.org/packages/01/6d/20886c8ff2e6d85e3cabadab6aab9bb90acaf1a5cfcb04d633f8d61b2626/setproctitle-1.3.7-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6fc87caf9e323ac426910306c3e5d3205cd9f8dcac06d233fcafe9337f0928a3", size = 18062, upload-time = "2025-09-05T12:50:29.78Z" },
{ url = "https://files.pythonhosted.org/packages/9a/60/26dfc5f198715f1343b95c2f7a1c16ae9ffa45bd89ffd45a60ed258d24ea/setproctitle-1.3.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6134c63853d87a4897ba7d5cc0e16abfa687f6c66fc09f262bb70d67718f2309", size = 13075, upload-time = "2025-09-05T12:50:31.604Z" },
{ url = "https://files.pythonhosted.org/packages/21/9c/980b01f50d51345dd513047e3ba9e96468134b9181319093e61db1c47188/setproctitle-1.3.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1403d2abfd32790b6369916e2313dffbe87d6b11dca5bbd898981bcde48e7a2b", size = 34744, upload-time = "2025-09-05T12:50:32.777Z" },
{ url = "https://files.pythonhosted.org/packages/86/b4/82cd0c86e6d1c4538e1a7eb908c7517721513b801dff4ba3f98ef816a240/setproctitle-1.3.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7c5bfe4228ea22373e3025965d1a4116097e555ee3436044f5c954a5e63ac45", size = 35589, upload-time = "2025-09-05T12:50:34.13Z" },
{ url = "https://files.pythonhosted.org/packages/8a/4f/9f6b2a7417fd45673037554021c888b31247f7594ff4bd2239918c5cd6d0/setproctitle-1.3.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:585edf25e54e21a94ccb0fe81ad32b9196b69ebc4fc25f81da81fb8a50cca9e4", size = 37698, upload-time = "2025-09-05T12:50:35.524Z" },
{ url = "https://files.pythonhosted.org/packages/20/92/927b7d4744aac214d149c892cb5fa6dc6f49cfa040cb2b0a844acd63dcaf/setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:96c38cdeef9036eb2724c2210e8d0b93224e709af68c435d46a4733a3675fee1", size = 34201, upload-time = "2025-09-05T12:50:36.697Z" },
{ url = "https://files.pythonhosted.org/packages/0a/0c/fd4901db5ba4b9d9013e62f61d9c18d52290497f956745cd3e91b0d80f90/setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:45e3ef48350abb49cf937d0a8ba15e42cee1e5ae13ca41a77c66d1abc27a5070", size = 35801, upload-time = "2025-09-05T12:50:38.314Z" },
{ url = "https://files.pythonhosted.org/packages/e7/e3/54b496ac724e60e61cc3447f02690105901ca6d90da0377dffe49ff99fc7/setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1fae595d032b30dab4d659bece20debd202229fce12b55abab978b7f30783d73", size = 33958, upload-time = "2025-09-05T12:50:39.841Z" },
{ url = "https://files.pythonhosted.org/packages/ea/a8/c84bb045ebf8c6fdc7f7532319e86f8380d14bbd3084e6348df56bdfe6fd/setproctitle-1.3.7-cp314-cp314t-win32.whl", hash = "sha256:02432f26f5d1329ab22279ff863c83589894977063f59e6c4b4845804a08f8c2", size = 12745, upload-time = "2025-09-05T12:50:41.377Z" },
{ url = "https://files.pythonhosted.org/packages/08/b6/3a5a4f9952972791a9114ac01dfc123f0df79903577a3e0a7a404a695586/setproctitle-1.3.7-cp314-cp314t-win_amd64.whl", hash = "sha256:cbc388e3d86da1f766d8fc2e12682e446064c01cea9f88a88647cfe7c011de6a", size = 13469, upload-time = "2025-09-05T12:50:42.67Z" },
{ url = "https://files.pythonhosted.org/packages/34/8a/aff5506ce89bc3168cb492b18ba45573158d528184e8a9759a05a09088a9/setproctitle-1.3.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:eb440c5644a448e6203935ed60466ec8d0df7278cd22dc6cf782d07911bcbea6", size = 12654, upload-time = "2025-09-05T12:51:17.141Z" },
{ url = "https://files.pythonhosted.org/packages/41/89/5b6f2faedd6ced3d3c085a5efbd91380fb1f61f4c12bc42acad37932f4e9/setproctitle-1.3.7-pp310-pypy310_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:502b902a0e4c69031b87870ff4986c290ebbb12d6038a70639f09c331b18efb2", size = 14284, upload-time = "2025-09-05T12:51:18.393Z" },
{ url = "https://files.pythonhosted.org/packages/0a/c0/4312fed3ca393a29589603fd48f17937b4ed0638b923bac75a728382e730/setproctitle-1.3.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f6f268caeabb37ccd824d749e7ce0ec6337c4ed954adba33ec0d90cc46b0ab78", size = 13282, upload-time = "2025-09-05T12:51:19.703Z" },
{ url = "https://files.pythonhosted.org/packages/c3/5b/5e1c117ac84e3cefcf8d7a7f6b2461795a87e20869da065a5c087149060b/setproctitle-1.3.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b1cac6a4b0252b8811d60b6d8d0f157c0fdfed379ac89c25a914e6346cf355a1", size = 12587, upload-time = "2025-09-05T12:51:21.195Z" },
{ url = "https://files.pythonhosted.org/packages/73/02/b9eadc226195dcfa90eed37afe56b5dd6fa2f0e5220ab8b7867b8862b926/setproctitle-1.3.7-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1704c9e041f2b1dc38f5be4552e141e1432fba3dd52c72eeffd5bc2db04dc65", size = 14286, upload-time = "2025-09-05T12:51:22.61Z" },
{ url = "https://files.pythonhosted.org/packages/28/26/1be1d2a53c2a91ec48fa2ff4a409b395f836798adf194d99de9c059419ea/setproctitle-1.3.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b08b61976ffa548bd5349ce54404bf6b2d51bd74d4f1b241ed1b0f25bce09c3a", size = 13282, upload-time = "2025-09-05T12:51:24.094Z" },
]
[[package]]
name = "tomli"
version = "2.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" },
{ url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" },
{ url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" },
{ url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" },
{ url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" },
{ url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" },
{ url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" },
{ url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" },
{ url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" },
{ url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" },
{ url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" },
{ url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" },
{ url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" },
{ url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" },
{ url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" },
{ url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" },
{ url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" },
{ url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" },
{ url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" },
{ url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" },
{ url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" },
{ url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" },
{ url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" },
{ url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" },
{ url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" },
{ url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" },
{ url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" },
{ url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" },
{ url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" },
{ url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" },
{ url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" },
{ url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" },
{ url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" },
{ url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" },
{ url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" },
{ url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" },
{ url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" },
{ url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" },
{ url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" },
{ url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" },
{ url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" },
{ url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" },
{ url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" },
{ url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" },
{ url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" },
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
]
[[package]]
name = "tornado"
version = "6.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632, upload-time = "2025-12-15T19:21:03.836Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909, upload-time = "2025-12-15T19:20:48.382Z" },
{ url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163, upload-time = "2025-12-15T19:20:49.791Z" },
{ url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746, upload-time = "2025-12-15T19:20:51.491Z" },
{ url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083, upload-time = "2025-12-15T19:20:52.778Z" },
{ url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315, upload-time = "2025-12-15T19:20:53.996Z" },
{ url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003, upload-time = "2025-12-15T19:20:56.101Z" },
{ url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412, upload-time = "2025-12-15T19:20:57.398Z" },
{ url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392, upload-time = "2025-12-15T19:20:58.692Z" },
{ url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481, upload-time = "2025-12-15T19:21:00.008Z" },
{ url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886, upload-time = "2025-12-15T19:21:01.287Z" },
{ url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910, upload-time = "2025-12-15T19:21:02.571Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "zope-event"
version = "6.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/46/33/d3eeac228fc14de76615612ee208be2d8a5b5b0fada36bf9b62d6b40600c/zope_event-6.1.tar.gz", hash = "sha256:6052a3e0cb8565d3d4ef1a3a7809336ac519bc4fe38398cb8d466db09adef4f0", size = 18739, upload-time = "2025-11-07T08:05:49.934Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/b0/956902e5e1302f8c5d124e219c6bf214e2649f92ad5fce85b05c039a04c9/zope_event-6.1-py3-none-any.whl", hash = "sha256:0ca78b6391b694272b23ec1335c0294cc471065ed10f7f606858fc54566c25a0", size = 6414, upload-time = "2025-11-07T08:05:48.874Z" },
]
[[package]]
name = "zope-interface"
version = "8.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/86/a4/77daa5ba398996d16bb43fc721599d27d03eae68fe3c799de1963c72e228/zope_interface-8.2.tar.gz", hash = "sha256:afb20c371a601d261b4f6edb53c3c418c249db1a9717b0baafc9a9bb39ba1224", size = 254019, upload-time = "2026-01-09T07:51:07.253Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/fa/6d9eb3a33998a3019d7eb4fa1802d01d6602fad90e0aea443e6e0fe8e49a/zope_interface-8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:788c293f3165964ec6527b2d861072c68eef53425213f36d3893ebee89a89623", size = 207541, upload-time = "2026-01-09T08:04:55.378Z" },
{ url = "https://files.pythonhosted.org/packages/19/8c/ad23c96fdee84cb1f768f6695dac187cc26e9038e01c69713ba0f7dc46ab/zope_interface-8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9a4e785097e741a1c953b3970ce28f2823bd63c00adc5d276f2981dd66c96c15", size = 208075, upload-time = "2026-01-09T08:04:57.118Z" },
{ url = "https://files.pythonhosted.org/packages/dd/35/1bfd5fec31a307f0cf4065ee74ade63858ded3e2a71e248f1508118fcc95/zope_interface-8.2-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:16c69da19a06566664ddd4785f37cad5693a51d48df1515d264c20d005d322e2", size = 249528, upload-time = "2026-01-09T08:04:59.074Z" },
{ url = "https://files.pythonhosted.org/packages/c6/3a/5d50b5fdb0f8226a2edff6adb7efdd3762ec95dff827dbab1761cb9a9e85/zope_interface-8.2-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c31acfa3d7cde48bec45701b0e1f4698daffc378f559bfb296837d8c834732f6", size = 254646, upload-time = "2026-01-09T08:05:00.964Z" },
{ url = "https://files.pythonhosted.org/packages/2f/2a/ee7d675e151578eaf77828b8faac2b7ed9a69fead350bf5cf0e4afe7c73d/zope_interface-8.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0723507127f8269b8f3f22663168f717e9c9742107d1b6c9f419df561b71aa6d", size = 255083, upload-time = "2026-01-09T08:05:02.857Z" },
{ url = "https://files.pythonhosted.org/packages/5d/07/99e2342f976c3700e142eddc01524e375a9e9078869a6885d9c72f3a3659/zope_interface-8.2-cp310-cp310-win_amd64.whl", hash = "sha256:3bf73a910bb27344def2d301a03329c559a79b308e1e584686b74171d736be4e", size = 211924, upload-time = "2026-01-09T08:05:04.702Z" },
{ url = "https://files.pythonhosted.org/packages/98/97/9c2aa8caae79915ed64eb114e18816f178984c917aa9adf2a18345e4f2e5/zope_interface-8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c65ade7ea85516e428651048489f5e689e695c79188761de8c622594d1e13322", size = 208081, upload-time = "2026-01-09T08:05:06.623Z" },
{ url = "https://files.pythonhosted.org/packages/34/86/4e2fcb01a8f6780ac84923748e450af0805531f47c0956b83065c99ab543/zope_interface-8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1ef4b43659e1348f35f38e7d1a6bbc1682efde239761f335ffc7e31e798b65b", size = 208522, upload-time = "2026-01-09T08:05:07.986Z" },
{ url = "https://files.pythonhosted.org/packages/f6/eb/08e277da32ddcd4014922854096cf6dcb7081fad415892c2da1bedefbf02/zope_interface-8.2-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:dfc4f44e8de2ff4eba20af4f0a3ca42d3c43ab24a08e49ccd8558b7a4185b466", size = 255198, upload-time = "2026-01-09T08:05:09.532Z" },
{ url = "https://files.pythonhosted.org/packages/ea/a1/b32484f3281a5dc83bc713ad61eca52c543735cdf204543172087a074a74/zope_interface-8.2-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8f094bfb49179ec5dc9981cb769af1275702bd64720ef94874d9e34da1390d4c", size = 259970, upload-time = "2026-01-09T08:05:11.477Z" },
{ url = "https://files.pythonhosted.org/packages/f6/81/bca0e8ae1e487d4093a8a7cfed2118aa2d4758c8cfd66e59d2af09d71f1c/zope_interface-8.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d2bb8e7364e18f083bf6744ccf30433b2a5f236c39c95df8514e3c13007098ce", size = 261153, upload-time = "2026-01-09T08:05:13.402Z" },
{ url = "https://files.pythonhosted.org/packages/40/1e/e3ff2a708011e56b10b271b038d4cb650a8ad5b7d24352fe2edf6d6b187a/zope_interface-8.2-cp311-cp311-win_amd64.whl", hash = "sha256:6f4b4dfcfdfaa9177a600bb31cebf711fdb8c8e9ed84f14c61c420c6aa398489", size = 212330, upload-time = "2026-01-09T08:05:15.267Z" },
{ url = "https://files.pythonhosted.org/packages/e0/a0/1e1fabbd2e9c53ef92b69df6d14f4adc94ec25583b1380336905dc37e9a0/zope_interface-8.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:624b6787fc7c3e45fa401984f6add2c736b70a7506518c3b537ffaacc4b29d4c", size = 208785, upload-time = "2026-01-09T08:05:17.348Z" },
{ url = "https://files.pythonhosted.org/packages/c3/2a/88d098a06975c722a192ef1fb7d623d1b57c6a6997cf01a7aabb45ab1970/zope_interface-8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc9ded9e97a0ed17731d479596ed1071e53b18e6fdb2fc33af1e43f5fd2d3aaa", size = 208976, upload-time = "2026-01-09T08:05:18.792Z" },
{ url = "https://files.pythonhosted.org/packages/e9/e8/757398549fdfd2f8c89f32c82ae4d2f0537ae2a5d2f21f4a2f711f5a059f/zope_interface-8.2-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:532367553e4420c80c0fc0cabcc2c74080d495573706f66723edee6eae53361d", size = 259411, upload-time = "2026-01-09T08:05:20.567Z" },
{ url = "https://files.pythonhosted.org/packages/91/af/502601f0395ce84dff622f63cab47488657a04d0065547df42bee3a680ff/zope_interface-8.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2bf9cf275468bafa3c72688aad8cfcbe3d28ee792baf0b228a1b2d93bd1d541a", size = 264859, upload-time = "2026-01-09T08:05:22.234Z" },
{ url = "https://files.pythonhosted.org/packages/89/0c/d2f765b9b4814a368a7c1b0ac23b68823c6789a732112668072fe596945d/zope_interface-8.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0009d2d3c02ea783045d7804da4fd016245e5c5de31a86cebba66dd6914d59a2", size = 264398, upload-time = "2026-01-09T08:05:23.853Z" },
{ url = "https://files.pythonhosted.org/packages/4a/81/2f171fbc4222066957e6b9220c4fb9146792540102c37e6d94e5d14aad97/zope_interface-8.2-cp312-cp312-win_amd64.whl", hash = "sha256:845d14e580220ae4544bd4d7eb800f0b6034fe5585fc2536806e0a26c2ee6640", size = 212444, upload-time = "2026-01-09T08:05:25.148Z" },
{ url = "https://files.pythonhosted.org/packages/66/47/45188fb101fa060b20e6090e500682398ab415e516a0c228fbb22bc7def2/zope_interface-8.2-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:6068322004a0158c80dfd4708dfb103a899635408c67c3b10e9acec4dbacefec", size = 209170, upload-time = "2026-01-09T08:05:26.616Z" },
{ url = "https://files.pythonhosted.org/packages/09/03/f6b9336c03c2b48403c4eb73a1ec961d94dc2fb5354c583dfb5fa05fd41f/zope_interface-8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2499de92e8275d0dd68f84425b3e19e9268cd1fa8507997900fa4175f157733c", size = 209229, upload-time = "2026-01-09T08:05:28.521Z" },
{ url = "https://files.pythonhosted.org/packages/07/b1/65fe1dca708569f302ade02e6cdca309eab6752bc9f80105514f5b708651/zope_interface-8.2-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f777e68c76208503609c83ca021a6864902b646530a1a39abb9ed310d1100664", size = 259393, upload-time = "2026-01-09T08:05:29.897Z" },
{ url = "https://files.pythonhosted.org/packages/eb/a5/97b49cfceb6ed53d3dcfb3f3ebf24d83b5553194f0337fbbb3a9fec6cf78/zope_interface-8.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b05a919fdb0ed6ea942e5a7800e09a8b6cdae6f98fee1bef1c9d1a3fc43aaa0", size = 264863, upload-time = "2026-01-09T08:05:31.501Z" },
{ url = "https://files.pythonhosted.org/packages/cb/02/0b7a77292810efe3a0586a505b077ebafd5114e10c6e6e659f0c8e387e1f/zope_interface-8.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ccc62b5712dd7bd64cfba3ee63089fb11e840f5914b990033beeae3b2180b6cb", size = 264369, upload-time = "2026-01-09T08:05:32.941Z" },
{ url = "https://files.pythonhosted.org/packages/fb/1d/0d1ff3846302ed1b5bbf659316d8084b30106770a5f346b7ff4e9f540f80/zope_interface-8.2-cp313-cp313-win_amd64.whl", hash = "sha256:34f877d1d3bb7565c494ed93828fa6417641ca26faf6e8f044e0d0d500807028", size = 212447, upload-time = "2026-01-09T08:05:35.064Z" },
{ url = "https://files.pythonhosted.org/packages/1a/da/3c89de3917751446728b8898b4d53318bc2f8f6bf8196e150a063c59905e/zope_interface-8.2-cp314-cp314-macosx_10_9_x86_64.whl", hash = "sha256:46c7e4e8cbc698398a67e56ca985d19cb92365b4aafbeb6a712e8c101090f4cb", size = 209223, upload-time = "2026-01-09T08:05:36.449Z" },
{ url = "https://files.pythonhosted.org/packages/00/7f/62d00ec53f0a6e5df0c984781e6f3999ed265129c4c3413df8128d1e0207/zope_interface-8.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a87fc7517f825a97ff4a4ca4c8a950593c59e0f8e7bfe1b6f898a38d5ba9f9cf", size = 209366, upload-time = "2026-01-09T08:05:38.197Z" },
{ url = "https://files.pythonhosted.org/packages/ef/a2/f241986315174be8e00aabecfc2153cf8029c1327cab8ed53a9d979d7e08/zope_interface-8.2-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:ccf52f7d44d669203c2096c1a0c2c15d52e36b2e7a9413df50f48392c7d4d080", size = 261037, upload-time = "2026-01-09T08:05:39.568Z" },
{ url = "https://files.pythonhosted.org/packages/02/cc/b321c51d6936ede296a1b8860cf173bee2928357fe1fff7f97234899173f/zope_interface-8.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aae807efc7bd26302eb2fea05cd6de7d59269ed6ae23a6de1ee47add6de99b8c", size = 264219, upload-time = "2026-01-09T08:05:41.624Z" },
{ url = "https://files.pythonhosted.org/packages/ab/fb/5f5e7b40a2f4efd873fe173624795ca47eaa22e29051270c981361b45209/zope_interface-8.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:05a0e42d6d830f547e114de2e7cd15750dc6c0c78f8138e6c5035e51ddfff37c", size = 264390, upload-time = "2026-01-09T08:05:42.936Z" },
{ url = "https://files.pythonhosted.org/packages/f9/82/3f2bc594370bc3abd58e5f9085d263bf682a222f059ed46275cde0570810/zope_interface-8.2-cp314-cp314-win_amd64.whl", hash = "sha256:561ce42390bee90bae51cf1c012902a8033b2aaefbd0deed81e877562a116d48", size = 212585, upload-time = "2026-01-09T08:05:44.419Z" },
]