From 5c5e9b365790d7c75cf2611e650d1013f587d316 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Thu, 31 Mar 2022 19:01:58 -0700 Subject: [PATCH] Request.fake(... url_vars), plus .fake() is now documented Also made 'from datasette import Request' shortcut work. Closes #1697 --- datasette/__init__.py | 2 +- datasette/utils/asgi.py | 4 +++- docs/internals.rst | 27 +++++++++++++++++++++++++++ tests/test_internals_request.py | 7 +++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/datasette/__init__.py b/datasette/__init__.py index faa36051..ea10c13d 100644 --- a/datasette/__init__.py +++ b/datasette/__init__.py @@ -1,5 +1,5 @@ from datasette.version import __version_info__, __version__ # noqa -from datasette.utils.asgi import Forbidden, NotFound, Response # noqa +from datasette.utils.asgi import Forbidden, NotFound, Request, Response # noqa from datasette.utils import actor_matches_allow # noqa from .hookspecs import hookimpl # noqa from .hookspecs import hookspec # noqa diff --git a/datasette/utils/asgi.py b/datasette/utils/asgi.py index cd3ec654..8a2fa060 100644 --- a/datasette/utils/asgi.py +++ b/datasette/utils/asgi.py @@ -118,7 +118,7 @@ class Request: return dict(parse_qsl(body.decode("utf-8"), keep_blank_values=True)) @classmethod - def fake(cls, path_with_query_string, method="GET", scheme="http"): + def fake(cls, path_with_query_string, method="GET", scheme="http", url_vars=None): """Useful for constructing Request objects for tests""" path, _, query_string = path_with_query_string.partition("?") scope = { @@ -130,6 +130,8 @@ class Request: "scheme": scheme, "type": "http", } + if url_vars: + scope["url_route"] = {"kwargs": url_vars} return cls(scope, None) diff --git a/docs/internals.rst b/docs/internals.rst index 0ba3fa69..854b96f8 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -60,6 +60,33 @@ The object also has two awaitable methods: ``await request.post_body()`` - bytes Returns the un-parsed body of a request submitted by ``POST`` - useful for things like incoming JSON data. +And a class method that can be used to create fake request objects for use in tests: + +``fake(path_with_query_string, method="GET", scheme="http", url_vars=None)`` + Returns a ``Request`` instance for the specified path and method. For example: + + .. code-block:: python + + from datasette import Request + from pprint import pprint + + request = Request.fake("/fixtures/facetable/", url_vars={ + "database": "fixtures", + "table": "facetable" + }) + pprint(request.scope) + + This outputs:: + + {'http_version': '1.1', + 'method': 'GET', + 'path': '/fixtures/facetable/', + 'query_string': b'', + 'raw_path': b'/fixtures/facetable/', + 'scheme': 'http', + 'type': 'http', + 'url_route': {'kwargs': {'database': 'fixtures', 'table': 'facetable'}}} + .. _internals_multiparams: The MultiParams class diff --git a/tests/test_internals_request.py b/tests/test_internals_request.py index 44aaa153..d1ca1f46 100644 --- a/tests/test_internals_request.py +++ b/tests/test_internals_request.py @@ -75,6 +75,13 @@ def test_request_args(): request.args["missing"] +def test_request_fake_url_vars(): + request = Request.fake("/") + assert request.url_vars == {} + request = Request.fake("/", url_vars={"database": "fixtures"}) + assert request.url_vars == {"database": "fixtures"} + + def test_request_repr(): request = Request.fake("/foo?multi=1&multi=2&single=3") assert (