is_request_stream for CompositionView and HTTPMethodView

This commit is contained in:
38elements 2017-05-08 23:04:45 +09:00
parent 15ad07f03d
commit 4d4f38fb35
7 changed files with 228 additions and 60 deletions

View File

@ -131,6 +131,7 @@ class Sanic:
self.is_request_stream = True
def response(handler):
if stream:
handler.is_stream = stream
self.router.add(uri=uri, methods=methods, handler=handler,
host=host, strict_slashes=strict_slashes)
@ -187,20 +188,28 @@ class Sanic:
:param host:
:return: function or class instance
"""
stream = False
# Handle HTTPMethodView differently
if hasattr(handler, 'view_class'):
methods = set()
for method in HTTP_METHODS:
if getattr(handler.view_class, method.lower(), None):
_handler = getattr(handler.view_class, method.lower(), None)
if _handler:
methods.add(method)
if hasattr(_handler, 'is_stream'):
stream = True
# handle composition view differently
if isinstance(handler, CompositionView):
methods = handler.handlers.keys()
for _handler in handler.handlers.values():
if hasattr(_handler, 'is_stream'):
stream = True
break
self.route(uri=uri, methods=methods, host=host,
strict_slashes=strict_slashes)(handler)
strict_slashes=strict_slashes, stream=stream)(handler)
return handler
# Decorator

View File

@ -355,4 +355,4 @@ class Router:
if (hasattr(handler, 'view_class') and
hasattr(handler.view_class, request.method.lower())):
handler = getattr(handler.view_class, request.method.lower())
return hasattr(handler, 'is_stream') and handler.is_stream
return hasattr(handler, 'is_stream')

View File

@ -89,6 +89,7 @@ class CompositionView:
self.handlers = {}
def add(self, methods, handler, stream=False):
if stream:
handler.is_stream = stream
for method in methods:
if method not in HTTP_METHODS:

View File

@ -17,10 +17,12 @@ def test_bp():
@bp.route('/')
def handler(request):
assert request.stream is None
return text('Hello')
app.blueprint(bp)
request, response = app.test_client.get('/')
assert app.is_request_stream is False
assert response.text == 'Hello'

View File

@ -1,3 +1,4 @@
import asyncio
from sanic import Sanic
from sanic.blueprints import Blueprint
from sanic.views import CompositionView
@ -5,17 +6,23 @@ from sanic.views import HTTPMethodView
from sanic.views import stream as stream_decorator
from sanic.response import stream, text
bp = Blueprint('test_blueprint_request_stream')
app = Sanic('test_request_stream')
data = "abc" * 100000
def test_request_stream_method_view():
'''for self.is_request_stream = True'''
app = Sanic('test_request_stream_method_view')
class SimpleView(HTTPMethodView):
def get(self, request):
assert request.stream is None
return text('OK')
@stream_decorator
async def post(self, request):
assert isinstance(request.stream, asyncio.Queue)
result = ''
while True:
body = await request.stream.get()
@ -24,9 +31,28 @@ class SimpleView(HTTPMethodView):
result += body.decode('utf-8')
return text(result)
app.add_route(SimpleView.as_view(), '/method_view')
assert app.is_request_stream is True
request, response = app.test_client.get('/method_view')
assert response.status == 200
assert response.text == 'OK'
request, response = app.test_client.post('/method_view', data=data)
assert response.status == 200
assert response.text == data
def test_request_stream_app():
'''for self.is_request_stream = True'''
app = Sanic('test_request_stream_app')
@app.stream('/stream')
async def handler(request):
assert isinstance(request.stream, asyncio.Queue)
async def streaming(response):
while True:
body = await request.stream.get()
@ -35,14 +61,31 @@ async def handler(request):
response.write(body.decode('utf-8'))
return stream(streaming)
@app.get('/get')
async def get(request):
assert request.stream is None
return text('OK')
assert app.is_request_stream is True
request, response = app.test_client.get('/get')
assert response.status == 200
assert response.text == 'OK'
request, response = app.test_client.post('/stream', data=data)
assert response.status == 200
assert response.text == data
def test_request_stream_blueprint():
'''for self.is_request_stream = True'''
app = Sanic('test_request_stream_blueprint')
bp = Blueprint('test_blueprint_request_stream_blueprint')
@bp.stream('/bp_stream')
async def bp_stream(request):
assert isinstance(request.stream, asyncio.Queue)
result = ''
while True:
body = await request.stream.get()
@ -51,17 +94,35 @@ async def bp_stream(request):
result += body.decode('utf-8')
return text(result)
@bp.get('/bp_get')
async def bp_get(request):
assert request.stream is None
return text('OK')
app.blueprint(bp)
assert app.is_request_stream is True
request, response = app.test_client.get('/bp_get')
assert response.status == 200
assert response.text == 'OK'
request, response = app.test_client.post('/bp_stream', data=data)
assert response.status == 200
assert response.text == data
def test_request_stream_composition_view():
'''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')
async def post_handler(request):
assert isinstance(request.stream, asyncio.Queue)
result = ''
while True:
body = await request.stream.get()
@ -70,6 +131,91 @@ async def post_handler(request):
result += body.decode('utf-8')
return text(result)
view = CompositionView()
view.add(['GET'], get_handler)
view.add(['POST'], post_handler, stream=True)
app.add_route(view, '/composition_view')
assert app.is_request_stream is True
request, response = app.test_client.get('/composition_view')
assert response.status == 200
assert response.text == 'OK'
request, response = app.test_client.post('/composition_view', data=data)
assert response.status == 200
assert response.text == data
def test_request_stream():
'''test for complex application'''
bp = Blueprint('test_blueprint_request_stream')
app = Sanic('test_request_stream')
class SimpleView(HTTPMethodView):
def get(self, request):
assert request.stream is None
return text('OK')
@stream_decorator
async def post(self, request):
assert isinstance(request.stream, asyncio.Queue)
result = ''
while True:
body = await request.stream.get()
if body is None:
break
result += body.decode('utf-8')
return text(result)
@app.stream('/stream')
async def handler(request):
assert isinstance(request.stream, asyncio.Queue)
async def streaming(response):
while True:
body = await request.stream.get()
if body is None:
break
response.write(body.decode('utf-8'))
return stream(streaming)
@app.get('/get')
async def get(request):
assert request.stream is None
return text('OK')
@bp.stream('/bp_stream')
async def bp_stream(request):
assert isinstance(request.stream, asyncio.Queue)
result = ''
while True:
body = await request.stream.get()
if body is None:
break
result += body.decode('utf-8')
return text(result)
@bp.get('/bp_get')
async def bp_get(request):
assert request.stream is None
return text('OK')
def get_handler(request):
assert request.stream is None
return text('OK')
async def post_handler(request):
assert isinstance(request.stream, asyncio.Queue)
result = ''
while True:
body = await request.stream.get()
if body is None:
break
result += body.decode('utf-8')
return text(result)
app.add_route(SimpleView.as_view(), '/method_view')
@ -81,9 +227,7 @@ app.blueprint(bp)
app.add_route(view, '/composition_view')
def test_request_stream():
data = "abc" * 100000
assert app.is_request_stream is True
request, response = app.test_client.get('/method_view')
assert response.status == 200

View File

@ -28,12 +28,15 @@ def test_route_strict_slash():
@app.get('/get', strict_slashes=True)
def handler(request):
assert request.stream is None
return text('OK')
@app.post('/post/', strict_slashes=True)
def handler(request):
return text('OK')
assert app.is_request_stream is False
request, response = app.test_client.get('/get')
assert response.text == 'OK'

View File

@ -16,6 +16,7 @@ def test_methods(method):
class DummyView(HTTPMethodView):
async def get(self, request):
assert request.stream is None
return text('', headers={'method': 'GET'})
def post(self, request):
@ -37,6 +38,7 @@ def test_methods(method):
return text('', headers={'method': 'DELETE'})
app.add_route(DummyView.as_view(), '/')
assert app.is_request_stream is False
request, response = getattr(app.test_client, method.lower())('/')
assert response.headers['method'] == method
@ -79,6 +81,7 @@ def test_with_bp():
class DummyView(HTTPMethodView):
def get(self, request):
assert request.stream is None
return text('I am get method')
bp.add_route(DummyView.as_view(), '/')
@ -86,6 +89,7 @@ def test_with_bp():
app.blueprint(bp)
request, response = app.test_client.get('/')
assert app.is_request_stream is False
assert response.text == 'I am get method'
@ -227,10 +231,15 @@ def test_composition_view_runs_methods_as_expected(method):
app = Sanic('test_composition_view')
view = CompositionView()
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method'))
def first(request):
assert request.stream is None
return text('first method')
view.add(['GET', 'POST', 'PUT'], first)
view.add(['DELETE', 'PATCH'], lambda x: text('second method'))
app.add_route(view, '/')
assert app.is_request_stream is False
if method in ['GET', 'POST', 'PUT']:
request, response = getattr(app.test_client, method.lower())('/')