Expose scope parameter in request object (#2432)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
This commit is contained in:
parent
78b6723149
commit
5d683c6ea4
|
@ -2,6 +2,8 @@ import sys
|
||||||
|
|
||||||
from typing import Any, AnyStr, TypeVar, Union
|
from typing import Any, AnyStr, TypeVar, Union
|
||||||
|
|
||||||
|
from sanic.models.asgi import ASGIScope
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8):
|
||||||
from asyncio import BaseTransport
|
from asyncio import BaseTransport
|
||||||
|
@ -17,6 +19,8 @@ else:
|
||||||
from typing import Protocol
|
from typing import Protocol
|
||||||
|
|
||||||
class TransportProtocol(Protocol):
|
class TransportProtocol(Protocol):
|
||||||
|
scope: ASGIScope
|
||||||
|
|
||||||
def get_protocol(self):
|
def get_protocol(self):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ from typing import (
|
||||||
|
|
||||||
from sanic_routing.route import Route # type: ignore
|
from sanic_routing.route import Route # type: ignore
|
||||||
|
|
||||||
|
from sanic.models.asgi import ASGIScope
|
||||||
from sanic.models.http_types import Credentials
|
from sanic.models.http_types import Credentials
|
||||||
|
|
||||||
|
|
||||||
|
@ -831,6 +832,21 @@ class Request:
|
||||||
view_name, _external=True, _scheme=scheme, _server=netloc, **kwargs
|
view_name, _external=True, _scheme=scheme, _server=netloc, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scope(self) -> ASGIScope:
|
||||||
|
"""
|
||||||
|
:return: The ASGI scope of the request.
|
||||||
|
If the app isn't an ASGI app, then raises an exception.
|
||||||
|
:rtype: Optional[ASGIScope]
|
||||||
|
"""
|
||||||
|
if not self.app.asgi:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"App isn't running in ASGI mode. "
|
||||||
|
"Scope is only available for ASGI apps."
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.transport.scope
|
||||||
|
|
||||||
|
|
||||||
class File(NamedTuple):
|
class File(NamedTuple):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -191,3 +191,29 @@ def test_bad_url_parse():
|
||||||
Mock(),
|
Mock(),
|
||||||
Mock(),
|
Mock(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_request_scope_raises_exception_when_no_asgi():
|
||||||
|
app = Sanic("no_asgi")
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def get(request):
|
||||||
|
return request.scope
|
||||||
|
|
||||||
|
request, response = app.test_client.get("/")
|
||||||
|
assert response.status == 500
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
_ = request.scope
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_request_scope_is_not_none_when_running_in_asgi(app):
|
||||||
|
@app.get("/")
|
||||||
|
async def get(request):
|
||||||
|
return response.empty()
|
||||||
|
|
||||||
|
request, _ = await app.asgi_client.get("/")
|
||||||
|
|
||||||
|
assert request.scope is not None
|
||||||
|
assert request.scope["method"].lower() == "get"
|
||||||
|
assert request.scope["path"].lower() == "/"
|
||||||
|
|
|
@ -1051,7 +1051,6 @@ async def test_post_form_urlencoded_keep_blanks_asgi(app):
|
||||||
assert request.form.get("test") == "" # For request.parsed_form
|
assert request.form.get("test") == "" # For request.parsed_form
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_post_form_urlencoded_drop_blanks(app):
|
def test_post_form_urlencoded_drop_blanks(app):
|
||||||
@app.route("/", methods=["POST"])
|
@app.route("/", methods=["POST"])
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -1066,6 +1065,7 @@ def test_post_form_urlencoded_drop_blanks(app):
|
||||||
|
|
||||||
assert "test" not in request.form.keys()
|
assert "test" not in request.form.keys()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_post_form_urlencoded_drop_blanks_asgi(app):
|
async def test_post_form_urlencoded_drop_blanks_asgi(app):
|
||||||
@app.route("/", methods=["POST"])
|
@app.route("/", methods=["POST"])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user