From fac8e9381500fc02cec99281122ee8e0c72fabe1 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Mon, 8 Jun 2020 20:40:00 -0700 Subject: [PATCH] request.url_vars property, closes #822 --- datasette/utils/asgi.py | 4 ++++ docs/internals.rst | 3 +++ docs/plugins.rst | 4 ++-- tests/plugins/my_plugin.py | 4 ++-- tests/test_internals_request.py | 17 +++++++++++++++++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/datasette/utils/asgi.py b/datasette/utils/asgi.py index 9e6c82dd..cdd6b148 100644 --- a/datasette/utils/asgi.py +++ b/datasette/utils/asgi.py @@ -32,6 +32,10 @@ class Request: (self.scheme, self.host, self.path, None, self.query_string, None) ) + @property + def url_vars(self): + return (self.scope.get("url_route") or {}).get("kwargs") or {} + @property def scheme(self): return self.scope.get("scheme") or "http" diff --git a/docs/internals.rst b/docs/internals.rst index b0096cfa..df21eb09 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -42,6 +42,9 @@ The request object is passed to various plugin hooks. It represents an incoming ``.args`` - MultiParams An object representing the parsed querystring parameters, see below. +``.url_vars`` - dictionary (str -> str) + Variables extracted from the URL path, if that path was defined using a regular expression. See :ref:`plugin_register_routes`. + ``.actor`` - dictionary (str -> Any) or None The currently authenticated actor (see :ref:`actors `), or ``None`` if the request is unauthenticated. diff --git a/docs/plugins.rst b/docs/plugins.rst index 465fcd52..17fd64df 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -850,8 +850,8 @@ Return a list of ``(regex, async_view_function)`` pairs, something like this: import html - async def hello_from(scope): - name = scope["url_route"]["kwargs"]["name"] + async def hello_from(request): + name = request.url_vars["name"] return Response.html("Hello from {}".format( html.escape(name) )) diff --git a/tests/plugins/my_plugin.py b/tests/plugins/my_plugin.py index 57803178..a0f7441b 100644 --- a/tests/plugins/my_plugin.py +++ b/tests/plugins/my_plugin.py @@ -152,8 +152,8 @@ def register_routes(): (await datasette.get_database().execute("select 1 + 1")).first()[0] ) - async def two(request, scope): - name = scope["url_route"]["kwargs"]["name"] + async def two(request): + name = request.url_vars["name"] greeting = request.args.get("greeting") return Response.text("{} {}".format(greeting, name)) diff --git a/tests/test_internals_request.py b/tests/test_internals_request.py index 433b23d5..8367a693 100644 --- a/tests/test_internals_request.py +++ b/tests/test_internals_request.py @@ -44,3 +44,20 @@ def test_request_args(): assert 2 == len(request.args) with pytest.raises(KeyError): request.args["missing"] + + +def test_request_url_vars(): + scope = { + "http_version": "1.1", + "method": "POST", + "path": "/", + "raw_path": b"/", + "query_string": b"", + "scheme": "http", + "type": "http", + "headers": [[b"content-type", b"application/x-www-form-urlencoded"]], + } + assert {} == Request(scope, None).url_vars + assert {"name": "cleo"} == Request( + dict(scope, url_route={"kwargs": {"name": "cleo"}}), None + ).url_vars