Merge pull request #24 from huge-success/master
merge upstream master branch
This commit is contained in:
commit
cc83c1f0cf
|
@ -1,7 +1,7 @@
|
||||||
[run]
|
[run]
|
||||||
branch = True
|
branch = True
|
||||||
source = sanic
|
source = sanic
|
||||||
omit = site-packages, sanic/utils.py
|
omit = site-packages, sanic/utils.py, sanic/__main__.py
|
||||||
|
|
||||||
[html]
|
[html]
|
||||||
directory = coverage
|
directory = coverage
|
||||||
|
|
|
@ -31,7 +31,7 @@ deploy:
|
||||||
provider: pypi
|
provider: pypi
|
||||||
user: channelcat
|
user: channelcat
|
||||||
password:
|
password:
|
||||||
secure: OgADRQH3+dTL5swGzXkeRJDNbLpFzwqYnXB4iLD0Npvzj9QnKyQVvkbaeq6VmV9dpEFb5ULaAKYQq19CrXYDm28yanUSn6jdJ4SukaHusi7xt07U6H7pmoX/uZ2WZYqCSLM8cSp8TXY/3oV3rY5Jfj/AibE5XTbim5/lrhsvW6NR+ALzxc0URRPAHDZEPpojTCjSTjpY0aDsaKWg4mXVRMFfY3O68j6KaIoukIZLuoHfePLKrbZxaPG5VxNhMHEaICdxVxE/dO+7pQmQxXuIsEOHK1QiVJ9YrSGcNqgEqhN36kYP8dqMeVB07sv8Xa6o/Uax2/wXS2HEJvuwP1YD6WkoZuo9ZB85bcMdg7BV9jJDbVFVPJwc75BnTLHrMa3Q1KrRlKRDBUXBUsQivPuWhFNwUgvEayq2qSI3aRQR4Z0O+DfboEhXYojSoD64/EWBTZ7vhgbvOTGEdukUQSYrKj9P8jc1s8exomTsAiqdFxTUpzfiammUSL+M93lP4urtahl1jjXFX7gd3DzdEEb0NsGkx5lm/qdsty8/TeAvKUmC+RVU6T856W6MqN0P+yGbpWUARcSE7fwztC3SPxwAuxvIN3BHmRhOUHoORPNG2VpfbnscIzBKJR4v0JKzbpi0IDa66K+tCGsCEvQuL4cxVOtoUySPWNSUAyUWWUrGM2k=
|
secure: h7oNDjA/ObDBGK7xt55SV0INHOclMJW/byxMrNxvCZ0JxiRk7WBNtWYt0WJjyf5lO/L0/sfgiAk0GIdFon57S24njSLPAq/a4ptkWZ68s2A+TaF6ezJSZvE9V8khivjoeub90TzfX6P5aukRja1CSxXKJm+v0V8hGE4CZGyCgEDvK3JqIakpXllSDl19DhVftCS/lQZD7AXrZlg1kZnPCMtB5IbCVR4L2bfrSJVNptBi2CqqxacY2MOLu+jv5FzJ2BGVIJ2zoIJS2T+JmGJzpiamF6y8Amv0667i9lg2DXWCtI3PsQzCmwa3F/ZsI+ohUAvJC5yvzP7SyTJyXifRBdJ9O137QkNAHFoJOOY3B4GSnTo8/boajKXEqGiV4h2EgwNjBaR0WJl0pB7HHUCBMkNRWqo6ACB8eCr04tXWXPvkGIc+wPjq960hsUZea1O31MuktYc9Ot6eiFqm7OKoItdi7LxCen1eTj93ePgkiEnVZ+p/04Hh1U7CX31UJMNu5kCvZPIANnAuDsS2SK7Qkr88OAuWL0wmrBcXKOcnVkJtZ5mzx8T54bI1RrSYtFDBLFfOPb0GucSziMBtQpE76qPEauVwIXBk3RnR8N57xBR/lvTaIk758tf+haO0llEO5rVls1zLNZ+VlTzXy7hX0OZbdopIAcCFBFWqWMAdXQc=
|
||||||
on:
|
on:
|
||||||
tags: true
|
tags: true
|
||||||
distributions: "sdist bdist_wheel"
|
distributions: "sdist bdist_wheel"
|
||||||
|
|
77
CHANGELOG.md
77
CHANGELOG.md
|
@ -1,3 +1,80 @@
|
||||||
|
Version 0.8
|
||||||
|
-----------
|
||||||
|
0.8.3
|
||||||
|
- Changes:
|
||||||
|
- Ownership changed to org 'huge-success'
|
||||||
|
|
||||||
|
0.8.0
|
||||||
|
- Changes:
|
||||||
|
- Add Server-Sent Events extension (Innokenty Lebedev)
|
||||||
|
- Graceful handling of request_handler_task cancellation (Ashley Sommer)
|
||||||
|
- Sanitize URL before redirection (aveao)
|
||||||
|
- Add url_bytes to request (johndoe46)
|
||||||
|
- py37 support for travisci (yunstanford)
|
||||||
|
- Auto reloader support for OSX (garyo)
|
||||||
|
- Add UUID route support (Volodymyr Maksymiv)
|
||||||
|
- Add pausable response streams (Ashley Sommer)
|
||||||
|
- Add weakref to request slots (vopankov)
|
||||||
|
- remove ubuntu 12.04 from test fixture due to deprecation (yunstanford)
|
||||||
|
- Allow streaming handlers in add_route (kinware)
|
||||||
|
- use travis_retry for tox (Raphael Deem)
|
||||||
|
- update aiohttp version for test client (yunstanford)
|
||||||
|
- add redirect import for clarity (yingshaoxo)
|
||||||
|
- Update HTTP Entity headers (Arnulfo Solís)
|
||||||
|
- Add register_listener method (Stephan Fitzpatrick)
|
||||||
|
- Remove uvloop/ujson dependencies for Windows (abuckenheimer)
|
||||||
|
- Content-length header on 204/304 responses (Arnulfo Solís)
|
||||||
|
- Extend WebSocketProtocol arguments and add docs (Bob Olde Hampsink, yunstanford)
|
||||||
|
- Update development status from pre-alpha to beta (Maksim Anisenkov)
|
||||||
|
- KeepAlive Timout log level changed to debug (Arnulfo Solís)
|
||||||
|
- Pin pytest to 3.3.2 because of pytest-dev/pytest#3170 (Maksim Aniskenov)
|
||||||
|
- Install Python 3.5 and 3.6 on docker container for tests (Shahin Azad)
|
||||||
|
- Add support for blueprint groups and nesting (Elias Tarhini)
|
||||||
|
- Remove uvloop for windows setup (Aleksandr Kurlov)
|
||||||
|
- Auto Reload (Yaser Amari)
|
||||||
|
- Documentation updates/fixups (multiple contributors)
|
||||||
|
|
||||||
|
- Fixes:
|
||||||
|
- Fix: auto_reload in Linux (Ashley Sommer)
|
||||||
|
- Fix: broken tests for aiohttp >= 3.3.0 (Ashley Sommer)
|
||||||
|
- Fix: disable auto_reload by default on windows (abuckenheimer)
|
||||||
|
- Fix (1143): Turn off access log with gunicorn (hqy)
|
||||||
|
- Fix (1268): Support status code for file response (Cosmo Borsky)
|
||||||
|
- Fix (1266): Add content_type flag to Sanic.static (Cosmo Borsky)
|
||||||
|
- Fix: subprotocols parameter missing from add_websocket_route (ciscorn)
|
||||||
|
- Fix (1242): Responses for CI header (yunstanford)
|
||||||
|
- Fix (1237): add version constraint for websockets (yunstanford)
|
||||||
|
- Fix (1231): memory leak - always release resource (Phillip Xu)
|
||||||
|
- Fix (1221): make request truthy if transport exists (Raphael Deem)
|
||||||
|
- Fix failing tests for aiohttp>=3.1.0 (Ashley Sommer)
|
||||||
|
- Fix try_everything examples (PyManiacGR, kot83)
|
||||||
|
- Fix (1158): default to auto_reload in debug mode (Raphael Deem)
|
||||||
|
- Fix (1136): ErrorHandler.response handler call too restrictive (Julien Castiaux)
|
||||||
|
- Fix: raw requires bytes-like object (cloudship)
|
||||||
|
- Fix (1120): passing a list in to a route decorator's host arg (Timothy Ebiuwhe)
|
||||||
|
- Fix: Bug in multipart/form-data parser (DirkGuijt)
|
||||||
|
- Fix: Exception for missing parameter when value is null (NyanKiyoshi)
|
||||||
|
- Fix: Parameter check (Howie Hu)
|
||||||
|
- Fix (1089): Routing issue with named parameters and different methods (yunstanford)
|
||||||
|
- Fix (1085): Signal handling in multi-worker mode (yunstanford)
|
||||||
|
- Fix: single quote in readme.rst (Cosven)
|
||||||
|
- Fix: method typos (Dmitry Dygalo)
|
||||||
|
- Fix: log_response correct output for ip and port (Wibowo Arindrarto)
|
||||||
|
- Fix (1042): Exception Handling (Raphael Deem)
|
||||||
|
- Fix: Chinese URIs (Howie Hu)
|
||||||
|
- Fix (1079): timeout bug when self.transport is None (Raphael Deem)
|
||||||
|
- Fix (1074): fix strict_slashes when route has slash (Raphael Deem)
|
||||||
|
- Fix (1050): add samesite cookie to cookie keys (Raphael Deem)
|
||||||
|
- Fix (1065): allow add_task after server starts (Raphael Deem)
|
||||||
|
- Fix (1061): double quotes in unauthorized exception (Raphael Deem)
|
||||||
|
- Fix (1062): inject the app in add_task method (Raphael Deem)
|
||||||
|
- Fix: update environment.yml for readthedocs (Eli Uriegas)
|
||||||
|
- Fix: Cancel request task when response timeout is triggered (Jeong YunWon)
|
||||||
|
- Fix (1052): Method not allowed response for RFC7231 compliance (Raphael Deem)
|
||||||
|
- Fix: IPv6 Address and Socket Data Format (Dan Palmer)
|
||||||
|
|
||||||
|
Note: Changelog was unmaintained between 0.1 and 0.7
|
||||||
|
|
||||||
Version 0.1
|
Version 0.1
|
||||||
-----------
|
-----------
|
||||||
- 0.1.7
|
- 0.1.7
|
||||||
|
|
28
README.rst
28
README.rst
|
@ -7,9 +7,9 @@ Sanic is a Flask-like Python 3.5+ web server that's written to go fast. It's ba
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Sanic is developed `on GitHub <https://github.com/channelcat/sanic/>`_. Contributions are welcome!
|
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!
|
||||||
|
|
||||||
If you have a project that utilizes Sanic make sure to comment on the `issue <https://github.com/channelcat/sanic/issues/396>`_ that we use to track those projects!
|
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!
|
||||||
|
|
||||||
Hello World Example
|
Hello World Example
|
||||||
-------------------
|
-------------------
|
||||||
|
@ -47,8 +47,8 @@ Documentation
|
||||||
|
|
||||||
.. |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
|
||||||
.. |Build Status| image:: https://travis-ci.org/channelcat/sanic.svg?branch=master
|
.. |Build Status| image:: https://travis-ci.org/huge-success/sanic.svg?branch=master
|
||||||
:target: https://travis-ci.org/channelcat/sanic
|
:target: https://travis-ci.org/huge-success/sanic
|
||||||
.. |Documentation| image:: https://readthedocs.org/projects/sanic/badge/?version=latest
|
.. |Documentation| image:: https://readthedocs.org/projects/sanic/badge/?version=latest
|
||||||
:target: http://sanic.readthedocs.io/en/latest/?badge=latest
|
:target: http://sanic.readthedocs.io/en/latest/?badge=latest
|
||||||
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
|
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
|
||||||
|
@ -57,23 +57,21 @@ Documentation
|
||||||
:target: https://pypi.python.org/pypi/sanic/
|
:target: https://pypi.python.org/pypi/sanic/
|
||||||
|
|
||||||
|
|
||||||
|
Questions and Discussion
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
`Ask a question or join the conversation <https://community.sanicframework.org/>`_.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
`Non-Core examples <https://github.com/channelcat/sanic/wiki/Examples/>`_. Examples of plugins and Sanic that are outside the scope of Sanic core.
|
`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/channelcat/sanic/wiki/Extensions/>`_. Sanic extensions created by the community.
|
`Extensions <https://github.com/huge-success/sanic/wiki/Extensions/>`_. Sanic extensions created by the community.
|
||||||
|
|
||||||
`Projects <https://github.com/channelcat/sanic/wiki/Projects/>`_. Sanic in production use.
|
`Projects <https://github.com/huge-success/sanic/wiki/Projects/>`_. Sanic in production use.
|
||||||
|
|
||||||
|
|
||||||
TODO
|
|
||||||
----
|
|
||||||
* http2
|
|
||||||
|
|
||||||
Limitations
|
|
||||||
-----------
|
|
||||||
* No wheels for uvloop and httptools on Windows :(
|
|
||||||
|
|
||||||
Final Thoughts
|
Final Thoughts
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
|
@ -31,3 +31,4 @@ A list of Sanic extensions created by the community.
|
||||||
- [Sanic-Auth](https://github.com/pyx/sanic-auth): A minimal backend agnostic session-based user authentication mechanism for Sanic.
|
- [Sanic-Auth](https://github.com/pyx/sanic-auth): A minimal backend agnostic session-based user authentication mechanism for Sanic.
|
||||||
- [Sanic-CookieSession](https://github.com/pyx/sanic-cookiesession): A client-side only, cookie-based session, similar to the built-in session in Flask.
|
- [Sanic-CookieSession](https://github.com/pyx/sanic-cookiesession): A client-side only, cookie-based session, similar to the built-in session in Flask.
|
||||||
- [Sanic-WTF](https://github.com/pyx/sanic-wtf): Sanic-WTF makes using WTForms with Sanic and CSRF (Cross-Site Request Forgery) protection a little bit easier.
|
- [Sanic-WTF](https://github.com/pyx/sanic-wtf): Sanic-WTF makes using WTForms with Sanic and CSRF (Cross-Site Request Forgery) protection a little bit easier.
|
||||||
|
- [sanic-sse](https://github.com/inn0kenty/sanic_sse): [Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) implementation for Sanic.
|
||||||
|
|
|
@ -37,7 +37,7 @@ async def handler(request):
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
body = body.decode('utf-8').replace('1', 'A')
|
body = body.decode('utf-8').replace('1', 'A')
|
||||||
response.write(body)
|
await response.write(body)
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,8 +85,8 @@ app = Sanic(__name__)
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
async def sample_streaming_fn(response):
|
async def sample_streaming_fn(response):
|
||||||
response.write('foo,')
|
await response.write('foo,')
|
||||||
response.write('bar')
|
await response.write('bar')
|
||||||
|
|
||||||
return stream(sample_streaming_fn, content_type='text/csv')
|
return stream(sample_streaming_fn, content_type='text/csv')
|
||||||
```
|
```
|
||||||
|
@ -100,7 +100,7 @@ async def index(request):
|
||||||
conn = await asyncpg.connect(database='test')
|
conn = await asyncpg.connect(database='test')
|
||||||
async with conn.transaction():
|
async with conn.transaction():
|
||||||
async for record in conn.cursor('SELECT generate_series(0, 10)'):
|
async for record in conn.cursor('SELECT generate_series(0, 10)'):
|
||||||
response.write(record[0])
|
await response.write(record[0])
|
||||||
|
|
||||||
return stream(stream_from_db)
|
return stream(stream_from_db)
|
||||||
```
|
```
|
||||||
|
|
|
@ -12,9 +12,10 @@ dependencies:
|
||||||
- zlib=1.2.8=0
|
- zlib=1.2.8=0
|
||||||
- pip:
|
- pip:
|
||||||
- uvloop>=0.5.3
|
- uvloop>=0.5.3
|
||||||
- httptools>=0.0.9
|
- httptools>=0.0.10
|
||||||
- ujson>=1.35
|
- ujson>=1.35
|
||||||
- aiofiles>=0.3.0
|
- aiofiles>=0.3.0
|
||||||
- websockets>=3.2
|
- websockets>=6.0
|
||||||
- sphinxcontrib-asyncio>=0.2.0
|
- sphinxcontrib-asyncio>=0.2.0
|
||||||
|
- multidict>=4.0<5.0
|
||||||
- https://github.com/channelcat/docutils-fork/zipball/master
|
- https://github.com/channelcat/docutils-fork/zipball/master
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
from sanic import Sanic
|
from sanic import Blueprint, Sanic
|
||||||
from sanic import Blueprint
|
from sanic.response import file, json
|
||||||
from sanic.response import json
|
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic(__name__)
|
||||||
blueprint = Blueprint('name', url_prefix='/my_blueprint')
|
blueprint = Blueprint('name', url_prefix='/my_blueprint')
|
||||||
|
@ -19,7 +17,12 @@ async def foo2(request):
|
||||||
return json({'msg': 'hi from blueprint2'})
|
return json({'msg': 'hi from blueprint2'})
|
||||||
|
|
||||||
|
|
||||||
@blueprint3.websocket('/foo')
|
@blueprint3.route('/foo')
|
||||||
|
async def index(request):
|
||||||
|
return await file('websocket.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.websocket('/feed')
|
||||||
async def foo3(request, ws):
|
async def foo3(request, ws):
|
||||||
while True:
|
while True:
|
||||||
data = 'hello!'
|
data = 'hello!'
|
||||||
|
|
|
@ -30,7 +30,7 @@ async def handler(request):
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
body = body.decode('utf-8').replace('1', 'A')
|
body = body.decode('utf-8').replace('1', 'A')
|
||||||
response.write(body)
|
await response.write(body)
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
|
|
||||||
|
|
42
examples/simple_async_view.py
Normal file
42
examples/simple_async_view.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.views import HTTPMethodView
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
app = Sanic('some_name')
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleView(HTTPMethodView):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
return text('I am get method')
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
return text('I am post method')
|
||||||
|
|
||||||
|
def put(self, request):
|
||||||
|
return text('I am put method')
|
||||||
|
|
||||||
|
def patch(self, request):
|
||||||
|
return text('I am patch method')
|
||||||
|
|
||||||
|
def delete(self, request):
|
||||||
|
return text('I am delete method')
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleAsyncView(HTTPMethodView):
|
||||||
|
|
||||||
|
async def get(self, request):
|
||||||
|
return text('I am async get method')
|
||||||
|
|
||||||
|
async def post(self, request):
|
||||||
|
return text('I am async post method')
|
||||||
|
|
||||||
|
async def put(self, request):
|
||||||
|
return text('I am async put method')
|
||||||
|
|
||||||
|
|
||||||
|
app.add_route(SimpleView.as_view(), '/')
|
||||||
|
app.add_route(SimpleAsyncView.as_view(), '/async')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host="0.0.0.0", port=8000, debug=True)
|
|
@ -1,12 +1,13 @@
|
||||||
aiofiles
|
aiofiles
|
||||||
aiohttp>=2.3.0
|
aiohttp>=2.3.0,<=3.2.1
|
||||||
chardet<=2.3.0
|
chardet<=2.3.0
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
coverage
|
coverage
|
||||||
httptools
|
httptools>=0.0.10
|
||||||
flake8
|
flake8
|
||||||
pytest==3.3.2
|
pytest==3.3.2
|
||||||
tox
|
tox
|
||||||
ujson; sys_platform != "win32" and implementation_name == "cpython"
|
ujson; sys_platform != "win32" and implementation_name == "cpython"
|
||||||
uvloop; sys_platform != "win32" and implementation_name == "cpython"
|
uvloop; sys_platform != "win32" and implementation_name == "cpython"
|
||||||
gunicorn
|
gunicorn
|
||||||
|
multidict>=4.0,<5.0
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
aiofiles
|
aiofiles
|
||||||
httptools
|
httptools>=0.0.10
|
||||||
ujson; sys_platform != "win32" and implementation_name == "cpython"
|
ujson; sys_platform != "win32" and implementation_name == "cpython"
|
||||||
uvloop; sys_platform != "win32" and implementation_name == "cpython"
|
uvloop; sys_platform != "win32" and implementation_name == "cpython"
|
||||||
websockets>=5.0,<6.0
|
websockets>=6.0,<7.0
|
||||||
|
multidict>=4.0,<5.0
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from sanic.app import Sanic
|
from sanic.app import Sanic
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
|
|
||||||
__version__ = '0.7.0'
|
__version__ = '0.8.3'
|
||||||
|
|
||||||
__all__ = ['Sanic', 'Blueprint']
|
__all__ = ['Sanic', 'Blueprint']
|
||||||
|
|
51
sanic/app.py
51
sanic/app.py
|
@ -315,13 +315,13 @@ class Sanic:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def add_websocket_route(self, handler, uri, host=None,
|
def add_websocket_route(self, handler, uri, host=None,
|
||||||
strict_slashes=None, name=None):
|
strict_slashes=None, subprotocols=None, name=None):
|
||||||
"""A helper method to register a function as a websocket route."""
|
"""A helper method to register a function as a websocket route."""
|
||||||
if strict_slashes is None:
|
if strict_slashes is None:
|
||||||
strict_slashes = self.strict_slashes
|
strict_slashes = self.strict_slashes
|
||||||
|
|
||||||
return self.websocket(uri, host=host, strict_slashes=strict_slashes,
|
return self.websocket(uri, host=host, strict_slashes=strict_slashes,
|
||||||
name=name)(handler)
|
subprotocols=subprotocols, name=name)(handler)
|
||||||
|
|
||||||
def enable_websocket(self, enable=True):
|
def enable_websocket(self, enable=True):
|
||||||
"""Enable or disable the support for websocket.
|
"""Enable or disable the support for websocket.
|
||||||
|
@ -386,13 +386,14 @@ class Sanic:
|
||||||
def static(self, uri, file_or_directory, pattern=r'/?.+',
|
def static(self, uri, file_or_directory, pattern=r'/?.+',
|
||||||
use_modified_since=True, use_content_range=False,
|
use_modified_since=True, use_content_range=False,
|
||||||
stream_large_files=False, name='static', host=None,
|
stream_large_files=False, name='static', host=None,
|
||||||
strict_slashes=None):
|
strict_slashes=None, content_type=None):
|
||||||
"""Register a root to serve files from. The input can either be a
|
"""Register a root to serve files from. The input can either be a
|
||||||
file or a directory. See
|
file or a directory. See
|
||||||
"""
|
"""
|
||||||
static_register(self, uri, file_or_directory, pattern,
|
static_register(self, uri, file_or_directory, pattern,
|
||||||
use_modified_since, use_content_range,
|
use_modified_since, use_content_range,
|
||||||
stream_large_files, name, host, strict_slashes)
|
stream_large_files, name, host, strict_slashes,
|
||||||
|
content_type)
|
||||||
|
|
||||||
def blueprint(self, blueprint, **options):
|
def blueprint(self, blueprint, **options):
|
||||||
"""Register a blueprint on the application.
|
"""Register a blueprint on the application.
|
||||||
|
@ -570,6 +571,10 @@ class Sanic:
|
||||||
|
|
||||||
:return: Nothing
|
:return: Nothing
|
||||||
"""
|
"""
|
||||||
|
# Define `response` var here to remove warnings about
|
||||||
|
# allocation before assignment below.
|
||||||
|
response = None
|
||||||
|
cancelled = False
|
||||||
try:
|
try:
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Request Middleware
|
# Request Middleware
|
||||||
|
@ -596,6 +601,13 @@ class Sanic:
|
||||||
response = handler(request, *args, **kwargs)
|
response = handler(request, *args, **kwargs)
|
||||||
if isawaitable(response):
|
if isawaitable(response):
|
||||||
response = await response
|
response = await response
|
||||||
|
except CancelledError:
|
||||||
|
# If response handler times out, the server handles the error
|
||||||
|
# and cancels the handle_request job.
|
||||||
|
# In this case, the transport is already closed and we cannot
|
||||||
|
# issue a response.
|
||||||
|
response = None
|
||||||
|
cancelled = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Response Generation Failed
|
# Response Generation Failed
|
||||||
|
@ -621,13 +633,22 @@ class Sanic:
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Response Middleware
|
# Response Middleware
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
|
# Don't run response middleware if response is None
|
||||||
|
if response is not None:
|
||||||
try:
|
try:
|
||||||
response = await self._run_response_middleware(request,
|
response = await self._run_response_middleware(request,
|
||||||
response)
|
response)
|
||||||
|
except CancelledError:
|
||||||
|
# Response middleware can timeout too, as above.
|
||||||
|
response = None
|
||||||
|
cancelled = True
|
||||||
except BaseException:
|
except BaseException:
|
||||||
error_logger.exception(
|
error_logger.exception(
|
||||||
'Exception occurred in one of response middleware handlers'
|
'Exception occurred in one of response '
|
||||||
|
'middleware handlers'
|
||||||
)
|
)
|
||||||
|
if cancelled:
|
||||||
|
raise CancelledError()
|
||||||
|
|
||||||
# pass the response to the correct callback
|
# pass the response to the correct callback
|
||||||
if isinstance(response, StreamingHTTPResponse):
|
if isinstance(response, StreamingHTTPResponse):
|
||||||
|
@ -670,8 +691,8 @@ class Sanic:
|
||||||
"""
|
"""
|
||||||
# Default auto_reload to false
|
# Default auto_reload to false
|
||||||
auto_reload = False
|
auto_reload = False
|
||||||
# If debug is set, default it to true
|
# If debug is set, default it to true (unless on windows)
|
||||||
if debug:
|
if debug and os.name == 'posix':
|
||||||
auto_reload = True
|
auto_reload = True
|
||||||
# Allow for overriding either of the defaults
|
# Allow for overriding either of the defaults
|
||||||
auto_reload = kwargs.get("auto_reload", auto_reload)
|
auto_reload = kwargs.get("auto_reload", auto_reload)
|
||||||
|
@ -687,11 +708,12 @@ class Sanic:
|
||||||
warnings.simplefilter('default')
|
warnings.simplefilter('default')
|
||||||
warnings.warn("stop_event will be removed from future versions.",
|
warnings.warn("stop_event will be removed from future versions.",
|
||||||
DeprecationWarning)
|
DeprecationWarning)
|
||||||
|
# compatibility old access_log params
|
||||||
|
self.config.ACCESS_LOG = access_log
|
||||||
server_settings = self._helper(
|
server_settings = self._helper(
|
||||||
host=host, port=port, debug=debug, ssl=ssl, sock=sock,
|
host=host, port=port, debug=debug, ssl=ssl, sock=sock,
|
||||||
workers=workers, protocol=protocol, backlog=backlog,
|
workers=workers, protocol=protocol, backlog=backlog,
|
||||||
register_sys_signals=register_sys_signals,
|
register_sys_signals=register_sys_signals, auto_reload=auto_reload)
|
||||||
access_log=access_log, auto_reload=auto_reload)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.is_running = True
|
self.is_running = True
|
||||||
|
@ -745,12 +767,12 @@ class Sanic:
|
||||||
warnings.simplefilter('default')
|
warnings.simplefilter('default')
|
||||||
warnings.warn("stop_event will be removed from future versions.",
|
warnings.warn("stop_event will be removed from future versions.",
|
||||||
DeprecationWarning)
|
DeprecationWarning)
|
||||||
|
# compatibility old access_log params
|
||||||
|
self.config.ACCESS_LOG = access_log
|
||||||
server_settings = self._helper(
|
server_settings = self._helper(
|
||||||
host=host, port=port, debug=debug, ssl=ssl, sock=sock,
|
host=host, port=port, debug=debug, ssl=ssl, sock=sock,
|
||||||
loop=get_event_loop(), protocol=protocol,
|
loop=get_event_loop(), protocol=protocol,
|
||||||
backlog=backlog, run_async=True,
|
backlog=backlog, run_async=True)
|
||||||
access_log=access_log)
|
|
||||||
|
|
||||||
# Trigger before_start events
|
# Trigger before_start events
|
||||||
await self.trigger_events(
|
await self.trigger_events(
|
||||||
|
@ -795,8 +817,7 @@ class Sanic:
|
||||||
def _helper(self, host=None, port=None, debug=False,
|
def _helper(self, host=None, port=None, debug=False,
|
||||||
ssl=None, sock=None, workers=1, loop=None,
|
ssl=None, sock=None, workers=1, loop=None,
|
||||||
protocol=HttpProtocol, backlog=100, stop_event=None,
|
protocol=HttpProtocol, backlog=100, stop_event=None,
|
||||||
register_sys_signals=True, run_async=False, access_log=True,
|
register_sys_signals=True, run_async=False, auto_reload=False):
|
||||||
auto_reload=False):
|
|
||||||
"""Helper function used by `run` and `create_server`."""
|
"""Helper function used by `run` and `create_server`."""
|
||||||
if isinstance(ssl, dict):
|
if isinstance(ssl, dict):
|
||||||
# try common aliaseses
|
# try common aliaseses
|
||||||
|
@ -837,7 +858,7 @@ class Sanic:
|
||||||
'loop': loop,
|
'loop': loop,
|
||||||
'register_sys_signals': register_sys_signals,
|
'register_sys_signals': register_sys_signals,
|
||||||
'backlog': backlog,
|
'backlog': backlog,
|
||||||
'access_log': access_log,
|
'access_log': self.config.ACCESS_LOG,
|
||||||
'websocket_max_size': self.config.WEBSOCKET_MAX_SIZE,
|
'websocket_max_size': self.config.WEBSOCKET_MAX_SIZE,
|
||||||
'websocket_max_queue': self.config.WEBSOCKET_MAX_QUEUE,
|
'websocket_max_queue': self.config.WEBSOCKET_MAX_QUEUE,
|
||||||
'websocket_read_limit': self.config.WEBSOCKET_READ_LIMIT,
|
'websocket_read_limit': self.config.WEBSOCKET_READ_LIMIT,
|
||||||
|
|
|
@ -39,6 +39,7 @@ class Config(dict):
|
||||||
self.WEBSOCKET_READ_LIMIT = 2 ** 16
|
self.WEBSOCKET_READ_LIMIT = 2 ** 16
|
||||||
self.WEBSOCKET_WRITE_LIMIT = 2 ** 16
|
self.WEBSOCKET_WRITE_LIMIT = 2 ** 16
|
||||||
self.GRACEFUL_SHUTDOWN_TIMEOUT = 15.0 # 15 sec
|
self.GRACEFUL_SHUTDOWN_TIMEOUT = 15.0 # 15 sec
|
||||||
|
self.ACCESS_LOG = True
|
||||||
|
|
||||||
if load_env:
|
if load_env:
|
||||||
prefix = SANIC_PREFIX if load_env is True else load_env
|
prefix = SANIC_PREFIX if load_env is True else load_env
|
||||||
|
|
|
@ -47,16 +47,15 @@ class CookieJar(dict):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
self.cookie_headers = {}
|
self.cookie_headers = {}
|
||||||
|
self.header_key = "Set-Cookie"
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
# If this cookie doesn't exist, add it to the header keys
|
# If this cookie doesn't exist, add it to the header keys
|
||||||
cookie_header = self.cookie_headers.get(key)
|
if not self.cookie_headers.get(key):
|
||||||
if not cookie_header:
|
|
||||||
cookie = Cookie(key, value)
|
cookie = Cookie(key, value)
|
||||||
cookie['path'] = '/'
|
cookie['path'] = '/'
|
||||||
cookie_header = MultiHeader("Set-Cookie")
|
self.cookie_headers[key] = self.header_key
|
||||||
self.cookie_headers[key] = cookie_header
|
self.headers.add(self.header_key, cookie)
|
||||||
self.headers[cookie_header] = cookie
|
|
||||||
return super().__setitem__(key, cookie)
|
return super().__setitem__(key, cookie)
|
||||||
else:
|
else:
|
||||||
self[key].value = value
|
self[key].value = value
|
||||||
|
@ -67,7 +66,11 @@ class CookieJar(dict):
|
||||||
self[key]['max-age'] = 0
|
self[key]['max-age'] = 0
|
||||||
else:
|
else:
|
||||||
cookie_header = self.cookie_headers[key]
|
cookie_header = self.cookie_headers[key]
|
||||||
del self.headers[cookie_header]
|
# remove it from header
|
||||||
|
cookies = self.headers.popall(cookie_header)
|
||||||
|
for cookie in cookies:
|
||||||
|
if cookie.key != key:
|
||||||
|
self.headers.add(cookie_header, cookie)
|
||||||
del self.cookie_headers[key]
|
del self.cookie_headers[key]
|
||||||
return super().__delitem__(key)
|
return super().__delitem__(key)
|
||||||
|
|
||||||
|
@ -124,18 +127,3 @@ class Cookie(dict):
|
||||||
output.append('%s=%s' % (self._keys[key], value))
|
output.append('%s=%s' % (self._keys[key], value))
|
||||||
|
|
||||||
return "; ".join(output).encode(encoding)
|
return "; ".join(output).encode(encoding)
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
|
||||||
# Header Trickery
|
|
||||||
# ------------------------------------------------------------ #
|
|
||||||
|
|
||||||
|
|
||||||
class MultiHeader:
|
|
||||||
"""String-holding object which allow us to set a header within response
|
|
||||||
that has a unique key, but may contain duplicate header names
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def encode(self):
|
|
||||||
return self.name.encode()
|
|
||||||
|
|
|
@ -74,7 +74,14 @@ def kill_process_children_unix(pid):
|
||||||
with open(children_proc_path) as children_list_file_2:
|
with open(children_proc_path) as children_list_file_2:
|
||||||
children_list_pid_2 = children_list_file_2.read().split()
|
children_list_pid_2 = children_list_file_2.read().split()
|
||||||
for _pid in children_list_pid_2:
|
for _pid in children_list_pid_2:
|
||||||
|
try:
|
||||||
os.kill(int(_pid), signal.SIGTERM)
|
os.kill(int(_pid), signal.SIGTERM)
|
||||||
|
except ProcessLookupError:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
os.kill(int(child_pid), signal.SIGTERM)
|
||||||
|
except ProcessLookupError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
def kill_process_children_osx(pid):
|
def kill_process_children_osx(pid):
|
||||||
|
@ -94,7 +101,7 @@ def kill_process_children(pid):
|
||||||
"""
|
"""
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
kill_process_children_osx(pid)
|
kill_process_children_osx(pid)
|
||||||
elif sys.platform == 'posix':
|
elif sys.platform == 'linux':
|
||||||
kill_process_children_unix(pid)
|
kill_process_children_unix(pid)
|
||||||
else:
|
else:
|
||||||
pass # should signal error here
|
pass # should signal error here
|
||||||
|
@ -136,8 +143,8 @@ def watchdog(sleep_interval):
|
||||||
continue
|
continue
|
||||||
elif mtime > old_time:
|
elif mtime > old_time:
|
||||||
kill_process_children(worker_process.pid)
|
kill_process_children(worker_process.pid)
|
||||||
|
worker_process.terminate()
|
||||||
worker_process = restart_with_reloader()
|
worker_process = restart_with_reloader()
|
||||||
|
|
||||||
mtimes[filename] = mtime
|
mtimes[filename] = mtime
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
@ -48,10 +48,11 @@ class Request(dict):
|
||||||
'app', 'headers', 'version', 'method', '_cookies', 'transport',
|
'app', 'headers', 'version', 'method', '_cookies', 'transport',
|
||||||
'body', 'parsed_json', 'parsed_args', 'parsed_form', 'parsed_files',
|
'body', 'parsed_json', 'parsed_args', 'parsed_form', 'parsed_files',
|
||||||
'_ip', '_parsed_url', 'uri_template', 'stream', '_remote_addr',
|
'_ip', '_parsed_url', 'uri_template', 'stream', '_remote_addr',
|
||||||
'_socket', '_port', '__weakref__'
|
'_socket', '_port', '__weakref__', 'raw_url'
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, url_bytes, headers, version, method, transport):
|
def __init__(self, url_bytes, headers, version, method, transport):
|
||||||
|
self.raw_url = url_bytes
|
||||||
# TODO: Content-Encoding detection
|
# TODO: Content-Encoding detection
|
||||||
self._parsed_url = parse_url(url_bytes)
|
self._parsed_url = parse_url(url_bytes)
|
||||||
self.app = None
|
self.app = None
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from os import path
|
from os import path
|
||||||
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ujson import dumps as json_dumps
|
from ujson import dumps as json_dumps
|
||||||
|
@ -7,6 +8,7 @@ except BaseException:
|
||||||
from json import dumps as json_dumps
|
from json import dumps as json_dumps
|
||||||
|
|
||||||
from aiofiles import open as open_async
|
from aiofiles import open as open_async
|
||||||
|
from multidict import CIMultiDict
|
||||||
|
|
||||||
from sanic import http
|
from sanic import http
|
||||||
from sanic.cookies import CookieJar
|
from sanic.cookies import CookieJar
|
||||||
|
@ -44,7 +46,7 @@ class BaseHTTPResponse:
|
||||||
|
|
||||||
class StreamingHTTPResponse(BaseHTTPResponse):
|
class StreamingHTTPResponse(BaseHTTPResponse):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'transport', 'streaming_fn', 'status',
|
'protocol', 'streaming_fn', 'status',
|
||||||
'content_type', 'headers', '_cookies'
|
'content_type', 'headers', '_cookies'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,10 +55,10 @@ class StreamingHTTPResponse(BaseHTTPResponse):
|
||||||
self.content_type = content_type
|
self.content_type = content_type
|
||||||
self.streaming_fn = streaming_fn
|
self.streaming_fn = streaming_fn
|
||||||
self.status = status
|
self.status = status
|
||||||
self.headers = headers or {}
|
self.headers = CIMultiDict(headers or {})
|
||||||
self._cookies = None
|
self._cookies = None
|
||||||
|
|
||||||
def write(self, data):
|
async def write(self, data):
|
||||||
"""Writes a chunk of data to the streaming response.
|
"""Writes a chunk of data to the streaming response.
|
||||||
|
|
||||||
:param data: bytes-ish data to be written.
|
:param data: bytes-ish data to be written.
|
||||||
|
@ -64,8 +66,9 @@ class StreamingHTTPResponse(BaseHTTPResponse):
|
||||||
if type(data) != bytes:
|
if type(data) != bytes:
|
||||||
data = self._encode_body(data)
|
data = self._encode_body(data)
|
||||||
|
|
||||||
self.transport.write(
|
self.protocol.push_data(
|
||||||
b"%x\r\n%b\r\n" % (len(data), data))
|
b"%x\r\n%b\r\n" % (len(data), data))
|
||||||
|
await self.protocol.drain()
|
||||||
|
|
||||||
async def stream(
|
async def stream(
|
||||||
self, version="1.1", keep_alive=False, keep_alive_timeout=None):
|
self, version="1.1", keep_alive=False, keep_alive_timeout=None):
|
||||||
|
@ -75,10 +78,12 @@ class StreamingHTTPResponse(BaseHTTPResponse):
|
||||||
headers = self.get_headers(
|
headers = self.get_headers(
|
||||||
version, keep_alive=keep_alive,
|
version, keep_alive=keep_alive,
|
||||||
keep_alive_timeout=keep_alive_timeout)
|
keep_alive_timeout=keep_alive_timeout)
|
||||||
self.transport.write(headers)
|
self.protocol.push_data(headers)
|
||||||
|
await self.protocol.drain()
|
||||||
await self.streaming_fn(self)
|
await self.streaming_fn(self)
|
||||||
self.transport.write(b'0\r\n\r\n')
|
self.protocol.push_data(b'0\r\n\r\n')
|
||||||
|
# no need to await drain here after this write, because it is the
|
||||||
|
# very last thing we write and nothing needs to wait for it.
|
||||||
|
|
||||||
def get_headers(
|
def get_headers(
|
||||||
self, version="1.1", keep_alive=False, keep_alive_timeout=None):
|
self, version="1.1", keep_alive=False, keep_alive_timeout=None):
|
||||||
|
@ -124,7 +129,7 @@ class HTTPResponse(BaseHTTPResponse):
|
||||||
self.body = body_bytes
|
self.body = body_bytes
|
||||||
|
|
||||||
self.status = status
|
self.status = status
|
||||||
self.headers = headers or {}
|
self.headers = CIMultiDict(headers or {})
|
||||||
self._cookies = None
|
self._cookies = None
|
||||||
|
|
||||||
def output(
|
def output(
|
||||||
|
@ -231,8 +236,8 @@ def html(body, status=200, headers=None):
|
||||||
content_type="text/html; charset=utf-8")
|
content_type="text/html; charset=utf-8")
|
||||||
|
|
||||||
|
|
||||||
async def file(
|
async def file(location, status=200, mime_type=None, headers=None,
|
||||||
location, mime_type=None, headers=None, filename=None, _range=None):
|
filename=None, _range=None):
|
||||||
"""Return a response object with file data.
|
"""Return a response object with file data.
|
||||||
|
|
||||||
:param location: Location of file on system.
|
:param location: Location of file on system.
|
||||||
|
@ -258,15 +263,14 @@ async def file(
|
||||||
out_stream = await _file.read()
|
out_stream = await _file.read()
|
||||||
|
|
||||||
mime_type = mime_type or guess_type(filename)[0] or 'text/plain'
|
mime_type = mime_type or guess_type(filename)[0] or 'text/plain'
|
||||||
return HTTPResponse(status=200,
|
return HTTPResponse(status=status,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
content_type=mime_type,
|
content_type=mime_type,
|
||||||
body_bytes=out_stream)
|
body_bytes=out_stream)
|
||||||
|
|
||||||
|
|
||||||
async def file_stream(
|
async def file_stream(location, status=200, chunk_size=4096, mime_type=None,
|
||||||
location, chunk_size=4096, mime_type=None, headers=None,
|
headers=None, filename=None, _range=None):
|
||||||
filename=None, _range=None):
|
|
||||||
"""Return a streaming response object with file data.
|
"""Return a streaming response object with file data.
|
||||||
|
|
||||||
:param location: Location of file on system.
|
:param location: Location of file on system.
|
||||||
|
@ -297,13 +301,13 @@ async def file_stream(
|
||||||
if len(content) < 1:
|
if len(content) < 1:
|
||||||
break
|
break
|
||||||
to_send -= len(content)
|
to_send -= len(content)
|
||||||
response.write(content)
|
await response.write(content)
|
||||||
else:
|
else:
|
||||||
while True:
|
while True:
|
||||||
content = await _file.read(chunk_size)
|
content = await _file.read(chunk_size)
|
||||||
if len(content) < 1:
|
if len(content) < 1:
|
||||||
break
|
break
|
||||||
response.write(content)
|
await response.write(content)
|
||||||
finally:
|
finally:
|
||||||
await _file.close()
|
await _file.close()
|
||||||
return # Returning from this fn closes the stream
|
return # Returning from this fn closes the stream
|
||||||
|
@ -313,7 +317,7 @@ async def file_stream(
|
||||||
headers['Content-Range'] = 'bytes %s-%s/%s' % (
|
headers['Content-Range'] = 'bytes %s-%s/%s' % (
|
||||||
_range.start, _range.end, _range.total)
|
_range.start, _range.end, _range.total)
|
||||||
return StreamingHTTPResponse(streaming_fn=_streaming_fn,
|
return StreamingHTTPResponse(streaming_fn=_streaming_fn,
|
||||||
status=200,
|
status=status,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
content_type=mime_type)
|
content_type=mime_type)
|
||||||
|
|
||||||
|
@ -359,8 +363,11 @@ def redirect(to, headers=None, status=302,
|
||||||
"""
|
"""
|
||||||
headers = headers or {}
|
headers = headers or {}
|
||||||
|
|
||||||
|
# URL Quote the URL before redirecting
|
||||||
|
safe_to = quote_plus(to, safe=":/#?&=@[]!$&'()*+,;")
|
||||||
|
|
||||||
# According to RFC 7231, a relative URI is now permitted.
|
# According to RFC 7231, a relative URI is now permitted.
|
||||||
headers['Location'] = to
|
headers['Location'] = safe_to
|
||||||
|
|
||||||
return HTTPResponse(
|
return HTTPResponse(
|
||||||
status=status,
|
status=status,
|
||||||
|
|
|
@ -18,6 +18,7 @@ from time import time
|
||||||
|
|
||||||
from httptools import HttpRequestParser
|
from httptools import HttpRequestParser
|
||||||
from httptools.parser.errors import HttpParserError
|
from httptools.parser.errors import HttpParserError
|
||||||
|
from multidict import CIMultiDict
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import uvloop
|
import uvloop
|
||||||
|
@ -39,25 +40,6 @@ class Signal:
|
||||||
stopped = False
|
stopped = False
|
||||||
|
|
||||||
|
|
||||||
class CIDict(dict):
|
|
||||||
"""Case Insensitive dict where all keys are converted to lowercase
|
|
||||||
This does not maintain the inputted case when calling items() or keys()
|
|
||||||
in favor of speed, since headers are case insensitive
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get(self, key, default=None):
|
|
||||||
return super().get(key.casefold(), default)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return super().__getitem__(key.casefold())
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
return super().__setitem__(key.casefold(), value)
|
|
||||||
|
|
||||||
def __contains__(self, key):
|
|
||||||
return super().__contains__(key.casefold())
|
|
||||||
|
|
||||||
|
|
||||||
class HttpProtocol(asyncio.Protocol):
|
class HttpProtocol(asyncio.Protocol):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
# event loop, connection
|
# event loop, connection
|
||||||
|
@ -73,7 +55,8 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
# connection management
|
# connection management
|
||||||
'_total_request_size', '_request_timeout_handler',
|
'_total_request_size', '_request_timeout_handler',
|
||||||
'_response_timeout_handler', '_keep_alive_timeout_handler',
|
'_response_timeout_handler', '_keep_alive_timeout_handler',
|
||||||
'_last_request_time', '_last_response_time', '_is_stream_handler')
|
'_last_request_time', '_last_response_time', '_is_stream_handler',
|
||||||
|
'_not_paused')
|
||||||
|
|
||||||
def __init__(self, *, loop, request_handler, error_handler,
|
def __init__(self, *, loop, request_handler, error_handler,
|
||||||
signal=Signal(), connections=set(), request_timeout=60,
|
signal=Signal(), connections=set(), request_timeout=60,
|
||||||
|
@ -100,6 +83,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self.request_class = request_class or Request
|
self.request_class = request_class or Request
|
||||||
self.is_request_stream = is_request_stream
|
self.is_request_stream = is_request_stream
|
||||||
self._is_stream_handler = False
|
self._is_stream_handler = False
|
||||||
|
self._not_paused = asyncio.Event(loop=loop)
|
||||||
self._total_request_size = 0
|
self._total_request_size = 0
|
||||||
self._request_timeout_handler = None
|
self._request_timeout_handler = None
|
||||||
self._response_timeout_handler = None
|
self._response_timeout_handler = None
|
||||||
|
@ -114,6 +98,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
if 'requests_count' not in self.state:
|
if 'requests_count' not in self.state:
|
||||||
self.state['requests_count'] = 0
|
self.state['requests_count'] = 0
|
||||||
self._debug = debug
|
self._debug = debug
|
||||||
|
self._not_paused.set()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keep_alive(self):
|
def keep_alive(self):
|
||||||
|
@ -142,6 +127,12 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
if self._keep_alive_timeout_handler:
|
if self._keep_alive_timeout_handler:
|
||||||
self._keep_alive_timeout_handler.cancel()
|
self._keep_alive_timeout_handler.cancel()
|
||||||
|
|
||||||
|
def pause_writing(self):
|
||||||
|
self._not_paused.clear()
|
||||||
|
|
||||||
|
def resume_writing(self):
|
||||||
|
self._not_paused.set()
|
||||||
|
|
||||||
def request_timeout_callback(self):
|
def request_timeout_callback(self):
|
||||||
# See the docstring in the RequestTimeout exception, to see
|
# See the docstring in the RequestTimeout exception, to see
|
||||||
# exactly what this timeout is checking for.
|
# exactly what this timeout is checking for.
|
||||||
|
@ -159,10 +150,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self._request_stream_task.cancel()
|
self._request_stream_task.cancel()
|
||||||
if self._request_handler_task:
|
if self._request_handler_task:
|
||||||
self._request_handler_task.cancel()
|
self._request_handler_task.cancel()
|
||||||
try:
|
self.write_error(RequestTimeout('Request Timeout'))
|
||||||
raise RequestTimeout('Request Timeout')
|
|
||||||
except RequestTimeout as exception:
|
|
||||||
self.write_error(exception)
|
|
||||||
|
|
||||||
def response_timeout_callback(self):
|
def response_timeout_callback(self):
|
||||||
# Check if elapsed time since response was initiated exceeds our
|
# Check if elapsed time since response was initiated exceeds our
|
||||||
|
@ -179,10 +167,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self._request_stream_task.cancel()
|
self._request_stream_task.cancel()
|
||||||
if self._request_handler_task:
|
if self._request_handler_task:
|
||||||
self._request_handler_task.cancel()
|
self._request_handler_task.cancel()
|
||||||
try:
|
self.write_error(ServiceUnavailable('Response Timeout'))
|
||||||
raise ServiceUnavailable('Response Timeout')
|
|
||||||
except ServiceUnavailable as exception:
|
|
||||||
self.write_error(exception)
|
|
||||||
|
|
||||||
def keep_alive_timeout_callback(self):
|
def keep_alive_timeout_callback(self):
|
||||||
# Check if elapsed time since last response exceeds our configured
|
# Check if elapsed time since last response exceeds our configured
|
||||||
|
@ -208,8 +193,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
# memory limits
|
# memory limits
|
||||||
self._total_request_size += len(data)
|
self._total_request_size += len(data)
|
||||||
if self._total_request_size > self.request_max_size:
|
if self._total_request_size > self.request_max_size:
|
||||||
exception = PayloadTooLarge('Payload Too Large')
|
self.write_error(PayloadTooLarge('Payload Too Large'))
|
||||||
self.write_error(exception)
|
|
||||||
|
|
||||||
# Create parser if this is the first time we're receiving data
|
# Create parser if this is the first time we're receiving data
|
||||||
if self.parser is None:
|
if self.parser is None:
|
||||||
|
@ -227,8 +211,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
message = 'Bad Request'
|
message = 'Bad Request'
|
||||||
if self._debug:
|
if self._debug:
|
||||||
message += '\n' + traceback.format_exc()
|
message += '\n' + traceback.format_exc()
|
||||||
exception = InvalidUsage(message)
|
self.write_error(InvalidUsage(message))
|
||||||
self.write_error(exception)
|
|
||||||
|
|
||||||
def on_url(self, url):
|
def on_url(self, url):
|
||||||
if not self.url:
|
if not self.url:
|
||||||
|
@ -242,8 +225,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
if self._header_fragment == b'Content-Length' \
|
if self._header_fragment == b'Content-Length' \
|
||||||
and int(value) > self.request_max_size:
|
and int(value) > self.request_max_size:
|
||||||
exception = PayloadTooLarge('Payload Too Large')
|
self.write_error(PayloadTooLarge('Payload Too Large'))
|
||||||
self.write_error(exception)
|
|
||||||
try:
|
try:
|
||||||
value = value.decode()
|
value = value.decode()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
|
@ -256,7 +238,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
def on_headers_complete(self):
|
def on_headers_complete(self):
|
||||||
self.request = self.request_class(
|
self.request = self.request_class(
|
||||||
url_bytes=self.url,
|
url_bytes=self.url,
|
||||||
headers=CIDict(self.headers),
|
headers=CIMultiDict(self.headers),
|
||||||
version=self.parser.get_http_version(),
|
version=self.parser.get_http_version(),
|
||||||
method=self.parser.get_method().decode(),
|
method=self.parser.get_method().decode(),
|
||||||
transport=self.transport
|
transport=self.transport
|
||||||
|
@ -369,6 +351,12 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self._last_response_time = current_time
|
self._last_response_time = current_time
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
|
||||||
|
async def drain(self):
|
||||||
|
await self._not_paused.wait()
|
||||||
|
|
||||||
|
def push_data(self, data):
|
||||||
|
self.transport.write(data)
|
||||||
|
|
||||||
async def stream_response(self, response):
|
async def stream_response(self, response):
|
||||||
"""
|
"""
|
||||||
Streams a response to the client asynchronously. Attaches
|
Streams a response to the client asynchronously. Attaches
|
||||||
|
@ -378,9 +366,10 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
if self._response_timeout_handler:
|
if self._response_timeout_handler:
|
||||||
self._response_timeout_handler.cancel()
|
self._response_timeout_handler.cancel()
|
||||||
self._response_timeout_handler = None
|
self._response_timeout_handler = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
keep_alive = self.keep_alive
|
keep_alive = self.keep_alive
|
||||||
response.transport = self.transport
|
response.protocol = self
|
||||||
await response.stream(
|
await response.stream(
|
||||||
self.request.version, keep_alive, self.keep_alive_timeout)
|
self.request.version, keep_alive, self.keep_alive_timeout)
|
||||||
self.log_response(response)
|
self.log_response(response)
|
||||||
|
@ -435,7 +424,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self.log_response(response)
|
self.log_response(response)
|
||||||
try:
|
try:
|
||||||
self.transport.close()
|
self.transport.close()
|
||||||
except AttributeError as e:
|
except AttributeError:
|
||||||
logger.debug('Connection lost before server could close it.')
|
logger.debug('Connection lost before server could close it.')
|
||||||
|
|
||||||
def bail_out(self, message, from_error=False):
|
def bail_out(self, message, from_error=False):
|
||||||
|
@ -445,8 +434,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self.transport.get_extra_info('peername'))
|
self.transport.get_extra_info('peername'))
|
||||||
logger.debug('Exception:\n%s', traceback.format_exc())
|
logger.debug('Exception:\n%s', traceback.format_exc())
|
||||||
else:
|
else:
|
||||||
exception = ServerError(message)
|
self.write_error(ServerError(message))
|
||||||
self.write_error(exception)
|
|
||||||
logger.error(message)
|
logger.error(message)
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
|
|
|
@ -19,7 +19,7 @@ from sanic.response import file, file_stream, HTTPResponse
|
||||||
def register(app, uri, file_or_directory, pattern,
|
def register(app, uri, file_or_directory, pattern,
|
||||||
use_modified_since, use_content_range,
|
use_modified_since, use_content_range,
|
||||||
stream_large_files, name='static', host=None,
|
stream_large_files, name='static', host=None,
|
||||||
strict_slashes=None):
|
strict_slashes=None, content_type=None):
|
||||||
# TODO: Though sanic is not a file server, I feel like we should at least
|
# TODO: Though sanic is not a file server, I feel like we should at least
|
||||||
# make a good effort here. Modified-since is nice, but we could
|
# make a good effort here. Modified-since is nice, but we could
|
||||||
# also look into etags, expires, and caching
|
# also look into etags, expires, and caching
|
||||||
|
@ -41,6 +41,7 @@ def register(app, uri, file_or_directory, pattern,
|
||||||
If this is an integer, this represents the
|
If this is an integer, this represents the
|
||||||
threshold size to switch to file_stream()
|
threshold size to switch to file_stream()
|
||||||
:param name: user defined name used for url_for
|
:param name: user defined name used for url_for
|
||||||
|
:param content_type: user defined content type for header
|
||||||
"""
|
"""
|
||||||
# If we're not trying to match a file directly,
|
# If we're not trying to match a file directly,
|
||||||
# serve from the folder
|
# serve from the folder
|
||||||
|
@ -95,10 +96,10 @@ def register(app, uri, file_or_directory, pattern,
|
||||||
del headers['Content-Length']
|
del headers['Content-Length']
|
||||||
for key, value in _range.headers.items():
|
for key, value in _range.headers.items():
|
||||||
headers[key] = value
|
headers[key] = value
|
||||||
|
headers['Content-Type'] = content_type \
|
||||||
|
or guess_type(file_path)[0] or 'text/plain'
|
||||||
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')
|
|
||||||
else:
|
else:
|
||||||
if stream_large_files:
|
if stream_large_files:
|
||||||
if isinstance(stream_large_files, int):
|
if isinstance(stream_large_files, int):
|
||||||
|
|
|
@ -57,17 +57,11 @@ class WebSocketProtocol(HttpProtocol):
|
||||||
|
|
||||||
async def websocket_handshake(self, request, subprotocols=None):
|
async def websocket_handshake(self, request, subprotocols=None):
|
||||||
# let the websockets package do the handshake with the client
|
# let the websockets package do the handshake with the client
|
||||||
headers = []
|
headers = {}
|
||||||
|
|
||||||
def get_header(k):
|
|
||||||
return request.headers.get(k, '')
|
|
||||||
|
|
||||||
def set_header(k, v):
|
|
||||||
headers.append((k, v))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
key = handshake.check_request(get_header)
|
key = handshake.check_request(request.headers)
|
||||||
handshake.build_response(set_header, key)
|
handshake.build_response(headers, key)
|
||||||
except InvalidHandshake:
|
except InvalidHandshake:
|
||||||
raise InvalidUsage('Invalid websocket request')
|
raise InvalidUsage('Invalid websocket request')
|
||||||
|
|
||||||
|
@ -79,12 +73,12 @@ class WebSocketProtocol(HttpProtocol):
|
||||||
for p in client_subprotocols:
|
for p in client_subprotocols:
|
||||||
if p in subprotocols:
|
if p in subprotocols:
|
||||||
subprotocol = p
|
subprotocol = p
|
||||||
set_header('Sec-Websocket-Protocol', subprotocol)
|
headers['Sec-Websocket-Protocol'] = subprotocol
|
||||||
break
|
break
|
||||||
|
|
||||||
# write the 101 response back to the client
|
# write the 101 response back to the client
|
||||||
rv = b'HTTP/1.1 101 Switching Protocols\r\n'
|
rv = b'HTTP/1.1 101 Switching Protocols\r\n'
|
||||||
for k, v in headers:
|
for k, v in headers.items():
|
||||||
rv += k.encode('utf-8') + b': ' + v.encode('utf-8') + b'\r\n'
|
rv += k.encode('utf-8') + b': ' + v.encode('utf-8') + b'\r\n'
|
||||||
rv += b'\r\n'
|
rv += b'\r\n'
|
||||||
request.transport.write(rv)
|
request.transport.write(rv)
|
||||||
|
|
5
setup.py
5
setup.py
|
@ -56,11 +56,12 @@ ujson = 'ujson>=1.35' + env_dependency
|
||||||
uvloop = 'uvloop>=0.5.3' + env_dependency
|
uvloop = 'uvloop>=0.5.3' + env_dependency
|
||||||
|
|
||||||
requirements = [
|
requirements = [
|
||||||
'httptools>=0.0.9',
|
'httptools>=0.0.10',
|
||||||
uvloop,
|
uvloop,
|
||||||
ujson,
|
ujson,
|
||||||
'aiofiles>=0.3.0',
|
'aiofiles>=0.3.0',
|
||||||
'websockets>=5.0,<6.0',
|
'websockets>=6.0,<7.0',
|
||||||
|
'multidict>=4.0,<5.0',
|
||||||
]
|
]
|
||||||
if strtobool(os.environ.get("SANIC_NO_UJSON", "no")):
|
if strtobool(os.environ.get("SANIC_NO_UJSON", "no")):
|
||||||
print("Installing without uJSON")
|
print("Installing without uJSON")
|
||||||
|
|
8
tests/conftest.py
Normal file
8
tests/conftest.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from sanic import Sanic
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app(request):
|
||||||
|
return Sanic(request.node.name)
|
26
tests/static/test.html
Normal file
26
tests/static/test.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
▄▄▄▄▄
|
||||||
|
▀▀▀██████▄▄▄ _______________
|
||||||
|
▄▄▄▄▄ █████████▄ / \
|
||||||
|
▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
|
||||||
|
▀▀█████▄▄ ▀██████▄██ | _________________/
|
||||||
|
▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
|
||||||
|
▀▀▀▄ ▀▀███ ▀ ▄▄
|
||||||
|
▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
|
||||||
|
██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
|
||||||
|
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
|
||||||
|
▌ ▐▀████▐███▒▒▒▒▒▐██▌
|
||||||
|
▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
|
||||||
|
▀▀█████████▀
|
||||||
|
▄▄██▀██████▀█
|
||||||
|
▄██▀ ▀▀▀ █
|
||||||
|
▄█ ▐▌
|
||||||
|
▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
|
||||||
|
▌ ▐ ▀▀▄▄▄▀
|
||||||
|
▀▀▄▄▀
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,9 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from sanic import Sanic
|
|
||||||
|
|
||||||
|
|
||||||
def test_bad_request_response():
|
def test_bad_request_response(app):
|
||||||
app = Sanic('test_bad_request_response')
|
|
||||||
lines = []
|
lines = []
|
||||||
@app.listener('after_server_start')
|
@app.listener('after_server_start')
|
||||||
async def _request(sanic, loop):
|
async def _request(sanic, loop):
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.response import json, text
|
from sanic.response import text
|
||||||
from sanic.exceptions import NotFound, ServerError, InvalidUsage
|
from sanic.exceptions import NotFound, ServerError, InvalidUsage
|
||||||
from sanic.constants import HTTP_METHODS
|
from sanic.constants import HTTP_METHODS
|
||||||
|
|
||||||
|
@ -13,9 +13,16 @@ from sanic.constants import HTTP_METHODS
|
||||||
# GET
|
# GET
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
||||||
|
def get_file_path(static_file_directory, file_name):
|
||||||
|
return os.path.join(static_file_directory, file_name)
|
||||||
|
|
||||||
|
def get_file_content(static_file_directory, file_name):
|
||||||
|
"""The content of the static file to check"""
|
||||||
|
with open(get_file_path(static_file_directory, file_name), 'rb') as file:
|
||||||
|
return file.read()
|
||||||
|
|
||||||
@pytest.mark.parametrize('method', HTTP_METHODS)
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
def test_versioned_routes_get(method):
|
def test_versioned_routes_get(app, method):
|
||||||
app = Sanic('test_shorhand_routes_get')
|
|
||||||
bp = Blueprint('test_text')
|
bp = Blueprint('test_text')
|
||||||
|
|
||||||
method = method.lower()
|
method = method.lower()
|
||||||
|
@ -37,8 +44,7 @@ def test_versioned_routes_get(method):
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
|
|
||||||
def test_bp():
|
def test_bp(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
bp = Blueprint('test_text')
|
bp = Blueprint('test_text')
|
||||||
|
|
||||||
@bp.route('/')
|
@bp.route('/')
|
||||||
|
@ -51,8 +57,7 @@ def test_bp():
|
||||||
|
|
||||||
assert response.text == 'Hello'
|
assert response.text == 'Hello'
|
||||||
|
|
||||||
def test_bp_strict_slash():
|
def test_bp_strict_slash(app):
|
||||||
app = Sanic('test_route_strict_slash')
|
|
||||||
bp = Blueprint('test_text')
|
bp = Blueprint('test_text')
|
||||||
|
|
||||||
@bp.get('/get', strict_slashes=True)
|
@bp.get('/get', strict_slashes=True)
|
||||||
|
@ -78,8 +83,7 @@ def test_bp_strict_slash():
|
||||||
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():
|
def test_bp_strict_slash_default_value(app):
|
||||||
app = Sanic('test_route_strict_slash')
|
|
||||||
bp = Blueprint('test_text', strict_slashes=True)
|
bp = Blueprint('test_text', strict_slashes=True)
|
||||||
|
|
||||||
@bp.get('/get')
|
@bp.get('/get')
|
||||||
|
@ -98,8 +102,7 @@ def test_bp_strict_slash_default_value():
|
||||||
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():
|
def test_bp_strict_slash_without_passing_default_value(app):
|
||||||
app = Sanic('test_route_strict_slash')
|
|
||||||
bp = Blueprint('test_text')
|
bp = Blueprint('test_text')
|
||||||
|
|
||||||
@bp.get('/get')
|
@bp.get('/get')
|
||||||
|
@ -118,8 +121,7 @@ def test_bp_strict_slash_without_passing_default_value():
|
||||||
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():
|
def test_bp_strict_slash_default_value_can_be_overwritten(app):
|
||||||
app = Sanic('test_route_strict_slash')
|
|
||||||
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)
|
||||||
|
@ -138,8 +140,7 @@ def test_bp_strict_slash_default_value_can_be_overwritten():
|
||||||
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():
|
def test_bp_with_url_prefix(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
bp = Blueprint('test_text', url_prefix='/test1')
|
bp = Blueprint('test_text', url_prefix='/test1')
|
||||||
|
|
||||||
@bp.route('/')
|
@bp.route('/')
|
||||||
|
@ -152,8 +153,7 @@ def test_bp_with_url_prefix():
|
||||||
assert response.text == 'Hello'
|
assert response.text == 'Hello'
|
||||||
|
|
||||||
|
|
||||||
def test_several_bp_with_url_prefix():
|
def test_several_bp_with_url_prefix(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
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')
|
||||||
|
|
||||||
|
@ -173,8 +173,7 @@ def test_several_bp_with_url_prefix():
|
||||||
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():
|
def test_bp_with_host(app):
|
||||||
app = Sanic('test_bp_host')
|
|
||||||
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('/')
|
||||||
|
@ -200,8 +199,7 @@ def test_bp_with_host():
|
||||||
assert response.text == 'Hello subdomain!'
|
assert response.text == 'Hello subdomain!'
|
||||||
|
|
||||||
|
|
||||||
def test_several_bp_with_host():
|
def test_several_bp_with_host(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
bp = Blueprint('test_text',
|
bp = Blueprint('test_text',
|
||||||
url_prefix='/test',
|
url_prefix='/test',
|
||||||
host="example.com")
|
host="example.com")
|
||||||
|
@ -244,8 +242,7 @@ def test_several_bp_with_host():
|
||||||
headers=headers)
|
headers=headers)
|
||||||
assert response.text == 'Hello3'
|
assert response.text == 'Hello3'
|
||||||
|
|
||||||
def test_bp_middleware():
|
def test_bp_middleware(app):
|
||||||
app = Sanic('test_middleware')
|
|
||||||
blueprint = Blueprint('test_middleware')
|
blueprint = Blueprint('test_middleware')
|
||||||
|
|
||||||
@blueprint.middleware('response')
|
@blueprint.middleware('response')
|
||||||
|
@ -263,8 +260,7 @@ def test_bp_middleware():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
def test_bp_exception_handler():
|
def test_bp_exception_handler(app):
|
||||||
app = Sanic('test_middleware')
|
|
||||||
blueprint = Blueprint('test_middleware')
|
blueprint = Blueprint('test_middleware')
|
||||||
|
|
||||||
@blueprint.route('/1')
|
@blueprint.route('/1')
|
||||||
|
@ -296,8 +292,7 @@ def test_bp_exception_handler():
|
||||||
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():
|
def test_bp_listeners(app):
|
||||||
app = Sanic('test_middleware')
|
|
||||||
blueprint = Blueprint('test_middleware')
|
blueprint = Blueprint('test_middleware')
|
||||||
|
|
||||||
order = []
|
order = []
|
||||||
|
@ -332,12 +327,11 @@ def test_bp_listeners():
|
||||||
|
|
||||||
assert order == [1,2,3,4,5,6]
|
assert order == [1,2,3,4,5,6]
|
||||||
|
|
||||||
def test_bp_static():
|
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()
|
||||||
|
|
||||||
app = Sanic('test_static')
|
|
||||||
blueprint = Blueprint('test_static')
|
blueprint = Blueprint('test_static')
|
||||||
|
|
||||||
blueprint.static('/testing.file', current_file)
|
blueprint.static('/testing.file', current_file)
|
||||||
|
@ -348,8 +342,28 @@ def test_bp_static():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.body == current_file_contents
|
assert response.body == current_file_contents
|
||||||
|
|
||||||
def test_bp_shorthand():
|
@pytest.mark.parametrize('file_name', ['test.html'])
|
||||||
app = Sanic('test_shorhand_routes')
|
def test_bp_static_content_type(app, file_name):
|
||||||
|
# This is done here, since no other test loads a file here
|
||||||
|
current_file = inspect.getfile(inspect.currentframe())
|
||||||
|
current_directory = os.path.dirname(os.path.abspath(current_file))
|
||||||
|
static_directory = os.path.join(current_directory, 'static')
|
||||||
|
|
||||||
|
blueprint = Blueprint('test_static')
|
||||||
|
blueprint.static(
|
||||||
|
'/testing.file',
|
||||||
|
get_file_path(static_directory, file_name),
|
||||||
|
content_type='text/html; charset=utf-8'
|
||||||
|
)
|
||||||
|
|
||||||
|
app.blueprint(blueprint)
|
||||||
|
|
||||||
|
request, response = app.test_client.get('/testing.file')
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.body == get_file_content(static_directory, file_name)
|
||||||
|
assert response.headers['Content-Type'] == 'text/html; charset=utf-8'
|
||||||
|
|
||||||
|
def test_bp_shorthand(app):
|
||||||
blueprint = Blueprint('test_shorhand_routes')
|
blueprint = Blueprint('test_shorhand_routes')
|
||||||
ev = asyncio.Event()
|
ev = asyncio.Event()
|
||||||
|
|
||||||
|
@ -447,9 +461,7 @@ def test_bp_shorthand():
|
||||||
assert response.status == 101
|
assert response.status == 101
|
||||||
assert ev.is_set()
|
assert ev.is_set()
|
||||||
|
|
||||||
def test_bp_group():
|
def test_bp_group(app):
|
||||||
app = Sanic('test_nested_bp_groups')
|
|
||||||
|
|
||||||
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')
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,7 @@ from tempfile import NamedTemporaryFile
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
|
|
||||||
|
|
||||||
def test_load_from_object():
|
def test_load_from_object(app):
|
||||||
app = Sanic('test_load_from_object')
|
|
||||||
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'
|
||||||
|
@ -16,26 +15,29 @@ def test_load_from_object():
|
||||||
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():
|
||||||
environ["SANIC_TEST_ANSWER"] = "42"
|
environ["SANIC_TEST_ANSWER"] = "42"
|
||||||
app = Sanic()
|
app = Sanic()
|
||||||
assert app.config.TEST_ANSWER == 42
|
assert app.config.TEST_ANSWER == 42
|
||||||
del environ["SANIC_TEST_ANSWER"]
|
del environ["SANIC_TEST_ANSWER"]
|
||||||
|
|
||||||
|
|
||||||
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) == 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"]
|
||||||
|
|
||||||
def test_load_from_file():
|
|
||||||
app = Sanic('test_load_from_file')
|
def test_load_from_file(app):
|
||||||
config = b"""
|
config = b"""
|
||||||
VALUE = 'some value'
|
VALUE = 'some value'
|
||||||
condition = 1 == 1
|
condition = 1 == 1
|
||||||
|
@ -53,14 +55,12 @@ if condition:
|
||||||
assert 'condition' not in app.config
|
assert 'condition' not in app.config
|
||||||
|
|
||||||
|
|
||||||
def test_load_from_missing_file():
|
def test_load_from_missing_file(app):
|
||||||
app = Sanic('test_load_from_missing_file')
|
|
||||||
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():
|
def test_load_from_envvar(app):
|
||||||
app = Sanic('test_load_from_envvar')
|
|
||||||
config = b"VALUE = 'some value'"
|
config = b"VALUE = 'some value'"
|
||||||
with NamedTemporaryFile() as config_file:
|
with NamedTemporaryFile() as config_file:
|
||||||
config_file.write(config)
|
config_file.write(config)
|
||||||
|
@ -71,15 +71,17 @@ def test_load_from_envvar():
|
||||||
assert app.config.VALUE == 'some value'
|
assert app.config.VALUE == 'some value'
|
||||||
|
|
||||||
|
|
||||||
def test_load_from_missing_envvar():
|
def test_load_from_missing_envvar(app):
|
||||||
app = Sanic('test_load_from_missing_envvar')
|
with pytest.raises(RuntimeError) as e:
|
||||||
with pytest.raises(RuntimeError):
|
|
||||||
app.config.from_envvar('non-existent variable')
|
app.config.from_envvar('non-existent variable')
|
||||||
|
assert str(e.value) == ("The environment variable 'non-existent "
|
||||||
|
"variable' is not set and thus configuration "
|
||||||
|
"could not be loaded.")
|
||||||
|
|
||||||
|
|
||||||
def test_overwrite_exisiting_config():
|
def test_overwrite_exisiting_config(app):
|
||||||
app = Sanic('test_overwrite_exisiting_config')
|
|
||||||
app.config.DEFAULT = 1
|
app.config.DEFAULT = 1
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
DEFAULT = 2
|
DEFAULT = 2
|
||||||
|
|
||||||
|
@ -87,7 +89,7 @@ def test_overwrite_exisiting_config():
|
||||||
assert app.config.DEFAULT == 2
|
assert app.config.DEFAULT == 2
|
||||||
|
|
||||||
|
|
||||||
def test_missing_config():
|
def test_missing_config(app):
|
||||||
app = Sanic('test_missing_config')
|
with pytest.raises(AttributeError) as e:
|
||||||
with pytest.raises(AttributeError):
|
|
||||||
app.config.NON_EXISTENT
|
app.config.NON_EXISTENT
|
||||||
|
assert str(e.value) == ("Config has no 'NON_EXISTENT'")
|
||||||
|
|
|
@ -9,8 +9,7 @@ import pytest
|
||||||
# GET
|
# GET
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
||||||
def test_cookies():
|
def test_cookies(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -25,12 +24,12 @@ def test_cookies():
|
||||||
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),
|
(False, False),
|
||||||
(True, True),
|
(True, True),
|
||||||
])
|
])
|
||||||
def test_false_cookies_encoded(httponly, expected):
|
def test_false_cookies_encoded(app, httponly, expected):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -48,8 +47,7 @@ def test_false_cookies_encoded(httponly, expected):
|
||||||
(False, False),
|
(False, False),
|
||||||
(True, True),
|
(True, True),
|
||||||
])
|
])
|
||||||
def test_false_cookies(httponly, expected):
|
def test_false_cookies(app, httponly, expected):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -64,8 +62,7 @@ def test_false_cookies(httponly, expected):
|
||||||
|
|
||||||
assert ('HttpOnly' in response_cookies['right_back'].output()) == expected
|
assert ('HttpOnly' in response_cookies['right_back'].output()) == expected
|
||||||
|
|
||||||
def test_http2_cookies():
|
def test_http2_cookies(app):
|
||||||
app = Sanic('test_http2_cookies')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -77,8 +74,7 @@ def test_http2_cookies():
|
||||||
|
|
||||||
assert response.text == 'Cookies are: working!'
|
assert response.text == 'Cookies are: working!'
|
||||||
|
|
||||||
def test_cookie_options():
|
def test_cookie_options(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -95,8 +91,7 @@ def test_cookie_options():
|
||||||
assert response_cookies['test'].value == 'at you'
|
assert response_cookies['test'].value == 'at you'
|
||||||
assert response_cookies['test']['httponly'] == True
|
assert response_cookies['test']['httponly'] == True
|
||||||
|
|
||||||
def test_cookie_deletion():
|
def test_cookie_deletion(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
from threading import Event
|
from threading import Event
|
||||||
import asyncio
|
import asyncio
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
|
|
||||||
|
|
||||||
def test_create_task():
|
def test_create_task(app):
|
||||||
e = Event()
|
e = Event()
|
||||||
|
|
||||||
async def coro():
|
async def coro():
|
||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05)
|
||||||
e.set()
|
e.set()
|
||||||
|
|
||||||
app = Sanic('test_create_task')
|
|
||||||
app.add_task(coro)
|
app.add_task(coro)
|
||||||
|
|
||||||
@app.route('/early')
|
@app.route('/early')
|
||||||
|
@ -30,8 +28,7 @@ def test_create_task():
|
||||||
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():
|
def test_create_task_with_app_arg(app):
|
||||||
app = Sanic('test_add_task')
|
|
||||||
q = Queue()
|
q = Queue()
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
|
@ -44,4 +41,4 @@ def test_create_task_with_app_arg():
|
||||||
app.add_task(coro)
|
app.add_task(coro)
|
||||||
|
|
||||||
request, response = app.test_client.get('/')
|
request, response = app.test_client.get('/')
|
||||||
assert q.get() == 'test_add_task'
|
assert q.get() == 'test_create_task_with_app_arg'
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.server import HttpProtocol
|
from sanic.server import HttpProtocol
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
app = Sanic('test_custom_porotocol')
|
|
||||||
|
|
||||||
|
|
||||||
class CustomHttpProtocol(HttpProtocol):
|
class CustomHttpProtocol(HttpProtocol):
|
||||||
|
|
||||||
|
@ -16,12 +13,12 @@ class CustomHttpProtocol(HttpProtocol):
|
||||||
self.transport.close()
|
self.transport.close()
|
||||||
|
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
|
||||||
def test_use_custom_protocol():
|
|
||||||
server_kwargs = {
|
server_kwargs = {
|
||||||
'protocol': CustomHttpProtocol
|
'protocol': CustomHttpProtocol
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,7 @@ import pytest
|
||||||
("put", "text", "OK2 test"),
|
("put", "text", "OK2 test"),
|
||||||
("delete", "status", 405),
|
("delete", "status", 405),
|
||||||
])
|
])
|
||||||
def test_overload_dynamic_routes(method, attr, expected):
|
def test_overload_dynamic_routes(app, method, attr, expected):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
@app.route('/overload/<param>', methods=['GET'])
|
@app.route('/overload/<param>', methods=['GET'])
|
||||||
async def handler1(request, param):
|
async def handler1(request, param):
|
||||||
|
@ -25,8 +24,7 @@ def test_overload_dynamic_routes(method, attr, expected):
|
||||||
assert getattr(response, attr) == expected
|
assert getattr(response, attr) == expected
|
||||||
|
|
||||||
|
|
||||||
def test_overload_dynamic_routes_exist():
|
def test_overload_dynamic_routes_exist(app):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
@app.route('/overload/<param>', methods=['GET'])
|
@app.route('/overload/<param>', methods=['GET'])
|
||||||
async def handler1(request, param):
|
async def handler1(request, param):
|
||||||
|
|
|
@ -81,8 +81,7 @@ def exception_app():
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
def test_catch_exception_list():
|
def test_catch_exception_list(app):
|
||||||
app = Sanic('exception_list')
|
|
||||||
|
|
||||||
@app.exception([SanicExceptionTestException, NotFound])
|
@app.exception([SanicExceptionTestException, NotFound])
|
||||||
def exception_list(request, exception):
|
def exception_list(request, exception):
|
||||||
|
|
|
@ -9,14 +9,39 @@ import aiohttp
|
||||||
from aiohttp import TCPConnector
|
from aiohttp import TCPConnector
|
||||||
from sanic.testing import SanicTestClient, HOST, PORT
|
from sanic.testing import SanicTestClient, HOST, PORT
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
import packaging # direct use
|
||||||
|
except ImportError:
|
||||||
|
# setuptools v39.0 and above.
|
||||||
|
try:
|
||||||
|
from setuptools.extern import packaging
|
||||||
|
except ImportError:
|
||||||
|
# Before setuptools v39.0
|
||||||
|
from pkg_resources.extern import packaging
|
||||||
|
version = packaging.version
|
||||||
|
except ImportError:
|
||||||
|
raise RuntimeError("The 'packaging' library is missing.")
|
||||||
|
|
||||||
|
aiohttp_version = version.parse(aiohttp.__version__)
|
||||||
|
|
||||||
class ReuseableTCPConnector(TCPConnector):
|
class ReuseableTCPConnector(TCPConnector):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(ReuseableTCPConnector, self).__init__(*args, **kwargs)
|
super(ReuseableTCPConnector, self).__init__(*args, **kwargs)
|
||||||
self.old_proto = None
|
self.old_proto = None
|
||||||
|
|
||||||
if aiohttp.__version__ >= '3.0':
|
if aiohttp_version >= version.parse('3.3.0'):
|
||||||
|
async def connect(self, req, traces, timeout):
|
||||||
|
new_conn = await super(ReuseableTCPConnector, self)\
|
||||||
|
.connect(req, traces, timeout)
|
||||||
|
if self.old_proto is not None:
|
||||||
|
if self.old_proto != new_conn._protocol:
|
||||||
|
raise RuntimeError(
|
||||||
|
"We got a new connection, wanted the same one!")
|
||||||
|
print(new_conn.__dict__)
|
||||||
|
self.old_proto = new_conn._protocol
|
||||||
|
return new_conn
|
||||||
|
elif aiohttp_version >= version.parse('3.0.0'):
|
||||||
async def connect(self, req, traces=None):
|
async def connect(self, req, traces=None):
|
||||||
new_conn = await super(ReuseableTCPConnector, self)\
|
new_conn = await super(ReuseableTCPConnector, self)\
|
||||||
.connect(req, traces=traces)
|
.connect(req, traces=traces)
|
||||||
|
@ -28,7 +53,6 @@ class ReuseableTCPConnector(TCPConnector):
|
||||||
self.old_proto = new_conn._protocol
|
self.old_proto = new_conn._protocol
|
||||||
return new_conn
|
return new_conn
|
||||||
else:
|
else:
|
||||||
|
|
||||||
async def connect(self, req):
|
async def connect(self, req):
|
||||||
new_conn = await super(ReuseableTCPConnector, self)\
|
new_conn = await super(ReuseableTCPConnector, self)\
|
||||||
.connect(req)
|
.connect(req)
|
||||||
|
|
|
@ -23,7 +23,7 @@ def reset_logging():
|
||||||
reload(logging)
|
reload(logging)
|
||||||
|
|
||||||
|
|
||||||
def test_log():
|
def test_log(app):
|
||||||
log_stream = StringIO()
|
log_stream = StringIO()
|
||||||
for handler in logging.root.handlers[:]:
|
for handler in logging.root.handlers[:]:
|
||||||
logging.root.removeHandler(handler)
|
logging.root.removeHandler(handler)
|
||||||
|
@ -33,7 +33,6 @@ def test_log():
|
||||||
stream=log_stream
|
stream=log_stream
|
||||||
)
|
)
|
||||||
log = logging.getLogger()
|
log = logging.getLogger()
|
||||||
app = Sanic('test_logging')
|
|
||||||
rand_string = str(uuid.uuid4())
|
rand_string = str(uuid.uuid4())
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
|
@ -80,9 +79,8 @@ def test_logging_pass_customer_logconfig():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('debug', (True, False, ))
|
@pytest.mark.parametrize('debug', (True, False, ))
|
||||||
def test_log_connection_lost(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 """
|
||||||
app = Sanic('connection_lost')
|
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
root = logging.getLogger('root')
|
root = logging.getLogger('root')
|
||||||
root.addHandler(logging.StreamHandler(stream))
|
root.addHandler(logging.StreamHandler(stream))
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
from json import loads as json_loads, dumps as json_dumps
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import json, text, HTTPResponse
|
from sanic.response import text, HTTPResponse
|
||||||
from sanic.exceptions import NotFound
|
from sanic.exceptions import NotFound
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,9 +7,7 @@ from sanic.exceptions import NotFound
|
||||||
# GET
|
# GET
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
||||||
def test_middleware_request():
|
def test_middleware_request(app):
|
||||||
app = Sanic('test_middleware_request')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.middleware
|
@app.middleware
|
||||||
|
@ -28,9 +24,7 @@ def test_middleware_request():
|
||||||
assert type(results[0]) is Request
|
assert type(results[0]) is Request
|
||||||
|
|
||||||
|
|
||||||
def test_middleware_response():
|
def test_middleware_response(app):
|
||||||
app = Sanic('test_middleware_response')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.middleware('request')
|
@app.middleware('request')
|
||||||
|
@ -54,8 +48,7 @@ def test_middleware_response():
|
||||||
assert isinstance(results[2], HTTPResponse)
|
assert isinstance(results[2], HTTPResponse)
|
||||||
|
|
||||||
|
|
||||||
def test_middleware_response_exception():
|
def test_middleware_response_exception(app):
|
||||||
app = Sanic('test_middleware_response_exception')
|
|
||||||
result = {'status_code': None}
|
result = {'status_code': None}
|
||||||
|
|
||||||
@app.middleware('response')
|
@app.middleware('response')
|
||||||
|
@ -75,8 +68,7 @@ def test_middleware_response_exception():
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
assert result['status_code'] == 404
|
assert result['status_code'] == 404
|
||||||
|
|
||||||
def test_middleware_override_request():
|
def test_middleware_override_request(app):
|
||||||
app = Sanic('test_middleware_override_request')
|
|
||||||
|
|
||||||
@app.middleware
|
@app.middleware
|
||||||
async def halt_request(request):
|
async def halt_request(request):
|
||||||
|
@ -92,8 +84,7 @@ def test_middleware_override_request():
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
|
|
||||||
def test_middleware_override_response():
|
def test_middleware_override_response(app):
|
||||||
app = Sanic('test_middleware_override_response')
|
|
||||||
|
|
||||||
@app.middleware('response')
|
@app.middleware('response')
|
||||||
async def process_response(request, response):
|
async def process_response(request, response):
|
||||||
|
@ -109,10 +100,7 @@ def test_middleware_override_response():
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
|
|
||||||
|
def test_middleware_order(app):
|
||||||
def test_middleware_order():
|
|
||||||
app = Sanic('test_middleware_order')
|
|
||||||
|
|
||||||
order = []
|
order = []
|
||||||
|
|
||||||
@app.middleware('request')
|
@app.middleware('request')
|
||||||
|
|
|
@ -2,15 +2,13 @@ import multiprocessing
|
||||||
import random
|
import random
|
||||||
import signal
|
import signal
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.testing import HOST, PORT
|
from sanic.testing import HOST, PORT
|
||||||
|
|
||||||
|
|
||||||
def test_multiprocessing():
|
def test_multiprocessing(app):
|
||||||
"""Tests that the number of children we produce is correct"""
|
"""Tests that the number of children we produce is correct"""
|
||||||
# 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))
|
||||||
app = Sanic('test_multiprocessing')
|
|
||||||
process_list = set()
|
process_list = set()
|
||||||
|
|
||||||
def stop_on_alarm(*args):
|
def stop_on_alarm(*args):
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
from sanic.exceptions import URLBuildError
|
from sanic.exceptions import URLBuildError
|
||||||
|
@ -16,8 +15,7 @@ from sanic.constants import HTTP_METHODS
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
||||||
@pytest.mark.parametrize('method', HTTP_METHODS)
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
def test_versioned_named_routes_get(method):
|
def test_versioned_named_routes_get(app, method):
|
||||||
app = Sanic('test_shorhand_routes_get')
|
|
||||||
bp = Blueprint('test_bp', url_prefix='/bp')
|
bp = Blueprint('test_bp', url_prefix='/bp')
|
||||||
|
|
||||||
method = method.lower()
|
method = method.lower()
|
||||||
|
@ -57,8 +55,7 @@ def test_versioned_named_routes_get(method):
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_default_routes_get():
|
def test_shorthand_default_routes_get(app):
|
||||||
app = Sanic('test_shorhand_routes_get')
|
|
||||||
|
|
||||||
@app.get('/get')
|
@app.get('/get')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -68,8 +65,7 @@ def test_shorthand_default_routes_get():
|
||||||
assert app.url_for('handler') == '/get'
|
assert app.url_for('handler') == '/get'
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_named_routes_get():
|
def test_shorthand_named_routes_get(app):
|
||||||
app = Sanic('test_shorhand_routes_get')
|
|
||||||
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')
|
||||||
|
@ -93,8 +89,7 @@ def test_shorthand_named_routes_get():
|
||||||
app.url_for('test_bp.handler2')
|
app.url_for('test_bp.handler2')
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_named_routes_post():
|
def test_shorthand_named_routes_post(app):
|
||||||
app = Sanic('test_shorhand_routes_post')
|
|
||||||
|
|
||||||
@app.post('/post', name='route_name')
|
@app.post('/post', name='route_name')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -106,8 +101,7 @@ def test_shorthand_named_routes_post():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_named_routes_put():
|
def test_shorthand_named_routes_put(app):
|
||||||
app = Sanic('test_shorhand_routes_put')
|
|
||||||
|
|
||||||
@app.put('/put', name='route_put')
|
@app.put('/put', name='route_put')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -121,8 +115,7 @@ def test_shorthand_named_routes_put():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_named_routes_delete():
|
def test_shorthand_named_routes_delete(app):
|
||||||
app = Sanic('test_shorhand_routes_delete')
|
|
||||||
|
|
||||||
@app.delete('/delete', name='route_delete')
|
@app.delete('/delete', name='route_delete')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -136,8 +129,7 @@ def test_shorthand_named_routes_delete():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_named_routes_patch():
|
def test_shorthand_named_routes_patch(app):
|
||||||
app = Sanic('test_shorhand_routes_patch')
|
|
||||||
|
|
||||||
@app.patch('/patch', name='route_patch')
|
@app.patch('/patch', name='route_patch')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -151,8 +143,7 @@ def test_shorthand_named_routes_patch():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_named_routes_head():
|
def test_shorthand_named_routes_head(app):
|
||||||
app = Sanic('test_shorhand_routes_head')
|
|
||||||
|
|
||||||
@app.head('/head', name='route_head')
|
@app.head('/head', name='route_head')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -166,8 +157,7 @@ def test_shorthand_named_routes_head():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_named_routes_options():
|
def test_shorthand_named_routes_options(app):
|
||||||
app = Sanic('test_shorhand_routes_options')
|
|
||||||
|
|
||||||
@app.options('/options', name='route_options')
|
@app.options('/options', name='route_options')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -181,8 +171,7 @@ def test_shorthand_named_routes_options():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_named_static_routes():
|
def test_named_static_routes(app):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
@app.route('/test', name='route_test')
|
@app.route('/test', name='route_test')
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
|
@ -205,9 +194,7 @@ def test_named_static_routes():
|
||||||
app.url_for('handler2')
|
app.url_for('handler2')
|
||||||
|
|
||||||
|
|
||||||
def test_named_dynamic_route():
|
def test_named_dynamic_route(app):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.route('/folder/<name>', name='route_dynamic')
|
@app.route('/folder/<name>', name='route_dynamic')
|
||||||
|
@ -221,8 +208,7 @@ def test_named_dynamic_route():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_named_route_regex():
|
def test_dynamic_named_route_regex(app):
|
||||||
app = Sanic('test_dynamic_route_regex')
|
|
||||||
|
|
||||||
@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):
|
||||||
|
@ -235,8 +221,7 @@ def test_dynamic_named_route_regex():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_named_route_path():
|
def test_dynamic_named_route_path(app):
|
||||||
app = Sanic('test_dynamic_route_path')
|
|
||||||
|
|
||||||
@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):
|
||||||
|
@ -249,8 +234,7 @@ def test_dynamic_named_route_path():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_named_route_unhashable():
|
def test_dynamic_named_route_unhashable(app):
|
||||||
app = Sanic('test_dynamic_route_unhashable')
|
|
||||||
|
|
||||||
@app.route('/folder/<unhashable:[A-Za-z0-9/]+>/end/',
|
@app.route('/folder/<unhashable:[A-Za-z0-9/]+>/end/',
|
||||||
name='route_unhashable')
|
name='route_unhashable')
|
||||||
|
@ -265,8 +249,7 @@ def test_dynamic_named_route_unhashable():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_websocket_named_route():
|
def test_websocket_named_route(app):
|
||||||
app = Sanic('test_websocket_route')
|
|
||||||
ev = asyncio.Event()
|
ev = asyncio.Event()
|
||||||
|
|
||||||
@app.websocket('/ws', name='route_ws')
|
@app.websocket('/ws', name='route_ws')
|
||||||
|
@ -280,8 +263,7 @@ def test_websocket_named_route():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_websocket_named_route_with_subprotocols():
|
def test_websocket_named_route_with_subprotocols(app):
|
||||||
app = Sanic('test_websocket_route')
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.websocket('/ws', subprotocols=['foo', 'bar'], name='route_ws')
|
@app.websocket('/ws', subprotocols=['foo', 'bar'], name='route_ws')
|
||||||
|
@ -294,8 +276,7 @@ def test_websocket_named_route_with_subprotocols():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_static_add_named_route():
|
def test_static_add_named_route(app):
|
||||||
app = Sanic('test_static_add_route')
|
|
||||||
|
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
return text('OK1')
|
return text('OK1')
|
||||||
|
@ -319,9 +300,7 @@ def test_static_add_named_route():
|
||||||
app.url_for('handler2')
|
app.url_for('handler2')
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_add_named_route():
|
def test_dynamic_add_named_route(app):
|
||||||
app = Sanic('test_dynamic_add_route')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
async def handler(request, name):
|
async def handler(request, name):
|
||||||
|
@ -335,8 +314,7 @@ def test_dynamic_add_named_route():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_add_named_route_unhashable():
|
def test_dynamic_add_named_route_unhashable(app):
|
||||||
app = Sanic('test_dynamic_add_route_unhashable')
|
|
||||||
|
|
||||||
async def handler(request, unhashable):
|
async def handler(request, unhashable):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -351,8 +329,7 @@ def test_dynamic_add_named_route_unhashable():
|
||||||
app.url_for('handler')
|
app.url_for('handler')
|
||||||
|
|
||||||
|
|
||||||
def test_overload_routes():
|
def test_overload_routes(app):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
@app.route('/overload', methods=['GET'], name='route_first')
|
@app.route('/overload', methods=['GET'], name='route_first')
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
|
|
|
@ -1,49 +1,45 @@
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.exceptions import PayloadTooLarge
|
from sanic.exceptions import PayloadTooLarge
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
def test_payload_too_large_from_error_handler():
|
def test_payload_too_large_from_error_handler(app):
|
||||||
data_received_app = Sanic('data_received')
|
app.config.REQUEST_MAX_SIZE = 1
|
||||||
data_received_app.config.REQUEST_MAX_SIZE = 1
|
|
||||||
|
|
||||||
@data_received_app.route('/1')
|
@app.route('/1')
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
|
||||||
@data_received_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 = data_received_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():
|
def test_payload_too_large_at_data_received_default(app):
|
||||||
data_received_default_app = Sanic('data_received_default')
|
app.config.REQUEST_MAX_SIZE = 1
|
||||||
data_received_default_app.config.REQUEST_MAX_SIZE = 1
|
|
||||||
|
|
||||||
@data_received_default_app.route('/1')
|
@app.route('/1')
|
||||||
async def handler2(request):
|
async def handler2(request):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
|
||||||
response = data_received_default_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():
|
def test_payload_too_large_at_on_header_default(app):
|
||||||
on_header_default_app = Sanic('on_header')
|
app.config.REQUEST_MAX_SIZE = 500
|
||||||
on_header_default_app.config.REQUEST_MAX_SIZE = 500
|
|
||||||
|
|
||||||
@on_header_default_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 = on_header_default_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'
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import text, redirect
|
from sanic.response import text, redirect
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def redirect_app():
|
def redirect_app(app):
|
||||||
app = Sanic('test_redirection')
|
|
||||||
|
|
||||||
@app.route('/redirect_init')
|
@app.route('/redirect_init')
|
||||||
async def redirect_init(request):
|
async def redirect_init(request):
|
||||||
|
@ -32,6 +30,10 @@ def redirect_app():
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
|
||||||
|
@app.route('/redirect_with_header_injection')
|
||||||
|
async def redirect_with_header_injection(request):
|
||||||
|
return redirect("/unsafe\ntest-header: test-value\n\ntest-body")
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,3 +94,16 @@ def test_chained_redirect(redirect_app):
|
||||||
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):
|
||||||
|
"""
|
||||||
|
Test redirection to a URL with header and body injections.
|
||||||
|
"""
|
||||||
|
request, response = redirect_app.test_client.get(
|
||||||
|
"/redirect_with_header_injection",
|
||||||
|
allow_redirects=False)
|
||||||
|
|
||||||
|
assert response.status == 302
|
||||||
|
assert "test-header" not in response.headers
|
||||||
|
assert not response.text.startswith('test-body')
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import json
|
from sanic.response import json
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -9,8 +8,7 @@ except ImportError:
|
||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
|
|
||||||
def test_storage():
|
def test_storage(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.middleware('request')
|
@app.middleware('request')
|
||||||
def store(request):
|
def store(request):
|
||||||
|
@ -29,8 +27,7 @@ def test_storage():
|
||||||
assert response_json.get('sidekick') is None
|
assert response_json.get('sidekick') is None
|
||||||
|
|
||||||
|
|
||||||
def test_app_injection():
|
def test_app_injection(app):
|
||||||
app = Sanic('test_app_injection')
|
|
||||||
expected = random.choice(range(0, 100))
|
expected = random.choice(range(0, 100))
|
||||||
|
|
||||||
@app.listener('after_server_start')
|
@app.listener('after_server_start')
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.views import CompositionView
|
from sanic.views import CompositionView
|
||||||
from sanic.views import HTTPMethodView
|
from sanic.views import HTTPMethodView
|
||||||
|
@ -9,11 +8,9 @@ from sanic.response import stream, text
|
||||||
data = "abc" * 100000
|
data = "abc" * 100000
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream_method_view():
|
def test_request_stream_method_view(app):
|
||||||
'''for self.is_request_stream = True'''
|
'''for self.is_request_stream = True'''
|
||||||
|
|
||||||
app = Sanic('test_request_stream_method_view')
|
|
||||||
|
|
||||||
class SimpleView(HTTPMethodView):
|
class SimpleView(HTTPMethodView):
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
@ -44,11 +41,9 @@ def test_request_stream_method_view():
|
||||||
assert response.text == data
|
assert response.text == data
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream_app():
|
def test_request_stream_app(app):
|
||||||
'''for self.is_request_stream = True and decorators'''
|
'''for self.is_request_stream = True and decorators'''
|
||||||
|
|
||||||
app = Sanic('test_request_stream_app')
|
|
||||||
|
|
||||||
@app.get('/get')
|
@app.get('/get')
|
||||||
async def get(request):
|
async def get(request):
|
||||||
assert request.stream is None
|
assert request.stream is None
|
||||||
|
@ -83,7 +78,7 @@ def test_request_stream_app():
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
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')
|
||||||
|
@ -100,7 +95,7 @@ def test_request_stream_app():
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
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')
|
||||||
|
@ -117,7 +112,7 @@ def test_request_stream_app():
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
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
|
||||||
|
@ -163,11 +158,9 @@ def test_request_stream_app():
|
||||||
assert response.text == data
|
assert response.text == data
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream_handle_exception():
|
def test_request_stream_handle_exception(app):
|
||||||
'''for handling exceptions properly'''
|
'''for handling exceptions properly'''
|
||||||
|
|
||||||
app = Sanic('test_request_stream_exception')
|
|
||||||
|
|
||||||
@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)
|
||||||
|
@ -177,7 +170,7 @@ def test_request_stream_handle_exception():
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
await response.write(body.decode('utf-8'))
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
# 404
|
# 404
|
||||||
|
@ -191,10 +184,8 @@ def test_request_stream_handle_exception():
|
||||||
assert response.text == 'Error: Method GET not allowed for URL /post/random_id'
|
assert response.text == 'Error: Method GET not allowed for URL /post/random_id'
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream_blueprint():
|
def test_request_stream_blueprint(app):
|
||||||
'''for self.is_request_stream = True'''
|
'''for self.is_request_stream = True'''
|
||||||
|
|
||||||
app = Sanic('test_request_stream_blueprint')
|
|
||||||
bp = Blueprint('test_blueprint_request_stream_blueprint')
|
bp = Blueprint('test_blueprint_request_stream_blueprint')
|
||||||
|
|
||||||
@app.get('/get')
|
@app.get('/get')
|
||||||
|
@ -231,7 +222,7 @@ def test_request_stream_blueprint():
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
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')
|
||||||
|
@ -248,7 +239,7 @@ def test_request_stream_blueprint():
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
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')
|
||||||
|
@ -265,7 +256,7 @@ def test_request_stream_blueprint():
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
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)
|
||||||
|
@ -313,11 +304,9 @@ def test_request_stream_blueprint():
|
||||||
assert response.text == data
|
assert response.text == data
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream_composition_view():
|
def test_request_stream_composition_view(app):
|
||||||
'''for self.is_request_stream = True'''
|
'''for self.is_request_stream = True'''
|
||||||
|
|
||||||
app = Sanic('test_request_stream__composition_view')
|
|
||||||
|
|
||||||
def get_handler(request):
|
def get_handler(request):
|
||||||
assert request.stream is None
|
assert request.stream is None
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -348,11 +337,9 @@ def test_request_stream_composition_view():
|
||||||
assert response.text == data
|
assert response.text == data
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream():
|
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')
|
||||||
app = Sanic('test_request_stream')
|
|
||||||
|
|
||||||
class SimpleView(HTTPMethodView):
|
class SimpleView(HTTPMethodView):
|
||||||
|
|
||||||
|
@ -380,7 +367,7 @@ def test_request_stream():
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
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')
|
||||||
|
|
|
@ -5,9 +5,24 @@ import asyncio
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
from sanic.config import Config
|
from sanic.config import Config
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from aiohttp import TCPConnector
|
from aiohttp import TCPConnector, ClientResponse
|
||||||
from sanic.testing import SanicTestClient, HOST, PORT
|
from sanic.testing import SanicTestClient, HOST, PORT
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
import packaging # direct use
|
||||||
|
except ImportError:
|
||||||
|
# setuptools v39.0 and above.
|
||||||
|
try:
|
||||||
|
from setuptools.extern import packaging
|
||||||
|
except ImportError:
|
||||||
|
# Before setuptools v39.0
|
||||||
|
from pkg_resources.extern import packaging
|
||||||
|
version = packaging.version
|
||||||
|
except ImportError:
|
||||||
|
raise RuntimeError("The 'packaging' library is missing.")
|
||||||
|
|
||||||
|
aiohttp_version = version.parse(aiohttp.__version__)
|
||||||
|
|
||||||
class DelayableTCPConnector(TCPConnector):
|
class DelayableTCPConnector(TCPConnector):
|
||||||
|
|
||||||
|
@ -38,6 +53,9 @@ class DelayableTCPConnector(TCPConnector):
|
||||||
self.orig_start = getattr(resp, 'start')
|
self.orig_start = getattr(resp, 'start')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if aiohttp_version >= version.parse("3.3.0"):
|
||||||
|
ret = await self.orig_start(connection)
|
||||||
|
else:
|
||||||
ret = await self.orig_start(connection,
|
ret = await self.orig_start(connection,
|
||||||
read_until_eof)
|
read_until_eof)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -58,14 +76,30 @@ class DelayableTCPConnector(TCPConnector):
|
||||||
t = req.loop.time()
|
t = req.loop.time()
|
||||||
print("sending at {}".format(t), flush=True)
|
print("sending at {}".format(t), flush=True)
|
||||||
conn = next(iter(args)) # first arg is connection
|
conn = next(iter(args)) # first arg is connection
|
||||||
if aiohttp.__version__ >= "3.1.0":
|
|
||||||
|
if aiohttp_version >= version.parse("3.1.0"):
|
||||||
try:
|
try:
|
||||||
delayed_resp = await self.orig_send(*args, **kwargs)
|
delayed_resp = await self.orig_send(*args, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if aiohttp_version >= version.parse("3.3.0"):
|
||||||
return aiohttp.ClientResponse(req.method, req.url,
|
return aiohttp.ClientResponse(req.method, req.url,
|
||||||
writer=None, continue100=None, timer=None,
|
writer=None,
|
||||||
request_info=None, auto_decompress=None, traces=[],
|
continue100=None,
|
||||||
loop=req.loop, session=None)
|
timer=None,
|
||||||
|
request_info=None,
|
||||||
|
traces=[],
|
||||||
|
loop=req.loop,
|
||||||
|
session=None)
|
||||||
|
else:
|
||||||
|
return aiohttp.ClientResponse(req.method, req.url,
|
||||||
|
writer=None,
|
||||||
|
continue100=None,
|
||||||
|
timer=None,
|
||||||
|
request_info=None,
|
||||||
|
auto_decompress=None,
|
||||||
|
traces=[],
|
||||||
|
loop=req.loop,
|
||||||
|
session=None)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
delayed_resp = self.orig_send(*args, **kwargs)
|
delayed_resp = self.orig_send(*args, **kwargs)
|
||||||
|
@ -73,7 +107,7 @@ class DelayableTCPConnector(TCPConnector):
|
||||||
return aiohttp.ClientResponse(req.method, req.url)
|
return aiohttp.ClientResponse(req.method, req.url)
|
||||||
return delayed_resp
|
return delayed_resp
|
||||||
|
|
||||||
if aiohttp.__version__ >= "3.1.0":
|
if aiohttp_version >= version.parse("3.1.0"):
|
||||||
# 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):
|
||||||
gen = self.delayed_send(*args, **kwargs)
|
gen = self.delayed_send(*args, **kwargs)
|
||||||
|
@ -96,12 +130,25 @@ class DelayableTCPConnector(TCPConnector):
|
||||||
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
|
||||||
|
|
||||||
if aiohttp.__version__ >= '3.0':
|
if aiohttp_version >= version.parse("3.3.0"):
|
||||||
|
async def connect(self, req, traces, timeout):
|
||||||
|
d_req = DelayableTCPConnector.\
|
||||||
|
RequestContextManager(req, self._pre_request_delay)
|
||||||
|
conn = await super(DelayableTCPConnector, self).\
|
||||||
|
connect(req, traces, timeout)
|
||||||
|
if self._post_connect_delay and self._post_connect_delay > 0:
|
||||||
|
await asyncio.sleep(self._post_connect_delay,
|
||||||
|
loop=self._loop)
|
||||||
|
req.send = d_req.send
|
||||||
|
t = req.loop.time()
|
||||||
|
print("Connected at {}".format(t), flush=True)
|
||||||
|
return conn
|
||||||
|
elif aiohttp_version >= version.parse("3.0.0"):
|
||||||
async def connect(self, req, traces=None):
|
async def connect(self, req, traces=None):
|
||||||
d_req = DelayableTCPConnector.\
|
d_req = DelayableTCPConnector.\
|
||||||
RequestContextManager(req, self._pre_request_delay)
|
RequestContextManager(req, self._pre_request_delay)
|
||||||
conn = await super(DelayableTCPConnector, self).connect(req, traces=traces)
|
conn = await super(DelayableTCPConnector, self).\
|
||||||
|
connect(req, traces=traces)
|
||||||
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)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import ssl
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.exceptions import ServerError
|
from sanic.exceptions import ServerError
|
||||||
from sanic.response import json, text
|
from sanic.response import json, text
|
||||||
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE
|
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE
|
||||||
|
@ -16,8 +15,7 @@ from sanic.testing import HOST, PORT
|
||||||
# GET
|
# GET
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
||||||
def test_sync():
|
def test_sync(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -27,8 +25,8 @@ def test_sync():
|
||||||
|
|
||||||
assert response.text == 'Hello'
|
assert response.text == 'Hello'
|
||||||
|
|
||||||
def test_remote_address():
|
|
||||||
app = Sanic('test_text')
|
def test_remote_address(app):
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -38,8 +36,8 @@ def test_remote_address():
|
||||||
|
|
||||||
assert response.text == '127.0.0.1'
|
assert response.text == '127.0.0.1'
|
||||||
|
|
||||||
def test_text():
|
|
||||||
app = Sanic('test_text')
|
def test_text(app):
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -50,8 +48,7 @@ def test_text():
|
||||||
assert response.text == 'Hello'
|
assert response.text == 'Hello'
|
||||||
|
|
||||||
|
|
||||||
def test_headers():
|
def test_headers(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -63,8 +60,7 @@ def test_headers():
|
||||||
assert response.headers.get('spam') == 'great'
|
assert response.headers.get('spam') == 'great'
|
||||||
|
|
||||||
|
|
||||||
def test_non_str_headers():
|
def test_non_str_headers(app):
|
||||||
app = Sanic('test_text')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -75,8 +71,8 @@ def test_non_str_headers():
|
||||||
|
|
||||||
assert response.headers.get('answer') == '42'
|
assert response.headers.get('answer') == '42'
|
||||||
|
|
||||||
def test_invalid_response():
|
|
||||||
app = Sanic('test_invalid_response')
|
def test_invalid_response(app):
|
||||||
|
|
||||||
@app.exception(ServerError)
|
@app.exception(ServerError)
|
||||||
def handler_exception(request, exception):
|
def handler_exception(request, exception):
|
||||||
|
@ -91,8 +87,7 @@ def test_invalid_response():
|
||||||
assert response.text == "Internal Server Error."
|
assert response.text == "Internal Server Error."
|
||||||
|
|
||||||
|
|
||||||
def test_json():
|
def test_json(app):
|
||||||
app = Sanic('test_json')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -105,8 +100,7 @@ def test_json():
|
||||||
assert results.get('test') == True
|
assert results.get('test') == True
|
||||||
|
|
||||||
|
|
||||||
def test_empty_json():
|
def test_empty_json(app):
|
||||||
app = Sanic('test_json')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -118,8 +112,7 @@ def test_empty_json():
|
||||||
assert response.text == 'null'
|
assert response.text == 'null'
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_json():
|
def test_invalid_json(app):
|
||||||
app = Sanic('test_json')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -131,8 +124,7 @@ def test_invalid_json():
|
||||||
assert response.status == 400
|
assert response.status == 400
|
||||||
|
|
||||||
|
|
||||||
def test_query_string():
|
def test_query_string(app):
|
||||||
app = Sanic('test_query_string')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -145,8 +137,7 @@ def test_query_string():
|
||||||
assert request.args.get('test2') == 'false'
|
assert request.args.get('test2') == 'false'
|
||||||
|
|
||||||
|
|
||||||
def test_uri_template():
|
def test_uri_template(app):
|
||||||
app = Sanic('test_uri_template')
|
|
||||||
|
|
||||||
@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):
|
||||||
|
@ -156,8 +147,7 @@ def test_uri_template():
|
||||||
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():
|
def test_token(app):
|
||||||
app = Sanic('test_post_token')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -204,8 +194,7 @@ def test_token():
|
||||||
assert request.token is None
|
assert request.token is None
|
||||||
|
|
||||||
|
|
||||||
def test_content_type():
|
def test_content_type(app):
|
||||||
app = Sanic('test_content_type')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -223,8 +212,7 @@ def test_content_type():
|
||||||
assert response.text == 'application/json'
|
assert response.text == 'application/json'
|
||||||
|
|
||||||
|
|
||||||
def test_remote_addr():
|
def test_remote_addr(app):
|
||||||
app = Sanic('test_content_type')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -249,8 +237,7 @@ def test_remote_addr():
|
||||||
assert response.text == '127.0.0.1'
|
assert response.text == '127.0.0.1'
|
||||||
|
|
||||||
|
|
||||||
def test_match_info():
|
def test_match_info(app):
|
||||||
app = Sanic('test_match_info')
|
|
||||||
|
|
||||||
@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):
|
||||||
|
@ -266,8 +253,7 @@ def test_match_info():
|
||||||
# POST
|
# POST
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
||||||
def test_post_json():
|
def test_post_json(app):
|
||||||
app = Sanic('test_post_json')
|
|
||||||
|
|
||||||
@app.route('/', methods=['POST'])
|
@app.route('/', methods=['POST'])
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -283,8 +269,7 @@ def test_post_json():
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
|
|
||||||
def test_post_form_urlencoded():
|
def test_post_form_urlencoded(app):
|
||||||
app = Sanic('test_post_form_urlencoded')
|
|
||||||
|
|
||||||
@app.route('/', methods=['POST'])
|
@app.route('/', methods=['POST'])
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -311,8 +296,7 @@ def test_post_form_urlencoded():
|
||||||
'OK\r\n' \
|
'OK\r\n' \
|
||||||
'------sanic--\r\n',
|
'------sanic--\r\n',
|
||||||
])
|
])
|
||||||
def test_post_form_multipart_form_data(payload):
|
def test_post_form_multipart_form_data(app, payload):
|
||||||
app = Sanic('test_post_form_multipart_form_data')
|
|
||||||
|
|
||||||
@app.route('/', methods=['POST'])
|
@app.route('/', methods=['POST'])
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -331,8 +315,7 @@ def test_post_form_multipart_form_data(payload):
|
||||||
('/bar/baz', '', 'http://{}:{}/bar/baz'),
|
('/bar/baz', '', 'http://{}:{}/bar/baz'),
|
||||||
('/moo/boo', 'arg1=val1', 'http://{}:{}/moo/boo?arg1=val1')
|
('/moo/boo', 'arg1=val1', 'http://{}:{}/moo/boo?arg1=val1')
|
||||||
])
|
])
|
||||||
def test_url_attributes_no_ssl(path, query, expected_url):
|
def test_url_attributes_no_ssl(app, path, query, expected_url):
|
||||||
app = Sanic('test_url_attrs_no_ssl')
|
|
||||||
|
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -356,9 +339,7 @@ def test_url_attributes_no_ssl(path, query, expected_url):
|
||||||
('/bar/baz', '', 'https://{}:{}/bar/baz'),
|
('/bar/baz', '', 'https://{}:{}/bar/baz'),
|
||||||
('/moo/boo', 'arg1=val1', 'https://{}:{}/moo/boo?arg1=val1')
|
('/moo/boo', 'arg1=val1', 'https://{}:{}/moo/boo?arg1=val1')
|
||||||
])
|
])
|
||||||
def test_url_attributes_with_ssl(path, query, expected_url):
|
def test_url_attributes_with_ssl(app, path, query, expected_url):
|
||||||
app = Sanic('test_url_attrs_with_ssl')
|
|
||||||
|
|
||||||
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(
|
||||||
|
|
|
@ -8,17 +8,16 @@ from urllib.parse import unquote
|
||||||
import pytest
|
import pytest
|
||||||
from random import choice
|
from random import choice
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import HTTPResponse, stream, StreamingHTTPResponse, file, file_stream, json
|
from sanic.response import HTTPResponse, stream, StreamingHTTPResponse, file, file_stream, json
|
||||||
|
from sanic.server import HttpProtocol
|
||||||
from sanic.testing import HOST, PORT
|
from sanic.testing import HOST, PORT
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
JSON_DATA = {'ok': True}
|
JSON_DATA = {'ok': True}
|
||||||
|
|
||||||
|
|
||||||
def test_response_body_not_a_string():
|
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"""
|
||||||
app = Sanic('response_body_not_a_string')
|
|
||||||
random_num = choice(range(1000))
|
random_num = choice(range(1000))
|
||||||
|
|
||||||
@app.route('/hello')
|
@app.route('/hello')
|
||||||
|
@ -30,13 +29,12 @@ def test_response_body_not_a_string():
|
||||||
|
|
||||||
|
|
||||||
async def sample_streaming_fn(response):
|
async def sample_streaming_fn(response):
|
||||||
response.write('foo,')
|
await response.write('foo,')
|
||||||
await asyncio.sleep(.001)
|
await asyncio.sleep(.001)
|
||||||
response.write('bar')
|
await response.write('bar')
|
||||||
|
|
||||||
|
|
||||||
def test_method_not_allowed():
|
def test_method_not_allowed(app):
|
||||||
app = Sanic('method_not_allowed')
|
|
||||||
|
|
||||||
@app.get('/')
|
@app.get('/')
|
||||||
async def test(request):
|
async def test(request):
|
||||||
|
@ -64,9 +62,27 @@ def test_method_not_allowed():
|
||||||
assert response.headers['Content-Length'] == '0'
|
assert response.headers['Content-Length'] == '0'
|
||||||
|
|
||||||
|
|
||||||
|
def test_response_header(app):
|
||||||
|
|
||||||
|
@app.get('/')
|
||||||
|
async def test(request):
|
||||||
|
return json({
|
||||||
|
"ok": True
|
||||||
|
}, headers={
|
||||||
|
'CONTENT-TYPE': 'application/json'
|
||||||
|
})
|
||||||
|
|
||||||
|
request, response = app.test_client.get('/')
|
||||||
|
assert dict(response.headers) == {
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Keep-Alive': '2',
|
||||||
|
'Content-Length': '11',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def json_app():
|
def json_app(app):
|
||||||
app = Sanic('json')
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
|
@ -124,8 +140,7 @@ def test_no_content(json_app):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def streaming_app():
|
def streaming_app(app):
|
||||||
app = Sanic('streaming')
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
|
@ -170,20 +185,30 @@ def test_stream_response_includes_chunked_header():
|
||||||
|
|
||||||
def test_stream_response_writes_correct_content_to_transport(streaming_app):
|
def test_stream_response_writes_correct_content_to_transport(streaming_app):
|
||||||
response = StreamingHTTPResponse(sample_streaming_fn)
|
response = StreamingHTTPResponse(sample_streaming_fn)
|
||||||
response.transport = MagicMock(asyncio.Transport)
|
response.protocol = MagicMock(HttpProtocol)
|
||||||
|
response.protocol.transport = MagicMock(asyncio.Transport)
|
||||||
|
|
||||||
|
async def mock_drain():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def mock_push_data(data):
|
||||||
|
response.protocol.transport.write(data)
|
||||||
|
|
||||||
|
response.protocol.push_data = mock_push_data
|
||||||
|
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.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.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.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'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -208,25 +233,25 @@ def 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_file_response(file_name, static_file_directory):
|
@pytest.mark.parametrize('status', [200, 401])
|
||||||
app = Sanic('test_file_helper')
|
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, mime_type=guess_type(file_path)[0] or 'text/plain')
|
return file(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 == 200
|
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('source,dest', [
|
@pytest.mark.parametrize('source,dest', [
|
||||||
('test.file', 'my_file.txt'), ('decode me.txt', 'readme.md'), ('python.png', 'logo.png')])
|
('test.file', 'my_file.txt'), ('decode me.txt', 'readme.md'), ('python.png', 'logo.png')])
|
||||||
def test_file_response_custom_filename(source, dest, static_file_directory):
|
def test_file_response_custom_filename(app, source, dest, static_file_directory):
|
||||||
app = Sanic('test_file_helper')
|
|
||||||
|
|
||||||
@app.route('/files/<filename>', methods=['GET'])
|
@app.route('/files/<filename>', methods=['GET'])
|
||||||
def file_route(request, filename):
|
def file_route(request, filename):
|
||||||
|
@ -241,8 +266,7 @@ def test_file_response_custom_filename(source, dest, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_file_head_response(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_file_helper')
|
|
||||||
|
|
||||||
@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):
|
||||||
|
@ -270,8 +294,7 @@ def test_file_head_response(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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_file_stream_response(file_name, static_file_directory):
|
def test_file_stream_response(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_file_helper')
|
|
||||||
|
|
||||||
@app.route('/files/<filename>', methods=['GET'])
|
@app.route('/files/<filename>', methods=['GET'])
|
||||||
def file_route(request, filename):
|
def file_route(request, filename):
|
||||||
|
@ -288,8 +311,7 @@ def test_file_stream_response(file_name, static_file_directory):
|
||||||
|
|
||||||
@pytest.mark.parametrize('source,dest', [
|
@pytest.mark.parametrize('source,dest', [
|
||||||
('test.file', 'my_file.txt'), ('decode me.txt', 'readme.md'), ('python.png', 'logo.png')])
|
('test.file', 'my_file.txt'), ('decode me.txt', 'readme.md'), ('python.png', 'logo.png')])
|
||||||
def test_file_stream_response_custom_filename(source, dest, static_file_directory):
|
def test_file_stream_response_custom_filename(app, source, dest, static_file_directory):
|
||||||
app = Sanic('test_file_helper')
|
|
||||||
|
|
||||||
@app.route('/files/<filename>', methods=['GET'])
|
@app.route('/files/<filename>', methods=['GET'])
|
||||||
def file_route(request, filename):
|
def file_route(request, filename):
|
||||||
|
@ -304,8 +326,7 @@ def test_file_stream_response_custom_filename(source, dest, static_file_director
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt'])
|
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt'])
|
||||||
def test_file_stream_head_response(file_name, static_file_directory):
|
def test_file_stream_head_response(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_file_helper')
|
|
||||||
|
|
||||||
@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):
|
||||||
|
|
|
@ -7,6 +7,7 @@ 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_timeout_app.route('/1')
|
@response_timeout_app.route('/1')
|
||||||
|
@ -36,3 +37,29 @@ 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.exception(asyncio.CancelledError)
|
||||||
|
def handler_cancelled(request, exception):
|
||||||
|
# If we get a CancelledError, it means sanic has already sent a response,
|
||||||
|
# we should not ever have to handle a CancelledError.
|
||||||
|
response_handler_cancelled_app.flag = True
|
||||||
|
return text("App received CancelledError!", 500)
|
||||||
|
# The client will never receive this response, because the socket
|
||||||
|
# is already closed when we get a CancelledError.
|
||||||
|
|
||||||
|
|
||||||
|
@response_handler_cancelled_app.route('/1')
|
||||||
|
async def handler_3(request):
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
return text('OK')
|
||||||
|
|
||||||
|
|
||||||
|
def test_response_handler_cancelled():
|
||||||
|
request, response = response_handler_cancelled_app.test_client.get('/1')
|
||||||
|
assert response.status == 503
|
||||||
|
assert response.text == 'Error: Response Timeout'
|
||||||
|
assert response_handler_cancelled_app.flag is False
|
||||||
|
|
|
@ -12,9 +12,7 @@ from sanic.constants import HTTP_METHODS
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
||||||
@pytest.mark.parametrize('method', HTTP_METHODS)
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
def test_versioned_routes_get(method):
|
def test_versioned_routes_get(app, method):
|
||||||
app = Sanic('test_shorhand_routes_get')
|
|
||||||
|
|
||||||
method = method.lower()
|
method = method.lower()
|
||||||
|
|
||||||
func = getattr(app, method)
|
func = getattr(app, method)
|
||||||
|
@ -32,8 +30,7 @@ def test_versioned_routes_get(method):
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_routes_get():
|
def test_shorthand_routes_get(app):
|
||||||
app = Sanic('test_shorhand_routes_get')
|
|
||||||
|
|
||||||
@app.get('/get')
|
@app.get('/get')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -46,8 +43,7 @@ def test_shorthand_routes_get():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_routes_multiple():
|
def test_shorthand_routes_multiple(app):
|
||||||
app = Sanic('test_shorthand_routes_multiple')
|
|
||||||
|
|
||||||
@app.get('/get')
|
@app.get('/get')
|
||||||
def get_handler(request):
|
def get_handler(request):
|
||||||
|
@ -65,8 +61,7 @@ def test_shorthand_routes_multiple():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
|
|
||||||
def test_route_strict_slash():
|
def test_route_strict_slash(app):
|
||||||
app = Sanic('test_route_strict_slash')
|
|
||||||
|
|
||||||
@app.get('/get', strict_slashes=True)
|
@app.get('/get', strict_slashes=True)
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -93,9 +88,8 @@ def test_route_strict_slash():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_route_invalid_parameter_syntax():
|
def test_route_invalid_parameter_syntax(app):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
app = Sanic('test_route_invalid_param_syntax')
|
|
||||||
|
|
||||||
@app.get('/get/<:string>', strict_slashes=True)
|
@app.get('/get/<:string>', strict_slashes=True)
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -115,8 +109,7 @@ def test_route_strict_slash_default_value():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_route_strict_slash_without_passing_default_value():
|
def test_route_strict_slash_without_passing_default_value(app):
|
||||||
app = Sanic('test_route_strict_slash')
|
|
||||||
|
|
||||||
@app.get('/get')
|
@app.get('/get')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -137,8 +130,7 @@ def test_route_strict_slash_default_value_can_be_overwritten():
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
|
|
||||||
def test_route_slashes_overload():
|
def test_route_slashes_overload(app):
|
||||||
app = Sanic('test_route_slashes_overload')
|
|
||||||
|
|
||||||
@app.get('/hello/')
|
@app.get('/hello/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -161,8 +153,7 @@ def test_route_slashes_overload():
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
|
|
||||||
def test_route_optional_slash():
|
def test_route_optional_slash(app):
|
||||||
app = Sanic('test_route_optional_slash')
|
|
||||||
|
|
||||||
@app.get('/get')
|
@app.get('/get')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -174,9 +165,8 @@ def test_route_optional_slash():
|
||||||
request, response = app.test_client.get('/get/')
|
request, response = app.test_client.get('/get/')
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
def test_route_strict_slashes_set_to_false_and_host_is_a_list():
|
def test_route_strict_slashes_set_to_false_and_host_is_a_list(app):
|
||||||
#Part of regression test for issue #1120
|
#Part of regression test for issue #1120
|
||||||
app = Sanic('test_route_strict_slashes_set_to_false_and_host_is_a_list')
|
|
||||||
|
|
||||||
site1 = 'localhost:{}'.format(app.test_client.port)
|
site1 = 'localhost:{}'.format(app.test_client.port)
|
||||||
|
|
||||||
|
@ -209,8 +199,7 @@ def test_route_strict_slashes_set_to_false_and_host_is_a_list():
|
||||||
request, response = app.test_client.delete('http://' + site1 +'/delete')
|
request, response = app.test_client.delete('http://' + site1 +'/delete')
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
def test_shorthand_routes_post():
|
def test_shorthand_routes_post(app):
|
||||||
app = Sanic('test_shorhand_routes_post')
|
|
||||||
|
|
||||||
@app.post('/post')
|
@app.post('/post')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -223,8 +212,7 @@ def test_shorthand_routes_post():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_routes_put():
|
def test_shorthand_routes_put(app):
|
||||||
app = Sanic('test_shorhand_routes_put')
|
|
||||||
|
|
||||||
@app.put('/put')
|
@app.put('/put')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -240,8 +228,7 @@ def test_shorthand_routes_put():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_routes_delete():
|
def test_shorthand_routes_delete(app):
|
||||||
app = Sanic('test_shorhand_routes_delete')
|
|
||||||
|
|
||||||
@app.delete('/delete')
|
@app.delete('/delete')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -257,8 +244,7 @@ def test_shorthand_routes_delete():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_routes_patch():
|
def test_shorthand_routes_patch(app):
|
||||||
app = Sanic('test_shorhand_routes_patch')
|
|
||||||
|
|
||||||
@app.patch('/patch')
|
@app.patch('/patch')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -274,8 +260,7 @@ def test_shorthand_routes_patch():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_routes_head():
|
def test_shorthand_routes_head(app):
|
||||||
app = Sanic('test_shorhand_routes_head')
|
|
||||||
|
|
||||||
@app.head('/head')
|
@app.head('/head')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -291,8 +276,7 @@ def test_shorthand_routes_head():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_shorthand_routes_options():
|
def test_shorthand_routes_options(app):
|
||||||
app = Sanic('test_shorhand_routes_options')
|
|
||||||
|
|
||||||
@app.options('/options')
|
@app.options('/options')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
@ -308,8 +292,7 @@ def test_shorthand_routes_options():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_static_routes():
|
def test_static_routes(app):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
@app.route('/test')
|
@app.route('/test')
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
|
@ -326,9 +309,7 @@ def test_static_routes():
|
||||||
assert response.text == 'OK2'
|
assert response.text == 'OK2'
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_route():
|
def test_dynamic_route(app):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.route('/folder/<name>')
|
@app.route('/folder/<name>')
|
||||||
|
@ -342,9 +323,7 @@ def test_dynamic_route():
|
||||||
assert results[0] == 'test123'
|
assert results[0] == 'test123'
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_route_string():
|
def test_dynamic_route_string(app):
|
||||||
app = Sanic('test_dynamic_route_string')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.route('/folder/<name:string>')
|
@app.route('/folder/<name:string>')
|
||||||
|
@ -363,9 +342,7 @@ def test_dynamic_route_string():
|
||||||
assert results[1] == 'favicon.ico'
|
assert results[1] == 'favicon.ico'
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_route_int():
|
def test_dynamic_route_int(app):
|
||||||
app = Sanic('test_dynamic_route_int')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.route('/folder/<folder_id:int>')
|
@app.route('/folder/<folder_id:int>')
|
||||||
|
@ -381,9 +358,7 @@ def test_dynamic_route_int():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_route_number():
|
def test_dynamic_route_number(app):
|
||||||
app = Sanic('test_dynamic_route_number')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.route('/weight/<weight:number>')
|
@app.route('/weight/<weight:number>')
|
||||||
|
@ -402,8 +377,7 @@ def test_dynamic_route_number():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_route_regex():
|
def test_dynamic_route_regex(app):
|
||||||
app = Sanic('test_dynamic_route_regex')
|
|
||||||
|
|
||||||
@app.route('/folder/<folder_id:[A-Za-z0-9]{0,4}>')
|
@app.route('/folder/<folder_id:[A-Za-z0-9]{0,4}>')
|
||||||
async def handler(request, folder_id):
|
async def handler(request, folder_id):
|
||||||
|
@ -422,9 +396,8 @@ def test_dynamic_route_regex():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_route_uuid():
|
def test_dynamic_route_uuid(app):
|
||||||
import uuid
|
import uuid
|
||||||
app = Sanic('test_dynamic_route_uuid')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
@ -444,8 +417,7 @@ def test_dynamic_route_uuid():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_route_path():
|
def test_dynamic_route_path(app):
|
||||||
app = Sanic('test_dynamic_route_path')
|
|
||||||
|
|
||||||
@app.route('/<path:path>/info')
|
@app.route('/<path:path>/info')
|
||||||
async def handler(request, path):
|
async def handler(request, path):
|
||||||
|
@ -468,8 +440,7 @@ def test_dynamic_route_path():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_route_unhashable():
|
def test_dynamic_route_unhashable(app):
|
||||||
app = Sanic('test_dynamic_route_unhashable')
|
|
||||||
|
|
||||||
@app.route('/folder/<unhashable:[A-Za-z0-9/]+>/end/')
|
@app.route('/folder/<unhashable:[A-Za-z0-9/]+>/end/')
|
||||||
async def handler(request, unhashable):
|
async def handler(request, unhashable):
|
||||||
|
@ -488,8 +459,7 @@ def test_dynamic_route_unhashable():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_websocket_route():
|
def test_websocket_route(app):
|
||||||
app = Sanic('test_websocket_route')
|
|
||||||
ev = asyncio.Event()
|
ev = asyncio.Event()
|
||||||
|
|
||||||
@app.websocket('/ws')
|
@app.websocket('/ws')
|
||||||
|
@ -506,8 +476,7 @@ def test_websocket_route():
|
||||||
assert ev.is_set()
|
assert ev.is_set()
|
||||||
|
|
||||||
|
|
||||||
def test_websocket_route_with_subprotocols():
|
def test_websocket_route_with_subprotocols(app):
|
||||||
app = Sanic('test_websocket_route')
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.websocket('/ws', subprotocols=['foo', 'bar'])
|
@app.websocket('/ws', subprotocols=['foo', 'bar'])
|
||||||
|
@ -548,8 +517,7 @@ def test_websocket_route_with_subprotocols():
|
||||||
assert results == ['bar', 'bar', None, None]
|
assert results == ['bar', 'bar', None, None]
|
||||||
|
|
||||||
|
|
||||||
def test_route_duplicate():
|
def test_route_duplicate(app):
|
||||||
app = Sanic('test_route_duplicate')
|
|
||||||
|
|
||||||
with pytest.raises(RouteExists):
|
with pytest.raises(RouteExists):
|
||||||
@app.route('/test')
|
@app.route('/test')
|
||||||
|
@ -570,8 +538,7 @@ def test_route_duplicate():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_method_not_allowed():
|
def test_method_not_allowed(app):
|
||||||
app = Sanic('test_method_not_allowed')
|
|
||||||
|
|
||||||
@app.route('/test', methods=['GET'])
|
@app.route('/test', methods=['GET'])
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -584,8 +551,7 @@ def test_method_not_allowed():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_static_add_route():
|
def test_static_add_route(app):
|
||||||
app = Sanic('test_static_add_route')
|
|
||||||
|
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
return text('OK1')
|
return text('OK1')
|
||||||
|
@ -603,8 +569,7 @@ def test_static_add_route():
|
||||||
assert response.text == 'OK2'
|
assert response.text == 'OK2'
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_add_route():
|
def test_dynamic_add_route(app):
|
||||||
app = Sanic('test_dynamic_add_route')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
@ -619,8 +584,7 @@ def test_dynamic_add_route():
|
||||||
assert results[0] == 'test123'
|
assert results[0] == 'test123'
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_add_route_string():
|
def test_dynamic_add_route_string(app):
|
||||||
app = Sanic('test_dynamic_add_route_string')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
@ -640,9 +604,7 @@ def test_dynamic_add_route_string():
|
||||||
assert results[1] == 'favicon.ico'
|
assert results[1] == 'favicon.ico'
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_add_route_int():
|
def test_dynamic_add_route_int(app):
|
||||||
app = Sanic('test_dynamic_add_route_int')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
async def handler(request, folder_id):
|
async def handler(request, folder_id):
|
||||||
|
@ -659,9 +621,7 @@ def test_dynamic_add_route_int():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_add_route_number():
|
def test_dynamic_add_route_number(app):
|
||||||
app = Sanic('test_dynamic_add_route_number')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
async def handler(request, weight):
|
async def handler(request, weight):
|
||||||
|
@ -681,8 +641,7 @@ def test_dynamic_add_route_number():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_add_route_regex():
|
def test_dynamic_add_route_regex(app):
|
||||||
app = Sanic('test_dynamic_route_int')
|
|
||||||
|
|
||||||
async def handler(request, folder_id):
|
async def handler(request, folder_id):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -702,8 +661,7 @@ def test_dynamic_add_route_regex():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_add_route_unhashable():
|
def test_dynamic_add_route_unhashable(app):
|
||||||
app = Sanic('test_dynamic_add_route_unhashable')
|
|
||||||
|
|
||||||
async def handler(request, unhashable):
|
async def handler(request, unhashable):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -723,8 +681,7 @@ def test_dynamic_add_route_unhashable():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_add_route_duplicate():
|
def test_add_route_duplicate(app):
|
||||||
app = Sanic('test_add_route_duplicate')
|
|
||||||
|
|
||||||
with pytest.raises(RouteExists):
|
with pytest.raises(RouteExists):
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
|
@ -747,8 +704,7 @@ def test_add_route_duplicate():
|
||||||
app.add_route(handler2, '/test/<dynamic>/')
|
app.add_route(handler2, '/test/<dynamic>/')
|
||||||
|
|
||||||
|
|
||||||
def test_add_route_method_not_allowed():
|
def test_add_route_method_not_allowed(app):
|
||||||
app = Sanic('test_add_route_method_not_allowed')
|
|
||||||
|
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -762,8 +718,7 @@ def test_add_route_method_not_allowed():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_remove_static_route():
|
def test_remove_static_route(app):
|
||||||
app = Sanic('test_remove_static_route')
|
|
||||||
|
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
return text('OK1')
|
return text('OK1')
|
||||||
|
@ -790,8 +745,7 @@ def test_remove_static_route():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_remove_dynamic_route():
|
def test_remove_dynamic_route(app):
|
||||||
app = Sanic('test_remove_dynamic_route')
|
|
||||||
|
|
||||||
async def handler(request, name):
|
async def handler(request, name):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -806,15 +760,13 @@ def test_remove_dynamic_route():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_remove_inexistent_route():
|
def test_remove_inexistent_route(app):
|
||||||
app = Sanic('test_remove_inexistent_route')
|
|
||||||
|
|
||||||
with pytest.raises(RouteDoesNotExist):
|
with pytest.raises(RouteDoesNotExist):
|
||||||
app.remove_route('/test')
|
app.remove_route('/test')
|
||||||
|
|
||||||
|
|
||||||
def test_removing_slash():
|
def test_removing_slash(app):
|
||||||
app = Sanic(__name__)
|
|
||||||
|
|
||||||
@app.get('/rest/<resource>')
|
@app.get('/rest/<resource>')
|
||||||
def get(_):
|
def get(_):
|
||||||
|
@ -827,8 +779,7 @@ def test_removing_slash():
|
||||||
assert len(app.router.routes_all.keys()) == 2
|
assert len(app.router.routes_all.keys()) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_remove_unhashable_route():
|
def test_remove_unhashable_route(app):
|
||||||
app = Sanic('test_remove_unhashable_route')
|
|
||||||
|
|
||||||
async def handler(request, unhashable):
|
async def handler(request, unhashable):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -856,8 +807,7 @@ def test_remove_unhashable_route():
|
||||||
assert response.status == 404
|
assert response.status == 404
|
||||||
|
|
||||||
|
|
||||||
def test_remove_route_without_clean_cache():
|
def test_remove_route_without_clean_cache(app):
|
||||||
app = Sanic('test_remove_static_route')
|
|
||||||
|
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
@ -884,8 +834,7 @@ def test_remove_route_without_clean_cache():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
|
|
||||||
def test_overload_routes():
|
def test_overload_routes(app):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
@app.route('/overload', methods=['GET'])
|
@app.route('/overload', methods=['GET'])
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
|
@ -913,8 +862,7 @@ def test_overload_routes():
|
||||||
return text('Duplicated')
|
return text('Duplicated')
|
||||||
|
|
||||||
|
|
||||||
def test_unmergeable_overload_routes():
|
def test_unmergeable_overload_routes(app):
|
||||||
app = Sanic('test_dynamic_route')
|
|
||||||
|
|
||||||
@app.route('/overload_whole', methods=None)
|
@app.route('/overload_whole', methods=None)
|
||||||
async def handler1(request):
|
async def handler1(request):
|
||||||
|
@ -947,8 +895,7 @@ def test_unmergeable_overload_routes():
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
def test_unicode_routes():
|
def test_unicode_routes(app):
|
||||||
app = Sanic('test_unicode_routes')
|
|
||||||
|
|
||||||
@app.get('/你好')
|
@app.get('/你好')
|
||||||
def handler1(request):
|
def handler1(request):
|
||||||
|
@ -965,8 +912,7 @@ def test_unicode_routes():
|
||||||
assert response.text == 'OK2 你好'
|
assert response.text == 'OK2 你好'
|
||||||
|
|
||||||
|
|
||||||
def test_uri_with_different_method_and_different_params():
|
def test_uri_with_different_method_and_different_params(app):
|
||||||
app = Sanic('test_uri')
|
|
||||||
|
|
||||||
@app.route('/ads/<ad_id>', methods=['GET'])
|
@app.route('/ads/<ad_id>', methods=['GET'])
|
||||||
async def ad_get(request, ad_id):
|
async def ad_get(request, ad_id):
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
from io import StringIO
|
|
||||||
from random import choice
|
|
||||||
from string import ascii_letters
|
|
||||||
import signal
|
import signal
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.testing import HOST, PORT
|
from sanic.testing import HOST, PORT
|
||||||
|
|
||||||
AVAILABLE_LISTENERS = [
|
AVAILABLE_LISTENERS = [
|
||||||
|
@ -37,54 +33,46 @@ def start_stop_app(random_name_app, **run_kwargs):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('listener_name', AVAILABLE_LISTENERS)
|
@pytest.mark.parametrize('listener_name', AVAILABLE_LISTENERS)
|
||||||
def test_single_listener(listener_name):
|
def test_single_listener(app, listener_name):
|
||||||
"""Test that listeners on their own work"""
|
"""Test that listeners on their own work"""
|
||||||
random_name_app = Sanic(''.join(
|
output = []
|
||||||
[choice(ascii_letters) for _ in range(choice(range(5, 10)))]))
|
|
||||||
output = list()
|
|
||||||
# Register listener
|
# Register listener
|
||||||
random_name_app.listener(listener_name)(
|
app.listener(listener_name)(
|
||||||
create_listener(listener_name, output))
|
create_listener(listener_name, output))
|
||||||
start_stop_app(random_name_app)
|
start_stop_app(app)
|
||||||
assert random_name_app.name + listener_name == output.pop()
|
assert app.name + listener_name == output.pop()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('listener_name', AVAILABLE_LISTENERS)
|
@pytest.mark.parametrize('listener_name', AVAILABLE_LISTENERS)
|
||||||
def test_register_listener(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
|
||||||
app.register_listener method
|
app.register_listener method
|
||||||
"""
|
"""
|
||||||
random_name_app = Sanic(''.join(
|
output = []
|
||||||
[choice(ascii_letters) for _ in range(choice(range(5, 10)))]))
|
|
||||||
output = list()
|
|
||||||
# Register listener
|
# Register listener
|
||||||
listener = create_listener(listener_name, output)
|
listener = create_listener(listener_name, output)
|
||||||
random_name_app.register_listener(listener,
|
app.register_listener(listener,
|
||||||
event=listener_name)
|
event=listener_name)
|
||||||
start_stop_app(random_name_app)
|
start_stop_app(app)
|
||||||
assert random_name_app.name + listener_name == output.pop()
|
assert app.name + listener_name == output.pop()
|
||||||
|
|
||||||
|
|
||||||
def test_all_listeners():
|
def test_all_listeners(app):
|
||||||
random_name_app = Sanic(''.join(
|
output = []
|
||||||
[choice(ascii_letters) for _ in range(choice(range(5, 10)))]))
|
|
||||||
output = list()
|
|
||||||
for listener_name in AVAILABLE_LISTENERS:
|
for listener_name in AVAILABLE_LISTENERS:
|
||||||
listener = create_listener(listener_name, output)
|
listener = create_listener(listener_name, output)
|
||||||
random_name_app.listener(listener_name)(listener)
|
app.listener(listener_name)(listener)
|
||||||
start_stop_app(random_name_app)
|
start_stop_app(app)
|
||||||
for listener_name in AVAILABLE_LISTENERS:
|
for listener_name in AVAILABLE_LISTENERS:
|
||||||
assert random_name_app.name + listener_name == output.pop()
|
assert app.name + listener_name == output.pop()
|
||||||
|
|
||||||
|
|
||||||
async def test_trigger_before_events_create_server():
|
async def test_trigger_before_events_create_server(app):
|
||||||
|
|
||||||
class MySanicDb:
|
class MySanicDb:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
app = Sanic("test_sanic_app")
|
|
||||||
|
|
||||||
@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()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import HTTPResponse
|
from sanic.response import HTTPResponse
|
||||||
from sanic.testing import HOST, PORT
|
from sanic.testing import HOST, PORT
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
@ -18,9 +17,8 @@ def set_loop(app, loop):
|
||||||
def after(app, loop):
|
def after(app, loop):
|
||||||
calledq.put(loop.add_signal_handler.called)
|
calledq.put(loop.add_signal_handler.called)
|
||||||
|
|
||||||
def test_register_system_signals():
|
def test_register_system_signals(app):
|
||||||
"""Test if sanic register system signals"""
|
"""Test if sanic register system signals"""
|
||||||
app = Sanic('test_register_system_signals')
|
|
||||||
|
|
||||||
@app.route('/hello')
|
@app.route('/hello')
|
||||||
async def hello_route(request):
|
async def hello_route(request):
|
||||||
|
@ -34,9 +32,8 @@ def test_register_system_signals():
|
||||||
assert calledq.get() == True
|
assert calledq.get() == True
|
||||||
|
|
||||||
|
|
||||||
def test_dont_register_system_signals():
|
def test_dont_register_system_signals(app):
|
||||||
"""Test if sanic don't register system signals"""
|
"""Test if sanic don't register system signals"""
|
||||||
app = Sanic('test_register_system_signals')
|
|
||||||
|
|
||||||
@app.route('/hello')
|
@app.route('/hello')
|
||||||
async def hello_route(request):
|
async def hello_route(request):
|
||||||
|
|
|
@ -3,8 +3,6 @@ import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module')
|
@pytest.fixture(scope='module')
|
||||||
def static_file_directory():
|
def static_file_directory():
|
||||||
|
@ -26,8 +24,7 @@ def 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_static_file(static_file_directory, file_name):
|
def test_static_file(app, static_file_directory, file_name):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name))
|
'/testing.file', get_file_path(static_file_directory, file_name))
|
||||||
|
|
||||||
|
@ -36,11 +33,23 @@ def test_static_file(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)
|
||||||
|
|
||||||
|
|
||||||
|
@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')
|
||||||
|
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'])
|
@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(file_name, base_uri, static_file_directory):
|
def test_static_directory(app, file_name, base_uri, static_file_directory):
|
||||||
|
|
||||||
app = Sanic('test_static')
|
|
||||||
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(
|
||||||
|
@ -50,8 +59,7 @@ def test_static_directory(file_name, base_uri, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_head_request(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -66,8 +74,7 @@ def test_static_head_request(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_correct(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -87,8 +94,7 @@ def test_static_content_range_correct(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_front(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -108,8 +114,7 @@ def test_static_content_range_front(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_back(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -129,8 +134,7 @@ def test_static_content_range_back(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_empty(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -146,8 +150,7 @@ def test_static_content_range_empty(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_error(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -164,8 +167,7 @@ def test_static_content_range_error(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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_static_file_specified_host(static_file_directory, file_name):
|
def test_static_file_specified_host(app, static_file_directory, file_name):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file',
|
'/testing.file',
|
||||||
get_file_path(static_file_directory, file_name),
|
get_file_path(static_file_directory, file_name),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import pytest as pytest
|
import pytest as pytest
|
||||||
from urllib.parse import urlsplit, parse_qsl
|
from urllib.parse import urlsplit, parse_qsl
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
from sanic.views import HTTPMethodView
|
from sanic.views import HTTPMethodView
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
|
@ -30,8 +29,7 @@ def _generate_handlers_from_names(app, l):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def simple_app():
|
def simple_app(app):
|
||||||
app = Sanic('simple_app')
|
|
||||||
handler_names = list(string.ascii_letters)
|
handler_names = list(string.ascii_letters)
|
||||||
|
|
||||||
_generate_handlers_from_names(app, handler_names)
|
_generate_handlers_from_names(app, handler_names)
|
||||||
|
@ -54,8 +52,7 @@ def test_simple_url_for_getting(simple_app):
|
||||||
(URL_FOR_ARGS2, URL_FOR_VALUE2),
|
(URL_FOR_ARGS2, URL_FOR_VALUE2),
|
||||||
(URL_FOR_ARGS3, URL_FOR_VALUE3),
|
(URL_FOR_ARGS3, URL_FOR_VALUE3),
|
||||||
(URL_FOR_ARGS4, URL_FOR_VALUE4)])
|
(URL_FOR_ARGS4, URL_FOR_VALUE4)])
|
||||||
def test_simple_url_for_getting_with_more_params(args, url):
|
def test_simple_url_for_getting_with_more_params(app, args, url):
|
||||||
app = Sanic('more_url_build')
|
|
||||||
|
|
||||||
@app.route('/myurl')
|
@app.route('/myurl')
|
||||||
def passes(request):
|
def passes(request):
|
||||||
|
@ -67,8 +64,7 @@ def test_simple_url_for_getting_with_more_params(args, url):
|
||||||
assert response.text == 'this should pass'
|
assert response.text == 'this should pass'
|
||||||
|
|
||||||
|
|
||||||
def test_fails_if_endpoint_not_found():
|
def test_fails_if_endpoint_not_found(app):
|
||||||
app = Sanic('fail_url_build')
|
|
||||||
|
|
||||||
@app.route('/fail')
|
@app.route('/fail')
|
||||||
def fail(request):
|
def fail(request):
|
||||||
|
@ -80,14 +76,12 @@ def test_fails_if_endpoint_not_found():
|
||||||
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():
|
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 = Sanic('fail_url_build')
|
|
||||||
|
|
||||||
@app.route(url)
|
@app.route(url)
|
||||||
def fail(request):
|
def fail(request):
|
||||||
return text('this should fail')
|
return text('this should fail')
|
||||||
|
@ -103,8 +97,7 @@ def test_fails_url_build_if_param_not_passed():
|
||||||
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():
|
def test_fails_url_build_if_params_not_passed(app):
|
||||||
app = Sanic('fail_url_build')
|
|
||||||
|
|
||||||
@app.route('/fail')
|
@app.route('/fail')
|
||||||
def fail(request):
|
def fail(request):
|
||||||
|
@ -126,8 +119,7 @@ PASSING_KWARGS = {
|
||||||
EXPECTED_BUILT_URL = '/4/woof/ba/normal/1.001'
|
EXPECTED_BUILT_URL = '/4/woof/ba/normal/1.001'
|
||||||
|
|
||||||
|
|
||||||
def test_fails_with_int_message():
|
def test_fails_with_int_message(app):
|
||||||
app = Sanic('fail_url_build')
|
|
||||||
|
|
||||||
@app.route(COMPLEX_PARAM_URL)
|
@app.route(COMPLEX_PARAM_URL)
|
||||||
def fail(request):
|
def fail(request):
|
||||||
|
@ -145,8 +137,7 @@ def test_fails_with_int_message():
|
||||||
assert str(e.value) == expected_error
|
assert str(e.value) == expected_error
|
||||||
|
|
||||||
|
|
||||||
def test_fails_with_two_letter_string_message():
|
def test_fails_with_two_letter_string_message(app):
|
||||||
app = Sanic('fail_url_build')
|
|
||||||
|
|
||||||
@app.route(COMPLEX_PARAM_URL)
|
@app.route(COMPLEX_PARAM_URL)
|
||||||
def fail(request):
|
def fail(request):
|
||||||
|
@ -165,8 +156,7 @@ def test_fails_with_two_letter_string_message():
|
||||||
assert str(e.value) == expected_error
|
assert str(e.value) == expected_error
|
||||||
|
|
||||||
|
|
||||||
def test_fails_with_number_message():
|
def test_fails_with_number_message(app):
|
||||||
app = Sanic('fail_url_build')
|
|
||||||
|
|
||||||
@app.route(COMPLEX_PARAM_URL)
|
@app.route(COMPLEX_PARAM_URL)
|
||||||
def fail(request):
|
def fail(request):
|
||||||
|
@ -185,8 +175,7 @@ def test_fails_with_number_message():
|
||||||
assert str(e.value) == expected_error
|
assert str(e.value) == expected_error
|
||||||
|
|
||||||
|
|
||||||
def test_adds_other_supplied_values_as_query_string():
|
def test_adds_other_supplied_values_as_query_string(app):
|
||||||
app = Sanic('passes')
|
|
||||||
|
|
||||||
@app.route(COMPLEX_PARAM_URL)
|
@app.route(COMPLEX_PARAM_URL)
|
||||||
def passes(request):
|
def passes(request):
|
||||||
|
@ -205,8 +194,7 @@ def test_adds_other_supplied_values_as_query_string():
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def blueprint_app():
|
def blueprint_app(app):
|
||||||
app = Sanic('blueprints')
|
|
||||||
|
|
||||||
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')
|
||||||
|
@ -252,8 +240,7 @@ def test_blueprints_work_with_params(blueprint_app):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def methodview_app():
|
def methodview_app(app):
|
||||||
app = Sanic('methodview')
|
|
||||||
|
|
||||||
class ViewOne(HTTPMethodView):
|
class ViewOne(HTTPMethodView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
|
|
@ -3,7 +3,6 @@ import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,8 +26,7 @@ def 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_static_file(static_file_directory, file_name):
|
def test_static_file(app, static_file_directory, file_name):
|
||||||
app = Sanic('test_static')
|
|
||||||
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(
|
||||||
|
@ -102,9 +100,7 @@ def test_static_file(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(file_name, base_uri, static_file_directory):
|
def test_static_directory(app, file_name, base_uri, static_file_directory):
|
||||||
|
|
||||||
app = Sanic('test_static')
|
|
||||||
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')
|
||||||
|
@ -156,10 +152,8 @@ def test_static_directory(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(file_name, static_file_directory):
|
def test_static_head_request(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -198,8 +192,7 @@ def test_static_head_request(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_correct(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -250,8 +243,7 @@ def test_static_content_range_correct(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_front(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -302,8 +294,7 @@ def test_static_content_range_front(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_back(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -354,8 +345,7 @@ def test_static_content_range_back(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_empty(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
@ -401,8 +391,7 @@ def test_static_content_range_empty(file_name, static_file_directory):
|
||||||
|
|
||||||
|
|
||||||
@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(file_name, static_file_directory):
|
def test_static_content_range_error(app, file_name, static_file_directory):
|
||||||
app = Sanic('test_static')
|
|
||||||
app.static(
|
app.static(
|
||||||
'/testing.file', get_file_path(static_file_directory, file_name),
|
'/testing.file', get_file_path(static_file_directory, file_name),
|
||||||
use_content_range=True)
|
use_content_range=True)
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
from json import loads as json_loads, dumps as json_dumps
|
from json import dumps as json_dumps
|
||||||
from sanic import Sanic
|
from sanic.response import text
|
||||||
from sanic.response import json, text
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
# UTF-8
|
# UTF-8
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
||||||
def test_utf8_query_string():
|
def test_utf8_query_string(app):
|
||||||
app = Sanic('test_utf8_query_string')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -18,8 +16,7 @@ def test_utf8_query_string():
|
||||||
assert request.args.get('utf8') == '✓'
|
assert request.args.get('utf8') == '✓'
|
||||||
|
|
||||||
|
|
||||||
def test_utf8_response():
|
def test_utf8_response(app):
|
||||||
app = Sanic('test_utf8_response')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -29,8 +26,7 @@ def test_utf8_response():
|
||||||
assert response.text == '✓'
|
assert response.text == '✓'
|
||||||
|
|
||||||
|
|
||||||
def skip_test_utf8_route():
|
def skip_test_utf8_route(app):
|
||||||
app = Sanic('skip_test_utf8_route')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -41,8 +37,7 @@ def skip_test_utf8_route():
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
|
|
||||||
def test_utf8_post_json():
|
def test_utf8_post_json(app):
|
||||||
app = Sanic('test_utf8_post_json')
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
from sanic import Sanic
|
from sanic.response import text
|
||||||
from sanic.response import json, text
|
|
||||||
|
|
||||||
|
|
||||||
def test_vhosts():
|
def test_vhosts(app):
|
||||||
app = Sanic('test_vhosts')
|
|
||||||
|
|
||||||
@app.route('/', host="example.com")
|
@app.route('/', host="example.com")
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -22,8 +20,7 @@ def test_vhosts():
|
||||||
assert response.text == "You're at subdomain.example.com!"
|
assert response.text == "You're at subdomain.example.com!"
|
||||||
|
|
||||||
|
|
||||||
def test_vhosts_with_list():
|
def test_vhosts_with_list(app):
|
||||||
app = Sanic('test_vhosts')
|
|
||||||
|
|
||||||
@app.route('/', host=["hello.com", "world.com"])
|
@app.route('/', host=["hello.com", "world.com"])
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -37,8 +34,8 @@ def test_vhosts_with_list():
|
||||||
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 = Sanic('test_vhosts')
|
def test_vhosts_with_defaults(app):
|
||||||
|
|
||||||
@app.route('/', host="hello.com")
|
@app.route('/', host="hello.com")
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import pytest as pytest
|
import pytest as pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.exceptions import InvalidUsage
|
from sanic.exceptions import InvalidUsage
|
||||||
from sanic.response import text, HTTPResponse
|
from sanic.response import text, HTTPResponse
|
||||||
from sanic.views import HTTPMethodView, CompositionView
|
from sanic.views import HTTPMethodView, CompositionView
|
||||||
|
@ -10,8 +9,7 @@ from sanic.constants import HTTP_METHODS
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('method', HTTP_METHODS)
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
def test_methods(method):
|
def test_methods(app, method):
|
||||||
app = Sanic('test_methods')
|
|
||||||
|
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
|
|
||||||
|
@ -44,8 +42,7 @@ def test_methods(method):
|
||||||
assert response.headers['method'] == method
|
assert response.headers['method'] == method
|
||||||
|
|
||||||
|
|
||||||
def test_unexisting_methods():
|
def test_unexisting_methods(app):
|
||||||
app = Sanic('test_unexisting_methods')
|
|
||||||
|
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
|
|
||||||
|
@ -59,8 +56,7 @@ def test_unexisting_methods():
|
||||||
assert response.text == 'Error: Method POST not allowed for URL /'
|
assert response.text == 'Error: Method POST not allowed for URL /'
|
||||||
|
|
||||||
|
|
||||||
def test_argument_methods():
|
def test_argument_methods(app):
|
||||||
app = Sanic('test_argument_methods')
|
|
||||||
|
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
|
|
||||||
|
@ -74,8 +70,7 @@ def test_argument_methods():
|
||||||
assert response.text == 'I am get method with test123'
|
assert response.text == 'I am get method with test123'
|
||||||
|
|
||||||
|
|
||||||
def test_with_bp():
|
def test_with_bp(app):
|
||||||
app = Sanic('test_with_bp')
|
|
||||||
bp = Blueprint('test_text')
|
bp = Blueprint('test_text')
|
||||||
|
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
|
@ -93,8 +88,7 @@ def test_with_bp():
|
||||||
assert response.text == 'I am get method'
|
assert response.text == 'I am get method'
|
||||||
|
|
||||||
|
|
||||||
def test_with_bp_with_url_prefix():
|
def test_with_bp_with_url_prefix(app):
|
||||||
app = Sanic('test_with_bp_with_url_prefix')
|
|
||||||
bp = Blueprint('test_text', url_prefix='/test1')
|
bp = Blueprint('test_text', url_prefix='/test1')
|
||||||
|
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
|
@ -110,8 +104,7 @@ def test_with_bp_with_url_prefix():
|
||||||
assert response.text == 'I am get method'
|
assert response.text == 'I am get method'
|
||||||
|
|
||||||
|
|
||||||
def test_with_middleware():
|
def test_with_middleware(app):
|
||||||
app = Sanic('test_with_middleware')
|
|
||||||
|
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
|
|
||||||
|
@ -132,9 +125,7 @@ def test_with_middleware():
|
||||||
assert type(results[0]) is Request
|
assert type(results[0]) is Request
|
||||||
|
|
||||||
|
|
||||||
def test_with_middleware_response():
|
def test_with_middleware_response(app):
|
||||||
app = Sanic('test_with_middleware_response')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@app.middleware('request')
|
@app.middleware('request')
|
||||||
|
@ -161,8 +152,7 @@ def test_with_middleware_response():
|
||||||
assert isinstance(results[2], HTTPResponse)
|
assert isinstance(results[2], HTTPResponse)
|
||||||
|
|
||||||
|
|
||||||
def test_with_custom_class_methods():
|
def test_with_custom_class_methods(app):
|
||||||
app = Sanic('test_with_custom_class_methods')
|
|
||||||
|
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
global_var = 0
|
global_var = 0
|
||||||
|
@ -179,9 +169,7 @@ def test_with_custom_class_methods():
|
||||||
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():
|
def test_with_decorator(app):
|
||||||
app = Sanic('test_with_decorator')
|
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
def stupid_decorator(view):
|
def stupid_decorator(view):
|
||||||
|
@ -227,9 +215,7 @@ def test_composition_view_rejects_duplicate_methods():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('method', HTTP_METHODS)
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
def test_composition_view_runs_methods_as_expected(method):
|
def test_composition_view_runs_methods_as_expected(app, method):
|
||||||
app = Sanic('test_composition_view')
|
|
||||||
|
|
||||||
view = CompositionView()
|
view = CompositionView()
|
||||||
|
|
||||||
def first(request):
|
def first(request):
|
||||||
|
@ -251,9 +237,7 @@ def test_composition_view_runs_methods_as_expected(method):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('method', HTTP_METHODS)
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
def test_composition_view_rejects_invalid_methods(method):
|
def test_composition_view_rejects_invalid_methods(app, method):
|
||||||
app = Sanic('test_composition_view')
|
|
||||||
|
|
||||||
view = CompositionView()
|
view = CompositionView()
|
||||||
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method'))
|
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method'))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user