Merge pull request #1443 from huge-success/new-readme

Update README with new logo, change Congfig.LOGO, run linter
This commit is contained in:
7 2018-12-30 13:23:12 -08:00 committed by GitHub
commit ff38a3c6b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 3169 additions and 2921 deletions

View File

@ -1,5 +1,8 @@
Sanic .. image:: https://raw.githubusercontent.com/huge-success/sanic-assets/master/png/sanic-framework-logo-400x97.png
===== :alt: Sanic | Build fast. Run fast.
Sanic | Build fast. Run fast.
=============================
.. start-badges .. start-badges
@ -13,8 +16,10 @@ Sanic
* - Package * - Package
- | |PyPI| |PyPI version| |Wheel| |Supported implementations| |Code style black| - | |PyPI| |PyPI version| |Wheel| |Supported implementations| |Code style black|
* - Support * - Support
- |Join the chat at https://gitter.im/sanic-python/Lobby| - | |Forums| |Join the chat at https://gitter.im/sanic-python/Lobby|
.. |Forums| image:: https://img.shields.io/badge/forums-community-ff0068.svg
:target: https://community.sanicframework.org/
.. |Join the chat at https://gitter.im/sanic-python/Lobby| image:: https://badges.gitter.im/sanic-python/Lobby.svg .. |Join the chat at https://gitter.im/sanic-python/Lobby| image:: https://badges.gitter.im/sanic-python/Lobby.svg
:target: https://gitter.im/sanic-python/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge :target: https://gitter.im/sanic-python/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |Codecov| image:: https://codecov.io/gh/huge-success/sanic/branch/master/graph/badge.svg .. |Codecov| image:: https://codecov.io/gh/huge-success/sanic/branch/master/graph/badge.svg
@ -40,24 +45,22 @@ Sanic
.. end-badges .. end-badges
Sanic is a Flask-like Python 3.5+ web server that's written to go fast. It's based on the work done by the amazing folks at magicstack, and was inspired by `this article <https://magic.io/blog/uvloop-blazing-fast-python-networking/>`_. Sanic is a Python web server that's written to go fast. It allows usage the `async` and `await` syntax added in Python 3.5, which makes your code non-blocking and speedy.
On top of being Flask-like, Sanic supports async request handlers. This means you can use the new shiny async/await syntax from Python 3.5, making your code non-blocking and speedy. `Source code on GitHub <https://github.com/huge-success/sanic/>`_ | `Help and discussion board <https://community.sanicframework.org/>`_.
Sanic is developed `on GitHub <https://github.com/huge-success/sanic/>`_. We also have `a community discussion board <https://community.sanicframework.org/>`_. Contributions are welcome! The project is maintained by the community, for the community **Contributions are welcome!**
If you have a project that utilizes Sanic make sure to comment on the `issue <https://github.com/huge-success/sanic/issues/396>`_ that we use to track those projects! The goal of the project is to provide a simple way to get up and running a highly performant HTTP server that is easy to build, to expand, and ultimately to scale.
Installation Installation
------------ ------------
- ``pip3 install sanic`` ``pip3 install sanic``
To install sanic without uvloop or ujson using bash, you can provide either or both of these environmental variables Sanic makes use of ``uvloop`` and ``ujson`` to help with performance. If you do not want to use those packages, simply add an environmental variable ``SANIC_NO_UVLOOP=true`` or ``SANIC_NO_UJSON=true`` at install time.
using any truthy string like `'y', 'yes', 't', 'true', 'on', '1'` and setting the ``SANIC_NO_X`` (``X`` = ``UVLOOP``/``UJSON``)
to true will stop that features installation.
- ``SANIC_NO_UVLOOP=true SANIC_NO_UJSON=true pip3 install sanic`` ``SANIC_NO_UVLOOP=true SANIC_NO_UJSON=true pip3 install sanic``
Hello World Example Hello World Example
@ -77,6 +80,27 @@ Hello World Example
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000) app.run(host='0.0.0.0', port=8000)
Sanic can now be easily run using ``python3 hello.py``.
.. code::
[2018-12-30 11:37:41 +0200] [13564] [INFO] Goin' Fast @ http://0.0.0.0:8000
[2018-12-30 11:37:41 +0200] [13564] [INFO] Starting worker [13564]
And, we can verify it is working: ``curl localhost:8000 -i``
.. code::
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: 5
Content-Length: 17
Content-Type: application/json
{"hello":"world"}
**Now, let's go build something fast!**
Documentation Documentation
------------- -------------
@ -89,37 +113,7 @@ Questions and Discussion
`Ask a question or join the conversation <https://community.sanicframework.org/>`_. `Ask a question or join the conversation <https://community.sanicframework.org/>`_.
Contribution
------------
Examples We are always happy to have new contributions. We have `marked issues good for anyone looking to get started <https://github.com/huge-success/sanic/issues?q=is%3Aopen+is%3Aissue+label%3Abeginner>`_, and welcome `questions on the forums <https://community.sanicframework.org/>`_. Please take a look at our `Contribution guidelines <https://github.com/huge-success/sanic/blob/master/CONTRIBUTING.md>`_.
--------
`Non-Core examples <https://github.com/huge-success/sanic/wiki/Examples/>`_. Examples of plugins and Sanic that are outside the scope of Sanic core.
`Extensions <https://github.com/huge-success/sanic/wiki/Extensions/>`_. Sanic extensions created by the community.
`Projects <https://github.com/huge-success/sanic/wiki/Projects/>`_. Sanic in production use.
Final Thoughts
--------------
::
▄▄▄▄▄
▀▀▀██████▄▄▄ _______________
▄▄▄▄▄ █████████▄ / \
▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
▀▀█████▄▄ ▀██████▄██ | _________________/
▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
▀▀▀▄ ▀▀███ ▀ ▄▄
▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
▌ ▐▀████▐███▒▒▒▒▒▐██▌
▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
▀▀█████████▀
▄▄██▀██████▀█
▄██▀ ▀▀▀ █
▄█ ▐▌
▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
▌ ▐ ▀▀▄▄▄▀
▀▀▄▄▀

View File

@ -13,7 +13,7 @@ from traceback import format_exc
from urllib.parse import urlencode, urlunparse from urllib.parse import urlencode, urlunparse
from sanic import reloader_helpers from sanic import reloader_helpers
from sanic.config import Config from sanic.config import BASE_LOGO, Config
from sanic.constants import HTTP_METHODS from sanic.constants import HTTP_METHODS
from sanic.exceptions import SanicException, ServerError, URLBuildError from sanic.exceptions import SanicException, ServerError, URLBuildError
from sanic.handlers import ErrorHandler from sanic.handlers import ErrorHandler
@ -1256,10 +1256,14 @@ class Sanic:
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
if ( if (
self.config.LOGO is not None self.config.LOGO
and os.environ.get("SANIC_SERVER_RUNNING") != "true" and os.environ.get("SANIC_SERVER_RUNNING") != "true"
): ):
logger.debug(self.config.LOGO) logger.debug(
self.config.LOGO
if isinstance(self.config.LOGO, str)
else BASE_LOGO
)
if run_async: if run_async:
server_settings["run_async"] = True server_settings["run_async"] = True

View File

@ -5,32 +5,18 @@ from sanic.exceptions import PyFileError
SANIC_PREFIX = "SANIC_" SANIC_PREFIX = "SANIC_"
BASE_LOGO = """
Sanic
Build Fast. Run Fast.
"""
class Config(dict): class Config(dict):
def __init__(self, defaults=None, load_env=True, keep_alive=True): def __init__(self, defaults=None, load_env=True, keep_alive=True):
super().__init__(defaults or {}) super().__init__(defaults or {})
self.LOGO = """ self.LOGO = BASE_LOGO
_______________
/ \\
| Gotta go fast! |
| _________________/
|/
"""
self.REQUEST_MAX_SIZE = 100000000 # 100 megabytes self.REQUEST_MAX_SIZE = 100000000 # 100 megabytes
self.REQUEST_BUFFER_QUEUE_SIZE = 100 self.REQUEST_BUFFER_QUEUE_SIZE = 100
self.REQUEST_TIMEOUT = 60 # 60 seconds self.REQUEST_TIMEOUT = 60 # 60 seconds

View File

@ -3,7 +3,7 @@ import pytest
from sanic import Sanic from sanic import Sanic
if sys.platform in ['win32', 'cygwin']: if sys.platform in ["win32", "cygwin"]:
collect_ignore = ["test_worker.py"] collect_ignore = ["test_worker.py"]

View File

@ -9,10 +9,15 @@ import ujson as json
loop = uvloop.new_event_loop() loop = uvloop.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
async def handle(request): async def handle(request):
return web.Response(body=json.dumps({"test":True}).encode('utf-8'), content_type='application/json') return web.Response(
body=json.dumps({"test": True}).encode("utf-8"),
content_type="application/json",
)
app = web.Application(loop=loop) app = web.Application(loop=loop)
app.router.add_route('GET', '/', handle) app.router.add_route("GET", "/", handle)
web.run_app(app, port=sys.argv[1], access_log=None) web.run_app(app, port=sys.argv[1], access_log=None)

View File

@ -4,8 +4,9 @@ from bottle import route, run
import ujson import ujson
@route('/') @route("/")
def index(): def index():
return ujson.dumps({'test': True}) return ujson.dumps({"test": True})
app = bottle.default_app() app = bottle.default_app()

View File

@ -3,9 +3,11 @@
import falcon import falcon
import ujson as json import ujson as json
class TestResource: class TestResource:
def on_get(self, req, resp): def on_get(self, req, resp):
resp.body = json.dumps({"test": True}) resp.body = json.dumps({"test": True})
app = falcon.API() app = falcon.API()
app.add_route('/', TestResource()) app.add_route("/", TestResource())

View File

@ -13,8 +13,14 @@ kyk = Kyoukai("example_app")
logger = logging.getLogger("Kyoukai") logger = logging.getLogger("Kyoukai")
logger.setLevel(logging.ERROR) logger.setLevel(logging.ERROR)
@kyk.route("/") @kyk.route("/")
async def index(ctx: HTTPRequestContext): async def index(ctx: HTTPRequestContext):
return ujson.dumps({"test":True}), 200, {"Content-Type": "application/json"} return (
ujson.dumps({"test": True}),
200,
{"Content-Type": "application/json"},
)
kyk.run() kyk.run()

View File

@ -3,8 +3,10 @@ import sys
import os import os
import inspect import inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) currentdir = os.path.dirname(
sys.path.insert(0, currentdir + '/../../../') os.path.abspath(inspect.getfile(inspect.currentframe()))
)
sys.path.insert(0, currentdir + "/../../../")
import timeit import timeit
@ -16,7 +18,11 @@ print("Running New 100,000 times")
times = 0 times = 0
total_time = 0 total_time = 0
for n in range(6): for n in range(6):
time = timeit.timeit('json({ "test":True }).output()', setup='from sanic.response import json', number=100000) time = timeit.timeit(
'json({ "test":True }).output()',
setup="from sanic.response import json",
number=100000,
)
print("Took {} seconds".format(time)) print("Took {} seconds".format(time))
total_time += time total_time += time
times += 1 times += 1
@ -26,7 +32,11 @@ print("Running Old 100,000 times")
times = 0 times = 0
total_time = 0 total_time = 0
for n in range(6): for n in range(6):
time = timeit.timeit('json({ "test":True }).output_old()', setup='from sanic.response import json', number=100000) time = timeit.timeit(
'json({ "test":True }).output_old()',
setup="from sanic.response import json",
number=100000,
)
print("Took {} seconds".format(time)) print("Took {} seconds".format(time))
total_time += time total_time += time
times += 1 times += 1

View File

@ -2,8 +2,10 @@ import sys
import os import os
import inspect import inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) currentdir = os.path.dirname(
sys.path.insert(0, currentdir + '/../../../') os.path.abspath(inspect.getfile(inspect.currentframe()))
)
sys.path.insert(0, currentdir + "/../../../")
from sanic import Sanic from sanic import Sanic
from sanic.response import json from sanic.response import json
@ -15,5 +17,6 @@ app = Sanic("test")
async def test(request): async def test(request):
return json({"test": True}) return json({"test": True})
if __name__ == '__main__':
if __name__ == "__main__":
app.run(host="0.0.0.0", port=sys.argv[1]) app.run(host="0.0.0.0", port=sys.argv[1])

View File

@ -2,8 +2,10 @@ import sys
import os import os
import inspect import inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) currentdir = os.path.dirname(
sys.path.insert(0, currentdir + '/../../../') os.path.abspath(inspect.getfile(inspect.currentframe()))
)
sys.path.insert(0, currentdir + "/../../../")
from sanic import Sanic from sanic import Sanic
from sanic.response import json, text from sanic.response import json, text
@ -17,7 +19,7 @@ async def test(request):
return json({"test": True}) return json({"test": True})
@app.route("/sync", methods=['GET', 'POST']) @app.route("/sync", methods=["GET", "POST"])
def test(request): def test(request):
return json({"test": True}) return json({"test": True})
@ -44,7 +46,14 @@ def post_json(request):
@app.route("/query_string") @app.route("/query_string")
def query_string(request): def query_string(request):
return json({"parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string}) return json(
{
"parsed": True,
"args": request.args,
"url": request.url,
"query_string": request.query_string,
}
)
import sys import sys
@ -52,7 +61,6 @@ import sys
app.run(host="0.0.0.0", port=sys.argv[1]) app.run(host="0.0.0.0", port=sys.argv[1])
# import asyncio_redis # import asyncio_redis
# import asyncpg # import asyncpg
# async def setup(sanic, loop): # async def setup(sanic, loop):

View File

@ -5,14 +5,14 @@ from tornado import ioloop, web
class MainHandler(web.RequestHandler): class MainHandler(web.RequestHandler):
def get(self): def get(self):
self.write(ujson.dumps({'test': True})) self.write(ujson.dumps({"test": True}))
app = web.Application([ app = web.Application(
(r'/', MainHandler) [(r"/", MainHandler)],
], debug=False, debug=False,
compress_response=False, compress_response=False,
static_hash_cache=True static_hash_cache=True,
) )
app.listen(8000) app.listen(8000)

View File

@ -12,16 +12,17 @@ from wheezy.web.middleware import path_routing_middleware_factory
import ujson import ujson
class WelcomeHandler(BaseHandler):
class WelcomeHandler(BaseHandler):
def get(self): def get(self):
response = HTTPResponse(content_type='application/json; charset=UTF-8') response = HTTPResponse(content_type="application/json; charset=UTF-8")
response.write(ujson.dumps({"test":True})) response.write(ujson.dumps({"test": True}))
return response return response
all_urls = [ all_urls = [
url('', WelcomeHandler, name='default'), url("", WelcomeHandler, name="default"),
# url('', welcome, name='welcome') # url('', welcome, name='welcome')
] ]
@ -29,18 +30,19 @@ options = {}
main = WSGIApplication( main = WSGIApplication(
middleware=[ middleware=[
bootstrap_defaults(url_mapping=all_urls), bootstrap_defaults(url_mapping=all_urls),
path_routing_middleware_factory path_routing_middleware_factory,
], ],
options=options options=options,
) )
if __name__ == '__main__': if __name__ == "__main__":
import sys import sys
from wsgiref.simple_server import make_server from wsgiref.simple_server import make_server
try: try:
print('Visit http://localhost:{}/'.format(sys.argv[-1])) print("Visit http://localhost:{}/".format(sys.argv[-1]))
make_server('', int(sys.argv[-1]), main).serve_forever() make_server("", int(sys.argv[-1]), main).serve_forever()
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
print('\nThanks!') print("\nThanks!")

View File

@ -8,14 +8,13 @@ from sanic.response import text
def test_app_loop_running(app): def test_app_loop_running(app):
@app.get("/test")
@app.get('/test')
async def handler(request): async def handler(request):
assert isinstance(app.loop, asyncio.AbstractEventLoop) assert isinstance(app.loop, asyncio.AbstractEventLoop)
return text('pass') return text("pass")
request, response = app.test_client.get('/test') request, response = app.test_client.get("/test")
assert response.text == 'pass' assert response.text == "pass"
def test_app_loop_not_running(app): def test_app_loop_not_running(app):
@ -23,105 +22,117 @@ def test_app_loop_not_running(app):
app.loop app.loop
assert str(excinfo.value) == ( assert str(excinfo.value) == (
'Loop can only be retrieved after the app has started ' "Loop can only be retrieved after the app has started "
'running. Not supported with `create_server` function' "running. Not supported with `create_server` function"
) )
def test_app_run_raise_type_error(app): def test_app_run_raise_type_error(app):
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
app.run(loop='loop') app.run(loop="loop")
assert str(excinfo.value) == ( assert str(excinfo.value) == (
'loop is not a valid argument. To use an existing loop, ' "loop is not a valid argument. To use an existing loop, "
'change to create_server().\nSee more: ' "change to create_server().\nSee more: "
'https://sanic.readthedocs.io/en/latest/sanic/deploying.html' "https://sanic.readthedocs.io/en/latest/sanic/deploying.html"
'#asynchronous-support' "#asynchronous-support"
) )
def test_app_route_raise_value_error(app): def test_app_route_raise_value_error(app):
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
@app.route('/test')
async def handler():
return text('test')
assert str(excinfo.value) == 'Required parameter `request` missing in the handler() route?' @app.route("/test")
async def handler():
return text("test")
assert (
str(excinfo.value)
== "Required parameter `request` missing in the handler() route?"
)
def test_app_handle_request_handler_is_none(app, monkeypatch): def test_app_handle_request_handler_is_none(app, monkeypatch):
def mockreturn(*args, **kwargs): def mockreturn(*args, **kwargs):
return None, [], {}, '' return None, [], {}, ""
# Not sure how to make app.router.get() return None, so use mock here. # Not sure how to make app.router.get() return None, so use mock here.
monkeypatch.setattr(app.router, 'get', mockreturn) monkeypatch.setattr(app.router, "get", mockreturn)
@app.get('/test') @app.get("/test")
def handler(request): def handler(request):
return text('test') return text("test")
request, response = app.test_client.get('/test') request, response = app.test_client.get("/test")
assert response.text == 'Error: \'None\' was returned while requesting a handler from the router' assert (
response.text
== "Error: 'None' was returned while requesting a handler from the router"
)
@pytest.mark.parametrize('websocket_enabled', [True, False]) @pytest.mark.parametrize("websocket_enabled", [True, False])
@pytest.mark.parametrize('enable', [True, False]) @pytest.mark.parametrize("enable", [True, False])
def test_app_enable_websocket(app, websocket_enabled, enable): def test_app_enable_websocket(app, websocket_enabled, enable):
app.websocket_enabled = websocket_enabled app.websocket_enabled = websocket_enabled
app.enable_websocket(enable=enable) app.enable_websocket(enable=enable)
assert app.websocket_enabled == enable assert app.websocket_enabled == enable
@app.websocket('/ws') @app.websocket("/ws")
async def handler(request, ws): async def handler(request, ws):
await ws.send('test') await ws.send("test")
assert app.websocket_enabled == True assert app.websocket_enabled == True
def test_handle_request_with_nested_exception(app, monkeypatch): def test_handle_request_with_nested_exception(app, monkeypatch):
err_msg = 'Mock Exception' err_msg = "Mock Exception"
# Not sure how to raise an exception in app.error_handler.response(), use mock here # Not sure how to raise an exception in app.error_handler.response(), use mock here
def mock_error_handler_response(*args, **kwargs): def mock_error_handler_response(*args, **kwargs):
raise Exception(err_msg) raise Exception(err_msg)
monkeypatch.setattr(app.error_handler, 'response', mock_error_handler_response) monkeypatch.setattr(
app.error_handler, "response", mock_error_handler_response
)
@app.get('/') @app.get("/")
def handler(request): def handler(request):
raise Exception raise Exception
return text('OK') return text("OK")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.status == 500 assert response.status == 500
assert response.text == 'An error occurred while handling an error' assert response.text == "An error occurred while handling an error"
def test_handle_request_with_nested_exception_debug(app, monkeypatch): def test_handle_request_with_nested_exception_debug(app, monkeypatch):
err_msg = 'Mock Exception' err_msg = "Mock Exception"
# Not sure how to raise an exception in app.error_handler.response(), use mock here # Not sure how to raise an exception in app.error_handler.response(), use mock here
def mock_error_handler_response(*args, **kwargs): def mock_error_handler_response(*args, **kwargs):
raise Exception(err_msg) raise Exception(err_msg)
monkeypatch.setattr(app.error_handler, 'response', mock_error_handler_response) monkeypatch.setattr(
app.error_handler, "response", mock_error_handler_response
)
@app.get('/') @app.get("/")
def handler(request): def handler(request):
raise Exception raise Exception
return text('OK') return text("OK")
request, response = app.test_client.get('/', debug=True) request, response = app.test_client.get("/", debug=True)
assert response.status == 500 assert response.status == 500
assert response.text.startswith( assert response.text.startswith(
'Error while handling error: {}\nStack: Traceback (most recent call last):\n'.format(err_msg) "Error while handling error: {}\nStack: Traceback (most recent call last):\n".format(
err_msg
)
) )
@ -129,21 +140,23 @@ def test_handle_request_with_nested_sanic_exception(app, monkeypatch, caplog):
# Not sure how to raise an exception in app.error_handler.response(), use mock here # Not sure how to raise an exception in app.error_handler.response(), use mock here
def mock_error_handler_response(*args, **kwargs): def mock_error_handler_response(*args, **kwargs):
raise SanicException('Mock SanicException') raise SanicException("Mock SanicException")
monkeypatch.setattr(app.error_handler, 'response', mock_error_handler_response) monkeypatch.setattr(
app.error_handler, "response", mock_error_handler_response
)
@app.get('/') @app.get("/")
def handler(request): def handler(request):
raise Exception raise Exception
return text('OK') return text("OK")
with caplog.at_level(logging.ERROR): with caplog.at_level(logging.ERROR):
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.status == 500 assert response.status == 500
assert response.text == 'Error: Mock SanicException' assert response.text == "Error: Mock SanicException"
assert caplog.record_tuples[0] == ( assert caplog.record_tuples[0] == (
'sanic.root', "sanic.root",
logging.ERROR, logging.ERROR,
"Exception occurred while handling uri: 'http://127.0.0.1:42101/'" "Exception occurred while handling uri: 'http://127.0.0.1:42101/'",
) )

View File

@ -4,17 +4,18 @@ import asyncio
def test_bad_request_response(app): def test_bad_request_response(app):
lines = [] lines = []
@app.listener('after_server_start') @app.listener("after_server_start")
async def _request(sanic, loop): async def _request(sanic, loop):
connect = asyncio.open_connection('127.0.0.1', 42101) connect = asyncio.open_connection("127.0.0.1", 42101)
reader, writer = await connect reader, writer = await connect
writer.write(b'not http') writer.write(b"not http")
while True: while True:
line = await reader.readline() line = await reader.readline()
if not line: if not line:
break break
lines.append(line) lines.append(line)
app.stop() app.stop()
app.run(host='127.0.0.1', port=42101, debug=False)
assert lines[0] == b'HTTP/1.1 400 Bad Request\r\n' app.run(host="127.0.0.1", port=42101, debug=False)
assert lines[-1] == b'Error: Bad Request' assert lines[0] == b"HTTP/1.1 400 Bad Request\r\n"
assert lines[-1] == b"Error: Bad Request"

View File

@ -17,12 +17,13 @@ from sanic.views import CompositionView
# GET # GET
# ------------------------------------------------------------ # # ------------------------------------------------------------ #
@pytest.fixture(scope='module')
@pytest.fixture(scope="module")
def static_file_directory(): def static_file_directory():
"""The static directory to serve""" """The static directory to serve"""
current_file = inspect.getfile(inspect.currentframe()) current_file = inspect.getfile(inspect.currentframe())
current_directory = os.path.dirname(os.path.abspath(current_file)) current_directory = os.path.dirname(os.path.abspath(current_file))
static_directory = os.path.join(current_directory, 'static') static_directory = os.path.join(current_directory, "static")
return static_directory return static_directory
@ -32,21 +33,23 @@ def get_file_path(static_file_directory, file_name):
def get_file_content(static_file_directory, file_name): def get_file_content(static_file_directory, file_name):
"""The content of the static file to check""" """The content of the static file to check"""
with open(get_file_path(static_file_directory, file_name), 'rb') as file: with open(get_file_path(static_file_directory, file_name), "rb") as file:
return file.read() return file.read()
@pytest.mark.parametrize('method', HTTP_METHODS) @pytest.mark.parametrize("method", HTTP_METHODS)
def test_versioned_routes_get(app, method): def test_versioned_routes_get(app, method):
bp = Blueprint('test_text') bp = Blueprint("test_text")
method = method.lower() method = method.lower()
func = getattr(bp, method) func = getattr(bp, method)
if callable(func): if callable(func):
@func('/{}'.format(method), version=1)
@func("/{}".format(method), version=1)
def handler(request): def handler(request):
return text('OK') return text("OK")
else: else:
print(func) print(func)
raise Exception("{} is not callable".format(func)) raise Exception("{} is not callable".format(func))
@ -55,245 +58,231 @@ def test_versioned_routes_get(app, method):
client_method = getattr(app.test_client, method) client_method = getattr(app.test_client, method)
request, response = client_method('/v1/{}'.format(method)) request, response = client_method("/v1/{}".format(method))
assert response.status == 200 assert response.status == 200
def test_bp(app): def test_bp(app):
bp = Blueprint('test_text') bp = Blueprint("test_text")
@bp.route('/') @bp.route("/")
def handler(request): def handler(request):
return text('Hello') return text("Hello")
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert app.is_request_stream is False assert app.is_request_stream is False
assert response.text == 'Hello' assert response.text == "Hello"
def test_bp_strict_slash(app): def test_bp_strict_slash(app):
bp = Blueprint('test_text') bp = Blueprint("test_text")
@bp.get('/get', strict_slashes=True) @bp.get("/get", strict_slashes=True)
def get_handler(request): def get_handler(request):
return text('OK') return text("OK")
@bp.post('/post/', strict_slashes=True) @bp.post("/post/", strict_slashes=True)
def post_handler(request): def post_handler(request):
return text('OK') return text("OK")
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get('/get') request, response = app.test_client.get("/get")
assert response.text == 'OK' assert response.text == "OK"
assert response.json is None assert response.json is None
request, response = app.test_client.get('/get/') request, response = app.test_client.get("/get/")
assert response.status == 404 assert response.status == 404
request, response = app.test_client.post('/post/') request, response = app.test_client.post("/post/")
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/post') request, response = app.test_client.post("/post")
assert response.status == 404 assert response.status == 404
def test_bp_strict_slash_default_value(app): def test_bp_strict_slash_default_value(app):
bp = Blueprint('test_text', strict_slashes=True) bp = Blueprint("test_text", strict_slashes=True)
@bp.get('/get') @bp.get("/get")
def get_handler(request): def get_handler(request):
return text('OK') return text("OK")
@bp.post('/post/') @bp.post("/post/")
def post_handler(request): def post_handler(request):
return text('OK') return text("OK")
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get('/get/') request, response = app.test_client.get("/get/")
assert response.status == 404 assert response.status == 404
request, response = app.test_client.post('/post') request, response = app.test_client.post("/post")
assert response.status == 404 assert response.status == 404
def test_bp_strict_slash_without_passing_default_value(app): def test_bp_strict_slash_without_passing_default_value(app):
bp = Blueprint('test_text') bp = Blueprint("test_text")
@bp.get('/get') @bp.get("/get")
def get_handler(request): def get_handler(request):
return text('OK') return text("OK")
@bp.post('/post/') @bp.post("/post/")
def post_handler(request): def post_handler(request):
return text('OK') return text("OK")
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get('/get/') request, response = app.test_client.get("/get/")
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/post') request, response = app.test_client.post("/post")
assert response.text == 'OK' assert response.text == "OK"
def test_bp_strict_slash_default_value_can_be_overwritten(app): def test_bp_strict_slash_default_value_can_be_overwritten(app):
bp = Blueprint('test_text', strict_slashes=True) bp = Blueprint("test_text", strict_slashes=True)
@bp.get('/get', strict_slashes=False) @bp.get("/get", strict_slashes=False)
def get_handler(request): def get_handler(request):
return text('OK') return text("OK")
@bp.post('/post/', strict_slashes=False) @bp.post("/post/", strict_slashes=False)
def post_handler(request): def post_handler(request):
return text('OK') return text("OK")
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get('/get/') request, response = app.test_client.get("/get/")
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/post') request, response = app.test_client.post("/post")
assert response.text == 'OK' assert response.text == "OK"
def test_bp_with_url_prefix(app): def test_bp_with_url_prefix(app):
bp = Blueprint('test_text', url_prefix='/test1') bp = Blueprint("test_text", url_prefix="/test1")
@bp.route('/') @bp.route("/")
def handler(request): def handler(request):
return text('Hello') return text("Hello")
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get('/test1/') request, response = app.test_client.get("/test1/")
assert response.text == 'Hello' assert response.text == "Hello"
def test_several_bp_with_url_prefix(app): def test_several_bp_with_url_prefix(app):
bp = Blueprint('test_text', url_prefix='/test1') bp = Blueprint("test_text", url_prefix="/test1")
bp2 = Blueprint('test_text2', url_prefix='/test2') bp2 = Blueprint("test_text2", url_prefix="/test2")
@bp.route('/') @bp.route("/")
def handler(request): def handler(request):
return text('Hello') return text("Hello")
@bp2.route('/') @bp2.route("/")
def handler2(request): def handler2(request):
return text('Hello2') return text("Hello2")
app.blueprint(bp) app.blueprint(bp)
app.blueprint(bp2) app.blueprint(bp2)
request, response = app.test_client.get('/test1/') request, response = app.test_client.get("/test1/")
assert response.text == 'Hello' assert response.text == "Hello"
request, response = app.test_client.get('/test2/') request, response = app.test_client.get("/test2/")
assert response.text == 'Hello2' assert response.text == "Hello2"
def test_bp_with_host(app): def test_bp_with_host(app):
bp = Blueprint('test_bp_host', url_prefix='/test1', host="example.com") bp = Blueprint("test_bp_host", url_prefix="/test1", host="example.com")
@bp.route('/') @bp.route("/")
def handler1(request): def handler1(request):
return text('Hello') return text("Hello")
@bp.route('/', host="sub.example.com") @bp.route("/", host="sub.example.com")
def handler2(request): def handler2(request):
return text('Hello subdomain!') return text("Hello subdomain!")
app.blueprint(bp) app.blueprint(bp)
headers = {"Host": "example.com"} headers = {"Host": "example.com"}
request, response = app.test_client.get( request, response = app.test_client.get("/test1/", headers=headers)
'/test1/', assert response.text == "Hello"
headers=headers)
assert response.text == 'Hello'
headers = {"Host": "sub.example.com"} headers = {"Host": "sub.example.com"}
request, response = app.test_client.get( request, response = app.test_client.get("/test1/", headers=headers)
'/test1/',
headers=headers)
assert response.text == 'Hello subdomain!' assert response.text == "Hello subdomain!"
def test_several_bp_with_host(app): def test_several_bp_with_host(app):
bp = Blueprint('test_text', bp = Blueprint("test_text", url_prefix="/test", host="example.com")
url_prefix='/test', bp2 = Blueprint("test_text2", url_prefix="/test", host="sub.example.com")
host="example.com")
bp2 = Blueprint('test_text2',
url_prefix='/test',
host="sub.example.com")
@bp.route('/') @bp.route("/")
def handler(request): def handler(request):
return text('Hello') return text("Hello")
@bp2.route('/') @bp2.route("/")
def handler1(request): def handler1(request):
return text('Hello2') return text("Hello2")
@bp2.route('/other/') @bp2.route("/other/")
def handler2(request): def handler2(request):
return text('Hello3') return text("Hello3")
app.blueprint(bp) app.blueprint(bp)
app.blueprint(bp2) app.blueprint(bp2)
assert bp.host == "example.com" assert bp.host == "example.com"
headers = {"Host": "example.com"} headers = {"Host": "example.com"}
request, response = app.test_client.get( request, response = app.test_client.get("/test/", headers=headers)
'/test/', assert response.text == "Hello"
headers=headers)
assert response.text == 'Hello'
assert bp2.host == "sub.example.com" assert bp2.host == "sub.example.com"
headers = {"Host": "sub.example.com"} headers = {"Host": "sub.example.com"}
request, response = app.test_client.get( request, response = app.test_client.get("/test/", headers=headers)
'/test/',
headers=headers)
assert response.text == 'Hello2' assert response.text == "Hello2"
request, response = app.test_client.get( request, response = app.test_client.get("/test/other/", headers=headers)
'/test/other/', assert response.text == "Hello3"
headers=headers)
assert response.text == 'Hello3'
def test_bp_middleware(app): def test_bp_middleware(app):
blueprint = Blueprint('test_middleware') blueprint = Blueprint("test_middleware")
@blueprint.middleware('response') @blueprint.middleware("response")
async def process_response(request, response): async def process_response(request, response):
return text('OK') return text("OK")
@app.route('/') @app.route("/")
async def handler(request): async def handler(request):
return text('FAIL') return text("FAIL")
app.blueprint(blueprint) app.blueprint(blueprint)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
def test_bp_exception_handler(app): def test_bp_exception_handler(app):
blueprint = Blueprint('test_middleware') blueprint = Blueprint("test_middleware")
@blueprint.route('/1') @blueprint.route("/1")
def handler_1(request): def handler_1(request):
raise InvalidUsage("OK") raise InvalidUsage("OK")
@blueprint.route('/2') @blueprint.route("/2")
def handler_2(request): def handler_2(request):
raise ServerError("OK") raise ServerError("OK")
@blueprint.route('/3') @blueprint.route("/3")
def handler_3(request): def handler_3(request):
raise NotFound("OK") raise NotFound("OK")
@ -303,131 +292,131 @@ def test_bp_exception_handler(app):
app.blueprint(blueprint) app.blueprint(blueprint)
request, response = app.test_client.get('/1') request, response = app.test_client.get("/1")
assert response.status == 400 assert response.status == 400
request, response = app.test_client.get('/2') request, response = app.test_client.get("/2")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.get('/3') request, response = app.test_client.get("/3")
assert response.status == 200 assert response.status == 200
def test_bp_listeners(app): def test_bp_listeners(app):
blueprint = Blueprint('test_middleware') blueprint = Blueprint("test_middleware")
order = [] order = []
@blueprint.listener('before_server_start') @blueprint.listener("before_server_start")
def handler_1(sanic, loop): def handler_1(sanic, loop):
order.append(1) order.append(1)
@blueprint.listener('after_server_start') @blueprint.listener("after_server_start")
def handler_2(sanic, loop): def handler_2(sanic, loop):
order.append(2) order.append(2)
@blueprint.listener('after_server_start') @blueprint.listener("after_server_start")
def handler_3(sanic, loop): def handler_3(sanic, loop):
order.append(3) order.append(3)
@blueprint.listener('before_server_stop') @blueprint.listener("before_server_stop")
def handler_4(sanic, loop): def handler_4(sanic, loop):
order.append(5) order.append(5)
@blueprint.listener('before_server_stop') @blueprint.listener("before_server_stop")
def handler_5(sanic, loop): def handler_5(sanic, loop):
order.append(4) order.append(4)
@blueprint.listener('after_server_stop') @blueprint.listener("after_server_stop")
def handler_6(sanic, loop): def handler_6(sanic, loop):
order.append(6) order.append(6)
app.blueprint(blueprint) app.blueprint(blueprint)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert order == [1, 2, 3, 4, 5, 6] assert order == [1, 2, 3, 4, 5, 6]
def test_bp_static(app): def test_bp_static(app):
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() current_file_contents = file.read()
blueprint = Blueprint('test_static') blueprint = Blueprint("test_static")
blueprint.static('/testing.file', current_file) blueprint.static("/testing.file", current_file)
app.blueprint(blueprint) app.blueprint(blueprint)
request, response = app.test_client.get('/testing.file') request, response = app.test_client.get("/testing.file")
assert response.status == 200 assert response.status == 200
assert response.body == current_file_contents assert response.body == current_file_contents
@pytest.mark.parametrize('file_name', ['test.html']) @pytest.mark.parametrize("file_name", ["test.html"])
def test_bp_static_content_type(app, file_name): def test_bp_static_content_type(app, file_name):
# This is done here, since no other test loads a file here # This is done here, since no other test loads a file here
current_file = inspect.getfile(inspect.currentframe()) current_file = inspect.getfile(inspect.currentframe())
current_directory = os.path.dirname(os.path.abspath(current_file)) current_directory = os.path.dirname(os.path.abspath(current_file))
static_directory = os.path.join(current_directory, 'static') static_directory = os.path.join(current_directory, "static")
blueprint = Blueprint('test_static') blueprint = Blueprint("test_static")
blueprint.static( blueprint.static(
'/testing.file', "/testing.file",
get_file_path(static_directory, file_name), get_file_path(static_directory, file_name),
content_type='text/html; charset=utf-8' content_type="text/html; charset=utf-8",
) )
app.blueprint(blueprint) app.blueprint(blueprint)
request, response = app.test_client.get('/testing.file') request, response = app.test_client.get("/testing.file")
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_directory, file_name) assert response.body == get_file_content(static_directory, file_name)
assert response.headers['Content-Type'] == 'text/html; charset=utf-8' assert response.headers["Content-Type"] == "text/html; charset=utf-8"
def test_bp_shorthand(app): def test_bp_shorthand(app):
blueprint = Blueprint('test_shorhand_routes') blueprint = Blueprint("test_shorhand_routes")
ev = asyncio.Event() ev = asyncio.Event()
@blueprint.get('/get') @blueprint.get("/get")
def handler(request): def handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@blueprint.put('/put') @blueprint.put("/put")
def put_handler(request): def put_handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@blueprint.post('/post') @blueprint.post("/post")
def post_handler(request): def post_handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@blueprint.head('/head') @blueprint.head("/head")
def head_handler(request): def head_handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@blueprint.options('/options') @blueprint.options("/options")
def options_handler(request): def options_handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@blueprint.patch('/patch') @blueprint.patch("/patch")
def patch_handler(request): def patch_handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@blueprint.delete('/delete') @blueprint.delete("/delete")
def delete_handler(request): def delete_handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@blueprint.websocket('/ws/', strict_slashes=True) @blueprint.websocket("/ws/", strict_slashes=True)
async def websocket_handler(request, ws): async def websocket_handler(request, ws):
assert request.stream is None assert request.stream is None
ev.set() ev.set()
@ -436,137 +425,146 @@ def test_bp_shorthand(app):
assert app.is_request_stream is False assert app.is_request_stream is False
request, response = app.test_client.get('/get') request, response = app.test_client.get("/get")
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/get') request, response = app.test_client.post("/get")
assert response.status == 405 assert response.status == 405
request, response = app.test_client.put('/put') request, response = app.test_client.put("/put")
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.get('/post') request, response = app.test_client.get("/post")
assert response.status == 405 assert response.status == 405
request, response = app.test_client.post('/post') request, response = app.test_client.post("/post")
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.get('/post') request, response = app.test_client.get("/post")
assert response.status == 405 assert response.status == 405
request, response = app.test_client.head('/head') request, response = app.test_client.head("/head")
assert response.status == 200 assert response.status == 200
request, response = app.test_client.get('/head') request, response = app.test_client.get("/head")
assert response.status == 405 assert response.status == 405
request, response = app.test_client.options('/options') request, response = app.test_client.options("/options")
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.get('/options') request, response = app.test_client.get("/options")
assert response.status == 405 assert response.status == 405
request, response = app.test_client.patch('/patch') request, response = app.test_client.patch("/patch")
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.get('/patch') request, response = app.test_client.get("/patch")
assert response.status == 405 assert response.status == 405
request, response = app.test_client.delete('/delete') request, response = app.test_client.delete("/delete")
assert response.text == 'OK' assert response.text == "OK"
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('/ws/', headers={ request, response = app.test_client.get(
'Upgrade': 'websocket', "/ws/",
'Connection': 'upgrade', headers={
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', "Upgrade": "websocket",
'Sec-WebSocket-Version': '13'}) "Connection": "upgrade",
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
"Sec-WebSocket-Version": "13",
},
)
assert response.status == 101 assert response.status == 101
assert ev.is_set() assert ev.is_set()
def test_bp_group(app): def test_bp_group(app):
deep_0 = Blueprint('deep_0', url_prefix='/deep') deep_0 = Blueprint("deep_0", url_prefix="/deep")
deep_1 = Blueprint('deep_1', url_prefix='/deep1') deep_1 = Blueprint("deep_1", url_prefix="/deep1")
@deep_0.route('/') @deep_0.route("/")
def handler(request): def handler(request):
return text('D0_OK') return text("D0_OK")
@deep_1.route('/bottom') @deep_1.route("/bottom")
def bottom_handler(request): def bottom_handler(request):
return text('D1B_OK') return text("D1B_OK")
mid_0 = Blueprint.group(deep_0, deep_1, url_prefix='/mid') mid_0 = Blueprint.group(deep_0, deep_1, url_prefix="/mid")
mid_1 = Blueprint('mid_tier', url_prefix='/mid1') mid_1 = Blueprint("mid_tier", url_prefix="/mid1")
@mid_1.route('/') @mid_1.route("/")
def handler1(request): def handler1(request):
return text('M1_OK') return text("M1_OK")
top = Blueprint.group(mid_0, mid_1) top = Blueprint.group(mid_0, mid_1)
app.blueprint(top) app.blueprint(top)
@app.route('/') @app.route("/")
def handler2(request): def handler2(request):
return text('TOP_OK') return text("TOP_OK")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'TOP_OK' assert response.text == "TOP_OK"
request, response = app.test_client.get('/mid1') request, response = app.test_client.get("/mid1")
assert response.text == 'M1_OK' assert response.text == "M1_OK"
request, response = app.test_client.get('/mid/deep') request, response = app.test_client.get("/mid/deep")
assert response.text == 'D0_OK' assert response.text == "D0_OK"
request, response = app.test_client.get('/mid/deep1/bottom') request, response = app.test_client.get("/mid/deep1/bottom")
assert response.text == 'D1B_OK' assert response.text == "D1B_OK"
def test_bp_group_with_default_url_prefix(app): def test_bp_group_with_default_url_prefix(app):
from sanic.response import json from sanic.response import json
bp_resources = Blueprint('bp_resources')
@bp_resources.get('/') bp_resources = Blueprint("bp_resources")
@bp_resources.get("/")
def list_resources_handler(request): def list_resources_handler(request):
resource = {} resource = {}
return json([resource]) return json([resource])
bp_resource = Blueprint('bp_resource', url_prefix='/<resource_id>') bp_resource = Blueprint("bp_resource", url_prefix="/<resource_id>")
@bp_resource.get('/') @bp_resource.get("/")
def get_resource_hander(request, resource_id): def get_resource_hander(request, resource_id):
resource = {'resource_id': resource_id} resource = {"resource_id": resource_id}
return json(resource) return json(resource)
bp_resources_group = Blueprint.group(bp_resources, bp_resource, bp_resources_group = Blueprint.group(
url_prefix='/resources') bp_resources, bp_resource, url_prefix="/resources"
bp_api_v1 = Blueprint('bp_api_v1') )
bp_api_v1 = Blueprint("bp_api_v1")
@bp_api_v1.get('/info') @bp_api_v1.get("/info")
def api_v1_info(request): def api_v1_info(request):
return text('api_version: v1') return text("api_version: v1")
bp_api_v1_group = Blueprint.group(bp_api_v1, bp_resources_group, bp_api_v1_group = Blueprint.group(
url_prefix='/v1') bp_api_v1, bp_resources_group, url_prefix="/v1"
bp_api_group = Blueprint.group(bp_api_v1_group, url_prefix='/api') )
bp_api_group = Blueprint.group(bp_api_v1_group, url_prefix="/api")
app.blueprint(bp_api_group) app.blueprint(bp_api_group)
request, response = app.test_client.get('/api/v1/info') request, response = app.test_client.get("/api/v1/info")
assert response.text == 'api_version: v1' assert response.text == "api_version: v1"
request, response = app.test_client.get('/api/v1/resources') request, response = app.test_client.get("/api/v1/resources")
assert response.json == [{}] assert response.json == [{}]
from uuid import uuid4 from uuid import uuid4
resource_id = str(uuid4()) resource_id = str(uuid4())
request, response = app.test_client.get( request, response = app.test_client.get(
'/api/v1/resources/{0}'.format(resource_id)) "/api/v1/resources/{0}".format(resource_id)
assert response.json == {'resource_id': resource_id} )
assert response.json == {"resource_id": resource_id}
def test_blueprint_middleware_with_args(app: Sanic): def test_blueprint_middleware_with_args(app: Sanic):
@ -588,19 +586,22 @@ def test_blueprint_middleware_with_args(app: Sanic):
app.blueprint(bp) app.blueprint(bp)
_, response = app.test_client.get("/wa", headers={"content-type": "application/json"}) _, response = app.test_client.get(
"/wa", headers={"content-type": "application/json"}
)
assert response.text == "value" assert response.text == "value"
_, response = app.test_client.get("/wa", headers={"content-type": "plain/text"}) _, response = app.test_client.get(
"/wa", headers={"content-type": "plain/text"}
)
assert response.json.get("test") == "value" assert response.json.get("test") == "value"
d = {} d = {}
@pytest.mark.parametrize('file_name', @pytest.mark.parametrize("file_name", ["test.file"])
['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() current_file_contents = file.read()
bp = Blueprint(name="static", url_prefix="/static", strict_slashes=False) bp = Blueprint(name="static", url_prefix="/static", strict_slashes=False)
@ -609,11 +610,12 @@ def test_static_blueprint_name(app: Sanic, static_file_directory, file_name):
"/test.file/", "/test.file/",
get_file_path(static_file_directory, file_name), get_file_path(static_file_directory, file_name),
name="static.testing", name="static.testing",
strict_slashes=True) strict_slashes=True,
)
app.blueprint(bp) app.blueprint(bp)
uri = app.url_for('static', name='static.testing') uri = app.url_for("static", name="static.testing")
assert uri == "/static/test.file" assert uri == "/static/test.file"
_, response = app.test_client.get("/static/test.file") _, response = app.test_client.get("/static/test.file")
@ -627,9 +629,7 @@ def test_route_handler_add(app: Sanic):
view = CompositionView() view = CompositionView()
async def get_handler(request): async def get_handler(request):
return json({ return json({"response": "OK"})
"response": "OK"
})
view.add(["GET"], get_handler, stream=False) view.add(["GET"], get_handler, stream=False)
@ -637,10 +637,7 @@ def test_route_handler_add(app: Sanic):
return text("OK") return text("OK")
bp = Blueprint(name="handler", url_prefix="/handler") bp = Blueprint(name="handler", url_prefix="/handler")
bp.add_route( bp.add_route(default_handler, uri="/default/", strict_slashes=True)
default_handler,
uri="/default/",
strict_slashes=True)
bp.add_route(view, uri="/view", name="test") bp.add_route(view, uri="/view", name="test")
@ -665,18 +662,21 @@ def test_websocket_route(app: Sanic):
app.blueprint(bp) app.blueprint(bp)
_, response = app.test_client.get("/ws/test", headers={ _, response = app.test_client.get(
'Upgrade': 'websocket', "/ws/test",
'Connection': 'upgrade', headers={
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', "Upgrade": "websocket",
'Sec-WebSocket-Version': '13' "Connection": "upgrade",
}) "Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
"Sec-WebSocket-Version": "13",
},
)
assert response.status == 101 assert response.status == 101
assert event.is_set() assert event.is_set()
def test_duplicate_blueprint(app): def test_duplicate_blueprint(app):
bp_name = 'bp' bp_name = "bp"
bp = Blueprint(bp_name) bp = Blueprint(bp_name)
bp1 = Blueprint(bp_name) bp1 = Blueprint(bp_name)
@ -687,13 +687,13 @@ def test_duplicate_blueprint(app):
assert str(excinfo.value) == ( assert str(excinfo.value) == (
'A blueprint with the name "{}" is already registered. ' 'A blueprint with the name "{}" is already registered. '
'Blueprint names must be unique.' "Blueprint names must be unique."
).format(bp_name) ).format(bp_name)
@pytest.mark.parametrize('debug', [True, False, None]) @pytest.mark.parametrize("debug", [True, False, None])
def test_register_blueprint(app, debug): def test_register_blueprint(app, debug):
bp = Blueprint('bp') bp = Blueprint("bp")
app.debug = debug app.debug = debug
with pytest.warns(DeprecationWarning) as record: with pytest.warns(DeprecationWarning) as record:

View File

@ -13,18 +13,18 @@ from sanic.exceptions import PyFileError
def temp_path(): def temp_path():
""" a simple cross platform replacement for NamedTemporaryFile """ """ a simple cross platform replacement for NamedTemporaryFile """
with TemporaryDirectory() as td: with TemporaryDirectory() as td:
yield Path(td, 'file') yield Path(td, "file")
def test_load_from_object(app): def test_load_from_object(app):
class Config: class Config:
not_for_config = 'should not be used' not_for_config = "should not be used"
CONFIG_VALUE = 'should be used' CONFIG_VALUE = "should be used"
app.config.from_object(Config) app.config.from_object(Config)
assert 'CONFIG_VALUE' in app.config assert "CONFIG_VALUE" in app.config
assert app.config.CONFIG_VALUE == 'should be used' assert app.config.CONFIG_VALUE == "should be used"
assert 'not_for_config' not in app.config assert "not_for_config" not in app.config
def test_auto_load_env(): def test_auto_load_env():
@ -37,13 +37,13 @@ def test_auto_load_env():
def test_dont_load_env(): def test_dont_load_env():
environ["SANIC_TEST_ANSWER"] = "42" environ["SANIC_TEST_ANSWER"] = "42"
app = Sanic(load_env=False) app = Sanic(load_env=False)
assert getattr(app.config, 'TEST_ANSWER', None) is None assert getattr(app.config, "TEST_ANSWER", None) is None
del environ["SANIC_TEST_ANSWER"] del environ["SANIC_TEST_ANSWER"]
def test_load_env_prefix(): def test_load_env_prefix():
environ["MYAPP_TEST_ANSWER"] = "42" environ["MYAPP_TEST_ANSWER"] = "42"
app = Sanic(load_env='MYAPP_') app = Sanic(load_env="MYAPP_")
assert app.config.TEST_ANSWER == 42 assert app.config.TEST_ANSWER == 42
del environ["MYAPP_TEST_ANSWER"] del environ["MYAPP_TEST_ANSWER"]
@ -63,43 +63,47 @@ def test_load_env_prefix_string_value():
def test_load_from_file(app): def test_load_from_file(app):
config = dedent(""" config = dedent(
"""
VALUE = 'some value' VALUE = 'some value'
condition = 1 == 1 condition = 1 == 1
if condition: if condition:
CONDITIONAL = 'should be set' CONDITIONAL = 'should be set'
""") """
)
with temp_path() as config_path: with temp_path() as config_path:
config_path.write_text(config) config_path.write_text(config)
app.config.from_pyfile(str(config_path)) app.config.from_pyfile(str(config_path))
assert 'VALUE' in app.config assert "VALUE" in app.config
assert app.config.VALUE == 'some value' assert app.config.VALUE == "some value"
assert 'CONDITIONAL' in app.config assert "CONDITIONAL" in app.config
assert app.config.CONDITIONAL == 'should be set' assert app.config.CONDITIONAL == "should be set"
assert 'condition' not in app.config assert "condition" not in app.config
def test_load_from_missing_file(app): def test_load_from_missing_file(app):
with pytest.raises(IOError): with pytest.raises(IOError):
app.config.from_pyfile('non-existent file') app.config.from_pyfile("non-existent file")
def test_load_from_envvar(app): def test_load_from_envvar(app):
config = "VALUE = 'some value'" config = "VALUE = 'some value'"
with temp_path() as config_path: with temp_path() as config_path:
config_path.write_text(config) config_path.write_text(config)
environ['APP_CONFIG'] = str(config_path) environ["APP_CONFIG"] = str(config_path)
app.config.from_envvar('APP_CONFIG') app.config.from_envvar("APP_CONFIG")
assert 'VALUE' in app.config assert "VALUE" in app.config
assert app.config.VALUE == 'some value' assert app.config.VALUE == "some value"
def test_load_from_missing_envvar(app): def test_load_from_missing_envvar(app):
with pytest.raises(RuntimeError) as e: with pytest.raises(RuntimeError) as e:
app.config.from_envvar('non-existent variable') app.config.from_envvar("non-existent variable")
assert str(e.value) == ("The environment variable 'non-existent " assert str(e.value) == (
"variable' is not set and thus configuration " "The environment variable 'non-existent "
"could not be loaded.") "variable' is not set and thus configuration "
"could not be loaded."
)
def test_load_config_from_file_invalid_syntax(app): def test_load_config_from_file_invalid_syntax(app):

View File

@ -8,109 +8,99 @@ from sanic.cookies import Cookie
# GET # GET
# ------------------------------------------------------------ # # ------------------------------------------------------------ #
def test_cookies(app): def test_cookies(app):
@app.route("/")
@app.route('/')
def handler(request): def handler(request):
response = text('Cookies are: {}'.format(request.cookies['test'])) response = text("Cookies are: {}".format(request.cookies["test"]))
response.cookies['right_back'] = 'at you' response.cookies["right_back"] = "at you"
return response return response
request, response = app.test_client.get('/', cookies={"test": "working!"}) request, response = app.test_client.get("/", cookies={"test": "working!"})
response_cookies = SimpleCookie() response_cookies = SimpleCookie()
response_cookies.load(response.headers.get('Set-Cookie', {})) response_cookies.load(response.headers.get("Set-Cookie", {}))
assert response.text == 'Cookies are: working!' assert response.text == "Cookies are: working!"
assert response_cookies['right_back'].value == 'at you' assert response_cookies["right_back"].value == "at you"
@pytest.mark.parametrize("httponly,expected", [ @pytest.mark.parametrize("httponly,expected", [(False, False), (True, True)])
(False, False),
(True, True),
])
def test_false_cookies_encoded(app, httponly, expected): def test_false_cookies_encoded(app, httponly, expected):
@app.route("/")
@app.route('/')
def handler(request): def handler(request):
response = text('hello cookies') response = text("hello cookies")
response.cookies['hello'] = 'world' response.cookies["hello"] = "world"
response.cookies['hello']['httponly'] = httponly response.cookies["hello"]["httponly"] = httponly
return text(response.cookies['hello'].encode('utf8')) return text(response.cookies["hello"].encode("utf8"))
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert ('HttpOnly' in response.text) == expected assert ("HttpOnly" in response.text) == expected
@pytest.mark.parametrize("httponly,expected", [ @pytest.mark.parametrize("httponly,expected", [(False, False), (True, True)])
(False, False),
(True, True),
])
def test_false_cookies(app, httponly, expected): def test_false_cookies(app, httponly, expected):
@app.route("/")
@app.route('/')
def handler(request): def handler(request):
response = text('hello cookies') response = text("hello cookies")
response.cookies['right_back'] = 'at you' response.cookies["right_back"] = "at you"
response.cookies['right_back']['httponly'] = httponly response.cookies["right_back"]["httponly"] = httponly
return response return response
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
response_cookies = SimpleCookie() response_cookies = SimpleCookie()
response_cookies.load(response.headers.get('Set-Cookie', {})) response_cookies.load(response.headers.get("Set-Cookie", {}))
assert ('HttpOnly' in response_cookies['right_back'].output()) == expected assert ("HttpOnly" in response_cookies["right_back"].output()) == expected
def test_http2_cookies(app): def test_http2_cookies(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
response = text('Cookies are: {}'.format(request.cookies['test'])) response = text("Cookies are: {}".format(request.cookies["test"]))
return response return response
headers = {'cookie': 'test=working!'} headers = {"cookie": "test=working!"}
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert response.text == 'Cookies are: working!' assert response.text == "Cookies are: working!"
def test_cookie_options(app): def test_cookie_options(app):
@app.route("/")
@app.route('/')
def handler(request): def handler(request):
response = text("OK") response = text("OK")
response.cookies['test'] = 'at you' response.cookies["test"] = "at you"
response.cookies['test']['httponly'] = True response.cookies["test"]["httponly"] = True
response.cookies['test']['expires'] = (datetime.now() + response.cookies["test"]["expires"] = datetime.now() + timedelta(
timedelta(seconds=10)) seconds=10
)
return response return response
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
response_cookies = SimpleCookie() response_cookies = SimpleCookie()
response_cookies.load(response.headers.get('Set-Cookie', {})) response_cookies.load(response.headers.get("Set-Cookie", {}))
assert response_cookies['test'].value == 'at you' assert response_cookies["test"].value == "at you"
assert response_cookies['test']['httponly'] is True assert response_cookies["test"]["httponly"] is True
def test_cookie_deletion(app): def test_cookie_deletion(app):
@app.route("/")
@app.route('/')
def handler(request): def handler(request):
response = text("OK") response = text("OK")
del response.cookies['i_want_to_die'] del response.cookies["i_want_to_die"]
response.cookies['i_never_existed'] = 'testing' response.cookies["i_never_existed"] = "testing"
del response.cookies['i_never_existed'] del response.cookies["i_never_existed"]
return response return response
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
response_cookies = SimpleCookie() response_cookies = SimpleCookie()
response_cookies.load(response.headers.get('Set-Cookie', {})) response_cookies.load(response.headers.get("Set-Cookie", {}))
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():
@ -134,58 +124,58 @@ def test_cookie_set_unknown_property():
def test_cookie_set_same_key(app): def test_cookie_set_same_key(app):
cookies = {'test': 'wait'} cookies = {"test": "wait"}
@app.get('/') @app.get("/")
def handler(request): def handler(request):
response = text('pass') response = text("pass")
response.cookies['test'] = 'modified' response.cookies["test"] = "modified"
response.cookies['test'] = 'pass' response.cookies["test"] = "pass"
return response return response
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"].value == "pass"
@pytest.mark.parametrize('max_age', ['0', 30, '30']) @pytest.mark.parametrize("max_age", ["0", 30, "30"])
def test_cookie_max_age(app, max_age): def test_cookie_max_age(app, max_age):
cookies = {'test': 'wait'} cookies = {"test": "wait"}
@app.get('/') @app.get("/")
def handler(request): def handler(request):
response = text('pass') response = text("pass")
response.cookies['test'] = 'pass' response.cookies["test"] = "pass"
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)
assert response.status == 200 assert response.status == 200
assert response.cookies['test'].value == 'pass' assert response.cookies["test"].value == "pass"
assert response.cookies['test']['max-age'] == str(max_age) assert response.cookies["test"]["max-age"] == str(max_age)
@pytest.mark.parametrize('expires', [ @pytest.mark.parametrize(
datetime.now() + timedelta(seconds=60), "expires",
'Fri, 21-Dec-2018 15:30:00 GMT' [datetime.now() + timedelta(seconds=60), "Fri, 21-Dec-2018 15:30:00 GMT"],
]) )
def test_cookie_expires(app, expires): def test_cookie_expires(app, expires):
cookies = {'test': 'wait'} cookies = {"test": "wait"}
@app.get('/') @app.get("/")
def handler(request): def handler(request):
response = text('pass') response = text("pass")
response.cookies['test'] = 'pass' response.cookies["test"] = "pass"
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)
assert response.status == 200 assert response.status == 200
assert response.cookies['test'].value == 'pass' assert response.cookies["test"].value == "pass"
if isinstance(expires, datetime): if isinstance(expires, datetime):
expires = expires.strftime("%a, %d-%b-%Y %T GMT") expires = expires.strftime("%a, %d-%b-%Y %T GMT")
assert response.cookies['test']['expires'] == expires assert response.cookies["test"]["expires"] == expires

View File

@ -13,26 +13,26 @@ def test_create_task(app):
app.add_task(coro) app.add_task(coro)
@app.route('/early') @app.route("/early")
def not_set(request): def not_set(request):
return text(e.is_set()) return text(e.is_set())
@app.route('/late') @app.route("/late")
async def set(request): async def set(request):
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
return text(e.is_set()) return text(e.is_set())
request, response = app.test_client.get('/early') request, response = app.test_client.get("/early")
assert response.body == b'False' assert response.body == b"False"
request, response = app.test_client.get('/late') request, response = app.test_client.get("/late")
assert response.body == b'True' assert response.body == b"True"
def test_create_task_with_app_arg(app): def test_create_task_with_app_arg(app):
q = Queue() q = Queue()
@app.route('/') @app.route("/")
def not_set(request): def not_set(request):
return "hello" return "hello"
@ -41,5 +41,5 @@ def test_create_task_with_app_arg(app):
app.add_task(coro) app.add_task(coro)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert q.get() == 'test_create_task_with_app_arg' assert q.get() == "test_create_task_with_app_arg"

View File

@ -3,26 +3,19 @@ from sanic.response import text
class CustomHttpProtocol(HttpProtocol): class CustomHttpProtocol(HttpProtocol):
def write_response(self, response): def write_response(self, response):
if isinstance(response, str): if isinstance(response, str):
response = text(response) response = text(response)
self.transport.write( self.transport.write(response.output(self.request.version))
response.output(self.request.version)
)
self.transport.close() self.transport.close()
def test_use_custom_protocol(app): def test_use_custom_protocol(app):
@app.route("/1")
@app.route('/1')
async def handler_1(request): async def handler_1(request):
return 'OK' return "OK"
server_kwargs = { server_kwargs = {"protocol": CustomHttpProtocol}
'protocol': CustomHttpProtocol request, response = app.test_client.get("/1", server_kwargs=server_kwargs)
}
request, response = app.test_client.get(
'/1', server_kwargs=server_kwargs)
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"

View File

@ -3,39 +3,41 @@ from sanic.router import RouteExists
import pytest import pytest
@pytest.mark.parametrize("method,attr, expected", [ @pytest.mark.parametrize(
("get", "text", "OK1 test"), "method,attr, expected",
("post", "text", "OK2 test"), [
("put", "text", "OK2 test"), ("get", "text", "OK1 test"),
("delete", "status", 405), ("post", "text", "OK2 test"),
]) ("put", "text", "OK2 test"),
("delete", "status", 405),
],
)
def test_overload_dynamic_routes(app, method, attr, expected): def test_overload_dynamic_routes(app, method, attr, expected):
@app.route("/overload/<param>", methods=["GET"])
@app.route('/overload/<param>', methods=['GET'])
async def handler1(request, param): async def handler1(request, param):
return text('OK1 ' + param) return text("OK1 " + param)
@app.route('/overload/<param>', methods=['POST', 'PUT']) @app.route("/overload/<param>", methods=["POST", "PUT"])
async def handler2(request, param): async def handler2(request, param):
return text('OK2 ' + param) return text("OK2 " + param)
request, response = getattr(app.test_client, method)('/overload/test') request, response = getattr(app.test_client, method)("/overload/test")
assert getattr(response, attr) == expected assert getattr(response, attr) == expected
def test_overload_dynamic_routes_exist(app): def test_overload_dynamic_routes_exist(app):
@app.route("/overload/<param>", methods=["GET"])
@app.route('/overload/<param>', methods=['GET'])
async def handler1(request, param): async def handler1(request, param):
return text('OK1 ' + param) return text("OK1 " + param)
@app.route('/overload/<param>', methods=['POST', 'PUT']) @app.route("/overload/<param>", methods=["POST", "PUT"])
async def handler2(request, param): async def handler2(request, param):
return text('OK2 ' + param) return text("OK2 " + param)
# if this doesn't raise an error, than at least the below should happen: # if this doesn't raise an error, than at least the below should happen:
# assert response.text == 'Duplicated' # assert response.text == 'Duplicated'
with pytest.raises(RouteExists): with pytest.raises(RouteExists):
@app.route('/overload/<param>', methods=['PUT', 'DELETE'])
@app.route("/overload/<param>", methods=["PUT", "DELETE"])
async def handler3(request): async def handler3(request):
return text('Duplicated') return text("Duplicated")

View File

@ -11,72 +11,74 @@ class SanicExceptionTestException(Exception):
pass pass
@pytest.fixture(scope='module') @pytest.fixture(scope="module")
def exception_app(): def exception_app():
app = Sanic('test_exceptions') app = Sanic("test_exceptions")
@app.route('/') @app.route("/")
def handler(request): def handler(request):
return text('OK') return text("OK")
@app.route('/error') @app.route("/error")
def handler_error(request): def handler_error(request):
raise ServerError("OK") raise ServerError("OK")
@app.route('/404') @app.route("/404")
def handler_404(request): def handler_404(request):
raise NotFound("OK") raise NotFound("OK")
@app.route('/403') @app.route("/403")
def handler_403(request): def handler_403(request):
raise Forbidden("Forbidden") raise Forbidden("Forbidden")
@app.route('/401') @app.route("/401")
def handler_401(request): def handler_401(request):
raise Unauthorized("Unauthorized") raise Unauthorized("Unauthorized")
@app.route('/401/basic') @app.route("/401/basic")
def handler_401_basic(request): def handler_401_basic(request):
raise Unauthorized("Unauthorized", scheme="Basic", realm="Sanic") raise Unauthorized("Unauthorized", scheme="Basic", realm="Sanic")
@app.route('/401/digest') @app.route("/401/digest")
def handler_401_digest(request): def handler_401_digest(request):
raise Unauthorized("Unauthorized", raise Unauthorized(
scheme="Digest", "Unauthorized",
realm="Sanic", scheme="Digest",
qop="auth, auth-int", realm="Sanic",
algorithm="MD5", qop="auth, auth-int",
nonce="abcdef", algorithm="MD5",
opaque="zyxwvu") nonce="abcdef",
opaque="zyxwvu",
)
@app.route('/401/bearer') @app.route("/401/bearer")
def handler_401_bearer(request): def handler_401_bearer(request):
raise Unauthorized("Unauthorized", scheme="Bearer") raise Unauthorized("Unauthorized", scheme="Bearer")
@app.route('/invalid') @app.route("/invalid")
def handler_invalid(request): def handler_invalid(request):
raise InvalidUsage("OK") raise InvalidUsage("OK")
@app.route('/abort/401') @app.route("/abort/401")
def handler_401_error(request): def handler_401_error(request):
abort(401) abort(401)
@app.route('/abort') @app.route("/abort")
def handler_500_error(request): def handler_500_error(request):
abort(500) abort(500)
return text("OK") return text("OK")
@app.route('/abort/message') @app.route("/abort/message")
def handler_abort_message(request): def handler_abort_message(request):
abort(500, message='Abort') abort(500, message="Abort")
@app.route('/divide_by_zero') @app.route("/divide_by_zero")
def handle_unhandled_exception(request): def handle_unhandled_exception(request):
1 / 0 1 / 0
@app.route('/error_in_error_handler_handler') @app.route("/error_in_error_handler_handler")
def custom_error_handler(request): def custom_error_handler(request):
raise SanicExceptionTestException('Dummy message!') raise SanicExceptionTestException("Dummy message!")
@app.exception(SanicExceptionTestException) @app.exception(SanicExceptionTestException)
def error_in_error_handler_handler(request, exception): def error_in_error_handler_handler(request, exception):
@ -86,126 +88,127 @@ def exception_app():
def test_catch_exception_list(app): def test_catch_exception_list(app):
@app.exception([SanicExceptionTestException, NotFound]) @app.exception([SanicExceptionTestException, NotFound])
def exception_list(request, exception): def exception_list(request, exception):
return text("ok") return text("ok")
@app.route('/') @app.route("/")
def exception(request): def exception(request):
raise SanicExceptionTestException("You won't see me") raise SanicExceptionTestException("You won't see me")
request, response = app.test_client.get('/random') request, response = app.test_client.get("/random")
assert response.text == 'ok' assert response.text == "ok"
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'ok' assert response.text == "ok"
def test_no_exception(exception_app): def test_no_exception(exception_app):
"""Test that a route works without an exception""" """Test that a route works without an exception"""
request, response = exception_app.test_client.get('/') request, response = exception_app.test_client.get("/")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
def test_server_error_exception(exception_app): def test_server_error_exception(exception_app):
"""Test the built-in ServerError exception works""" """Test the built-in ServerError exception works"""
request, response = exception_app.test_client.get('/error') request, response = exception_app.test_client.get("/error")
assert response.status == 500 assert response.status == 500
def test_invalid_usage_exception(exception_app): def test_invalid_usage_exception(exception_app):
"""Test the built-in InvalidUsage exception works""" """Test the built-in InvalidUsage exception works"""
request, response = exception_app.test_client.get('/invalid') request, response = exception_app.test_client.get("/invalid")
assert response.status == 400 assert response.status == 400
def test_not_found_exception(exception_app): def test_not_found_exception(exception_app):
"""Test the built-in NotFound exception works""" """Test the built-in NotFound exception works"""
request, response = exception_app.test_client.get('/404') request, response = exception_app.test_client.get("/404")
assert response.status == 404 assert response.status == 404
def test_forbidden_exception(exception_app): def test_forbidden_exception(exception_app):
"""Test the built-in Forbidden exception""" """Test the built-in Forbidden exception"""
request, response = exception_app.test_client.get('/403') request, response = exception_app.test_client.get("/403")
assert response.status == 403 assert response.status == 403
def test_unauthorized_exception(exception_app): def test_unauthorized_exception(exception_app):
"""Test the built-in Unauthorized exception""" """Test the built-in Unauthorized exception"""
request, response = exception_app.test_client.get('/401') request, response = exception_app.test_client.get("/401")
assert response.status == 401 assert response.status == 401
request, response = exception_app.test_client.get('/401/basic') request, response = exception_app.test_client.get("/401/basic")
assert response.status == 401 assert response.status == 401
assert response.headers.get('WWW-Authenticate') is not None assert response.headers.get("WWW-Authenticate") is not None
assert response.headers.get('WWW-Authenticate') == 'Basic realm="Sanic"' assert response.headers.get("WWW-Authenticate") == 'Basic realm="Sanic"'
request, response = exception_app.test_client.get('/401/digest') request, response = exception_app.test_client.get("/401/digest")
assert response.status == 401 assert response.status == 401
auth_header = response.headers.get('WWW-Authenticate') auth_header = response.headers.get("WWW-Authenticate")
assert auth_header is not None assert auth_header is not None
assert auth_header.startswith('Digest') assert auth_header.startswith("Digest")
assert 'qop="auth, auth-int"' in auth_header assert 'qop="auth, auth-int"' in auth_header
assert 'algorithm="MD5"' in auth_header assert 'algorithm="MD5"' in auth_header
assert 'nonce="abcdef"' in auth_header assert 'nonce="abcdef"' in auth_header
assert 'opaque="zyxwvu"' in auth_header assert 'opaque="zyxwvu"' in auth_header
request, response = exception_app.test_client.get('/401/bearer') request, response = exception_app.test_client.get("/401/bearer")
assert response.status == 401 assert response.status == 401
assert response.headers.get('WWW-Authenticate') == "Bearer" assert response.headers.get("WWW-Authenticate") == "Bearer"
def test_handled_unhandled_exception(exception_app): def test_handled_unhandled_exception(exception_app):
"""Test that an exception not built into sanic is handled""" """Test that an exception not built into sanic is handled"""
request, response = exception_app.test_client.get('/divide_by_zero') request, response = exception_app.test_client.get("/divide_by_zero")
assert response.status == 500 assert response.status == 500
soup = BeautifulSoup(response.body, 'html.parser') soup = BeautifulSoup(response.body, "html.parser")
assert soup.h1.text == 'Internal Server Error' assert soup.h1.text == "Internal Server Error"
message = " ".join(soup.p.text.split()) message = " ".join(soup.p.text.split())
assert message == ( assert message == (
"The server encountered an internal error and " "The server encountered an internal error and "
"cannot complete your request.") "cannot complete your request."
)
def test_exception_in_exception_handler(exception_app): def test_exception_in_exception_handler(exception_app):
"""Test that an exception thrown in an error handler is handled""" """Test that an exception thrown in an error handler is handled"""
request, response = exception_app.test_client.get( request, response = exception_app.test_client.get(
'/error_in_error_handler_handler') "/error_in_error_handler_handler"
)
assert response.status == 500 assert response.status == 500
assert response.body == b'An error occurred while handling an error' assert response.body == b"An error occurred while handling an error"
def test_exception_in_exception_handler_debug_off(exception_app): def test_exception_in_exception_handler_debug_off(exception_app):
"""Test that an exception thrown in an error handler is handled""" """Test that an exception thrown in an error handler is handled"""
request, response = exception_app.test_client.get( request, response = exception_app.test_client.get(
'/error_in_error_handler_handler', "/error_in_error_handler_handler", debug=False
debug=False) )
assert response.status == 500 assert response.status == 500
assert response.body == b'An error occurred while handling an error' assert response.body == b"An error occurred while handling an error"
def test_exception_in_exception_handler_debug_on(exception_app): def test_exception_in_exception_handler_debug_on(exception_app):
"""Test that an exception thrown in an error handler is handled""" """Test that an exception thrown in an error handler is handled"""
request, response = exception_app.test_client.get( request, response = exception_app.test_client.get(
'/error_in_error_handler_handler', "/error_in_error_handler_handler", debug=True
debug=True) )
assert response.status == 500 assert response.status == 500
assert response.body.startswith(b'Exception raised in exception ') assert response.body.startswith(b"Exception raised in exception ")
def test_abort(exception_app): def test_abort(exception_app):
"""Test the abort function""" """Test the abort function"""
request, response = exception_app.test_client.get('/abort/401') request, response = exception_app.test_client.get("/abort/401")
assert response.status == 401 assert response.status == 401
request, response = exception_app.test_client.get('/abort') request, response = exception_app.test_client.get("/abort")
assert response.status == 500 assert response.status == 500
request, response = exception_app.test_client.get('/abort/message') request, response = exception_app.test_client.get("/abort/message")
assert response.status == 500 assert response.status == 500
assert response.text == 'Error: Abort' assert response.text == "Error: Abort"

View File

@ -4,38 +4,39 @@ from sanic.exceptions import InvalidUsage, ServerError, NotFound
from sanic.handlers import ErrorHandler from sanic.handlers import ErrorHandler
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
exception_handler_app = Sanic('test_exception_handler') exception_handler_app = Sanic("test_exception_handler")
@exception_handler_app.route('/1') @exception_handler_app.route("/1")
def handler_1(request): def handler_1(request):
raise InvalidUsage("OK") raise InvalidUsage("OK")
@exception_handler_app.route('/2') @exception_handler_app.route("/2")
def handler_2(request): def handler_2(request):
raise ServerError("OK") raise ServerError("OK")
@exception_handler_app.route('/3') @exception_handler_app.route("/3")
def handler_3(request): def handler_3(request):
raise NotFound("OK") raise NotFound("OK")
@exception_handler_app.route('/4') @exception_handler_app.route("/4")
def handler_4(request): def handler_4(request):
foo = bar # noqa -- F821 undefined name 'bar' is done to throw exception foo = bar # noqa -- F821 undefined name 'bar' is done to throw exception
return text(foo) return text(foo)
@exception_handler_app.route('/5') @exception_handler_app.route("/5")
def handler_5(request): def handler_5(request):
class CustomServerError(ServerError): class CustomServerError(ServerError):
pass pass
raise CustomServerError('Custom server error')
raise CustomServerError("Custom server error")
@exception_handler_app.route('/6/<arg:int>') @exception_handler_app.route("/6/<arg:int>")
def handler_6(request, arg): def handler_6(request, arg):
try: try:
foo = 1 / arg foo = 1 / arg
@ -50,67 +51,67 @@ def handler_exception(request, exception):
def test_invalid_usage_exception_handler(): def test_invalid_usage_exception_handler():
request, response = exception_handler_app.test_client.get('/1') request, response = exception_handler_app.test_client.get("/1")
assert response.status == 400 assert response.status == 400
def test_server_error_exception_handler(): def test_server_error_exception_handler():
request, response = exception_handler_app.test_client.get('/2') request, response = exception_handler_app.test_client.get("/2")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
def test_not_found_exception_handler(): def test_not_found_exception_handler():
request, response = exception_handler_app.test_client.get('/3') request, response = exception_handler_app.test_client.get("/3")
assert response.status == 200 assert response.status == 200
def test_text_exception__handler(): def test_text_exception__handler():
request, response = exception_handler_app.test_client.get('/random') request, response = exception_handler_app.test_client.get("/random")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
def test_html_traceback_output_in_debug_mode(): def test_html_traceback_output_in_debug_mode():
request, response = exception_handler_app.test_client.get( request, response = exception_handler_app.test_client.get("/4", debug=True)
'/4', debug=True)
assert response.status == 500 assert response.status == 500
soup = BeautifulSoup(response.body, 'html.parser') soup = BeautifulSoup(response.body, "html.parser")
html = str(soup) html = str(soup)
assert 'response = handler(request, *args, **kwargs)' in html assert "response = handler(request, *args, **kwargs)" in html
assert 'handler_4' in html assert "handler_4" in html
assert 'foo = bar' in html assert "foo = bar" in html
summary_text = " ".join(soup.select('.summary')[0].text.split()) summary_text = " ".join(soup.select(".summary")[0].text.split())
assert ( assert (
"NameError: name 'bar' " "NameError: name 'bar' " "is not defined while handling path /4"
"is not defined while handling path /4") == summary_text ) == summary_text
def test_inherited_exception_handler(): def test_inherited_exception_handler():
request, response = exception_handler_app.test_client.get('/5') request, response = exception_handler_app.test_client.get("/5")
assert response.status == 200 assert response.status == 200
def test_chained_exception_handler(): def test_chained_exception_handler():
request, response = exception_handler_app.test_client.get( request, response = exception_handler_app.test_client.get(
'/6/0', debug=True) "/6/0", debug=True
)
assert response.status == 500 assert response.status == 500
soup = BeautifulSoup(response.body, 'html.parser') soup = BeautifulSoup(response.body, "html.parser")
html = str(soup) html = str(soup)
assert 'response = handler(request, *args, **kwargs)' in html assert "response = handler(request, *args, **kwargs)" in html
assert 'handler_6' in html assert "handler_6" in html
assert 'foo = 1 / arg' in html assert "foo = 1 / arg" in html
assert 'ValueError' in html assert "ValueError" in html
assert 'The above exception was the direct cause' in html assert "The above exception was the direct cause" in html
summary_text = " ".join(soup.select('.summary')[0].text.split()) summary_text = " ".join(soup.select(".summary")[0].text.split())
assert ( assert (
"ZeroDivisionError: division by zero " "ZeroDivisionError: division by zero " "while handling path /6/0"
"while handling path /6/0") == summary_text ) == summary_text
def test_exception_handler_lookup(): def test_exception_handler_lookup():
@ -132,6 +133,7 @@ def test_exception_handler_lookup():
try: try:
ModuleNotFoundError ModuleNotFoundError
except Exception: except Exception:
class ModuleNotFoundError(ImportError): class ModuleNotFoundError(ImportError):
pass pass
@ -143,12 +145,12 @@ def test_exception_handler_lookup():
assert handler.lookup(ImportError()) == import_error_handler assert handler.lookup(ImportError()) == import_error_handler
assert handler.lookup(ModuleNotFoundError()) == import_error_handler assert handler.lookup(ModuleNotFoundError()) == import_error_handler
assert handler.lookup(CustomError()) == custom_error_handler assert handler.lookup(CustomError()) == custom_error_handler
assert handler.lookup(ServerError('Error')) == server_error_handler assert handler.lookup(ServerError("Error")) == server_error_handler
assert handler.lookup(CustomServerError('Error')) == server_error_handler assert handler.lookup(CustomServerError("Error")) == server_error_handler
# once again to ensure there is no caching bug # once again to ensure there is no caching bug
assert handler.lookup(ImportError()) == import_error_handler assert handler.lookup(ImportError()) == import_error_handler
assert handler.lookup(ModuleNotFoundError()) == import_error_handler assert handler.lookup(ModuleNotFoundError()) == import_error_handler
assert handler.lookup(CustomError()) == custom_error_handler assert handler.lookup(CustomError()) == custom_error_handler
assert handler.lookup(ServerError('Error')) == server_error_handler assert handler.lookup(ServerError("Error")) == server_error_handler
assert handler.lookup(CustomServerError('Error')) == server_error_handler assert handler.lookup(CustomServerError("Error")) == server_error_handler

View File

@ -38,35 +38,19 @@ def test_is_hop_by_hop_header():
def test_remove_entity_headers(): def test_remove_entity_headers():
tests = ( tests = (
( ({}, {}),
{}, ({"Allow": "GET, POST, HEAD"}, {}),
{}
),
(
{
"Allow": "GET, POST, HEAD",
},
{}
),
( (
{ {
"Content-Type": "application/json", "Content-Type": "application/json",
"Expires": "Wed, 21 Oct 2015 07:28:00 GMT", "Expires": "Wed, 21 Oct 2015 07:28:00 GMT",
"Foo": "Bar" "Foo": "Bar",
},
{
"Expires": "Wed, 21 Oct 2015 07:28:00 GMT",
"Foo": "Bar"
}, },
{"Expires": "Wed, 21 Oct 2015 07:28:00 GMT", "Foo": "Bar"},
), ),
( (
{ {"Allow": "GET, POST, HEAD", "Content-Location": "/test"},
"Allow": "GET, POST, HEAD", {"Content-Location": "/test"},
"Content-Location": "/test"
},
{
"Content-Location": "/test"
},
), ),
) )

View File

@ -16,12 +16,14 @@ class ReuseableTCPConnector(TCPConnector):
self.old_proto = None self.old_proto = None
async def connect(self, req, *args, **kwargs): async def connect(self, req, *args, **kwargs):
new_conn = await super(ReuseableTCPConnector, self)\ new_conn = await super(ReuseableTCPConnector, self).connect(
.connect(req, *args, **kwargs) req, *args, **kwargs
)
if self.old_proto is not None: if self.old_proto is not None:
if self.old_proto != new_conn._protocol: if self.old_proto != new_conn._protocol:
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__) print(new_conn.__dict__)
self.old_proto = new_conn._protocol self.old_proto = new_conn._protocol
return new_conn return new_conn
@ -40,31 +42,39 @@ class ReuseableSanicTestClient(SanicTestClient):
# Copied from SanicTestClient, but with some changes to reuse the # Copied from SanicTestClient, but with some changes to reuse the
# same loop for the same app. # same loop for the same app.
def _sanic_endpoint_test( def _sanic_endpoint_test(
self, method='get', uri='/', gather_request=True, self,
debug=False, server_kwargs={}, method="get",
*request_args, **request_kwargs): uri="/",
gather_request=True,
debug=False,
server_kwargs={},
*request_args,
**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) do_kill_server = request_kwargs.pop("end_server", False)
if gather_request: if gather_request:
def _collect_request(request): def _collect_request(request):
if results[0] is None: if results[0] is None:
results[0] = request results[0] = request
self.app.request_middleware.appendleft(_collect_request) self.app.request_middleware.appendleft(_collect_request)
@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: if do_kill_server:
request_kwargs['end_session'] = True request_kwargs["end_session"] = True
response = await self._local_request( response = await self._local_request(
method, uri, *request_args, method, uri, *request_args, **request_kwargs
**request_kwargs) )
results[-1] = response results[-1] = response
except Exception as e2: except Exception as e2:
import traceback 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() # Don't stop here! self.app.stop()
@ -72,23 +82,25 @@ class ReuseableSanicTestClient(SanicTestClient):
if self._server is not None: if self._server is not None:
_server = self._server _server = self._server
else: else:
_server_co = self.app.create_server(host=HOST, debug=debug, _server_co = self.app.create_server(
port=PORT, **server_kwargs) host=HOST, debug=debug, port=PORT, **server_kwargs
)
server.trigger_events( server.trigger_events(
self.app.listeners['before_server_start'], loop) self.app.listeners["before_server_start"], loop
)
try: try:
loop._stopping = False loop._stopping = False
http_server = loop.run_until_complete(_server_co) http_server = loop.run_until_complete(_server_co)
except Exception as e1: except Exception as e1:
import traceback import traceback
traceback.print_tb(e1.__traceback__) traceback.print_tb(e1.__traceback__)
raise e1 raise e1
self._server = _server = http_server self._server = _server = http_server
server.trigger_events( server.trigger_events(self.app.listeners["after_server_start"], loop)
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: if do_kill_server:
try: try:
@ -98,11 +110,11 @@ class ReuseableSanicTestClient(SanicTestClient):
self.app.stop() self.app.stop()
except Exception as e3: except Exception as e3:
import traceback import traceback
traceback.print_tb(e3.__traceback__) traceback.print_tb(e3.__traceback__)
exceptions.append(e3) exceptions.append(e3)
if exceptions: if exceptions:
raise ValueError( raise ValueError("Exception during request: {}".format(exceptions))
"Exception during request: {}".format(exceptions))
if gather_request: if gather_request:
self.app.request_middleware.pop() self.app.request_middleware.pop()
@ -112,28 +124,32 @@ class ReuseableSanicTestClient(SanicTestClient):
except Exception: except Exception:
raise ValueError( raise ValueError(
"Request and response object expected, got ({})".format( "Request and response object expected, got ({})".format(
results)) results
)
)
else: else:
try: try:
return results[-1] return results[-1]
except Exception: except Exception:
raise ValueError( raise ValueError(
"Request object expected, got ({})".format(results)) "Request object expected, got ({})".format(results)
)
# 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, async def _local_request(self, method, uri, cookies=None, *args, **kwargs):
**kwargs): request_keepalive = kwargs.pop(
request_keepalive = kwargs.pop('request_keepalive', "request_keepalive", Config.KEEP_ALIVE_TIMEOUT
Config.KEEP_ALIVE_TIMEOUT) )
if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')): if uri.startswith(("http:", "https:", "ftp:", "ftps://" "//")):
url = uri url = uri
else: else:
url = 'http://{host}:{port}{uri}'.format( url = "http://{host}:{port}{uri}".format(
host=HOST, port=self.port, uri=uri) host=HOST, port=self.port, uri=uri
do_kill_session = kwargs.pop('end_session', False) )
do_kill_session = kwargs.pop("end_session", False)
if self._session: if self._session:
session = self._session session = self._session
else: else:
@ -143,16 +159,17 @@ class ReuseableSanicTestClient(SanicTestClient):
conn = ReuseableTCPConnector( conn = ReuseableTCPConnector(
verify_ssl=False, verify_ssl=False,
loop=self._loop, loop=self._loop,
keepalive_timeout=request_keepalive keepalive_timeout=request_keepalive,
) )
self._tcp_connector = conn self._tcp_connector = conn
session = aiohttp.ClientSession(cookies=cookies, session = aiohttp.ClientSession(
connector=conn, cookies=cookies, connector=conn, loop=self._loop
loop=self._loop) )
self._session = session self._session = session
async with getattr(session, method.lower())( async with getattr(session, method.lower())(
url, *args, **kwargs) as response: url, *args, **kwargs
) as response:
try: try:
response.text = await response.text() response.text = await response.text()
except UnicodeDecodeError: except UnicodeDecodeError:
@ -160,9 +177,11 @@ class ReuseableSanicTestClient(SanicTestClient):
try: try:
response.json = await response.json() response.json = await response.json()
except (JSONDecodeError, except (
UnicodeDecodeError, JSONDecodeError,
aiohttp.ClientResponseError): UnicodeDecodeError,
aiohttp.ClientResponseError,
):
response.json = None response.json = None
response.body = await response.read() response.body = await response.read()
@ -174,24 +193,24 @@ class ReuseableSanicTestClient(SanicTestClient):
Config.KEEP_ALIVE_TIMEOUT = 2 Config.KEEP_ALIVE_TIMEOUT = 2
Config.KEEP_ALIVE = True Config.KEEP_ALIVE = True
keep_alive_timeout_app_reuse = Sanic('test_ka_timeout_reuse') keep_alive_timeout_app_reuse = Sanic("test_ka_timeout_reuse")
keep_alive_app_client_timeout = Sanic('test_ka_client_timeout') keep_alive_app_client_timeout = Sanic("test_ka_client_timeout")
keep_alive_app_server_timeout = Sanic('test_ka_server_timeout') keep_alive_app_server_timeout = Sanic("test_ka_server_timeout")
@keep_alive_timeout_app_reuse.route('/1') @keep_alive_timeout_app_reuse.route("/1")
async def handler1(request): async def handler1(request):
return text('OK') return text("OK")
@keep_alive_app_client_timeout.route('/1') @keep_alive_app_client_timeout.route("/1")
async def handler2(request): async def handler2(request):
return text('OK') return text("OK")
@keep_alive_app_server_timeout.route('/1') @keep_alive_app_server_timeout.route("/1")
async def handler3(request): async def handler3(request):
return text('OK') return text("OK")
def test_keep_alive_timeout_reuse(): def test_keep_alive_timeout_reuse():
@ -201,16 +220,14 @@ def test_keep_alive_timeout_reuse():
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
client = ReuseableSanicTestClient(keep_alive_timeout_app_reuse, loop) client = ReuseableSanicTestClient(keep_alive_timeout_app_reuse, loop)
headers = { headers = {"Connection": "keep-alive"}
'Connection': 'keep-alive' request, response = client.get("/1", headers=headers)
}
request, response = client.get('/1', headers=headers)
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", end_server=True)
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
def test_keep_alive_client_timeout(): def test_keep_alive_client_timeout():
@ -218,20 +235,17 @@ def test_keep_alive_client_timeout():
keep-alive timeout, client will try to create a new connection here.""" keep-alive timeout, client will try to create a new connection here."""
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
client = ReuseableSanicTestClient(keep_alive_app_client_timeout, client = ReuseableSanicTestClient(keep_alive_app_client_timeout, loop)
loop) headers = {"Connection": "keep-alive"}
headers = { request, response = client.get("/1", headers=headers, request_keepalive=1)
'Connection': 'keep-alive'
}
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: try:
request, response = client.get('/1', end_server=True, request, response = client.get(
request_keepalive=1) "/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
@ -246,23 +260,22 @@ def test_keep_alive_server_timeout():
broken server connection.""" broken server connection."""
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
client = ReuseableSanicTestClient(keep_alive_app_server_timeout, client = ReuseableSanicTestClient(keep_alive_app_server_timeout, loop)
loop) headers = {"Connection": "keep-alive"}
headers = { request, response = client.get("/1", headers=headers, request_keepalive=60)
'Connection': 'keep-alive'
}
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: try:
request, response = client.get('/1', request_keepalive=60, request, response = client.get(
end_server=True) "/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
assert isinstance(exception, ValueError) assert isinstance(exception, ValueError)
assert "Connection reset" in exception.args[0] or \ assert (
"got a new connection" in exception.args[0] "Connection reset" in exception.args[0]
or "got a new connection" in exception.args[0]
)

View File

@ -14,9 +14,9 @@ from sanic import Sanic
from sanic.log import logger from sanic.log import logger
logging_format = '''module: %(module)s; \ logging_format = """module: %(module)s; \
function: %(funcName)s(); \ function: %(funcName)s(); \
message: %(message)s''' message: %(message)s"""
def reset_logging(): def reset_logging():
@ -29,19 +29,17 @@ def test_log(app):
for handler in logging.root.handlers[:]: for handler in logging.root.handlers[:]:
logging.root.removeHandler(handler) logging.root.removeHandler(handler)
logging.basicConfig( logging.basicConfig(
format=logging_format, format=logging_format, level=logging.DEBUG, stream=log_stream
level=logging.DEBUG,
stream=log_stream
) )
log = logging.getLogger() log = logging.getLogger()
rand_string = str(uuid.uuid4()) rand_string = str(uuid.uuid4())
@app.route('/') @app.route("/")
def handler(request): def handler(request):
log.info(rand_string) log.info(rand_string)
return text('hello') return text("hello")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
log_text = log_stream.getvalue() log_text = log_stream.getvalue()
assert rand_string in log_text assert rand_string in log_text
@ -50,59 +48,80 @@ def test_logging_defaults():
# reset_logging() # reset_logging()
app = Sanic("test_logging") app = Sanic("test_logging")
for fmt in [h.formatter for h in logging.getLogger('sanic.root').handlers]: for fmt in [h.formatter for h in logging.getLogger("sanic.root").handlers]:
assert fmt._fmt == LOGGING_CONFIG_DEFAULTS['formatters']['generic']['format'] assert (
fmt._fmt
== LOGGING_CONFIG_DEFAULTS["formatters"]["generic"]["format"]
)
for fmt in [h.formatter for h in logging.getLogger('sanic.error').handlers]: for fmt in [
assert fmt._fmt == LOGGING_CONFIG_DEFAULTS['formatters']['generic']['format'] h.formatter for h in logging.getLogger("sanic.error").handlers
]:
assert (
fmt._fmt
== LOGGING_CONFIG_DEFAULTS["formatters"]["generic"]["format"]
)
for fmt in [h.formatter for h in logging.getLogger('sanic.access').handlers]: for fmt in [
assert fmt._fmt == LOGGING_CONFIG_DEFAULTS['formatters']['access']['format'] h.formatter for h in logging.getLogger("sanic.access").handlers
]:
assert (
fmt._fmt
== LOGGING_CONFIG_DEFAULTS["formatters"]["access"]["format"]
)
def test_logging_pass_customer_logconfig(): def test_logging_pass_customer_logconfig():
# reset_logging() # reset_logging()
modified_config = LOGGING_CONFIG_DEFAULTS modified_config = LOGGING_CONFIG_DEFAULTS
modified_config['formatters']['generic']['format'] = '%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s' modified_config["formatters"]["generic"][
modified_config['formatters']['access']['format'] = '%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s' "format"
] = "%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s"
modified_config["formatters"]["access"][
"format"
] = "%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s"
app = Sanic("test_logging", log_config=modified_config) app = Sanic("test_logging", log_config=modified_config)
for fmt in [h.formatter for h in logging.getLogger('sanic.root').handlers]: for fmt in [h.formatter for h in logging.getLogger("sanic.root").handlers]:
assert fmt._fmt == modified_config['formatters']['generic']['format'] assert fmt._fmt == modified_config["formatters"]["generic"]["format"]
for fmt in [h.formatter for h in logging.getLogger('sanic.error').handlers]: for fmt in [
assert fmt._fmt == modified_config['formatters']['generic']['format'] h.formatter for h in logging.getLogger("sanic.error").handlers
]:
assert fmt._fmt == modified_config["formatters"]["generic"]["format"]
for fmt in [h.formatter for h in logging.getLogger('sanic.access').handlers]: for fmt in [
assert fmt._fmt == modified_config['formatters']['access']['format'] h.formatter for h in logging.getLogger("sanic.access").handlers
]:
assert fmt._fmt == modified_config["formatters"]["access"]["format"]
@pytest.mark.parametrize('debug', (True, False, )) @pytest.mark.parametrize("debug", (True, False))
def test_log_connection_lost(app, debug, monkeypatch): def test_log_connection_lost(app, debug, monkeypatch):
""" Should not log Connection lost exception on non debug """ """ Should not log Connection lost exception on non debug """
stream = StringIO() stream = StringIO()
root = logging.getLogger('sanic.root') root = logging.getLogger("sanic.root")
root.addHandler(logging.StreamHandler(stream)) root.addHandler(logging.StreamHandler(stream))
monkeypatch.setattr(sanic.server, 'logger', root) monkeypatch.setattr(sanic.server, "logger", root)
@app.route('/conn_lost') @app.route("/conn_lost")
async def conn_lost(request): async def conn_lost(request):
response = text('Ok') response = text("Ok")
response.output = Mock(side_effect=RuntimeError) response.output = Mock(side_effect=RuntimeError)
return response return response
with pytest.raises(ValueError): with pytest.raises(ValueError):
# catch ValueError: Exception during request # catch ValueError: Exception during request
app.test_client.get('/conn_lost', debug=debug) app.test_client.get("/conn_lost", debug=debug)
log = stream.getvalue() log = stream.getvalue()
if debug: if debug:
assert 'Connection lost before response written @' in log assert "Connection lost before response written @" in log
else: else:
assert 'Connection lost before response written @' not in log assert "Connection lost before response written @" not in log
def test_logger(caplog): def test_logger(caplog):
@ -110,26 +129,38 @@ def test_logger(caplog):
app = Sanic() app = Sanic()
@app.get('/') @app.get("/")
def log_info(request): def log_info(request):
logger.info(rand_string) logger.info(rand_string)
return text('hello') return text("hello")
with caplog.at_level(logging.INFO): with caplog.at_level(logging.INFO):
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert caplog.record_tuples[0] == ('sanic.root', logging.INFO, 'Goin\' Fast @ http://127.0.0.1:42101') assert caplog.record_tuples[0] == (
assert caplog.record_tuples[1] == ('sanic.root', logging.INFO, 'http://127.0.0.1:42101/') "sanic.root",
assert caplog.record_tuples[2] == ('sanic.root', logging.INFO, rand_string) logging.INFO,
assert caplog.record_tuples[-1] == ('sanic.root', logging.INFO, 'Server Stopped') "Goin' Fast @ http://127.0.0.1:42101",
)
assert caplog.record_tuples[1] == (
"sanic.root",
logging.INFO,
"http://127.0.0.1:42101/",
)
assert caplog.record_tuples[2] == ("sanic.root", logging.INFO, rand_string)
assert caplog.record_tuples[-1] == (
"sanic.root",
logging.INFO,
"Server Stopped",
)
def test_logging_modified_root_logger_config(): def test_logging_modified_root_logger_config():
# reset_logging() # reset_logging()
modified_config = LOGGING_CONFIG_DEFAULTS modified_config = LOGGING_CONFIG_DEFAULTS
modified_config['loggers']['sanic.root']['level'] = 'DEBUG' modified_config["loggers"]["sanic.root"]["level"] = "DEBUG"
app = Sanic("test_logging", log_config=modified_config) app = Sanic("test_logging", log_config=modified_config)
assert logging.getLogger('sanic.root').getEffectiveLevel() == logging.DEBUG assert logging.getLogger("sanic.root").getEffectiveLevel() == logging.DEBUG

84
tests/test_logo.py Normal file
View File

@ -0,0 +1,84 @@
import logging
import asyncio
from sanic.config import BASE_LOGO
try:
import uvloop # noqa
ROW = 0
except BaseException:
ROW = 1
def test_logo_base(app, caplog):
server = app.create_server(debug=True)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop._stopping = False
with caplog.at_level(logging.DEBUG):
_server = loop.run_until_complete(server)
_server.close()
loop.run_until_complete(_server.wait_closed())
app.stop()
assert caplog.record_tuples[ROW][1] == logging.DEBUG
assert caplog.record_tuples[ROW][2] == BASE_LOGO
def test_logo_false(app, caplog):
app.config.LOGO = False
server = app.create_server(debug=True)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop._stopping = False
with caplog.at_level(logging.DEBUG):
_server = loop.run_until_complete(server)
_server.close()
loop.run_until_complete(_server.wait_closed())
app.stop()
assert caplog.record_tuples[ROW][1] == logging.INFO
assert caplog.record_tuples[ROW][2] == "Goin' Fast @ http://127.0.0.1:8000"
def test_logo_true(app, caplog):
app.config.LOGO = True
server = app.create_server(debug=True)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop._stopping = False
with caplog.at_level(logging.DEBUG):
_server = loop.run_until_complete(server)
_server.close()
loop.run_until_complete(_server.wait_closed())
app.stop()
assert caplog.record_tuples[ROW][1] == logging.DEBUG
assert caplog.record_tuples[ROW][2] == BASE_LOGO
def test_logo_custom(app, caplog):
app.config.LOGO = "My Custom Logo"
server = app.create_server(debug=True)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop._stopping = False
with caplog.at_level(logging.DEBUG):
_server = loop.run_until_complete(server)
_server.close()
loop.run_until_complete(_server.wait_closed())
app.stop()
assert caplog.record_tuples[ROW][1] == logging.DEBUG
assert caplog.record_tuples[ROW][2] == "My Custom Logo"

View File

@ -9,6 +9,7 @@ from sanic.response import HTTPResponse, text
# GET # GET
# ------------------------------------------------------------ # # ------------------------------------------------------------ #
def test_middleware_request(app): def test_middleware_request(app):
results = [] results = []
@ -16,168 +17,164 @@ def test_middleware_request(app):
async def handler1(request): async def handler1(request):
results.append(request) results.append(request)
@app.route('/') @app.route("/")
async def handler2(request): async def handler2(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'OK' assert response.text == "OK"
assert type(results[0]) is Request assert type(results[0]) is Request
def test_middleware_response(app): def test_middleware_response(app):
results = [] results = []
@app.middleware('request') @app.middleware("request")
async def process_request(request): async def process_request(request):
results.append(request) results.append(request)
@app.middleware('response') @app.middleware("response")
async def process_response(request, response): async def process_response(request, response):
results.append(request) results.append(request)
results.append(response) results.append(response)
@app.route('/') @app.route("/")
async def handler(request): async def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'OK' assert response.text == "OK"
assert type(results[0]) is Request assert type(results[0]) is Request
assert type(results[1]) is Request assert type(results[1]) is Request
assert isinstance(results[2], HTTPResponse) assert isinstance(results[2], HTTPResponse)
def test_middleware_response_exception(app): def test_middleware_response_exception(app):
result = {'status_code': None} result = {"status_code": None}
@app.middleware('response') @app.middleware("response")
async def process_response(request, response): async def process_response(request, response):
result['status_code'] = response.status result["status_code"] = response.status
return response return response
@app.exception(NotFound) @app.exception(NotFound)
async def error_handler(request, exception): async def error_handler(request, exception):
return text('OK', exception.status_code) return text("OK", exception.status_code)
@app.route('/') @app.route("/")
async def handler(request): async def handler(request):
return text('FAIL') return text("FAIL")
request, response = app.test_client.get('/page_not_found') request, response = app.test_client.get("/page_not_found")
assert response.text == 'OK' assert response.text == "OK"
assert result['status_code'] == 404 assert result["status_code"] == 404
def test_middleware_response_raise_cancelled_error(app, caplog): def test_middleware_response_raise_cancelled_error(app, caplog):
@app.middleware("response")
@app.middleware('response')
async def process_response(request, response): async def process_response(request, response):
raise CancelledError('CancelledError at response middleware') raise CancelledError("CancelledError at response middleware")
@app.get('/') @app.get("/")
def handler(request): def handler(request):
return text('OK') return text("OK")
with caplog.at_level(logging.ERROR): with caplog.at_level(logging.ERROR):
reqrequest, response = app.test_client.get('/') reqrequest, response = app.test_client.get("/")
assert response.status == 503 assert response.status == 503
assert caplog.record_tuples[0] == ( assert caplog.record_tuples[0] == (
'sanic.root', "sanic.root",
logging.ERROR, logging.ERROR,
'Exception occurred while handling uri: \'http://127.0.0.1:42101/\'' "Exception occurred while handling uri: 'http://127.0.0.1:42101/'",
) )
def test_middleware_response_raise_exception(app, caplog): def test_middleware_response_raise_exception(app, caplog):
@app.middleware("response")
@app.middleware('response')
async def process_response(request, response): async def process_response(request, response):
raise Exception('Exception at response middleware') raise Exception("Exception at response middleware")
with caplog.at_level(logging.ERROR): with caplog.at_level(logging.ERROR):
reqrequest, response = app.test_client.get('/') reqrequest, response = app.test_client.get("/")
assert response.status == 404 assert response.status == 404
assert caplog.record_tuples[0] == ( assert caplog.record_tuples[0] == (
'sanic.root', "sanic.root",
logging.ERROR, logging.ERROR,
'Exception occurred while handling uri: \'http://127.0.0.1:42101/\'' "Exception occurred while handling uri: 'http://127.0.0.1:42101/'",
) )
assert caplog.record_tuples[1] == ( assert caplog.record_tuples[1] == (
'sanic.error', "sanic.error",
logging.ERROR, logging.ERROR,
'Exception occurred in one of response middleware handlers' "Exception occurred in one of response middleware handlers",
) )
def test_middleware_override_request(app): def test_middleware_override_request(app):
@app.middleware @app.middleware
async def halt_request(request): async def halt_request(request):
return text('OK') return text("OK")
@app.route('/') @app.route("/")
async def handler(request): async def handler(request):
return text('FAIL') return text("FAIL")
response = app.test_client.get('/', gather_request=False) response = app.test_client.get("/", gather_request=False)
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
def test_middleware_override_response(app): def test_middleware_override_response(app):
@app.middleware("response")
@app.middleware('response')
async def process_response(request, response): async def process_response(request, response):
return text('OK') return text("OK")
@app.route('/') @app.route("/")
async def handler(request): async def handler(request):
return text('FAIL') return text("FAIL")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
def test_middleware_order(app): def test_middleware_order(app):
order = [] order = []
@app.middleware('request') @app.middleware("request")
async def request1(request): async def request1(request):
order.append(1) order.append(1)
@app.middleware('request') @app.middleware("request")
async def request2(request): async def request2(request):
order.append(2) order.append(2)
@app.middleware('request') @app.middleware("request")
async def request3(request): async def request3(request):
order.append(3) order.append(3)
@app.middleware('response') @app.middleware("response")
async def response1(request, response): async def response1(request, response):
order.append(6) order.append(6)
@app.middleware('response') @app.middleware("response")
async def response2(request, response): async def response2(request, response):
order.append(5) order.append(5)
@app.middleware('response') @app.middleware("response")
async def response3(request, response): async def response3(request, response):
order.append(4) order.append(4)
@app.route('/') @app.route("/")
async def handler(request): async def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.status == 200 assert response.status == 200
assert order == [1, 2, 3, 4, 5, 6] assert order == [1, 2, 3, 4, 5, 6]

View File

@ -9,9 +9,9 @@ from sanic.response import text
@pytest.mark.skipif( @pytest.mark.skipif(
not hasattr(signal, 'SIGALRM'), not hasattr(signal, "SIGALRM"),
reason='SIGALRM is not implemented for this platform, we have to come ' reason="SIGALRM is not implemented for this platform, we have to come "
'up with another timeout strategy to test these' "up with another timeout strategy to test these",
) )
def test_multiprocessing(app): def test_multiprocessing(app):
"""Tests that the number of children we produce is correct""" """Tests that the number of children we produce is correct"""
@ -32,11 +32,12 @@ def test_multiprocessing(app):
@pytest.mark.skipif( @pytest.mark.skipif(
not hasattr(signal, 'SIGALRM'), not hasattr(signal, "SIGALRM"),
reason='SIGALRM is not implemented for this platform', reason="SIGALRM is not implemented for this platform",
) )
def test_multiprocessing_with_blueprint(app): def test_multiprocessing_with_blueprint(app):
from sanic import Blueprint from sanic import Blueprint
# Selects a number at random so we can spot check # Selects a number at random so we can spot check
num_workers = random.choice(range(2, multiprocessing.cpu_count() * 2 + 1)) num_workers = random.choice(range(2, multiprocessing.cpu_count() * 2 + 1))
process_list = set() process_list = set()
@ -49,7 +50,7 @@ def test_multiprocessing_with_blueprint(app):
signal.signal(signal.SIGALRM, stop_on_alarm) signal.signal(signal.SIGALRM, stop_on_alarm)
signal.alarm(3) signal.alarm(3)
bp = Blueprint('test_text') bp = Blueprint("test_text")
app.blueprint(bp) app.blueprint(bp)
app.run(HOST, PORT, workers=num_workers) app.run(HOST, PORT, workers=num_workers)
@ -59,28 +60,30 @@ def test_multiprocessing_with_blueprint(app):
# this function must be outside a test function so that it can be # this function must be outside a test function so that it can be
# able to be pickled (local functions cannot be pickled). # able to be pickled (local functions cannot be pickled).
def handler(request): def handler(request):
return text('Hello') return text("Hello")
# Muliprocessing on Windows requires app to be able to be pickled # Muliprocessing on Windows requires app to be able to be pickled
@pytest.mark.parametrize('protocol', [3, 4]) @pytest.mark.parametrize("protocol", [3, 4])
def test_pickle_app(app, protocol): def test_pickle_app(app, protocol):
app.route('/')(handler) app.route("/")(handler)
p_app = pickle.dumps(app, protocol=protocol) p_app = pickle.dumps(app, protocol=protocol)
up_p_app = pickle.loads(p_app) up_p_app = pickle.loads(p_app)
assert up_p_app assert up_p_app
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'Hello' assert response.text == "Hello"
@pytest.mark.parametrize('protocol', [3, 4]) @pytest.mark.parametrize("protocol", [3, 4])
def test_pickle_app_with_bp(app, protocol): def test_pickle_app_with_bp(app, protocol):
from sanic import Blueprint from sanic import Blueprint
bp = Blueprint('test_text')
bp.route('/')(handler) bp = Blueprint("test_text")
bp.route("/")(handler)
app.blueprint(bp) app.blueprint(bp)
p_app = pickle.dumps(app, protocol=protocol) p_app = pickle.dumps(app, protocol=protocol)
up_p_app = pickle.loads(p_app) up_p_app = pickle.loads(p_app)
assert up_p_app assert up_p_app
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert app.is_request_stream is False assert app.is_request_stream is False
assert response.text == 'Hello' assert response.text == "Hello"

View File

@ -14,28 +14,32 @@ from sanic.constants import HTTP_METHODS
# UTF-8 # UTF-8
# ------------------------------------------------------------ # # ------------------------------------------------------------ #
@pytest.mark.parametrize('method', HTTP_METHODS)
@pytest.mark.parametrize("method", HTTP_METHODS)
def test_versioned_named_routes_get(app, method): def test_versioned_named_routes_get(app, method):
bp = Blueprint('test_bp', url_prefix='/bp') bp = Blueprint("test_bp", url_prefix="/bp")
method = method.lower() method = method.lower()
route_name = 'route_{}'.format(method) route_name = "route_{}".format(method)
route_name2 = 'route2_{}'.format(method) route_name2 = "route2_{}".format(method)
func = getattr(app, method) func = getattr(app, method)
if callable(func): if callable(func):
@func('/{}'.format(method), version=1, name=route_name)
@func("/{}".format(method), version=1, name=route_name)
def handler(request): def handler(request):
return text('OK') return text("OK")
else: else:
print(func) print(func)
raise raise
func = getattr(bp, method) func = getattr(bp, method)
if callable(func): if callable(func):
@func('/{}'.format(method), version=1, name=route_name2)
@func("/{}".format(method), version=1, name=route_name2)
def handler2(request): def handler2(request):
return text('OK') return text("OK")
else: else:
print(func) print(func)
@ -43,261 +47,250 @@ def test_versioned_named_routes_get(app, method):
app.blueprint(bp) app.blueprint(bp)
assert app.router.routes_all['/v1/{}'.format(method)].name == route_name assert app.router.routes_all["/v1/{}".format(method)].name == route_name
route = app.router.routes_all['/v1/bp/{}'.format(method)] route = app.router.routes_all["/v1/bp/{}".format(method)]
assert route.name == 'test_bp.{}'.format(route_name2) assert route.name == "test_bp.{}".format(route_name2)
assert app.url_for(route_name) == '/v1/{}'.format(method) assert app.url_for(route_name) == "/v1/{}".format(method)
url = app.url_for('test_bp.{}'.format(route_name2)) url = app.url_for("test_bp.{}".format(route_name2))
assert url == '/v1/bp/{}'.format(method) assert url == "/v1/bp/{}".format(method)
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_shorthand_default_routes_get(app): def test_shorthand_default_routes_get(app):
@app.get("/get")
@app.get('/get')
def handler(request): def handler(request):
return text('OK') return text("OK")
assert app.router.routes_all['/get'].name == 'handler' assert app.router.routes_all["/get"].name == "handler"
assert app.url_for('handler') == '/get' assert app.url_for("handler") == "/get"
def test_shorthand_named_routes_get(app): def test_shorthand_named_routes_get(app):
bp = Blueprint('test_bp', url_prefix='/bp') bp = Blueprint("test_bp", url_prefix="/bp")
@app.get('/get', name='route_get') @app.get("/get", name="route_get")
def handler(request): def handler(request):
return text('OK') return text("OK")
@bp.get('/get', name='route_bp') @bp.get("/get", name="route_bp")
def handler2(request): def handler2(request):
return text('Blueprint') return text("Blueprint")
app.blueprint(bp) app.blueprint(bp)
assert app.router.routes_all['/get'].name == 'route_get' assert app.router.routes_all["/get"].name == "route_get"
assert app.url_for('route_get') == '/get' assert app.url_for("route_get") == "/get"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
assert app.router.routes_all['/bp/get'].name == 'test_bp.route_bp' assert app.router.routes_all["/bp/get"].name == "test_bp.route_bp"
assert app.url_for('test_bp.route_bp') == '/bp/get' assert app.url_for("test_bp.route_bp") == "/bp/get"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('test_bp.handler2') app.url_for("test_bp.handler2")
def test_shorthand_named_routes_post(app): def test_shorthand_named_routes_post(app):
@app.post("/post", name="route_name")
@app.post('/post', name='route_name')
def handler(request): def handler(request):
return text('OK') return text("OK")
assert app.router.routes_all['/post'].name == 'route_name' assert app.router.routes_all["/post"].name == "route_name"
assert app.url_for('route_name') == '/post' assert app.url_for("route_name") == "/post"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_shorthand_named_routes_put(app): def test_shorthand_named_routes_put(app):
@app.put("/put", name="route_put")
@app.put('/put', name='route_put')
def handler(request): def handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
assert app.is_request_stream is False assert app.is_request_stream is False
assert app.router.routes_all['/put'].name == 'route_put' assert app.router.routes_all["/put"].name == "route_put"
assert app.url_for('route_put') == '/put' assert app.url_for("route_put") == "/put"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_shorthand_named_routes_delete(app): def test_shorthand_named_routes_delete(app):
@app.delete("/delete", name="route_delete")
@app.delete('/delete', name='route_delete')
def handler(request): def handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
assert app.is_request_stream is False assert app.is_request_stream is False
assert app.router.routes_all['/delete'].name == 'route_delete' assert app.router.routes_all["/delete"].name == "route_delete"
assert app.url_for('route_delete') == '/delete' assert app.url_for("route_delete") == "/delete"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_shorthand_named_routes_patch(app): def test_shorthand_named_routes_patch(app):
@app.patch("/patch", name="route_patch")
@app.patch('/patch', name='route_patch')
def handler(request): def handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
assert app.is_request_stream is False assert app.is_request_stream is False
assert app.router.routes_all['/patch'].name == 'route_patch' assert app.router.routes_all["/patch"].name == "route_patch"
assert app.url_for('route_patch') == '/patch' assert app.url_for("route_patch") == "/patch"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_shorthand_named_routes_head(app): def test_shorthand_named_routes_head(app):
@app.head("/head", name="route_head")
@app.head('/head', name='route_head')
def handler(request): def handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
assert app.is_request_stream is False assert app.is_request_stream is False
assert app.router.routes_all['/head'].name == 'route_head' assert app.router.routes_all["/head"].name == "route_head"
assert app.url_for('route_head') == '/head' assert app.url_for("route_head") == "/head"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_shorthand_named_routes_options(app): def test_shorthand_named_routes_options(app):
@app.options("/options", name="route_options")
@app.options('/options', name='route_options')
def handler(request): def handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
assert app.is_request_stream is False assert app.is_request_stream is False
assert app.router.routes_all['/options'].name == 'route_options' assert app.router.routes_all["/options"].name == "route_options"
assert app.url_for('route_options') == '/options' assert app.url_for("route_options") == "/options"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_named_static_routes(app): def test_named_static_routes(app):
@app.route("/test", name="route_test")
@app.route('/test', name='route_test')
async def handler1(request): async def handler1(request):
return text('OK1') return text("OK1")
@app.route('/pizazz', name='route_pizazz') @app.route("/pizazz", name="route_pizazz")
async def handler2(request): async def handler2(request):
return text('OK2') return text("OK2")
assert app.router.routes_all['/test'].name == 'route_test' assert app.router.routes_all["/test"].name == "route_test"
assert app.router.routes_static['/test'].name == 'route_test' assert app.router.routes_static["/test"].name == "route_test"
assert app.url_for('route_test') == '/test' assert app.url_for("route_test") == "/test"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler1') app.url_for("handler1")
assert app.router.routes_all['/pizazz'].name == 'route_pizazz' assert app.router.routes_all["/pizazz"].name == "route_pizazz"
assert app.router.routes_static['/pizazz'].name == 'route_pizazz' assert app.router.routes_static["/pizazz"].name == "route_pizazz"
assert app.url_for('route_pizazz') == '/pizazz' assert app.url_for("route_pizazz") == "/pizazz"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler2') app.url_for("handler2")
def test_named_dynamic_route(app): def test_named_dynamic_route(app):
results = [] results = []
@app.route('/folder/<name>', name='route_dynamic') @app.route("/folder/<name>", name="route_dynamic")
async def handler(request, name): async def handler(request, name):
results.append(name) results.append(name)
return text('OK') return text("OK")
assert app.router.routes_all['/folder/<name>'].name == 'route_dynamic' assert app.router.routes_all["/folder/<name>"].name == "route_dynamic"
assert app.url_for('route_dynamic', name='test') == '/folder/test' assert app.url_for("route_dynamic", name="test") == "/folder/test"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_dynamic_named_route_regex(app): def test_dynamic_named_route_regex(app):
@app.route("/folder/<folder_id:[A-Za-z0-9]{0,4}>", name="route_re")
@app.route('/folder/<folder_id:[A-Za-z0-9]{0,4}>', name='route_re')
async def handler(request, folder_id): async def handler(request, folder_id):
return text('OK') return text("OK")
route = app.router.routes_all['/folder/<folder_id:[A-Za-z0-9]{0,4}>'] route = app.router.routes_all["/folder/<folder_id:[A-Za-z0-9]{0,4}>"]
assert route.name == 'route_re' assert route.name == "route_re"
assert app.url_for('route_re', folder_id='test') == '/folder/test' assert app.url_for("route_re", folder_id="test") == "/folder/test"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_dynamic_named_route_path(app): def test_dynamic_named_route_path(app):
@app.route("/<path:path>/info", name="route_dynamic_path")
@app.route('/<path:path>/info', name='route_dynamic_path')
async def handler(request, path): async def handler(request, path):
return text('OK') return text("OK")
route = app.router.routes_all['/<path:path>/info'] route = app.router.routes_all["/<path:path>/info"]
assert route.name == 'route_dynamic_path' assert route.name == "route_dynamic_path"
assert app.url_for('route_dynamic_path', path='path/1') == '/path/1/info' assert app.url_for("route_dynamic_path", path="path/1") == "/path/1/info"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_dynamic_named_route_unhashable(app): def test_dynamic_named_route_unhashable(app):
@app.route(
@app.route('/folder/<unhashable:[A-Za-z0-9/]+>/end/', "/folder/<unhashable:[A-Za-z0-9/]+>/end/", name="route_unhashable"
name='route_unhashable') )
async def handler(request, unhashable): async def handler(request, unhashable):
return text('OK') return text("OK")
route = app.router.routes_all['/folder/<unhashable:[A-Za-z0-9/]+>/end/'] route = app.router.routes_all["/folder/<unhashable:[A-Za-z0-9/]+>/end/"]
assert route.name == 'route_unhashable' assert route.name == "route_unhashable"
url = app.url_for('route_unhashable', unhashable='test/asdf') url = app.url_for("route_unhashable", unhashable="test/asdf")
assert url == '/folder/test/asdf/end' assert url == "/folder/test/asdf/end"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_websocket_named_route(app): def test_websocket_named_route(app):
ev = asyncio.Event() ev = asyncio.Event()
@app.websocket('/ws', name='route_ws') @app.websocket("/ws", name="route_ws")
async def handler(request, ws): async def handler(request, ws):
assert ws.subprotocol is None assert ws.subprotocol is None
ev.set() ev.set()
assert app.router.routes_all['/ws'].name == 'route_ws' assert app.router.routes_all["/ws"].name == "route_ws"
assert app.url_for('route_ws') == '/ws' assert app.url_for("route_ws") == "/ws"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_websocket_named_route_with_subprotocols(app): def test_websocket_named_route_with_subprotocols(app):
results = [] results = []
@app.websocket('/ws', subprotocols=['foo', 'bar'], name='route_ws') @app.websocket("/ws", subprotocols=["foo", "bar"], name="route_ws")
async def handler(request, ws): async def handler(request, ws):
results.append(ws.subprotocol) results.append(ws.subprotocol)
assert app.router.routes_all['/ws'].name == 'route_ws' assert app.router.routes_all["/ws"].name == "route_ws"
assert app.url_for('route_ws') == '/ws' assert app.url_for("route_ws") == "/ws"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_static_add_named_route(app): def test_static_add_named_route(app):
async def handler1(request): async def handler1(request):
return text('OK1') return text("OK1")
async def handler2(request): async def handler2(request):
return text('OK2') return text("OK2")
app.add_route(handler1, '/test', name='route_test') app.add_route(handler1, "/test", name="route_test")
app.add_route(handler2, '/test2', name='route_test2') app.add_route(handler2, "/test2", name="route_test2")
assert app.router.routes_all['/test'].name == 'route_test' assert app.router.routes_all["/test"].name == "route_test"
assert app.router.routes_static['/test'].name == 'route_test' assert app.router.routes_static["/test"].name == "route_test"
assert app.url_for('route_test') == '/test' assert app.url_for("route_test") == "/test"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler1') app.url_for("handler1")
assert app.router.routes_all['/test2'].name == 'route_test2' assert app.router.routes_all["/test2"].name == "route_test2"
assert app.router.routes_static['/test2'].name == 'route_test2' assert app.router.routes_static["/test2"].name == "route_test2"
assert app.url_for('route_test2') == '/test2' assert app.url_for("route_test2") == "/test2"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler2') app.url_for("handler2")
def test_dynamic_add_named_route(app): def test_dynamic_add_named_route(app):
@ -305,61 +298,62 @@ def test_dynamic_add_named_route(app):
async def handler(request, name): async def handler(request, name):
results.append(name) results.append(name)
return text('OK') return text("OK")
app.add_route(handler, '/folder/<name>', name='route_dynamic') app.add_route(handler, "/folder/<name>", name="route_dynamic")
assert app.router.routes_all['/folder/<name>'].name == 'route_dynamic' assert app.router.routes_all["/folder/<name>"].name == "route_dynamic"
assert app.url_for('route_dynamic', name='test') == '/folder/test' assert app.url_for("route_dynamic", name="test") == "/folder/test"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_dynamic_add_named_route_unhashable(app): def test_dynamic_add_named_route_unhashable(app):
async def handler(request, unhashable): async def handler(request, unhashable):
return text('OK') return text("OK")
app.add_route(handler, '/folder/<unhashable:[A-Za-z0-9/]+>/end/', app.add_route(
name='route_unhashable') handler,
route = app.router.routes_all['/folder/<unhashable:[A-Za-z0-9/]+>/end/'] "/folder/<unhashable:[A-Za-z0-9/]+>/end/",
assert route.name == 'route_unhashable' name="route_unhashable",
url = app.url_for('route_unhashable', unhashable='folder1') )
assert url == '/folder/folder1/end' route = app.router.routes_all["/folder/<unhashable:[A-Za-z0-9/]+>/end/"]
assert route.name == "route_unhashable"
url = app.url_for("route_unhashable", unhashable="folder1")
assert url == "/folder/folder1/end"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler') app.url_for("handler")
def test_overload_routes(app): def test_overload_routes(app):
@app.route("/overload", methods=["GET"], name="route_first")
@app.route('/overload', methods=['GET'], name='route_first')
async def handler1(request): async def handler1(request):
return text('OK1') return text("OK1")
@app.route('/overload', methods=['POST', 'PUT'], name='route_second') @app.route("/overload", methods=["POST", "PUT"], name="route_second")
async def handler2(request): async def handler2(request):
return text('OK2') return text("OK2")
request, response = app.test_client.get(app.url_for('route_first')) request, response = app.test_client.get(app.url_for("route_first"))
assert response.text == 'OK1' assert response.text == "OK1"
request, response = app.test_client.post(app.url_for('route_first')) request, response = app.test_client.post(app.url_for("route_first"))
assert response.text == 'OK2' assert response.text == "OK2"
request, response = app.test_client.put(app.url_for('route_first')) request, response = app.test_client.put(app.url_for("route_first"))
assert response.text == 'OK2' assert response.text == "OK2"
request, response = app.test_client.get(app.url_for('route_second')) request, response = app.test_client.get(app.url_for("route_second"))
assert response.text == 'OK1' assert response.text == "OK1"
request, response = app.test_client.post(app.url_for('route_second')) request, response = app.test_client.post(app.url_for("route_second"))
assert response.text == 'OK2' assert response.text == "OK2"
request, response = app.test_client.put(app.url_for('route_second')) request, response = app.test_client.put(app.url_for("route_second"))
assert response.text == 'OK2' assert response.text == "OK2"
assert app.router.routes_all['/overload'].name == 'route_first' assert app.router.routes_all["/overload"].name == "route_first"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
app.url_for('handler1') app.url_for("handler1")
assert app.url_for('route_first') == '/overload' assert app.url_for("route_first") == "/overload"
assert app.url_for('route_second') == app.url_for('route_first') assert app.url_for("route_second") == app.url_for("route_first")

View File

@ -5,41 +5,39 @@ from sanic.response import text
def test_payload_too_large_from_error_handler(app): def test_payload_too_large_from_error_handler(app):
app.config.REQUEST_MAX_SIZE = 1 app.config.REQUEST_MAX_SIZE = 1
@app.route('/1') @app.route("/1")
async def handler1(request): async def handler1(request):
return text('OK') return text("OK")
@app.exception(PayloadTooLarge) @app.exception(PayloadTooLarge)
def handler_exception(request, exception): def handler_exception(request, exception):
return text('Payload Too Large from error_handler.', 413) return text("Payload Too Large from error_handler.", 413)
response = app.test_client.get('/1', gather_request=False) response = app.test_client.get("/1", gather_request=False)
assert response.status == 413 assert response.status == 413
assert response.text == 'Payload Too Large from error_handler.' assert response.text == "Payload Too Large from error_handler."
def test_payload_too_large_at_data_received_default(app): def test_payload_too_large_at_data_received_default(app):
app.config.REQUEST_MAX_SIZE = 1 app.config.REQUEST_MAX_SIZE = 1
@app.route('/1') @app.route("/1")
async def handler2(request): async def handler2(request):
return text('OK') return text("OK")
response = app.test_client.get( response = app.test_client.get("/1", gather_request=False)
'/1', gather_request=False)
assert response.status == 413 assert response.status == 413
assert response.text == 'Error: Payload Too Large' assert response.text == "Error: Payload Too Large"
def test_payload_too_large_at_on_header_default(app): def test_payload_too_large_at_on_header_default(app):
app.config.REQUEST_MAX_SIZE = 500 app.config.REQUEST_MAX_SIZE = 500
@app.post('/1') @app.post("/1")
async def handler3(request): async def handler3(request):
return text('OK') return text("OK")
data = 'a' * 1000 data = "a" * 1000
response = app.test_client.post( response = app.test_client.post("/1", gather_request=False, data=data)
'/1', gather_request=False, data=data)
assert response.status == 413 assert response.status == 413
assert response.text == 'Error: Payload Too Large' assert response.text == "Error: Payload Too Large"

View File

@ -6,32 +6,31 @@ from sanic.response import text, redirect
@pytest.fixture @pytest.fixture
def redirect_app(app): def redirect_app(app):
@app.route("/redirect_init")
@app.route('/redirect_init')
async def redirect_init(request): async def redirect_init(request):
return redirect("/redirect_target") return redirect("/redirect_target")
@app.route('/redirect_init_with_301') @app.route("/redirect_init_with_301")
async def redirect_init_with_301(request): async def redirect_init_with_301(request):
return redirect("/redirect_target", status=301) return redirect("/redirect_target", status=301)
@app.route('/redirect_target') @app.route("/redirect_target")
async def redirect_target(request): async def redirect_target(request):
return text('OK') return text("OK")
@app.route('/1') @app.route("/1")
def handler1(request): def handler1(request):
return redirect('/2') return redirect("/2")
@app.route('/2') @app.route("/2")
def handler2(request): def handler2(request):
return redirect('/3') return redirect("/3")
@app.route('/3') @app.route("/3")
def handler3(request): def handler3(request):
return text('OK') return text("OK")
@app.route('/redirect_with_header_injection') @app.route("/redirect_with_header_injection")
async def redirect_with_header_injection(request): async def redirect_with_header_injection(request):
return redirect("/unsafe\ntest-header: test-value\n\ntest-body") return redirect("/unsafe\ntest-header: test-value\n\ntest-body")
@ -43,19 +42,18 @@ def test_redirect_default_302(redirect_app):
We expect a 302 default status code and the headers to be set. We expect a 302 default status code and the headers to be set.
""" """
request, response = redirect_app.test_client.get( request, response = redirect_app.test_client.get(
'/redirect_init', "/redirect_init", allow_redirects=False
allow_redirects=False) )
assert response.status == 302 assert response.status == 302
assert response.headers["Location"] == "/redirect_target" assert response.headers["Location"] == "/redirect_target"
assert response.headers["Content-Type"] == 'text/html; charset=utf-8' assert response.headers["Content-Type"] == "text/html; charset=utf-8"
def test_redirect_headers_none(redirect_app): def test_redirect_headers_none(redirect_app):
request, response = redirect_app.test_client.get( request, response = redirect_app.test_client.get(
uri="/redirect_init", uri="/redirect_init", headers=None, allow_redirects=False
headers=None, )
allow_redirects=False)
assert response.status == 302 assert response.status == 302
assert response.headers["Location"] == "/redirect_target" assert response.headers["Location"] == "/redirect_target"
@ -66,8 +64,8 @@ def test_redirect_with_301(redirect_app):
Test redirection with a different status code. Test redirection with a different status code.
""" """
request, response = redirect_app.test_client.get( request, response = redirect_app.test_client.get(
"/redirect_init_with_301", "/redirect_init_with_301", allow_redirects=False
allow_redirects=False) )
assert response.status == 301 assert response.status == 301
assert response.headers["Location"] == "/redirect_target" assert response.headers["Location"] == "/redirect_target"
@ -78,23 +76,23 @@ def test_get_then_redirect_follow_redirect(redirect_app):
With `allow_redirects` we expect a 200. With `allow_redirects` we expect a 200.
""" """
request, response = redirect_app.test_client.get( request, response = redirect_app.test_client.get(
"/redirect_init", "/redirect_init", allow_redirects=True
allow_redirects=True) )
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
def test_chained_redirect(redirect_app): def test_chained_redirect(redirect_app):
"""Test test_client is working for redirection""" """Test test_client is working for redirection"""
request, response = redirect_app.test_client.get('/1') request, response = redirect_app.test_client.get("/1")
assert request.url.endswith('/1') assert request.url.endswith("/1")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
try: try:
assert response.url.endswith('/3') assert response.url.endswith("/3")
except AttributeError: except AttributeError:
assert response.url.path.endswith('/3') assert response.url.path.endswith("/3")
def test_redirect_with_header_injection(redirect_app): def test_redirect_with_header_injection(redirect_app):
@ -102,17 +100,16 @@ def test_redirect_with_header_injection(redirect_app):
Test redirection to a URL with header and body injections. Test redirection to a URL with header and body injections.
""" """
request, response = redirect_app.test_client.get( request, response = redirect_app.test_client.get(
"/redirect_with_header_injection", "/redirect_with_header_injection", allow_redirects=False
allow_redirects=False) )
assert response.status == 302 assert response.status == 302
assert "test-header" not in response.headers assert "test-header" not in response.headers
assert not response.text.startswith('test-body') assert not response.text.startswith("test-body")
@pytest.mark.parametrize("test_str", ["sanic-test", "sanictest", "sanic test"]) @pytest.mark.parametrize("test_str", ["sanic-test", "sanictest", "sanic test"])
async def test_redirect_with_params(app, test_client, test_str): async def test_redirect_with_params(app, test_client, test_str):
@app.route("/api/v1/test/<test>/") @app.route("/api/v1/test/<test>/")
async def init_handler(request, test): async def init_handler(request, test):
assert test == test_str assert test == test_str

View File

@ -7,17 +7,17 @@ from sanic.response import text, stream
async def test_request_cancel_when_connection_lost(loop, app, test_client): async def test_request_cancel_when_connection_lost(loop, app, test_client):
app.still_serving_cancelled_request = False app.still_serving_cancelled_request = False
@app.get('/') @app.get("/")
async def handler(request): async def handler(request):
await asyncio.sleep(1.0) await asyncio.sleep(1.0)
# at this point client is already disconnected # at this point client is already disconnected
app.still_serving_cancelled_request = True app.still_serving_cancelled_request = True
return text('OK') return text("OK")
test_cli = await test_client(app) test_cli = await test_client(app)
# schedule client call # schedule client call
task = loop.create_task(test_cli.get('/')) task = loop.create_task(test_cli.get("/"))
loop.call_later(0.01, task) loop.call_later(0.01, task)
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
@ -36,7 +36,7 @@ async def test_request_cancel_when_connection_lost(loop, app, test_client):
async def test_stream_request_cancel_when_conn_lost(loop, app, test_client): async def test_stream_request_cancel_when_conn_lost(loop, app, test_client):
app.still_serving_cancelled_request = False app.still_serving_cancelled_request = False
@app.post('/post/<id>', stream=True) @app.post("/post/<id>", stream=True)
async def post(request, id): async def post(request, id):
assert isinstance(request.stream, asyncio.Queue) assert isinstance(request.stream, asyncio.Queue)
@ -45,7 +45,7 @@ async def test_stream_request_cancel_when_conn_lost(loop, app, test_client):
body = await request.stream.get() body = await request.stream.get()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
await asyncio.sleep(1.0) await asyncio.sleep(1.0)
# at this point client is already disconnected # at this point client is already disconnected
@ -56,7 +56,7 @@ async def test_stream_request_cancel_when_conn_lost(loop, app, test_client):
test_cli = await test_client(app) test_cli = await test_client(app)
# schedule client call # schedule client call
task = loop.create_task(test_cli.post('/post/1')) task = loop.create_task(test_cli.post("/post/1"))
loop.call_later(0.01, task) loop.call_later(0.01, task)
await asyncio.sleep(0.5) await asyncio.sleep(0.5)

View File

@ -9,39 +9,37 @@ except ImportError:
def test_storage(app): def test_storage(app):
@app.middleware("request")
@app.middleware('request')
def store(request): def store(request):
request['user'] = 'sanic' request["user"] = "sanic"
request['sidekick'] = 'tails' request["sidekick"] = "tails"
del request['sidekick'] del request["sidekick"]
@app.route('/') @app.route("/")
def handler(request): def handler(request):
return json({ return json(
'user': request.get('user'), {"user": request.get("user"), "sidekick": request.get("sidekick")}
'sidekick': request.get('sidekick') )
})
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
response_json = loads(response.text) response_json = loads(response.text)
assert response_json['user'] == 'sanic' assert response_json["user"] == "sanic"
assert response_json.get('sidekick') is None assert response_json.get("sidekick") is None
def test_app_injection(app): def test_app_injection(app):
expected = random.choice(range(0, 100)) expected = random.choice(range(0, 100))
@app.listener('after_server_start') @app.listener("after_server_start")
async def inject_data(app, loop): async def inject_data(app, loop):
app.injected = expected app.injected = expected
@app.get('/') @app.get("/")
async def handler(request): async def handler(request):
return json({'injected': request.app.injected}) return json({"injected": request.app.injected})
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
response_json = loads(response.text) response_json = loads(response.text)
assert response_json['injected'] == expected assert response_json["injected"] == expected

View File

@ -11,67 +11,66 @@ data = "abc" * 10000000
def test_request_stream_method_view(app): def test_request_stream_method_view(app):
'''for self.is_request_stream = True''' """for self.is_request_stream = True"""
class SimpleView(HTTPMethodView): class SimpleView(HTTPMethodView):
def get(self, request): def get(self, request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@stream_decorator @stream_decorator
async def post(self, request): async def post(self, request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
result = '' result = ""
while True: while True:
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
result += body.decode('utf-8') result += body.decode("utf-8")
return text(result) return text(result)
app.add_route(SimpleView.as_view(), '/method_view') app.add_route(SimpleView.as_view(), "/method_view")
assert app.is_request_stream is True assert app.is_request_stream is True
request, response = app.test_client.get('/method_view') request, response = app.test_client.get("/method_view")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/method_view', data=data) request, response = app.test_client.post("/method_view", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
def test_request_stream_app(app): def test_request_stream_app(app):
'''for self.is_request_stream = True and decorators''' """for self.is_request_stream = True and decorators"""
@app.get('/get') @app.get("/get")
async def get(request): async def get(request):
assert request.stream is None assert request.stream is None
return text('GET') return text("GET")
@app.head('/head') @app.head("/head")
async def head(request): async def head(request):
assert request.stream is None assert request.stream is None
return text('HEAD') return text("HEAD")
@app.delete('/delete') @app.delete("/delete")
async def delete(request): async def delete(request):
assert request.stream is None assert request.stream is None
return text('DELETE') return text("DELETE")
@app.options('/options') @app.options("/options")
async def options(request): async def options(request):
assert request.stream is None assert request.stream is None
return text('OPTIONS') return text("OPTIONS")
@app.post('/_post/<id>') @app.post("/_post/<id>")
async def _post(request, id): async def _post(request, id):
assert request.stream is None assert request.stream is None
return text('_POST') return text("_POST")
@app.post('/post/<id>', stream=True) @app.post("/post/<id>", stream=True)
async def post(request, id): async def post(request, id):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
@ -80,15 +79,16 @@ def test_request_stream_app(app):
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
return stream(streaming) return stream(streaming)
@app.put('/_put') @app.put("/_put")
async def _put(request): async def _put(request):
assert request.stream is None assert request.stream is None
return text('_PUT') return text("_PUT")
@app.put('/put', stream=True) @app.put("/put", stream=True)
async def put(request): async def put(request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
@ -97,15 +97,16 @@ def test_request_stream_app(app):
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
return stream(streaming) return stream(streaming)
@app.patch('/_patch') @app.patch("/_patch")
async def _patch(request): async def _patch(request):
assert request.stream is None assert request.stream is None
return text('_PATCH') return text("_PATCH")
@app.patch('/patch', stream=True) @app.patch("/patch", stream=True)
async def patch(request): async def patch(request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
@ -114,56 +115,57 @@ def test_request_stream_app(app):
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
return stream(streaming) return stream(streaming)
assert app.is_request_stream is True assert app.is_request_stream is True
request, response = app.test_client.get('/get') request, response = app.test_client.get("/get")
assert response.status == 200 assert response.status == 200
assert response.text == 'GET' assert response.text == "GET"
request, response = app.test_client.head('/head') request, response = app.test_client.head("/head")
assert response.status == 200 assert response.status == 200
assert response.text == '' assert response.text == ""
request, response = app.test_client.delete('/delete') request, response = app.test_client.delete("/delete")
assert response.status == 200 assert response.status == 200
assert response.text == 'DELETE' assert response.text == "DELETE"
request, response = app.test_client.options('/options') request, response = app.test_client.options("/options")
assert response.status == 200 assert response.status == 200
assert response.text == 'OPTIONS' assert response.text == "OPTIONS"
request, response = app.test_client.post('/_post/1', data=data) request, response = app.test_client.post("/_post/1", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == '_POST' assert response.text == "_POST"
request, response = app.test_client.post('/post/1', data=data) request, response = app.test_client.post("/post/1", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
request, response = app.test_client.put('/_put', data=data) request, response = app.test_client.put("/_put", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == '_PUT' assert response.text == "_PUT"
request, response = app.test_client.put('/put', data=data) request, response = app.test_client.put("/put", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
request, response = app.test_client.patch('/_patch', data=data) request, response = app.test_client.patch("/_patch", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == '_PATCH' assert response.text == "_PATCH"
request, response = app.test_client.patch('/patch', data=data) request, response = app.test_client.patch("/patch", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
def test_request_stream_handle_exception(app): def test_request_stream_handle_exception(app):
'''for handling exceptions properly''' """for handling exceptions properly"""
@app.post('/post/<id>', stream=True) @app.post("/post/<id>", stream=True)
async def post(request, id): async def post(request, id):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
@ -172,51 +174,54 @@ def test_request_stream_handle_exception(app):
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
return stream(streaming) return stream(streaming)
# 404 # 404
request, response = app.test_client.post('/in_valid_post', data=data) request, response = app.test_client.post("/in_valid_post", data=data)
assert response.status == 404 assert response.status == 404
assert response.text == 'Error: Requested URL /in_valid_post not found' assert response.text == "Error: Requested URL /in_valid_post not found"
# 405 # 405
request, response = app.test_client.get('/post/random_id', data=data) request, response = app.test_client.get("/post/random_id", data=data)
assert response.status == 405 assert response.status == 405
assert response.text == 'Error: Method GET not allowed for URL' \ assert (
' /post/random_id' response.text == "Error: Method GET not allowed for URL"
" /post/random_id"
)
def test_request_stream_blueprint(app): def test_request_stream_blueprint(app):
'''for self.is_request_stream = True''' """for self.is_request_stream = True"""
bp = Blueprint('test_blueprint_request_stream_blueprint') bp = Blueprint("test_blueprint_request_stream_blueprint")
@app.get('/get') @app.get("/get")
async def get(request): async def get(request):
assert request.stream is None assert request.stream is None
return text('GET') return text("GET")
@bp.head('/head') @bp.head("/head")
async def head(request): async def head(request):
assert request.stream is None assert request.stream is None
return text('HEAD') return text("HEAD")
@bp.delete('/delete') @bp.delete("/delete")
async def delete(request): async def delete(request):
assert request.stream is None assert request.stream is None
return text('DELETE') return text("DELETE")
@bp.options('/options') @bp.options("/options")
async def options(request): async def options(request):
assert request.stream is None assert request.stream is None
return text('OPTIONS') return text("OPTIONS")
@bp.post('/_post/<id>') @bp.post("/_post/<id>")
async def _post(request, id): async def _post(request, id):
assert request.stream is None assert request.stream is None
return text('_POST') return text("_POST")
@bp.post('/post/<id>', stream=True) @bp.post("/post/<id>", stream=True)
async def post(request, id): async def post(request, id):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
@ -225,15 +230,16 @@ def test_request_stream_blueprint(app):
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
return stream(streaming) return stream(streaming)
@bp.put('/_put') @bp.put("/_put")
async def _put(request): async def _put(request):
assert request.stream is None assert request.stream is None
return text('_PUT') return text("_PUT")
@bp.put('/put', stream=True) @bp.put("/put", stream=True)
async def put(request): async def put(request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
@ -242,15 +248,16 @@ def test_request_stream_blueprint(app):
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
return stream(streaming) return stream(streaming)
@bp.patch('/_patch') @bp.patch("/_patch")
async def _patch(request): async def _patch(request):
assert request.stream is None assert request.stream is None
return text('_PATCH') return text("_PATCH")
@bp.patch('/patch', stream=True) @bp.patch("/patch", stream=True)
async def patch(request): async def patch(request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
@ -259,109 +266,109 @@ def test_request_stream_blueprint(app):
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
return stream(streaming) return stream(streaming)
app.blueprint(bp) app.blueprint(bp)
assert app.is_request_stream is True assert app.is_request_stream is True
request, response = app.test_client.get('/get') request, response = app.test_client.get("/get")
assert response.status == 200 assert response.status == 200
assert response.text == 'GET' assert response.text == "GET"
request, response = app.test_client.head('/head') request, response = app.test_client.head("/head")
assert response.status == 200 assert response.status == 200
assert response.text == '' assert response.text == ""
request, response = app.test_client.delete('/delete') request, response = app.test_client.delete("/delete")
assert response.status == 200 assert response.status == 200
assert response.text == 'DELETE' assert response.text == "DELETE"
request, response = app.test_client.options('/options') request, response = app.test_client.options("/options")
assert response.status == 200 assert response.status == 200
assert response.text == 'OPTIONS' assert response.text == "OPTIONS"
request, response = app.test_client.post('/_post/1', data=data) request, response = app.test_client.post("/_post/1", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == '_POST' assert response.text == "_POST"
request, response = app.test_client.post('/post/1', data=data) request, response = app.test_client.post("/post/1", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
request, response = app.test_client.put('/_put', data=data) request, response = app.test_client.put("/_put", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == '_PUT' assert response.text == "_PUT"
request, response = app.test_client.put('/put', data=data) request, response = app.test_client.put("/put", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
request, response = app.test_client.patch('/_patch', data=data) request, response = app.test_client.patch("/_patch", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == '_PATCH' assert response.text == "_PATCH"
request, response = app.test_client.patch('/patch', data=data) request, response = app.test_client.patch("/patch", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
def test_request_stream_composition_view(app): def test_request_stream_composition_view(app):
'''for self.is_request_stream = True''' """for self.is_request_stream = True"""
def get_handler(request): def get_handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
async def post_handler(request): async def post_handler(request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
result = '' result = ""
while True: while True:
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
result += body.decode('utf-8') result += body.decode("utf-8")
return text(result) return text(result)
view = CompositionView() view = CompositionView()
view.add(['GET'], get_handler) view.add(["GET"], get_handler)
view.add(['POST'], post_handler, stream=True) view.add(["POST"], post_handler, stream=True)
app.add_route(view, '/composition_view') app.add_route(view, "/composition_view")
assert app.is_request_stream is True assert app.is_request_stream is True
request, response = app.test_client.get('/composition_view') request, response = app.test_client.get("/composition_view")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/composition_view', data=data) request, response = app.test_client.post("/composition_view", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
def test_request_stream(app): def test_request_stream(app):
'''test for complex application''' """test for complex application"""
bp = Blueprint('test_blueprint_request_stream') bp = Blueprint("test_blueprint_request_stream")
class SimpleView(HTTPMethodView): class SimpleView(HTTPMethodView):
def get(self, request): def get(self, request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@stream_decorator @stream_decorator
async def post(self, request): async def post(self, request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
result = '' result = ""
while True: while True:
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
result += body.decode('utf-8') result += body.decode("utf-8")
return text(result) return text(result)
@app.post('/stream', stream=True) @app.post("/stream", stream=True)
async def handler(request): async def handler(request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
@ -370,84 +377,85 @@ def test_request_stream(app):
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
await response.write(body.decode('utf-8')) await response.write(body.decode("utf-8"))
return stream(streaming) return stream(streaming)
@app.get('/get') @app.get("/get")
async def get(request): async def get(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
@bp.post('/bp_stream', stream=True) @bp.post("/bp_stream", stream=True)
async def bp_stream(request): async def bp_stream(request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
result = '' result = ""
while True: while True:
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
result += body.decode('utf-8') result += body.decode("utf-8")
return text(result) return text(result)
@bp.get('/bp_get') @bp.get("/bp_get")
async def bp_get(request): async def bp_get(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
def get_handler(request): def get_handler(request):
assert request.stream is None assert request.stream is None
return text('OK') return text("OK")
async def post_handler(request): async def post_handler(request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
result = '' result = ""
while True: while True:
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
result += body.decode('utf-8') result += body.decode("utf-8")
return text(result) return text(result)
app.add_route(SimpleView.as_view(), '/method_view') app.add_route(SimpleView.as_view(), "/method_view")
view = CompositionView() view = CompositionView()
view.add(['GET'], get_handler) view.add(["GET"], get_handler)
view.add(['POST'], post_handler, stream=True) view.add(["POST"], post_handler, stream=True)
app.blueprint(bp) app.blueprint(bp)
app.add_route(view, '/composition_view') app.add_route(view, "/composition_view")
assert app.is_request_stream is True assert app.is_request_stream is True
request, response = app.test_client.get('/method_view') request, response = app.test_client.get("/method_view")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/method_view', data=data) request, response = app.test_client.post("/method_view", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
request, response = app.test_client.get('/composition_view') request, response = app.test_client.get("/composition_view")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/composition_view', data=data) request, response = app.test_client.post("/composition_view", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
request, response = app.test_client.get('/get') request, response = app.test_client.get("/get")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/stream', data=data) request, response = app.test_client.post("/stream", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data
request, response = app.test_client.get('/bp_get') request, response = app.test_client.get("/bp_get")
assert response.status == 200 assert response.status == 200
assert response.text == 'OK' assert response.text == "OK"
request, response = app.test_client.post('/bp_stream', data=data) request, response = app.test_client.post("/bp_stream", data=data)
assert response.status == 200 assert response.status == 200
assert response.text == data assert response.text == data

View File

@ -12,6 +12,7 @@ try:
try: try:
# direct use # direct use
import packaging import packaging
version = packaging.version version = packaging.version
except (ImportError, AttributeError): except (ImportError, AttributeError):
# setuptools v39.0 and above. # setuptools v39.0 and above.
@ -29,15 +30,15 @@ aiohttp_version = version.parse(aiohttp.__version__)
class DelayableTCPConnector(TCPConnector): class DelayableTCPConnector(TCPConnector):
class RequestContextManager(object): class RequestContextManager(object):
def __new__(cls, req, delay): def __new__(cls, req, delay):
cls = super(DelayableTCPConnector.RequestContextManager, cls).\ cls = super(
__new__(cls) DelayableTCPConnector.RequestContextManager, cls
).__new__(cls)
cls.req = req cls.req = req
cls.send_task = None cls.send_task = None
cls.resp = None cls.resp = None
cls.orig_send = getattr(req, 'send') cls.orig_send = getattr(req, "send")
cls.orig_start = None cls.orig_start = None
cls.delay = delay cls.delay = delay
cls._acting_as = req cls._acting_as = req
@ -54,7 +55,7 @@ class DelayableTCPConnector(TCPConnector):
self.send_task = None self.send_task = None
self.resp = resp self.resp = resp
self._acting_as = self.resp self._acting_as = self.resp
self.orig_start = getattr(resp, 'start') self.orig_start = getattr(resp, "start")
try: try:
if aiohttp_version >= version.parse("3.3.0"): if aiohttp_version >= version.parse("3.3.0"):
@ -92,10 +93,10 @@ class DelayableTCPConnector(TCPConnector):
request_info=None, request_info=None,
traces=[], traces=[],
loop=req.loop, loop=req.loop,
session=None session=None,
) )
if aiohttp_version < version.parse("3.3.0"): if aiohttp_version < version.parse("3.3.0"):
kw['auto_decompress'] = None kw["auto_decompress"] = None
return aiohttp.ClientResponse(req.method, req.url, **kw) return aiohttp.ClientResponse(req.method, req.url, **kw)
def _send(self, *args, **kwargs): def _send(self, *args, **kwargs):
@ -109,24 +110,26 @@ class DelayableTCPConnector(TCPConnector):
# aiohttp changed the request.send method to async # aiohttp changed the request.send method to async
async def send(self, *args, **kwargs): async def send(self, *args, **kwargs):
return self._send(*args, **kwargs) return self._send(*args, **kwargs)
else: else:
send = _send send = _send
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
_post_connect_delay = kwargs.pop('post_connect_delay', 0) _post_connect_delay = kwargs.pop("post_connect_delay", 0)
_pre_request_delay = kwargs.pop('pre_request_delay', 0) _pre_request_delay = kwargs.pop("pre_request_delay", 0)
super(DelayableTCPConnector, self).__init__(*args, **kwargs) super(DelayableTCPConnector, self).__init__(*args, **kwargs)
self._post_connect_delay = _post_connect_delay self._post_connect_delay = _post_connect_delay
self._pre_request_delay = _pre_request_delay self._pre_request_delay = _pre_request_delay
async def connect(self, req, *args, **kwargs): async def connect(self, req, *args, **kwargs):
d_req = DelayableTCPConnector.\ d_req = DelayableTCPConnector.RequestContextManager(
RequestContextManager(req, self._pre_request_delay) req, self._pre_request_delay
conn = await super(DelayableTCPConnector, self).\ )
connect(req, *args, **kwargs) conn = await super(DelayableTCPConnector, self).connect(
req, *args, **kwargs
)
if self._post_connect_delay and self._post_connect_delay > 0: if self._post_connect_delay and self._post_connect_delay > 0:
await asyncio.sleep(self._post_connect_delay, await asyncio.sleep(self._post_connect_delay, loop=self._loop)
loop=self._loop)
req.send = d_req.send req.send = d_req.send
t = req.loop.time() t = req.loop.time()
print("Connected at {}".format(t), flush=True) print("Connected at {}".format(t), flush=True)
@ -139,24 +142,29 @@ class DelayableSanicTestClient(SanicTestClient):
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, async def _local_request(self, method, uri, cookies=None, *args, **kwargs):
**kwargs):
if self._loop is None: if self._loop is None:
self._loop = asyncio.get_event_loop() self._loop = asyncio.get_event_loop()
if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')): if uri.startswith(("http:", "https:", "ftp:", "ftps://" "//")):
url = uri url = uri
else: else:
url = 'http://{host}:{port}{uri}'.format( url = "http://{host}:{port}{uri}".format(
host=HOST, port=self.port, uri=uri) host=HOST, port=self.port, uri=uri
conn = DelayableTCPConnector(pre_request_delay=self._request_delay, )
verify_ssl=False, loop=self._loop) conn = DelayableTCPConnector(
async with aiohttp.ClientSession(cookies=cookies, connector=conn, pre_request_delay=self._request_delay,
loop=self._loop) as session: verify_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 # Insert a delay after creating the connection
# But before sending the request. # But before sending the request.
async with getattr(session, method.lower())( async with getattr(session, method.lower())(
url, *args, **kwargs) as response: url, *args, **kwargs
) as response:
try: try:
response.text = await response.text() response.text = await response.text()
except UnicodeDecodeError: except UnicodeDecodeError:
@ -164,9 +172,11 @@ class DelayableSanicTestClient(SanicTestClient):
try: try:
response.json = await response.json() response.json = await response.json()
except (JSONDecodeError, except (
UnicodeDecodeError, JSONDecodeError,
aiohttp.ClientResponseError): UnicodeDecodeError,
aiohttp.ClientResponseError,
):
response.json = None response.json = None
response.body = await response.read() response.body = await response.read()
@ -174,50 +184,50 @@ class DelayableSanicTestClient(SanicTestClient):
Config.REQUEST_TIMEOUT = 0.6 Config.REQUEST_TIMEOUT = 0.6
request_timeout_default_app = Sanic('test_request_timeout_default') request_timeout_default_app = Sanic("test_request_timeout_default")
request_no_timeout_app = Sanic('test_request_no_timeout') request_no_timeout_app = Sanic("test_request_no_timeout")
@request_timeout_default_app.route('/1') @request_timeout_default_app.route("/1")
async def handler1(request): async def handler1(request):
return text('OK') return text("OK")
@request_no_timeout_app.route('/1') @request_no_timeout_app.route("/1")
async def handler2(request): async def handler2(request):
return text('OK') return text("OK")
@request_timeout_default_app.websocket('/ws1') @request_timeout_default_app.websocket("/ws1")
async def ws_handler1(request, ws): async def ws_handler1(request, ws):
await ws.send('OK') await ws.send("OK")
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, None, 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, None, 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"
def test_default_server_error_websocket_request_timeout(): def test_default_server_error_websocket_request_timeout():
headers={ headers = {
'Upgrade': 'websocket', "Upgrade": "websocket",
'Connection': 'upgrade', "Connection": "upgrade",
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', "Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
'Sec-WebSocket-Version': '13' "Sec-WebSocket-Version": "13",
} }
client = DelayableSanicTestClient(request_timeout_default_app, None, 2) client = DelayableSanicTestClient(request_timeout_default_app, None, 2)
request, response = client.get('/ws1', headers=headers) request, response = client.get("/ws1", headers=headers)
assert response.status == 408 assert response.status == 408
assert response.text == 'Error: Request Timeout' assert response.text == "Error: Request Timeout"

View File

@ -16,235 +16,214 @@ from sanic.testing import HOST, PORT
# GET # GET
# ------------------------------------------------------------ # # ------------------------------------------------------------ #
def test_sync(app): def test_sync(app):
@app.route("/")
@app.route('/')
def handler(request): def handler(request):
return text('Hello') return text("Hello")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'Hello' assert response.text == "Hello"
def test_remote_address(app): def test_remote_address(app):
@app.route("/")
@app.route('/')
def handler(request): def handler(request):
return text("{}".format(request.ip)) return text("{}".format(request.ip))
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == '127.0.0.1' assert response.text == "127.0.0.1"
def test_text(app): def test_text(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text('Hello') return text("Hello")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'Hello' assert response.text == "Hello"
def test_headers(app): def test_headers(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
headers = {"spam": "great"} headers = {"spam": "great"}
return text('Hello', headers=headers) return text("Hello", headers=headers)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.headers.get('spam') == 'great' assert response.headers.get("spam") == "great"
def test_non_str_headers(app): def test_non_str_headers(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
headers = {"answer": 42} headers = {"answer": 42}
return text('Hello', headers=headers) return text("Hello", headers=headers)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.headers.get('answer') == '42' assert response.headers.get("answer") == "42"
def test_invalid_response(app): def test_invalid_response(app):
@app.exception(ServerError) @app.exception(ServerError)
def handler_exception(request, exception): def handler_exception(request, exception):
return text('Internal Server Error.', 500) return text("Internal Server Error.", 500)
@app.route('/') @app.route("/")
async def handler(request): async def handler(request):
return 'This should fail' return "This should fail"
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.status == 500 assert response.status == 500
assert response.text == "Internal Server Error." assert response.text == "Internal Server Error."
def test_json(app): def test_json(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return json({"test": True}) return json({"test": True})
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
results = json_loads(response.text) results = json_loads(response.text)
assert results.get('test') is True assert results.get("test") is True
def test_empty_json(app): def test_empty_json(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
assert request.json is None assert request.json is None
return json(request.json) return json(request.json)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.status == 200 assert response.status == 200
assert response.text == 'null' assert response.text == "null"
def test_invalid_json(app): def test_invalid_json(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return json(request.json) return json(request.json)
data = "I am not json" data = "I am not json"
request, response = app.test_client.get('/', data=data) request, response = app.test_client.get("/", data=data)
assert response.status == 400 assert response.status == 400
def test_query_string(app): def test_query_string(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get( request, response = app.test_client.get(
'/', params=[("test1", "1"), ("test2", "false"), ("test2", "true")]) "/", params=[("test1", "1"), ("test2", "false"), ("test2", "true")]
)
assert request.args.get('test1') == '1' assert request.args.get("test1") == "1"
assert request.args.get('test2') == 'false' assert request.args.get("test2") == "false"
def test_uri_template(app): def test_uri_template(app):
@app.route("/foo/<id:int>/bar/<name:[A-z]+>")
@app.route('/foo/<id:int>/bar/<name:[A-z]+>')
async def handler(request): async def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/foo/123/bar/baz') request, response = app.test_client.get("/foo/123/bar/baz")
assert request.uri_template == '/foo/<id:int>/bar/<name:[A-z]+>' assert request.uri_template == "/foo/<id:int>/bar/<name:[A-z]+>"
def test_token(app): def test_token(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text('OK') return text("OK")
# uuid4 generated token. # uuid4 generated token.
token = 'a1d895e0-553a-421a-8e22-5ff8ecb48cbf' token = "a1d895e0-553a-421a-8e22-5ff8ecb48cbf"
headers = { headers = {
'content-type': 'application/json', "content-type": "application/json",
'Authorization': '{}'.format(token) "Authorization": "{}".format(token),
} }
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert request.token == token assert request.token == token
token = 'a1d895e0-553a-421a-8e22-5ff8ecb48cbf' token = "a1d895e0-553a-421a-8e22-5ff8ecb48cbf"
headers = { headers = {
'content-type': 'application/json', "content-type": "application/json",
'Authorization': 'Token {}'.format(token) "Authorization": "Token {}".format(token),
} }
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert request.token == token assert request.token == token
token = 'a1d895e0-553a-421a-8e22-5ff8ecb48cbf' token = "a1d895e0-553a-421a-8e22-5ff8ecb48cbf"
headers = { headers = {
'content-type': 'application/json', "content-type": "application/json",
'Authorization': 'Bearer {}'.format(token) "Authorization": "Bearer {}".format(token),
} }
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert request.token == token assert request.token == token
# no Authorization headers # no Authorization headers
headers = { headers = {"content-type": "application/json"}
'content-type': 'application/json'
}
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert request.token is None assert request.token is None
def test_content_type(app): def test_content_type(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text(request.content_type) return text(request.content_type)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert request.content_type == DEFAULT_HTTP_CONTENT_TYPE assert request.content_type == DEFAULT_HTTP_CONTENT_TYPE
assert response.text == DEFAULT_HTTP_CONTENT_TYPE assert response.text == DEFAULT_HTTP_CONTENT_TYPE
headers = { headers = {"content-type": "application/json"}
'content-type': 'application/json', request, response = app.test_client.get("/", headers=headers)
} assert request.content_type == "application/json"
request, response = app.test_client.get('/', headers=headers) assert response.text == "application/json"
assert request.content_type == 'application/json'
assert response.text == 'application/json'
def test_remote_addr(app): def test_remote_addr(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text(request.remote_addr) return text(request.remote_addr)
headers = { headers = {"X-Forwarded-For": "127.0.0.1, 127.0.1.2"}
'X-Forwarded-For': '127.0.0.1, 127.0.1.2' request, response = app.test_client.get("/", headers=headers)
} assert request.remote_addr == "127.0.0.1"
request, response = app.test_client.get('/', headers=headers) assert response.text == "127.0.0.1"
assert request.remote_addr == '127.0.0.1'
assert response.text == '127.0.0.1'
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert request.remote_addr == '' assert request.remote_addr == ""
assert response.text == '' assert response.text == ""
headers = { headers = {"X-Forwarded-For": "127.0.0.1, , ,,127.0.1.2"}
'X-Forwarded-For': '127.0.0.1, , ,,127.0.1.2' request, response = app.test_client.get("/", headers=headers)
} assert request.remote_addr == "127.0.0.1"
request, response = app.test_client.get('/', headers=headers) assert response.text == "127.0.0.1"
assert request.remote_addr == '127.0.0.1'
assert response.text == '127.0.0.1'
def test_match_info(app): def test_match_info(app):
@app.route("/api/v1/user/<user_id>/")
@app.route('/api/v1/user/<user_id>/')
async def handler(request, user_id): async def handler(request, user_id):
return json(request.match_info) return json(request.match_info)
request, response = app.test_client.get('/api/v1/user/sanic_user/') request, response = app.test_client.get("/api/v1/user/sanic_user/")
assert request.match_info == {"user_id": "sanic_user"} assert request.match_info == {"user_id": "sanic_user"}
assert json_loads(response.text) == {"user_id": "sanic_user"} assert json_loads(response.text) == {"user_id": "sanic_user"}
@ -254,79 +233,82 @@ def test_match_info(app):
# POST # POST
# ------------------------------------------------------------ # # ------------------------------------------------------------ #
def test_post_json(app): def test_post_json(app):
@app.route("/", methods=["POST"])
@app.route('/', methods=['POST'])
async def handler(request): async def handler(request):
return text('OK') return text("OK")
payload = {'test': 'OK'} payload = {"test": "OK"}
headers = {'content-type': 'application/json'} headers = {"content-type": "application/json"}
request, response = app.test_client.post( request, response = app.test_client.post(
'/', data=json_dumps(payload), headers=headers) "/", data=json_dumps(payload), headers=headers
)
assert request.json.get('test') == 'OK' assert request.json.get("test") == "OK"
assert request.json.get('test') == 'OK' # for request.parsed_json assert request.json.get("test") == "OK" # for request.parsed_json
assert response.text == 'OK' assert response.text == "OK"
def test_post_form_urlencoded(app): def test_post_form_urlencoded(app):
@app.route("/", methods=["POST"])
@app.route('/', methods=['POST'])
async def handler(request): async def handler(request):
return text('OK') return text("OK")
payload = 'test=OK' payload = "test=OK"
headers = {'content-type': 'application/x-www-form-urlencoded'} headers = {"content-type": "application/x-www-form-urlencoded"}
request, response = app.test_client.post('/', data=payload, request, response = app.test_client.post(
headers=headers) "/", data=payload, headers=headers
)
assert request.form.get('test') == 'OK' assert request.form.get("test") == "OK"
assert request.form.get('test') == 'OK' # For request.parsed_form assert request.form.get("test") == "OK" # For request.parsed_form
@pytest.mark.parametrize( @pytest.mark.parametrize(
'payload', [ "payload",
'------sanic\r\n' [
"------sanic\r\n"
'Content-Disposition: form-data; name="test"\r\n' 'Content-Disposition: form-data; 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; name="test"\r\n' 'content-disposition: form-data; name="test"\r\n'
'\r\n' "\r\n"
'OK\r\n' "OK\r\n"
'------sanic--\r\n', "------sanic--\r\n",
]) ],
)
def test_post_form_multipart_form_data(app, payload): def test_post_form_multipart_form_data(app, payload):
@app.route("/", methods=["POST"])
@app.route('/', methods=['POST'])
async def handler(request): async def handler(request):
return text('OK') return text("OK")
headers = {'content-type': 'multipart/form-data; boundary=----sanic'} headers = {"content-type": "multipart/form-data; boundary=----sanic"}
request, response = app.test_client.post(data=payload, headers=headers) request, response = app.test_client.post(data=payload, headers=headers)
assert request.form.get('test') == 'OK' assert request.form.get("test") == "OK"
@pytest.mark.parametrize( @pytest.mark.parametrize(
'path,query,expected_url', [ "path,query,expected_url",
('/foo', '', 'http://{}:{}/foo'), [
('/bar/baz', '', 'http://{}:{}/bar/baz'), ("/foo", "", "http://{}:{}/foo"),
('/moo/boo', 'arg1=val1', 'http://{}:{}/moo/boo?arg1=val1') ("/bar/baz", "", "http://{}:{}/bar/baz"),
]) ("/moo/boo", "arg1=val1", "http://{}:{}/moo/boo?arg1=val1"),
],
)
def test_url_attributes_no_ssl(app, path, query, expected_url): def test_url_attributes_no_ssl(app, path, query, expected_url):
async def handler(request): async def handler(request):
return text('OK') return text("OK")
app.add_route(handler, path) app.add_route(handler, path)
request, response = app.test_client.get(path + '?{}'.format(query)) request, response = app.test_client.get(path + "?{}".format(query))
assert request.url == expected_url.format(HOST, PORT) assert request.url == expected_url.format(HOST, PORT)
parsed = urlparse(request.url) parsed = urlparse(request.url)
@ -338,26 +320,30 @@ def test_url_attributes_no_ssl(app, path, query, expected_url):
@pytest.mark.parametrize( @pytest.mark.parametrize(
'path,query,expected_url', [ "path,query,expected_url",
('/foo', '', 'https://{}:{}/foo'), [
('/bar/baz', '', 'https://{}:{}/bar/baz'), ("/foo", "", "https://{}:{}/foo"),
('/moo/boo', 'arg1=val1', 'https://{}:{}/moo/boo?arg1=val1') ("/bar/baz", "", "https://{}:{}/bar/baz"),
]) ("/moo/boo", "arg1=val1", "https://{}:{}/moo/boo?arg1=val1"),
],
)
def test_url_attributes_with_ssl_context(app, path, query, expected_url): def test_url_attributes_with_ssl_context(app, path, query, expected_url):
current_dir = os.path.dirname(os.path.realpath(__file__)) current_dir = os.path.dirname(os.path.realpath(__file__))
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain( context.load_cert_chain(
os.path.join(current_dir, 'certs/selfsigned.cert'), os.path.join(current_dir, "certs/selfsigned.cert"),
keyfile=os.path.join(current_dir, 'certs/selfsigned.key')) keyfile=os.path.join(current_dir, "certs/selfsigned.key"),
)
async def handler(request): async def handler(request):
return text('OK') return text("OK")
app.add_route(handler, path) app.add_route(handler, path)
request, response = app.test_client.get( request, response = app.test_client.get(
'https://{}:{}'.format(HOST, PORT) + path + '?{}'.format(query), "https://{}:{}".format(HOST, PORT) + path + "?{}".format(query),
server_kwargs={'ssl': context}) server_kwargs={"ssl": context},
)
assert request.url == expected_url.format(HOST, PORT) assert request.url == expected_url.format(HOST, PORT)
parsed = urlparse(request.url) parsed = urlparse(request.url)
@ -369,30 +355,30 @@ def test_url_attributes_with_ssl_context(app, path, query, expected_url):
@pytest.mark.parametrize( @pytest.mark.parametrize(
'path,query,expected_url', [ "path,query,expected_url",
('/foo', '', 'https://{}:{}/foo'), [
('/bar/baz', '', 'https://{}:{}/bar/baz'), ("/foo", "", "https://{}:{}/foo"),
('/moo/boo', 'arg1=val1', 'https://{}:{}/moo/boo?arg1=val1') ("/bar/baz", "", "https://{}:{}/bar/baz"),
]) ("/moo/boo", "arg1=val1", "https://{}:{}/moo/boo?arg1=val1"),
],
)
def test_url_attributes_with_ssl_dict(app, path, query, expected_url): def test_url_attributes_with_ssl_dict(app, path, query, expected_url):
current_dir = os.path.dirname(os.path.realpath(__file__)) current_dir = os.path.dirname(os.path.realpath(__file__))
ssl_cert = os.path.join(current_dir, 'certs/selfsigned.cert') ssl_cert = os.path.join(current_dir, "certs/selfsigned.cert")
ssl_key = os.path.join(current_dir, 'certs/selfsigned.key') ssl_key = os.path.join(current_dir, "certs/selfsigned.key")
ssl_dict = { ssl_dict = {"cert": ssl_cert, "key": ssl_key}
'cert': ssl_cert,
'key': ssl_key
}
async def handler(request): async def handler(request):
return text('OK') return text("OK")
app.add_route(handler, path) app.add_route(handler, path)
request, response = app.test_client.get( request, response = app.test_client.get(
'https://{}:{}'.format(HOST, PORT) + path + '?{}'.format(query), "https://{}:{}".format(HOST, PORT) + path + "?{}".format(query),
server_kwargs={'ssl': ssl_dict}) server_kwargs={"ssl": ssl_dict},
)
assert request.url == expected_url.format(HOST, PORT) assert request.url == expected_url.format(HOST, PORT)
parsed = urlparse(request.url) parsed = urlparse(request.url)
@ -404,69 +390,69 @@ def test_url_attributes_with_ssl_dict(app, path, query, expected_url):
def test_invalid_ssl_dict(app): def test_invalid_ssl_dict(app):
@app.get("/test")
@app.get('/test')
async def handler(request): async def handler(request):
return text('ssl test') return text("ssl test")
ssl_dict = { ssl_dict = {"cert": None, "key": None}
'cert': None,
'key': None
}
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
request, response = app.test_client.get('/test', server_kwargs={'ssl': ssl_dict}) request, response = app.test_client.get(
"/test", server_kwargs={"ssl": ssl_dict}
)
assert str(excinfo.value) == 'SSLContext or certificate and key required.' assert str(excinfo.value) == "SSLContext or certificate and key required."
def test_form_with_multiple_values(app): def test_form_with_multiple_values(app):
@app.route("/", methods=["POST"])
@app.route('/', methods=['POST'])
async def handler(request): async def handler(request):
return text("OK") return text("OK")
payload="selectedItems=v1&selectedItems=v2&selectedItems=v3" payload = "selectedItems=v1&selectedItems=v2&selectedItems=v3"
headers = {'content-type': 'application/x-www-form-urlencoded'} headers = {"content-type": "application/x-www-form-urlencoded"}
request, response = app.test_client.post('/', data=payload, request, response = app.test_client.post(
headers=headers) "/", data=payload, headers=headers
)
assert request.form.getlist("selectedItems") == ["v1", "v2", "v3"] assert request.form.getlist("selectedItems") == ["v1", "v2", "v3"]
def test_request_string_representation(app): def test_request_string_representation(app):
@app.route('/', methods=["GET"]) @app.route("/", methods=["GET"])
async def get(request): async def get(request):
return text("OK") return text("OK")
request, _ = app.test_client.get("/") request, _ = app.test_client.get("/")
assert repr(request) == '<Request: GET />' assert repr(request) == "<Request: GET />"
@pytest.mark.parametrize( @pytest.mark.parametrize(
'payload', [ "payload",
'------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', "------sanic--\r\n",
'------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"
'content-type: application/json; {"field": "value"}\r\n' 'content-type: application/json; {"field": "value"}\r\n'
'------sanic--\r\n', "------sanic--\r\n",
]) ],
)
def test_request_multipart_files(app, payload): def test_request_multipart_files(app, payload):
@app.route("/", methods=["POST"]) @app.route("/", methods=["POST"])
async def post(request): async def post(request):
return text("OK") return text("OK")
headers = {'content-type': 'multipart/form-data; boundary=----sanic'} headers = {"content-type": "multipart/form-data; boundary=----sanic"}
request, _ = app.test_client.post(data=payload, headers=headers) request, _ = app.test_client.post(data=payload, headers=headers)
assert request.files.get('test').name == "filename" assert request.files.get("test").name == "filename"
def test_request_multipart_file_with_json_content_type(app): def test_request_multipart_file_with_json_content_type(app):
@ -475,72 +461,79 @@ def test_request_multipart_file_with_json_content_type(app):
return text("OK") return text("OK")
payload = ( payload = (
'------sanic\r\n' "------sanic\r\n"
'Content-Disposition: form-data; name="file"; filename="test.json"\r\n' 'Content-Disposition: form-data; name="file"; filename="test.json"\r\n'
'Content-Type: application/json\r\n' "Content-Type: application/json\r\n"
'Content-Length: 0' "Content-Length: 0"
'\r\n' "\r\n"
'\r\n' "\r\n"
'------sanic--' "------sanic--"
) )
headers = {'content-type': 'multipart/form-data; boundary=------sanic'} headers = {"content-type": "multipart/form-data; boundary=------sanic"}
request, _ = app.test_client.post(data=payload, headers=headers) request, _ = app.test_client.post(data=payload, headers=headers)
assert request.files.get('file').type == 'application/json' assert request.files.get("file").type == "application/json"
def test_request_multipart_file_without_field_name(app, caplog): def test_request_multipart_file_without_field_name(app, caplog):
@app.route("/", methods=["POST"]) @app.route("/", methods=["POST"])
async def post(request): async def post(request):
return text("OK") return text("OK")
payload = ( payload = (
'------sanic\r\nContent-Disposition: form-data; filename="test.json"' '------sanic\r\nContent-Disposition: form-data; filename="test.json"'
'\r\nContent-Type: application/json\r\n\r\n\r\n------sanic--' "\r\nContent-Type: application/json\r\n\r\n\r\n------sanic--"
) )
headers = {'content-type': 'multipart/form-data; boundary=------sanic'} headers = {"content-type": "multipart/form-data; boundary=------sanic"}
request, _ = app.test_client.post(data=payload, headers=headers, debug=True) request, _ = app.test_client.post(
data=payload, headers=headers, debug=True
)
with caplog.at_level(logging.DEBUG): with caplog.at_level(logging.DEBUG):
request.form request.form
assert caplog.record_tuples[-1] == ('sanic.root', logging.DEBUG, assert caplog.record_tuples[-1] == (
"sanic.root",
logging.DEBUG,
"Form-data field does not have a 'name' parameter " "Form-data field does not have a 'name' parameter "
"in the Content-Disposition header" "in the Content-Disposition header",
) )
def test_request_multipart_file_duplicate_filed_name(app): def test_request_multipart_file_duplicate_filed_name(app):
@app.route("/", methods=["POST"]) @app.route("/", methods=["POST"])
async def post(request): async def post(request):
return text("OK") return text("OK")
payload = ( payload = (
'--e73ffaa8b1b2472b8ec848de833cb05b\r\n' "--e73ffaa8b1b2472b8ec848de833cb05b\r\n"
'Content-Disposition: form-data; name="file"\r\n' 'Content-Disposition: form-data; name="file"\r\n'
'Content-Type: application/octet-stream\r\n' "Content-Type: application/octet-stream\r\n"
'Content-Length: 15\r\n' "Content-Length: 15\r\n"
'\r\n' "\r\n"
'{"test":"json"}\r\n' '{"test":"json"}\r\n'
'--e73ffaa8b1b2472b8ec848de833cb05b\r\n' "--e73ffaa8b1b2472b8ec848de833cb05b\r\n"
'Content-Disposition: form-data; name="file"\r\n' 'Content-Disposition: form-data; name="file"\r\n'
'Content-Type: application/octet-stream\r\n' "Content-Type: application/octet-stream\r\n"
'Content-Length: 15\r\n' "Content-Length: 15\r\n"
'\r\n' "\r\n"
'{"test":"json2"}\r\n' '{"test":"json2"}\r\n'
'--e73ffaa8b1b2472b8ec848de833cb05b--\r\n' "--e73ffaa8b1b2472b8ec848de833cb05b--\r\n"
) )
headers = { headers = {
'Content-Type': 'multipart/form-data; boundary=e73ffaa8b1b2472b8ec848de833cb05b' "Content-Type": "multipart/form-data; boundary=e73ffaa8b1b2472b8ec848de833cb05b"
} }
request, _ = app.test_client.post(data=payload, headers=headers, debug=True) request, _ = app.test_client.post(
assert request.form.getlist('file') == ['{"test":"json"}', '{"test":"json2"}'] data=payload, headers=headers, debug=True
)
assert request.form.getlist("file") == [
'{"test":"json"}',
'{"test":"json2"}',
]
def test_request_multipart_with_multiple_files_and_type(app): def test_request_multipart_with_multiple_files_and_type(app):
@ -548,38 +541,38 @@ def test_request_multipart_with_multiple_files_and_type(app):
async def post(request): async def post(request):
return text("OK") return text("OK")
payload = '------sanic\r\nContent-Disposition: form-data; name="file"; filename="test.json"' \ payload = (
'\r\nContent-Type: application/json\r\n\r\n\r\n' \ '------sanic\r\nContent-Disposition: form-data; name="file"; filename="test.json"'
'------sanic\r\nContent-Disposition: form-data; name="file"; filename="some_file.pdf"\r\n' \ "\r\nContent-Type: application/json\r\n\r\n\r\n"
'Content-Type: application/pdf\r\n\r\n\r\n------sanic--' '------sanic\r\nContent-Disposition: form-data; name="file"; filename="some_file.pdf"\r\n'
headers = {'content-type': 'multipart/form-data; boundary=------sanic'} "Content-Type: application/pdf\r\n\r\n\r\n------sanic--"
)
headers = {"content-type": "multipart/form-data; boundary=------sanic"}
request, _ = app.test_client.post(data=payload, headers=headers) request, _ = app.test_client.post(data=payload, headers=headers)
assert len(request.files.getlist('file')) == 2 assert len(request.files.getlist("file")) == 2
assert request.files.getlist('file')[0].type == 'application/json' assert request.files.getlist("file")[0].type == "application/json"
assert request.files.getlist('file')[1].type == 'application/pdf' assert request.files.getlist("file")[1].type == "application/pdf"
def test_request_repr(app): def test_request_repr(app):
@app.get("/")
@app.get('/')
def handler(request): def handler(request):
return text('pass') return text("pass")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert repr(request) == '<Request: GET />' assert repr(request) == "<Request: GET />"
request.method = None request.method = None
assert repr(request) == '<Request>' assert repr(request) == "<Request>"
def test_request_bool(app): def test_request_bool(app):
@app.get("/")
@app.get('/')
def handler(request): def handler(request):
return text('pass') return text("pass")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert bool(request) assert bool(request)
request.transport = False request.transport = False
@ -587,98 +580,99 @@ def test_request_bool(app):
def test_request_parsing_form_failed(app, caplog): def test_request_parsing_form_failed(app, caplog):
@app.route("/", methods=["POST"])
@app.route('/', methods=['POST'])
async def handler(request): async def handler(request):
return text('OK') return text("OK")
payload = 'test=OK' payload = "test=OK"
headers = {'content-type': 'multipart/form-data'} headers = {"content-type": "multipart/form-data"}
request, response = app.test_client.post('/', data=payload, headers=headers) request, response = app.test_client.post(
"/", data=payload, headers=headers
)
with caplog.at_level(logging.ERROR): with caplog.at_level(logging.ERROR):
request.form request.form
assert caplog.record_tuples[-1] == ('sanic.error', logging.ERROR, 'Failed when parsing form') assert caplog.record_tuples[-1] == (
"sanic.error",
logging.ERROR,
"Failed when parsing form",
)
def test_request_args_no_query_string(app): def test_request_args_no_query_string(app):
@app.get("/")
@app.get('/')
def handler(request): def handler(request):
return text('pass') return text("pass")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert request.args == {} assert request.args == {}
def test_request_raw_args(app): def test_request_raw_args(app):
params = {'test': 'OK'} params = {"test": "OK"}
@app.get('/') @app.get("/")
def handler(request): def handler(request):
return text('pass') return text("pass")
request, response = app.test_client.get('/', params=params) request, response = app.test_client.get("/", params=params)
assert request.raw_args == params assert request.raw_args == params
def test_request_cookies(app): def test_request_cookies(app):
cookies = {'test': 'OK'} cookies = {"test": "OK"}
@app.get('/') @app.get("/")
def handler(request): def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/', cookies=cookies) request, response = app.test_client.get("/", cookies=cookies)
assert request.cookies == cookies assert request.cookies == cookies
assert request.cookies == cookies # For request._cookies assert request.cookies == cookies # For request._cookies
def test_request_cookies_without_cookies(app): def test_request_cookies_without_cookies(app):
@app.get("/")
@app.get('/')
def handler(request): def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert request.cookies == {} assert request.cookies == {}
def test_request_port(app): def test_request_port(app):
@app.get("/")
@app.get('/')
def handler(request): def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
port = request.port port = request.port
assert isinstance(port, int) assert isinstance(port, int)
delattr(request, '_socket') delattr(request, "_socket")
delattr(request, '_port') delattr(request, "_port")
port = request.port port = request.port
assert isinstance(port, int) assert isinstance(port, int)
assert hasattr(request, '_socket') assert hasattr(request, "_socket")
assert hasattr(request, '_port') assert hasattr(request, "_port")
def test_request_socket(app): def test_request_socket(app):
@app.get("/")
@app.get('/')
def handler(request): def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
socket = request.socket socket = request.socket
assert isinstance(socket, tuple) assert isinstance(socket, tuple)
@ -689,19 +683,18 @@ def test_request_socket(app):
assert ip == request.ip assert ip == request.ip
assert port == request.port assert port == request.port
delattr(request, '_socket') delattr(request, "_socket")
socket = request.socket socket = request.socket
assert isinstance(socket, tuple) assert isinstance(socket, tuple)
assert hasattr(request, '_socket') assert hasattr(request, "_socket")
def test_request_form_invalid_content_type(app): def test_request_form_invalid_content_type(app):
@app.route("/", methods=["POST"]) @app.route("/", methods=["POST"])
async def post(request): async def post(request):
return text("OK") return text("OK")
request, response = app.test_client.post('/', json={'test': 'OK'}) request, response = app.test_client.post("/", json={"test": "OK"})
assert request.form == {} assert request.form == {}

View File

@ -10,127 +10,128 @@ 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 (HTTPResponse, StreamingHTTPResponse, file, from sanic.response import (
file_stream, json, raw, stream, text) HTTPResponse,
StreamingHTTPResponse,
file,
file_stream,
json,
raw,
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}
def test_response_body_not_a_string(app): def test_response_body_not_a_string(app):
"""Test when a response body sent from the application is not a string""" """Test when a response body sent from the application is not a string"""
random_num = choice(range(1000)) random_num = choice(range(1000))
@app.route('/hello') @app.route("/hello")
async def hello_route(request): async def hello_route(request):
return HTTPResponse(body=random_num) return HTTPResponse(body=random_num)
request, response = app.test_client.get('/hello') request, response = app.test_client.get("/hello")
assert response.text == str(random_num) assert response.text == str(random_num)
async def sample_streaming_fn(response): async def sample_streaming_fn(response):
await response.write('foo,') await response.write("foo,")
await asyncio.sleep(.001) await asyncio.sleep(0.001)
await response.write('bar') await response.write("bar")
def test_method_not_allowed(app): def test_method_not_allowed(app):
@app.get('/') @app.get("/")
async def test_get(request): async def test_get(request):
return response.json({'hello': 'world'}) return response.json({"hello": "world"})
request, response = app.test_client.head('/') request, response = app.test_client.head("/")
assert response.headers['Allow'] == 'GET' assert response.headers["Allow"] == "GET"
request, response = app.test_client.post('/') request, response = app.test_client.post("/")
assert response.headers['Allow'] == 'GET' assert response.headers["Allow"] == "GET"
@app.post('/') @app.post("/")
async def test_post(request): async def test_post(request):
return response.json({'hello': 'world'}) return response.json({"hello": "world"})
request, response = app.test_client.head('/') request, response = app.test_client.head("/")
assert response.status == 405 assert response.status == 405
assert set(response.headers['Allow'].split(', ')) == {'GET', 'POST'} assert set(response.headers["Allow"].split(", ")) == {"GET", "POST"}
assert response.headers['Content-Length'] == '0' assert response.headers["Content-Length"] == "0"
request, response = app.test_client.patch('/') request, response = app.test_client.patch("/")
assert response.status == 405 assert response.status == 405
assert set(response.headers['Allow'].split(', ')) == {'GET', 'POST'} assert set(response.headers["Allow"].split(", ")) == {"GET", "POST"}
assert response.headers['Content-Length'] == '0' assert response.headers["Content-Length"] == "0"
def test_response_header(app): def test_response_header(app):
@app.get("/")
@app.get('/')
async def test(request): async def test(request):
return json({ return json({"ok": True}, headers={"CONTENT-TYPE": "application/json"})
"ok": True
}, headers={
'CONTENT-TYPE': 'application/json'
})
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",
} }
def test_response_content_length(app): def test_response_content_length(app):
@app.get("/response_with_space") @app.get("/response_with_space")
async def response_with_space(request): async def response_with_space(request):
return json({ return json(
"message": "Data", {"message": "Data", "details": "Some Details"},
"details": "Some Details" headers={"CONTENT-TYPE": "application/json"},
}, headers={ )
'CONTENT-TYPE': 'application/json'
})
@app.get("/response_without_space") @app.get("/response_without_space")
async def response_without_space(request): async def response_without_space(request):
return json({ return json(
"message":"Data", {"message": "Data", "details": "Some Details"},
"details":"Some Details" headers={"CONTENT-TYPE": "application/json"},
}, headers={ )
'CONTENT-TYPE': 'application/json'
})
_, response = app.test_client.get("/response_with_space") _, response = app.test_client.get("/response_with_space")
content_length_for_response_with_space = response.headers.get("Content-Length") content_length_for_response_with_space = response.headers.get(
"Content-Length"
)
_, response = app.test_client.get("/response_without_space") _, response = app.test_client.get("/response_without_space")
content_length_for_response_without_space = response.headers.get("Content-Length") content_length_for_response_without_space = response.headers.get(
"Content-Length"
)
assert content_length_for_response_with_space == content_length_for_response_without_space assert (
content_length_for_response_with_space
== content_length_for_response_without_space
)
assert content_length_for_response_with_space == '43' assert content_length_for_response_with_space == "43"
def test_response_content_length_with_different_data_types(app): def test_response_content_length_with_different_data_types(app):
@app.get("/") @app.get("/")
async def get_data_with_different_types(request): async def get_data_with_different_types(request):
# Indentation issues in the Response is intentional. Please do not fix # Indentation issues in the Response is intentional. Please do not fix
return json({ return json(
'bool': True, {"bool": True, "none": None, "string": "string", "number": -1},
'none': None, headers={"CONTENT-TYPE": "application/json"},
'string':'string', )
'number': -1},
headers={
'CONTENT-TYPE': 'application/json'
})
_, response = app.test_client.get("/") _, response = app.test_client.get("/")
assert response.headers.get("Content-Length") == '55' assert response.headers.get("Content-Length") == "55"
@pytest.fixture @pytest.fixture
def json_app(app): def json_app(app):
@app.route("/") @app.route("/")
async def test(request): async def test(request):
return json(JSON_DATA) return json(JSON_DATA)
@ -156,70 +157,72 @@ def json_app(app):
def test_json_response(json_app): def test_json_response(json_app):
from sanic.response import json_dumps from sanic.response import json_dumps
request, response = json_app.test_client.get('/')
request, response = json_app.test_client.get("/")
assert response.status == 200 assert response.status == 200
assert response.text == json_dumps(JSON_DATA) assert response.text == json_dumps(JSON_DATA)
assert response.json == JSON_DATA assert response.json == JSON_DATA
def test_no_content(json_app): def test_no_content(json_app):
request, response = json_app.test_client.get('/no-content') request, response = json_app.test_client.get("/no-content")
assert response.status == 204 assert response.status == 204
assert response.text == '' assert response.text == ""
assert 'Content-Length' not in response.headers assert "Content-Length" not in response.headers
request, response = json_app.test_client.get('/no-content/unmodified') request, response = json_app.test_client.get("/no-content/unmodified")
assert response.status == 304 assert response.status == 304
assert response.text == '' assert response.text == ""
assert 'Content-Length' not in response.headers assert "Content-Length" not in response.headers
assert 'Content-Type' not in response.headers assert "Content-Type" not in response.headers
request, response = json_app.test_client.get('/unmodified') request, response = json_app.test_client.get("/unmodified")
assert response.status == 304 assert response.status == 304
assert response.text == '' assert response.text == ""
assert 'Content-Length' not in response.headers assert "Content-Length" not in response.headers
assert 'Content-Type' not in response.headers assert "Content-Type" not in response.headers
request, response = json_app.test_client.delete('/') request, response = json_app.test_client.delete("/")
assert response.status == 204 assert response.status == 204
assert response.text == '' assert response.text == ""
assert 'Content-Length' not in response.headers assert "Content-Length" not in response.headers
@pytest.fixture @pytest.fixture
def streaming_app(app): def streaming_app(app):
@app.route("/") @app.route("/")
async def test(request): async def test(request):
return stream(sample_streaming_fn, content_type='text/csv') return stream(sample_streaming_fn, content_type="text/csv")
return app return app
def test_streaming_adds_correct_headers(streaming_app): def test_streaming_adds_correct_headers(streaming_app):
request, response = streaming_app.test_client.get('/') request, response = streaming_app.test_client.get("/")
assert response.headers['Transfer-Encoding'] == 'chunked' assert response.headers["Transfer-Encoding"] == "chunked"
assert response.headers['Content-Type'] == 'text/csv' assert response.headers["Content-Type"] == "text/csv"
def test_streaming_returns_correct_content(streaming_app): def test_streaming_returns_correct_content(streaming_app):
request, response = streaming_app.test_client.get('/') request, response = streaming_app.test_client.get("/")
assert response.text == 'foo,bar' assert response.text == "foo,bar"
@pytest.mark.parametrize('status', [200, 201, 400, 401]) @pytest.mark.parametrize("status", [200, 201, 400, 401])
def test_stream_response_status_returns_correct_headers(status): def test_stream_response_status_returns_correct_headers(status):
response = StreamingHTTPResponse(sample_streaming_fn, status=status) response = StreamingHTTPResponse(sample_streaming_fn, status=status)
headers = response.get_headers() headers = response.get_headers()
assert b"HTTP/1.1 %s" % str(status).encode() in headers assert b"HTTP/1.1 %s" % str(status).encode() in headers
@pytest.mark.parametrize('keep_alive_timeout', [10, 20, 30]) @pytest.mark.parametrize("keep_alive_timeout", [10, 20, 30])
def test_stream_response_keep_alive_returns_correct_headers( def test_stream_response_keep_alive_returns_correct_headers(
keep_alive_timeout): keep_alive_timeout
):
response = StreamingHTTPResponse(sample_streaming_fn) response = StreamingHTTPResponse(sample_streaming_fn)
headers = response.get_headers( headers = response.get_headers(
keep_alive=True, keep_alive_timeout=keep_alive_timeout) keep_alive=True, keep_alive_timeout=keep_alive_timeout
)
assert b"Keep-Alive: %s\r\n" % str(keep_alive_timeout).encode() in headers assert b"Keep-Alive: %s\r\n" % str(keep_alive_timeout).encode() in headers
@ -244,19 +247,19 @@ def test_stream_response_writes_correct_content_to_transport(streaming_app):
response.protocol.push_data = mock_push_data response.protocol.push_data = mock_push_data
response.protocol.drain = mock_drain response.protocol.drain = mock_drain
@streaming_app.listener('after_server_start') @streaming_app.listener("after_server_start")
async def run_stream(app, loop): async def run_stream(app, loop):
await response.stream() await response.stream()
assert response.protocol.transport.write.call_args_list[1][0][0] == ( assert response.protocol.transport.write.call_args_list[1][0][0] == (
b'4\r\nfoo,\r\n' b"4\r\nfoo,\r\n"
) )
assert response.protocol.transport.write.call_args_list[2][0][0] == ( assert response.protocol.transport.write.call_args_list[2][0][0] == (
b'3\r\nbar\r\n' b"3\r\nbar\r\n"
) )
assert response.protocol.transport.write.call_args_list[3][0][0] == ( assert response.protocol.transport.write.call_args_list[3][0][0] == (
b'0\r\n\r\n' b"0\r\n\r\n"
) )
app.stop() app.stop()
@ -265,25 +268,23 @@ def test_stream_response_writes_correct_content_to_transport(streaming_app):
def test_stream_response_with_cookies(app): def test_stream_response_with_cookies(app):
@app.route("/") @app.route("/")
async def test(request): async def test(request):
response = stream(sample_streaming_fn, content_type='text/csv') response = stream(sample_streaming_fn, content_type="text/csv")
response.cookies['test'] = 'modified' response.cookies["test"] = "modified"
response.cookies['test'] = 'pass' response.cookies["test"] = "pass"
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"].value == "pass"
def test_stream_response_without_cookies(app): def test_stream_response_without_cookies(app):
@app.route("/") @app.route("/")
async def test(request): async def test(request):
return stream(sample_streaming_fn, content_type='text/csv') return stream(sample_streaming_fn, content_type="text/csv")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.cookies == {} assert response.cookies == {}
@ -292,195 +293,214 @@ def static_file_directory():
"""The static directory to serve""" """The static directory to serve"""
current_file = inspect.getfile(inspect.currentframe()) current_file = inspect.getfile(inspect.currentframe())
current_directory = os.path.dirname(os.path.abspath(current_file)) current_directory = os.path.dirname(os.path.abspath(current_file))
static_directory = os.path.join(current_directory, 'static') static_directory = os.path.join(current_directory, "static")
return static_directory return static_directory
def get_file_content(static_file_directory, file_name): def get_file_content(static_file_directory, file_name):
"""The content of the static file to check""" """The content of the static file to check"""
with open(os.path.join(static_file_directory, file_name), 'rb') as file: with open(os.path.join(static_file_directory, file_name), "rb") as file:
return file.read() return file.read()
@pytest.mark.parametrize('file_name', @pytest.mark.parametrize(
['test.file', 'decode me.txt', 'python.png']) "file_name", ["test.file", "decode me.txt", "python.png"]
@pytest.mark.parametrize('status', [200, 401]) )
@pytest.mark.parametrize("status", [200, 401])
def test_file_response(app, file_name, static_file_directory, status): def test_file_response(app, file_name, static_file_directory, status):
@app.route("/files/<filename>", methods=["GET"])
@app.route('/files/<filename>', methods=['GET'])
def file_route(request, filename): def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename) file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path)) file_path = os.path.abspath(unquote(file_path))
return file(file_path, status=status, return file(
mime_type=guess_type(file_path)[0] or 'text/plain') file_path,
status=status,
mime_type=guess_type(file_path)[0] or "text/plain",
)
request, response = app.test_client.get('/files/{}'.format(file_name)) request, response = app.test_client.get("/files/{}".format(file_name))
assert response.status == status assert response.status == status
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
assert 'Content-Disposition' not in response.headers assert "Content-Disposition" not in response.headers
@pytest.mark.parametrize( @pytest.mark.parametrize(
'source,dest', "source,dest",
[ [
('test.file', 'my_file.txt'), ('decode me.txt', 'readme.md'), ("test.file", "my_file.txt"),
('python.png', 'logo.png') ("decode me.txt", "readme.md"),
] ("python.png", "logo.png"),
],
) )
def test_file_response_custom_filename(app, source, dest, def test_file_response_custom_filename(
static_file_directory): app, source, dest, static_file_directory
):
@app.route('/files/<filename>', methods=['GET']) @app.route("/files/<filename>", methods=["GET"])
def file_route(request, filename): def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename) file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path)) file_path = os.path.abspath(unquote(file_path))
return file(file_path, filename=dest) return file(file_path, filename=dest)
request, response = app.test_client.get('/files/{}'.format(source)) request, response = app.test_client.get("/files/{}".format(source))
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, source) assert response.body == get_file_content(static_file_directory, source)
assert response.headers['Content-Disposition'] == \ assert response.headers[
'attachment; filename="{}"'.format(dest) "Content-Disposition"
] == 'attachment; filename="{}"'.format(dest)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_file_head_response(app, file_name, static_file_directory): def test_file_head_response(app, file_name, static_file_directory):
@app.route("/files/<filename>", methods=["GET", "HEAD"])
@app.route('/files/<filename>', methods=['GET', 'HEAD'])
async def file_route(request, filename): async def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename) file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path)) file_path = os.path.abspath(unquote(file_path))
stats = await async_os.stat(file_path) stats = await async_os.stat(file_path)
headers = dict() headers = dict()
headers['Accept-Ranges'] = 'bytes' headers["Accept-Ranges"] = "bytes"
headers['Content-Length'] = str(stats.st_size) headers["Content-Length"] = str(stats.st_size)
if request.method == "HEAD": if request.method == "HEAD":
return HTTPResponse( return HTTPResponse(
headers=headers, headers=headers,
content_type=guess_type(file_path)[0] or 'text/plain') content_type=guess_type(file_path)[0] or "text/plain",
)
else: else:
return file(file_path, headers=headers, return file(
mime_type=guess_type(file_path)[0] or 'text/plain') file_path,
headers=headers,
mime_type=guess_type(file_path)[0] or "text/plain",
)
request, response = app.test_client.head('/files/{}'.format(file_name)) request, response = app.test_client.head("/files/{}".format(file_name))
assert response.status == 200 assert response.status == 200
assert 'Accept-Ranges' in response.headers assert "Accept-Ranges" in response.headers
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert int(response.headers[ assert int(response.headers["Content-Length"]) == len(
'Content-Length']) == len( get_file_content(static_file_directory, file_name)
get_file_content(static_file_directory, file_name)) )
@pytest.mark.parametrize('file_name',
['test.file', 'decode me.txt', 'python.png'])
def test_file_stream_response(app, file_name, static_file_directory):
@app.route('/files/<filename>', methods=['GET'])
def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path))
return file_stream(file_path, chunk_size=32,
mime_type=guess_type(file_path)[0] or 'text/plain')
request, response = app.test_client.get('/files/{}'.format(file_name))
assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name)
assert 'Content-Disposition' not in response.headers
@pytest.mark.parametrize( @pytest.mark.parametrize(
'source,dest', "file_name", ["test.file", "decode me.txt", "python.png"]
[
('test.file', 'my_file.txt'), ('decode me.txt', 'readme.md'),
('python.png', 'logo.png')
]
) )
def test_file_stream_response_custom_filename(app, source, dest, def test_file_stream_response(app, file_name, static_file_directory):
static_file_directory): @app.route("/files/<filename>", methods=["GET"])
@app.route('/files/<filename>', methods=['GET'])
def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path))
return file_stream(file_path, chunk_size=32, filename=dest)
request, response = app.test_client.get('/files/{}'.format(source))
assert response.status == 200
assert response.body == get_file_content(static_file_directory, source)
assert response.headers['Content-Disposition'] == \
'attachment; filename="{}"'.format(dest)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt'])
def test_file_stream_head_response(app, file_name, static_file_directory):
@app.route('/files/<filename>', methods=['GET', 'HEAD'])
async def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path))
headers = dict()
headers['Accept-Ranges'] = 'bytes'
if request.method == "HEAD":
# Return a normal HTTPResponse, not a
# StreamingHTTPResponse for a HEAD request
stats = await async_os.stat(file_path)
headers['Content-Length'] = str(stats.st_size)
return HTTPResponse(
headers=headers,
content_type=guess_type(file_path)[0] or 'text/plain')
else:
return file_stream(
file_path, chunk_size=32, headers=headers,
mime_type=guess_type(file_path)[0] or 'text/plain'
)
request, response = app.test_client.head('/files/{}'.format(file_name))
assert response.status == 200
# A HEAD request should never be streamed/chunked.
if 'Transfer-Encoding' in response.headers:
assert response.headers['Transfer-Encoding'] != "chunked"
assert 'Accept-Ranges' in response.headers
# A HEAD request should get the Content-Length too
assert 'Content-Length' in response.headers
assert int(response.headers[
'Content-Length']) == len(
get_file_content(static_file_directory, file_name))
@pytest.mark.parametrize('file_name',
['test.file', 'decode me.txt', 'python.png'])
@pytest.mark.parametrize('size,start,end', [
(1024, 0, 1024),
(4096, 1024, 8192),
])
def test_file_stream_response_range(app, file_name, static_file_directory, size, start, end):
Range = namedtuple('Range', ['size', 'start', 'end', 'total'])
total = len(get_file_content(static_file_directory, file_name))
range = Range(size=size, start=start, end=end, total=total)
@app.route('/files/<filename>', methods=['GET'])
def file_route(request, filename): def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename) file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path)) file_path = os.path.abspath(unquote(file_path))
return file_stream( return file_stream(
file_path, file_path,
chunk_size=32, chunk_size=32,
mime_type=guess_type(file_path)[0] or 'text/plain', mime_type=guess_type(file_path)[0] or "text/plain",
_range=range) )
request, response = app.test_client.get('/files/{}'.format(file_name)) request, response = app.test_client.get("/files/{}".format(file_name))
assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name)
assert "Content-Disposition" not in response.headers
@pytest.mark.parametrize(
"source,dest",
[
("test.file", "my_file.txt"),
("decode me.txt", "readme.md"),
("python.png", "logo.png"),
],
)
def test_file_stream_response_custom_filename(
app, source, dest, static_file_directory
):
@app.route("/files/<filename>", methods=["GET"])
def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path))
return file_stream(file_path, chunk_size=32, filename=dest)
request, response = app.test_client.get("/files/{}".format(source))
assert response.status == 200
assert response.body == get_file_content(static_file_directory, source)
assert response.headers[
"Content-Disposition"
] == 'attachment; filename="{}"'.format(dest)
@pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_file_stream_head_response(app, file_name, static_file_directory):
@app.route("/files/<filename>", methods=["GET", "HEAD"])
async def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path))
headers = dict()
headers["Accept-Ranges"] = "bytes"
if request.method == "HEAD":
# Return a normal HTTPResponse, not a
# StreamingHTTPResponse for a HEAD request
stats = await async_os.stat(file_path)
headers["Content-Length"] = str(stats.st_size)
return HTTPResponse(
headers=headers,
content_type=guess_type(file_path)[0] or "text/plain",
)
else:
return file_stream(
file_path,
chunk_size=32,
headers=headers,
mime_type=guess_type(file_path)[0] or "text/plain",
)
request, response = app.test_client.head("/files/{}".format(file_name))
assert response.status == 200
# A HEAD request should never be streamed/chunked.
if "Transfer-Encoding" in response.headers:
assert response.headers["Transfer-Encoding"] != "chunked"
assert "Accept-Ranges" in response.headers
# A HEAD request should get the Content-Length too
assert "Content-Length" in response.headers
assert int(response.headers["Content-Length"]) == len(
get_file_content(static_file_directory, file_name)
)
@pytest.mark.parametrize(
"file_name", ["test.file", "decode me.txt", "python.png"]
)
@pytest.mark.parametrize(
"size,start,end", [(1024, 0, 1024), (4096, 1024, 8192)]
)
def test_file_stream_response_range(
app, file_name, static_file_directory, size, start, end
):
Range = namedtuple("Range", ["size", "start", "end", "total"])
total = len(get_file_content(static_file_directory, file_name))
range = Range(size=size, start=start, end=end, total=total)
@app.route("/files/<filename>", methods=["GET"])
def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path))
return file_stream(
file_path,
chunk_size=32,
mime_type=guess_type(file_path)[0] or "text/plain",
_range=range,
)
request, response = app.test_client.get("/files/{}".format(file_name))
assert response.status == 206 assert response.status == 206
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
assert response.headers['Content-Range'] == 'bytes {}-{}/{}'.format(range.start, range.end, range.total) assert response.headers["Content-Range"] == "bytes {}-{}/{}".format(
range.start, range.end, range.total
)
def test_raw_response(app): def test_raw_response(app):
@app.get("/test")
@app.get('/test')
def handler(request): def handler(request):
return raw(b'raw_response') return raw(b"raw_response")
request, response = app.test_client.get('/test') request, response = app.test_client.get("/test")
assert response.content_type == 'application/octet-stream' assert response.content_type == "application/octet-stream"
assert response.body == b'raw_response' assert response.body == b"raw_response"

View File

@ -5,38 +5,38 @@ from sanic.exceptions import ServiceUnavailable
from sanic.config import Config from sanic.config import Config
Config.RESPONSE_TIMEOUT = 1 Config.RESPONSE_TIMEOUT = 1
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")
response_handler_cancelled_app = Sanic('test_response_handler_cancelled') response_handler_cancelled_app = Sanic("test_response_handler_cancelled")
@response_timeout_app.route('/1') @response_timeout_app.route("/1")
async def handler_1(request): async def handler_1(request):
await asyncio.sleep(2) await asyncio.sleep(2)
return text('OK') return text("OK")
@response_timeout_app.exception(ServiceUnavailable) @response_timeout_app.exception(ServiceUnavailable)
def handler_exception(request, exception): def handler_exception(request, exception):
return text('Response Timeout from error_handler.', 503) return text("Response Timeout from error_handler.", 503)
def test_server_error_response_timeout(): def test_server_error_response_timeout():
request, response = response_timeout_app.test_client.get('/1') request, response = response_timeout_app.test_client.get("/1")
assert response.status == 503 assert response.status == 503
assert response.text == 'Response Timeout from error_handler.' assert response.text == "Response Timeout from error_handler."
@response_timeout_default_app.route('/1') @response_timeout_default_app.route("/1")
async def handler_2(request): async def handler_2(request):
await asyncio.sleep(2) await asyncio.sleep(2)
return text('OK') return text("OK")
def test_default_server_error_response_timeout(): def test_default_server_error_response_timeout():
request, response = response_timeout_default_app.test_client.get('/1') request, response = response_timeout_default_app.test_client.get("/1")
assert response.status == 503 assert response.status == 503
assert response.text == 'Error: Response Timeout' assert response.text == "Error: Response Timeout"
response_handler_cancelled_app.flag = False response_handler_cancelled_app.flag = False
@ -52,14 +52,14 @@ def handler_cancelled(request, exception):
# is already closed when we get a CancelledError. # is already closed when we get a CancelledError.
@response_handler_cancelled_app.route('/1') @response_handler_cancelled_app.route("/1")
async def handler_3(request): async def handler_3(request):
await asyncio.sleep(2) await asyncio.sleep(2)
return text('OK') return text("OK")
def test_response_handler_cancelled(): def test_response_handler_cancelled():
request, response = response_handler_cancelled_app.test_client.get('/1') request, response = response_handler_cancelled_app.test_client.get("/1")
assert response.status == 503 assert response.status == 503
assert response.text == 'Error: Response Timeout' assert response.text == "Error: Response Timeout"
assert response_handler_cancelled_app.flag is False assert response_handler_cancelled_app.flag is False

File diff suppressed because it is too large Load Diff

View File

@ -5,30 +5,30 @@ 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",
'before_server_stop', "before_server_stop",
'after_server_stop' "after_server_stop",
] ]
skipif_no_alarm = pytest.mark.skipif( skipif_no_alarm = pytest.mark.skipif(
not hasattr(signal, 'SIGALRM'), not hasattr(signal, "SIGALRM"),
reason='SIGALRM is not implemented for this platform, we have to come ' reason="SIGALRM is not implemented for this platform, we have to come "
'up with another timeout strategy to test these' "up with another timeout strategy to test these",
) )
def create_listener(listener_name, in_list): def create_listener(listener_name, in_list):
async def _listener(app, loop): async def _listener(app, loop):
print('DEBUG MESSAGE FOR PYTEST for {}'.format(listener_name)) print("DEBUG MESSAGE FOR PYTEST for {}".format(listener_name))
in_list.insert(0, app.name + listener_name) in_list.insert(0, app.name + listener_name)
return _listener return _listener
def start_stop_app(random_name_app, **run_kwargs): def start_stop_app(random_name_app, **run_kwargs):
def stop_on_alarm(signum, frame): def stop_on_alarm(signum, frame):
raise KeyboardInterrupt('SIGINT for sanic to stop gracefully') raise KeyboardInterrupt("SIGINT for sanic to stop gracefully")
signal.signal(signal.SIGALRM, stop_on_alarm) signal.signal(signal.SIGALRM, stop_on_alarm)
signal.alarm(1) signal.alarm(1)
@ -39,19 +39,18 @@ def start_stop_app(random_name_app, **run_kwargs):
@skipif_no_alarm @skipif_no_alarm
@pytest.mark.parametrize('listener_name', AVAILABLE_LISTENERS) @pytest.mark.parametrize("listener_name", AVAILABLE_LISTENERS)
def test_single_listener(app, listener_name): def test_single_listener(app, listener_name):
"""Test that listeners on their own work""" """Test that listeners on their own work"""
output = [] output = []
# Register listener # Register listener
app.listener(listener_name)( app.listener(listener_name)(create_listener(listener_name, output))
create_listener(listener_name, output))
start_stop_app(app) start_stop_app(app)
assert app.name + listener_name == output.pop() assert app.name + listener_name == output.pop()
@skipif_no_alarm @skipif_no_alarm
@pytest.mark.parametrize('listener_name', AVAILABLE_LISTENERS) @pytest.mark.parametrize("listener_name", AVAILABLE_LISTENERS)
def test_register_listener(app, listener_name): def test_register_listener(app, listener_name):
""" """
Test that listeners on their own work with Test that listeners on their own work with
@ -77,11 +76,10 @@ def test_all_listeners(app):
async def test_trigger_before_events_create_server(app): async def test_trigger_before_events_create_server(app):
class MySanicDb: class MySanicDb:
pass pass
@app.listener('before_server_start') @app.listener("before_server_start")
async def init_db(app, loop): async def init_db(app, loop):
app.db = MySanicDb() app.db = MySanicDb()

View File

@ -9,6 +9,7 @@ async def stop(app, loop):
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
app.stop() app.stop()
calledq = Queue() calledq = Queue()
@ -23,13 +24,13 @@ def after(app, loop):
def test_register_system_signals(app): def test_register_system_signals(app):
"""Test if sanic register system signals""" """Test if sanic register system signals"""
@app.route('/hello') @app.route("/hello")
async def hello_route(request): async def hello_route(request):
return HTTPResponse() return HTTPResponse()
app.listener('after_server_start')(stop) app.listener("after_server_start")(stop)
app.listener('before_server_start')(set_loop) app.listener("before_server_start")(set_loop)
app.listener('after_server_stop')(after) app.listener("after_server_stop")(after)
app.run(HOST, PORT) app.run(HOST, PORT)
assert calledq.get() is True assert calledq.get() is True
@ -38,13 +39,13 @@ def test_register_system_signals(app):
def test_dont_register_system_signals(app): def test_dont_register_system_signals(app):
"""Test if sanic don't register system signals""" """Test if sanic don't register system signals"""
@app.route('/hello') @app.route("/hello")
async def hello_route(request): async def hello_route(request):
return HTTPResponse() return HTTPResponse()
app.listener('after_server_start')(stop) app.listener("after_server_start")(stop)
app.listener('before_server_start')(set_loop) app.listener("before_server_start")(set_loop)
app.listener('after_server_stop')(after) app.listener("after_server_stop")(after)
app.run(HOST, PORT, register_sys_signals=False) app.run(HOST, PORT, register_sys_signals=False)
assert calledq.get() is False assert calledq.get() is False

View File

@ -5,12 +5,12 @@ from time import gmtime, strftime
import pytest import pytest
@pytest.fixture(scope='module') @pytest.fixture(scope="module")
def static_file_directory(): def static_file_directory():
"""The static directory to serve""" """The static directory to serve"""
current_file = inspect.getfile(inspect.currentframe()) current_file = inspect.getfile(inspect.currentframe())
current_directory = os.path.dirname(os.path.abspath(current_file)) current_directory = os.path.dirname(os.path.abspath(current_file))
static_directory = os.path.join(current_directory, 'static') static_directory = os.path.join(current_directory, "static")
return static_directory return static_directory
@ -20,336 +20,376 @@ def get_file_path(static_file_directory, file_name):
def get_file_content(static_file_directory, file_name): def get_file_content(static_file_directory, file_name):
"""The content of the static file to check""" """The content of the static file to check"""
with open(get_file_path(static_file_directory, file_name), 'rb') as file: with open(get_file_path(static_file_directory, file_name), "rb") as file:
return file.read() return file.read()
@pytest.fixture(scope='module') @pytest.fixture(scope="module")
def large_file(static_file_directory): def large_file(static_file_directory):
large_file_path = os.path.join(static_file_directory, 'large.file') large_file_path = os.path.join(static_file_directory, "large.file")
size = 2 * 1024 * 1024 size = 2 * 1024 * 1024
with open(large_file_path, 'w') as f: with open(large_file_path, "w") as f:
f.write('a' * size) f.write("a" * size)
yield large_file_path yield large_file_path
os.remove(large_file_path) os.remove(large_file_path)
@pytest.fixture(autouse=True, scope='module') @pytest.fixture(autouse=True, scope="module")
def symlink(static_file_directory): def symlink(static_file_directory):
src = os.path.abspath(os.path.join(os.path.dirname(static_file_directory), 'conftest.py')) src = os.path.abspath(
symlink = 'symlink' os.path.join(os.path.dirname(static_file_directory), "conftest.py")
)
symlink = "symlink"
dist = os.path.join(static_file_directory, symlink) dist = os.path.join(static_file_directory, symlink)
os.symlink(src, dist) os.symlink(src, dist)
yield symlink yield symlink
os.remove(dist) os.remove(dist)
@pytest.fixture(autouse=True, scope='module') @pytest.fixture(autouse=True, scope="module")
def hard_link(static_file_directory): def hard_link(static_file_directory):
src = os.path.abspath(os.path.join(os.path.dirname(static_file_directory), 'conftest.py')) src = os.path.abspath(
hard_link = 'hard_link' os.path.join(os.path.dirname(static_file_directory), "conftest.py")
)
hard_link = "hard_link"
dist = os.path.join(static_file_directory, hard_link) dist = os.path.join(static_file_directory, hard_link)
os.link(src, dist) os.link(src, dist)
yield hard_link yield hard_link
os.remove(dist) os.remove(dist)
@pytest.mark.parametrize('file_name', @pytest.mark.parametrize(
['test.file', 'decode me.txt', 'python.png', 'symlink', 'hard_link']) "file_name",
["test.file", "decode me.txt", "python.png", "symlink", "hard_link"],
)
def test_static_file(app, static_file_directory, file_name): def test_static_file(app, static_file_directory, file_name):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name)) "/testing.file", get_file_path(static_file_directory, file_name)
request, response = app.test_client.get('/testing.file')
assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name)
@pytest.mark.parametrize('file_name', ['test.html'])
def test_static_file_content_type(app, static_file_directory, file_name):
app.static(
'/testing.file',
get_file_path(static_file_directory, file_name),
content_type='text/html; charset=utf-8'
) )
request, response = app.test_client.get('/testing.file') request, response = app.test_client.get("/testing.file")
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
assert response.headers['Content-Type'] == 'text/html; charset=utf-8'
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt', 'symlink', 'hard_link']) @pytest.mark.parametrize("file_name", ["test.html"])
@pytest.mark.parametrize('base_uri', ['/static', '', '/dir']) def test_static_file_content_type(app, static_file_directory, file_name):
app.static(
"/testing.file",
get_file_path(static_file_directory, file_name),
content_type="text/html; charset=utf-8",
)
request, response = app.test_client.get("/testing.file")
assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name)
assert response.headers["Content-Type"] == "text/html; charset=utf-8"
@pytest.mark.parametrize(
"file_name", ["test.file", "decode me.txt", "symlink", "hard_link"]
)
@pytest.mark.parametrize("base_uri", ["/static", "", "/dir"])
def test_static_directory(app, file_name, base_uri, static_file_directory): def test_static_directory(app, file_name, base_uri, static_file_directory):
app.static(base_uri, static_file_directory) app.static(base_uri, static_file_directory)
request, response = app.test_client.get( request, response = app.test_client.get(
uri='{}/{}'.format(base_uri, file_name)) uri="{}/{}".format(base_uri, file_name)
)
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_head_request(app, file_name, static_file_directory): def test_static_head_request(app, file_name, static_file_directory):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True)
request, response = app.test_client.head('/testing.file')
assert response.status == 200
assert 'Accept-Ranges' in response.headers
assert 'Content-Length' in response.headers
assert int(response.headers[
'Content-Length']) == len(
get_file_content(static_file_directory, file_name))
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt'])
def test_static_content_range_correct(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
headers = {
'Range': 'bytes=12-19'
}
request, response = app.test_client.get('/testing.file', headers=headers)
assert response.status == 206
assert 'Content-Length' in response.headers
assert 'Content-Range' in response.headers
static_content = bytes(get_file_content(
static_file_directory, file_name))[12:20]
assert int(response.headers[
'Content-Length']) == len(static_content)
assert response.body == static_content
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt'])
def test_static_content_range_front(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
headers = {
'Range': 'bytes=12-'
}
request, response = app.test_client.get('/testing.file', headers=headers)
assert response.status == 206
assert 'Content-Length' in response.headers
assert 'Content-Range' in response.headers
static_content = bytes(get_file_content(
static_file_directory, file_name))[12:]
assert int(response.headers[
'Content-Length']) == len(static_content)
assert response.body == static_content
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt'])
def test_static_content_range_back(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
headers = {
'Range': 'bytes=-12'
}
request, response = app.test_client.get('/testing.file', headers=headers)
assert response.status == 206
assert 'Content-Length' in response.headers
assert 'Content-Range' in response.headers
static_content = bytes(get_file_content(
static_file_directory, file_name))[-12:]
assert int(response.headers[
'Content-Length']) == len(static_content)
assert response.body == static_content
@pytest.mark.parametrize('use_modified_since', [True, False])
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt'])
def test_static_content_range_empty(app, file_name, static_file_directory, use_modified_since):
app.static(
'/testing.file',
get_file_path(static_file_directory, file_name), get_file_path(static_file_directory, file_name),
use_content_range=True, use_content_range=True,
use_modified_since=use_modified_since
) )
request, response = app.test_client.get('/testing.file') request, response = app.test_client.head("/testing.file")
assert response.status == 200 assert response.status == 200
assert 'Content-Length' in response.headers assert "Accept-Ranges" in response.headers
assert 'Content-Range' not in response.headers assert "Content-Length" in response.headers
assert int(response.headers['Content-Length']) == \ assert int(response.headers["Content-Length"]) == len(
len(get_file_content(static_file_directory, file_name)) get_file_content(static_file_directory, file_name)
)
@pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_correct(app, file_name, static_file_directory):
app.static(
"/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
headers = {"Range": "bytes=12-19"}
request, response = app.test_client.get("/testing.file", headers=headers)
assert response.status == 206
assert "Content-Length" in response.headers
assert "Content-Range" in response.headers
static_content = bytes(get_file_content(static_file_directory, file_name))[
12:20
]
assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content
@pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_front(app, file_name, static_file_directory):
app.static(
"/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
headers = {"Range": "bytes=12-"}
request, response = app.test_client.get("/testing.file", headers=headers)
assert response.status == 206
assert "Content-Length" in response.headers
assert "Content-Range" in response.headers
static_content = bytes(get_file_content(static_file_directory, file_name))[
12:
]
assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content
@pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_back(app, file_name, static_file_directory):
app.static(
"/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
headers = {"Range": "bytes=-12"}
request, response = app.test_client.get("/testing.file", headers=headers)
assert response.status == 206
assert "Content-Length" in response.headers
assert "Content-Range" in response.headers
static_content = bytes(get_file_content(static_file_directory, file_name))[
-12:
]
assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content
@pytest.mark.parametrize("use_modified_since", [True, False])
@pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_empty(
app, file_name, static_file_directory, use_modified_since
):
app.static(
"/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
use_modified_since=use_modified_since,
)
request, response = app.test_client.get("/testing.file")
assert response.status == 200
assert "Content-Length" in response.headers
assert "Content-Range" not in response.headers
assert int(response.headers["Content-Length"]) == len(
get_file_content(static_file_directory, file_name)
)
assert response.body == bytes( assert response.body == bytes(
get_file_content(static_file_directory, file_name)) get_file_content(static_file_directory, file_name)
)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_error(app, file_name, static_file_directory): def test_static_content_range_error(app, file_name, static_file_directory):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
headers = { headers = {"Range": "bytes=1-0"}
'Range': 'bytes=1-0' request, response = app.test_client.get("/testing.file", headers=headers)
}
request, response = app.test_client.get('/testing.file', headers=headers)
assert response.status == 416 assert response.status == 416
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
assert response.headers['Content-Range'] == "bytes */%s" % ( assert response.headers["Content-Range"] == "bytes */%s" % (
len(get_file_content(static_file_directory, file_name)),) len(get_file_content(static_file_directory, file_name)),
)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_invalid_unit(app, file_name, static_file_directory): def test_static_content_range_invalid_unit(
app, file_name, static_file_directory
):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
unit = 'bit' unit = "bit"
headers = { headers = {"Range": "{}=1-0".format(unit)}
'Range': '{}=1-0'.format(unit) request, response = app.test_client.get("/testing.file", headers=headers)
}
request, response = app.test_client.get('/testing.file', headers=headers)
assert response.status == 416 assert response.status == 416
assert response.text == "Error: {} is not a valid Range Type".format(unit) assert response.text == "Error: {} is not a valid Range Type".format(unit)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_invalid_start(app, file_name, static_file_directory): def test_static_content_range_invalid_start(
app, file_name, static_file_directory
):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
start = 'start' start = "start"
headers = { headers = {"Range": "bytes={}-0".format(start)}
'Range': 'bytes={}-0'.format(start) request, response = app.test_client.get("/testing.file", headers=headers)
}
request, response = app.test_client.get('/testing.file', headers=headers)
assert response.status == 416 assert response.status == 416
assert response.text == "Error: '{}' is invalid for Content Range".format(start) assert response.text == "Error: '{}' is invalid for Content Range".format(
start
)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_invalid_end(app, file_name, static_file_directory): def test_static_content_range_invalid_end(
app, file_name, static_file_directory
):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
end = 'end' end = "end"
headers = { headers = {"Range": "bytes=1-{}".format(end)}
'Range': 'bytes=1-{}'.format(end) request, response = app.test_client.get("/testing.file", headers=headers)
}
request, response = app.test_client.get('/testing.file', headers=headers)
assert response.status == 416 assert response.status == 416
assert response.text == "Error: '{}' is invalid for Content Range".format(end) assert response.text == "Error: '{}' is invalid for Content Range".format(
end
)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_invalid_parameters(app, file_name, static_file_directory): def test_static_content_range_invalid_parameters(
app, file_name, static_file_directory
):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
headers = { headers = {"Range": "bytes=-"}
'Range': 'bytes=-' request, response = app.test_client.get("/testing.file", headers=headers)
}
request, response = app.test_client.get('/testing.file', headers=headers)
assert response.status == 416 assert response.status == 416
assert response.text == "Error: Invalid for Content Range parameters" assert response.text == "Error: Invalid for Content Range parameters"
@pytest.mark.parametrize('file_name', @pytest.mark.parametrize(
['test.file', 'decode me.txt', 'python.png']) "file_name", ["test.file", "decode me.txt", "python.png"]
)
def test_static_file_specified_host(app, static_file_directory, file_name): def test_static_file_specified_host(app, static_file_directory, file_name):
app.static( app.static(
'/testing.file', "/testing.file",
get_file_path(static_file_directory, file_name), get_file_path(static_file_directory, file_name),
host="www.example.com" host="www.example.com",
) )
headers = {"Host": "www.example.com"} headers = {"Host": "www.example.com"}
request, response = app.test_client.get('/testing.file', headers=headers) request, response = app.test_client.get("/testing.file", headers=headers)
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
request, response = app.test_client.get('/testing.file') request, response = app.test_client.get("/testing.file")
assert response.status == 404 assert response.status == 404
@pytest.mark.parametrize('use_modified_since', [True, False]) @pytest.mark.parametrize("use_modified_since", [True, False])
@pytest.mark.parametrize('stream_large_files', [True, 1024]) @pytest.mark.parametrize("stream_large_files", [True, 1024])
@pytest.mark.parametrize('file_name', ['test.file', 'large.file']) @pytest.mark.parametrize("file_name", ["test.file", "large.file"])
def test_static_stream_large_file(app, static_file_directory, file_name, use_modified_since, stream_large_files, large_file): def test_static_stream_large_file(
app,
static_file_directory,
file_name,
use_modified_since,
stream_large_files,
large_file,
):
app.static( app.static(
'/testing.file', "/testing.file",
get_file_path(static_file_directory, file_name), get_file_path(static_file_directory, file_name),
use_modified_since=use_modified_since, use_modified_since=use_modified_since,
stream_large_files=stream_large_files stream_large_files=stream_large_files,
) )
request, response = app.test_client.get('/testing.file') request, response = app.test_client.get("/testing.file")
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt', 'python.png']) @pytest.mark.parametrize(
"file_name", ["test.file", "decode me.txt", "python.png"]
)
def test_use_modified_since(app, static_file_directory, file_name): def test_use_modified_since(app, static_file_directory, file_name):
file_stat = os.stat(get_file_path(static_file_directory, file_name)) file_stat = os.stat(get_file_path(static_file_directory, file_name))
modified_since = strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime(file_stat.st_mtime)) modified_since = strftime(
"%a, %d %b %Y %H:%M:%S GMT", gmtime(file_stat.st_mtime)
)
app.static( app.static(
'/testing.file', "/testing.file",
get_file_path(static_file_directory, file_name), get_file_path(static_file_directory, file_name),
use_modified_since=True use_modified_since=True,
) )
request, response = app.test_client.get( request, response = app.test_client.get(
'/testing.file', headers={'If-Modified-Since': modified_since}) "/testing.file", headers={"If-Modified-Since": modified_since}
)
assert response.status == 304 assert response.status == 304
def test_file_not_found(app, static_file_directory): def test_file_not_found(app, static_file_directory):
app.static('/static', static_file_directory) app.static("/static", static_file_directory)
request, response = app.test_client.get('/static/not_found') request, response = app.test_client.get("/static/not_found")
assert response.status == 404 assert response.status == 404
assert response.text == 'Error: File not found' assert response.text == "Error: File not found"
@pytest.mark.parametrize('static_name', ['_static_name', 'static']) @pytest.mark.parametrize("static_name", ["_static_name", "static"])
@pytest.mark.parametrize('file_name', ['test.html']) @pytest.mark.parametrize("file_name", ["test.html"])
def test_static_name(app, static_file_directory, static_name, file_name): def test_static_name(app, static_file_directory, static_name, file_name):
app.static('/static', static_file_directory, name=static_name) app.static("/static", static_file_directory, name=static_name)
request, response = app.test_client.get('/static/{}'.format(file_name)) request, response = app.test_client.get("/static/{}".format(file_name))
assert response.status == 200 assert response.status == 200
@pytest.mark.parametrize('file_name', @pytest.mark.parametrize("file_name", ["test.file"])
['test.file'])
def test_static_remove_route(app, static_file_directory, file_name): def test_static_remove_route(app, static_file_directory, file_name):
app.static( app.static(
'/testing.file', "/testing.file", get_file_path(static_file_directory, file_name)
get_file_path(static_file_directory, file_name)
) )
request, response = app.test_client.get('/testing.file') request, response = app.test_client.get("/testing.file")
assert response.status == 200 assert response.status == 200
app.remove_route('/testing.file') app.remove_route("/testing.file")
request, response = app.test_client.get('/testing.file') request, response = app.test_client.get("/testing.file")
assert response.status == 404 assert response.status == 404

View File

@ -9,27 +9,39 @@ from sanic.exceptions import URLBuildError
import string import string
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")
URL_FOR_VALUE2 = '/myurl?arg1=v1&arg1=v2#anchor' URL_FOR_VALUE2 = "/myurl?arg1=v1&arg1=v2#anchor"
URL_FOR_ARGS3 = dict( URL_FOR_ARGS3 = dict(
arg1='v1', _anchor='anchor', _scheme='http', arg1="v1",
_server='{}:{}'.format(test_host, test_port), _external=True _anchor="anchor",
_scheme="http",
_server="{}:{}".format(test_host, test_port),
_external=True,
)
URL_FOR_VALUE3 = "http://{}:{}/myurl?arg1=v1#anchor".format(
test_host, test_port
)
URL_FOR_ARGS4 = dict(
arg1="v1",
_anchor="anchor",
_external=True,
_server="http://{}:{}".format(test_host, test_port),
)
URL_FOR_VALUE4 = "http://{}:{}/myurl?arg1=v1#anchor".format(
test_host, test_port
) )
URL_FOR_VALUE3 = 'http://{}:{}/myurl?arg1=v1#anchor'.format(test_host,
test_port)
URL_FOR_ARGS4 = dict(arg1='v1', _anchor='anchor', _external=True,
_server='http://{}:{}'.format(test_host, test_port))
URL_FOR_VALUE4 = 'http://{}:{}/myurl?arg1=v1#anchor'.format(test_host,
test_port)
def _generate_handlers_from_names(app, l): def _generate_handlers_from_names(app, l):
for name in l: for name in l:
# this is the easiest way to generate functions with dynamic names # this is the easiest way to generate functions with dynamic names
exec('@app.route(name)\ndef {}(request):\n\treturn text("{}")'.format( exec(
name, name)) '@app.route(name)\ndef {}(request):\n\treturn text("{}")'.format(
name, name
)
)
@pytest.fixture @pytest.fixture
@ -45,69 +57,69 @@ def test_simple_url_for_getting(simple_app):
for letter in string.ascii_letters: for letter in string.ascii_letters:
url = simple_app.url_for(letter) url = simple_app.url_for(letter)
assert url == '/{}'.format(letter) assert url == "/{}".format(letter)
request, response = simple_app.test_client.get(url) request, response = simple_app.test_client.get(url)
assert response.status == 200 assert response.status == 200
assert response.text == letter assert response.text == letter
@pytest.mark.parametrize('args,url', @pytest.mark.parametrize(
[(URL_FOR_ARGS1, URL_FOR_VALUE1), "args,url",
(URL_FOR_ARGS2, URL_FOR_VALUE2), [
(URL_FOR_ARGS3, URL_FOR_VALUE3), (URL_FOR_ARGS1, URL_FOR_VALUE1),
(URL_FOR_ARGS4, URL_FOR_VALUE4)]) (URL_FOR_ARGS2, URL_FOR_VALUE2),
(URL_FOR_ARGS3, URL_FOR_VALUE3),
(URL_FOR_ARGS4, URL_FOR_VALUE4),
],
)
def test_simple_url_for_getting_with_more_params(app, args, url): def test_simple_url_for_getting_with_more_params(app, args, url):
@app.route("/myurl")
@app.route('/myurl')
def passes(request): def passes(request):
return text('this should pass') return text("this should pass")
assert url == app.url_for('passes', **args) assert url == app.url_for("passes", **args)
request, response = app.test_client.get(url) request, response = app.test_client.get(url)
assert response.status == 200 assert response.status == 200
assert response.text == 'this should pass' assert response.text == "this should pass"
def test_url_for_with_server_name(app): def test_url_for_with_server_name(app):
server_name = '{}:{}'.format(test_host, test_port) server_name = "{}:{}".format(test_host, test_port)
app.config.update({ app.config.update({"SERVER_NAME": server_name})
'SERVER_NAME': server_name path = "/myurl"
})
path = '/myurl'
@app.route(path) @app.route(path)
def passes(request): def passes(request):
return text('this should pass') return text("this should pass")
url = 'http://{}{}'.format(server_name, path) url = "http://{}{}".format(server_name, path)
assert url == app.url_for('passes', _server=None, _external=True) assert url == app.url_for("passes", _server=None, _external=True)
request, response = app.test_client.get(url) request, response = app.test_client.get(url)
assert response.status == 200 assert response.status == 200
assert response.text == 'this should pass' assert response.text == "this should pass"
def test_fails_if_endpoint_not_found(app): def test_fails_if_endpoint_not_found(app):
@app.route("/fail")
@app.route('/fail')
def fail(request): def fail(request):
return text('this should fail') return text("this should fail")
with pytest.raises(URLBuildError) as e: with pytest.raises(URLBuildError) as e:
app.url_for('passes') app.url_for("passes")
assert str(e.value) == 'Endpoint with name `passes` was not found' assert str(e.value) == "Endpoint with name `passes` was not found"
def test_fails_url_build_if_param_not_passed(app): def test_fails_url_build_if_param_not_passed(app):
url = '/' url = "/"
for letter in string.ascii_letters: for letter in string.ascii_letters:
url += '<{}>/'.format(letter) url += "<{}>/".format(letter)
@app.route(url) @app.route(url)
def fail(request): def fail(request):
return text('this should fail') return text("this should fail")
fail_args = list(string.ascii_letters) fail_args = list(string.ascii_letters)
fail_args.pop() fail_args.pop()
@ -115,130 +127,130 @@ def test_fails_url_build_if_param_not_passed(app):
fail_kwargs = {l: l for l in fail_args} fail_kwargs = {l: l for l in fail_args}
with pytest.raises(URLBuildError) as e: with pytest.raises(URLBuildError) as e:
app.url_for('fail', **fail_kwargs) app.url_for("fail", **fail_kwargs)
assert 'Required parameter `Z` was not passed to url_for' in str(e.value) assert "Required parameter `Z` was not passed to url_for" in str(e.value)
def test_fails_url_build_if_params_not_passed(app): def test_fails_url_build_if_params_not_passed(app):
@app.route("/fail")
@app.route('/fail')
def fail(request): def fail(request):
return text('this should fail') return text("this should fail")
with pytest.raises(ValueError) as e: with pytest.raises(ValueError) as e:
app.url_for('fail', _scheme='http') app.url_for("fail", _scheme="http")
assert str(e.value) == 'When specifying _scheme, _external must be True' assert str(e.value) == "When specifying _scheme, _external must be True"
COMPLEX_PARAM_URL = ( COMPLEX_PARAM_URL = (
'/<foo:int>/<four_letter_string:[A-z]{4}>/' "/<foo:int>/<four_letter_string:[A-z]{4}>/"
'<two_letter_string:[A-z]{2}>/<normal_string>/<some_number:number>') "<two_letter_string:[A-z]{2}>/<normal_string>/<some_number:number>"
)
PASSING_KWARGS = { PASSING_KWARGS = {
'foo': 4, 'four_letter_string': 'woof', "foo": 4,
'two_letter_string': 'ba', 'normal_string': 'normal', "four_letter_string": "woof",
'some_number': '1.001'} "two_letter_string": "ba",
EXPECTED_BUILT_URL = '/4/woof/ba/normal/1.001' "normal_string": "normal",
"some_number": "1.001",
}
EXPECTED_BUILT_URL = "/4/woof/ba/normal/1.001"
def test_fails_with_int_message(app): def test_fails_with_int_message(app):
@app.route(COMPLEX_PARAM_URL) @app.route(COMPLEX_PARAM_URL)
def fail(request): def fail(request):
return text('this should fail') return text("this should fail")
failing_kwargs = dict(PASSING_KWARGS) failing_kwargs = dict(PASSING_KWARGS)
failing_kwargs['foo'] = 'not_int' failing_kwargs["foo"] = "not_int"
with pytest.raises(URLBuildError) as e: with pytest.raises(URLBuildError) as e:
app.url_for('fail', **failing_kwargs) app.url_for("fail", **failing_kwargs)
expected_error = ( expected_error = (
'Value "not_int" for parameter `foo` ' 'Value "not_int" for parameter `foo` '
'does not match pattern for type `int`: \d+') "does not match pattern for type `int`: \d+"
)
assert str(e.value) == expected_error assert str(e.value) == expected_error
def test_fails_with_two_letter_string_message(app): def test_fails_with_two_letter_string_message(app):
@app.route(COMPLEX_PARAM_URL) @app.route(COMPLEX_PARAM_URL)
def fail(request): def fail(request):
return text('this should fail') return text("this should fail")
failing_kwargs = dict(PASSING_KWARGS) failing_kwargs = dict(PASSING_KWARGS)
failing_kwargs['two_letter_string'] = 'foobar' failing_kwargs["two_letter_string"] = "foobar"
with pytest.raises(URLBuildError) as e: with pytest.raises(URLBuildError) as e:
app.url_for('fail', **failing_kwargs) app.url_for("fail", **failing_kwargs)
expected_error = ( expected_error = (
'Value "foobar" for parameter `two_letter_string` ' 'Value "foobar" for parameter `two_letter_string` '
'does not satisfy pattern [A-z]{2}') "does not satisfy pattern [A-z]{2}"
)
assert str(e.value) == expected_error assert str(e.value) == expected_error
def test_fails_with_number_message(app): def test_fails_with_number_message(app):
@app.route(COMPLEX_PARAM_URL) @app.route(COMPLEX_PARAM_URL)
def fail(request): def fail(request):
return text('this should fail') return text("this should fail")
failing_kwargs = dict(PASSING_KWARGS) failing_kwargs = dict(PASSING_KWARGS)
failing_kwargs['some_number'] = 'foo' failing_kwargs["some_number"] = "foo"
with pytest.raises(URLBuildError) as e: with pytest.raises(URLBuildError) as e:
app.url_for('fail', **failing_kwargs) app.url_for("fail", **failing_kwargs)
expected_error = ( expected_error = (
'Value "foo" for parameter `some_number` ' 'Value "foo" for parameter `some_number` '
'does not match pattern for type `float`: [0-9\\\\.]+') "does not match pattern for type `float`: [0-9\\\\.]+"
)
assert str(e.value) == expected_error assert str(e.value) == expected_error
def test_adds_other_supplied_values_as_query_string(app): def test_adds_other_supplied_values_as_query_string(app):
@app.route(COMPLEX_PARAM_URL) @app.route(COMPLEX_PARAM_URL)
def passes(request): def passes(request):
return text('this should pass') return text("this should pass")
new_kwargs = dict(PASSING_KWARGS) new_kwargs = dict(PASSING_KWARGS)
new_kwargs['added_value_one'] = 'one' new_kwargs["added_value_one"] = "one"
new_kwargs['added_value_two'] = 'two' new_kwargs["added_value_two"] = "two"
url = app.url_for('passes', **new_kwargs) url = app.url_for("passes", **new_kwargs)
query = dict(parse_qsl(urlsplit(url).query)) query = dict(parse_qsl(urlsplit(url).query))
assert query['added_value_one'] == 'one' assert query["added_value_one"] == "one"
assert query['added_value_two'] == 'two' assert query["added_value_two"] == "two"
@pytest.fixture @pytest.fixture
def blueprint_app(app): def blueprint_app(app):
first_print = Blueprint('first', url_prefix='/first') first_print = Blueprint("first", url_prefix="/first")
second_print = Blueprint('second', url_prefix='/second') second_print = Blueprint("second", url_prefix="/second")
@first_print.route('/foo') @first_print.route("/foo")
def foo(request): def foo(request):
return text('foo from first') return text("foo from first")
@first_print.route('/foo/<param>') @first_print.route("/foo/<param>")
def foo_with_param(request, param): def foo_with_param(request, param):
return text( return text("foo from first : {}".format(param))
'foo from first : {}'.format(param))
@second_print.route('/foo') # noqa @second_print.route("/foo") # noqa
def foo(request): def foo(request):
return text('foo from second') return text("foo from second")
@second_print.route('/foo/<param>') # noqa @second_print.route("/foo/<param>") # noqa
def foo_with_param(request, param): def foo_with_param(request, param):
return text( return text("foo from second : {}".format(param))
'foo from second : {}'.format(param))
app.blueprint(first_print) app.blueprint(first_print)
app.blueprint(second_print) app.blueprint(second_print)
@ -247,66 +259,65 @@ def blueprint_app(app):
def test_blueprints_are_named_correctly(blueprint_app): def test_blueprints_are_named_correctly(blueprint_app):
first_url = blueprint_app.url_for('first.foo') first_url = blueprint_app.url_for("first.foo")
assert first_url == '/first/foo' assert first_url == "/first/foo"
second_url = blueprint_app.url_for('second.foo') second_url = blueprint_app.url_for("second.foo")
assert second_url == '/second/foo' assert second_url == "/second/foo"
def test_blueprints_work_with_params(blueprint_app): def test_blueprints_work_with_params(blueprint_app):
first_url = blueprint_app.url_for('first.foo_with_param', param='bar') first_url = blueprint_app.url_for("first.foo_with_param", param="bar")
assert first_url == '/first/foo/bar' assert first_url == "/first/foo/bar"
second_url = blueprint_app.url_for('second.foo_with_param', param='bar') second_url = blueprint_app.url_for("second.foo_with_param", param="bar")
assert second_url == '/second/foo/bar' assert second_url == "/second/foo/bar"
@pytest.fixture @pytest.fixture
def methodview_app(app): def methodview_app(app):
class ViewOne(HTTPMethodView): class ViewOne(HTTPMethodView):
def get(self, request): def get(self, request):
return text('I am get method') return text("I am get method")
def post(self, request): def post(self, request):
return text('I am post method') return text("I am post method")
def put(self, request): def put(self, request):
return text('I am put method') return text("I am put method")
def patch(self, request): def patch(self, request):
return text('I am patch method') return text("I am patch method")
def delete(self, request): def delete(self, request):
return text('I am delete method') return text("I am delete method")
app.add_route(ViewOne.as_view('view_one'), '/view_one') app.add_route(ViewOne.as_view("view_one"), "/view_one")
class ViewTwo(HTTPMethodView): class ViewTwo(HTTPMethodView):
def get(self, request): def get(self, request):
return text('I am get method') return text("I am get method")
def post(self, request): def post(self, request):
return text('I am post method') return text("I am post method")
def put(self, request): def put(self, request):
return text('I am put method') return text("I am put method")
def patch(self, request): def patch(self, request):
return text('I am patch method') return text("I am patch method")
def delete(self, request): def delete(self, request):
return text('I am delete method') return text("I am delete method")
app.add_route(ViewTwo.as_view(), '/view_two') app.add_route(ViewTwo.as_view(), "/view_two")
return app return app
def test_methodview_naming(methodview_app): def test_methodview_naming(methodview_app):
viewone_url = methodview_app.url_for('ViewOne') viewone_url = methodview_app.url_for("ViewOne")
viewtwo_url = methodview_app.url_for('ViewTwo') viewtwo_url = methodview_app.url_for("ViewTwo")
assert viewone_url == '/view_one' assert viewone_url == "/view_one"
assert viewtwo_url == '/view_two' assert viewtwo_url == "/view_two"

View File

@ -6,12 +6,12 @@ import pytest
from sanic.blueprints import Blueprint from sanic.blueprints import Blueprint
@pytest.fixture(scope='module') @pytest.fixture(scope="module")
def static_file_directory(): def static_file_directory():
"""The static directory to serve""" """The static directory to serve"""
current_file = inspect.getfile(inspect.currentframe()) current_file = inspect.getfile(inspect.currentframe())
current_directory = os.path.dirname(os.path.abspath(current_file)) current_directory = os.path.dirname(os.path.abspath(current_file))
static_directory = os.path.join(current_directory, 'static') static_directory = os.path.join(current_directory, "static")
return static_directory return static_directory
@ -21,24 +21,28 @@ def get_file_path(static_file_directory, file_name):
def get_file_content(static_file_directory, file_name): def get_file_content(static_file_directory, file_name):
"""The content of the static file to check""" """The content of the static file to check"""
with open(get_file_path(static_file_directory, file_name), 'rb') as file: with open(get_file_path(static_file_directory, file_name), "rb") as file:
return file.read() return file.read()
@pytest.mark.parametrize('file_name', @pytest.mark.parametrize(
['test.file', 'decode me.txt', 'python.png']) "file_name", ["test.file", "decode me.txt", "python.png"]
)
def test_static_file(app, static_file_directory, file_name): def test_static_file(app, static_file_directory, file_name):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name)) "/testing.file", get_file_path(static_file_directory, file_name)
)
app.static( app.static(
'/testing2.file', get_file_path(static_file_directory, file_name), "/testing2.file",
name='testing_file') get_file_path(static_file_directory, file_name),
name="testing_file",
)
uri = app.url_for('static') uri = app.url_for("static")
uri2 = app.url_for('static', filename='any') uri2 = app.url_for("static", filename="any")
uri3 = app.url_for('static', name='static', filename='any') uri3 = app.url_for("static", name="static", filename="any")
assert uri == '/testing.file' assert uri == "/testing.file"
assert uri == uri2 assert uri == uri2
assert uri2 == uri3 assert uri2 == uri3
@ -46,23 +50,25 @@ def test_static_file(app, static_file_directory, file_name):
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
bp = Blueprint('test_bp_static', url_prefix='/bp') bp = Blueprint("test_bp_static", url_prefix="/bp")
bp.static('/testing.file', get_file_path(static_file_directory, file_name)) bp.static("/testing.file", get_file_path(static_file_directory, file_name))
bp.static('/testing2.file', bp.static(
get_file_path(static_file_directory, file_name), "/testing2.file",
name='testing_file') get_file_path(static_file_directory, file_name),
name="testing_file",
)
app.blueprint(bp) app.blueprint(bp)
uri = app.url_for('static', name='test_bp_static.static') uri = app.url_for("static", name="test_bp_static.static")
uri2 = app.url_for('static', name='test_bp_static.static', filename='any') uri2 = app.url_for("static", name="test_bp_static.static", filename="any")
uri3 = app.url_for('test_bp_static.static') uri3 = app.url_for("test_bp_static.static")
uri4 = app.url_for('test_bp_static.static', name='any') uri4 = app.url_for("test_bp_static.static", name="any")
uri5 = app.url_for('test_bp_static.static', filename='any') uri5 = app.url_for("test_bp_static.static", filename="any")
uri6 = app.url_for('test_bp_static.static', name='any', filename='any') uri6 = app.url_for("test_bp_static.static", name="any", filename="any")
assert uri == '/bp/testing.file' assert uri == "/bp/testing.file"
assert uri == uri2 assert uri == uri2
assert uri2 == uri3 assert uri2 == uri3
assert uri3 == uri4 assert uri3 == uri4
@ -74,78 +80,87 @@ def test_static_file(app, static_file_directory, file_name):
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
# test for other parameters # test for other parameters
uri = app.url_for('static', _external=True, _server='http://localhost') uri = app.url_for("static", _external=True, _server="http://localhost")
assert uri == 'http://localhost/testing.file' assert uri == "http://localhost/testing.file"
uri = app.url_for('static', name='test_bp_static.static', uri = app.url_for(
_external=True, _server='http://localhost') "static",
assert uri == 'http://localhost/bp/testing.file' name="test_bp_static.static",
_external=True,
_server="http://localhost",
)
assert uri == "http://localhost/bp/testing.file"
# test for defined name # test for defined name
uri = app.url_for('static', name='testing_file') uri = app.url_for("static", name="testing_file")
assert uri == '/testing2.file' assert uri == "/testing2.file"
request, response = app.test_client.get(uri) request, response = app.test_client.get(uri)
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
uri = app.url_for('static', name='test_bp_static.testing_file') uri = app.url_for("static", name="test_bp_static.testing_file")
assert uri == '/bp/testing2.file' assert uri == "/bp/testing2.file"
assert uri == app.url_for('static', name='test_bp_static.testing_file', assert uri == app.url_for(
filename='any') "static", name="test_bp_static.testing_file", filename="any"
)
request, response = app.test_client.get(uri) request, response = app.test_client.get(uri)
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
@pytest.mark.parametrize('base_uri', ['/static', '', '/dir']) @pytest.mark.parametrize("base_uri", ["/static", "", "/dir"])
def test_static_directory(app, file_name, base_uri, static_file_directory): def test_static_directory(app, file_name, base_uri, static_file_directory):
app.static(base_uri, static_file_directory) app.static(base_uri, static_file_directory)
base_uri2 = base_uri + '/2' base_uri2 = base_uri + "/2"
app.static(base_uri2, static_file_directory, name='uploads') app.static(base_uri2, static_file_directory, name="uploads")
uri = app.url_for('static', name='static', filename=file_name) uri = app.url_for("static", name="static", filename=file_name)
assert uri == '{}/{}'.format(base_uri, file_name) assert uri == "{}/{}".format(base_uri, file_name)
request, response = app.test_client.get(uri) request, response = app.test_client.get(uri)
assert response.status == 200 assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
uri2 = app.url_for('static', name='static', filename='/' + file_name) uri2 = app.url_for("static", name="static", filename="/" + file_name)
uri3 = app.url_for('static', filename=file_name) uri3 = app.url_for("static", filename=file_name)
uri4 = app.url_for('static', filename='/' + file_name) uri4 = app.url_for("static", filename="/" + file_name)
uri5 = app.url_for('static', name='uploads', filename=file_name) uri5 = app.url_for("static", name="uploads", filename=file_name)
uri6 = app.url_for('static', name='uploads', filename='/' + file_name) uri6 = app.url_for("static", name="uploads", filename="/" + file_name)
assert uri == uri2 assert uri == uri2
assert uri2 == uri3 assert uri2 == uri3
assert uri3 == uri4 assert uri3 == uri4
assert uri5 == '{}/{}'.format(base_uri2, file_name) assert uri5 == "{}/{}".format(base_uri2, file_name)
assert uri5 == uri6 assert uri5 == uri6
bp = Blueprint('test_bp_static', url_prefix='/bp') bp = Blueprint("test_bp_static", url_prefix="/bp")
bp.static(base_uri, static_file_directory) bp.static(base_uri, static_file_directory)
bp.static(base_uri2, static_file_directory, name='uploads') bp.static(base_uri2, static_file_directory, name="uploads")
app.blueprint(bp) app.blueprint(bp)
uri = app.url_for('static', name='test_bp_static.static', uri = app.url_for(
filename=file_name) "static", name="test_bp_static.static", filename=file_name
uri2 = app.url_for('static', name='test_bp_static.static', )
filename='/' + file_name) uri2 = app.url_for(
"static", name="test_bp_static.static", filename="/" + file_name
)
uri4 = app.url_for('static', name='test_bp_static.uploads', uri4 = app.url_for(
filename=file_name) "static", name="test_bp_static.uploads", filename=file_name
uri5 = app.url_for('static', name='test_bp_static.uploads', )
filename='/' + file_name) uri5 = app.url_for(
"static", name="test_bp_static.uploads", filename="/" + file_name
)
assert uri == '/bp{}/{}'.format(base_uri, file_name) assert uri == "/bp{}/{}".format(base_uri, file_name)
assert uri == uri2 assert uri == uri2
assert uri4 == '/bp{}/{}'.format(base_uri2, file_name) assert uri4 == "/bp{}/{}".format(base_uri2, file_name)
assert uri4 == uri5 assert uri4 == uri5
request, response = app.test_client.get(uri) request, response = app.test_client.get(uri)
@ -153,284 +168,323 @@ def test_static_directory(app, file_name, base_uri, static_file_directory):
assert response.body == get_file_content(static_file_directory, file_name) assert response.body == get_file_content(static_file_directory, file_name)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_head_request(app, file_name, static_file_directory): def test_static_head_request(app, file_name, static_file_directory):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
bp = Blueprint('test_bp_static', url_prefix='/bp') bp = Blueprint("test_bp_static", url_prefix="/bp")
bp.static('/testing.file', get_file_path(static_file_directory, file_name), bp.static(
use_content_range=True) "/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
app.blueprint(bp) app.blueprint(bp)
uri = app.url_for('static') uri = app.url_for("static")
assert uri == '/testing.file' assert uri == "/testing.file"
assert uri == app.url_for('static', name='static') assert uri == app.url_for("static", name="static")
assert uri == app.url_for('static', name='static', filename='any') assert uri == app.url_for("static", name="static", filename="any")
request, response = app.test_client.head(uri) request, response = app.test_client.head(uri)
assert response.status == 200 assert response.status == 200
assert 'Accept-Ranges' in response.headers assert "Accept-Ranges" in response.headers
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert int(response.headers[ assert int(response.headers["Content-Length"]) == len(
'Content-Length']) == len( get_file_content(static_file_directory, file_name)
get_file_content(static_file_directory, file_name)) )
# blueprint # blueprint
uri = app.url_for('static', name='test_bp_static.static') uri = app.url_for("static", name="test_bp_static.static")
assert uri == '/bp/testing.file' assert uri == "/bp/testing.file"
assert uri == app.url_for('static', name='test_bp_static.static', assert uri == app.url_for(
filename='any') "static", name="test_bp_static.static", filename="any"
)
request, response = app.test_client.head(uri) request, response = app.test_client.head(uri)
assert response.status == 200 assert response.status == 200
assert 'Accept-Ranges' in response.headers assert "Accept-Ranges" in response.headers
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert int(response.headers[ assert int(response.headers["Content-Length"]) == len(
'Content-Length']) == len( get_file_content(static_file_directory, file_name)
get_file_content(static_file_directory, file_name)) )
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_correct(app, file_name, static_file_directory): def test_static_content_range_correct(app, file_name, static_file_directory):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
bp = Blueprint('test_bp_static', url_prefix='/bp') bp = Blueprint("test_bp_static", url_prefix="/bp")
bp.static('/testing.file', get_file_path(static_file_directory, file_name), bp.static(
use_content_range=True) "/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
app.blueprint(bp) app.blueprint(bp)
headers = { headers = {"Range": "bytes=12-19"}
'Range': 'bytes=12-19' uri = app.url_for("static")
} assert uri == "/testing.file"
uri = app.url_for('static') assert uri == app.url_for("static", name="static")
assert uri == '/testing.file' assert uri == app.url_for("static", name="static", filename="any")
assert uri == app.url_for('static', name='static')
assert uri == app.url_for('static', name='static', filename='any')
request, response = app.test_client.get(uri, headers=headers) request, response = app.test_client.get(uri, headers=headers)
assert response.status == 206 assert response.status == 206
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
static_content = bytes(get_file_content( static_content = bytes(get_file_content(static_file_directory, file_name))[
static_file_directory, file_name))[12:20] 12:20
assert int(response.headers[ ]
'Content-Length']) == len(static_content) assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content assert response.body == static_content
# blueprint # blueprint
uri = app.url_for('static', name='test_bp_static.static') uri = app.url_for("static", name="test_bp_static.static")
assert uri == '/bp/testing.file' assert uri == "/bp/testing.file"
assert uri == app.url_for('static', name='test_bp_static.static', assert uri == app.url_for(
filename='any') "static", name="test_bp_static.static", filename="any"
assert uri == app.url_for('test_bp_static.static') )
assert uri == app.url_for('test_bp_static.static', name='any') assert uri == app.url_for("test_bp_static.static")
assert uri == app.url_for('test_bp_static.static', filename='any') assert uri == app.url_for("test_bp_static.static", name="any")
assert uri == app.url_for('test_bp_static.static', name='any', assert uri == app.url_for("test_bp_static.static", filename="any")
filename='any') assert uri == app.url_for(
"test_bp_static.static", name="any", filename="any"
)
request, response = app.test_client.get(uri, headers=headers) request, response = app.test_client.get(uri, headers=headers)
assert response.status == 206 assert response.status == 206
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
static_content = bytes(get_file_content( static_content = bytes(get_file_content(static_file_directory, file_name))[
static_file_directory, file_name))[12:20] 12:20
assert int(response.headers[ ]
'Content-Length']) == len(static_content) assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content assert response.body == static_content
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_front(app, file_name, static_file_directory): def test_static_content_range_front(app, file_name, static_file_directory):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
bp = Blueprint('test_bp_static', url_prefix='/bp') bp = Blueprint("test_bp_static", url_prefix="/bp")
bp.static('/testing.file', get_file_path(static_file_directory, file_name), bp.static(
use_content_range=True) "/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
app.blueprint(bp) app.blueprint(bp)
headers = { headers = {"Range": "bytes=12-"}
'Range': 'bytes=12-' uri = app.url_for("static")
} assert uri == "/testing.file"
uri = app.url_for('static') assert uri == app.url_for("static", name="static")
assert uri == '/testing.file' assert uri == app.url_for("static", name="static", filename="any")
assert uri == app.url_for('static', name='static')
assert uri == app.url_for('static', name='static', filename='any')
request, response = app.test_client.get(uri, headers=headers) request, response = app.test_client.get(uri, headers=headers)
assert response.status == 206 assert response.status == 206
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
static_content = bytes(get_file_content( static_content = bytes(get_file_content(static_file_directory, file_name))[
static_file_directory, file_name))[12:] 12:
assert int(response.headers[ ]
'Content-Length']) == len(static_content) assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content assert response.body == static_content
# blueprint # blueprint
uri = app.url_for('static', name='test_bp_static.static') uri = app.url_for("static", name="test_bp_static.static")
assert uri == '/bp/testing.file' assert uri == "/bp/testing.file"
assert uri == app.url_for('static', name='test_bp_static.static', assert uri == app.url_for(
filename='any') "static", name="test_bp_static.static", filename="any"
assert uri == app.url_for('test_bp_static.static') )
assert uri == app.url_for('test_bp_static.static', name='any') assert uri == app.url_for("test_bp_static.static")
assert uri == app.url_for('test_bp_static.static', filename='any') assert uri == app.url_for("test_bp_static.static", name="any")
assert uri == app.url_for('test_bp_static.static', name='any', assert uri == app.url_for("test_bp_static.static", filename="any")
filename='any') assert uri == app.url_for(
"test_bp_static.static", name="any", filename="any"
)
request, response = app.test_client.get(uri, headers=headers) request, response = app.test_client.get(uri, headers=headers)
assert response.status == 206 assert response.status == 206
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
static_content = bytes(get_file_content( static_content = bytes(get_file_content(static_file_directory, file_name))[
static_file_directory, file_name))[12:] 12:
assert int(response.headers[ ]
'Content-Length']) == len(static_content) assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content assert response.body == static_content
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_back(app, file_name, static_file_directory): def test_static_content_range_back(app, file_name, static_file_directory):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
bp = Blueprint('test_bp_static', url_prefix='/bp') bp = Blueprint("test_bp_static", url_prefix="/bp")
bp.static('/testing.file', get_file_path(static_file_directory, file_name), bp.static(
use_content_range=True) "/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
app.blueprint(bp) app.blueprint(bp)
headers = { headers = {"Range": "bytes=-12"}
'Range': 'bytes=-12' uri = app.url_for("static")
} assert uri == "/testing.file"
uri = app.url_for('static') assert uri == app.url_for("static", name="static")
assert uri == '/testing.file' assert uri == app.url_for("static", name="static", filename="any")
assert uri == app.url_for('static', name='static')
assert uri == app.url_for('static', name='static', filename='any')
request, response = app.test_client.get(uri, headers=headers) request, response = app.test_client.get(uri, headers=headers)
assert response.status == 206 assert response.status == 206
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
static_content = bytes(get_file_content( static_content = bytes(get_file_content(static_file_directory, file_name))[
static_file_directory, file_name))[-12:] -12:
assert int(response.headers[ ]
'Content-Length']) == len(static_content) assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content assert response.body == static_content
# blueprint # blueprint
uri = app.url_for('static', name='test_bp_static.static') uri = app.url_for("static", name="test_bp_static.static")
assert uri == '/bp/testing.file' assert uri == "/bp/testing.file"
assert uri == app.url_for('static', name='test_bp_static.static', assert uri == app.url_for(
filename='any') "static", name="test_bp_static.static", filename="any"
assert uri == app.url_for('test_bp_static.static') )
assert uri == app.url_for('test_bp_static.static', name='any') assert uri == app.url_for("test_bp_static.static")
assert uri == app.url_for('test_bp_static.static', filename='any') assert uri == app.url_for("test_bp_static.static", name="any")
assert uri == app.url_for('test_bp_static.static', name='any', assert uri == app.url_for("test_bp_static.static", filename="any")
filename='any') assert uri == app.url_for(
"test_bp_static.static", name="any", filename="any"
)
request, response = app.test_client.get(uri, headers=headers) request, response = app.test_client.get(uri, headers=headers)
assert response.status == 206 assert response.status == 206
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
static_content = bytes(get_file_content( static_content = bytes(get_file_content(static_file_directory, file_name))[
static_file_directory, file_name))[-12:] -12:
assert int(response.headers[ ]
'Content-Length']) == len(static_content) assert int(response.headers["Content-Length"]) == len(static_content)
assert response.body == static_content assert response.body == static_content
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_empty(app, file_name, static_file_directory): def test_static_content_range_empty(app, file_name, static_file_directory):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
bp = Blueprint('test_bp_static', url_prefix='/bp') bp = Blueprint("test_bp_static", url_prefix="/bp")
bp.static('/testing.file', get_file_path(static_file_directory, file_name), bp.static(
use_content_range=True) "/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
app.blueprint(bp) app.blueprint(bp)
uri = app.url_for('static') uri = app.url_for("static")
assert uri == '/testing.file' assert uri == "/testing.file"
assert uri == app.url_for('static', name='static') assert uri == app.url_for("static", name="static")
assert uri == app.url_for('static', name='static', filename='any') assert uri == app.url_for("static", name="static", filename="any")
request, response = app.test_client.get(uri) request, response = app.test_client.get(uri)
assert response.status == 200 assert response.status == 200
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' not in response.headers assert "Content-Range" not in response.headers
assert int(response.headers['Content-Length']) == \ assert int(response.headers["Content-Length"]) == len(
len(get_file_content(static_file_directory, file_name)) get_file_content(static_file_directory, file_name)
)
assert response.body == bytes( assert response.body == bytes(
get_file_content(static_file_directory, file_name)) get_file_content(static_file_directory, file_name)
)
# blueprint # blueprint
uri = app.url_for('static', name='test_bp_static.static') uri = app.url_for("static", name="test_bp_static.static")
assert uri == '/bp/testing.file' assert uri == "/bp/testing.file"
assert uri == app.url_for('static', name='test_bp_static.static', assert uri == app.url_for(
filename='any') "static", name="test_bp_static.static", filename="any"
assert uri == app.url_for('test_bp_static.static') )
assert uri == app.url_for('test_bp_static.static', name='any') assert uri == app.url_for("test_bp_static.static")
assert uri == app.url_for('test_bp_static.static', filename='any') assert uri == app.url_for("test_bp_static.static", name="any")
assert uri == app.url_for('test_bp_static.static', name='any', assert uri == app.url_for("test_bp_static.static", filename="any")
filename='any') assert uri == app.url_for(
"test_bp_static.static", name="any", filename="any"
)
request, response = app.test_client.get(uri) request, response = app.test_client.get(uri)
assert response.status == 200 assert response.status == 200
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' not in response.headers assert "Content-Range" not in response.headers
assert int(response.headers['Content-Length']) == \ assert int(response.headers["Content-Length"]) == len(
len(get_file_content(static_file_directory, file_name)) get_file_content(static_file_directory, file_name)
)
assert response.body == bytes( assert response.body == bytes(
get_file_content(static_file_directory, file_name)) get_file_content(static_file_directory, file_name)
)
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize("file_name", ["test.file", "decode me.txt"])
def test_static_content_range_error(app, file_name, static_file_directory): def test_static_content_range_error(app, file_name, static_file_directory):
app.static( app.static(
'/testing.file', get_file_path(static_file_directory, file_name), "/testing.file",
use_content_range=True) get_file_path(static_file_directory, file_name),
use_content_range=True,
)
bp = Blueprint('test_bp_static', url_prefix='/bp') bp = Blueprint("test_bp_static", url_prefix="/bp")
bp.static('/testing.file', get_file_path(static_file_directory, file_name), bp.static(
use_content_range=True) "/testing.file",
get_file_path(static_file_directory, file_name),
use_content_range=True,
)
app.blueprint(bp) app.blueprint(bp)
headers = { headers = {"Range": "bytes=1-0"}
'Range': 'bytes=1-0' uri = app.url_for("static")
} assert uri == "/testing.file"
uri = app.url_for('static') assert uri == app.url_for("static", name="static")
assert uri == '/testing.file' assert uri == app.url_for("static", name="static", filename="any")
assert uri == app.url_for('static', name='static')
assert uri == app.url_for('static', name='static', filename='any')
request, response = app.test_client.get(uri, headers=headers) request, response = app.test_client.get(uri, headers=headers)
assert response.status == 416 assert response.status == 416
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
assert response.headers['Content-Range'] == "bytes */%s" % ( assert response.headers["Content-Range"] == "bytes */%s" % (
len(get_file_content(static_file_directory, file_name)),) len(get_file_content(static_file_directory, file_name)),
)
# blueprint # blueprint
uri = app.url_for('static', name='test_bp_static.static') uri = app.url_for("static", name="test_bp_static.static")
assert uri == '/bp/testing.file' assert uri == "/bp/testing.file"
assert uri == app.url_for('static', name='test_bp_static.static', assert uri == app.url_for(
filename='any') "static", name="test_bp_static.static", filename="any"
assert uri == app.url_for('test_bp_static.static') )
assert uri == app.url_for('test_bp_static.static', name='any') assert uri == app.url_for("test_bp_static.static")
assert uri == app.url_for('test_bp_static.static', filename='any') assert uri == app.url_for("test_bp_static.static", name="any")
assert uri == app.url_for('test_bp_static.static', name='any', assert uri == app.url_for("test_bp_static.static", filename="any")
filename='any') assert uri == app.url_for(
"test_bp_static.static", name="any", filename="any"
)
request, response = app.test_client.get(uri, headers=headers) request, response = app.test_client.get(uri, headers=headers)
assert response.status == 416 assert response.status == 416
assert 'Content-Length' in response.headers assert "Content-Length" in response.headers
assert 'Content-Range' in response.headers assert "Content-Range" in response.headers
assert response.headers['Content-Range'] == "bytes */%s" % ( assert response.headers["Content-Range"] == "bytes */%s" % (
len(get_file_content(static_file_directory, file_name)),) len(get_file_content(static_file_directory, file_name)),
)

View File

@ -6,49 +6,46 @@ from sanic.response import text
# UTF-8 # UTF-8
# ------------------------------------------------------------ # # ------------------------------------------------------------ #
def test_utf8_query_string(app): def test_utf8_query_string(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text('OK') return text("OK")
request, response = app.test_client.get('/', params=[("utf8", '')]) request, response = app.test_client.get("/", params=[("utf8", "")])
assert request.args.get('utf8') == '' assert request.args.get("utf8") == ""
def test_utf8_response(app): def test_utf8_response(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text('') return text("")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == '' assert response.text == ""
def skip_test_utf8_route(app): def skip_test_utf8_route(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text('OK') return text("OK")
# UTF-8 Paths are not supported # UTF-8 Paths are not supported
request, response = app.test_client.get('/✓') request, response = app.test_client.get("/✓")
assert response.text == 'OK' assert response.text == "OK"
def test_utf8_post_json(app): def test_utf8_post_json(app):
@app.route("/")
@app.route('/')
async def handler(request): async def handler(request):
return text('OK') return text("OK")
payload = {'test': ''} payload = {"test": ""}
headers = {'content-type': 'application/json'} headers = {"content-type": "application/json"}
request, response = app.test_client.get( request, response = app.test_client.get(
'/', "/", data=json_dumps(payload), headers=headers
data=json_dumps(payload), headers=headers) )
assert request.json.get('test') == '' assert request.json.get("test") == ""
assert response.text == 'OK' assert response.text == "OK"

View File

@ -2,67 +2,63 @@ from sanic.response import text
def test_vhosts(app): def test_vhosts(app):
@app.route("/", host="example.com")
@app.route('/', host="example.com")
async def handler1(request): async def handler1(request):
return text("You're at example.com!") return text("You're at example.com!")
@app.route('/', host="subdomain.example.com") @app.route("/", host="subdomain.example.com")
async def handler2(request): async def handler2(request):
return text("You're at subdomain.example.com!") return text("You're at subdomain.example.com!")
headers = {"Host": "example.com"} headers = {"Host": "example.com"}
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert response.text == "You're at example.com!" assert response.text == "You're at example.com!"
headers = {"Host": "subdomain.example.com"} headers = {"Host": "subdomain.example.com"}
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert response.text == "You're at subdomain.example.com!" assert response.text == "You're at subdomain.example.com!"
def test_vhosts_with_list(app): def test_vhosts_with_list(app):
@app.route("/", host=["hello.com", "world.com"])
@app.route('/', host=["hello.com", "world.com"])
async def handler(request): async def handler(request):
return text("Hello, world!") return text("Hello, world!")
headers = {"Host": "hello.com"} headers = {"Host": "hello.com"}
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert response.text == "Hello, world!" assert response.text == "Hello, world!"
headers = {"Host": "world.com"} headers = {"Host": "world.com"}
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert response.text == "Hello, world!" assert response.text == "Hello, world!"
def test_vhosts_with_defaults(app): def test_vhosts_with_defaults(app):
@app.route("/", host="hello.com")
@app.route('/', host="hello.com")
async def handler1(request): async def handler1(request):
return text("Hello, world!") return text("Hello, world!")
@app.route('/') @app.route("/")
async def handler2(request): async def handler2(request):
return text("default") return text("default")
headers = {"Host": "hello.com"} headers = {"Host": "hello.com"}
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert response.text == "Hello, world!" assert response.text == "Hello, world!"
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == "default" assert response.text == "default"
def test_remove_vhost_route(app): def test_remove_vhost_route(app):
@app.route("/", host="example.com")
@app.route('/', host="example.com")
async def handler1(request): async def handler1(request):
return text("You're at example.com!") return text("You're at example.com!")
headers = {"Host": "example.com"} headers = {"Host": "example.com"}
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert response.status == 200 assert response.status == 200
app.remove_route('/', host="example.com") app.remove_route("/", host="example.com")
request, response = app.test_client.get('/', headers=headers) request, response = app.test_client.get("/", headers=headers)
assert response.status == 404 assert response.status == 404

View File

@ -8,110 +8,100 @@ from sanic.request import Request
from sanic.constants import HTTP_METHODS from sanic.constants import HTTP_METHODS
@pytest.mark.parametrize('method', HTTP_METHODS) @pytest.mark.parametrize("method", HTTP_METHODS)
def test_methods(app, method): def test_methods(app, method):
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
async def get(self, request): async def get(self, request):
assert request.stream is None assert request.stream is None
return text('', headers={'method': 'GET'}) return text("", headers={"method": "GET"})
def post(self, request): def post(self, request):
return text('', headers={'method': 'POST'}) return text("", headers={"method": "POST"})
async def put(self, request): async def put(self, request):
return text('', headers={'method': 'PUT'}) return text("", headers={"method": "PUT"})
def head(self, request): def head(self, request):
return text('', headers={'method': 'HEAD'}) return text("", headers={"method": "HEAD"})
def options(self, request): def options(self, request):
return text('', headers={'method': 'OPTIONS'}) return text("", headers={"method": "OPTIONS"})
async def patch(self, request): async def patch(self, request):
return text('', headers={'method': 'PATCH'}) return text("", headers={"method": "PATCH"})
def delete(self, request): def delete(self, request):
return text('', headers={'method': 'DELETE'}) return text("", headers={"method": "DELETE"})
app.add_route(DummyView.as_view(), '/') app.add_route(DummyView.as_view(), "/")
assert app.is_request_stream is False assert app.is_request_stream is False
request, response = getattr(app.test_client, method.lower())('/') request, response = getattr(app.test_client, method.lower())("/")
assert response.headers['method'] == method assert response.headers["method"] == method
def test_unexisting_methods(app): def test_unexisting_methods(app):
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
def get(self, request): def get(self, request):
return text('I am get method') return text("I am get method")
app.add_route(DummyView.as_view(), '/') app.add_route(DummyView.as_view(), "/")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'I am get method' assert response.text == "I am get method"
request, response = app.test_client.post('/') request, response = app.test_client.post("/")
assert response.text == 'Error: Method POST not allowed for URL /' assert response.text == "Error: Method POST not allowed for URL /"
def test_argument_methods(app): def test_argument_methods(app):
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
def get(self, request, my_param_here): def get(self, request, my_param_here):
return text('I am get method with %s' % my_param_here) return text("I am get method with %s" % my_param_here)
app.add_route(DummyView.as_view(), '/<my_param_here>') app.add_route(DummyView.as_view(), "/<my_param_here>")
request, response = app.test_client.get('/test123') request, response = app.test_client.get("/test123")
assert response.text == 'I am get method with test123' assert response.text == "I am get method with test123"
def test_with_bp(app): def test_with_bp(app):
bp = Blueprint('test_text') bp = Blueprint("test_text")
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
def get(self, request): def get(self, request):
assert request.stream is None assert request.stream is None
return text('I am get method') return text("I am get method")
bp.add_route(DummyView.as_view(), '/') bp.add_route(DummyView.as_view(), "/")
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert app.is_request_stream is False assert app.is_request_stream is False
assert response.text == 'I am get method' assert response.text == "I am get method"
def test_with_bp_with_url_prefix(app): def test_with_bp_with_url_prefix(app):
bp = Blueprint('test_text', url_prefix='/test1') bp = Blueprint("test_text", url_prefix="/test1")
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
def get(self, request): def get(self, request):
return text('I am get method') return text("I am get method")
bp.add_route(DummyView.as_view(), '/') bp.add_route(DummyView.as_view(), "/")
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get('/test1/') request, response = app.test_client.get("/test1/")
assert response.text == 'I am get method' assert response.text == "I am get method"
def test_with_middleware(app): def test_with_middleware(app):
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
def get(self, request): def get(self, request):
return text('I am get method') return text("I am get method")
app.add_route(DummyView.as_view(), '/') app.add_route(DummyView.as_view(), "/")
results = [] results = []
@ -119,41 +109,39 @@ def test_with_middleware(app):
async def handler(request): async def handler(request):
results.append(request) results.append(request)
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'I am get method' assert response.text == "I am get method"
assert type(results[0]) is Request assert type(results[0]) is Request
def test_with_middleware_response(app): def test_with_middleware_response(app):
results = [] results = []
@app.middleware('request') @app.middleware("request")
async def process_request(request): async def process_request(request):
results.append(request) results.append(request)
@app.middleware('response') @app.middleware("response")
async def process_response(request, response): async def process_response(request, response):
results.append(request) results.append(request)
results.append(response) results.append(response)
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
def get(self, request): def get(self, request):
return text('I am get method') return text("I am get method")
app.add_route(DummyView.as_view(), '/') app.add_route(DummyView.as_view(), "/")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'I am get method' assert response.text == "I am get method"
assert type(results[0]) is Request assert type(results[0]) is Request
assert type(results[1]) is Request assert type(results[1]) is Request
assert isinstance(results[2], HTTPResponse) assert isinstance(results[2], HTTPResponse)
def test_with_custom_class_methods(app): def test_with_custom_class_methods(app):
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
global_var = 0 global_var = 0
@ -162,12 +150,14 @@ def test_with_custom_class_methods(app):
def get(self, request): def get(self, request):
self._iternal_method() self._iternal_method()
return text('I am get method and global var ' return text(
'is {}'.format(self.global_var)) "I am get method and global var "
"is {}".format(self.global_var)
)
app.add_route(DummyView.as_view(), '/') app.add_route(DummyView.as_view(), "/")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'I am get method and global var is 10' assert response.text == "I am get method and global var is 10"
def test_with_decorator(app): def test_with_decorator(app):
@ -177,84 +167,86 @@ def test_with_decorator(app):
def decorator(*args, **kwargs): def decorator(*args, **kwargs):
results.append(1) results.append(1)
return view(*args, **kwargs) return view(*args, **kwargs)
return decorator return decorator
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
decorators = [stupid_decorator] decorators = [stupid_decorator]
def get(self, request): def get(self, request):
return text('I am get method') return text("I am get method")
app.add_route(DummyView.as_view(), '/') app.add_route(DummyView.as_view(), "/")
request, response = app.test_client.get('/') request, response = app.test_client.get("/")
assert response.text == 'I am get method' assert response.text == "I am get method"
assert results[0] == 1 assert results[0] == 1
def test_composition_view_rejects_incorrect_methods(): def test_composition_view_rejects_incorrect_methods():
def foo(request): def foo(request):
return text('Foo') return text("Foo")
view = CompositionView() view = CompositionView()
with pytest.raises(InvalidUsage) as e: with pytest.raises(InvalidUsage) as e:
view.add(['GET', 'FOO'], foo) view.add(["GET", "FOO"], foo)
assert str(e.value) == 'FOO is not a valid HTTP method.' assert str(e.value) == "FOO is not a valid HTTP method."
def test_composition_view_rejects_duplicate_methods(): def test_composition_view_rejects_duplicate_methods():
def foo(request): def foo(request):
return text('Foo') return text("Foo")
view = CompositionView() view = CompositionView()
with pytest.raises(InvalidUsage) as e: with pytest.raises(InvalidUsage) as e:
view.add(['GET', 'POST', 'GET'], foo) view.add(["GET", "POST", "GET"], foo)
assert str(e.value) == 'Method GET is already registered.' assert str(e.value) == "Method GET is already registered."
@pytest.mark.parametrize('method', HTTP_METHODS) @pytest.mark.parametrize("method", HTTP_METHODS)
def test_composition_view_runs_methods_as_expected(app, method): def test_composition_view_runs_methods_as_expected(app, method):
view = CompositionView() view = CompositionView()
def first(request): def first(request):
assert request.stream is None assert request.stream is None
return text('first method') return text("first method")
view.add(['GET', 'POST', 'PUT'], first)
view.add(['DELETE', 'PATCH'], lambda x: text('second method'))
app.add_route(view, '/') view.add(["GET", "POST", "PUT"], first)
view.add(["DELETE", "PATCH"], lambda x: text("second method"))
app.add_route(view, "/")
assert app.is_request_stream is False assert app.is_request_stream is False
if method in ['GET', 'POST', 'PUT']: if method in ["GET", "POST", "PUT"]:
request, response = getattr(app.test_client, method.lower())('/') request, response = getattr(app.test_client, method.lower())("/")
assert response.text == 'first method' assert response.text == "first method"
response = view(request) response = view(request)
assert response.body.decode() == 'first method' assert response.body.decode() == "first method"
if method in ['DELETE', 'PATCH']: if method in ["DELETE", "PATCH"]:
request, response = getattr(app.test_client, method.lower())('/') request, response = getattr(app.test_client, method.lower())("/")
assert response.text == 'second method' assert response.text == "second method"
response = view(request) response = view(request)
assert response.body.decode() == 'second method' assert response.body.decode() == "second method"
@pytest.mark.parametrize('method', HTTP_METHODS) @pytest.mark.parametrize("method", HTTP_METHODS)
def test_composition_view_rejects_invalid_methods(app, method): def test_composition_view_rejects_invalid_methods(app, method):
view = CompositionView() view = CompositionView()
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method')) view.add(["GET", "POST", "PUT"], lambda x: text("first method"))
app.add_route(view, '/') app.add_route(view, "/")
if method in ['GET', 'POST', 'PUT']: if method in ["GET", "POST", "PUT"]:
request, response = getattr(app.test_client, method.lower())('/') request, response = getattr(app.test_client, method.lower())("/")
assert response.status == 200 assert response.status == 200
assert response.text == 'first method' assert response.text == "first method"
if method in ['DELETE', 'PATCH']: if method in ["DELETE", "PATCH"]:
request, response = getattr(app.test_client, method.lower())('/') request, response = getattr(app.test_client, method.lower())("/")
assert response.status == 405 assert response.status == 405

View File

@ -10,13 +10,13 @@ import asyncio
import pytest import pytest
@pytest.fixture(scope='module') @pytest.fixture(scope="module")
def gunicorn_worker(): def gunicorn_worker():
command = ( command = (
'gunicorn ' "gunicorn "
'--bind 127.0.0.1:1337 ' "--bind 127.0.0.1:1337 "
'--worker-class sanic.worker.GunicornWorker ' "--worker-class sanic.worker.GunicornWorker "
'examples.simple_server:app' "examples.simple_server:app"
) )
worker = subprocess.Popen(shlex.split(command)) worker = subprocess.Popen(shlex.split(command))
time.sleep(3) time.sleep(3)
@ -25,13 +25,12 @@ def gunicorn_worker():
def test_gunicorn_worker(gunicorn_worker): def test_gunicorn_worker(gunicorn_worker):
with urllib.request.urlopen('http://localhost:1337/') as f: with urllib.request.urlopen("http://localhost:1337/") as f:
res = json.loads(f.read(100).decode()) res = json.loads(f.read(100).decode())
assert res['test'] assert res["test"]
class GunicornTestWorker(GunicornWorker): class GunicornTestWorker(GunicornWorker):
def __init__(self): def __init__(self):
self.app = mock.Mock() self.app = mock.Mock()
self.app.callable = Sanic("test_gunicorn_worker") self.app.callable = Sanic("test_gunicorn_worker")
@ -47,7 +46,7 @@ def worker():
def test_worker_init_process(worker): def test_worker_init_process(worker):
with mock.patch('sanic.worker.asyncio') as mock_asyncio: with mock.patch("sanic.worker.asyncio") as mock_asyncio:
try: try:
worker.init_process() worker.init_process()
except TypeError: except TypeError:
@ -65,7 +64,7 @@ def test_worker_init_signals(worker):
def test_handle_abort(worker): def test_handle_abort(worker):
with mock.patch('sanic.worker.sys') as mock_sys: with mock.patch("sanic.worker.sys") as mock_sys:
worker.handle_abort(object(), object()) worker.handle_abort(object(), object())
assert not worker.alive assert not worker.alive
assert worker.exit_code == 1 assert worker.exit_code == 1
@ -83,7 +82,7 @@ def test_run_max_requests_exceeded(worker):
worker.ppid = 1 worker.ppid = 1
worker.alive = True worker.alive = True
sock = mock.Mock() sock = mock.Mock()
sock.cfg_addr = ('localhost', 8080) sock.cfg_addr = ("localhost", 8080)
worker.sockets = [sock] worker.sockets = [sock]
worker.wsgi = mock.Mock() worker.wsgi = mock.Mock()
worker.connections = set() worker.connections = set()
@ -102,8 +101,9 @@ def test_run_max_requests_exceeded(worker):
assert not worker.alive assert not worker.alive
worker.notify.assert_called_with() worker.notify.assert_called_with()
worker.log.info.assert_called_with("Max requests exceeded, shutting " worker.log.info.assert_called_with(
"down: %s", worker) "Max requests exceeded, shutting " "down: %s", worker
)
def test_worker_close(worker): def test_worker_close(worker):
@ -125,11 +125,10 @@ def test_worker_close(worker):
worker.loop = loop worker.loop = loop
server = mock.Mock() server = mock.Mock()
server.close = mock.Mock(wraps=lambda *a, **kw: None) server.close = mock.Mock(wraps=lambda *a, **kw: None)
server.wait_closed = mock.Mock(wraps=asyncio.coroutine( server.wait_closed = mock.Mock(
lambda *a, **kw: None)) wraps=asyncio.coroutine(lambda *a, **kw: None)
worker.servers = { )
server: {"requests_count": 14}, worker.servers = {server: {"requests_count": 14}}
}
worker.max_requests = 10 worker.max_requests = 10
# close worker # close worker