Merge pull request #1562 from huge-success/testing-client
Testing client
This commit is contained in:
commit
ae2b8f0056
|
@ -2,11 +2,6 @@ version: "{branch}.{build}"
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- TOXENV: py35-no-ext
|
|
||||||
PYTHON: "C:\\Python35-x64"
|
|
||||||
PYTHON_VERSION: "3.5.x"
|
|
||||||
PYTHON_ARCH: "64"
|
|
||||||
|
|
||||||
- TOXENV: py36-no-ext
|
- TOXENV: py36-no-ext
|
||||||
PYTHON: "C:\\Python36-x64"
|
PYTHON: "C:\\Python36-x64"
|
||||||
PYTHON_VERSION: "3.6.x"
|
PYTHON_VERSION: "3.6.x"
|
||||||
|
|
|
@ -5,10 +5,6 @@ cache:
|
||||||
- $HOME/.cache/pip
|
- $HOME/.cache/pip
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- env: TOX_ENV=py35
|
|
||||||
python: 3.5
|
|
||||||
- env: TOX_ENV=py35-no-ext
|
|
||||||
python: 3.5
|
|
||||||
- env: TOX_ENV=py36
|
- env: TOX_ENV=py36
|
||||||
python: 3.6
|
python: 3.6
|
||||||
- env: TOX_ENV=py36-no-ext
|
- env: TOX_ENV=py36-no-ext
|
||||||
|
|
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -1,3 +1,18 @@
|
||||||
|
Version 19.6
|
||||||
|
------------
|
||||||
|
19.6.0
|
||||||
|
- Changes:
|
||||||
|
- Remove `aiohttp` dependencey and create new `SanicTestClient` based upon
|
||||||
|
[`requests-async`](https://github.com/encode/requests-async).
|
||||||
|
|
||||||
|
- Deprecation:
|
||||||
|
- Support for Python 3.5
|
||||||
|
|
||||||
|
Note: Sanic will not support Python 3.5 from version 19.6 and forward. However,
|
||||||
|
version 18.12LTS will have its support period extended thru December 2020, and
|
||||||
|
therefore passing Python's official support version 3.5, which is set to expire
|
||||||
|
in September 2020.
|
||||||
|
|
||||||
Version 19.3
|
Version 19.3
|
||||||
-------------
|
-------------
|
||||||
19.3.1
|
19.3.1
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016-present Channel Cat
|
Copyright (c) 2016-present Sanic Community
|
||||||
|
|
||||||
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
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -47,12 +47,12 @@ ifdef include_tests
|
||||||
isort -rc sanic tests
|
isort -rc sanic tests
|
||||||
else
|
else
|
||||||
$(info Sorting Imports)
|
$(info Sorting Imports)
|
||||||
isort -rc sanic
|
isort -rc sanic tests
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
black:
|
black:
|
||||||
black --config ./pyproject.toml sanic tests
|
black --config ./.black.toml sanic tests
|
||||||
|
|
||||||
fix-import: black
|
fix-import: black
|
||||||
isort -rc sanic
|
isort -rc sanic tests
|
||||||
|
|
|
@ -17,6 +17,8 @@ Sanic | Build fast. Run fast.
|
||||||
- | |PyPI| |PyPI version| |Wheel| |Supported implementations| |Code style black|
|
- | |PyPI| |PyPI version| |Wheel| |Supported implementations| |Code style black|
|
||||||
* - Support
|
* - Support
|
||||||
- | |Forums| |Join the chat at https://gitter.im/sanic-python/Lobby|
|
- | |Forums| |Join the chat at https://gitter.im/sanic-python/Lobby|
|
||||||
|
* - Stats
|
||||||
|
- | |Downloads|
|
||||||
|
|
||||||
.. |Forums| image:: https://img.shields.io/badge/forums-community-ff0068.svg
|
.. |Forums| image:: https://img.shields.io/badge/forums-community-ff0068.svg
|
||||||
:target: https://community.sanicframework.org/
|
:target: https://community.sanicframework.org/
|
||||||
|
@ -42,10 +44,13 @@ Sanic | Build fast. Run fast.
|
||||||
.. |Supported implementations| image:: https://img.shields.io/pypi/implementation/sanic.svg
|
.. |Supported implementations| image:: https://img.shields.io/pypi/implementation/sanic.svg
|
||||||
:alt: Supported implementations
|
:alt: Supported implementations
|
||||||
:target: https://pypi.python.org/pypi/sanic
|
:target: https://pypi.python.org/pypi/sanic
|
||||||
|
.. |Downloads| image:: https://pepy.tech/badge/sanic/month
|
||||||
|
:alt: Downloads
|
||||||
|
:target: https://pepy.tech/project/sanic
|
||||||
|
|
||||||
.. end-badges
|
.. end-badges
|
||||||
|
|
||||||
Sanic is a Python 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.
|
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.
|
||||||
|
|
||||||
`Source code on GitHub <https://github.com/huge-success/sanic/>`_ | `Help and discussion board <https://community.sanicframework.org/>`_.
|
`Source code on GitHub <https://github.com/huge-success/sanic/>`_ | `Help and discussion board <https://community.sanicframework.org/>`_.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
Version 19.6
|
||||||
|
------------
|
||||||
|
19.6.0
|
||||||
|
- Changes:
|
||||||
|
- Remove `aiohttp` dependencey and create new `SanicTestClient` based upon
|
||||||
|
[`requests-async`](https://github.com/encode/requests-async).
|
||||||
|
|
||||||
|
- Deprecation:
|
||||||
|
- Support for Python 3.5
|
||||||
|
|
||||||
|
Note: Sanic will not support Python 3.5 from version 19.6 and forward. However,
|
||||||
|
version 18.12LTS will have its support period extended thru December 2020, and
|
||||||
|
therefore passing Python's official support version 3.5, which is set to expire
|
||||||
|
in September 2020.
|
||||||
|
|
||||||
Version 19.3
|
Version 19.3
|
||||||
-------------
|
-------------
|
||||||
19.3.1
|
19.3.1
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# Testing
|
# Testing
|
||||||
|
|
||||||
Sanic endpoints can be tested locally using the `test_client` object, which
|
Sanic endpoints can be tested locally using the `test_client` object, which
|
||||||
depends on the additional [aiohttp](https://aiohttp.readthedocs.io/en/stable/)
|
depends on the additional [`requests-async`](https://github.com/encode/requests-async)
|
||||||
library.
|
library, which implements an API that mirrors the `requests` library.
|
||||||
|
|
||||||
The `test_client` exposes `get`, `post`, `put`, `delete`, `patch`, `head` and `options` methods
|
The `test_client` exposes `get`, `post`, `put`, `delete`, `patch`, `head` and `options` methods
|
||||||
for you to run against your application. A simple example (using pytest) is like follows:
|
for you to run against your application. A simple example (using pytest) is like follows:
|
||||||
|
@ -21,7 +21,7 @@ def test_index_put_not_allowed():
|
||||||
```
|
```
|
||||||
|
|
||||||
Internally, each time you call one of the `test_client` methods, the Sanic app is run at `127.0.0.1:42101` and
|
Internally, each time you call one of the `test_client` methods, the Sanic app is run at `127.0.0.1:42101` and
|
||||||
your test request is executed against your application, using `aiohttp`.
|
your test request is executed against your application, using `requests-async`.
|
||||||
|
|
||||||
The `test_client` methods accept the following arguments and keyword arguments:
|
The `test_client` methods accept the following arguments and keyword arguments:
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ The `test_client` methods accept the following arguments and keyword arguments:
|
||||||
- `server_kwargs` *(default `{}`) a dict of additional arguments to pass into `app.run` before the test request is run.
|
- `server_kwargs` *(default `{}`) a dict of additional arguments to pass into `app.run` before the test request is run.
|
||||||
- `debug` *(default `False`)* A boolean which determines whether to run the server in debug mode.
|
- `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.
|
The function further takes the `*request_args` and `**request_kwargs`, which are passed directly to the request.
|
||||||
|
|
||||||
For example, to supply data to a GET request, you would do the following:
|
For example, to supply data to a GET request, you would do the following:
|
||||||
|
|
||||||
|
@ -55,8 +55,8 @@ def test_post_json_request_includes_data():
|
||||||
|
|
||||||
|
|
||||||
More information about
|
More information about
|
||||||
the available arguments to aiohttp can be found
|
the available arguments to `requests-async` can be found
|
||||||
[in the documentation for ClientSession](https://aiohttp.readthedocs.io/en/stable/client_reference.html#client-session).
|
[in the documentation for `requests`](https://2.python-requests.org/en/master/).
|
||||||
|
|
||||||
|
|
||||||
## Using a random port
|
## Using a random port
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
from socket import socket
|
from socket import socket
|
||||||
|
|
||||||
|
import requests_async as requests
|
||||||
|
import websockets
|
||||||
|
|
||||||
from sanic.exceptions import MethodNotSupported
|
from sanic.exceptions import MethodNotSupported
|
||||||
from sanic.log import logger
|
from sanic.log import logger
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
@ -16,32 +19,41 @@ class SanicTestClient:
|
||||||
self.app = app
|
self.app = app
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
||||||
async def _local_request(self, method, url, cookies=None, *args, **kwargs):
|
def get_new_session(self):
|
||||||
import aiohttp
|
return requests.Session()
|
||||||
|
|
||||||
|
async def _local_request(self, method, url, *args, **kwargs):
|
||||||
logger.info(url)
|
logger.info(url)
|
||||||
conn = aiohttp.TCPConnector(ssl=False)
|
raw_cookies = kwargs.pop("raw_cookies", None)
|
||||||
async with aiohttp.ClientSession(
|
|
||||||
cookies=cookies, connector=conn
|
if method == "websocket":
|
||||||
) as session:
|
async with websockets.connect(url, *args, **kwargs) as websocket:
|
||||||
async with getattr(session, method.lower())(
|
websocket.opened = websocket.open
|
||||||
url, *args, **kwargs
|
return websocket
|
||||||
) as response:
|
else:
|
||||||
try:
|
async with self.get_new_session() as session:
|
||||||
response.text = await response.text()
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
response.text = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response.json = await response.json()
|
response = await getattr(session, method.lower())(
|
||||||
except (
|
url, verify=False, *args, **kwargs
|
||||||
JSONDecodeError,
|
)
|
||||||
UnicodeDecodeError,
|
except NameError:
|
||||||
aiohttp.ClientResponseError,
|
raise Exception(response.status_code)
|
||||||
):
|
|
||||||
|
try:
|
||||||
|
response.json = response.json()
|
||||||
|
except (JSONDecodeError, UnicodeDecodeError):
|
||||||
response.json = None
|
response.json = None
|
||||||
|
|
||||||
response.body = await response.read()
|
response.body = await response.read()
|
||||||
|
response.status = response.status_code
|
||||||
|
response.content_type = response.headers.get("content-type")
|
||||||
|
|
||||||
|
if raw_cookies:
|
||||||
|
response.raw_cookies = {}
|
||||||
|
for cookie in response.cookies:
|
||||||
|
response.raw_cookies[cookie.name] = cookie
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _sanic_endpoint_test(
|
def _sanic_endpoint_test(
|
||||||
|
@ -83,11 +95,15 @@ class SanicTestClient:
|
||||||
server_kwargs = dict(sock=sock, **server_kwargs)
|
server_kwargs = dict(sock=sock, **server_kwargs)
|
||||||
host, port = sock.getsockname()
|
host, port = sock.getsockname()
|
||||||
|
|
||||||
if uri.startswith(("http:", "https:", "ftp:", "ftps://", "//")):
|
if uri.startswith(
|
||||||
|
("http:", "https:", "ftp:", "ftps://", "//", "ws:", "wss:")
|
||||||
|
):
|
||||||
url = uri
|
url = uri
|
||||||
else:
|
else:
|
||||||
url = "http://{host}:{port}{uri}".format(
|
uri = uri if uri.startswith("/") else "/{uri}".format(uri=uri)
|
||||||
host=host, port=port, uri=uri
|
scheme = "ws" if method == "websocket" else "http"
|
||||||
|
url = "{scheme}://{host}:{port}{uri}".format(
|
||||||
|
scheme=scheme, host=host, port=port, uri=uri
|
||||||
)
|
)
|
||||||
|
|
||||||
@self.app.listener("after_server_start")
|
@self.app.listener("after_server_start")
|
||||||
|
@ -146,3 +162,6 @@ class SanicTestClient:
|
||||||
|
|
||||||
def head(self, *args, **kwargs):
|
def head(self, *args, **kwargs):
|
||||||
return self._sanic_endpoint_test("head", *args, **kwargs)
|
return self._sanic_endpoint_test("head", *args, **kwargs)
|
||||||
|
|
||||||
|
def websocket(self, *args, **kwargs):
|
||||||
|
return self._sanic_endpoint_test("websocket", *args, **kwargs)
|
||||||
|
|
|
@ -14,7 +14,7 @@ multi_line_output = 3
|
||||||
not_skip = __init__.py
|
not_skip = __init__.py
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
current_version = 0.8.3
|
current_version = 19.3.1
|
||||||
file = sanic/__init__.py
|
file = sanic/__init__.py
|
||||||
current_version_pattern = __version__ = "{current_version}"
|
current_version_pattern = __version__ = "{current_version}"
|
||||||
new_version_pattern = __version__ = "{new_version}"
|
new_version_pattern = __version__ = "{new_version}"
|
||||||
|
|
14
setup.py
14
setup.py
|
@ -50,12 +50,12 @@ with open_local(["README.rst"]) as rm:
|
||||||
setup_kwargs = {
|
setup_kwargs = {
|
||||||
"name": "sanic",
|
"name": "sanic",
|
||||||
"version": version,
|
"version": version,
|
||||||
"url": "http://github.com/channelcat/sanic/",
|
"url": "http://github.com/huge-success/sanic/",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Channel Cat",
|
"author": "Sanic Community",
|
||||||
"author_email": "channelcat@gmail.com",
|
"author_email": "admhpkns@gmail.com",
|
||||||
"description": (
|
"description": (
|
||||||
"A microframework based on uvloop, httptools, and learnings of flask"
|
"A web server and web framework that's written to go fast. Build fast. Run fast."
|
||||||
),
|
),
|
||||||
"long_description": long_description,
|
"long_description": long_description,
|
||||||
"packages": ["sanic"],
|
"packages": ["sanic"],
|
||||||
|
@ -64,7 +64,6 @@ setup_kwargs = {
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 4 - Beta",
|
||||||
"Environment :: Web Environment",
|
"Environment :: Web Environment",
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
"Programming Language :: Python :: 3.5",
|
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.6",
|
||||||
"Programming Language :: Python :: 3.7",
|
"Programming Language :: Python :: 3.7",
|
||||||
],
|
],
|
||||||
|
@ -90,7 +89,8 @@ tests_require = [
|
||||||
"multidict>=4.0,<5.0",
|
"multidict>=4.0,<5.0",
|
||||||
"gunicorn",
|
"gunicorn",
|
||||||
"pytest-cov",
|
"pytest-cov",
|
||||||
"aiohttp>=2.3.0,<=3.2.1",
|
"httpcore==0.1.1",
|
||||||
|
"requests-async==0.4.0",
|
||||||
"beautifulsoup4",
|
"beautifulsoup4",
|
||||||
uvloop,
|
uvloop,
|
||||||
ujson,
|
ujson,
|
||||||
|
@ -119,7 +119,7 @@ extras_require = {
|
||||||
"recommonmark",
|
"recommonmark",
|
||||||
"sphinxcontrib-asyncio",
|
"sphinxcontrib-asyncio",
|
||||||
"docutils",
|
"docutils",
|
||||||
"pygments"
|
"pygments",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
from random import choice, seed
|
from random import choice, seed
|
||||||
|
|
||||||
from pytest import mark
|
from pytest import mark
|
||||||
|
|
||||||
import sanic.router
|
import sanic.router
|
||||||
|
|
||||||
|
|
||||||
seed("Pack my box with five dozen liquor jugs.")
|
seed("Pack my box with five dozen liquor jugs.")
|
||||||
|
|
||||||
# Disable Caching for testing purpose
|
# Disable Caching for testing purpose
|
||||||
|
|
|
@ -9,6 +9,7 @@ import pytest
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.router import RouteExists, Router
|
from sanic.router import RouteExists, Router
|
||||||
|
|
||||||
|
|
||||||
random.seed("Pack my box with five dozen liquor jugs.")
|
random.seed("Pack my box with five dozen liquor jugs.")
|
||||||
|
|
||||||
if sys.platform in ["win32", "cygwin"]:
|
if sys.platform in ["win32", "cygwin"]:
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
# Run with python3 simple_server.py PORT
|
# Run with python3 simple_server.py PORT
|
||||||
|
|
||||||
from aiohttp import web
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import sys
|
import sys
|
||||||
import uvloop
|
|
||||||
import ujson as json
|
import ujson as json
|
||||||
|
import uvloop
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
|
||||||
loop = uvloop.new_event_loop()
|
loop = uvloop.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# Run with: gunicorn --workers=1 --worker-class=meinheld.gmeinheld.MeinheldWorker -b :8000 simple_server:app
|
# Run with: gunicorn --workers=1 --worker-class=meinheld.gmeinheld.MeinheldWorker -b :8000 simple_server:app
|
||||||
import bottle
|
import bottle
|
||||||
from bottle import route, run
|
|
||||||
import ujson
|
import ujson
|
||||||
|
|
||||||
|
from bottle import route, run
|
||||||
|
|
||||||
|
|
||||||
@route("/")
|
@route("/")
|
||||||
def index():
|
def index():
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
# Run with: python3 -O simple_server.py
|
# Run with: python3 -O simple_server.py
|
||||||
import asyncio
|
import asyncio
|
||||||
from kyoukai import Kyoukai, HTTPRequestContext
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import ujson
|
import ujson
|
||||||
import uvloop
|
import uvloop
|
||||||
|
|
||||||
|
from kyoukai import HTTPRequestContext, Kyoukai
|
||||||
|
|
||||||
|
|
||||||
loop = uvloop.new_event_loop()
|
loop = uvloop.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import asyncpg
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import timeit
|
||||||
|
|
||||||
|
import asyncpg
|
||||||
|
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
|
||||||
currentdir = os.path.dirname(
|
currentdir = os.path.dirname(
|
||||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||||
)
|
)
|
||||||
sys.path.insert(0, currentdir + "/../../../")
|
sys.path.insert(0, currentdir + "/../../../")
|
||||||
|
|
||||||
import timeit
|
|
||||||
|
|
||||||
from sanic.response import json
|
|
||||||
|
|
||||||
print(json({"test": True}).output())
|
print(json({"test": True}).output())
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import json
|
||||||
|
|
||||||
|
|
||||||
currentdir = os.path.dirname(
|
currentdir = os.path.dirname(
|
||||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||||
)
|
)
|
||||||
sys.path.insert(0, currentdir + "/../../../")
|
sys.path.insert(0, currentdir + "/../../../")
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import json
|
|
||||||
|
|
||||||
app = Sanic("test")
|
app = Sanic("test")
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.exceptions import ServerError
|
||||||
|
from sanic.response import json, text
|
||||||
|
|
||||||
|
|
||||||
currentdir = os.path.dirname(
|
currentdir = os.path.dirname(
|
||||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||||
)
|
)
|
||||||
sys.path.insert(0, currentdir + "/../../../")
|
sys.path.insert(0, currentdir + "/../../../")
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import json, text
|
|
||||||
from sanic.exceptions import ServerError
|
|
||||||
|
|
||||||
app = Sanic("test")
|
app = Sanic("test")
|
||||||
|
|
||||||
|
@ -56,8 +58,6 @@ def query_string(request):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
app.run(host="0.0.0.0", port=sys.argv[1])
|
app.run(host="0.0.0.0", port=sys.argv[1])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# Run with: python simple_server.py
|
# Run with: python simple_server.py
|
||||||
import ujson
|
import ujson
|
||||||
|
|
||||||
from tornado import ioloop, web
|
from tornado import ioloop, web
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,16 @@
|
||||||
""" Minimal helloworld application.
|
""" Minimal helloworld application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from wheezy.http import HTTPResponse
|
import ujson
|
||||||
from wheezy.http import WSGIApplication
|
|
||||||
|
from wheezy.http import HTTPResponse, WSGIApplication
|
||||||
from wheezy.http.response import json_response
|
from wheezy.http.response import json_response
|
||||||
from wheezy.routing import url
|
from wheezy.routing import url
|
||||||
from wheezy.web.handlers import BaseHandler
|
from wheezy.web.handlers import BaseHandler
|
||||||
from wheezy.web.middleware import bootstrap_defaults
|
from wheezy.web.middleware import (
|
||||||
from wheezy.web.middleware import path_routing_middleware_factory
|
bootstrap_defaults,
|
||||||
|
path_routing_middleware_factory,
|
||||||
import ujson
|
)
|
||||||
|
|
||||||
|
|
||||||
class WelcomeHandler(BaseHandler):
|
class WelcomeHandler(BaseHandler):
|
||||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from inspect import isawaitable
|
from inspect import isawaitable
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import SanicException
|
||||||
|
@ -11,7 +12,7 @@ from sanic.response import text
|
||||||
|
|
||||||
def uvloop_installed():
|
def uvloop_installed():
|
||||||
try:
|
try:
|
||||||
import uvloop
|
import uvloop # noqa
|
||||||
|
|
||||||
return True
|
return True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
@ -3,7 +3,8 @@ from pytest import raises
|
||||||
from sanic.app import Sanic
|
from sanic.app import Sanic
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import text, HTTPResponse
|
from sanic.response import HTTPResponse, text
|
||||||
|
|
||||||
|
|
||||||
MIDDLEWARE_INVOKE_COUNTER = {"request": 0, "response": 0}
|
MIDDLEWARE_INVOKE_COUNTER = {"request": 0, "response": 0}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ import pytest
|
||||||
from sanic.app import Sanic
|
from sanic.app import Sanic
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.constants import HTTP_METHODS
|
from sanic.constants import HTTP_METHODS
|
||||||
from sanic.exceptions import NotFound, ServerError, InvalidUsage
|
from sanic.exceptions import InvalidUsage, NotFound, ServerError
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import text, json
|
from sanic.response import json, text
|
||||||
from sanic.views import CompositionView
|
from sanic.views import CompositionView
|
||||||
|
|
||||||
|
|
||||||
|
@ -467,16 +467,8 @@ def test_bp_shorthand(app):
|
||||||
request, response = app.test_client.get("/delete")
|
request, response = app.test_client.get("/delete")
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
request, response = app.test_client.get(
|
request, response = app.test_client.websocket("/ws/")
|
||||||
"/ws/",
|
assert response.opened is True
|
||||||
headers={
|
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "upgrade",
|
|
||||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
|
||||||
"Sec-WebSocket-Version": "13",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status == 101
|
|
||||||
assert ev.is_set()
|
assert ev.is_set()
|
||||||
|
|
||||||
|
|
||||||
|
@ -595,14 +587,13 @@ def test_blueprint_middleware_with_args(app: Sanic):
|
||||||
"/wa", headers={"content-type": "plain/text"}
|
"/wa", headers={"content-type": "plain/text"}
|
||||||
)
|
)
|
||||||
assert response.json.get("test") == "value"
|
assert response.json.get("test") == "value"
|
||||||
d = {}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("file_name", ["test.file"])
|
@pytest.mark.parametrize("file_name", ["test.file"])
|
||||||
def test_static_blueprint_name(app: Sanic, static_file_directory, file_name):
|
def test_static_blueprint_name(app: Sanic, static_file_directory, file_name):
|
||||||
current_file = inspect.getfile(inspect.currentframe())
|
current_file = inspect.getfile(inspect.currentframe())
|
||||||
with open(current_file, "rb") as file:
|
with open(current_file, "rb") as file:
|
||||||
current_file_contents = file.read()
|
file.read()
|
||||||
|
|
||||||
bp = Blueprint(name="static", url_prefix="/static", strict_slashes=False)
|
bp = Blueprint(name="static", url_prefix="/static", strict_slashes=False)
|
||||||
|
|
||||||
|
@ -662,16 +653,8 @@ def test_websocket_route(app: Sanic):
|
||||||
|
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
|
|
||||||
_, response = app.test_client.get(
|
_, response = app.test_client.websocket("/ws/test")
|
||||||
"/ws/test",
|
assert response.opened is True
|
||||||
headers={
|
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "upgrade",
|
|
||||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
|
||||||
"Sec-WebSocket-Version": "13",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status == 101
|
|
||||||
assert event.is_set()
|
assert event.is_set()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
from contextlib import contextmanager
|
||||||
from os import environ
|
from os import environ
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from contextlib import contextmanager
|
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.config import Config, DEFAULT_CONFIG
|
from sanic.config import DEFAULT_CONFIG, Config
|
||||||
from sanic.exceptions import PyFileError
|
from sanic.exceptions import PyFileError
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from http.cookies import SimpleCookie
|
from http.cookies import SimpleCookie
|
||||||
from sanic.response import text
|
|
||||||
import pytest
|
import pytest
|
||||||
from sanic.cookies import Cookie, DEFAULT_MAX_AGE
|
|
||||||
|
from sanic.cookies import Cookie
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
# GET
|
# GET
|
||||||
|
@ -100,7 +103,7 @@ def test_cookie_deletion(app):
|
||||||
|
|
||||||
assert int(response_cookies["i_want_to_die"]["max-age"]) == 0
|
assert int(response_cookies["i_want_to_die"]["max-age"]) == 0
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
_ = response.cookies["i_never_existed"]
|
response.cookies["i_never_existed"]
|
||||||
|
|
||||||
|
|
||||||
def test_cookie_reserved_cookie():
|
def test_cookie_reserved_cookie():
|
||||||
|
@ -135,7 +138,7 @@ def test_cookie_set_same_key(app):
|
||||||
|
|
||||||
request, response = app.test_client.get("/", cookies=cookies)
|
request, response = app.test_client.get("/", cookies=cookies)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.cookies["test"].value == "pass"
|
assert response.cookies["test"] == "pass"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("max_age", ["0", 30, 30.0, 30.1, "30", "test"])
|
@pytest.mark.parametrize("max_age", ["0", 30, 30.0, 30.1, "30", "test"])
|
||||||
|
@ -149,19 +152,42 @@ def test_cookie_max_age(app, max_age):
|
||||||
response.cookies["test"]["max-age"] = max_age
|
response.cookies["test"]["max-age"] = max_age
|
||||||
return response
|
return response
|
||||||
|
|
||||||
request, response = app.test_client.get("/", cookies=cookies)
|
request, response = app.test_client.get(
|
||||||
|
"/", cookies=cookies, raw_cookies=True
|
||||||
|
)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
assert response.cookies["test"].value == "pass"
|
cookie = response.cookies.get("test")
|
||||||
|
if (
|
||||||
|
str(max_age).isdigit()
|
||||||
|
and int(max_age) == float(max_age)
|
||||||
|
and int(max_age) != 0
|
||||||
|
):
|
||||||
|
cookie_expires = datetime.utcfromtimestamp(
|
||||||
|
response.raw_cookies["test"].expires
|
||||||
|
).replace(microsecond=0)
|
||||||
|
|
||||||
if str(max_age).isdigit() and int(max_age) == float(max_age):
|
# Grabbing utcnow after the response may lead to it being off slightly.
|
||||||
assert response.cookies["test"]["max-age"] == str(max_age)
|
# Therefore, we 0 out the microseconds, and accept the test if there
|
||||||
|
# is a 1 second difference.
|
||||||
|
expires = datetime.utcnow().replace(microsecond=0) + timedelta(
|
||||||
|
seconds=int(max_age)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert cookie == "pass"
|
||||||
|
assert (
|
||||||
|
cookie_expires == expires
|
||||||
|
or cookie_expires == expires + timedelta(seconds=-1)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
assert response.cookies["test"]["max-age"] == str(DEFAULT_MAX_AGE)
|
assert cookie is None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("expires", [datetime.now() + timedelta(seconds=60)])
|
@pytest.mark.parametrize(
|
||||||
|
"expires", [datetime.utcnow() + timedelta(seconds=60)]
|
||||||
|
)
|
||||||
def test_cookie_expires(app, expires):
|
def test_cookie_expires(app, expires):
|
||||||
|
expires = expires.replace(microsecond=0)
|
||||||
cookies = {"test": "wait"}
|
cookies = {"test": "wait"}
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
@ -171,15 +197,16 @@ def test_cookie_expires(app, expires):
|
||||||
response.cookies["test"]["expires"] = expires
|
response.cookies["test"]["expires"] = expires
|
||||||
return response
|
return response
|
||||||
|
|
||||||
request, response = app.test_client.get("/", cookies=cookies)
|
request, response = app.test_client.get(
|
||||||
|
"/", cookies=cookies, raw_cookies=True
|
||||||
|
)
|
||||||
|
cookie_expires = datetime.utcfromtimestamp(
|
||||||
|
response.raw_cookies["test"].expires
|
||||||
|
).replace(microsecond=0)
|
||||||
|
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
assert response.cookies["test"] == "pass"
|
||||||
assert response.cookies["test"].value == "pass"
|
assert cookie_expires == expires
|
||||||
|
|
||||||
if isinstance(expires, datetime):
|
|
||||||
expires = expires.strftime("%a, %d-%b-%Y %T GMT")
|
|
||||||
|
|
||||||
assert response.cookies["test"]["expires"] == expires
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("expires", ["Fri, 21-Dec-2018 15:30:00 GMT"])
|
@pytest.mark.parametrize("expires", ["Fri, 21-Dec-2018 15:30:00 GMT"])
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from sanic.response import text
|
|
||||||
from threading import Event
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
|
from threading import Event
|
||||||
|
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
def test_create_task(app):
|
def test_create_task(app):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from sanic.server import HttpProtocol
|
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
from sanic.server import HttpProtocol
|
||||||
|
|
||||||
|
|
||||||
class CustomHttpProtocol(HttpProtocol):
|
class CustomHttpProtocol(HttpProtocol):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
from sanic.router import RouteExists
|
from sanic.router import RouteExists
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
|
from sanic.exceptions import (
|
||||||
|
Forbidden,
|
||||||
|
InvalidUsage,
|
||||||
|
NotFound,
|
||||||
|
ServerError,
|
||||||
|
Unauthorized,
|
||||||
|
abort,
|
||||||
|
)
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
from sanic.exceptions import InvalidUsage, ServerError, NotFound, Unauthorized
|
|
||||||
from sanic.exceptions import Forbidden, abort
|
|
||||||
|
|
||||||
|
|
||||||
class SanicExceptionTestException(Exception):
|
class SanicExceptionTestException(Exception):
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import text
|
|
||||||
from sanic.exceptions import InvalidUsage, ServerError, NotFound
|
|
||||||
from sanic.handlers import ErrorHandler
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.exceptions import InvalidUsage, NotFound, ServerError
|
||||||
|
from sanic.handlers import ErrorHandler
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
exception_handler_app = Sanic("test_exception_handler")
|
exception_handler_app = Sanic("test_exception_handler")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,143 @@
|
||||||
from json import JSONDecodeError
|
|
||||||
from sanic import Sanic
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import functools
|
||||||
|
import socket
|
||||||
|
|
||||||
from asyncio import sleep as aio_sleep
|
from asyncio import sleep as aio_sleep
|
||||||
|
from http.client import _encode
|
||||||
|
from json import JSONDecodeError
|
||||||
|
|
||||||
|
import httpcore
|
||||||
|
import requests_async as requests
|
||||||
|
|
||||||
|
from httpcore import PoolTimeout
|
||||||
|
|
||||||
|
from sanic import Sanic, server
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
from sanic import server
|
from sanic.testing import HOST, PORT, SanicTestClient
|
||||||
import aiohttp
|
|
||||||
from aiohttp import TCPConnector
|
|
||||||
from sanic.testing import SanicTestClient, HOST, PORT
|
# import traceback
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_FOR_TESTS = {"KEEP_ALIVE_TIMEOUT": 2, "KEEP_ALIVE": True}
|
CONFIG_FOR_TESTS = {"KEEP_ALIVE_TIMEOUT": 2, "KEEP_ALIVE": True}
|
||||||
|
|
||||||
|
old_conn = None
|
||||||
|
|
||||||
class ReuseableTCPConnector(TCPConnector):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(ReuseableTCPConnector, self).__init__(*args, **kwargs)
|
|
||||||
self.old_proto = None
|
|
||||||
|
|
||||||
async def connect(self, req, *args, **kwargs):
|
class ReusableSanicConnectionPool(httpcore.ConnectionPool):
|
||||||
new_conn = await super(ReuseableTCPConnector, self).connect(
|
async def acquire_connection(self, url, ssl, timeout):
|
||||||
req, *args, **kwargs
|
global old_conn
|
||||||
|
if timeout.connect_timeout and not timeout.pool_timeout:
|
||||||
|
timeout.pool_timeout = timeout.connect_timeout
|
||||||
|
key = (url.scheme, url.hostname, url.port, ssl, timeout)
|
||||||
|
try:
|
||||||
|
connection = self._keepalive_connections[key].pop()
|
||||||
|
if not self._keepalive_connections[key]:
|
||||||
|
del self._keepalive_connections[key]
|
||||||
|
self.num_keepalive_connections -= 1
|
||||||
|
self.num_active_connections += 1
|
||||||
|
|
||||||
|
except (KeyError, IndexError):
|
||||||
|
ssl_context = await self.get_ssl_context(url, ssl)
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(
|
||||||
|
self._max_connections.acquire(), timeout.pool_timeout
|
||||||
)
|
)
|
||||||
if self.old_proto is not None:
|
except asyncio.TimeoutError:
|
||||||
if self.old_proto != new_conn._protocol:
|
raise PoolTimeout()
|
||||||
|
release = functools.partial(self.release_connection, key=key)
|
||||||
|
connection = httpcore.connections.Connection(
|
||||||
|
timeout=timeout, on_release=release
|
||||||
|
)
|
||||||
|
self.num_active_connections += 1
|
||||||
|
await connection.open(url.hostname, url.port, ssl=ssl_context)
|
||||||
|
|
||||||
|
if old_conn is not None:
|
||||||
|
if old_conn != connection:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"We got a new connection, wanted the same one!"
|
"We got a new connection, wanted the same one!"
|
||||||
)
|
)
|
||||||
print(new_conn.__dict__)
|
old_conn = connection
|
||||||
self.old_proto = new_conn._protocol
|
|
||||||
return new_conn
|
return connection
|
||||||
|
|
||||||
|
|
||||||
|
class ReusableSanicAdapter(requests.adapters.HTTPAdapter):
|
||||||
|
def __init__(self):
|
||||||
|
self.pool = ReusableSanicConnectionPool()
|
||||||
|
|
||||||
|
async def send(
|
||||||
|
self,
|
||||||
|
request,
|
||||||
|
stream=False,
|
||||||
|
timeout=None,
|
||||||
|
verify=True,
|
||||||
|
cert=None,
|
||||||
|
proxies=None,
|
||||||
|
):
|
||||||
|
|
||||||
|
method = request.method
|
||||||
|
url = request.url
|
||||||
|
headers = [
|
||||||
|
(_encode(k), _encode(v)) for k, v in request.headers.items()
|
||||||
|
]
|
||||||
|
|
||||||
|
if not request.body:
|
||||||
|
body = b""
|
||||||
|
elif isinstance(request.body, str):
|
||||||
|
body = _encode(request.body)
|
||||||
|
else:
|
||||||
|
body = request.body
|
||||||
|
|
||||||
|
if isinstance(timeout, tuple):
|
||||||
|
timeout_kwargs = {
|
||||||
|
"connect_timeout": timeout[0],
|
||||||
|
"read_timeout": timeout[1],
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
timeout_kwargs = {
|
||||||
|
"connect_timeout": timeout,
|
||||||
|
"read_timeout": timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl = httpcore.SSLConfig(cert=cert, verify=verify)
|
||||||
|
timeout = httpcore.TimeoutConfig(**timeout_kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await self.pool.request(
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
headers=headers,
|
||||||
|
body=body,
|
||||||
|
stream=stream,
|
||||||
|
ssl=ssl,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
except (httpcore.BadResponse, socket.error) as err:
|
||||||
|
raise ConnectionError(err, request=request)
|
||||||
|
except httpcore.ConnectTimeout as err:
|
||||||
|
raise requests.exceptions.ConnectTimeout(err, request=request)
|
||||||
|
except httpcore.ReadTimeout as err:
|
||||||
|
raise requests.exceptions.ReadTimeout(err, request=request)
|
||||||
|
|
||||||
|
return self.build_response(request, response)
|
||||||
|
|
||||||
|
|
||||||
|
class ResusableSanicSession(requests.Session):
|
||||||
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
adapter = ReusableSanicAdapter()
|
||||||
|
self.mount("http://", adapter)
|
||||||
|
self.mount("https://", adapter)
|
||||||
|
|
||||||
|
|
||||||
class ReuseableSanicTestClient(SanicTestClient):
|
class ReuseableSanicTestClient(SanicTestClient):
|
||||||
def __init__(self, app, loop=None):
|
def __init__(self, app, loop=None):
|
||||||
super(ReuseableSanicTestClient, self).__init__(app)
|
super().__init__(app)
|
||||||
if loop is None:
|
if loop is None:
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
self._loop = loop
|
self._loop = loop
|
||||||
|
@ -51,12 +155,11 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
debug=False,
|
debug=False,
|
||||||
server_kwargs={"return_asyncio_server": True},
|
server_kwargs={"return_asyncio_server": True},
|
||||||
*request_args,
|
*request_args,
|
||||||
**request_kwargs
|
**request_kwargs,
|
||||||
):
|
):
|
||||||
loop = self._loop
|
loop = self._loop
|
||||||
results = [None, None]
|
results = [None, None]
|
||||||
exceptions = []
|
exceptions = []
|
||||||
do_kill_server = request_kwargs.pop("end_server", False)
|
|
||||||
if gather_request:
|
if gather_request:
|
||||||
|
|
||||||
def _collect_request(request):
|
def _collect_request(request):
|
||||||
|
@ -65,21 +168,27 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
|
|
||||||
self.app.request_middleware.appendleft(_collect_request)
|
self.app.request_middleware.appendleft(_collect_request)
|
||||||
|
|
||||||
|
if uri.startswith(
|
||||||
|
("http:", "https:", "ftp:", "ftps://", "//", "ws:", "wss:")
|
||||||
|
):
|
||||||
|
url = uri
|
||||||
|
else:
|
||||||
|
uri = uri if uri.startswith("/") else "/{uri}".format(uri=uri)
|
||||||
|
scheme = "http"
|
||||||
|
url = "{scheme}://{host}:{port}{uri}".format(
|
||||||
|
scheme=scheme, host=HOST, port=PORT, uri=uri
|
||||||
|
)
|
||||||
|
|
||||||
@self.app.listener("after_server_start")
|
@self.app.listener("after_server_start")
|
||||||
async def _collect_response(loop):
|
async def _collect_response(loop):
|
||||||
try:
|
try:
|
||||||
if do_kill_server:
|
|
||||||
request_kwargs["end_session"] = True
|
|
||||||
response = await self._local_request(
|
response = await self._local_request(
|
||||||
method, uri, *request_args, **request_kwargs
|
method, url, *request_args, **request_kwargs
|
||||||
)
|
)
|
||||||
results[-1] = response
|
results[-1] = response
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
import traceback
|
# traceback.print_tb(e2.__traceback__)
|
||||||
|
|
||||||
traceback.print_tb(e2.__traceback__)
|
|
||||||
exceptions.append(e2)
|
exceptions.append(e2)
|
||||||
# Don't stop here! self.app.stop()
|
|
||||||
|
|
||||||
if self._server is not None:
|
if self._server is not None:
|
||||||
_server = self._server
|
_server = self._server
|
||||||
|
@ -94,27 +203,14 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
loop._stopping = False
|
loop._stopping = False
|
||||||
http_server = loop.run_until_complete(_server_co)
|
_server = loop.run_until_complete(_server_co)
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
import traceback
|
# traceback.print_tb(e1.__traceback__)
|
||||||
|
|
||||||
traceback.print_tb(e1.__traceback__)
|
|
||||||
raise e1
|
raise e1
|
||||||
self._server = _server = http_server
|
self._server = _server
|
||||||
server.trigger_events(self.app.listeners["after_server_start"], loop)
|
server.trigger_events(self.app.listeners["after_server_start"], loop)
|
||||||
self.app.listeners["after_server_start"].pop()
|
self.app.listeners["after_server_start"].pop()
|
||||||
|
|
||||||
if do_kill_server:
|
|
||||||
try:
|
|
||||||
_server.close()
|
|
||||||
self._server = None
|
|
||||||
loop.run_until_complete(_server.wait_closed())
|
|
||||||
self.app.stop()
|
|
||||||
except Exception as e3:
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
traceback.print_tb(e3.__traceback__)
|
|
||||||
exceptions.append(e3)
|
|
||||||
if exceptions:
|
if exceptions:
|
||||||
raise ValueError("Exception during request: {}".format(exceptions))
|
raise ValueError("Exception during request: {}".format(exceptions))
|
||||||
|
|
||||||
|
@ -137,59 +233,61 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
"Request object expected, got ({})".format(results)
|
"Request object expected, got ({})".format(results)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def kill_server(self):
|
||||||
|
try:
|
||||||
|
if self._server:
|
||||||
|
self._server.close()
|
||||||
|
self._loop.run_until_complete(self._server.wait_closed())
|
||||||
|
self._server = None
|
||||||
|
self.app.stop()
|
||||||
|
|
||||||
|
if self._session:
|
||||||
|
self._loop.run_until_complete(self._session.close())
|
||||||
|
self._session = None
|
||||||
|
|
||||||
|
except Exception as e3:
|
||||||
|
raise e3
|
||||||
|
|
||||||
# Copied from SanicTestClient, but with some changes to reuse the
|
# Copied from SanicTestClient, but with some changes to reuse the
|
||||||
# same TCPConnection and the sane ClientSession more than once.
|
# same TCPConnection and the sane ClientSession more than once.
|
||||||
# Note, you cannot use the same session if you are in a _different_
|
# Note, you cannot use the same session if you are in a _different_
|
||||||
# loop, so the changes above are required too.
|
# loop, so the changes above are required too.
|
||||||
async def _local_request(self, method, uri, cookies=None, *args, **kwargs):
|
async def _local_request(self, method, url, *args, **kwargs):
|
||||||
|
raw_cookies = kwargs.pop("raw_cookies", None)
|
||||||
request_keepalive = kwargs.pop(
|
request_keepalive = kwargs.pop(
|
||||||
"request_keepalive", CONFIG_FOR_TESTS["KEEP_ALIVE_TIMEOUT"]
|
"request_keepalive", CONFIG_FOR_TESTS["KEEP_ALIVE_TIMEOUT"]
|
||||||
)
|
)
|
||||||
if uri.startswith(("http:", "https:", "ftp:", "ftps://" "//")):
|
|
||||||
url = uri
|
|
||||||
else:
|
|
||||||
url = "http://{host}:{port}{uri}".format(
|
|
||||||
host=HOST, port=self.port, uri=uri
|
|
||||||
)
|
|
||||||
do_kill_session = kwargs.pop("end_session", False)
|
|
||||||
if self._session:
|
if self._session:
|
||||||
session = self._session
|
_session = self._session
|
||||||
else:
|
else:
|
||||||
if self._tcp_connector:
|
_session = ResusableSanicSession()
|
||||||
conn = self._tcp_connector
|
self._session = _session
|
||||||
else:
|
async with _session as session:
|
||||||
conn = ReuseableTCPConnector(
|
|
||||||
ssl=False,
|
|
||||||
loop=self._loop,
|
|
||||||
keepalive_timeout=request_keepalive,
|
|
||||||
)
|
|
||||||
self._tcp_connector = conn
|
|
||||||
session = aiohttp.ClientSession(
|
|
||||||
cookies=cookies, connector=conn, loop=self._loop
|
|
||||||
)
|
|
||||||
self._session = session
|
|
||||||
|
|
||||||
async with getattr(session, method.lower())(
|
|
||||||
url, *args, **kwargs
|
|
||||||
) as response:
|
|
||||||
try:
|
try:
|
||||||
response.text = await response.text()
|
response = await getattr(session, method.lower())(
|
||||||
except UnicodeDecodeError:
|
url,
|
||||||
response.text = None
|
verify=False,
|
||||||
|
timeout=request_keepalive,
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
except NameError:
|
||||||
|
raise Exception(response.status_code)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response.json = await response.json()
|
response.json = response.json()
|
||||||
except (
|
except (JSONDecodeError, UnicodeDecodeError):
|
||||||
JSONDecodeError,
|
|
||||||
UnicodeDecodeError,
|
|
||||||
aiohttp.ClientResponseError,
|
|
||||||
):
|
|
||||||
response.json = None
|
response.json = None
|
||||||
|
|
||||||
response.body = await response.read()
|
response.body = await response.read()
|
||||||
if do_kill_session:
|
response.status = response.status_code
|
||||||
await session.close()
|
response.content_type = response.headers.get("content-type")
|
||||||
self._session = None
|
|
||||||
|
if raw_cookies:
|
||||||
|
response.raw_cookies = {}
|
||||||
|
for cookie in response.cookies:
|
||||||
|
response.raw_cookies[cookie.name] = cookie
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@ -229,9 +327,10 @@ def test_keep_alive_timeout_reuse():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "OK"
|
assert response.text == "OK"
|
||||||
loop.run_until_complete(aio_sleep(1))
|
loop.run_until_complete(aio_sleep(1))
|
||||||
request, response = client.get("/1", end_server=True)
|
request, response = client.get("/1")
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "OK"
|
assert response.text == "OK"
|
||||||
|
client.kill_server()
|
||||||
|
|
||||||
|
|
||||||
def test_keep_alive_client_timeout():
|
def test_keep_alive_client_timeout():
|
||||||
|
@ -241,20 +340,21 @@ def test_keep_alive_client_timeout():
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
client = ReuseableSanicTestClient(keep_alive_app_client_timeout, loop)
|
client = ReuseableSanicTestClient(keep_alive_app_client_timeout, loop)
|
||||||
headers = {"Connection": "keep-alive"}
|
headers = {"Connection": "keep-alive"}
|
||||||
request, response = client.get("/1", headers=headers, request_keepalive=1)
|
try:
|
||||||
|
request, response = client.get(
|
||||||
|
"/1", headers=headers, request_keepalive=1
|
||||||
|
)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "OK"
|
assert response.text == "OK"
|
||||||
loop.run_until_complete(aio_sleep(2))
|
loop.run_until_complete(aio_sleep(2))
|
||||||
exception = None
|
exception = None
|
||||||
try:
|
request, response = client.get("/1", request_keepalive=1)
|
||||||
request, response = client.get(
|
|
||||||
"/1", end_server=True, request_keepalive=1
|
|
||||||
)
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
exception = e
|
exception = e
|
||||||
assert exception is not None
|
assert exception is not None
|
||||||
assert isinstance(exception, ValueError)
|
assert isinstance(exception, ValueError)
|
||||||
assert "got a new connection" in exception.args[0]
|
assert "got a new connection" in exception.args[0]
|
||||||
|
client.kill_server()
|
||||||
|
|
||||||
|
|
||||||
def test_keep_alive_server_timeout():
|
def test_keep_alive_server_timeout():
|
||||||
|
@ -266,15 +366,15 @@ def test_keep_alive_server_timeout():
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
client = ReuseableSanicTestClient(keep_alive_app_server_timeout, loop)
|
client = ReuseableSanicTestClient(keep_alive_app_server_timeout, loop)
|
||||||
headers = {"Connection": "keep-alive"}
|
headers = {"Connection": "keep-alive"}
|
||||||
request, response = client.get("/1", headers=headers, request_keepalive=60)
|
try:
|
||||||
|
request, response = client.get(
|
||||||
|
"/1", headers=headers, request_keepalive=60
|
||||||
|
)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "OK"
|
assert response.text == "OK"
|
||||||
loop.run_until_complete(aio_sleep(3))
|
loop.run_until_complete(aio_sleep(3))
|
||||||
exception = None
|
exception = None
|
||||||
try:
|
request, response = client.get("/1", request_keepalive=60)
|
||||||
request, response = client.get(
|
|
||||||
"/1", request_keepalive=60, end_server=True
|
|
||||||
)
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
exception = e
|
exception = e
|
||||||
assert exception is not None
|
assert exception is not None
|
||||||
|
@ -283,3 +383,4 @@ def test_keep_alive_server_timeout():
|
||||||
"Connection reset" in exception.args[0]
|
"Connection reset" in exception.args[0]
|
||||||
or "got a new connection" in exception.args[0]
|
or "got a new connection" in exception.args[0]
|
||||||
)
|
)
|
||||||
|
client.kill_server()
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import uuid
|
|
||||||
import logging
|
import logging
|
||||||
|
import uuid
|
||||||
|
|
||||||
from io import StringIO
|
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
|
from io import StringIO
|
||||||
import pytest
|
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
import sanic
|
import sanic
|
||||||
from sanic.response import text
|
|
||||||
from sanic.log import LOGGING_CONFIG_DEFAULTS
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.log import logger
|
from sanic.log import LOGGING_CONFIG_DEFAULTS, logger
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
logging_format = """module: %(module)s; \
|
logging_format = """module: %(module)s; \
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import logging
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import logging
|
||||||
|
|
||||||
from sanic.config import BASE_LOGO
|
from sanic.config import BASE_LOGO
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import uvloop # noqa
|
import uvloop # noqa
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from asyncio import CancelledError
|
from asyncio import CancelledError
|
||||||
|
|
||||||
from sanic.exceptions import NotFound
|
from sanic.exceptions import NotFound
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import HTTPResponse, text
|
from sanic.response import HTTPResponse, text
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
# GET
|
# GET
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
import pickle
|
||||||
import random
|
import random
|
||||||
import signal
|
import signal
|
||||||
import pickle
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic.testing import HOST, PORT
|
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
from sanic.testing import HOST, PORT
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.response import text
|
|
||||||
from sanic.exceptions import URLBuildError
|
|
||||||
from sanic.constants import HTTP_METHODS
|
from sanic.constants import HTTP_METHODS
|
||||||
|
from sanic.exceptions import URLBuildError
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import pytest
|
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
from sanic.response import text, redirect
|
import pytest
|
||||||
|
|
||||||
|
from sanic.response import redirect, text
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
from sanic.response import text, stream
|
from sanic.response import stream, text
|
||||||
|
|
||||||
|
|
||||||
async def test_request_cancel_when_connection_lost(loop, app, test_client):
|
async def test_request_cancel_when_connection_lost(loop, app, test_client):
|
||||||
|
|
|
@ -2,6 +2,7 @@ import random
|
||||||
|
|
||||||
from sanic.response import json
|
from sanic.response import json
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ujson import loads
|
from ujson import loads
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import asyncio
|
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.views import CompositionView
|
|
||||||
from sanic.views import HTTPMethodView
|
|
||||||
from sanic.views import stream as stream_decorator
|
|
||||||
from sanic.response import stream, text
|
|
||||||
from sanic.request import StreamBuffer
|
from sanic.request import StreamBuffer
|
||||||
|
from sanic.response import stream, text
|
||||||
|
from sanic.views import CompositionView, HTTPMethodView
|
||||||
|
from sanic.views import stream as stream_decorator
|
||||||
|
|
||||||
|
|
||||||
data = "abc" * 10000000
|
data = "abc" * 10000000
|
||||||
|
|
|
@ -1,183 +1,73 @@
|
||||||
from json import JSONDecodeError
|
import asyncio
|
||||||
|
|
||||||
|
import httpcore
|
||||||
|
import requests_async as requests
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
import asyncio
|
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
import aiohttp
|
from sanic.testing import SanicTestClient
|
||||||
from aiohttp import TCPConnector
|
|
||||||
from sanic.testing import SanicTestClient, HOST
|
|
||||||
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
# direct use
|
|
||||||
import packaging
|
|
||||||
|
|
||||||
version = packaging.version
|
|
||||||
except (ImportError, AttributeError):
|
|
||||||
# setuptools v39.0 and above.
|
|
||||||
try:
|
|
||||||
from setuptools.extern import packaging
|
|
||||||
except ImportError:
|
|
||||||
# Before setuptools v39.0
|
|
||||||
from pkg_resources.extern import packaging
|
|
||||||
version = packaging.version
|
|
||||||
except ImportError:
|
|
||||||
raise RuntimeError("The 'packaging' library is missing.")
|
|
||||||
|
|
||||||
|
|
||||||
aiohttp_version = version.parse(aiohttp.__version__)
|
class DelayableSanicConnectionPool(httpcore.ConnectionPool):
|
||||||
|
def __init__(self, request_delay=None, *args, **kwargs):
|
||||||
|
self._request_delay = request_delay
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
async def request(
|
||||||
|
self,
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
headers=(),
|
||||||
|
body=b"",
|
||||||
|
stream=False,
|
||||||
|
ssl=None,
|
||||||
|
timeout=None,
|
||||||
|
):
|
||||||
|
if ssl is None:
|
||||||
|
ssl = self.ssl_config
|
||||||
|
if timeout is None:
|
||||||
|
timeout = self.timeout
|
||||||
|
|
||||||
class DelayableTCPConnector(TCPConnector):
|
parsed_url = httpcore.URL(url)
|
||||||
class RequestContextManager(object):
|
request = httpcore.Request(
|
||||||
def __new__(cls, req, delay):
|
method, parsed_url, headers=headers, body=body
|
||||||
cls = super(
|
|
||||||
DelayableTCPConnector.RequestContextManager, cls
|
|
||||||
).__new__(cls)
|
|
||||||
cls.req = req
|
|
||||||
cls.send_task = None
|
|
||||||
cls.resp = None
|
|
||||||
cls.orig_send = getattr(req, "send")
|
|
||||||
cls.orig_start = None
|
|
||||||
cls.delay = delay
|
|
||||||
cls._acting_as = req
|
|
||||||
return cls
|
|
||||||
|
|
||||||
def __getattr__(self, item):
|
|
||||||
acting_as = self._acting_as
|
|
||||||
return getattr(acting_as, item)
|
|
||||||
|
|
||||||
async def start(self, connection, read_until_eof=False):
|
|
||||||
if self.send_task is None:
|
|
||||||
raise RuntimeError("do a send() before you do a start()")
|
|
||||||
resp = await self.send_task
|
|
||||||
self.send_task = None
|
|
||||||
self.resp = resp
|
|
||||||
self._acting_as = self.resp
|
|
||||||
self.orig_start = getattr(resp, "start")
|
|
||||||
|
|
||||||
try:
|
|
||||||
if aiohttp_version >= version.parse("3.3.0"):
|
|
||||||
ret = await self.orig_start(connection)
|
|
||||||
else:
|
|
||||||
ret = await self.orig_start(connection, read_until_eof)
|
|
||||||
except Exception as e:
|
|
||||||
raise e
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.resp is not None:
|
|
||||||
self.resp.close()
|
|
||||||
if self.send_task is not None:
|
|
||||||
self.send_task.cancel()
|
|
||||||
|
|
||||||
async def delayed_send(self, *args, **kwargs):
|
|
||||||
req = self.req
|
|
||||||
if self.delay and self.delay > 0:
|
|
||||||
# sync_sleep(self.delay)
|
|
||||||
await asyncio.sleep(self.delay)
|
|
||||||
t = req.loop.time()
|
|
||||||
print("sending at {}".format(t), flush=True)
|
|
||||||
next(iter(args)) # first arg is connection
|
|
||||||
|
|
||||||
try:
|
|
||||||
return await self.orig_send(*args, **kwargs)
|
|
||||||
except Exception as e:
|
|
||||||
if aiohttp_version < version.parse("3.1.0"):
|
|
||||||
return aiohttp.ClientResponse(req.method, req.url)
|
|
||||||
kw = dict(
|
|
||||||
writer=None,
|
|
||||||
continue100=None,
|
|
||||||
timer=None,
|
|
||||||
request_info=None,
|
|
||||||
traces=[],
|
|
||||||
loop=req.loop,
|
|
||||||
session=None,
|
|
||||||
)
|
)
|
||||||
if aiohttp_version < version.parse("3.3.0"):
|
connection = await self.acquire_connection(
|
||||||
kw["auto_decompress"] = None
|
parsed_url, ssl=ssl, timeout=timeout
|
||||||
return aiohttp.ClientResponse(req.method, req.url, **kw)
|
|
||||||
|
|
||||||
def _send(self, *args, **kwargs):
|
|
||||||
gen = self.delayed_send(*args, **kwargs)
|
|
||||||
task = self.req.loop.create_task(gen)
|
|
||||||
self.send_task = task
|
|
||||||
self._acting_as = task
|
|
||||||
return self
|
|
||||||
|
|
||||||
if aiohttp_version >= version.parse("3.1.0"):
|
|
||||||
# aiohttp changed the request.send method to async
|
|
||||||
async def send(self, *args, **kwargs):
|
|
||||||
return self._send(*args, **kwargs)
|
|
||||||
|
|
||||||
else:
|
|
||||||
send = _send
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
_post_connect_delay = kwargs.pop("post_connect_delay", 0)
|
|
||||||
_pre_request_delay = kwargs.pop("pre_request_delay", 0)
|
|
||||||
super(DelayableTCPConnector, self).__init__(*args, **kwargs)
|
|
||||||
self._post_connect_delay = _post_connect_delay
|
|
||||||
self._pre_request_delay = _pre_request_delay
|
|
||||||
|
|
||||||
async def connect(self, req, *args, **kwargs):
|
|
||||||
d_req = DelayableTCPConnector.RequestContextManager(
|
|
||||||
req, self._pre_request_delay
|
|
||||||
)
|
)
|
||||||
conn = await super(DelayableTCPConnector, self).connect(
|
if self._request_delay:
|
||||||
req, *args, **kwargs
|
print(f"\t>> Sleeping ({self._request_delay})")
|
||||||
)
|
await asyncio.sleep(self._request_delay)
|
||||||
if self._post_connect_delay and self._post_connect_delay > 0:
|
response = await connection.send(request)
|
||||||
await asyncio.sleep(self._post_connect_delay, loop=self._loop)
|
if not stream:
|
||||||
req.send = d_req.send
|
try:
|
||||||
t = req.loop.time()
|
await response.read()
|
||||||
print("Connected at {}".format(t), flush=True)
|
finally:
|
||||||
return conn
|
await response.close()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class DelayableSanicAdapter(requests.adapters.HTTPAdapter):
|
||||||
|
def __init__(self, request_delay=None):
|
||||||
|
self.pool = DelayableSanicConnectionPool(request_delay=request_delay)
|
||||||
|
|
||||||
|
|
||||||
|
class DelayableSanicSession(requests.Session):
|
||||||
|
def __init__(self, request_delay=None, *args, **kwargs) -> None:
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
adapter = DelayableSanicAdapter(request_delay=request_delay)
|
||||||
|
self.mount("http://", adapter)
|
||||||
|
self.mount("https://", adapter)
|
||||||
|
|
||||||
|
|
||||||
class DelayableSanicTestClient(SanicTestClient):
|
class DelayableSanicTestClient(SanicTestClient):
|
||||||
def __init__(self, app, loop, request_delay=1):
|
def __init__(self, app, request_delay=None):
|
||||||
super(DelayableSanicTestClient, self).__init__(app)
|
super().__init__(app)
|
||||||
self._request_delay = request_delay
|
self._request_delay = request_delay
|
||||||
self._loop = None
|
self._loop = None
|
||||||
|
|
||||||
async def _local_request(self, method, uri, cookies=None, *args, **kwargs):
|
def get_new_session(self):
|
||||||
if self._loop is None:
|
return DelayableSanicSession(request_delay=self._request_delay)
|
||||||
self._loop = asyncio.get_event_loop()
|
|
||||||
if uri.startswith(("http:", "https:", "ftp:", "ftps://" "//")):
|
|
||||||
url = uri
|
|
||||||
else:
|
|
||||||
url = "http://{host}:{port}{uri}".format(
|
|
||||||
host=HOST, port=self.port, uri=uri
|
|
||||||
)
|
|
||||||
conn = DelayableTCPConnector(
|
|
||||||
pre_request_delay=self._request_delay, ssl=False, loop=self._loop
|
|
||||||
)
|
|
||||||
async with aiohttp.ClientSession(
|
|
||||||
cookies=cookies, connector=conn, loop=self._loop
|
|
||||||
) as session:
|
|
||||||
# Insert a delay after creating the connection
|
|
||||||
# But before sending the request.
|
|
||||||
|
|
||||||
async with getattr(session, method.lower())(
|
|
||||||
url, *args, **kwargs
|
|
||||||
) as response:
|
|
||||||
try:
|
|
||||||
response.text = await response.text()
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
response.text = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
response.json = await response.json()
|
|
||||||
except (
|
|
||||||
JSONDecodeError,
|
|
||||||
UnicodeDecodeError,
|
|
||||||
aiohttp.ClientResponseError,
|
|
||||||
):
|
|
||||||
response.json = None
|
|
||||||
|
|
||||||
response.body = await response.read()
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
request_timeout_default_app = Sanic("test_request_timeout_default")
|
request_timeout_default_app = Sanic("test_request_timeout_default")
|
||||||
|
@ -202,14 +92,14 @@ async def ws_handler1(request, ws):
|
||||||
|
|
||||||
|
|
||||||
def test_default_server_error_request_timeout():
|
def test_default_server_error_request_timeout():
|
||||||
client = DelayableSanicTestClient(request_timeout_default_app, None, 2)
|
client = DelayableSanicTestClient(request_timeout_default_app, 2)
|
||||||
request, response = client.get("/1")
|
request, response = client.get("/1")
|
||||||
assert response.status == 408
|
assert response.status == 408
|
||||||
assert response.text == "Error: Request Timeout"
|
assert response.text == "Error: Request Timeout"
|
||||||
|
|
||||||
|
|
||||||
def test_default_server_error_request_dont_timeout():
|
def test_default_server_error_request_dont_timeout():
|
||||||
client = DelayableSanicTestClient(request_no_timeout_app, None, 0.2)
|
client = DelayableSanicTestClient(request_no_timeout_app, 0.2)
|
||||||
request, response = client.get("/1")
|
request, response = client.get("/1")
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "OK"
|
assert response.text == "OK"
|
||||||
|
@ -224,7 +114,7 @@ def test_default_server_error_websocket_request_timeout():
|
||||||
"Sec-WebSocket-Version": "13",
|
"Sec-WebSocket-Version": "13",
|
||||||
}
|
}
|
||||||
|
|
||||||
client = DelayableSanicTestClient(request_timeout_default_app, None, 2)
|
client = DelayableSanicTestClient(request_timeout_default_app, 2)
|
||||||
request, response = client.get("/ws1", headers=headers)
|
request, response = client.get("/ws1", headers=headers)
|
||||||
|
|
||||||
assert response.status == 408
|
assert response.status == 408
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import ssl
|
import ssl
|
||||||
|
|
||||||
from json import dumps as json_dumps
|
from json import dumps as json_dumps
|
||||||
from json import loads as json_loads
|
from json import loads as json_loads
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Blueprint, Sanic
|
||||||
from sanic import Blueprint
|
|
||||||
from sanic.exceptions import ServerError
|
from sanic.exceptions import ServerError
|
||||||
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE, RequestParameters
|
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE, RequestParameters
|
||||||
from sanic.response import json, text
|
from sanic.response import json, text
|
||||||
from sanic.testing import HOST, PORT
|
from sanic.testing import HOST, PORT
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
# GET
|
# GET
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
@ -529,36 +530,54 @@ def test_request_string_representation(app):
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"payload,filename",
|
"payload,filename",
|
||||||
[
|
[
|
||||||
("------sanic\r\n"
|
(
|
||||||
|
"------sanic\r\n"
|
||||||
'Content-Disposition: form-data; filename="filename"; name="test"\r\n'
|
'Content-Disposition: form-data; filename="filename"; name="test"\r\n'
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"OK\r\n"
|
"OK\r\n"
|
||||||
"------sanic--\r\n", "filename"),
|
"------sanic--\r\n",
|
||||||
("------sanic\r\n"
|
"filename",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"------sanic\r\n"
|
||||||
'content-disposition: form-data; filename="filename"; name="test"\r\n'
|
'content-disposition: form-data; filename="filename"; name="test"\r\n'
|
||||||
"\r\n"
|
"\r\n"
|
||||||
'content-type: application/json; {"field": "value"}\r\n'
|
'content-type: application/json; {"field": "value"}\r\n'
|
||||||
"------sanic--\r\n", "filename"),
|
"------sanic--\r\n",
|
||||||
("------sanic\r\n"
|
"filename",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"------sanic\r\n"
|
||||||
'Content-Disposition: form-data; filename=""; name="test"\r\n'
|
'Content-Disposition: form-data; filename=""; name="test"\r\n'
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"OK\r\n"
|
"OK\r\n"
|
||||||
"------sanic--\r\n", ""),
|
"------sanic--\r\n",
|
||||||
("------sanic\r\n"
|
"",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"------sanic\r\n"
|
||||||
'content-disposition: form-data; filename=""; name="test"\r\n'
|
'content-disposition: form-data; filename=""; name="test"\r\n'
|
||||||
"\r\n"
|
"\r\n"
|
||||||
'content-type: application/json; {"field": "value"}\r\n'
|
'content-type: application/json; {"field": "value"}\r\n'
|
||||||
"------sanic--\r\n", ""),
|
"------sanic--\r\n",
|
||||||
("------sanic\r\n"
|
"",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"------sanic\r\n"
|
||||||
'Content-Disposition: form-data; filename*="utf-8\'\'filename_%C2%A0_test"; name="test"\r\n'
|
'Content-Disposition: form-data; filename*="utf-8\'\'filename_%C2%A0_test"; name="test"\r\n'
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"OK\r\n"
|
"OK\r\n"
|
||||||
"------sanic--\r\n", "filename_\u00A0_test"),
|
"------sanic--\r\n",
|
||||||
("------sanic\r\n"
|
"filename_\u00A0_test",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"------sanic\r\n"
|
||||||
'content-disposition: form-data; filename*="utf-8\'\'filename_%C2%A0_test"; name="test"\r\n'
|
'content-disposition: form-data; filename*="utf-8\'\'filename_%C2%A0_test"; name="test"\r\n'
|
||||||
"\r\n"
|
"\r\n"
|
||||||
'content-type: application/json; {"field": "value"}\r\n'
|
'content-type: application/json; {"field": "value"}\r\n'
|
||||||
"------sanic--\r\n", "filename_\u00A0_test"),
|
"------sanic--\r\n",
|
||||||
|
"filename_\u00A0_test",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_request_multipart_files(app, payload, filename):
|
def test_request_multipart_files(app, payload, filename):
|
||||||
|
@ -743,7 +762,7 @@ def test_request_raw_args(app):
|
||||||
|
|
||||||
def test_request_query_args(app):
|
def test_request_query_args(app):
|
||||||
# test multiple params with the same key
|
# test multiple params with the same key
|
||||||
params = [('test', 'value1'), ('test', 'value2')]
|
params = [("test", "value1"), ("test", "value2")]
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -754,7 +773,10 @@ def test_request_query_args(app):
|
||||||
assert request.query_args == params
|
assert request.query_args == params
|
||||||
|
|
||||||
# test cached value
|
# test cached value
|
||||||
assert request.parsed_not_grouped_args[(False, False, "utf-8", "replace")] == request.query_args
|
assert (
|
||||||
|
request.parsed_not_grouped_args[(False, False, "utf-8", "replace")]
|
||||||
|
== request.query_args
|
||||||
|
)
|
||||||
|
|
||||||
# test params directly in the url
|
# test params directly in the url
|
||||||
request, response = app.test_client.get("/?test=value1&test=value2")
|
request, response = app.test_client.get("/?test=value1&test=value2")
|
||||||
|
@ -762,7 +784,7 @@ def test_request_query_args(app):
|
||||||
assert request.query_args == params
|
assert request.query_args == params
|
||||||
|
|
||||||
# test unique params
|
# test unique params
|
||||||
params = [('test1', 'value1'), ('test2', 'value2')]
|
params = [("test1", "value1"), ("test2", "value2")]
|
||||||
|
|
||||||
request, response = app.test_client.get("/", params=params)
|
request, response = app.test_client.get("/", params=params)
|
||||||
|
|
||||||
|
@ -779,25 +801,22 @@ def test_request_query_args_custom_parsing(app):
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return text("pass")
|
return text("pass")
|
||||||
|
|
||||||
request, response = app.test_client.get("/?test1=value1&test2=&test3=value3")
|
request, response = app.test_client.get(
|
||||||
|
"/?test1=value1&test2=&test3=value3"
|
||||||
|
)
|
||||||
|
|
||||||
assert request.get_query_args(
|
assert request.get_query_args(keep_blank_values=True) == [
|
||||||
keep_blank_values=True
|
("test1", "value1"),
|
||||||
) == [
|
("test2", ""),
|
||||||
('test1', 'value1'), ('test2', ''), ('test3', 'value3')
|
("test3", "value3"),
|
||||||
]
|
]
|
||||||
assert request.query_args == [
|
assert request.query_args == [("test1", "value1"), ("test3", "value3")]
|
||||||
('test1', 'value1'), ('test3', 'value3')
|
assert request.get_query_args(keep_blank_values=False) == [
|
||||||
]
|
("test1", "value1"),
|
||||||
assert request.get_query_args(
|
("test3", "value3"),
|
||||||
keep_blank_values=False
|
|
||||||
) == [
|
|
||||||
('test1', 'value1'), ('test3', 'value3')
|
|
||||||
]
|
]
|
||||||
|
|
||||||
assert request.get_args(
|
assert request.get_args(keep_blank_values=True) == RequestParameters(
|
||||||
keep_blank_values=True
|
|
||||||
) == RequestParameters(
|
|
||||||
{"test1": ["value1"], "test2": [""], "test3": ["value3"]}
|
{"test1": ["value1"], "test2": [""], "test3": ["value3"]}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -805,9 +824,7 @@ def test_request_query_args_custom_parsing(app):
|
||||||
{"test1": ["value1"], "test3": ["value3"]}
|
{"test1": ["value1"], "test3": ["value3"]}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert request.get_args(
|
assert request.get_args(keep_blank_values=False) == RequestParameters(
|
||||||
keep_blank_values=False
|
|
||||||
) == RequestParameters(
|
|
||||||
{"test1": ["value1"], "test3": ["value3"]}
|
{"test1": ["value1"], "test3": ["value3"]}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from random import choice
|
from random import choice
|
||||||
|
@ -8,6 +9,7 @@ from unittest.mock import MagicMock
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from aiofiles import os as async_os
|
from aiofiles import os as async_os
|
||||||
|
|
||||||
from sanic.response import (
|
from sanic.response import (
|
||||||
|
@ -18,11 +20,11 @@ from sanic.response import (
|
||||||
json,
|
json,
|
||||||
raw,
|
raw,
|
||||||
stream,
|
stream,
|
||||||
text,
|
|
||||||
)
|
)
|
||||||
from sanic.server import HttpProtocol
|
from sanic.server import HttpProtocol
|
||||||
from sanic.testing import HOST, PORT
|
from sanic.testing import HOST, PORT
|
||||||
|
|
||||||
|
|
||||||
JSON_DATA = {"ok": True}
|
JSON_DATA = {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,10 +79,10 @@ def test_response_header(app):
|
||||||
|
|
||||||
request, response = app.test_client.get("/")
|
request, response = app.test_client.get("/")
|
||||||
assert dict(response.headers) == {
|
assert dict(response.headers) == {
|
||||||
"Connection": "keep-alive",
|
"connection": "keep-alive",
|
||||||
"Keep-Alive": str(app.config.KEEP_ALIVE_TIMEOUT),
|
"keep-alive": str(app.config.KEEP_ALIVE_TIMEOUT),
|
||||||
"Content-Length": "11",
|
"content-length": "11",
|
||||||
"Content-Type": "application/json",
|
"content-type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -363,7 +365,7 @@ def test_stream_response_with_cookies(app):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
request, response = app.test_client.get("/")
|
request, response = app.test_client.get("/")
|
||||||
assert response.cookies["test"].value == "pass"
|
assert response.cookies["test"] == "pass"
|
||||||
|
|
||||||
|
|
||||||
def test_stream_response_without_cookies(app):
|
def test_stream_response_without_cookies(app):
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from sanic import Sanic
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from sanic.response import text
|
|
||||||
|
from sanic import Sanic
|
||||||
from sanic.exceptions import ServiceUnavailable
|
from sanic.exceptions import ServiceUnavailable
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
response_timeout_app = Sanic("test_response_timeout")
|
response_timeout_app = Sanic("test_response_timeout")
|
||||||
response_timeout_default_app = Sanic("test_response_timeout_default")
|
response_timeout_default_app = Sanic("test_response_timeout_default")
|
||||||
|
|
|
@ -7,6 +7,7 @@ from sanic.constants import HTTP_METHODS
|
||||||
from sanic.response import json, text
|
from sanic.response import json, text
|
||||||
from sanic.router import ParameterNameConflicts, RouteDoesNotExist, RouteExists
|
from sanic.router import ParameterNameConflicts, RouteDoesNotExist, RouteExists
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
# UTF-8
|
# UTF-8
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
@ -468,16 +469,8 @@ def test_websocket_route(app, url):
|
||||||
assert ws.subprotocol is None
|
assert ws.subprotocol is None
|
||||||
ev.set()
|
ev.set()
|
||||||
|
|
||||||
request, response = app.test_client.get(
|
request, response = app.test_client.websocket(url)
|
||||||
"/ws",
|
assert response.opened is True
|
||||||
headers={
|
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "upgrade",
|
|
||||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
|
||||||
"Sec-WebSocket-Version": "13",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status == 101
|
|
||||||
assert ev.is_set()
|
assert ev.is_set()
|
||||||
|
|
||||||
|
|
||||||
|
@ -487,54 +480,24 @@ def test_websocket_route_with_subprotocols(app):
|
||||||
@app.websocket("/ws", subprotocols=["foo", "bar"])
|
@app.websocket("/ws", subprotocols=["foo", "bar"])
|
||||||
async def handler(request, ws):
|
async def handler(request, ws):
|
||||||
results.append(ws.subprotocol)
|
results.append(ws.subprotocol)
|
||||||
|
assert ws.subprotocol is not None
|
||||||
|
|
||||||
request, response = app.test_client.get(
|
request, response = app.test_client.websocket("/ws", subprotocols=["bar"])
|
||||||
"/ws",
|
assert response.opened is True
|
||||||
headers={
|
assert results == ["bar"]
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "upgrade",
|
request, response = app.test_client.websocket(
|
||||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
"/ws", subprotocols=["bar", "foo"]
|
||||||
"Sec-WebSocket-Version": "13",
|
|
||||||
"Sec-WebSocket-Protocol": "bar",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
assert response.status == 101
|
assert response.opened is True
|
||||||
|
assert results == ["bar", "bar"]
|
||||||
|
|
||||||
request, response = app.test_client.get(
|
request, response = app.test_client.websocket("/ws", subprotocols=["baz"])
|
||||||
"/ws",
|
assert response.opened is True
|
||||||
headers={
|
assert results == ["bar", "bar", None]
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "upgrade",
|
|
||||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
|
||||||
"Sec-WebSocket-Version": "13",
|
|
||||||
"Sec-WebSocket-Protocol": "bar, foo",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status == 101
|
|
||||||
|
|
||||||
request, response = app.test_client.get(
|
|
||||||
"/ws",
|
|
||||||
headers={
|
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "upgrade",
|
|
||||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
|
||||||
"Sec-WebSocket-Version": "13",
|
|
||||||
"Sec-WebSocket-Protocol": "baz",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status == 101
|
|
||||||
|
|
||||||
request, response = app.test_client.get(
|
|
||||||
"/ws",
|
|
||||||
headers={
|
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "upgrade",
|
|
||||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
|
||||||
"Sec-WebSocket-Version": "13",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status == 101
|
|
||||||
|
|
||||||
|
request, response = app.test_client.websocket("/ws")
|
||||||
|
assert response.opened is True
|
||||||
assert results == ["bar", "bar", None, None]
|
assert results == ["bar", "bar", None, None]
|
||||||
|
|
||||||
|
|
||||||
|
@ -547,16 +510,8 @@ def test_add_webscoket_route(app, strict_slashes):
|
||||||
ev.set()
|
ev.set()
|
||||||
|
|
||||||
app.add_websocket_route(handler, "/ws", strict_slashes=strict_slashes)
|
app.add_websocket_route(handler, "/ws", strict_slashes=strict_slashes)
|
||||||
request, response = app.test_client.get(
|
request, response = app.test_client.websocket("/ws")
|
||||||
"/ws",
|
assert response.opened is True
|
||||||
headers={
|
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "upgrade",
|
|
||||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
|
||||||
"Sec-WebSocket-Version": "13",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status == 101
|
|
||||||
assert ev.is_set()
|
assert ev.is_set()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import pytest
|
||||||
|
|
||||||
from sanic.testing import HOST, PORT
|
from sanic.testing import HOST, PORT
|
||||||
|
|
||||||
|
|
||||||
AVAILABLE_LISTENERS = [
|
AVAILABLE_LISTENERS = [
|
||||||
"before_server_start",
|
"before_server_start",
|
||||||
"after_server_start",
|
"after_server_start",
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from queue import Queue
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from sanic.response import HTTPResponse
|
from sanic.response import HTTPResponse
|
||||||
from sanic.testing import HOST, PORT
|
from sanic.testing import HOST, PORT
|
||||||
from unittest.mock import MagicMock
|
|
||||||
import asyncio
|
|
||||||
from queue import Queue
|
|
||||||
|
|
||||||
|
|
||||||
async def stop(app, loop):
|
async def stop(app, loop):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from time import gmtime, strftime
|
from time import gmtime, strftime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from sanic.testing import PORT, SanicTestClient
|
|
||||||
from sanic.response import json, text
|
from sanic.response import json, text
|
||||||
|
from sanic.testing import PORT, SanicTestClient
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
# UTF-8
|
# UTF-8
|
||||||
|
@ -9,26 +10,26 @@ from sanic.response import json, text
|
||||||
|
|
||||||
|
|
||||||
def test_test_client_port_none(app):
|
def test_test_client_port_none(app):
|
||||||
@app.get('/get')
|
@app.get("/get")
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return text('OK')
|
return text("OK")
|
||||||
|
|
||||||
test_client = SanicTestClient(app, port=None)
|
test_client = SanicTestClient(app, port=None)
|
||||||
|
|
||||||
request, response = test_client.get('/get')
|
request, response = test_client.get("/get")
|
||||||
assert response.text == 'OK'
|
assert response.text == "OK"
|
||||||
|
|
||||||
request, response = test_client.post('/get')
|
request, response = test_client.post("/get")
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_test_client_port_default(app):
|
def test_test_client_port_default(app):
|
||||||
@app.get('/get')
|
@app.get("/get")
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return json(request.transport.get_extra_info('sockname')[1])
|
return json(request.transport.get_extra_info("sockname")[1])
|
||||||
|
|
||||||
test_client = SanicTestClient(app)
|
test_client = SanicTestClient(app)
|
||||||
assert test_client.port == PORT
|
assert test_client.port == PORT
|
||||||
|
|
||||||
request, response = test_client.get('/get')
|
request, response = test_client.get("/get")
|
||||||
assert response.json == PORT
|
assert response.json == PORT
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import pytest as pytest
|
|
||||||
from urllib.parse import urlsplit, parse_qsl
|
|
||||||
|
|
||||||
from sanic.response import text
|
|
||||||
from sanic.views import HTTPMethodView
|
|
||||||
from sanic.blueprints import Blueprint
|
|
||||||
from sanic.testing import PORT as test_port, HOST as test_host
|
|
||||||
from sanic.exceptions import URLBuildError
|
|
||||||
|
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
from urllib.parse import parse_qsl, urlsplit
|
||||||
|
|
||||||
|
import pytest as pytest
|
||||||
|
|
||||||
|
from sanic.blueprints import Blueprint
|
||||||
|
from sanic.exceptions import URLBuildError
|
||||||
|
from sanic.response import text
|
||||||
|
from sanic.testing import HOST as test_host
|
||||||
|
from sanic.testing import PORT as test_port
|
||||||
|
from sanic.views import HTTPMethodView
|
||||||
|
|
||||||
|
|
||||||
URL_FOR_ARGS1 = dict(arg1=["v1", "v2"])
|
URL_FOR_ARGS1 = dict(arg1=["v1", "v2"])
|
||||||
URL_FOR_VALUE1 = "/myurl?arg1=v1&arg1=v2"
|
URL_FOR_VALUE1 = "/myurl?arg1=v1&arg1=v2"
|
||||||
URL_FOR_ARGS2 = dict(arg1=["v1", "v2"], _anchor="anchor")
|
URL_FOR_ARGS2 = dict(arg1=["v1", "v2"], _anchor="anchor")
|
||||||
|
@ -170,7 +173,7 @@ def test_fails_with_int_message(app):
|
||||||
|
|
||||||
expected_error = (
|
expected_error = (
|
||||||
r'Value "not_int" for parameter `foo` '
|
r'Value "not_int" for parameter `foo` '
|
||||||
r'does not match pattern for type `int`: -?\d+'
|
r"does not match pattern for type `int`: -?\d+"
|
||||||
)
|
)
|
||||||
assert str(e.value) == expected_error
|
assert str(e.value) == expected_error
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from json import dumps as json_dumps
|
from json import dumps as json_dumps
|
||||||
|
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import pytest as pytest
|
import pytest as pytest
|
||||||
|
|
||||||
from sanic.exceptions import InvalidUsage
|
|
||||||
from sanic.response import text, HTTPResponse
|
|
||||||
from sanic.views import HTTPMethodView, CompositionView
|
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.request import Request
|
|
||||||
from sanic.constants import HTTP_METHODS
|
from sanic.constants import HTTP_METHODS
|
||||||
|
from sanic.exceptions import InvalidUsage
|
||||||
|
from sanic.request import Request
|
||||||
|
from sanic.response import HTTPResponse, text
|
||||||
|
from sanic.views import CompositionView, HTTPMethodView
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("method", HTTP_METHODS)
|
@pytest.mark.parametrize("method", HTTP_METHODS)
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import time
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from sanic.worker import GunicornWorker
|
|
||||||
from sanic.app import Sanic
|
|
||||||
import asyncio
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from sanic.app import Sanic
|
||||||
|
from sanic.worker import GunicornWorker
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def gunicorn_worker():
|
def gunicorn_worker():
|
||||||
|
|
13
tox.ini
13
tox.ini
|
@ -1,24 +1,25 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py35, py36, py37, {py35,py36,py37}-no-ext, lint, check
|
envlist = py36, py37, {py36,py37}-no-ext, lint, check
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
setenv =
|
setenv =
|
||||||
{py35,py36,py37}-no-ext: SANIC_NO_UJSON=1
|
{py36,py37}-no-ext: SANIC_NO_UJSON=1
|
||||||
{py35,py36,py37}-no-ext: SANIC_NO_UVLOOP=1
|
{py36,py37}-no-ext: SANIC_NO_UVLOOP=1
|
||||||
deps =
|
deps =
|
||||||
coverage
|
coverage
|
||||||
pytest==4.1.0
|
pytest==4.1.0
|
||||||
pytest-cov
|
pytest-cov
|
||||||
pytest-sanic
|
pytest-sanic
|
||||||
pytest-sugar
|
pytest-sugar
|
||||||
aiohttp>=2.3,<=3.2.1
|
httpcore==0.1.1
|
||||||
|
requests-async==0.4.0
|
||||||
chardet<=2.3.0
|
chardet<=2.3.0
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
gunicorn
|
gunicorn
|
||||||
pytest-benchmark
|
pytest-benchmark
|
||||||
commands =
|
commands =
|
||||||
pytest tests --cov sanic --cov-report= {posargs}
|
pytest {posargs:tests --cov sanic}
|
||||||
- coverage combine --append
|
- coverage combine --append
|
||||||
coverage report -m
|
coverage report -m
|
||||||
coverage html -i
|
coverage html -i
|
||||||
|
@ -31,7 +32,7 @@ deps =
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
flake8 sanic
|
flake8 sanic
|
||||||
black --check --verbose sanic
|
black --config ./.black.toml --check --verbose sanic
|
||||||
isort --check-only --recursive sanic
|
isort --check-only --recursive sanic
|
||||||
|
|
||||||
[testenv:check]
|
[testenv:check]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user