`_ that we use to track those projects!
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
: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
- :target: https://travis-ci.org/channelcat/sanic
+.. |Build Status| image:: https://travis-ci.org/huge-success/sanic.svg?branch=master
+ :target: https://travis-ci.org/huge-success/sanic
.. |Documentation| image:: https://readthedocs.org/projects/sanic/badge/?version=latest
:target: http://sanic.readthedocs.io/en/latest/?badge=latest
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
@@ -56,24 +56,22 @@ Documentation
.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic.svg
:target: https://pypi.python.org/pypi/sanic/
+
+Questions and Discussion
+------------------------
+
+`Ask a question or join the conversation `_.
+
Examples
--------
-`Non-Core examples `_. Examples of plugins and Sanic that are outside the scope of Sanic core.
+`Non-Core examples `_. Examples of plugins and Sanic that are outside the scope of Sanic core.
-`Extensions `_. Sanic extensions created by the community.
+`Extensions `_. Sanic extensions created by the community.
-`Projects `_. Sanic in production use.
+`Projects `_. Sanic in production use.
-TODO
-----
- * http2
-
-Limitations
------------
-* No wheels for uvloop and httptools on Windows :(
-
Final Thoughts
--------------
diff --git a/docs/sanic/extensions.md b/docs/sanic/extensions.md
index 01c89f95..c0728627 100644
--- a/docs/sanic/extensions.md
+++ b/docs/sanic/extensions.md
@@ -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-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-sse](https://github.com/inn0kenty/sanic_sse): [Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) implementation for Sanic.
diff --git a/docs/sanic/streaming.md b/docs/sanic/streaming.md
index a785322a..bf3ca664 100644
--- a/docs/sanic/streaming.md
+++ b/docs/sanic/streaming.md
@@ -37,7 +37,7 @@ async def handler(request):
if body is None:
break
body = body.decode('utf-8').replace('1', 'A')
- response.write(body)
+ await response.write(body)
return stream(streaming)
@@ -85,8 +85,8 @@ app = Sanic(__name__)
@app.route("/")
async def test(request):
async def sample_streaming_fn(response):
- response.write('foo,')
- response.write('bar')
+ await response.write('foo,')
+ await response.write('bar')
return stream(sample_streaming_fn, content_type='text/csv')
```
@@ -100,7 +100,7 @@ async def index(request):
conn = await asyncpg.connect(database='test')
async with conn.transaction():
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)
```
diff --git a/environment.yml b/environment.yml
index 1c1dd82f..78eddfd0 100644
--- a/environment.yml
+++ b/environment.yml
@@ -12,9 +12,10 @@ dependencies:
- zlib=1.2.8=0
- pip:
- uvloop>=0.5.3
- - httptools>=0.0.9
+ - httptools>=0.0.10
- ujson>=1.35
- aiofiles>=0.3.0
- - websockets>=3.2
+ - websockets>=6.0
- sphinxcontrib-asyncio>=0.2.0
+ - multidict>=4.0<5.0
- https://github.com/channelcat/docutils-fork/zipball/master
diff --git a/examples/blueprints.py b/examples/blueprints.py
index 03154049..29144c4e 100644
--- a/examples/blueprints.py
+++ b/examples/blueprints.py
@@ -1,7 +1,5 @@
-from sanic import Sanic
-from sanic import Blueprint
-from sanic.response import json
-
+from sanic import Blueprint, Sanic
+from sanic.response import file, json
app = Sanic(__name__)
blueprint = Blueprint('name', url_prefix='/my_blueprint')
@@ -19,7 +17,12 @@ async def foo2(request):
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):
while True:
data = 'hello!'
diff --git a/examples/request_stream/server.py b/examples/request_stream/server.py
index e53a224c..d3d35aef 100644
--- a/examples/request_stream/server.py
+++ b/examples/request_stream/server.py
@@ -30,7 +30,7 @@ async def handler(request):
if body is None:
break
body = body.decode('utf-8').replace('1', 'A')
- response.write(body)
+ await response.write(body)
return stream(streaming)
diff --git a/examples/simple_async_view.py b/examples/simple_async_view.py
new file mode 100644
index 00000000..990aa21a
--- /dev/null
+++ b/examples/simple_async_view.py
@@ -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)
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 674ef91d..12b29a2b 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,12 +1,13 @@
aiofiles
-aiohttp>=2.3.0
+aiohttp>=2.3.0,<=3.2.1
chardet<=2.3.0
beautifulsoup4
coverage
-httptools
+httptools>=0.0.10
flake8
pytest==3.3.2
tox
ujson; sys_platform != "win32" and implementation_name == "cpython"
uvloop; sys_platform != "win32" and implementation_name == "cpython"
gunicorn
+multidict>=4.0,<5.0
diff --git a/requirements.txt b/requirements.txt
index 05968bd8..3e577b2b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,6 @@
aiofiles
-httptools
+httptools>=0.0.10
ujson; 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
diff --git a/sanic/__init__.py b/sanic/__init__.py
index 78bc7bd9..51c8268e 100644
--- a/sanic/__init__.py
+++ b/sanic/__init__.py
@@ -1,6 +1,6 @@
from sanic.app import Sanic
from sanic.blueprints import Blueprint
-__version__ = '0.7.0'
+__version__ = '0.8.3'
__all__ = ['Sanic', 'Blueprint']
diff --git a/sanic/app.py b/sanic/app.py
index ca50edc1..5af21751 100644
--- a/sanic/app.py
+++ b/sanic/app.py
@@ -170,7 +170,7 @@ class Sanic:
return handler
else:
raise ValueError(
- 'Required parameter `request` missing'
+ 'Required parameter `request` missing '
'in the {0}() route?'.format(
handler.__name__))
@@ -315,13 +315,13 @@ class Sanic:
return response
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."""
if strict_slashes is None:
strict_slashes = self.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):
"""Enable or disable the support for websocket.
@@ -386,13 +386,14 @@ class Sanic:
def static(self, uri, file_or_directory, pattern=r'/?.+',
use_modified_since=True, use_content_range=False,
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
file or a directory. See
"""
static_register(self, uri, file_or_directory, pattern,
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):
"""Register a blueprint on the application.
@@ -570,6 +571,10 @@ class Sanic:
:return: Nothing
"""
+ # Define `response` var here to remove warnings about
+ # allocation before assignment below.
+ response = None
+ cancelled = False
try:
# -------------------------------------------- #
# Request Middleware
@@ -596,6 +601,13 @@ class Sanic:
response = handler(request, *args, **kwargs)
if isawaitable(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:
# -------------------------------------------- #
# Response Generation Failed
@@ -621,13 +633,22 @@ class Sanic:
# -------------------------------------------- #
# Response Middleware
# -------------------------------------------- #
- try:
- response = await self._run_response_middleware(request,
- response)
- except BaseException:
- error_logger.exception(
- 'Exception occurred in one of response middleware handlers'
- )
+ # Don't run response middleware if response is None
+ if response is not None:
+ try:
+ response = await self._run_response_middleware(request,
+ response)
+ except CancelledError:
+ # Response middleware can timeout too, as above.
+ response = None
+ cancelled = True
+ except BaseException:
+ error_logger.exception(
+ 'Exception occurred in one of response '
+ 'middleware handlers'
+ )
+ if cancelled:
+ raise CancelledError()
# pass the response to the correct callback
if isinstance(response, StreamingHTTPResponse):
@@ -670,8 +691,8 @@ class Sanic:
"""
# Default auto_reload to false
auto_reload = False
- # If debug is set, default it to true
- if debug:
+ # If debug is set, default it to true (unless on windows)
+ if debug and os.name == 'posix':
auto_reload = True
# Allow for overriding either of the defaults
auto_reload = kwargs.get("auto_reload", auto_reload)
@@ -687,11 +708,12 @@ class Sanic:
warnings.simplefilter('default')
warnings.warn("stop_event will be removed from future versions.",
DeprecationWarning)
+ # compatibility old access_log params
+ self.config.ACCESS_LOG = access_log
server_settings = self._helper(
host=host, port=port, debug=debug, ssl=ssl, sock=sock,
workers=workers, protocol=protocol, backlog=backlog,
- register_sys_signals=register_sys_signals,
- access_log=access_log, auto_reload=auto_reload)
+ register_sys_signals=register_sys_signals, auto_reload=auto_reload)
try:
self.is_running = True
@@ -745,12 +767,12 @@ class Sanic:
warnings.simplefilter('default')
warnings.warn("stop_event will be removed from future versions.",
DeprecationWarning)
-
+ # compatibility old access_log params
+ self.config.ACCESS_LOG = access_log
server_settings = self._helper(
host=host, port=port, debug=debug, ssl=ssl, sock=sock,
loop=get_event_loop(), protocol=protocol,
- backlog=backlog, run_async=True,
- access_log=access_log)
+ backlog=backlog, run_async=True)
# Trigger before_start events
await self.trigger_events(
@@ -795,8 +817,7 @@ class Sanic:
def _helper(self, host=None, port=None, debug=False,
ssl=None, sock=None, workers=1, loop=None,
protocol=HttpProtocol, backlog=100, stop_event=None,
- register_sys_signals=True, run_async=False, access_log=True,
- auto_reload=False):
+ register_sys_signals=True, run_async=False, auto_reload=False):
"""Helper function used by `run` and `create_server`."""
if isinstance(ssl, dict):
# try common aliaseses
@@ -837,7 +858,7 @@ class Sanic:
'loop': loop,
'register_sys_signals': register_sys_signals,
'backlog': backlog,
- 'access_log': access_log,
+ 'access_log': self.config.ACCESS_LOG,
'websocket_max_size': self.config.WEBSOCKET_MAX_SIZE,
'websocket_max_queue': self.config.WEBSOCKET_MAX_QUEUE,
'websocket_read_limit': self.config.WEBSOCKET_READ_LIMIT,
diff --git a/sanic/config.py b/sanic/config.py
index 8e1f383c..c5e42de5 100644
--- a/sanic/config.py
+++ b/sanic/config.py
@@ -39,6 +39,7 @@ class Config(dict):
self.WEBSOCKET_READ_LIMIT = 2 ** 16
self.WEBSOCKET_WRITE_LIMIT = 2 ** 16
self.GRACEFUL_SHUTDOWN_TIMEOUT = 15.0 # 15 sec
+ self.ACCESS_LOG = True
if load_env:
prefix = SANIC_PREFIX if load_env is True else load_env
diff --git a/sanic/cookies.py b/sanic/cookies.py
index f4cbf6a3..19daac11 100644
--- a/sanic/cookies.py
+++ b/sanic/cookies.py
@@ -47,16 +47,15 @@ class CookieJar(dict):
super().__init__()
self.headers = headers
self.cookie_headers = {}
+ self.header_key = "Set-Cookie"
def __setitem__(self, key, value):
# If this cookie doesn't exist, add it to the header keys
- cookie_header = self.cookie_headers.get(key)
- if not cookie_header:
+ if not self.cookie_headers.get(key):
cookie = Cookie(key, value)
cookie['path'] = '/'
- cookie_header = MultiHeader("Set-Cookie")
- self.cookie_headers[key] = cookie_header
- self.headers[cookie_header] = cookie
+ self.cookie_headers[key] = self.header_key
+ self.headers.add(self.header_key, cookie)
return super().__setitem__(key, cookie)
else:
self[key].value = value
@@ -67,7 +66,11 @@ class CookieJar(dict):
self[key]['max-age'] = 0
else:
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]
return super().__delitem__(key)
@@ -124,18 +127,3 @@ class Cookie(dict):
output.append('%s=%s' % (self._keys[key], value))
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()
diff --git a/sanic/reloader_helpers.py b/sanic/reloader_helpers.py
index 73759124..e0cb42e0 100644
--- a/sanic/reloader_helpers.py
+++ b/sanic/reloader_helpers.py
@@ -74,7 +74,14 @@ def kill_process_children_unix(pid):
with open(children_proc_path) as children_list_file_2:
children_list_pid_2 = children_list_file_2.read().split()
for _pid in children_list_pid_2:
- os.kill(int(_pid), signal.SIGTERM)
+ try:
+ 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):
@@ -94,7 +101,7 @@ def kill_process_children(pid):
"""
if sys.platform == 'darwin':
kill_process_children_osx(pid)
- elif sys.platform == 'posix':
+ elif sys.platform == 'linux':
kill_process_children_unix(pid)
else:
pass # should signal error here
@@ -136,8 +143,8 @@ def watchdog(sleep_interval):
continue
elif mtime > old_time:
kill_process_children(worker_process.pid)
+ worker_process.terminate()
worker_process = restart_with_reloader()
-
mtimes[filename] = mtime
break
diff --git a/sanic/request.py b/sanic/request.py
index 7ce7620d..c8b470d4 100644
--- a/sanic/request.py
+++ b/sanic/request.py
@@ -48,10 +48,11 @@ class Request(dict):
'app', 'headers', 'version', 'method', '_cookies', 'transport',
'body', 'parsed_json', 'parsed_args', 'parsed_form', 'parsed_files',
'_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):
+ self.raw_url = url_bytes
# TODO: Content-Encoding detection
self._parsed_url = parse_url(url_bytes)
self.app = None
diff --git a/sanic/response.py b/sanic/response.py
index b32e9daf..f169b4f2 100644
--- a/sanic/response.py
+++ b/sanic/response.py
@@ -1,5 +1,6 @@
from mimetypes import guess_type
from os import path
+from urllib.parse import quote_plus
try:
from ujson import dumps as json_dumps
@@ -7,6 +8,7 @@ except BaseException:
from json import dumps as json_dumps
from aiofiles import open as open_async
+from multidict import CIMultiDict
from sanic import http
from sanic.cookies import CookieJar
@@ -44,7 +46,7 @@ class BaseHTTPResponse:
class StreamingHTTPResponse(BaseHTTPResponse):
__slots__ = (
- 'transport', 'streaming_fn', 'status',
+ 'protocol', 'streaming_fn', 'status',
'content_type', 'headers', '_cookies'
)
@@ -53,10 +55,10 @@ class StreamingHTTPResponse(BaseHTTPResponse):
self.content_type = content_type
self.streaming_fn = streaming_fn
self.status = status
- self.headers = headers or {}
+ self.headers = CIMultiDict(headers or {})
self._cookies = None
- def write(self, data):
+ async def write(self, data):
"""Writes a chunk of data to the streaming response.
:param data: bytes-ish data to be written.
@@ -64,8 +66,9 @@ class StreamingHTTPResponse(BaseHTTPResponse):
if type(data) != bytes:
data = self._encode_body(data)
- self.transport.write(
+ self.protocol.push_data(
b"%x\r\n%b\r\n" % (len(data), data))
+ await self.protocol.drain()
async def stream(
self, version="1.1", keep_alive=False, keep_alive_timeout=None):
@@ -75,10 +78,12 @@ class StreamingHTTPResponse(BaseHTTPResponse):
headers = self.get_headers(
version, keep_alive=keep_alive,
keep_alive_timeout=keep_alive_timeout)
- self.transport.write(headers)
-
+ self.protocol.push_data(headers)
+ await self.protocol.drain()
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(
self, version="1.1", keep_alive=False, keep_alive_timeout=None):
@@ -124,7 +129,7 @@ class HTTPResponse(BaseHTTPResponse):
self.body = body_bytes
self.status = status
- self.headers = headers or {}
+ self.headers = CIMultiDict(headers or {})
self._cookies = None
def output(
@@ -231,8 +236,8 @@ def html(body, status=200, headers=None):
content_type="text/html; charset=utf-8")
-async def file(
- location, mime_type=None, headers=None, filename=None, _range=None):
+async def file(location, status=200, mime_type=None, headers=None,
+ filename=None, _range=None):
"""Return a response object with file data.
:param location: Location of file on system.
@@ -258,15 +263,14 @@ async def file(
out_stream = await _file.read()
mime_type = mime_type or guess_type(filename)[0] or 'text/plain'
- return HTTPResponse(status=200,
+ return HTTPResponse(status=status,
headers=headers,
content_type=mime_type,
body_bytes=out_stream)
-async def file_stream(
- location, chunk_size=4096, mime_type=None, headers=None,
- filename=None, _range=None):
+async def file_stream(location, status=200, chunk_size=4096, mime_type=None,
+ headers=None, filename=None, _range=None):
"""Return a streaming response object with file data.
:param location: Location of file on system.
@@ -297,13 +301,13 @@ async def file_stream(
if len(content) < 1:
break
to_send -= len(content)
- response.write(content)
+ await response.write(content)
else:
while True:
content = await _file.read(chunk_size)
if len(content) < 1:
break
- response.write(content)
+ await response.write(content)
finally:
await _file.close()
return # Returning from this fn closes the stream
@@ -313,7 +317,7 @@ async def file_stream(
headers['Content-Range'] = 'bytes %s-%s/%s' % (
_range.start, _range.end, _range.total)
return StreamingHTTPResponse(streaming_fn=_streaming_fn,
- status=200,
+ status=status,
headers=headers,
content_type=mime_type)
@@ -359,8 +363,11 @@ def redirect(to, headers=None, status=302,
"""
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.
- headers['Location'] = to
+ headers['Location'] = safe_to
return HTTPResponse(
status=status,
diff --git a/sanic/server.py b/sanic/server.py
index fc8291b5..17529a32 100644
--- a/sanic/server.py
+++ b/sanic/server.py
@@ -18,6 +18,7 @@ from time import time
from httptools import HttpRequestParser
from httptools.parser.errors import HttpParserError
+from multidict import CIMultiDict
try:
import uvloop
@@ -39,25 +40,6 @@ class Signal:
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):
__slots__ = (
# event loop, connection
@@ -73,7 +55,8 @@ class HttpProtocol(asyncio.Protocol):
# connection management
'_total_request_size', '_request_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,
signal=Signal(), connections=set(), request_timeout=60,
@@ -100,6 +83,7 @@ class HttpProtocol(asyncio.Protocol):
self.request_class = request_class or Request
self.is_request_stream = is_request_stream
self._is_stream_handler = False
+ self._not_paused = asyncio.Event(loop=loop)
self._total_request_size = 0
self._request_timeout_handler = None
self._response_timeout_handler = None
@@ -114,6 +98,7 @@ class HttpProtocol(asyncio.Protocol):
if 'requests_count' not in self.state:
self.state['requests_count'] = 0
self._debug = debug
+ self._not_paused.set()
@property
def keep_alive(self):
@@ -142,6 +127,12 @@ class HttpProtocol(asyncio.Protocol):
if self._keep_alive_timeout_handler:
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):
# See the docstring in the RequestTimeout exception, to see
# exactly what this timeout is checking for.
@@ -159,10 +150,7 @@ class HttpProtocol(asyncio.Protocol):
self._request_stream_task.cancel()
if self._request_handler_task:
self._request_handler_task.cancel()
- try:
- raise RequestTimeout('Request Timeout')
- except RequestTimeout as exception:
- self.write_error(exception)
+ self.write_error(RequestTimeout('Request Timeout'))
def response_timeout_callback(self):
# Check if elapsed time since response was initiated exceeds our
@@ -179,10 +167,7 @@ class HttpProtocol(asyncio.Protocol):
self._request_stream_task.cancel()
if self._request_handler_task:
self._request_handler_task.cancel()
- try:
- raise ServiceUnavailable('Response Timeout')
- except ServiceUnavailable as exception:
- self.write_error(exception)
+ self.write_error(ServiceUnavailable('Response Timeout'))
def keep_alive_timeout_callback(self):
# Check if elapsed time since last response exceeds our configured
@@ -208,8 +193,7 @@ class HttpProtocol(asyncio.Protocol):
# memory limits
self._total_request_size += len(data)
if self._total_request_size > self.request_max_size:
- exception = PayloadTooLarge('Payload Too Large')
- self.write_error(exception)
+ self.write_error(PayloadTooLarge('Payload Too Large'))
# Create parser if this is the first time we're receiving data
if self.parser is None:
@@ -227,8 +211,7 @@ class HttpProtocol(asyncio.Protocol):
message = 'Bad Request'
if self._debug:
message += '\n' + traceback.format_exc()
- exception = InvalidUsage(message)
- self.write_error(exception)
+ self.write_error(InvalidUsage(message))
def on_url(self, url):
if not self.url:
@@ -242,8 +225,7 @@ class HttpProtocol(asyncio.Protocol):
if value is not None:
if self._header_fragment == b'Content-Length' \
and int(value) > self.request_max_size:
- exception = PayloadTooLarge('Payload Too Large')
- self.write_error(exception)
+ self.write_error(PayloadTooLarge('Payload Too Large'))
try:
value = value.decode()
except UnicodeDecodeError:
@@ -256,7 +238,7 @@ class HttpProtocol(asyncio.Protocol):
def on_headers_complete(self):
self.request = self.request_class(
url_bytes=self.url,
- headers=CIDict(self.headers),
+ headers=CIMultiDict(self.headers),
version=self.parser.get_http_version(),
method=self.parser.get_method().decode(),
transport=self.transport
@@ -369,6 +351,12 @@ class HttpProtocol(asyncio.Protocol):
self._last_response_time = current_time
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):
"""
Streams a response to the client asynchronously. Attaches
@@ -378,9 +366,10 @@ class HttpProtocol(asyncio.Protocol):
if self._response_timeout_handler:
self._response_timeout_handler.cancel()
self._response_timeout_handler = None
+
try:
keep_alive = self.keep_alive
- response.transport = self.transport
+ response.protocol = self
await response.stream(
self.request.version, keep_alive, self.keep_alive_timeout)
self.log_response(response)
@@ -435,7 +424,7 @@ class HttpProtocol(asyncio.Protocol):
self.log_response(response)
try:
self.transport.close()
- except AttributeError as e:
+ except AttributeError:
logger.debug('Connection lost before server could close it.')
def bail_out(self, message, from_error=False):
@@ -445,8 +434,7 @@ class HttpProtocol(asyncio.Protocol):
self.transport.get_extra_info('peername'))
logger.debug('Exception:\n%s', traceback.format_exc())
else:
- exception = ServerError(message)
- self.write_error(exception)
+ self.write_error(ServerError(message))
logger.error(message)
def cleanup(self):
diff --git a/sanic/static.py b/sanic/static.py
index f2d02ab0..07831390 100644
--- a/sanic/static.py
+++ b/sanic/static.py
@@ -19,7 +19,7 @@ from sanic.response import file, file_stream, HTTPResponse
def register(app, uri, file_or_directory, pattern,
use_modified_since, use_content_range,
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
# make a good effort here. Modified-since is nice, but we could
# 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
threshold size to switch to file_stream()
: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,
# serve from the folder
@@ -95,10 +96,10 @@ def register(app, uri, file_or_directory, pattern,
del headers['Content-Length']
for key, value in _range.headers.items():
headers[key] = value
+ headers['Content-Type'] = content_type \
+ or guess_type(file_path)[0] or 'text/plain'
if request.method == 'HEAD':
- return HTTPResponse(
- headers=headers,
- content_type=guess_type(file_path)[0] or 'text/plain')
+ return HTTPResponse(headers=headers)
else:
if stream_large_files:
if isinstance(stream_large_files, int):
diff --git a/sanic/websocket.py b/sanic/websocket.py
index 99408af5..9ccf9fdf 100644
--- a/sanic/websocket.py
+++ b/sanic/websocket.py
@@ -57,17 +57,11 @@ class WebSocketProtocol(HttpProtocol):
async def websocket_handshake(self, request, subprotocols=None):
# let the websockets package do the handshake with the client
- headers = []
-
- def get_header(k):
- return request.headers.get(k, '')
-
- def set_header(k, v):
- headers.append((k, v))
+ headers = {}
try:
- key = handshake.check_request(get_header)
- handshake.build_response(set_header, key)
+ key = handshake.check_request(request.headers)
+ handshake.build_response(headers, key)
except InvalidHandshake:
raise InvalidUsage('Invalid websocket request')
@@ -79,12 +73,12 @@ class WebSocketProtocol(HttpProtocol):
for p in client_subprotocols:
if p in subprotocols:
subprotocol = p
- set_header('Sec-Websocket-Protocol', subprotocol)
+ headers['Sec-Websocket-Protocol'] = subprotocol
break
# write the 101 response back to the client
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 += b'\r\n'
request.transport.write(rv)
diff --git a/setup.py b/setup.py
index 73cb559f..027918bc 100644
--- a/setup.py
+++ b/setup.py
@@ -56,11 +56,12 @@ ujson = 'ujson>=1.35' + env_dependency
uvloop = 'uvloop>=0.5.3' + env_dependency
requirements = [
- 'httptools>=0.0.9',
+ 'httptools>=0.0.10',
uvloop,
ujson,
'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")):
print("Installing without uJSON")
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 00000000..5844e3a1
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,8 @@
+import pytest
+
+from sanic import Sanic
+
+
+@pytest.fixture
+def app(request):
+ return Sanic(request.node.name)
diff --git a/tests/static/test.html b/tests/static/test.html
new file mode 100644
index 00000000..4ba71873
--- /dev/null
+++ b/tests/static/test.html
@@ -0,0 +1,26 @@
+
+
+
+ ▄▄▄▄▄
+ ▀▀▀██████▄▄▄ _______________
+ ▄▄▄▄▄ █████████▄ / \
+ ▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
+ ▀▀█████▄▄ ▀██████▄██ | _________________/
+ ▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
+ ▀▀▀▄ ▀▀███ ▀ ▄▄
+ ▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
+ ██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
+▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
+▌ ▐▀████▐███▒▒▒▒▒▐██▌
+▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
+ ▀▀█████████▀
+ ▄▄██▀██████▀█
+ ▄██▀ ▀▀▀ █
+ ▄█ ▐▌
+ ▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
+▌ ▐ ▀▀▄▄▄▀
+ ▀▀▄▄▀
+
+
+
+
diff --git a/tests/test_bad_request.py b/tests/test_bad_request.py
index bf595085..eed4e83a 100644
--- a/tests/test_bad_request.py
+++ b/tests/test_bad_request.py
@@ -1,9 +1,7 @@
import asyncio
-from sanic import Sanic
-def test_bad_request_response():
- app = Sanic('test_bad_request_response')
+def test_bad_request_response(app):
lines = []
@app.listener('after_server_start')
async def _request(sanic, loop):
diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py
index 4c321646..4b821e91 100644
--- a/tests/test_blueprints.py
+++ b/tests/test_blueprints.py
@@ -1,10 +1,10 @@
import asyncio
import inspect
+import os
import pytest
-from sanic import Sanic
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.constants import HTTP_METHODS
@@ -13,9 +13,16 @@ from sanic.constants import HTTP_METHODS
# 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)
-def test_versioned_routes_get(method):
- app = Sanic('test_shorhand_routes_get')
+def test_versioned_routes_get(app, method):
bp = Blueprint('test_text')
method = method.lower()
@@ -37,8 +44,7 @@ def test_versioned_routes_get(method):
assert response.status == 200
-def test_bp():
- app = Sanic('test_text')
+def test_bp(app):
bp = Blueprint('test_text')
@bp.route('/')
@@ -51,8 +57,7 @@ def test_bp():
assert response.text == 'Hello'
-def test_bp_strict_slash():
- app = Sanic('test_route_strict_slash')
+def test_bp_strict_slash(app):
bp = Blueprint('test_text')
@bp.get('/get', strict_slashes=True)
@@ -78,8 +83,7 @@ def test_bp_strict_slash():
request, response = app.test_client.post('/post')
assert response.status == 404
-def test_bp_strict_slash_default_value():
- app = Sanic('test_route_strict_slash')
+def test_bp_strict_slash_default_value(app):
bp = Blueprint('test_text', strict_slashes=True)
@bp.get('/get')
@@ -98,8 +102,7 @@ def test_bp_strict_slash_default_value():
request, response = app.test_client.post('/post')
assert response.status == 404
-def test_bp_strict_slash_without_passing_default_value():
- app = Sanic('test_route_strict_slash')
+def test_bp_strict_slash_without_passing_default_value(app):
bp = Blueprint('test_text')
@bp.get('/get')
@@ -118,8 +121,7 @@ def test_bp_strict_slash_without_passing_default_value():
request, response = app.test_client.post('/post')
assert response.text == 'OK'
-def test_bp_strict_slash_default_value_can_be_overwritten():
- app = Sanic('test_route_strict_slash')
+def test_bp_strict_slash_default_value_can_be_overwritten(app):
bp = Blueprint('test_text', strict_slashes=True)
@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')
assert response.text == 'OK'
-def test_bp_with_url_prefix():
- app = Sanic('test_text')
+def test_bp_with_url_prefix(app):
bp = Blueprint('test_text', url_prefix='/test1')
@bp.route('/')
@@ -152,8 +153,7 @@ def test_bp_with_url_prefix():
assert response.text == 'Hello'
-def test_several_bp_with_url_prefix():
- app = Sanic('test_text')
+def test_several_bp_with_url_prefix(app):
bp = Blueprint('test_text', url_prefix='/test1')
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/')
assert response.text == 'Hello2'
-def test_bp_with_host():
- app = Sanic('test_bp_host')
+def test_bp_with_host(app):
bp = Blueprint('test_bp_host', url_prefix='/test1', host="example.com")
@bp.route('/')
@@ -200,8 +199,7 @@ def test_bp_with_host():
assert response.text == 'Hello subdomain!'
-def test_several_bp_with_host():
- app = Sanic('test_text')
+def test_several_bp_with_host(app):
bp = Blueprint('test_text',
url_prefix='/test',
host="example.com")
@@ -244,8 +242,7 @@ def test_several_bp_with_host():
headers=headers)
assert response.text == 'Hello3'
-def test_bp_middleware():
- app = Sanic('test_middleware')
+def test_bp_middleware(app):
blueprint = Blueprint('test_middleware')
@blueprint.middleware('response')
@@ -263,8 +260,7 @@ def test_bp_middleware():
assert response.status == 200
assert response.text == 'OK'
-def test_bp_exception_handler():
- app = Sanic('test_middleware')
+def test_bp_exception_handler(app):
blueprint = Blueprint('test_middleware')
@blueprint.route('/1')
@@ -296,8 +292,7 @@ def test_bp_exception_handler():
request, response = app.test_client.get('/3')
assert response.status == 200
-def test_bp_listeners():
- app = Sanic('test_middleware')
+def test_bp_listeners(app):
blueprint = Blueprint('test_middleware')
order = []
@@ -332,12 +327,11 @@ def test_bp_listeners():
assert order == [1,2,3,4,5,6]
-def test_bp_static():
+def test_bp_static(app):
current_file = inspect.getfile(inspect.currentframe())
with open(current_file, 'rb') as file:
current_file_contents = file.read()
- app = Sanic('test_static')
blueprint = Blueprint('test_static')
blueprint.static('/testing.file', current_file)
@@ -348,8 +342,28 @@ def test_bp_static():
assert response.status == 200
assert response.body == current_file_contents
-def test_bp_shorthand():
- app = Sanic('test_shorhand_routes')
+@pytest.mark.parametrize('file_name', ['test.html'])
+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')
ev = asyncio.Event()
@@ -447,43 +461,41 @@ def test_bp_shorthand():
assert response.status == 101
assert ev.is_set()
-def test_bp_group():
- app = Sanic('test_nested_bp_groups')
-
+def test_bp_group(app):
deep_0 = Blueprint('deep_0', url_prefix='/deep')
deep_1 = Blueprint('deep_1', url_prefix = '/deep1')
@deep_0.route('/')
def handler(request):
return text('D0_OK')
-
+
@deep_1.route('/bottom')
def handler(request):
return text('D1B_OK')
mid_0 = Blueprint.group(deep_0, deep_1, url_prefix='/mid')
mid_1 = Blueprint('mid_tier', url_prefix='/mid1')
-
+
@mid_1.route('/')
def handler(request):
return text('M1_OK')
top = Blueprint.group(mid_0, mid_1)
-
+
app.blueprint(top)
-
+
@app.route('/')
def handler(request):
return text('TOP_OK')
-
+
request, response = app.test_client.get('/')
assert response.text == 'TOP_OK'
-
+
request, response = app.test_client.get('/mid1')
assert response.text == 'M1_OK'
-
+
request, response = app.test_client.get('/mid/deep')
assert response.text == 'D0_OK'
-
+
request, response = app.test_client.get('/mid/deep1/bottom')
assert response.text == 'D1B_OK'
diff --git a/tests/test_config.py b/tests/test_config.py
index e393d02b..14b28d46 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -5,8 +5,7 @@ from tempfile import NamedTemporaryFile
from sanic import Sanic
-def test_load_from_object():
- app = Sanic('test_load_from_object')
+def test_load_from_object(app):
class Config:
not_for_config = 'should not 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 'not_for_config' not in app.config
+
def test_auto_load_env():
environ["SANIC_TEST_ANSWER"] = "42"
app = Sanic()
assert app.config.TEST_ANSWER == 42
del environ["SANIC_TEST_ANSWER"]
+
def test_dont_load_env():
environ["SANIC_TEST_ANSWER"] = "42"
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"]
+
def test_load_env_prefix():
environ["MYAPP_TEST_ANSWER"] = "42"
app = Sanic(load_env='MYAPP_')
assert app.config.TEST_ANSWER == 42
del environ["MYAPP_TEST_ANSWER"]
-def test_load_from_file():
- app = Sanic('test_load_from_file')
+
+def test_load_from_file(app):
config = b"""
VALUE = 'some value'
condition = 1 == 1
@@ -53,14 +55,12 @@ if condition:
assert 'condition' not in app.config
-def test_load_from_missing_file():
- app = Sanic('test_load_from_missing_file')
+def test_load_from_missing_file(app):
with pytest.raises(IOError):
app.config.from_pyfile('non-existent file')
-def test_load_from_envvar():
- app = Sanic('test_load_from_envvar')
+def test_load_from_envvar(app):
config = b"VALUE = 'some value'"
with NamedTemporaryFile() as config_file:
config_file.write(config)
@@ -71,15 +71,17 @@ def test_load_from_envvar():
assert app.config.VALUE == 'some value'
-def test_load_from_missing_envvar():
- app = Sanic('test_load_from_missing_envvar')
- with pytest.raises(RuntimeError):
+def test_load_from_missing_envvar(app):
+ with pytest.raises(RuntimeError) as e:
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():
- app = Sanic('test_overwrite_exisiting_config')
+def test_overwrite_exisiting_config(app):
app.config.DEFAULT = 1
+
class Config:
DEFAULT = 2
@@ -87,7 +89,7 @@ def test_overwrite_exisiting_config():
assert app.config.DEFAULT == 2
-def test_missing_config():
- app = Sanic('test_missing_config')
- with pytest.raises(AttributeError):
+def test_missing_config(app):
+ with pytest.raises(AttributeError) as e:
app.config.NON_EXISTENT
+ assert str(e.value) == ("Config has no 'NON_EXISTENT'")
diff --git a/tests/test_cookies.py b/tests/test_cookies.py
index 84b493cb..87d7dcad 100644
--- a/tests/test_cookies.py
+++ b/tests/test_cookies.py
@@ -9,8 +9,7 @@ import pytest
# GET
# ------------------------------------------------------------ #
-def test_cookies():
- app = Sanic('test_text')
+def test_cookies(app):
@app.route('/')
def handler(request):
@@ -25,12 +24,12 @@ def test_cookies():
assert response.text == 'Cookies are: working!'
assert response_cookies['right_back'].value == 'at you'
+
@pytest.mark.parametrize("httponly,expected", [
(False, False),
(True, True),
])
-def test_false_cookies_encoded(httponly, expected):
- app = Sanic('test_text')
+def test_false_cookies_encoded(app, httponly, expected):
@app.route('/')
def handler(request):
@@ -48,8 +47,7 @@ def test_false_cookies_encoded(httponly, expected):
(False, False),
(True, True),
])
-def test_false_cookies(httponly, expected):
- app = Sanic('test_text')
+def test_false_cookies(app, httponly, expected):
@app.route('/')
def handler(request):
@@ -64,8 +62,7 @@ def test_false_cookies(httponly, expected):
assert ('HttpOnly' in response_cookies['right_back'].output()) == expected
-def test_http2_cookies():
- app = Sanic('test_http2_cookies')
+def test_http2_cookies(app):
@app.route('/')
async def handler(request):
@@ -77,8 +74,7 @@ def test_http2_cookies():
assert response.text == 'Cookies are: working!'
-def test_cookie_options():
- app = Sanic('test_text')
+def test_cookie_options(app):
@app.route('/')
def handler(request):
@@ -95,8 +91,7 @@ def test_cookie_options():
assert response_cookies['test'].value == 'at you'
assert response_cookies['test']['httponly'] == True
-def test_cookie_deletion():
- app = Sanic('test_text')
+def test_cookie_deletion(app):
@app.route('/')
def handler(request):
diff --git a/tests/test_create_task.py b/tests/test_create_task.py
index 1517ca8c..3a94884d 100644
--- a/tests/test_create_task.py
+++ b/tests/test_create_task.py
@@ -1,18 +1,16 @@
-from sanic import Sanic
from sanic.response import text
from threading import Event
import asyncio
from queue import Queue
-def test_create_task():
+def test_create_task(app):
e = Event()
async def coro():
await asyncio.sleep(0.05)
e.set()
- app = Sanic('test_create_task')
app.add_task(coro)
@app.route('/early')
@@ -30,8 +28,7 @@ def test_create_task():
request, response = app.test_client.get('/late')
assert response.body == b'True'
-def test_create_task_with_app_arg():
- app = Sanic('test_add_task')
+def test_create_task_with_app_arg(app):
q = Queue()
@app.route('/')
@@ -44,4 +41,4 @@ def test_create_task_with_app_arg():
app.add_task(coro)
request, response = app.test_client.get('/')
- assert q.get() == 'test_add_task'
+ assert q.get() == 'test_create_task_with_app_arg'
diff --git a/tests/test_custom_protocol.py b/tests/test_custom_protocol.py
index 74564012..236eb831 100644
--- a/tests/test_custom_protocol.py
+++ b/tests/test_custom_protocol.py
@@ -1,9 +1,6 @@
-from sanic import Sanic
from sanic.server import HttpProtocol
from sanic.response import text
-app = Sanic('test_custom_porotocol')
-
class CustomHttpProtocol(HttpProtocol):
@@ -16,12 +13,12 @@ class CustomHttpProtocol(HttpProtocol):
self.transport.close()
-@app.route('/1')
-async def handler_1(request):
- return 'OK'
+def test_use_custom_protocol(app):
+ @app.route('/1')
+ async def handler_1(request):
+ return 'OK'
-def test_use_custom_protocol():
server_kwargs = {
'protocol': CustomHttpProtocol
}
diff --git a/tests/test_dynamic_routes.py b/tests/test_dynamic_routes.py
index 950584a8..a37861e3 100644
--- a/tests/test_dynamic_routes.py
+++ b/tests/test_dynamic_routes.py
@@ -10,8 +10,7 @@ import pytest
("put", "text", "OK2 test"),
("delete", "status", 405),
])
-def test_overload_dynamic_routes(method, attr, expected):
- app = Sanic('test_dynamic_route')
+def test_overload_dynamic_routes(app, method, attr, expected):
@app.route('/overload/', methods=['GET'])
async def handler1(request, param):
@@ -25,8 +24,7 @@ def test_overload_dynamic_routes(method, attr, expected):
assert getattr(response, attr) == expected
-def test_overload_dynamic_routes_exist():
- app = Sanic('test_dynamic_route')
+def test_overload_dynamic_routes_exist(app):
@app.route('/overload/', methods=['GET'])
async def handler1(request, param):
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
index b4e2c6ea..cf41982d 100644
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -81,8 +81,7 @@ def exception_app():
return app
-def test_catch_exception_list():
- app = Sanic('exception_list')
+def test_catch_exception_list(app):
@app.exception([SanicExceptionTestException, NotFound])
def exception_list(request, exception):
diff --git a/tests/test_keep_alive_timeout.py b/tests/test_keep_alive_timeout.py
index 2a9e93a2..53a2872e 100644
--- a/tests/test_keep_alive_timeout.py
+++ b/tests/test_keep_alive_timeout.py
@@ -9,14 +9,39 @@ import aiohttp
from aiohttp import TCPConnector
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):
def __init__(self, *args, **kwargs):
super(ReuseableTCPConnector, self).__init__(*args, **kwargs)
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):
new_conn = await super(ReuseableTCPConnector, self)\
.connect(req, traces=traces)
@@ -28,7 +53,6 @@ class ReuseableTCPConnector(TCPConnector):
self.old_proto = new_conn._protocol
return new_conn
else:
-
async def connect(self, req):
new_conn = await super(ReuseableTCPConnector, self)\
.connect(req)
diff --git a/tests/test_logging.py b/tests/test_logging.py
index 1a040a5c..3af3f122 100644
--- a/tests/test_logging.py
+++ b/tests/test_logging.py
@@ -23,7 +23,7 @@ def reset_logging():
reload(logging)
-def test_log():
+def test_log(app):
log_stream = StringIO()
for handler in logging.root.handlers[:]:
logging.root.removeHandler(handler)
@@ -33,7 +33,6 @@ def test_log():
stream=log_stream
)
log = logging.getLogger()
- app = Sanic('test_logging')
rand_string = str(uuid.uuid4())
@app.route('/')
@@ -80,9 +79,8 @@ def test_logging_pass_customer_logconfig():
@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 """
- app = Sanic('connection_lost')
stream = StringIO()
root = logging.getLogger('root')
root.addHandler(logging.StreamHandler(stream))
diff --git a/tests/test_middleware.py b/tests/test_middleware.py
index 4d4d6901..d2098cd1 100644
--- a/tests/test_middleware.py
+++ b/tests/test_middleware.py
@@ -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.response import json, text, HTTPResponse
+from sanic.response import text, HTTPResponse
from sanic.exceptions import NotFound
@@ -9,9 +7,7 @@ from sanic.exceptions import NotFound
# GET
# ------------------------------------------------------------ #
-def test_middleware_request():
- app = Sanic('test_middleware_request')
-
+def test_middleware_request(app):
results = []
@app.middleware
@@ -28,9 +24,7 @@ def test_middleware_request():
assert type(results[0]) is Request
-def test_middleware_response():
- app = Sanic('test_middleware_response')
-
+def test_middleware_response(app):
results = []
@app.middleware('request')
@@ -54,8 +48,7 @@ def test_middleware_response():
assert isinstance(results[2], HTTPResponse)
-def test_middleware_response_exception():
- app = Sanic('test_middleware_response_exception')
+def test_middleware_response_exception(app):
result = {'status_code': None}
@app.middleware('response')
@@ -75,8 +68,7 @@ def test_middleware_response_exception():
assert response.text == 'OK'
assert result['status_code'] == 404
-def test_middleware_override_request():
- app = Sanic('test_middleware_override_request')
+def test_middleware_override_request(app):
@app.middleware
async def halt_request(request):
@@ -92,8 +84,7 @@ def test_middleware_override_request():
assert response.text == 'OK'
-def test_middleware_override_response():
- app = Sanic('test_middleware_override_response')
+def test_middleware_override_response(app):
@app.middleware('response')
async def process_response(request, response):
@@ -109,10 +100,7 @@ def test_middleware_override_response():
assert response.text == 'OK'
-
-def test_middleware_order():
- app = Sanic('test_middleware_order')
-
+def test_middleware_order(app):
order = []
@app.middleware('request')
diff --git a/tests/test_multiprocessing.py b/tests/test_multiprocessing.py
index 7d94a972..c78e19fd 100644
--- a/tests/test_multiprocessing.py
+++ b/tests/test_multiprocessing.py
@@ -2,15 +2,13 @@ import multiprocessing
import random
import signal
-from sanic import Sanic
from sanic.testing import HOST, PORT
-def test_multiprocessing():
+def test_multiprocessing(app):
"""Tests that the number of children we produce is correct"""
# Selects a number at random so we can spot check
num_workers = random.choice(range(2, multiprocessing.cpu_count() * 2 + 1))
- app = Sanic('test_multiprocessing')
process_list = set()
def stop_on_alarm(*args):
diff --git a/tests/test_named_routes.py b/tests/test_named_routes.py
index ca377e8d..65d5955a 100644
--- a/tests/test_named_routes.py
+++ b/tests/test_named_routes.py
@@ -4,7 +4,6 @@
import asyncio
import pytest
-from sanic import Sanic
from sanic.blueprints import Blueprint
from sanic.response import text
from sanic.exceptions import URLBuildError
@@ -16,8 +15,7 @@ from sanic.constants import HTTP_METHODS
# ------------------------------------------------------------ #
@pytest.mark.parametrize('method', HTTP_METHODS)
-def test_versioned_named_routes_get(method):
- app = Sanic('test_shorhand_routes_get')
+def test_versioned_named_routes_get(app, method):
bp = Blueprint('test_bp', url_prefix='/bp')
method = method.lower()
@@ -57,8 +55,7 @@ def test_versioned_named_routes_get(method):
app.url_for('handler')
-def test_shorthand_default_routes_get():
- app = Sanic('test_shorhand_routes_get')
+def test_shorthand_default_routes_get(app):
@app.get('/get')
def handler(request):
@@ -68,8 +65,7 @@ def test_shorthand_default_routes_get():
assert app.url_for('handler') == '/get'
-def test_shorthand_named_routes_get():
- app = Sanic('test_shorhand_routes_get')
+def test_shorthand_named_routes_get(app):
bp = Blueprint('test_bp', url_prefix='/bp')
@app.get('/get', name='route_get')
@@ -93,8 +89,7 @@ def test_shorthand_named_routes_get():
app.url_for('test_bp.handler2')
-def test_shorthand_named_routes_post():
- app = Sanic('test_shorhand_routes_post')
+def test_shorthand_named_routes_post(app):
@app.post('/post', name='route_name')
def handler(request):
@@ -106,8 +101,7 @@ def test_shorthand_named_routes_post():
app.url_for('handler')
-def test_shorthand_named_routes_put():
- app = Sanic('test_shorhand_routes_put')
+def test_shorthand_named_routes_put(app):
@app.put('/put', name='route_put')
def handler(request):
@@ -121,8 +115,7 @@ def test_shorthand_named_routes_put():
app.url_for('handler')
-def test_shorthand_named_routes_delete():
- app = Sanic('test_shorhand_routes_delete')
+def test_shorthand_named_routes_delete(app):
@app.delete('/delete', name='route_delete')
def handler(request):
@@ -136,8 +129,7 @@ def test_shorthand_named_routes_delete():
app.url_for('handler')
-def test_shorthand_named_routes_patch():
- app = Sanic('test_shorhand_routes_patch')
+def test_shorthand_named_routes_patch(app):
@app.patch('/patch', name='route_patch')
def handler(request):
@@ -151,8 +143,7 @@ def test_shorthand_named_routes_patch():
app.url_for('handler')
-def test_shorthand_named_routes_head():
- app = Sanic('test_shorhand_routes_head')
+def test_shorthand_named_routes_head(app):
@app.head('/head', name='route_head')
def handler(request):
@@ -166,8 +157,7 @@ def test_shorthand_named_routes_head():
app.url_for('handler')
-def test_shorthand_named_routes_options():
- app = Sanic('test_shorhand_routes_options')
+def test_shorthand_named_routes_options(app):
@app.options('/options', name='route_options')
def handler(request):
@@ -181,8 +171,7 @@ def test_shorthand_named_routes_options():
app.url_for('handler')
-def test_named_static_routes():
- app = Sanic('test_dynamic_route')
+def test_named_static_routes(app):
@app.route('/test', name='route_test')
async def handler1(request):
@@ -205,9 +194,7 @@ def test_named_static_routes():
app.url_for('handler2')
-def test_named_dynamic_route():
- app = Sanic('test_dynamic_route')
-
+def test_named_dynamic_route(app):
results = []
@app.route('/folder/', name='route_dynamic')
@@ -221,8 +208,7 @@ def test_named_dynamic_route():
app.url_for('handler')
-def test_dynamic_named_route_regex():
- app = Sanic('test_dynamic_route_regex')
+def test_dynamic_named_route_regex(app):
@app.route('/folder/', name='route_re')
async def handler(request, folder_id):
@@ -235,8 +221,7 @@ def test_dynamic_named_route_regex():
app.url_for('handler')
-def test_dynamic_named_route_path():
- app = Sanic('test_dynamic_route_path')
+def test_dynamic_named_route_path(app):
@app.route('//info', name='route_dynamic_path')
async def handler(request, path):
@@ -249,8 +234,7 @@ def test_dynamic_named_route_path():
app.url_for('handler')
-def test_dynamic_named_route_unhashable():
- app = Sanic('test_dynamic_route_unhashable')
+def test_dynamic_named_route_unhashable(app):
@app.route('/folder//end/',
name='route_unhashable')
@@ -265,8 +249,7 @@ def test_dynamic_named_route_unhashable():
app.url_for('handler')
-def test_websocket_named_route():
- app = Sanic('test_websocket_route')
+def test_websocket_named_route(app):
ev = asyncio.Event()
@app.websocket('/ws', name='route_ws')
@@ -280,8 +263,7 @@ def test_websocket_named_route():
app.url_for('handler')
-def test_websocket_named_route_with_subprotocols():
- app = Sanic('test_websocket_route')
+def test_websocket_named_route_with_subprotocols(app):
results = []
@app.websocket('/ws', subprotocols=['foo', 'bar'], name='route_ws')
@@ -294,8 +276,7 @@ def test_websocket_named_route_with_subprotocols():
app.url_for('handler')
-def test_static_add_named_route():
- app = Sanic('test_static_add_route')
+def test_static_add_named_route(app):
async def handler1(request):
return text('OK1')
@@ -319,9 +300,7 @@ def test_static_add_named_route():
app.url_for('handler2')
-def test_dynamic_add_named_route():
- app = Sanic('test_dynamic_add_route')
-
+def test_dynamic_add_named_route(app):
results = []
async def handler(request, name):
@@ -335,8 +314,7 @@ def test_dynamic_add_named_route():
app.url_for('handler')
-def test_dynamic_add_named_route_unhashable():
- app = Sanic('test_dynamic_add_route_unhashable')
+def test_dynamic_add_named_route_unhashable(app):
async def handler(request, unhashable):
return text('OK')
@@ -351,8 +329,7 @@ def test_dynamic_add_named_route_unhashable():
app.url_for('handler')
-def test_overload_routes():
- app = Sanic('test_dynamic_route')
+def test_overload_routes(app):
@app.route('/overload', methods=['GET'], name='route_first')
async def handler1(request):
diff --git a/tests/test_payload_too_large.py b/tests/test_payload_too_large.py
index ecac605c..49ad5ab7 100644
--- a/tests/test_payload_too_large.py
+++ b/tests/test_payload_too_large.py
@@ -1,49 +1,45 @@
-from sanic import Sanic
from sanic.exceptions import PayloadTooLarge
from sanic.response import text
-def test_payload_too_large_from_error_handler():
- data_received_app = Sanic('data_received')
- data_received_app.config.REQUEST_MAX_SIZE = 1
+def test_payload_too_large_from_error_handler(app):
+ app.config.REQUEST_MAX_SIZE = 1
- @data_received_app.route('/1')
+ @app.route('/1')
async def handler1(request):
return text('OK')
- @data_received_app.exception(PayloadTooLarge)
+ @app.exception(PayloadTooLarge)
def handler_exception(request, exception):
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.text == 'Payload Too Large from error_handler.'
-def test_payload_too_large_at_data_received_default():
- data_received_default_app = Sanic('data_received_default')
- data_received_default_app.config.REQUEST_MAX_SIZE = 1
+def test_payload_too_large_at_data_received_default(app):
+ app.config.REQUEST_MAX_SIZE = 1
- @data_received_default_app.route('/1')
+ @app.route('/1')
async def handler2(request):
return text('OK')
- response = data_received_default_app.test_client.get(
+ response = app.test_client.get(
'/1', gather_request=False)
assert response.status == 413
assert response.text == 'Error: Payload Too Large'
-def test_payload_too_large_at_on_header_default():
- on_header_default_app = Sanic('on_header')
- on_header_default_app.config.REQUEST_MAX_SIZE = 500
+def test_payload_too_large_at_on_header_default(app):
+ app.config.REQUEST_MAX_SIZE = 500
- @on_header_default_app.post('/1')
+ @app.post('/1')
async def handler3(request):
return text('OK')
data = 'a' * 1000
- response = on_header_default_app.test_client.post(
+ response = app.test_client.post(
'/1', gather_request=False, data=data)
assert response.status == 413
assert response.text == 'Error: Payload Too Large'
diff --git a/tests/test_redirect.py b/tests/test_redirect.py
index f5b734e3..f5efac60 100644
--- a/tests/test_redirect.py
+++ b/tests/test_redirect.py
@@ -1,12 +1,10 @@
import pytest
-from sanic import Sanic
from sanic.response import text, redirect
@pytest.fixture
-def redirect_app():
- app = Sanic('test_redirection')
+def redirect_app(app):
@app.route('/redirect_init')
async def redirect_init(request):
@@ -32,6 +30,10 @@ def redirect_app():
def handler(request):
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
@@ -92,3 +94,16 @@ def test_chained_redirect(redirect_app):
assert response.url.endswith('/3')
except AttributeError:
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')
diff --git a/tests/test_request_data.py b/tests/test_request_data.py
index f795ff1f..69935fc0 100644
--- a/tests/test_request_data.py
+++ b/tests/test_request_data.py
@@ -1,6 +1,5 @@
import random
-from sanic import Sanic
from sanic.response import json
try:
@@ -9,8 +8,7 @@ except ImportError:
from json import loads
-def test_storage():
- app = Sanic('test_text')
+def test_storage(app):
@app.middleware('request')
def store(request):
@@ -29,8 +27,7 @@ def test_storage():
assert response_json.get('sidekick') is None
-def test_app_injection():
- app = Sanic('test_app_injection')
+def test_app_injection(app):
expected = random.choice(range(0, 100))
@app.listener('after_server_start')
diff --git a/tests/test_request_stream.py b/tests/test_request_stream.py
index 4ca4e44e..97cd5a4a 100644
--- a/tests/test_request_stream.py
+++ b/tests/test_request_stream.py
@@ -1,5 +1,4 @@
import asyncio
-from sanic import Sanic
from sanic.blueprints import Blueprint
from sanic.views import CompositionView
from sanic.views import HTTPMethodView
@@ -9,11 +8,9 @@ from sanic.response import stream, text
data = "abc" * 100000
-def test_request_stream_method_view():
+def test_request_stream_method_view(app):
'''for self.is_request_stream = True'''
- app = Sanic('test_request_stream_method_view')
-
class SimpleView(HTTPMethodView):
def get(self, request):
@@ -44,11 +41,9 @@ def test_request_stream_method_view():
assert response.text == data
-def test_request_stream_app():
+def test_request_stream_app(app):
'''for self.is_request_stream = True and decorators'''
- app = Sanic('test_request_stream_app')
-
@app.get('/get')
async def get(request):
assert request.stream is None
@@ -83,7 +78,7 @@ def test_request_stream_app():
body = await request.stream.get()
if body is None:
break
- response.write(body.decode('utf-8'))
+ await response.write(body.decode('utf-8'))
return stream(streaming)
@app.put('/_put')
@@ -100,7 +95,7 @@ def test_request_stream_app():
body = await request.stream.get()
if body is None:
break
- response.write(body.decode('utf-8'))
+ await response.write(body.decode('utf-8'))
return stream(streaming)
@app.patch('/_patch')
@@ -117,7 +112,7 @@ def test_request_stream_app():
body = await request.stream.get()
if body is None:
break
- response.write(body.decode('utf-8'))
+ await response.write(body.decode('utf-8'))
return stream(streaming)
assert app.is_request_stream is True
@@ -163,11 +158,9 @@ def test_request_stream_app():
assert response.text == data
-def test_request_stream_handle_exception():
+def test_request_stream_handle_exception(app):
'''for handling exceptions properly'''
- app = Sanic('test_request_stream_exception')
-
@app.post('/post/', stream=True)
async def post(request, id):
assert isinstance(request.stream, asyncio.Queue)
@@ -177,7 +170,7 @@ def test_request_stream_handle_exception():
body = await request.stream.get()
if body is None:
break
- response.write(body.decode('utf-8'))
+ await response.write(body.decode('utf-8'))
return stream(streaming)
# 404
@@ -191,10 +184,8 @@ def test_request_stream_handle_exception():
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'''
-
- app = Sanic('test_request_stream_blueprint')
bp = Blueprint('test_blueprint_request_stream_blueprint')
@app.get('/get')
@@ -231,7 +222,7 @@ def test_request_stream_blueprint():
body = await request.stream.get()
if body is None:
break
- response.write(body.decode('utf-8'))
+ await response.write(body.decode('utf-8'))
return stream(streaming)
@bp.put('/_put')
@@ -248,7 +239,7 @@ def test_request_stream_blueprint():
body = await request.stream.get()
if body is None:
break
- response.write(body.decode('utf-8'))
+ await response.write(body.decode('utf-8'))
return stream(streaming)
@bp.patch('/_patch')
@@ -265,7 +256,7 @@ def test_request_stream_blueprint():
body = await request.stream.get()
if body is None:
break
- response.write(body.decode('utf-8'))
+ await response.write(body.decode('utf-8'))
return stream(streaming)
app.blueprint(bp)
@@ -313,11 +304,9 @@ def test_request_stream_blueprint():
assert response.text == data
-def test_request_stream_composition_view():
+def test_request_stream_composition_view(app):
'''for self.is_request_stream = True'''
- app = Sanic('test_request_stream__composition_view')
-
def get_handler(request):
assert request.stream is None
return text('OK')
@@ -348,11 +337,9 @@ def test_request_stream_composition_view():
assert response.text == data
-def test_request_stream():
+def test_request_stream(app):
'''test for complex application'''
-
bp = Blueprint('test_blueprint_request_stream')
- app = Sanic('test_request_stream')
class SimpleView(HTTPMethodView):
@@ -380,7 +367,7 @@ def test_request_stream():
body = await request.stream.get()
if body is None:
break
- response.write(body.decode('utf-8'))
+ await response.write(body.decode('utf-8'))
return stream(streaming)
@app.get('/get')
diff --git a/tests/test_request_timeout.py b/tests/test_request_timeout.py
index b3eb78aa..672d0588 100644
--- a/tests/test_request_timeout.py
+++ b/tests/test_request_timeout.py
@@ -5,9 +5,24 @@ import asyncio
from sanic.response import text
from sanic.config import Config
import aiohttp
-from aiohttp import TCPConnector
+from aiohttp import TCPConnector, ClientResponse
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):
@@ -38,8 +53,11 @@ class DelayableTCPConnector(TCPConnector):
self.orig_start = getattr(resp, 'start')
try:
- ret = await self.orig_start(connection,
- read_until_eof)
+ if aiohttp_version >= version.parse("3.3.0"):
+ ret = await self.orig_start(connection)
+ else:
+ ret = await self.orig_start(connection,
+ read_until_eof)
except Exception as e:
raise e
return ret
@@ -57,15 +75,31 @@ class DelayableTCPConnector(TCPConnector):
await asyncio.sleep(self.delay)
t = req.loop.time()
print("sending at {}".format(t), flush=True)
- conn = next(iter(args)) # first arg is connection
- if aiohttp.__version__ >= "3.1.0":
+ conn = next(iter(args)) # first arg is connection
+
+ if aiohttp_version >= version.parse("3.1.0"):
try:
delayed_resp = await self.orig_send(*args, **kwargs)
except Exception as e:
- 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)
+ if aiohttp_version >= version.parse("3.3.0"):
+ return aiohttp.ClientResponse(req.method, req.url,
+ writer=None,
+ continue100=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:
try:
delayed_resp = self.orig_send(*args, **kwargs)
@@ -73,7 +107,7 @@ class DelayableTCPConnector(TCPConnector):
return aiohttp.ClientResponse(req.method, req.url)
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
async def send(self, *args, **kwargs):
gen = self.delayed_send(*args, **kwargs)
@@ -96,12 +130,25 @@ class DelayableTCPConnector(TCPConnector):
self._post_connect_delay = _post_connect_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):
d_req = DelayableTCPConnector.\
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:
await asyncio.sleep(self._post_connect_delay,
loop=self._loop)
diff --git a/tests/test_requests.py b/tests/test_requests.py
index 2a91fb9b..9617216e 100644
--- a/tests/test_requests.py
+++ b/tests/test_requests.py
@@ -5,7 +5,6 @@ import ssl
import pytest
-from sanic import Sanic
from sanic.exceptions import ServerError
from sanic.response import json, text
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE
@@ -16,8 +15,7 @@ from sanic.testing import HOST, PORT
# GET
# ------------------------------------------------------------ #
-def test_sync():
- app = Sanic('test_text')
+def test_sync(app):
@app.route('/')
def handler(request):
@@ -27,8 +25,8 @@ def test_sync():
assert response.text == 'Hello'
-def test_remote_address():
- app = Sanic('test_text')
+
+def test_remote_address(app):
@app.route('/')
def handler(request):
@@ -38,8 +36,8 @@ def test_remote_address():
assert response.text == '127.0.0.1'
-def test_text():
- app = Sanic('test_text')
+
+def test_text(app):
@app.route('/')
async def handler(request):
@@ -50,8 +48,7 @@ def test_text():
assert response.text == 'Hello'
-def test_headers():
- app = Sanic('test_text')
+def test_headers(app):
@app.route('/')
async def handler(request):
@@ -63,8 +60,7 @@ def test_headers():
assert response.headers.get('spam') == 'great'
-def test_non_str_headers():
- app = Sanic('test_text')
+def test_non_str_headers(app):
@app.route('/')
async def handler(request):
@@ -75,8 +71,8 @@ def test_non_str_headers():
assert response.headers.get('answer') == '42'
-def test_invalid_response():
- app = Sanic('test_invalid_response')
+
+def test_invalid_response(app):
@app.exception(ServerError)
def handler_exception(request, exception):
@@ -91,8 +87,7 @@ def test_invalid_response():
assert response.text == "Internal Server Error."
-def test_json():
- app = Sanic('test_json')
+def test_json(app):
@app.route('/')
async def handler(request):
@@ -105,8 +100,7 @@ def test_json():
assert results.get('test') == True
-def test_empty_json():
- app = Sanic('test_json')
+def test_empty_json(app):
@app.route('/')
async def handler(request):
@@ -118,8 +112,7 @@ def test_empty_json():
assert response.text == 'null'
-def test_invalid_json():
- app = Sanic('test_json')
+def test_invalid_json(app):
@app.route('/')
async def handler(request):
@@ -131,8 +124,7 @@ def test_invalid_json():
assert response.status == 400
-def test_query_string():
- app = Sanic('test_query_string')
+def test_query_string(app):
@app.route('/')
async def handler(request):
@@ -145,8 +137,7 @@ def test_query_string():
assert request.args.get('test2') == 'false'
-def test_uri_template():
- app = Sanic('test_uri_template')
+def test_uri_template(app):
@app.route('/foo//bar/')
async def handler(request):
@@ -156,8 +147,7 @@ def test_uri_template():
assert request.uri_template == '/foo//bar/'
-def test_token():
- app = Sanic('test_post_token')
+def test_token(app):
@app.route('/')
async def handler(request):
@@ -204,8 +194,7 @@ def test_token():
assert request.token is None
-def test_content_type():
- app = Sanic('test_content_type')
+def test_content_type(app):
@app.route('/')
async def handler(request):
@@ -223,8 +212,7 @@ def test_content_type():
assert response.text == 'application/json'
-def test_remote_addr():
- app = Sanic('test_content_type')
+def test_remote_addr(app):
@app.route('/')
async def handler(request):
@@ -249,8 +237,7 @@ def test_remote_addr():
assert response.text == '127.0.0.1'
-def test_match_info():
- app = Sanic('test_match_info')
+def test_match_info(app):
@app.route('/api/v1/user//')
async def handler(request, user_id):
@@ -266,8 +253,7 @@ def test_match_info():
# POST
# ------------------------------------------------------------ #
-def test_post_json():
- app = Sanic('test_post_json')
+def test_post_json(app):
@app.route('/', methods=['POST'])
async def handler(request):
@@ -283,8 +269,7 @@ def test_post_json():
assert response.text == 'OK'
-def test_post_form_urlencoded():
- app = Sanic('test_post_form_urlencoded')
+def test_post_form_urlencoded(app):
@app.route('/', methods=['POST'])
async def handler(request):
@@ -311,8 +296,7 @@ def test_post_form_urlencoded():
'OK\r\n' \
'------sanic--\r\n',
])
-def test_post_form_multipart_form_data(payload):
- app = Sanic('test_post_form_multipart_form_data')
+def test_post_form_multipart_form_data(app, payload):
@app.route('/', methods=['POST'])
async def handler(request):
@@ -331,8 +315,7 @@ def test_post_form_multipart_form_data(payload):
('/bar/baz', '', 'http://{}:{}/bar/baz'),
('/moo/boo', 'arg1=val1', 'http://{}:{}/moo/boo?arg1=val1')
])
-def test_url_attributes_no_ssl(path, query, expected_url):
- app = Sanic('test_url_attrs_no_ssl')
+def test_url_attributes_no_ssl(app, path, query, expected_url):
async def handler(request):
return text('OK')
@@ -356,9 +339,7 @@ def test_url_attributes_no_ssl(path, query, expected_url):
('/bar/baz', '', 'https://{}:{}/bar/baz'),
('/moo/boo', 'arg1=val1', 'https://{}:{}/moo/boo?arg1=val1')
])
-def test_url_attributes_with_ssl(path, query, expected_url):
- app = Sanic('test_url_attrs_with_ssl')
-
+def test_url_attributes_with_ssl(app, path, query, expected_url):
current_dir = os.path.dirname(os.path.realpath(__file__))
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(
diff --git a/tests/test_response.py b/tests/test_response.py
index 12049460..78f9f103 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -8,17 +8,16 @@ from urllib.parse import unquote
import pytest
from random import choice
-from sanic import Sanic
from sanic.response import HTTPResponse, stream, StreamingHTTPResponse, file, file_stream, json
+from sanic.server import HttpProtocol
from sanic.testing import HOST, PORT
from unittest.mock import MagicMock
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"""
- app = Sanic('response_body_not_a_string')
random_num = choice(range(1000))
@app.route('/hello')
@@ -30,13 +29,12 @@ def test_response_body_not_a_string():
async def sample_streaming_fn(response):
- response.write('foo,')
+ await response.write('foo,')
await asyncio.sleep(.001)
- response.write('bar')
+ await response.write('bar')
-def test_method_not_allowed():
- app = Sanic('method_not_allowed')
+def test_method_not_allowed(app):
@app.get('/')
async def test(request):
@@ -64,9 +62,27 @@ def test_method_not_allowed():
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
-def json_app():
- app = Sanic('json')
+def json_app(app):
@app.route("/")
async def test(request):
@@ -124,8 +140,7 @@ def test_no_content(json_app):
@pytest.fixture
-def streaming_app():
- app = Sanic('streaming')
+def streaming_app(app):
@app.route("/")
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):
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')
async def run_stream(app, loop):
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'
)
- 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'
)
- 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'
)
@@ -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'])
-def test_file_response(file_name, static_file_directory):
- app = Sanic('test_file_helper')
+@pytest.mark.parametrize('status', [200, 401])
+def test_file_response(app, file_name, static_file_directory, status):
@app.route('/files/', methods=['GET'])
def file_route(request, filename):
file_path = os.path.join(static_file_directory, filename)
file_path = os.path.abspath(unquote(file_path))
- return file(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))
- assert response.status == 200
+ assert response.status == status
assert response.body == get_file_content(static_file_directory, file_name)
assert 'Content-Disposition' not in response.headers
@pytest.mark.parametrize('source,dest', [
('test.file', 'my_file.txt'), ('decode me.txt', 'readme.md'), ('python.png', 'logo.png')])
-def test_file_response_custom_filename(source, dest, static_file_directory):
- app = Sanic('test_file_helper')
+def test_file_response_custom_filename(app, source, dest, static_file_directory):
@app.route('/files/', methods=['GET'])
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'])
-def test_file_head_response(file_name, static_file_directory):
- app = Sanic('test_file_helper')
+def test_file_head_response(app, file_name, static_file_directory):
@app.route('/files/', methods=['GET', 'HEAD'])
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'])
-def test_file_stream_response(file_name, static_file_directory):
- app = Sanic('test_file_helper')
+def test_file_stream_response(app, file_name, static_file_directory):
@app.route('/files/', methods=['GET'])
def file_route(request, filename):
@@ -288,8 +311,7 @@ def test_file_stream_response(file_name, static_file_directory):
@pytest.mark.parametrize('source,dest', [
('test.file', 'my_file.txt'), ('decode me.txt', 'readme.md'), ('python.png', 'logo.png')])
-def test_file_stream_response_custom_filename(source, dest, static_file_directory):
- app = Sanic('test_file_helper')
+def test_file_stream_response_custom_filename(app, source, dest, static_file_directory):
@app.route('/files/', methods=['GET'])
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'])
-def test_file_stream_head_response(file_name, static_file_directory):
- app = Sanic('test_file_helper')
+def test_file_stream_head_response(app, file_name, static_file_directory):
@app.route('/files/', methods=['GET', 'HEAD'])
async def file_route(request, filename):
diff --git a/tests/test_response_timeout.py b/tests/test_response_timeout.py
index bf55a42e..44204b5e 100644
--- a/tests/test_response_timeout.py
+++ b/tests/test_response_timeout.py
@@ -7,6 +7,7 @@ from sanic.config import Config
Config.RESPONSE_TIMEOUT = 1
response_timeout_app = Sanic('test_response_timeout')
response_timeout_default_app = Sanic('test_response_timeout_default')
+response_handler_cancelled_app = Sanic('test_response_handler_cancelled')
@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')
assert response.status == 503
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
diff --git a/tests/test_routes.py b/tests/test_routes.py
index 146db97c..d70bf975 100644
--- a/tests/test_routes.py
+++ b/tests/test_routes.py
@@ -12,9 +12,7 @@ from sanic.constants import HTTP_METHODS
# ------------------------------------------------------------ #
@pytest.mark.parametrize('method', HTTP_METHODS)
-def test_versioned_routes_get(method):
- app = Sanic('test_shorhand_routes_get')
-
+def test_versioned_routes_get(app, method):
method = method.lower()
func = getattr(app, method)
@@ -32,8 +30,7 @@ def test_versioned_routes_get(method):
assert response.status == 200
-def test_shorthand_routes_get():
- app = Sanic('test_shorhand_routes_get')
+def test_shorthand_routes_get(app):
@app.get('/get')
def handler(request):
@@ -46,8 +43,7 @@ def test_shorthand_routes_get():
assert response.status == 405
-def test_shorthand_routes_multiple():
- app = Sanic('test_shorthand_routes_multiple')
+def test_shorthand_routes_multiple(app):
@app.get('/get')
def get_handler(request):
@@ -65,8 +61,7 @@ def test_shorthand_routes_multiple():
assert response.status == 200
-def test_route_strict_slash():
- app = Sanic('test_route_strict_slash')
+def test_route_strict_slash(app):
@app.get('/get', strict_slashes=True)
def handler(request):
@@ -93,9 +88,8 @@ def test_route_strict_slash():
assert response.status == 404
-def test_route_invalid_parameter_syntax():
+def test_route_invalid_parameter_syntax(app):
with pytest.raises(ValueError):
- app = Sanic('test_route_invalid_param_syntax')
@app.get('/get/<:string>', strict_slashes=True)
def handler(request):
@@ -115,8 +109,7 @@ def test_route_strict_slash_default_value():
assert response.status == 404
-def test_route_strict_slash_without_passing_default_value():
- app = Sanic('test_route_strict_slash')
+def test_route_strict_slash_without_passing_default_value(app):
@app.get('/get')
def handler(request):
@@ -137,8 +130,7 @@ def test_route_strict_slash_default_value_can_be_overwritten():
assert response.text == 'OK'
-def test_route_slashes_overload():
- app = Sanic('test_route_slashes_overload')
+def test_route_slashes_overload(app):
@app.get('/hello/')
def handler(request):
@@ -161,8 +153,7 @@ def test_route_slashes_overload():
assert response.text == 'OK'
-def test_route_optional_slash():
- app = Sanic('test_route_optional_slash')
+def test_route_optional_slash(app):
@app.get('/get')
def handler(request):
@@ -174,9 +165,8 @@ def test_route_optional_slash():
request, response = app.test_client.get('/get/')
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
- app = Sanic('test_route_strict_slashes_set_to_false_and_host_is_a_list')
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')
assert response.text == 'OK'
-def test_shorthand_routes_post():
- app = Sanic('test_shorhand_routes_post')
+def test_shorthand_routes_post(app):
@app.post('/post')
def handler(request):
@@ -223,8 +212,7 @@ def test_shorthand_routes_post():
assert response.status == 405
-def test_shorthand_routes_put():
- app = Sanic('test_shorhand_routes_put')
+def test_shorthand_routes_put(app):
@app.put('/put')
def handler(request):
@@ -240,8 +228,7 @@ def test_shorthand_routes_put():
assert response.status == 405
-def test_shorthand_routes_delete():
- app = Sanic('test_shorhand_routes_delete')
+def test_shorthand_routes_delete(app):
@app.delete('/delete')
def handler(request):
@@ -257,8 +244,7 @@ def test_shorthand_routes_delete():
assert response.status == 405
-def test_shorthand_routes_patch():
- app = Sanic('test_shorhand_routes_patch')
+def test_shorthand_routes_patch(app):
@app.patch('/patch')
def handler(request):
@@ -274,8 +260,7 @@ def test_shorthand_routes_patch():
assert response.status == 405
-def test_shorthand_routes_head():
- app = Sanic('test_shorhand_routes_head')
+def test_shorthand_routes_head(app):
@app.head('/head')
def handler(request):
@@ -291,8 +276,7 @@ def test_shorthand_routes_head():
assert response.status == 405
-def test_shorthand_routes_options():
- app = Sanic('test_shorhand_routes_options')
+def test_shorthand_routes_options(app):
@app.options('/options')
def handler(request):
@@ -308,8 +292,7 @@ def test_shorthand_routes_options():
assert response.status == 405
-def test_static_routes():
- app = Sanic('test_dynamic_route')
+def test_static_routes(app):
@app.route('/test')
async def handler1(request):
@@ -326,9 +309,7 @@ def test_static_routes():
assert response.text == 'OK2'
-def test_dynamic_route():
- app = Sanic('test_dynamic_route')
-
+def test_dynamic_route(app):
results = []
@app.route('/folder/')
@@ -342,9 +323,7 @@ def test_dynamic_route():
assert results[0] == 'test123'
-def test_dynamic_route_string():
- app = Sanic('test_dynamic_route_string')
-
+def test_dynamic_route_string(app):
results = []
@app.route('/folder/')
@@ -363,9 +342,7 @@ def test_dynamic_route_string():
assert results[1] == 'favicon.ico'
-def test_dynamic_route_int():
- app = Sanic('test_dynamic_route_int')
-
+def test_dynamic_route_int(app):
results = []
@app.route('/folder/')
@@ -381,9 +358,7 @@ def test_dynamic_route_int():
assert response.status == 404
-def test_dynamic_route_number():
- app = Sanic('test_dynamic_route_number')
-
+def test_dynamic_route_number(app):
results = []
@app.route('/weight/')
@@ -402,8 +377,7 @@ def test_dynamic_route_number():
assert response.status == 404
-def test_dynamic_route_regex():
- app = Sanic('test_dynamic_route_regex')
+def test_dynamic_route_regex(app):
@app.route('/folder/')
async def handler(request, folder_id):
@@ -422,9 +396,8 @@ def test_dynamic_route_regex():
assert response.status == 200
-def test_dynamic_route_uuid():
+def test_dynamic_route_uuid(app):
import uuid
- app = Sanic('test_dynamic_route_uuid')
results = []
@@ -444,8 +417,7 @@ def test_dynamic_route_uuid():
assert response.status == 404
-def test_dynamic_route_path():
- app = Sanic('test_dynamic_route_path')
+def test_dynamic_route_path(app):
@app.route('//info')
async def handler(request, path):
@@ -468,8 +440,7 @@ def test_dynamic_route_path():
assert response.status == 200
-def test_dynamic_route_unhashable():
- app = Sanic('test_dynamic_route_unhashable')
+def test_dynamic_route_unhashable(app):
@app.route('/folder//end/')
async def handler(request, unhashable):
@@ -488,8 +459,7 @@ def test_dynamic_route_unhashable():
assert response.status == 404
-def test_websocket_route():
- app = Sanic('test_websocket_route')
+def test_websocket_route(app):
ev = asyncio.Event()
@app.websocket('/ws')
@@ -506,8 +476,7 @@ def test_websocket_route():
assert ev.is_set()
-def test_websocket_route_with_subprotocols():
- app = Sanic('test_websocket_route')
+def test_websocket_route_with_subprotocols(app):
results = []
@app.websocket('/ws', subprotocols=['foo', 'bar'])
@@ -548,8 +517,7 @@ def test_websocket_route_with_subprotocols():
assert results == ['bar', 'bar', None, None]
-def test_route_duplicate():
- app = Sanic('test_route_duplicate')
+def test_route_duplicate(app):
with pytest.raises(RouteExists):
@app.route('/test')
@@ -570,8 +538,7 @@ def test_route_duplicate():
pass
-def test_method_not_allowed():
- app = Sanic('test_method_not_allowed')
+def test_method_not_allowed(app):
@app.route('/test', methods=['GET'])
async def handler(request):
@@ -584,8 +551,7 @@ def test_method_not_allowed():
assert response.status == 405
-def test_static_add_route():
- app = Sanic('test_static_add_route')
+def test_static_add_route(app):
async def handler1(request):
return text('OK1')
@@ -603,8 +569,7 @@ def test_static_add_route():
assert response.text == 'OK2'
-def test_dynamic_add_route():
- app = Sanic('test_dynamic_add_route')
+def test_dynamic_add_route(app):
results = []
@@ -619,8 +584,7 @@ def test_dynamic_add_route():
assert results[0] == 'test123'
-def test_dynamic_add_route_string():
- app = Sanic('test_dynamic_add_route_string')
+def test_dynamic_add_route_string(app):
results = []
@@ -640,9 +604,7 @@ def test_dynamic_add_route_string():
assert results[1] == 'favicon.ico'
-def test_dynamic_add_route_int():
- app = Sanic('test_dynamic_add_route_int')
-
+def test_dynamic_add_route_int(app):
results = []
async def handler(request, folder_id):
@@ -659,9 +621,7 @@ def test_dynamic_add_route_int():
assert response.status == 404
-def test_dynamic_add_route_number():
- app = Sanic('test_dynamic_add_route_number')
-
+def test_dynamic_add_route_number(app):
results = []
async def handler(request, weight):
@@ -681,8 +641,7 @@ def test_dynamic_add_route_number():
assert response.status == 404
-def test_dynamic_add_route_regex():
- app = Sanic('test_dynamic_route_int')
+def test_dynamic_add_route_regex(app):
async def handler(request, folder_id):
return text('OK')
@@ -702,8 +661,7 @@ def test_dynamic_add_route_regex():
assert response.status == 200
-def test_dynamic_add_route_unhashable():
- app = Sanic('test_dynamic_add_route_unhashable')
+def test_dynamic_add_route_unhashable(app):
async def handler(request, unhashable):
return text('OK')
@@ -723,8 +681,7 @@ def test_dynamic_add_route_unhashable():
assert response.status == 404
-def test_add_route_duplicate():
- app = Sanic('test_add_route_duplicate')
+def test_add_route_duplicate(app):
with pytest.raises(RouteExists):
async def handler1(request):
@@ -747,8 +704,7 @@ def test_add_route_duplicate():
app.add_route(handler2, '/test//')
-def test_add_route_method_not_allowed():
- app = Sanic('test_add_route_method_not_allowed')
+def test_add_route_method_not_allowed(app):
async def handler(request):
return text('OK')
@@ -762,8 +718,7 @@ def test_add_route_method_not_allowed():
assert response.status == 405
-def test_remove_static_route():
- app = Sanic('test_remove_static_route')
+def test_remove_static_route(app):
async def handler1(request):
return text('OK1')
@@ -790,8 +745,7 @@ def test_remove_static_route():
assert response.status == 404
-def test_remove_dynamic_route():
- app = Sanic('test_remove_dynamic_route')
+def test_remove_dynamic_route(app):
async def handler(request, name):
return text('OK')
@@ -806,15 +760,13 @@ def test_remove_dynamic_route():
assert response.status == 404
-def test_remove_inexistent_route():
- app = Sanic('test_remove_inexistent_route')
+def test_remove_inexistent_route(app):
with pytest.raises(RouteDoesNotExist):
app.remove_route('/test')
-def test_removing_slash():
- app = Sanic(__name__)
+def test_removing_slash(app):
@app.get('/rest/')
def get(_):
@@ -827,8 +779,7 @@ def test_removing_slash():
assert len(app.router.routes_all.keys()) == 2
-def test_remove_unhashable_route():
- app = Sanic('test_remove_unhashable_route')
+def test_remove_unhashable_route(app):
async def handler(request, unhashable):
return text('OK')
@@ -856,8 +807,7 @@ def test_remove_unhashable_route():
assert response.status == 404
-def test_remove_route_without_clean_cache():
- app = Sanic('test_remove_static_route')
+def test_remove_route_without_clean_cache(app):
async def handler(request):
return text('OK')
@@ -884,8 +834,7 @@ def test_remove_route_without_clean_cache():
assert response.status == 200
-def test_overload_routes():
- app = Sanic('test_dynamic_route')
+def test_overload_routes(app):
@app.route('/overload', methods=['GET'])
async def handler1(request):
@@ -913,8 +862,7 @@ def test_overload_routes():
return text('Duplicated')
-def test_unmergeable_overload_routes():
- app = Sanic('test_dynamic_route')
+def test_unmergeable_overload_routes(app):
@app.route('/overload_whole', methods=None)
async def handler1(request):
@@ -947,8 +895,7 @@ def test_unmergeable_overload_routes():
assert response.status == 405
-def test_unicode_routes():
- app = Sanic('test_unicode_routes')
+def test_unicode_routes(app):
@app.get('/你好')
def handler1(request):
@@ -965,8 +912,7 @@ def test_unicode_routes():
assert response.text == 'OK2 你好'
-def test_uri_with_different_method_and_different_params():
- app = Sanic('test_uri')
+def test_uri_with_different_method_and_different_params(app):
@app.route('/ads/', methods=['GET'])
async def ad_get(request, ad_id):
diff --git a/tests/test_server_events.py b/tests/test_server_events.py
index c2ccc7dc..68e097eb 100644
--- a/tests/test_server_events.py
+++ b/tests/test_server_events.py
@@ -1,11 +1,7 @@
-from io import StringIO
-from random import choice
-from string import ascii_letters
import signal
import pytest
-from sanic import Sanic
from sanic.testing import HOST, PORT
AVAILABLE_LISTENERS = [
@@ -37,54 +33,46 @@ def start_stop_app(random_name_app, **run_kwargs):
@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"""
- random_name_app = Sanic(''.join(
- [choice(ascii_letters) for _ in range(choice(range(5, 10)))]))
- output = list()
+ output = []
# Register listener
- random_name_app.listener(listener_name)(
+ app.listener(listener_name)(
create_listener(listener_name, output))
- start_stop_app(random_name_app)
- assert random_name_app.name + listener_name == output.pop()
+ start_stop_app(app)
+ assert app.name + listener_name == output.pop()
@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
app.register_listener method
"""
- random_name_app = Sanic(''.join(
- [choice(ascii_letters) for _ in range(choice(range(5, 10)))]))
- output = list()
+ output = []
# Register listener
listener = create_listener(listener_name, output)
- random_name_app.register_listener(listener,
+ app.register_listener(listener,
event=listener_name)
- start_stop_app(random_name_app)
- assert random_name_app.name + listener_name == output.pop()
+ start_stop_app(app)
+ assert app.name + listener_name == output.pop()
-def test_all_listeners():
- random_name_app = Sanic(''.join(
- [choice(ascii_letters) for _ in range(choice(range(5, 10)))]))
- output = list()
+def test_all_listeners(app):
+ output = []
for listener_name in AVAILABLE_LISTENERS:
listener = create_listener(listener_name, output)
- random_name_app.listener(listener_name)(listener)
- start_stop_app(random_name_app)
+ app.listener(listener_name)(listener)
+ start_stop_app(app)
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:
pass
- app = Sanic("test_sanic_app")
-
@app.listener('before_server_start')
async def init_db(app, loop):
app.db = MySanicDb()
diff --git a/tests/test_signal_handlers.py b/tests/test_signal_handlers.py
index bee4f8e7..7c1327c2 100644
--- a/tests/test_signal_handlers.py
+++ b/tests/test_signal_handlers.py
@@ -1,4 +1,3 @@
-from sanic import Sanic
from sanic.response import HTTPResponse
from sanic.testing import HOST, PORT
from unittest.mock import MagicMock
@@ -18,9 +17,8 @@ def set_loop(app, loop):
def after(app, loop):
calledq.put(loop.add_signal_handler.called)
-def test_register_system_signals():
+def test_register_system_signals(app):
"""Test if sanic register system signals"""
- app = Sanic('test_register_system_signals')
@app.route('/hello')
async def hello_route(request):
@@ -34,9 +32,8 @@ def test_register_system_signals():
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"""
- app = Sanic('test_register_system_signals')
@app.route('/hello')
async def hello_route(request):
diff --git a/tests/test_static.py b/tests/test_static.py
index 276001cc..e436aa74 100644
--- a/tests/test_static.py
+++ b/tests/test_static.py
@@ -3,8 +3,6 @@ import os
import pytest
-from sanic import Sanic
-
@pytest.fixture(scope='module')
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'])
-def test_static_file(static_file_directory, file_name):
- app = Sanic('test_static')
+def test_static_file(app, static_file_directory, file_name):
app.static(
'/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)
+@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('base_uri', ['/static', '', '/dir'])
-def test_static_directory(file_name, base_uri, static_file_directory):
-
- app = Sanic('test_static')
+def test_static_directory(app, file_name, base_uri, static_file_directory):
app.static(base_uri, static_file_directory)
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'])
-def test_static_head_request(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_head_request(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
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'])
-def test_static_content_range_correct(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_correct(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
@@ -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'])
-def test_static_content_range_front(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_front(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
@@ -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'])
-def test_static_content_range_back(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_back(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
@@ -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'])
-def test_static_content_range_empty(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_empty(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
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'])
-def test_static_content_range_error(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_error(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
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'])
-def test_static_file_specified_host(static_file_directory, file_name):
- app = Sanic('test_static')
+def test_static_file_specified_host(app, static_file_directory, file_name):
app.static(
'/testing.file',
get_file_path(static_file_directory, file_name),
diff --git a/tests/test_url_building.py b/tests/test_url_building.py
index 98bbc20a..1768f78f 100644
--- a/tests/test_url_building.py
+++ b/tests/test_url_building.py
@@ -1,7 +1,6 @@
import pytest as pytest
from urllib.parse import urlsplit, parse_qsl
-from sanic import Sanic
from sanic.response import text
from sanic.views import HTTPMethodView
from sanic.blueprints import Blueprint
@@ -30,8 +29,7 @@ def _generate_handlers_from_names(app, l):
@pytest.fixture
-def simple_app():
- app = Sanic('simple_app')
+def simple_app(app):
handler_names = list(string.ascii_letters)
_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_ARGS3, URL_FOR_VALUE3),
(URL_FOR_ARGS4, URL_FOR_VALUE4)])
-def test_simple_url_for_getting_with_more_params(args, url):
- app = Sanic('more_url_build')
+def test_simple_url_for_getting_with_more_params(app, args, url):
@app.route('/myurl')
def passes(request):
@@ -67,8 +64,7 @@ def test_simple_url_for_getting_with_more_params(args, url):
assert response.text == 'this should pass'
-def test_fails_if_endpoint_not_found():
- app = Sanic('fail_url_build')
+def test_fails_if_endpoint_not_found(app):
@app.route('/fail')
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'
-def test_fails_url_build_if_param_not_passed():
+def test_fails_url_build_if_param_not_passed(app):
url = '/'
for letter in string.ascii_letters:
url += '<{}>/'.format(letter)
- app = Sanic('fail_url_build')
-
@app.route(url)
def fail(request):
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)
-def test_fails_url_build_if_params_not_passed():
- app = Sanic('fail_url_build')
+def test_fails_url_build_if_params_not_passed(app):
@app.route('/fail')
def fail(request):
@@ -126,8 +119,7 @@ PASSING_KWARGS = {
EXPECTED_BUILT_URL = '/4/woof/ba/normal/1.001'
-def test_fails_with_int_message():
- app = Sanic('fail_url_build')
+def test_fails_with_int_message(app):
@app.route(COMPLEX_PARAM_URL)
def fail(request):
@@ -145,8 +137,7 @@ def test_fails_with_int_message():
assert str(e.value) == expected_error
-def test_fails_with_two_letter_string_message():
- app = Sanic('fail_url_build')
+def test_fails_with_two_letter_string_message(app):
@app.route(COMPLEX_PARAM_URL)
def fail(request):
@@ -165,8 +156,7 @@ def test_fails_with_two_letter_string_message():
assert str(e.value) == expected_error
-def test_fails_with_number_message():
- app = Sanic('fail_url_build')
+def test_fails_with_number_message(app):
@app.route(COMPLEX_PARAM_URL)
def fail(request):
@@ -185,8 +175,7 @@ def test_fails_with_number_message():
assert str(e.value) == expected_error
-def test_adds_other_supplied_values_as_query_string():
- app = Sanic('passes')
+def test_adds_other_supplied_values_as_query_string(app):
@app.route(COMPLEX_PARAM_URL)
def passes(request):
@@ -205,8 +194,7 @@ def test_adds_other_supplied_values_as_query_string():
@pytest.fixture
-def blueprint_app():
- app = Sanic('blueprints')
+def blueprint_app(app):
first_print = Blueprint('first', url_prefix='/first')
second_print = Blueprint('second', url_prefix='/second')
@@ -252,8 +240,7 @@ def test_blueprints_work_with_params(blueprint_app):
@pytest.fixture
-def methodview_app():
- app = Sanic('methodview')
+def methodview_app(app):
class ViewOne(HTTPMethodView):
def get(self, request):
diff --git a/tests/test_url_for_static.py b/tests/test_url_for_static.py
index d1d8fc9b..2e802700 100644
--- a/tests/test_url_for_static.py
+++ b/tests/test_url_for_static.py
@@ -3,7 +3,6 @@ import os
import pytest
-from sanic import Sanic
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'])
-def test_static_file(static_file_directory, file_name):
- app = Sanic('test_static')
+def test_static_file(app, static_file_directory, file_name):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name))
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('base_uri', ['/static', '', '/dir'])
-def test_static_directory(file_name, base_uri, static_file_directory):
-
- app = Sanic('test_static')
+def test_static_directory(app, file_name, base_uri, static_file_directory):
app.static(base_uri, static_file_directory)
base_uri2 = base_uri + '/2'
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)
-
@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt'])
-def test_static_head_request(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_head_request(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
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'])
-def test_static_content_range_correct(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_correct(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
@@ -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'])
-def test_static_content_range_front(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_front(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
@@ -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'])
-def test_static_content_range_back(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_back(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
@@ -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'])
-def test_static_content_range_empty(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_empty(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
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'])
-def test_static_content_range_error(file_name, static_file_directory):
- app = Sanic('test_static')
+def test_static_content_range_error(app, file_name, static_file_directory):
app.static(
'/testing.file', get_file_path(static_file_directory, file_name),
use_content_range=True)
diff --git a/tests/test_utf8.py b/tests/test_utf8.py
index e1602958..4a8b9e73 100644
--- a/tests/test_utf8.py
+++ b/tests/test_utf8.py
@@ -1,14 +1,12 @@
-from json import loads as json_loads, dumps as json_dumps
-from sanic import Sanic
-from sanic.response import json, text
+from json import dumps as json_dumps
+from sanic.response import text
# ------------------------------------------------------------ #
# UTF-8
# ------------------------------------------------------------ #
-def test_utf8_query_string():
- app = Sanic('test_utf8_query_string')
+def test_utf8_query_string(app):
@app.route('/')
async def handler(request):
@@ -18,8 +16,7 @@ def test_utf8_query_string():
assert request.args.get('utf8') == '✓'
-def test_utf8_response():
- app = Sanic('test_utf8_response')
+def test_utf8_response(app):
@app.route('/')
async def handler(request):
@@ -29,8 +26,7 @@ def test_utf8_response():
assert response.text == '✓'
-def skip_test_utf8_route():
- app = Sanic('skip_test_utf8_route')
+def skip_test_utf8_route(app):
@app.route('/')
async def handler(request):
@@ -41,8 +37,7 @@ def skip_test_utf8_route():
assert response.text == 'OK'
-def test_utf8_post_json():
- app = Sanic('test_utf8_post_json')
+def test_utf8_post_json(app):
@app.route('/')
async def handler(request):
diff --git a/tests/test_vhosts.py b/tests/test_vhosts.py
index 2b88bff3..0be537e6 100644
--- a/tests/test_vhosts.py
+++ b/tests/test_vhosts.py
@@ -1,9 +1,7 @@
-from sanic import Sanic
-from sanic.response import json, text
+from sanic.response import text
-def test_vhosts():
- app = Sanic('test_vhosts')
+def test_vhosts(app):
@app.route('/', host="example.com")
async def handler(request):
@@ -22,8 +20,7 @@ def test_vhosts():
assert response.text == "You're at subdomain.example.com!"
-def test_vhosts_with_list():
- app = Sanic('test_vhosts')
+def test_vhosts_with_list(app):
@app.route('/', host=["hello.com", "world.com"])
async def handler(request):
@@ -37,8 +34,8 @@ def test_vhosts_with_list():
request, response = app.test_client.get('/', headers=headers)
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")
async def handler(request):
diff --git a/tests/test_views.py b/tests/test_views.py
index 71d32a7f..5d339f55 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -1,6 +1,5 @@
import pytest as pytest
-from sanic import Sanic
from sanic.exceptions import InvalidUsage
from sanic.response import text, HTTPResponse
from sanic.views import HTTPMethodView, CompositionView
@@ -10,8 +9,7 @@ from sanic.constants import HTTP_METHODS
@pytest.mark.parametrize('method', HTTP_METHODS)
-def test_methods(method):
- app = Sanic('test_methods')
+def test_methods(app, method):
class DummyView(HTTPMethodView):
@@ -44,8 +42,7 @@ def test_methods(method):
assert response.headers['method'] == method
-def test_unexisting_methods():
- app = Sanic('test_unexisting_methods')
+def test_unexisting_methods(app):
class DummyView(HTTPMethodView):
@@ -59,8 +56,7 @@ def test_unexisting_methods():
assert response.text == 'Error: Method POST not allowed for URL /'
-def test_argument_methods():
- app = Sanic('test_argument_methods')
+def test_argument_methods(app):
class DummyView(HTTPMethodView):
@@ -74,8 +70,7 @@ def test_argument_methods():
assert response.text == 'I am get method with test123'
-def test_with_bp():
- app = Sanic('test_with_bp')
+def test_with_bp(app):
bp = Blueprint('test_text')
class DummyView(HTTPMethodView):
@@ -93,8 +88,7 @@ def test_with_bp():
assert response.text == 'I am get method'
-def test_with_bp_with_url_prefix():
- app = Sanic('test_with_bp_with_url_prefix')
+def test_with_bp_with_url_prefix(app):
bp = Blueprint('test_text', url_prefix='/test1')
class DummyView(HTTPMethodView):
@@ -110,8 +104,7 @@ def test_with_bp_with_url_prefix():
assert response.text == 'I am get method'
-def test_with_middleware():
- app = Sanic('test_with_middleware')
+def test_with_middleware(app):
class DummyView(HTTPMethodView):
@@ -132,9 +125,7 @@ def test_with_middleware():
assert type(results[0]) is Request
-def test_with_middleware_response():
- app = Sanic('test_with_middleware_response')
-
+def test_with_middleware_response(app):
results = []
@app.middleware('request')
@@ -161,8 +152,7 @@ def test_with_middleware_response():
assert isinstance(results[2], HTTPResponse)
-def test_with_custom_class_methods():
- app = Sanic('test_with_custom_class_methods')
+def test_with_custom_class_methods(app):
class DummyView(HTTPMethodView):
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'
-def test_with_decorator():
- app = Sanic('test_with_decorator')
-
+def test_with_decorator(app):
results = []
def stupid_decorator(view):
@@ -227,9 +215,7 @@ def test_composition_view_rejects_duplicate_methods():
@pytest.mark.parametrize('method', HTTP_METHODS)
-def test_composition_view_runs_methods_as_expected(method):
- app = Sanic('test_composition_view')
-
+def test_composition_view_runs_methods_as_expected(app, method):
view = CompositionView()
def first(request):
@@ -251,9 +237,7 @@ def test_composition_view_runs_methods_as_expected(method):
@pytest.mark.parametrize('method', HTTP_METHODS)
-def test_composition_view_rejects_invalid_methods(method):
- app = Sanic('test_composition_view')
-
+def test_composition_view_rejects_invalid_methods(app, method):
view = CompositionView()
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method'))