Compare commits

..

35 Commits

Author SHA1 Message Date
kennethreitz dc71a0b725 deploy: b678542192 2026-04-12 22:12:14 +00:00
kennethreitz 969c0b408d deploy: 2f5e46e233 2026-04-12 21:53:01 +00:00
kennethreitz 6cd596b2b9 deploy: c1d789f279 2026-03-25 23:49:13 +00:00
kennethreitz 5cfbe13ddf deploy: 691f6b4d5c 2026-03-24 20:24:26 +00:00
kennethreitz 9d31549fe8 deploy: 90a082a0ac 2026-03-24 20:23:23 +00:00
kennethreitz 7ae8062fc5 deploy: 5ee0de6458 2026-03-24 20:03:19 +00:00
kennethreitz 03ee860ceb deploy: 1c729c8542 2026-03-24 20:01:17 +00:00
kennethreitz 9dd9792204 deploy: 536428a787 2026-03-24 19:54:34 +00:00
kennethreitz 60b4516894 deploy: 3d5f3c7e93 2026-03-24 19:52:20 +00:00
kennethreitz 8442a0af85 deploy: e0cce231ea 2026-03-24 19:42:35 +00:00
kennethreitz 42d5aea168 deploy: 44c33475b2 2026-03-24 19:36:03 +00:00
kennethreitz feb473e952 deploy: f4a292108b 2026-03-24 19:35:14 +00:00
kennethreitz 7dccbc7e19 deploy: 25ea333ad4 2026-03-24 19:33:33 +00:00
kennethreitz 6e5ce5b42a deploy: 6279835040 2026-03-24 19:33:09 +00:00
kennethreitz 1d3ec74e09 deploy: 0e493ad8d1 2026-03-24 19:32:03 +00:00
kennethreitz b3fe28f522 deploy: ce3ab46d59 2026-03-24 19:27:59 +00:00
kennethreitz bfd9f875b7 deploy: 6f9c87d71c 2026-03-24 19:25:36 +00:00
kennethreitz d00d5ec391 deploy: 29d0621d98 2026-03-24 19:23:53 +00:00
kennethreitz b68d349afe deploy: 30fa2dfda7 2026-03-24 19:22:30 +00:00
kennethreitz abdf9d7b86 deploy: 43c803a426 2026-03-24 19:21:42 +00:00
kennethreitz a8f9bc2bc1 deploy: ff6d530338 2026-03-24 19:21:23 +00:00
kennethreitz fb4cec76d8 deploy: a375984310 2026-03-24 19:16:48 +00:00
kennethreitz 0aee74f03a deploy: 46c6f440c5 2026-03-24 00:19:33 +00:00
kennethreitz 0b0b366804 deploy: c87e8c876d 2026-03-23 22:28:49 +00:00
kennethreitz 6ef0a4cb91 deploy: f86c7eed70 2026-03-23 02:11:12 +00:00
kennethreitz e70b638271 deploy: bf17b02653 2026-03-22 17:34:18 +00:00
kennethreitz f99530d723 deploy: 9383cd0f16 2026-03-22 17:10:10 +00:00
kennethreitz f28cd7001a deploy: 226bd63ed3 2026-03-22 17:08:16 +00:00
kennethreitz 5c5d0bbd74 deploy: b3c55f68d9 2026-03-22 17:00:48 +00:00
kennethreitz 4c00c90c49 deploy: a2a2ae21ff 2026-03-22 16:58:23 +00:00
kennethreitz 093819b249 deploy: 84074860aa 2026-03-22 16:55:58 +00:00
kennethreitz 1193bdcb6c deploy: 21baa03640 2026-03-22 16:51:37 +00:00
kennethreitz cc6f10f9bd deploy: 0c552e25cb 2026-03-22 16:48:27 +00:00
kennethreitz 4ad2f00c1c Create CNAME 2023-11-26 13:19:52 -05:00
kennethreitz a491f9a3fe deploy: 12a9b8a471 2023-11-21 08:14:10 +00:00
158 changed files with 13469 additions and 57037 deletions
+4
View File
@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 5ce83980cbbb1ff8c0566b9128c91144
tags: 645f666f9bcd5a90fca523b33c5a78b7
-42
View File
@@ -1,42 +0,0 @@
Release a new version of responder to PyPI and GitHub.
Usage: /release <version> (e.g. /release 3.6.0)
If no version is provided, ask the user what version to release.
## Steps
1. **Verify clean state**: Run `git status` and ensure the working tree is clean. If not, stop and ask the user.
2. **Run tests**: Run `uv run pytest -x --no-header -q`. If any fail, stop and report.
3. **Bump version**: Update `responder/__version__.py` to the new version.
4. **Update changelog**:
- Run `git log --oneline $(git describe --tags --abbrev=0)..HEAD` to get commits since last release.
- Add a new section in `CHANGELOG.md` under `## [Unreleased]` with the date, categorized into Added/Changed/Fixed/Removed.
- Update the compare links at the bottom of the file.
5. **Lock deps**: Run `uv lock`.
6. **Commit**: Stage `responder/__version__.py`, `CHANGELOG.md`, and `uv.lock`. Commit with message `Bump version to X.Y.Z and update changelog`.
7. **Push and tag**:
```
git push
git tag vX.Y.Z
git push origin vX.Y.Z
```
8. **GitHub release**: Create a release with `gh release create` including highlights and a link to the full changelog.
9. **Build and publish**:
```
uv build
uvx twine upload dist/responder-X.Y.Z*
```
Note: This requires a PyPI token. If twine fails due to auth, tell the user to set `TWINE_USERNAME=__token__` and `TWINE_PASSWORD` and re-run, or run `! uvx twine upload dist/responder-X.Y.Z*` interactively.
10. **Update GitHub release**: Edit the release to add a link to the PyPI page: `https://pypi.org/project/responder/X.Y.Z/`
11. **Report**: Print a summary with links to the GitHub release and PyPI page.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-3
View File
@@ -1,3 +0,0 @@
github: kennethreitz
thanks_dev: kennethreitz
custom: https://cash.app/$KennethReitz
-16
View File
@@ -1,16 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
-45
View File
@@ -1,45 +0,0 @@
name: "Documentation"
on:
push:
branches: [ main ]
pull_request: ~
workflow_dispatch:
# Cancel redundant in-progress jobs.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
jobs:
documentation:
name: "Documentation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
activate-environment: true
enable-cache: true
python-version: "3.14"
- name: Install package and documentation dependencies
run: uv pip install '.[docs]'
- name: Build static HTML documentation
run: sphinx-build -W --keep-going docs/source docs/build
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/build
cname: responder.kennethreitz.org
-50
View File
@@ -1,50 +0,0 @@
name: "Tests"
on:
push:
branches: [ main ]
pull_request: ~
workflow_dispatch:
# Cancel redundant in-progress jobs.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: "Python ${{ matrix.python-version }}"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [
"3.10",
"3.11",
"3.12",
"3.13",
"3.14",
"3.14t",
"pypy3.11",
]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
activate-environment: true
enable-cache: true
cache-suffix: ${{ matrix.python-version }}
python-version: ${{ matrix.python-version }}
- name: Install package
run: uv pip install '.[develop,test]'
- name: Run tests
run: pytest
-20
View File
@@ -1,20 +0,0 @@
.venv*
.vscode/
.cache
.idea
.python-version
.coverage
.pytest_cache
.DS_Store
coverage.xml
.coverage*
*.lock
__pycache__
tests/__pycache__
build
responder.egg-info/
dist/
app.py
app2.py
-44
View File
@@ -1,44 +0,0 @@
# Responder
A familiar HTTP Service Framework for Python, by Kenneth Reitz.
## Commands
- **Tests**: `uv run pytest` (runs full suite with coverage)
- **Single test**: `uv run pytest tests/test_responder.py::test_name -xvs`
- **Lint**: `uv run ruff check .`
- **Type check**: `uv run mypy`
- **Build docs**: `cd docs && uv run make html`
- **Build package**: `uv build`
- **Lock deps**: `uv lock`
## Architecture
- `responder/api.py` — Main `API` class, the entry point for all apps
- `responder/routes.py``Router`, `Route`, `WebSocketRoute` dispatch
- `responder/models.py``Request` and `Response` wrappers around Starlette
- `responder/ext/` — Extensions: CLI, GraphQL, OpenAPI, rate limiting
- `responder/background.py` — Background task queue
- `responder/formats.py` — Content negotiation (JSON, YAML, msgpack)
- `responder/__version__.py` — Single source of truth for version string
## Conventions
- Python 3.10+ only. Use `from __future__ import annotations` where present.
- Use `inspect.iscoroutinefunction` (not `asyncio.iscoroutinefunction`).
- Tests use `api.requests` (Starlette TestClient) with `allowed_hosts=[";"]` or `["localhost"]`.
- Werkzeug 3.1.7+ rejects invalid Host headers — use `localhost` when mounting WSGI apps in tests.
- Version is in `responder/__version__.py`, bump it there.
- Changelog follows [Keep a Changelog](https://keepachangelog.com/) format in `CHANGELOG.md`.
- Compare links at the bottom of CHANGELOG.md must be updated when adding a release.
- All deps managed via `uv`. Lock file (`uv.lock`) is not committed.
## Release Process
1. Bump version in `responder/__version__.py`
2. Add changelog entry in `CHANGELOG.md` (update compare links too)
3. `uv lock` to refresh the lock file
4. Commit: `Bump version to X.Y.Z and update changelog`
5. `git tag vX.Y.Z && git push && git push origin vX.Y.Z`
6. `gh release create vX.Y.Z --title "vX.Y.Z" --notes "..."`
7. `uv build && uvx twine upload dist/responder-X.Y.Z*`
+1
View File
@@ -0,0 +1 @@
responder.kennethreitz.org
-178
View File
@@ -1,178 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
-109
View File
@@ -1,109 +0,0 @@
# Responder
A familiar HTTP Service Framework for Python, powered by [Starlette](https://www.starlette.io/).
```python
import responder
api = responder.API()
@api.route("/{greeting}")
async def greet_world(req, resp, *, greeting):
resp.text = f"{greeting}, world!"
if __name__ == "__main__":
api.run()
```
$ pip install responder
That's it. Supports Python 3.10+.
## The Basics
- `resp.text` sends back text. `resp.html` sends back HTML. `resp.content` sends back bytes.
- `resp.media` sends back JSON (or YAML, with content negotiation).
- `resp.file("path.pdf")` serves a file with automatic content-type detection.
- `req.headers` is case-insensitive. `req.params` gives you query parameters.
- Both sync and async views work — the `async` is optional.
## Highlights
```python
# Type-safe route parameters
@api.route("/users/{user_id:int}")
async def get_user(req, resp, *, user_id):
resp.media = {"id": user_id}
# HTTP method filtering
@api.route("/items", methods=["POST"])
async def create_item(req, resp):
data = await req.media()
resp.media = {"created": data}
# Class-based views
@api.route("/things/{id}")
class ThingResource:
def on_get(self, req, resp, *, id):
resp.media = {"id": id}
def on_post(self, req, resp, *, id):
resp.text = "created"
# Before-request hooks (auth, rate limiting, etc.)
@api.route(before_request=True)
def check_auth(req, resp):
if not req.headers.get("Authorization"):
resp.status_code = 401
resp.media = {"error": "unauthorized"}
# Custom error handling
@api.exception_handler(ValueError)
async def handle_error(req, resp, exc):
resp.status_code = 400
resp.media = {"error": str(exc)}
# Lifespan events
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app):
print("starting up")
yield
print("shutting down")
api = responder.API(lifespan=lifespan)
# GraphQL
import graphene
api.graphql("/graphql", schema=graphene.Schema(query=Query))
# WebSockets
@api.route("/ws", websocket=True)
async def websocket(ws):
await ws.accept()
while True:
name = await ws.receive_text()
await ws.send_text(f"Hello {name}!")
# Mount WSGI/ASGI apps
from flask import Flask
flask_app = Flask(__name__)
api.mount("/flask", flask_app)
# Background tasks
@api.route("/work")
def do_work(req, resp):
@api.background.task
def process():
import time; time.sleep(10)
process()
resp.media = {"status": "processing"}
```
Built-in OpenAPI docs, cookie-based sessions, gzip compression, static file serving, Jinja2 templates, and a production uvicorn server.
Route convertors: `str`, `int`, `float`, `uuid`, `path`.
## Documentation
https://responder.kennethreitz.org
+92
View File
@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Overview: module code &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="../_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="../_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="../_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="../_static/documentation_options.js?v=c0c9fa11"></script>
<script src="../_static/doctools.js?v=9bcbadda"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../_static/clipboard.min.js?v=a7894cd8"></script>
<script src="../_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="../_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="stylesheet" href="../_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>All modules for which code is available</h1>
<ul><li><a href="responder/api.html">responder.api</a></li>
<li><a href="responder/background.html">responder.background</a></li>
<li><a href="responder/ext/ratelimit.html">responder.ext.ratelimit</a></li>
<li><a href="responder/models.html">responder.models</a></li>
<li><a href="responder/status_codes.html">responder.status_codes</a></li>
</ul>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="../index.html">
<img class="logo" src="../_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
</div>
</body>
</html>
+777
View File
@@ -0,0 +1,777 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>responder.api &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="../../_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="../../_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="../../_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="../../_static/documentation_options.js?v=c0c9fa11"></script>
<script src="../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/clipboard.min.js?v=a7894cd8"></script>
<script src="../../_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="../../_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for responder.api</h1><div class="highlight"><pre>
<span></span><span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">inspect</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">pathlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;API&quot;</span><span class="p">]</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">uvicorn</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.cors</span><span class="w"> </span><span class="kn">import</span> <span class="n">CORSMiddleware</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.errors</span><span class="w"> </span><span class="kn">import</span> <span class="n">ServerErrorMiddleware</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.exceptions</span><span class="w"> </span><span class="kn">import</span> <span class="n">ExceptionMiddleware</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.gzip</span><span class="w"> </span><span class="kn">import</span> <span class="n">GZipMiddleware</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.httpsredirect</span><span class="w"> </span><span class="kn">import</span> <span class="n">HTTPSRedirectMiddleware</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.sessions</span><span class="w"> </span><span class="kn">import</span> <span class="n">SessionMiddleware</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.trustedhost</span><span class="w"> </span><span class="kn">import</span> <span class="n">TrustedHostMiddleware</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.</span><span class="w"> </span><span class="kn">import</span> <span class="n">status_codes</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.background</span><span class="w"> </span><span class="kn">import</span> <span class="n">BackgroundQueue</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.formats</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_formats</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.models</span><span class="w"> </span><span class="kn">import</span> <span class="n">Request</span><span class="p">,</span> <span class="n">Response</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.routes</span><span class="w"> </span><span class="kn">import</span> <span class="n">Router</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.staticfiles</span><span class="w"> </span><span class="kn">import</span> <span class="n">StaticFiles</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.statics</span><span class="w"> </span><span class="kn">import</span> <span class="n">DEFAULT_CORS_PARAMS</span><span class="p">,</span> <span class="n">DEFAULT_OPENAPI_THEME</span><span class="p">,</span> <span class="n">DEFAULT_SECRET_KEY</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.templates</span><span class="w"> </span><span class="kn">import</span> <span class="n">Templates</span>
<div class="viewcode-block" id="API">
<a class="viewcode-back" href="../../api.html#responder.API">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">API</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The primary web-service class.</span>
<span class="sd"> :param static_dir: The directory to use for static files. Will be created for you if it doesn&#39;t already exist.</span>
<span class="sd"> :param templates_dir: The directory to use for templates. Will be created for you if it doesn&#39;t already exist.</span>
<span class="sd"> :param auto_escape: If ``True``, HTML and XML templates will automatically be escaped.</span>
<span class="sd"> :param enable_hsts: If ``True``, send all responses to HTTPS URLs.</span>
<span class="sd"> :param gzip: If ``True`` (the default), compress responses with GZip.</span>
<span class="sd"> :param openapi_theme: OpenAPI documentation theme, must be one of ``elements``, ``rapidoc``, ``redoc``, ``swagger_ui``</span>
<span class="sd"> &quot;&quot;&quot;</span> <span class="c1"># noqa: E501</span>
<span class="n">status_codes</span> <span class="o">=</span> <span class="n">status_codes</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="o">*</span><span class="p">,</span>
<span class="n">debug</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">title</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">version</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">description</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">terms_of_service</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">contact</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">license</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="c1"># noqa: A002</span>
<span class="n">openapi</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">openapi_route</span><span class="o">=</span><span class="s2">&quot;/schema.yml&quot;</span><span class="p">,</span>
<span class="n">static_dir</span><span class="o">=</span><span class="s2">&quot;static&quot;</span><span class="p">,</span>
<span class="n">static_route</span><span class="o">=</span><span class="s2">&quot;/static&quot;</span><span class="p">,</span>
<span class="n">templates_dir</span><span class="o">=</span><span class="s2">&quot;templates&quot;</span><span class="p">,</span>
<span class="n">auto_escape</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">secret_key</span><span class="o">=</span><span class="n">DEFAULT_SECRET_KEY</span><span class="p">,</span>
<span class="n">enable_hsts</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">docs_route</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">cors</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">cors_params</span><span class="o">=</span><span class="n">DEFAULT_CORS_PARAMS</span><span class="p">,</span>
<span class="n">allowed_hosts</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">openapi_theme</span><span class="o">=</span><span class="n">DEFAULT_OPENAPI_THEME</span><span class="p">,</span>
<span class="n">lifespan</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">gzip</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">request_id</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">enable_logging</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Create a new Responder API instance.</span>
<span class="sd"> :param debug: If ``True``, enable debug mode with verbose error pages.</span>
<span class="sd"> :param title: The title of the API, used in OpenAPI documentation.</span>
<span class="sd"> :param version: The version string for the API (e.g. ``&quot;1.0&quot;``).</span>
<span class="sd"> :param description: A longer description of the API for OpenAPI docs.</span>
<span class="sd"> :param terms_of_service: URL to the API&#39;s terms of service.</span>
<span class="sd"> :param contact: Contact information dict for the API (``name``, ``url``, ``email``).</span>
<span class="sd"> :param license: License information dict (``name``, ``url``).</span>
<span class="sd"> :param openapi: The OpenAPI version string (e.g. ``&quot;3.0.2&quot;``). Enables OpenAPI schema generation.</span>
<span class="sd"> :param openapi_route: The URL path for the OpenAPI schema (default ``&quot;/schema.yml&quot;``).</span>
<span class="sd"> :param static_dir: Directory for static files. Set to ``None`` to disable. Created automatically if missing.</span>
<span class="sd"> :param static_route: URL prefix for serving static files (default ``&quot;/static&quot;``).</span>
<span class="sd"> :param templates_dir: Directory for Jinja2 templates (default ``&quot;templates&quot;``).</span>
<span class="sd"> :param auto_escape: If ``True``, auto-escape HTML/XML in templates.</span>
<span class="sd"> :param secret_key: Secret key for signing cookie-based sessions. **Always set this in production.**</span>
<span class="sd"> :param enable_hsts: If ``True``, redirect all HTTP requests to HTTPS.</span>
<span class="sd"> :param docs_route: URL path for interactive API docs (e.g. ``&quot;/docs&quot;``). Enables OpenAPI if not already set.</span>
<span class="sd"> :param cors: If ``True``, enable CORS middleware.</span>
<span class="sd"> :param cors_params: Dict of CORS configuration (``allow_origins``, ``allow_methods``, etc.).</span>
<span class="sd"> :param allowed_hosts: List of allowed hostnames (e.g. ``[&quot;example.com&quot;]``). Defaults to ``[&quot;*&quot;]``.</span>
<span class="sd"> :param openapi_theme: Documentation UI theme: ``&quot;swagger_ui&quot;``, ``&quot;redoc&quot;``, ``&quot;rapidoc&quot;``, or ``&quot;elements&quot;``.</span>
<span class="sd"> :param lifespan: An async context manager for startup/shutdown logic.</span>
<span class="sd"> :param gzip: If ``True`` (the default), compress responses with GZip.</span>
<span class="sd"> :param request_id: If ``True``, add ``X-Request-ID`` headers to all responses.</span>
<span class="sd"> :param enable_logging: If ``True``, enable structured logging with per-request context (request ID, method, path, client IP).</span>
<span class="sd"> &quot;&quot;&quot;</span> <span class="c1"># noqa: E501</span>
<span class="bp">self</span><span class="o">.</span><span class="n">background</span> <span class="o">=</span> <span class="n">BackgroundQueue</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">secret_key</span> <span class="o">=</span> <span class="n">secret_key</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span> <span class="o">=</span> <span class="n">Router</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
<span class="k">if</span> <span class="n">static_dir</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">if</span> <span class="n">static_route</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">static_route</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="n">static_dir</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">static_dir</span><span class="p">)</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">static_dir</span> <span class="o">=</span> <span class="n">static_dir</span>
<span class="bp">self</span><span class="o">.</span><span class="n">static_route</span> <span class="o">=</span> <span class="n">static_route</span>
<span class="bp">self</span><span class="o">.</span><span class="n">hsts_enabled</span> <span class="o">=</span> <span class="n">enable_hsts</span>
<span class="bp">self</span><span class="o">.</span><span class="n">cors</span> <span class="o">=</span> <span class="n">cors</span>
<span class="bp">self</span><span class="o">.</span><span class="n">cors_params</span> <span class="o">=</span> <span class="n">cors_params</span>
<span class="bp">self</span><span class="o">.</span><span class="n">debug</span> <span class="o">=</span> <span class="n">debug</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">allowed_hosts</span><span class="p">:</span>
<span class="n">allowed_hosts</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;*&quot;</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">allowed_hosts</span> <span class="o">=</span> <span class="n">allowed_hosts</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">static_dir</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">static_dir</span><span class="o">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mount</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">static_route</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">static_app</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">formats</span> <span class="o">=</span> <span class="n">get_formats</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_session</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">default_endpoint</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">ExceptionMiddleware</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="p">,</span> <span class="n">debug</span><span class="o">=</span><span class="n">debug</span><span class="p">)</span>
<span class="k">if</span> <span class="n">gzip</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">GZipMiddleware</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">hsts_enabled</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">HTTPSRedirectMiddleware</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">TrustedHostMiddleware</span><span class="p">,</span> <span class="n">allowed_hosts</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">allowed_hosts</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">cors</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">CORSMiddleware</span><span class="p">,</span> <span class="o">**</span><span class="bp">self</span><span class="o">.</span><span class="n">cors_params</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">ServerErrorMiddleware</span><span class="p">,</span> <span class="n">debug</span><span class="o">=</span><span class="n">debug</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">SessionMiddleware</span><span class="p">,</span> <span class="n">secret_key</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">secret_key</span><span class="p">)</span>
<span class="k">if</span> <span class="n">openapi</span> <span class="ow">or</span> <span class="n">docs_route</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.ext.openapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">OpenAPISchema</span>
<span class="k">except</span> <span class="ne">ImportError</span> <span class="k">as</span> <span class="n">ex</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ImportError</span><span class="p">(</span>
<span class="s2">&quot;The dependencies for the OpenAPI extension are not installed. &quot;</span>
<span class="s2">&quot;Install them using: pip install responder&quot;</span>
<span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">ex</span>
<span class="bp">self</span><span class="o">.</span><span class="n">openapi</span> <span class="o">=</span> <span class="n">OpenAPISchema</span><span class="p">(</span>
<span class="n">app</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span>
<span class="n">title</span><span class="o">=</span><span class="n">title</span><span class="p">,</span>
<span class="n">version</span><span class="o">=</span><span class="n">version</span><span class="p">,</span>
<span class="n">openapi</span><span class="o">=</span><span class="n">openapi</span><span class="p">,</span>
<span class="n">docs_route</span><span class="o">=</span><span class="n">docs_route</span><span class="p">,</span>
<span class="n">description</span><span class="o">=</span><span class="n">description</span><span class="p">,</span>
<span class="n">terms_of_service</span><span class="o">=</span><span class="n">terms_of_service</span><span class="p">,</span>
<span class="n">contact</span><span class="o">=</span><span class="n">contact</span><span class="p">,</span>
<span class="n">license</span><span class="o">=</span><span class="n">license</span><span class="p">,</span>
<span class="n">openapi_route</span><span class="o">=</span><span class="n">openapi_route</span><span class="p">,</span>
<span class="n">static_route</span><span class="o">=</span><span class="n">static_route</span><span class="p">,</span>
<span class="n">openapi_theme</span><span class="o">=</span><span class="n">openapi_theme</span><span class="p">,</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">templates</span> <span class="o">=</span> <span class="n">Templates</span><span class="p">(</span><span class="n">directory</span><span class="o">=</span><span class="n">templates_dir</span><span class="p">)</span>
<span class="k">if</span> <span class="n">request_id</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">enable_logging</span><span class="p">:</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">uuid</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">_uuid</span>
<span class="k">def</span><span class="w"> </span><span class="nf">_add_request_id</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">rid</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;X-Request-ID&quot;</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">_uuid</span><span class="o">.</span><span class="n">uuid4</span><span class="p">()))</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-Request-ID&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">rid</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">after_request</span><span class="p">(</span><span class="n">_add_request_id</span><span class="p">)</span>
<span class="k">if</span> <span class="n">enable_logging</span><span class="p">:</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">_logging</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.ext.logging</span><span class="w"> </span><span class="kn">import</span> <span class="n">LoggingMiddleware</span><span class="p">,</span> <span class="n">get_logger</span><span class="p">,</span> <span class="n">setup_logging</span>
<span class="n">log_level</span> <span class="o">=</span> <span class="n">_logging</span><span class="o">.</span><span class="n">DEBUG</span> <span class="k">if</span> <span class="n">debug</span> <span class="k">else</span> <span class="n">_logging</span><span class="o">.</span><span class="n">INFO</span>
<span class="n">setup_logging</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">log_level</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">LoggingMiddleware</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">log</span> <span class="o">=</span> <span class="n">get_logger</span><span class="p">(</span><span class="s2">&quot;responder.app&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">_logging</span>
<span class="bp">self</span><span class="o">.</span><span class="n">log</span> <span class="o">=</span> <span class="n">_logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;responder.app&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">requests</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A test client connected to the ASGI app. Lazily initialized.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">session</span><span class="p">()</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">static_app</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The Starlette ``StaticFiles`` application for serving static assets.&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;_static_app&quot;</span><span class="p">):</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">static_dir</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_static_app</span> <span class="o">=</span> <span class="n">StaticFiles</span><span class="p">(</span><span class="n">directory</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">static_dir</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_static_app</span>
<div class="viewcode-block" id="API.before_request">
<a class="viewcode-back" href="../../api.html#responder.API.before_request">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">before_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">websocket</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Register a function to run before every request.</span>
<span class="sd"> If the hook sets ``resp.status_code``, the route handler is skipped</span>
<span class="sd"> and the response is sent immediately (short-circuiting).</span>
<span class="sd"> :param websocket: If ``True``, register as a WebSocket before-request hook instead of HTTP.</span>
<span class="sd"> Usage::</span>
<span class="sd"> @api.before_request()</span>
<span class="sd"> def check_auth(req, resp):</span>
<span class="sd"> if &quot;Authorization&quot; not in req.headers:</span>
<span class="sd"> resp.status_code = 401</span>
<span class="sd"> resp.media = {&quot;error&quot;: &quot;unauthorized&quot;}</span>
<span class="sd"> &quot;&quot;&quot;</span> <span class="c1"># noqa: E501</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">before_request</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">websocket</span><span class="o">=</span><span class="n">websocket</span><span class="p">)</span>
<span class="k">return</span> <span class="n">f</span>
<span class="k">return</span> <span class="n">decorator</span></div>
<div class="viewcode-block" id="API.after_request">
<a class="viewcode-back" href="../../api.html#responder.API.after_request">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">after_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Register a function to run after every request.</span>
<span class="sd"> Usage::</span>
<span class="sd"> @api.after_request()</span>
<span class="sd"> def add_request_id(req, resp):</span>
<span class="sd"> resp.headers[&quot;X-Request-ID&quot;] = str(uuid.uuid4())</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">after_request</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">return</span> <span class="n">f</span>
<span class="k">return</span> <span class="n">decorator</span></div>
<div class="viewcode-block" id="API.add_middleware">
<a class="viewcode-back" href="../../api.html#responder.API.add_middleware">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">add_middleware</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">middleware_cls</span><span class="p">,</span> <span class="o">**</span><span class="n">middleware_config</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Add ASGI middleware to the application.</span>
<span class="sd"> Middleware wraps the entire application and can inspect or modify</span>
<span class="sd"> every request and response. Middleware is applied in reverse order —</span>
<span class="sd"> the last middleware added runs first.</span>
<span class="sd"> :param middleware_cls: A Starlette-compatible middleware class.</span>
<span class="sd"> :param middleware_config: Keyword arguments passed to the middleware constructor.</span>
<span class="sd"> Usage::</span>
<span class="sd"> from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware</span>
<span class="sd"> api.add_middleware(HTTPSRedirectMiddleware)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">middleware_cls</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="p">,</span> <span class="o">**</span><span class="n">middleware_config</span><span class="p">)</span></div>
<div class="viewcode-block" id="API.exception_handler">
<a class="viewcode-back" href="../../api.html#responder.API.exception_handler">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">exception_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exception_cls</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Register a handler for a specific exception type.</span>
<span class="sd"> Usage::</span>
<span class="sd"> @api.exception_handler(ValueError)</span>
<span class="sd"> async def handle_value_error(req, resp, exc):</span>
<span class="sd"> resp.status_code = 400</span>
<span class="sd"> resp.media = {&quot;error&quot;: str(exc)}</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">_handler</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">exc</span><span class="p">):</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.responses</span><span class="w"> </span><span class="kn">import</span> <span class="n">Response</span> <span class="k">as</span> <span class="n">StarletteResp</span>
<span class="n">req</span> <span class="o">=</span> <span class="n">Request</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">scope</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">receive</span><span class="p">,</span> <span class="n">formats</span><span class="o">=</span><span class="n">get_formats</span><span class="p">())</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">Response</span><span class="p">(</span><span class="n">req</span><span class="o">=</span><span class="n">req</span><span class="p">,</span> <span class="n">formats</span><span class="o">=</span><span class="n">get_formats</span><span class="p">())</span>
<span class="k">if</span> <span class="n">inspect</span><span class="o">.</span><span class="n">iscoroutinefunction</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="k">await</span> <span class="n">func</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="n">exc</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">func</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="n">exc</span><span class="p">)</span>
<span class="k">if</span> <span class="n">resp</span><span class="o">.</span><span class="n">status_code</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">resp</span><span class="o">.</span><span class="n">status_code</span> <span class="o">=</span> <span class="mi">500</span>
<span class="n">body</span><span class="p">,</span> <span class="n">headers</span> <span class="o">=</span> <span class="k">await</span> <span class="n">resp</span><span class="o">.</span><span class="n">body</span>
<span class="k">return</span> <span class="n">StarletteResp</span><span class="p">(</span>
<span class="n">content</span><span class="o">=</span><span class="n">body</span><span class="p">,</span> <span class="n">status_code</span><span class="o">=</span><span class="n">resp</span><span class="o">.</span><span class="n">status_code</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">headers</span>
<span class="p">)</span>
<span class="c1"># Register with the ExceptionMiddleware</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">_exception_handlers</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="p">,</span> <span class="s2">&quot;_exception_handlers&quot;</span><span class="p">,</span> <span class="p">{}</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">_exception_handlers</span><span class="p">[</span><span class="n">exception_cls</span><span class="p">]</span> <span class="o">=</span> <span class="n">_handler</span>
<span class="c1"># Also register on the ASGI app chain</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.middleware.exceptions</span><span class="w"> </span><span class="kn">import</span> <span class="n">ExceptionMiddleware</span> <span class="k">as</span> <span class="n">EM</span>
<span class="n">app</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span>
<span class="k">while</span> <span class="n">app</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">EM</span><span class="p">):</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_exception_handler</span><span class="p">(</span><span class="n">exception_cls</span><span class="p">,</span> <span class="n">_handler</span><span class="p">)</span>
<span class="k">break</span>
<span class="n">app</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="s2">&quot;app&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">return</span> <span class="n">func</span>
<span class="k">return</span> <span class="n">decorator</span></div>
<div class="viewcode-block" id="API.schema">
<a class="viewcode-back" href="../../api.html#responder.API.schema">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">schema</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Decorator for creating new routes around function and class definitions.</span>
<span class="sd"> Usage::</span>
<span class="sd"> from marshmallow import Schema, fields</span>
<span class="sd"> @api.schema(&quot;Pet&quot;)</span>
<span class="sd"> class PetSchema(Schema):</span>
<span class="sd"> name = fields.Str()</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">openapi</span><span class="o">.</span><span class="n">add_schema</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">f</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">)</span>
<span class="k">return</span> <span class="n">f</span>
<span class="k">return</span> <span class="n">decorator</span></div>
<div class="viewcode-block" id="API.path_matches_route">
<a class="viewcode-back" href="../../api.html#responder.API.path_matches_route">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">path_matches_route</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Given a path portion of a URL, tests that it matches against any registered route.</span>
<span class="sd"> :param path: The path portion of a URL, to test all known routes against.</span>
<span class="sd"> &quot;&quot;&quot;</span> <span class="c1"># noqa: E501 (Line too long)</span>
<span class="k">for</span> <span class="n">route</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">routes</span><span class="p">:</span>
<span class="n">match</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">route</span><span class="o">.</span><span class="n">matches</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="k">if</span> <span class="n">match</span><span class="p">:</span>
<span class="k">return</span> <span class="n">route</span>
<span class="k">return</span> <span class="kc">None</span></div>
<div class="viewcode-block" id="API.add_route">
<a class="viewcode-back" href="../../api.html#responder.API.add_route">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">add_route</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">route</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">endpoint</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="o">*</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">static</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">check_existing</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">websocket</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">before_request</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">methods</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Adds a route to the API.</span>
<span class="sd"> :param route: A string representation of the route.</span>
<span class="sd"> :param endpoint: The endpoint for the route -- can be a callable, or a class.</span>
<span class="sd"> :param default: If ``True``, all unknown requests will route to this view.</span>
<span class="sd"> :param static: If ``True``, and no endpoint was passed, render &quot;static/index.html&quot;.</span>
<span class="sd"> Also, it will become a default route.</span>
<span class="sd"> :param methods: Optional list of HTTP methods (e.g. ``[&quot;GET&quot;, &quot;POST&quot;]``).</span>
<span class="sd"> &quot;&quot;&quot;</span> <span class="c1"># noqa: E501</span>
<span class="k">if</span> <span class="n">static</span><span class="p">:</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">static_dir</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">endpoint</span><span class="p">:</span>
<span class="n">endpoint</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_static_response</span>
<span class="n">default</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span>
<span class="n">route</span><span class="p">,</span>
<span class="n">endpoint</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">,</span>
<span class="n">websocket</span><span class="o">=</span><span class="n">websocket</span><span class="p">,</span>
<span class="n">before_request</span><span class="o">=</span><span class="n">before_request</span><span class="p">,</span>
<span class="n">check_existing</span><span class="o">=</span><span class="n">check_existing</span><span class="p">,</span>
<span class="n">methods</span><span class="o">=</span><span class="n">methods</span><span class="p">,</span>
<span class="p">)</span></div>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">_static_response</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">static_dir</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="n">index</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">static_dir</span> <span class="o">/</span> <span class="s2">&quot;index.html&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span>
<span class="k">if</span> <span class="n">index</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
<span class="n">resp</span><span class="o">.</span><span class="n">html</span> <span class="o">=</span> <span class="n">index</span><span class="o">.</span><span class="n">read_text</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">resp</span><span class="o">.</span><span class="n">status_code</span> <span class="o">=</span> <span class="n">status_codes</span><span class="o">.</span><span class="n">HTTP_404</span> <span class="c1"># type: ignore[attr-defined]</span>
<span class="n">resp</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s2">&quot;Not found.&quot;</span>
<div class="viewcode-block" id="API.redirect">
<a class="viewcode-back" href="../../api.html#responder.API.redirect">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">redirect</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">resp</span><span class="p">,</span>
<span class="n">location</span><span class="p">,</span>
<span class="o">*</span><span class="p">,</span>
<span class="n">set_text</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">status_code</span><span class="o">=</span><span class="n">status_codes</span><span class="o">.</span><span class="n">HTTP_301</span><span class="p">,</span> <span class="c1"># type: ignore[attr-defined]</span>
<span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Redirects a given response to a given location.</span>
<span class="sd"> :param resp: The Response to mutate.</span>
<span class="sd"> :param location: The location of the redirect.</span>
<span class="sd"> :param set_text: If ``True``, sets the Redirect body content automatically.</span>
<span class="sd"> :param status_code: an `API.status_codes` attribute, or an integer,</span>
<span class="sd"> representing the HTTP status code of the redirect.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">resp</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">location</span><span class="p">,</span> <span class="n">set_text</span><span class="o">=</span><span class="n">set_text</span><span class="p">,</span> <span class="n">status_code</span><span class="o">=</span><span class="n">status_code</span><span class="p">)</span></div>
<div class="viewcode-block" id="API.on_event">
<a class="viewcode-back" href="../../api.html#responder.API.on_event">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">on_event</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event_type</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="o">**</span><span class="n">args</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Decorator for registering functions or coroutines to run at certain events</span>
<span class="sd"> Supported events: startup, shutdown</span>
<span class="sd"> Usage::</span>
<span class="sd"> @api.on_event(&#39;startup&#39;)</span>
<span class="sd"> async def open_database_connection_pool():</span>
<span class="sd"> ...</span>
<span class="sd"> @api.on_event(&#39;shutdown&#39;)</span>
<span class="sd"> async def close_database_connection_pool():</span>
<span class="sd"> ...</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_event_handler</span><span class="p">(</span><span class="n">event_type</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">**</span><span class="n">args</span><span class="p">)</span>
<span class="k">return</span> <span class="n">func</span>
<span class="k">return</span> <span class="n">decorator</span></div>
<div class="viewcode-block" id="API.add_event_handler">
<a class="viewcode-back" href="../../api.html#responder.API.add_event_handler">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">add_event_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event_type</span><span class="p">,</span> <span class="n">handler</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Adds an event handler to the API.</span>
<span class="sd"> :param event_type: A string in (&quot;startup&quot;, &quot;shutdown&quot;)</span>
<span class="sd"> :param handler: The function to run. Can be either a function or a coroutine.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">add_event_handler</span><span class="p">(</span><span class="n">event_type</span><span class="p">,</span> <span class="n">handler</span><span class="p">)</span></div>
<div class="viewcode-block" id="API.route">
<a class="viewcode-back" href="../../api.html#responder.API.route">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">route</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">route</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">request_model</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Decorator for creating new routes around function and class definitions.</span>
<span class="sd"> Usage::</span>
<span class="sd"> @api.route(&quot;/hello&quot;)</span>
<span class="sd"> def hello(req, resp):</span>
<span class="sd"> resp.text = &quot;hello, world!&quot;</span>
<span class="sd"> With Pydantic models for OpenAPI documentation::</span>
<span class="sd"> from pydantic import BaseModel</span>
<span class="sd"> class ItemIn(BaseModel):</span>
<span class="sd"> name: str</span>
<span class="sd"> price: float</span>
<span class="sd"> class ItemOut(BaseModel):</span>
<span class="sd"> id: int</span>
<span class="sd"> name: str</span>
<span class="sd"> price: float</span>
<span class="sd"> @api.route(&quot;/items&quot;, methods=[&quot;POST&quot;],</span>
<span class="sd"> request_model=ItemIn, response_model=ItemOut)</span>
<span class="sd"> async def create_item(req, resp):</span>
<span class="sd"> data = await req.media()</span>
<span class="sd"> resp.media = {&quot;id&quot;: 1, **data}</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="k">if</span> <span class="n">request_model</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">f</span><span class="o">.</span><span class="n">_request_model</span> <span class="o">=</span> <span class="n">request_model</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;openapi&quot;</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">openapi</span><span class="o">.</span><span class="n">add_schema</span><span class="p">(</span>
<span class="n">request_model</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">request_model</span><span class="p">,</span> <span class="n">check_existing</span><span class="o">=</span><span class="kc">False</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">response_model</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">f</span><span class="o">.</span><span class="n">_response_model</span> <span class="o">=</span> <span class="n">response_model</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;openapi&quot;</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">openapi</span><span class="o">.</span><span class="n">add_schema</span><span class="p">(</span>
<span class="n">response_model</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">response_model</span><span class="p">,</span> <span class="n">check_existing</span><span class="o">=</span><span class="kc">False</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">)</span>
<span class="k">return</span> <span class="n">f</span>
<span class="k">return</span> <span class="n">decorator</span></div>
<div class="viewcode-block" id="API.graphql">
<a class="viewcode-back" href="../../api.html#responder.API.graphql">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">graphql</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">route</span><span class="o">=</span><span class="s2">&quot;/graphql&quot;</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">schema</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Mount a GraphQL API at the given route.</span>
<span class="sd"> Usage::</span>
<span class="sd"> import graphene</span>
<span class="sd"> class Query(graphene.ObjectType):</span>
<span class="sd"> hello = graphene.String(name=graphene.String(default_value=&quot;stranger&quot;))</span>
<span class="sd"> def resolve_hello(self, info, name):</span>
<span class="sd"> return f&quot;Hello {name}&quot;</span>
<span class="sd"> api.graphql(&quot;/graphql&quot;, schema=graphene.Schema(query=Query))</span>
<span class="sd"> :param route: The URL path for the GraphQL endpoint.</span>
<span class="sd"> :param schema: A Graphene schema instance.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.ext.graphql</span><span class="w"> </span><span class="kn">import</span> <span class="n">GraphQLView</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">GraphQLView</span><span class="p">(</span><span class="n">api</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">schema</span><span class="p">))</span></div>
<div class="viewcode-block" id="API.mount">
<a class="viewcode-back" href="../../api.html#responder.API.mount">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">mount</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">route</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Mounts an WSGI / ASGI application at a given route.</span>
<span class="sd"> :param route: String representation of the route to be used</span>
<span class="sd"> (shouldn&#39;t be parameterized).</span>
<span class="sd"> :param app: The other WSGI / ASGI app.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">update</span><span class="p">({</span><span class="n">route</span><span class="p">:</span> <span class="n">app</span><span class="p">})</span></div>
<div class="viewcode-block" id="API.session">
<a class="viewcode-back" href="../../api.html#responder.API.session">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">session</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">base_url</span><span class="o">=</span><span class="s2">&quot;http://;&quot;</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Testing HTTP client. Returns a Starlette TestClient instance,</span>
<span class="sd"> able to send HTTP requests to the Responder application.</span>
<span class="sd"> :param base_url: The base URL for the test client.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_session</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.testclient</span><span class="w"> </span><span class="kn">import</span> <span class="n">TestClient</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_session</span> <span class="o">=</span> <span class="n">TestClient</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">base_url</span><span class="o">=</span><span class="n">base_url</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_session</span></div>
<div class="viewcode-block" id="API.url_for">
<a class="viewcode-back" href="../../api.html#responder.API.url_for">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">url_for</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">endpoint</span><span class="p">,</span> <span class="o">**</span><span class="n">params</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Given an endpoint, returns a rendered URL for its route.</span>
<span class="sd"> :param endpoint: The route endpoint you&#39;re searching for.</span>
<span class="sd"> :param params: Data to pass into the URL generator (for parameterized URLs).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">router</span><span class="o">.</span><span class="n">url_for</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="o">**</span><span class="n">params</span><span class="p">)</span></div>
<div class="viewcode-block" id="API.template">
<a class="viewcode-back" href="../../api.html#responder.API.template">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">template</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filename</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sa">r</span><span class="sd">&quot;&quot;&quot;Render a Jinja2 template file with the provided values.</span>
<span class="sd"> :param filename: The filename of the jinja2 template, in ``templates_dir``.</span>
<span class="sd"> :param \*args: Data to pass into the template.</span>
<span class="sd"> :param \*\*kwargs: Data to pass into the template.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">templates</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>
<div class="viewcode-block" id="API.template_string">
<a class="viewcode-back" href="../../api.html#responder.API.template_string">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">template_string</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sa">r</span><span class="sd">&quot;&quot;&quot;Render a Jinja2 template string with the provided values.</span>
<span class="sd"> :param source: The template to use, a Jinja2 template string.</span>
<span class="sd"> :param \*args: Data to pass into the template.</span>
<span class="sd"> :param \*\*kwargs: Data to pass into the template.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">templates</span><span class="o">.</span><span class="n">render_string</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>
<div class="viewcode-block" id="API.serve">
<a class="viewcode-back" href="../../api.html#responder.API.serve">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">serve</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">address</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">debug</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Run the application with uvicorn.</span>
<span class="sd"> If the ``PORT`` environment variable is set, requests will be served on that port</span>
<span class="sd"> automatically to all known hosts.</span>
<span class="sd"> :param address: The address to bind to.</span>
<span class="sd"> :param port: The port to bind to. If none is provided, one will be selected at random.</span>
<span class="sd"> :param debug: Whether to run application in debug mode.</span>
<span class="sd"> :param options: Additional keyword arguments to send to ``uvicorn.run()``.</span>
<span class="sd"> &quot;&quot;&quot;</span> <span class="c1"># noqa: E501</span>
<span class="k">if</span> <span class="s2">&quot;PORT&quot;</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">:</span>
<span class="k">if</span> <span class="n">address</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">address</span> <span class="o">=</span> <span class="s2">&quot;0.0.0.0&quot;</span> <span class="c1"># noqa: S104</span>
<span class="n">port</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&quot;PORT&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="n">address</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">address</span> <span class="o">=</span> <span class="s2">&quot;127.0.0.1&quot;</span>
<span class="k">if</span> <span class="n">port</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">port</span> <span class="o">=</span> <span class="mi">5042</span>
<span class="k">if</span> <span class="n">debug</span><span class="p">:</span>
<span class="n">options</span><span class="p">[</span><span class="s2">&quot;log_level&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;debug&quot;</span>
<span class="n">uvicorn</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">host</span><span class="o">=</span><span class="n">address</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="n">port</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">)</span></div>
<div class="viewcode-block" id="API.run">
<a class="viewcode-back" href="../../api.html#responder.API.run">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Run the application. Shorthand for :meth:`serve` that inherits the ``debug`` setting.</span>
<span class="sd"> :param kwargs: Keyword arguments passed through to :meth:`serve`.</span>
<span class="sd"> &quot;&quot;&quot;</span> <span class="c1"># noqa: E501</span>
<span class="k">if</span> <span class="s2">&quot;debug&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="p">:</span>
<span class="n">kwargs</span><span class="o">.</span><span class="n">update</span><span class="p">({</span><span class="s2">&quot;debug&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">debug</span><span class="p">})</span>
<span class="bp">self</span><span class="o">.</span><span class="n">serve</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>
<div class="viewcode-block" id="API.group">
<a class="viewcode-back" href="../../api.html#responder.API.group">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">group</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prefix</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Create a route group with a shared URL prefix.</span>
<span class="sd"> Usage::</span>
<span class="sd"> v1 = api.group(&quot;/v1&quot;)</span>
<span class="sd"> @v1.route(&quot;/users&quot;)</span>
<span class="sd"> def list_users(req, resp):</span>
<span class="sd"> resp.media = []</span>
<span class="sd"> @v1.route(&quot;/users/{id:int}&quot;)</span>
<span class="sd"> def get_user(req, resp, *, id):</span>
<span class="sd"> resp.media = {&quot;id&quot;: id}</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">RouteGroup</span><span class="p">(</span><span class="n">api</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span> <span class="n">prefix</span><span class="o">=</span><span class="n">prefix</span><span class="p">)</span></div>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">,</span> <span class="n">send</span><span class="p">):</span>
<span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="p">(</span><span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">,</span> <span class="n">send</span><span class="p">)</span></div>
<div class="viewcode-block" id="RouteGroup">
<a class="viewcode-back" href="../../api.html#responder.RouteGroup">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">RouteGroup</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A group of routes with a shared URL prefix.&quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">api</span><span class="p">,</span> <span class="n">prefix</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">api</span> <span class="o">=</span> <span class="n">api</span>
<span class="bp">self</span><span class="o">.</span><span class="n">prefix</span> <span class="o">=</span> <span class="n">prefix</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">&quot;/&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">route</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">route</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">):</span>
<span class="n">full_route</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">prefix</span><span class="si">}{</span><span class="n">route</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="n">full_route</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">before_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">api</span><span class="o">.</span><span class="n">before_request</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>
</pre></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="../../index.html">
<img class="logo" src="../../_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
</div>
</body>
</html>
+178
View File
@@ -0,0 +1,178 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>responder.background &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="../../_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="../../_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="../../_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="../../_static/documentation_options.js?v=c0c9fa11"></script>
<script src="../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/clipboard.min.js?v=a7894cd8"></script>
<script src="../../_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="../../_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for responder.background</h1><div class="highlight"><pre>
<span></span><span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">concurrent.futures</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">inspect</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">multiprocessing</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">traceback</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.concurrency</span><span class="w"> </span><span class="kn">import</span> <span class="n">run_in_threadpool</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;BackgroundQueue&quot;</span><span class="p">]</span>
<div class="viewcode-block" id="BackgroundQueue">
<a class="viewcode-back" href="../../api.html#responder.background.BackgroundQueue">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">BackgroundQueue</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A queue for running tasks in background threads.</span>
<span class="sd"> Uses a ``ThreadPoolExecutor`` sized to the number of CPUs. Access it</span>
<span class="sd"> via ``api.background``.</span>
<span class="sd"> Usage::</span>
<span class="sd"> # As a decorator — fire and forget</span>
<span class="sd"> @api.background.task</span>
<span class="sd"> def send_email(to, subject):</span>
<span class="sd"> ...</span>
<span class="sd"> send_email(&quot;user@example.com&quot;, &quot;Hello&quot;)</span>
<span class="sd"> # Direct submission</span>
<span class="sd"> future = api.background.run(send_email, &quot;user@example.com&quot;, &quot;Hello&quot;)</span>
<span class="sd"> # As a callable (supports async functions)</span>
<span class="sd"> await api.background(send_email, &quot;user@example.com&quot;, &quot;Hello&quot;)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Create a new background queue.</span>
<span class="sd"> :param n: Number of worker threads. Defaults to CPU count.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">n</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">n</span> <span class="o">=</span> <span class="n">multiprocessing</span><span class="o">.</span><span class="n">cpu_count</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">n</span> <span class="o">=</span> <span class="n">n</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pool</span> <span class="o">=</span> <span class="n">concurrent</span><span class="o">.</span><span class="n">futures</span><span class="o">.</span><span class="n">ThreadPoolExecutor</span><span class="p">(</span><span class="n">max_workers</span><span class="o">=</span><span class="n">n</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
<div class="viewcode-block" id="BackgroundQueue.run">
<a class="viewcode-back" href="../../api.html#responder.background.BackgroundQueue.run">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Submit a function to run in a background thread.</span>
<span class="sd"> :param f: The function to run.</span>
<span class="sd"> :returns: A ``concurrent.futures.Future`` for the result.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">f</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pool</span><span class="o">.</span><span class="n">submit</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">results</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">return</span> <span class="n">f</span></div>
<div class="viewcode-block" id="BackgroundQueue.task">
<a class="viewcode-back" href="../../api.html#responder.background.BackgroundQueue.task">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">task</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Decorator that wraps a function to run in the background thread pool.</span>
<span class="sd"> The decorated function returns a ``Future`` instead of blocking.</span>
<span class="sd"> Exceptions are printed to stderr via traceback.</span>
<span class="sd"> :param f: The function to wrap.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">on_future_done</span><span class="p">(</span><span class="n">fs</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">fs</span><span class="o">.</span><span class="n">result</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
<span class="n">traceback</span><span class="o">.</span><span class="n">print_exc</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">do_task</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="n">result</span><span class="o">.</span><span class="n">add_done_callback</span><span class="p">(</span><span class="n">on_future_done</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
<span class="k">return</span> <span class="n">do_task</span></div>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">if</span> <span class="n">inspect</span><span class="o">.</span><span class="n">iscoroutinefunction</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">))</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">run_in_threadpool</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>
</pre></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="../../index.html">
<img class="logo" src="../../_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
</div>
</body>
</html>
+168
View File
@@ -0,0 +1,168 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>responder.ext.ratelimit &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="../../../_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="../../../_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="../../../_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="../../../_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="../../../_static/documentation_options.js?v=c0c9fa11"></script>
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/clipboard.min.js?v=a7894cd8"></script>
<script src="../../../_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="../../../_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="stylesheet" href="../../../_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for responder.ext.ratelimit</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;Simple in-memory rate limiter for Responder.&quot;&quot;&quot;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">threading</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">time</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">collections</span><span class="w"> </span><span class="kn">import</span> <span class="n">defaultdict</span>
<div class="viewcode-block" id="RateLimiter">
<a class="viewcode-back" href="../../../api.html#responder.ext.ratelimit.RateLimiter">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">RateLimiter</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Token bucket rate limiter.</span>
<span class="sd"> Usage::</span>
<span class="sd"> from responder.ext.ratelimit import RateLimiter</span>
<span class="sd"> limiter = RateLimiter(requests=100, period=60) # 100 req/min</span>
<span class="sd"> @api.route(before_request=True)</span>
<span class="sd"> def rate_limit(req, resp):</span>
<span class="sd"> limiter.check(req, resp)</span>
<span class="sd"> Or use the shorthand::</span>
<span class="sd"> limiter = RateLimiter(requests=100, period=60)</span>
<span class="sd"> limiter.install(api)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">requests</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="n">period</span><span class="o">=</span><span class="mi">60</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">max_requests</span> <span class="o">=</span> <span class="n">requests</span>
<span class="bp">self</span><span class="o">.</span><span class="n">period</span> <span class="o">=</span> <span class="n">period</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_buckets</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">float</span><span class="p">]]</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_lock</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">_client_key</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">req</span><span class="p">):</span>
<span class="n">client</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">client</span>
<span class="k">if</span> <span class="n">client</span><span class="p">:</span>
<span class="k">return</span> <span class="n">client</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">return</span> <span class="n">req</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;X-Forwarded-For&quot;</span><span class="p">,</span> <span class="s2">&quot;unknown&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">_cleanup</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
<span class="n">cutoff</span> <span class="o">=</span> <span class="n">now</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">period</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_buckets</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">t</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_buckets</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="k">if</span> <span class="n">t</span> <span class="o">&gt;</span> <span class="n">cutoff</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_buckets</span><span class="p">[</span><span class="n">key</span><span class="p">]:</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">_buckets</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<div class="viewcode-block" id="RateLimiter.check">
<a class="viewcode-back" href="../../../api.html#responder.ext.ratelimit.RateLimiter.check">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">check</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Check rate limit. Sets 429 status if exceeded.&quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_client_key</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
<span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">_lock</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_cleanup</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_buckets</span><span class="p">[</span><span class="n">key</span><span class="p">])</span> <span class="o">&gt;=</span> <span class="bp">self</span><span class="o">.</span><span class="n">max_requests</span><span class="p">:</span>
<span class="n">resp</span><span class="o">.</span><span class="n">status_code</span> <span class="o">=</span> <span class="mi">429</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;error&quot;</span><span class="p">:</span> <span class="s2">&quot;rate limit exceeded&quot;</span><span class="p">}</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Retry-After&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">period</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_buckets</span><span class="p">[</span><span class="n">key</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">())</span>
<span class="n">remaining</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">max_requests</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_buckets</span><span class="p">[</span><span class="n">key</span><span class="p">])</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-RateLimit-Limit&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">max_requests</span><span class="p">)</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-RateLimit-Remaining&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">remaining</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">True</span></div>
<div class="viewcode-block" id="RateLimiter.install">
<a class="viewcode-back" href="../../../api.html#responder.ext.ratelimit.RateLimiter.install">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">install</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">api</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Install as a before_request hook on the API.&quot;&quot;&quot;</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="n">before_request</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">_rate_limit</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">)</span></div>
</div>
</pre></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="../../../index.html">
<img class="logo" src="../../../_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
</div>
</body>
</html>
+755
View File
@@ -0,0 +1,755 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>responder.models &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="../../_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="../../_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="../../_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="../../_static/documentation_options.js?v=c0c9fa11"></script>
<script src="../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/clipboard.min.js?v=a7894cd8"></script>
<script src="../../_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="../../_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for responder.models</h1><div class="highlight"><pre>
<span></span><span class="kn">from</span><span class="w"> </span><span class="nn">__future__</span><span class="w"> </span><span class="kn">import</span> <span class="n">annotations</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">functools</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">inspect</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">collections.abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">Callable</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">http.cookies</span><span class="w"> </span><span class="kn">import</span> <span class="n">SimpleCookie</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">urllib.parse</span><span class="w"> </span><span class="kn">import</span> <span class="n">parse_qs</span><span class="p">,</span> <span class="n">urlparse</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;Request&quot;</span><span class="p">,</span> <span class="s2">&quot;Response&quot;</span><span class="p">,</span> <span class="s2">&quot;QueryDict&quot;</span><span class="p">]</span>
<span class="k">try</span><span class="p">:</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">chardet</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="n">chardet</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># type: ignore[assignment]</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.requests</span><span class="w"> </span><span class="kn">import</span> <span class="n">Request</span> <span class="k">as</span> <span class="n">StarletteRequest</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.requests</span><span class="w"> </span><span class="kn">import</span> <span class="n">State</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.responses</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
<span class="n">Response</span> <span class="k">as</span> <span class="n">StarletteResponse</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.responses</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
<span class="n">StreamingResponse</span> <span class="k">as</span> <span class="n">StarletteStreamingResponse</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.statics</span><span class="w"> </span><span class="kn">import</span> <span class="n">DEFAULT_ENCODING</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.status_codes</span><span class="w"> </span><span class="kn">import</span> <span class="n">HTTP_301</span> <span class="c1"># type: ignore[attr-defined]</span>
<span class="k">class</span><span class="w"> </span><span class="nc">CaseInsensitiveDict</span><span class="p">(</span><span class="nb">dict</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A case-insensitive dict for HTTP headers.&quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__setitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__setitem__</span><span class="p">(</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">(),</span> <span class="n">value</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__getitem__</span><span class="p">(</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__delitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__delitem__</span><span class="p">(</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__contains__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__contains__</span><span class="p">(</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
<span class="k">def</span><span class="w"> </span><span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">(),</span> <span class="n">default</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">pop</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">(),</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">setdefault</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">(),</span> <span class="n">default</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">other</span><span class="p">:</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">other</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="bp">self</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="bp">self</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<div class="viewcode-block" id="QueryDict">
<a class="viewcode-back" href="../../api.html#responder.QueryDict">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">QueryDict</span><span class="p">(</span><span class="nb">dict</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A dictionary for query string parameters that handles multi-value keys.</span>
<span class="sd"> Single-value access returns the last value for a key. Use :meth:`get_list`</span>
<span class="sd"> to retrieve all values for a multi-value parameter.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query_string</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">parse_qs</span><span class="p">(</span><span class="n">query_string</span><span class="p">))</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Return the last data value for this key, or [] if it&#39;s an empty list;</span>
<span class="sd"> raise KeyError if not found.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">list_</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__getitem__</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">list_</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<div class="viewcode-block" id="QueryDict.get">
<a class="viewcode-back" href="../../api.html#responder.QueryDict.get">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Return the last data value for the passed key. If key doesn&#39;t exist</span>
<span class="sd"> or value is an empty list, return `default`.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">val</span> <span class="o">=</span> <span class="bp">self</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">return</span> <span class="n">default</span>
<span class="k">if</span> <span class="n">val</span> <span class="o">==</span> <span class="p">[]:</span>
<span class="k">return</span> <span class="n">default</span>
<span class="k">return</span> <span class="n">val</span></div>
<span class="k">def</span><span class="w"> </span><span class="nf">_get_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">force_list</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Return a list of values for the key.</span>
<span class="sd"> Used internally to manipulate values list. If force_list is True,</span>
<span class="sd"> return a new copy of values.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">values</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__getitem__</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">if</span> <span class="n">default</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="k">return</span> <span class="n">default</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">force_list</span><span class="p">:</span>
<span class="n">values</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="k">if</span> <span class="n">values</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">values</span>
<div class="viewcode-block" id="QueryDict.get_list">
<a class="viewcode-back" href="../../api.html#responder.QueryDict.get_list">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">get_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Return the list of values for the key. If key doesn&#39;t exist, return a</span>
<span class="sd"> default value.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_list</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="p">,</span> <span class="n">force_list</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span></div>
<div class="viewcode-block" id="QueryDict.items">
<a class="viewcode-back" href="../../api.html#responder.QueryDict.items">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">items</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Yield (key, value) pairs, where value is the last item in the list</span>
<span class="sd"> associated with the key.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">key</span><span class="p">,</span> <span class="bp">self</span><span class="p">[</span><span class="n">key</span><span class="p">]</span></div>
<div class="viewcode-block" id="QueryDict.items_list">
<a class="viewcode-back" href="../../api.html#responder.QueryDict.items_list">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">items_list</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Yield (key, value) pairs, where value is the the list.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">yield from</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">items</span><span class="p">()</span></div>
</div>
<div class="viewcode-block" id="Request">
<a class="viewcode-back" href="../../api.html#responder.Request">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">Request</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;An HTTP request, passed to each view as the first argument.</span>
<span class="sd"> Provides access to headers, cookies, query parameters, the request body,</span>
<span class="sd"> session data, and more. Most properties are synchronous; reading the body</span>
<span class="sd"> (via :attr:`content`, :attr:`text`, or :meth:`media`) requires ``await``.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;_starlette&quot;</span><span class="p">,</span>
<span class="s2">&quot;formats&quot;</span><span class="p">,</span>
<span class="s2">&quot;_headers&quot;</span><span class="p">,</span>
<span class="s2">&quot;_encoding&quot;</span><span class="p">,</span>
<span class="s2">&quot;api&quot;</span><span class="p">,</span>
<span class="s2">&quot;_content&quot;</span><span class="p">,</span>
<span class="s2">&quot;_cookies&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">,</span> <span class="n">api</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">formats</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span> <span class="o">=</span> <span class="n">StarletteRequest</span><span class="p">(</span><span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">formats</span> <span class="o">=</span> <span class="n">formats</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_encoding</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">api</span> <span class="o">=</span> <span class="n">api</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_content</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">headers</span><span class="p">:</span> <span class="n">CaseInsensitiveDict</span> <span class="o">=</span> <span class="n">CaseInsensitiveDict</span><span class="p">()</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="n">headers</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_headers</span> <span class="o">=</span> <span class="n">headers</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_cookies</span> <span class="o">=</span> <span class="kc">None</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">session</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The session data, in dict form, from the Request.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span><span class="o">.</span><span class="n">session</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">headers</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A case-insensitive dictionary, containing all headers sent in the Request.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_headers</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">mimetype</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The MIME type of the request body, from the ``Content-Type`` header.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;Content-Type&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">is_json</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Returns ``True`` if the request content type is JSON.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;json&quot;</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The incoming HTTP method used for the request, lower-cased.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span><span class="o">.</span><span class="n">method</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">full_url</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The full URL of the Request, query parameters and all.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span><span class="o">.</span><span class="n">url</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">url</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The parsed URL of the Request.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">urlparse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">full_url</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">cookies</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The cookies sent in the Request, as a dictionary.&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_cookies</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">cookies</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">cookie_header</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;Cookie&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">bc</span><span class="p">:</span> <span class="n">SimpleCookie</span> <span class="o">=</span> <span class="n">SimpleCookie</span><span class="p">(</span><span class="n">cookie_header</span><span class="p">)</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">morsel</span> <span class="ow">in</span> <span class="n">bc</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="n">cookies</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">morsel</span><span class="o">.</span><span class="n">value</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_cookies</span> <span class="o">=</span> <span class="n">cookies</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_cookies</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">params</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A dictionary of the parsed query parameters used for the Request.&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">QueryDict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">url</span><span class="o">.</span><span class="n">query</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">return</span> <span class="n">QueryDict</span><span class="p">({})</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">path_params</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The path parameters extracted from the URL route.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span><span class="o">.</span><span class="n">path_params</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">client</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The client&#39;s address as a (host, port) named tuple, or None.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span><span class="o">.</span><span class="n">client</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">state</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">State</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Use the state to store additional information.</span>
<span class="sd"> This can be a very helpful feature, if you want to hand over</span>
<span class="sd"> information from a middelware or a route decorator to the</span>
<span class="sd"> actual route handler.</span>
<span class="sd"> Usage: ``request.state.time_started = time.time()``</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span><span class="o">.</span><span class="n">state</span>
<span class="nd">@property</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">encoding</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The encoding of the Request&#39;s body. Can be set, manually. Must be awaited.&quot;&quot;&quot;</span>
<span class="c1"># Use the user-set encoding first.</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_encoding</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_encoding</span>
<span class="k">return</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">apparent_encoding</span>
<span class="nd">@encoding</span><span class="o">.</span><span class="n">setter</span>
<span class="k">def</span><span class="w"> </span><span class="nf">encoding</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_encoding</span> <span class="o">=</span> <span class="n">value</span>
<span class="nd">@property</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">content</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The Request body, as bytes. Must be awaited.&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_content</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_content</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_starlette</span><span class="o">.</span><span class="n">body</span><span class="p">()</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_content</span>
<span class="nd">@property</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">text</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The Request body, as unicode. Must be awaited.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="p">(</span><span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">content</span><span class="p">)</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">encoding</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">declared_encoding</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">if</span> <span class="s2">&quot;Encoding&quot;</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Encoding&quot;</span><span class="p">]</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="nd">@property</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">apparent_encoding</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The apparent encoding, detected automatically. Must be awaited.</span>
<span class="sd"> Uses chardet for detection if installed, otherwise falls back to UTF-8.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">declared_encoding</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">declared_encoding</span>
<span class="k">if</span> <span class="n">declared_encoding</span><span class="p">:</span>
<span class="k">return</span> <span class="n">declared_encoding</span>
<span class="k">if</span> <span class="n">chardet</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">chardet</span><span class="o">.</span><span class="n">detect</span><span class="p">(</span><span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">content</span><span class="p">)[</span><span class="s2">&quot;encoding&quot;</span><span class="p">]</span> <span class="ow">or</span> <span class="n">DEFAULT_ENCODING</span>
<span class="k">return</span> <span class="n">DEFAULT_ENCODING</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">is_secure</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;``True`` if the request was made over HTTPS.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">url</span><span class="o">.</span><span class="n">scheme</span> <span class="o">==</span> <span class="s2">&quot;https&quot;</span>
<div class="viewcode-block" id="Request.accepts">
<a class="viewcode-back" href="../../api.html#responder.Request.accepts">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">accepts</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content_type</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Returns ``True`` if the incoming Request accepts the given ``content_type``.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">content_type</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;Accept&quot;</span><span class="p">,</span> <span class="p">[])</span></div>
<div class="viewcode-block" id="Request.media">
<a class="viewcode-back" href="../../api.html#responder.Request.media">[docs]</a>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">media</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">format</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="n">Callable</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span> <span class="c1"># noqa: A002</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Renders incoming json/yaml/form data as Python objects. Must be awaited.</span>
<span class="sd"> :param format: The name of the format being used.</span>
<span class="sd"> Alternatively, accepts a custom callable for the format type.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="nb">format</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="nb">format</span> <span class="o">=</span> <span class="s2">&quot;yaml&quot;</span> <span class="k">if</span> <span class="s2">&quot;yaml&quot;</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="k">else</span> <span class="s2">&quot;json&quot;</span> <span class="c1"># noqa: A001</span>
<span class="nb">format</span> <span class="o">=</span> <span class="s2">&quot;form&quot;</span> <span class="k">if</span> <span class="s2">&quot;form&quot;</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="k">else</span> <span class="nb">format</span> <span class="c1"># noqa: A001</span>
<span class="n">formatter</span><span class="p">:</span> <span class="n">Callable</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="nb">format</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">formats</span><span class="p">[</span><span class="nb">format</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span> <span class="k">as</span> <span class="n">ex</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unable to process data in &#39;</span><span class="si">{</span><span class="nb">format</span><span class="si">}</span><span class="s2">&#39; format&quot;</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">ex</span>
<span class="k">elif</span> <span class="nb">callable</span><span class="p">(</span><span class="nb">format</span><span class="p">):</span>
<span class="n">formatter</span> <span class="o">=</span> <span class="nb">format</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid &#39;format&#39; argument: </span><span class="si">{</span><span class="nb">format</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">formatter</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div>
</div>
<span class="k">def</span><span class="w"> </span><span class="nf">content_setter</span><span class="p">(</span><span class="n">mimetype</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="nf">getter</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
<span class="k">return</span> <span class="n">instance</span><span class="o">.</span><span class="n">content</span>
<span class="k">def</span><span class="w"> </span><span class="nf">setter</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">instance</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="n">value</span>
<span class="n">instance</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">=</span> <span class="n">mimetype</span>
<span class="k">return</span> <span class="nb">property</span><span class="p">(</span><span class="n">fget</span><span class="o">=</span><span class="n">getter</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="n">setter</span><span class="p">)</span>
<div class="viewcode-block" id="Response">
<a class="viewcode-back" href="../../api.html#responder.Response">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">Response</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;An HTTP response, passed to each view as the second argument.</span>
<span class="sd"> Mutate this object to control what gets sent back to the client. Set</span>
<span class="sd"> :attr:`text`, :attr:`html`, :attr:`media`, or :attr:`content` to define</span>
<span class="sd"> the body. Use :attr:`headers` and :meth:`set_cookie` to control metadata.</span>
<span class="sd"> :var text: Set the response body as plain text (sets ``Content-Type: text/plain``).</span>
<span class="sd"> :var html: Set the response body as HTML (sets ``Content-Type: text/html``).</span>
<span class="sd"> :var media: Set a Python object (dict, list) to be serialized as JSON (or negotiated format).</span>
<span class="sd"> :var content: Set the raw response body as bytes.</span>
<span class="sd"> :var status_code: The HTTP status code (e.g. ``200``, ``404``). Defaults to ``200`` if not set.</span>
<span class="sd"> :var headers: A dict of response headers.</span>
<span class="sd"> :var cookies: A ``SimpleCookie`` holding cookies to set on the response.</span>
<span class="sd"> :var session: A dict of session data. Changes are persisted in a signed cookie.</span>
<span class="sd"> &quot;&quot;&quot;</span> <span class="c1"># noqa: E501</span>
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;req&quot;</span><span class="p">,</span>
<span class="s2">&quot;status_code&quot;</span><span class="p">,</span>
<span class="s2">&quot;content&quot;</span><span class="p">,</span>
<span class="s2">&quot;encoding&quot;</span><span class="p">,</span>
<span class="s2">&quot;media&quot;</span><span class="p">,</span>
<span class="s2">&quot;headers&quot;</span><span class="p">,</span>
<span class="s2">&quot;formats&quot;</span><span class="p">,</span>
<span class="s2">&quot;cookies&quot;</span><span class="p">,</span>
<span class="s2">&quot;session&quot;</span><span class="p">,</span>
<span class="s2">&quot;mimetype&quot;</span><span class="p">,</span>
<span class="s2">&quot;_stream&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">content_setter</span><span class="p">(</span><span class="s2">&quot;text/plain&quot;</span><span class="p">)</span>
<span class="n">html</span> <span class="o">=</span> <span class="n">content_setter</span><span class="p">(</span><span class="s2">&quot;text/html&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">formats</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">req</span> <span class="o">=</span> <span class="n">req</span>
<span class="bp">self</span><span class="o">.</span><span class="n">status_code</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">encoding</span> <span class="o">=</span> <span class="n">DEFAULT_ENCODING</span>
<span class="bp">self</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_stream</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">headers</span> <span class="o">=</span> <span class="p">{}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">formats</span> <span class="o">=</span> <span class="n">formats</span>
<span class="bp">self</span><span class="o">.</span><span class="n">cookies</span><span class="p">:</span> <span class="n">SimpleCookie</span> <span class="o">=</span> <span class="n">SimpleCookie</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">session</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">session</span>
<div class="viewcode-block" id="Response.stream">
<a class="viewcode-back" href="../../api.html#responder.Response.stream">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">stream</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Set up a streaming response from an async generator function.</span>
<span class="sd"> The generator yields chunks of bytes that are sent to the client</span>
<span class="sd"> as they are produced, without buffering the full response in memory.</span>
<span class="sd"> Usage::</span>
<span class="sd"> @api.route(&quot;/stream&quot;)</span>
<span class="sd"> async def stream_data(req, resp):</span>
<span class="sd"> @resp.stream</span>
<span class="sd"> async def body():</span>
<span class="sd"> for i in range(10):</span>
<span class="sd"> yield f&quot;chunk {i}\\n&quot;.encode()</span>
<span class="sd"> :param func: An async generator function that yields response chunks.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">assert</span> <span class="n">inspect</span><span class="o">.</span><span class="n">isasyncgenfunction</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_stream</span> <span class="o">=</span> <span class="n">functools</span><span class="o">.</span><span class="n">partial</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="n">func</span></div>
<div class="viewcode-block" id="Response.sse">
<a class="viewcode-back" href="../../api.html#responder.Response.sse">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">sse</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Set up Server-Sent Events streaming.</span>
<span class="sd"> Usage::</span>
<span class="sd"> @api.route(&quot;/events&quot;)</span>
<span class="sd"> async def events(req, resp):</span>
<span class="sd"> @resp.sse</span>
<span class="sd"> async def stream():</span>
<span class="sd"> for i in range(10):</span>
<span class="sd"> yield {&quot;data&quot;: f&quot;message {i}&quot;}</span>
<span class="sd"> Each yielded dict can have: data, event, id, retry.</span>
<span class="sd"> Yielding a string is treated as data.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">assert</span> <span class="n">inspect</span><span class="o">.</span><span class="n">isasyncgenfunction</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">sse_generator</span><span class="p">():</span>
<span class="k">async</span> <span class="k">for</span> <span class="n">event</span> <span class="ow">in</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="k">yield</span> <span class="sa">f</span><span class="s2">&quot;data: </span><span class="si">{</span><span class="n">event</span><span class="si">}</span><span class="se">\n\n</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">encode</span><span class="p">()</span>
<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="nb">dict</span><span class="p">):</span>
<span class="n">parts</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="s2">&quot;event&quot;</span> <span class="ow">in</span> <span class="n">event</span><span class="p">:</span>
<span class="n">parts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;event: </span><span class="si">{</span><span class="n">event</span><span class="p">[</span><span class="s1">&#39;event&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="s2">&quot;id&quot;</span> <span class="ow">in</span> <span class="n">event</span><span class="p">:</span>
<span class="n">parts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;id: </span><span class="si">{</span><span class="n">event</span><span class="p">[</span><span class="s1">&#39;id&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="s2">&quot;retry&quot;</span> <span class="ow">in</span> <span class="n">event</span><span class="p">:</span>
<span class="n">parts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;retry: </span><span class="si">{</span><span class="n">event</span><span class="p">[</span><span class="s1">&#39;retry&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">event</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">str</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">):</span>
<span class="n">parts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;data: </span><span class="si">{</span><span class="n">line</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">parts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">parts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">yield</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">parts</span><span class="p">)</span><span class="o">.</span><span class="n">encode</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">yield</span> <span class="sa">f</span><span class="s2">&quot;data: </span><span class="si">{</span><span class="n">event</span><span class="si">}</span><span class="se">\n\n</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">encode</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_stream</span> <span class="o">=</span> <span class="n">sse_generator</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">=</span> <span class="s2">&quot;text/event-stream&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Cache-Control&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;no-cache&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Connection&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;keep-alive&quot;</span>
<span class="k">return</span> <span class="n">func</span></div>
<div class="viewcode-block" id="Response.stream_file">
<a class="viewcode-back" href="../../api.html#responder.Response.stream_file">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">stream_file</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">content_type</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">chunk_size</span><span class="o">=</span><span class="mi">8192</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Stream a file without loading it entirely into memory.</span>
<span class="sd"> :param path: Path to the file.</span>
<span class="sd"> :param content_type: Optional MIME type override.</span>
<span class="sd"> :param chunk_size: Size of chunks to read (default 8192 bytes).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">pathlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span> <span class="k">as</span> <span class="n">PathType</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">PathType</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="k">if</span> <span class="n">content_type</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">=</span> <span class="n">content_type</span>
<span class="k">else</span><span class="p">:</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">mimetypes</span>
<span class="n">guessed</span> <span class="o">=</span> <span class="n">mimetypes</span><span class="o">.</span><span class="n">guess_type</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">path</span><span class="p">))[</span><span class="mi">0</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">=</span> <span class="n">guessed</span> <span class="ow">or</span> <span class="s2">&quot;application/octet-stream&quot;</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">file_generator</span><span class="p">():</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">anyio</span>
<span class="k">async</span> <span class="k">with</span> <span class="k">await</span> <span class="n">anyio</span><span class="o">.</span><span class="n">open_file</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s2">&quot;rb&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">chunk</span> <span class="o">=</span> <span class="k">await</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">chunk_size</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">chunk</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">yield</span> <span class="n">chunk</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_stream</span> <span class="o">=</span> <span class="n">file_generator</span></div>
<div class="viewcode-block" id="Response.file">
<a class="viewcode-back" href="../../api.html#responder.Response.file">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">file</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">content_type</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Serve a file from disk as the response.</span>
<span class="sd"> :param path: Path to the file to serve.</span>
<span class="sd"> :param content_type: Optional MIME type override.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">pathlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="n">path</span><span class="o">.</span><span class="n">read_bytes</span><span class="p">()</span>
<span class="k">if</span> <span class="n">content_type</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">=</span> <span class="n">content_type</span>
<span class="k">else</span><span class="p">:</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">mimetypes</span>
<span class="n">guessed</span> <span class="o">=</span> <span class="n">mimetypes</span><span class="o">.</span><span class="n">guess_type</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">path</span><span class="p">))[</span><span class="mi">0</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">=</span> <span class="n">guessed</span> <span class="ow">or</span> <span class="s2">&quot;application/octet-stream&quot;</span></div>
<div class="viewcode-block" id="Response.redirect">
<a class="viewcode-back" href="../../api.html#responder.Response.redirect">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">redirect</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">location</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">set_text</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">status_code</span><span class="o">=</span><span class="n">HTTP_301</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Redirect the client to a different URL.</span>
<span class="sd"> :param location: The URL to redirect to.</span>
<span class="sd"> :param set_text: If ``True``, set a default redirect message as the body.</span>
<span class="sd"> :param status_code: The HTTP status code (default ``301``).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">status_code</span> <span class="o">=</span> <span class="n">status_code</span>
<span class="k">if</span> <span class="n">set_text</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;Redirecting to: </span><span class="si">{</span><span class="n">location</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">update</span><span class="p">({</span><span class="s2">&quot;Location&quot;</span><span class="p">:</span> <span class="n">location</span><span class="p">})</span></div>
<span class="nd">@property</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">body</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stream</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Content-Type&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span>
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_stream</span><span class="p">(),</span> <span class="n">headers</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">content</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">content</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">content</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Content-Type&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">==</span> <span class="s2">&quot;text/plain&quot;</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">encoding</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">headers</span><span class="p">[</span><span class="s2">&quot;Encoding&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">encoding</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">content</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">encoding</span><span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
<span class="k">for</span> <span class="n">format_</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">formats</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">req</span><span class="o">.</span><span class="n">accepts</span><span class="p">(</span><span class="n">format_</span><span class="p">):</span>
<span class="k">return</span> <span class="p">(</span><span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">formats</span><span class="p">[</span><span class="n">format_</span><span class="p">](</span><span class="bp">self</span><span class="p">,</span> <span class="n">encode</span><span class="o">=</span><span class="kc">True</span><span class="p">)),</span> <span class="p">{}</span>
<span class="c1"># Default to JSON anyway.</span>
<span class="k">return</span> <span class="p">(</span>
<span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">formats</span><span class="p">[</span><span class="s2">&quot;json&quot;</span><span class="p">](</span><span class="bp">self</span><span class="p">,</span> <span class="n">encode</span><span class="o">=</span><span class="kc">True</span><span class="p">),</span>
<span class="p">{</span><span class="s2">&quot;Content-Type&quot;</span><span class="p">:</span> <span class="s2">&quot;application/json&quot;</span><span class="p">},</span>
<span class="p">)</span>
<div class="viewcode-block" id="Response.set_cookie">
<a class="viewcode-back" href="../../api.html#responder.Response.set_cookie">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">set_cookie</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
<span class="n">key</span><span class="p">,</span>
<span class="n">value</span><span class="o">=</span><span class="s2">&quot;&quot;</span><span class="p">,</span>
<span class="n">expires</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">path</span><span class="o">=</span><span class="s2">&quot;/&quot;</span><span class="p">,</span>
<span class="n">domain</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">max_age</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">secure</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">httponly</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Set a cookie on the response with full control over directives.</span>
<span class="sd"> :param key: The cookie name.</span>
<span class="sd"> :param value: The cookie value.</span>
<span class="sd"> :param expires: Expiration date string (e.g. ``&quot;Thu, 01 Jan 2026 00:00:00 GMT&quot;``).</span>
<span class="sd"> :param path: URL path the cookie applies to (default ``&quot;/&quot;``).</span>
<span class="sd"> :param domain: Domain the cookie is valid for.</span>
<span class="sd"> :param max_age: Maximum age in seconds before the cookie expires.</span>
<span class="sd"> :param secure: If ``True``, cookie is only sent over HTTPS.</span>
<span class="sd"> :param httponly: If ``True`` (default), cookie is inaccessible to JavaScript.</span>
<span class="sd"> Usage::</span>
<span class="sd"> resp.set_cookie(</span>
<span class="sd"> &quot;token&quot;, value=&quot;abc123&quot;,</span>
<span class="sd"> max_age=3600, secure=True, httponly=True,</span>
<span class="sd"> )</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">cookies</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="n">morsel</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">cookies</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">if</span> <span class="n">expires</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">morsel</span><span class="p">[</span><span class="s2">&quot;expires&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">expires</span>
<span class="k">if</span> <span class="n">path</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">morsel</span><span class="p">[</span><span class="s2">&quot;path&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">path</span>
<span class="k">if</span> <span class="n">domain</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">morsel</span><span class="p">[</span><span class="s2">&quot;domain&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">domain</span>
<span class="k">if</span> <span class="n">max_age</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">morsel</span><span class="p">[</span><span class="s2">&quot;max-age&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">max_age</span>
<span class="n">morsel</span><span class="p">[</span><span class="s2">&quot;secure&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">secure</span>
<span class="n">morsel</span><span class="p">[</span><span class="s2">&quot;httponly&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">httponly</span></div>
<span class="k">def</span><span class="w"> </span><span class="nf">_prepare_cookies</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">starlette_response</span><span class="p">):</span>
<span class="n">cookie_header</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">(</span><span class="sa">b</span><span class="s2">&quot;set-cookie&quot;</span><span class="p">,</span> <span class="n">morsel</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">header</span><span class="o">=</span><span class="s2">&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;latin-1&quot;</span><span class="p">))</span>
<span class="k">for</span> <span class="n">morsel</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">cookies</span><span class="o">.</span><span class="n">values</span><span class="p">()</span>
<span class="p">)</span>
<span class="n">starlette_response</span><span class="o">.</span><span class="n">raw_headers</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">cookie_header</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">,</span> <span class="n">send</span><span class="p">):</span>
<span class="n">body</span><span class="p">,</span> <span class="n">headers</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="p">:</span>
<span class="n">headers</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="p">)</span>
<span class="n">response_cls</span><span class="p">:</span> <span class="nb">type</span><span class="p">[</span><span class="n">StarletteResponse</span><span class="p">]</span> <span class="o">|</span> <span class="nb">type</span><span class="p">[</span><span class="n">StarletteStreamingResponse</span><span class="p">]</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stream</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">response_cls</span> <span class="o">=</span> <span class="n">StarletteStreamingResponse</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">response_cls</span> <span class="o">=</span> <span class="n">StarletteResponse</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">response_cls</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="n">status_code</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">status_code_safe</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_prepare_cookies</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>
<span class="k">await</span> <span class="n">response</span><span class="p">(</span><span class="n">scope</span><span class="p">,</span> <span class="n">receive</span><span class="p">,</span> <span class="n">send</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">ok</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;``True`` if the status code is in the 2xx range (success).&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="mi">200</span> <span class="o">&lt;=</span> <span class="bp">self</span><span class="o">.</span><span class="n">status_code_safe</span> <span class="o">&lt;</span> <span class="mi">300</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">status_code_safe</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Return the status code, raising ``RuntimeError`` if it hasn&#39;t been set.&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">status_code</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;HTTP status code has not been defined&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">status_code</span></div>
</pre></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="../../index.html">
<img class="logo" src="../../_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
</div>
</body>
</html>
+210
View File
@@ -0,0 +1,210 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>responder.status_codes &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="../../_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="../../_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="../../_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="../../_static/documentation_options.js?v=c0c9fa11"></script>
<script src="../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/clipboard.min.js?v=a7894cd8"></script>
<script src="../../_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="../../_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for responder.status_codes</h1><div class="highlight"><pre>
<span></span><span class="n">codes</span> <span class="o">=</span> <span class="p">{</span>
<span class="c1"># Informational.</span>
<span class="mi">100</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;continue&quot;</span><span class="p">,),</span>
<span class="mi">101</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;switching_protocols&quot;</span><span class="p">,),</span>
<span class="mi">102</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;processing&quot;</span><span class="p">,),</span>
<span class="mi">103</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;checkpoint&quot;</span><span class="p">,),</span>
<span class="mi">122</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;uri_too_long&quot;</span><span class="p">,</span> <span class="s2">&quot;request_uri_too_long&quot;</span><span class="p">),</span>
<span class="mi">200</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;ok&quot;</span><span class="p">,</span> <span class="s2">&quot;okay&quot;</span><span class="p">,</span> <span class="s2">&quot;all_ok&quot;</span><span class="p">,</span> <span class="s2">&quot;all_okay&quot;</span><span class="p">,</span> <span class="s2">&quot;all_good&quot;</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\\</span><span class="s2">o/&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">),</span>
<span class="mi">201</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;created&quot;</span><span class="p">,),</span>
<span class="mi">202</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;accepted&quot;</span><span class="p">,),</span>
<span class="mi">203</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;non_authoritative_info&quot;</span><span class="p">,</span> <span class="s2">&quot;non_authoritative_information&quot;</span><span class="p">),</span>
<span class="mi">204</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;no_content&quot;</span><span class="p">,),</span>
<span class="mi">205</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;reset_content&quot;</span><span class="p">,</span> <span class="s2">&quot;reset&quot;</span><span class="p">),</span>
<span class="mi">206</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;partial_content&quot;</span><span class="p">,</span> <span class="s2">&quot;partial&quot;</span><span class="p">),</span>
<span class="mi">207</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;multi_status&quot;</span><span class="p">,</span> <span class="s2">&quot;multiple_status&quot;</span><span class="p">,</span> <span class="s2">&quot;multi_stati&quot;</span><span class="p">,</span> <span class="s2">&quot;multiple_stati&quot;</span><span class="p">),</span>
<span class="mi">208</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;already_reported&quot;</span><span class="p">,),</span>
<span class="mi">226</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;im_used&quot;</span><span class="p">,),</span>
<span class="c1"># Redirection.</span>
<span class="mi">300</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;multiple_choices&quot;</span><span class="p">,),</span>
<span class="mi">301</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;moved_permanently&quot;</span><span class="p">,</span> <span class="s2">&quot;moved&quot;</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\\</span><span class="s2">o-&quot;</span><span class="p">),</span>
<span class="mi">302</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;found&quot;</span><span class="p">,),</span>
<span class="mi">303</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;see_other&quot;</span><span class="p">,</span> <span class="s2">&quot;other&quot;</span><span class="p">),</span>
<span class="mi">304</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;not_modified&quot;</span><span class="p">,),</span>
<span class="mi">305</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;use_proxy&quot;</span><span class="p">,),</span>
<span class="mi">306</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;switch_proxy&quot;</span><span class="p">,),</span>
<span class="mi">307</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;temporary_redirect&quot;</span><span class="p">,</span> <span class="s2">&quot;temporary_moved&quot;</span><span class="p">,</span> <span class="s2">&quot;temporary&quot;</span><span class="p">),</span>
<span class="mi">308</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;permanent_redirect&quot;</span><span class="p">,),</span>
<span class="c1"># Client Error.</span>
<span class="mi">400</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;bad_request&quot;</span><span class="p">,</span> <span class="s2">&quot;bad&quot;</span><span class="p">),</span>
<span class="mi">401</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;unauthorized&quot;</span><span class="p">,),</span>
<span class="mi">402</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;payment_required&quot;</span><span class="p">,</span> <span class="s2">&quot;payment&quot;</span><span class="p">),</span>
<span class="mi">403</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;forbidden&quot;</span><span class="p">,),</span>
<span class="mi">404</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;not_found&quot;</span><span class="p">,</span> <span class="s2">&quot;-o-&quot;</span><span class="p">),</span>
<span class="mi">405</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;method_not_allowed&quot;</span><span class="p">,</span> <span class="s2">&quot;not_allowed&quot;</span><span class="p">),</span>
<span class="mi">406</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;not_acceptable&quot;</span><span class="p">,),</span>
<span class="mi">407</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;proxy_authentication_required&quot;</span><span class="p">,</span> <span class="s2">&quot;proxy_auth&quot;</span><span class="p">,</span> <span class="s2">&quot;proxy_authentication&quot;</span><span class="p">),</span>
<span class="mi">408</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;request_timeout&quot;</span><span class="p">,</span> <span class="s2">&quot;timeout&quot;</span><span class="p">),</span>
<span class="mi">409</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;conflict&quot;</span><span class="p">,),</span>
<span class="mi">410</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;gone&quot;</span><span class="p">,),</span>
<span class="mi">411</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;length_required&quot;</span><span class="p">,),</span>
<span class="mi">412</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;precondition_failed&quot;</span><span class="p">,</span> <span class="s2">&quot;precondition&quot;</span><span class="p">),</span>
<span class="mi">413</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;request_entity_too_large&quot;</span><span class="p">,),</span>
<span class="mi">414</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;request_uri_too_large&quot;</span><span class="p">,),</span>
<span class="mi">415</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;unsupported_media_type&quot;</span><span class="p">,</span> <span class="s2">&quot;unsupported_media&quot;</span><span class="p">,</span> <span class="s2">&quot;media_type&quot;</span><span class="p">),</span>
<span class="mi">416</span><span class="p">:</span> <span class="p">(</span>
<span class="s2">&quot;requested_range_not_satisfiable&quot;</span><span class="p">,</span>
<span class="s2">&quot;requested_range&quot;</span><span class="p">,</span>
<span class="s2">&quot;range_not_satisfiable&quot;</span><span class="p">,</span>
<span class="p">),</span>
<span class="mi">417</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;expectation_failed&quot;</span><span class="p">,),</span>
<span class="mi">418</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;im_a_teapot&quot;</span><span class="p">,</span> <span class="s2">&quot;teapot&quot;</span><span class="p">,</span> <span class="s2">&quot;i_am_a_teapot&quot;</span><span class="p">),</span>
<span class="mi">421</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;misdirected_request&quot;</span><span class="p">,),</span>
<span class="mi">422</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;unprocessable_entity&quot;</span><span class="p">,</span> <span class="s2">&quot;unprocessable&quot;</span><span class="p">),</span>
<span class="mi">423</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;locked&quot;</span><span class="p">,),</span>
<span class="mi">424</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;failed_dependency&quot;</span><span class="p">,</span> <span class="s2">&quot;dependency&quot;</span><span class="p">),</span>
<span class="mi">425</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;unordered_collection&quot;</span><span class="p">,</span> <span class="s2">&quot;unordered&quot;</span><span class="p">),</span>
<span class="mi">426</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;upgrade_required&quot;</span><span class="p">,</span> <span class="s2">&quot;upgrade&quot;</span><span class="p">),</span>
<span class="mi">428</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;precondition_required&quot;</span><span class="p">,</span> <span class="s2">&quot;precondition&quot;</span><span class="p">),</span>
<span class="mi">429</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;too_many_requests&quot;</span><span class="p">,</span> <span class="s2">&quot;too_many&quot;</span><span class="p">),</span>
<span class="mi">431</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;header_fields_too_large&quot;</span><span class="p">,</span> <span class="s2">&quot;fields_too_large&quot;</span><span class="p">),</span>
<span class="mi">444</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;no_response&quot;</span><span class="p">,</span> <span class="s2">&quot;none&quot;</span><span class="p">),</span>
<span class="mi">449</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;retry_with&quot;</span><span class="p">,</span> <span class="s2">&quot;retry&quot;</span><span class="p">),</span>
<span class="mi">450</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;blocked_by_windows_parental_controls&quot;</span><span class="p">,</span> <span class="s2">&quot;parental_controls&quot;</span><span class="p">),</span>
<span class="mi">451</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;unavailable_for_legal_reasons&quot;</span><span class="p">,</span> <span class="s2">&quot;legal_reasons&quot;</span><span class="p">),</span>
<span class="mi">499</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;client_closed_request&quot;</span><span class="p">,),</span>
<span class="c1"># Server Error.</span>
<span class="mi">500</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;internal_server_error&quot;</span><span class="p">,</span> <span class="s2">&quot;server_error&quot;</span><span class="p">,</span> <span class="s2">&quot;/o</span><span class="se">\\</span><span class="s2">&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">),</span>
<span class="mi">501</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;not_implemented&quot;</span><span class="p">,),</span>
<span class="mi">502</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;bad_gateway&quot;</span><span class="p">,),</span>
<span class="mi">503</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;service_unavailable&quot;</span><span class="p">,</span> <span class="s2">&quot;unavailable&quot;</span><span class="p">),</span>
<span class="mi">504</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;gateway_timeout&quot;</span><span class="p">,),</span>
<span class="mi">505</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;http_version_not_supported&quot;</span><span class="p">,</span> <span class="s2">&quot;http_version&quot;</span><span class="p">),</span>
<span class="mi">506</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;variant_also_negotiates&quot;</span><span class="p">,),</span>
<span class="mi">507</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;insufficient_storage&quot;</span><span class="p">,),</span>
<span class="mi">509</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;bandwidth_limit_exceeded&quot;</span><span class="p">,</span> <span class="s2">&quot;bandwidth&quot;</span><span class="p">),</span>
<span class="mi">510</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;not_extended&quot;</span><span class="p">,),</span>
<span class="mi">511</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;network_authentication_required&quot;</span><span class="p">,</span> <span class="s2">&quot;network_auth&quot;</span><span class="p">,</span> <span class="s2">&quot;network_authentication&quot;</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">number</span> <span class="ow">in</span> <span class="n">codes</span><span class="p">:</span>
<span class="nb">locals</span><span class="p">()[</span><span class="sa">f</span><span class="s2">&quot;HTTP_</span><span class="si">{</span><span class="n">number</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">number</span>
<span class="k">for</span> <span class="n">label</span> <span class="ow">in</span> <span class="n">codes</span><span class="p">[</span><span class="n">number</span><span class="p">]:</span>
<span class="nb">locals</span><span class="p">()[</span><span class="n">label</span><span class="p">]</span> <span class="o">=</span> <span class="n">number</span>
<span class="k">def</span><span class="w"> </span><span class="nf">_is_category</span><span class="p">(</span><span class="n">category</span><span class="p">,</span> <span class="n">status_code</span><span class="p">):</span>
<span class="k">return</span> <span class="n">category</span> <span class="o">&lt;=</span> <span class="n">status_code</span> <span class="o">&lt;</span> <span class="n">category</span> <span class="o">+</span> <span class="mi">100</span>
<div class="viewcode-block" id="is_100">
<a class="viewcode-back" href="../../api.html#responder.status_codes.is_100">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">is_100</span><span class="p">(</span><span class="n">status_code</span><span class="p">):</span>
<span class="k">return</span> <span class="n">_is_category</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="n">status_code</span><span class="p">)</span></div>
<div class="viewcode-block" id="is_200">
<a class="viewcode-back" href="../../api.html#responder.status_codes.is_200">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">is_200</span><span class="p">(</span><span class="n">status_code</span><span class="p">):</span>
<span class="k">return</span> <span class="n">_is_category</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="n">status_code</span><span class="p">)</span></div>
<div class="viewcode-block" id="is_300">
<a class="viewcode-back" href="../../api.html#responder.status_codes.is_300">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">is_300</span><span class="p">(</span><span class="n">status_code</span><span class="p">):</span>
<span class="k">return</span> <span class="n">_is_category</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="n">status_code</span><span class="p">)</span></div>
<div class="viewcode-block" id="is_400">
<a class="viewcode-back" href="../../api.html#responder.status_codes.is_400">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">is_400</span><span class="p">(</span><span class="n">status_code</span><span class="p">):</span>
<span class="k">return</span> <span class="n">_is_category</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="n">status_code</span><span class="p">)</span></div>
<div class="viewcode-block" id="is_500">
<a class="viewcode-back" href="../../api.html#responder.status_codes.is_500">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">is_500</span><span class="p">(</span><span class="n">status_code</span><span class="p">):</span>
<span class="k">return</span> <span class="n">_is_category</span><span class="p">(</span><span class="mi">500</span><span class="p">,</span> <span class="n">status_code</span><span class="p">)</span></div>
</pre></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="../../index.html">
<img class="logo" src="../../_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
</div>
</body>
</html>
+29
View File
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v3.6.2] - 2026-04-12
### Fixed
- GraphQL error responses now correctly return 400 status instead of always 200
- OpenAPI docs UI now respects custom `openapi_route` instead of hardcoding `/schema.yml`
- `before_requests` default type mismatch that could crash routes called outside the router
- Blocking synchronous file I/O in `Response.stream_file()` — now uses async I/O via anyio
- Memory leak in rate limiter (empty bucket keys never cleaned up)
- Race condition in rate limiter `check()` — added thread-safe locking
- WSGI fallback catching all `TypeError`s instead of just call-signature mismatches
- Pydantic request/response model validation crashing on non-dict bodies
- Test assertions that could never fail (`or True`, `< 500` patterns)
- `CaseInsensitiveDict` missing `__delitem__`, `pop`, and `setdefault` overrides
- `assert` used for input validation in OpenAPI extension (stripped by `python -O`)
- Potential XSS in GraphiQL template endpoint injection
- Dead `or ""` in media format detection logic
### Changed
- `DELETE` requests now participate in Pydantic request body validation
- Simplified status code category check to use chained comparison
### Removed
- Unused `method` parameter from `load_target()`
- Unused Node.js setup step from CI test workflow
## [v3.6.1] - 2026-04-12
### Added
@@ -497,6 +525,7 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Conception!
[v3.6.2]: https://github.com/kennethreitz/responder/compare/v3.6.1..v3.6.2
[v3.6.1]: https://github.com/kennethreitz/responder/compare/v3.6.0..v3.6.1
[v3.6.0]: https://github.com/kennethreitz/responder/compare/v3.5.0..v3.6.0
[v3.5.0]: https://github.com/kennethreitz/responder/compare/v3.4.0..v3.5.0
@@ -0,0 +1,26 @@
document.addEventListener("DOMContentLoaded", () => {
console.info("Initializing sphinx-design-elements");
setup_dropdown_group();
});
function setup_dropdown_group() {
// Select all relevant detail elements nested within container elements using the `dropdown-group` class.
const dropdown_details = document.querySelectorAll(".dropdown-group details");
// Add event listener for special toggling.
dropdown_details.forEach((details) => {
details.addEventListener("toggle", toggleOpenGroup);
});
// When toggling elements, exclusively open one element, and close all others.
function toggleOpenGroup(e) {
if (this.open) {
dropdown_details.forEach((details) => {
if (details !== this && details.open) {
details.open = false;
}
});
}
}
}
@@ -0,0 +1,90 @@
/**
* Appearance shortcuts.
*/
/* General */
/* Display paragraphs inline (side by side) */
.inline p {
display: inline;
}
/* No borders */
.no-margin {
margin: 0;
}
hr.docutils {
margin-top: 0.75rem;
margin-bottom: 0.75rem;
}
/* Very small text size */
.text-small {
font-size: small;
}
/* Smaller text size */
.text-smaller {
font-size: smaller;
}
/* Medium text size */
.text-medium {
font-size: medium;
}
/* Large text size */
.text-large {
font-size: large;
}
/* Very large text size */
.text-larger {
font-size: larger;
}
/* Grid table: Light-weight table-row outline styling. */
.top-border {
border-top-width: thin;
border-top-style: solid;
border-top-color: lightgray;
}
.bottom-border {
border-bottom-width: thin;
border-bottom-style: solid;
border-top-color: lightgray;
}
/**
* Appearance bugfixes.
*/
/* Grid table: Fix margins of child elements inside table items. */
.sd-col > .highlight-markdown {
margin: unset;
}
.sd-col > .highlight-restructuredtext {
margin: unset;
}
/* Shield: Inside a grid table, code blocks in neighbouring cells did not have any margins to each other. */
.col-compact * {
margin-bottom: 0.3em;
}
.sd-col .highlight {
margin-bottom: 0.3em !important;
}
/* Info card: Fix grid item formatting. */
.sd-col > div.line-block {
margin-top: unset;
margin-bottom: unset;
}
.bottom-margin-generous {
margin-bottom: 2em !important;
}
+663
View File
@@ -0,0 +1,663 @@
/* -- page layout ----------------------------------------------------------- */
body {
font-family: Georgia, serif;
font-size: 17px;
background-color: #fff;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: 940px;
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 220px;
}
div.sphinxsidebar {
width: 220px;
font-size: 14px;
line-height: 1.5;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #fff;
color: #3E4349;
padding: 0 30px 0 30px;
}
div.body > .section {
text-align: left;
}
div.footer {
width: 940px;
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
p.caption {
font-family: inherit;
font-size: inherit;
}
div.relations {
display: none;
}
div.sphinxsidebar {
max-height: 100%;
overflow-y: auto;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0;
margin: -10px 0 0 0px;
text-align: center;
}
div.sphinxsidebarwrapper h1.logo {
margin-top: -10px;
text-align: center;
margin-bottom: 5px;
text-align: left;
}
div.sphinxsidebarwrapper h1.logo-name {
margin-top: 0px;
}
div.sphinxsidebarwrapper p.blurb {
margin-top: 0;
font-style: normal;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Georgia, serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar ul li.toctree-l1 > a {
font-size: 120%;
}
div.sphinxsidebar ul li.toctree-l2 > a {
font-size: 110%;
}
div.sphinxsidebar input {
border: 1px solid #CCC;
font-family: Georgia, serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox {
margin: 1em 0;
}
div.sphinxsidebar .search > div {
display: table-cell;
}
div.sphinxsidebar hr {
border: none;
height: 1px;
color: #AAA;
background: #AAA;
text-align: left;
margin-left: 0;
width: 50%;
}
div.sphinxsidebar .badge {
border-bottom: none;
}
div.sphinxsidebar .badge:hover {
border-bottom: none;
}
/* To address an issue with donation coming after search */
div.sphinxsidebar h3.donation {
margin-top: 10px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: Georgia, serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #DDD;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #EAEAEA;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
margin: 20px 0px;
padding: 10px 30px;
background-color: #EEE;
border: 1px solid #CCC;
}
div.admonition tt.xref, div.admonition code.xref, div.admonition a tt {
background-color: #FBFBFB;
border-bottom: 1px solid #fafafa;
}
div.admonition p.admonition-title {
font-family: Georgia, serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.warning {
background-color: #FCC;
border: 1px solid #FAA;
}
div.danger {
background-color: #FCC;
border: 1px solid #FAA;
-moz-box-shadow: 2px 2px 4px #D52C2C;
-webkit-box-shadow: 2px 2px 4px #D52C2C;
box-shadow: 2px 2px 4px #D52C2C;
}
div.error {
background-color: #FCC;
border: 1px solid #FAA;
-moz-box-shadow: 2px 2px 4px #D52C2C;
-webkit-box-shadow: 2px 2px 4px #D52C2C;
box-shadow: 2px 2px 4px #D52C2C;
}
div.caution {
background-color: #FCC;
border: 1px solid #FAA;
}
div.attention {
background-color: #FCC;
border: 1px solid #FAA;
}
div.important {
background-color: #EEE;
border: 1px solid #CCC;
}
div.note {
background-color: #EEE;
border: 1px solid #CCC;
}
div.tip {
background-color: #EEE;
border: 1px solid #CCC;
}
div.hint {
background-color: #EEE;
border: 1px solid #CCC;
}
div.seealso {
background-color: #EEE;
border: 1px solid #CCC;
}
div.topic {
background-color: #EEE;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt, code {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
.hll {
background-color: #FFC;
margin: 0 -12px;
padding: 0 12px;
display: block;
}
img.screenshot {
}
tt.descname, tt.descclassname, code.descname, code.descclassname {
font-size: 0.95em;
}
tt.descname, code.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #EEE;
-webkit-box-shadow: 2px 2px 4px #EEE;
box-shadow: 2px 2px 4px #EEE;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #EEE;
-webkit-box-shadow: 2px 2px 4px #EEE;
box-shadow: 2px 2px 4px #EEE;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #EEE;
background: #FDFDFD;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.field-list p {
margin-bottom: 0.8em;
}
/* Cloned from
* https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68
*/
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
table.footnote td.label {
width: .1px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
/* Matches the 30px from the narrow-screen "li > ul" selector below */
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: unset;
padding: 7px 30px;
margin: 15px 0px;
line-height: 1.3em;
}
div.viewcode-block:target {
background: #ffd;
}
dl pre, blockquote pre, li pre {
margin-left: 0;
padding-left: 30px;
}
tt, code {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, code.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid #fff;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
/* Don't put an underline on images */
a.image-reference, a.image-reference:hover {
border-bottom: none;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt, a:hover code {
background: #EEE;
}
@media screen and (max-width: 940px) {
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: #fff;
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.sphinxsidebar {
display: block;
float: none;
width: unset;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: #FFF;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: #fff;
}
div.sphinxsidebar a {
color: #AAA;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
min-width: auto; /* fixes width on small screens, breaks .hll */
padding: 0;
}
.hll {
/* "fixes" the breakage */
width: max-content;
}
.rtd_doc_footer {
display: none;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.github {
display: none;
}
ul {
margin-left: 0;
}
li > ul {
/* Matches the 30px from the "ul, ol" selector above */
margin-left: 30px;
}
}
/* misc. */
.revsys-inline {
display: none!important;
}
/* Hide ugly table cell borders in ..bibliography:: directive output */
table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
border: none;
/* Below needed in some edge cases; if not applied, bottom shadows appear */
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
/* relbar */
.related {
line-height: 30px;
width: 100%;
font-size: 0.9rem;
}
.related.top {
border-bottom: 1px solid #EEE;
margin-bottom: 20px;
}
.related.bottom {
border-top: 1px solid #EEE;
}
.related ul {
padding: 0;
margin: 0;
list-style: none;
}
.related li {
display: inline;
}
nav#rellinks {
float: right;
}
nav#rellinks li+li:before {
content: "|";
}
nav#breadcrumbs li+li:before {
content: "\00BB";
}
/* Hide certain items when printing */
@media print {
div.related {
display: none;
}
}
img.github {
position: absolute;
top: 0;
border: 0;
right: 0;
}
+906
View File
@@ -0,0 +1,906 @@
/*
* Sphinx stylesheet -- basic theme.
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin-top: 10px;
}
ul.search li {
padding: 5px 0;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: inherit;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
a:visited {
color: #551A8B;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
.sig dd {
margin-top: 0px;
margin-bottom: 0px;
}
.sig dl {
margin-top: 0px;
margin-bottom: 0px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-check" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#22863a" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M5 12l5 5l10 -10" />
</svg>

After

Width:  |  Height:  |  Size: 313 B

+7
View File
File diff suppressed because one or more lines are too long
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="8" y="8" width="12" height="12" rx="2" />
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
</svg>

After

Width:  |  Height:  |  Size: 411 B

+94
View File
@@ -0,0 +1,94 @@
/* Copy buttons */
button.copybtn {
position: absolute;
display: flex;
top: .3em;
right: .3em;
width: 1.7em;
height: 1.7em;
opacity: 0;
transition: opacity 0.3s, border .3s, background-color .3s;
user-select: none;
padding: 0;
border: none;
outline: none;
border-radius: 0.4em;
/* The colors that GitHub uses */
border: #1b1f2426 1px solid;
background-color: #f6f8fa;
color: #57606a;
}
button.copybtn.success {
border-color: #22863a;
color: #22863a;
}
button.copybtn svg {
stroke: currentColor;
width: 1.5em;
height: 1.5em;
padding: 0.1em;
}
div.highlight {
position: relative;
}
/* Show the copybutton */
.highlight:hover button.copybtn, button.copybtn.success {
opacity: 1;
}
.highlight button.copybtn:hover {
background-color: rgb(235, 235, 235);
}
.highlight button.copybtn:active {
background-color: rgb(187, 187, 187);
}
/**
* A minimal CSS-only tooltip copied from:
* https://codepen.io/mildrenben/pen/rVBrpK
*
* To use, write HTML like the following:
*
* <p class="o-tooltip--left" data-tooltip="Hey">Short</p>
*/
.o-tooltip--left {
position: relative;
}
.o-tooltip--left:after {
opacity: 0;
visibility: hidden;
position: absolute;
content: attr(data-tooltip);
padding: .2em;
font-size: .8em;
left: -.2em;
background: grey;
color: white;
white-space: nowrap;
z-index: 2;
border-radius: 2px;
transform: translateX(-102%) translateY(0);
transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1);
}
.o-tooltip--left:hover:after {
display: block;
opacity: 1;
visibility: visible;
transform: translateX(-100%) translateY(0);
transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1);
transition-delay: .5s;
}
/* By default the copy button shouldn't show up when printing a page */
@media print {
button.copybtn {
display: none;
}
}
+248
View File
@@ -0,0 +1,248 @@
// Localization support
const messages = {
'en': {
'copy': 'Copy',
'copy_to_clipboard': 'Copy to clipboard',
'copy_success': 'Copied!',
'copy_failure': 'Failed to copy',
},
'es' : {
'copy': 'Copiar',
'copy_to_clipboard': 'Copiar al portapapeles',
'copy_success': '¡Copiado!',
'copy_failure': 'Error al copiar',
},
'de' : {
'copy': 'Kopieren',
'copy_to_clipboard': 'In die Zwischenablage kopieren',
'copy_success': 'Kopiert!',
'copy_failure': 'Fehler beim Kopieren',
},
'fr' : {
'copy': 'Copier',
'copy_to_clipboard': 'Copier dans le presse-papier',
'copy_success': 'Copié !',
'copy_failure': 'Échec de la copie',
},
'ru': {
'copy': 'Скопировать',
'copy_to_clipboard': 'Скопировать в буфер',
'copy_success': 'Скопировано!',
'copy_failure': 'Не удалось скопировать',
},
'zh-CN': {
'copy': '复制',
'copy_to_clipboard': '复制到剪贴板',
'copy_success': '复制成功!',
'copy_failure': '复制失败',
},
'it' : {
'copy': 'Copiare',
'copy_to_clipboard': 'Copiato negli appunti',
'copy_success': 'Copiato!',
'copy_failure': 'Errore durante la copia',
}
}
let locale = 'en'
if( document.documentElement.lang !== undefined
&& messages[document.documentElement.lang] !== undefined ) {
locale = document.documentElement.lang
}
let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT;
if (doc_url_root == '#') {
doc_url_root = '';
}
/**
* SVG files for our copy buttons
*/
let iconCheck = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-check" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#22863a" fill="none" stroke-linecap="round" stroke-linejoin="round">
<title>${messages[locale]['copy_success']}</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M5 12l5 5l10 -10" />
</svg>`
// If the user specified their own SVG use that, otherwise use the default
let iconCopy = ``;
if (!iconCopy) {
iconCopy = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke-linecap="round" stroke-linejoin="round">
<title>${messages[locale]['copy_to_clipboard']}</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="8" y="8" width="12" height="12" rx="2" />
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
</svg>`
}
/**
* Set up copy/paste for code blocks
*/
const runWhenDOMLoaded = cb => {
if (document.readyState != 'loading') {
cb()
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', cb)
} else {
document.attachEvent('onreadystatechange', function() {
if (document.readyState == 'complete') cb()
})
}
}
const codeCellId = index => `codecell${index}`
// Clears selected text since ClipboardJS will select the text when copying
const clearSelection = () => {
if (window.getSelection) {
window.getSelection().removeAllRanges()
} else if (document.selection) {
document.selection.empty()
}
}
// Changes tooltip text for a moment, then changes it back
// We want the timeout of our `success` class to be a bit shorter than the
// tooltip and icon change, so that we can hide the icon before changing back.
var timeoutIcon = 2000;
var timeoutSuccessClass = 1500;
const temporarilyChangeTooltip = (el, oldText, newText) => {
el.setAttribute('data-tooltip', newText)
el.classList.add('success')
// Remove success a little bit sooner than we change the tooltip
// So that we can use CSS to hide the copybutton first
setTimeout(() => el.classList.remove('success'), timeoutSuccessClass)
setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon)
}
// Changes the copy button icon for two seconds, then changes it back
const temporarilyChangeIcon = (el) => {
el.innerHTML = iconCheck;
setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon)
}
const addCopyButtonToCodeCells = () => {
// If ClipboardJS hasn't loaded, wait a bit and try again. This
// happens because we load ClipboardJS asynchronously.
if (window.ClipboardJS === undefined) {
setTimeout(addCopyButtonToCodeCells, 250)
return
}
// Add copybuttons to all of our code cells
const COPYBUTTON_SELECTOR = 'div.highlight pre';
const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR)
codeCells.forEach((codeCell, index) => {
const id = codeCellId(index)
codeCell.setAttribute('id', id)
const clipboardButton = id =>
`<button class="copybtn o-tooltip--left" data-tooltip="${messages[locale]['copy']}" data-clipboard-target="#${id}">
${iconCopy}
</button>`
codeCell.insertAdjacentHTML('afterend', clipboardButton(id))
})
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
/**
* Removes excluded text from a Node.
*
* @param {Node} target Node to filter.
* @param {string} exclude CSS selector of nodes to exclude.
* @returns {DOMString} Text from `target` with text removed.
*/
function filterText(target, exclude) {
const clone = target.cloneNode(true); // clone as to not modify the live DOM
if (exclude) {
// remove excluded nodes
clone.querySelectorAll(exclude).forEach(node => node.remove());
}
return clone.innerText;
}
// Callback when a copy button is clicked. Will be passed the node that was clicked
// should then grab the text and replace pieces of text that shouldn't be used in output
function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
var regexp;
var match;
// Do we check for line continuation characters and "HERE-documents"?
var useLineCont = !!lineContinuationChar
var useHereDoc = !!hereDocDelim
// create regexp to capture prompt and remaining line
if (isRegexp) {
regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)')
} else {
regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)')
}
const outputLines = [];
var promptFound = false;
var gotLineCont = false;
var gotHereDoc = false;
const lineGotPrompt = [];
for (const line of textContent.split('\n')) {
match = line.match(regexp)
if (match || gotLineCont || gotHereDoc) {
promptFound = regexp.test(line)
lineGotPrompt.push(promptFound)
if (removePrompts && promptFound) {
outputLines.push(match[2])
} else {
outputLines.push(line)
}
gotLineCont = line.endsWith(lineContinuationChar) & useLineCont
if (line.includes(hereDocDelim) & useHereDoc)
gotHereDoc = !gotHereDoc
} else if (!onlyCopyPromptLines) {
outputLines.push(line)
} else if (copyEmptyLines && line.trim() === '') {
outputLines.push(line)
}
}
// If no lines with the prompt were found then just use original lines
if (lineGotPrompt.some(v => v === true)) {
textContent = outputLines.join('\n');
}
// Remove a trailing newline to avoid auto-running when pasting
if (textContent.endsWith("\n")) {
textContent = textContent.slice(0, -1)
}
return textContent
}
var copyTargetText = (trigger) => {
var target = document.querySelector(trigger.attributes['data-clipboard-target'].value);
// get filtered text
let exclude = '.linenos';
let text = filterText(target, exclude);
return formatCopyText(text, '>>> |\\.\\.\\. |\\$ ', true, true, true, true, '', '')
}
// Initialize with a callback so we can modify the text before copy
const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText})
// Update UI with error/success messages
clipboard.on('success', event => {
clearSelection()
temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success'])
temporarilyChangeIcon(event.trigger)
})
clipboard.on('error', event => {
temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure'])
})
}
runWhenDOMLoaded(addCopyButtonToCodeCells)
+73
View File
@@ -0,0 +1,73 @@
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
/**
* Removes excluded text from a Node.
*
* @param {Node} target Node to filter.
* @param {string} exclude CSS selector of nodes to exclude.
* @returns {DOMString} Text from `target` with text removed.
*/
export function filterText(target, exclude) {
const clone = target.cloneNode(true); // clone as to not modify the live DOM
if (exclude) {
// remove excluded nodes
clone.querySelectorAll(exclude).forEach(node => node.remove());
}
return clone.innerText;
}
// Callback when a copy button is clicked. Will be passed the node that was clicked
// should then grab the text and replace pieces of text that shouldn't be used in output
export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
var regexp;
var match;
// Do we check for line continuation characters and "HERE-documents"?
var useLineCont = !!lineContinuationChar
var useHereDoc = !!hereDocDelim
// create regexp to capture prompt and remaining line
if (isRegexp) {
regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)')
} else {
regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)')
}
const outputLines = [];
var promptFound = false;
var gotLineCont = false;
var gotHereDoc = false;
const lineGotPrompt = [];
for (const line of textContent.split('\n')) {
match = line.match(regexp)
if (match || gotLineCont || gotHereDoc) {
promptFound = regexp.test(line)
lineGotPrompt.push(promptFound)
if (removePrompts && promptFound) {
outputLines.push(match[2])
} else {
outputLines.push(line)
}
gotLineCont = line.endsWith(lineContinuationChar) & useLineCont
if (line.includes(hereDocDelim) & useHereDoc)
gotHereDoc = !gotHereDoc
} else if (!onlyCopyPromptLines) {
outputLines.push(line)
} else if (copyEmptyLines && line.trim() === '') {
outputLines.push(line)
}
}
// If no lines with the prompt were found then just use original lines
if (lineGotPrompt.some(v => v === true)) {
textContent = outputLines.join('\n');
}
// Remove a trailing newline to avoid auto-running when pasting
if (textContent.endsWith("\n")) {
textContent = textContent.slice(0, -1)
}
return textContent
}
+1
View File
@@ -0,0 +1 @@
/* This file intentionally left blank. */
@@ -0,0 +1,26 @@
document.addEventListener("DOMContentLoaded", () => {
console.info("Initializing sphinx-design-elements");
setup_dropdown_group();
});
function setup_dropdown_group() {
// Select all relevant detail elements nested within container elements using the `dropdown-group` class.
const dropdown_details = document.querySelectorAll(".dropdown-group details");
// Add event listener for special toggling.
dropdown_details.forEach((details) => {
details.addEventListener("toggle", toggleOpenGroup);
});
// When toggling elements, exclusively open one element, and close all others.
function toggleOpenGroup(e) {
if (this.open) {
dropdown_details.forEach((details) => {
if (details !== this && details.open) {
details.open = false;
}
});
}
}
}
@@ -0,0 +1,90 @@
/**
* Appearance shortcuts.
*/
/* General */
/* Display paragraphs inline (side by side) */
.inline p {
display: inline;
}
/* No borders */
.no-margin {
margin: 0;
}
hr.docutils {
margin-top: 0.75rem;
margin-bottom: 0.75rem;
}
/* Very small text size */
.text-small {
font-size: small;
}
/* Smaller text size */
.text-smaller {
font-size: smaller;
}
/* Medium text size */
.text-medium {
font-size: medium;
}
/* Large text size */
.text-large {
font-size: large;
}
/* Very large text size */
.text-larger {
font-size: larger;
}
/* Grid table: Light-weight table-row outline styling. */
.top-border {
border-top-width: thin;
border-top-style: solid;
border-top-color: lightgray;
}
.bottom-border {
border-bottom-width: thin;
border-bottom-style: solid;
border-top-color: lightgray;
}
/**
* Appearance bugfixes.
*/
/* Grid table: Fix margins of child elements inside table items. */
.sd-col > .highlight-markdown {
margin: unset;
}
.sd-col > .highlight-restructuredtext {
margin: unset;
}
/* Shield: Inside a grid table, code blocks in neighbouring cells did not have any margins to each other. */
.col-compact * {
margin-bottom: 0.3em;
}
.sd-col .highlight {
margin-bottom: 0.3em !important;
}
/* Info card: Fix grid item formatting. */
.sd-col > div.line-block {
margin-top: unset;
margin-bottom: unset;
}
.bottom-margin-generous {
margin-bottom: 2em !important;
}
+149
View File
@@ -0,0 +1,149 @@
/*
* Base JavaScript utilities for all Sphinx HTML documentation.
*/
"use strict";
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
};
/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`
);
Documentation.LOCALE = catalog.locale;
},
/**
* helper function to focus on search bar
*/
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* Initialise the domain index toggle buttons
*/
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget))
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.altKey || event.ctrlKey || event.metaKey) return;
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
},
};
// quick alias for translations
const _ = Documentation.gettext;
_ready(Documentation.init);
+13
View File
@@ -0,0 +1,13 @@
const DOCUMENTATION_OPTIONS = {
VERSION: '3.6.2',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: true,
};
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 250 250" fill="#fff">
<path d="M0 0l115 115h15l12 27 108 108V0z" fill="#151513"/>
<path d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16"/>
<path d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"/>
</svg>

After

Width:  |  Height:  |  Size: 490 B

+192
View File
@@ -0,0 +1,192 @@
/*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*/
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
/* Non-minified version is copied as a separate JS file, if available */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

+84
View File
@@ -0,0 +1,84 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #8F5902; font-style: italic } /* Comment */
.highlight .err { color: #A40000; border: 1px solid #EF2929 } /* Error */
.highlight .g { color: #000 } /* Generic */
.highlight .k { color: #004461; font-weight: bold } /* Keyword */
.highlight .l { color: #000 } /* Literal */
.highlight .n { color: #000 } /* Name */
.highlight .o { color: #582800 } /* Operator */
.highlight .x { color: #000 } /* Other */
.highlight .p { color: #000; font-weight: bold } /* Punctuation */
.highlight .ch { color: #8F5902; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #8F5902; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #8F5902 } /* Comment.Preproc */
.highlight .cpf { color: #8F5902; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #8F5902; font-style: italic } /* Comment.Single */
.highlight .cs { color: #8F5902; font-style: italic } /* Comment.Special */
.highlight .gd { color: #A40000 } /* Generic.Deleted */
.highlight .ge { color: #000; font-style: italic } /* Generic.Emph */
.highlight .ges { color: #000 } /* Generic.EmphStrong */
.highlight .gr { color: #EF2929 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #888 } /* Generic.Output */
.highlight .gp { color: #745334 } /* Generic.Prompt */
.highlight .gs { color: #000; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #A40000; font-weight: bold } /* Generic.Traceback */
.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */
.highlight .ld { color: #000 } /* Literal.Date */
.highlight .m { color: #900 } /* Literal.Number */
.highlight .s { color: #4E9A06 } /* Literal.String */
.highlight .na { color: #C4A000 } /* Name.Attribute */
.highlight .nb { color: #004461 } /* Name.Builtin */
.highlight .nc { color: #000 } /* Name.Class */
.highlight .no { color: #000 } /* Name.Constant */
.highlight .nd { color: #888 } /* Name.Decorator */
.highlight .ni { color: #CE5C00 } /* Name.Entity */
.highlight .ne { color: #C00; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #000 } /* Name.Function */
.highlight .nl { color: #F57900 } /* Name.Label */
.highlight .nn { color: #000 } /* Name.Namespace */
.highlight .nx { color: #000 } /* Name.Other */
.highlight .py { color: #000 } /* Name.Property */
.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #000 } /* Name.Variable */
.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */
.highlight .pm { color: #000; font-weight: bold } /* Punctuation.Marker */
.highlight .w { color: #F8F8F8 } /* Text.Whitespace */
.highlight .mb { color: #900 } /* Literal.Number.Bin */
.highlight .mf { color: #900 } /* Literal.Number.Float */
.highlight .mh { color: #900 } /* Literal.Number.Hex */
.highlight .mi { color: #900 } /* Literal.Number.Integer */
.highlight .mo { color: #900 } /* Literal.Number.Oct */
.highlight .sa { color: #4E9A06 } /* Literal.String.Affix */
.highlight .sb { color: #4E9A06 } /* Literal.String.Backtick */
.highlight .sc { color: #4E9A06 } /* Literal.String.Char */
.highlight .dl { color: #4E9A06 } /* Literal.String.Delimiter */
.highlight .sd { color: #8F5902; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4E9A06 } /* Literal.String.Double */
.highlight .se { color: #4E9A06 } /* Literal.String.Escape */
.highlight .sh { color: #4E9A06 } /* Literal.String.Heredoc */
.highlight .si { color: #4E9A06 } /* Literal.String.Interpol */
.highlight .sx { color: #4E9A06 } /* Literal.String.Other */
.highlight .sr { color: #4E9A06 } /* Literal.String.Regex */
.highlight .s1 { color: #4E9A06 } /* Literal.String.Single */
.highlight .ss { color: #4E9A06 } /* Literal.String.Symbol */
.highlight .bp { color: #3465A4 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #000 } /* Name.Function.Magic */
.highlight .vc { color: #000 } /* Name.Variable.Class */
.highlight .vg { color: #000 } /* Name.Variable.Global */
.highlight .vi { color: #000 } /* Name.Variable.Instance */
.highlight .vm { color: #000 } /* Name.Variable.Magic */
.highlight .il { color: #900 } /* Literal.Number.Integer.Long */

Before

Width:  |  Height:  |  Size: 762 KiB

After

Width:  |  Height:  |  Size: 762 KiB

+635
View File
@@ -0,0 +1,635 @@
/*
* Sphinx JavaScript utilities for the full-text search.
*/
"use strict";
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: result => {
const [docname, title, anchor, descr, score, filename, kind] = result
return score
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2,
};
}
// Global search result kind enum, used by themes to style search results.
class SearchResultKind {
static get index() { return "index"; }
static get object() { return "object"; }
static get text() { return "text"; }
static get title() { return "title"; }
}
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _displayItem = (item, searchTerms, highlightTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const contentRoot = document.documentElement.dataset.content_root;
const [docName, title, anchor, descr, score, _filename, kind] = item;
let listItem = document.createElement("li");
// Add a class representing the item's type:
// can be used by a theme's CSS selector for styling
// See SearchResultKind for the class names.
listItem.classList.add(`kind-${kind}`);
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = contentRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = contentRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + anchor;
linkEl.dataset.score = score;
linkEl.innerHTML = title;
if (descr) {
listItem.appendChild(document.createElement("span")).innerHTML =
" (" + descr + ")";
// highlight search terms in the description
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
}
else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms, anchor)
);
// highlight search terms in the summary
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
);
else
Search.status.innerText = Documentation.ngettext(
"Search finished, found one page matching the search query.",
"Search finished, found ${resultCount} pages matching the search query.",
resultCount,
).replace('${resultCount}', resultCount);
};
const _displayNextItem = (
results,
resultCount,
searchTerms,
highlightTerms,
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), searchTerms, highlightTerms);
setTimeout(
() => _displayNextItem(results, resultCount, searchTerms, highlightTerms),
5
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
// Helper function used by query() to order search results.
// Each input is an array of [docname, title, anchor, descr, score, filename, kind].
// Order the results by score (in opposite order of appearance, since the
// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically.
const _orderResultsByScoreThenName = (a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) => query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter(term => term) // remove remaining empty strings
}
/**
* Search Module
*/
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
htmlToText: (htmlString, anchor) => {
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
for (const removalQuery of [".headerlink", "script", "style"]) {
htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() });
}
if (anchor) {
const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`);
if (anchorContent) return anchorContent.textContent;
console.warn(
`Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`
);
}
// if anchor not specified or not found, fall back to main content
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template."
);
return "";
},
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex: () => Search._index !== null,
deferQuery: (query) => (Search._queued_query = query),
stopPulse: () => (Search._pulse_status = -1),
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch: (query) => {
// create the required interface elements
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.setAttribute("role", "list");
searchList.classList.add("search");
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
_parseQuery: (query) => {
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords array is from language_data.js
if (
stopwords.indexOf(queryTermLower) !== -1 ||
queryTerm.match(/^\d+$/)
)
return;
// stem the word
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
});
if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
}
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
return [query, searchTerms, excludedTerms, highlightTerms, objectTerms];
},
/**
* execute search (requires search index to be loaded)
*/
_performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const allTitles = Search._index.alltitles;
const indexEntries = Search._index.indexentries;
// Collect multiple result groups to be sorted separately and then ordered.
// Each is an array of [docname, title, anchor, descr, score, filename, kind].
const normalResults = [];
const nonMainIndexResults = [];
_removeChildren(document.getElementById("search-progress"));
const queryLower = query.toLowerCase().trim();
for (const [title, foundTitles] of Object.entries(allTitles)) {
if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) {
for (const [file, id] of foundTitles) {
const score = Math.round(Scorer.title * queryLower.length / title.length);
const boost = titles[file] === title ? 1 : 0; // add a boost for document titles
normalResults.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
null,
score + boost,
filenames[file],
SearchResultKind.title,
]);
}
}
}
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
for (const [file, id, isMain] of foundEntries) {
const score = Math.round(100 * queryLower.length / entry.length);
const result = [
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
SearchResultKind.index,
];
if (isMain) {
normalResults.push(result);
} else {
nonMainIndexResults.push(result);
}
}
}
}
// lookup as object
objectTerms.forEach((term) =>
normalResults.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) {
normalResults.forEach((item) => (item[4] = Scorer.score(item)));
nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item)));
}
// Sort each group of results by score and then alphabetically by name.
normalResults.sort(_orderResultsByScoreThenName);
nonMainIndexResults.sort(_orderResultsByScoreThenName);
// Combine the result groups in (reverse) order.
// Non-main index entries are typically arbitrary cross-references,
// so display them after other results.
let results = [...nonMainIndexResults, ...normalResults];
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
return results.reverse();
},
query: (query) => {
const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query);
const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms);
// for debugging
//Search.lastresults = results.slice(); // a copy
// console.info("search results:", Search.lastresults);
// print the results
_displayNextItem(results, results.length, searchTerms, highlightTerms);
},
/**
* search for object names
*/
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
const results = [];
const objectSearchCallback = (prefix, match) => {
const name = match[4]
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
SearchResultKind.object,
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) =>
objectSearchCallback(prefix, array)
)
);
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
searchTerms.forEach((word) => {
const files = [];
// find documents, if any, containing the query word in their text/title term indices
// use Object.hasOwnProperty to avoid mismatching against prototype properties
const arr = [
{ files: terms.hasOwnProperty(word) ? terms[word] : undefined, score: Scorer.term },
{ files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
if (!terms.hasOwnProperty(word)) {
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord))
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
}
if (!titleTerms.hasOwnProperty(word)) {
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord))
arr.push({ files: titleTerms[term], score: Scorer.partialTitle });
});
}
}
// no match but word was a required one
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
arr.forEach((record) => {
if (record.files === undefined) return;
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, new Map());
const fileScores = scoreMap.get(file);
fileScores.set(word, record.score);
});
});
// create the mapping
files.forEach((file) => {
if (!fileMap.has(file)) fileMap.set(file, [word]);
else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word);
});
});
// now check if the files don't contain excluded terms
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2
).length;
if (
wordList.length !== searchTerms.size &&
wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
if (
[...excludedTerms].some(
(term) =>
terms[term] === file ||
titleTerms[term] === file ||
(terms[term] || []).includes(file) ||
(titleTerms[term] || []).includes(file)
)
)
break;
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w)));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
SearchResultKind.text,
]);
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words.
*/
makeSearchSummary: (htmlText, keywords, anchor) => {
const text = Search.htmlToText(htmlText, anchor);
if (text === "") return null;
const textLower = text.toLowerCase();
const actualStartPosition = [...keywords]
.map((k) => textLower.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("p");
summary.classList.add("context");
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
return summary;
},
};
_ready(Search.init);
+154
View File
@@ -0,0 +1,154 @@
/* Highlighting utilities for Sphinx HTML documentation. */
"use strict";
const SPHINX_HIGHLIGHT_ENABLED = true
/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
*/
const _highlight = (node, addItems, text, className) => {
if (node.nodeType === Node.TEXT_NODE) {
const val = node.nodeValue;
const parent = node.parentNode;
const pos = val.toLowerCase().indexOf(text);
if (
pos >= 0 &&
!parent.classList.contains(className) &&
!parent.classList.contains("nohighlight")
) {
let span;
const closestNode = parent.closest("body, svg, foreignObject");
const isInSVG = closestNode && closestNode.matches("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.classList.add(className);
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
const rest = document.createTextNode(val.substr(pos + text.length));
parent.insertBefore(
span,
parent.insertBefore(
rest,
node.nextSibling
)
);
node.nodeValue = val.substr(0, pos);
/* There may be more occurrences of search term in this node. So call this
* function recursively on the remaining fragment.
*/
_highlight(rest, addItems, text, className);
if (isInSVG) {
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect"
);
const bbox = parent.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute("class", className);
addItems.push({ parent: parent, target: rect });
}
}
} else if (node.matches && !node.matches("button, select, textarea")) {
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
}
};
const _highlightText = (thisNode, text, className) => {
let addItems = [];
_highlight(thisNode, addItems, text, className);
addItems.forEach((obj) =>
obj.parent.insertAdjacentElement("beforebegin", obj.target)
);
};
/**
* Small JavaScript module for the documentation.
*/
const SphinxHighlight = {
/**
* highlight the search words provided in localstorage in the text
*/
highlightSearchWords: () => {
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
// get and clear terms from localstorage
const url = new URL(window.location);
const highlight =
localStorage.getItem("sphinx_highlight_terms")
|| url.searchParams.get("highlight")
|| "";
localStorage.removeItem("sphinx_highlight_terms")
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
// get individual terms from highlight string
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
if (terms.length === 0) return; // nothing to do
// There should never be more than one element matching "div.body"
const divBody = document.querySelectorAll("div.body");
const body = divBody.length ? divBody[0] : document.querySelector("body");
window.setTimeout(() => {
terms.forEach((term) => _highlightText(body, term, "highlighted"));
}, 10);
const searchBox = document.getElementById("searchbox");
if (searchBox === null) return;
searchBox.appendChild(
document
.createRange()
.createContextualFragment(
'<p class="highlight-link">' +
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
_("Hide Search Matches") +
"</a></p>"
)
);
},
/**
* helper function to hide the search marks again
*/
hideSearchWords: () => {
document
.querySelectorAll("#searchbox .highlight-link")
.forEach((el) => el.remove());
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
localStorage.removeItem("sphinx_highlight_terms")
},
initEscapeListener: () => {
// only install a listener if it is really needed
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
SphinxHighlight.hideSearchWords();
event.preventDefault();
}
});
},
};
_ready(() => {
/* Do not call highlightSearchWords() when we are on the search page.
* It will highlight words from the *previous* search query.
*/
if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords();
SphinxHighlight.initEscapeListener();
});
+1131
View File
File diff suppressed because one or more lines are too long
+114
View File
@@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Backlog &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="_static/documentation_options.js?v=c0c9fa11"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="prev" title="Development Sandbox" href="sandbox.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="backlog">
<h1>Backlog<a class="headerlink" href="#backlog" title="Link to this heading"></a></h1>
<section id="future-ideas">
<h2>Future Ideas<a class="headerlink" href="#future-ideas" title="Link to this heading"></a></h2>
<ul class="simple">
<li><p>WebSocket before_request short-circuit support (reject before accept)</p></li>
<li><p>Per-route rate limiting (different limits for different endpoints)</p></li>
<li><p>Built-in structured logging with request context</p></li>
<li><p>OpenAPI 3.1 support</p></li>
<li><p>Dependency injection for route handlers</p></li>
</ul>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="index.html">
<img class="logo" src="_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<div>
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Backlog</a><ul>
<li><a class="reference internal" href="#future-ideas">Future Ideas</a></li>
</ul>
</li>
</ul>
</div>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
|
<a href="_sources/backlog.md.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>
+1076
View File
File diff suppressed because it is too large Load Diff
+198
View File
@@ -0,0 +1,198 @@
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Command Line Interface &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="_static/documentation_options.js?v=c0c9fa11"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Building a REST API" href="tutorial-rest.html" />
<link rel="prev" title="API Reference" href="api.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="command-line-interface">
<h1>Command Line Interface<a class="headerlink" href="#command-line-interface" title="Link to this heading"></a></h1>
<p>Responder installs a <code class="docutils literal notranslate"><span class="pre">responder</span></code> command that lets you launch
applications from the terminal. You can point it at a Python module,
a local file, or even a URL — and it will find your <code class="docutils literal notranslate"><span class="pre">API</span></code> instance
and start serving.</p>
<section id="launching-from-a-module">
<h2>Launching from a Module<a class="headerlink" href="#launching-from-a-module" title="Link to this heading"></a></h2>
<p>The most common way to run a Responder application in production. Use
Pythons standard dotted module path:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ responder run acme.app
</pre></div>
</div>
<p>This imports <code class="docutils literal notranslate"><span class="pre">acme.app</span></code> and looks for an attribute called <code class="docutils literal notranslate"><span class="pre">api</span></code>
(a <code class="docutils literal notranslate"><span class="pre">responder.API</span></code> instance). Its the same import system Python
uses everywhere — your <code class="docutils literal notranslate"><span class="pre">PYTHONPATH</span></code> and virtual environment are
respected.</p>
</section>
<section id="launching-from-a-file">
<h2>Launching from a File<a class="headerlink" href="#launching-from-a-file" title="Link to this heading"></a></h2>
<p>During development, you often have a single file you want to run:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ responder run helloworld.py
</pre></div>
</div>
<p>This loads the file directly and starts the server. Quick and easy for
prototyping and single-file applications.</p>
<p>You can test it with a simple HTTP request:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ curl http://127.0.0.1:5042/hello
hello, world!
</pre></div>
</div>
</section>
<section id="launching-from-a-url">
<h2>Launching from a URL<a class="headerlink" href="#launching-from-a-url" title="Link to this heading"></a></h2>
<p>Responder can fetch and run a Python file from any URL — great for
demos, sharing examples, and running code from GitHub:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ responder run https://github.com/kennethreitz/responder/raw/refs/heads/main/examples/helloworld.py
</pre></div>
</div>
<p>This also works with <code class="docutils literal notranslate"><span class="pre">github://</span></code> URLs and any filesystem protocol
supported by <a class="reference external" href="https://filesystem-spec.readthedocs.io/">fsspec</a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ responder run github://kennethreitz:responder@/examples/helloworld.py
</pre></div>
</div>
<p>Cloud storage is supported too — Azure Blob Storage, Google Cloud
Storage, S3, HDFS, SFTP, and more. Install <code class="docutils literal notranslate"><span class="pre">fsspec[full]</span></code> for all
protocols:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uv pip install &#39;fsspec[full]&#39;
</pre></div>
</div>
</section>
<section id="custom-instance-names">
<h2>Custom Instance Names<a class="headerlink" href="#custom-instance-names" title="Link to this heading"></a></h2>
<p>By default, Responder looks for an attribute called <code class="docutils literal notranslate"><span class="pre">api</span></code>. If your
application uses a different name, specify it with a colon:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ responder run acme.app:service
$ responder run myapp.py:application
</pre></div>
</div>
<p>For URLs, use a fragment:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ responder run https://example.com/app.py#service
</pre></div>
</div>
</section>
<section id="environment-variables">
<h2>Environment Variables<a class="headerlink" href="#environment-variables" title="Link to this heading"></a></h2>
<p>Responder automatically reads the <code class="docutils literal notranslate"><span class="pre">PORT</span></code> environment variable at
runtime:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">PORT</span></code> — bind to <code class="docutils literal notranslate"><span class="pre">0.0.0.0</span></code> on this port (cloud platform convention)</p></li>
</ul>
<p>When <code class="docutils literal notranslate"><span class="pre">PORT</span></code> is set, the server binds to all interfaces automatically.
This is how cloud platforms like Fly.io, Railway, and Heroku inject the
listen port.</p>
<p>For other settings like <code class="docutils literal notranslate"><span class="pre">SECRET_KEY</span></code>, read them in your application
code and pass them to <code class="docutils literal notranslate"><span class="pre">responder.API()</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">secret_key</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&quot;SECRET_KEY&quot;</span><span class="p">])</span>
</pre></div>
</div>
</section>
<section id="building-frontend-assets">
<h2>Building Frontend Assets<a class="headerlink" href="#building-frontend-assets" title="Link to this heading"></a></h2>
<p>If your project includes a JavaScript frontend with a <code class="docutils literal notranslate"><span class="pre">package.json</span></code>,
the <code class="docutils literal notranslate"><span class="pre">build</span></code> subcommand runs <code class="docutils literal notranslate"><span class="pre">npm</span> <span class="pre">run</span> <span class="pre">build</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ responder build
$ responder build /path/to/frontend
</pre></div>
</div>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="index.html">
<img class="logo" src="_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<div>
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Command Line Interface</a><ul>
<li><a class="reference internal" href="#launching-from-a-module">Launching from a Module</a></li>
<li><a class="reference internal" href="#launching-from-a-file">Launching from a File</a></li>
<li><a class="reference internal" href="#launching-from-a-url">Launching from a URL</a></li>
<li><a class="reference internal" href="#custom-instance-names">Custom Instance Names</a></li>
<li><a class="reference internal" href="#environment-variables">Environment Variables</a></li>
<li><a class="reference internal" href="#building-frontend-assets">Building Frontend Assets</a></li>
</ul>
</li>
</ul>
</div>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
|
<a href="_sources/cli.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>
+278
View File
@@ -0,0 +1,278 @@
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Deployment &#8212; responder 3.6.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="_static/documentation_options.js?v=c0c9fa11"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Testing" href="testing.html" />
<link rel="prev" title="Feature Tour" href="tour.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="deployment">
<h1>Deployment<a class="headerlink" href="#deployment" title="Link to this heading"></a></h1>
<p>Responder applications are standard <a class="reference external" href="https://asgi.readthedocs.io/">ASGI</a>
apps. ASGI (Asynchronous Server Gateway Interface) is the modern successor
to WSGI — it supports async, WebSockets, and HTTP/2. This means you can
deploy a Responder app anywhere that runs Python, using any ASGI server.</p>
<section id="running-locally">
<h2>Running Locally<a class="headerlink" href="#running-locally" title="Link to this heading"></a></h2>
<p>During development, <code class="docutils literal notranslate"><span class="pre">api.run()</span></code> is all you need:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="n">api</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
</div>
<p>This starts a <a class="reference external" href="https://www.uvicorn.org/">uvicorn</a> server on
<code class="docutils literal notranslate"><span class="pre">127.0.0.1:5042</span></code>. Uvicorn is a lightning-fast ASGI server built on
<a class="reference external" href="https://uvloop.readthedocs.io/">uvloop</a> — it handles thousands of
concurrent connections efficiently and protects against slowloris attacks,
making a reverse proxy like nginx optional for many deployments.</p>
</section>
<section id="docker">
<h2>Docker<a class="headerlink" href="#docker" title="Link to this heading"></a></h2>
<p>Docker is the most common way to package and deploy web applications.
Heres a minimal Dockerfile:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">FROM</span> <span class="n">python</span><span class="p">:</span><span class="mf">3.13</span><span class="o">-</span><span class="n">slim</span>
<span class="n">WORKDIR</span> <span class="o">/</span><span class="n">app</span>
<span class="n">COPY</span> <span class="o">--</span><span class="n">from</span><span class="o">=</span><span class="n">ghcr</span><span class="o">.</span><span class="n">io</span><span class="o">/</span><span class="n">astral</span><span class="o">-</span><span class="n">sh</span><span class="o">/</span><span class="n">uv</span><span class="p">:</span><span class="n">latest</span> <span class="o">/</span><span class="n">uv</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">uv</span>
<span class="n">COPY</span> <span class="o">.</span> <span class="o">.</span>
<span class="n">RUN</span> <span class="n">uv</span> <span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">system</span> <span class="n">responder</span>
<span class="n">ENV</span> <span class="n">PORT</span><span class="o">=</span><span class="mi">80</span>
<span class="n">EXPOSE</span> <span class="mi">80</span>
<span class="n">CMD</span> <span class="p">[</span><span class="s2">&quot;python&quot;</span><span class="p">,</span> <span class="s2">&quot;api.py&quot;</span><span class="p">]</span>
</pre></div>
</div>
<p>Build and run:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ docker build -t myapi .
$ docker run -p 8000:80 myapi
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">python:3.13-slim</span></code> image is about 150MB — small enough for fast
deploys but includes everything you need. Using <code class="docutils literal notranslate"><span class="pre">uv</span></code> for installs
is significantly faster than pip. For even smaller images, you can use
<code class="docutils literal notranslate"><span class="pre">python:3.13-alpine</span></code>, though some packages may need extra build
dependencies.</p>
</section>
<section id="cloud-platforms">
<h2>Cloud Platforms<a class="headerlink" href="#cloud-platforms" title="Link to this heading"></a></h2>
<p>Responder automatically honors the <code class="docutils literal notranslate"><span class="pre">PORT</span></code> environment variable. When
<code class="docutils literal notranslate"><span class="pre">PORT</span></code> is set, the server binds to <code class="docutils literal notranslate"><span class="pre">0.0.0.0</span></code> on that port — this is
the convention that virtually every cloud platform uses.</p>
<p>This means zero configuration on:</p>
<ul class="simple">
<li><p><strong>Fly.io</strong><code class="docutils literal notranslate"><span class="pre">fly</span> <span class="pre">launch</span></code> and youre done</p></li>
<li><p><strong>Railway</strong> — push your code, Railway sets <code class="docutils literal notranslate"><span class="pre">PORT</span></code></p></li>
<li><p><strong>Render</strong> — set start command to <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">api.py</span></code></p></li>
<li><p><strong>Google Cloud Run</strong> — containerize and deploy</p></li>
<li><p><strong>Azure Container Apps</strong> — same pattern</p></li>
<li><p><strong>AWS App Runner</strong> — and here too</p></li>
</ul>
<p>The pattern is always the same: deploy your code, set the start command
to <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">api.py</span></code>, and the platform handles the rest.</p>
</section>
<section id="health-check-endpoint">
<h2>Health Check Endpoint<a class="headerlink" href="#health-check-endpoint" title="Link to this heading"></a></h2>
<p>Every production deployment needs a health check — a lightweight endpoint
that monitoring tools, load balancers, and orchestrators can poll to verify
your service is running:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/health&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">health</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;healthy&quot;</span><span class="p">}</span>
</pre></div>
</div>
<p>Keep it simple. Dont query the database or do expensive work — the health
check should return instantly. Cloud platforms, Docker, and Kubernetes all
look for an HTTP 200 to confirm your service is alive.</p>
<p>For Docker, add a <code class="docutils literal notranslate"><span class="pre">HEALTHCHECK</span></code> instruction:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">HEALTHCHECK</span> <span class="o">--</span><span class="n">interval</span><span class="o">=</span><span class="mi">30</span><span class="n">s</span> <span class="o">--</span><span class="n">timeout</span><span class="o">=</span><span class="mi">3</span><span class="n">s</span> \
<span class="n">CMD</span> <span class="n">curl</span> <span class="o">-</span><span class="n">f</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="o">/</span><span class="n">health</span> <span class="o">||</span> <span class="n">exit</span> <span class="mi">1</span>
</pre></div>
</div>
</section>
<section id="uvicorn-directly">
<h2>Uvicorn Directly<a class="headerlink" href="#uvicorn-directly" title="Link to this heading"></a></h2>
<p>For production deployments where you want more control, bypass
<code class="docutils literal notranslate"><span class="pre">api.run()</span></code> and use uvicorn directly:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uvicorn api:api --host 0.0.0.0 --port 8000 --workers 4
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">--workers</span></code> flag spawns multiple processes, each handling requests
independently. A good starting point is 2-4 workers per CPU core.</p>
<p>Uvicorn supports many options — SSL certificates, access logging, graceful
shutdown timeouts, and more. See the
<a class="reference external" href="https://www.uvicorn.org/deployment/">uvicorn documentation</a> for details.</p>
<p>For platforms like Heroku or Railway that use a <code class="docutils literal notranslate"><span class="pre">Procfile</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>web: uvicorn api:api --host 0.0.0.0 --port $PORT --workers 4
</pre></div>
</div>
</section>
<section id="docker-compose">
<h2>Docker Compose<a class="headerlink" href="#docker-compose" title="Link to this heading"></a></h2>
<p>For local development with databases and other services, Docker Compose
ties everything together:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># docker-compose.yml</span>
<span class="n">services</span><span class="p">:</span>
<span class="n">api</span><span class="p">:</span>
<span class="n">build</span><span class="p">:</span> <span class="o">.</span>
<span class="n">ports</span><span class="p">:</span>
<span class="o">-</span> <span class="s2">&quot;5042:80&quot;</span>
<span class="n">environment</span><span class="p">:</span>
<span class="o">-</span> <span class="n">PORT</span><span class="o">=</span><span class="mi">80</span>
<span class="o">-</span> <span class="n">DATABASE_URL</span><span class="o">=</span><span class="n">postgresql</span><span class="o">+</span><span class="n">asyncpg</span><span class="p">:</span><span class="o">//</span><span class="n">user</span><span class="p">:</span><span class="k">pass</span><span class="nd">@db</span><span class="o">/</span><span class="n">myapp</span>
<span class="o">-</span> <span class="n">SECRET_KEY</span><span class="o">=</span><span class="n">dev</span><span class="o">-</span><span class="n">secret</span>
<span class="n">depends_on</span><span class="p">:</span>
<span class="o">-</span> <span class="n">db</span>
<span class="n">db</span><span class="p">:</span>
<span class="n">image</span><span class="p">:</span> <span class="n">docker</span><span class="o">.</span><span class="n">io</span><span class="o">/</span><span class="n">postgres</span><span class="p">:</span><span class="mi">16</span><span class="o">-</span><span class="n">alpine</span>
<span class="n">environment</span><span class="p">:</span>
<span class="n">POSTGRES_USER</span><span class="p">:</span> <span class="n">user</span>
<span class="n">POSTGRES_PASSWORD</span><span class="p">:</span> <span class="k">pass</span>
<span class="n">POSTGRES_DB</span><span class="p">:</span> <span class="n">myapp</span>
<span class="n">volumes</span><span class="p">:</span>
<span class="o">-</span> <span class="n">pgdata</span><span class="p">:</span><span class="o">/</span><span class="n">var</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">postgresql</span><span class="o">/</span><span class="n">data</span>
<span class="n">volumes</span><span class="p">:</span>
<span class="n">pgdata</span><span class="p">:</span>
</pre></div>
</div>
<p>Run with <code class="docutils literal notranslate"><span class="pre">docker</span> <span class="pre">compose</span> <span class="pre">up</span></code>. The API waits for <code class="docutils literal notranslate"><span class="pre">db</span></code> to start, then
connects using the <code class="docutils literal notranslate"><span class="pre">DATABASE_URL</span></code> environment variable.</p>
</section>
<section id="reverse-proxy">
<h2>Reverse Proxy<a class="headerlink" href="#reverse-proxy" title="Link to this heading"></a></h2>
<p>For high-traffic production deployments, you may want a reverse proxy like
<a class="reference external" href="https://nginx.org/">nginx</a> or <a class="reference external" href="https://caddyserver.com/">Caddy</a> in
front of your application for:</p>
<ul class="simple">
<li><p><strong>SSL/TLS termination</strong> — let the proxy handle HTTPS certificates</p></li>
<li><p><strong>Load balancing</strong> — distribute traffic across multiple app instances</p></li>
<li><p><strong>Static asset serving</strong> — offload static files to the proxy</p></li>
<li><p><strong>Rate limiting</strong> — at the infrastructure level</p></li>
</ul>
<p>A minimal Caddy config that handles HTTPS automatically:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Caddyfile</span>
<span class="n">example</span><span class="o">.</span><span class="n">com</span> <span class="p">{</span>
<span class="n">reverse_proxy</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">5042</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Responders <code class="docutils literal notranslate"><span class="pre">TrustedHostMiddleware</span></code> and <code class="docutils literal notranslate"><span class="pre">HTTPSRedirectMiddleware</span></code> work
correctly behind proxies that set standard forwarding headers
(<code class="docutils literal notranslate"><span class="pre">X-Forwarded-For</span></code>, <code class="docutils literal notranslate"><span class="pre">X-Forwarded-Proto</span></code>).</p>
<p>That said, uvicorn is production-ready on its own. Many applications run
uvicorn directly without a reverse proxy and do just fine.</p>
</section>
<section id="production-checklist">
<h2>Production Checklist<a class="headerlink" href="#production-checklist" title="Link to this heading"></a></h2>
<p>Before going live:</p>
<ul class="simple">
<li><p><strong>Set a secret key</strong><code class="docutils literal notranslate"><span class="pre">SECRET_KEY</span></code> env var, never the default</p></li>
<li><p><strong>Disable debug mode</strong><code class="docutils literal notranslate"><span class="pre">DEBUG=false</span></code> or omit it entirely</p></li>
<li><p><strong>Set allowed hosts</strong> — restrict to your actual domain names</p></li>
<li><p><strong>Use multiple workers</strong><code class="docutils literal notranslate"><span class="pre">--workers</span> <span class="pre">4</span></code> or more, depending on CPU cores</p></li>
<li><p><strong>Add a health check</strong><code class="docutils literal notranslate"><span class="pre">/health</span></code> endpoint for monitoring</p></li>
<li><p><strong>Enable HTTPS</strong> — via your proxy, cloud platform, or uvicorns <code class="docutils literal notranslate"><span class="pre">--ssl-*</span></code> flags</p></li>
<li><p><strong>Set up logging</strong> — uvicorn logs requests by default; pipe them to your log aggregator</p></li>
<li><p><strong>Pin your dependencies</strong> — use a lock file or pinned requirements for reproducible deploys</p></li>
</ul>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="index.html">
<img class="logo" src="_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v3.6.2</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<div>
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Deployment</a><ul>
<li><a class="reference internal" href="#running-locally">Running Locally</a></li>
<li><a class="reference internal" href="#docker">Docker</a></li>
<li><a class="reference internal" href="#cloud-platforms">Cloud Platforms</a></li>
<li><a class="reference internal" href="#health-check-endpoint">Health Check Endpoint</a></li>
<li><a class="reference internal" href="#uvicorn-directly">Uvicorn Directly</a></li>
<li><a class="reference internal" href="#docker-compose">Docker Compose</a></li>
<li><a class="reference internal" href="#reverse-proxy">Reverse Proxy</a></li>
<li><a class="reference internal" href="#production-checklist">Production Checklist</a></li>
</ul>
</li>
</ul>
</div>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
|
<a href="_sources/deployment.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>
-19
View File
@@ -1,19 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-35
View File
@@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
-21
View File
@@ -1,21 +0,0 @@
<style type="text/css">
/* Make the document a little wider. */
div.document {
width: 1008px;
}
/* Better spacing around code blocks. */
div.highlight pre {
padding: 11px 14px;
}
/* Responsive layout. */
@media screen and (max-width: 1008px) {
div.sphinxsidebar {
display: none;
}
div.document {
width: 100% !important;
}
}
</style>
-16
View File
@@ -1,16 +0,0 @@
<p class="logo">
<a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/responder.png', 1) }}" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v{{ version }}</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
-1
View File
@@ -1 +0,0 @@
../../CHANGELOG.md
-52
View File
@@ -1,52 +0,0 @@
# Sphinx configuration for Responder documentation.
import os
project = "responder"
copyright = "2018-2026, Kenneth Reitz"
author = "Kenneth Reitz"
here = os.path.abspath(os.path.dirname(__file__))
about = {}
with open(os.path.join(here, "..", "..", "responder", "__version__.py")) as f:
exec(f.read(), about)
version = about["__version__"]
release = about["__version__"]
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.viewcode",
"myst_parser",
"sphinx_copybutton",
"sphinx_design_elements",
]
templates_path = ["_templates"]
source_suffix = {".rst": "restructuredtext"}
master_doc = "index"
language = "en"
exclude_patterns = []
# Theme
html_theme = "alabaster"
html_theme_options = {
"show_powered_by": False,
"github_user": "kennethreitz",
"github_repo": "responder",
"github_banner": False,
"show_related": False,
}
html_static_path = ["_static"]
html_sidebars = {
"index": ["sidebarintro.html", "searchbox.html"],
"**": ["sidebarintro.html", "localtoc.html", "searchbox.html"],
}
# MyST
myst_heading_anchors = 3
# Copybutton
copybutton_remove_prompts = True
copybutton_prompt_text = r">>> |\.\.\. |\$ "
copybutton_prompt_is_regexp = True
-19
View File
@@ -1,19 +0,0 @@
# Example HTTP service definition, using Responder.
# https://pypi.org/project/responder/
import responder
api = responder.API()
@api.route("/")
async def index(req, resp):
resp.text = "hello, world!"
@api.route("/{greeting}")
async def greet_world(req, resp, *, greeting):
resp.text = f"{greeting}, world!"
if __name__ == "__main__":
api.run()
-26
View File
@@ -1,26 +0,0 @@
# Example showing the lifespan context manager pattern.
# https://pypi.org/project/responder/
from contextlib import asynccontextmanager
import responder
@asynccontextmanager
async def lifespan(app):
# Startup: initialize resources
print("Starting up...")
yield
# Shutdown: clean up resources
print("Shutting down...")
api = responder.API(lifespan=lifespan)
@api.route("/{greeting}")
async def greet_world(req, resp, *, greeting):
resp.text = f"{greeting}, world!"
if __name__ == "__main__":
api.run()
-31
View File
@@ -1,31 +0,0 @@
"""Mount marimo notebooks inside a Responder API.
Requirements:
pip install responder marimo
Usage:
python examples/marimo_mount.py
Then visit:
http://127.0.0.1:5042/hello → Responder JSON endpoint
http://127.0.0.1:5042/notebooks/ → Interactive marimo notebook
"""
import marimo
import responder
api = responder.API()
@api.route("/hello")
def hello(req, resp):
resp.media = {"message": "Hello from Responder!"}
# Mount marimo notebooks at /notebooks
server = marimo.create_asgi_app().with_app(path="", root="notebooks/hello.py")
api.mount("/notebooks", server.build())
if __name__ == "__main__":
api.run()
-76
View File
@@ -1,76 +0,0 @@
# Complete REST API example with Pydantic validation.
# https://responder.kennethreitz.org/tutorial-rest.html
from pydantic import BaseModel
import responder
class BookIn(BaseModel):
title: str
author: str
year: int
isbn: str | None = None
class BookOut(BaseModel):
id: int
title: str
author: str
year: int
isbn: str | None = None
api = responder.API(
title="Book Catalog",
version="1.0",
openapi="3.0.2",
docs_route="/docs",
)
books_db: dict[int, dict] = {}
next_id = 1
@api.route("/books", methods=["GET"])
def list_books(req, resp):
resp.media = list(books_db.values())
@api.route(
"/books",
methods=["POST"],
check_existing=False,
request_model=BookIn,
response_model=BookOut,
)
async def create_book(req, resp):
global next_id
data = await req.media()
book = {"id": next_id, **data}
books_db[next_id] = book
next_id += 1
resp.media = book
resp.status_code = 201
@api.route("/books/{book_id:int}", methods=["GET"])
def get_book(req, resp, *, book_id):
if book_id not in books_db:
resp.status_code = 404
resp.media = {"error": f"Book {book_id} not found"}
return
resp.media = books_db[book_id]
@api.route("/books/{book_id:int}", methods=["DELETE"], check_existing=False)
def delete_book(req, resp, *, book_id):
if book_id not in books_db:
resp.status_code = 404
resp.media = {"error": f"Book {book_id} not found"}
return
del books_db[book_id]
resp.status_code = 204
if __name__ == "__main__":
api.run()
-42
View File
@@ -1,42 +0,0 @@
# Server-Sent Events streaming example.
# https://responder.kennethreitz.org/tour.html#server-sent-events-sse
import asyncio
import responder
api = responder.API()
@api.route("/")
def index(req, resp):
resp.html = """
<!DOCTYPE html>
<html>
<body>
<h1>SSE Stream</h1>
<div id="events"></div>
<script>
const source = new EventSource("/stream");
const events = document.getElementById("events");
source.onmessage = (e) => {
const p = document.createElement("p");
p.textContent = e.data;
events.appendChild(p);
};
</script>
</body>
</html>
"""
@api.route("/stream")
async def stream(req, resp):
@resp.sse
async def events():
for i in range(20):
yield {"data": f"Event #{i}"}
await asyncio.sleep(0.5)
if __name__ == "__main__":
api.run()
-25
View File
@@ -1,25 +0,0 @@
# Example HTTP service definition, using Responder.
# https://pypi.org/project/responder/
import responder
api = responder.API()
@api.route("/")
async def index(req, resp):
resp.text = "Welcome"
@api.route("/user")
async def user_create(req, resp):
data = await req.media()
resp.text = f"Hello, {data['username']}"
@api.route("/user/{identifier}")
async def user_get(req, resp, *, identifier):
resp.text = f"Hello, user {identifier}"
if __name__ == "__main__":
api.run()
-59
View File
@@ -1,59 +0,0 @@
# WebSocket chat room example.
# https://responder.kennethreitz.org/tutorial-websockets.html
from starlette.websockets import WebSocketDisconnect
import responder
api = responder.API()
connected = set()
@api.route("/")
def index(req, resp):
resp.html = """
<!DOCTYPE html>
<html>
<body>
<h1>Chat Room</h1>
<div id="messages" style="height:300px;overflow-y:scroll;border:1px solid #ccc;padding:10px;"></div>
<input id="input" placeholder="Type a message..." style="width:300px;" />
<script>
const ws = new WebSocket(`ws://${location.host}/chat`);
const messages = document.getElementById("messages");
const input = document.getElementById("input");
ws.onmessage = (e) => {
const p = document.createElement("p");
p.textContent = e.data;
messages.appendChild(p);
messages.scrollTop = messages.scrollHeight;
};
input.addEventListener("keypress", (e) => {
if (e.key === "Enter" && input.value) {
ws.send(input.value);
input.value = "";
}
});
</script>
</body>
</html>
""" # noqa: E501
@api.route("/chat", websocket=True)
async def chat(ws):
await ws.accept()
connected.add(ws)
try:
while True:
message = await ws.receive_text()
for client in connected:
await client.send_text(message)
except WebSocketDisconnect:
pass
finally:
connected.discard(ws)
if __name__ == "__main__":
api.run()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve"><polygon points="32.625,51 21.836,51 28.536,13 39.325,13 "></polygon><polygon points="49.107,51 38.319,51 45.019,13 55.808,13 "></polygon><rect x="9" y="18" width="12" height="12"></rect><rect x="9" y="33" width="12" height="12"></rect></svg>

Before

Width:  |  Height:  |  Size: 430 B

Some files were not shown because too many files have changed in this diff Show More