{"html_url": "https://github.com/simonw/datasette/issues/272#issuecomment-504857097", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504857097, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDg1NzA5Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-24T04:54:15Z", "updated_at": "2019-06-24T04:54:15Z", "author_association": "OWNER", "body": "I wrote about this on my blog: https://simonwillison.net/2019/Jun/23/datasette-asgi/", "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-504844339", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504844339, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDg0NDMzOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-24T03:33:06Z", "updated_at": "2019-06-24T03:33:06Z", "author_association": "OWNER", "body": "It's alive! Here's the first deployed version: https://a559123.datasette.io/\r\n\r\nYou can confirm it's running under ASGI by viewing https://a559123.datasette.io/-/versions and looking for the `\"asgi\"` key.\r\n\r\nCompare to the last version of master running on Sanic here: http://aa91112.datasette.io/", "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-504761039", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504761039, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDc2MTAzOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T15:15:41Z", "updated_at": "2019-06-23T15:18:36Z", "author_association": "OWNER", "body": "And now the tests are all passing!\r\n\r\nStill to do:\r\n\r\n* Use `raw_path` so table names containing `/` can work correctly\r\n* Get ?_trace=1 working again\r\n* Replacement for `@app.listener(\"before_server_start\")`\r\n* Replace Sanic request object with my own request class, so I can remove Sanic dependency", "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-504761165", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504761165, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDc2MTE2NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T15:17:07Z", "updated_at": "2019-06-23T15:17:07Z", "author_association": "OWNER", "body": "I'm going to move the remaining work into a pull request.", "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-504716988", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504716988, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDcxNjk4OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T03:43:46Z", "updated_at": "2019-06-23T15:15:26Z", "author_association": "OWNER", "body": "OK, it's beginning to shape up now. Next steps:\r\n\r\n- [x] Static file support (including for plugins) - plus tests\r\n- [x] Streaming support so the CSV tests will pass\r\n- [x] Ability to download the database file\r\n- [x] Implement missing-slash redirects\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-504760061", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504760061, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDc2MDA2MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T15:02:52Z", "updated_at": "2019-06-23T15:02:52Z", "author_association": "OWNER", "body": "Tests are failing on Python 3.5: https://travis-ci.org/simonw/datasette/jobs/549380098 - error is `TypeError: the JSON object must be str, not 'bytes'`", "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-504759842", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504759842, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDc1OTg0Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T15:00:06Z", "updated_at": "2019-06-23T15:00:06Z", "author_association": "OWNER", "body": "I also need to actually take advantage of `raw_path` such that pages like https://fivethirtyeight.datasettes.com/fivethirtyeight/twitter-ratio%2Fsenators can be correctly served.", "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-504759683", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504759683, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDc1OTY4Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T14:57:50Z", "updated_at": "2019-06-23T14:57:50Z", "author_association": "OWNER", "body": "All of the tests are now passing!\r\n\r\nI still need a solution for this:\r\n\r\nhttps://github.com/simonw/datasette/blob/5bd510b01adae3f719e4426b9bfbc346a946ba5c/datasette/app.py#L706-L714\r\n\r\nI think the answer is ASGI lifespan, which is supported by Uvicorn. https://asgi.readthedocs.io/en/latest/specs/lifespan.html#startup", "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-504754552", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504754552, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDc1NDU1Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T13:53:39Z", "updated_at": "2019-06-23T13:53:39Z", "author_association": "OWNER", "body": "Next test to fix (because by new test harness doesn't actually obey the `allow_redirects=` parameter):\r\n```\r\n_____________ test_database_page_redirects_with_url_hash _____________\r\n\r\napp_client_with_hash = \r\n\r\n def test_database_page_redirects_with_url_hash(app_client_with_hash):\r\n response = app_client_with_hash.get(\"/fixtures\", allow_redirects=False)\r\n assert response.status == 302\r\n response = app_client_with_hash.get(\"/fixtures\")\r\n> assert \"fixtures\" in response.text\r\nE AssertionError: assert 'fixtures' in ''\r\nE + where '' = .text\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-504754433", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504754433, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDc1NDQzMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T13:51:53Z", "updated_at": "2019-06-23T13:51:53Z", "author_association": "OWNER", "body": "CSV tests all pass as of https://github.com/simonw/datasette/commit/ff9efa668ebc33f17ef9b30139960e29906a18fb\r\n\r\nThis code could be a lot neater though. At the very least I'm going to refactor `datasette/utils.py` into a `datasette/utils` package and put all of my new ASGI utilities in `datasette/utils/asgi.py`\r\n\r\nThe way I implemented streaming on top of a writer object (inspired by Sanic) is a bit of a weird hack. I think I'd rather use an abstraction where my view functions can yield chunks of body data.", "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-504711468", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504711468, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDcxMTQ2OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T01:36:33Z", "updated_at": "2019-06-23T01:36:33Z", "author_association": "OWNER", "body": "Published an in-progress demo:\r\n\r\n datasette publish now fixtures.db -n datasette-asgi-early-demo --branch=asgi\r\n\r\nHere it is: https://datasette-asgi-early-demo-qahhxctqpw.now.sh/", "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-504710331", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504710331, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDcxMDMzMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-23T01:08:45Z", "updated_at": "2019-06-23T01:08:45Z", "author_association": "OWNER", "body": "Lots still to do:\r\n\r\n* Static files are not being served\r\n* Streaming CSV files don't work\r\n* Tests all fail\r\n* Some URLs (e.g. the 'next' link on tables) are incorrect\r\n\r\nI'm going to work on getting the unit test framework to be ASGI-compatible next.", "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-504697742", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 504697742, "node_id": "MDEyOklzc3VlQ29tbWVudDUwNDY5Nzc0Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-22T20:55:59Z", "updated_at": "2019-06-22T20:56:22Z", "author_association": "OWNER", "body": "Getting this to work with both Sanic AND ASGI at the same time (via the classes described previously with an `--asgi` command-line option) is proving way trickier than I expected, mainly because of the complexity of [the current Datasette.app() method](https://github.com/simonw/datasette/blob/35429f90894321eda7f2db31b9ea7976f31f73ac/datasette/app.py#L545-L721).\r\n\r\nI'm going to drop the compatibility path for a bit and see if I can make progress on a pure-ASGI port.", "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} {"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-503351966", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 503351966, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMzM1MTk2Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-18T23:45:17Z", "updated_at": "2019-06-18T23:45:17Z", "author_association": "OWNER", "body": "Uvicorn 0.8.1 is our and supports `raw_path`!", "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-503195217", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 503195217, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMzE5NTIxNw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-18T15:46:31Z", "updated_at": "2019-06-18T15:54:18Z", "author_association": "OWNER", "body": "How should file serving work?\r\n\r\nStarlette and Sanic both use `aiofiles` - https://github.com/Tinche/aiofiles - which is a small wrapper around file operations which runs them all in an executor thread. It doesn't have any C dependencies so it looks like a good option. [Quart uses it too](https://gitlab.com/pgjones/quart/blob/317562ea660edb7159efc20fa57b95223d408ea0/quart/wrappers/response.py#L122-169).\r\n\r\n`aiohttp` does things differently: it has [an implementation based on sendfile](https://github.com/aio-libs/aiohttp/blob/7a324fd46ff7dc9bb0bb1bc5afb326e04cf7cef0/aiohttp/web_fileresponse.py#L46-L122) with [an alternative fallback](https://github.com/aio-libs/aiohttp/blob/7a324fd46ff7dc9bb0bb1bc5afb326e04cf7cef0/aiohttp/web_fileresponse.py#L175-L200) which reads chunks from a file object and yields them one chunk at a time, \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-502466466", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 502466466, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMjQ2NjQ2Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-16T16:28:10Z", "updated_at": "2019-06-16T16:28:10Z", "author_association": "OWNER", "body": "I have an open pull request to Uvicorn with an implementation of `raw_path`: https://github.com/encode/uvicorn/pull/372", "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-502401078", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 502401078, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMjQwMTA3OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-15T21:35:26Z", "updated_at": "2019-06-15T21:35:26Z", "author_association": "OWNER", "body": "Started sketching out the router in the `asgi` branch: https://github.com/simonw/datasette/commit/7cdc55c6836fe246b1ca8a13a965a39991c9ffec", "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-502395689", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 502395689, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMjM5NTY4OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-15T20:05:26Z", "updated_at": "2019-06-15T20:05:26Z", "author_association": "OWNER", "body": "For the routing component: I'm going to base my implementation on the one from Django Channels.\r\n\r\nhttps://github.com/django/channels/blob/507cb54fcb36df63282dd19653ea743036e7d63c/channels/routing.py#L123-L149\r\n\r\nDocumented here: https://channels.readthedocs.io/en/latest/topics/routing.html#urlrouter\r\n\r\nParticularly relevant: my view classes need access to the components that were already parsed out of the URL by the router. I'm going to copy the Django Channels mechanism of stashing those in `scope[\"url_route\"][\"kwargs\"]`.", "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-502394420", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 502394420, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMjM5NDQyMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-15T19:45:46Z", "updated_at": "2019-06-15T19:45:46Z", "author_association": "OWNER", "body": "For reference, here's some WIP code I wrote last year against the old ASGI 2 spec: https://github.com/simonw/datasette/commit/4fd36ba2f3f91da7258859808616078e3464fb97", "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-502393267", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 502393267, "node_id": "MDEyOklzc3VlQ29tbWVudDUwMjM5MzI2Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-06-15T19:28:27Z", "updated_at": "2019-06-15T19:28:27Z", "author_association": "OWNER", "body": "I'll probably revert 9fdb47ca952b93b7b60adddb965ea6642b1ff523 from https://github.com/simonw/datasette/issues/272#issuecomment-494192779 since I won't need it now that ASGI is getting `raw_path` support.", "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-494297022", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 494297022, "node_id": "MDEyOklzc3VlQ29tbWVudDQ5NDI5NzAyMg==", "user": {"value": 647359, "label": "tomchristie"}, "created_at": "2019-05-21T08:39:17Z", "updated_at": "2019-05-21T08:39:17Z", "author_association": "NONE", "body": "Useful context stuff:\r\n\r\n> ASGI decodes %2F encoded slashes in URLs automatically\r\n\r\n`raw_path` for ASGI looks to be under consideration: https://github.com/django/asgiref/issues/87\r\n\r\n> uvicorn doesn't support Python 3.5\r\n\r\nThat was an issue specifically against the <=3.5.2 minor point releases of Python, now resolved: https://github.com/encode/uvicorn/issues/330 \ud83d\udc4d\r\n\r\n> Starlette for things like form parsing - but it's 3.6+ only!\r\n\r\nYeah - the bits that require 3.6 are anywhere with the \"async for\" syntax. If it wasn't for that I'd downport it, but that one's a pain. It's the one bit of syntax to watch out for if you're looking to bring any bits of implementation across to Datasette.\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-494192779", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 494192779, "node_id": "MDEyOklzc3VlQ29tbWVudDQ5NDE5Mjc3OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-05-21T00:10:47Z", "updated_at": "2019-05-21T00:10:47Z", "author_association": "OWNER", "body": "https://github.com/simonw/datasette/commit/9fdb47ca952b93b7b60adddb965ea6642b1ff523 added `decode_path_component()` and `encode_path_component()` functions because ASGI decodes %2F encoded slashes in URLs automatically. The new encoding scheme looks like this:\r\n\r\n \"table/and/slashes\" => \"tableU+002FandU+002Fslashes\"\r\n \"~table\" => \"U+007Etable\"\r\n \"+bobcats!\" => \"U+002Bbobcats!\"\r\n \"U+007Etable\" => \"UU+002B007Etable\"\r\n\r\nFor background see this comment: https://github.com/django/asgiref/issues/51#issuecomment-450603464", "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-494192163", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 494192163, "node_id": "MDEyOklzc3VlQ29tbWVudDQ5NDE5MjE2Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-05-21T00:07:25Z", "updated_at": "2019-05-21T00:07:25Z", "author_association": "OWNER", "body": "Bah, I'd much rather depend on Starlette for things like form parsing - but it's 3.6+ only!\r\n\r\nhttps://github.com/encode/starlette/blob/ab86530eddfcf56e0f7e5ca56f6ab69c15594a7d/setup.py#L39\r\n\r\nMaybe I could require Python 3.6 or higher if you want to handle POST data? This would make my internals far too complicated though I think.", "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-494191738", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 494191738, "node_id": "MDEyOklzc3VlQ29tbWVudDQ5NDE5MTczOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-05-21T00:05:02Z", "updated_at": "2019-05-21T00:05:02Z", "author_association": "OWNER", "body": "While I'm not depending on Starlette any more I will need to instead depend on https://github.com/andrew-d/python-multipart for POST form parsing - as used by Starlette here https://github.com/encode/starlette/blob/ab86530eddfcf56e0f7e5ca56f6ab69c15594a7d/starlette/requests.py#L178-L193", "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-494191378", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 494191378, "node_id": "MDEyOklzc3VlQ29tbWVudDQ5NDE5MTM3OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-05-21T00:02:48Z", "updated_at": "2019-05-21T00:02:48Z", "author_association": "OWNER", "body": "I said earlier that I only need to support GET - I actually need to be able to support POST too, mainly to support plugins (e.g. a plugin that allows authenticated login before you can view Datasette, but potentially also plugins that let you write data directly to SQLite as well).", "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-494190922", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 494190922, "node_id": "MDEyOklzc3VlQ29tbWVudDQ5NDE5MDkyMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-05-21T00:00:40Z", "updated_at": "2019-05-21T00:01:09Z", "author_association": "OWNER", "body": "Wow, this issue has been open for a full year now!\r\n\r\nI've been thinking about this a lot. I've decided I want Datasette to use ASGI 3.0 internally with no dependencies on anything else - then I want the option to run Datasette under both daphne and uvicorn - because uvicorn doesn't support Python 3.5 but Datasette still needs to (primarily for Glitch), and daphne works with 3.5.\r\n\r\nSo I'm going to try to go the following route:\r\n\r\n- Every Datasette view becomes an ASGI app\r\n- The Datasette application itself is an ASGI app that routes to those views\r\n- When you `pip install datasette` you get Daphne as a dependency (I'd like you to be able to opt-out of installing Daphne, I'm not yet sure how that would work)\r\n- A new `asgi_serve` plugin hook allows a plugin to serve Datasette using uvicorn (or hypercorn) instead", "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-418695115", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 418695115, "node_id": "MDEyOklzc3VlQ29tbWVudDQxODY5NTExNQ==", "user": {"value": 647359, "label": "tomchristie"}, "created_at": "2018-09-05T11:21:25Z", "updated_at": "2018-09-05T11:21:25Z", "author_association": "NONE", "body": "Some notes:\r\n\r\n* Starlette just got a bump to 0.3.0 - there's some renamings in there. It's got enough functionality now that you can treat it either as a framework or as a toolkit. Either way the component design is all just *here's an ASGI app* all the way through.\r\n* Uvicorn got a bump to 0.3.3 - Removed some cyclical references that were causing garbage collection to impact performance. Ought to be a decent speed bump.\r\n* Wrt. passing config - Either use a single envvar that points to a config, or use multiple envvars for the config. Uvicorn could get a flag to read a `.env` file, but I don't see ASGI itself having a specific interface there.", "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-408478935", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 408478935, "node_id": "MDEyOklzc3VlQ29tbWVudDQwODQ3ODkzNQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-07-27T17:00:08Z", "updated_at": "2018-07-27T17:00:08Z", "author_association": "OWNER", "body": "Refs \r\nhttps://github.com/encode/uvicorn/issues/168", "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-408105251", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 408105251, "node_id": "MDEyOklzc3VlQ29tbWVudDQwODEwNTI1MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-07-26T13:54:06Z", "updated_at": "2018-07-26T13:54:06Z", "author_association": "OWNER", "body": "Tom shipped my fix for that bug already, so https://datasette-starlette-demo.now.sh/ is now serving CSS!", "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-408093480", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 408093480, "node_id": "MDEyOklzc3VlQ29tbWVudDQwODA5MzQ4MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-07-26T13:15:55Z", "updated_at": "2018-07-26T13:46:40Z", "author_association": "OWNER", "body": "I'm now hacking around with an initial version of this in the [starlette branch](https://github.com/simonw/datasette/tree/starlette).\r\n\r\nHere's my work in progress, deployed using `datasette publish now fixtures.db -n datasette-starlette-demo --branch=starlette --extra-options=\"--asgi\"`\r\n\r\nhttps://datasette-starlette-demo.now.sh/\r\n\r\nLots more work to do - the CSS isn't being served correctly for example, it's showing this error when I hit `/-/static/app.css`:\r\n```\r\nINFO: 127.0.0.1 - \"GET /-/static/app.css HTTP/1.1\" 200\r\nERROR: Exception in ASGI application\r\nTraceback (most recent call last):\r\n File \"/Users/simonw/Dropbox/Development/datasette/venv/lib/python3.6/site-packages/uvicorn/protocols/http/httptools_impl.py\", line 363, in run_asgi\r\n result = await asgi(self.receive, self.send)\r\n File \"/Users/simonw/Dropbox/Development/datasette/venv/lib/python3.6/site-packages/starlette/staticfiles.py\", line 91, in __call__\r\n await response(receive, send)\r\n File \"/Users/simonw/Dropbox/Development/datasette/venv/lib/python3.6/site-packages/starlette/response.py\", line 180, in __call__\r\n {\"type\": \"http.response.body\", \"body\": chunk, \"more_body\": False}\r\n File \"/Users/simonw/Dropbox/Development/datasette/venv/lib/python3.6/site-packages/uvicorn/protocols/http/httptools_impl.py\", line 483, in send\r\n raise RuntimeError(\"Response content shorter than Content-Length\")\r\nRuntimeError: Response content shorter than Content-Length\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-408097719", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 408097719, "node_id": "MDEyOklzc3VlQ29tbWVudDQwODA5NzcxOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-07-26T13:29:38Z", "updated_at": "2018-07-26T13:29:38Z", "author_association": "OWNER", "body": "It looks like that's a bug in Starlette - filed here: https://github.com/encode/starlette/issues/32", "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-404514973", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 404514973, "node_id": "MDEyOklzc3VlQ29tbWVudDQwNDUxNDk3Mw==", "user": {"value": 647359, "label": "tomchristie"}, "created_at": "2018-07-12T13:38:24Z", "updated_at": "2018-07-12T13:38:24Z", "author_association": "NONE", "body": "Okay. I reckon the latest version should have all the kinds of components you'd need:\r\n\r\nRecently added ASGI components for Routing and Static Files support, as well as making few tweaks to make sure requests and responses are instantiated efficiently.\r\n\r\nDon't have any redirect-to-slash / redirect-to-non-slash stuff out of the box yet, which it looks like you might miss.", "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-403959704", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 403959704, "node_id": "MDEyOklzc3VlQ29tbWVudDQwMzk1OTcwNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-07-10T20:44:47Z", "updated_at": "2018-07-10T20:44:47Z", "author_association": "OWNER", "body": "No cookies or sessions - no POST requests in fact, Datasette just cares about GET (path and querystring) and being able to return custom HTTP headers.", "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-400571521", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 400571521, "node_id": "MDEyOklzc3VlQ29tbWVudDQwMDU3MTUyMQ==", "user": {"value": 647359, "label": "tomchristie"}, "created_at": "2018-06-27T07:30:07Z", "updated_at": "2018-06-27T07:30:07Z", "author_association": "NONE", "body": "I\u2019m up for helping with this.\r\n\r\nLooks like you\u2019d need static files support, which I\u2019m planning on adding a component for. Anything else obviously missing?\r\n\r\nFor a quick overview it looks very doable - the test client ought to me your test cases stay roughly the same.\r\n\r\nAre you using any middleware or other components for the Sanic ecosystem? Do you use cookies or sessions at all?", "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-400166540", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 400166540, "node_id": "MDEyOklzc3VlQ29tbWVudDQwMDE2NjU0MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-06-26T03:29:43Z", "updated_at": "2018-06-26T03:29:43Z", "author_association": "OWNER", "body": "This looks VERY relevant: https://github.com/encode/starlette", "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-392118755", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 392118755, "node_id": "MDEyOklzc3VlQ29tbWVudDM5MjExODc1NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-05-25T16:56:40Z", "updated_at": "2018-06-05T16:01:13Z", "author_association": "OWNER", "body": "Thinking about this further, maybe I should embrace ASGI turtles-all-the-way-down and teach each datasette view class to take a scope to the constructor and act entirely as an ASGI component. Would be a nice way of diving deep into ASGI and I can add utility helpers for things like querystring evaluation as I need them.", "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-394764713", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 394764713, "node_id": "MDEyOklzc3VlQ29tbWVudDM5NDc2NDcxMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-06-05T15:58:54Z", "updated_at": "2018-06-05T16:00:40Z", "author_association": "OWNER", "body": "https://github.com/encode/uvicorn/blob/572b5fe6c811b63298d5350a06b664839624c860/uvicorn/run.py#L63 is how you start a Uvicorn server from code as opposed to the `uvicorn` CLI\r\n\r\n from uvicorn.run import UvicornServer\r\n UvicornServer().run(app, host=host, port=port)\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-394503399", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 394503399, "node_id": "MDEyOklzc3VlQ29tbWVudDM5NDUwMzM5OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-06-04T21:20:14Z", "updated_at": "2018-06-04T21:20:14Z", "author_association": "OWNER", "body": "Results of an extremely simple micro-benchmark comparing the two shows that uvicorn is at least as fast as Sanic (benchmarks a little faster with a very simple payload): https://gist.github.com/simonw/418950af178c01c416363cc057420851", "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-394431323", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 394431323, "node_id": "MDEyOklzc3VlQ29tbWVudDM5NDQzMTMyMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-06-04T17:17:37Z", "updated_at": "2018-06-04T17:17:37Z", "author_association": "OWNER", "body": "I built this ASGI debugging tool to help with this migration: https://asgi-scope.now.sh/fivethirtyeight-34d6604/most-common-name%2Fsurnames.json?foo=bar&bazoeuto=onetuh&a=.", "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-391011268", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/272", "id": 391011268, "node_id": "MDEyOklzc3VlQ29tbWVudDM5MTAxMTI2OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-05-22T14:28:12Z", "updated_at": "2018-05-22T14:28:12Z", "author_association": "OWNER", "body": "I think I can do this almost entirely within my existing BaseView class structure.\r\n\r\nFirst, decouple the async data() methods by teaching them to take a querystring object as an argument instead of a Sanic request object. The get() method can then send that new object instead of a request.\r\n\r\nNext teach the base class how to obey the ASGI protocol.\r\n\r\nI should be able to get support for both Sanic and uvicorn/daphne working in the same codebase, which will make it easy to compare their performance. ", "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}