Compare commits

...

9 Commits

Author SHA1 Message Date
Adam Hopkins
3b85b3bbad Potential server crash if running Python 3.10 w/ Sanic 20.12 (#2400) 2022-02-16 18:03:05 +02:00
Stephen Sadowski
6e55e73da1 fix: websocket dependency for websockets 9.1 security fix (#2366)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-01-16 20:42:38 +02:00
Adam Hopkins
89d942451f Merge branch 'pr2129' into 20.12LTS 2021-10-03 01:19:53 +03:00
Adam Hopkins
4d6205e6fe Bump version 2021-10-03 01:05:08 +03:00
Thomas Grainger
1684b0b986 remove reference to yanked packages 2021-07-02 10:02:17 +01:00
Thomas Grainger
4f5faa4a3c unpin uvloop 2021-05-04 18:14:14 +01:00
Arthur Goldberg
cbb77b536a fix issue where request.args.pop removed parameters inconsistently (#2112)
Co-authored-by: Adam Hopkins <admhpkns@gmail.com>
2021-04-22 12:49:08 +03:00
Adam Hopkins
35c76253bf Bump version 20.12.3 (#2062) 2021-03-21 09:48:44 +02:00
laggardkernel
8d86c3c598 Remove unnecessary prefix from websocket handler name (#2021)
Remove the websocket prefix "websocket_handler_" introduced in
761eef7. Add a backward support for url_for() calling with this prefix
in param "view_name".
2021-03-14 20:33:07 +02:00
27 changed files with 611 additions and 37 deletions

40
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: "CodeQL"
on:
push:
branches:
- main
- "*LTS"
pull_request:
branches:
- main
- "*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

37
.github/workflows/coverage.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Coverage check
on:
push:
branches:
- main
- "*LTS"
tags:
- "!*" # Do not execute on tags
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
test:
if: github.event.pull_request.draft == false
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.9]
os: [ubuntu-latest]
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies 🔨
run: |
python -m pip install --upgrade pip
pip install tox
- uses: paambaati/codeclimate-action@v2.5.3
if: always()
env:
CC_TEST_REPORTER_ID: ${{ secrets.CODECLIMATE }}
with:
coverageCommand: tox -e coverage

39
.github/workflows/on-demand.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: On Demand Task
on:
workflow_dispatch:
inputs:
python-version:
description: 'Version of Python to use for running Test'
required: false
default: "3.8"
tox-env:
description: 'Test Environment to Run'
required: true
default: ''
os:
description: 'Operating System to Run Test on'
required: false
default: ubuntu-latest
jobs:
onDemand:
name: tox-${{ matrix.config.tox-env }}-on-${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ["${{ github.event.inputs.os}}"]
config:
- { tox-env: "${{ github.event.inputs.tox-env }}", py-version: "${{ github.event.inputs.python-version }}"}
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Run tests
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.py-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"
experimental-ignore-error: "yes"

36
.github/workflows/pr-bandit.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Security Analysis
on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
jobs:
bandit:
if: github.event.pull_request.draft == false
name: type-check-${{ matrix.config.python-version }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
config:
- { python-version: 3.7, tox-env: security}
- { python-version: 3.8, tox-env: security}
- { python-version: 3.9, tox-env: security}
- { python-version: "3.10", tox-env: security}
steps:
- name: Checkout the repository
uses: actions/checkout@v2
id: checkout-branch
- name: Run Linter Checks
id: linter-check
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"

32
.github/workflows/pr-docs.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Document Linter
on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
jobs:
docsLinter:
if: github.event.pull_request.draft == false
name: Lint Documentation
runs-on: ubuntu-latest
strategy:
matrix:
config:
- {python-version: "3.8", tox-env: "docs"}
fail-fast: false
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Run Document Linter
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"

33
.github/workflows/pr-linter.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Linter Checks
on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
jobs:
linter:
if: github.event.pull_request.draft == false
name: lint
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
config:
- { python-version: 3.8, tox-env: lint}
steps:
- name: Checkout the repository
uses: actions/checkout@v2
id: checkout-branch
- name: Run Linter Checks
id: linter-check
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"

41
.github/workflows/pr-python-pypy.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Python PyPy Tests
on:
workflow_dispatch:
inputs:
tox-env:
description: "Tox Env to run on the PyPy Infra"
required: false
default: "pypy37"
pypy-version:
description: "Version of PyPy to use"
required: false
default: "pypy-3.7"
jobs:
testPyPy:
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest]
config:
- {
python-version: "${{ github.event.inputs.pypy-version }}",
tox-env: "${{ github.event.inputs.tox-env }}",
}
steps:
- name: Checkout the Repository
uses: actions/checkout@v2
id: checkout-branch
- name: Run Unit Tests
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"
experimental-ignore-error: "true"
command-timeout: "600000"

35
.github/workflows/pr-python37.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Python 3.7 Tests
on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
jobs:
testPy37:
if: github.event.pull_request.draft == false
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
# os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest]
config:
- { python-version: 3.7, tox-env: py37 }
- { python-version: 3.7, tox-env: py37-no-ext }
steps:
- name: Checkout the Repository
uses: actions/checkout@v2
id: checkout-branch
- name: Run Unit Tests
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"
test-failure-retry: "3"

35
.github/workflows/pr-python38.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Python 3.8 Tests
on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
jobs:
testPy38:
if: github.event.pull_request.draft == false
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
# os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest]
config:
- { python-version: 3.8, tox-env: py38 }
- { python-version: 3.8, tox-env: py38-no-ext }
steps:
- name: Checkout the Repository
uses: actions/checkout@v2
id: checkout-branch
- name: Run Unit Tests
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"
test-failure-retry: "3"

47
.github/workflows/pr-python39.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Python 3.9 Tests
on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
jobs:
testPy39:
if: github.event.pull_request.draft == false
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
# os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest]
config:
- {
python-version: 3.9,
tox-env: py39,
ignore-error-flake: "false",
command-timeout: "0",
}
- {
python-version: 3.9,
tox-env: py39-no-ext,
ignore-error-flake: "true",
command-timeout: "600000",
}
steps:
- name: Checkout the Repository
uses: actions/checkout@v2
id: checkout-branch
- name: Run Unit Tests
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }},-vv=''"
experimental-ignore-error: "${{ matrix.config.ignore-error-flake }}"
command-timeout: "${{ matrix.config.command-timeout }}"
test-failure-retry: "3"

35
.github/workflows/pr-type-check.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Typing Checks
on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
jobs:
typeChecking:
if: github.event.pull_request.draft == false
name: type-check-${{ matrix.config.python-version }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
config:
# - { python-version: 3.7, tox-env: type-checking}
- { python-version: 3.8, tox-env: type-checking}
- { python-version: 3.9, tox-env: type-checking}
steps:
- name: Checkout the repository
uses: actions/checkout@v2
id: checkout-branch
- name: Run Linter Checks
id: linter-check
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"

37
.github/workflows/pr-windows.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Run Unit Tests on Windows
on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
jobs:
testsOnWindows:
if: github.event.pull_request.draft == false
name: ut-${{ matrix.config.tox-env }}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
config:
- { python-version: 3.7, tox-env: py37-no-ext }
- { python-version: 3.8, tox-env: py38-no-ext }
- { python-version: 3.9, tox-env: py39-no-ext }
- { python-version: pypy-3.7, tox-env: pypy37-no-ext }
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Run Unit Tests
uses: ahopkins/custom-actions@pip-extra-args
with:
python-version: ${{ matrix.config.python-version }}
test-infra-tool: tox
test-infra-version: latest
action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}"
experimental-ignore-error: "true"
command-timeout: "600000"
pip-extra-args: "--user"

48
.github/workflows/publish-images.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Publish Docker Images
on:
workflow_run:
workflows:
- 'Publish Artifacts'
types:
- completed
jobs:
publishDockerImages:
name: Docker Image Build [${{ matrix.python-version }}]
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
python-version: ["3.7", "3.8", "3.9"]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Build Latest Base images for ${{ matrix.python-version }}
uses: harshanarayana/custom-actions@main
with:
docker-image-base-name: sanicframework/sanic-build
ignore-python-setup: 'true'
dockerfile-base-dir: './docker'
action: 'image-publish'
docker-image-tag: "${{ matrix.python-version }}"
docker-file-suffix: "base"
docker-build-args: "PYTHON_VERSION=${{ matrix.python-version }}"
registry-auth-user: ${{ secrets.DOCKER_ACCESS_USER }}
registry-auth-password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
push-images: 'true'
- name: Publish Sanic Docker Image for ${{ matrix.python-version }}
uses: harshanarayana/custom-actions@main
with:
docker-image-base-name: sanicframework/sanic
ignore-python-setup: 'true'
dockerfile-base-dir: './docker'
action: 'image-publish'
docker-build-args: "BASE_IMAGE_TAG=${{ matrix.python-version }}"
docker-image-prefix: "${{ matrix.python-version }}"
registry-auth-user: ${{ secrets.DOCKER_ACCESS_USER }}
registry-auth-password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
push-images: 'true'

28
.github/workflows/publish-package.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Publish Artifacts
on:
release:
types: [created]
jobs:
publishPythonPackage:
name: Publishing Sanic Release Artifacts
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
python-version: ["3.8"]
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Publish Python Package
uses: harshanarayana/custom-actions@main
with:
python-version: ${{ matrix.python-version }}
package-infra-name: "twine"
pypi-user: __token__
pypi-access-token: ${{ secrets.PYPI_ACCESS_TOKEN }}
action: "package-publish"
pypi-verify-metadata: "true"

View File

@@ -1,3 +1,13 @@
Version 20.12.5
===============
Bugfixes
********
*
`#2366 <https://github.com/sanic-org/sanic/pull/2366>`_
websocket dependency for websockets 9.1 security fix
Version 20.12.0 Version 20.12.0
=============== ===============

View File

@@ -1 +1 @@
__version__ = "20.12.2" __version__ = "20.12.6"

View File

@@ -2,6 +2,7 @@ import logging
import logging.config import logging.config
import os import os
import re import re
import sys
from asyncio import CancelledError, Protocol, ensure_future, get_event_loop from asyncio import CancelledError, Protocol, ensure_future, get_event_loop
from collections import defaultdict, deque from collections import defaultdict, deque
@@ -12,6 +13,7 @@ from ssl import Purpose, SSLContext, create_default_context
from traceback import format_exc from traceback import format_exc
from typing import Any, Dict, Optional, Type, Union from typing import Any, Dict, Optional, Type, Union
from urllib.parse import urlencode, urlunparse from urllib.parse import urlencode, urlunparse
from warnings import warn
from sanic import reloader_helpers from sanic import reloader_helpers
from sanic.asgi import ASGIApp from sanic.asgi import ASGIApp
@@ -64,6 +66,18 @@ class Sanic:
if configure_logging: if configure_logging:
logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS) logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS)
if sys.version_info >= (3, 10):
error_logger.error(
"Unsupported version of Python has been detected.\n\nPython "
f"version {sys.version} is not supported by this version of "
"Sanic. There is a security advisory that has been issued for "
"Sanic v20.12 while running Python 3.10+. You should either "
"use a supported version of Python (v3.6 - v3.9) or upgrade "
"Sanic to v21+.\n\nPlease see https://github.com/sanic-org/"
"sanic/security/advisories/GHSA-7p79-6x2v-5h88 for "
"more information.\n"
)
self.name = name self.name = name
self.asgi = False self.asgi = False
self.router = router or Router(self) self.router = router or Router(self)
@@ -494,9 +508,7 @@ class Sanic:
websocket_handler = partial( websocket_handler = partial(
self._websocket_handler, handler, subprotocols=subprotocols self._websocket_handler, handler, subprotocols=subprotocols
) )
websocket_handler.__name__ = ( websocket_handler.__name__ = handler.__name__
"websocket_handler_" + handler.__name__
)
routes.extend( routes.extend(
self.router.add( self.router.add(
uri=uri, uri=uri,
@@ -747,6 +759,24 @@ class Sanic:
kw.update(name=view_name) kw.update(name=view_name)
uri, route = self.router.find_route_by_view_name(view_name, **kw) uri, route = self.router.find_route_by_view_name(view_name, **kw)
# TODO(laggardkernel): this fix should be removed in v21.3.
# Try again without the unnecessary prefix "websocket_handler_",
# which was added by accident on non-blueprint handlers. GH-2021
if not (uri and route) and view_name.startswith("websocket_handler_"):
view_name = view_name[18:]
uri, route = self.router.find_route_by_view_name(view_name, **kw)
if uri and route:
warn(
"The bug of adding unnecessary `websocket_handler_` "
"prefix in param `view_name` for non-blueprint handlers "
"is fixed. This backward support will be removed in "
"v21.3. Please update `Sanic.url_for()` callings in your "
"code soon.",
DeprecationWarning,
stacklevel=2,
)
if not (uri and route): if not (uri and route):
raise URLBuildError( raise URLBuildError(
f"Endpoint with name `{view_name}` was not found" f"Endpoint with name `{view_name}` was not found"

View File

@@ -20,7 +20,6 @@ if use_trio:
def stat_async(path): def stat_async(path):
return Path(path).stat() return Path(path).stat()
else: else:
from aiofiles import open as aio_open # type: ignore from aiofiles import open as aio_open # type: ignore
from aiofiles.os import stat as stat_async # type: ignore # noqa: F401 from aiofiles.os import stat as stat_async # type: ignore # noqa: F401

View File

@@ -50,7 +50,7 @@ class StreamBuffer:
self._queue = asyncio.Queue(buffer_size) self._queue = asyncio.Queue(buffer_size)
async def read(self): async def read(self):
""" Stop reading when gets None """ """Stop reading when gets None"""
payload = await self._queue.get() payload = await self._queue.get()
self._queue.task_done() self._queue.task_done()
return payload return payload
@@ -265,9 +265,12 @@ class Request:
:type errors: str :type errors: str
:return: RequestParameters :return: RequestParameters
""" """
if not self.parsed_args[ if (
(keep_blank_values, strict_parsing, encoding, errors) keep_blank_values,
]: strict_parsing,
encoding,
errors,
) not in self.parsed_args:
if self.query_string: if self.query_string:
self.parsed_args[ self.parsed_args[
(keep_blank_values, strict_parsing, encoding, errors) (keep_blank_values, strict_parsing, encoding, errors)
@@ -321,9 +324,12 @@ class Request:
:type errors: str :type errors: str
:return: list :return: list
""" """
if not self.parsed_not_grouped_args[ if (
(keep_blank_values, strict_parsing, encoding, errors) keep_blank_values,
]: strict_parsing,
encoding,
errors,
) not in self.parsed_not_grouped_args:
if self.query_string: if self.query_string:
self.parsed_not_grouped_args[ self.parsed_not_grouped_args[
(keep_blank_values, strict_parsing, encoding, errors) (keep_blank_values, strict_parsing, encoding, errors)

View File

@@ -169,7 +169,11 @@ class HttpProtocol(asyncio.Protocol):
self.request_class = self.app.request_class or Request self.request_class = self.app.request_class or Request
self.is_request_stream = self.app.is_request_stream self.is_request_stream = self.app.is_request_stream
self._is_stream_handler = False self._is_stream_handler = False
self._not_paused = asyncio.Event(loop=deprecated_loop) self._not_paused = (
asyncio.Event()
if sys.version_info >= (3, 10)
else asyncio.Event(loop=deprecated_loop)
)
self._total_request_size = 0 self._total_request_size = 0
self._request_timeout_handler = None self._request_timeout_handler = None
self._response_timeout_handler = None self._response_timeout_handler = None

View File

@@ -81,14 +81,14 @@ env_dependency = (
'; sys_platform != "win32" ' 'and implementation_name == "cpython"' '; sys_platform != "win32" ' 'and implementation_name == "cpython"'
) )
ujson = "ujson>=1.35" + env_dependency ujson = "ujson>=1.35" + env_dependency
uvloop = "uvloop>=0.5.3,<0.15.0" + env_dependency uvloop = "uvloop>=0.5.3" + env_dependency
requirements = [ requirements = [
"httptools>=0.0.10", "httptools>=0.0.10",
uvloop, uvloop,
ujson, ujson,
"aiofiles>=0.6.0", "aiofiles>=0.6.0",
"websockets>=8.1,<9.0", "websockets>=8.1,<=9.1",
"multidict>=5.0,<6.0", "multidict>=5.0,<6.0",
"httpx==0.15.4", "httpx==0.15.4",
] ]

View File

@@ -1,6 +1,3 @@
import asyncio
import sys
from collections import deque, namedtuple from collections import deque, namedtuple
import pytest import pytest
@@ -82,14 +79,6 @@ def test_listeners_triggered(app):
with pytest.warns(UserWarning): with pytest.warns(UserWarning):
server.run() server.run()
all_tasks = (
asyncio.Task.all_tasks()
if sys.version_info < (3, 7)
else asyncio.all_tasks(asyncio.get_event_loop())
)
for task in all_tasks:
task.cancel()
assert before_server_start assert before_server_start
assert after_server_start assert after_server_start
assert before_server_stop assert before_server_stop
@@ -132,14 +121,6 @@ def test_listeners_triggered_async(app):
with pytest.warns(UserWarning): with pytest.warns(UserWarning):
server.run() server.run()
all_tasks = (
asyncio.Task.all_tasks()
if sys.version_info < (3, 7)
else asyncio.all_tasks(asyncio.get_event_loop())
)
for task in all_tasks:
task.cancel()
assert before_server_start assert before_server_start
assert after_server_start assert after_server_start
assert before_server_stop assert before_server_stop

View File

@@ -13,7 +13,7 @@ from sanic.exceptions import PyFileError
@contextmanager @contextmanager
def temp_path(): def temp_path():
""" a simple cross platform replacement for NamedTemporaryFile """ """a simple cross platform replacement for NamedTemporaryFile"""
with TemporaryDirectory() as td: with TemporaryDirectory() as td:
yield Path(td, "file") yield Path(td, "file")

View File

@@ -102,7 +102,7 @@ def test_logging_pass_customer_logconfig():
@pytest.mark.parametrize("debug", (True, False)) @pytest.mark.parametrize("debug", (True, False))
def test_log_connection_lost(app, debug, monkeypatch): def test_log_connection_lost(app, debug, monkeypatch):
""" Should not log Connection lost exception on non debug """ """Should not log Connection lost exception on non debug"""
stream = StringIO() stream = StringIO()
root = logging.getLogger("sanic.root") root = logging.getLogger("sanic.root")
root.addHandler(logging.StreamHandler(stream)) root.addHandler(logging.StreamHandler(stream))

View File

@@ -290,6 +290,17 @@ def test_query_string(app):
assert request.args.get("test3", default="My value") == "My value" assert request.args.get("test3", default="My value") == "My value"
def test_popped_stays_popped(app):
@app.route("/")
async def handler(request):
return text("OK")
request, response = app.test_client.get("/", params=[("test1", "1")])
assert request.args.pop("test1") == ["1"]
assert "test1" not in request.args
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_query_string_asgi(app): async def test_query_string_asgi(app):
@app.route("/") @app.route("/")

View File

@@ -348,3 +348,13 @@ def test_methodview_naming(methodview_app):
assert viewone_url == "/view_one" assert viewone_url == "/view_one"
assert viewtwo_url == "/view_two" assert viewtwo_url == "/view_two"
def test_url_for_with_websocket_handlers(app):
# Test for a specific bugfix in GH-2021
@app.websocket("/ws")
async def my_handler(request, ws):
pass
assert app.url_for("my_handler") == "/ws"
assert app.url_for("websocket_handler_my_handler") == "/ws"

View File

@@ -20,7 +20,7 @@ deps =
beautifulsoup4 beautifulsoup4
gunicorn==20.0.4 gunicorn==20.0.4
uvicorn uvicorn
websockets>=8.1,<9.0 websockets>=8.1,<=9.1
commands = commands =
pytest {posargs:tests --cov sanic} pytest {posargs:tests --cov sanic}
- coverage combine --append - coverage combine --append