{"html_url": "https://github.com/simonw/datasette/issues/2168#issuecomment-1701823609", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2168", "id": 1701823609, "node_id": "IC_kwDOBm6k_c5lb8R5", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-08-31T21:43:06Z", "updated_at": "2023-08-31T21:44:13Z", "author_association": "OWNER", "body": "Not sure what to call this. Maybe `app_wrapper()`?\r\n\r\nOr perhaps it's simpler than that, something like this:\r\n\r\n```python\r\n@hookspec\r\ndef process_response(datasette, request, response):\r\n \"\"\"Last chance to modify the response before it is returned to the client\"\"\"\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1876353656, "label": "Consider a request/response wrapping hook slightly higher level than asgi_wrapper()"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/2168#issuecomment-1701826521", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2168", "id": 1701826521, "node_id": "IC_kwDOBm6k_c5lb8_Z", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-08-31T21:46:13Z", "updated_at": "2023-08-31T21:46:13Z", "author_association": "OWNER", "body": "This could even be a pair of hooks - `process_request()` and `process_response()`.\r\n\r\nOr could take a leaf from Django, which redesigned middleware to use this pattern instead:\r\n\r\n```python\r\ndef simple_middleware(get_response):\r\n # One-time configuration and initialization.\r\n def middleware(request):\r\n # Code to be executed for each request before\r\n # the view (and later middleware) are called.\r\n response = get_response(request)\r\n # Code to be executed for each request/response after\r\n # the view is called.\r\n return response\r\n return middleware\r\n```\r\nOr even borrow an idea from `pytest` where fixtures can `yield` in the middle, like this:\r\n```python\r\n@pytest.fixture\r\ndef sending_user(mail_admin):\r\n user = mail_admin.create_user()\r\n yield user\r\n mail_admin.delete_user(user)\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1876353656, "label": "Consider a request/response wrapping hook slightly higher level than asgi_wrapper()"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/2168#issuecomment-1701828197", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2168", "id": 1701828197, "node_id": "IC_kwDOBm6k_c5lb9Zl", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-08-31T21:48:00Z", "updated_at": "2023-08-31T21:48:57Z", "author_association": "OWNER", "body": "A pattern like this could be interesting:\r\n```python\r\nasync def my_middleware(datasette, request, get_response):\r\n # Mess with request here if neccessary\r\n response = await get_response(request)\r\n # mess with response\r\n return response\r\n```\r\nThe Django pattern is more complicated but does have that mechanism for running one-time configuration prior to defining the `middleware()` function, which is neat.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1876353656, "label": "Consider a request/response wrapping hook slightly higher level than asgi_wrapper()"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/2168#issuecomment-1701830241", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2168", "id": 1701830241, "node_id": "IC_kwDOBm6k_c5lb95h", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-08-31T21:50:18Z", "updated_at": "2023-08-31T21:50:18Z", "author_association": "OWNER", "body": "The hook could be called `register_middleware()` and could work like `register_routes()` and `register_commands()`:\r\n\r\n```python\r\n@hookspec\r\ndef register_middleware(datasette):\r\n \"\"\"Register middleware: returns a list of async def middleware functions\"\"\"\r\n\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1876353656, "label": "Consider a request/response wrapping hook slightly higher level than asgi_wrapper()"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/2168#issuecomment-1701831013", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2168", "id": 1701831013, "node_id": "IC_kwDOBm6k_c5lb-Fl", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-08-31T21:51:12Z", "updated_at": "2023-08-31T21:52:15Z", "author_association": "OWNER", "body": "Need to make sure the design of this takes streaming responses into account. Those could be pretty tricky here.\r\n\r\nI nice thing about `asgi_wrapper()` is that it handles those already.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1876353656, "label": "Consider a request/response wrapping hook slightly higher level than asgi_wrapper()"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/2168#issuecomment-1712897194", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2168", "id": 1712897194, "node_id": "IC_kwDOBm6k_c5mGLyq", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-09-10T17:54:07Z", "updated_at": "2023-09-10T17:54:07Z", "author_association": "OWNER", "body": "This looks relevant:\r\n\r\nhttps://pluggy.readthedocs.io/en/stable/#wrappers\r\n\r\n> A *hookimpl* can be marked with the `\"wrapper\"` option, which indicates that the function will be called to *wrap* (or surround) all other normal *hookimpl* calls. A *hook wrapper* can thus execute some code ahead and after the execution of all corresponding non-wrappper *hookimpls*.\r\n\r\nThis could be the perfect mechanism for implementing this hook, although I still need to figure out how it interacts with streaming.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1876353656, "label": "Consider a request/response wrapping hook slightly higher level than asgi_wrapper()"}, "performed_via_github_app": null}