{"html_url": "https://github.com/simonw/datasette/issues/272#issuecomment-502393107", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 502393107, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMjM5MzEwNw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-15T19:25:54Z", "updated_at": "2019-06-19T01:20:14Z", "author_association": "OWNER", "body": "OK, time for a solid implementation plan.\r\n\r\nAs soon as https://github.com/django/asgiref/pull/92 is merged (hopefully very soon) the ASGI spec will have support for an optional `raw_path` - which means we can continue to use `table%2Fnames` with embedded `/` without being unable to tell if a path has been decoded or not.\r\n\r\nSteps to implement:\r\n\r\n## Refactor classes, then add .asgi() method to BaseView\r\n\r\nAdd a `.asgi(self, scope, receive, send)` method to my base view class. This will expose an ASGI interface to the outside world: the method itself will construct a request object and call the existing `.get()` method.\r\n\r\nMy only true shared base class is actually `RenderMixin` because the `IndexView` doesn't extend `BaseView`. I'm going to refactor the class hierarchy a bit here - `AsgiView` will be my top level class with the `.asgi()` method on it. `RenderMixin` will be renamed `BaseView(AsgiView)`, while existing `BaseView` will be renamed `DataView(BaseView)` since it mainly exists to introduce the handy `.data()` abstraction.\r\n\r\nSo...\r\n\r\n* `AsgiView` - has `.asgi()` method, extends Sanic `HTTPMethodView` (for the moment)\r\n* `BaseView(AsgiView)` - defines utility methods currently on `RenderMixin`\r\n* `IndexView(BaseView)` - the current `IndexView`\r\n* `DataView(BaseView)` - defines the utilities currently on `BaseView`, including `data()`\r\n* Everything else subclasses `DataView`\r\n\r\n## Extract routing logic out into a new `DatasetteView`\r\n\r\nI considered calling this `RouteView`, but one of the goals of this project is to allow other ASGI apps to import Datasette itself and reuse it as its own ASGI function.\r\n\r\nSo `DatasetteView` will subclass `BaseView` and will do all of the routing logic. That logic currently lives here:\r\n\r\nhttps://github.com/simonw/datasette/blob/aa911122feab13f8e65875c98edb00fd3832b7b8/datasette/app.py#L594-L640\r\n\r\n## For tests: Implement a version of app_client.get() that calls ASGI instead\r\n\r\nAlmost all of the unit tests currently use `app_client.get(\"/path...\")`. I want to be able to run tests against both ASGI and existing-Sanic, so for the moment I'm going to teach `app_client.get()` to use ASGI instead but only in the presence of a new environment variable. I can then have Travis run the tests twice - once with that environement variable and once without.\r\n\r\n## Make datasette serve --asgi run ASGI and uvicorn\r\n\r\nUvicorn supports Python 3.5 again as of https://github.com/encode/uvicorn/issues/330 - so it's going to be the new dependency for Datasette. \r\n\r\n## Do some comparative testing of ASGI and non-ASGI\r\n\r\nJust some sanity checking to make sure there aren't any weird issues.\r\n\r\n## Final step: refactor out Sanic\r\n\r\nHopefully this will just involve changes being made to the AsgiView base class, since that subclasses Sanic `HTTPMethodView`.\r\n\r\nBonus: It looks like dropping Sanic as a dependency in favour of Uvicorn should give us Windows support! https://github.com/encode/uvicorn/issues/82\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 324188953, "label": "Port Datasette to ASGI"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/272#issuecomment-503369834", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 503369834, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMzM2OTgzNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-19T01:26:24Z", "updated_at": "2019-06-19T01:26:24Z", "author_association": "OWNER", "body": "I need to be able to define the URL routes once and have them work for both Sanic and ASGI.\r\n\r\nI'm going to extract the web application bits out of the `Datasette` class into a `DatasetteServer` class. Then I can have a `add_route()` method on that class, then have `DatasetteSanic` and `DatasetteAsgi` subclasses which redefine that method.\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 324188953, "label": "Port Datasette to ASGI"}, "performed_via_github_app": null}