Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing client #1562

Merged
merged 1 commit into from
May 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
File renamed without changes.
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 6 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand All @@ -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/>`_.

Expand Down
15 changes: 15 additions & 0 deletions docs/sanic/changelog.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 6 additions & 6 deletions docs/sanic/testing.md
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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:

Expand All @@ -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:

Expand All @@ -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
Expand Down
61 changes: 40 additions & 21 deletions sanic/testing.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,32 +19,41 @@ def __init__(self, app, port=PORT):
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:
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.text = await response.text()
except UnicodeDecodeError:
response.text = None
response = await getattr(session, method.lower())(
url, verify=False, *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()
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(
Expand Down Expand Up @@ -83,11 +95,15 @@ async def error_handler(request, exception):
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")
Expand Down Expand Up @@ -146,3 +162,6 @@ def options(self, *args, **kwargs):

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)
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 7 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ def open_local(paths, mode="r", encoding="utf8"):
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"],
Expand All @@ -64,7 +64,6 @@ def open_local(paths, mode="r", encoding="utf8"):
"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",
],
Expand All @@ -90,7 +89,8 @@ def open_local(paths, mode="r", encoding="utf8"):
"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,
Expand Down Expand Up @@ -119,7 +119,7 @@ def open_local(paths, mode="r", encoding="utf8"):
"recommonmark",
"sphinxcontrib-asyncio",
"docutils",
"pygments"
"pygments",
],
}

Expand Down
2 changes: 2 additions & 0 deletions tests/benchmark/test_route_resolution_benchmark.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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"]:
Expand Down
7 changes: 5 additions & 2 deletions tests/performance/aiohttp/simple_server.py
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
3 changes: 2 additions & 1 deletion tests/performance/bottle/simple_server.py
Original file line number Diff line number Diff line change
@@ -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():
Expand Down
5 changes: 4 additions & 1 deletion tests/performance/kyoukai/simple_server.py
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
14 changes: 8 additions & 6 deletions tests/performance/sanic/http_response.py
Original file line number Diff line number Diff line change
@@ -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())

Expand Down
Loading