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:
|
||||
matrix:
|
||||
- TOXENV: py35-no-ext
|
||||
PYTHON: "C:\\Python35-x64"
|
||||
PYTHON_VERSION: "3.5.x"
|
||||
PYTHON_ARCH: "64"
|
||||
|
||||
- TOXENV: py36-no-ext
|
||||
PYTHON: "C:\\Python36-x64"
|
||||
PYTHON_VERSION: "3.6.x"
|
||||
|
|
|
@ -5,10 +5,6 @@ cache:
|
|||
- $HOME/.cache/pip
|
||||
matrix:
|
||||
include:
|
||||
- env: TOX_ENV=py35
|
||||
python: 3.5
|
||||
- env: TOX_ENV=py35-no-ext
|
||||
python: 3.5
|
||||
- env: TOX_ENV=py36
|
||||
python: 3.6
|
||||
- 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
|
||||
-------------
|
||||
19.3.1
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
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
|
||||
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
|
||||
else
|
||||
$(info Sorting Imports)
|
||||
isort -rc sanic
|
||||
isort -rc sanic tests
|
||||
endif
|
||||
endif
|
||||
|
||||
black:
|
||||
black --config ./pyproject.toml sanic tests
|
||||
black --config ./.black.toml sanic tests
|
||||
|
||||
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|
|
||||
* - Support
|
||||
- | |Forums| |Join the chat at https://gitter.im/sanic-python/Lobby|
|
||||
* - Stats
|
||||
- | |Downloads|
|
||||
|
||||
.. |Forums| image:: https://img.shields.io/badge/forums-community-ff0068.svg
|
||||
: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
|
||||
:alt: Supported implementations
|
||||
: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
|
||||
|
||||
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/>`_.
|
||||
|
||||
|
|
|
@ -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
|
||||
-------------
|
||||
19.3.1
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Testing
|
||||
|
||||
Sanic endpoints can be tested locally using the `test_client` object, which
|
||||
depends on the additional [aiohttp](https://aiohttp.readthedocs.io/en/stable/)
|
||||
library.
|
||||
depends on the additional [`requests-async`](https://github.com/encode/requests-async)
|
||||
library, which implements an API that mirrors the `requests` library.
|
||||
|
||||
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:
|
||||
|
@ -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
|
||||
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:
|
||||
|
||||
|
@ -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.
|
||||
- `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:
|
||||
|
||||
|
@ -55,8 +55,8 @@ def test_post_json_request_includes_data():
|
|||
|
||||
|
||||
More information about
|
||||
the available arguments to aiohttp can be found
|
||||
[in the documentation for ClientSession](https://aiohttp.readthedocs.io/en/stable/client_reference.html#client-session).
|
||||
the available arguments to `requests-async` can be found
|
||||
[in the documentation for `requests`](https://2.python-requests.org/en/master/).
|
||||
|
||||
|
||||
## Using a random port
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
from json import JSONDecodeError
|
||||
from socket import socket
|
||||
|
||||
import requests_async as requests
|
||||
import websockets
|
||||
|
||||
from sanic.exceptions import MethodNotSupported
|
||||
from sanic.log import logger
|
||||
from sanic.response import text
|
||||
|
@ -16,32 +19,41 @@ class SanicTestClient:
|
|||
self.app = app
|
||||
self.port = port
|
||||
|
||||
async def _local_request(self, method, url, cookies=None, *args, **kwargs):
|
||||
import aiohttp
|
||||
def get_new_session(self):
|
||||
return requests.Session()
|
||||
|
||||
async def _local_request(self, method, url, *args, **kwargs):
|
||||
logger.info(url)
|
||||
conn = aiohttp.TCPConnector(ssl=False)
|
||||
async with aiohttp.ClientSession(
|
||||
cookies=cookies, connector=conn
|
||||
) as session:
|
||||
async with getattr(session, method.lower())(
|
||||
url, *args, **kwargs
|
||||
) as response:
|
||||
try:
|
||||
response.text = await response.text()
|
||||
except UnicodeDecodeError:
|
||||
response.text = None
|
||||
raw_cookies = kwargs.pop("raw_cookies", None)
|
||||
|
||||
if method == "websocket":
|
||||
async with websockets.connect(url, *args, **kwargs) as websocket:
|
||||
websocket.opened = websocket.open
|
||||
return websocket
|
||||
else:
|
||||
async with self.get_new_session() as session:
|
||||
|
||||
try:
|
||||
response.json = await response.json()
|
||||
except (
|
||||
JSONDecodeError,
|
||||
UnicodeDecodeError,
|
||||
aiohttp.ClientResponseError,
|
||||
):
|
||||
response = await getattr(session, method.lower())(
|
||||
url, verify=False, *args, **kwargs
|
||||
)
|
||||
except NameError:
|
||||
raise Exception(response.status_code)
|
||||
|
||||
try:
|
||||
response.json = response.json()
|
||||
except (JSONDecodeError, UnicodeDecodeError):
|
||||
response.json = None
|
||||
|
||||
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
|
||||
|
||||
def _sanic_endpoint_test(
|
||||
|
@ -83,11 +95,15 @@ class SanicTestClient:
|
|||
server_kwargs = dict(sock=sock, **server_kwargs)
|
||||
host, port = sock.getsockname()
|
||||
|
||||
if uri.startswith(("http:", "https:", "ftp:", "ftps://", "//")):
|
||||
if uri.startswith(
|
||||
("http:", "https:", "ftp:", "ftps://", "//", "ws:", "wss:")
|
||||
):
|
||||
url = uri
|
||||
else:
|
||||
url = "http://{host}:{port}{uri}".format(
|
||||
host=host, port=port, uri=uri
|
||||
uri = uri if uri.startswith("/") else "/{uri}".format(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")
|
||||
|
@ -146,3 +162,6 @@ class SanicTestClient:
|
|||
|
||||
def head(self, *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
|
||||
|
||||
[version]
|
||||
current_version = 0.8.3
|
||||
current_version = 19.3.1
|
||||
file = sanic/__init__.py
|
||||
current_version_pattern = __version__ = "{current_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 = {
|
||||
"name": "sanic",
|
||||
"version": version,
|
||||
"url": "http://github.com/channelcat/sanic/",
|
||||
"url": "http://github.com/huge-success/sanic/",
|
||||
"license": "MIT",
|
||||
"author": "Channel Cat",
|
||||
"author_email": "channelcat@gmail.com",
|
||||
"author": "Sanic Community",
|
||||
"author_email": "admhpkns@gmail.com",
|
||||
"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,
|
||||
"packages": ["sanic"],
|
||||
|
@ -64,7 +64,6 @@ setup_kwargs = {
|
|||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Web Environment",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
],
|
||||
|
@ -90,7 +89,8 @@ tests_require = [
|
|||
"multidict>=4.0,<5.0",
|
||||
"gunicorn",
|
||||
"pytest-cov",
|
||||
"aiohttp>=2.3.0,<=3.2.1",
|
||||
"httpcore==0.1.1",
|
||||
"requests-async==0.4.0",
|
||||
"beautifulsoup4",
|
||||
uvloop,
|
||||
ujson,
|
||||
|
@ -119,7 +119,7 @@ extras_require = {
|
|||
"recommonmark",
|
||||
"sphinxcontrib-asyncio",
|
||||
"docutils",
|
||||
"pygments"
|
||||
"pygments",
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from random import choice, seed
|
||||
|
||||
from pytest import mark
|
||||
|
||||
import sanic.router
|
||||
|
||||
|
||||
seed("Pack my box with five dozen liquor jugs.")
|
||||
|
||||
# Disable Caching for testing purpose
|
||||
|
|
|
@ -9,6 +9,7 @@ import pytest
|
|||
from sanic import Sanic
|
||||
from sanic.router import RouteExists, Router
|
||||
|
||||
|
||||
random.seed("Pack my box with five dozen liquor jugs.")
|
||||
|
||||
if sys.platform in ["win32", "cygwin"]:
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
# Run with python3 simple_server.py PORT
|
||||
|
||||
from aiohttp import web
|
||||
import asyncio
|
||||
import sys
|
||||
import uvloop
|
||||
|
||||
import ujson as json
|
||||
import uvloop
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
|
||||
loop = uvloop.new_event_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
|
||||
import bottle
|
||||
from bottle import route, run
|
||||
import ujson
|
||||
|
||||
from bottle import route, run
|
||||
|
||||
|
||||
@route("/")
|
||||
def index():
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
# Run with: python3 -O simple_server.py
|
||||
import asyncio
|
||||
from kyoukai import Kyoukai, HTTPRequestContext
|
||||
import logging
|
||||
|
||||
import ujson
|
||||
import uvloop
|
||||
|
||||
from kyoukai import HTTPRequestContext, Kyoukai
|
||||
|
||||
|
||||
loop = uvloop.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import asyncpg
|
||||
import sys
|
||||
import os
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import timeit
|
||||
|
||||
import asyncpg
|
||||
|
||||
from sanic.response import json
|
||||
|
||||
|
||||
currentdir = os.path.dirname(
|
||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||
)
|
||||
sys.path.insert(0, currentdir + "/../../../")
|
||||
|
||||
import timeit
|
||||
|
||||
from sanic.response import json
|
||||
|
||||
print(json({"test": True}).output())
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import sys
|
||||
import os
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
|
||||
|
||||
currentdir = os.path.dirname(
|
||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||
)
|
||||
sys.path.insert(0, currentdir + "/../../../")
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
|
||||
app = Sanic("test")
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import sys
|
||||
import os
|
||||
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(
|
||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||
)
|
||||
sys.path.insert(0, currentdir + "/../../../")
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json, text
|
||||
from sanic.exceptions import ServerError
|
||||
|
||||
app = Sanic("test")
|
||||
|
||||
|
@ -56,8 +58,6 @@ def query_string(request):
|
|||
)
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
app.run(host="0.0.0.0", port=sys.argv[1])
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Run with: python simple_server.py
|
||||
import ujson
|
||||
|
||||
from tornado import ioloop, web
|
||||
|
||||
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
""" Minimal helloworld application.
|
||||
"""
|
||||
|
||||
from wheezy.http import HTTPResponse
|
||||
from wheezy.http import WSGIApplication
|
||||
import ujson
|
||||
|
||||
from wheezy.http import HTTPResponse, WSGIApplication
|
||||
from wheezy.http.response import json_response
|
||||
from wheezy.routing import url
|
||||
from wheezy.web.handlers import BaseHandler
|
||||
from wheezy.web.middleware import bootstrap_defaults
|
||||
from wheezy.web.middleware import path_routing_middleware_factory
|
||||
|
||||
import ujson
|
||||
from wheezy.web.middleware import (
|
||||
bootstrap_defaults,
|
||||
path_routing_middleware_factory,
|
||||
)
|
||||
|
||||
|
||||
class WelcomeHandler(BaseHandler):
|
||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
|||
import sys
|
||||
|
||||
from inspect import isawaitable
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic.exceptions import SanicException
|
||||
|
@ -11,7 +12,7 @@ from sanic.response import text
|
|||
|
||||
def uvloop_installed():
|
||||
try:
|
||||
import uvloop
|
||||
import uvloop # noqa
|
||||
|
||||
return True
|
||||
except ImportError:
|
||||
|
|
|
@ -3,7 +3,8 @@ from pytest import raises
|
|||
from sanic.app import Sanic
|
||||
from sanic.blueprints import Blueprint
|
||||
from sanic.request import Request
|
||||
from sanic.response import text, HTTPResponse
|
||||
from sanic.response import HTTPResponse, text
|
||||
|
||||
|
||||
MIDDLEWARE_INVOKE_COUNTER = {"request": 0, "response": 0}
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ import pytest
|
|||
from sanic.app import Sanic
|
||||
from sanic.blueprints import Blueprint
|
||||
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.response import text, json
|
||||
from sanic.response import json, text
|
||||
from sanic.views import CompositionView
|
||||
|
||||
|
||||
|
@ -467,16 +467,8 @@ def test_bp_shorthand(app):
|
|||
request, response = app.test_client.get("/delete")
|
||||
assert response.status == 405
|
||||
|
||||
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 ev.is_set()
|
||||
|
||||
|
||||
|
@ -595,14 +587,13 @@ def test_blueprint_middleware_with_args(app: Sanic):
|
|||
"/wa", headers={"content-type": "plain/text"}
|
||||
)
|
||||
assert response.json.get("test") == "value"
|
||||
d = {}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("file_name", ["test.file"])
|
||||
def test_static_blueprint_name(app: Sanic, static_file_directory, file_name):
|
||||
current_file = inspect.getfile(inspect.currentframe())
|
||||
with open(current_file, "rb") as file:
|
||||
current_file_contents = file.read()
|
||||
file.read()
|
||||
|
||||
bp = Blueprint(name="static", url_prefix="/static", strict_slashes=False)
|
||||
|
||||
|
@ -662,16 +653,8 @@ def test_websocket_route(app: Sanic):
|
|||
|
||||
app.blueprint(bp)
|
||||
|
||||
_, response = app.test_client.get(
|
||||
"/ws/test",
|
||||
headers={
|
||||
"Upgrade": "websocket",
|
||||
"Connection": "upgrade",
|
||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
||||
"Sec-WebSocket-Version": "13",
|
||||
},
|
||||
)
|
||||
assert response.status == 101
|
||||
_, response = app.test_client.websocket("/ws/test")
|
||||
assert response.opened is True
|
||||
assert event.is_set()
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
from contextlib import contextmanager
|
||||
from os import environ
|
||||
from pathlib import Path
|
||||
from contextlib import contextmanager
|
||||
from tempfile import TemporaryDirectory
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.config import Config, DEFAULT_CONFIG
|
||||
from sanic.config import DEFAULT_CONFIG, Config
|
||||
from sanic.exceptions import PyFileError
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
from datetime import datetime, timedelta
|
||||
from http.cookies import SimpleCookie
|
||||
from sanic.response import text
|
||||
|
||||
import pytest
|
||||
from sanic.cookies import Cookie, DEFAULT_MAX_AGE
|
||||
|
||||
from sanic.cookies import Cookie
|
||||
from sanic.response import text
|
||||
|
||||
|
||||
# ------------------------------------------------------------ #
|
||||
# GET
|
||||
|
@ -100,7 +103,7 @@ def test_cookie_deletion(app):
|
|||
|
||||
assert int(response_cookies["i_want_to_die"]["max-age"]) == 0
|
||||
with pytest.raises(KeyError):
|
||||
_ = response.cookies["i_never_existed"]
|
||||
response.cookies["i_never_existed"]
|
||||
|
||||
|
||||
def test_cookie_reserved_cookie():
|
||||
|
@ -135,7 +138,7 @@ def test_cookie_set_same_key(app):
|
|||
|
||||
request, response = app.test_client.get("/", cookies=cookies)
|
||||
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"])
|
||||
|
@ -149,19 +152,42 @@ def test_cookie_max_age(app, max_age):
|
|||
response.cookies["test"]["max-age"] = max_age
|
||||
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.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):
|
||||
assert response.cookies["test"]["max-age"] == str(max_age)
|
||||
# Grabbing utcnow after the response may lead to it being off slightly.
|
||||
# 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:
|
||||
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):
|
||||
expires = expires.replace(microsecond=0)
|
||||
cookies = {"test": "wait"}
|
||||
|
||||
@app.get("/")
|
||||
|
@ -171,15 +197,16 @@ def test_cookie_expires(app, expires):
|
|||
response.cookies["test"]["expires"] = expires
|
||||
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.cookies["test"].value == "pass"
|
||||
|
||||
if isinstance(expires, datetime):
|
||||
expires = expires.strftime("%a, %d-%b-%Y %T GMT")
|
||||
|
||||
assert response.cookies["test"]["expires"] == expires
|
||||
assert response.cookies["test"] == "pass"
|
||||
assert cookie_expires == expires
|
||||
|
||||
|
||||
@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
|
||||
|
||||
from queue import Queue
|
||||
from threading import Event
|
||||
|
||||
from sanic.response import text
|
||||
|
||||
|
||||
def test_create_task(app):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from sanic.server import HttpProtocol
|
||||
from sanic.response import text
|
||||
from sanic.server import HttpProtocol
|
||||
|
||||
|
||||
class CustomHttpProtocol(HttpProtocol):
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import pytest
|
||||
|
||||
from sanic.response import text
|
||||
from sanic.router import RouteExists
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import pytest
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.exceptions import (
|
||||
Forbidden,
|
||||
InvalidUsage,
|
||||
NotFound,
|
||||
ServerError,
|
||||
Unauthorized,
|
||||
abort,
|
||||
)
|
||||
from sanic.response import text
|
||||
from sanic.exceptions import InvalidUsage, ServerError, NotFound, Unauthorized
|
||||
from sanic.exceptions import Forbidden, abort
|
||||
|
||||
|
||||
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 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")
|
||||
|
||||
|
||||
|
|
|
@ -1,39 +1,143 @@
|
|||
from json import JSONDecodeError
|
||||
from sanic import Sanic
|
||||
import asyncio
|
||||
import functools
|
||||
import socket
|
||||
|
||||
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 import server
|
||||
import aiohttp
|
||||
from aiohttp import TCPConnector
|
||||
from sanic.testing import SanicTestClient, HOST, PORT
|
||||
from sanic.testing import HOST, PORT, SanicTestClient
|
||||
|
||||
|
||||
# import traceback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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):
|
||||
new_conn = await super(ReuseableTCPConnector, self).connect(
|
||||
req, *args, **kwargs
|
||||
class ReusableSanicConnectionPool(httpcore.ConnectionPool):
|
||||
async def acquire_connection(self, url, ssl, timeout):
|
||||
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:
|
||||
if self.old_proto != new_conn._protocol:
|
||||
except asyncio.TimeoutError:
|
||||
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(
|
||||
"We got a new connection, wanted the same one!"
|
||||
)
|
||||
print(new_conn.__dict__)
|
||||
self.old_proto = new_conn._protocol
|
||||
return new_conn
|
||||
old_conn = connection
|
||||
|
||||
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):
|
||||
def __init__(self, app, loop=None):
|
||||
super(ReuseableSanicTestClient, self).__init__(app)
|
||||
super().__init__(app)
|
||||
if loop is None:
|
||||
loop = asyncio.get_event_loop()
|
||||
self._loop = loop
|
||||
|
@ -51,12 +155,11 @@ class ReuseableSanicTestClient(SanicTestClient):
|
|||
debug=False,
|
||||
server_kwargs={"return_asyncio_server": True},
|
||||
*request_args,
|
||||
**request_kwargs
|
||||
**request_kwargs,
|
||||
):
|
||||
loop = self._loop
|
||||
results = [None, None]
|
||||
exceptions = []
|
||||
do_kill_server = request_kwargs.pop("end_server", False)
|
||||
if gather_request:
|
||||
|
||||
def _collect_request(request):
|
||||
|
@ -65,21 +168,27 @@ class ReuseableSanicTestClient(SanicTestClient):
|
|||
|
||||
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")
|
||||
async def _collect_response(loop):
|
||||
try:
|
||||
if do_kill_server:
|
||||
request_kwargs["end_session"] = True
|
||||
response = await self._local_request(
|
||||
method, uri, *request_args, **request_kwargs
|
||||
method, url, *request_args, **request_kwargs
|
||||
)
|
||||
results[-1] = response
|
||||
except Exception as e2:
|
||||
import traceback
|
||||
|
||||
traceback.print_tb(e2.__traceback__)
|
||||
# traceback.print_tb(e2.__traceback__)
|
||||
exceptions.append(e2)
|
||||
# Don't stop here! self.app.stop()
|
||||
|
||||
if self._server is not None:
|
||||
_server = self._server
|
||||
|
@ -94,27 +203,14 @@ class ReuseableSanicTestClient(SanicTestClient):
|
|||
|
||||
try:
|
||||
loop._stopping = False
|
||||
http_server = loop.run_until_complete(_server_co)
|
||||
_server = loop.run_until_complete(_server_co)
|
||||
except Exception as e1:
|
||||
import traceback
|
||||
|
||||
traceback.print_tb(e1.__traceback__)
|
||||
# traceback.print_tb(e1.__traceback__)
|
||||
raise e1
|
||||
self._server = _server = http_server
|
||||
self._server = _server
|
||||
server.trigger_events(self.app.listeners["after_server_start"], loop)
|
||||
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:
|
||||
raise ValueError("Exception during request: {}".format(exceptions))
|
||||
|
||||
|
@ -137,59 +233,61 @@ class ReuseableSanicTestClient(SanicTestClient):
|
|||
"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
|
||||
# same TCPConnection and the sane ClientSession more than once.
|
||||
# Note, you cannot use the same session if you are in a _different_
|
||||
# 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", 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:
|
||||
session = self._session
|
||||
_session = self._session
|
||||
else:
|
||||
if self._tcp_connector:
|
||||
conn = self._tcp_connector
|
||||
else:
|
||||
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:
|
||||
_session = ResusableSanicSession()
|
||||
self._session = _session
|
||||
async with _session as session:
|
||||
try:
|
||||
response.text = await response.text()
|
||||
except UnicodeDecodeError:
|
||||
response.text = None
|
||||
response = await getattr(session, method.lower())(
|
||||
url,
|
||||
verify=False,
|
||||
timeout=request_keepalive,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
except NameError:
|
||||
raise Exception(response.status_code)
|
||||
|
||||
try:
|
||||
response.json = await response.json()
|
||||
except (
|
||||
JSONDecodeError,
|
||||
UnicodeDecodeError,
|
||||
aiohttp.ClientResponseError,
|
||||
):
|
||||
response.json = response.json()
|
||||
except (JSONDecodeError, UnicodeDecodeError):
|
||||
response.json = None
|
||||
|
||||
response.body = await response.read()
|
||||
if do_kill_session:
|
||||
await session.close()
|
||||
self._session = None
|
||||
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
|
||||
|
||||
|
||||
|
@ -229,9 +327,10 @@ def test_keep_alive_timeout_reuse():
|
|||
assert response.status == 200
|
||||
assert response.text == "OK"
|
||||
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.text == "OK"
|
||||
client.kill_server()
|
||||
|
||||
|
||||
def test_keep_alive_client_timeout():
|
||||
|
@ -241,20 +340,21 @@ def test_keep_alive_client_timeout():
|
|||
asyncio.set_event_loop(loop)
|
||||
client = ReuseableSanicTestClient(keep_alive_app_client_timeout, loop)
|
||||
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.text == "OK"
|
||||
loop.run_until_complete(aio_sleep(2))
|
||||
exception = None
|
||||
try:
|
||||
request, response = client.get(
|
||||
"/1", end_server=True, request_keepalive=1
|
||||
)
|
||||
request, response = client.get("/1", request_keepalive=1)
|
||||
except ValueError as e:
|
||||
exception = e
|
||||
assert exception is not None
|
||||
assert isinstance(exception, ValueError)
|
||||
assert "got a new connection" in exception.args[0]
|
||||
client.kill_server()
|
||||
|
||||
|
||||
def test_keep_alive_server_timeout():
|
||||
|
@ -266,15 +366,15 @@ def test_keep_alive_server_timeout():
|
|||
asyncio.set_event_loop(loop)
|
||||
client = ReuseableSanicTestClient(keep_alive_app_server_timeout, loop)
|
||||
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.text == "OK"
|
||||
loop.run_until_complete(aio_sleep(3))
|
||||
exception = None
|
||||
try:
|
||||
request, response = client.get(
|
||||
"/1", request_keepalive=60, end_server=True
|
||||
)
|
||||
request, response = client.get("/1", request_keepalive=60)
|
||||
except ValueError as e:
|
||||
exception = e
|
||||
assert exception is not None
|
||||
|
@ -283,3 +383,4 @@ def test_keep_alive_server_timeout():
|
|||
"Connection reset" 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 uuid
|
||||
|
||||
from io import StringIO
|
||||
from importlib import reload
|
||||
|
||||
import pytest
|
||||
from io import StringIO
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
import sanic
|
||||
from sanic.response import text
|
||||
from sanic.log import LOGGING_CONFIG_DEFAULTS
|
||||
|
||||
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; \
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import logging
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from sanic.config import BASE_LOGO
|
||||
|
||||
|
||||
try:
|
||||
import uvloop # noqa
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import logging
|
||||
|
||||
from asyncio import CancelledError
|
||||
|
||||
from sanic.exceptions import NotFound
|
||||
from sanic.request import Request
|
||||
from sanic.response import HTTPResponse, text
|
||||
|
||||
|
||||
# ------------------------------------------------------------ #
|
||||
# GET
|
||||
# ------------------------------------------------------------ #
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import multiprocessing
|
||||
import pickle
|
||||
import random
|
||||
import signal
|
||||
import pickle
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic.testing import HOST, PORT
|
||||
from sanic.response import text
|
||||
from sanic.testing import HOST, PORT
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic.blueprints import Blueprint
|
||||
from sanic.response import text
|
||||
from sanic.exceptions import URLBuildError
|
||||
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 sanic.response import text, redirect
|
||||
import pytest
|
||||
|
||||
from sanic.response import redirect, text
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import asyncio
|
||||
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):
|
||||
|
|
|
@ -2,6 +2,7 @@ import random
|
|||
|
||||
from sanic.response import json
|
||||
|
||||
|
||||
try:
|
||||
from ujson import loads
|
||||
except ImportError:
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import asyncio
|
||||
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.response import stream, text
|
||||
from sanic.views import CompositionView, HTTPMethodView
|
||||
from sanic.views import stream as stream_decorator
|
||||
|
||||
|
||||
data = "abc" * 10000000
|
||||
|
|
|
@ -1,183 +1,73 @@
|
|||
from json import JSONDecodeError
|
||||
import asyncio
|
||||
|
||||
import httpcore
|
||||
import requests_async as requests
|
||||
|
||||
from sanic import Sanic
|
||||
import asyncio
|
||||
from sanic.response import text
|
||||
import aiohttp
|
||||
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.")
|
||||
from sanic.testing import SanicTestClient
|
||||
|
||||
|
||||
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):
|
||||
class RequestContextManager(object):
|
||||
def __new__(cls, req, delay):
|
||||
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,
|
||||
parsed_url = httpcore.URL(url)
|
||||
request = httpcore.Request(
|
||||
method, parsed_url, headers=headers, body=body
|
||||
)
|
||||
if aiohttp_version < version.parse("3.3.0"):
|
||||
kw["auto_decompress"] = None
|
||||
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
|
||||
connection = await self.acquire_connection(
|
||||
parsed_url, ssl=ssl, timeout=timeout
|
||||
)
|
||||
conn = await super(DelayableTCPConnector, self).connect(
|
||||
req, *args, **kwargs
|
||||
)
|
||||
if self._post_connect_delay and self._post_connect_delay > 0:
|
||||
await asyncio.sleep(self._post_connect_delay, loop=self._loop)
|
||||
req.send = d_req.send
|
||||
t = req.loop.time()
|
||||
print("Connected at {}".format(t), flush=True)
|
||||
return conn
|
||||
if self._request_delay:
|
||||
print(f"\t>> Sleeping ({self._request_delay})")
|
||||
await asyncio.sleep(self._request_delay)
|
||||
response = await connection.send(request)
|
||||
if not stream:
|
||||
try:
|
||||
await response.read()
|
||||
finally:
|
||||
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):
|
||||
def __init__(self, app, loop, request_delay=1):
|
||||
super(DelayableSanicTestClient, self).__init__(app)
|
||||
def __init__(self, app, request_delay=None):
|
||||
super().__init__(app)
|
||||
self._request_delay = request_delay
|
||||
self._loop = None
|
||||
|
||||
async def _local_request(self, method, uri, cookies=None, *args, **kwargs):
|
||||
if self._loop is None:
|
||||
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
|
||||
def get_new_session(self):
|
||||
return DelayableSanicSession(request_delay=self._request_delay)
|
||||
|
||||
|
||||
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():
|
||||
client = DelayableSanicTestClient(request_timeout_default_app, None, 2)
|
||||
client = DelayableSanicTestClient(request_timeout_default_app, 2)
|
||||
request, response = client.get("/1")
|
||||
assert response.status == 408
|
||||
assert response.text == "Error: Request 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")
|
||||
assert response.status == 200
|
||||
assert response.text == "OK"
|
||||
|
@ -224,7 +114,7 @@ def test_default_server_error_websocket_request_timeout():
|
|||
"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)
|
||||
|
||||
assert response.status == 408
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import logging
|
||||
import os
|
||||
import ssl
|
||||
|
||||
from json import dumps as json_dumps
|
||||
from json import loads as json_loads
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic import Blueprint
|
||||
from sanic import Blueprint, Sanic
|
||||
from sanic.exceptions import ServerError
|
||||
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE, RequestParameters
|
||||
from sanic.response import json, text
|
||||
from sanic.testing import HOST, PORT
|
||||
|
||||
|
||||
# ------------------------------------------------------------ #
|
||||
# GET
|
||||
# ------------------------------------------------------------ #
|
||||
|
@ -529,36 +530,54 @@ def test_request_string_representation(app):
|
|||
@pytest.mark.parametrize(
|
||||
"payload,filename",
|
||||
[
|
||||
("------sanic\r\n"
|
||||
(
|
||||
"------sanic\r\n"
|
||||
'Content-Disposition: form-data; filename="filename"; name="test"\r\n'
|
||||
"\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'
|
||||
"\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'
|
||||
"\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'
|
||||
"\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'
|
||||
"\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'
|
||||
"\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):
|
||||
|
@ -743,7 +762,7 @@ def test_request_raw_args(app):
|
|||
|
||||
def test_request_query_args(app):
|
||||
# test multiple params with the same key
|
||||
params = [('test', 'value1'), ('test', 'value2')]
|
||||
params = [("test", "value1"), ("test", "value2")]
|
||||
|
||||
@app.get("/")
|
||||
def handler(request):
|
||||
|
@ -754,7 +773,10 @@ def test_request_query_args(app):
|
|||
assert request.query_args == params
|
||||
|
||||
# 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
|
||||
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
|
||||
|
||||
# test unique params
|
||||
params = [('test1', 'value1'), ('test2', 'value2')]
|
||||
params = [("test1", "value1"), ("test2", "value2")]
|
||||
|
||||
request, response = app.test_client.get("/", params=params)
|
||||
|
||||
|
@ -779,25 +801,22 @@ def test_request_query_args_custom_parsing(app):
|
|||
def handler(request):
|
||||
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(
|
||||
keep_blank_values=True
|
||||
) == [
|
||||
('test1', 'value1'), ('test2', ''), ('test3', 'value3')
|
||||
assert request.get_query_args(keep_blank_values=True) == [
|
||||
("test1", "value1"),
|
||||
("test2", ""),
|
||||
("test3", "value3"),
|
||||
]
|
||||
assert request.query_args == [
|
||||
('test1', 'value1'), ('test3', 'value3')
|
||||
]
|
||||
assert request.get_query_args(
|
||||
keep_blank_values=False
|
||||
) == [
|
||||
('test1', 'value1'), ('test3', 'value3')
|
||||
assert request.query_args == [("test1", "value1"), ("test3", "value3")]
|
||||
assert request.get_query_args(keep_blank_values=False) == [
|
||||
("test1", "value1"),
|
||||
("test3", "value3"),
|
||||
]
|
||||
|
||||
assert request.get_args(
|
||||
keep_blank_values=True
|
||||
) == RequestParameters(
|
||||
assert request.get_args(keep_blank_values=True) == RequestParameters(
|
||||
{"test1": ["value1"], "test2": [""], "test3": ["value3"]}
|
||||
)
|
||||
|
||||
|
@ -805,9 +824,7 @@ def test_request_query_args_custom_parsing(app):
|
|||
{"test1": ["value1"], "test3": ["value3"]}
|
||||
)
|
||||
|
||||
assert request.get_args(
|
||||
keep_blank_values=False
|
||||
) == RequestParameters(
|
||||
assert request.get_args(keep_blank_values=False) == RequestParameters(
|
||||
{"test1": ["value1"], "test3": ["value3"]}
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import asyncio
|
||||
import inspect
|
||||
import os
|
||||
|
||||
from collections import namedtuple
|
||||
from mimetypes import guess_type
|
||||
from random import choice
|
||||
|
@ -8,6 +9,7 @@ from unittest.mock import MagicMock
|
|||
from urllib.parse import unquote
|
||||
|
||||
import pytest
|
||||
|
||||
from aiofiles import os as async_os
|
||||
|
||||
from sanic.response import (
|
||||
|
@ -18,11 +20,11 @@ from sanic.response import (
|
|||
json,
|
||||
raw,
|
||||
stream,
|
||||
text,
|
||||
)
|
||||
from sanic.server import HttpProtocol
|
||||
from sanic.testing import HOST, PORT
|
||||
|
||||
|
||||
JSON_DATA = {"ok": True}
|
||||
|
||||
|
||||
|
@ -77,10 +79,10 @@ def test_response_header(app):
|
|||
|
||||
request, response = app.test_client.get("/")
|
||||
assert dict(response.headers) == {
|
||||
"Connection": "keep-alive",
|
||||
"Keep-Alive": str(app.config.KEEP_ALIVE_TIMEOUT),
|
||||
"Content-Length": "11",
|
||||
"Content-Type": "application/json",
|
||||
"connection": "keep-alive",
|
||||
"keep-alive": str(app.config.KEEP_ALIVE_TIMEOUT),
|
||||
"content-length": "11",
|
||||
"content-type": "application/json",
|
||||
}
|
||||
|
||||
|
||||
|
@ -363,7 +365,7 @@ def test_stream_response_with_cookies(app):
|
|||
return response
|
||||
|
||||
request, response = app.test_client.get("/")
|
||||
assert response.cookies["test"].value == "pass"
|
||||
assert response.cookies["test"] == "pass"
|
||||
|
||||
|
||||
def test_stream_response_without_cookies(app):
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from sanic import Sanic
|
||||
import asyncio
|
||||
from sanic.response import text
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.exceptions import ServiceUnavailable
|
||||
from sanic.response import text
|
||||
|
||||
|
||||
response_timeout_app = Sanic("test_response_timeout")
|
||||
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.router import ParameterNameConflicts, RouteDoesNotExist, RouteExists
|
||||
|
||||
|
||||
# ------------------------------------------------------------ #
|
||||
# UTF-8
|
||||
# ------------------------------------------------------------ #
|
||||
|
@ -468,16 +469,8 @@ def test_websocket_route(app, url):
|
|||
assert ws.subprotocol is None
|
||||
ev.set()
|
||||
|
||||
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(url)
|
||||
assert response.opened is True
|
||||
assert ev.is_set()
|
||||
|
||||
|
||||
|
@ -487,54 +480,24 @@ def test_websocket_route_with_subprotocols(app):
|
|||
@app.websocket("/ws", subprotocols=["foo", "bar"])
|
||||
async def handler(request, ws):
|
||||
results.append(ws.subprotocol)
|
||||
assert ws.subprotocol is not None
|
||||
|
||||
request, response = app.test_client.get(
|
||||
"/ws",
|
||||
headers={
|
||||
"Upgrade": "websocket",
|
||||
"Connection": "upgrade",
|
||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
||||
"Sec-WebSocket-Version": "13",
|
||||
"Sec-WebSocket-Protocol": "bar",
|
||||
},
|
||||
request, response = app.test_client.websocket("/ws", subprotocols=["bar"])
|
||||
assert response.opened is True
|
||||
assert results == ["bar"]
|
||||
|
||||
request, response = app.test_client.websocket(
|
||||
"/ws", subprotocols=["bar", "foo"]
|
||||
)
|
||||
assert response.status == 101
|
||||
assert response.opened is True
|
||||
assert results == ["bar", "bar"]
|
||||
|
||||
request, response = app.test_client.get(
|
||||
"/ws",
|
||||
headers={
|
||||
"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", subprotocols=["baz"])
|
||||
assert response.opened is True
|
||||
assert results == ["bar", "bar", None]
|
||||
|
||||
request, response = app.test_client.websocket("/ws")
|
||||
assert response.opened is True
|
||||
assert results == ["bar", "bar", None, None]
|
||||
|
||||
|
||||
|
@ -547,16 +510,8 @@ def test_add_webscoket_route(app, strict_slashes):
|
|||
ev.set()
|
||||
|
||||
app.add_websocket_route(handler, "/ws", strict_slashes=strict_slashes)
|
||||
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 ev.is_set()
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import pytest
|
|||
|
||||
from sanic.testing import HOST, PORT
|
||||
|
||||
|
||||
AVAILABLE_LISTENERS = [
|
||||
"before_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.testing import HOST, PORT
|
||||
from unittest.mock import MagicMock
|
||||
import asyncio
|
||||
from queue import Queue
|
||||
|
||||
|
||||
async def stop(app, loop):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import inspect
|
||||
import os
|
||||
|
||||
from time import gmtime, strftime
|
||||
|
||||
import pytest
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import socket
|
||||
|
||||
from sanic.testing import PORT, SanicTestClient
|
||||
from sanic.response import json, text
|
||||
from sanic.testing import PORT, SanicTestClient
|
||||
|
||||
|
||||
# ------------------------------------------------------------ #
|
||||
# UTF-8
|
||||
|
@ -9,26 +10,26 @@ from sanic.response import json, text
|
|||
|
||||
|
||||
def test_test_client_port_none(app):
|
||||
@app.get('/get')
|
||||
@app.get("/get")
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
return text("OK")
|
||||
|
||||
test_client = SanicTestClient(app, port=None)
|
||||
|
||||
request, response = test_client.get('/get')
|
||||
assert response.text == 'OK'
|
||||
request, response = test_client.get("/get")
|
||||
assert response.text == "OK"
|
||||
|
||||
request, response = test_client.post('/get')
|
||||
request, response = test_client.post("/get")
|
||||
assert response.status == 405
|
||||
|
||||
|
||||
def test_test_client_port_default(app):
|
||||
@app.get('/get')
|
||||
@app.get("/get")
|
||||
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)
|
||||
assert test_client.port == PORT
|
||||
|
||||
request, response = test_client.get('/get')
|
||||
request, response = test_client.get("/get")
|
||||
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
|
||||
|
||||
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_VALUE1 = "/myurl?arg1=v1&arg1=v2"
|
||||
URL_FOR_ARGS2 = dict(arg1=["v1", "v2"], _anchor="anchor")
|
||||
|
@ -170,7 +173,7 @@ def test_fails_with_int_message(app):
|
|||
|
||||
expected_error = (
|
||||
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
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from json import dumps as json_dumps
|
||||
|
||||
from sanic.response import text
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
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.request import Request
|
||||
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)
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import time
|
||||
import asyncio
|
||||
import json
|
||||
import shlex
|
||||
import subprocess
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
from unittest import mock
|
||||
from sanic.worker import GunicornWorker
|
||||
from sanic.app import Sanic
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic.app import Sanic
|
||||
from sanic.worker import GunicornWorker
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def gunicorn_worker():
|
||||
|
|
13
tox.ini
13
tox.ini
|
@ -1,24 +1,25 @@
|
|||
[tox]
|
||||
envlist = py35, py36, py37, {py35,py36,py37}-no-ext, lint, check
|
||||
envlist = py36, py37, {py36,py37}-no-ext, lint, check
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
setenv =
|
||||
{py35,py36,py37}-no-ext: SANIC_NO_UJSON=1
|
||||
{py35,py36,py37}-no-ext: SANIC_NO_UVLOOP=1
|
||||
{py36,py37}-no-ext: SANIC_NO_UJSON=1
|
||||
{py36,py37}-no-ext: SANIC_NO_UVLOOP=1
|
||||
deps =
|
||||
coverage
|
||||
pytest==4.1.0
|
||||
pytest-cov
|
||||
pytest-sanic
|
||||
pytest-sugar
|
||||
aiohttp>=2.3,<=3.2.1
|
||||
httpcore==0.1.1
|
||||
requests-async==0.4.0
|
||||
chardet<=2.3.0
|
||||
beautifulsoup4
|
||||
gunicorn
|
||||
pytest-benchmark
|
||||
commands =
|
||||
pytest tests --cov sanic --cov-report= {posargs}
|
||||
pytest {posargs:tests --cov sanic}
|
||||
- coverage combine --append
|
||||
coverage report -m
|
||||
coverage html -i
|
||||
|
@ -31,7 +32,7 @@ deps =
|
|||
|
||||
commands =
|
||||
flake8 sanic
|
||||
black --check --verbose sanic
|
||||
black --config ./.black.toml --check --verbose sanic
|
||||
isort --check-only --recursive sanic
|
||||
|
||||
[testenv:check]
|
||||
|
|
Loading…
Reference in New Issue
Block a user