def test_insert(canned_write_client):
response = canned_write_client.post(
""/data/add_name"",
{""name"": ""Hello""},
allow_redirects=False,
csrftoken_from=True,
cookies={""foo"": ""bar""},
)
assert 302 == response.status
> assert ""/data/add_name?success"" == response.headers[""Location""]
E AssertionError: assert '/data/add_name?success' == '/data/add_name'
E - /data/add_name
E + /data/add_name?success
E ? ++++++++
/Users/simon/Dropbox/Development/datasette/tests/test_canned_queries.py:66: AssertionError
```
No idea why this change would affect that test.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",679650632,Don't hang in db.execute_write_fn() if connection fails,
https://github.com/simonw/datasette/issues/935#issuecomment-674451012,https://api.github.com/repos/simonw/datasette/issues/935,674451012,MDEyOklzc3VlQ29tbWVudDY3NDQ1MTAxMg==,9599,simonw,2020-08-15T21:56:13Z,2020-08-15T21:56:13Z,OWNER,"This implementation seems to fix it, need to work out how to test though.
```diff
diff --git a/datasette/database.py b/datasette/database.py
index ffa7a79..7ba1456 100644
--- a/datasette/database.py
+++ b/datasette/database.py
@@ -89,14 +89,22 @@ class Database:
def _execute_writes(self):
# Infinite looping thread that protects the single write connection
# to this database
- conn = self.connect(write=True)
+ conn_exception = None
+ conn = None
+ try:
+ conn = self.connect(write=True)
+ except Exception as e:
+ conn_exception = e
while True:
task = self._write_queue.get()
- try:
- result = task.fn(conn)
- except Exception as e:
- print(e)
- result = e
+ if conn_exception is not None:
+ result = conn_exception
+ else:
+ try:
+ result = task.fn(conn)
+ except Exception as e:
+ print(e)
+ result = e
task.reply_queue.sync_q.put(result)
async def execute_fn(self, fn):
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",679646710,"db.execute_write_fn(create_tables, block=True) hangs a thread if connection fails",
https://github.com/simonw/datasette/issues/935#issuecomment-674450652,https://api.github.com/repos/simonw/datasette/issues/935,674450652,MDEyOklzc3VlQ29tbWVudDY3NDQ1MDY1Mg==,9599,simonw,2020-08-15T21:51:22Z,2020-08-15T21:51:22Z,OWNER,"The easiest way to recreate this is to attempt a write against an immutable database, which triggers this assertion error:
https://github.com/simonw/datasette/blob/13b3b51087964d5e1a8c1cdd2495e07bdbe176b8/datasette/database.py#L47-L55","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",679646710,"db.execute_write_fn(create_tables, block=True) hangs a thread if connection fails",
https://github.com/simonw/datasette/issues/935#issuecomment-674450607,https://api.github.com/repos/simonw/datasette/issues/935,674450607,MDEyOklzc3VlQ29tbWVudDY3NDQ1MDYwNw==,9599,simonw,2020-08-15T21:50:41Z,2020-08-15T21:50:41Z,OWNER,"The bug is here:
https://github.com/simonw/datasette/blob/13b3b51087964d5e1a8c1cdd2495e07bdbe176b8/datasette/database.py#L89-L100
If `conn = self.connect(write=True)` raises an exception the entire server hangs, like this:
```
% datasette -i fixtures.db --get /
Exception in thread Thread-1:
Traceback (most recent call last):
File ""/usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/3.8/lib/python3.8/threading.py"", line 932, in _bootstrap_inner
self.run()
File ""/usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/3.8/lib/python3.8/threading.py"", line 870, in run
self._target(*self._args, **self._kwargs)
File ""/Users/simon/.local/share/virtualenvs/latest-datasette-with-all-plugins-PJL_Xy9e/lib/python3.8/site-packages/datasette/database.py"", line 92, in _execute_writes
conn = self.connect(write=True)
File ""/Users/simon/.local/share/virtualenvs/latest-datasette-with-all-plugins-PJL_Xy9e/lib/python3.8/site-packages/datasette/database.py"", line 55, in connect
assert not (write and not self.is_mutable)
AssertionError
... server hangs here ...
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",679646710,"db.execute_write_fn(create_tables, block=True) hangs a thread if connection fails",
https://github.com/simonw/datasette/issues/932#issuecomment-674144798,https://api.github.com/repos/simonw/datasette/issues/932,674144798,MDEyOklzc3VlQ29tbWVudDY3NDE0NDc5OA==,9599,simonw,2020-08-14T16:02:24Z,2020-08-14T16:02:24Z,OWNER,"Things to go in here:
- What is Datasette?
- A *database* contains *tables* full of *records*. A table has *rows* and *columns*.
- Understanding faceting
- How to use the filter interface
- How to export data
- How to link to data
- How to run SQL","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",678760988,End-user documentation,
https://github.com/simonw/datasette/issues/932#issuecomment-673735299,https://api.github.com/repos/simonw/datasette/issues/932,673735299,MDEyOklzc3VlQ29tbWVudDY3MzczNTI5OQ==,9599,simonw,2020-08-13T22:10:40Z,2020-08-13T22:11:06Z,OWNER,"Idea: plugins can provide their own user-facing documentation. Datasette can like to eg `datasette.io/help?plugins=datasette-vega,datasette-cluster-map` to get the user manual with extra sections for those plugins.
Or... link to `?url=datasette-url` and the documentation site can hit `/-/plugins.json` to figure out what extra manual sections to display!","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",678760988,End-user documentation,
https://github.com/simonw/datasette/issues/932#issuecomment-673734387,https://api.github.com/repos/simonw/datasette/issues/932,673734387,MDEyOklzc3VlQ29tbWVudDY3MzczNDM4Nw==,9599,simonw,2020-08-13T22:08:06Z,2020-08-13T22:08:06Z,OWNER,One challenge: how does this interact with plugins?,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",678760988,End-user documentation,
https://github.com/simonw/datasette/issues/932#issuecomment-673733904,https://api.github.com/repos/simonw/datasette/issues/932,673733904,MDEyOklzc3VlQ29tbWVudDY3MzczMzkwNA==,9599,simonw,2020-08-13T22:06:50Z,2020-08-13T22:06:50Z,OWNER,Title: **Using Datasette**. `using.rst`,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",678760988,End-user documentation,
https://github.com/simonw/datasette/issues/931#issuecomment-673123213,https://api.github.com/repos/simonw/datasette/issues/931,673123213,MDEyOklzc3VlQ29tbWVudDY3MzEyMzIxMw==,9599,simonw,2020-08-12T21:36:20Z,2020-08-12T21:36:20Z,OWNER,That worked: https://hub.docker.com/r/datasetteproject/datasette/tags,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677926613,Docker container is no longer being pushed (it's stuck on 0.45),
https://github.com/simonw/datasette/issues/931#issuecomment-673104851,https://api.github.com/repos/simonw/datasette/issues/931,673104851,MDEyOklzc3VlQ29tbWVudDY3MzEwNDg1MQ==,9599,simonw,2020-08-12T20:52:08Z,2020-08-12T20:52:08Z,OWNER,"```
docker run -p 8001:8001 -v `pwd`:/mnt \
datasette-updated-spatialite \
datasette -p 8001 -h 0.0.0.0 /mnt/fixtures.db --load-extension=/usr/local/lib/mod_spatialite.so
```
This seems to work `/-/versions` reports the SpatiaLite versions as expected:
```
""extensions"": {
""json1"": null,
""spatialite"": ""4.4.0-RC0""
},
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677926613,Docker container is no longer being pushed (it's stuck on 0.45),
https://github.com/simonw/datasette/issues/931#issuecomment-673088110,https://api.github.com/repos/simonw/datasette/issues/931,673088110,MDEyOklzc3VlQ29tbWVudDY3MzA4ODExMA==,9599,simonw,2020-08-12T20:15:28Z,2020-08-12T20:15:28Z,OWNER,"I changed the Dockerfile and built it on my laptop using:
docker build -f Dockerfile -t datasette-updated-spatialite .
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677926613,Docker container is no longer being pushed (it's stuck on 0.45),
https://github.com/simonw/datasette/issues/931#issuecomment-673074297,https://api.github.com/repos/simonw/datasette/issues/931,673074297,MDEyOklzc3VlQ29tbWVudDY3MzA3NDI5Nw==,9599,simonw,2020-08-12T19:46:29Z,2020-08-12T19:46:29Z,OWNER,"Looks like the old files have moved to:
- http://www.gaia-gis.it/gaia-sins/spatialite-tools-sources/
- http://www.gaia-gis.it/gaia-sins/libspatialite-sources/","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677926613,Docker container is no longer being pushed (it's stuck on 0.45),
https://github.com/simonw/datasette/issues/931#issuecomment-673070308,https://api.github.com/repos/simonw/datasette/issues/931,673070308,MDEyOklzc3VlQ29tbWVudDY3MzA3MDMwOA==,9599,simonw,2020-08-12T19:37:55Z,2020-08-12T19:37:55Z,OWNER,"Project news here: https://groups.google.com/g/spatialite-users/c/8AG3hQzXDmo
> FreeXL
> ==============================
> the current stable version is now 1.0.6
> (supporting few minor bug fixes and refreshed
> build scripts)
> DONE: no further development is expected in
> the near future.
>
> http://www.gaia-gis.it/gaia-sins/freexl-1.0.6.tar.gz
> http://www.gaia-gis.it/gaia-sins/freexl-1.0.6.zip
>
>
> ReadOSM
> ===============================
> the current stable version is now 1.1.0a
> (supporting few minor bug fixes and refreshed
> build scripts)
> DONE: no further development is expected in
> the near future.
>
> http://www.gaia-gis.it/gaia-sins/readosm-1.1.0a.tar.gz
> http://www.gaia-gis.it/gaia-sins/readosm-1.1.0a.zip
>
>
> VirtualPG
> ===============================
> the current stanle version is now 2.0.0
> (supporting few minor bug fixes and refreshed
> build scripts)
> DONE: no further development is expected in
> the near future.
>
> http://www.gaia-gis.it/gaia-sins/virtualpg-2.0.0.tar.gz
> http://www.gaia-gis.it/gaia-sins/virtualpg-2.0.0.zip
>
>
> libspatialite
> ===============================
> 5.0.0 Release Candidate 1 (RC1) is now ready
> to become a stable release.
> I'll simply wait for more or less a week so to
> allow for a reasonable community testing; if
> no critical issue will be reported in the meanwhile
> I'll go ASAP to release 5.0.0 ""stable""
>
> http://www.gaia-gis.it/gaia-sins/libspatialite-5.0.0-RC1.tar.gz
> http://www.gaia-gis.it/gaia-sins/libspatialite-5.0.0-RC1.zip
>
>
> librasterlite2
> ================================
> still under active development.
> a very relevant milestone has been achieved,
> now the DB layout required for fully integrating
> libspatialte and librasterlie2 is definetely
> consolidated, no further changes are expected.
>
> http://www.gaia-gis.it/gaia-sins/librasterlite2-sources/librasterlite2-1.1.0-beta1.tar.gz
> http://www.gaia-gis.it/gaia-sins/librasterlite2-sources/librasterlite2-1.1.0-beta1.zip
>
> more development activity is still required for
> fully implementing SQL-driven graphic rendering
> of both Vector and Raster Coverages based on
> standard SLD/SE styles.
>
> a reasonable estimate is two man-month (and
> not necessarily one development month equals
> one calendar month)
>
>
> spatialite_gui
> =================================
> the GUI tool is expected to take full profit from
> the advanced features of RasterLite2 so to
> become a complete self-contained GIS viewer
> with map editing capabilities.
>
> consequently it wiil surely be the last
> member of the SpatiaLite family to be
> released in a final stable form.
> several transient development versions
> will be possibly released so to closely
> follow the evolution of RasterLite2.
>
> http://www.gaia-gis.it/gaia-sins/spatialite-gui-sources/spatialite_gui-2.1.0-beta1.tar.gz
> http://www.gaia-gis.it/gaia-sins/spatialite-gui-sources/spatialite_gui-2.1.0-beta1.zip","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677926613,Docker container is no longer being pushed (it's stuck on 0.45),
https://github.com/simonw/datasette/issues/931#issuecomment-673068919,https://api.github.com/repos/simonw/datasette/issues/931,673068919,MDEyOklzc3VlQ29tbWVudDY3MzA2ODkxOQ==,9599,simonw,2020-08-12T19:34:57Z,2020-08-12T19:34:57Z,OWNER,"Looks like SpatiaLite released new versions: https://www.gaia-gis.it/fossil/freexl/index says ""Sources: current version is 1.0.6 (released on 2020-08-02)"" and links to http://www.gaia-gis.it/gaia-sins/freexl-1.0.6.tar.gz","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677926613,Docker container is no longer being pushed (it's stuck on 0.45),
https://github.com/simonw/datasette/issues/931#issuecomment-673068327,https://api.github.com/repos/simonw/datasette/issues/931,673068327,MDEyOklzc3VlQ29tbWVudDY3MzA2ODMyNw==,9599,simonw,2020-08-12T19:33:42Z,2020-08-12T19:33:42Z,OWNER,"https://hub.docker.com/r/datasetteproject/datasette/tags shows this:
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677926613,Docker container is no longer being pushed (it's stuck on 0.45),
https://github.com/simonw/sqlite-utils/issues/133#issuecomment-672997703,https://api.github.com/repos/simonw/sqlite-utils/issues/133,672997703,MDEyOklzc3VlQ29tbWVudDY3Mjk5NzcwMw==,9599,simonw,2020-08-12T17:05:06Z,2020-08-12T17:05:06Z,OWNER,Released.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677839979,Release a sdist to PyPI,
https://github.com/simonw/datasette/issues/930#issuecomment-672550662,https://api.github.com/repos/simonw/datasette/issues/930,672550662,MDEyOklzc3VlQ29tbWVudDY3MjU1MDY2Mg==,9599,simonw,2020-08-12T03:30:59Z,2020-08-12T03:30:59Z,OWNER,https://datasette.readthedocs.io/en/stable/changelog.html#v0-47-1,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677326155,Datasette sdist is missing templates (hence broken when installing from Homebrew),
https://github.com/simonw/datasette/issues/930#issuecomment-672519787,https://api.github.com/repos/simonw/datasette/issues/930,672519787,MDEyOklzc3VlQ29tbWVudDY3MjUxOTc4Nw==,9599,simonw,2020-08-12T02:52:46Z,2020-08-12T02:52:46Z,OWNER,"Homebrew install is now fixed, using a temporary URL while waiting for Travis to ship the new release to PyPI: https://github.com/simonw/homebrew-datasette/issues/6","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677326155,Datasette sdist is missing templates (hence broken when installing from Homebrew),
https://github.com/simonw/datasette/issues/930#issuecomment-672488293,https://api.github.com/repos/simonw/datasette/issues/930,672488293,MDEyOklzc3VlQ29tbWVudDY3MjQ4ODI5Mw==,9599,simonw,2020-08-12T02:35:34Z,2020-08-12T02:35:34Z,OWNER,"OK, this has fixed it - I used `sdist` to create the `.tar.gz` and then `pip installed` it into a new environment, and everything worked just fine. Going to ship this as Datasette 0.47.1.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677326155,Datasette sdist is missing templates (hence broken when installing from Homebrew),
https://github.com/simonw/datasette/issues/930#issuecomment-672480811,https://api.github.com/repos/simonw/datasette/issues/930,672480811,MDEyOklzc3VlQ29tbWVudDY3MjQ4MDgxMQ==,9599,simonw,2020-08-12T02:31:38Z,2020-08-12T02:31:38Z,OWNER,The root cause appears to be that the `templates/` are missing from the `sdist` `.tar.gz`: https://github.com/simonw/homebrew-datasette/issues/6#issuecomment-672477352,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677326155,Datasette sdist is missing templates (hence broken when installing from Homebrew),
https://github.com/simonw/datasette/issues/930#issuecomment-672472518,https://api.github.com/repos/simonw/datasette/issues/930,672472518,MDEyOklzc3VlQ29tbWVudDY3MjQ3MjUxOA==,9599,simonw,2020-08-12T02:24:49Z,2020-08-12T02:24:49Z,OWNER,I checked and `datasette publish` deploys a 0.47 version that works fine too: https://datasette-0-47-j7hipcg4aq-uc.a.run.app/-/versions,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677326155,Datasette sdist is missing templates (hence broken when installing from Homebrew),
https://github.com/simonw/datasette/issues/930#issuecomment-672471431,https://api.github.com/repos/simonw/datasette/issues/930,672471431,MDEyOklzc3VlQ29tbWVudDY3MjQ3MTQzMQ==,9599,simonw,2020-08-12T02:21:19Z,2020-08-12T02:21:19Z,OWNER,"Correction: pip installed Datasette works fine, it's just the Homebrew release that is broken.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677326155,Datasette sdist is missing templates (hence broken when installing from Homebrew),
https://github.com/simonw/datasette/issues/926#issuecomment-672393737,https://api.github.com/repos/simonw/datasette/issues/926,672393737,MDEyOklzc3VlQ29tbWVudDY3MjM5MzczNw==,9599,simonw,2020-08-12T00:26:17Z,2020-08-12T00:26:17Z,OWNER,"```
$ datasette --get '/:memory:.json?sql=select+sqlite_version()' | jq .
{
""database"": "":memory:"",
""query_name"": null,
""rows"": [
[
""3.32.3""
]
],
""truncated"": false,
""columns"": [
""sqlite_version()""
],
""query"": {
""sql"": ""select sqlite_version()"",
""params"": {}
},
""private"": false,
""allow_execute_sql"": true,
""query_ms"": 1.165628433227539
}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677250834,"datasette fixtures.db --get ""/fixtures.json""",
https://github.com/simonw/datasette/pull/927#issuecomment-672391299,https://api.github.com/repos/simonw/datasette/issues/927,672391299,MDEyOklzc3VlQ29tbWVudDY3MjM5MTI5OQ==,9599,simonw,2020-08-12T00:19:20Z,2020-08-12T00:19:20Z,OWNER,Docs: https://github.com/simonw/datasette/blob/2111da01a03cfc62303b6a4b59ea9f96d22c0f78/docs/getting_started.rst#datasette---get,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677265716,"'datasette --get' option, refs #926",
https://github.com/simonw/datasette/pull/927#issuecomment-672382108,https://api.github.com/repos/simonw/datasette/issues/927,672382108,MDEyOklzc3VlQ29tbWVudDY3MjM4MjEwOA==,9599,simonw,2020-08-12T00:09:18Z,2020-08-12T00:09:18Z,OWNER,Documentation can go here: https://datasette.readthedocs.io/en/latest/getting_started.html#datasette-serve-options,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677265716,"'datasette --get' option, refs #926",
https://github.com/simonw/datasette/issues/928#issuecomment-672373061,https://api.github.com/repos/simonw/datasette/issues/928,672373061,MDEyOklzc3VlQ29tbWVudDY3MjM3MzA2MQ==,9599,simonw,2020-08-11T23:56:19Z,2020-08-11T23:56:19Z,OWNER,"New implementation of the `install` command:
https://github.com/simonw/datasette/blob/afdeda8216d4d3027f87583ccdbef17ad85022ef/datasette/cli.py#L235-L240","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677272618,Test failures caused by failed attempts to mock pip,
https://github.com/simonw/datasette/issues/928#issuecomment-672372465,https://api.github.com/repos/simonw/datasette/issues/928,672372465,MDEyOklzc3VlQ29tbWVudDY3MjM3MjQ2NQ==,9599,simonw,2020-08-11T23:54:28Z,2020-08-11T23:54:28Z,OWNER,"While debugging this I found a useful clue in https://github.com/pypa/pip/blob/e060970d51c5946beac8447eb95585d83019582d/src/pip/_internal/cli/main.py#L23-L47
```
# Do not import and use main() directly! Using it directly is actively
# discouraged by pip's maintainers. The name, location and behavior of
# this function is subject to change, so calling it directly is not
# portable across different pip versions.
# In addition, running pip in-process is unsupported and unsafe. This is
# elaborated in detail at
# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program.
# That document also provides suggestions that should work for nearly
# all users that are considering importing and using main() directly.
# However, we know that certain users will still want to invoke pip
# in-process. If you understand and accept the implications of using pip
# in an unsupported manner, the best approach is to use runpy to avoid
# depending on the exact location of this entry point.
# The following example shows how to use runpy to invoke pip in that
# case:
#
# sys.argv = [""pip"", your, args, here]
# runpy.run_module(""pip"", run_name=""__main__"")
#
# Note that this will exit the process after running, unlike a direct
# call to main. As it is not safe to do any processing after calling
# main, this should not be an issue in practice.
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677272618,Test failures caused by failed attempts to mock pip,
https://github.com/simonw/datasette/issues/928#issuecomment-672372197,https://api.github.com/repos/simonw/datasette/issues/928,672372197,MDEyOklzc3VlQ29tbWVudDY3MjM3MjE5Nw==,9599,simonw,2020-08-11T23:53:38Z,2020-08-11T23:53:38Z,OWNER,Caused by the tests for #925,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677272618,Test failures caused by failed attempts to mock pip,
https://github.com/simonw/datasette/pull/927#issuecomment-672357176,https://api.github.com/repos/simonw/datasette/issues/927,672357176,MDEyOklzc3VlQ29tbWVudDY3MjM1NzE3Ng==,9599,simonw,2020-08-11T23:32:08Z,2020-08-11T23:33:09Z,OWNER,Needs documentation and tests.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677265716,"'datasette --get' option, refs #926",
https://github.com/simonw/datasette/pull/927#issuecomment-672357902,https://api.github.com/repos/simonw/datasette/issues/927,672357902,MDEyOklzc3VlQ29tbWVudDY3MjM1NzkwMg==,9599,simonw,2020-08-11T23:32:39Z,2020-08-11T23:32:39Z,OWNER,"It works:
```
$ datasette --get '/:memory:.json?sql=select * from sqlite_master' | jq .
{
""database"": "":memory:"",
""query_name"": null,
""rows"": [],
""truncated"": false,
""columns"": [
""type"",
""name"",
""tbl_name"",
""rootpage"",
""sql""
],
""query"": {
""sql"": ""select * from sqlite_master"",
""params"": {}
},
""private"": false,
""allow_execute_sql"": true,
""query_ms"": 0.8032321929931641
}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677265716,"'datasette --get' option, refs #926",
https://github.com/simonw/datasette/issues/926#issuecomment-672338113,https://api.github.com/repos/simonw/datasette/issues/926,672338113,MDEyOklzc3VlQ29tbWVudDY3MjMzODExMw==,9599,simonw,2020-08-11T22:57:28Z,2020-08-11T22:57:28Z,OWNER,"I partly want this so I can easily implement a better `test` method for the Homebrew package. The test I have right now looks like this:
https://github.com/simonw/homebrew-datasette/blob/8aa30aa183158051a987a7e3f50e7e3ee05d8ee9/Formula/datasette.rb#L125-L127
```
test do
system bin/""datasette"", ""--help""
end
```
The Homebrew docs at https://docs.brew.sh/Formula-Cookbook#add-a-test-to-the-formula say:
> We want tests that don't require any user input and test the basic functionality of the application. For example `foo build-foo input.foo` is a good test and (despite their widespread use) `foo --version` and `foo --help` are bad tests. However, a bad test is better than no test at all.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677250834,"datasette fixtures.db --get ""/fixtures.json""",
https://github.com/simonw/datasette/issues/923#issuecomment-672336720,https://api.github.com/repos/simonw/datasette/issues/923,672336720,MDEyOklzc3VlQ29tbWVudDY3MjMzNjcyMA==,9599,simonw,2020-08-11T22:53:07Z,2020-08-11T22:53:07Z,OWNER,https://github.com/simonw/datasette/blob/5126ecb1267ed3850bf3b0ab270accd031a02e79/docs/installation.rst#using-homebrew,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677037043,Add homebrew installation to documentation,
https://github.com/simonw/datasette/issues/923#issuecomment-672329101,https://api.github.com/repos/simonw/datasette/issues/923,672329101,MDEyOklzc3VlQ29tbWVudDY3MjMyOTEwMQ==,9599,simonw,2020-08-11T22:35:13Z,2020-08-11T22:35:13Z,OWNER,I added the `datasette install name-of-plugin` command in #925 mainly to simplify the process of installing plugins if Datasette itself was installed using homebrew.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677037043,Add homebrew installation to documentation,
https://github.com/simonw/datasette/issues/925#issuecomment-672328807,https://api.github.com/repos/simonw/datasette/issues/925,672328807,MDEyOklzc3VlQ29tbWVudDY3MjMyODgwNw==,9599,simonw,2020-08-11T22:34:37Z,2020-08-11T22:34:37Z,OWNER,"This will simplify the instructions for installing plugins with Datasette install via homebrew, refs #923","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677227912,"""datasette install"" and ""datasette uninstall"" commands",
https://github.com/simonw/datasette/issues/925#issuecomment-672328436,https://api.github.com/repos/simonw/datasette/issues/925,672328436,MDEyOklzc3VlQ29tbWVudDY3MjMyODQzNg==,9599,simonw,2020-08-11T22:33:32Z,2020-08-11T22:33:42Z,OWNER,"```
$ datasette install --help
Usage: datasette install [OPTIONS] PACKAGES...
Install Python packages - e.g. Datasette plugins - into the same
environment as Datasette
Options:
--help Show this message and exit.
$ datasette uninstall --help
Usage: datasette uninstall [OPTIONS] PACKAGES...
Uninstall Python packages (e.g. plugins) from the Datasette environment
Options:
-y, --yes Don't ask for confirmation
--help Show this message and exit.
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677227912,"""datasette install"" and ""datasette uninstall"" commands",
https://github.com/simonw/datasette/issues/925#issuecomment-672304650,https://api.github.com/repos/simonw/datasette/issues/925,672304650,MDEyOklzc3VlQ29tbWVudDY3MjMwNDY1MA==,9599,simonw,2020-08-11T22:04:48Z,2020-08-11T22:04:48Z,OWNER,Prototyped in this thread: https://github.com/simonw/datasette/issues/335#issuecomment-671005731,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677227912,"""datasette install"" and ""datasette uninstall"" commands",
https://github.com/simonw/datasette/issues/923#issuecomment-672288845,https://api.github.com/repos/simonw/datasette/issues/923,672288845,MDEyOklzc3VlQ29tbWVudDY3MjI4ODg0NQ==,9599,simonw,2020-08-11T21:28:17Z,2020-08-11T21:28:17Z,OWNER,"Here's a pattern for installing plugins:
```
$ datasette plugins
[]
$ /usr/local/opt/datasette/libexec/bin/pip install datasette-vega
Collecting datasette-vega
Using cached datasette_vega-0.6.2-py3-none-any.whl (1.8 MB)
Requirement already satisfied: datasette in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette-vega) (0.46)
Requirement already satisfied: click~=7.1.1 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (7.1.2)
Requirement already satisfied: click-default-group~=1.2.2 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (1.2.2)
Requirement already satisfied: Jinja2<2.12.0,>=2.10.3 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (2.11.2)
Requirement already satisfied: hupper~=1.9 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (1.10.2)
Requirement already satisfied: pint~=0.9 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (0.14)
Requirement already satisfied: pluggy~=0.13.0 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (0.13.1)
Requirement already satisfied: uvicorn~=0.11 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (0.11.8)
Requirement already satisfied: aiofiles<0.6,>=0.4 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (0.5.0)
Requirement already satisfied: janus<0.6,>=0.4 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (0.5.0)
Requirement already satisfied: asgi-csrf>=0.6 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (0.6.1)
Requirement already satisfied: PyYAML~=5.3 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (5.3.1)
Requirement already satisfied: mergedeep<1.4.0,>=1.1.1 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (1.3.0)
Requirement already satisfied: itsdangerous~=1.1 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (1.1.0)
Requirement already satisfied: python-baseconv==1.2.2 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from datasette->datasette-vega) (1.2.2)
Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from Jinja2<2.12.0,>=2.10.3->datasette->datasette-vega) (1.1.1)
Requirement already satisfied: setuptools in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from pint~=0.9->datasette->datasette-vega) (49.3.1)
Requirement already satisfied: packaging in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from pint~=0.9->datasette->datasette-vega) (20.4)
Requirement already satisfied: h11<0.10,>=0.8 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from uvicorn~=0.11->datasette->datasette-vega) (0.9.0)
Requirement already satisfied: websockets==8.* in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from uvicorn~=0.11->datasette->datasette-vega) (8.1)
Requirement already satisfied: httptools==0.1.* in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from uvicorn~=0.11->datasette->datasette-vega) (0.1.1)
Requirement already satisfied: uvloop>=0.14.0 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from uvicorn~=0.11->datasette->datasette-vega) (0.14.0)
Requirement already satisfied: pyparsing>=2.0.2 in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from packaging->pint~=0.9->datasette->datasette-vega) (2.4.7)
Requirement already satisfied: six in /usr/local/Cellar/datasette/0.46/libexec/lib/python3.8/site-packages (from packaging->pint~=0.9->datasette->datasette-vega) (1.15.0)
Installing collected packages: datasette-vega
Successfully installed datasette-vega-0.6.2
$ datasette plugins
[
{
""name"": ""datasette-vega"",
""static"": true,
""templates"": false,
""version"": ""0.6.2"",
""hooks"": [
""extra_css_urls"",
""extra_js_urls""
]
}
]
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677037043,Add homebrew installation to documentation,
https://github.com/simonw/datasette/issues/923#issuecomment-672287754,https://api.github.com/repos/simonw/datasette/issues/923,672287754,MDEyOklzc3VlQ29tbWVudDY3MjI4Nzc1NA==,9599,simonw,2020-08-11T21:25:33Z,2020-08-11T21:25:33Z,OWNER,.. and confirm if `brew tap ...` is even needed if you run `brew install simonw/datasette/datasette`,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677037043,Add homebrew installation to documentation,
https://github.com/simonw/datasette/issues/923#issuecomment-672089281,https://api.github.com/repos/simonw/datasette/issues/923,672089281,MDEyOklzc3VlQ29tbWVudDY3MjA4OTI4MQ==,9599,simonw,2020-08-11T16:54:50Z,2020-08-11T16:54:50Z,OWNER,Also need to talk about how you install plugins.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",677037043,Add homebrew installation to documentation,
https://github.com/simonw/datasette/issues/335#issuecomment-672088880,https://api.github.com/repos/simonw/datasette/issues/335,672088880,MDEyOklzc3VlQ29tbWVudDY3MjA4ODg4MA==,9599,simonw,2020-08-11T16:54:06Z,2020-08-11T16:54:06Z,OWNER,"It works!
```
$ brew tap simonw/datasette
$ brew install simonw/datasette/datasette
$ datasette --version
datasette, version 0.46
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",339505204,Package datasette for installation using homebrew,
https://github.com/simonw/datasette/issues/335#issuecomment-671733187,https://api.github.com/repos/simonw/datasette/issues/335,671733187,MDEyOklzc3VlQ29tbWVudDY3MTczMzE4Nw==,9599,simonw,2020-08-11T05:25:23Z,2020-08-11T05:25:23Z,OWNER,I got this almost working in `simonw/homebrew-datasette` - see https://github.com/simonw/homebrew-datasette/issues/2 for the last missing detail.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",339505204,Package datasette for installation using homebrew,
https://github.com/simonw/sqlite-utils/issues/132#issuecomment-671151461,https://api.github.com/repos/simonw/sqlite-utils/issues/132,671151461,MDEyOklzc3VlQ29tbWVudDY3MTE1MTQ2MQ==,9599,simonw,2020-08-10T03:54:06Z,2020-08-10T03:54:06Z,OWNER,For the moment I'll build it without the `--retry` mode.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675839512,Features for enabling and disabling WAL mode,
https://github.com/simonw/sqlite-utils/issues/132#issuecomment-671151170,https://api.github.com/repos/simonw/sqlite-utils/issues/132,671151170,MDEyOklzc3VlQ29tbWVudDY3MTE1MTE3MA==,9599,simonw,2020-08-10T03:52:02Z,2020-08-10T03:52:02Z,OWNER,"I'm having trouble figuring out how to write a test that locks a SQLite database (so I can test that `--retry` actually works). I tried this recipe but it didn't seem to prevent another process from running `pragma journal_mode='wal';` against that database:
```python
import time
import sys
import sqlite3
filename = sys.argv[-1]
db = sqlite3.connect(filename)
with db:
db.execute(""create table if not exists counter(id integer primary key, counter text)"")
time.sleep(100)
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675839512,Features for enabling and disabling WAL mode,
https://github.com/simonw/sqlite-utils/issues/132#issuecomment-671147344,https://api.github.com/repos/simonw/sqlite-utils/issues/132,671147344,MDEyOklzc3VlQ29tbWVudDY3MTE0NzM0NA==,9599,simonw,2020-08-10T03:29:00Z,2020-08-10T03:29:00Z,OWNER,"The CLI options should take multiple database files:
$ sqlite-utils enable-wal *.db
It's possible for this to fail if the DB is locked. How about a `--retry` option that causes it to retry a bunch of times if that happens?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675839512,Features for enabling and disabling WAL mode,
https://github.com/simonw/sqlite-utils/issues/132#issuecomment-671147148,https://api.github.com/repos/simonw/sqlite-utils/issues/132,671147148,MDEyOklzc3VlQ29tbWVudDY3MTE0NzE0OA==,9599,simonw,2020-08-10T03:27:50Z,2020-08-10T03:27:50Z,OWNER,"https://www.sqlite.org/pragma.html#pragma_journal_mode lists six modes: DELETE | TRUNCATE | PERSIST | MEMORY | WAL | OFF
I'm only going to implement utilities for DELETE (wal-off) and WAL (wal-on) - the other modes look like they're for specialist purposes that I don't need to support.
If it turns out I do need them I can add those to `sqlite-utils` later.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675839512,Features for enabling and disabling WAL mode,
https://github.com/simonw/sqlite-utils/issues/132#issuecomment-671146948,https://api.github.com/repos/simonw/sqlite-utils/issues/132,671146948,MDEyOklzc3VlQ29tbWVudDY3MTE0Njk0OA==,9599,simonw,2020-08-10T03:26:51Z,2020-08-10T03:26:51Z,OWNER,"For the CLI:
$ sqlite-utils enable-wal github.db
$ sqlite-utils disable-wal github.db
For the Python library:
```python
import sqlite_utils
db = sqlite_utils.Database(""github.db"")
db.enable_wal()
db.disable_wal()
mode = db.journal_mode # ""wal"" or ""delete"" or others
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675839512,Features for enabling and disabling WAL mode,
https://github.com/simonw/sqlite-utils/issues/131#issuecomment-671088832,https://api.github.com/repos/simonw/sqlite-utils/issues/131,671088832,MDEyOklzc3VlQ29tbWVudDY3MTA4ODgzMg==,9599,simonw,2020-08-09T19:00:41Z,2020-08-09T19:00:41Z,OWNER,"Should be consistent with the `create-table` command as much as possible:
```
$ sqlite-utils create-table mydb.db mytable \
id integer \
name text \
age integer \
is_good integer \
--not-null name \
--not-null age \
--default is_good 1 \
--pk=id
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675753042,sqlite-utils insert: options for column types,
https://github.com/simonw/datasette/issues/335#issuecomment-671077168,https://api.github.com/repos/simonw/datasette/issues/335,671077168,MDEyOklzc3VlQ29tbWVudDY3MTA3NzE2OA==,9599,simonw,2020-08-09T17:10:15Z,2020-08-09T18:13:39Z,OWNER,"Here's the issue that explains that warning: https://github.com/pypa/pip/issues/5599
This should fix it (risky):
from pip._internal.cli.main import main","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",339505204,Package datasette for installation using homebrew,
https://github.com/simonw/datasette/issues/335#issuecomment-671076975,https://api.github.com/repos/simonw/datasette/issues/335,671076975,MDEyOklzc3VlQ29tbWVudDY3MTA3Njk3NQ==,9599,simonw,2020-08-09T17:08:34Z,2020-08-09T17:09:21Z,OWNER,"Quick prototype of `datasette install`:
```diff
diff --git a/datasette/cli.py b/datasette/cli.py
index 287195a..95b6eb7 100644
--- a/datasette/cli.py
+++ b/datasette/cli.py
@@ -231,6 +231,18 @@ def package(
call(args)
+@cli.command()
+@click.argument(""packages"", nargs=-1, required=True)
+def install(packages):
+ ""Install Python packages - e.g. Datasette plugins - into the same environment as Datasett""
+ import pip
+
+ try:
+ pip.main([""install""] + list(packages))
+ except SystemExit as e:
+ pass
+
+
@cli.command()
@click.argument(""files"", type=click.Path(exists=True), nargs=-1)
@click.option(
```
```
$ datasette install
Usage: datasette install [OPTIONS] PACKAGES...
Try 'datasette install --help' for help.
Error: Missing argument 'PACKAGES...'.
$ datasette install datasette-vega
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Collecting datasette-vega
Using cached datasette_vega-0.6.2-py3-none-any.whl (1.8 MB)
...
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",339505204,Package datasette for installation using homebrew,
https://github.com/simonw/datasette/issues/335#issuecomment-671005731,https://api.github.com/repos/simonw/datasette/issues/335,671005731,MDEyOklzc3VlQ29tbWVudDY3MTAwNTczMQ==,9599,simonw,2020-08-09T04:44:13Z,2020-08-09T17:04:21Z,OWNER,"Telling people how to figure out that `pip` location is going to be pretty unpleasant.
How about instead providing a `datasette plugins --install=datasette-graphql` command?
Or `datasette install datasette-vega`
It would run `pip install` in the same virtualenv as Datasette itself.
http://jelly.codes/articles/python-pip-module/ shows how to do this:
```python
import pip
try:
pip.main([""install"", ""plumbum""])
except SystemExit as e:
pass
```
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",339505204,Package datasette for installation using homebrew,
https://github.com/simonw/datasette/issues/918#issuecomment-671075764,https://api.github.com/repos/simonw/datasette/issues/918,671075764,MDEyOklzc3VlQ29tbWVudDY3MTA3NTc2NA==,9599,simonw,2020-08-09T16:56:48Z,2020-08-09T16:56:48Z,OWNER,GitHub security advisory: https://github.com/simonw/datasette/security/advisories/GHSA-q6j3-c4wc-63vw,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675724951,Security issue: read-only canned queries leak CSRF token in URL,
https://github.com/simonw/datasette/issues/915#issuecomment-671073223,https://api.github.com/repos/simonw/datasette/issues/915,671073223,MDEyOklzc3VlQ29tbWVudDY3MTA3MzIyMw==,9599,simonw,2020-08-09T16:35:20Z,2020-08-09T16:36:10Z,OWNER,"`datasette-graphql` uses the logic from `TableView` right now. It wasn't too unpleasant, but I do worry about the two of them being coupled together in this way.
https://github.com/simonw/datasette-graphql/blob/cc65ec294b0bf8e26213fc68bb5487066de9caab/datasette_graphql/utils.py#L412-L417
```python
request = Request.fake(path_with_query_string)
view = TableView(DatasetteSpecialConfig(datasette))
data, _, _ = await view.data(
request, database=database_name, hash=None, table=table_name, _next=after
)
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",671763164,Refactor TableView class so things like datasette-graphql can reuse the logic,
https://github.com/simonw/datasette/issues/919#issuecomment-671072223,https://api.github.com/repos/simonw/datasette/issues/919,671072223,MDEyOklzc3VlQ29tbWVudDY3MTA3MjIyMw==,9599,simonw,2020-08-09T16:26:17Z,2020-08-09T16:26:17Z,OWNER,Should be released in a couple of minutes: https://travis-ci.org/github/simonw/datasette/builds/716328883,"{""total_count"": 1, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 1, ""rocket"": 0, ""eyes"": 0}",675727366,"Travis should not build the master branch, only the main branch",
https://github.com/simonw/datasette/issues/918#issuecomment-671071710,https://api.github.com/repos/simonw/datasette/issues/918,671071710,MDEyOklzc3VlQ29tbWVudDY3MTA3MTcxMA==,9599,simonw,2020-08-09T16:21:41Z,2020-08-09T16:21:41Z,OWNER,Submitting the form on https://latest.datasette.io/fixtures/neighborhood_search demonstrates that this is fixed.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675724951,Security issue: read-only canned queries leak CSRF token in URL,
https://github.com/simonw/datasette/issues/919#issuecomment-671071461,https://api.github.com/repos/simonw/datasette/issues/919,671071461,MDEyOklzc3VlQ29tbWVudDY3MTA3MTQ2MQ==,9599,simonw,2020-08-09T16:19:37Z,2020-08-09T16:19:37Z,OWNER,That appears to have worked.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675727366,"Travis should not build the master branch, only the main branch",
https://github.com/simonw/datasette/issues/918#issuecomment-671070528,https://api.github.com/repos/simonw/datasette/issues/918,671070528,MDEyOklzc3VlQ29tbWVudDY3MTA3MDUyOA==,9599,simonw,2020-08-09T16:12:16Z,2020-08-09T16:12:16Z,OWNER,"It's worth noting that in order to exploit this issue the following would all need to be true:
- A user is running a copy of Datasette protected by a cookie-based authentication plugin AND configured with at least one writable canned query
- An attacker is in control of a URL that could concievably be returned on a page that is displayed as the result of submitting a read-only canned query
- An authenticated user of that Datasette instance, who is running a browser that doesn't support the `SameSite=lax` cookie parameter (which is [widely supported](https://caniuse.com/#feat=same-site-cookie-attribute) by modern browsers), submits the read-only canned query form and then clicks a link to the attacker's off-site page, exposing their CSRFToken in the attacker's HTTP referer logs
- The attacker then tricks that user into visiting their own malicious web page which includes a POST form that auto-submits against the writable canned query that the attacker wishes to exploit, including the CSRF token as a hidden field
The attacker would need full knowledge of the URL and form layout of the Datasette instance that they are exploiting.
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675724951,Security issue: read-only canned queries leak CSRF token in URL,
https://github.com/simonw/datasette/issues/918#issuecomment-671070486,https://api.github.com/repos/simonw/datasette/issues/918,671070486,MDEyOklzc3VlQ29tbWVudDY3MTA3MDQ4Ng==,9599,simonw,2020-08-09T16:11:59Z,2020-08-09T16:11:59Z,OWNER,Fix has been released in Datasette 0.46: https://datasette.readthedocs.io/en/latest/changelog.html#v0-46,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",675724951,Security issue: read-only canned queries leak CSRF token in URL,
https://github.com/simonw/datasette/issues/335#issuecomment-671001457,https://api.github.com/repos/simonw/datasette/issues/335,671001457,MDEyOklzc3VlQ29tbWVudDY3MTAwMTQ1Nw==,9599,simonw,2020-08-09T03:37:39Z,2020-08-09T03:37:39Z,OWNER,"Here's what happened when I installed `homebrew-vd`: https://gist.github.com/simonw/7bfd971a62743d7ca248e6b5e696c240
It worked! And from digging around, it has a virtual environment at `/usr/local/Cellar/visidata/1.5.2/libexec/`
Which means `/usr/local/Cellar/visidata/1.5.2/libexec/bin/pip` is a working `pip`
And I tried running these commands and confirmed that I get a `datasette` with an additional plugin:
```
/usr/local/Cellar/visidata/1.5.2/libexec/bin/pip install datasette
/usr/local/Cellar/visidata/1.5.2/libexec/bin/pip install datasette-graphql
/usr/local/Cellar/visidata/1.5.2/libexec/bin/datasette plugins
[
{
""name"": ""datasette-graphql"",
""static"": false,
""templates"": true,
""version"": ""0.11"",
""hooks"": [
""register_routes"",
""startup""
]
}
]
```
So I can package Datasette as a homebrew package AND I can give people instructions for installing plugins.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",339505204,Package datasette for installation using homebrew,
https://github.com/simonw/datasette/issues/335#issuecomment-670999860,https://api.github.com/repos/simonw/datasette/issues/335,670999860,MDEyOklzc3VlQ29tbWVudDY3MDk5OTg2MA==,9599,simonw,2020-08-09T03:12:44Z,2020-08-09T03:12:44Z,OWNER,How would plugin installation work if Datasette was installed via homebrew?,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",339505204,Package datasette for installation using homebrew,
https://github.com/simonw/datasette/issues/335#issuecomment-670999832,https://api.github.com/repos/simonw/datasette/issues/335,670999832,MDEyOklzc3VlQ29tbWVudDY3MDk5OTgzMg==,9599,simonw,2020-08-09T03:12:14Z,2020-08-09T03:12:14Z,OWNER,Another useful example: https://github.com/Homebrew/homebrew-core/blob/master/Formula/trailscraper.rb,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",339505204,Package datasette for installation using homebrew,
https://github.com/simonw/sqlite-utils/issues/130#issuecomment-667585598,https://api.github.com/repos/simonw/sqlite-utils/issues/130,667585598,MDEyOklzc3VlQ29tbWVudDY2NzU4NTU5OA==,9599,simonw,2020-08-01T20:51:28Z,2020-08-01T20:51:28Z,OWNER,CLI documentation: https://github.com/simonw/sqlite-utils/commit/57e4eb8e5564af5d97f892b3be8342451ee177a2?short_path=7240b7c#diff-7240b7c71b1a8194da0c001c64fc8d40,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",671130371,Support tokenize option for FTS,
https://github.com/simonw/sqlite-utils/issues/130#issuecomment-667585561,https://api.github.com/repos/simonw/sqlite-utils/issues/130,667585561,MDEyOklzc3VlQ29tbWVudDY2NzU4NTU2MQ==,9599,simonw,2020-08-01T20:50:59Z,2020-08-01T20:50:59Z,OWNER,Turns out it works for FTS4 as well: https://www.sqlite.org/fts3.html#tokenizer,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",671130371,Support tokenize option for FTS,
https://github.com/simonw/sqlite-utils/issues/130#issuecomment-667584567,https://api.github.com/repos/simonw/sqlite-utils/issues/130,667584567,MDEyOklzc3VlQ29tbWVudDY2NzU4NDU2Nw==,9599,simonw,2020-08-01T20:41:09Z,2020-08-01T20:41:09Z,OWNER,API documentation here: https://github.com/simonw/sqlite-utils/commit/617e6f070c85be66ea04c80b78dafd08c875f8c8?short_path=e156262#diff-e1562629b8def6da772d9b0903faf703,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",671130371,Support tokenize option for FTS,
https://github.com/simonw/datasette/issues/900#issuecomment-667431123,https://api.github.com/repos/simonw/datasette/issues/900,667431123,MDEyOklzc3VlQ29tbWVudDY2NzQzMTEyMw==,9599,simonw,2020-07-31T23:56:33Z,2020-07-31T23:56:33Z,OWNER,I think this is the same issue as #865. I'll look at these together!,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",661605489,Some links don't honor base_url,
https://github.com/simonw/datasette/issues/899#issuecomment-667430790,https://api.github.com/repos/simonw/datasette/issues/899,667430790,MDEyOklzc3VlQ29tbWVudDY2NzQzMDc5MA==,9599,simonw,2020-07-31T23:54:40Z,2020-07-31T23:54:40Z,OWNER,"There's no mechanism that can do this at the moment.
You could absolutely support this with a plugin, probably using the `asgi_wrapper` plugin hook. There's an existing package at https://pypi.org/project/asgi-ratelimit/ which may be usable for this - it may even be possible to configure that using https://github.com/simonw/datasette-configure-asgi rather than using it to write a custom plugin.
Using a separate revers proxy would also be a good way to solve this. It depends which option would work best in your environment.
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",660827546,How to setup a request limit per user,
https://github.com/simonw/datasette/issues/913#issuecomment-667430352,https://api.github.com/repos/simonw/datasette/issues/913,667430352,MDEyOklzc3VlQ29tbWVudDY2NzQzMDM1Mg==,9599,simonw,2020-07-31T23:52:10Z,2020-07-31T23:52:10Z,OWNER,The bigger question here is when this mechanism should be used in place of `metadata.json` or `metadata.yml`. Especially since I'm already considering renaming or reworking that mechanism since plugin configuration has nothing to do with database metadata: #493,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",670209331,Mechanism for passing additional options to `datasette my.db` that affect plugins,
https://github.com/simonw/datasette/issues/913#issuecomment-667429616,https://api.github.com/repos/simonw/datasette/issues/913,667429616,MDEyOklzc3VlQ29tbWVudDY2NzQyOTYxNg==,9599,simonw,2020-07-31T23:48:25Z,2020-07-31T23:49:59Z,OWNER,"I could let plugins add additional options to `datasette serve` - but what if two plugins both try to register an option with the same name?
A better solution could be to use the existing `--config` option - and allow plugins to register their own, namespaced config options. So you could do things like:
datasette my.db --config datasette-insert:unsafe:1
Maybe even drop the `datasette-` prefix?
datasette my.db --config insert:unsafe:1
I think I prefer keeping the prefix to be honest - it makes it more obvious that this is a setting which comes from a specific named plugin.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",670209331,Mechanism for passing additional options to `datasette my.db` that affect plugins,
https://github.com/simonw/datasette/issues/913#issuecomment-667429690,https://api.github.com/repos/simonw/datasette/issues/913,667429690,MDEyOklzc3VlQ29tbWVudDY2NzQyOTY5MA==,9599,simonw,2020-07-31T23:48:48Z,2020-07-31T23:48:48Z,OWNER,"Here's the code in Datasette that parses `--config` options at the moment:
https://github.com/simonw/datasette/blob/7ca8c0521ac1ea48a3cd8d0fe9275d1316e54b43/datasette/cli.py#L25-L40","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",670209331,Mechanism for passing additional options to `datasette my.db` that affect plugins,
https://github.com/simonw/datasette/issues/849#issuecomment-667424128,https://api.github.com/repos/simonw/datasette/issues/849,667424128,MDEyOklzc3VlQ29tbWVudDY2NzQyNDEyOA==,9599,simonw,2020-07-31T23:21:56Z,2020-07-31T23:23:24Z,OWNER,"I'm going to change the default branch on the GitHub repository. If something breaks I can always change it back again.
Done that! Default is now `main`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639072811,Rename master branch to main,
https://github.com/simonw/datasette/issues/849#issuecomment-667424020,https://api.github.com/repos/simonw/datasette/issues/849,667424020,MDEyOklzc3VlQ29tbWVudDY2NzQyNDAyMA==,9599,simonw,2020-07-31T23:21:30Z,2020-07-31T23:21:30Z,OWNER,https://github.com/simonw/datasette/tree/main branch now exists and will automatically mirror master (and vice-versa).,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639072811,Rename master branch to main,
https://github.com/simonw/datasette/issues/849#issuecomment-667295759,https://api.github.com/repos/simonw/datasette/issues/849,667295759,MDEyOklzc3VlQ29tbWVudDY2NzI5NTc1OQ==,9599,simonw,2020-07-31T18:45:35Z,2020-07-31T18:45:35Z,OWNER,"Watch out for places in the documentation that might link to `master` - e.g. here:
https://github.com/simonw/datasette/blob/2d7fa8b9058dfbf9c7c371cdeec115d32a177dc9/docs/custom_templates.rst#L247","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639072811,Rename master branch to main,
https://github.com/simonw/sqlite-utils/issues/124#issuecomment-664105302,https://api.github.com/repos/simonw/sqlite-utils/issues/124,664105302,MDEyOklzc3VlQ29tbWVudDY2NDEwNTMwMg==,9599,simonw,2020-07-27T03:54:24Z,2020-07-30T22:57:51Z,OWNER,"Documentation: https://github.com/simonw/sqlite-utils/commit/814d4a7f90991be865d38aac45ff12e36df1c67d?short_path=7240b7c#diff-7240b7c71b1a8194da0c001c64fc8d40
> You can pass named parameters to the query using -p:
>
> $ sqlite-utils query dogs.db ""select :num * :num2"" -p num 5 -p num2 6
> [{"":num * :num2"": 30}]
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665802405,sqlite-utils query should support named parameters,
https://github.com/simonw/sqlite-utils/issues/129#issuecomment-666752039,https://api.github.com/repos/simonw/sqlite-utils/issues/129,666752039,MDEyOklzc3VlQ29tbWVudDY2Njc1MjAzOQ==,9599,simonw,2020-07-30T22:40:55Z,2020-07-30T22:40:55Z,OWNER,"This should be a separate command from `insert-files`. SQLite Archives should use a table with this schema:
```sql
CREATE TABLE sqlar(
name TEXT PRIMARY KEY, -- name of the file
mode INT, -- access permissions
mtime INT, -- last modification time
sz INT, -- original file size
data BLOB -- compressed content
);
```
`insert-files` currently treats the table name as a required argument - but it's not necessary for this table. Also there shouldn't be any support for the `--column` option.
So if I write this command it should be this instead:
sqlite-utils sqlar files.db file.txt file2.txt
But at that point, why bother? Users can use `sqlite3 files.db -Ac *.txt` instead.
So I'm not going to bother implementing this.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",668308777,"""insert-files --sqlar"" for creating SQLite archives",
https://github.com/simonw/sqlite-utils/issues/127#issuecomment-666063689,https://api.github.com/repos/simonw/sqlite-utils/issues/127,666063689,MDEyOklzc3VlQ29tbWVudDY2NjA2MzY4OQ==,9599,simonw,2020-07-30T03:08:51Z,2020-07-30T03:08:51Z,OWNER,Documentation at the bottom of this section: https://github.com/simonw/sqlite-utils/blob/8fe1e6d1be021aeeb8f08b0f77f03b75a83b6f75/docs/cli.rst#inserting-binary-data-from-files,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",666040390,Ability to insert files piped to insert-files stdin,
https://github.com/simonw/sqlite-utils/issues/127#issuecomment-666047928,https://api.github.com/repos/simonw/sqlite-utils/issues/127,666047928,MDEyOklzc3VlQ29tbWVudDY2NjA0NzkyOA==,9599,simonw,2020-07-30T02:31:05Z,2020-07-30T02:31:05Z,OWNER,"Maybe could do this using an improved version of this lambda? Could teach it to look for `-` and read from `sys.stdin` if it sees it.
https://github.com/simonw/sqlite-utils/blob/710454d72aed5094573e642344fd075a0ef5372c/sqlite_utils/cli.py#L839","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",666040390,Ability to insert files piped to insert-files stdin,
https://github.com/simonw/sqlite-utils/issues/129#issuecomment-666046819,https://api.github.com/repos/simonw/sqlite-utils/issues/129,666046819,MDEyOklzc3VlQ29tbWVudDY2NjA0NjgxOQ==,9599,simonw,2020-07-30T02:28:34Z,2020-07-30T02:28:34Z,OWNER,This code looks useful as inspiration: https://github.com/j4mie/sqlsite/blob/f2dadb8db5ed7880f8872b6591d8cb1487f777ea/sqlsite/sqlar.py,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",668308777,"""insert-files --sqlar"" for creating SQLite archives",
https://github.com/simonw/datasette/issues/909#issuecomment-666010395,https://api.github.com/repos/simonw/datasette/issues/909,666010395,MDEyOklzc3VlQ29tbWVudDY2NjAxMDM5NQ==,9599,simonw,2020-07-30T00:56:17Z,2020-07-30T00:56:17Z,OWNER,"```
$ curl -I https://latest.datasette.io/fixtures.db
HTTP/1.1 200 OK
content-disposition: attachment; filename=""fixtures.db""
content-type: application/octet-stream
Date: Thu, 30 Jul 2020 00:56:05 GMT
Server: Google Frontend
Transfer-Encoding: chunked
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",667467128,AsgiFileDownload: filename not correctly passed,
https://github.com/simonw/datasette/issues/909#issuecomment-665854704,https://api.github.com/repos/simonw/datasette/issues/909,665854704,MDEyOklzc3VlQ29tbWVudDY2NTg1NDcwNA==,9599,simonw,2020-07-29T19:22:31Z,2020-07-29T19:22:31Z,OWNER,"I think this results in a bug where the ""download database"" link doesn't include the correct filename: https://github.com/simonw/datasette/blob/549b1c2063db48c4622ee5c7b478a1e3cbc1ac07/datasette/views/database.py#L110-L131","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",667467128,AsgiFileDownload: filename not correctly passed,
https://github.com/simonw/sqlite-utils/issues/128#issuecomment-664683608,https://api.github.com/repos/simonw/sqlite-utils/issues/128,664683608,MDEyOklzc3VlQ29tbWVudDY2NDY4MzYwOA==,9599,simonw,2020-07-27T23:09:22Z,2020-07-27T23:09:22Z,OWNER,"This seems to work, but needs more tests:
```diff
diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py
index d6b9ecf..ee26433 100644
--- a/sqlite_utils/db.py
+++ b/sqlite_utils/db.py
@@ -7,6 +7,7 @@ import itertools
import json
import os
import pathlib
+import uuid
SQLITE_MAX_VARS = 999
@@ -40,11 +41,13 @@ COLUMN_TYPE_MAPPING = {
str: ""TEXT"",
bytes.__class__: ""BLOB"",
bytes: ""BLOB"",
+ memoryview: ""BLOB"",
datetime.datetime: ""TEXT"",
datetime.date: ""TEXT"",
datetime.time: ""TEXT"",
decimal.Decimal: ""FLOAT"",
None.__class__: ""TEXT"",
+ uuid.UUID: ""TEXT"",
# SQLite explicit types
""TEXT"": ""TEXT"",
""INTEGER"": ""INTEGER"",
@@ -1336,6 +1339,8 @@ def jsonify_if_needed(value):
return json.dumps(value, default=repr)
elif isinstance(value, (datetime.time, datetime.date, datetime.datetime)):
return value.isoformat()
+ elif isinstance(value, uuid.UUID):
+ return str(value)
else:
return value
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",666639051,Support UUID and memoryview types,
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-664163524,https://api.github.com/repos/simonw/sqlite-utils/issues/122,664163524,MDEyOklzc3VlQ29tbWVudDY2NDE2MzUyNA==,9599,simonw,2020-07-27T07:10:41Z,2020-07-27T07:10:41Z,OWNER,Docs: https://github.com/simonw/sqlite-utils/blob/ebc802f7ff0e640b6ae11ea525290fea0115228c/docs/cli.rst#inserting-binary-data-from-files,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/127#issuecomment-664163206,https://api.github.com/repos/simonw/sqlite-utils/issues/127,664163206,MDEyOklzc3VlQ29tbWVudDY2NDE2MzIwNg==,9599,simonw,2020-07-27T07:10:05Z,2020-07-27T07:10:05Z,OWNER,I tried to get this working but it was a bit tricky because `-` doesn't behave like a regular `pathlib.Path` - needs a bit more thought on how the implementation would work.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",666040390,Ability to insert files piped to insert-files stdin,
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-664128071,https://api.github.com/repos/simonw/sqlite-utils/issues/122,664128071,MDEyOklzc3VlQ29tbWVudDY2NDEyODA3MQ==,9599,simonw,2020-07-27T05:30:54Z,2020-07-27T05:30:54Z,OWNER,"Inserting files by piping them in should work - but since a filename cannot be derived this will need a `--name blah.gif` option.
cat blah.gif | sqlite-utils insert-files files.db files - --name=blah.gif","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-664127741,https://api.github.com/repos/simonw/sqlite-utils/issues/122,664127741,MDEyOklzc3VlQ29tbWVudDY2NDEyNzc0MQ==,9599,simonw,2020-07-27T05:29:48Z,2020-07-27T05:29:48Z,OWNER,"Test command:
```
sqlite-utils insert-files gifs.db *.gif \
-c filename:filename \
-c filepath:filepath \
-c absolutepath:absolutepath \
-c sha256:sha256 \
-c md5:md5 \
-c content:content \
-c mtime:mtime \
-c ctime:ctime \
-c mtime_iso:mtime_iso \
-c ctime_iso:ctime_iso \
-c size:size \
--pk absolutepath
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-663931279,https://api.github.com/repos/simonw/sqlite-utils/issues/122,663931279,MDEyOklzc3VlQ29tbWVudDY2MzkzMTI3OQ==,9599,simonw,2020-07-26T03:33:23Z,2020-07-27T04:30:49Z,OWNER,"One idea: `sqlite-utils insert-files`
It could work something like this:
sqlite-utils insert-files files.db /tmp/blah.jpg /tmp/foo.gif \
--table files \
-c key:filename -c hash:sha256 -c body:content \
--pk key
This would insert those two image files into the database in a table called `files` with a schema that looks something like this:
```sql
CREATE TABLE files (
key text primary key,
hash text,
body blob
);
```
The `-c key:filename` options here are the most interesting: they let you create the table with a specific layout. The bit before the `:` is the column name. The bit after the `:` can be a range of different things:
- `filename` - just the filename
- `filepath` - the full filepath (provided on the command-line)
- `absolutepath` - the filepath expanded to start with `/home/...` or whatever
- `sha256` - the SHA256 of the contents
- `md5` - the MD5
- `content` - the binary content itself
- `mtime` - the mtime (floating point timestamp)
- `ctime` - the ctime (floating point timestamp)
- `mtime_iso` - the mtime as an ISO datetime
- `ctime_iso` - the mtime as an ISO datetime
- `size` - the size of the file in bytes","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-664106621,https://api.github.com/repos/simonw/sqlite-utils/issues/114,664106621,MDEyOklzc3VlQ29tbWVudDY2NDEwNjYyMQ==,9599,simonw,2020-07-27T04:01:13Z,2020-07-27T04:01:13Z,OWNER,Work in progress in `transform` branch here: https://github.com/simonw/sqlite-utils/tree/transform,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/126#issuecomment-664106405,https://api.github.com/repos/simonw/sqlite-utils/issues/126,664106405,MDEyOklzc3VlQ29tbWVudDY2NDEwNjQwNQ==,9599,simonw,2020-07-27T04:00:08Z,2020-07-27T04:00:33Z,OWNER,"```
$ echo '[
{
""name"": ""transparent.gif"",
""content"": {
""$base64"": true,
""encoded"": ""R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7""
}
}
]' | sqlite-utils insert trans.db files - --pk=name
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665819048,Ability to insert binary data on the CLI using JSON,
https://github.com/simonw/sqlite-utils/issues/126#issuecomment-664065597,https://api.github.com/repos/simonw/sqlite-utils/issues/126,664065597,MDEyOklzc3VlQ29tbWVudDY2NDA2NTU5Nw==,9599,simonw,2020-07-27T00:51:11Z,2020-07-27T00:51:11Z,OWNER,"I'm going to implement this as the reverse of #125 - binary columns in JSON are now output like this:
```json
{
""name"": ""lorem.txt"",
""mode"": 33188,
""mtime"": 1595805965,
""sz"": 16984,
""data"": {
""$base64"": true,
""encoded"": ""eJzt0c1xAyEMBeC7q1ABHleR3HxNAQrIjmb4M0gelx+RTY7p4N2WBYT0vmufUknH8kq5lz5pqRFXsTOl3pYkE/NJnHXoStruJEVjc0mOCyTqq/ZMJnXEZW1Js2ZvRm5U+DPKk9hRWqjyvTFx0YfzhT6MpGmN2lR1fzxjyfVMD9dFrS+bnkleMpMam/ZGXgrX1I/K+5Au3S/9lNQRh0k4Gq/RUz8GiKfsQm+7JLsJ6fTo5JhVG00ZU76kZZkxePx49uIjnpNoJyYlWUsoaSl/CcVATje/Kxu13RANnrHweaH3V5Jh4jvGyKCnxJLiXPKhmW3fiCnG7Jql7RR3UvFo8jJ4z039dtOkTFmWzL1be9lt8A5II471m6vXy+l0BR/4wAc+8IEPfOADH/jABz7wgQ984AMf+MAHPvCBD3zgAx/4wAc+8IEPfOADH/jABz7wgQ984AMf+MAHPvCBD3zgAx/4wAc+8IEPfOADH/jABz7wgQ984PuP7xubBoN9""
}
}
]
```
So the `sqlite-utils insert` command should learn to spot `{""$base64"": true...}` values and base64 decode them before inserting them.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665819048,Ability to insert binary data on the CLI using JSON,
https://github.com/simonw/sqlite-utils/issues/125#issuecomment-664065341,https://api.github.com/repos/simonw/sqlite-utils/issues/125,664065341,MDEyOklzc3VlQ29tbWVudDY2NDA2NTM0MQ==,9599,simonw,2020-07-27T00:49:41Z,2020-07-27T00:49:41Z,OWNER,Documentation: https://github.com/simonw/sqlite-utils/commit/20e543e9a492f2e764caae73c38e87f18eaec444?short_path=7240b7c#diff-7240b7c71b1a8194da0c001c64fc8d40,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665817570,"Output binary columns in ""sqlite-utils query"" JSON",
https://github.com/simonw/sqlite-utils/issues/125#issuecomment-664062546,https://api.github.com/repos/simonw/sqlite-utils/issues/125,664062546,MDEyOklzc3VlQ29tbWVudDY2NDA2MjU0Ng==,9599,simonw,2020-07-27T00:33:03Z,2020-07-27T00:33:03Z,OWNER,"I'm going to imitate how Datasette solves this problem:
```json
[
{
""name"": ""lorem.txt"",
""mode"": 33188,
""mtime"": 1595805965,
""sz"": 16984,
""data"": {
""$base64"": true,
""encoded"": ""eJzt0c1xAyEMBeC7q1ABHleR3HxNAQrIjmb4M0gelx+RTY7p4N2WBYT0vmufUknH8kq5lz5pqRFXsTOl3pYkE/NJnHXoStruJEVjc0mOCyTqq/ZMJnXEZW1Js2ZvRm5U+DPKk9hRWqjyvTFx0YfzhT6MpGmN2lR1fzxjyfVMD9dFrS+bnkleMpMam/ZGXgrX1I/K+5Au3S/9lNQRh0k4Gq/RUz8GiKfsQm+7JLsJ6fTo5JhVG00ZU76kZZkxePx49uIjnpNoJyYlWUsoaSl/CcVATje/Kxu13RANnrHweaH3V5Jh4jvGyKCnxJLiXPKhmW3fiCnG7Jql7RR3UvFo8jJ4z039dtOkTFmWzL1be9lt8A5II471m6vXy+l0BR/4wAc+8IEPfOADH/jABz7wgQ984AMf+MAHPvCBD3zgAx/4wAc+8IEPfOADH/jABz7wgQ984AMf+MAHPvCBD3zgAx/4wAc+8IEPfOADH/jABz7wgQ984PuP7xubBoN9""
}
}
]
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665817570,"Output binary columns in ""sqlite-utils query"" JSON",
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-664048720,https://api.github.com/repos/simonw/sqlite-utils/issues/122,664048720,MDEyOklzc3VlQ29tbWVudDY2NDA0ODcyMA==,9599,simonw,2020-07-26T22:32:50Z,2020-07-26T22:33:20Z,OWNER,"This seems to work in creating a SQLite archive containing all `.gif` files in the current directory:
/usr/local/Cellar/sqlite/3.32.1/bin/sqlite3 archive.db -A -c *.gif
Then listing files like this:
```
$ /usr/local/Cellar/sqlite/3.32.1/bin/sqlite3 archive.db -A -t
copyable.gif
debug-allow.gif
flash.gif
table-md.gif
```
Here's the schema:
```
$ sqlite3 archive.db .schema
CREATE TABLE sqlar(
name TEXT PRIMARY KEY, -- name of the file
mode INT, -- access permissions
mtime INT, -- last modification time
sz INT, -- original file size
data BLOB -- compressed content
);
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-664048432,https://api.github.com/repos/simonw/sqlite-utils/issues/122,664048432,MDEyOklzc3VlQ29tbWVudDY2NDA0ODQzMg==,9599,simonw,2020-07-26T22:29:31Z,2020-07-26T22:29:31Z,OWNER,"I'm trying to play with `sqlite3 -A` on my Mac.
`sqlite3 -A` tells me that it's an unknown option - but I used `brew info sqlite` to find my homebrew installed version and it turns out this works:
```
% /usr/local/Cellar/sqlite/3.32.1/bin/sqlite3 -A
Wrong number of arguments. Usage:
.archive ... Manage SQL archives
Each command must have exactly one of the following options:
-c, --create Create a new archive
-u, --update Add or update files with changed mtime
-i, --insert Like -u but always add even if unchanged
-t, --list List contents of archive
-x, --extract Extract files from archive
Optional arguments:
-v, --verbose Print each filename as it is processed
-f FILE, --file FILE Use archive FILE (default is current db)
-a FILE, --append FILE Open FILE using the apndvfs VFS
-C DIR, --directory DIR Read/extract files from directory DIR
-n, --dryrun Show the SQL that would have occurred
Examples:
.ar -cf ARCHIVE foo bar # Create ARCHIVE from files foo and bar
.ar -tf ARCHIVE # List members of ARCHIVE
.ar -xvf ARCHIVE # Verbosely extract files from ARCHIVE
See also:
http://sqlite.org/cli.html#sqlar_archive_support
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-664013338,https://api.github.com/repos/simonw/sqlite-utils/issues/122,664013338,MDEyOklzc3VlQ29tbWVudDY2NDAxMzMzOA==,9599,simonw,2020-07-26T16:57:35Z,2020-07-26T16:57:35Z,OWNER,"I should consider easy compatibility with https://www.sqlite.org/sqlar.html
> An SQLite Archive is an ordinary SQLite database file that contains the following table as part of its schema:
> ```
> CREATE TABLE sqlar(
> name TEXT PRIMARY KEY, -- name of the file
> mode INT, -- access permissions
> mtime INT, -- last modification time
> sz INT, -- original file size
> data BLOB -- compressed content
> );
> ```
> Each row of the SQLAR table holds the content of a single file. The filename (the full pathname relative to the root of the archive) is in the ""name"" field. The ""mode"" field is an integer which is the unix-style access permissions for the file. ""mtime"" is the modification time of the file in seconds since 1970. ""sz"" is the original uncompressed size of the file. The ""data"" field contains the file content. The content is usually compressed using [Deflate](http://zlib.net/), though not always. If the ""sz"" field is equal to the size of the ""data"" field, then the content is stored uncompressed.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/125#issuecomment-664012247,https://api.github.com/repos/simonw/sqlite-utils/issues/125,664012247,MDEyOklzc3VlQ29tbWVudDY2NDAxMjI0Nw==,9599,simonw,2020-07-26T16:48:46Z,2020-07-26T16:48:46Z,OWNER,"I could solve round tripping (at least a bit) by allowing insert to be run with a flag that says ""these columns are base64 encoded, store the decoded data in a BLOB"".
That would solve inserting binary data using JSON too.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665817570,"Output binary columns in ""sqlite-utils query"" JSON",
https://github.com/simonw/sqlite-utils/issues/125#issuecomment-664012148,https://api.github.com/repos/simonw/sqlite-utils/issues/125,664012148,MDEyOklzc3VlQ29tbWVudDY2NDAxMjE0OA==,9599,simonw,2020-07-26T16:47:51Z,2020-07-26T16:47:51Z,OWNER,Best solution I can think of is to return the data as base64. It's a bit nasty since it means you can't round trip it back again.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665817570,"Output binary columns in ""sqlite-utils query"" JSON",
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-663931426,https://api.github.com/repos/simonw/sqlite-utils/issues/122,663931426,MDEyOklzc3VlQ29tbWVudDY2MzkzMTQyNg==,9599,simonw,2020-07-26T03:35:58Z,2020-07-26T16:44:33Z,OWNER,Related: #123 (`--raw` option),"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-663931662,https://api.github.com/repos/simonw/sqlite-utils/issues/122,663931662,MDEyOklzc3VlQ29tbWVudDY2MzkzMTY2Mg==,9599,simonw,2020-07-26T03:40:29Z,2020-07-26T03:40:29Z,OWNER,Maybe support `--replace` for replacing images with an existing primary key.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/sqlite-utils/issues/122#issuecomment-663931317,https://api.github.com/repos/simonw/sqlite-utils/issues/122,663931317,MDEyOklzc3VlQ29tbWVudDY2MzkzMTMxNw==,9599,simonw,2020-07-26T03:33:54Z,2020-07-26T03:33:54Z,OWNER,"The command also accepts one or more directories, in which case it will recursively scan them for all files that they contain.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665700495,CLI utility for inserting binary files into SQLite,
https://github.com/simonw/datasette/issues/906#issuecomment-663779460,https://api.github.com/repos/simonw/datasette/issues/906,663779460,MDEyOklzc3VlQ29tbWVudDY2Mzc3OTQ2MA==,9599,simonw,2020-07-25T00:07:10Z,2020-07-25T00:07:10Z,OWNER,"Demo of the new functionality:
* https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22root%22%0D%0A%7D&allow=false
* https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22root%22%0D%0A%7D&allow=true","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665400224,"""allow"": true for anyone, ""allow"": false for nobody",
https://github.com/simonw/datasette/issues/908#issuecomment-663779179,https://api.github.com/repos/simonw/datasette/issues/908,663779179,MDEyOklzc3VlQ29tbWVudDY2Mzc3OTE3OQ==,9599,simonw,2020-07-25T00:05:48Z,2020-07-25T00:06:15Z,OWNER,"The documentation section here now has a bunch of different links to live demos illustrating different ""allow"" block syntax: https://github.com/simonw/datasette/blob/092874202c8748d6e0d4800eaf707c0145d95ffe/docs/authentication.rst#defining-permissions-with-allow-blocks","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665407663,"Interactive debugging tool for ""allow"" blocks",
https://github.com/simonw/datasette/issues/906#issuecomment-663767678,https://api.github.com/repos/simonw/datasette/issues/906,663767678,MDEyOklzc3VlQ29tbWVudDY2Mzc2NzY3OA==,9599,simonw,2020-07-24T23:07:22Z,2020-07-24T23:07:22Z,OWNER,"Illustration of current system:
https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22terry%22%0D%0A%7D&allow=null - `null` allows
https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22terry%22%0D%0A%7D&allow={} - `{}` denies","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665400224,"""allow"": true for anyone, ""allow"": false for nobody",
https://github.com/simonw/datasette/issues/908#issuecomment-663765308,https://api.github.com/repos/simonw/datasette/issues/908,663765308,MDEyOklzc3VlQ29tbWVudDY2Mzc2NTMwOA==,9599,simonw,2020-07-24T22:57:15Z,2020-07-24T22:57:15Z,OWNER,Tool lives at https://latest.datasette.io/-/allow-debug,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665407663,"Interactive debugging tool for ""allow"" blocks",
https://github.com/simonw/datasette/issues/907#issuecomment-663764203,https://api.github.com/repos/simonw/datasette/issues/907,663764203,MDEyOklzc3VlQ29tbWVudDY2Mzc2NDIwMw==,9599,simonw,2020-07-24T22:53:07Z,2020-07-24T22:53:07Z,OWNER,"Actually that is already covered here:
https://github.com/simonw/datasette/blob/6be5654ffab282e8cf39cc138ba2d4496ebc7407/docs/authentication.rst#L158","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665403403,Allow documentation doesn't explain what happens with multiple allow keys,
https://github.com/simonw/datasette/issues/908#issuecomment-663726318,https://api.github.com/repos/simonw/datasette/issues/908,663726318,MDEyOklzc3VlQ29tbWVudDY2MzcyNjMxOA==,9599,simonw,2020-07-24T20:43:57Z,2020-07-24T20:45:38Z,OWNER,"I can implement this as a plugin. Or it could ship as part of Datasette, somewhere under the `/-/` namespace like the `PermissionsDebugView` and `MessagesDebugView` tools.
I'm going to ship it in Datasette core, to further reinforce the philosophy that debugging tools are important.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665407663,"Interactive debugging tool for ""allow"" blocks",
https://github.com/simonw/datasette/issues/907#issuecomment-663726146,https://api.github.com/repos/simonw/datasette/issues/907,663726146,MDEyOklzc3VlQ29tbWVudDY2MzcyNjE0Ng==,9599,simonw,2020-07-24T20:43:27Z,2020-07-24T20:43:27Z,OWNER,"It might be good to have a little interactive tool which helps debug these things, since there are quite a few edge-cases and the damage caused if people use them incorrectly is substantial.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665403403,Allow documentation doesn't explain what happens with multiple allow keys,
https://github.com/simonw/datasette/issues/456#issuecomment-663724675,https://api.github.com/repos/simonw/datasette/issues/456,663724675,MDEyOklzc3VlQ29tbWVudDY2MzcyNDY3NQ==,9599,simonw,2020-07-24T20:39:17Z,2020-07-24T20:39:17Z,OWNER,Yes this is still a bug!,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",442327592,Installing installs the tests package,
https://github.com/simonw/datasette/pull/902#issuecomment-663724425,https://api.github.com/repos/simonw/datasette/issues/902,663724425,MDEyOklzc3VlQ29tbWVudDY2MzcyNDQyNQ==,9599,simonw,2020-07-24T20:38:42Z,2020-07-24T20:38:42Z,OWNER,Thanks for spotting this!,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",662439034,Don't install tests package,
https://github.com/simonw/datasette/issues/906#issuecomment-663720907,https://api.github.com/repos/simonw/datasette/issues/906,663720907,MDEyOklzc3VlQ29tbWVudDY2MzcyMDkwNw==,9599,simonw,2020-07-24T20:29:24Z,2020-07-24T20:29:24Z,OWNER,"Here are the existing test cases:
https://github.com/simonw/datasette/blob/2115d7e3457b48b3cf9c81551b9fed2d0e9cd111/tests/test_utils.py#L468-L505","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",665400224,"""allow"": true for anyone, ""allow"": false for nobody",
https://github.com/simonw/datasette/issues/905#issuecomment-662241702,https://api.github.com/repos/simonw/datasette/issues/905,662241702,MDEyOklzc3VlQ29tbWVudDY2MjI0MTcwMg==,9599,simonw,2020-07-22T04:59:46Z,2020-07-22T04:59:46Z,OWNER,"Deployed and working:
```
% curl -I 'https://fivethirtyeight.datasettes.com/fivethirtyeight.db'
HTTP/1.1 200 OK
Date: Wed, 22 Jul 2020 04:59:23 GMT
Content-Type: application/octet-stream
Content-Length: 281845760
Connection: keep-alive
Set-Cookie: __cfduid=d550b15c99aa59144e49557ced64fc48a1595393963; expires=Fri, 21-Aug-20 04:59:23 GMT; path=/; domain=.datasettes.com; HttpOnly; SameSite=Lax
Via: 1.1 vegur
Cache-Control: max-age=14400
CF-Cache-Status: MISS
Accept-Ranges: bytes
cf-request-id: 04167d0c7100000540f98e8200000001
Expect-CT: max-age=604800, report-uri=""https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct""
Server: cloudflare
CF-RAY: 5b6a978d89b30540-LAX
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",663317875,/database.db download should include content-length header,
https://github.com/simonw/datasette/issues/905#issuecomment-662114881,https://api.github.com/repos/simonw/datasette/issues/905,662114881,MDEyOklzc3VlQ29tbWVudDY2MjExNDg4MQ==,9599,simonw,2020-07-21T21:25:37Z,2020-07-21T21:25:37Z,OWNER,I can use `aiofiles.os.stat` for this: https://github.com/Tinche/aiofiles/blob/master/aiofiles/os.py,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",663317875,/database.db download should include content-length header,
https://github.com/simonw/datasette/issues/898#issuecomment-660419792,https://api.github.com/repos/simonw/datasette/issues/898,660419792,MDEyOklzc3VlQ29tbWVudDY2MDQxOTc5Mg==,9599,simonw,2020-07-18T03:57:46Z,2020-07-18T03:57:46Z,OWNER,"This requires some thought. There are various testing utilities that don't exist yet that plugins might benefit from - off the top of my head:
- `assert_permissions_checked`
- `assert_template_rendered`
I should resist the temptation to provide a reusable version of `make_app_client` that provides a fully configured Datasette instance because I need to be able to change the design of the Datasette `fixtures.db` test database without accidentally breaking any plugins that depend on it.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",659873662,datasette.utils.testing module,
https://github.com/simonw/datasette/issues/898#issuecomment-660419499,https://api.github.com/repos/simonw/datasette/issues/898,660419499,MDEyOklzc3VlQ29tbWVudDY2MDQxOTQ5OQ==,9599,simonw,2020-07-18T03:55:13Z,2020-07-18T03:55:13Z,OWNER,Maybe I should make `httpx` a testing dependency of Datasette itself. It's usage is already encouraged in plugins by https://datasette.readthedocs.io/en/stable/testing_plugins.html,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",659873662,datasette.utils.testing module,
https://github.com/simonw/datasette/issues/897#issuecomment-660318063,https://api.github.com/repos/simonw/datasette/issues/897,660318063,MDEyOklzc3VlQ29tbWVudDY2MDMxODA2Mw==,9599,simonw,2020-07-17T20:16:02Z,2020-07-17T20:16:02Z,OWNER,Documentation here: https://datasette.readthedocs.io/en/latest/internals.html#request-object,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",659580487,Request method for retrieving the unparsed request body,
https://github.com/simonw/datasette/issues/896#issuecomment-659773897,https://api.github.com/repos/simonw/datasette/issues/896,659773897,MDEyOklzc3VlQ29tbWVudDY1OTc3Mzg5Nw==,9599,simonw,2020-07-17T01:26:08Z,2020-07-17T01:26:08Z,OWNER,I manually tested it with those plugins and it seems to interoperate just fine - since both of those use `` tags for the cases that I care about so they're already expecting white-space to be pre wrapped in some way.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",658476055,Use white-space: pre-wrap on ALL table cell contents,
https://github.com/simonw/datasette/issues/896#issuecomment-659734703,https://api.github.com/repos/simonw/datasette/issues/896,659734703,MDEyOklzc3VlQ29tbWVudDY1OTczNDcwMw==,9599,simonw,2020-07-16T23:34:57Z,2020-07-16T23:34:57Z,OWNER,"I'm worried about how this will interact with some of the plugins:
* https://github.com/simonw/datasette-json-html
* https://github.com/simonw/datasette-pretty-json","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",658476055,Use white-space: pre-wrap on ALL table cell contents,
https://github.com/simonw/datasette/issues/896#issuecomment-659615034,https://api.github.com/repos/simonw/datasette/issues/896,659615034,MDEyOklzc3VlQ29tbWVudDY1OTYxNTAzNA==,9599,simonw,2020-07-16T19:14:07Z,2020-07-16T19:14:07Z,OWNER,"Demo: https://srccon-2020.datasette.io/srccon?sql=select+id%2C+day%2C+time%2C+event_name%2C+event_description%2C+facilitators+from+sessions+order+by+event_dtstart+limit+101
I really like this:
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",658476055,Use white-space: pre-wrap on ALL table cell contents,
https://github.com/simonw/datasette/issues/896#issuecomment-659610687,https://api.github.com/repos/simonw/datasette/issues/896,659610687,MDEyOklzc3VlQ29tbWVudDY1OTYxMDY4Nw==,9599,simonw,2020-07-16T19:05:43Z,2020-07-16T19:05:43Z,OWNER,I'm going to give this a go - if it turns out to be a bad idea I can revert it back out again.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",658476055,Use white-space: pre-wrap on ALL table cell contents,
https://github.com/simonw/datasette/issues/895#issuecomment-659085528,https://api.github.com/repos/simonw/datasette/issues/895,659085528,MDEyOklzc3VlQ29tbWVudDY1OTA4NTUyOA==,9599,simonw,2020-07-16T00:32:47Z,2020-07-16T00:32:47Z,OWNER,"This was added in https://github.com/simonw/datasette/commit/504196341c49840270bd75ea1a1871ef386ba7ea - here's the relevant code (which only applies on the table page, not the query page):
https://github.com/simonw/datasette/blob/d6e03b04302a0852e7133dc030eab50177c37be7/datasette/views/table.py#L196-L204","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",657747959,SQL query output should show numeric values in a different colour,
https://github.com/simonw/datasette/issues/892#issuecomment-657268433,https://api.github.com/repos/simonw/datasette/issues/892,657268433,MDEyOklzc3VlQ29tbWVudDY1NzI2ODQzMw==,9599,simonw,2020-07-12T20:02:17Z,2020-07-12T20:02:35Z,OWNER,"Fixed https://datasette.readthedocs.io/en/latest/
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",655465863,"""latest"" in new documentation navbar is invisible",
https://github.com/simonw/datasette/issues/892#issuecomment-657268051,https://api.github.com/repos/simonw/datasette/issues/892,657268051,MDEyOklzc3VlQ29tbWVudDY1NzI2ODA1MQ==,9599,simonw,2020-07-12T19:58:24Z,2020-07-12T19:58:24Z,OWNER,"```css
.wy-side-nav-search > div.version {
margin-top: -.4045em;
margin-bottom: .809em;
font-weight: normal;
color: rgba(255,255,255,0.3);
}
```
Fix can go here: https://github.com/simonw/datasette/blob/ee0ef016523a765b6ef6eaa43cad9ad568f78ae4/docs/_static/css/custom.css#L1-L3","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",655465863,"""latest"" in new documentation navbar is invisible",
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-656363548,https://api.github.com/repos/simonw/sqlite-utils/issues/114,656363548,MDEyOklzc3VlQ29tbWVudDY1NjM2MzU0OA==,9599,simonw,2020-07-09T21:37:28Z,2020-07-09T21:37:28Z,OWNER,"I'm going to add a second method `.transform_table_sql(...)` - which returns the SQL that would have been executed but does NOT execute it.
Advanced callers can use this to include their own additional steps in the same transaction - e.g. recreating views or triggers.
More importantly it gives me a useful hook for writing some unit tests against the generated SQL.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655786374,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655786374,MDEyOklzc3VlQ29tbWVudDY1NTc4NjM3NA==,9599,simonw,2020-07-08T22:16:54Z,2020-07-08T22:16:54Z,OWNER,"According to https://www.sqlite.org/lang_altertable.html#making_other_kinds_of_table_schema_changes the hardest bits to consider are how to deal with existing foreign key relationships, triggers and views.
I'm OK leaving views as an exercise for the caller - many of these transformations may not need any view changes at all.
Foreign key relationships are important: it should handle these automatically as effectively as possible.
Likewise trigger changes: need to think about what this means.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655785396,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655785396,MDEyOklzc3VlQ29tbWVudDY1NTc4NTM5Ng==,9599,simonw,2020-07-08T22:14:10Z,2020-07-08T22:14:10Z,OWNER,"Work in progress: not quite right yet, I need smarter logic for how renamed columns are reflected in the generated `INSERT INTO ... SELECT ...` query:
```python
def transform_table(
self,
columns=None,
rename=None,
change_type=None,
pk=None,
foreign_keys=None,
column_order=None,
not_null=None,
defaults=None,
hash_id=None,
extracts=None,
):
assert self.exists(), ""Cannot transform a table that doesn't exist yet""
columns = columns or self.columns_dict
if rename is not None or change_type is not None:
columns = {rename.get(key, key): change_type.get(key, value) for key, value in columns.items()}
new_table_name = ""{}_new_{}"".format(self.name, os.urandom(6).hex())
previous_columns = set(self.columns_dict.keys())
with self.db.conn:
columns = {name: value for (name, value) in columns.items()}
new_table = self.db.create_table(
new_table_name,
columns,
pk=pk,
foreign_keys=foreign_keys,
column_order=column_order,
not_null=not_null,
defaults=defaults,
hash_id=hash_id,
extracts=extracts,
)
# Copy across data - but only for columns that exist in both
new_columns = set(columns.keys())
columns_to_copy = new_columns.intersection(previous_columns)
copy_sql = ""INSERT INTO [{new_table}] ({new_cols}) SELECT {old_cols} FROM [{old_table}]"".format(
new_table=new_table_name,
old_table=self.name,
old_cols="", "".join(""[{}]"".format(col) for col in columns_to_copy),
new_cols="", "".join(""[{}]"".format(rename.get(col, col)) for col in columns_to_copy),
)
self.db.conn.execute(copy_sql)
# Drop the old table
self.db.conn.execute(""DROP TABLE [{}]"".format(self.name))
# Rename the new one
self.db.conn.execute(
""ALTER TABLE [{}] RENAME TO [{}]"".format(new_table_name, self.name)
)
return self
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655783875,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655783875,MDEyOklzc3VlQ29tbWVudDY1NTc4Mzg3NQ==,9599,simonw,2020-07-08T22:09:51Z,2020-07-08T22:10:16Z,OWNER,I can have a convenient `change_type={...}` parameter for changing column types too.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655782477,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655782477,MDEyOklzc3VlQ29tbWVudDY1NTc4MjQ3Nw==,9599,simonw,2020-07-08T22:06:23Z,2020-07-08T22:06:23Z,OWNER,"Thinking about the method signature:
```python
def transform_table(
self,
columns,
pk=None,
foreign_keys=None,
column_order=None,
not_null=None,
defaults=None,
hash_id=None,
extracts=None,
):
```
This requires the caller to provide the exact set of columns for the new table.
It would be useful if this was optional - if you could omit the columns and have it automatically use the previous columns. This would let you change things like the primary key or the column order using the other arguments.
Even better: allow column renaming using an optional `rename={...}` argument:
```python
db[""dogs""].transform_table(rename={""name"": ""dog_name""})
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655778058,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655778058,MDEyOklzc3VlQ29tbWVudDY1NTc3ODA1OA==,9599,simonw,2020-07-08T21:54:30Z,2020-07-08T21:54:30Z,OWNER,"Don't forget this step:
> If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF. ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655677909,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655677909,MDEyOklzc3VlQ29tbWVudDY1NTY3NzkwOQ==,9599,simonw,2020-07-08T18:16:39Z,2020-07-08T18:16:39Z,OWNER,"Since neither the term ""transform"" or ""migrate"" are used in the codebase at the moment, I think I'll go with `.transform_table()` - that leaves the term ""migrate"" available for any future database migrations system (similar to Django's).","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655677396,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655677396,MDEyOklzc3VlQ29tbWVudDY1NTY3NzM5Ng==,9599,simonw,2020-07-08T18:15:39Z,2020-07-08T18:15:39Z,OWNER,"Alternative possible names:
- `.transform_table()`
- `.migrate()`
- `.transform()`
I'm torn between `.migrate_table()` and `.transform_table()`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655677099,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655677099,MDEyOklzc3VlQ29tbWVudDY1NTY3NzA5OQ==,9599,simonw,2020-07-08T18:15:02Z,2020-07-08T18:15:02Z,OWNER,"I'm not so keen on that chained API - it's pretty complicated.
Here's an idea for a much simpler interface. Essentially it lets you say ""take table X and migrate its contents to a new table with this structure - then atomically rename the tables to switch them"":
```python
db[""mytable""].migrate_table({""id"": int, ""name"": str""}, pk=""id"")
```
The `migrate_table()` method would take the same exact signature as the `table.create()` method: https://github.com/simonw/sqlite-utils/blob/a236a6bc771a5a6a9d7e814f1986d461afc422d2/sqlite_utils/db.py#L615-L625","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/issues/119#issuecomment-655674910,https://api.github.com/repos/simonw/sqlite-utils/issues/119,655674910,MDEyOklzc3VlQ29tbWVudDY1NTY3NDkxMA==,9599,simonw,2020-07-08T18:10:18Z,2020-07-08T18:10:18Z,OWNER,"This will work similar to how `.add_foreign_keys()` works: turn on `writable_schema` and rewrite the `sql` for that table in the `sqlite_master` table.
Here's that code today - it could be adapted to include removal of foreign keys that we no longer want:
https://github.com/simonw/sqlite-utils/blob/a236a6bc771a5a6a9d7e814f1986d461afc422d2/sqlite_utils/db.py#L391-L401","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",652700770,Ability to remove a foreign key,
https://github.com/simonw/sqlite-utils/issues/121#issuecomment-655673896,https://api.github.com/repos/simonw/sqlite-utils/issues/121,655673896,MDEyOklzc3VlQ29tbWVudDY1NTY3Mzg5Ng==,9599,simonw,2020-07-08T18:08:11Z,2020-07-08T18:08:11Z,OWNER,"I'm with you on most of this. Completely agreed that the CLI should do everything in a transaction.
The one thing I'm not keen on is forcing calling code to explicitly start a transaction, for a couple of reasons:
1. It will break all of the existing code out there
2. It doesn't match to how I most commonly use this library - as an interactive tool in a Jupyter notebook, where I'm generally working against a brand new scratch database and any errors don't actually matter
So... how about this: IF you wrap your code in a `with db:` block then the `.insert()` and suchlike methods expect you to manage transactions yourself. But if you don't use the context manager they behave like they do at the moment (or maybe a bit more sensibly).
That way existing code works as it does today, lazy people like me can call `.insert()` without thinking about transactions, but people writing actual production code (as opposed to Jupyter hacks) have a sensible way to take control of the transactions themselves.","{""total_count"": 1, ""+1"": 1, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",652961907,Improved (and better documented) support for transactions,
https://github.com/simonw/sqlite-utils/pull/118#issuecomment-655653292,https://api.github.com/repos/simonw/sqlite-utils/issues/118,655653292,MDEyOklzc3VlQ29tbWVudDY1NTY1MzI5Mg==,9599,simonw,2020-07-08T17:26:02Z,2020-07-08T17:26:02Z,OWNER,"Awesome, thank you very much.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",651844316,Add insert --truncate option,
https://github.com/simonw/sqlite-utils/issues/114#issuecomment-655290625,https://api.github.com/repos/simonw/sqlite-utils/issues/114,655290625,MDEyOklzc3VlQ29tbWVudDY1NTI5MDYyNQ==,9599,simonw,2020-07-08T05:15:45Z,2020-07-08T05:15:45Z,OWNER,"Ideally this would all happen in a single transaction, such that other processes talking to the database would not see any inconsistent state while the table copy was taking place. Need to confirm that this is possible. Also refs transactions thoughts in #121.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table,
https://github.com/simonw/sqlite-utils/pull/120#issuecomment-655289686,https://api.github.com/repos/simonw/sqlite-utils/issues/120,655289686,MDEyOklzc3VlQ29tbWVudDY1NTI4OTY4Ng==,9599,simonw,2020-07-08T05:13:11Z,2020-07-08T05:13:11Z,OWNER,"This is an excellent fix, thanks!","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",652816158,Fix query command's support for DML,
https://github.com/simonw/sqlite-utils/pull/118#issuecomment-655286864,https://api.github.com/repos/simonw/sqlite-utils/issues/118,655286864,MDEyOklzc3VlQ29tbWVudDY1NTI4Njg2NA==,9599,simonw,2020-07-08T05:05:27Z,2020-07-08T05:05:36Z,OWNER,"The only thing missing from this PR is updates to the documentation. Those need to go in two places:
- In the Python API docs. I suggest adding a note to this section about bulk inserts: https://github.com/simonw/sqlite-utils/blob/d0cdaaaf00249230e847be3a3b393ee2689fbfe4/docs/python-api.rst#bulk-inserts
- In the CLI docs, in this section: https://github.com/simonw/sqlite-utils/blob/d0cdaaaf00249230e847be3a3b393ee2689fbfe4/docs/cli.rst#inserting-json-data
Here's an example of a previous commit that includes updates to both CLI and API documentation: https://github.com/simonw/sqlite-utils/commit/f9473ace14878212c1fa968b7bd2f51e4f064dba#diff-e3e2a9bfd88566b05001b02a3f51d286","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",651844316,Add insert --truncate option,
https://github.com/simonw/sqlite-utils/pull/118#issuecomment-655284168,https://api.github.com/repos/simonw/sqlite-utils/issues/118,655284168,MDEyOklzc3VlQ29tbWVudDY1NTI4NDE2OA==,9599,simonw,2020-07-08T04:58:00Z,2020-07-08T04:58:00Z,OWNER,"Oops didn't mean to click ""close"" there.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",651844316,Add insert --truncate option,
https://github.com/simonw/sqlite-utils/pull/118#issuecomment-655284054,https://api.github.com/repos/simonw/sqlite-utils/issues/118,655284054,MDEyOklzc3VlQ29tbWVudDY1NTI4NDA1NA==,9599,simonw,2020-07-08T04:57:38Z,2020-07-08T04:57:38Z,OWNER,Thoughts on transactions would be much appreciated in #121 ,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",651844316,Add insert --truncate option,
https://github.com/simonw/sqlite-utils/pull/118#issuecomment-655283393,https://api.github.com/repos/simonw/sqlite-utils/issues/118,655283393,MDEyOklzc3VlQ29tbWVudDY1NTI4MzM5Mw==,9599,simonw,2020-07-08T04:55:18Z,2020-07-08T04:55:18Z,OWNER,"This is a really good idea - and thank you for the detailed discussion in the pull request.
I'm keen to discuss how transactions can work better. I tend to use this pattern in my own code:
with db.conn:
db[""table""].insert(...)
But it's not documented and I've not though very hard about it!
I like having inserts that handle 10,000+ rows commit on every chunk so I can watch their progress from another process, but the library should absolutely support people who want to commit all of the rows in a single transaction - or combine changes with DML.
Lots to discuss here. I'll start a new issue.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",651844316,Add insert --truncate option,
https://github.com/simonw/datasette/issues/784#issuecomment-654424704,https://api.github.com/repos/simonw/datasette/issues/784,654424704,MDEyOklzc3VlQ29tbWVudDY1NDQyNDcwNA==,9599,simonw,2020-07-06T19:31:53Z,2020-07-06T19:31:53Z,OWNER,Documentation: https://datasette.readthedocs.io/en/stable/authentication.html#using-the-root-actor,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",628003707,Ability to sign in to Datasette as a root account,
https://github.com/simonw/datasette/pull/890#issuecomment-653314465,https://api.github.com/repos/simonw/datasette/issues/890,653314465,MDEyOklzc3VlQ29tbWVudDY1MzMxNDQ2NQ==,9599,simonw,2020-07-03T03:07:41Z,2020-07-03T03:07:41Z,OWNER,"This is an excellent fix. Thanks!
Not sure why codecov is complaining. I'm going to merge it as-is.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",650305298,Load only python files from plugins-dir.,
https://github.com/simonw/datasette/issues/886#issuecomment-652732460,https://api.github.com/repos/simonw/datasette/issues/886,652732460,MDEyOklzc3VlQ29tbWVudDY1MjczMjQ2MA==,9599,simonw,2020-07-02T01:52:02Z,2020-07-02T01:52:02Z,OWNER,In investigating this I'm not convinced 500 errors are being correctly raised by errors in canned writable queries.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",649429772,Reconsider how _actor_X magic parameter deals with missing values,
https://github.com/simonw/datasette/issues/886#issuecomment-652731459,https://api.github.com/repos/simonw/datasette/issues/886,652731459,MDEyOklzc3VlQ29tbWVudDY1MjczMTQ1OQ==,9599,simonw,2020-07-02T01:48:08Z,2020-07-02T01:48:08Z,OWNER,"A common error with this (and other) magic parameters is for the database query to result in the following:
You did not supply a value for binding 3.
This is a pretty crufty error. I'm inclined to say that ANY missing or invalid magic parameter should be treated as a `None` value instead.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",649429772,Reconsider how _actor_X magic parameter deals with missing values,
https://github.com/simonw/datasette/issues/887#issuecomment-652711822,https://api.github.com/repos/simonw/datasette/issues/887,652711822,MDEyOklzc3VlQ29tbWVudDY1MjcxMTgyMg==,9599,simonw,2020-07-02T00:31:33Z,2020-07-02T00:31:33Z,OWNER,"If a canned query has a title defined that will be used instead: https://latest.datasette.io/fixtures/neighborhood_search
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",649437530,Canned query page should show the name of the canned query,
https://github.com/simonw/datasette/issues/887#issuecomment-652711562,https://api.github.com/repos/simonw/datasette/issues/887,652711562,MDEyOklzc3VlQ29tbWVudDY1MjcxMTU2Mg==,9599,simonw,2020-07-02T00:30:43Z,2020-07-02T00:30:43Z,OWNER,"Demo has updated: https://latest.datasette.io/fixtures/magic_parameters
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",649437530,Canned query page should show the name of the canned query,
https://github.com/simonw/datasette/pull/883#issuecomment-652710178,https://api.github.com/repos/simonw/datasette/issues/883,652710178,MDEyOklzc3VlQ29tbWVudDY1MjcxMDE3OA==,9599,simonw,2020-07-02T00:25:44Z,2020-07-02T00:25:44Z,OWNER,This is a great idea.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648749062,Skip counting hidden tables,
https://github.com/simonw/datasette/issues/887#issuecomment-652709199,https://api.github.com/repos/simonw/datasette/issues/887,652709199,MDEyOklzc3VlQ29tbWVudDY1MjcwOTE5OQ==,9599,simonw,2020-07-02T00:21:54Z,2020-07-02T00:21:54Z,OWNER,"Example in the live demo: https://latest.datasette.io/fixtures/magic_parameters
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",649437530,Canned query page should show the name of the canned query,
https://github.com/simonw/datasette/issues/885#issuecomment-652681996,https://api.github.com/repos/simonw/datasette/issues/885,652681996,MDEyOklzc3VlQ29tbWVudDY1MjY4MTk5Ng==,9599,simonw,2020-07-01T22:44:47Z,2020-07-01T22:44:47Z,OWNER,https://simonwillison.net/2020/Jul/1/datasette-045/,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",649373451,Blog entry about the release,
https://github.com/simonw/datasette/issues/882#issuecomment-652663177,https://api.github.com/repos/simonw/datasette/issues/882,652663177,MDEyOklzc3VlQ29tbWVudDY1MjY2MzE3Nw==,9599,simonw,2020-07-01T21:48:08Z,2020-07-01T21:48:08Z,OWNER,https://datasette.readthedocs.io/en/latest/changelog.html#v0-45,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648673556,Release notes for 0.45,
https://github.com/simonw/datasette/issues/880#issuecomment-652646487,https://api.github.com/repos/simonw/datasette/issues/880,652646487,MDEyOklzc3VlQ29tbWVudDY1MjY0NjQ4Nw==,9599,simonw,2020-07-01T21:05:48Z,2020-07-01T21:05:48Z,OWNER,"I've been testing the WIP using this in the console:
```javascript
fetch('/data/add_name.json', {
method: 'POST',
body: 'name=XXXfetch',
credentials: 'omit',
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
.then(response => console.log(response))
```
Against a canned query configured like this:
```yaml
databases:
data:
queries:
add_name:
sql: insert into names (name) values (:name)
write: true
```
I haven't got it to work yet. Latest error is this one:
```
INFO: Uvicorn running on http://127.0.0.1:8001 (Press CTRL+C to quit)
Traceback (most recent call last):
File ""/Users/simon/Dropbox/Development/datasette/datasette/app.py"", line 975, in route_path
await response.asgi_send(send)
AttributeError: 'tuple' object has no attribute 'asgi_send'
INFO: 127.0.0.1:49938 - ""POST /data/add_name.json HTTP/1.1"" 500 Internal Server Error
```
It looks like I'm going to have to rethink how the `BaseView` code around tables, formats and hashes is structured in order to fix this. That's a big refactoring! I'm moving this to a new milestone for Datasette 0.46.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648637666,POST to /db/canned-query that returns JSON should be supported (for API clients),
https://github.com/simonw/datasette/issues/882#issuecomment-652604569,https://api.github.com/repos/simonw/datasette/issues/882,652604569,MDEyOklzc3VlQ29tbWVudDY1MjYwNDU2OQ==,9599,simonw,2020-07-01T19:27:17Z,2020-07-01T19:27:17Z,OWNER,Don't forget to update the news in the README.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648673556,Release notes for 0.45,
https://github.com/simonw/datasette/issues/877#issuecomment-652597975,https://api.github.com/repos/simonw/datasette/issues/877,652597975,MDEyOklzc3VlQ29tbWVudDY1MjU5Nzk3NQ==,9599,simonw,2020-07-01T19:12:15Z,2020-07-01T19:12:15Z,OWNER,The latest release of https://github.com/simonw/datasette-auth-tokens (0.2) now supports SQL configuration of tokens.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648421105,Consider dropping explicit CSRF protection entirely?,
https://github.com/simonw/datasette/issues/877#issuecomment-652520496,https://api.github.com/repos/simonw/datasette/issues/877,652520496,MDEyOklzc3VlQ29tbWVudDY1MjUyMDQ5Ng==,9599,simonw,2020-07-01T16:26:52Z,2020-07-01T16:26:52Z,OWNER,Tokens get verified by plugins. So far there's only one: https://github.com/simonw/datasette-auth-tokens - which has you hard-coding plugins in a configuration file. I have a issue there to add support for database-backed tokens too: https://github.com/simonw/datasette-auth-tokens/issues/1,"{""total_count"": 1, ""+1"": 1, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648421105,Consider dropping explicit CSRF protection entirely?,
https://github.com/simonw/datasette/issues/877#issuecomment-652182990,https://api.github.com/repos/simonw/datasette/issues/877,652182990,MDEyOklzc3VlQ29tbWVudDY1MjE4Mjk5MA==,9599,simonw,2020-07-01T04:29:38Z,2020-07-01T04:42:59Z,OWNER,"Have you tried the method described here? https://datasette.readthedocs.io/en/latest/internals.html#csrf-protection - I'm happy to bulk out that section of the documentation if that doesn't help solve your problem.
I just closed #835 which should make CSRF protection easier to work with - it won't interfere with requests without cookies or requests with `Authentication: Bearer token` tokens. See also https://github.com/simonw/asgi-csrf/issues/11
You can try out `pip install datasette==0.45a5` to get those features. Hopefully releasing a full 0.45 tomorrow.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648421105,Consider dropping explicit CSRF protection entirely?,
https://github.com/simonw/datasette/issues/812#issuecomment-652165709,https://api.github.com/repos/simonw/datasette/issues/812,652165709,MDEyOklzc3VlQ29tbWVudDY1MjE2NTcwOQ==,9599,simonw,2020-07-01T03:26:35Z,2020-07-01T03:26:35Z,OWNER,"This case may not be covered without extra work:
https://github.com/simonw/datasette/blob/3ec5b1abf6afa2d22a3378092809a1a8c0249d26/datasette/views/database.py#L122-L123","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",634112607,Ability to customize what happens when a view permission fails,
https://github.com/simonw/datasette/issues/812#issuecomment-652163450,https://api.github.com/repos/simonw/datasette/issues/812,652163450,MDEyOklzc3VlQ29tbWVudDY1MjE2MzQ1MA==,9599,simonw,2020-07-01T03:18:51Z,2020-07-01T03:20:28Z,OWNER,"This can be a plugin hook:
```python
@hookspec
def forbidden(datasette, request, message, send):
""Custom response for a 403 forbidden error""
```
If the hook returns a `Response` object, it will be returned to the user. Plugins are likely to want to return a redirect response.
Maybe the hook can instead use the `send` argument to respond to the request and return `True` which means ""I've responded to this""?
I'm going to leave `send` off for the moment - I can add that in the future if it turns out it would have been a good idea.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",634112607,Ability to customize what happens when a view permission fails,
https://github.com/simonw/datasette/issues/880#issuecomment-652162722,https://api.github.com/repos/simonw/datasette/issues/880,652162722,MDEyOklzc3VlQ29tbWVudDY1MjE2MjcyMg==,9599,simonw,2020-07-01T03:16:07Z,2020-07-01T03:16:07Z,OWNER,The response from this will never be a 302 - it will always be a 200 if the response worked or a 400 for bad parameters or a 500 for errors. The body returned will always be in JSON format.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648637666,POST to /db/canned-query that returns JSON should be supported (for API clients),
https://github.com/simonw/datasette/issues/835#issuecomment-652159398,https://api.github.com/repos/simonw/datasette/issues/835,652159398,MDEyOklzc3VlQ29tbWVudDY1MjE1OTM5OA==,9599,simonw,2020-07-01T03:03:51Z,2020-07-01T03:03:51Z,OWNER,I'm going to add some tests for this.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637363686,Mechanism for skipping CSRF checks on API posts,
https://github.com/simonw/datasette/issues/876#issuecomment-652106227,https://api.github.com/repos/simonw/datasette/issues/876,652106227,MDEyOklzc3VlQ29tbWVudDY1MjEwNjIyNw==,9599,simonw,2020-06-30T23:49:55Z,2020-06-30T23:50:04Z,OWNER,"Done: https://latest.datasette.io/-/patterns
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647879783,Add log out link to the pattern portfolio,
https://github.com/simonw/datasette/issues/879#issuecomment-652105722,https://api.github.com/repos/simonw/datasette/issues/879,652105722,MDEyOklzc3VlQ29tbWVudDY1MjEwNTcyMg==,9599,simonw,2020-06-30T23:48:06Z,2020-06-30T23:48:06Z,OWNER,Updated documentation: https://datasette.readthedocs.io/en/latest/pages.html,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648569227,Database page documentation still talks about hashes in URLs,
https://github.com/simonw/datasette/issues/832#issuecomment-652103895,https://api.github.com/repos/simonw/datasette/issues/832,652103895,MDEyOklzc3VlQ29tbWVudDY1MjEwMzg5NQ==,9599,simonw,2020-06-30T23:41:22Z,2020-06-30T23:41:22Z,OWNER,I don't think this needs any additional documentation - the new behaviour matches how the permissions are documented here: https://datasette.readthedocs.io/en/0.44/authentication.html#built-in-permissions,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501,Having view-table permission but NOT view-database should still grant access to /db/table,
https://github.com/simonw/datasette/issues/832#issuecomment-651999516,https://api.github.com/repos/simonw/datasette/issues/832,651999516,MDEyOklzc3VlQ29tbWVudDY1MTk5OTUxNg==,9599,simonw,2020-06-30T19:33:49Z,2020-06-30T21:34:59Z,OWNER,"Tests needed for this:
- If a user has view table but NOT view database / view instance, can they view the table page?
- If a user has view canned query but NOT view database / view instance, can they view the canned query page?
- If a user has view database but NOT view instance, can they view the database page?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501,Having view-table permission but NOT view-database should still grant access to /db/table,
https://github.com/simonw/datasette/issues/832#issuecomment-651995453,https://api.github.com/repos/simonw/datasette/issues/832,651995453,MDEyOklzc3VlQ29tbWVudDY1MTk5NTQ1Mw==,9599,simonw,2020-06-30T19:25:13Z,2020-06-30T19:25:26Z,OWNER,I'm going to put the new `check_permissions()` method on `BaseView` as well. If I want that method to be available to plugins I can do so by turning that `BaseView` class into a documented API that plugins are encouraged to use themselves.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501,Having view-table permission but NOT view-database should still grant access to /db/table,
https://github.com/simonw/datasette/issues/832#issuecomment-651994978,https://api.github.com/repos/simonw/datasette/issues/832,651994978,MDEyOklzc3VlQ29tbWVudDY1MTk5NDk3OA==,9599,simonw,2020-06-30T19:24:12Z,2020-06-30T19:24:12Z,OWNER,"Hah... but check_permission` is a method on `BaseView`. Here are the various permission methods at the moment:
https://github.com/simonw/datasette/blob/6c2634583627bfab750c115cb13850252821d637/datasette/default_permissions.py#L5-L14
And on BaseView:
https://github.com/simonw/datasette/blob/a8a5f813722f72703a7aae41135ccc40635cc02f/datasette/views/base.py#L65-L70","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501,Having view-table permission but NOT view-database should still grant access to /db/table,
https://github.com/simonw/datasette/issues/832#issuecomment-651993977,https://api.github.com/repos/simonw/datasette/issues/832,651993977,MDEyOklzc3VlQ29tbWVudDY1MTk5Mzk3Nw==,9599,simonw,2020-06-30T19:22:06Z,2020-06-30T19:22:06Z,OWNER,`permission_allowed` is already the name of the pugin hook. It's actually a bit confusing that it's also the name of a method on `datasette.`.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501,Having view-table permission but NOT view-database should still grant access to /db/table,
https://github.com/simonw/datasette/issues/832#issuecomment-651993537,https://api.github.com/repos/simonw/datasette/issues/832,651993537,MDEyOklzc3VlQ29tbWVudDY1MTk5MzUzNw==,9599,simonw,2020-06-30T19:21:15Z,2020-06-30T19:21:15Z,OWNER,"I could rename `permission_allowed()` to `check_permission()` and have a complementary `check_permissions()` method.
This is a breaking change but we're pre-1.0 so I think that's OK. I could even set up a temporary `permission_allowed()` alias which prints a deprecation warning to the console, then remove that at 1.0.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501,Having view-table permission but NOT view-database should still grant access to /db/table,
https://github.com/simonw/datasette/issues/832#issuecomment-651992737,https://api.github.com/repos/simonw/datasette/issues/832,651992737,MDEyOklzc3VlQ29tbWVudDY1MTk5MjczNw==,9599,simonw,2020-06-30T19:19:33Z,2020-06-30T19:20:02Z,OWNER,"I already have this method on Datasette:
```python
async def permission_allowed(self, actor, action, resource=None, default=False):
```
What would be a good method name that complements that and indicates ""check a list of permissions in order""? Should it even run against the request or should you have to hand it `request.actor`?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501,Having view-table permission but NOT view-database should still grant access to /db/table,
https://github.com/simonw/datasette/issues/877#issuecomment-651984989,https://api.github.com/repos/simonw/datasette/issues/877,651984989,MDEyOklzc3VlQ29tbWVudDY1MTk4NDk4OQ==,9599,simonw,2020-06-30T19:03:25Z,2020-06-30T19:03:25Z,OWNER,Relevant: #835,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648421105,Consider dropping explicit CSRF protection entirely?,
https://github.com/simonw/datasette/issues/877#issuecomment-651984355,https://api.github.com/repos/simonw/datasette/issues/877,651984355,MDEyOklzc3VlQ29tbWVudDY1MTk4NDM1NQ==,9599,simonw,2020-06-30T19:02:15Z,2020-06-30T19:02:15Z,OWNER,"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#login-csrf
> Login CSRF can be mitigated by creating pre-sessions (sessions before a user is authenticated) and including tokens in login form.
Sounds like regular CSRF protection to me.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",648421105,Consider dropping explicit CSRF protection entirely?,
https://github.com/simonw/datasette/issues/805#issuecomment-651302221,https://api.github.com/repos/simonw/datasette/issues/805,651302221,MDEyOklzc3VlQ29tbWVudDY1MTMwMjIyMQ==,9599,simonw,2020-06-29T19:02:45Z,2020-06-29T19:05:26Z,OWNER,"No I prefer the idea that logged out users can still perform some writes, in a not-likely-to-attract-abuse way.
So a root-user-can-configure-polls, logged-out-users-can-vote-in-them demo would be good.
Or... crazy idea: a collaborative drawing program? A grid of cells of emoji, anyone can add an emoji to a cell. Would involve a bit of JavaScript. I could use https://github.com/joeattardi/emoji-button for this.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632724154,Writable canned queries live demo on Glitch,
https://github.com/simonw/datasette/issues/805#issuecomment-651301202,https://api.github.com/repos/simonw/datasette/issues/805,651301202,MDEyOklzc3VlQ29tbWVudDY1MTMwMTIwMg==,9599,simonw,2020-06-29T19:00:37Z,2020-06-29T19:00:37Z,OWNER,"How about a blog? Pre-configured canned queries that are only available to `""root""`, plus datasette-template-sql and default templates for the index page and blog entry pages.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632724154,Writable canned queries live demo on Glitch,
https://github.com/simonw/datasette/issues/875#issuecomment-651293559,https://api.github.com/repos/simonw/datasette/issues/875,651293559,MDEyOklzc3VlQ29tbWVudDY1MTI5MzU1OQ==,9599,simonw,2020-06-29T18:43:50Z,2020-06-29T18:43:50Z,OWNER,"
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647103735,"""Logged in as: XXX - logout"" navigation item",
https://github.com/simonw/datasette/issues/873#issuecomment-651203178,https://api.github.com/repos/simonw/datasette/issues/873,651203178,MDEyOklzc3VlQ29tbWVudDY1MTIwMzE3OA==,9599,simonw,2020-06-29T15:44:38Z,2020-06-29T15:44:54Z,OWNER,I'm having real trouble figuring out how to gain access to the port that was used to start the server. I'm treating this as a very low priority - it only affects the exact `-p 0 --root` combination which isn't going to affect many people at all.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-651193594,https://api.github.com/repos/simonw/datasette/issues/873,651193594,MDEyOklzc3VlQ29tbWVudDY1MTE5MzU5NA==,9599,simonw,2020-06-29T15:27:46Z,2020-06-29T15:27:46Z,OWNER,Uninstalling `datasette-debug-asgi` caused the server to startup correctly again.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-651193131,https://api.github.com/repos/simonw/datasette/issues/873,651193131,MDEyOklzc3VlQ29tbWVudDY1MTE5MzEzMQ==,9599,simonw,2020-06-29T15:27:00Z,2020-06-29T15:27:00Z,OWNER,"Aha! Yes it's not being called, and the reason is this: https://github.com/encode/starlette/issues/486
Short version: by default an exception raised during that phase is silently swallowed! You can avoid the swallowing by adding `lifespan=""on""` to the call to `uvicorn.run()`.
When I did that here:
`uvicorn.run(ds.app(), host=host, port=port, log_level=""info"", lifespan=""on"")`
The server failed to start with this error:
```
INFO: Started server process [68849]
INFO: Waiting for application startup.
ERROR: Exception in 'lifespan' protocol
Traceback (most recent call last):
File "".../uvicorn/lifespan/on.py"", line 48, in main
await app(scope, self.receive, self.send)
File "".../uvicorn/middleware/proxy_headers.py"", line 45, in __call__
return await self.app(scope, receive, send)
File "".../datasette_debug_asgi.py"", line 9, in wrapped_app
if scope[""path""] == ""/-/asgi-scope"":
KeyError: 'path'
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650910137,https://api.github.com/repos/simonw/datasette/issues/873,650910137,MDEyOklzc3VlQ29tbWVudDY1MDkxMDEzNw==,9599,simonw,2020-06-29T05:16:32Z,2020-06-29T05:16:32Z,OWNER,I'm not convinced that function is ever actually being called - I added a `print()` statement to it and it's not executing. I don't think the tests cover it.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650909476,https://api.github.com/repos/simonw/datasette/issues/873,650909476,MDEyOklzc3VlQ29tbWVudDY1MDkwOTQ3Ng==,9599,simonw,2020-06-29T05:14:08Z,2020-06-29T05:14:08Z,OWNER,"I already have a `AsgiLifespan` class:
https://github.com/simonw/datasette/blob/35aee82c60b2c9a0185b934db5528c8bd11830f2/datasette/app.py#L896-L905
It runs this function: https://github.com/simonw/datasette/blob/35aee82c60b2c9a0185b934db5528c8bd11830f2/datasette/app.py#L890-L894
Could that startup function also output the `--root` login URL, if needed?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650909136,https://api.github.com/repos/simonw/datasette/issues/873,650909136,MDEyOklzc3VlQ29tbWVudDY1MDkwOTEzNg==,9599,simonw,2020-06-29T05:12:58Z,2020-06-29T05:12:58Z,OWNER,"On startup Datasette currently outputs:
```
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Application startup complete.
```
So the ASGI lifespan protocol is almost certainly the right way to solve this.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650908854,https://api.github.com/repos/simonw/datasette/issues/873,650908854,MDEyOklzc3VlQ29tbWVudDY1MDkwODg1NA==,9599,simonw,2020-06-29T05:12:04Z,2020-06-29T05:12:04Z,OWNER,Can I detect the port the server is running on from within the regular Datasette ASGI code? If so I could use that ability and maybe output the magic `--root` link a second after the server starts up somehow.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650908534,https://api.github.com/repos/simonw/datasette/issues/873,650908534,MDEyOklzc3VlQ29tbWVudDY1MDkwODUzNA==,9599,simonw,2020-06-29T05:11:06Z,2020-06-29T05:11:06Z,OWNER,"Uvicorn's lifespan stuff isn't easy to figure out, but this test suite holds some clues: https://github.com/encode/uvicorn/blob/master/tests/test_lifespan.py","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650907323,https://api.github.com/repos/simonw/datasette/issues/873,650907323,MDEyOklzc3VlQ29tbWVudDY1MDkwNzMyMw==,9599,simonw,2020-06-29T05:07:16Z,2020-06-29T05:07:16Z,OWNER,"This line is interesting: is this a hook I can attach to somehow?
```python
await self.lifespan.startup()
```
From https://github.com/encode/uvicorn/blob/a75fe1381f6b1f78901691c71894f3cf487b5d30/uvicorn/main.py#L475","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650906533,https://api.github.com/repos/simonw/datasette/issues/873,650906533,MDEyOklzc3VlQ29tbWVudDY1MDkwNjUzMw==,9599,simonw,2020-06-29T05:04:44Z,2020-06-29T05:04:44Z,OWNER,The challenge is... can we run our own custom code after that line has executed that has access to `server` and can hence access `server.servers[0].sockets[0].getsockname()[1]` to find the port?,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650906318,https://api.github.com/repos/simonw/datasette/issues/873,650906318,MDEyOklzc3VlQ29tbWVudDY1MDkwNjMxOA==,9599,simonw,2020-06-29T05:04:04Z,2020-06-29T05:04:12Z,OWNER,"Within uvicorn it does this:
```python
if port == 0:
port = server.sockets[0].getsockname()[1]
```
That `server` variable is later stashed here:
```
self.servers = [server]
```
Where `self` is the instance of `class Server` - which is the class that Uvicorn instantiates and calls `.run()` on when we do `uvicorn.run()` here: https://github.com/simonw/datasette/blob/35aee82c60b2c9a0185b934db5528c8bd11830f2/datasette/cli.py#L409","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/873#issuecomment-650905399,https://api.github.com/repos/simonw/datasette/issues/873,650905399,MDEyOklzc3VlQ29tbWVudDY1MDkwNTM5OQ==,9599,simonw,2020-06-29T05:01:03Z,2020-06-29T05:01:03Z,OWNER,This is a bit tricky to fix. This change to uvicorn is relevant: https://github.com/encode/uvicorn/commit/a75fe1381f6b1f78901691c71894f3cf487b5d30,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647095487,"""datasette -p 0 --root"" gives the wrong URL",
https://github.com/simonw/datasette/issues/875#issuecomment-650899265,https://api.github.com/repos/simonw/datasette/issues/875,650899265,MDEyOklzc3VlQ29tbWVudDY1MDg5OTI2NQ==,9599,simonw,2020-06-29T04:34:32Z,2020-06-29T04:34:32Z,OWNER,"From https://github.com/simonw/datasette/issues/840#issuecomment-643454625
> Another problem: what to display in the ""you are logged in as"", since we don't dictate an actor design.
>
> I'm going to use a includes template for this that can easily be over-ridden by administrators or by plugins.
>
> The default will look for the first available of the following keys:
>
> * display
> * name
> * username
> * login
> * id","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647103735,"""Logged in as: XXX - logout"" navigation item",
https://github.com/simonw/datasette/issues/875#issuecomment-650898808,https://api.github.com/repos/simonw/datasette/issues/875,650898808,MDEyOklzc3VlQ29tbWVudDY1MDg5ODgwOA==,9599,simonw,2020-06-29T04:32:31Z,2020-06-29T04:33:30Z,OWNER,"I could borrow the implementation for this from `datasette-auth-github`
https://github.com/simonw/datasette-auth-github/blob/182298b034ecb647971b65057d1d3e7b7fbbb482/datasette_auth_github/templates/base.html
```html+jinja
{% extends ""default:base.html"" %}
{% block extra_head %}
{% endblock %}
{% block nav %}
{{ super() }}
{% if auth and auth.username %}
{{ auth.username }} · Log out
{% endif %}
{% endblock %}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",647103735,"""Logged in as: XXX - logout"" navigation item",
https://github.com/simonw/datasette/issues/840#issuecomment-650895874,https://api.github.com/repos/simonw/datasette/issues/840,650895874,MDEyOklzc3VlQ29tbWVudDY1MDg5NTg3NA==,9599,simonw,2020-06-29T04:18:59Z,2020-06-29T04:19:11Z,OWNER,"Now just need the ""Logged in as: XXX <logout>"" navigation item.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637966833,Log out mechanism for clearing ds_actor cookie,
https://github.com/simonw/datasette/issues/840#issuecomment-650891502,https://api.github.com/repos/simonw/datasette/issues/840,650891502,MDEyOklzc3VlQ29tbWVudDY1MDg5MTUwMg==,9599,simonw,2020-06-29T03:58:08Z,2020-06-29T03:58:08Z,OWNER,"Step one: a ""logout"" page at `/-/logout` - which shows you a single CSRF-protected ""logout"" button if you do a GET against it and logs you out if you do a POST against it.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637966833,Log out mechanism for clearing ds_actor cookie,
https://github.com/simonw/datasette/issues/805#issuecomment-650891257,https://api.github.com/repos/simonw/datasette/issues/805,650891257,MDEyOklzc3VlQ29tbWVudDY1MDg5MTI1Nw==,9599,simonw,2020-06-29T03:56:48Z,2020-06-29T03:56:48Z,OWNER,Using `datasette-glitch` and the new https://github.com/simonw/datasette-write - currently running on `datasette==0.45a4` - works on Glitch. The console shows a login link which gives you a cookie which allows you access to the `/-/write` interface.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632724154,Writable canned queries live demo on Glitch,
https://github.com/simonw/datasette/issues/864#issuecomment-650847013,https://api.github.com/repos/simonw/datasette/issues/864,650847013,MDEyOklzc3VlQ29tbWVudDY1MDg0NzAxMw==,9599,simonw,2020-06-29T00:41:55Z,2020-06-29T00:41:55Z,OWNER,To test this I'll need a plugin test that renders a custom template. Here's an example I can imitate: https://github.com/simonw/datasette/blob/7ac4936cec87f5a591e5d2680f0acefc3d35a705/tests/test_plugins.py#L588-L596,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",644309017,datasette.add_message() doesn't work inside plugins,
https://github.com/simonw/datasette/issues/864#issuecomment-650846625,https://api.github.com/repos/simonw/datasette/issues/864,650846625,MDEyOklzc3VlQ29tbWVudDY1MDg0NjYyNQ==,9599,simonw,2020-06-29T00:39:47Z,2020-06-29T00:39:47Z,OWNER,"I think the fix is to move the `""show_messages""` variable to here:
https://github.com/simonw/datasette/blob/7ac4936cec87f5a591e5d2680f0acefc3d35a705/datasette/app.py#L735-L748","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",644309017,datasette.add_message() doesn't work inside plugins,
https://github.com/simonw/datasette/issues/864#issuecomment-650846473,https://api.github.com/repos/simonw/datasette/issues/864,650846473,MDEyOklzc3VlQ29tbWVudDY1MDg0NjQ3Mw==,9599,simonw,2020-06-29T00:39:04Z,2020-06-29T00:39:04Z,OWNER,"Re-opening: plugins may get to set messages but they don't display them, even if they render a template that extends `base.html`. For example, this code in a plugin:
```python
return Response.html(
await datasette.render_template(
""write.html"",
{""databases"": databases, ""sql"": request.args.get(""sql"") or """"},
request=request,
)
)
```
This won't display messages. The reason is that the messages are made available to the template context in the `BaseView.render()` method here:
https://github.com/simonw/datasette/blob/7ac4936cec87f5a591e5d2680f0acefc3d35a705/datasette/views/base.py#L87-L95","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",644309017,datasette.add_message() doesn't work inside plugins,
https://github.com/simonw/datasette/issues/864#issuecomment-650842514,https://api.github.com/repos/simonw/datasette/issues/864,650842514,MDEyOklzc3VlQ29tbWVudDY1MDg0MjUxNA==,9599,simonw,2020-06-29T00:12:59Z,2020-06-29T00:12:59Z,OWNER,"> I've made enough progress on this to be able to solve the messages issue in #864. I may still complete this overall goal (registering internal views with `register_routes()`) as part of Datasette 0.45 but it would be OK if it slipped to a later release.
https://github.com/simonw/datasette/issues/870#issuecomment-650842381","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",644309017,datasette.add_message() doesn't work inside plugins,
https://github.com/simonw/datasette/issues/870#issuecomment-650842381,https://api.github.com/repos/simonw/datasette/issues/870,650842381,MDEyOklzc3VlQ29tbWVudDY1MDg0MjM4MQ==,9599,simonw,2020-06-29T00:12:07Z,2020-06-29T00:12:07Z,OWNER,I've made enough progress on this to be able to solve the messages issue in #864. I may still complete this overall goal (registering internal views with `register_routes()`) as part of Datasette 0.45 but it would be OK if it slipped to a later release.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/870#issuecomment-650838972,https://api.github.com/repos/simonw/datasette/issues/870,650838972,MDEyOklzc3VlQ29tbWVudDY1MDgzODk3Mg==,9599,simonw,2020-06-28T23:46:40Z,2020-06-28T23:46:40Z,OWNER,I'm going to create the single `Request()` instance in the `DatasetteRouter` class - at the beginning of the `route_path` method: https://github.com/simonw/datasette/blob/3bc2461c77ecba3e1a95301dd440a9bef56b1283/datasette/app.py#L905-L925,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/870#issuecomment-650838691,https://api.github.com/repos/simonw/datasette/issues/870,650838691,MDEyOklzc3VlQ29tbWVudDY1MDgzODY5MQ==,9599,simonw,2020-06-28T23:44:12Z,2020-06-28T23:44:25Z,OWNER,"This code is interesting:
https://github.com/simonw/datasette/blob/3bc2461c77ecba3e1a95301dd440a9bef56b1283/datasette/app.py#L948-L955
I want to change the signature of that `return await view(new_scope, receive, send)` method to instead take `(request, send)` - so I can have a single shared request object that's created just once per HTTP request.
The problem is the scope modification: I have code that modifies the scope, but how should that impact a shared `Request` instance? Should its `.scope` be replaced with alternative scopes as it travels through the codebase?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/870#issuecomment-650834666,https://api.github.com/repos/simonw/datasette/issues/870,650834666,MDEyOklzc3VlQ29tbWVudDY1MDgzNDY2Ng==,9599,simonw,2020-06-28T23:07:19Z,2020-06-28T23:07:19Z,OWNER,So now the problem is simpler: I need to get `BaseView` to a state where it can accept a shared `request` object and it can be used in conjunction with `register_routes()`.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/870#issuecomment-650834251,https://api.github.com/repos/simonw/datasette/issues/870,650834251,MDEyOklzc3VlQ29tbWVudDY1MDgzNDI1MQ==,9599,simonw,2020-06-28T23:03:28Z,2020-06-28T23:03:28Z,OWNER,"I'm going to ditch that `AsgiView` class too, by combining it into `BaseView`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/870#issuecomment-650820068,https://api.github.com/repos/simonw/datasette/issues/870,650820068,MDEyOklzc3VlQ29tbWVudDY1MDgyMDA2OA==,9599,simonw,2020-06-28T20:52:09Z,2020-06-28T20:53:00Z,OWNER,"Maybe I could add a `as_request_view` method as an alternative to `as_asgi`:
https://github.com/simonw/datasette/blob/a8bcafc1775c8a8655b365ae22a3d64f6361c74a/datasette/utils/asgi.py#L150-L174
Or I could teach the `Router` to spot the `dispatch_request` method and call it directly.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/847#issuecomment-650819895,https://api.github.com/repos/simonw/datasette/issues/847,650819895,MDEyOklzc3VlQ29tbWVudDY1MDgxOTg5NQ==,9599,simonw,2020-06-28T20:50:21Z,2020-06-28T20:50:21Z,OWNER,I'm happy enough with https://codecov.io/gh/simonw/datasette that I'm not going to spend any more time on this.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638259643,Take advantage of .coverage being a SQLite database,
https://github.com/simonw/datasette/issues/870#issuecomment-650818309,https://api.github.com/repos/simonw/datasette/issues/870,650818309,MDEyOklzc3VlQ29tbWVudDY1MDgxODMwOQ==,9599,simonw,2020-06-28T20:36:28Z,2020-06-28T20:36:52Z,OWNER,"Since `AsgiRouter` is only used as the super-class of the `DatasetteRouter` class maybe I should get rid of `AsgiRouter` entirely - no point in having a Datasette-specific subclass of it if the parent class isn't ever used by anything else.
I could also rename it to just `Router` which is a nicer name than `DatasetteRouter`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/870#issuecomment-650818086,https://api.github.com/repos/simonw/datasette/issues/870,650818086,MDEyOklzc3VlQ29tbWVudDY1MDgxODA4Ng==,9599,simonw,2020-06-28T20:34:33Z,2020-06-28T20:34:33Z,OWNER,"The key to all of this may be the `DatasetteRouter` class. It deals with `scope` right now but if it internally dealt with `request` that could be enough to fix #864 by adding logic needed by the `.add_message()` mechanism.
https://github.com/simonw/datasette/blob/0991ea75cc7b265389aa8362414a305ba532d31a/datasette/app.py#L904-L938","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/870#issuecomment-650815278,https://api.github.com/repos/simonw/datasette/issues/870,650815278,MDEyOklzc3VlQ29tbWVudDY1MDgxNTI3OA==,9599,simonw,2020-06-28T20:09:07Z,2020-06-28T20:11:21Z,OWNER,"There's a lot of complex logic in the `DataView` class, which handles conditionally returning content as `.json` or as HTML or as `.csv`.
That view subclasses `AsgiView` which is itself request-aware, so maybe I don't need to reconsider how those classes work - just figure out how to hook them up with `register_routes`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646737558,Refactor default views to use register_routes,
https://github.com/simonw/datasette/issues/871#issuecomment-650812444,https://api.github.com/repos/simonw/datasette/issues/871,650812444,MDEyOklzc3VlQ29tbWVudDY1MDgxMjQ0NA==,9599,simonw,2020-06-28T19:43:27Z,2020-06-28T19:43:27Z,OWNER,"Currently:
> `_timestamp_epoch`
>
> The number of seconds since the Unix epoch.
>
> `_timestamp_date_utc`
>
> The date in UTC, e.g. `2020-06-01`
>
> `_timestamp_datetime_utc`
>
> The ISO 8601 datetime in UTC, e.g. `2020-06-24T18:01:07Z`
I'm going to rename them to:
- `_now_epoch`
- `_now_date_utc`
- `_now_datetime_utc`","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646840273,Rename the _timestamp magic parameters to _now,
https://github.com/simonw/datasette/issues/834#issuecomment-650811919,https://api.github.com/repos/simonw/datasette/issues/834,650811919,MDEyOklzc3VlQ29tbWVudDY1MDgxMTkxOQ==,9599,simonw,2020-06-28T19:38:50Z,2020-06-28T19:38:50Z,OWNER,"I have two plugins in progress that use this hook now:
- https://github.com/simonw/datasette-init creates tables and views on startup
- https://github.com/simonw/datasette-glitch outputs the login-as-root secret link on Glitch","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637342551,startup() plugin hook,
https://github.com/simonw/datasette/issues/805#issuecomment-650784162,https://api.github.com/repos/simonw/datasette/issues/805,650784162,MDEyOklzc3VlQ29tbWVudDY1MDc4NDE2Mg==,9599,simonw,2020-06-28T15:48:32Z,2020-06-28T15:48:32Z,OWNER,https://github.com/simonw/datasette-glitch is my new plugin that outputs the root login link on Glitch when the server starts.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632724154,Writable canned queries live demo on Glitch,
https://github.com/simonw/datasette/issues/834#issuecomment-643657067,https://api.github.com/repos/simonw/datasette/issues/834,643657067,MDEyOklzc3VlQ29tbWVudDY0MzY1NzA2Nw==,9599,simonw,2020-06-13T17:59:42Z,2020-06-28T04:01:52Z,OWNER,Documentation: https://datasette.readthedocs.io/en/latest/plugin_hooks.html#startup-datasette,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637342551,startup() plugin hook,
https://github.com/simonw/datasette/issues/842#issuecomment-650684635,https://api.github.com/repos/simonw/datasette/issues/842,650684635,MDEyOklzc3VlQ29tbWVudDY1MDY4NDYzNQ==,9599,simonw,2020-06-28T03:30:31Z,2020-06-28T03:30:31Z,OWNER,Live demo: https://latest.datasette.io/fixtures/magic_parameters,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/805#issuecomment-650681496,https://api.github.com/repos/simonw/datasette/issues/805,650681496,MDEyOklzc3VlQ29tbWVudDY1MDY4MTQ5Ng==,9599,simonw,2020-06-28T03:11:51Z,2020-06-28T03:11:51Z,OWNER,I can use magic parameters from #842 in this.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632724154,Writable canned queries live demo on Glitch,
https://github.com/simonw/datasette/issues/842#issuecomment-650679100,https://api.github.com/repos/simonw/datasette/issues/842,650679100,MDEyOklzc3VlQ29tbWVudDY1MDY3OTEwMA==,9599,simonw,2020-06-28T03:00:44Z,2020-06-28T03:00:44Z,OWNER,I'm going to add some canned queries to the `metadata.json` used by the live demo that illustrate this feature.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/842#issuecomment-650678951,https://api.github.com/repos/simonw/datasette/issues/842,650678951,MDEyOklzc3VlQ29tbWVudDY1MDY3ODk1MQ==,9599,simonw,2020-06-28T02:59:52Z,2020-06-28T02:59:52Z,OWNER,"Documentation: https://datasette.readthedocs.io/en/latest/sql_queries.html#magic-parameters
Plugin hook documentation: https://datasette.readthedocs.io/en/latest/plugin_hooks.html#plugin-hook-register-magic-parameters","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/842#issuecomment-650648434,https://api.github.com/repos/simonw/datasette/issues/842,650648434,MDEyOklzc3VlQ29tbWVudDY1MDY0ODQzNA==,9599,simonw,2020-06-27T23:27:35Z,2020-06-27T23:37:38Z,OWNER,I'm going to rename `_request_X` to `_header_X` as that better reflects what it now does.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/pull/868#issuecomment-650600606,https://api.github.com/repos/simonw/datasette/issues/868,650600606,MDEyOklzc3VlQ29tbWVudDY1MDYwMDYwNg==,9599,simonw,2020-06-27T18:44:28Z,2020-06-27T18:44:28Z,OWNER,"This is really exciting! Thanks so much for looking into this.
I'm interested in moving CI for this repo over to GitHub Actions, so I'd be fine with you getting this to work as an Action rather than through Travis. If you can get it working in Travis though I'll happily land that and figure out how to convert that to GitHub Actions later on.","{""total_count"": 1, ""+1"": 1, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",646448486,initial windows ci setup,
https://github.com/simonw/datasette/issues/835#issuecomment-650598710,https://api.github.com/repos/simonw/datasette/issues/835,650598710,MDEyOklzc3VlQ29tbWVudDY1MDU5ODcxMA==,9599,simonw,2020-06-27T18:32:22Z,2020-06-27T18:32:22Z,OWNER,"Skipping CSRF on `Authorization: Bearer xxx` headers also makes sense for JWT applications, which tend to send JWTs using that form of header.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637363686,Mechanism for skipping CSRF checks on API posts,
https://github.com/simonw/datasette/issues/842#issuecomment-650593122,https://api.github.com/repos/simonw/datasette/issues/842,650593122,MDEyOklzc3VlQ29tbWVudDY1MDU5MzEyMg==,9599,simonw,2020-06-27T18:03:02Z,2020-06-27T18:03:10Z,OWNER,"> Security thought: make sure it's not possible to accidentally open up a security hole where an attacker can send a GET request that causes the magic parameter `_cookie_ds_actor` to be resolved and returned as JSON data that the attacker can see.
This is an open security hole in https://github.com/simonw/datasette/commit/94c1315f0030fd58ce46a9294052c5c9d9d181c7 - it's useful for testing, but I need to remove it before I land that branch.
https://github.com/simonw/datasette/blob/94c1315f0030fd58ce46a9294052c5c9d9d181c7/datasette/views/database.py#L231-L237
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/842#issuecomment-650458857,https://api.github.com/repos/simonw/datasette/issues/842,650458857,MDEyOklzc3VlQ29tbWVudDY1MDQ1ODg1Nw==,9599,simonw,2020-06-27T00:11:04Z,2020-06-27T00:11:04Z,OWNER,Security thought: make sure it's not possible to accidentally open up a security hole where an attacker can send a GET request that causes the magic parameter `_cookie_ds_actor` to be resolved and returned as JSON data that the attacker can see.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/842#issuecomment-650455793,https://api.github.com/repos/simonw/datasette/issues/842,650455793,MDEyOklzc3VlQ29tbWVudDY1MDQ1NTc5Mw==,9599,simonw,2020-06-26T23:57:30Z,2020-06-27T00:00:16Z,OWNER,"Maybe I should ship a default `_scope_headers_...` parameter instead, which reads from a dictionary of `scope[""headers""]` - https://asgi-scope.now.sh/ shows what those look like.
```
{'client': ('148.64.98.14', 0),
'headers': [[b'host', b'asgi-scope.now.sh'],
[b'x-forwarded-for', b'148.64.98.14'],
[b'x-vercel-id', b'sw72x-1593215573008-024e4e603806'],
[b'x-forwarded-host', b'asgi-scope.now.sh'],
[b'accept',
b'text/html,application/xhtml+xml,application/xml;q=0.9,image/'
b'webp,*/*;q=0.8'],
[b'x-real-ip', b'148.64.98.14'],
[b'x-vercel-deployment-url', b'asgi-scope-9eyeojbek.now.sh'],
[b'upgrade-insecure-requests', b'1'],
[b'x-vercel-trace', b'sfo1'],
[b'x-forwarded-proto', b'https'],
[b'accept-language', b'en-US,en;q=0.5'],
[b'user-agent',
b'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:77.0) Gecko'
b'/20100101 Firefox/77.0'],
[b'x-vercel-forwarded-for', b'148.64.98.14'],
[b'accept-encoding', b'gzip, deflate, br'],
[b'dnt', b'1'],
[b'te', b'trailers']],
'http_version': '1.1',
'method': 'GET',
'path': '/',
'query_string': b'',
'raw_path': b'/',
'root_path': '',
'scheme': 'https',
'server': ('asgi-scope.now.sh', 80),
'type': 'http'}
```
I'm going to have `_request_X` actually mean ""find the first value for X in `scope[""headers""`]"" - with underscores converted to hyphens.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/842#issuecomment-650455353,https://api.github.com/repos/simonw/datasette/issues/842,650455353,MDEyOklzc3VlQ29tbWVudDY1MDQ1NTM1Mw==,9599,simonw,2020-06-26T23:55:40Z,2020-06-26T23:55:40Z,OWNER,"`_request_ip` is actually quite hard to implement - should it take into account things like the `x-forwarded-for` header?
It probably should - but that means it now needs a bunch of extra configuration to tell it which of those headers can be trusted in the current environment.
As such I think I'll leave that for a plugin.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/867#issuecomment-649931714,https://api.github.com/repos/simonw/datasette/issues/867,649931714,MDEyOklzc3VlQ29tbWVudDY0OTkzMTcxNA==,9599,simonw,2020-06-26T03:12:51Z,2020-06-26T03:12:51Z,OWNER,"Here's the relevant code:
https://github.com/simonw/datasette/blob/1bb33dab49fd25f77b9f8e7ab7ee23b3d64c123c/datasette/app.py#L1057-L1070
And the relevant test code:
https://github.com/simonw/datasette/blob/1bb33dab49fd25f77b9f8e7ab7ee23b3d64c123c/tests/test_plugins.py#L567-L573
https://github.com/simonw/datasette/blob/1bb33dab49fd25f77b9f8e7ab7ee23b3d64c123c/tests/plugins/my_plugin.py#L162-L196","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",645975649,register_routes() should support non-async view functions too,
https://github.com/simonw/datasette/issues/842#issuecomment-649014757,https://api.github.com/repos/simonw/datasette/issues/842,649014757,MDEyOklzc3VlQ29tbWVudDY0OTAxNDc1Nw==,9599,simonw,2020-06-24T19:15:46Z,2020-06-24T19:31:52Z,OWNER,I'm building this documentation-first - here's the documentation so far: https://github.com/simonw/datasette/blob/6fc8bd9c473f4a25e0a076f24c7e5a9b2f353bb8/docs/sql_queries.rst#magic-parameters,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/842#issuecomment-646271834,https://api.github.com/repos/simonw/datasette/issues/842,646271834,MDEyOklzc3VlQ29tbWVudDY0NjI3MTgzNA==,9599,simonw,2020-06-18T19:49:41Z,2020-06-24T18:49:22Z,OWNER,"But then what kind of magic parameters might plugins want to add?
Here's a crazy idea: `_scrapedcontent_url` - it would look for the `url` column on the data being inserted, scrape the content from it and insert that. This does suggest that the magic resolving function `scrapedcontent()` would need to optionally be sent the full row dictionary being inserted too.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/842#issuecomment-646270702,https://api.github.com/repos/simonw/datasette/issues/842,646270702,MDEyOklzc3VlQ29tbWVudDY0NjI3MDcwMg==,9599,simonw,2020-06-18T19:47:19Z,2020-06-24T18:48:48Z,OWNER,"Brainstorming more potential magic parameters:
* `_actor_id`
* `_actor_name`
* `_request_ip`
* `_request_user_agent`
* `_cookie_cookiename`
* `_signedcookie_cookiename` - reading signed cookies would be cool, not sure how to specify namespace though, maybe always use the same one? Or have the namespace come last, `_signedcookie_cookiename_mynamespace`. Might not need special signed cookie support since `actor` is already usually from a signed cookie.
* `_timestamp_unix` (not happy with these names yet)
* `_timestamp_localtime`
* `_timestamp_datetime`
* `_timestamp_utc`","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/842#issuecomment-649000075,https://api.github.com/repos/simonw/datasette/issues/842,649000075,MDEyOklzc3VlQ29tbWVudDY0OTAwMDA3NQ==,9599,simonw,2020-06-24T18:46:36Z,2020-06-24T18:47:37Z,OWNER,"Another magic parameter that would be useful would be `_random`. Consider https://github.com/simonw/datasette-auth-tokens/issues/1 for example - I'd like to be able to provide a writable canned query which can create new authentication tokens in the database, but ideally it would automatically populate a secure random secret for each one.
Maybe `_random_chars_128` to create a 128 character long random string (using `os.urandom(64).hex()`).
This would be the first example of a magic parameter where part of the parameter name is used to configure the resulting value. Maybe neater to separate that with a different character? Unfortunately `_random_chars:128` wouldn't work because these parameters are used in a SQLite query where `:` has special meaning: `insert into blah (secret) values (:_random_chars:128)` wouldn't make sense.
Actually this is already supported by the proposed design - `_random_chars_128` would become `random(""chars_128"")` so the `random()` function could split off the 128 itself.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085,Magic parameters for canned queries,
https://github.com/simonw/datasette/issues/865#issuecomment-648998264,https://api.github.com/repos/simonw/datasette/issues/865,648998264,MDEyOklzc3VlQ29tbWVudDY0ODk5ODI2NA==,9599,simonw,2020-06-24T18:43:02Z,2020-06-24T18:43:02Z,OWNER,Thanks for the bug report. Yes I think #838 may be the same issue. Will investigate.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",644582921,"base_url doesn't seem to work when adding criteria and clicking ""apply""",
https://github.com/simonw/datasette/issues/858#issuecomment-648997857,https://api.github.com/repos/simonw/datasette/issues/858,648997857,MDEyOklzc3VlQ29tbWVudDY0ODk5Nzg1Nw==,9599,simonw,2020-06-24T18:42:10Z,2020-06-24T18:42:10Z,OWNER,I really need to get myself a Windows 10 development environment working so I can dig into this kind of bug properly. I have a gaming PC lying around that I could re-task for that.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",642388564,publish heroku does not work on Windows 10,