Merge pull request #19 from channelcat/master
merge upstream master branch
This commit is contained in:
commit
31cf83f10b
|
@ -19,7 +19,7 @@ matrix:
|
||||||
- env: TOX_ENV=check
|
- env: TOX_ENV=check
|
||||||
python: 3.6
|
python: 3.6
|
||||||
install: pip install -U tox
|
install: pip install -U tox
|
||||||
script: tox -e $TOX_ENV
|
script: travis_retry tox -e $TOX_ENV
|
||||||
deploy:
|
deploy:
|
||||||
provider: pypi
|
provider: pypi
|
||||||
user: channelcat
|
user: channelcat
|
||||||
|
|
|
@ -13,7 +13,7 @@ To throw an exception, simply `raise` the relevant exception from the
|
||||||
from sanic.exceptions import ServerError
|
from sanic.exceptions import ServerError
|
||||||
|
|
||||||
@app.route('/killme')
|
@app.route('/killme')
|
||||||
def i_am_ready_to_die(request):
|
async def i_am_ready_to_die(request):
|
||||||
raise ServerError("Something bad happened", status_code=500)
|
raise ServerError("Something bad happened", status_code=500)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ from sanic.exceptions import abort
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
@app.route('/youshallnotpass')
|
@app.route('/youshallnotpass')
|
||||||
def no_no(request):
|
async def no_no(request):
|
||||||
abort(401)
|
abort(401)
|
||||||
# this won't happen
|
# this won't happen
|
||||||
text("OK")
|
text("OK")
|
||||||
|
@ -43,7 +43,7 @@ from sanic.response import text
|
||||||
from sanic.exceptions import NotFound
|
from sanic.exceptions import NotFound
|
||||||
|
|
||||||
@app.exception(NotFound)
|
@app.exception(NotFound)
|
||||||
def ignore_404s(request, exception):
|
async def ignore_404s(request, exception):
|
||||||
return text("Yep, I totally found the page: {}".format(request.url))
|
return text("Yep, I totally found the page: {}".format(request.url))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -138,13 +138,14 @@ app.add_route(person_handler2, '/person/<name:[A-z]>', methods=['GET'])
|
||||||
Sanic provides a `url_for` method, to generate URLs based on the handler method name. This is useful if you want to avoid hardcoding url paths into your app; instead, you can just reference the handler name. For example:
|
Sanic provides a `url_for` method, to generate URLs based on the handler method name. This is useful if you want to avoid hardcoding url paths into your app; instead, you can just reference the handler name. For example:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
from sanic.response import redirect
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def index(request):
|
async def index(request):
|
||||||
# generate a URL for the endpoint `post_handler`
|
# generate a URL for the endpoint `post_handler`
|
||||||
url = app.url_for('post_handler', post_id=5)
|
url = app.url_for('post_handler', post_id=5)
|
||||||
# the URL is `/posts/5`, redirect to it
|
# the URL is `/posts/5`, redirect to it
|
||||||
return redirect(url)
|
return redirect(url)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/posts/<post_id>')
|
@app.route('/posts/<post_id>')
|
||||||
async def post_handler(request, post_id):
|
async def post_handler(request, post_id):
|
||||||
|
|
|
@ -220,7 +220,7 @@ class Sanic:
|
||||||
name=name)
|
name=name)
|
||||||
|
|
||||||
def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None,
|
def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None,
|
||||||
strict_slashes=None, version=None, name=None):
|
strict_slashes=None, version=None, name=None, stream=False):
|
||||||
"""A helper method to register class instance or
|
"""A helper method to register class instance or
|
||||||
functions as a handler to the application url
|
functions as a handler to the application url
|
||||||
routes.
|
routes.
|
||||||
|
@ -233,9 +233,9 @@ class Sanic:
|
||||||
:param strict_slashes:
|
:param strict_slashes:
|
||||||
:param version:
|
:param version:
|
||||||
:param name: user defined route name for url_for
|
:param name: user defined route name for url_for
|
||||||
|
:param stream: boolean specifying if the handler is a stream handler
|
||||||
:return: function or class instance
|
:return: function or class instance
|
||||||
"""
|
"""
|
||||||
stream = False
|
|
||||||
# Handle HTTPMethodView differently
|
# Handle HTTPMethodView differently
|
||||||
if hasattr(handler, 'view_class'):
|
if hasattr(handler, 'view_class'):
|
||||||
methods = set()
|
methods = set()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import traceback
|
import traceback
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
|
|
||||||
from sanic.log import logger
|
from sanic.log import logger
|
||||||
|
from sanic.exceptions import MethodNotSupported
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
HOST = '127.0.0.1'
|
HOST = '127.0.0.1'
|
||||||
PORT = 42101
|
PORT = 42101
|
||||||
|
@ -54,6 +56,15 @@ class SanicTestClient:
|
||||||
results[0] = request
|
results[0] = request
|
||||||
self.app.request_middleware.appendleft(_collect_request)
|
self.app.request_middleware.appendleft(_collect_request)
|
||||||
|
|
||||||
|
@self.app.exception(MethodNotSupported)
|
||||||
|
async def error_handler(request, exception):
|
||||||
|
if request.method in ['HEAD', 'PATCH', 'PUT', 'DELETE']:
|
||||||
|
return text(
|
||||||
|
'', exception.status_code, headers=exception.headers
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return self.app.error_handler.default(request, exception)
|
||||||
|
|
||||||
@self.app.listener('after_server_start')
|
@self.app.listener('after_server_start')
|
||||||
async def _collect_response(sanic, loop):
|
async def _collect_response(sanic, loop):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -15,17 +15,30 @@ class ReuseableTCPConnector(TCPConnector):
|
||||||
super(ReuseableTCPConnector, self).__init__(*args, **kwargs)
|
super(ReuseableTCPConnector, self).__init__(*args, **kwargs)
|
||||||
self.old_proto = None
|
self.old_proto = None
|
||||||
|
|
||||||
@asyncio.coroutine
|
if aiohttp.__version__ >= '3.0':
|
||||||
def connect(self, req):
|
|
||||||
new_conn = yield from super(ReuseableTCPConnector, self)\
|
async def connect(self, req, traces=None):
|
||||||
.connect(req)
|
new_conn = await super(ReuseableTCPConnector, self)\
|
||||||
if self.old_proto is not None:
|
.connect(req, traces=traces)
|
||||||
if self.old_proto != new_conn._protocol:
|
if self.old_proto is not None:
|
||||||
raise RuntimeError(
|
if self.old_proto != new_conn._protocol:
|
||||||
"We got a new connection, wanted the same one!")
|
raise RuntimeError(
|
||||||
print(new_conn.__dict__)
|
"We got a new connection, wanted the same one!")
|
||||||
self.old_proto = new_conn._protocol
|
print(new_conn.__dict__)
|
||||||
return new_conn
|
self.old_proto = new_conn._protocol
|
||||||
|
return new_conn
|
||||||
|
else:
|
||||||
|
|
||||||
|
async def connect(self, req):
|
||||||
|
new_conn = await super(ReuseableTCPConnector, self)\
|
||||||
|
.connect(req)
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class ReuseableSanicTestClient(SanicTestClient):
|
class ReuseableSanicTestClient(SanicTestClient):
|
||||||
|
@ -168,7 +181,7 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
|
|
||||||
response.body = await response.read()
|
response.body = await response.read()
|
||||||
if do_kill_session:
|
if do_kill_session:
|
||||||
session.close()
|
await session.close()
|
||||||
self._session = None
|
self._session = None
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
|
@ -28,19 +28,18 @@ class DelayableTCPConnector(TCPConnector):
|
||||||
acting_as = self._acting_as
|
acting_as = self._acting_as
|
||||||
return getattr(acting_as, item)
|
return getattr(acting_as, item)
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def start(self, connection, read_until_eof=False):
|
||||||
def start(self, connection, read_until_eof=False):
|
|
||||||
if self.send_task is None:
|
if self.send_task is None:
|
||||||
raise RuntimeError("do a send() before you do a start()")
|
raise RuntimeError("do a send() before you do a start()")
|
||||||
resp = yield from self.send_task
|
resp = await self.send_task
|
||||||
self.send_task = None
|
self.send_task = None
|
||||||
self.resp = resp
|
self.resp = resp
|
||||||
self._acting_as = self.resp
|
self._acting_as = self.resp
|
||||||
self.orig_start = getattr(resp, 'start')
|
self.orig_start = getattr(resp, 'start')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ret = yield from self.orig_start(connection,
|
ret = await self.orig_start(connection,
|
||||||
read_until_eof)
|
read_until_eof)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
return ret
|
return ret
|
||||||
|
@ -51,12 +50,11 @@ class DelayableTCPConnector(TCPConnector):
|
||||||
if self.send_task is not None:
|
if self.send_task is not None:
|
||||||
self.send_task.cancel()
|
self.send_task.cancel()
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def delayed_send(self, *args, **kwargs):
|
||||||
def delayed_send(self, *args, **kwargs):
|
|
||||||
req = self.req
|
req = self.req
|
||||||
if self.delay and self.delay > 0:
|
if self.delay and self.delay > 0:
|
||||||
#sync_sleep(self.delay)
|
#sync_sleep(self.delay)
|
||||||
_ = yield from asyncio.sleep(self.delay)
|
await asyncio.sleep(self.delay)
|
||||||
t = req.loop.time()
|
t = req.loop.time()
|
||||||
print("sending at {}".format(t), flush=True)
|
print("sending at {}".format(t), flush=True)
|
||||||
conn = next(iter(args)) # first arg is connection
|
conn = next(iter(args)) # first arg is connection
|
||||||
|
@ -80,18 +78,32 @@ class DelayableTCPConnector(TCPConnector):
|
||||||
self._post_connect_delay = _post_connect_delay
|
self._post_connect_delay = _post_connect_delay
|
||||||
self._pre_request_delay = _pre_request_delay
|
self._pre_request_delay = _pre_request_delay
|
||||||
|
|
||||||
@asyncio.coroutine
|
if aiohttp.__version__ >= '3.0':
|
||||||
def connect(self, req):
|
|
||||||
d_req = DelayableTCPConnector.\
|
async def connect(self, req, traces=None):
|
||||||
RequestContextManager(req, self._pre_request_delay)
|
d_req = DelayableTCPConnector.\
|
||||||
conn = yield from super(DelayableTCPConnector, self).connect(req)
|
RequestContextManager(req, self._pre_request_delay)
|
||||||
if self._post_connect_delay and self._post_connect_delay > 0:
|
conn = await super(DelayableTCPConnector, self).connect(req, traces=traces)
|
||||||
_ = yield from asyncio.sleep(self._post_connect_delay,
|
if self._post_connect_delay and self._post_connect_delay > 0:
|
||||||
loop=self._loop)
|
await asyncio.sleep(self._post_connect_delay,
|
||||||
req.send = d_req.send
|
loop=self._loop)
|
||||||
t = req.loop.time()
|
req.send = d_req.send
|
||||||
print("Connected at {}".format(t), flush=True)
|
t = req.loop.time()
|
||||||
return conn
|
print("Connected at {}".format(t), flush=True)
|
||||||
|
return conn
|
||||||
|
else:
|
||||||
|
|
||||||
|
async def connect(self, req):
|
||||||
|
d_req = DelayableTCPConnector.\
|
||||||
|
RequestContextManager(req, self._pre_request_delay)
|
||||||
|
conn = await super(DelayableTCPConnector, self).connect(req)
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class DelayableSanicTestClient(SanicTestClient):
|
class DelayableSanicTestClient(SanicTestClient):
|
||||||
|
|
|
@ -45,6 +45,10 @@ def test_method_not_allowed():
|
||||||
request, response = app.test_client.head('/')
|
request, response = app.test_client.head('/')
|
||||||
assert response.headers['Allow'] == 'GET'
|
assert response.headers['Allow'] == 'GET'
|
||||||
|
|
||||||
|
request, response = app.test_client.post('/')
|
||||||
|
assert response.headers['Allow'] == 'GET'
|
||||||
|
|
||||||
|
|
||||||
@app.post('/')
|
@app.post('/')
|
||||||
async def test(request):
|
async def test(request):
|
||||||
return response.json({'hello': 'world'})
|
return response.json({'hello': 'world'})
|
||||||
|
@ -54,6 +58,11 @@ def test_method_not_allowed():
|
||||||
assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST'])
|
assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST'])
|
||||||
assert response.headers['Content-Length'] == '0'
|
assert response.headers['Content-Length'] == '0'
|
||||||
|
|
||||||
|
request, response = app.test_client.patch('/')
|
||||||
|
assert response.status == 405
|
||||||
|
assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST'])
|
||||||
|
assert response.headers['Content-Length'] == '0'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def json_app():
|
def json_app():
|
||||||
|
|
Loading…
Reference in New Issue
Block a user