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/236#issuecomment-645067611,https://api.github.com/repos/simonw/datasette/issues/236,645067611,MDEyOklzc3VlQ29tbWVudDY0NTA2NzYxMQ==,9599,2020-06-16T23:50:12Z,2020-06-16T23:50:59Z,OWNER,"As for your other questions: > 1. I assume the goal is to have a CORS-friendly HTTPS endpoint that hosts the datasette service + user's db. Yes, exactly. I know this will limit the size of database that can be deployed (since Lambda has a 50MB total package limit as far as I can tell) but there are plenty of interesting databases that are small enough to fit there. The new EFS support for Lambda means that theoretically the size of database is now unlimited, which is really interesting. That's what got me inspired to take a look at a proof of concept in #850. > 2. If that's the goal, I think Lambda alone is insufficient. Lambda provides the compute fabric, but not the HTTP routing. You'd also need to add Application Load Balancer or API Gateway to provide an HTTP endpoint that routes to the lambda function. > > Do you have a preference between ALB or API GW? ALB has better economics at scale, but has a minimum monthly cost. API GW has worse per-request economics, but scales to zero when no requests are happening. I personally like scale-to-zero because many of my projects are likely to receive very little traffic. So API GW first, and maybe ALB as an option later on for people operating at scale? > 3. Does Datasette have any native components, or is it all pure python? If it has native bits, they'll likely need to be recompiled to work on Amazon Linux 2. As you've found, the only native component is uvloop which is only needed if uvicorn is being used to serve requests. > 4. There are a few disparate services that need to be wired together to expose a Python service securely to the web. If I was doing this outside of the datasette publish system, I'd use an AWS CloudFormation template. Even within datasette, I think it still makes sense to use a CloudFormation template and just have the publish plugin invoke it (via the standard `aws` cli) with user-specified parameters. Does that sound reasonable to you? For the eventual ""datasette publish lambda"" command I want whatever results in the smallest amount of inconvenience for users. I've been trying out Amazon SAM in #850 and it requires users to run Docker on their machines, which is a pretty huge barrier to entry! I don't have much experience with CloudFormation but it's probably a better bet, especially if you can ""pip install"" the dependencies needed to deploy with it.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",317001500, https://github.com/simonw/datasette/issues/236#issuecomment-645066486,https://api.github.com/repos/simonw/datasette/issues/236,645066486,MDEyOklzc3VlQ29tbWVudDY0NTA2NjQ4Ng==,9599,2020-06-16T23:45:45Z,2020-06-16T23:45:45Z,OWNER,"Hi Colin, Sorry I didn't see this sooner! I've just started digging into this myself, to try and play with the new EFS Lambda support: #850. Yes, uvloop is only needed because of uvicorn. I have a branch here that removes that dependency just for trying out Lambda: https://github.com/simonw/datasette/tree/no-uvicorn - so you can run `pip install https://github.com/simonw/datasette/archive/no-uvicorn.zip` to get that. I'm going to try out your `datasette-lambda` project next - really excited to see how far you've got with it.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",317001500, https://github.com/simonw/datasette/issues/850#issuecomment-645064332,https://api.github.com/repos/simonw/datasette/issues/850,645064332,MDEyOklzc3VlQ29tbWVudDY0NTA2NDMzMg==,9599,2020-06-16T23:37:34Z,2020-06-16T23:37:34Z,OWNER,Just realized Colin Dellow reported an issue with Datasette and Mangum back in April - #719 - and has in fact been working on https://github.com/code402/datasette-lambda for a while!,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645063386,https://api.github.com/repos/simonw/datasette/issues/850,645063386,MDEyOklzc3VlQ29tbWVudDY0NTA2MzM4Ng==,9599,2020-06-16T23:34:07Z,2020-06-16T23:34:07Z,OWNER,"Tried `sam local invoke`: ``` simon@Simons-MacBook-Pro datasette-proof-of-concept % sam local invoke Invoking app.lambda_handler (python3.8) Fetching lambci/lambda:python3.8 Docker container image...... Mounting /private/tmp/datasette-proof-of-concept/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container START RequestId: 7c04480b-5d42-168e-dec0-4e8bf34fa596 Version: $LATEST [INFO] 2020-06-16T23:33:27.24Z 7c04480b-5d42-168e-dec0-4e8bf34fa596 Waiting for application startup. [INFO] 2020-06-16T23:33:27.24Z 7c04480b-5d42-168e-dec0-4e8bf34fa596 LifespanCycleState.STARTUP: 'lifespan.startup.complete' event received from application. [INFO] 2020-06-16T23:33:27.24Z 7c04480b-5d42-168e-dec0-4e8bf34fa596 Application startup complete. [INFO] 2020-06-16T23:33:27.24Z 7c04480b-5d42-168e-dec0-4e8bf34fa596 Waiting for application shutdown. [INFO] 2020-06-16T23:33:27.24Z 7c04480b-5d42-168e-dec0-4e8bf34fa596 LifespanCycleState.SHUTDOWN: 'lifespan.shutdown.complete' event received from application. [ERROR] KeyError: 'requestContext' Traceback (most recent call last):   File ""/var/task/mangum/adapter.py"", line 110, in __call__     return self.handler(event, context)   File ""/var/task/mangum/adapter.py"", line 130, in handler     if ""eventType"" in event[""requestContext""]: END RequestId: 7c04480b-5d42-168e-dec0-4e8bf34fa596 REPORT RequestId: 7c04480b-5d42-168e-dec0-4e8bf34fa596 Init Duration: 1120.76 ms Duration: 7.08 ms Billed Duration: 100 ms Memory Size: 128 MBMax Memory Used: 47 MB {""errorType"":""KeyError"",""errorMessage"":""'requestContext'"",""stackTrace"":["" File \""/var/task/mangum/adapter.py\"", line 110, in __call__\n return self.handler(event, context)\n"","" File \""/var/task/mangum/adapter.py\"", line 130, in handler\n if \""eventType\"" in event[\""requestContext\""]:\n""]} ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645062266,https://api.github.com/repos/simonw/datasette/issues/850,645062266,MDEyOklzc3VlQ29tbWVudDY0NTA2MjI2Ng==,9599,2020-06-16T23:30:12Z,2020-06-16T23:33:12Z,OWNER,"OK, changed `requirements.txt` to this: ``` https://github.com/simonw/datasette/archive/no-uvicorn.zip mangum ``` No `sam build --use-container` runs without errors. Ran `sam deploy` too.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645063058,https://api.github.com/repos/simonw/datasette/issues/850,645063058,MDEyOklzc3VlQ29tbWVudDY0NTA2MzA1OA==,9599,2020-06-16T23:32:57Z,2020-06-16T23:32:57Z,OWNER,https://q7lymja3sj.execute-api.us-east-1.amazonaws.com/Prod/hello/ is now giving me a 500 internal server error.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645061088,https://api.github.com/repos/simonw/datasette/issues/850,645061088,MDEyOklzc3VlQ29tbWVudDY0NTA2MTA4OA==,9599,2020-06-16T23:25:41Z,2020-06-16T23:25:41Z,OWNER,"Someone else ran into this problem: https://github.com/iwpnd/fastapi-aws-lambda-example/issues/1 So I need to be able to pip install MOST of Datasette, but skip `uvicorn`. Tricky. I'll try installing a custom fork?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645060598,https://api.github.com/repos/simonw/datasette/issues/850,645060598,MDEyOklzc3VlQ29tbWVudDY0NTA2MDU5OA==,9599,2020-06-16T23:24:01Z,2020-06-16T23:24:01Z,OWNER,"I changed `requirements.txt` to this: ``` datasette mangum ``` And `app.py` to this: ```python from datasette.app import Datasette from mangum import Mangum datasette = Datasette([], memory=True) lambda_handler = Mangum(datasette.app()) ``` But then when I ran `sam build --use-container` I got this: ``` simon@Simons-MacBook-Pro datasette-proof-of-concept % sam build --use-container Starting Build inside a container Building function 'HelloWorldFunction' Fetching lambci/lambda:build-python3.8 Docker container image...... Mounting /private/tmp/datasette-proof-of-concept/hello_world as /tmp/samcli/source:ro,delegated inside runtime container Build Failed Running PythonPipBuilder:ResolveDependencies Error: PythonPipBuilder:ResolveDependencies - {uvloop==0.14.0(wheel)} ``` `uvloop` isn't actually necessary for this project, since it's used by `uvicorn` which isn't needed if Lambda is serving ASGI traffic directly.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645059663,https://api.github.com/repos/simonw/datasette/issues/850,645059663,MDEyOklzc3VlQ29tbWVudDY0NTA1OTY2Mw==,9599,2020-06-16T23:20:46Z,2020-06-16T23:20:46Z,OWNER,"I added an exclamation mark to hello world and ran `sam deploy` again. https://q7lymja3sj.execute-api.us-east-1.amazonaws.com/Prod/hello/ still shows the old message. Running `sam build --use-container` first and then `sam deploy` did the right thing.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645058947,https://api.github.com/repos/simonw/datasette/issues/850,645058947,MDEyOklzc3VlQ29tbWVudDY0NTA1ODk0Nw==,9599,2020-06-16T23:18:18Z,2020-06-16T23:18:18Z,OWNER,"https://q7lymja3sj.execute-api.us-east-1.amazonaws.com/Prod/hello/ That's a pretty ugly URL. I'm not sure how to get rid of the `/Prod/` prefix on it. Might have to use the `base_url` setting to get something working: https://datasette.readthedocs.io/en/stable/config.html#base-url ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645058617,https://api.github.com/repos/simonw/datasette/issues/850,645058617,MDEyOklzc3VlQ29tbWVudDY0NTA1ODYxNw==,9599,2020-06-16T23:17:09Z,2020-06-16T23:17:09Z,OWNER,"OK, `sam deploy --guided` now works! ``` simon@Simons-MacBook-Pro datasette-proof-of-concept % sam deploy --guided Configuring SAM deploy ====================== Looking for samconfig.toml : Not found Setting default arguments for 'sam deploy' ========================================= Stack Name [sam-app]: datasette-proof-of-concept AWS Region [us-east-1]: #Shows you resources changes to be deployed and require a 'Y' to initiate deploy Confirm changes before deploy [y/N]: #SAM needs permission to be able to create roles to connect to the resources in your template Allow SAM CLI IAM role creation [Y/n]: HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y Save arguments to samconfig.toml [Y/n]: Looking for resources needed for deployment: Not found. Creating the required resources... Successfully created! Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1ksajo4h62s07 A different default S3 bucket can be set in samconfig.toml Saved arguments to config file Running 'sam deploy' for future deployments will use the parameters saved above. The above parameters can be changed by modifying samconfig.toml Learn more about samconfig.toml syntax at https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html Deploying with following values =============================== Stack name : datasette-proof-of-concept Region : us-east-1 Confirm changeset : False Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-1ksajo4h62s07 Capabilities : [""CAPABILITY_IAM""] Parameter overrides : {} Initiating deployment ===================== Uploading to datasette-proof-of-concept/0c208b5656a7aeb6186d49bebc595237 535344 / 535344.0 (100.00%) HelloWorldFunction may not have authorization defined. Uploading to datasette-proof-of-concept/14bd9ce3e21f9c88634d13c0c9b377e4.template 1147 / 1147.0 (100.00%) Waiting for changeset to be created.. CloudFormation stack changeset --------------------------------------------------------------------------------------------------------------------------------------------------------- Operation LogicalResourceId ResourceType --------------------------------------------------------------------------------------------------------------------------------------------------------- + Add HelloWorldFunctionHelloWorldPermissionProd AWS::Lambda::Permission + Add HelloWorldFunctionRole AWS::IAM::Role + Add HelloWorldFunction AWS::Lambda::Function + Add ServerlessRestApiDeployment47fc2d5f9d AWS::ApiGateway::Deployment + Add ServerlessRestApiProdStage AWS::ApiGateway::Stage + Add ServerlessRestApi AWS::ApiGateway::RestApi --------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset created successfully. arn:aws:cloudformation:us-east-1:462092780466:changeSet/samcli-deploy1592349262/d685f2de-87c1-4b8e-b13a-67b94f8fc928 2020-06-16 16:14:29 - Waiting for stack create/update to complete CloudFormation events from changeset --------------------------------------------------------------------------------------------------------------------------------------------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason --------------------------------------------------------------------------------------------------------------------------------------------------------- CREATE_IN_PROGRESS AWS::IAM::Role HelloWorldFunctionRole - CREATE_IN_PROGRESS AWS::IAM::Role HelloWorldFunctionRole Resource creation Initiated CREATE_COMPLETE AWS::IAM::Role HelloWorldFunctionRole - CREATE_IN_PROGRESS AWS::Lambda::Function HelloWorldFunction Resource creation Initiated CREATE_IN_PROGRESS AWS::Lambda::Function HelloWorldFunction - CREATE_COMPLETE AWS::Lambda::Function HelloWorldFunction - CREATE_IN_PROGRESS AWS::ApiGateway::RestApi ServerlessRestApi Resource creation Initiated CREATE_IN_PROGRESS AWS::ApiGateway::RestApi ServerlessRestApi - CREATE_COMPLETE AWS::ApiGateway::RestApi ServerlessRestApi - CREATE_IN_PROGRESS AWS::Lambda::Permission HelloWorldFunctionHelloWorldPermissi - onProd CREATE_IN_PROGRESS AWS::ApiGateway::Deployment ServerlessRestApiDeployment47fc2d5f9 - d CREATE_COMPLETE AWS::ApiGateway::Deployment ServerlessRestApiDeployment47fc2d5f9 - d CREATE_IN_PROGRESS AWS::ApiGateway::Deployment ServerlessRestApiDeployment47fc2d5f9 Resource creation Initiated d CREATE_IN_PROGRESS AWS::Lambda::Permission HelloWorldFunctionHelloWorldPermissi Resource creation Initiated onProd CREATE_IN_PROGRESS AWS::ApiGateway::Stage ServerlessRestApiProdStage - CREATE_COMPLETE AWS::ApiGateway::Stage ServerlessRestApiProdStage - CREATE_IN_PROGRESS AWS::ApiGateway::Stage ServerlessRestApiProdStage Resource creation Initiated CREATE_COMPLETE AWS::Lambda::Permission HelloWorldFunctionHelloWorldPermissi - onProd CREATE_COMPLETE AWS::CloudFormation::Stack datasette-proof-of-concept - --------------------------------------------------------------------------------------------------------------------------------------------------------- CloudFormation outputs from deployed stack --------------------------------------------------------------------------------------------------------------------------------------------------------- Outputs --------------------------------------------------------------------------------------------------------------------------------------------------------- Key HelloWorldFunctionIamRole Description Implicit IAM Role created for Hello World function Value arn:aws:iam::462092780466:role/datasette-proof-of-concept-HelloWorldFunctionRole-8MIDNIV5ECA6 Key HelloWorldApi Description API Gateway endpoint URL for Prod stage for Hello World function Value https://q7lymja3sj.execute-api.us-east-1.amazonaws.com/Prod/hello/ Key HelloWorldFunction Description Hello World Lambda Function ARN Value arn:aws:lambda:us-east-1:462092780466:function:datasette-proof-of-concept-HelloWorldFunction-QTF78ZEUDCB --------------------------------------------------------------------------------------------------------------------------------------------------------- Successfully created/updated stack - datasette-proof-of-concept in us-east-1 ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645056636,https://api.github.com/repos/simonw/datasette/issues/850,645056636,MDEyOklzc3VlQ29tbWVudDY0NTA1NjYzNg==,9599,2020-06-16T23:10:22Z,2020-06-16T23:10:22Z,OWNER,"Clicking that button generated me an access key ID / access key secret pair. Dropping those into `~/.aws/credentials` using this format: ``` [default] aws_access_key_id = your_access_key_id aws_secret_access_key = your_secret_access_key ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645055200,https://api.github.com/repos/simonw/datasette/issues/850,645055200,MDEyOklzc3VlQ29tbWVudDY0NTA1NTIwMA==,9599,2020-06-16T23:05:48Z,2020-06-16T23:05:48Z,OWNER,"Logged in as `simon-administrator` I'm using https://console.aws.amazon.com/iam/home?region=us-east-2#/security_credentials to create credentials: ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645054206,https://api.github.com/repos/simonw/datasette/issues/850,645054206,MDEyOklzc3VlQ29tbWVudDY0NTA1NDIwNg==,9599,2020-06-16T23:02:54Z,2020-06-16T23:04:59Z,OWNER,"I think I need to sign in to the AWS console with this new `simon-administrator` account and create IAM credentials for it. ... for which I needed my root ""account ID"" - a 12 digit number - to use on the IAM login form.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645053923,https://api.github.com/repos/simonw/datasette/issues/850,645053923,MDEyOklzc3VlQ29tbWVudDY0NTA1MzkyMw==,9599,2020-06-16T23:01:49Z,2020-06-16T23:01:49Z,OWNER,"I used https://console.aws.amazon.com/billing/home?#/account and activated ""IAM user/role access to billing information"" - what a puzzling first step! I created a new user with AWS console access (which means access to the web UI) called `simon-administrator` and set a password. I created an `Administrators` group with `AdministratorAccess`. ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645051972,https://api.github.com/repos/simonw/datasette/issues/850,645051972,MDEyOklzc3VlQ29tbWVudDY0NTA1MTk3Mg==,9599,2020-06-16T22:55:04Z,2020-06-16T22:55:04Z,OWNER,"``` simon@Simons-MacBook-Pro datasette-proof-of-concept % sam deploy --guided Configuring SAM deploy ====================== Looking for samconfig.toml : Not found Setting default arguments for 'sam deploy' ========================================= Stack Name [sam-app]: datasette-proof-of-concept AWS Region [us-east-1]: #Shows you resources changes to be deployed and require a 'Y' to initiate deploy Confirm changes before deploy [y/N]: y #SAM needs permission to be able to create roles to connect to the resources in your template Allow SAM CLI IAM role creation [Y/n]: y HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y Save arguments to samconfig.toml [Y/n]: y Error: Failed to create managed resources: Unable to locate credentials ``` I need to get my AWS credentials sorted. I'm going to follow https://docs.aws.amazon.com/IAM/latest/UserGuide/getting-started_create-admin-group.html and https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started-set-up-credentials.html","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645051370,https://api.github.com/repos/simonw/datasette/issues/850,645051370,MDEyOklzc3VlQ29tbWVudDY0NTA1MTM3MA==,9599,2020-06-16T22:53:05Z,2020-06-16T22:53:05Z,OWNER,"``` simon@Simons-MacBook-Pro datasette-proof-of-concept % sam local invoke Invoking app.lambda_handler (python3.8) Fetching lambci/lambda:python3.8 Docker container image.................................................................................................................................................................................................................................... Mounting /private/tmp/datasette-proof-of-concept/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container START RequestId: 4616ab43-6882-1627-e5e3-5a29730d52f9 Version: $LATEST END RequestId: 4616ab43-6882-1627-e5e3-5a29730d52f9 REPORT RequestId: 4616ab43-6882-1627-e5e3-5a29730d52f9 Init Duration: 140.84 ms Duration: 2.49 ms Billed Duration: 100 ms Memory Size: 128 MBMax Memory Used: 25 MB {""statusCode"":200,""body"":""{\""message\"": \""hello world\""}""} simon@Simons-MacBook-Pro datasette-proof-of-concept % sam local invoke Invoking app.lambda_handler (python3.8) Fetching lambci/lambda:python3.8 Docker container image...... Mounting /private/tmp/datasette-proof-of-concept/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container START RequestId: 3189df2f-e9c0-1be4-b9ac-f329c5fcd067 Version: $LATEST END RequestId: 3189df2f-e9c0-1be4-b9ac-f329c5fcd067 REPORT RequestId: 3189df2f-e9c0-1be4-b9ac-f329c5fcd067 Init Duration: 87.22 ms Duration: 2.34 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 25 MB {""statusCode"":200,""body"":""{\""message\"": \""hello world\""}""} ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645050948,https://api.github.com/repos/simonw/datasette/issues/850,645050948,MDEyOklzc3VlQ29tbWVudDY0NTA1MDk0OA==,9599,2020-06-16T22:51:30Z,2020-06-16T22:52:30Z,OWNER,"``` simon@Simons-MacBook-Pro datasette-proof-of-concept % sam build --use-container Starting Build inside a container Building function 'HelloWorldFunction' Fetching lambci/lambda:build-python3.8 Docker container image.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... Mounting /private/tmp/datasette-proof-of-concept/hello_world as /tmp/samcli/source:ro,delegated inside runtime container Build Succeeded Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml Commands you can use next ========================= [*] Invoke Function: sam local invoke [*] Deploy: sam deploy --guided Running PythonPipBuilder:ResolveDependencies Running PythonPipBuilder:CopySource ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645048062,https://api.github.com/repos/simonw/datasette/issues/850,645048062,MDEyOklzc3VlQ29tbWVudDY0NTA0ODA2Mg==,9599,2020-06-16T22:41:33Z,2020-06-16T22:41:33Z,OWNER,"``` simon@Simons-MacBook-Pro /tmp % sam init SAM CLI now collects telemetry to better understand customer needs. You can OPT OUT and disable telemetry collection by setting the environment variable SAM_CLI_TELEMETRY=0 in your shell. Thanks for your help! Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location Choice: 1 Which runtime would you like to use? 1 - nodejs12.x 2 - python3.8 3 - ruby2.7 4 - go1.x 5 - java11 6 - dotnetcore3.1 7 - nodejs10.x 8 - python3.7 9 - python3.6 10 - python2.7 11 - ruby2.5 12 - java8 13 - dotnetcore2.1 Runtime: 2 Project name [sam-app]: datasette-proof-of-concept Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git AWS quick start application templates: 1 - Hello World Example 2 - EventBridge Hello World 3 - EventBridge App from scratch (100+ Event Schemas) 4 - Step Functions Sample App (Stock Trader) Template selection: 1 ----------------------- Generating application: ----------------------- Name: datasette-proof-of-concept Runtime: python3.8 Dependency Manager: pip Application Template: hello-world Output Directory: . Next steps can be found in the README file at ./datasette-proof-of-concept/README.md ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645047703,https://api.github.com/repos/simonw/datasette/issues/850,645047703,MDEyOklzc3VlQ29tbWVudDY0NTA0NzcwMw==,9599,2020-06-16T22:40:19Z,2020-06-16T22:40:19Z,OWNER,"Installed SAM: ``` brew tap aws/tap brew install aws-sam-cli sam --version SAM CLI, version 0.52.0 ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645045055,https://api.github.com/repos/simonw/datasette/issues/850,645045055,MDEyOklzc3VlQ29tbWVudDY0NTA0NTA1NQ==,9599,2020-06-16T22:31:49Z,2020-06-16T22:31:49Z,OWNER,It looks like SAM - AWS Serverless Application Model - is the currently recommended way to deploy Python apps to Lambda from the command-line: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645042625,https://api.github.com/repos/simonw/datasette/issues/850,645042625,MDEyOklzc3VlQ29tbWVudDY0NTA0MjYyNQ==,9599,2020-06-16T22:24:26Z,2020-06-16T22:24:26Z,OWNER,"From https://mangum.io/adapter/ > The AWS Lambda handler `event` and `context` arguments are made available to an ASGI application in the ASGI connection scope. > > ``` > scope['aws.event'] > scope['aws.context'] > ``` I can use https://github.com/simonw/datasette-debug-asgi to see that.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645041663,https://api.github.com/repos/simonw/datasette/issues/850,645041663,MDEyOklzc3VlQ29tbWVudDY0NTA0MTY2Mw==,9599,2020-06-16T22:21:44Z,2020-06-16T22:21:44Z,OWNER,"https://github.com/jordaneremieff/mangum looks like the best way to run an ASGI app on Lambda at the moment. ```python from mangum import Mangum async def app(scope, receive, send): await send( { ""type"": ""http.response.start"", ""status"": 200, ""headers"": [[b""content-type"", b""text/plain; charset=utf-8""]], } ) await send({""type"": ""http.response.body"", ""body"": b""Hello, world!""}) handler = Mangum(app) ``` ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645032643,https://api.github.com/repos/simonw/datasette/issues/850,645032643,MDEyOklzc3VlQ29tbWVudDY0NTAzMjY0Mw==,9599,2020-06-16T21:57:10Z,2020-06-16T21:57:10Z,OWNER,https://docs.aws.amazon.com/efs/latest/ug/wt1-getting-started.html is an EFS walk-through using the AWS CLI tool instead of clicking around in their web interface.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645031225,https://api.github.com/repos/simonw/datasette/issues/850,645031225,MDEyOklzc3VlQ29tbWVudDY0NTAzMTIyNQ==,9599,2020-06-16T21:53:25Z,2020-06-16T21:53:25Z,OWNER,"Easier solution to this might be to have two functions - a ""read-only"" one which is allowed to scale as much as it likes, and a ""write-only"" one which can write to the database files but is limited to running a maximum of one Lambda instance. https://docs.aws.amazon.com/lambda/latest/dg/invocation-scaling.html","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/850#issuecomment-645030262,https://api.github.com/repos/simonw/datasette/issues/850,645030262,MDEyOklzc3VlQ29tbWVudDY0NTAzMDI2Mg==,9599,2020-06-16T21:51:01Z,2020-06-16T21:51:39Z,OWNER,"File locking is interesting here. https://docs.aws.amazon.com/lambda/latest/dg/services-efs.html > Amazon EFS supports [file locking](https://docs.aws.amazon.com/efs/latest/ug/how-it-works.html#consistency) to prevent corruption if multiple functions try to write to the same file system at the same time. Locking in Amazon EFS follows the NFS v4.1 protocol for advisory locking, and enables your applications to use both whole file and byte range locks. SQLite can apparently work on NFS v4.1. I think I'd rather set things up so there's only ever one writer - so a Datasette instance could scale reads by running lots more lambda functions but only one function ever writes to a file at a time. Not sure if that's feasible with Lambda though - maybe by adding some additional shared state mechanism like Redis?","{""total_count"": 1, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 1, ""rocket"": 0, ""eyes"": 0}",639993467, https://github.com/simonw/datasette/issues/690#issuecomment-644987083,https://api.github.com/repos/simonw/datasette/issues/690,644987083,MDEyOklzc3VlQ29tbWVudDY0NDk4NzA4Mw==,9599,2020-06-16T20:11:35Z,2020-06-16T20:11:35Z,OWNER,"Twitter conversation about drop-down menu solutions that are accessible, fast loading and use minimal JavaScript: https://twitter.com/simonw/status/1272974294545395712 I _really_ like the approach taken by GitHub Primer, which builds on top of HTML `` `
` tags: https://primer.style/css/components/dropdown","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",573755726, https://github.com/simonw/datasette/issues/849#issuecomment-644584075,https://api.github.com/repos/simonw/datasette/issues/849,644584075,MDEyOklzc3VlQ29tbWVudDY0NDU4NDA3NQ==,9599,2020-06-16T07:24:08Z,2020-06-16T07:24:08Z,OWNER,This guide is fantastic - I'll be following it closely: https://github.com/chancancode/branch-rename/blob/main/README.md - in particular the Action to mirror master and main for a while.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639072811, https://github.com/simonw/datasette/issues/849#issuecomment-644384787,https://api.github.com/repos/simonw/datasette/issues/849,644384787,MDEyOklzc3VlQ29tbWVudDY0NDM4NDc4Nw==,9599,2020-06-15T20:56:07Z,2020-06-15T20:56:19Z,OWNER,"The big question is how this impacts existing CI configuration. `datasette-psutil` is configured to use Circle CI, what happens if I push a new commit?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639072811, https://github.com/simonw/datasette/issues/849#issuecomment-644384417,https://api.github.com/repos/simonw/datasette/issues/849,644384417,MDEyOklzc3VlQ29tbWVudDY0NDM4NDQxNw==,9599,2020-06-15T20:55:23Z,2020-06-15T20:55:23Z,OWNER,"I'm doing https://github.com/simonw/datasette-psutil first. In my local checkout: ``` git branch -m master main git push -u origin main ``` (Thanks, https://www.hanselman.com/blog/EasilyRenameYourGitDefaultBranchFromMasterToMain.aspx) Then in https://github.com/simonw/datasette-psutil/settings/branches I changed the default branch to `main`. Links to these docs: https://help.github.com/en/github/administering-a-repository/setting-the-default-branch That worked! https://github.com/simonw/datasette-psutil One catch, which I think will impact my most widely used repos the most (like datasette) - linking to a specific file now looks like this: https://github.com/simonw/datasette-psutil/blob/main/datasette_psutil/__init__.py The old https://github.com/simonw/datasette-psutil/blob/master/datasette_psutil/__init__.py link is presumably frozen in time? I've definitely got links spread around the web to my ""most recent version of this code"" that would use the `master` reference, which would need to be updated to `main` instead. Most of those are probably in the Datasette docs and on my blog though.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639072811, https://github.com/simonw/datasette/issues/849#issuecomment-644322234,https://api.github.com/repos/simonw/datasette/issues/849,644322234,MDEyOklzc3VlQ29tbWVudDY0NDMyMjIzNA==,9599,2020-06-15T19:06:16Z,2020-06-15T19:06:16Z,OWNER,I'll make this change on a few of my other repos first to make sure I haven't missed any tricky edge-cases.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",639072811, https://github.com/simonw/datasette/issues/847#issuecomment-643704730,https://api.github.com/repos/simonw/datasette/issues/847,643704730,MDEyOklzc3VlQ29tbWVudDY0MzcwNDczMA==,9599,2020-06-14T01:28:34Z,2020-06-14T01:28:34Z,OWNER,"Here's the plugin that adds those custom SQLite functions: ```python from datasette import hookimpl from coverage.numbits import register_sqlite_functions @hookimpl def prepare_connection(conn): register_sqlite_functions(conn) ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638259643, https://github.com/simonw/datasette/issues/847#issuecomment-643704565,https://api.github.com/repos/simonw/datasette/issues/847,643704565,MDEyOklzc3VlQ29tbWVudDY0MzcwNDU2NQ==,9599,2020-06-14T01:26:56Z,2020-06-14T01:26:56Z,OWNER,"On closer inspection, I don't know if there's that much useful stuff you can do with the data from `.coverage` on its own. Consider the following query against a `.coverage` run against Datasette itself: ```sql select file_id, context_id, numbits_to_nums(numbits) from line_bits ``` It looks like this tells me which lines of which files were executed during the test run. But... without the actual source code, I don't think I can calculate the coverage percentage for each file. I don't want to count comment lines or whitespace as untested for example, and I don't know how many lines were in the file. If I'm right that it's not possible to calculate percentage coverage from just the `.coverage` data then I'll need to do something a bit more involved - maybe parsing the `coverage.xml` report and loading that into my own schema?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638259643, https://github.com/simonw/datasette/issues/847#issuecomment-643702715,https://api.github.com/repos/simonw/datasette/issues/847,643702715,MDEyOklzc3VlQ29tbWVudDY0MzcwMjcxNQ==,9599,2020-06-14T01:03:30Z,2020-06-14T01:03:40Z,OWNER,Filed a related issue with some ideas against `coveragepy` here: https://github.com/nedbat/coveragepy/issues/999,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638259643, https://github.com/simonw/datasette/issues/846#issuecomment-643699583,https://api.github.com/repos/simonw/datasette/issues/846,643699583,MDEyOklzc3VlQ29tbWVudDY0MzY5OTU4Mw==,9599,2020-06-14T00:26:31Z,2020-06-14T00:26:31Z,OWNER,"That seems to have fixed the problem, at least for the moment.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638241779, https://github.com/simonw/datasette/issues/846#issuecomment-643699063,https://api.github.com/repos/simonw/datasette/issues/846,643699063,MDEyOklzc3VlQ29tbWVudDY0MzY5OTA2Mw==,9599,2020-06-14T00:22:32Z,2020-06-14T00:22:32Z,OWNER,"Idea: `num_sql_threads` (described as ""Number of threads in the thread pool for executing SQLite queries"") defaults to 3 - can I knock that down to 1 in the tests and open less connections as a result?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638241779, https://github.com/simonw/datasette/issues/846#issuecomment-643698790,https://api.github.com/repos/simonw/datasette/issues/846,643698790,MDEyOklzc3VlQ29tbWVudDY0MzY5ODc5MA==,9599,2020-06-14T00:20:42Z,2020-06-14T00:20:42Z,OWNER,"Released a new plugin, `datasette-psutil`, as a side-effect of this investigation: https://github.com/simonw/datasette-psutil","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638241779, https://github.com/simonw/datasette/issues/846#issuecomment-643685669,https://api.github.com/repos/simonw/datasette/issues/846,643685669,MDEyOklzc3VlQ29tbWVudDY0MzY4NTY2OQ==,9599,2020-06-13T22:24:22Z,2020-06-13T22:24:22Z,OWNER,"I tried this experiment: ```python import sqlite3, psutil def show_things(): conn = sqlite3.connect(""fixtures.db"") tables = [r[0] for r in conn.execute(""select * from sqlite_master"").fetchall()] return tables print(psutil.Process().open_files()) print(show_things()) print(psutil.Process().open_files()) ``` To see if the connection would be automatically released when the `conn` variable was garbage collected at the end of the function... and it was correctly released - the two calls to `open_files()` showed that the file did not remain open. Likewise: ``` In [11]: conn = sqlite3.connect(""fixtures.db"") In [12]: psutil.Process().open_files() Out[12]: [popenfile(path='/Users/simon/.ipython/profile_default/history.sqlite', fd=4), popenfile(path='/Users/simon/.ipython/profile_default/history.sqlite', fd=5), popenfile(path='/Users/simon/Dropbox/Development/datasette/fixtures.db', fd=12)] In [13]: del conn In [14]: psutil.Process().open_files() Out[14]: [popenfile(path='/Users/simon/.ipython/profile_default/history.sqlite', fd=4), popenfile(path='/Users/simon/.ipython/profile_default/history.sqlite', fd=5)] ``` So presumably there's something about the way my pytest fixtures work that's causing the many different `Datasette()` instances and their underlying SQLite connections that I create not to be cleaned up later.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638241779, https://github.com/simonw/datasette/issues/846#issuecomment-643685333,https://api.github.com/repos/simonw/datasette/issues/846,643685333,MDEyOklzc3VlQ29tbWVudDY0MzY4NTMzMw==,9599,2020-06-13T22:19:38Z,2020-06-13T22:19:38Z,OWNER,That's 91 open files but only 29 unique filenames.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638241779, https://github.com/simonw/datasette/issues/846#issuecomment-643685207,https://api.github.com/repos/simonw/datasette/issues/846,643685207,MDEyOklzc3VlQ29tbWVudDY0MzY4NTIwNw==,9599,2020-06-13T22:18:01Z,2020-06-13T22:18:01Z,OWNER,"This shows currently open files (after `pip install psutil`): ``` import psutil psutil.Process().open_files() ``` I ran it inside `pytest -x --pdb` and got this: ``` > /Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/lib/python3.7/site-packages/jinja2/utils.py(154)open_if_exists() -> return open(filename, mode) (Pdb) import psutil (Pdb) psutil.Process().open_files() popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp9uhx5d8x/fixtures.db', fd=10), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpyfw44ica/fixtures.dot.db', fd=11), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpyrg6g48b/fixtures.db', fd=12), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp33kkg62s/fixtures.db', fd=13), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp33kkg62s/fixtures.db', fd=14), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp33kkg62s/fixtures.db', fd=15), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp33kkg62s/fixtures.db', fd=16), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp33kkg62s/fixtures.db', fd=17), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp33kkg62s/fixtures.db', fd=18), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp33kkg62s/fixtures.db', fd=19), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpng4lg84_/fixtures.db', fd=20), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp9uhx5d8x/fixtures.db', fd=21), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp9uhx5d8x/fixtures.db', fd=22), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp9uhx5d8x/fixtures.db', fd=23), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmph11oalw_/fixtures.db', fd=24), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpyfw44ica/fixtures.dot.db', fd=25), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpyfw44ica/fixtures.dot.db', fd=26), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpyfw44ica/fixtures.dot.db', fd=27), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpiorb2bo9/fixtures.db', fd=28), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpyrg6g48b/fixtures.db', fd=29), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpyrg6g48b/fixtures.db', fd=30), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpyrg6g48b/fixtures.db', fd=31), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmprvyj5udv/fixtures.db', fd=32), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpng4lg84_/fixtures.db', fd=33), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpng4lg84_/fixtures.db', fd=34), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpng4lg84_/fixtures.db', fd=35), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpb_l6gmq0/fixtures.db', fd=36), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmph11oalw_/extra database.db', fd=40), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpf0py4thp/fixtures.db', fd=41), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpiorb2bo9/fixtures.db', fd=42), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpiorb2bo9/fixtures.db', fd=43), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpiorb2bo9/fixtures.db', fd=44), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmph11oalw_/fixtures.db', fd=45), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmph11oalw_/fixtures.db', fd=52), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/fixtures.db', fd=53), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmprvyj5udv/fixtures.db', fd=54), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmprvyj5udv/fixtures.db', fd=55), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmprvyj5udv/fixtures.db', fd=56), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpoveuwqn6/fixtures.db', fd=57), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpb_l6gmq0/fixtures.db', fd=61), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpb_l6gmq0/fixtures.db', fd=62), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpb_l6gmq0/fixtures.db', fd=63), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp_j4h9mrn/fixtures.db', fd=64), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpf0py4thp/fixtures.db', fd=65), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpf0py4thp/fixtures.db', fd=66), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpf0py4thp/extra database.db', fd=67), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpf0py4thp/extra database.db', fd=68), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpf0py4thp/fixtures.db', fd=69), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpf0py4thp/extra database.db', fd=70) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpub3eodj1/fixtures.db', fd=71) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/fixtures.db', fd=72) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/foo.db', fd=73) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/foo.db', fd=74) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/fixtures.db', fd=75) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/fixtures.db', fd=76) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/foo.db', fd=77) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/foo-bar.db', fd=78) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/foo-bar.db', fd=79) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpwgcnmg4b/foo-bar.db', fd=80) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/pytest-of-simon/pytest-4/config-dir0/immutable.db', fd=81), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpoveuwqn6/fixtures.db', fd=82), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpoveuwqn6/fixtures.db', fd=83), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpoveuwqn6/fixtures.db', fd=84), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp44w5d5wo/fixtures.db', fd=85), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp_j4h9mrn/fixtures.db', fd=86), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp_j4h9mrn/fixtures.db', fd=87), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp_j4h9mrn/fixtures.db', fd=88), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpvu7h14uy/fixtures.db', fd=89), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/pytest-of-simon/pytest-4/config-dir0/demo.db', fd=119), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/pytest-of-simon/pytest-4/config-dir0/demo.db', fd=120), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/pytest-of-simon/pytest-4/config-dir0/demo.db', fd=121), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp0xcnrjag/fixtures.db', fd=122), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpub3eodj1/fixtures.db', fd=123), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpub3eodj1/fixtures.db', fd=124), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpub3eodj1/fixtures.db', fd=125), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpfz8go8rk/fixtures.db', fd=126), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp44w5d5wo/fixtures.db', fd=127), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp44w5d5wo/fixtures.db', fd=128), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp44w5d5wo/fixtures.db', fd=129), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp5j3k1ep_/fixtures.db', fd=130) popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpvu7h14uy/fixtures.db', fd=131), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpvu7h14uy/fixtures.db', fd=132), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpvo3cobk9/fixtures.db', fd=133), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp2t9txyir/fixtures.db', fd=134), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpfz8go8rk/fixtures.db', fd=135), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmpfz8go8rk/fixtures.db', fd=136), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp7h3skv8b/fixtures.db', fd=137), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp5j3k1ep_/fixtures.db', fd=138), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp5j3k1ep_/fixtures.db', fd=139), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp5j3k1ep_/fixtures.db', fd=140), popenfile(path='/private/var/folders/wr/hn3206rs1yzgq3r49bz8nvnh0000gn/T/tmp5j3k1ep_/extra database.db', fd=141), ``` So yeah, that's too many open files! ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638241779, https://github.com/simonw/datasette/issues/841#issuecomment-643681747,https://api.github.com/repos/simonw/datasette/issues/841,643681747,MDEyOklzc3VlQ29tbWVudDY0MzY4MTc0Nw==,9599,2020-06-13T21:38:46Z,2020-06-13T21:38:46Z,OWNER,Closing this because I've researched feasibility. I may start a milestone in the future to help me get to 100%.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/pull/844#issuecomment-643681517,https://api.github.com/repos/simonw/datasette/issues/844,643681517,MDEyOklzc3VlQ29tbWVudDY0MzY4MTUxNw==,9599,2020-06-13T21:36:15Z,2020-06-13T21:36:15Z,OWNER,"OK, this works now: https://codecov.io/gh/simonw/datasette/tree/1210d9f41841bdca450f85a2342cdb0ff339c1b4","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638230433, https://github.com/simonw/datasette/issues/843#issuecomment-643676314,https://api.github.com/repos/simonw/datasette/issues/843,643676314,MDEyOklzc3VlQ29tbWVudDY0MzY3NjMxNA==,9599,2020-06-13T20:47:37Z,2020-06-13T20:47:37Z,OWNER,I can use this action: https://github.com/codecov/codecov-action,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638229448, https://github.com/simonw/datasette/issues/843#issuecomment-643676069,https://api.github.com/repos/simonw/datasette/issues/843,643676069,MDEyOklzc3VlQ29tbWVudDY0MzY3NjA2OQ==,9599,2020-06-13T20:45:29Z,2020-06-13T20:45:29Z,OWNER,I set up https://codecov.io/gh/simonw/datasette/settings and added a `CODECOV_TOKEN` to the GitHub Actions secrets for this repo.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638229448, https://github.com/simonw/datasette/issues/842#issuecomment-643663005,https://api.github.com/repos/simonw/datasette/issues/842,643663005,MDEyOklzc3VlQ29tbWVudDY0MzY2MzAwNQ==,9599,2020-06-13T18:51:57Z,2020-06-13T18:51:57Z,OWNER,"Two potential designs: - `_actor_id`, `_request_ip`, `_now_timestamp` - so special reserved parameters - a SQL function: `update blah set up = special('ip')` I fee the first would be easier to implement.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638212085, https://github.com/simonw/datasette/issues/841#issuecomment-643661125,https://api.github.com/repos/simonw/datasette/issues/841,643661125,MDEyOklzc3VlQ29tbWVudDY0MzY2MTEyNQ==,9599,2020-06-13T18:35:30Z,2020-06-13T18:36:50Z,OWNER,"I ran export CODECOV_TOKEN=""f7935cad..."", then ran this: ``` datasette $ bash <(curl -s https://codecov.io/bash) _____ _ / ____| | | | | ___ __| | ___ ___ _____ __ | | / _ \ / _` |/ _ \/ __/ _ \ \ / / | |___| (_) | (_| | __/ (_| (_) \ V / \_____\___/ \__,_|\___|\___\___/ \_/ Bash-20200602-f809a24 x> No CI provider detected. Testing inside Docker? http://docs.codecov.io/docs/testing-with-docker Testing with Tox? https://docs.codecov.io/docs/python#section-testing-with-tox project root: . --> token set from env Yaml not found, that's ok! Learn more at http://docs.codecov.io/docs/codecov-yaml ==> Running gcov in . (disable via -X gcov) ==> Searching for coverage reports in: + . -> Found 1 reports ==> Detecting git/mercurial file structure ==> Reading reports + ./coverage.xml bytes=139174 ==> Appending adjustments https://docs.codecov.io/docs/fixing-reports -> No adjustments found ==> Gzipping contents ==> Uploading reports url: https://codecov.io query: branch=master&commit=0e49842e227a0f1f69d48108c87d17fe0379e548&build=&build_url=&name=&tag=&slug=simonw%2Fdatasette&service=&flags=&pr=&job= -> Pinging Codecov https://codecov.io/upload/v4?package=bash-20200602-f809a24&token=secret&branch=master&commit=0e49842e227a0f1f69d48108c87d17fe0379e548&build=&build_url=&name=&tag=&slug=simonw%2Fdatasette&service=&flags=&pr=&job= -> Uploading -> View reports at https://codecov.io/github/simonw/datasette/commit/0e49842e227a0f1f69d48108c87d17fe0379e548 ``` But https://codecov.io/github/simonw/datasette/commit/0e49842e227a0f1f69d48108c87d17fe0379e548 is a 404, so it doesn't seem to have worked? UPDATE: It works now, took about 30 seconds before the report showed up at that URL.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/issues/841#issuecomment-643660757,https://api.github.com/repos/simonw/datasette/issues/841,643660757,MDEyOklzc3VlQ29tbWVudDY0MzY2MDc1Nw==,9599,2020-06-13T18:32:20Z,2020-06-13T18:32:20Z,OWNER,"Looking at options for publishing coverage reports: * https://github.com/codecov/codecov-action * https://github.com/coveralls-clients/coveralls-python I'm going to try https://codecov.io/","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/issues/841#issuecomment-643660427,https://api.github.com/repos/simonw/datasette/issues/841,643660427,MDEyOklzc3VlQ29tbWVudDY0MzY2MDQyNw==,9599,2020-06-13T18:29:30Z,2020-06-13T18:29:36Z,OWNER,"This one looks easy enough to fix: ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/issues/841#issuecomment-643658036,https://api.github.com/repos/simonw/datasette/issues/841,643658036,MDEyOklzc3VlQ29tbWVudDY0MzY1ODAzNg==,9599,2020-06-13T18:08:13Z,2020-06-13T18:08:13Z,OWNER,"From digging through that report it looks like the majority stuff that isn't fully covered is corner-cases... which are the kind of things I really do want the tests to catch. I'm not entirely ready to commit to 100%, but I'm going to start digging through and seeing how close I can get. If I can get to 98% (I'm on 91% already) I may as well push all the way to 100.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/issues/841#issuecomment-643657287,https://api.github.com/repos/simonw/datasette/issues/841,643657287,MDEyOklzc3VlQ29tbWVudDY0MzY1NzI4Nw==,9599,2020-06-13T18:01:39Z,2020-06-13T18:01:39Z,OWNER,Added `--cov-report html` and got this report: https://static.simonwillison.net/static/2020/htmlcov-issue-841/index.html,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/issues/841#issuecomment-643656053,https://api.github.com/repos/simonw/datasette/issues/841,643656053,MDEyOklzc3VlQ29tbWVudDY0MzY1NjA1Mw==,9599,2020-06-13T17:50:34Z,2020-06-13T17:50:34Z,OWNER,"Added a `.coveragerc` file: ``` [run] omit = datasette/_version.py, datasette/utils/shutil_backport.py ``` And ran again: `pytest --cov=datasette --cov-config=.coveragerc` ``` Name Stmts Miss Cover ------------------------------------------------------ datasette/__init__.py 3 0 100% datasette/__main__.py 3 3 0% datasette/actor_auth_cookie.py 19 3 84% datasette/app.py 499 27 95% datasette/cli.py 157 45 71% datasette/database.py 233 17 93% datasette/default_permissions.py 39 0 100% datasette/facets.py 209 24 89% datasette/filters.py 122 7 94% datasette/hookspecs.py 19 0 100% datasette/inspect.py 37 23 38% datasette/plugins.py 34 6 82% datasette/publish/__init__.py 0 0 100% datasette/publish/cloudrun.py 55 2 96% datasette/publish/common.py 19 1 95% datasette/publish/heroku.py 95 13 86% datasette/renderer.py 63 4 94% datasette/sql_functions.py 4 0 100% datasette/tracer.py 85 16 81% datasette/utils/__init__.py 503 31 94% datasette/utils/asgi.py 253 25 90% datasette/version.py 4 0 100% datasette/views/__init__.py 0 0 100% datasette/views/base.py 288 19 93% datasette/views/database.py 120 2 98% datasette/views/index.py 57 2 96% datasette/views/special.py 72 16 78% datasette/views/table.py 418 18 96% ------------------------------------------------------ TOTAL 3410 304 91% ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/issues/841#issuecomment-643655108,https://api.github.com/repos/simonw/datasette/issues/841,643655108,MDEyOklzc3VlQ29tbWVudDY0MzY1NTEwOA==,9599,2020-06-13T17:43:15Z,2020-06-13T17:43:15Z,OWNER,"Using https://pypi.org/project/pytest-cov/ and running `pytest --cov=datasette`: ``` ---------- coverage: platform darwin, python 3.7.7-final-0 ----------- Name Stmts Miss Cover -------------------------------------------------------- datasette/__init__.py 3 0 100% datasette/__main__.py 3 3 0% datasette/_version.py 277 152 45% datasette/actor_auth_cookie.py 19 3 84% datasette/app.py 499 27 95% datasette/cli.py 157 45 71% datasette/database.py 233 17 93% datasette/default_permissions.py 39 0 100% datasette/facets.py 209 24 89% datasette/filters.py 122 7 94% datasette/hookspecs.py 19 0 100% datasette/inspect.py 37 23 38% datasette/plugins.py 34 6 82% datasette/publish/__init__.py 0 0 100% datasette/publish/cloudrun.py 55 2 96% datasette/publish/common.py 19 1 95% datasette/publish/heroku.py 95 13 86% datasette/renderer.py 63 4 94% datasette/sql_functions.py 4 0 100% datasette/tracer.py 85 16 81% datasette/utils/__init__.py 503 31 94% datasette/utils/asgi.py 253 25 90% datasette/utils/shutil_backport.py 44 40 9% datasette/version.py 4 0 100% datasette/views/__init__.py 0 0 100% datasette/views/base.py 288 19 93% datasette/views/database.py 120 2 98% datasette/views/index.py 57 2 96% datasette/views/special.py 72 16 78% datasette/views/table.py 418 18 96% -------------------------------------------------------- TOTAL 3731 496 87% ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/issues/834#issuecomment-643648359,https://api.github.com/repos/simonw/datasette/issues/834,643648359,MDEyOklzc3VlQ29tbWVudDY0MzY0ODM1OQ==,9599,2020-06-13T16:47:29Z,2020-06-13T16:47:29Z,OWNER,"Implementing this is proving surprisingly tricky, because of the need to be able to optionally `await` the returned value. It's a bit of a fiddle to get this to work within unit tests because they run in non-async functions - due to this cunning `async_to_sync` usage in the test client: https://github.com/simonw/datasette/blob/b906030235efbdff536405d66078f4868ce0d3bd/tests/fixtures.py#L115-L133 I could switch to using `async def test_*` functions decorated with `@pytest.mark.asyncio` but I'd rather not re-engineer the entire test suite just for this one feature, so I'll try to find another way.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637342551, https://github.com/simonw/datasette/issues/841#issuecomment-643576372,https://api.github.com/repos/simonw/datasette/issues/841,643576372,MDEyOklzc3VlQ29tbWVudDY0MzU3NjM3Mg==,9599,2020-06-13T06:08:34Z,2020-06-13T06:08:34Z,OWNER,Starlette achieves this. https://github.com/encode/starlette,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",638104520, https://github.com/simonw/datasette/issues/834#issuecomment-643510240,https://api.github.com/repos/simonw/datasette/issues/834,643510240,MDEyOklzc3VlQ29tbWVudDY0MzUxMDI0MA==,9599,2020-06-12T22:40:26Z,2020-06-12T22:40:26Z,OWNER,Another use-case: plugins that need their own database with the correct tables. They can write to the database on startup to create their tables.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637342551, https://github.com/simonw/datasette/issues/834#issuecomment-643509358,https://api.github.com/repos/simonw/datasette/issues/834,643509358,MDEyOklzc3VlQ29tbWVudDY0MzUwOTM1OA==,9599,2020-06-12T22:36:37Z,2020-06-12T22:36:37Z,OWNER,This should be able to optionally return an async function which is then awaited.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637342551, https://github.com/simonw/datasette/issues/805#issuecomment-643501428,https://api.github.com/repos/simonw/datasette/issues/805,643501428,MDEyOklzc3VlQ29tbWVudDY0MzUwMTQyOA==,9599,2020-06-12T22:06:08Z,2020-06-12T22:06:08Z,OWNER,"This needs the `startup` hook, see https://github.com/simonw/datasette/issues/834#issuecomment-643501064","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632724154, https://github.com/simonw/datasette/issues/834#issuecomment-643501064,https://api.github.com/repos/simonw/datasette/issues/834,643501064,MDEyOklzc3VlQ29tbWVudDY0MzUwMTA2NA==,9599,2020-06-12T22:04:43Z,2020-06-12T22:04:43Z,OWNER,Another use-case for this: I want to use the `--root` option on Glitch but it gives me a 127.0.0.1 URL. Glitch has a `PROJECT_DOMAIN` environment variable which tells me the URL. A `datasette-glitch` plugin could use a `startup` hook to output the correct login URL.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637342551, https://github.com/simonw/datasette/issues/840#issuecomment-643454625,https://api.github.com/repos/simonw/datasette/issues/840,643454625,MDEyOklzc3VlQ29tbWVudDY0MzQ1NDYyNQ==,9599,2020-06-12T19:47:38Z,2020-06-12T19:47:53Z,OWNER,"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}",637966833, https://github.com/simonw/datasette/issues/840#issuecomment-643453128,https://api.github.com/repos/simonw/datasette/issues/840,643453128,MDEyOklzc3VlQ29tbWVudDY0MzQ1MzEyOA==,9599,2020-06-12T19:43:15Z,2020-06-12T19:43:15Z,OWNER,"I don't like how this often involves a logout link that can be maliciously activated. I'm going to use a CSRF protected form button styled to look like a link instead.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637966833, https://github.com/simonw/sqlite-utils/issues/115#issuecomment-643406939,https://api.github.com/repos/simonw/sqlite-utils/issues/115,643406939,MDEyOklzc3VlQ29tbWVudDY0MzQwNjkzOQ==,9599,2020-06-12T17:51:11Z,2020-06-12T17:51:11Z,OWNER,https://github.com/simonw/sqlite-utils/blob/03ee97d2258254581bea72842518904fc1cbe60f/tests/test_cli.py#L1112-L1128,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637889964, https://github.com/simonw/datasette/issues/806#issuecomment-643010591,https://api.github.com/repos/simonw/datasette/issues/806,643010591,MDEyOklzc3VlQ29tbWVudDY0MzAxMDU5MQ==,9599,2020-06-12T01:13:06Z,2020-06-12T01:13:06Z,OWNER,Tests are passing again: https://github.com/simonw/datasette/commit/9ae0d483ead93c0832142e5dc85959ae3c8f73ea,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632753851, https://github.com/simonw/datasette/issues/806#issuecomment-643000948,https://api.github.com/repos/simonw/datasette/issues/806,643000948,MDEyOklzc3VlQ29tbWVudDY0MzAwMDk0OA==,9599,2020-06-12T00:34:21Z,2020-06-12T00:34:21Z,OWNER,I'm going to add https://github.com/simonw/datasette-auth-tokens and https://github.com/simonw/datasette-permissions-sql to the documentation and release notes in a few places.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632753851, https://github.com/simonw/datasette/issues/806#issuecomment-642998097,https://api.github.com/repos/simonw/datasette/issues/806,642998097,MDEyOklzc3VlQ29tbWVudDY0Mjk5ODA5Nw==,9599,2020-06-12T00:26:00Z,2020-06-12T00:26:00Z,OWNER,"OK, I'm ready to ship. Last check of the release notes, then I'll update the news section in the README and release 0.44!","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632753851, https://github.com/simonw/datasette/issues/838#issuecomment-642993277,https://api.github.com/repos/simonw/datasette/issues/838,642993277,MDEyOklzc3VlQ29tbWVudDY0Mjk5MzI3Nw==,9599,2020-06-12T00:18:26Z,2020-06-12T00:18:50Z,OWNER,"Have you tried this without the `ProxyPassReverse` directive? I'm worried that might be confusing Datasette. This is the test I used to ensure this feature works - it scrapes all of the links on a bunch of different pages. Could it be missing something here? https://github.com/simonw/datasette/blob/647c5ff0f3e8140f40d7f41f0874ce4e1f4df65c/tests/test_html.py#L1233-L1274 ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637395097, https://github.com/simonw/datasette/issues/824#issuecomment-642991513,https://api.github.com/repos/simonw/datasette/issues/824,642991513,MDEyOklzc3VlQ29tbWVudDY0Mjk5MTUxMw==,9599,2020-06-12T00:11:50Z,2020-06-12T00:11:50Z,OWNER,Done: https://github.com/simonw/datasette-auth-tokens and https://pypi.org/project/datasette-auth-tokens/,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",635108074, https://github.com/simonw/datasette/issues/833#issuecomment-642958225,https://api.github.com/repos/simonw/datasette/issues/833,642958225,MDEyOklzc3VlQ29tbWVudDY0Mjk1ODIyNQ==,9599,2020-06-11T22:15:32Z,2020-06-11T22:15:32Z,OWNER,https://github.com/simonw/datasette/blob/29c5ff493ad7918b8fc44ea7920b41530e56dd5d/tests/test_permissions.py#L327-L348,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637253789, https://github.com/simonw/datasette/issues/824#issuecomment-642953605,https://api.github.com/repos/simonw/datasette/issues/824,642953605,MDEyOklzc3VlQ29tbWVudDY0Mjk1MzYwNQ==,9599,2020-06-11T22:02:32Z,2020-06-11T22:02:32Z,OWNER,`datasette-auth-tokens` can be the name. I can get a simple initial version of it running pretty quickly.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",635108074, https://github.com/simonw/datasette/issues/824#issuecomment-642952962,https://api.github.com/repos/simonw/datasette/issues/824,642952962,MDEyOklzc3VlQ29tbWVudDY0Mjk1Mjk2Mg==,9599,2020-06-11T22:01:58Z,2020-06-11T22:01:58Z,OWNER,"Alternative idea: a plugin that handles Bearer token authentication. Uses `metadata.json` with secret plugin values to map an incoming token to an actor dictionary, which can then be mapped to permissions.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",635108074, https://github.com/simonw/datasette/issues/824#issuecomment-642951150,https://api.github.com/repos/simonw/datasette/issues/824,642951150,MDEyOklzc3VlQ29tbWVudDY0Mjk1MTE1MA==,9599,2020-06-11T22:00:17Z,2020-06-11T22:00:17Z,OWNER,"I got this working: https://github.com/simonw/datasette-auth-github/pull/64 Just one problem: it uses the existing `ds_actor` cookie, which means it doesn't actually exercise the `actor_from_request` plugin! It does use `register_routes` though.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",635108074, https://github.com/simonw/datasette/issues/220#issuecomment-642944645,https://api.github.com/repos/simonw/datasette/issues/220,642944645,MDEyOklzc3VlQ29tbWVudDY0Mjk0NDY0NQ==,9599,2020-06-11T21:49:55Z,2020-06-11T21:49:55Z,OWNER,"I'm OK with not implementing this - I've got used to the existing mechanism, and it doesn't frustrate me enough to work on this more.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",314847571, https://github.com/simonw/datasette/issues/832#issuecomment-642907021,https://api.github.com/repos/simonw/datasette/issues/832,642907021,MDEyOklzc3VlQ29tbWVudDY0MjkwNzAyMQ==,9599,2020-06-11T20:20:35Z,2020-06-11T20:20:35Z,OWNER,"I think the new `.check_permissions()` should be a documented utility that is available to plugins. Maybe a method on `datasette`?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501, https://github.com/simonw/datasette/issues/832#issuecomment-642906681,https://api.github.com/repos/simonw/datasette/issues/832,642906681,MDEyOklzc3VlQ29tbWVudDY0MjkwNjY4MQ==,9599,2020-06-11T20:19:47Z,2020-06-11T20:20:02Z,OWNER,"So for the following: ``` await self.check_permissions(request, [ (""view-table"", (database, table)), (""view-database"", database), ""view-instance"", ]) ``` The logic is: if the first test returns `True`, you get access. If it returns `False` you are denied. If it says `None` then move on to the next check in the list and repeat.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501, https://github.com/simonw/datasette/issues/833#issuecomment-642905424,https://api.github.com/repos/simonw/datasette/issues/833,642905424,MDEyOklzc3VlQ29tbWVudDY0MjkwNTQyNA==,9599,2020-06-11T20:16:41Z,2020-06-11T20:16:41Z,OWNER,I'll add a new test in `test_permissions.py` which locks down an instance and then loops through paths as the anonymous user making sure they aren't accessible.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637253789, https://github.com/simonw/datasette/issues/833#issuecomment-642902208,https://api.github.com/repos/simonw/datasette/issues/833,642902208,MDEyOklzc3VlQ29tbWVudDY0MjkwMjIwOA==,9599,2020-06-11T20:08:57Z,2020-06-11T20:08:57Z,OWNER,"I'm tempted to add a `view-instance` check before routing any URLs, but that wouldn't be compatible with the idea in #832 that having `view-table` should be enough to view a table even if you don't pass `view-instance`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637253789, https://github.com/simonw/datasette/issues/833#issuecomment-642874724,https://api.github.com/repos/simonw/datasette/issues/833,642874724,MDEyOklzc3VlQ29tbWVudDY0Mjg3NDcyNA==,9599,2020-06-11T19:07:49Z,2020-06-11T19:07:49Z,OWNER,A live demo running the `datasette-auth-github` plugin will help demonstrate this.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",637253789, https://github.com/simonw/datasette/issues/801#issuecomment-642870553,https://api.github.com/repos/simonw/datasette/issues/801,642870553,MDEyOklzc3VlQ29tbWVudDY0Mjg3MDU1Mw==,9599,2020-06-11T18:58:49Z,2020-06-11T18:58:49Z,OWNER,I've implemented this in a plugin instead: https://github.com/simonw/datasette-permissions-sql,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",631932926, https://github.com/simonw/datasette/issues/832#issuecomment-642795966,https://api.github.com/repos/simonw/datasette/issues/832,642795966,MDEyOklzc3VlQ29tbWVudDY0Mjc5NTk2Ng==,9599,2020-06-11T16:37:21Z,2020-06-11T16:37:21Z,OWNER,"How would I document this? Probably in another section on https://datasette.readthedocs.io/en/latest/authentication.html#permissions But I'd also need to add documentation to the individual views stating what permissions are checked and in what order. I could do that on this page: 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}",636722501, https://github.com/simonw/datasette/pull/809#issuecomment-642772344,https://api.github.com/repos/simonw/datasette/issues/809,642772344,MDEyOklzc3VlQ29tbWVudDY0Mjc3MjM0NA==,9599,2020-06-11T16:01:15Z,2020-06-11T16:01:15Z,OWNER,"``` datasette package fixtures.db --secret woot --branch master Sending build context to Docker daemon 260.6kB Step 1/9 : FROM python:3.8 3.8: Pulling from library/python e9afc4f90ab0: Downloading [=======> ] 7.195MB/50.39MB 989e6b19a265: Downloading [============================> ] 4.475MB/7.812MB af14b6c2f878: Downloading [===========================> ] 5.422MB/9.996MB 5573c4b30949: Waiting 11a88e764313: Waiting ee776f0e36af: Waiting 513c90a1afc3: Waiting df9b9e95bdb9: Waiting 86c9edb54464: Waiting ... datasette package fixtures.db --secret woot --branch master docker run -p 8001:8001 a155798bd842 ``` This works too.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632919570, https://github.com/simonw/datasette/pull/809#issuecomment-642754589,https://api.github.com/repos/simonw/datasette/issues/809,642754589,MDEyOklzc3VlQ29tbWVudDY0Mjc1NDU4OQ==,9599,2020-06-11T15:45:25Z,2020-06-11T15:45:25Z,OWNER," datasette publish cloudrun fixtures.db --service datasette-publish-secret --branch=master https://datasette-publish-secret-j7hipcg4aq-uw.a.run.app/-/messages","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632919570, https://github.com/simonw/datasette/pull/809#issuecomment-642750790,https://api.github.com/repos/simonw/datasette/issues/809,642750790,MDEyOklzc3VlQ29tbWVudDY0Mjc1MDc5MA==,9599,2020-06-11T15:42:23Z,2020-06-11T15:42:23Z,OWNER," datasette publish heroku fixtures.db -n datasette-publish-secret --branch=master https://datasette-publish-secret.herokuapp.com/-/messages - Heroku works. ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632919570, https://github.com/simonw/datasette/pull/809#issuecomment-642745518,https://api.github.com/repos/simonw/datasette/issues/809,642745518,MDEyOklzc3VlQ29tbWVudDY0Mjc0NTUxOA==,9599,2020-06-11T15:38:51Z,2020-06-11T15:38:51Z,OWNER,The way to manually test this is to publish a database to each provider and then check that the `/-/messages` debug tool works.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632919570, https://github.com/simonw/datasette/issues/832#issuecomment-642741930,https://api.github.com/repos/simonw/datasette/issues/832,642741930,MDEyOklzc3VlQ29tbWVudDY0Mjc0MTkzMA==,9599,2020-06-11T15:35:53Z,2020-06-11T15:36:05Z,OWNER,"May the fix here is to implement a `.check_permissions()` method which passes when the first permission passes? ```python await self.check_permissions(request, [ (""view-table"", (database, table)), (""view-database"", database), ""view-instance"", ]) ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501, https://github.com/simonw/datasette/issues/818#issuecomment-642420375,https://api.github.com/repos/simonw/datasette/issues/818,642420375,MDEyOklzc3VlQ29tbWVudDY0MjQyMDM3NQ==,9599,2020-06-11T05:40:07Z,2020-06-11T05:40:07Z,OWNER,https://github.com/simonw/datasette-permissions-sql is now released as a 0.1a here: https://pypi.org/project/datasette-permissions-sql/,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",634917088, https://github.com/simonw/datasette/issues/832#issuecomment-642412017,https://api.github.com/repos/simonw/datasette/issues/832,642412017,MDEyOklzc3VlQ29tbWVudDY0MjQxMjAxNw==,9599,2020-06-11T05:13:59Z,2020-06-11T05:13:59Z,OWNER,"Relevant code: https://github.com/simonw/datasette/blob/ce4958018ede00fbdadf0c37a99889b6901bfb9b/datasette/views/table.py#L267-L272","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636722501, https://github.com/simonw/datasette/issues/831#issuecomment-642324847,https://api.github.com/repos/simonw/datasette/issues/831,642324847,MDEyOklzc3VlQ29tbWVudDY0MjMyNDg0Nw==,9599,2020-06-10T23:50:55Z,2020-06-10T23:50:55Z,OWNER,"Actually I'm not sure about this. If `""allow"": null` means ""no-one can do this"", what's the allow block syntax for ""everyone can do this""? It could be `""allow"": {}` - but that's not intuitive because normally the allow block shows keys that need to match. `{}` suggests to me that no matches are possible. So I think I'm going to stick with the current mechanism, which is that `""allow"": null` means ""anyone can do this"" and `""allow"": {}` means ""no-one can do this"".","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636614868, https://github.com/simonw/datasette/issues/818#issuecomment-642231871,https://api.github.com/repos/simonw/datasette/issues/818,642231871,MDEyOklzc3VlQ29tbWVudDY0MjIzMTg3MQ==,9599,2020-06-10T20:11:50Z,2020-06-10T20:11:50Z,OWNER,"`datasette-permissions-sql` ```yaml plugins: datasette-permissions-sql: view-instance: |- select count(*) from users where admin = 1 and id = :id ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",634917088, https://github.com/simonw/datasette/issues/818#issuecomment-642230499,https://api.github.com/repos/simonw/datasette/issues/818,642230499,MDEyOklzc3VlQ29tbWVudDY0MjIzMDQ5OQ==,9599,2020-06-10T20:08:46Z,2020-06-10T20:09:26Z,OWNER,"What's a simple but useful plugin I could release that exercises this hook? Ideally one which executes permission checks against the database somehow. I could do a simplest-possible implementation of the idea in #801 (allow-by-query).","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",634917088, https://github.com/simonw/datasette/issues/818#issuecomment-642229899,https://api.github.com/repos/simonw/datasette/issues/818,642229899,MDEyOklzc3VlQ29tbWVudDY0MjIyOTg5OQ==,9599,2020-06-10T20:07:36Z,2020-06-10T20:07:36Z,OWNER,"New policy in 9f236c4 dictates that this should be in Milestone 0.44 after all: > * **New plugin hooks** should only be shipped if accompanied by a separate release of a non-demo plugin that uses them.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",634917088, https://github.com/simonw/datasette/issues/829#issuecomment-642217520,https://api.github.com/repos/simonw/datasette/issues/829,642217520,MDEyOklzc3VlQ29tbWVudDY0MjIxNzUyMA==,9599,2020-06-10T19:41:35Z,2020-06-10T19:41:35Z,OWNER,"I didn't bother with the alternative epoch - it only shaves off two or three bytes from the cookie. Documentation for the new `ds_actor` cookie shape is here: https://datasette.readthedocs.io/en/latest/authentication.html#the-ds-actor-cookie","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636426530, https://github.com/simonw/datasette/issues/829#issuecomment-642178604,https://api.github.com/repos/simonw/datasette/issues/829,642178604,MDEyOklzc3VlQ29tbWVudDY0MjE3ODYwNA==,9599,2020-06-10T18:18:36Z,2020-06-10T18:20:19Z,OWNER,"Even shorter: encode an integer that is the difference between that expiry timestamp and a more recent epoch - June 1st 2020 will do. ``` >>> import datetime, calendar >>> calendar.timegm(datetime.date(2020, 6, 1).timetuple()) 1590969600 >>> import baseconv >>> baseconv.base62.encode(int(time.time() - 1590969600)) '3XST' ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636426530, https://github.com/simonw/datasette/issues/829#issuecomment-642176180,https://api.github.com/repos/simonw/datasette/issues/829,642176180,MDEyOklzc3VlQ29tbWVudDY0MjE3NjE4MA==,9599,2020-06-10T18:14:02Z,2020-06-10T18:14:15Z,OWNER,"And the `e` key can be `null`or missing for ""never expires"".","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636426530, https://github.com/simonw/datasette/issues/829#issuecomment-642175892,https://api.github.com/repos/simonw/datasette/issues/829,642175892,MDEyOklzc3VlQ29tbWVudDY0MjE3NTg5Mg==,9599,2020-06-10T18:13:26Z,2020-06-10T18:13:26Z,OWNER,"I'm going with `expires_at` - except to keep the cookies shorter the key will be called `e` and the actor will go in `a`, like this: ```json { ""e"": ""1UuHoo"", ""a"": {""id"": ""root""} } ``` That `e` value is a base64 encoded expiry integer timestamp (again for a shorter cookie) - using https://pypi.org/project/python-baseconv/","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636426530, https://github.com/simonw/datasette/issues/829#issuecomment-642174272,https://api.github.com/repos/simonw/datasette/issues/829,642174272,MDEyOklzc3VlQ29tbWVudDY0MjE3NDI3Mg==,9599,2020-06-10T18:10:13Z,2020-06-10T18:10:13Z,OWNER,"Some options: - Redesign the `ds_actor` cookie to be `{""expires_at"": 1591811250, ""actor"": ...}` - check if it has expired in that default `actor_from_request` hook - Let plugins set an additional cookie of some sort - Expect plugins that care about this to set a cookie with a different name and implement their own `actor_from_request` against that","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636426530, https://github.com/simonw/datasette/issues/829#issuecomment-642161210,https://api.github.com/repos/simonw/datasette/issues/829,642161210,MDEyOklzc3VlQ29tbWVudDY0MjE2MTIxMA==,9599,2020-06-10T17:45:58Z,2020-06-10T17:45:58Z,OWNER,"`itsdangerous` has this ability but you specify the max-age when you call unsign: https://itsdangerous.palletsprojects.com/en/1.1.x/timed/ > s.unsign(string, max_age=5) > Traceback (most recent call last): > ... > itsdangerous.exc.SignatureExpired: Signature age 15 > 5 seconds I currently only decode the `ds_actor` cookie in one place: https://github.com/simonw/datasette/blob/d828abaddec0dce3ec4b4eeddc3a74384e52cf34/datasette/actor_auth_cookie.py#L5-L12 If plugins want to be able to set their own policies on how long the `ds_actor` cookie should remain valid, how do I know to listen to them when decoding the cookie here?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",636426530, https://github.com/simonw/datasette/issues/828#issuecomment-641713087,https://api.github.com/repos/simonw/datasette/issues/828,641713087,MDEyOklzc3VlQ29tbWVudDY0MTcxMzA4Nw==,9599,2020-06-10T04:28:17Z,2020-06-10T04:28:17Z,OWNER,"Fixed. https://datasette.readthedocs.io/en/latest/changelog.html ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",635914822, https://github.com/simonw/datasette/issues/828#issuecomment-641710745,https://api.github.com/repos/simonw/datasette/issues/828,641710745,MDEyOklzc3VlQ29tbWVudDY0MTcxMDc0NQ==,9599,2020-06-10T04:19:31Z,2020-06-10T04:19:31Z,OWNER,https://docs.readthedocs.io/en/stable/guides/adding-custom-css.html,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",635914822, https://github.com/simonw/datasette/issues/828#issuecomment-641710670,https://api.github.com/repos/simonw/datasette/issues/828,641710670,MDEyOklzc3VlQ29tbWVudDY0MTcxMDY3MA==,9599,2020-06-10T04:19:17Z,2020-06-10T04:19:17Z,OWNER,"This CSS seems to fix it: ```css a.external {overflow-wrap: anywhere;} ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",635914822, https://github.com/simonw/datasette/issues/806#issuecomment-641637696,https://api.github.com/repos/simonw/datasette/issues/806,641637696,MDEyOklzc3VlQ29tbWVudDY0MTYzNzY5Ng==,9599,2020-06-09T23:46:00Z,2020-06-09T23:46:00Z,OWNER,"The issues that should be referenced from this release are: #395, #519, #576, #699, #706, #774, #777, #781, #784, #788, #790, #797, #798, #800, #802, #804, #819, #822 ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632753851, https://github.com/simonw/datasette/issues/806#issuecomment-641634749,https://api.github.com/repos/simonw/datasette/issues/806,641634749,MDEyOklzc3VlQ29tbWVudDY0MTYzNDc0OQ==,9599,2020-06-09T23:34:52Z,2020-06-09T23:34:52Z,OWNER,Preview of the release notes is now available here: https://datasette.readthedocs.io/en/latest/changelog.html#v0-44,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",632753851, https://github.com/simonw/datasette/issues/795#issuecomment-641616185,https://api.github.com/repos/simonw/datasette/issues/795,641616185,MDEyOklzc3VlQ29tbWVudDY0MTYxNjE4NQ==,9599,2020-06-09T22:33:33Z,2020-06-09T22:33:33Z,OWNER,Documentation: https://datasette.readthedocs.io/en/latest/internals.html#setting-cookies-with-response-set-cookie,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",629541395,