Compare commits
No commits in common. "main" and "0.3.0" have entirely different histories.
@ -1,38 +0,0 @@
|
|||||||
version: "{branch}.{build}"
|
|
||||||
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
- TOXENV: py36-no-ext
|
|
||||||
PYTHON: "C:\\Python36-x64"
|
|
||||||
PYTHON_VERSION: "3.6.x"
|
|
||||||
PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
- TOXENV: py37-no-ext
|
|
||||||
PYTHON: "C:\\Python37-x64"
|
|
||||||
PYTHON_VERSION: "3.7.x"
|
|
||||||
PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
- TOXENV: py38-no-ext
|
|
||||||
PYTHON: "C:\\Python38-x64"
|
|
||||||
PYTHON_VERSION: "3.8.x"
|
|
||||||
PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
# - TOXENV: py39-no-ext
|
|
||||||
# PYTHON: "C:\\Python39-x64\\python"
|
|
||||||
# PYTHONPATH: "C:\\Python39-x64"
|
|
||||||
# PYTHON_VERSION: "3.9.x"
|
|
||||||
# PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
init: SET "PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
|
||||||
|
|
||||||
install:
|
|
||||||
- pip install tox
|
|
||||||
|
|
||||||
build: off
|
|
||||||
|
|
||||||
test_script: tox
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
- provider: Email
|
|
||||||
on_build_success: false
|
|
||||||
on_build_status_changed: false
|
|
22
.coveragerc
22
.coveragerc
@ -1,25 +1,7 @@
|
|||||||
[run]
|
[run]
|
||||||
branch = True
|
branch = True
|
||||||
source = sanic
|
source = sanic, tests
|
||||||
omit =
|
omit = site-packages
|
||||||
site-packages
|
|
||||||
sanic/__main__.py
|
|
||||||
sanic/server/legacy.py
|
|
||||||
sanic/compat.py
|
|
||||||
sanic/simple.py
|
|
||||||
sanic/utils.py
|
|
||||||
sanic/cli
|
|
||||||
sanic/pages
|
|
||||||
|
|
||||||
[html]
|
[html]
|
||||||
directory = coverage
|
directory = coverage
|
||||||
|
|
||||||
[report]
|
|
||||||
exclude_lines =
|
|
||||||
no cov
|
|
||||||
no qa
|
|
||||||
noqa
|
|
||||||
NOQA
|
|
||||||
pragma: no cover
|
|
||||||
TYPE_CHECKING
|
|
||||||
skip_empty = True
|
|
||||||
|
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@ -1,3 +0,0 @@
|
|||||||
* @sanic-org/sanic-release-managers
|
|
||||||
/sanic/ @sanic-org/framework
|
|
||||||
/tests/ @sanic-org/framework
|
|
12
.github/FUNDING.yml
vendored
12
.github/FUNDING.yml
vendored
@ -1,12 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
||||||
patreon: # Replace with a single Patreon username
|
|
||||||
open_collective: sanic-org # Replace with a single Open Collective username
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: # Replace with a single Liberapay username
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
78
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
78
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@ -1,78 +0,0 @@
|
|||||||
name: 🐞 Bug report
|
|
||||||
description: Create a report to help us improve
|
|
||||||
labels: ["bug", "triage"]
|
|
||||||
body:
|
|
||||||
- type: checkboxes
|
|
||||||
id: existing
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search to see if an issue already exists for the bug you encountered.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Describe the bug
|
|
||||||
description: A clear and concise description of what the bug is, make sure to paste any exceptions and tracebacks using markdown code-block syntax to make it easier to read.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: code
|
|
||||||
attributes:
|
|
||||||
label: Code snippet
|
|
||||||
description: |
|
|
||||||
Relevant source code, make sure to remove what is not necessary. Please try and format your code so that it is easier to read. For example:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from sanic import Sanic
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
```
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
id: expected
|
|
||||||
attributes:
|
|
||||||
label: Expected Behavior
|
|
||||||
description: A concise description of what you expected to happen.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: dropdown
|
|
||||||
id: running
|
|
||||||
attributes:
|
|
||||||
label: How do you run Sanic?
|
|
||||||
options:
|
|
||||||
- Sanic CLI
|
|
||||||
- As a module
|
|
||||||
- As a script (`app.run` or `Sanic.serve`)
|
|
||||||
- ASGI
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
id: os
|
|
||||||
attributes:
|
|
||||||
label: Operating System
|
|
||||||
description: What OS?
|
|
||||||
options:
|
|
||||||
- Linux
|
|
||||||
- MacOS
|
|
||||||
- Windows
|
|
||||||
- Other (tell us in the description)
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: Sanic Version
|
|
||||||
description: Check startup logs or try `sanic --version`
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: additional
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add any other context about the problem here.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,8 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Questions and Help
|
|
||||||
url: https://community.sanicframework.org/c/questions-and-help
|
|
||||||
about: Do you need help with Sanic? Ask your questions here.
|
|
||||||
- name: Discussion and Support
|
|
||||||
url: https://discord.gg/FARQzAEMAA
|
|
||||||
about: For live discussion and support, checkout the Sanic Discord server.
|
|
34
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
34
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@ -1,34 +0,0 @@
|
|||||||
name: 🌟 Feature request
|
|
||||||
description: Suggest an enhancement for Sanic
|
|
||||||
labels: ["feature request"]
|
|
||||||
body:
|
|
||||||
- type: checkboxes
|
|
||||||
id: existing
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search to see if an issue already exists for the enhancement you are proposing.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Is your feature request related to a problem? Please describe.
|
|
||||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
id: code
|
|
||||||
attributes:
|
|
||||||
label: Describe the solution you'd like
|
|
||||||
description: A clear and concise description of what you want to happen.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: additional
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add any other context about the problem here.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
33
.github/ISSUE_TEMPLATE/rfc.yml
vendored
33
.github/ISSUE_TEMPLATE/rfc.yml
vendored
@ -1,33 +0,0 @@
|
|||||||
name: 💡 Request for Comments
|
|
||||||
description: Open an RFC for discussion
|
|
||||||
labels: ["RFC"]
|
|
||||||
body:
|
|
||||||
- type: input
|
|
||||||
id: compare
|
|
||||||
attributes:
|
|
||||||
label: Link to code
|
|
||||||
description: If available, share a [comparison](https://github.com/sanic-org/sanic/compare) from a POC branch to main
|
|
||||||
placeholder: https://github.com/sanic-org/sanic/compare/main...some-new-branch
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
id: proposal
|
|
||||||
attributes:
|
|
||||||
label: Proposal
|
|
||||||
description: A thorough discussion of the proposal discussing the problem it solves, potential code, use cases, and impacts
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: additional
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add any other context that is relevant
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: breaking
|
|
||||||
attributes:
|
|
||||||
label: Is this a breaking change?
|
|
||||||
options:
|
|
||||||
- label: "Yes"
|
|
||||||
required: false
|
|
20
.github/stale.yml
vendored
20
.github/stale.yml
vendored
@ -1,20 +0,0 @@
|
|||||||
# Number of days of inactivity before an issue becomes stale
|
|
||||||
daysUntilStale: 90
|
|
||||||
# Number of days of inactivity before a stale issue is closed
|
|
||||||
daysUntilClose: 30
|
|
||||||
# Issues with these labels will never be considered stale
|
|
||||||
exemptLabels:
|
|
||||||
- bug
|
|
||||||
- urgent
|
|
||||||
- necessary
|
|
||||||
- help wanted
|
|
||||||
- RFC
|
|
||||||
# Label to use when marking an issue as stale
|
|
||||||
staleLabel: stale
|
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed if no further activity occurs. If this
|
|
||||||
is incorrect, please respond with an update. Thank you for your contributions.
|
|
||||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
|
||||||
closeComment: false
|
|
42
.github/workflows/codeql-analysis.yml
vendored
42
.github/workflows/codeql-analysis.yml
vendored
@ -1,42 +0,0 @@
|
|||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- current-release
|
|
||||||
- "*LTS"
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- current-release
|
|
||||||
- "*LTS"
|
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
|
||||||
schedule:
|
|
||||||
- cron: '25 16 * * 0'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
if: github.event.pull_request.draft == false
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'python' ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v1
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v1
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v1
|
|
34
.github/workflows/coverage.yml
vendored
34
.github/workflows/coverage.yml
vendored
@ -1,34 +0,0 @@
|
|||||||
name: Coverage check
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- current-release
|
|
||||||
- "*LTS"
|
|
||||||
tags:
|
|
||||||
- "!*" # Do not execute on tags
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- current-release
|
|
||||||
- "*LTS"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
coverage:
|
|
||||||
name: Check coverage
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Run coverage
|
|
||||||
uses: sanic-org/simple-tox-action@v1
|
|
||||||
with:
|
|
||||||
python-version: "3.11"
|
|
||||||
tox-env: coverage
|
|
||||||
ignore-errors: true
|
|
||||||
- name: Run Codecov
|
|
||||||
uses: codecov/codecov-action@v3
|
|
||||||
with:
|
|
||||||
files: ./coverage.xml
|
|
||||||
fail_ci_if_error: false
|
|
174
.github/workflows/publish-release.yml
vendored
174
.github/workflows/publish-release.yml
vendored
@ -1,174 +0,0 @@
|
|||||||
name: Publish release
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
env:
|
|
||||||
IS_TEST: false
|
|
||||||
DOCKER_ORG_NAME: sanicframework
|
|
||||||
DOCKER_IMAGE_NAME: sanic
|
|
||||||
DOCKER_BASE_IMAGE_NAME: sanic-build
|
|
||||||
DOCKER_IMAGE_DOCKERFILE: ./docker/Dockerfile
|
|
||||||
DOCKER_BASE_IMAGE_DOCKERFILE: ./docker/Dockerfile-base
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
generate_info:
|
|
||||||
name: Generate info
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
docker-tags: ${{ steps.generate_docker_info.outputs.tags }}
|
|
||||||
pypi-version: ${{ steps.parse_version_tag.outputs.pypi-version }}
|
|
||||||
steps:
|
|
||||||
- name: Parse version tag
|
|
||||||
id: parse_version_tag
|
|
||||||
env:
|
|
||||||
TAG_NAME: ${{ github.event.release.tag_name }}
|
|
||||||
run: |
|
|
||||||
tag_name="${{ env.TAG_NAME }}"
|
|
||||||
|
|
||||||
if [[ ! "${tag_name}" =~ ^v([0-9]{2})\.([0-9]{1,2})\.([0-9]+)$ ]]; then
|
|
||||||
echo "::error::Tag name must be in the format vYY.MM.MICRO"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
year_output="year=${BASH_REMATCH[1]}"
|
|
||||||
month_output="month=${BASH_REMATCH[2]}"
|
|
||||||
pypi_output="pypi-version=${tag_name#v}"
|
|
||||||
|
|
||||||
echo "${year_output}"
|
|
||||||
echo "${month_output}"
|
|
||||||
echo "${pypi_output}"
|
|
||||||
|
|
||||||
echo "${year_output}" >> $GITHUB_OUTPUT
|
|
||||||
echo "${month_output}" >> $GITHUB_OUTPUT
|
|
||||||
echo "${pypi_output}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Get latest release
|
|
||||||
id: get_latest_release
|
|
||||||
run: |
|
|
||||||
latest_tag=$(
|
|
||||||
curl -L \
|
|
||||||
-H "Accept: application/vnd.github+json" \
|
|
||||||
-H "Authorization: Bearer ${{ github.token }}" \
|
|
||||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
||||||
https://api.github.com/repos/${{ github.repository }}/releases/latest \
|
|
||||||
| jq -r '.tag_name'
|
|
||||||
)
|
|
||||||
echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Generate Docker info
|
|
||||||
id: generate_docker_info
|
|
||||||
run: |
|
|
||||||
tag_year="${{ steps.parse_version_tag.outputs.year }}"
|
|
||||||
tag_month="${{ steps.parse_version_tag.outputs.month }}"
|
|
||||||
latest_tag="${{ steps.get_latest_release.outputs.latest_tag }}"
|
|
||||||
tag="${{ github.event.release.tag_name }}"
|
|
||||||
|
|
||||||
tags="${tag_year}.${tag_month}"
|
|
||||||
|
|
||||||
if [[ "${tag_month}" == "12" ]]; then
|
|
||||||
tags+=",LTS"
|
|
||||||
echo "::notice::Tag ${tag} is LTS version"
|
|
||||||
else
|
|
||||||
echo "::notice::Tag ${tag} is not LTS version"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${latest_tag}" == "${{ github.event.release.tag_name }}" ]]; then
|
|
||||||
tags+=",latest"
|
|
||||||
echo "::notice::Tag ${tag} is marked as latest"
|
|
||||||
else
|
|
||||||
echo "::notice::Tag ${tag} is not marked as latest"
|
|
||||||
fi
|
|
||||||
|
|
||||||
tags_output="tags=${tags}"
|
|
||||||
|
|
||||||
echo "${tags_output}"
|
|
||||||
echo "${tags_output}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
publish_package:
|
|
||||||
name: Build and publish package
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: generate_info
|
|
||||||
steps:
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.11"
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pip install build twine
|
|
||||||
|
|
||||||
- name: Update package version
|
|
||||||
run: |
|
|
||||||
echo "__version__ = \"${{ needs.generate_info.outputs.pypi-version }}\"" > sanic/__version__.py
|
|
||||||
|
|
||||||
- name: Build a binary wheel and a source tarball
|
|
||||||
run: python -m build --sdist --wheel --outdir dist/ .
|
|
||||||
|
|
||||||
- name: Publish to PyPi 🚀
|
|
||||||
run: twine upload --non-interactive --disable-progress-bar dist/*
|
|
||||||
env:
|
|
||||||
TWINE_USERNAME: __token__
|
|
||||||
TWINE_PASSWORD: ${{ env.IS_TEST == 'true' && secrets.SANIC_TEST_PYPI_API_TOKEN || secrets.SANIC_PYPI_API_TOKEN }}
|
|
||||||
TWINE_REPOSITORY: ${{ env.IS_TEST == 'true' && 'testpypi' || 'pypi' }}
|
|
||||||
|
|
||||||
publish_docker:
|
|
||||||
name: Publish Docker / Python ${{ matrix.python-version }}
|
|
||||||
needs: [generate_info, publish_package]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
python-version: ["3.10", "3.11"]
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_ACCESS_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build and push base image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
push: ${{ env.IS_TEST == 'false' }}
|
|
||||||
file: ${{ env.DOCKER_BASE_IMAGE_DOCKERFILE }}
|
|
||||||
tags: ${{ env.DOCKER_ORG_NAME }}/${{ env.DOCKER_BASE_IMAGE_NAME }}:${{ matrix.python-version }}
|
|
||||||
build-args: |
|
|
||||||
PYTHON_VERSION=${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Parse tags for this Python version
|
|
||||||
id: parse_tags
|
|
||||||
run: |
|
|
||||||
IFS=',' read -ra tags <<< "${{ needs.generate_info.outputs.docker-tags }}"
|
|
||||||
tag_args=""
|
|
||||||
|
|
||||||
for tag in "${tags[@]}"; do
|
|
||||||
tag_args+=",${{ env.DOCKER_ORG_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:${tag}-py${{ matrix.python-version }}"
|
|
||||||
done
|
|
||||||
|
|
||||||
tag_args_output="tag_args=${tag_args:1}"
|
|
||||||
|
|
||||||
echo "${tag_args_output}"
|
|
||||||
echo "${tag_args_output}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Build and push Sanic image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
push: ${{ env.IS_TEST == 'false' }}
|
|
||||||
file: ${{ env.DOCKER_IMAGE_DOCKERFILE }}
|
|
||||||
tags: ${{ steps.parse_tags.outputs.tag_args }}
|
|
||||||
build-args: |
|
|
||||||
BASE_IMAGE_ORG=${{ env.DOCKER_ORG_NAME }}
|
|
||||||
BASE_IMAGE_NAME=${{ env.DOCKER_BASE_IMAGE_NAME }}
|
|
||||||
BASE_IMAGE_TAG=${{ matrix.python-version }}
|
|
||||||
SANIC_PYPI_VERSION=${{ needs.generate_info.outputs.pypi-version }}
|
|
56
.github/workflows/tests.yml
vendored
56
.github/workflows/tests.yml
vendored
@ -1,56 +0,0 @@
|
|||||||
name: Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- current-release
|
|
||||||
- "*LTS"
|
|
||||||
tags:
|
|
||||||
- "!*"
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- current-release
|
|
||||||
- "*LTS"
|
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run_tests:
|
|
||||||
name: "${{ matrix.config.platform == 'windows-latest' && 'Windows' || 'Linux' }} / Python ${{ matrix.config.python-version }} / tox -e ${{ matrix.config.tox-env }}"
|
|
||||||
if: github.event.pull_request.draft == false
|
|
||||||
runs-on: ${{ matrix.config.platform || 'ubuntu-latest' }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- { python-version: "3.8", tox-env: security }
|
|
||||||
- { python-version: "3.9", tox-env: security }
|
|
||||||
- { python-version: "3.10", tox-env: security }
|
|
||||||
- { python-version: "3.11", tox-env: security }
|
|
||||||
- { python-version: "3.10", tox-env: lint }
|
|
||||||
# - { python-version: "3.10", tox-env: docs }
|
|
||||||
- { python-version: "3.8", tox-env: type-checking }
|
|
||||||
- { python-version: "3.9", tox-env: type-checking }
|
|
||||||
- { python-version: "3.10", tox-env: type-checking }
|
|
||||||
- { python-version: "3.11", tox-env: type-checking }
|
|
||||||
- { python-version: "3.8", tox-env: py38, max-attempts: 3 }
|
|
||||||
- { python-version: "3.8", tox-env: py38-no-ext, max-attempts: 3 }
|
|
||||||
- { python-version: "3.9", tox-env: py39, max-attempts: 3 }
|
|
||||||
- { python-version: "3.9", tox-env: py39-no-ext, max-attempts: 3 }
|
|
||||||
- { python-version: "3.10", tox-env: py310, max-attempts: 3 }
|
|
||||||
- { python-version: "3.10", tox-env: py310-no-ext, max-attempts: 3 }
|
|
||||||
- { python-version: "3.11", tox-env: py311, max-attempts: 3 }
|
|
||||||
- { python-version: "3.11", tox-env: py311-no-ext, max-attempts: 3 }
|
|
||||||
- { python-version: "3.8", tox-env: py38-no-ext, platform: windows-latest, ignore-errors: true }
|
|
||||||
- { python-version: "3.9", tox-env: py39-no-ext, platform: windows-latest, ignore-errors: true }
|
|
||||||
- { python-version: "3.10", tox-env: py310-no-ext, platform: windows-latest, ignore-errors: true }
|
|
||||||
- { python-version: "3.11", tox-env: py310-no-ext, platform: windows-latest, ignore-errors: true }
|
|
||||||
steps:
|
|
||||||
- name: Run tests
|
|
||||||
uses: sanic-org/simple-tox-action@v1
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.config.python-version }}
|
|
||||||
tox-env: ${{ matrix.config.tox-env }}
|
|
||||||
max-attempts: ${{ matrix.config.max-attempts || 1 }}
|
|
||||||
ignore-errors: ${{ matrix.config.ignore-errors || false }}
|
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -6,21 +6,10 @@
|
|||||||
.coverage
|
.coverage
|
||||||
.coverage.*
|
.coverage.*
|
||||||
coverage
|
coverage
|
||||||
coverage.xml
|
|
||||||
.tox
|
.tox
|
||||||
settings.py
|
settings.py
|
||||||
.idea/*
|
.idea/*
|
||||||
.cache/*
|
.cache/*
|
||||||
.mypy_cache/
|
|
||||||
.python-version
|
.python-version
|
||||||
docs/_build/
|
docs/_build/
|
||||||
docs/_api/
|
docs/_api/
|
||||||
build/*
|
|
||||||
.DS_Store
|
|
||||||
dist/*
|
|
||||||
pip-wheel-metadata/
|
|
||||||
.pytest_cache/*
|
|
||||||
.venv/*
|
|
||||||
venv/*
|
|
||||||
.vscode/*
|
|
||||||
guide/node_modules/
|
|
||||||
|
14
.travis.yml
Normal file
14
.travis.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
sudo: false
|
||||||
|
language: python
|
||||||
|
python:
|
||||||
|
- '3.5'
|
||||||
|
- '3.6'
|
||||||
|
install: pip install tox-travis
|
||||||
|
script: tox
|
||||||
|
deploy:
|
||||||
|
provider: pypi
|
||||||
|
user: channelcat
|
||||||
|
password:
|
||||||
|
secure: jH4+Di2/qcBwWVhI5/3NYd/JuDDgf5/cF85h+oQnAjgwP6me3th9RS0PHL2gjKJrmyRgwrW7a3eSAityo5sQSlBloQCNrtCE30rkDiwtgoIxDW72NR/nE8nUkS9Utgy87eS+3B4NrO7ag4GTqO5ET8SQ4/MCiQwyUQATLXj2s2eTpQvqJeZG6YgoeFAOYvlR580yznXoOwldWlkiymJiWSdR/01lthtWCi40sYC/QoU7psODJ/tPcsqgQtQKyUVsci7mKvp3Y8ImkoO/POM01jYNsS9qLh5pKTNCEYxtyzC77whenCNHn7WReVidd56g1ADosbNo4yY/1D3VAvwjUnkQ0SzdBQfT7IIzccEuC0j1NXKPN97OX0a6XzyUMYJ1XiU3juTJOPxdYBPbsDM3imQiwrOh1faIf0HCgNTN+Lxe5l8obCH7kffNcVUhs2zI0+2t4MS5tjb/OVuYD/TFn+bM33DqzLctTOK/pGn6xefzZcdzb191LPo99Lof+4fo6jNUpb0UmcBu5ZJzxh0lGe8FPIK3UAG/hrYDDgjx8s8RtUJjcEUQz0659XffYx7DLlgHO7cWyfjrHD3yrLzDbYr5mAS4FR+4D917V7UL+on4SsKHN00UuMGPguqSYo/xYyPLnJU5XK0du4MIpsNMB8TtrJOIewOOfD32+AisPQ8=
|
||||||
|
on:
|
||||||
|
tags: true
|
22
CHANGELOG.md
Normal file
22
CHANGELOG.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Version 0.1
|
||||||
|
-----------
|
||||||
|
- 0.1.7
|
||||||
|
- Reversed static url and directory arguments to meet spec
|
||||||
|
- 0.1.6
|
||||||
|
- Static files
|
||||||
|
- Lazy Cookie Loading
|
||||||
|
- 0.1.5
|
||||||
|
- Cookies
|
||||||
|
- Blueprint listeners and ordering
|
||||||
|
- Faster Router
|
||||||
|
- Fix: Incomplete file reads on medium+ sized post requests
|
||||||
|
- Breaking: after_start and before_stop now pass sanic as their first argument
|
||||||
|
- 0.1.4
|
||||||
|
- Multiprocessing
|
||||||
|
- 0.1.3
|
||||||
|
- Blueprint support
|
||||||
|
- Faster Response processing
|
||||||
|
- 0.1.1 - 0.1.2
|
||||||
|
- Struggling to update pypi via CI
|
||||||
|
- 0.1.0
|
||||||
|
- Released to public
|
1159
CHANGELOG.rst
1159
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to making participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
|
||||||
nationality, personal appearance, race, religion, or sexual identity and
|
|
||||||
orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
behavior and are expected to take appropriate and fair corrective action in
|
|
||||||
response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community. Examples of
|
|
||||||
representing a project or community include using an official project e-mail
|
|
||||||
address, posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event. Representation of a project may be
|
|
||||||
further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the project team at adam@sanicframework.org. All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at [http://contributor-covenant.org/version/1/4][version]
|
|
||||||
|
|
||||||
[homepage]: http://contributor-covenant.org
|
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
240
CONTRIBUTING.rst
240
CONTRIBUTING.rst
@ -1,240 +0,0 @@
|
|||||||
Thank you for your interest! Sanic is always looking for contributors. If you
|
|
||||||
don't feel comfortable contributing code, adding docstrings to the source files,
|
|
||||||
or helping with the `Sanic User Guide <https://github.com/sanic-org/sanic-guide>`_
|
|
||||||
by providing documentation or implementation examples would be appreciated!
|
|
||||||
|
|
||||||
We are committed to providing a friendly, safe and welcoming environment for all,
|
|
||||||
regardless of gender, sexual orientation, disability, ethnicity, religion,
|
|
||||||
or similar personal characteristic.
|
|
||||||
Our `code of conduct <https://github.com/sanic-org/sanic/blob/master/CONDUCT.md>`_ sets the standards for behavior.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
To develop on Sanic (and mainly to just run the tests) it is highly recommend to
|
|
||||||
install from sources.
|
|
||||||
|
|
||||||
So assume you have already cloned the repo and are in the working directory with
|
|
||||||
a virtual environment already set up, then run:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
pip install -e ".[dev]"
|
|
||||||
|
|
||||||
Dependency Changes
|
|
||||||
------------------
|
|
||||||
|
|
||||||
``Sanic`` doesn't use ``requirements*.txt`` files to manage any kind of dependencies related to it in order to simplify the
|
|
||||||
effort required in managing the dependencies. Please make sure you have read and understood the following section of
|
|
||||||
the document that explains the way ``sanic`` manages dependencies inside the ``setup.py`` file.
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - Dependency Type
|
|
||||||
- Usage
|
|
||||||
- Installation
|
|
||||||
* - requirements
|
|
||||||
- Bare minimum dependencies required for sanic to function
|
|
||||||
- ``pip3 install -e .``
|
|
||||||
* - tests_require / extras_require['test']
|
|
||||||
- Dependencies required to run the Unit Tests for ``sanic``
|
|
||||||
- ``pip3 install -e '.[test]'``
|
|
||||||
* - extras_require['dev']
|
|
||||||
- Additional Development requirements to add contributing
|
|
||||||
- ``pip3 install -e '.[dev]'``
|
|
||||||
* - extras_require['docs']
|
|
||||||
- Dependencies required to enable building and enhancing sanic documentation
|
|
||||||
- ``pip3 install -e '.[docs]'``
|
|
||||||
|
|
||||||
|
|
||||||
Running all tests
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
To run the tests for Sanic it is recommended to use tox like so:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
tox
|
|
||||||
|
|
||||||
See it's that simple!
|
|
||||||
|
|
||||||
``tox.ini`` contains different environments. Running ``tox`` without any arguments will
|
|
||||||
run all unittests, perform lint and other checks.
|
|
||||||
|
|
||||||
Run unittests
|
|
||||||
-------------
|
|
||||||
|
|
||||||
``tox`` environment -> ``[testenv]``
|
|
||||||
|
|
||||||
To execute only unittests, run ``tox`` with environment like so:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
tox -e py37 -v -- tests/test_config.py
|
|
||||||
# or
|
|
||||||
tox -e py310 -v -- tests/test_config.py
|
|
||||||
|
|
||||||
Run lint checks
|
|
||||||
---------------
|
|
||||||
|
|
||||||
``tox`` environment -> ``[testenv:lint]``
|
|
||||||
|
|
||||||
Permform ``flake8``\ , ``black`` and ``isort`` checks.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
tox -e lint
|
|
||||||
|
|
||||||
Run type annotation checks
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
``tox`` environment -> ``[testenv:type-checking]``
|
|
||||||
|
|
||||||
Permform ``mypy`` checks.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
tox -e type-checking
|
|
||||||
|
|
||||||
Run other checks
|
|
||||||
----------------
|
|
||||||
|
|
||||||
``tox`` environment -> ``[testenv:check]``
|
|
||||||
|
|
||||||
Perform other checks.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
tox -e check
|
|
||||||
|
|
||||||
Run Static Analysis
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
``tox`` environment -> ``[testenv:security]``
|
|
||||||
|
|
||||||
Perform static analysis security scan
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
tox -e security
|
|
||||||
|
|
||||||
Run Documentation sanity check
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
``tox`` environment -> ``[testenv:docs]``
|
|
||||||
|
|
||||||
Perform sanity check on documentation
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
tox -e docs
|
|
||||||
|
|
||||||
|
|
||||||
Code Style
|
|
||||||
----------
|
|
||||||
|
|
||||||
To maintain the code consistency, Sanic uses following tools.
|
|
||||||
|
|
||||||
|
|
||||||
#. `isort <https://github.com/timothycrosley/isort>`_
|
|
||||||
#. `black <https://github.com/python/black>`_
|
|
||||||
#. `flake8 <https://github.com/PyCQA/flake8>`_
|
|
||||||
#. `slotscheck <https://github.com/ariebovenberg/slotscheck>`_
|
|
||||||
|
|
||||||
isort
|
|
||||||
*****
|
|
||||||
|
|
||||||
``isort`` sorts Python imports. It divides imports into three
|
|
||||||
categories sorted each in alphabetical order.
|
|
||||||
|
|
||||||
|
|
||||||
#. built-in
|
|
||||||
#. third-party
|
|
||||||
#. project-specific
|
|
||||||
|
|
||||||
black
|
|
||||||
*****
|
|
||||||
|
|
||||||
``black`` is a Python code formatter.
|
|
||||||
|
|
||||||
flake8
|
|
||||||
******
|
|
||||||
|
|
||||||
``flake8`` is a Python style guide that wraps following tools into one.
|
|
||||||
|
|
||||||
|
|
||||||
#. PyFlakes
|
|
||||||
#. pycodestyle
|
|
||||||
#. Ned Batchelder's McCabe script
|
|
||||||
|
|
||||||
slotscheck
|
|
||||||
**********
|
|
||||||
|
|
||||||
``slotscheck`` ensures that there are no problems with ``__slots__``
|
|
||||||
(e.g. overlaps, or missing slots in base classes).
|
|
||||||
|
|
||||||
``isort``\ , ``black``\ , ``flake8`` and ``slotscheck`` checks are performed during ``tox`` lint checks.
|
|
||||||
|
|
||||||
The **easiest** way to make your code conform is to run the following before committing.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
make pretty
|
|
||||||
|
|
||||||
Refer `tox <https://tox.readthedocs.io/en/latest/index.html>`_ documentation for more details.
|
|
||||||
|
|
||||||
Pull requests
|
|
||||||
-------------
|
|
||||||
|
|
||||||
So the pull request approval rules are pretty simple:
|
|
||||||
|
|
||||||
#. All pull requests must pass unit tests.
|
|
||||||
#. All pull requests must be reviewed and approved by at least one current member of the Core Developer team.
|
|
||||||
#. All pull requests must pass flake8 checks.
|
|
||||||
#. All pull requests must match ``isort`` and ``black`` requirements.
|
|
||||||
#. All pull requests must be **PROPERLY** type annotated, unless exemption is given.
|
|
||||||
#. All pull requests must be consistent with the existing code.
|
|
||||||
#. If you decide to remove/change anything from any common interface a deprecation message should accompany it in accordance with our `deprecation policy <https://sanicframework.org/en/guide/project/policies.html#deprecation>`_.
|
|
||||||
#. If you implement a new feature you should have at least one unit test to accompany it.
|
|
||||||
#. An example must be one of the following:
|
|
||||||
|
|
||||||
* Example of how to use Sanic
|
|
||||||
* Example of how to use Sanic extensions
|
|
||||||
* Example of how to use Sanic and asynchronous library
|
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Sanic's API documentation is built using `sphinx <http://www.sphinx-doc.org/en/1.5.1/>`_ with module references
|
|
||||||
automatically generated using ``sphinx-apidoc``.
|
|
||||||
|
|
||||||
The User Guide is in the `sanic-guide <https://github.com/sanic-org/sanic-guide>`_ repository.
|
|
||||||
|
|
||||||
To generate the documentation from scratch:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
sphinx-apidoc -fo docs/_api/ sanic
|
|
||||||
sphinx-build -b html docs docs/_build
|
|
||||||
|
|
||||||
# There is a simple make command provided to ease the work required in generating
|
|
||||||
# the documentation
|
|
||||||
make docs
|
|
||||||
|
|
||||||
The HTML documentation will be created in the ``docs/_build`` folder.
|
|
||||||
|
|
||||||
You can run the following to have a live development server with the API documents
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
make docs-serve
|
|
||||||
|
|
||||||
Refer to the User Guide repo for documentation on how to contribute there.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
One of the main goals of Sanic is speed. Code that lowers the performance of
|
|
||||||
Sanic without significant gains in usability, security, or features may not be
|
|
||||||
merged. Please don't let this intimidate you! If you have any concerns about an
|
|
||||||
idea, open an issue for discussion and help.
|
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016-present Sanic Community
|
Copyright (c) [year] [fullname]
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
15
MANIFEST.in
15
MANIFEST.in
@ -1,15 +0,0 @@
|
|||||||
# Non Code related contents
|
|
||||||
include LICENSE
|
|
||||||
include README.rst
|
|
||||||
include pyproject.toml
|
|
||||||
|
|
||||||
# Setup
|
|
||||||
include setup.py
|
|
||||||
include Makefile
|
|
||||||
|
|
||||||
# Tests
|
|
||||||
include .coveragerc
|
|
||||||
graft tests
|
|
||||||
|
|
||||||
global-exclude __pycache__
|
|
||||||
global-exclude *.py[co]
|
|
105
Makefile
105
Makefile
@ -1,105 +0,0 @@
|
|||||||
.PHONY: help test test-coverage install docker-test black fix-import beautify
|
|
||||||
|
|
||||||
.DEFAULT: help
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
|
||||||
@echo "test"
|
|
||||||
@echo " Run Sanic Unit Tests"
|
|
||||||
@echo "test-coverage"
|
|
||||||
@echo " Run Sanic Unit Tests with Coverage"
|
|
||||||
@echo "install"
|
|
||||||
@echo " Install Sanic"
|
|
||||||
@echo "docker-test"
|
|
||||||
@echo " Run Sanic Unit Tests using Docker"
|
|
||||||
@echo "black"
|
|
||||||
@echo " Analyze and fix linting issues using Black"
|
|
||||||
@echo "fix-import"
|
|
||||||
@echo " Analyze and fix import order using isort"
|
|
||||||
@echo "beautify [sort_imports=1] [include_tests=1]"
|
|
||||||
@echo " Analyze and fix linting issue using black and optionally fix import sort using isort"
|
|
||||||
@echo ""
|
|
||||||
@echo "docs"
|
|
||||||
@echo " Generate Sanic documentation"
|
|
||||||
@echo ""
|
|
||||||
@echo "clean-docs"
|
|
||||||
@echo " Clean Sanic documentation"
|
|
||||||
@echo ""
|
|
||||||
@echo "docs-test"
|
|
||||||
@echo " Test Sanic Documentation for errors"
|
|
||||||
@echo ""
|
|
||||||
@echo "changelog"
|
|
||||||
@echo " Generate changelog for Sanic to prepare for new release"
|
|
||||||
@echo ""
|
|
||||||
@echo "release"
|
|
||||||
@echo " Prepare Sanic for a new changes by version bump and changelog"
|
|
||||||
@echo ""
|
|
||||||
|
|
||||||
|
|
||||||
clean:
|
|
||||||
find . ! -path "./.eggs/*" -name "*.pyc" -exec rm {} \;
|
|
||||||
find . ! -path "./.eggs/*" -name "*.pyo" -exec rm {} \;
|
|
||||||
find . ! -path "./.eggs/*" -name ".coverage" -exec rm {} \;
|
|
||||||
rm -rf build/* > /dev/null 2>&1
|
|
||||||
rm -rf dist/* > /dev/null 2>&1
|
|
||||||
|
|
||||||
test: clean
|
|
||||||
python setup.py test
|
|
||||||
|
|
||||||
test-coverage: clean
|
|
||||||
python setup.py test --pytest-args="--cov sanic --cov-report term --cov-append "
|
|
||||||
|
|
||||||
view-coverage:
|
|
||||||
sanic ./coverage --simple
|
|
||||||
|
|
||||||
install:
|
|
||||||
python setup.py install
|
|
||||||
|
|
||||||
docker-test: clean
|
|
||||||
docker build -t sanic/test-image -f docker/Dockerfile .
|
|
||||||
docker run -t sanic/test-image tox
|
|
||||||
|
|
||||||
beautify: black
|
|
||||||
ifdef sort_imports
|
|
||||||
ifdef include_tests
|
|
||||||
$(warning It is suggested that you do not run sort import on tests)
|
|
||||||
isort -rc sanic tests
|
|
||||||
else
|
|
||||||
$(info Sorting Imports)
|
|
||||||
isort -rc sanic tests
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
black:
|
|
||||||
black sanic tests
|
|
||||||
|
|
||||||
isort:
|
|
||||||
isort sanic tests
|
|
||||||
|
|
||||||
pretty: black isort
|
|
||||||
|
|
||||||
docs-clean:
|
|
||||||
cd docs && make clean
|
|
||||||
|
|
||||||
docs: docs-clean
|
|
||||||
cd docs && make html
|
|
||||||
|
|
||||||
docs-test: docs-clean
|
|
||||||
cd docs && make dummy
|
|
||||||
|
|
||||||
docs-serve:
|
|
||||||
sphinx-autobuild docs docs/_build/html --port 9999 --watch ./
|
|
||||||
|
|
||||||
changelog:
|
|
||||||
python scripts/changelog.py
|
|
||||||
|
|
||||||
guide-serve:
|
|
||||||
cd guide && sanic server:app -r -R ./content -R ./style
|
|
||||||
|
|
||||||
release:
|
|
||||||
ifdef version
|
|
||||||
python scripts/release.py --release-version ${version} --generate-changelog
|
|
||||||
else
|
|
||||||
python scripts/release.py --generate-changelog
|
|
||||||
endif
|
|
||||||
|
|
218
README.rst
218
README.rst
@ -1,105 +1,41 @@
|
|||||||
.. image:: https://raw.githubusercontent.com/sanic-org/sanic-assets/master/png/sanic-framework-logo-400x97.png
|
Sanic
|
||||||
:alt: Sanic | Build fast. Run fast.
|
=================================
|
||||||
|
|
||||||
Sanic | Build fast. Run fast.
|
|Join the chat at https://gitter.im/sanic-python/Lobby| |Build Status| |PyPI| |PyPI version|
|
||||||
=============================
|
|
||||||
|
|
||||||
.. start-badges
|
Sanic is a Flask-like Python 3.5+ web server that's written to go fast. It's based on the work done by the amazing folks at magicstack, and was inspired by `this article <https://magic.io/blog/uvloop-blazing-fast-python-networking/>`_.
|
||||||
|
|
||||||
.. list-table::
|
On top of being Flask-like, Sanic supports async request handlers. This means you can use the new shiny async/await syntax from Python 3.5, making your code non-blocking and speedy.
|
||||||
:widths: 15 85
|
|
||||||
:stub-columns: 1
|
|
||||||
|
|
||||||
* - Build
|
Sanic is developed `on GitHub <https://github.com/channelcat/sanic/>`_. Contributions are welcome!
|
||||||
- | |Tests|
|
|
||||||
* - Docs
|
|
||||||
- | |UserGuide| |Documentation|
|
|
||||||
* - Package
|
|
||||||
- | |PyPI| |PyPI version| |Wheel| |Supported implementations| |Code style black|
|
|
||||||
* - Support
|
|
||||||
- | |Forums| |Discord| |Awesome|
|
|
||||||
* - Stats
|
|
||||||
- | |Monthly Downloads| |Weekly Downloads| |Conda downloads|
|
|
||||||
|
|
||||||
.. |UserGuide| image:: https://img.shields.io/badge/user%20guide-sanic-ff0068
|
Benchmarks
|
||||||
:target: https://sanicframework.org/
|
----------
|
||||||
.. |Forums| image:: https://img.shields.io/badge/forums-community-ff0068.svg
|
|
||||||
:target: https://community.sanicframework.org/
|
|
||||||
.. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord
|
|
||||||
:target: https://discord.gg/FARQzAEMAA
|
|
||||||
.. |Tests| image:: https://github.com/sanic-org/sanic/actions/workflows/tests.yml/badge.svg?branch=main
|
|
||||||
:target: https://github.com/sanic-org/sanic/actions/workflows/tests.yml
|
|
||||||
.. |Documentation| image:: https://readthedocs.org/projects/sanic/badge/?version=latest
|
|
||||||
:target: http://sanic.readthedocs.io/en/latest/?badge=latest
|
|
||||||
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
|
|
||||||
:target: https://pypi.python.org/pypi/sanic/
|
|
||||||
.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic.svg
|
|
||||||
:target: https://pypi.python.org/pypi/sanic/
|
|
||||||
.. |Code style black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
||||||
:target: https://github.com/ambv/black
|
|
||||||
.. |Wheel| image:: https://img.shields.io/pypi/wheel/sanic.svg
|
|
||||||
:alt: PyPI Wheel
|
|
||||||
:target: https://pypi.python.org/pypi/sanic
|
|
||||||
.. |Supported implementations| image:: https://img.shields.io/pypi/implementation/sanic.svg
|
|
||||||
:alt: Supported implementations
|
|
||||||
:target: https://pypi.python.org/pypi/sanic
|
|
||||||
.. |Awesome| image:: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg
|
|
||||||
:alt: Awesome Sanic List
|
|
||||||
:target: https://github.com/mekicha/awesome-sanic
|
|
||||||
.. |Monthly Downloads| image:: https://img.shields.io/pypi/dm/sanic.svg
|
|
||||||
:alt: Downloads
|
|
||||||
:target: https://pepy.tech/project/sanic
|
|
||||||
.. |Weekly Downloads| image:: https://img.shields.io/pypi/dw/sanic.svg
|
|
||||||
:alt: Downloads
|
|
||||||
:target: https://pepy.tech/project/sanic
|
|
||||||
.. |Conda downloads| image:: https://img.shields.io/conda/dn/conda-forge/sanic.svg
|
|
||||||
:alt: Downloads
|
|
||||||
:target: https://anaconda.org/conda-forge/sanic
|
|
||||||
.. |Linode| image:: https://www.linode.com/wp-content/uploads/2021/01/Linode-Logo-Black.svg
|
|
||||||
:alt: Linode
|
|
||||||
:target: https://www.linode.com
|
|
||||||
:width: 200px
|
|
||||||
|
|
||||||
.. end-badges
|
All tests were run on an AWS medium instance running ubuntu, using 1
|
||||||
|
process. Each script delivered a small JSON response and was tested with
|
||||||
Sanic is a **Python 3.8+** web server and web framework that's written to go fast. It allows the usage of the ``async/await`` syntax added in Python 3.5, which makes your code non-blocking and speedy.
|
wrk using 100 connections. Pypy was tested for Falcon and Flask but did
|
||||||
|
not speed up requests.
|
||||||
Sanic is also ASGI compliant, so you can deploy it with an `alternative ASGI webserver <https://sanicframework.org/en/guide/deployment/running.html#asgi>`_.
|
|
||||||
|
|
||||||
`Source code on GitHub <https://github.com/sanic-org/sanic/>`_ | `Help and discussion board <https://community.sanicframework.org/>`_ | `User Guide <https://sanicframework.org>`_ | `Chat on Discord <https://discord.gg/FARQzAEMAA>`_
|
|
||||||
|
|
||||||
The project is maintained by the community, for the community. **Contributions are welcome!**
|
|
||||||
|
|
||||||
The goal of the project is to provide a simple way to get up and running a highly performant HTTP server that is easy to build, to expand, and ultimately to scale.
|
|
||||||
|
|
||||||
Sponsor
|
|
||||||
-------
|
|
||||||
|
|
||||||
Check out `open collective <https://opencollective.com/sanic-org>`_ to learn more about helping to fund Sanic.
|
|
||||||
|
|
||||||
Thanks to `Linode <https://www.linode.com>`_ for their contribution towards the development and community of Sanic.
|
|
||||||
|
|
||||||
|Linode|
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
``pip3 install sanic``
|
|
||||||
|
|
||||||
Sanic makes use of ``uvloop`` and ``ujson`` to help with performance. If you do not want to use those packages, simply add an environmental variable ``SANIC_NO_UVLOOP=true`` or ``SANIC_NO_UJSON=true`` at install time.
|
|
||||||
|
|
||||||
.. code:: shell
|
|
||||||
|
|
||||||
$ export SANIC_NO_UVLOOP=true
|
|
||||||
$ export SANIC_NO_UJSON=true
|
|
||||||
$ pip3 install --no-binary :all: sanic
|
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
If you are running on a clean install of Fedora 28 or above, please make sure you have the ``redhat-rpm-config`` package installed in case if you want to
|
|
||||||
use ``sanic`` with ``ujson`` dependency.
|
|
||||||
|
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
| Server | Implementation | Requests/sec | Avg Latency |
|
||||||
|
+===========+=======================+================+===============+
|
||||||
|
| Sanic | Python 3.5 + uvloop | 33,342 | 2.96ms |
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
| Wheezy | gunicorn + meinheld | 20,244 | 4.94ms |
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
| Falcon | gunicorn + meinheld | 18,972 | 5.27ms |
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
| Bottle | gunicorn + meinheld | 13,596 | 7.36ms |
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
| Flask | gunicorn + meinheld | 4,988 | 20.08ms |
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
| Kyoukai | Python 3.5 + uvloop | 3,889 | 27.44ms |
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
| Aiohttp | Python 3.5 + uvloop | 2,979 | 33.42ms |
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
| Tornado | Python 3.5 | 2,138 | 46.66ms |
|
||||||
|
+-----------+-----------------------+----------------+---------------+
|
||||||
|
|
||||||
Hello World Example
|
Hello World Example
|
||||||
-------------------
|
-------------------
|
||||||
@ -109,55 +45,83 @@ Hello World Example
|
|||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.response import json
|
from sanic.response import json
|
||||||
|
|
||||||
app = Sanic("my-hello-world-app")
|
|
||||||
|
|
||||||
@app.route('/')
|
app = Sanic()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
return json({'hello': 'world'})
|
return json({"hello": "world"})
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run()
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
|
||||||
Sanic can now be easily run using ``sanic hello.app``.
|
SSL Example
|
||||||
|
-----------
|
||||||
|
|
||||||
.. code::
|
Optionally pass in an SSLContext:
|
||||||
|
|
||||||
[2018-12-30 11:37:41 +0200] [13564] [INFO] Goin' Fast @ http://127.0.0.1:8000
|
.. code:: python
|
||||||
[2018-12-30 11:37:41 +0200] [13564] [INFO] Starting worker [13564]
|
|
||||||
|
|
||||||
And, we can verify it is working: ``curl localhost:8000 -i``
|
import ssl
|
||||||
|
certificate = "/path/to/certificate"
|
||||||
|
keyfile = "/path/to/keyfile"
|
||||||
|
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
|
||||||
|
context.load_cert_chain(certificate, keyfile=keyfile)
|
||||||
|
|
||||||
.. code::
|
app.run(host="0.0.0.0", port=8443, ssl=context)
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
Installation
|
||||||
Connection: keep-alive
|
------------
|
||||||
Keep-Alive: 5
|
|
||||||
Content-Length: 17
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"hello":"world"}
|
- ``python -m pip install sanic``
|
||||||
|
|
||||||
**Now, let's go build something fast!**
|
|
||||||
|
|
||||||
Minimum Python version is 3.8. If you need Python 3.7 support, please use v22.12LTS.
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
`User Guide <https://sanic.dev>`__ and `API Documentation <http://sanic.readthedocs.io/>`__.
|
Documentation can be found in the ``docs`` directory.
|
||||||
|
|
||||||
Changelog
|
.. |Join the chat at https://gitter.im/sanic-python/Lobby| image:: https://badges.gitter.im/sanic-python/Lobby.svg
|
||||||
---------
|
:target: https://gitter.im/sanic-python/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||||
|
.. |Build Status| image:: https://travis-ci.org/channelcat/sanic.svg?branch=master
|
||||||
|
:target: https://travis-ci.org/channelcat/sanic
|
||||||
|
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
|
||||||
|
:target: https://pypi.python.org/pypi/sanic/
|
||||||
|
.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic.svg
|
||||||
|
:target: https://pypi.python.org/pypi/sanic/
|
||||||
|
|
||||||
`Release Changelogs <https://sanic.readthedocs.io/en/stable/sanic/changelog.html>`__.
|
TODO
|
||||||
|
----
|
||||||
|
* Streamed file processing
|
||||||
|
* File output
|
||||||
|
* Examples of integrations with 3rd-party modules
|
||||||
|
* RESTful router
|
||||||
|
|
||||||
|
Limitations
|
||||||
|
-----------
|
||||||
|
* No wheels for uvloop and httptools on Windows :(
|
||||||
|
|
||||||
Questions and Discussion
|
Final Thoughts
|
||||||
------------------------
|
--------------
|
||||||
|
|
||||||
`Ask a question or join the conversation <https://community.sanicframework.org/>`__.
|
::
|
||||||
|
|
||||||
Contribution
|
▄▄▄▄▄
|
||||||
------------
|
▀▀▀██████▄▄▄ _______________
|
||||||
|
▄▄▄▄▄ █████████▄ / \
|
||||||
We are always happy to have new contributions. We have `marked issues good for anyone looking to get started <https://github.com/sanic-org/sanic/issues?q=is%3Aopen+is%3Aissue+label%3Abeginner>`_, and welcome `questions on the forums <https://community.sanicframework.org/>`_. Please take a look at our `Contribution guidelines <https://github.com/sanic-org/sanic/blob/master/CONTRIBUTING.rst>`_.
|
▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
|
||||||
|
▀▀█████▄▄ ▀██████▄██ | _________________/
|
||||||
|
▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
|
||||||
|
▀▀▀▄ ▀▀███ ▀ ▄▄
|
||||||
|
▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
|
||||||
|
██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
|
||||||
|
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
|
||||||
|
▌ ▐▀████▐███▒▒▒▒▒▐██▌
|
||||||
|
▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
|
||||||
|
▀▀█████████▀
|
||||||
|
▄▄██▀██████▀█
|
||||||
|
▄██▀ ▀▀▀ █
|
||||||
|
▄█ ▐▌
|
||||||
|
▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
|
||||||
|
▌ ▐ ▀▀▄▄▄▀
|
||||||
|
▀▀▄▄▀
|
||||||
|
45
SECURITY.md
45
SECURITY.md
@ -1,45 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Sanic releases long term support release once a year in December. LTS releases receive bug and security updates for **24 months**. Interim releases throughout the year occur every three months, and are supported until the subsequent interim release.
|
|
||||||
|
|
||||||
|
|
||||||
| Version | LTS | Supported |
|
|
||||||
| ------- | ------------- | ----------------------- |
|
|
||||||
| 22.12 | until 2024-12 | :white_check_mark: |
|
|
||||||
| 22.9 | | :x: |
|
|
||||||
| 22.6 | | :x: |
|
|
||||||
| 22.3 | | :x: |
|
|
||||||
| 21.12 | until 2023-12 | :ballot_box_with_check: |
|
|
||||||
| 21.9 | | :x: |
|
|
||||||
| 21.6 | | :x: |
|
|
||||||
| 21.3 | | :x: |
|
|
||||||
| 20.12 | | :x: |
|
|
||||||
| 20.9 | | :x: |
|
|
||||||
| 20.6 | | :x: |
|
|
||||||
| 20.3 | | :x: |
|
|
||||||
| 19.12 | | :x: |
|
|
||||||
| 19.9 | | :x: |
|
|
||||||
| 19.6 | | :x: |
|
|
||||||
| 19.3 | | :x: |
|
|
||||||
| 18.12 | | :x: |
|
|
||||||
| 0.8.3 | | :x: |
|
|
||||||
| 0.7.0 | | :x: |
|
|
||||||
| 0.6.0 | | :x: |
|
|
||||||
| 0.5.4 | | :x: |
|
|
||||||
| 0.4.1 | | :x: |
|
|
||||||
| 0.3.1 | | :x: |
|
|
||||||
| 0.2.0 | | :x: |
|
|
||||||
| 0.1.9 | | :x: |
|
|
||||||
|
|
||||||
:ballot_box_with_check: = security/bug fixes
|
|
||||||
:white_check_mark: = full support
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
If you discover a security vulnerability, we ask that you **do not** create an issue on GitHub. Instead, please [send a message to the core-devs](https://community.sanicframework.org/g/core-devs) on the community forums. Once logged in, you can send a message to the core-devs by clicking the message button.
|
|
||||||
|
|
||||||
Alternatively, you can send a private message to Adam Hopkins on Discord. Find him on the [Sanic discord server](https://discord.gg/FARQzAEMAA).
|
|
||||||
|
|
||||||
This will help to not publicize the issue until the team can address it and resolve it.
|
|
2
changelogs/.gitignore
vendored
2
changelogs/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
# Except this file
|
|
||||||
!.gitignore
|
|
@ -1 +0,0 @@
|
|||||||
Remove [version] section.
|
|
@ -1,3 +0,0 @@
|
|||||||
Adds WEBSOCKET_PING_TIMEOUT and WEBSOCKET_PING_INTERVAL configuration values
|
|
||||||
|
|
||||||
Allows setting the ping_interval and ping_timeout arguments when initializing `WebSocketCommonProtocol`.
|
|
@ -1 +0,0 @@
|
|||||||
Adds py.typed file to expose type information to other packages.
|
|
28
codecov.yml
28
codecov.yml
@ -1,28 +0,0 @@
|
|||||||
coverage:
|
|
||||||
status:
|
|
||||||
patch:
|
|
||||||
default:
|
|
||||||
target: auto
|
|
||||||
threshold: 0.75
|
|
||||||
informational: true
|
|
||||||
project:
|
|
||||||
default:
|
|
||||||
target: auto
|
|
||||||
threshold: 0.5
|
|
||||||
precision: 3
|
|
||||||
codecov:
|
|
||||||
require_ci_to_pass: false
|
|
||||||
ignore:
|
|
||||||
- "sanic/__main__.py"
|
|
||||||
- "sanic/compat.py"
|
|
||||||
- "sanic/simple.py"
|
|
||||||
- "sanic/utils.py"
|
|
||||||
- "sanic/cli/"
|
|
||||||
- "sanic/pages/"
|
|
||||||
- ".github/"
|
|
||||||
- "changelogs/"
|
|
||||||
- "docker/"
|
|
||||||
- "docs/"
|
|
||||||
- "examples/"
|
|
||||||
- "scripts/"
|
|
||||||
- "tests/"
|
|
@ -1,13 +0,0 @@
|
|||||||
ARG BASE_IMAGE_ORG
|
|
||||||
ARG BASE_IMAGE_NAME
|
|
||||||
ARG BASE_IMAGE_TAG
|
|
||||||
|
|
||||||
FROM ${BASE_IMAGE_ORG}/${BASE_IMAGE_NAME}:${BASE_IMAGE_TAG}
|
|
||||||
|
|
||||||
RUN apk update
|
|
||||||
RUN update-ca-certificates
|
|
||||||
|
|
||||||
ARG SANIC_PYPI_VERSION
|
|
||||||
|
|
||||||
RUN pip install -U pip && pip install sanic==${SANIC_PYPI_VERSION}
|
|
||||||
RUN apk del build-base
|
|
@ -1,9 +0,0 @@
|
|||||||
ARG PYTHON_VERSION
|
|
||||||
|
|
||||||
FROM python:${PYTHON_VERSION}-alpine
|
|
||||||
RUN apk update
|
|
||||||
RUN apk add --no-cache --update build-base \
|
|
||||||
ca-certificates \
|
|
||||||
openssl
|
|
||||||
RUN update-ca-certificates
|
|
||||||
RUN rm -rf /var/cache/apk/*
|
|
225
docs/Makefile
225
docs/Makefile
@ -1,225 +0,0 @@
|
|||||||
# Makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
|
||||||
SPHINXOPTS =
|
|
||||||
SPHINXBUILD = sphinx-build
|
|
||||||
PAPER =
|
|
||||||
BUILDDIR = _build
|
|
||||||
|
|
||||||
# Internal variables.
|
|
||||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
|
||||||
PAPEROPT_letter = -D latex_paper_size=letter
|
|
||||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
|
||||||
# the i18n builder cannot share the environment and doctrees with the others
|
|
||||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
|
||||||
|
|
||||||
.PHONY: help
|
|
||||||
help:
|
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
|
||||||
@echo " html to make standalone HTML files"
|
|
||||||
@echo " dirhtml to make HTML files named index.html in directories"
|
|
||||||
@echo " singlehtml to make a single large HTML file"
|
|
||||||
@echo " pickle to make pickle files"
|
|
||||||
@echo " json to make JSON files"
|
|
||||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
|
||||||
@echo " qthelp to make HTML files and a qthelp project"
|
|
||||||
@echo " applehelp to make an Apple Help Book"
|
|
||||||
@echo " devhelp to make HTML files and a Devhelp project"
|
|
||||||
@echo " epub to make an epub"
|
|
||||||
@echo " epub3 to make an epub3"
|
|
||||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
|
||||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
|
||||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
|
||||||
@echo " text to make text files"
|
|
||||||
@echo " man to make manual pages"
|
|
||||||
@echo " texinfo to make Texinfo files"
|
|
||||||
@echo " info to make Texinfo files and run them through makeinfo"
|
|
||||||
@echo " gettext to make PO message catalogs"
|
|
||||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
|
||||||
@echo " xml to make Docutils-native XML files"
|
|
||||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
|
||||||
@echo " linkcheck to check all external links for integrity"
|
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
|
||||||
@echo " coverage to run coverage check of the documentation (if enabled)"
|
|
||||||
@echo " dummy to check syntax errors of document sources"
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
rm -rf $(BUILDDIR)/*
|
|
||||||
|
|
||||||
.PHONY: html
|
|
||||||
html:
|
|
||||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
|
||||||
|
|
||||||
.PHONY: dirhtml
|
|
||||||
dirhtml:
|
|
||||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
|
||||||
|
|
||||||
.PHONY: singlehtml
|
|
||||||
singlehtml:
|
|
||||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
|
||||||
|
|
||||||
.PHONY: pickle
|
|
||||||
pickle:
|
|
||||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the pickle files."
|
|
||||||
|
|
||||||
.PHONY: json
|
|
||||||
json:
|
|
||||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the JSON files."
|
|
||||||
|
|
||||||
.PHONY: htmlhelp
|
|
||||||
htmlhelp:
|
|
||||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
|
||||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
|
||||||
|
|
||||||
.PHONY: qthelp
|
|
||||||
qthelp:
|
|
||||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
|
||||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
|
||||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/aiographite.qhcp"
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/aiographite.qhc"
|
|
||||||
|
|
||||||
.PHONY: applehelp
|
|
||||||
applehelp:
|
|
||||||
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
|
||||||
@echo "N.B. You won't be able to view it unless you put it in" \
|
|
||||||
"~/Library/Documentation/Help or install it in your application" \
|
|
||||||
"bundle."
|
|
||||||
|
|
||||||
.PHONY: devhelp
|
|
||||||
devhelp:
|
|
||||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished."
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/aiographite"
|
|
||||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/aiographite"
|
|
||||||
@echo "# devhelp"
|
|
||||||
|
|
||||||
.PHONY: epub
|
|
||||||
epub:
|
|
||||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
|
||||||
|
|
||||||
.PHONY: epub3
|
|
||||||
epub3:
|
|
||||||
$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
|
|
||||||
|
|
||||||
.PHONY: latex
|
|
||||||
latex:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
|
||||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
|
||||||
"(use \`make latexpdf' here to do that automatically)."
|
|
||||||
|
|
||||||
.PHONY: latexpdf
|
|
||||||
latexpdf:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo "Running LaTeX files through pdflatex..."
|
|
||||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
|
||||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
|
||||||
|
|
||||||
.PHONY: latexpdfja
|
|
||||||
latexpdfja:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
|
||||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
|
||||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
|
||||||
|
|
||||||
.PHONY: text
|
|
||||||
text:
|
|
||||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
|
||||||
|
|
||||||
.PHONY: man
|
|
||||||
man:
|
|
||||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
|
||||||
|
|
||||||
.PHONY: texinfo
|
|
||||||
texinfo:
|
|
||||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
|
||||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
|
||||||
"(use \`make info' here to do that automatically)."
|
|
||||||
|
|
||||||
.PHONY: info
|
|
||||||
info:
|
|
||||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
|
||||||
@echo "Running Texinfo files through makeinfo..."
|
|
||||||
make -C $(BUILDDIR)/texinfo info
|
|
||||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
|
||||||
|
|
||||||
.PHONY: gettext
|
|
||||||
gettext:
|
|
||||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
|
||||||
|
|
||||||
.PHONY: changes
|
|
||||||
changes:
|
|
||||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
|
||||||
@echo
|
|
||||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
|
||||||
|
|
||||||
.PHONY: linkcheck
|
|
||||||
linkcheck:
|
|
||||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
|
||||||
@echo
|
|
||||||
@echo "Link check complete; look for any errors in the above output " \
|
|
||||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
|
||||||
|
|
||||||
.PHONY: doctest
|
|
||||||
doctest:
|
|
||||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
|
||||||
@echo "Testing of doctests in the sources finished, look at the " \
|
|
||||||
"results in $(BUILDDIR)/doctest/output.txt."
|
|
||||||
|
|
||||||
.PHONY: coverage
|
|
||||||
coverage:
|
|
||||||
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
|
||||||
@echo "Testing of coverage in the sources finished, look at the " \
|
|
||||||
"results in $(BUILDDIR)/coverage/python.txt."
|
|
||||||
|
|
||||||
.PHONY: xml
|
|
||||||
xml:
|
|
||||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
|
||||||
|
|
||||||
.PHONY: pseudoxml
|
|
||||||
pseudoxml:
|
|
||||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
|
||||||
|
|
||||||
.PHONY: dummy
|
|
||||||
dummy:
|
|
||||||
$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. Dummy builder generates no files."
|
|
0
docs/_static/.gitkeep
vendored
0
docs/_static/.gitkeep
vendored
13
docs/_static/custom.css
vendored
13
docs/_static/custom.css
vendored
@ -1,13 +0,0 @@
|
|||||||
.wy-side-nav-search,
|
|
||||||
.wy-nav-top {
|
|
||||||
background: #444444;
|
|
||||||
}
|
|
||||||
|
|
||||||
#changelog section {
|
|
||||||
padding-left: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#changelog section h2,
|
|
||||||
#changelog section h3 {
|
|
||||||
margin-left: -3rem;
|
|
||||||
}
|
|
BIN
docs/_static/sanic-framework-logo-white-400x97.png
vendored
BIN
docs/_static/sanic-framework-logo-white-400x97.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
164
docs/blueprints.md
Normal file
164
docs/blueprints.md
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
# Blueprints
|
||||||
|
|
||||||
|
Blueprints are objects that can be used for sub-routing within an application.
|
||||||
|
Instead of adding routes to the application instance, blueprints define similar
|
||||||
|
methods for adding routes, which are then registered with the application in a
|
||||||
|
flexible and pluggable manner.
|
||||||
|
|
||||||
|
Blueprints are especially useful for larger applications, where your
|
||||||
|
application logic can be broken down into several groups or areas of
|
||||||
|
responsibility.
|
||||||
|
|
||||||
|
## My First Blueprint
|
||||||
|
|
||||||
|
The following shows a very simple blueprint that registers a handler-function at
|
||||||
|
the root `/` of your application.
|
||||||
|
|
||||||
|
Suppose you save this file as `my_blueprint.py`, which can be imported into your
|
||||||
|
main application later.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import json
|
||||||
|
from sanic import Blueprint
|
||||||
|
|
||||||
|
bp = Blueprint('my_blueprint')
|
||||||
|
|
||||||
|
@bp.route('/')
|
||||||
|
async def bp_root(request):
|
||||||
|
return json({'my': 'blueprint'})
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Registering blueprints
|
||||||
|
|
||||||
|
Blueprints must be registered with the application.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic import Sanic
|
||||||
|
from my_blueprint import bp
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
app.blueprint(bp)
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=8000, debug=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
This will add the blueprint to the application and register any routes defined
|
||||||
|
by that blueprint. In this example, the registered routes in the `app.router`
|
||||||
|
will look like:
|
||||||
|
|
||||||
|
```python
|
||||||
|
[Route(handler=<function bp_root at 0x7f908382f9d8>, methods=None, pattern=re.compile('^/$'), parameters=[])]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using blueprints
|
||||||
|
|
||||||
|
Blueprints have much the same functionality as an application instance.
|
||||||
|
|
||||||
|
### Middleware
|
||||||
|
|
||||||
|
Using blueprints allows you to also register middleware globally.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@bp.middleware
|
||||||
|
async def halt_request(request):
|
||||||
|
print("I am a spy")
|
||||||
|
|
||||||
|
@bp.middleware('request')
|
||||||
|
async def halt_request(request):
|
||||||
|
return text('I halted the request')
|
||||||
|
|
||||||
|
@bp.middleware('response')
|
||||||
|
async def halt_response(request, response):
|
||||||
|
return text('I halted the response')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exceptions
|
||||||
|
|
||||||
|
Exceptions can be applied exclusively to blueprints globally.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@bp.exception(NotFound)
|
||||||
|
def ignore_404s(request, exception):
|
||||||
|
return text("Yep, I totally found the page: {}".format(request.url))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static files
|
||||||
|
|
||||||
|
Static files can be served globally, under the blueprint prefix.
|
||||||
|
|
||||||
|
```python
|
||||||
|
bp.static('/folder/to/serve', '/web/path')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Start and stop
|
||||||
|
|
||||||
|
Blueprints can run functions during the start and stop process of the server.
|
||||||
|
If running in multiprocessor mode (more than 1 worker), these are triggered
|
||||||
|
after the workers fork.
|
||||||
|
|
||||||
|
Available events are:
|
||||||
|
|
||||||
|
- `before_server_start`: Executed before the server begins to accept connections
|
||||||
|
- `after_server_start`: Executed after the server begins to accept connections
|
||||||
|
- `before_server_stop`: Executed before the server stops accepting connections
|
||||||
|
- `after_server_stop`: Executed after the server is stopped and all requests are complete
|
||||||
|
|
||||||
|
```python
|
||||||
|
bp = Blueprint('my_blueprint')
|
||||||
|
|
||||||
|
@bp.listener('before_server_start')
|
||||||
|
async def setup_connection(app, loop):
|
||||||
|
global database
|
||||||
|
database = mysql.connect(host='127.0.0.1'...)
|
||||||
|
|
||||||
|
@bp.listener('after_server_stop')
|
||||||
|
async def close_connection(app, loop):
|
||||||
|
await database.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use-case: API versioning
|
||||||
|
|
||||||
|
Blueprints can be very useful for API versioning, where one blueprint may point
|
||||||
|
at `/v1/<routes>`, and another pointing at `/v2/<routes>`.
|
||||||
|
|
||||||
|
When a blueprint is initialised, it can take an optional `url_prefix` argument,
|
||||||
|
which will be prepended to all routes defined on the blueprint. This feature
|
||||||
|
can be used to implement our API versioning scheme.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# blueprints.py
|
||||||
|
from sanic.response import text
|
||||||
|
from sanic import Blueprint
|
||||||
|
|
||||||
|
blueprint_v1 = Blueprint('v1')
|
||||||
|
blueprint_v2 = Blueprint('v2')
|
||||||
|
|
||||||
|
@blueprint_v1.route('/')
|
||||||
|
async def api_v1_root(request):
|
||||||
|
return text('Welcome to version 1 of our documentation')
|
||||||
|
|
||||||
|
@blueprint_v2.route('/')
|
||||||
|
async def api_v2_root(request):
|
||||||
|
return text('Welcome to version 2 of our documentation')
|
||||||
|
```
|
||||||
|
|
||||||
|
When we register our blueprints on the app, the routes `/v1` and `/v2` will now
|
||||||
|
point to the individual blueprints, which allows the creation of *sub-sites*
|
||||||
|
for each API version.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# main.py
|
||||||
|
from sanic import Sanic
|
||||||
|
from blueprints import blueprint_v1, blueprint_v2
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
app.blueprint(blueprint_v1)
|
||||||
|
app.blueprint(blueprint_v2)
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=8000, debug=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Exceptions](exceptions.md)
|
||||||
|
|
||||||
|
**Next:** [Class-based views](class_based_views.md)
|
112
docs/class_based_views.md
Normal file
112
docs/class_based_views.md
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# Class-Based Views
|
||||||
|
|
||||||
|
Class-based views are simply classes which implement response behaviour to
|
||||||
|
requests. They provide a way to compartmentalise handling of different HTTP
|
||||||
|
request types at the same endpoint. Rather than defining and decorating three
|
||||||
|
different handler functions, one for each of an endpoint's supported request
|
||||||
|
type, the endpoint can be assigned a class-based view.
|
||||||
|
|
||||||
|
## Defining views
|
||||||
|
|
||||||
|
A class-based view should subclass `HTTPMethodView`. You can then implement
|
||||||
|
class methods for every HTTP request type you want to support. If a request is
|
||||||
|
received that has no defined method, a `405: Method not allowed` response will
|
||||||
|
be generated.
|
||||||
|
|
||||||
|
To register a class-based view on an endpoint, the `app.add_route` method is
|
||||||
|
used. The first argument should be the defined class with the method `as_view`
|
||||||
|
invoked, and the second should be the URL endpoint.
|
||||||
|
|
||||||
|
The available methods are `get`, `post`, `put`, `patch`, and `delete`. A class
|
||||||
|
using all these methods would look like the following.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.views import HTTPMethodView
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
app = Sanic('some_name')
|
||||||
|
|
||||||
|
class SimpleView(HTTPMethodView):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
return text('I am get method')
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
return text('I am post method')
|
||||||
|
|
||||||
|
def put(self, request):
|
||||||
|
return text('I am put method')
|
||||||
|
|
||||||
|
def patch(self, request):
|
||||||
|
return text('I am patch method')
|
||||||
|
|
||||||
|
def delete(self, request):
|
||||||
|
return text('I am delete method')
|
||||||
|
|
||||||
|
app.add_route(SimpleView.as_view(), '/')
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## URL parameters
|
||||||
|
|
||||||
|
If you need any URL parameters, as discussed in the routing guide, include them
|
||||||
|
in the method definition.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class NameView(HTTPMethodView):
|
||||||
|
|
||||||
|
def get(self, request, name):
|
||||||
|
return text('Hello {}'.format(name))
|
||||||
|
|
||||||
|
app.add_route(NameView.as_view(), '/<name>')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Decorators
|
||||||
|
|
||||||
|
If you want to add any decorators to the class, you can set the `decorators`
|
||||||
|
class variable. These will be applied to the class when `as_view` is called.
|
||||||
|
|
||||||
|
```
|
||||||
|
class ViewWithDecorator(HTTPMethodView):
|
||||||
|
decorators = [some_decorator_here]
|
||||||
|
|
||||||
|
def get(self, request, name):
|
||||||
|
return text('Hello I have a decorator')
|
||||||
|
|
||||||
|
app.add_route(ViewWithDecorator.as_view(), '/url')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using CompositionView
|
||||||
|
|
||||||
|
As an alternative to the `HTTPMethodView`, you can use `CompositionView` to
|
||||||
|
move handler functions outside of the view class.
|
||||||
|
|
||||||
|
Handler functions for each supported HTTP method are defined elsewhere in the
|
||||||
|
source, and then added to the view using the `CompositionView.add` method. The
|
||||||
|
first parameter is a list of HTTP methods to handle (e.g. `['GET', 'POST']`),
|
||||||
|
and the second is the handler function. The following example shows
|
||||||
|
`CompositionView` usage with both an external handler function and an inline
|
||||||
|
lambda:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.views import CompositionView
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
def get_handler(request):
|
||||||
|
return text('I am a get method')
|
||||||
|
|
||||||
|
view = CompositionView()
|
||||||
|
view.add(['GET'], get_handler)
|
||||||
|
view.add(['POST', 'PUT'], lambda request: text('I am a post/put method'))
|
||||||
|
|
||||||
|
# Use the new view to handle requests to the base URL
|
||||||
|
app.add_route(view, '/')
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Blueprints](blueprints.md)
|
||||||
|
|
||||||
|
**Next:** [Cookies](cookies.md)
|
92
docs/conf.py
92
docs/conf.py
@ -10,9 +10,8 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
# Add support for Markdown documentation using Recommonmark
|
||||||
# Add support for auto-doc
|
from recommonmark.parser import CommonMarkParser
|
||||||
|
|
||||||
|
|
||||||
# Ensure that sanic is present in the path, to allow sphinx-apidoc to
|
# Ensure that sanic is present in the path, to allow sphinx-apidoc to
|
||||||
# autogenerate documentation from docstrings
|
# autogenerate documentation from docstrings
|
||||||
@ -21,29 +20,26 @@ sys.path.insert(0, root_directory)
|
|||||||
|
|
||||||
import sanic
|
import sanic
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
extensions = [
|
extensions = ['sphinx.ext.autodoc',
|
||||||
"sphinx.ext.autodoc",
|
'sphinx.ext.viewcode',
|
||||||
"m2r2",
|
'sphinx.ext.githubpages']
|
||||||
"enum_tools.autoenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
templates_path = ["_templates"]
|
templates_path = ['_templates']
|
||||||
|
|
||||||
# Enable support for both Restructured Text and Markdown
|
# Enable support for both Restructured Text and Markdown
|
||||||
source_suffix = [".rst", ".md"]
|
source_parsers = {'.md': CommonMarkParser}
|
||||||
|
source_suffix = ['.rst', '.md']
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = "index"
|
master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = "Sanic"
|
project = 'Sanic'
|
||||||
copyright = "2021, Sanic Community Organization"
|
copyright = '2016, Sanic contributors'
|
||||||
author = "Sanic Community Organization"
|
author = 'Sanic contributors'
|
||||||
|
|
||||||
html_logo = "./_static/sanic-framework-logo-white-400x97.png"
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
# built documents.
|
# built documents.
|
||||||
@ -58,7 +54,7 @@ release = sanic.__version__
|
|||||||
#
|
#
|
||||||
# This is also used if you do content translation via gettext catalogs.
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
# Usually you set "language" from the command line for these cases.
|
# Usually you set "language" from the command line for these cases.
|
||||||
language = "en"
|
language = 'en'
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
@ -66,29 +62,32 @@ language = "en"
|
|||||||
#
|
#
|
||||||
# modules.rst is generated by sphinx-apidoc but is unused. This suppresses
|
# modules.rst is generated by sphinx-apidoc but is unused. This suppresses
|
||||||
# a warning about it.
|
# a warning about it.
|
||||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "modules.rst"]
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'modules.rst']
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = "sphinx"
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||||
todo_include_todos = False
|
todo_include_todos = False
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ----------------------------------------------
|
# -- Options for HTML output ----------------------------------------------
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
html_theme = "sphinx_rtd_theme"
|
html_theme = 'alabaster'
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ["_static"]
|
html_static_path = ['_static']
|
||||||
html_css_files = ["custom.css"]
|
|
||||||
|
|
||||||
# -- Options for HTMLHelp output ------------------------------------------
|
# -- Options for HTMLHelp output ------------------------------------------
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = "Sanicdoc"
|
htmlhelp_basename = 'Sanicdoc'
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output ---------------------------------------------
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
@ -96,12 +95,15 @@ latex_elements = {
|
|||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#
|
#
|
||||||
# 'papersize': 'letterpaper',
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
#
|
#
|
||||||
# 'pointsize': '10pt',
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
# Additional stuff for the LaTeX preamble.
|
||||||
#
|
#
|
||||||
# 'preamble': '',
|
# 'preamble': '',
|
||||||
|
|
||||||
# Latex figure (float) alignment
|
# Latex figure (float) alignment
|
||||||
#
|
#
|
||||||
# 'figure_align': 'htbp',
|
# 'figure_align': 'htbp',
|
||||||
@ -111,20 +113,20 @@ latex_elements = {
|
|||||||
# (source start file, target name, title,
|
# (source start file, target name, title,
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# author, documentclass [howto, manual, or own class]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
(
|
(master_doc, 'Sanic.tex', 'Sanic Documentation',
|
||||||
master_doc,
|
'Sanic contributors', 'manual'),
|
||||||
"Sanic.tex",
|
|
||||||
"Sanic Documentation",
|
|
||||||
"Sanic contributors",
|
|
||||||
"manual",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output ---------------------------------------
|
# -- Options for manual page output ---------------------------------------
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [(master_doc, "sanic", "Sanic Documentation", [author], 1)]
|
man_pages = [
|
||||||
|
(master_doc, 'sanic', 'Sanic Documentation',
|
||||||
|
[author], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output -------------------------------------------
|
# -- Options for Texinfo output -------------------------------------------
|
||||||
|
|
||||||
@ -132,17 +134,13 @@ man_pages = [(master_doc, "sanic", "Sanic Documentation", [author], 1)]
|
|||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
(
|
(master_doc, 'Sanic', 'Sanic Documentation',
|
||||||
master_doc,
|
author, 'Sanic', 'One line description of project.',
|
||||||
"Sanic",
|
'Miscellaneous'),
|
||||||
"Sanic Documentation",
|
|
||||||
author,
|
|
||||||
"Sanic",
|
|
||||||
"One line description of project.",
|
|
||||||
"Miscellaneous",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Epub output ----------------------------------------------
|
# -- Options for Epub output ----------------------------------------------
|
||||||
|
|
||||||
# Bibliographic Dublin Core info.
|
# Bibliographic Dublin Core info.
|
||||||
@ -152,18 +150,6 @@ epub_publisher = author
|
|||||||
epub_copyright = copyright
|
epub_copyright = copyright
|
||||||
|
|
||||||
# A list of files that should not be packed into the epub file.
|
# A list of files that should not be packed into the epub file.
|
||||||
epub_exclude_files = ["search.html"]
|
epub_exclude_files = ['search.html']
|
||||||
|
|
||||||
# -- Custom Settings -------------------------------------------------------
|
|
||||||
|
|
||||||
suppress_warnings = ["image.nonlocal_uri"]
|
|
||||||
|
|
||||||
|
|
||||||
autodoc_typehints = "description"
|
|
||||||
autodoc_default_options = {
|
|
||||||
"member-order": "groupwise",
|
|
||||||
}
|
|
||||||
|
|
||||||
html_theme_options = {
|
|
||||||
"style_external_links": False,
|
|
||||||
}
|
|
||||||
|
35
docs/contributing.md
Normal file
35
docs/contributing.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
Thank you for your interest! Sanic is always looking for contributors. If you
|
||||||
|
don't feel comfortable contributing code, adding docstrings to the source files
|
||||||
|
is very appreciated.
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
* `python -m pip install pytest`
|
||||||
|
* `python -m pytest tests`
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Sanic's documentation is built
|
||||||
|
using [sphinx](http://www.sphinx-doc.org/en/1.5.1/). Guides are written in
|
||||||
|
Markdown and can be found in the `docs` folder, while the module reference is
|
||||||
|
automatically generated using `sphinx-apidoc`.
|
||||||
|
|
||||||
|
To generate the documentation from scratch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sphinx-apidoc -fo docs/_api/ sanic
|
||||||
|
sphinx-build -b html docs docs/_build
|
||||||
|
```
|
||||||
|
|
||||||
|
The HTML documentation will be created in the `docs/_build` folder.
|
||||||
|
|
||||||
|
## Warning
|
||||||
|
|
||||||
|
One of the main goals of Sanic is speed. Code that lowers the performance of
|
||||||
|
Sanic without significant gains in usability, security, or features may not be
|
||||||
|
merged. Please don't let this intimidate you! If you have any concerns about an
|
||||||
|
idea, open an issue for discussion and help.
|
||||||
|
|
||||||
|
**Previous:** [Sanic extensions](extensions.md)
|
79
docs/cookies.md
Normal file
79
docs/cookies.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# Cookies
|
||||||
|
|
||||||
|
Cookies are pieces of data which persist inside a user's browser. Sanic can
|
||||||
|
both read and write cookies, which are stored as key-value pairs.
|
||||||
|
|
||||||
|
## Reading cookies
|
||||||
|
|
||||||
|
A user's cookies can be accessed `Request` object's `cookie` dictionary.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
@app.route("/cookie")
|
||||||
|
async def test(request):
|
||||||
|
test_cookie = request.cookies.get('test')
|
||||||
|
return text("Test cookie set to: {}".format(test_cookie))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Writing cookies
|
||||||
|
|
||||||
|
When returning a response, cookies can be set on the `Response` object.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
@app.route("/cookie")
|
||||||
|
async def test(request):
|
||||||
|
response = text("There's a cookie up in this response")
|
||||||
|
response.cookies['test'] = 'It worked!'
|
||||||
|
response.cookies['test']['domain'] = '.gotta-go-fast.com'
|
||||||
|
response.cookies['test']['httponly'] = True
|
||||||
|
return response
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deleting cookies
|
||||||
|
|
||||||
|
Cookies can be removed semantically or explicitly.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
@app.route("/cookie")
|
||||||
|
async def test(request):
|
||||||
|
response = text("Time to eat some cookies muahaha")
|
||||||
|
|
||||||
|
# This cookie will be set to expire in 0 seconds
|
||||||
|
del response.cookies['kill_me']
|
||||||
|
|
||||||
|
# This cookie will self destruct in 5 seconds
|
||||||
|
response.cookies['short_life'] = 'Glad to be here'
|
||||||
|
response.cookies['short_life']['max-age'] = 5
|
||||||
|
del response.cookies['favorite_color']
|
||||||
|
|
||||||
|
# This cookie will remain unchanged
|
||||||
|
response.cookies['favorite_color'] = 'blue'
|
||||||
|
response.cookies['favorite_color'] = 'pink'
|
||||||
|
del response.cookies['favorite_color']
|
||||||
|
|
||||||
|
return response
|
||||||
|
```
|
||||||
|
|
||||||
|
Response cookies can be set like dictionary values and have the following
|
||||||
|
parameters available:
|
||||||
|
|
||||||
|
- `expires` (datetime): The time for the cookie to expire on the
|
||||||
|
client's browser.
|
||||||
|
- `path` (string): The subset of URLs to which this cookie applies. Defaults to /.
|
||||||
|
- `comment` (string): A comment (metadata).
|
||||||
|
- `domain` (string): Specifies the domain for which the cookie is valid. An
|
||||||
|
explicitly specified domain must always start with a dot.
|
||||||
|
- `max-age` (number): Number of seconds the cookie should live for.
|
||||||
|
- `secure` (boolean): Specifies whether the cookie will only be sent via
|
||||||
|
HTTPS.
|
||||||
|
- `httponly` (boolean): Specifies whether the cookie cannot be read by
|
||||||
|
Javascript.
|
||||||
|
|
||||||
|
**Previous:** [Class-based views](class_based_views.md)
|
||||||
|
|
||||||
|
**Next:** [Custom protocols](custom_protocol.md)
|
76
docs/custom_protocol.md
Normal file
76
docs/custom_protocol.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Custom Protocols
|
||||||
|
|
||||||
|
*Note: this is advanced usage, and most readers will not need such functionality.*
|
||||||
|
|
||||||
|
You can change the behavior of Sanic's protocol by specifying a custom
|
||||||
|
protocol, which should be a subclass
|
||||||
|
of
|
||||||
|
[asyncio.protocol](https://docs.python.org/3/library/asyncio-protocol.html#protocol-classes).
|
||||||
|
This protocol can then be passed as the keyword argument `protocol` to the `sanic.run` method.
|
||||||
|
|
||||||
|
The constructor of the custom protocol class receives the following keyword
|
||||||
|
arguments from Sanic.
|
||||||
|
|
||||||
|
- `loop`: an `asyncio`-compatible event loop.
|
||||||
|
- `connections`: a `set` to store protocol objects. When Sanic receives
|
||||||
|
`SIGINT` or `SIGTERM`, it executes `protocol.close_if_idle` for all protocol
|
||||||
|
objects stored in this set.
|
||||||
|
- `signal`: a `sanic.server.Signal` object with the `stopped` attribute. When
|
||||||
|
Sanic receives `SIGINT` or `SIGTERM`, `signal.stopped` is assigned `True`.
|
||||||
|
- `request_handler`: a coroutine that takes a `sanic.request.Request` object
|
||||||
|
and a `response` callback as arguments.
|
||||||
|
- `error_handler`: a `sanic.exceptions.Handler` which is called when exceptions
|
||||||
|
are raised.
|
||||||
|
- `request_timeout`: the number of seconds before a request times out.
|
||||||
|
- `request_max_size`: an integer specifying the maximum size of a request, in bytes.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
An error occurs in the default protocol if a handler function does not return
|
||||||
|
an `HTTPResponse` object.
|
||||||
|
|
||||||
|
By overriding the `write_response` protocol method, if a handler returns a
|
||||||
|
string it will be converted to an `HTTPResponse object`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.server import HttpProtocol
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomHttpProtocol(HttpProtocol):
|
||||||
|
|
||||||
|
def __init__(self, *, loop, request_handler, error_handler,
|
||||||
|
signal, connections, request_timeout, request_max_size):
|
||||||
|
super().__init__(
|
||||||
|
loop=loop, request_handler=request_handler,
|
||||||
|
error_handler=error_handler, signal=signal,
|
||||||
|
connections=connections, request_timeout=request_timeout,
|
||||||
|
request_max_size=request_max_size)
|
||||||
|
|
||||||
|
def write_response(self, response):
|
||||||
|
if isinstance(response, str):
|
||||||
|
response = text(response)
|
||||||
|
self.transport.write(
|
||||||
|
response.output(self.request.version)
|
||||||
|
)
|
||||||
|
self.transport.close()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
async def string(request):
|
||||||
|
return 'string'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/1')
|
||||||
|
async def response(request):
|
||||||
|
return text('response')
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=8000, protocol=CustomHttpProtocol)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Cookies](cookies.md)
|
||||||
|
|
||||||
|
**Next:** [Testing](testing.md)
|
59
docs/deploying.md
Normal file
59
docs/deploying.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Deploying
|
||||||
|
|
||||||
|
Deploying Sanic is made simple by the inbuilt webserver. After defining an
|
||||||
|
instance of `sanic.Sanic`, we can call the `run` method with the following
|
||||||
|
keyword arguments:
|
||||||
|
|
||||||
|
- `host` *(default `"127.0.0.1"`)*: Address to host the server on.
|
||||||
|
- `port` *(default `8000`)*: Port to host the server on.
|
||||||
|
- `debug` *(default `False`)*: Enables debug output (slows server).
|
||||||
|
- `before_start` *(default `None`)*: Function or list of functions to be executed
|
||||||
|
before the server starts accepting connections.
|
||||||
|
- `after_start` *(default `None`)*: Function or list of functions to be executed
|
||||||
|
after the server starts accepting connections.
|
||||||
|
- `before_stop` *(default `None`)*: Function or list of functions to be
|
||||||
|
executed when a stop signal is received before it is
|
||||||
|
respected.
|
||||||
|
- `after_stop` *(default `None`)*: Function or list of functions to be executed
|
||||||
|
when all requests are complete.
|
||||||
|
- `ssl` *(default `None`)*: `SSLContext` for SSL encryption of worker(s).
|
||||||
|
- `sock` *(default `None`)*: Socket for the server to accept connections from.
|
||||||
|
- `workers` *(default `1`)*: Number of worker processes to spawn.
|
||||||
|
- `loop` *(default `None`)*: An `asyncio`-compatible event loop. If none is
|
||||||
|
specified, Sanic creates its own event loop.
|
||||||
|
- `protocol` *(default `HttpProtocol`)*: Subclass
|
||||||
|
of
|
||||||
|
[asyncio.protocol](https://docs.python.org/3/library/asyncio-protocol.html#protocol-classes).
|
||||||
|
|
||||||
|
## Workers
|
||||||
|
|
||||||
|
By default, Sanic listens in the main process using only one CPU core. To crank
|
||||||
|
up the juice, just specify the number of workers in the `run` arguments.
|
||||||
|
|
||||||
|
```python
|
||||||
|
app.run(host='0.0.0.0', port=1337, workers=4)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sanic will automatically spin up multiple processes and route traffic between
|
||||||
|
them. We recommend as many workers as you have available cores.
|
||||||
|
|
||||||
|
## Running via command
|
||||||
|
|
||||||
|
If you like using command line arguments, you can launch a Sanic server by
|
||||||
|
executing the module. For example, if you initialized Sanic as `app` in a file
|
||||||
|
named `server.py`, you could run the server like so:
|
||||||
|
|
||||||
|
`python -m sanic server.app --host=0.0.0.0 --port=1337 --workers=4`
|
||||||
|
|
||||||
|
With this way of running sanic, it is not necessary to invoke `app.run` in your
|
||||||
|
Python file. If you do, make sure you wrap it so that it only executes when
|
||||||
|
directly run by the interpreter.
|
||||||
|
|
||||||
|
```python
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=1337, workers=4)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Request Data](request_data.md)
|
||||||
|
|
||||||
|
**Next:** [Static Files](static_files.md)
|
49
docs/exceptions.md
Normal file
49
docs/exceptions.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Exceptions
|
||||||
|
|
||||||
|
Exceptions can be thrown from within request handlers and will automatically be
|
||||||
|
handled by Sanic. Exceptions take a message as their first argument, and can
|
||||||
|
also take a status code to be passed back in the HTTP response.
|
||||||
|
|
||||||
|
## Throwing an exception
|
||||||
|
|
||||||
|
To throw an exception, simply `raise` the relevant exception from the
|
||||||
|
`sanic.exceptions` module.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.exceptions import ServerError
|
||||||
|
|
||||||
|
@app.route('/killme')
|
||||||
|
def i_am_ready_to_die(request):
|
||||||
|
raise ServerError("Something bad happened", status_code=500)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Handling exceptions
|
||||||
|
|
||||||
|
To override Sanic's default handling of an exception, the `@app.exception`
|
||||||
|
decorator is used. The decorator expects a list of exceptions to handle as
|
||||||
|
arguments. You can pass `SanicException` to catch them all! The decorated
|
||||||
|
exception handler function must take a `Request` and `Exception` object as
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
from sanic.exceptions import NotFound
|
||||||
|
|
||||||
|
@app.exception(NotFound)
|
||||||
|
def ignore_404s(request, exception):
|
||||||
|
return text("Yep, I totally found the page: {}".format(request.url))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful exceptions
|
||||||
|
|
||||||
|
Some of the most useful exceptions are presented below:
|
||||||
|
|
||||||
|
- `NotFound`: called when a suitable route for the request isn't found.
|
||||||
|
- `ServerError`: called when something goes wrong inside the server. This
|
||||||
|
usually occurs if there is an exception raised in user code.
|
||||||
|
|
||||||
|
See the `sanic.exceptions` module for the full list of exceptions to throw.
|
||||||
|
|
||||||
|
**Previous:** [Middleware](middleware.md)
|
||||||
|
|
||||||
|
**Next:** [Blueprints](blueprints.md)
|
12
docs/extensions.md
Normal file
12
docs/extensions.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Sanic Extensions
|
||||||
|
|
||||||
|
A list of Sanic extensions created by the community.
|
||||||
|
|
||||||
|
- [Sessions](https://github.com/subyraman/sanic_session): Support for sessions.
|
||||||
|
Allows using redis, memcache or an in memory store.
|
||||||
|
- [CORS](https://github.com/ashleysommer/sanic-cors): A port of flask-cors.
|
||||||
|
- [Jinja2](https://github.com/lixxu/sanic-jinja2): Support for Jinja2 template.
|
||||||
|
|
||||||
|
**Previous:** [Testing](testing.md)
|
||||||
|
|
||||||
|
**Next:** [Contributing](contributing.md)
|
29
docs/getting_started.md
Normal file
29
docs/getting_started.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Getting Started
|
||||||
|
|
||||||
|
Make sure you have both [pip](https://pip.pypa.io/en/stable/installing/) and at
|
||||||
|
least version 3.5 of Python before starting. Sanic uses the new `async`/`await`
|
||||||
|
syntax, so earlier versions of python won't work.
|
||||||
|
|
||||||
|
1. Install Sanic: `python3 -m pip install sanic`
|
||||||
|
2. Create a file called `main.py` with the following code:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
async def test(request):
|
||||||
|
return text('Hello world!')
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=8000, debug=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Run the server: `python3 main.py`
|
||||||
|
4. Open the address `http://0.0.0.0:8000` in your web browser. You should see
|
||||||
|
the message *Hello world!*.
|
||||||
|
|
||||||
|
You now have a working Sanic server!
|
||||||
|
|
||||||
|
**Next:** [Routing](routing.md)
|
468
docs/index.html
468
docs/index.html
@ -1,468 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<meta name="generator" content="Docutils 0.15.2: http://docutils.sourceforge.net/" />
|
|
||||||
<title>index.rst</title>
|
|
||||||
<style type="text/css">
|
|
||||||
|
|
||||||
/*
|
|
||||||
:Author: David Goodger (goodger@python.org)
|
|
||||||
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
|
|
||||||
:Copyright: This stylesheet has been placed in the public domain.
|
|
||||||
|
|
||||||
Default cascading style sheet for the HTML output of Docutils.
|
|
||||||
|
|
||||||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
|
||||||
customize this style sheet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* used to remove borders from tables and images */
|
|
||||||
.borderless, table.borderless td, table.borderless th {
|
|
||||||
border: 0 }
|
|
||||||
|
|
||||||
table.borderless td, table.borderless th {
|
|
||||||
/* Override padding for "table.docutils td" with "! important".
|
|
||||||
The right padding separates the table cells. */
|
|
||||||
padding: 0 0.5em 0 0 ! important }
|
|
||||||
|
|
||||||
.first {
|
|
||||||
/* Override more specific margin styles with "! important". */
|
|
||||||
margin-top: 0 ! important }
|
|
||||||
|
|
||||||
.last, .with-subtitle {
|
|
||||||
margin-bottom: 0 ! important }
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none }
|
|
||||||
|
|
||||||
.subscript {
|
|
||||||
vertical-align: sub;
|
|
||||||
font-size: smaller }
|
|
||||||
|
|
||||||
.superscript {
|
|
||||||
vertical-align: super;
|
|
||||||
font-size: smaller }
|
|
||||||
|
|
||||||
a.toc-backref {
|
|
||||||
text-decoration: none ;
|
|
||||||
color: black }
|
|
||||||
|
|
||||||
blockquote.epigraph {
|
|
||||||
margin: 2em 5em ; }
|
|
||||||
|
|
||||||
dl.docutils dd {
|
|
||||||
margin-bottom: 0.5em }
|
|
||||||
|
|
||||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
|
||||||
dl.docutils dt {
|
|
||||||
font-weight: bold }
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.abstract {
|
|
||||||
margin: 2em 5em }
|
|
||||||
|
|
||||||
div.abstract p.topic-title {
|
|
||||||
font-weight: bold ;
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
|
||||||
div.hint, div.important, div.note, div.tip, div.warning {
|
|
||||||
margin: 2em ;
|
|
||||||
border: medium outset ;
|
|
||||||
padding: 1em }
|
|
||||||
|
|
||||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
|
||||||
div.important p.admonition-title, div.note p.admonition-title,
|
|
||||||
div.tip p.admonition-title {
|
|
||||||
font-weight: bold ;
|
|
||||||
font-family: sans-serif }
|
|
||||||
|
|
||||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
|
||||||
div.danger p.admonition-title, div.error p.admonition-title,
|
|
||||||
div.warning p.admonition-title, .code .error {
|
|
||||||
color: red ;
|
|
||||||
font-weight: bold ;
|
|
||||||
font-family: sans-serif }
|
|
||||||
|
|
||||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
|
||||||
compound paragraphs.
|
|
||||||
div.compound .compound-first, div.compound .compound-middle {
|
|
||||||
margin-bottom: 0.5em }
|
|
||||||
|
|
||||||
div.compound .compound-last, div.compound .compound-middle {
|
|
||||||
margin-top: 0.5em }
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.dedication {
|
|
||||||
margin: 2em 5em ;
|
|
||||||
text-align: center ;
|
|
||||||
font-style: italic }
|
|
||||||
|
|
||||||
div.dedication p.topic-title {
|
|
||||||
font-weight: bold ;
|
|
||||||
font-style: normal }
|
|
||||||
|
|
||||||
div.figure {
|
|
||||||
margin-left: 2em ;
|
|
||||||
margin-right: 2em }
|
|
||||||
|
|
||||||
div.footer, div.header {
|
|
||||||
clear: both;
|
|
||||||
font-size: smaller }
|
|
||||||
|
|
||||||
div.line-block {
|
|
||||||
display: block ;
|
|
||||||
margin-top: 1em ;
|
|
||||||
margin-bottom: 1em }
|
|
||||||
|
|
||||||
div.line-block div.line-block {
|
|
||||||
margin-top: 0 ;
|
|
||||||
margin-bottom: 0 ;
|
|
||||||
margin-left: 1.5em }
|
|
||||||
|
|
||||||
div.sidebar {
|
|
||||||
margin: 0 0 0.5em 1em ;
|
|
||||||
border: medium outset ;
|
|
||||||
padding: 1em ;
|
|
||||||
background-color: #ffffee ;
|
|
||||||
width: 40% ;
|
|
||||||
float: right ;
|
|
||||||
clear: right }
|
|
||||||
|
|
||||||
div.sidebar p.rubric {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-size: medium }
|
|
||||||
|
|
||||||
div.system-messages {
|
|
||||||
margin: 5em }
|
|
||||||
|
|
||||||
div.system-messages h1 {
|
|
||||||
color: red }
|
|
||||||
|
|
||||||
div.system-message {
|
|
||||||
border: medium outset ;
|
|
||||||
padding: 1em }
|
|
||||||
|
|
||||||
div.system-message p.system-message-title {
|
|
||||||
color: red ;
|
|
||||||
font-weight: bold }
|
|
||||||
|
|
||||||
div.topic {
|
|
||||||
margin: 2em }
|
|
||||||
|
|
||||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
|
||||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
|
||||||
margin-top: 0.4em }
|
|
||||||
|
|
||||||
h1.title {
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
h2.subtitle {
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
hr.docutils {
|
|
||||||
width: 75% }
|
|
||||||
|
|
||||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
|
||||||
clear: left ;
|
|
||||||
float: left ;
|
|
||||||
margin-right: 1em }
|
|
||||||
|
|
||||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
|
||||||
clear: right ;
|
|
||||||
float: right ;
|
|
||||||
margin-left: 1em }
|
|
||||||
|
|
||||||
img.align-center, .figure.align-center, object.align-center {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.align-center {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
text-align: left }
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
clear: both ;
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
text-align: right }
|
|
||||||
|
|
||||||
/* reset inner alignment in figures */
|
|
||||||
div.align-right {
|
|
||||||
text-align: inherit }
|
|
||||||
|
|
||||||
/* div.align-center * { */
|
|
||||||
/* text-align: left } */
|
|
||||||
|
|
||||||
.align-top {
|
|
||||||
vertical-align: top }
|
|
||||||
|
|
||||||
.align-middle {
|
|
||||||
vertical-align: middle }
|
|
||||||
|
|
||||||
.align-bottom {
|
|
||||||
vertical-align: bottom }
|
|
||||||
|
|
||||||
ol.simple, ul.simple {
|
|
||||||
margin-bottom: 1em }
|
|
||||||
|
|
||||||
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 }
|
|
||||||
|
|
||||||
p.attribution {
|
|
||||||
text-align: right ;
|
|
||||||
margin-left: 50% }
|
|
||||||
|
|
||||||
p.caption {
|
|
||||||
font-style: italic }
|
|
||||||
|
|
||||||
p.credits {
|
|
||||||
font-style: italic ;
|
|
||||||
font-size: smaller }
|
|
||||||
|
|
||||||
p.label {
|
|
||||||
white-space: nowrap }
|
|
||||||
|
|
||||||
p.rubric {
|
|
||||||
font-weight: bold ;
|
|
||||||
font-size: larger ;
|
|
||||||
color: maroon ;
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
p.sidebar-title {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-weight: bold ;
|
|
||||||
font-size: larger }
|
|
||||||
|
|
||||||
p.sidebar-subtitle {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-weight: bold }
|
|
||||||
|
|
||||||
p.topic-title {
|
|
||||||
font-weight: bold }
|
|
||||||
|
|
||||||
pre.address {
|
|
||||||
margin-bottom: 0 ;
|
|
||||||
margin-top: 0 ;
|
|
||||||
font: inherit }
|
|
||||||
|
|
||||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
|
||||||
margin-left: 2em ;
|
|
||||||
margin-right: 2em }
|
|
||||||
|
|
||||||
pre.code .ln { color: grey; } /* line numbers */
|
|
||||||
pre.code, code { background-color: #eeeeee }
|
|
||||||
pre.code .comment, code .comment { color: #5C6576 }
|
|
||||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
|
||||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
|
||||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
|
||||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
|
||||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
|
||||||
|
|
||||||
span.classifier {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-style: oblique }
|
|
||||||
|
|
||||||
span.classifier-delimiter {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-weight: bold }
|
|
||||||
|
|
||||||
span.interpreted {
|
|
||||||
font-family: sans-serif }
|
|
||||||
|
|
||||||
span.option {
|
|
||||||
white-space: nowrap }
|
|
||||||
|
|
||||||
span.pre {
|
|
||||||
white-space: pre }
|
|
||||||
|
|
||||||
span.problematic {
|
|
||||||
color: red }
|
|
||||||
|
|
||||||
span.section-subtitle {
|
|
||||||
/* font-size relative to parent (h1..h6 element) */
|
|
||||||
font-size: 80% }
|
|
||||||
|
|
||||||
table.citation {
|
|
||||||
border-left: solid 1px gray;
|
|
||||||
margin-left: 1px }
|
|
||||||
|
|
||||||
table.docinfo {
|
|
||||||
margin: 2em 4em }
|
|
||||||
|
|
||||||
table.docutils {
|
|
||||||
margin-top: 0.5em ;
|
|
||||||
margin-bottom: 0.5em }
|
|
||||||
|
|
||||||
table.footnote {
|
|
||||||
border-left: solid 1px black;
|
|
||||||
margin-left: 1px }
|
|
||||||
|
|
||||||
table.docutils td, table.docutils th,
|
|
||||||
table.docinfo td, table.docinfo th {
|
|
||||||
padding-left: 0.5em ;
|
|
||||||
padding-right: 0.5em ;
|
|
||||||
vertical-align: top }
|
|
||||||
|
|
||||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
|
||||||
font-weight: bold ;
|
|
||||||
text-align: left ;
|
|
||||||
white-space: nowrap ;
|
|
||||||
padding-left: 0 }
|
|
||||||
|
|
||||||
/* "booktabs" style (no vertical lines) */
|
|
||||||
table.docutils.booktabs {
|
|
||||||
border: 0px;
|
|
||||||
border-top: 2px solid;
|
|
||||||
border-bottom: 2px solid;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
table.docutils.booktabs * {
|
|
||||||
border: 0px;
|
|
||||||
}
|
|
||||||
table.docutils.booktabs th {
|
|
||||||
border-bottom: thin solid;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
|
||||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
|
||||||
font-size: 100% }
|
|
||||||
|
|
||||||
ul.auto-toc {
|
|
||||||
list-style-type: none }
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="document">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="section" id="sanic">
|
|
||||||
<h1>Sanic</h1>
|
|
||||||
<p>Sanic is a Python 3.6+ web server and web framework that's written to go fast. It allows the usage of the async/await syntax added in Python 3.5, which makes your code non-blocking and speedy.</p>
|
|
||||||
<p>The goal of the project is to provide a simple way to get up and running a highly performant HTTP server that is easy to build, to expand, and ultimately to scale.</p>
|
|
||||||
<p>Sanic is developed <a class="reference external" href="https://github.com/channelcat/sanic/">on GitHub</a>. Contributions are welcome!</p>
|
|
||||||
<div class="section" id="sanic-aspires-to-be-simple">
|
|
||||||
<h2>Sanic aspires to be simple</h2>
|
|
||||||
<pre class="code python literal-block">
|
|
||||||
<span class="keyword namespace">from</span> <span class="name namespace">sanic</span> <span class="keyword namespace">import</span> <span class="name">Sanic</span>
|
|
||||||
<span class="keyword namespace">from</span> <span class="name namespace">sanic.response</span> <span class="keyword namespace">import</span> <span class="name">json</span>
|
|
||||||
|
|
||||||
<span class="name">app</span> <span class="operator">=</span> <span class="name">Sanic</span><span class="punctuation">()</span>
|
|
||||||
|
|
||||||
<span class="name decorator">@app.route</span><span class="punctuation">(</span><span class="literal string double">"/"</span><span class="punctuation">)</span>
|
|
||||||
<span class="name">async</span> <span class="keyword">def</span> <span class="name function">test</span><span class="punctuation">(</span><span class="name">request</span><span class="punctuation">):</span>
|
|
||||||
<span class="keyword">return</span> <span class="name">json</span><span class="punctuation">({</span><span class="literal string double">"hello"</span><span class="punctuation">:</span> <span class="literal string double">"world"</span><span class="punctuation">})</span>
|
|
||||||
|
|
||||||
<span class="keyword">if</span> <span class="name variable magic">__name__</span> <span class="operator">==</span> <span class="literal string double">"__main__"</span><span class="punctuation">:</span>
|
|
||||||
<span class="name">app</span><span class="operator">.</span><span class="name">run</span><span class="punctuation">(</span><span class="name">host</span><span class="operator">=</span><span class="literal string double">"0.0.0.0"</span><span class="punctuation">,</span> <span class="name">port</span><span class="operator">=</span><span class="literal number integer">8000</span><span class="punctuation">)</span>
|
|
||||||
</pre>
|
|
||||||
<div class="admonition note">
|
|
||||||
<p class="first admonition-title">Note</p>
|
|
||||||
<p class="last">Sanic does not support Python 3.5 from version 19.6 and forward. However, version 18.12LTS is supported thru
|
|
||||||
December 2020. Official Python support for version 3.5 is set to expire in September 2020.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="guides">
|
|
||||||
<h1>Guides</h1>
|
|
||||||
<div class="system-message">
|
|
||||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 6)</p>
|
|
||||||
<p>Unknown directive type "toctree".</p>
|
|
||||||
<pre class="literal-block">
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
sanic/getting_started
|
|
||||||
sanic/config
|
|
||||||
sanic/logging
|
|
||||||
sanic/request_data
|
|
||||||
sanic/response
|
|
||||||
sanic/cookies
|
|
||||||
sanic/routing
|
|
||||||
sanic/blueprints
|
|
||||||
sanic/static_files
|
|
||||||
sanic/versioning
|
|
||||||
sanic/exceptions
|
|
||||||
sanic/middleware
|
|
||||||
sanic/websocket
|
|
||||||
sanic/decorators
|
|
||||||
sanic/streaming
|
|
||||||
sanic/class_based_views
|
|
||||||
sanic/custom_protocol
|
|
||||||
sanic/sockets
|
|
||||||
sanic/ssl
|
|
||||||
sanic/debug_mode
|
|
||||||
sanic/testing
|
|
||||||
sanic/deploying
|
|
||||||
sanic/extensions
|
|
||||||
sanic/examples
|
|
||||||
sanic/changelog
|
|
||||||
sanic/contributing
|
|
||||||
sanic/api_reference
|
|
||||||
sanic/asyncio_python37
|
|
||||||
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="module-documentation">
|
|
||||||
<h1>Module Documentation</h1>
|
|
||||||
<div class="system-message">
|
|
||||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 42)</p>
|
|
||||||
<p>Unknown directive type "toctree".</p>
|
|
||||||
<pre class="literal-block">
|
|
||||||
.. toctree::
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><p class="first"><a href="#id1"><span class="problematic" id="id2">:ref:`genindex`</span></a></p>
|
|
||||||
<div class="system-message" id="id1">
|
|
||||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 44); <em><a href="#id2">backlink</a></em></p>
|
|
||||||
<p>Unknown interpreted text role "ref".</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><p class="first"><a href="#id3"><span class="problematic" id="id4">:ref:`modindex`</span></a></p>
|
|
||||||
<div class="system-message" id="id3">
|
|
||||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 45); <em><a href="#id4">backlink</a></em></p>
|
|
||||||
<p>Unknown interpreted text role "ref".</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><p class="first"><a href="#id5"><span class="problematic" id="id6">:ref:`search`</span></a></p>
|
|
||||||
<div class="system-message" id="id5">
|
|
||||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 46); <em><a href="#id6">backlink</a></em></p>
|
|
||||||
<p>Unknown interpreted text role "ref".</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,23 +1,25 @@
|
|||||||
.. include:: ../README.rst
|
.. include:: ../README.rst
|
||||||
|
|
||||||
User Guide
|
Guides
|
||||||
==========
|
======
|
||||||
|
|
||||||
To learn about using Sanic, checkout the `User Guide <https://sanicframework.org/guide/>`__.
|
|
||||||
|
|
||||||
API
|
|
||||||
===
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 3
|
:maxdepth: 2
|
||||||
|
|
||||||
👥 User Guide <https://sanicframework.org/guide/>
|
getting_started
|
||||||
sanic/api_reference
|
routing
|
||||||
💻 Source code <https://github.com/sanic-org/sanic/>
|
request_data
|
||||||
sanic/changelog
|
deploying
|
||||||
sanic/contributing
|
static_files
|
||||||
❓ Support <https://community.sanicframework.org/>
|
middleware
|
||||||
💬 Chat <https://discord.gg/FARQzAEMAA>
|
exceptions
|
||||||
|
blueprints
|
||||||
|
class_based_views
|
||||||
|
cookies
|
||||||
|
custom_protocol
|
||||||
|
testing
|
||||||
|
extensions
|
||||||
|
contributing
|
||||||
|
|
||||||
|
|
||||||
Module Documentation
|
Module Documentation
|
||||||
@ -25,5 +27,7 @@ Module Documentation
|
|||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|
||||||
|
Module Reference <_api/sanic>
|
||||||
|
|
||||||
* :ref:`genindex`
|
* :ref:`genindex`
|
||||||
* :ref:`modindex`
|
* :ref:`search`
|
||||||
|
281
docs/make.bat
281
docs/make.bat
@ -1,281 +0,0 @@
|
|||||||
@ECHO OFF
|
|
||||||
|
|
||||||
REM Command file for Sphinx documentation
|
|
||||||
|
|
||||||
if "%SPHINXBUILD%" == "" (
|
|
||||||
set SPHINXBUILD=sphinx-build
|
|
||||||
)
|
|
||||||
set BUILDDIR=_build
|
|
||||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
|
||||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
|
||||||
if NOT "%PAPER%" == "" (
|
|
||||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
|
||||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
|
||||||
|
|
||||||
if "%1" == "help" (
|
|
||||||
:help
|
|
||||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
|
||||||
echo. html to make standalone HTML files
|
|
||||||
echo. dirhtml to make HTML files named index.html in directories
|
|
||||||
echo. singlehtml to make a single large HTML file
|
|
||||||
echo. pickle to make pickle files
|
|
||||||
echo. json to make JSON files
|
|
||||||
echo. htmlhelp to make HTML files and a HTML help project
|
|
||||||
echo. qthelp to make HTML files and a qthelp project
|
|
||||||
echo. devhelp to make HTML files and a Devhelp project
|
|
||||||
echo. epub to make an epub
|
|
||||||
echo. epub3 to make an epub3
|
|
||||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
|
||||||
echo. text to make text files
|
|
||||||
echo. man to make manual pages
|
|
||||||
echo. texinfo to make Texinfo files
|
|
||||||
echo. gettext to make PO message catalogs
|
|
||||||
echo. changes to make an overview over all changed/added/deprecated items
|
|
||||||
echo. xml to make Docutils-native XML files
|
|
||||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
|
||||||
echo. linkcheck to check all external links for integrity
|
|
||||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
|
||||||
echo. coverage to run coverage check of the documentation if enabled
|
|
||||||
echo. dummy to check syntax errors of document sources
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "clean" (
|
|
||||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
|
||||||
del /q /s %BUILDDIR%\*
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
REM Check if sphinx-build is available and fallback to Python version if any
|
|
||||||
%SPHINXBUILD% 1>NUL 2>NUL
|
|
||||||
if errorlevel 9009 goto sphinx_python
|
|
||||||
goto sphinx_ok
|
|
||||||
|
|
||||||
:sphinx_python
|
|
||||||
|
|
||||||
set SPHINXBUILD=python -m sphinx.__init__
|
|
||||||
%SPHINXBUILD% 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
|
|
||||||
)
|
|
||||||
|
|
||||||
:sphinx_ok
|
|
||||||
|
|
||||||
|
|
||||||
if "%1" == "html" (
|
|
||||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "dirhtml" (
|
|
||||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "singlehtml" (
|
|
||||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "pickle" (
|
|
||||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the pickle files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "json" (
|
|
||||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the JSON files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "htmlhelp" (
|
|
||||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
|
||||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "qthelp" (
|
|
||||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
|
||||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
|
||||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\aiographite.qhcp
|
|
||||||
echo.To view the help file:
|
|
||||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\aiographite.ghc
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "devhelp" (
|
|
||||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "epub" (
|
|
||||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "epub3" (
|
|
||||||
%SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "latex" (
|
|
||||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "latexpdf" (
|
|
||||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
|
||||||
cd %BUILDDIR%/latex
|
|
||||||
make all-pdf
|
|
||||||
cd %~dp0
|
|
||||||
echo.
|
|
||||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "latexpdfja" (
|
|
||||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
|
||||||
cd %BUILDDIR%/latex
|
|
||||||
make all-pdf-ja
|
|
||||||
cd %~dp0
|
|
||||||
echo.
|
|
||||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "text" (
|
|
||||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "man" (
|
|
||||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "texinfo" (
|
|
||||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "gettext" (
|
|
||||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "changes" (
|
|
||||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.The overview file is in %BUILDDIR%/changes.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "linkcheck" (
|
|
||||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Link check complete; look for any errors in the above output ^
|
|
||||||
or in %BUILDDIR%/linkcheck/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "doctest" (
|
|
||||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Testing of doctests in the sources finished, look at the ^
|
|
||||||
results in %BUILDDIR%/doctest/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "coverage" (
|
|
||||||
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Testing of coverage in the sources finished, look at the ^
|
|
||||||
results in %BUILDDIR%/coverage/python.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "xml" (
|
|
||||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "pseudoxml" (
|
|
||||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "dummy" (
|
|
||||||
%SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. Dummy builder generates no files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
:end
|
|
70
docs/middleware.md
Normal file
70
docs/middleware.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Middleware
|
||||||
|
|
||||||
|
Middleware are functions which are executed before or after requests to the
|
||||||
|
server. They can be used to modify the *request to* or *response from*
|
||||||
|
user-defined handler functions.
|
||||||
|
|
||||||
|
There are two types of middleware: request and response. Both are declared
|
||||||
|
using the `@app.middleware` decorator, with the decorator's parameter being a
|
||||||
|
string representing its type: `'request'` or `'response'`. Response middleware
|
||||||
|
receives both the request and the response as arguments.
|
||||||
|
|
||||||
|
|
||||||
|
The simplest middleware doesn't modify the request or response at all:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.middleware('request')
|
||||||
|
async def print_on_request(request):
|
||||||
|
print("I print when a request is received by the server")
|
||||||
|
|
||||||
|
@app.middleware('response')
|
||||||
|
async def print_on_response(request, response):
|
||||||
|
print("I print when a response is returned by the server")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modifying the request or response
|
||||||
|
|
||||||
|
Middleware can modify the request or response parameter it is given, *as long
|
||||||
|
as it does not return it*. The following example shows a practical use-case for
|
||||||
|
this.
|
||||||
|
|
||||||
|
```python
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
@app.middleware('response')
|
||||||
|
async def custom_banner(request, response):
|
||||||
|
response.headers["Server"] = "Fake-Server"
|
||||||
|
|
||||||
|
@app.middleware('response')
|
||||||
|
async def prevent_xss(request, response):
|
||||||
|
response.headers["x-xss-protection"] = "1; mode=block"
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
```
|
||||||
|
|
||||||
|
The above code will apply the two middleware in order. First, the middleware
|
||||||
|
**custom_banner** will change the HTTP response header *Server* to
|
||||||
|
*Fake-Server*, and the second middleware **prevent_xss** will add the HTTP
|
||||||
|
header for preventing Cross-Site-Scripting (XSS) attacks. These two functions
|
||||||
|
are invoked *after* a user function returns a response.
|
||||||
|
|
||||||
|
## Responding early
|
||||||
|
|
||||||
|
If middleware returns a `HTTPResponse` object, the request will stop processing
|
||||||
|
and the response will be returned. If this occurs to a request before the
|
||||||
|
relevant user route handler is reached, the handler will never be called.
|
||||||
|
Returning a response will also prevent any further middleware from running.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.middleware('request')
|
||||||
|
async def halt_request(request):
|
||||||
|
return text('I halted the request')
|
||||||
|
|
||||||
|
@app.middleware('response')
|
||||||
|
async def halt_response(request, response):
|
||||||
|
return text('I halted the response')
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Static Files](static_files.md)
|
||||||
|
|
||||||
|
**Next:** [Exceptions](exceptions.md)
|
95
docs/request_data.md
Normal file
95
docs/request_data.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Request Data
|
||||||
|
|
||||||
|
When an endpoint receives a HTTP request, the route function is passed a
|
||||||
|
`Request` object.
|
||||||
|
|
||||||
|
The following variables are accessible as properties on `Request` objects:
|
||||||
|
|
||||||
|
- `json` (any) - JSON body
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
@app.route("/json")
|
||||||
|
def post_json(request):
|
||||||
|
return json({ "received": True, "message": request.json })
|
||||||
|
```
|
||||||
|
|
||||||
|
- `args` (dict) - Query string variables. A query string is the section of a
|
||||||
|
URL that resembles `?key1=value1&key2=value2`. If that URL were to be parsed,
|
||||||
|
the `args` dictionary would look like `{'key1': 'value1', 'key2': 'value2'}`.
|
||||||
|
The request's `query_string` variable holds the unparsed string value.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
@app.route("/query_string")
|
||||||
|
def query_string(request):
|
||||||
|
return json({ "parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string })
|
||||||
|
```
|
||||||
|
|
||||||
|
- `files` (dictionary of `File` objects) - List of files that have a name, body, and type
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
@app.route("/files")
|
||||||
|
def post_json(request):
|
||||||
|
test_file = request.files.get('test')
|
||||||
|
|
||||||
|
file_parameters = {
|
||||||
|
'body': test_file.body,
|
||||||
|
'name': test_file.name,
|
||||||
|
'type': test_file.type,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json({ "received": True, "file_names": request.files.keys(), "test_file_parameters": file_parameters })
|
||||||
|
```
|
||||||
|
|
||||||
|
- `form` (dict) - Posted form variables.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
@app.route("/form")
|
||||||
|
def post_json(request):
|
||||||
|
return json({ "received": True, "form_data": request.form, "test": request.form.get('test') })
|
||||||
|
```
|
||||||
|
|
||||||
|
- `body` (bytes) - Posted raw body. This property allows retrieval of the
|
||||||
|
request's raw data, regardless of content type.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
@app.route("/users", methods=["POST",])
|
||||||
|
def create_user(request):
|
||||||
|
return text("You are trying to create a user with the following POST: %s" % request.body)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `ip` (str) - IP address of the requester.
|
||||||
|
|
||||||
|
## Accessing values using `get` and `getlist`
|
||||||
|
|
||||||
|
The request properties which return a dictionary actually return a subclass of
|
||||||
|
`dict` called `RequestParameters`. The key difference when using this object is
|
||||||
|
the distinction between the `get` and `getlist` methods.
|
||||||
|
|
||||||
|
- `get(key, default=None)` operates as normal, except that when the value of
|
||||||
|
the given key is a list, *only the first item is returned*.
|
||||||
|
- `getlist(key, default=None)` operates as normal, *returning the entire list*.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.request import RequestParameters
|
||||||
|
|
||||||
|
args = RequestParameters()
|
||||||
|
args['titles'] = ['Post 1', 'Post 2']
|
||||||
|
|
||||||
|
args.get('titles') # => 'Post 1'
|
||||||
|
|
||||||
|
args.getlist('titles') # => ['Post 1', 'Post 2']
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Routing](routing.md)
|
||||||
|
|
||||||
|
**Next:** [Deploying](deploying.md)
|
111
docs/routing.md
Normal file
111
docs/routing.md
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
# Routing
|
||||||
|
|
||||||
|
Routing allows the user to specify handler functions for different URL endpoints.
|
||||||
|
|
||||||
|
A basic route looks like the following, where `app` is an instance of the
|
||||||
|
`Sanic` class:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
async def test(request):
|
||||||
|
return json({ "hello": "world" })
|
||||||
|
```
|
||||||
|
|
||||||
|
When the url `http://server.url/` is accessed (the base url of the server), the
|
||||||
|
final `/` is matched by the router to the handler function, `test`, which then
|
||||||
|
returns a JSON object.
|
||||||
|
|
||||||
|
Sanic handler functions must be defined using the `async def` syntax, as they
|
||||||
|
are asynchronous functions.
|
||||||
|
|
||||||
|
## Request parameters
|
||||||
|
|
||||||
|
Sanic comes with a basic router that supports request parameters.
|
||||||
|
|
||||||
|
To specify a parameter, surround it with angle quotes like so: `<PARAM>`.
|
||||||
|
Request parameters will be passed to the route handler functions as keyword
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
@app.route('/tag/<tag>')
|
||||||
|
async def tag_handler(request, tag):
|
||||||
|
return text('Tag - {}'.format(tag))
|
||||||
|
```
|
||||||
|
|
||||||
|
To specify a type for the parameter, add a `:type` after the parameter name,
|
||||||
|
inside the quotes. If the parameter does not match the specified type, Sanic
|
||||||
|
will throw a `NotFound` exception, resulting in a `404: Page not found` error
|
||||||
|
on the URL.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
@app.route('/number/<integer_arg:int>')
|
||||||
|
async def integer_handler(request, integer_arg):
|
||||||
|
return text('Integer - {}'.format(integer_arg))
|
||||||
|
|
||||||
|
@app.route('/number/<number_arg:number>')
|
||||||
|
async def number_handler(request, number_arg):
|
||||||
|
return text('Number - {}'.format(number_arg))
|
||||||
|
|
||||||
|
@app.route('/person/<name:[A-z]>')
|
||||||
|
async def person_handler(request, name):
|
||||||
|
return text('Person - {}'.format(name))
|
||||||
|
|
||||||
|
@app.route('/folder/<folder_id:[A-z0-9]{0,4}>')
|
||||||
|
async def folder_handler(request, folder_id):
|
||||||
|
return text('Folder - {}'.format(folder_id))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## HTTP request types
|
||||||
|
|
||||||
|
By default, a route defined on a URL will be used for all requests to that URL.
|
||||||
|
However, the `@app.route` decorator accepts an optional parameter, `methods`,
|
||||||
|
which restricts the handler function to the HTTP methods in the given list.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
@app.route('/post')
|
||||||
|
async def post_handler(request, methods=['POST']):
|
||||||
|
return text('POST request - {}'.format(request.json))
|
||||||
|
|
||||||
|
@app.route('/get')
|
||||||
|
async def GET_handler(request, methods=['GET']):
|
||||||
|
return text('GET request - {}'.format(request.args))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## The `add_route` method
|
||||||
|
|
||||||
|
As we have seen, routes are often specified using the `@app.route` decorator.
|
||||||
|
However, this decorator is really just a wrapper for the `app.add_route`
|
||||||
|
method, which is used as follows:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
# Define the handler functions
|
||||||
|
async def handler1(request):
|
||||||
|
return text('OK')
|
||||||
|
|
||||||
|
async def handler2(request, name):
|
||||||
|
return text('Folder - {}'.format(name))
|
||||||
|
|
||||||
|
async def person_handler2(request, name):
|
||||||
|
return text('Person - {}'.format(name))
|
||||||
|
|
||||||
|
# Add each handler function as a route
|
||||||
|
app.add_route(handler1, '/test')
|
||||||
|
app.add_route(handler2, '/folder/<name>')
|
||||||
|
app.add_route(person_handler2, '/person/<name:[A-z]>', methods=['GET'])
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Getting Started](getting_started.md)
|
||||||
|
|
||||||
|
**Next:** [Request Data](request_data.md)
|
@ -1,33 +0,0 @@
|
|||||||
Application
|
|
||||||
===========
|
|
||||||
|
|
||||||
sanic.app
|
|
||||||
---------
|
|
||||||
|
|
||||||
.. automodule:: sanic.app
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
:inherited-members:
|
|
||||||
|
|
||||||
sanic.config
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.config
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
sanic.application.constants
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.application.constants
|
|
||||||
:exclude-members: StrEnum
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
:inherited-members:
|
|
||||||
|
|
||||||
sanic.application.state
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.application.state
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
@ -1,17 +0,0 @@
|
|||||||
Blueprints
|
|
||||||
==========
|
|
||||||
|
|
||||||
sanic.blueprints
|
|
||||||
----------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.blueprints
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
:inherited-members:
|
|
||||||
|
|
||||||
sanic.blueprint_group
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.blueprint_group
|
|
||||||
:members:
|
|
||||||
:special-members:
|
|
@ -1,48 +0,0 @@
|
|||||||
Core
|
|
||||||
====
|
|
||||||
|
|
||||||
sanic.cookies
|
|
||||||
-------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.cookies
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
|
|
||||||
sanic.handlers
|
|
||||||
--------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.handlers
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
|
|
||||||
sanic.headers
|
|
||||||
--------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.headers
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
|
|
||||||
sanic.request
|
|
||||||
-------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.request
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
sanic.response
|
|
||||||
--------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.response
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
|
|
||||||
sanic.views
|
|
||||||
-----------
|
|
||||||
|
|
||||||
.. automodule:: sanic.views
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
@ -1,16 +0,0 @@
|
|||||||
Exceptions
|
|
||||||
==========
|
|
||||||
|
|
||||||
sanic.errorpages
|
|
||||||
----------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.errorpages
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
sanic.exceptions
|
|
||||||
----------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.exceptions
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
@ -1,18 +0,0 @@
|
|||||||
Routing
|
|
||||||
=======
|
|
||||||
|
|
||||||
sanic_routing models
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. autoclass:: sanic_routing.route::Route
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: sanic_routing.group::RouteGroup
|
|
||||||
:members:
|
|
||||||
|
|
||||||
sanic.router
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.router
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
@ -1,18 +0,0 @@
|
|||||||
Sanic Server
|
|
||||||
============
|
|
||||||
|
|
||||||
sanic.http
|
|
||||||
----------
|
|
||||||
|
|
||||||
.. automodule:: sanic.http
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
|
|
||||||
sanic.server
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.server
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
Utility
|
|
||||||
=======
|
|
||||||
|
|
||||||
sanic.compat
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.compat
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
sanic.log
|
|
||||||
---------
|
|
||||||
|
|
||||||
.. automodule:: sanic.log
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
@ -1,13 +0,0 @@
|
|||||||
📑 API Reference
|
|
||||||
================
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
api/app
|
|
||||||
api/blueprints
|
|
||||||
api/core
|
|
||||||
api/exceptions
|
|
||||||
api/router
|
|
||||||
api/server
|
|
||||||
api/utility
|
|
@ -1,16 +0,0 @@
|
|||||||
📜 Changelog
|
|
||||||
============
|
|
||||||
|
|
||||||
| 🔶 Current release
|
|
||||||
| 🔷 In support release
|
|
||||||
|
|
|
||||||
|
|
||||||
.. mdinclude:: ./releases/23/23.6.md
|
|
||||||
.. mdinclude:: ./releases/23/23.3.md
|
|
||||||
.. mdinclude:: ./releases/22/22.12.md
|
|
||||||
.. mdinclude:: ./releases/22/22.9.md
|
|
||||||
.. mdinclude:: ./releases/22/22.6.md
|
|
||||||
.. mdinclude:: ./releases/22/22.3.md
|
|
||||||
.. mdinclude:: ./releases/21/21.12.md
|
|
||||||
.. mdinclude:: ./releases/21/21.9.md
|
|
||||||
.. include:: ../../CHANGELOG.rst
|
|
@ -1,4 +0,0 @@
|
|||||||
♥️ Contributing
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. include:: ../../CONTRIBUTING.rst
|
|
@ -1,66 +0,0 @@
|
|||||||
## Version 21.12.1 🔷
|
|
||||||
|
|
||||||
_Current LTS version_
|
|
||||||
|
|
||||||
- [#2349](https://github.com/sanic-org/sanic/pull/2349) Only display MOTD on startup
|
|
||||||
- [#2354](https://github.com/sanic-org/sanic/pull/2354) Ignore name argument in Python 3.7
|
|
||||||
- [#2355](https://github.com/sanic-org/sanic/pull/2355) Add config.update support for all config values
|
|
||||||
|
|
||||||
## Version 21.12.0 🔹
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- [#2260](https://github.com/sanic-org/sanic/pull/2260) Allow early Blueprint registrations to still apply later added objects
|
|
||||||
- [#2262](https://github.com/sanic-org/sanic/pull/2262) Noisy exceptions - force logging of all exceptions
|
|
||||||
- [#2264](https://github.com/sanic-org/sanic/pull/2264) Optional `uvloop` by configuration
|
|
||||||
- [#2270](https://github.com/sanic-org/sanic/pull/2270) Vhost support using multiple TLS certificates
|
|
||||||
- [#2277](https://github.com/sanic-org/sanic/pull/2277) Change signal routing for increased consistency
|
|
||||||
- *BREAKING CHANGE*: If you were manually routing signals there is a breaking change. The signal router's `get` is no longer 100% determinative. There is now an additional step to loop thru the returned signals for proper matching on the requirements. If signals are being dispatched using `app.dispatch` or `bp.dispatch`, there is no change.
|
|
||||||
- [#2290](https://github.com/sanic-org/sanic/pull/2290) Add contextual exceptions
|
|
||||||
- [#2291](https://github.com/sanic-org/sanic/pull/2291) Increase join concat performance
|
|
||||||
- [#2295](https://github.com/sanic-org/sanic/pull/2295), [#2316](https://github.com/sanic-org/sanic/pull/2316), [#2331](https://github.com/sanic-org/sanic/pull/2331) Restructure of CLI and application state with new displays and more command parity with `app.run`
|
|
||||||
- [#2302](https://github.com/sanic-org/sanic/pull/2302) Add route context at definition time
|
|
||||||
- [#2304](https://github.com/sanic-org/sanic/pull/2304) Named tasks and new API for managing background tasks
|
|
||||||
- [#2307](https://github.com/sanic-org/sanic/pull/2307) On app auto-reload, provide insight of changed files
|
|
||||||
- [#2308](https://github.com/sanic-org/sanic/pull/2308) Auto extend application with [Sanic Extensions](https://sanicframework.org/en/plugins/sanic-ext/getting-started.html) if it is installed, and provide first class support for accessing the extensions
|
|
||||||
- [#2309](https://github.com/sanic-org/sanic/pull/2309) Builtin signals changed to `Enum`
|
|
||||||
- [#2313](https://github.com/sanic-org/sanic/pull/2313) Support additional config implementation use case
|
|
||||||
- [#2321](https://github.com/sanic-org/sanic/pull/2321) Refactor environment variable hydration logic
|
|
||||||
- [#2327](https://github.com/sanic-org/sanic/pull/2327) Prevent sending multiple or mixed responses on a single request
|
|
||||||
- [#2330](https://github.com/sanic-org/sanic/pull/2330) Custom type casting on environment variables
|
|
||||||
- [#2332](https://github.com/sanic-org/sanic/pull/2332) Make all deprecation notices consistent
|
|
||||||
- [#2335](https://github.com/sanic-org/sanic/pull/2335) Allow underscore to start instance names
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#2273](https://github.com/sanic-org/sanic/pull/2273) Replace assignation by typing for `websocket_handshake`
|
|
||||||
- [#2285](https://github.com/sanic-org/sanic/pull/2285) Fix IPv6 display in startup logs
|
|
||||||
- [#2299](https://github.com/sanic-org/sanic/pull/2299) Dispatch `http.lifecyle.response` from exception handler
|
|
||||||
|
|
||||||
### Deprecations and Removals
|
|
||||||
- [#2306](https://github.com/sanic-org/sanic/pull/2306) Removal of deprecated items
|
|
||||||
- `Sanic` and `Blueprint` may no longer have arbitrary properties attached to them
|
|
||||||
- `Sanic` and `Blueprint` forced to have compliant names
|
|
||||||
- alphanumeric + `_` + `-`
|
|
||||||
- must start with letter or `_`
|
|
||||||
- `load_env` keyword argument of `Sanic`
|
|
||||||
- `sanic.exceptions.abort`
|
|
||||||
- `sanic.views.CompositionView`
|
|
||||||
- `sanic.response.StreamingHTTPResponse`
|
|
||||||
- *NOTE:* the `stream()` response method (where you pass a callable streaming function) has been deprecated and will be removed in v22.6. You should upgrade all streaming responses to the new style: https://sanicframework.org/en/guide/advanced/streaming.html#response-streaming
|
|
||||||
- [#2320](https://github.com/sanic-org/sanic/pull/2320) Remove app instance from Config for error handler setting
|
|
||||||
|
|
||||||
### Developer infrastructure
|
|
||||||
- [#2251](https://github.com/sanic-org/sanic/pull/2251) Change dev install command
|
|
||||||
- [#2286](https://github.com/sanic-org/sanic/pull/2286) Change codeclimate complexity threshold from 5 to 10
|
|
||||||
- [#2287](https://github.com/sanic-org/sanic/pull/2287) Update host test function names so they are not overwritten
|
|
||||||
- [#2292](https://github.com/sanic-org/sanic/pull/2292) Fail CI on error
|
|
||||||
- [#2311](https://github.com/sanic-org/sanic/pull/2311), [#2324](https://github.com/sanic-org/sanic/pull/2324) Do not run tests for draft PRs
|
|
||||||
- [#2336](https://github.com/sanic-org/sanic/pull/2336) Remove paths from coverage checks
|
|
||||||
- [#2338](https://github.com/sanic-org/sanic/pull/2338) Cleanup ports on tests
|
|
||||||
|
|
||||||
### Improved Documentation
|
|
||||||
- [#2269](https://github.com/sanic-org/sanic/pull/2269), [#2329](https://github.com/sanic-org/sanic/pull/2329), [#2333](https://github.com/sanic-org/sanic/pull/2333) Cleanup typos and fix language
|
|
||||||
|
|
||||||
### Miscellaneous
|
|
||||||
- [#2257](https://github.com/sanic-org/sanic/pull/2257), [#2294](https://github.com/sanic-org/sanic/pull/2294), [#2341](https://github.com/sanic-org/sanic/pull/2341) Add Python 3.10 support
|
|
||||||
- [#2279](https://github.com/sanic-org/sanic/pull/2279), [#2317](https://github.com/sanic-org/sanic/pull/2317), [#2322](https://github.com/sanic-org/sanic/pull/2322) Add/correct missing type annotations
|
|
||||||
- [#2305](https://github.com/sanic-org/sanic/pull/2305) Fix examples to use modern implementations
|
|
@ -1,50 +0,0 @@
|
|||||||
## Version 21.9.3
|
|
||||||
*Rerelease of v21.9.2 with some cleanup*
|
|
||||||
|
|
||||||
## Version 21.9.2
|
|
||||||
- [#2268](https://github.com/sanic-org/sanic/pull/2268) Make HTTP connections start in IDLE stage, avoiding delays and error messages
|
|
||||||
- [#2310](https://github.com/sanic-org/sanic/pull/2310) More consistent config setting with post-FALLBACK_ERROR_FORMAT apply
|
|
||||||
|
|
||||||
## Version 21.9.1
|
|
||||||
- [#2259](https://github.com/sanic-org/sanic/pull/2259) Allow non-conforming ErrorHandlers
|
|
||||||
|
|
||||||
## Version 21.9.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- [#2158](https://github.com/sanic-org/sanic/pull/2158), [#2248](https://github.com/sanic-org/sanic/pull/2248) Complete overhaul of I/O to websockets
|
|
||||||
- [#2160](https://github.com/sanic-org/sanic/pull/2160) Add new 17 signals into server and request lifecycles
|
|
||||||
- [#2162](https://github.com/sanic-org/sanic/pull/2162) Smarter `auto` fallback formatting upon exception
|
|
||||||
- [#2184](https://github.com/sanic-org/sanic/pull/2184) Introduce implementation for copying a Blueprint
|
|
||||||
- [#2200](https://github.com/sanic-org/sanic/pull/2200) Accept header parsing
|
|
||||||
- [#2207](https://github.com/sanic-org/sanic/pull/2207) Log remote address if available
|
|
||||||
- [#2209](https://github.com/sanic-org/sanic/pull/2209) Add convenience methods to BP groups
|
|
||||||
- [#2216](https://github.com/sanic-org/sanic/pull/2216) Add default messages to SanicExceptions
|
|
||||||
- [#2225](https://github.com/sanic-org/sanic/pull/2225) Type annotation convenience for annotated handlers with path parameters
|
|
||||||
- [#2236](https://github.com/sanic-org/sanic/pull/2236) Allow Falsey (but not-None) responses from route handlers
|
|
||||||
- [#2238](https://github.com/sanic-org/sanic/pull/2238) Add `exception` decorator to Blueprint Groups
|
|
||||||
- [#2244](https://github.com/sanic-org/sanic/pull/2244) Explicit static directive for serving file or dir (ex: `static(..., resource_type="file")`)
|
|
||||||
- [#2245](https://github.com/sanic-org/sanic/pull/2245) Close HTTP loop when connection task cancelled
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#2188](https://github.com/sanic-org/sanic/pull/2188) Fix the handling of the end of a chunked request
|
|
||||||
- [#2195](https://github.com/sanic-org/sanic/pull/2195) Resolve unexpected error handling on static requests
|
|
||||||
- [#2208](https://github.com/sanic-org/sanic/pull/2208) Make blueprint-based exceptions attach and trigger in a more intuitive manner
|
|
||||||
- [#2211](https://github.com/sanic-org/sanic/pull/2211) Fixed for handling exceptions of asgi app call
|
|
||||||
- [#2213](https://github.com/sanic-org/sanic/pull/2213) Fix bug where ws exceptions not being logged
|
|
||||||
- [#2231](https://github.com/sanic-org/sanic/pull/2231) Cleaner closing of tasks by using `abort()` in strategic places to avoid dangling sockets
|
|
||||||
- [#2247](https://github.com/sanic-org/sanic/pull/2247) Fix logging of auto-reload status in debug mode
|
|
||||||
- [#2246](https://github.com/sanic-org/sanic/pull/2246) Account for BP with exception handler but no routes
|
|
||||||
|
|
||||||
### Developer infrastructure
|
|
||||||
- [#2194](https://github.com/sanic-org/sanic/pull/2194) HTTP unit tests with raw client
|
|
||||||
- [#2199](https://github.com/sanic-org/sanic/pull/2199) Switch to codeclimate
|
|
||||||
- [#2214](https://github.com/sanic-org/sanic/pull/2214) Try Reopening Windows Tests
|
|
||||||
- [#2229](https://github.com/sanic-org/sanic/pull/2229) Refactor `HttpProtocol` into a base class
|
|
||||||
- [#2230](https://github.com/sanic-org/sanic/pull/2230) Refactor `server.py` into multi-file module
|
|
||||||
|
|
||||||
### Miscellaneous
|
|
||||||
- [#2173](https://github.com/sanic-org/sanic/pull/2173) Remove Duplicated Dependencies and PEP 517 Support
|
|
||||||
- [#2193](https://github.com/sanic-org/sanic/pull/2193), [#2196](https://github.com/sanic-org/sanic/pull/2196), [#2217](https://github.com/sanic-org/sanic/pull/2217) Type annotation changes
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
|||||||
## Version 22.12.0 🔷
|
|
||||||
|
|
||||||
_Current version_
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- [#2569](https://github.com/sanic-org/sanic/pull/2569) Add `JSONResponse` class with some convenient methods when updating a response object
|
|
||||||
- [#2598](https://github.com/sanic-org/sanic/pull/2598) Change `uvloop` requirement to `>=0.15.0`
|
|
||||||
- [#2609](https://github.com/sanic-org/sanic/pull/2609) Add compatibility with `websockets` v11.0
|
|
||||||
- [#2610](https://github.com/sanic-org/sanic/pull/2610) Kill server early on worker error
|
|
||||||
- Raise deadlock timeout to 30s
|
|
||||||
- [#2617](https://github.com/sanic-org/sanic/pull/2617) Scale number of running server workers
|
|
||||||
- [#2621](https://github.com/sanic-org/sanic/pull/2621) [#2634](https://github.com/sanic-org/sanic/pull/2634) Send `SIGKILL` on subsequent `ctrl+c` to force worker exit
|
|
||||||
- [#2622](https://github.com/sanic-org/sanic/pull/2622) Add API to restart all workers from the multiplexer
|
|
||||||
- [#2624](https://github.com/sanic-org/sanic/pull/2624) Default to `spawn` for all subprocesses unless specifically set:
|
|
||||||
```python
|
|
||||||
from sanic import Sanic
|
|
||||||
|
|
||||||
Sanic.start_method = "fork"
|
|
||||||
```
|
|
||||||
- [#2625](https://github.com/sanic-org/sanic/pull/2625) Filename normalisation of form-data/multipart file uploads
|
|
||||||
- [#2626](https://github.com/sanic-org/sanic/pull/2626) Move to HTTP Inspector:
|
|
||||||
- Remote access to inspect running Sanic instances
|
|
||||||
- TLS support for encrypted calls to Inspector
|
|
||||||
- Authentication to Inspector with API key
|
|
||||||
- Ability to extend Inspector with custom commands
|
|
||||||
- [#2632](https://github.com/sanic-org/sanic/pull/2632) Control order of restart operations
|
|
||||||
- [#2633](https://github.com/sanic-org/sanic/pull/2633) Move reload interval to class variable
|
|
||||||
- [#2636](https://github.com/sanic-org/sanic/pull/2636) Add `priority` to `register_middleware` method
|
|
||||||
- [#2639](https://github.com/sanic-org/sanic/pull/2639) Add `unquote` to `add_route` method
|
|
||||||
- [#2640](https://github.com/sanic-org/sanic/pull/2640) ASGI websockets to receive `text` or `bytes`
|
|
||||||
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2607](https://github.com/sanic-org/sanic/pull/2607) Force socket shutdown before close to allow rebinding
|
|
||||||
- [#2590](https://github.com/sanic-org/sanic/pull/2590) Use actual `StrEnum` in Python 3.11+
|
|
||||||
- [#2615](https://github.com/sanic-org/sanic/pull/2615) Ensure middleware executes only once per request timeout
|
|
||||||
- [#2627](https://github.com/sanic-org/sanic/pull/2627) Crash ASGI application on lifespan failure
|
|
||||||
- [#2635](https://github.com/sanic-org/sanic/pull/2635) Resolve error with low-level server creation on Windows
|
|
||||||
|
|
||||||
|
|
||||||
### Deprecations and Removals
|
|
||||||
|
|
||||||
- [#2608](https://github.com/sanic-org/sanic/pull/2608) [#2630](https://github.com/sanic-org/sanic/pull/2630) Signal conditions and triggers saved on `signal.extra`
|
|
||||||
- [#2626](https://github.com/sanic-org/sanic/pull/2626) Move to HTTP Inspector
|
|
||||||
- 🚨 *BREAKING CHANGE*: Moves the Inspector to a Sanic app from a simple TCP socket with a custom protocol
|
|
||||||
- *DEPRECATE*: The `--inspect*` commands have been deprecated in favor of `inspect ...` commands
|
|
||||||
- [#2628](https://github.com/sanic-org/sanic/pull/2628) Replace deprecated `distutils.strtobool`
|
|
||||||
|
|
||||||
|
|
||||||
### Developer infrastructure
|
|
||||||
|
|
||||||
- [#2612](https://github.com/sanic-org/sanic/pull/2612) Add CI testing for Python 3.11
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
|||||||
## Version 22.3.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- [#2347](https://github.com/sanic-org/sanic/pull/2347) API for multi-application server
|
|
||||||
- 🚨 *BREAKING CHANGE*: The old `sanic.worker.GunicornWorker` has been **removed**. To run Sanic with `gunicorn`, you should use it thru `uvicorn` [as described in their docs](https://www.uvicorn.org/#running-with-gunicorn).
|
|
||||||
- 🧁 *SIDE EFFECT*: Named background tasks are now supported, even in Python 3.7
|
|
||||||
- [#2357](https://github.com/sanic-org/sanic/pull/2357) Parse `Authorization` header as `Request.credentials`
|
|
||||||
- [#2361](https://github.com/sanic-org/sanic/pull/2361) Add config option to skip `Touchup` step in application startup
|
|
||||||
- [#2372](https://github.com/sanic-org/sanic/pull/2372) Updates to CLI help messaging
|
|
||||||
- [#2382](https://github.com/sanic-org/sanic/pull/2382) Downgrade warnings to backwater debug messages
|
|
||||||
- [#2396](https://github.com/sanic-org/sanic/pull/2396) Allow for `multidict` v0.6
|
|
||||||
- [#2401](https://github.com/sanic-org/sanic/pull/2401) Upgrade CLI catching for alternative application run types
|
|
||||||
- [#2402](https://github.com/sanic-org/sanic/pull/2402) Conditionally inject CLI arguments into factory
|
|
||||||
- [#2413](https://github.com/sanic-org/sanic/pull/2413) Add new start and stop event listeners to reloader process
|
|
||||||
- [#2414](https://github.com/sanic-org/sanic/pull/2414) Remove loop as required listener arg
|
|
||||||
- [#2415](https://github.com/sanic-org/sanic/pull/2415) Better exception for bad URL parsing
|
|
||||||
- [sanic-routing#47](https://github.com/sanic-org/sanic-routing/pull/47) Add a new extention parameter type: `<file:ext>`, `<file:ext=jpg>`, `<file:ext=jpg|png|gif|svg>`, `<file=int:ext>`, `<file=int:ext=jpg|png|gif|svg>`, `<file=float:ext=tar.gz>`
|
|
||||||
- 👶 *BETA FEATURE*: This feature will not work with `path` type matching, and is being released as a beta feature only.
|
|
||||||
- [sanic-routing#57](https://github.com/sanic-org/sanic-routing/pull/57) Change `register_pattern` to accept a `str` or `Pattern`
|
|
||||||
- [sanic-routing#58](https://github.com/sanic-org/sanic-routing/pull/58) Default matching on non-empty strings only, and new `strorempty` pattern type
|
|
||||||
- 🚨 *BREAKING CHANGE*: Previously a route with a dynamic string parameter (`/<foo>` or `/<foo:str>`) would match on any string, including empty strings. It will now **only** match a non-empty string. To retain the old behavior, you should use the new parameter type: `/<foo:strorempty>`.
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#2373](https://github.com/sanic-org/sanic/pull/2373) Remove `error_logger` on websockets
|
|
||||||
- [#2381](https://github.com/sanic-org/sanic/pull/2381) Fix newly assigned `None` in task registry
|
|
||||||
- [sanic-routing#52](https://github.com/sanic-org/sanic-routing/pull/52) Add type casting to regex route matching
|
|
||||||
- [sanic-routing#60](https://github.com/sanic-org/sanic-routing/pull/60) Add requirements check on regex routes (this resolves, for example, multiple static directories with differing `host` values)
|
|
||||||
|
|
||||||
### Deprecations and Removals
|
|
||||||
- [#2362](https://github.com/sanic-org/sanic/pull/2362) 22.3 Deprecations and changes
|
|
||||||
1. `debug=True` and `--debug` do _NOT_ automatically run `auto_reload`
|
|
||||||
2. Default error render is with plain text (browsers still get HTML by default because `auto` looks at headers)
|
|
||||||
3. `config` is required for `ErrorHandler.finalize`
|
|
||||||
4. `ErrorHandler.lookup` requires two positional args
|
|
||||||
5. Unused websocket protocol args removed
|
|
||||||
- [#2344](https://github.com/sanic-org/sanic/pull/2344) Deprecate loading of lowercase environment variables
|
|
||||||
|
|
||||||
### Developer infrastructure
|
|
||||||
- [#2363](https://github.com/sanic-org/sanic/pull/2363) Revert code coverage back to Codecov
|
|
||||||
- [#2405](https://github.com/sanic-org/sanic/pull/2405) Upgrade tests for `sanic-routing` changes
|
|
||||||
- [sanic-testing#35](https://github.com/sanic-org/sanic-testing/pull/35) Allow for httpx v0.22
|
|
||||||
|
|
||||||
### Improved Documentation
|
|
||||||
- [#2350](https://github.com/sanic-org/sanic/pull/2350) Fix link in README for ASGI
|
|
||||||
- [#2398](https://github.com/sanic-org/sanic/pull/2398) Document middleware on_request and on_response
|
|
||||||
- [#2409](https://github.com/sanic-org/sanic/pull/2409) Add missing documentation for `Request.respond`
|
|
||||||
|
|
||||||
### Miscellaneous
|
|
||||||
- [#2376](https://github.com/sanic-org/sanic/pull/2376) Fix typing for `ListenerMixin.listener`
|
|
||||||
- [#2383](https://github.com/sanic-org/sanic/pull/2383) Clear deprecation warning in `asyncio.wait`
|
|
||||||
- [#2387](https://github.com/sanic-org/sanic/pull/2387) Cleanup `__slots__` implementations
|
|
||||||
- [#2390](https://github.com/sanic-org/sanic/pull/2390) Clear deprecation warning in `asyncio.get_event_loop`
|
|
@ -1,54 +0,0 @@
|
|||||||
## Version 22.6.2
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2522](https://github.com/sanic-org/sanic/pull/2522) Always show server location in ASGI
|
|
||||||
|
|
||||||
## Version 22.6.1
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2477](https://github.com/sanic-org/sanic/pull/2477) Sanic static directory fails when folder name ends with ".."
|
|
||||||
|
|
||||||
|
|
||||||
## Version 22.6.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- [#2378](https://github.com/sanic-org/sanic/pull/2378) Introduce HTTP/3 and autogeneration of TLS certificates in `DEBUG` mode
|
|
||||||
- 👶 *EARLY RELEASE FEATURE*: Serving Sanic over HTTP/3 is an early release feature. It does not yet fully cover the HTTP/3 spec, but instead aims for feature parity with Sanic's existing HTTP/1.1 server. Websockets, WebTransport, push responses are examples of some features not yet implemented.
|
|
||||||
- 📦 *EXTRA REQUIREMENT*: Not all HTTP clients are capable of interfacing with HTTP/3 servers. You may need to install a [HTTP/3 capable client](https://curl.se/docs/http3.html).
|
|
||||||
- 📦 *EXTRA REQUIREMENT*: In order to use TLS autogeneration, you must install either [mkcert](https://github.com/FiloSottile/mkcert) or [trustme](https://github.com/python-trio/trustme).
|
|
||||||
- [#2416](https://github.com/sanic-org/sanic/pull/2416) Add message to `task.cancel`
|
|
||||||
- [#2420](https://github.com/sanic-org/sanic/pull/2420) Add exception aliases for more consistent naming with standard HTTP response types (`BadRequest`, `MethodNotAllowed`, `RangeNotSatisfiable`)
|
|
||||||
- [#2432](https://github.com/sanic-org/sanic/pull/2432) Expose ASGI `scope` as a property on the `Request` object
|
|
||||||
- [#2438](https://github.com/sanic-org/sanic/pull/2438) Easier access to websocket class for annotation: `from sanic import Websocket`
|
|
||||||
- [#2439](https://github.com/sanic-org/sanic/pull/2439) New API for reading form values with options: `Request.get_form`
|
|
||||||
- [#2445](https://github.com/sanic-org/sanic/pull/2445) Add custom `loads` function
|
|
||||||
- [#2447](https://github.com/sanic-org/sanic/pull/2447), [#2486](https://github.com/sanic-org/sanic/pull/2486) Improved API to support setting cache control headers
|
|
||||||
- [#2453](https://github.com/sanic-org/sanic/pull/2453) Move verbosity filtering to logger
|
|
||||||
- [#2475](https://github.com/sanic-org/sanic/pull/2475) Expose getter for current request using `Request.get_current()`
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#2448](https://github.com/sanic-org/sanic/pull/2448) Fix to allow running with `pythonw.exe` or places where there is no `sys.stdout`
|
|
||||||
- [#2451](https://github.com/sanic-org/sanic/pull/2451) Trigger `http.lifecycle.request` signal in ASGI mode
|
|
||||||
- [#2455](https://github.com/sanic-org/sanic/pull/2455) Resolve typing of stacked route definitions
|
|
||||||
- [#2463](https://github.com/sanic-org/sanic/pull/2463) Properly catch websocket CancelledError in websocket handler in Python 3.7
|
|
||||||
|
|
||||||
### Deprecations and Removals
|
|
||||||
- [#2487](https://github.com/sanic-org/sanic/pull/2487) v22.6 deprecations and changes
|
|
||||||
1. Optional application registry
|
|
||||||
1. Execution of custom handlers after some part of response was sent
|
|
||||||
1. Configuring fallback handlers on the `ErrorHandler`
|
|
||||||
1. Custom `LOGO` setting
|
|
||||||
1. `sanic.response.stream`
|
|
||||||
1. `AsyncioServer.init`
|
|
||||||
|
|
||||||
### Developer infrastructure
|
|
||||||
- [#2449](https://github.com/sanic-org/sanic/pull/2449) Clean up `black` and `isort` config
|
|
||||||
- [#2479](https://github.com/sanic-org/sanic/pull/2479) Fix some flappy tests
|
|
||||||
|
|
||||||
### Improved Documentation
|
|
||||||
- [#2461](https://github.com/sanic-org/sanic/pull/2461) Update example to match current application naming standards
|
|
||||||
- [#2466](https://github.com/sanic-org/sanic/pull/2466) Better type annotation for `Extend`
|
|
||||||
- [#2485](https://github.com/sanic-org/sanic/pull/2485) Improved help messages in CLI
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
|||||||
## Version 22.9.1
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- [#2585](https://github.com/sanic-org/sanic/pull/2585) Improved error message when no applications have been registered
|
|
||||||
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2578](https://github.com/sanic-org/sanic/pull/2578) Add certificate loader for in process certificate creation
|
|
||||||
- [#2591](https://github.com/sanic-org/sanic/pull/2591) Do not use sentinel identity for `spawn` compatibility
|
|
||||||
- [#2592](https://github.com/sanic-org/sanic/pull/2592) Fix properties in nested blueprint groups
|
|
||||||
- [#2595](https://github.com/sanic-org/sanic/pull/2595) Introduce sleep interval on new worker reloader
|
|
||||||
|
|
||||||
|
|
||||||
### Deprecations and Removals
|
|
||||||
|
|
||||||
|
|
||||||
### Developer infrastructure
|
|
||||||
|
|
||||||
- [#2588](https://github.com/sanic-org/sanic/pull/2588) Markdown templates on issue forms
|
|
||||||
|
|
||||||
|
|
||||||
### Improved Documentation
|
|
||||||
|
|
||||||
- [#2556](https://github.com/sanic-org/sanic/pull/2556) v22.9 documentation
|
|
||||||
- [#2582](https://github.com/sanic-org/sanic/pull/2582) Cleanup documentation on Windows support
|
|
||||||
|
|
||||||
|
|
||||||
## Version 22.9.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- [#2445](https://github.com/sanic-org/sanic/pull/2445) Add custom loads function
|
|
||||||
- [#2490](https://github.com/sanic-org/sanic/pull/2490) Make `WebsocketImplProtocol` async iterable
|
|
||||||
- [#2499](https://github.com/sanic-org/sanic/pull/2499) Sanic Server WorkerManager refactor
|
|
||||||
- [#2506](https://github.com/sanic-org/sanic/pull/2506) Use `pathlib` for path resolution (for static file serving)
|
|
||||||
- [#2508](https://github.com/sanic-org/sanic/pull/2508) Use `path.parts` instead of `match` (for static file serving)
|
|
||||||
- [#2513](https://github.com/sanic-org/sanic/pull/2513) Better request cancel handling
|
|
||||||
- [#2516](https://github.com/sanic-org/sanic/pull/2516) Add request properties for HTTP method info:
|
|
||||||
- `request.is_safe`
|
|
||||||
- `request.is_idempotent`
|
|
||||||
- `request.is_cacheable`
|
|
||||||
- *See* [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) *for more information about when these apply*
|
|
||||||
- [#2522](https://github.com/sanic-org/sanic/pull/2522) Always show server location in ASGI
|
|
||||||
- [#2526](https://github.com/sanic-org/sanic/pull/2526) Cache control support for static files for returning 304 when appropriate
|
|
||||||
- [#2533](https://github.com/sanic-org/sanic/pull/2533) Refactor `_static_request_handler`
|
|
||||||
- [#2540](https://github.com/sanic-org/sanic/pull/2540) Add signals before and after handler execution
|
|
||||||
- `http.handler.before`
|
|
||||||
- `http.handler.after`
|
|
||||||
- [#2542](https://github.com/sanic-org/sanic/pull/2542) Add *[redacted]* to CLI :)
|
|
||||||
- [#2546](https://github.com/sanic-org/sanic/pull/2546) Add deprecation warning filter
|
|
||||||
- [#2550](https://github.com/sanic-org/sanic/pull/2550) Middleware priority and performance enhancements
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2495](https://github.com/sanic-org/sanic/pull/2495) Prevent directory traversion with static files
|
|
||||||
- [#2515](https://github.com/sanic-org/sanic/pull/2515) Do not apply double slash to paths in certain static dirs in Blueprints
|
|
||||||
|
|
||||||
### Deprecations and Removals
|
|
||||||
|
|
||||||
- [#2525](https://github.com/sanic-org/sanic/pull/2525) Warn on duplicate route names, will be prevented outright in v23.3
|
|
||||||
- [#2537](https://github.com/sanic-org/sanic/pull/2537) Raise warning and deprecation notice on duplicate exceptions, will be prevented outright in v23.3
|
|
||||||
|
|
||||||
### Developer infrastructure
|
|
||||||
|
|
||||||
- [#2504](https://github.com/sanic-org/sanic/pull/2504) Cleanup test suite
|
|
||||||
- [#2505](https://github.com/sanic-org/sanic/pull/2505) Replace Unsupported Python Version Number from the Contributing Doc
|
|
||||||
- [#2530](https://github.com/sanic-org/sanic/pull/2530) Do not include tests folder in installed package resolver
|
|
||||||
|
|
||||||
### Improved Documentation
|
|
||||||
|
|
||||||
- [#2502](https://github.com/sanic-org/sanic/pull/2502) Fix a few typos
|
|
||||||
- [#2517](https://github.com/sanic-org/sanic/pull/2517) [#2536](https://github.com/sanic-org/sanic/pull/2536) Add some type hints
|
|
@ -1,53 +0,0 @@
|
|||||||
## Version 23.3.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- [#2545](https://github.com/sanic-org/sanic/pull/2545) Standardize init of exceptions for more consistent control of HTTP responses using exceptions
|
|
||||||
- [#2606](https://github.com/sanic-org/sanic/pull/2606) Decode headers as UTF-8 also in ASGI
|
|
||||||
- [#2646](https://github.com/sanic-org/sanic/pull/2646) Separate ASGI request and lifespan callables
|
|
||||||
- [#2659](https://github.com/sanic-org/sanic/pull/2659) Use ``FALLBACK_ERROR_FORMAT`` for handlers that return ``empty()``
|
|
||||||
- [#2662](https://github.com/sanic-org/sanic/pull/2662) Add basic file browser (HTML page) and auto-index serving
|
|
||||||
- [#2667](https://github.com/sanic-org/sanic/pull/2667) Nicer traceback formatting (HTML page)
|
|
||||||
- [#2668](https://github.com/sanic-org/sanic/pull/2668) Smarter error page rendering format selection; more reliant upon header and "common sense" defaults
|
|
||||||
- [#2680](https://github.com/sanic-org/sanic/pull/2680) Check the status of socket before shutting down with ``SHUT_RDWR``
|
|
||||||
- [#2687](https://github.com/sanic-org/sanic/pull/2687) Refresh ``Request.accept`` functionality to be more performant and spec-compliant
|
|
||||||
- [#2696](https://github.com/sanic-org/sanic/pull/2696) Add header accessors as properties
|
|
||||||
```
|
|
||||||
Example-Field: Foo, Bar
|
|
||||||
Example-Field: Baz
|
|
||||||
```
|
|
||||||
```python
|
|
||||||
request.headers.example_field == "Foo, Bar,Baz"
|
|
||||||
```
|
|
||||||
- [#2700](https://github.com/sanic-org/sanic/pull/2700) Simpler CLI targets
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ sanic path.to.module:app # global app instance
|
|
||||||
$ sanic path.to.module:create_app # factory pattern
|
|
||||||
$ sanic ./path/to/directory/ # simple serve
|
|
||||||
```
|
|
||||||
- [#2701](https://github.com/sanic-org/sanic/pull/2701) API to define a number of workers in managed processes
|
|
||||||
- [#2704](https://github.com/sanic-org/sanic/pull/2704) Add convenience for dynamic changes to routing
|
|
||||||
- [#2706](https://github.com/sanic-org/sanic/pull/2706) Add convenience methods for cookie creation and deletion
|
|
||||||
|
|
||||||
```python
|
|
||||||
response = text("...")
|
|
||||||
response.add_cookie("test", "It worked!", domain=".yummy-yummy-cookie.com")
|
|
||||||
```
|
|
||||||
- [#2707](https://github.com/sanic-org/sanic/pull/2707) Simplified ``parse_content_header`` escaping to be RFC-compliant and remove outdated FF hack
|
|
||||||
- [#2710](https://github.com/sanic-org/sanic/pull/2710) Stricter charset handling and escaping of request URLs
|
|
||||||
- [#2711](https://github.com/sanic-org/sanic/pull/2711) Consume body on ``DELETE`` by default
|
|
||||||
- [#2719](https://github.com/sanic-org/sanic/pull/2719) Allow ``password`` to be passed to TLS context
|
|
||||||
- [#2720](https://github.com/sanic-org/sanic/pull/2720) Skip middleware on ``RequestCancelled``
|
|
||||||
- [#2721](https://github.com/sanic-org/sanic/pull/2721) Change access logging format to ``%s``
|
|
||||||
- [#2722](https://github.com/sanic-org/sanic/pull/2722) Add ``CertLoader`` as application option for directly controlling ``SSLContext`` objects
|
|
||||||
- [#2725](https://github.com/sanic-org/sanic/pull/2725) Worker sync state tolerance on race condition
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#2651](https://github.com/sanic-org/sanic/pull/2651) ASGI websocket to pass thru bytes as is
|
|
||||||
- [#2697](https://github.com/sanic-org/sanic/pull/2697) Fix comparison between datetime aware and naive in ``file`` when using ``If-Modified-Since``
|
|
||||||
|
|
||||||
### Deprecations and Removals
|
|
||||||
- [#2666](https://github.com/sanic-org/sanic/pull/2666) Remove deprecated ``__blueprintname__`` property
|
|
||||||
|
|
||||||
### Improved Documentation
|
|
||||||
- [#2712](https://github.com/sanic-org/sanic/pull/2712) Improved example using ``'https'`` to create the redirect
|
|
@ -1,33 +0,0 @@
|
|||||||
## Version 23.6.0 🔶
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- [#2670](https://github.com/sanic-org/sanic/pull/2670) Increase `KEEP_ALIVE_TIMEOUT` default to 120 seconds
|
|
||||||
- [#2716](https://github.com/sanic-org/sanic/pull/2716) Adding allow route overwrite option in blueprint
|
|
||||||
- [#2724](https://github.com/sanic-org/sanic/pull/2724) and [#2792](https://github.com/sanic-org/sanic/pull/2792) Add a new exception signal for ALL exceptions raised anywhere in application
|
|
||||||
- [#2727](https://github.com/sanic-org/sanic/pull/2727) Add name prefixing to BP groups
|
|
||||||
- [#2754](https://github.com/sanic-org/sanic/pull/2754) Update request type on middleware types
|
|
||||||
- [#2770](https://github.com/sanic-org/sanic/pull/2770) Better exception message on startup time application induced import error
|
|
||||||
- [#2776](https://github.com/sanic-org/sanic/pull/2776) Set multiprocessing start method early
|
|
||||||
- [#2785](https://github.com/sanic-org/sanic/pull/2785) Add custom typing to config and ctx objects
|
|
||||||
- [#2790](https://github.com/sanic-org/sanic/pull/2790) Add `request.client_ip`
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#2728](https://github.com/sanic-org/sanic/pull/2728) Fix traversals for intended results
|
|
||||||
- [#2729](https://github.com/sanic-org/sanic/pull/2729) Handle case when headers argument of ResponseStream constructor is None
|
|
||||||
- [#2737](https://github.com/sanic-org/sanic/pull/2737) Fix type annotation for `JSONREsponse` default content type
|
|
||||||
- [#2740](https://github.com/sanic-org/sanic/pull/2740) Use Sanic's serializer for JSON responses in the Inspector
|
|
||||||
- [#2760](https://github.com/sanic-org/sanic/pull/2760) Support for `Request.get_current` in ASGI mode
|
|
||||||
- [#2773](https://github.com/sanic-org/sanic/pull/2773) Alow Blueprint routes to explicitly define error_format
|
|
||||||
- [#2774](https://github.com/sanic-org/sanic/pull/2774) Resolve headers on different renderers
|
|
||||||
- [#2782](https://github.com/sanic-org/sanic/pull/2782) Resolve pypy compatibility issues
|
|
||||||
|
|
||||||
### Deprecations and Removals
|
|
||||||
- [#2777](https://github.com/sanic-org/sanic/pull/2777) Remove Python 3.7 support
|
|
||||||
|
|
||||||
### Developer infrastructure
|
|
||||||
- [#2766](https://github.com/sanic-org/sanic/pull/2766) Unpin setuptools version
|
|
||||||
- [#2779](https://github.com/sanic-org/sanic/pull/2779) Run keep alive tests in loop to get available port
|
|
||||||
|
|
||||||
### Improved Documentation
|
|
||||||
- [#2741](https://github.com/sanic-org/sanic/pull/2741) Better documentation examples about running Sanic
|
|
||||||
From that list, the items to highlight in the release notes:
|
|
23
docs/static_files.md
Normal file
23
docs/static_files.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Static Files
|
||||||
|
|
||||||
|
Static files and directories, such as an image file, are served by Sanic when
|
||||||
|
registered with the `app.static` method. The method takes an endpoint URL and a
|
||||||
|
filename. The file specified will then be accessible via the given endpoint.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sanic import Sanic
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
# Serves files from the static folder to the URL /static
|
||||||
|
app.static('/static', './static')
|
||||||
|
|
||||||
|
# Serves the file /home/ubuntu/test.png when the URL /the_best.png
|
||||||
|
# is requested
|
||||||
|
app.static('/the_best.png', '/home/ubuntu/test.png')
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Deploying](deploying.md)
|
||||||
|
|
||||||
|
**Next:** [Middleware](middleware.md)
|
55
docs/testing.md
Normal file
55
docs/testing.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Testing
|
||||||
|
|
||||||
|
Sanic endpoints can be tested locally using the `sanic.utils` module, which
|
||||||
|
depends on the additional [aiohttp](https://aiohttp.readthedocs.io/en/stable/)
|
||||||
|
library. The `sanic_endpoint_test` function runs a local server, issues a
|
||||||
|
configurable request to an endpoint, and returns the result. It takes the
|
||||||
|
following arguments:
|
||||||
|
|
||||||
|
- `app` An instance of a Sanic app.
|
||||||
|
- `method` *(default `'get'`)* A string representing the HTTP method to use.
|
||||||
|
- `uri` *(default `'/'`)* A string representing the endpoint to test.
|
||||||
|
- `gather_request` *(default `True`)* A boolean which determines whether the
|
||||||
|
original request will be returned by the function. If set to `True`, the
|
||||||
|
return value is a tuple of `(request, response)`, if `False` only the
|
||||||
|
response is returned.
|
||||||
|
- `loop` *(default `None`)* The event loop to use.
|
||||||
|
- `debug` *(default `False`)* A boolean which determines whether to run the
|
||||||
|
server in debug mode.
|
||||||
|
|
||||||
|
The function further takes the `*request_args` and `**request_kwargs`, which
|
||||||
|
are passed directly to the aiohttp ClientSession request. For example, to
|
||||||
|
supply data with a GET request, `method` would be `get` and the keyword
|
||||||
|
argument `params={'value', 'key'}` would be supplied. More information about
|
||||||
|
the available arguments to aiohttp can be found
|
||||||
|
[in the documentation for ClientSession](https://aiohttp.readthedocs.io/en/stable/client_reference.html#client-session).
|
||||||
|
|
||||||
|
Below is a complete example of an endpoint test,
|
||||||
|
using [pytest](http://doc.pytest.org/en/latest/). The test checks that the
|
||||||
|
`/challenge` endpoint responds to a GET request with a supplied challenge
|
||||||
|
string.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pytest
|
||||||
|
import aiohttp
|
||||||
|
from sanic.utils import sanic_endpoint_test
|
||||||
|
|
||||||
|
# Import the Sanic app, usually created with Sanic(__name__)
|
||||||
|
from external_server import app
|
||||||
|
|
||||||
|
def test_endpoint_challenge():
|
||||||
|
# Create the challenge data
|
||||||
|
request_data = {'challenge': 'dummy_challenge'}
|
||||||
|
|
||||||
|
# Send the request to the endpoint, using the default `get` method
|
||||||
|
request, response = sanic_endpoint_test(app,
|
||||||
|
uri='/challenge',
|
||||||
|
params=request_data)
|
||||||
|
|
||||||
|
# Assert that the server responds with the challenge string
|
||||||
|
assert response.text == request_data['challenge']
|
||||||
|
```
|
||||||
|
|
||||||
|
**Previous:** [Custom protocols](custom_protocol.md)
|
||||||
|
|
||||||
|
**Next:** [Sanic extensions](extensions.md)
|
@ -1,8 +1,11 @@
|
|||||||
FROM sanicframework/sanic:LTS
|
FROM python:3.5
|
||||||
|
MAINTAINER Channel Cat <channelcat@gmail.com>
|
||||||
|
|
||||||
RUN mkdir /srv
|
ADD . /code
|
||||||
COPY . /srv
|
RUN pip3 install git+https://github.com/channelcat/sanic
|
||||||
|
|
||||||
WORKDIR /srv
|
EXPOSE 8000
|
||||||
|
|
||||||
CMD ["sanic", "simple_server.app"]
|
WORKDIR /code
|
||||||
|
|
||||||
|
CMD ["python", "simple_server.py"]
|
@ -1,19 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
async def notify_server_started_after_five_seconds():
|
|
||||||
await asyncio.sleep(5)
|
|
||||||
print("Server successfully started!")
|
|
||||||
|
|
||||||
|
|
||||||
app.add_task(notify_server_started_after_five_seconds())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000)
|
|
28
examples/aiohttp_example.py
Normal file
28
examples/aiohttp_example.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
async def fetch(session, url):
|
||||||
|
"""
|
||||||
|
Use session object to perform 'get' request on url
|
||||||
|
"""
|
||||||
|
async with session.get(url) as response:
|
||||||
|
return await response.json()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
async def test(request):
|
||||||
|
"""
|
||||||
|
Download and serve example JSON
|
||||||
|
"""
|
||||||
|
url = "https://api.github.com/repos/channelcat/sanic"
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
response = await fetch(session, url)
|
||||||
|
return json(response)
|
||||||
|
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=8000, workers=2)
|
@ -1,29 +0,0 @@
|
|||||||
from random import randint
|
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import text
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
@app.middleware("request")
|
|
||||||
def append_request(request):
|
|
||||||
request.ctx.num = randint(0, 100)
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/pop")
|
|
||||||
def pop_handler(request):
|
|
||||||
return text(request.ctx.num)
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/key_exist")
|
|
||||||
def key_exist_handler(request):
|
|
||||||
# Check the key is exist or not
|
|
||||||
if hasattr(request.ctx, "num"):
|
|
||||||
return text("num exist in request")
|
|
||||||
|
|
||||||
return text("num does not exist in request")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000, debug=True)
|
|
@ -1,44 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import json
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
def check_request_for_authorization_status(request):
|
|
||||||
# Note: Define your check, for instance cookie, session.
|
|
||||||
flag = True
|
|
||||||
return flag
|
|
||||||
|
|
||||||
|
|
||||||
def authorized(f):
|
|
||||||
@wraps(f)
|
|
||||||
async def decorated_function(request, *args, **kwargs):
|
|
||||||
# run some method that checks the request
|
|
||||||
# for the client's authorization status
|
|
||||||
is_authorized = check_request_for_authorization_status(request)
|
|
||||||
|
|
||||||
if is_authorized:
|
|
||||||
# the user is authorized.
|
|
||||||
# run the handler method and return the response
|
|
||||||
response = await f(request, *args, **kwargs)
|
|
||||||
return response
|
|
||||||
else:
|
|
||||||
# the user is not authorized.
|
|
||||||
return json({"status": "not_authorized"}, 403)
|
|
||||||
|
|
||||||
return decorated_function
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
@authorized
|
|
||||||
async def test(request):
|
|
||||||
return json({"status": "authorized"})
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000)
|
|
@ -1,54 +0,0 @@
|
|||||||
from sanic import Blueprint, Sanic
|
|
||||||
from sanic.response import text
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Demonstrates that blueprint request middleware are executed in the order they
|
|
||||||
are added. And blueprint response middleware are executed in _reverse_ order.
|
|
||||||
On a valid request, it should print "1 2 3 6 5 4" to terminal
|
|
||||||
"""
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
bp = Blueprint("bp_example")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.on_request
|
|
||||||
def request_middleware_1(request):
|
|
||||||
print("1")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.on_request
|
|
||||||
def request_middleware_2(request):
|
|
||||||
print("2")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.on_request
|
|
||||||
def request_middleware_3(request):
|
|
||||||
print("3")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.on_response
|
|
||||||
def resp_middleware_4(request, response):
|
|
||||||
print("4")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.on_response
|
|
||||||
def resp_middleware_5(request, response):
|
|
||||||
print("5")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.on_response
|
|
||||||
def resp_middleware_6(request, response):
|
|
||||||
print("6")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/")
|
|
||||||
def pop_handler(request):
|
|
||||||
return text("hello world")
|
|
||||||
|
|
||||||
|
|
||||||
app.blueprint(bp, url_prefix="/bp")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False)
|
|
@ -1,41 +1,24 @@
|
|||||||
from sanic import Blueprint, Sanic
|
from sanic import Sanic
|
||||||
from sanic.response import file, json
|
from sanic import Blueprint
|
||||||
|
from sanic.response import json, text
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
app = Sanic(__name__)
|
||||||
blueprint = Blueprint("bp_example", url_prefix="/my_blueprint")
|
blueprint = Blueprint('name', url_prefix='/my_blueprint')
|
||||||
blueprint2 = Blueprint("bp_example2", url_prefix="/my_blueprint2")
|
blueprint2 = Blueprint('name', url_prefix='/my_blueprint2')
|
||||||
blueprint3 = Blueprint("bp_example3", url_prefix="/my_blueprint3")
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/foo")
|
@blueprint.route('/foo')
|
||||||
async def foo(request):
|
async def foo(request):
|
||||||
return json({"msg": "hi from blueprint"})
|
return json({'msg': 'hi from blueprint'})
|
||||||
|
|
||||||
|
|
||||||
@blueprint2.route("/foo")
|
@blueprint2.route('/foo')
|
||||||
async def foo2(request):
|
async def foo2(request):
|
||||||
return json({"msg": "hi from blueprint2"})
|
return json({'msg': 'hi from blueprint2'})
|
||||||
|
|
||||||
|
|
||||||
@blueprint3.route("/foo")
|
app.register_blueprint(blueprint)
|
||||||
async def index(request):
|
app.register_blueprint(blueprint2)
|
||||||
return await file("websocket.html")
|
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=8000, debug=True)
|
||||||
@app.websocket("/feed")
|
|
||||||
async def foo3(request, ws):
|
|
||||||
while True:
|
|
||||||
data = "hello!"
|
|
||||||
print("Sending: " + data)
|
|
||||||
await ws.send(data)
|
|
||||||
data = await ws.recv()
|
|
||||||
print("Received: " + data)
|
|
||||||
|
|
||||||
|
|
||||||
app.blueprint(blueprint)
|
|
||||||
app.blueprint(blueprint2)
|
|
||||||
app.blueprint(blueprint3)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=9999, debug=True)
|
|
||||||
|
41
examples/cache_example.py
Normal file
41
examples/cache_example.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
"""
|
||||||
|
Example of caching using aiocache package. To run it you will need a Redis
|
||||||
|
instance running in localhost:6379.
|
||||||
|
|
||||||
|
Running this example you will see that the first call lasts 3 seconds and
|
||||||
|
the rest are instant because the value is retrieved from the Redis.
|
||||||
|
|
||||||
|
If you want more info about the package check
|
||||||
|
https://github.com/argaen/aiocache
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import aiocache
|
||||||
|
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import json
|
||||||
|
from sanic.log import log
|
||||||
|
from aiocache import cached
|
||||||
|
from aiocache.serializers import JsonSerializer
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
aiocache.settings.set_defaults(
|
||||||
|
class_="aiocache.RedisCache"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@cached(key="my_custom_key", serializer=JsonSerializer())
|
||||||
|
async def expensive_call():
|
||||||
|
log.info("Expensive has been called")
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
return {"test": True}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
async def test(request):
|
||||||
|
log.info("Received GET /")
|
||||||
|
return json(await expensive_call())
|
||||||
|
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=8000, loop=asyncio.get_event_loop())
|
@ -1,22 +0,0 @@
|
|||||||
from asyncio import sleep
|
|
||||||
|
|
||||||
from sanic import Sanic, response
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("DelayedResponseApp", strict_slashes=True)
|
|
||||||
app.config.AUTO_EXTEND = False
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
async def handler(request):
|
|
||||||
return response.redirect("/sleep/3")
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/sleep/<t:float>")
|
|
||||||
async def handler2(request, t=0.3):
|
|
||||||
await sleep(t)
|
|
||||||
return response.text(f"Slept {t:.1f} seconds.\n")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000)
|
|
@ -1,23 +1,23 @@
|
|||||||
"""
|
"""
|
||||||
Example intercepting uncaught exceptions using Sanic's error handler framework.
|
Example intercepting uncaught exceptions using Sanic's error handler framework.
|
||||||
|
|
||||||
This may be useful for developers wishing to use Sentry, Airbrake, etc.
|
This may be useful for developers wishing to use Sentry, Airbrake, etc.
|
||||||
or a custom system to log and monitor unexpected errors in production.
|
or a custom system to log and monitor unexpected errors in production.
|
||||||
|
|
||||||
First we create our own class inheriting from Handler in sanic.exceptions,
|
First we create our own class inheriting from Handler in sanic.exceptions,
|
||||||
and pass in an instance of it when we create our Sanic instance. Inside this
|
and pass in an instance of it when we create our Sanic instance. Inside this
|
||||||
class' default handler, we can do anything including sending exceptions to
|
class' default handler, we can do anything including sending exceptions to
|
||||||
an external service.
|
an external service.
|
||||||
"""
|
"""
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import Handler, SanicException
|
||||||
from sanic.handlers import ErrorHandler
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Imports and code relevant for our CustomHandler class
|
Imports and code relevant for our CustomHandler class
|
||||||
(Ordinarily this would be in a separate file)
|
(Ordinarily this would be in a separate file)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class CustomHandler(ErrorHandler):
|
class CustomHandler(Handler):
|
||||||
|
|
||||||
def default(self, request, exception):
|
def default(self, request, exception):
|
||||||
# Here, we have access to the exception object
|
# Here, we have access to the exception object
|
||||||
# and can do anything with it (log, send to external service, etc)
|
# and can do anything with it (log, send to external service, etc)
|
||||||
@ -38,18 +38,20 @@ server's error_handler to an instance of our CustomHandler
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
handler = CustomHandler()
|
handler = CustomHandler(sanic=app)
|
||||||
app = Sanic("Example", error_handler=handler)
|
app.error_handler = handler
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
# Here, something occurs which causes an unexpected exception
|
# Here, something occurs which causes an unexpected exception
|
||||||
# This exception will flow to our custom handler.
|
# This exception will flow to our custom handler.
|
||||||
raise SanicException("You Broke It!")
|
1 / 0
|
||||||
|
return json({"test": True})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
app.run(host="0.0.0.0", port=8000, debug=True)
|
||||||
app.run(host="0.0.0.0", port=8000, debug=True)
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
from sanic import Sanic, response
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
async def test(request):
|
|
||||||
return response.json({"test": True})
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000)
|
|
@ -1,73 +0,0 @@
|
|||||||
from sanic import Sanic, response, text
|
|
||||||
from sanic.handlers import ErrorHandler
|
|
||||||
from sanic.server.async_server import AsyncioServer
|
|
||||||
|
|
||||||
|
|
||||||
HTTP_PORT = 9999
|
|
||||||
HTTPS_PORT = 8888
|
|
||||||
|
|
||||||
http = Sanic("http")
|
|
||||||
http.config.SERVER_NAME = f"localhost:{HTTP_PORT}"
|
|
||||||
https = Sanic("https")
|
|
||||||
https.config.SERVER_NAME = f"localhost:{HTTPS_PORT}"
|
|
||||||
|
|
||||||
|
|
||||||
@https.get("/foo")
|
|
||||||
def foo(request):
|
|
||||||
return text("foo")
|
|
||||||
|
|
||||||
|
|
||||||
@https.get("/bar")
|
|
||||||
def bar(request):
|
|
||||||
return text("bar")
|
|
||||||
|
|
||||||
|
|
||||||
@http.get("/<path:path>")
|
|
||||||
def proxy(request, path):
|
|
||||||
url = request.app.url_for(
|
|
||||||
"proxy",
|
|
||||||
path=path,
|
|
||||||
_server=https.config.SERVER_NAME,
|
|
||||||
_external=True,
|
|
||||||
_scheme="https",
|
|
||||||
)
|
|
||||||
return response.redirect(url)
|
|
||||||
|
|
||||||
|
|
||||||
@https.main_process_start
|
|
||||||
async def start(app, _):
|
|
||||||
http_server = await http.create_server(
|
|
||||||
port=HTTP_PORT, return_asyncio_server=True
|
|
||||||
)
|
|
||||||
app.add_task(runner(http, http_server))
|
|
||||||
app.ctx.http_server = http_server
|
|
||||||
app.ctx.http = http
|
|
||||||
|
|
||||||
|
|
||||||
@https.main_process_stop
|
|
||||||
async def stop(app, _):
|
|
||||||
await app.ctx.http_server.before_stop()
|
|
||||||
await app.ctx.http_server.close()
|
|
||||||
for connection in app.ctx.http_server.connections:
|
|
||||||
connection.close_if_idle()
|
|
||||||
await app.ctx.http_server.after_stop()
|
|
||||||
app.ctx.http = False
|
|
||||||
|
|
||||||
|
|
||||||
async def runner(app: Sanic, app_server: AsyncioServer):
|
|
||||||
app.is_running = True
|
|
||||||
try:
|
|
||||||
app.signalize()
|
|
||||||
app.finalize()
|
|
||||||
ErrorHandler.finalize(app.error_handler)
|
|
||||||
app_server.init = True
|
|
||||||
|
|
||||||
await app_server.before_start()
|
|
||||||
await app_server.after_start()
|
|
||||||
await app_server.serve_forever()
|
|
||||||
finally:
|
|
||||||
app.is_running = False
|
|
||||||
app.is_stopping = True
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
https.run(port=HTTPS_PORT, debug=True)
|
|
18
examples/jinja_example.py
Normal file
18
examples/jinja_example.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
## To use this example:
|
||||||
|
# curl -d '{"name": "John Doe"}' localhost:8000
|
||||||
|
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import html
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
|
template = Template('Hello {{ name }}!')
|
||||||
|
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
async def test(request):
|
||||||
|
data = request.json
|
||||||
|
return html(template.render(**data))
|
||||||
|
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=8000)
|
@ -1,30 +1,24 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.response import json
|
from sanic.response import json
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
app = Sanic("Example")
|
app = Sanic(__name__)
|
||||||
|
|
||||||
sem = None
|
sem = None
|
||||||
|
|
||||||
|
def init(sanic, loop):
|
||||||
@app.before_server_start
|
|
||||||
def init(sanic, _):
|
|
||||||
global sem
|
global sem
|
||||||
concurrency_per_worker = 4
|
CONCURRENCY_PER_WORKER = 4
|
||||||
sem = asyncio.Semaphore(concurrency_per_worker)
|
sem = asyncio.Semaphore(CONCURRENCY_PER_WORKER, loop=loop)
|
||||||
|
|
||||||
|
|
||||||
async def bounded_fetch(session, url):
|
async def bounded_fetch(session, url):
|
||||||
"""
|
"""
|
||||||
Use session object to perform 'get' request on url
|
Use session object to perform 'get' request on url
|
||||||
"""
|
"""
|
||||||
async with sem:
|
async with sem, session.get(url) as response:
|
||||||
response = await session.get(url)
|
return await response.json()
|
||||||
return response.json()
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
@ -32,12 +26,11 @@ async def test(request):
|
|||||||
"""
|
"""
|
||||||
Download and serve example JSON
|
Download and serve example JSON
|
||||||
"""
|
"""
|
||||||
url = "https://api.github.com/repos/sanic-org/sanic"
|
url = "https://api.github.com/repos/channelcat/sanic"
|
||||||
|
|
||||||
async with httpx.AsyncClient() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
response = await bounded_fetch(session, url)
|
response = await bounded_fetch(session, url)
|
||||||
return json(response)
|
return json(response)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
app.run(host="0.0.0.0", port=8000, workers=2, before_start=init)
|
||||||
app.run(host="0.0.0.0", port=8000, workers=2)
|
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from contextvars import ContextVar
|
|
||||||
|
|
||||||
from sanic import Sanic, response
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class RequestIdFilter(logging.Filter):
|
|
||||||
def filter(self, record):
|
|
||||||
try:
|
|
||||||
record.request_id = app.ctx.request_id.get(None) or "n/a"
|
|
||||||
except AttributeError:
|
|
||||||
record.request_id = "n/a"
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
LOG_SETTINGS = {
|
|
||||||
"version": 1,
|
|
||||||
"disable_existing_loggers": False,
|
|
||||||
"handlers": {
|
|
||||||
"console": {
|
|
||||||
"class": "logging.StreamHandler",
|
|
||||||
"level": "DEBUG",
|
|
||||||
"formatter": "default",
|
|
||||||
"filters": ["requestid"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"filters": {
|
|
||||||
"requestid": {
|
|
||||||
"()": RequestIdFilter,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"formatters": {
|
|
||||||
"default": {
|
|
||||||
"format": "%(asctime)s %(levelname)s %(name)s:%(lineno)d %(request_id)s | %(message)s",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"loggers": {
|
|
||||||
"": {"level": "DEBUG", "handlers": ["console"], "propagate": True},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example", log_config=LOG_SETTINGS)
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_request
|
|
||||||
async def set_request_id(request):
|
|
||||||
request.app.ctx.request_id.set(request.id)
|
|
||||||
log.info(f"Setting {request.id=}")
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_response
|
|
||||||
async def set_request_header(request, response):
|
|
||||||
response.headers["X-Request-ID"] = request.id
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
async def test(request):
|
|
||||||
log.debug("X-Request-ID: %s", request.id)
|
|
||||||
log.info("Hello from test!")
|
|
||||||
return response.json({"test": True})
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_server_start
|
|
||||||
def setup(app, loop):
|
|
||||||
app.ctx.request_id = ContextVar("request_id")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(port=9999, debug=True)
|
|
@ -1,60 +0,0 @@
|
|||||||
import logging
|
|
||||||
import socket
|
|
||||||
|
|
||||||
from os import getenv
|
|
||||||
from platform import node
|
|
||||||
from uuid import getnode as get_mac
|
|
||||||
|
|
||||||
from logdna import LogDNAHandler
|
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.request import Request
|
|
||||||
from sanic.response import json
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger("logdna")
|
|
||||||
log.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
|
|
||||||
def get_my_ip_address(remote_server="google.com"):
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
|
||||||
s.connect((remote_server, 80))
|
|
||||||
return s.getsockname()[0]
|
|
||||||
|
|
||||||
|
|
||||||
def get_mac_address():
|
|
||||||
h = iter(hex(get_mac())[2:].zfill(12))
|
|
||||||
return ":".join(i + next(h) for i in h)
|
|
||||||
|
|
||||||
|
|
||||||
logdna_options = {
|
|
||||||
"app": __name__,
|
|
||||||
"index_meta": True,
|
|
||||||
"hostname": node(),
|
|
||||||
"ip": get_my_ip_address(),
|
|
||||||
"mac": get_mac_address(),
|
|
||||||
}
|
|
||||||
|
|
||||||
logdna_handler = LogDNAHandler(
|
|
||||||
getenv("LOGDNA_API_KEY"), options=logdna_options
|
|
||||||
)
|
|
||||||
|
|
||||||
logdna = logging.getLogger(__name__)
|
|
||||||
logdna.setLevel(logging.INFO)
|
|
||||||
logdna.addHandler(logdna_handler)
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
@app.middleware
|
|
||||||
def log_request(request: Request):
|
|
||||||
logdna.info("I was Here with a new Request to URL: {}".format(request.url))
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
def default(request):
|
|
||||||
return json({"response": "I was here"})
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
|
@ -1,30 +0,0 @@
|
|||||||
"""
|
|
||||||
Modify header or status in response
|
|
||||||
"""
|
|
||||||
|
|
||||||
from sanic import Sanic, response
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
def handle_request(request):
|
|
||||||
return response.json(
|
|
||||||
{"message": "Hello world!"},
|
|
||||||
headers={"X-Served-By": "sanic"},
|
|
||||||
status=200,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/unauthorized")
|
|
||||||
def handle_request(request):
|
|
||||||
return response.json(
|
|
||||||
{"message": "You are not authorized"},
|
|
||||||
headers={"X-Served-By": "sanic"},
|
|
||||||
status=404,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000, debug=True)
|
|
@ -1,24 +1,23 @@
|
|||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import text
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from sanic import Sanic, text
|
|
||||||
|
|
||||||
|
|
||||||
logging_format = "[%(asctime)s] %(process)d-%(levelname)s "
|
logging_format = "[%(asctime)s] %(process)d-%(levelname)s "
|
||||||
logging_format += "%(module)s::%(funcName)s():l%(lineno)d: "
|
logging_format += "%(module)s::%(funcName)s():l%(lineno)d: "
|
||||||
logging_format += "%(message)s"
|
logging_format += "%(message)s"
|
||||||
|
|
||||||
logging.basicConfig(format=logging_format, level=logging.DEBUG)
|
logging.basicConfig(
|
||||||
|
format=logging_format,
|
||||||
|
level=logging.DEBUG
|
||||||
|
)
|
||||||
log = logging.getLogger()
|
log = logging.getLogger()
|
||||||
|
|
||||||
# Set logger to override default basicConfig
|
# Set logger to override default basicConfig
|
||||||
app = Sanic("app")
|
sanic = Sanic()
|
||||||
|
@sanic.route("/")
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
def test(request):
|
def test(request):
|
||||||
log.info("received request; responding with 'hey'")
|
log.info("received request; responding with 'hey'")
|
||||||
return text("hey")
|
return text("hey")
|
||||||
|
|
||||||
|
sanic.run(host="0.0.0.0", port=8000)
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000)
|
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
"""pytest-xdist example for sanic server
|
|
||||||
|
|
||||||
Install testing tools:
|
|
||||||
|
|
||||||
$ pip install pytest pytest-xdist
|
|
||||||
|
|
||||||
Run with xdist params:
|
|
||||||
|
|
||||||
$ pytest examples/pytest_xdist.py -n 8 # 8 workers
|
|
||||||
"""
|
|
||||||
import re
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from sanic_testing import SanicTestClient
|
|
||||||
from sanic_testing.testing import PORT as PORT_BASE
|
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import text
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def test_port(worker_id):
|
|
||||||
m = re.search(r"[0-9]+", worker_id)
|
|
||||||
if m:
|
|
||||||
num_id = m.group(0)
|
|
||||||
else:
|
|
||||||
num_id = 0
|
|
||||||
port = PORT_BASE + int(num_id)
|
|
||||||
return port
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def app():
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
async def index(request):
|
|
||||||
return text("OK")
|
|
||||||
|
|
||||||
return app
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def client(app, test_port):
|
|
||||||
return SanicTestClient(app, test_port)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("run_id", range(100))
|
|
||||||
def test_index(client, run_id):
|
|
||||||
request, response = client._sanic_endpoint_test("get", "/")
|
|
||||||
assert response.status == 200
|
|
||||||
assert response.text == "OK"
|
|
@ -1,33 +0,0 @@
|
|||||||
from os import getenv
|
|
||||||
|
|
||||||
from raygun4py.raygunprovider import RaygunSender
|
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.exceptions import SanicException
|
|
||||||
from sanic.handlers import ErrorHandler
|
|
||||||
|
|
||||||
|
|
||||||
class RaygunExceptionReporter(ErrorHandler):
|
|
||||||
def __init__(self, raygun_api_key=None):
|
|
||||||
super().__init__()
|
|
||||||
if raygun_api_key is None:
|
|
||||||
raygun_api_key = getenv("RAYGUN_API_KEY")
|
|
||||||
|
|
||||||
self.sender = RaygunSender(raygun_api_key)
|
|
||||||
|
|
||||||
def default(self, request, exception):
|
|
||||||
self.sender.send_exception(exception=exception)
|
|
||||||
return super().default(request, exception)
|
|
||||||
|
|
||||||
|
|
||||||
raygun_error_reporter = RaygunExceptionReporter()
|
|
||||||
app = Sanic("Example", error_handler=raygun_error_reporter)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/raise")
|
|
||||||
async def test(request):
|
|
||||||
raise SanicException("You Broke It!")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
|
@ -1,18 +0,0 @@
|
|||||||
from sanic import Sanic, response
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
def handle_request(request):
|
|
||||||
return response.redirect("/redirect")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/redirect")
|
|
||||||
async def test(request):
|
|
||||||
return response.json({"Redirected": True})
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000)
|
|
@ -1,10 +0,0 @@
|
|||||||
import requests
|
|
||||||
|
|
||||||
# Warning: This is a heavy process.
|
|
||||||
|
|
||||||
data = ""
|
|
||||||
for i in range(1, 250000):
|
|
||||||
data += str(i)
|
|
||||||
|
|
||||||
r = requests.post("http://0.0.0.0:8000/stream", data=data)
|
|
||||||
print(r.text)
|
|
@ -1,63 +0,0 @@
|
|||||||
from sanic import Sanic
|
|
||||||
from sanic.blueprints import Blueprint
|
|
||||||
from sanic.response import stream, text
|
|
||||||
from sanic.views import HTTPMethodView
|
|
||||||
from sanic.views import stream as stream_decorator
|
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint("bp_example")
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleView(HTTPMethodView):
|
|
||||||
@stream_decorator
|
|
||||||
async def post(self, request):
|
|
||||||
result = ""
|
|
||||||
while True:
|
|
||||||
body = await request.stream.get()
|
|
||||||
if body is None:
|
|
||||||
break
|
|
||||||
result += body.decode("utf-8")
|
|
||||||
return text(result)
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/stream", stream=True)
|
|
||||||
async def handler(request):
|
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
|
||||||
body = await request.stream.get()
|
|
||||||
if body is None:
|
|
||||||
break
|
|
||||||
body = body.decode("utf-8").replace("1", "A")
|
|
||||||
await response.write(body)
|
|
||||||
|
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.put("/bp_stream", stream=True)
|
|
||||||
async def bp_handler(request):
|
|
||||||
result = ""
|
|
||||||
while True:
|
|
||||||
body = await request.stream.get()
|
|
||||||
if body is None:
|
|
||||||
break
|
|
||||||
result += body.decode("utf-8").replace("1", "A")
|
|
||||||
return text(result)
|
|
||||||
|
|
||||||
|
|
||||||
async def post_handler(request):
|
|
||||||
result = ""
|
|
||||||
while True:
|
|
||||||
body = await request.stream.get()
|
|
||||||
if body is None:
|
|
||||||
break
|
|
||||||
result += body.decode("utf-8")
|
|
||||||
return text(result)
|
|
||||||
|
|
||||||
|
|
||||||
app.blueprint(bp)
|
|
||||||
app.add_route(SimpleView.as_view(), "/method_view")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000)
|
|
@ -1,24 +1,21 @@
|
|||||||
|
from sanic import Sanic
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from sanic.response import text
|
||||||
from sanic import Sanic, response
|
|
||||||
from sanic.config import Config
|
from sanic.config import Config
|
||||||
from sanic.exceptions import RequestTimeout
|
from sanic.exceptions import RequestTimeout
|
||||||
|
|
||||||
|
|
||||||
Config.REQUEST_TIMEOUT = 1
|
Config.REQUEST_TIMEOUT = 1
|
||||||
app = Sanic("Example")
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route('/')
|
||||||
async def test(request):
|
async def test(request):
|
||||||
await asyncio.sleep(3)
|
await asyncio.sleep(3)
|
||||||
return response.text("Hello, world!")
|
return text('Hello, world!')
|
||||||
|
|
||||||
|
|
||||||
@app.exception(RequestTimeout)
|
@app.exception(RequestTimeout)
|
||||||
def timeout(request, exception):
|
def timeout(request, exception):
|
||||||
return response.text("RequestTimeout from error_handler.", 408)
|
return text('RequestTimeout from error_handler.', 408)
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=8000)
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=8000)
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
from os import getenv
|
|
||||||
|
|
||||||
import rollbar
|
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.exceptions import SanicException
|
|
||||||
from sanic.handlers import ErrorHandler
|
|
||||||
|
|
||||||
|
|
||||||
rollbar.init(getenv("ROLLBAR_API_KEY"))
|
|
||||||
|
|
||||||
|
|
||||||
class RollbarExceptionHandler(ErrorHandler):
|
|
||||||
def default(self, request, exception):
|
|
||||||
rollbar.report_message(str(exception))
|
|
||||||
return super().default(request, exception)
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example", error_handler=RollbarExceptionHandler())
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/raise")
|
|
||||||
def create_error(request):
|
|
||||||
raise SanicException("I was here and I don't like where I am")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
|
@ -1,89 +0,0 @@
|
|||||||
"""
|
|
||||||
1. Create a simple Sanic app
|
|
||||||
0. Run with an ASGI server:
|
|
||||||
$ uvicorn run_asgi:app
|
|
||||||
or
|
|
||||||
$ hypercorn run_asgi:app
|
|
||||||
"""
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from sanic import Sanic, response
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/text")
|
|
||||||
def handler_text(request):
|
|
||||||
return response.text("Hello")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/json")
|
|
||||||
def handler_json(request):
|
|
||||||
return response.json({"foo": "bar"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.websocket("/ws")
|
|
||||||
async def handler_ws(request, ws):
|
|
||||||
name = "<someone>"
|
|
||||||
while True:
|
|
||||||
data = f"Hello {name}"
|
|
||||||
await ws.send(data)
|
|
||||||
name = await ws.recv()
|
|
||||||
|
|
||||||
if not name:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/file")
|
|
||||||
async def handler_file(request):
|
|
||||||
return await response.file(Path("../") / "setup.py")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/file_stream")
|
|
||||||
async def handler_file_stream(request):
|
|
||||||
return await response.file_stream(
|
|
||||||
Path("../") / "setup.py", chunk_size=1024
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/stream", stream=True)
|
|
||||||
async def handler_stream(request):
|
|
||||||
while True:
|
|
||||||
body = await request.stream.read()
|
|
||||||
if body is None:
|
|
||||||
break
|
|
||||||
body = body.decode("utf-8").replace("1", "A")
|
|
||||||
await response.write(body)
|
|
||||||
return response.stream(body)
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_server_start
|
|
||||||
async def listener_before_server_start(*args, **kwargs):
|
|
||||||
print("before_server_start")
|
|
||||||
|
|
||||||
|
|
||||||
@app.after_server_start
|
|
||||||
async def listener_after_server_start(*args, **kwargs):
|
|
||||||
print("after_server_start")
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_server_stop
|
|
||||||
async def listener_before_server_stop(*args, **kwargs):
|
|
||||||
print("before_server_stop")
|
|
||||||
|
|
||||||
|
|
||||||
@app.after_server_stop
|
|
||||||
async def listener_after_server_stop(*args, **kwargs):
|
|
||||||
print("after_server_stop")
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_request
|
|
||||||
async def print_on_request(request):
|
|
||||||
print("print_on_request")
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_response
|
|
||||||
async def print_on_response(request, response):
|
|
||||||
print("print_on_response")
|
|
@ -1,30 +1,20 @@
|
|||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import json
|
||||||
|
from multiprocessing import Event
|
||||||
|
from signal import signal, SIGINT
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import uvloop
|
app = Sanic(__name__)
|
||||||
|
|
||||||
from sanic import Sanic, response
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
return response.json({"answer": "42"})
|
return json({"answer": "42"})
|
||||||
|
|
||||||
|
server = app.create_server(host="0.0.0.0", port=8001)
|
||||||
async def main():
|
loop = asyncio.get_event_loop()
|
||||||
server = await app.create_server(
|
task = asyncio.ensure_future(server)
|
||||||
port=8000, host="0.0.0.0", return_asyncio_server=True
|
signal(SIGINT, lambda s, f: loop.close())
|
||||||
)
|
try:
|
||||||
|
loop.run_forever()
|
||||||
if server is None:
|
except:
|
||||||
return
|
loop.stop()
|
||||||
|
|
||||||
await server.startup()
|
|
||||||
await server.serve_forever()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
asyncio.set_event_loop(uvloop.new_event_loop())
|
|
||||||
asyncio.run(main())
|
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
from signal import SIGINT, signal
|
|
||||||
|
|
||||||
import uvloop
|
|
||||||
|
|
||||||
from sanic import Sanic, response
|
|
||||||
from sanic.server import AsyncioServer
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic("Example")
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_server_start
|
|
||||||
async def before_server_start(app, loop):
|
|
||||||
print("Async Server starting")
|
|
||||||
|
|
||||||
|
|
||||||
@app.after_server_start
|
|
||||||
async def after_server_start(app, loop):
|
|
||||||
print("Async Server started")
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_server_stop
|
|
||||||
async def before_server_stop(app, loop):
|
|
||||||
print("Async Server stopping")
|
|
||||||
|
|
||||||
|
|
||||||
@app.after_server_stop
|
|
||||||
async def after_server_stop(app, loop):
|
|
||||||
print("Async Server stopped")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
async def test(request):
|
|
||||||
return response.json({"answer": "42"})
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
asyncio.set_event_loop(uvloop.new_event_loop())
|
|
||||||
serv_coro = app.create_server(
|
|
||||||
host="0.0.0.0", port=8000, return_asyncio_server=True
|
|
||||||
)
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
serv_task = asyncio.ensure_future(serv_coro, loop=loop)
|
|
||||||
signal(SIGINT, lambda s, f: loop.stop())
|
|
||||||
server: AsyncioServer = loop.run_until_complete(serv_task)
|
|
||||||
loop.run_until_complete(server.startup())
|
|
||||||
|
|
||||||
# When using app.run(), this actually triggers before the serv_coro.
|
|
||||||
# But, in this example, we are using the convenience method, even if it is
|
|
||||||
# out of order.
|
|
||||||
loop.run_until_complete(server.before_start())
|
|
||||||
loop.run_until_complete(server.after_start())
|
|
||||||
try:
|
|
||||||
loop.run_forever()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
loop.stop()
|
|
||||||
finally:
|
|
||||||
loop.run_until_complete(server.before_stop())
|
|
||||||
|
|
||||||
# Wait for server to close
|
|
||||||
close_task = server.close()
|
|
||||||
loop.run_until_complete(close_task)
|
|
||||||
|
|
||||||
# Complete all tasks on the loop
|
|
||||||
for connection in server.connections:
|
|
||||||
connection.close_if_idle()
|
|
||||||
loop.run_until_complete(server.after_stop())
|
|
65
examples/sanic_aiopg_example.py
Normal file
65
examples/sanic_aiopg_example.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
""" To run this example you need additional aiopg package
|
||||||
|
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
import uvloop
|
||||||
|
import aiopg
|
||||||
|
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
database_name = os.environ['DATABASE_NAME']
|
||||||
|
database_host = os.environ['DATABASE_HOST']
|
||||||
|
database_user = os.environ['DATABASE_USER']
|
||||||
|
database_password = os.environ['DATABASE_PASSWORD']
|
||||||
|
|
||||||
|
connection = 'postgres://{0}:{1}@{2}/{3}'.format(database_user,
|
||||||
|
database_password,
|
||||||
|
database_host,
|
||||||
|
database_name)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_pool():
|
||||||
|
return await aiopg.create_pool(connection)
|
||||||
|
|
||||||
|
app = Sanic(name=__name__)
|
||||||
|
|
||||||
|
async def prepare_db(app, loop):
|
||||||
|
"""
|
||||||
|
Let's create some table and add some data
|
||||||
|
"""
|
||||||
|
async with aiopg.create_pool(connection) as pool:
|
||||||
|
async with pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cur:
|
||||||
|
await cur.execute('DROP TABLE IF EXISTS sanic_polls')
|
||||||
|
await cur.execute("""CREATE TABLE sanic_polls (
|
||||||
|
id serial primary key,
|
||||||
|
question varchar(50),
|
||||||
|
pub_date timestamp
|
||||||
|
);""")
|
||||||
|
for i in range(0, 100):
|
||||||
|
await cur.execute("""INSERT INTO sanic_polls
|
||||||
|
(id, question, pub_date) VALUES ({}, {}, now())
|
||||||
|
""".format(i, i))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
async def handle(request):
|
||||||
|
result = []
|
||||||
|
async def test_select():
|
||||||
|
async with aiopg.create_pool(connection) as pool:
|
||||||
|
async with pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cur:
|
||||||
|
await cur.execute("SELECT question, pub_date FROM sanic_polls")
|
||||||
|
async for row in cur:
|
||||||
|
result.append({"question": row[0], "pub_date": row[1]})
|
||||||
|
res = await test_select()
|
||||||
|
return json({'polls': result})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0',
|
||||||
|
port=8000,
|
||||||
|
debug=True,
|
||||||
|
before_start=prepare_db)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user