html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,issue,performed_via_github_app
https://github.com/simonw/datasette/issues/1822#issuecomment-1258760299,https://api.github.com/repos/simonw/datasette/issues/1822,1258760299,IC_kwDOBm6k_c5LByhr,9599,2022-09-26T23:25:12Z,2022-09-26T23:25:55Z,OWNER,"A start:
```diff
diff --git a/datasette/utils/asgi.py b/datasette/utils/asgi.py
index 8a2fa060..41ade961 100644
--- a/datasette/utils/asgi.py
+++ b/datasette/utils/asgi.py
@@ -118,7 +118,7 @@ class Request:
return dict(parse_qsl(body.decode(""utf-8""), keep_blank_values=True))
@classmethod
- def fake(cls, path_with_query_string, method=""GET"", scheme=""http"", url_vars=None):
+ def fake(cls, path_with_query_string, *, method=""GET"", scheme=""http"", url_vars=None):
""""""Useful for constructing Request objects for tests""""""
path, _, query_string = path_with_query_string.partition(""?"")
scope = {
@@ -204,7 +204,7 @@ class AsgiWriter:
)
-async def asgi_send_json(send, info, status=200, headers=None):
+async def asgi_send_json(send, info, *, status=200, headers=None):
headers = headers or {}
await asgi_send(
send,
@@ -215,7 +215,7 @@ async def asgi_send_json(send, info, status=200, headers=None):
)
-async def asgi_send_html(send, html, status=200, headers=None):
+async def asgi_send_html(send, html, *, status=200, headers=None):
headers = headers or {}
await asgi_send(
send,
@@ -226,7 +226,7 @@ async def asgi_send_html(send, html, status=200, headers=None):
)
-async def asgi_send_redirect(send, location, status=302):
+async def asgi_send_redirect(send, location, *, status=302):
await asgi_send(
send,
"""",
@@ -236,12 +236,12 @@ async def asgi_send_redirect(send, location, status=302):
)
-async def asgi_send(send, content, status, headers=None, content_type=""text/plain""):
+async def asgi_send(send, content, status, *, headers=None, content_type=""text/plain""):
await asgi_start(send, status, headers, content_type)
await send({""type"": ""http.response.body"", ""body"": content.encode(""utf-8"")})
-async def asgi_start(send, status, headers=None, content_type=""text/plain""):
+async def asgi_start(send, status, *, headers=None, content_type=""text/plain""):
headers = headers or {}
# Remove any existing content-type header
headers = {k: v for k, v in headers.items() if k.lower() != ""content-type""}
@@ -259,7 +259,7 @@ async def asgi_start(send, status, headers=None, content_type=""text/plain""):
async def asgi_send_file(
- send, filepath, filename=None, content_type=None, chunk_size=4096, headers=None
+ send, filepath, filename=None, *, content_type=None, chunk_size=4096, headers=None
):
headers = headers or {}
if filename:
@@ -284,7 +284,7 @@ async def asgi_send_file(
)
-def asgi_static(root_path, chunk_size=4096, headers=None, content_type=None):
+def asgi_static(root_path, *, chunk_size=4096, headers=None, content_type=None):
root_path = Path(root_path)
async def inner_static(request, send):
@@ -313,7 +313,7 @@ def asgi_static(root_path, chunk_size=4096, headers=None, content_type=None):
class Response:
- def __init__(self, body=None, status=200, headers=None, content_type=""text/plain""):
+ def __init__(self, body=None, *, status=200, headers=None, content_type=""text/plain""):
self.body = body
self.status = status
self.headers = headers or {}
@@ -346,6 +346,7 @@ class Response:
self,
key,
value="""",
+ *,
max_age=None,
expires=None,
path=""/"",
@@ -374,7 +375,7 @@ class Response:
self._set_cookie_headers.append(cookie.output(header="""").strip())
@classmethod
- def html(cls, body, status=200, headers=None):
+ def html(cls, body, *, status=200, headers=None):
return cls(
body,
status=status,
@@ -383,7 +384,7 @@ class Response:
)
@classmethod
- def text(cls, body, status=200, headers=None):
+ def text(cls, body, *, status=200, headers=None):
return cls(
str(body),
status=status,
@@ -392,7 +393,7 @@ class Response:
)
@classmethod
- def json(cls, body, status=200, headers=None, default=None):
+ def json(cls, body, *, status=200, headers=None, default=None):
return cls(
json.dumps(body, default=default),
status=status,
@@ -401,7 +402,7 @@ class Response:
)
@classmethod
- def redirect(cls, path, status=302, headers=None):
+ def redirect(cls, path, *, status=302, headers=None):
headers = headers or {}
headers[""Location""] = path
return cls("""", status=status, headers=headers)
@@ -412,6 +413,7 @@ class AsgiFileDownload:
self,
filepath,
filename=None,
+ *,
content_type=""application/octet-stream"",
headers=None,
):
```
```diff
diff --git a/datasette/app.py b/datasette/app.py
index 03d1dacc..4d4e5584 100644
--- a/datasette/app.py
+++ b/datasette/app.py
@@ -190,6 +190,7 @@ class Datasette:
def __init__(
self,
files=None,
+ *,
immutables=None,
cache_headers=True,
cors=False,
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1386854246,
https://github.com/simonw/datasette/issues/1822#issuecomment-1258757544,https://api.github.com/repos/simonw/datasette/issues/1822,1258757544,IC_kwDOBm6k_c5LBx2o,9599,2022-09-26T23:21:23Z,2022-09-26T23:21:23Z,OWNER,Everything on https://docs.datasette.io/en/stable/internals.html that uses keyword arguments should do this I think.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1386854246,
https://github.com/simonw/datasette/issues/1817#issuecomment-1258756231,https://api.github.com/repos/simonw/datasette/issues/1817,1258756231,IC_kwDOBm6k_c5LBxiH,9599,2022-09-26T23:19:34Z,2022-09-26T23:19:34Z,OWNER,"This is a good idea - it's something I should do before Datasette 1.0.
I was a tiny bit worried about compatibility (Datasette is 3.7+) but it looks like they have been in Python since 3.0!","{""total_count"": 1, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 1, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1384273985,
https://github.com/simonw/datasette/issues/1819#issuecomment-1258754105,https://api.github.com/repos/simonw/datasette/issues/1819,1258754105,IC_kwDOBm6k_c5LBxA5,9599,2022-09-26T23:16:15Z,2022-09-26T23:16:15Z,OWNER,Demo: https://latest.datasette.io/_memory?sql=with+recursive+counter(x)+as+(%0D%0A++select+0%0D%0A++++union%0D%0A++select+x+%2B+1+from+counter%0D%0A)%2C%0D%0Ablah+as+(select+*+from+counter+limit+5000000)%0D%0Aselect+count(*)+from+blah,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1385026210,
https://github.com/simonw/datasette/issues/1819#issuecomment-1258746600,https://api.github.com/repos/simonw/datasette/issues/1819,1258746600,IC_kwDOBm6k_c5LBvLo,9599,2022-09-26T23:05:40Z,2022-09-26T23:05:40Z,OWNER,"Implementing it like this, so at least you can copy and paste the SQL query back out again:
I'm not doing a full textarea because this error can be raised in multiple places, including on the table page itself. It's not just an error associated with the manual query page.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1385026210,
https://github.com/simonw/datasette/issues/1819#issuecomment-1258738435,https://api.github.com/repos/simonw/datasette/issues/1819,1258738435,IC_kwDOBm6k_c5LBtMD,9599,2022-09-26T22:52:19Z,2022-09-26T22:52:19Z,OWNER,This is a good idea.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1385026210,
https://github.com/simonw/datasette/issues/1818#issuecomment-1258735747,https://api.github.com/repos/simonw/datasette/issues/1818,1258735747,IC_kwDOBm6k_c5LBsiD,9599,2022-09-26T22:47:59Z,2022-09-26T22:47:59Z,OWNER,Another option here is to tie into a feature I built in `sqlite-utils` with this problem in mind but never introduced on the Datasette side of things: https://sqlite-utils.datasette.io/en/stable/python-api.html#cached-table-counts-using-triggers,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1384549993,
https://github.com/simonw/datasette/issues/1818#issuecomment-1258735283,https://api.github.com/repos/simonw/datasette/issues/1818,1258735283,IC_kwDOBm6k_c5LBsaz,9599,2022-09-26T22:47:19Z,2022-09-26T22:47:19Z,OWNER,"That's a really interesting idea: for a lot of databases (those made out of straight imports from CSV) `max(rowid)` would indeed reflect the size of the table, but would be a MUCH faster operation than attempting a `count(*)`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1384549993,
https://github.com/simonw/sqlite-utils/issues/491#issuecomment-1258697384,https://api.github.com/repos/simonw/sqlite-utils/issues/491,1258697384,IC_kwDOCGYnMM5LBjKo,9599,2022-09-26T22:12:45Z,2022-09-26T22:12:45Z,OWNER,That feels like a slightly different command to me - maybe `sqlite-utils backup data.db data-backup.db`? It doesn't have any of the mechanics for merging tables together. Could be a useful feature separately though.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1383646615,
https://github.com/simonw/datasette/issues/1821#issuecomment-1258692555,https://api.github.com/repos/simonw/datasette/issues/1821,1258692555,IC_kwDOBm6k_c5LBh_L,9599,2022-09-26T22:06:39Z,2022-09-26T22:06:39Z,OWNER,"- https://github.com/simonw/datasette/actions/runs/3131344150
- https://github.com/simonw/datasette/releases/tag/0.63a0
- https://pypi.org/project/datasette/0.63a0/","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1386734383,
https://github.com/simonw/sqlite-utils/issues/494#issuecomment-1258521333,https://api.github.com/repos/simonw/sqlite-utils/issues/494,1258521333,IC_kwDOCGYnMM5LA4L1,9599,2022-09-26T19:32:36Z,2022-09-26T19:32:36Z,OWNER,Tweeted about it too: https://twitter.com/simonw/status/1574481628507668480,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1386593843,
https://github.com/simonw/sqlite-utils/issues/494#issuecomment-1258516872,https://api.github.com/repos/simonw/sqlite-utils/issues/494,1258516872,IC_kwDOCGYnMM5LA3GI,9599,2022-09-26T19:28:36Z,2022-09-26T19:28:36Z,OWNER,New documentation: https://sqlite-utils.datasette.io/en/latest/contributing.html#using-just-and-pipenv,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1386593843,
https://github.com/simonw/sqlite-utils/issues/483#issuecomment-1258479462,https://api.github.com/repos/simonw/sqlite-utils/issues/483,1258479462,IC_kwDOCGYnMM5LAt9m,9599,2022-09-26T19:04:29Z,2022-09-26T19:04:43Z,OWNER,"Documentation:
- https://sqlite-utils.datasette.io/en/latest/cli.html#cli-install
- https://sqlite-utils.datasette.io/en/latest/cli.html#cli-uninstall
- https://sqlite-utils.datasette.io/en/latest/cli-reference.html#install
- https://sqlite-utils.datasette.io/en/latest/cli-reference.html#uninstall
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1363765916,
https://github.com/simonw/sqlite-utils/issues/493#issuecomment-1258476455,https://api.github.com/repos/simonw/sqlite-utils/issues/493,1258476455,IC_kwDOCGYnMM5LAtOn,9599,2022-09-26T19:01:49Z,2022-09-26T19:01:49Z,OWNER,"I tried the tips in https://stackoverflow.com/questions/15258831/how-to-handle-two-dashes-in-rest (not the settings change though, because I might want smart quotes elsewhere) and they didn't work.
Maybe I should disable smart quotes entirely?
I feel like there should be an escaping trick that works here though. I tried `insert -\\-convert` but it didn't help.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1386562662,
https://github.com/simonw/sqlite-utils/issues/483#issuecomment-1258451968,https://api.github.com/repos/simonw/sqlite-utils/issues/483,1258451968,IC_kwDOCGYnMM5LAnQA,9599,2022-09-26T18:37:54Z,2022-09-26T18:40:41Z,OWNER,"The implementation of this can be an almost exact copy of Datasette's, which was added in this commit: https://github.com/simonw/datasette/commit/01fe5b740171bfaea3752fc5754431dac53777e3
Current code for that is here: https://github.com/simonw/datasette/blob/0.62/datasette/cli.py#L319-L340 - which is improved to use the `from runpy import run_module` function.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1363765916,
https://github.com/simonw/sqlite-utils/issues/491#issuecomment-1258450447,https://api.github.com/repos/simonw/sqlite-utils/issues/491,1258450447,IC_kwDOCGYnMM5LAm4P,9599,2022-09-26T18:36:23Z,2022-09-26T18:36:23Z,OWNER,This is also the kind of feature that would need to express itself in both the Python library and the CLI utility.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1383646615,
https://github.com/simonw/sqlite-utils/issues/491#issuecomment-1258449887,https://api.github.com/repos/simonw/sqlite-utils/issues/491,1258449887,IC_kwDOCGYnMM5LAmvf,9599,2022-09-26T18:35:50Z,2022-09-26T18:35:50Z,OWNER,"This is a really interesting idea.
I'm nervous about needing to set the rules for how duplicate tables should be merged though. This feels like a complex topic - one where there isn't necessarily an obviously ""correct"" way of doing it, but where different problems that people are solving might need different merging approaches.
Likewise, merging isn't just a database-to-database thing at that point - I could see a need for merging two tables using similar rules to those used for merging two databases.
So I think I'd want to have some good concrete use-cases in mind before trying to design how something like this should work. Will leave this thread open for people to drop those in!","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1383646615,
https://github.com/simonw/sqlite-utils/issues/492#issuecomment-1258446128,https://api.github.com/repos/simonw/sqlite-utils/issues/492,1258446128,IC_kwDOCGYnMM5LAl0w,9599,2022-09-26T18:32:14Z,2022-09-26T18:33:19Z,OWNER,"This idea would make more sense if there was a good mechanism to say ""run the conversion script held in this file"" as opposed to passing it as an option. This would also make having to remember bash escaping rules ([see tip](https://til.simonwillison.net/zsh/argument-heredoc)) much easier!
`shot-scraper` has that for `--javascript`, using the `--input` option: https://shot-scraper.datasette.io/en/stable/javascript.html#shot-scraper-javascript-help
Maybe `--convert-script` would work here? Or `--convert-file`? It should accept `-` for stdin too.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1386530156,
https://github.com/simonw/sqlite-utils/issues/490#issuecomment-1258437060,https://api.github.com/repos/simonw/sqlite-utils/issues/490,1258437060,IC_kwDOCGYnMM5LAjnE,9599,2022-09-26T18:24:44Z,2022-09-26T18:24:44Z,OWNER,Just saw your great write-up on this: https://jeqo.github.io/notes/2022-09-24-ingest-logs-sqlite/,"{""total_count"": 1, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 1, ""rocket"": 0, ""eyes"": 0}",1382457780,