is_request_stream for CompositionView and HTTPMethodView
This commit is contained in:
parent
15ad07f03d
commit
4d4f38fb35
15
sanic/app.py
15
sanic/app.py
|
@ -131,7 +131,8 @@ class Sanic:
|
||||||
self.is_request_stream = True
|
self.is_request_stream = True
|
||||||
|
|
||||||
def response(handler):
|
def response(handler):
|
||||||
handler.is_stream = stream
|
if stream:
|
||||||
|
handler.is_stream = stream
|
||||||
self.router.add(uri=uri, methods=methods, handler=handler,
|
self.router.add(uri=uri, methods=methods, handler=handler,
|
||||||
host=host, strict_slashes=strict_slashes)
|
host=host, strict_slashes=strict_slashes)
|
||||||
return handler
|
return handler
|
||||||
|
@ -187,20 +188,28 @@ class Sanic:
|
||||||
:param host:
|
:param host:
|
||||||
: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()
|
||||||
|
|
||||||
for method in HTTP_METHODS:
|
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)
|
methods.add(method)
|
||||||
|
if hasattr(_handler, 'is_stream'):
|
||||||
|
stream = True
|
||||||
|
|
||||||
# handle composition view differently
|
# handle composition view differently
|
||||||
if isinstance(handler, CompositionView):
|
if isinstance(handler, CompositionView):
|
||||||
methods = handler.handlers.keys()
|
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,
|
self.route(uri=uri, methods=methods, host=host,
|
||||||
strict_slashes=strict_slashes)(handler)
|
strict_slashes=strict_slashes, stream=stream)(handler)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
# Decorator
|
# Decorator
|
||||||
|
|
|
@ -355,4 +355,4 @@ class Router:
|
||||||
if (hasattr(handler, 'view_class') and
|
if (hasattr(handler, 'view_class') and
|
||||||
hasattr(handler.view_class, request.method.lower())):
|
hasattr(handler.view_class, request.method.lower())):
|
||||||
handler = getattr(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')
|
||||||
|
|
|
@ -89,7 +89,8 @@ class CompositionView:
|
||||||
self.handlers = {}
|
self.handlers = {}
|
||||||
|
|
||||||
def add(self, methods, handler, stream=False):
|
def add(self, methods, handler, stream=False):
|
||||||
handler.is_stream = stream
|
if stream:
|
||||||
|
handler.is_stream = stream
|
||||||
for method in methods:
|
for method in methods:
|
||||||
if method not in HTTP_METHODS:
|
if method not in HTTP_METHODS:
|
||||||
raise InvalidUsage(
|
raise InvalidUsage(
|
||||||
|
|
|
@ -17,10 +17,12 @@ def test_bp():
|
||||||
|
|
||||||
@bp.route('/')
|
@bp.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
assert request.stream is None
|
||||||
return text('Hello')
|
return text('Hello')
|
||||||
|
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
request, response = app.test_client.get('/')
|
request, response = app.test_client.get('/')
|
||||||
|
assert app.is_request_stream is False
|
||||||
|
|
||||||
assert response.text == 'Hello'
|
assert response.text == 'Hello'
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import asyncio
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.views import CompositionView
|
from sanic.views import CompositionView
|
||||||
|
@ -5,17 +6,86 @@ from sanic.views import HTTPMethodView
|
||||||
from sanic.views import stream as stream_decorator
|
from sanic.views import stream as stream_decorator
|
||||||
from sanic.response import stream, text
|
from sanic.response import stream, text
|
||||||
|
|
||||||
bp = Blueprint('test_blueprint_request_stream')
|
data = "abc" * 100000
|
||||||
app = Sanic('test_request_stream')
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleView(HTTPMethodView):
|
def test_request_stream_method_view():
|
||||||
|
'''for self.is_request_stream = True'''
|
||||||
|
|
||||||
def get(self, request):
|
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()
|
||||||
|
if body is None:
|
||||||
|
break
|
||||||
|
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()
|
||||||
|
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')
|
return text('OK')
|
||||||
|
|
||||||
@stream_decorator
|
assert app.is_request_stream is True
|
||||||
async def post(self, request):
|
|
||||||
|
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 = ''
|
result = ''
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
|
@ -24,66 +94,140 @@ class SimpleView(HTTPMethodView):
|
||||||
result += body.decode('utf-8')
|
result += body.decode('utf-8')
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
|
@bp.get('/bp_get')
|
||||||
|
async def bp_get(request):
|
||||||
|
assert request.stream is None
|
||||||
|
return text('OK')
|
||||||
|
|
||||||
@app.stream('/stream')
|
app.blueprint(bp)
|
||||||
async def handler(request):
|
|
||||||
async def streaming(response):
|
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:
|
while True:
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
result += body.decode('utf-8')
|
||||||
return stream(streaming)
|
return text(result)
|
||||||
|
|
||||||
|
view = CompositionView()
|
||||||
|
view.add(['GET'], get_handler)
|
||||||
|
view.add(['POST'], post_handler, stream=True)
|
||||||
|
app.add_route(view, '/composition_view')
|
||||||
|
|
||||||
@app.get('/get')
|
assert app.is_request_stream is True
|
||||||
async def get(request):
|
|
||||||
return text('OK')
|
|
||||||
|
|
||||||
|
request, response = app.test_client.get('/composition_view')
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.text == 'OK'
|
||||||
|
|
||||||
@bp.stream('/bp_stream')
|
request, response = app.test_client.post('/composition_view', data=data)
|
||||||
async def bp_stream(request):
|
assert response.status == 200
|
||||||
result = ''
|
assert response.text == data
|
||||||
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):
|
|
||||||
return text('OK')
|
|
||||||
|
|
||||||
|
|
||||||
def get_handler(request):
|
|
||||||
return text('OK')
|
|
||||||
|
|
||||||
|
|
||||||
async def post_handler(request):
|
|
||||||
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')
|
|
||||||
|
|
||||||
view = CompositionView()
|
|
||||||
view.add(['GET'], get_handler)
|
|
||||||
view.add(['POST'], post_handler, stream=True)
|
|
||||||
|
|
||||||
app.blueprint(bp)
|
|
||||||
|
|
||||||
app.add_route(view, '/composition_view')
|
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream():
|
def test_request_stream():
|
||||||
data = "abc" * 100000
|
'''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')
|
||||||
|
|
||||||
|
view = CompositionView()
|
||||||
|
view.add(['GET'], get_handler)
|
||||||
|
view.add(['POST'], post_handler, stream=True)
|
||||||
|
|
||||||
|
app.blueprint(bp)
|
||||||
|
|
||||||
|
app.add_route(view, '/composition_view')
|
||||||
|
|
||||||
|
assert app.is_request_stream is True
|
||||||
|
|
||||||
request, response = app.test_client.get('/method_view')
|
request, response = app.test_client.get('/method_view')
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
|
@ -28,12 +28,15 @@ def test_route_strict_slash():
|
||||||
|
|
||||||
@app.get('/get', strict_slashes=True)
|
@app.get('/get', strict_slashes=True)
|
||||||
def handler(request):
|
def handler(request):
|
||||||
|
assert request.stream is None
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
|
||||||
@app.post('/post/', strict_slashes=True)
|
@app.post('/post/', strict_slashes=True)
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
|
||||||
|
assert app.is_request_stream is False
|
||||||
|
|
||||||
request, response = app.test_client.get('/get')
|
request, response = app.test_client.get('/get')
|
||||||
assert response.text == 'OK'
|
assert response.text == 'OK'
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ def test_methods(method):
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
|
|
||||||
async def get(self, request):
|
async def get(self, request):
|
||||||
|
assert request.stream is None
|
||||||
return text('', headers={'method': 'GET'})
|
return text('', headers={'method': 'GET'})
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
|
@ -37,6 +38,7 @@ def test_methods(method):
|
||||||
return text('', headers={'method': 'DELETE'})
|
return text('', headers={'method': 'DELETE'})
|
||||||
|
|
||||||
app.add_route(DummyView.as_view(), '/')
|
app.add_route(DummyView.as_view(), '/')
|
||||||
|
assert app.is_request_stream is False
|
||||||
|
|
||||||
request, response = getattr(app.test_client, method.lower())('/')
|
request, response = getattr(app.test_client, method.lower())('/')
|
||||||
assert response.headers['method'] == method
|
assert response.headers['method'] == method
|
||||||
|
@ -79,6 +81,7 @@ def test_with_bp():
|
||||||
class DummyView(HTTPMethodView):
|
class DummyView(HTTPMethodView):
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
assert request.stream is None
|
||||||
return text('I am get method')
|
return text('I am get method')
|
||||||
|
|
||||||
bp.add_route(DummyView.as_view(), '/')
|
bp.add_route(DummyView.as_view(), '/')
|
||||||
|
@ -86,6 +89,7 @@ def test_with_bp():
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
request, response = app.test_client.get('/')
|
request, response = app.test_client.get('/')
|
||||||
|
|
||||||
|
assert app.is_request_stream is False
|
||||||
assert response.text == 'I am get method'
|
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')
|
app = Sanic('test_composition_view')
|
||||||
|
|
||||||
view = CompositionView()
|
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'))
|
view.add(['DELETE', 'PATCH'], lambda x: text('second method'))
|
||||||
|
|
||||||
app.add_route(view, '/')
|
app.add_route(view, '/')
|
||||||
|
assert app.is_request_stream is False
|
||||||
|
|
||||||
if method in ['GET', 'POST', 'PUT']:
|
if method in ['GET', 'POST', 'PUT']:
|
||||||
request, response = getattr(app.test_client, method.lower())('/')
|
request, response = getattr(app.test_client, method.lower())('/')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user