Migrate docs build to MkDocs

This commit is contained in:
Benoit Chesneau 2025-10-06 17:41:59 +02:00
parent 58d803977d
commit 19a2efec63
43 changed files with 5979 additions and 3 deletions

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

@ -0,0 +1,72 @@
name: Docs
on:
push:
branches: [ main ]
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'requirements_dev.txt'
- '.github/workflows/docs.yml'
pull_request:
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'requirements_dev.txt'
- '.github/workflows/docs.yml'
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 -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/main'
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 -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
commit_message: "docs: deploy {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

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

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

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.

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

@ -0,0 +1,83 @@
<span id="design"></span>
# Design
A brief look at Gunicorn's architecture.
## Server model
Gunicorn uses a pre-fork worker model: a master process manages worker
processes, while the workers handle requests and responses. The master never
touches individual client sockets.
### Master
The master process listens for signals (TTIN, TTOU, CHLD, etc.) and adjusts the
worker pool accordingly. `TTIN`/`TTOU` change the number of workers; `CHLD`
indicates a worker exited and must be restarted.
### Sync workers
The default `sync` worker handles one request at a time. Errors affect only the
current request. Because connections close after each response, persistent
connections are not supported even if you set `Keep-Alive` headers manually.
### Async workers
Async workers are powered by [greenlets](https://github.com/python-greenlet/greenlet)
through [Eventlet](http://eventlet.net/) or [Gevent](http://www.gevent.org/).
Most apps work without modification, though full compatibility may require
patches (for example installing [`psycogreen`](https://github.com/psycopg/psycogreen/)
when using [Psycopg](http://initd.org/psycopg/)). Some apps that depend on the
original blocking behaviour may not be compatible.
### Gthread workers
`gthread` is a threaded worker. The main loop accepts connections and places
them in a thread pool. Keep-alive connections return to the pool to await
further events; idle connections close after the keepalive timeout.
### Tornado workers
A Tornado worker class exists for Tornado-based applications. While it can
serve WSGI apps, this configuration is not recommended.
<span id="asyncio-workers"></span>
### AsyncIO workers
Use third-party workers to pair Gunicorn with asyncio frameworks (see the
[aiohttp deployment guide](https://docs.aiohttp.org/en/stable/deployment.html#nginx-gunicorn)
or the [Flask aiohttp example](https://github.com/benoitc/gunicorn/blob/master/examples/frameworks/flaskapp_aiohttp_wsgi.py)).
## Choosing a worker type
Synchronous workers assume your app is CPU/network bound and avoids indefinite
operations. Any outbound HTTP calls or other blocking behaviour benefit from an
async worker. Because synchronous workers are vulnerable to slow clients,
Gunicorn requires a buffering proxy in front of the default configuration. Tools
like [Hey](https://github.com/rakyll/hey) can simulate slow responses to test
this scenario.
Examples that need async workers:
- Long blocking calls (outbound web services)
- Direct internet traffic (no buffering proxy)
- Streaming request/response bodies
- Long polling
- WebSockets / Comet
## How many workers?
Do **not** scale workers to match client count. Gunicorn usually needs only 412
workers to handle heavy traffic. Start with `(2 * num_cores) + 1` and adjust
under load using `TTIN`/`TTOU`.
Too many workers waste resources and can reduce throughput.
## How many threads?
Since Gunicorn 19 you can set `--threads` (with the `gthread` worker) to process
requests concurrently. Threads can extend request time beyond the worker
timeout while still notifying the master. The optimal mix of threads and worker
processes depends on the runtime (for example CPython vs. Jython). Threads share
memory, lowering footprint, and still allow reloads because application code is
loaded in worker processes.

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.

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

@ -0,0 +1,68 @@
# Gunicorn
<div class="hero">
<div class="hero__inner">
<div class="hero__copy">
<img class="hero__logo" src="assets/gunicorn.svg" alt="Gunicorn mascot" />
<h1>Production-ready Python web services</h1>
<p>Gunicorn is a dependable WSGI HTTP server for UNIX that keeps Python applications running fast and resilient in production. Built on a pre-fork worker model and trusted in countless deployments, it pairs clean configuration with flexible worker strategies so you can meet any traffic pattern.</p>
<div class="hero__cta">
<a class="md-button md-button--primary" href="install/">Install Gunicorn</a>
<a class="md-button" href="https://github.com/benoitc/gunicorn">View on GitHub</a>
</div>
</div>
<div class="hero__code">
<pre><code class="language-console">$ pip install gunicorn
$ gunicorn example:app --workers 3</code></pre>
<div class="hero__version">Latest release: <span>{{ release }}</span></div>
</div>
</div>
</div>
## Quickstart
1. Install Gunicorn into your application environment.
2. Point Gunicorn at your WSGI app: `gunicorn myproject.wsgi`.
3. Tune worker type, concurrency, and hooks using the rich [settings](reference/settings.md).
Need a longer walkthrough? Jump into the [install guide](install.md).
## Why teams choose Gunicorn
<section class="feature-grid">
<article class="feature-card">
<h3>Works with your framework</h3>
<p>Django, Flask, FastAPI, Pyramid, you name it&mdash;Gunicorn speaks WSGI so your stack just runs.</p>
<a href="run/">Running Gunicorn &rarr;</a>
</article>
<article class="feature-card">
<h3>Flexible workers</h3>
<p>Sync, async, gevent, eventlet&mdash;choose the concurrency model that fits.</p>
<a href="reference/settings/#worker_class">Worker classes &rarr;</a>
</article>
<article class="feature-card">
<h3>Battle-tested hooks</h3>
<p>Lifecycle hooks let you instrument, reload, and extend Gunicorn to match your deployment requirements.</p>
<a href="custom/">Server hooks &rarr;</a>
</article>
<article class="feature-card">
<h3>Containers to bare metal</h3>
<p>Deploy with systemd, Kubernetes, Heroku, or Docker&mdash;the configuration stays predictable everywhere.</p>
<a href="deploy/">Deployment patterns &rarr;</a>
</article>
</section>
## Documentation map
- [Install](install.md): Set up Gunicorn in a clean environment.
- [Run](run.md): CLI usage and integration with frameworks.
- [Configure](configure.md): Combine CLI flags and config files effectively.
- [Settings reference](reference/settings.md): Generated from the Gunicorn source of truth.
- [Signals](signals.md): Manage worker lifecycle in production.
- [Instrumentation](instrumentation.md): Monitor metrics and logs.
## Community & support
- Report bugs or request features on [GitHub Issues](https://github.com/benoitc/gunicorn/issues).
- Discuss strategies with maintainers in `#gunicorn` on [Libera Chat](https://libera.chat/).
- Contributions are welcome&mdash;see the [contributing guide](community.md#contributing) and say hi to the maintainers.

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

@ -0,0 +1,142 @@
# Installation
!!! note
Gunicorn requires **Python 3.12 or newer**.
```bash
pip install gunicorn
```
## From source
Install Gunicorn from GitHub if you want the latest development version:
```bash
pip install git+https://github.com/benoitc/gunicorn.git
```
Stay current by upgrading in place:
```bash
pip install -U git+https://github.com/benoitc/gunicorn.git
```
## Async workers
Install Eventlet or Gevent if your application benefits from cooperative I/O.
Both rely on `greenlet`, so make sure the Python headers are available (for
example, install the `python-dev` package on Ubuntu).
```bash
pip install greenlet # Required for both
pip install eventlet # For eventlet workers
pip install gunicorn[eventlet] # Or, using extra
pip install gevent # For gevent workers
pip install gunicorn[gevent] # Or, using extra
```
!!! note
Gevent also needs `libevent` 1.4.x or 2.0.4+. Install it from your package
manager or build it manually if the packaged version is too old.
## Extra packages
Some Gunicorn options require additional dependencies. Install them via
extras to pull everything in with one command.
Most extras enable alternative worker types—see the
[design docs](design.md) for when each worker makes sense.
- `gunicorn[eventlet]` &mdash; Eventlet-based greenlet workers
- `gunicorn[gevent]` &mdash; Gevent-based greenlet workers
- `gunicorn[gthread]` &mdash; Threaded workers
- `gunicorn[tornado]` &mdash; Tornado-based workers (not recommended)
If you run more than one Gunicorn instance, the
[`proc_name`](reference/settings.md#proc_name) setting helps distinguish them in tools such
as `ps` and `top`.
- `gunicorn[setproctitle]` &mdash; Enables setting the process name
You can combine multiple extras, for example:
```bash
pip install gunicorn[gevent,setproctitle]
```
## Debian GNU/Linux
On Debian systems prefer the distribution packages unless you need per-project
virtual environments:
- Zero-effort installation: automatically starts multiple instances based on
configs in `/etc/gunicorn.d`.
- Sensible log locations (`/var/log/gunicorn`) with `logrotate` support.
- Improved security: run each instance with a dedicated UNIX user/group.
- Safe upgrades: minimal downtime, reversible changes, and easy package purge.
### stable ("buster")
The Debian [stable](https://www.debian.org/releases/stable/) release ships
Gunicorn 19.9.0 (December 2020):
```bash
sudo apt-get install gunicorn3
```
Install Gunicorn 20.0.4 from [Debian Backports](https://backports.debian.org/)
by adding this line to `/etc/apt/sources.list`:
```text
deb http://ftp.debian.org/debian buster-backports main
```
Refresh package metadata and install:
```bash
sudo apt-get update
sudo apt-get -t buster-backports install gunicorn
```
### oldstable ("stretch")
Stretch provides Gunicorn 19.6.0 (December 2020). Install the Python 3 version:
```bash
sudo apt-get install gunicorn3
```
To upgrade to 19.7.1 from backports, add:
```text
deb http://ftp.debian.org/debian stretch-backports main
```
Then update and install:
```bash
sudo apt-get update
sudo apt-get -t stretch-backports install gunicorn3
```
### testing ("bullseye") and unstable ("sid")
Both distributions include Gunicorn 20.0.4. Install it in the usual way:
```bash
sudo apt-get install gunicorn
```
## Ubuntu
Ubuntu 20.04 LTS (Focal Fossa) and newer include Gunicorn 20.0.4. Keep it
current through the package manager:
```bash
sudo apt-get update
sudo apt-get install gunicorn
```

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)

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

@ -0,0 +1,75 @@
<span id="news"></span>
# Changelog
## 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
- [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)

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,192 @@
:root {
--gunicorn-green: #1d692d;
--gunicorn-green-dark: #14501f;
--gunicorn-green-light: #2a8729;
--gunicorn-cream: #f6f6f1;
--md-primary-fg-color: var(--gunicorn-green-light);
--md-primary-fg-color--light: #3da843;
--md-primary-fg-color--dark: var(--gunicorn-green-dark);
--md-accent-fg-color: var(--gunicorn-green);
}
[data-md-color-scheme="slate"] {
--gunicorn-cream: #1d1f1d;
--md-primary-fg-color: var(--gunicorn-green);
--md-primary-fg-color--light: #3da843;
--md-primary-fg-color--dark: var(--gunicorn-green-dark);
--md-accent-fg-color: var(--gunicorn-green-light);
}
.md-header__button.md-logo svg {
height: 1.8rem;
}
.md-typeset .hero {
margin: 2rem 0 3rem;
padding: 3.5rem;
background: linear-gradient(135deg, rgba(29, 105, 45, 0.96), rgba(42, 135, 41, 0.85));
color: #fff;
border-radius: 18px;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.12);
}
[data-md-color-scheme="slate"] .md-typeset .hero {
background: linear-gradient(135deg, rgba(20, 80, 31, 0.95), rgba(29, 105, 45, 0.88));
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
}
.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: 700;
line-height: 1.2;
}
.md-typeset .hero__cta {
margin-top: 1.75rem;
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.md-typeset .hero__code {
flex: 1 1 260px;
max-width: 420px;
background: rgba(255, 255, 255, 0.08);
border-radius: 14px;
padding: 1.5rem;
backdrop-filter: blur(4px);
font-size: 0.95rem;
}
[data-md-color-scheme="slate"] .md-typeset .hero__code {
background: rgba(0, 0, 0, 0.35);
}
.md-typeset .hero__code pre {
margin: 0 0 1rem;
border: none;
background: rgba(0, 0, 0, 0.35);
color: #e8f5ea;
}
[data-md-color-scheme="slate"] .md-typeset .hero__code pre {
background: rgba(0, 0, 0, 0.55);
}
.md-typeset .hero__version {
font-weight: 600;
letter-spacing: 0.01em;
}
.md-typeset .feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.6rem;
margin: 2.5rem 0 3rem;
}
.md-typeset .feature-card {
background: var(--gunicorn-cream);
border-radius: 14px;
padding: 1.5rem;
border: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
[data-md-color-scheme="slate"] .md-typeset .feature-card {
background: rgba(45, 48, 45, 0.9);
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.35);
}
.md-typeset .feature-card h3 {
margin-top: 0;
font-size: 1.3rem;
color: var(--gunicorn-green-dark);
}
[data-md-color-scheme="slate"] .md-typeset .feature-card h3 {
color: var(--gunicorn-cream);
}
.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:hover {
transform: translateY(-4px);
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.12);
}
.md-typeset .feature-card:hover a::after {
content: '\\2192';
opacity: 1;
transform: translateX(4px);
}
.md-typeset .feature-card a::after {
content: '\\2192';
opacity: 0;
transition: opacity 0.2s ease, transform 0.2s ease;
transform: translateX(0);
}
@media (max-width: 960px) {
.md-typeset .hero {
padding: 2.25rem;
}
.md-typeset .hero__copy h1 {
font-size: 2.2rem;
}
}
@media (max-width: 720px) {
.md-typeset .hero {
margin-top: 1.5rem;
padding: 1.75rem;
}
.md-typeset .hero__cta {
flex-direction: column;
align-items: stretch;
}
.md-typeset .hero__code {
width: 100%;
}
}
.md-footer-meta__inner {
flex-wrap: wrap;
}
.md-typeset .hero__logo {
height: 64px;
margin-bottom: 1.25rem;
}
[data-md-color-scheme="slate"] .md-typeset .hero__logo {
filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.35));
}

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.

94
mkdocs.yml Normal file
View File

@ -0,0 +1,94 @@
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
- Guides:
- Install: install.md
- Run: run.md
- Configure: configure.md
- Deploy: deploy.md
- Signals: signals.md
- Instrumentation: instrumentation.md
- Custom: custom.md
- Community: community.md
- FAQ: faq.md
- Design: design.md
- Reference:
- Settings: reference/settings.md
- News:
- Latest: 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
language: en
logo: assets/gunicorn.svg
favicon: assets/gunicorn.svg
features:
- content.code.copy
- navigation.instant
- navigation.tracking
- navigation.sections
- navigation.tabs
- navigation.top
- search.highlight
- search.suggest
- toc.follow
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
extra:
social:
- icon: fontawesome/brands/github
link: https://github.com/benoitc/gunicorn
- icon: fontawesome/brands/python
link: https://pypi.org/project/gunicorn/

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" },
]