html_url,issue_url,id,node_id,user,user_label,created_at,updated_at,author_association,body,reactions,issue,issue_label,performed_via_github_app https://github.com/simonw/datasette/issues/576#issuecomment-586053947,https://api.github.com/repos/simonw/datasette/issues/576,586053947,MDEyOklzc3VlQ29tbWVudDU4NjA1Mzk0Nw==,9599,simonw,2020-02-14T01:29:48Z,2020-05-30T13:22:09Z,OWNER,"OK, I've made a start on this now in 3ffb8f3b98252531d11897fd431711e9b8045ace - still plenty more methods to document. More importantly that class has a LOT of junk methods on that no-one should ever call from a plugin, so I need to decide what to do about those. https://datasette.readthedocs.io/en/latest/internals.html","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",497170355,Documented internals API for use in plugins, https://github.com/simonw/datasette/issues/576#issuecomment-636340618,https://api.github.com/repos/simonw/datasette/issues/576,636340618,MDEyOklzc3VlQ29tbWVudDYzNjM0MDYxOA==,9599,simonw,2020-05-30T14:46:04Z,2020-05-30T18:41:41Z,OWNER,"I should also think about the class properties (as opposed to methods) that are setup in the Datasette constructor. Many of these should be private, some should be documented. - cache_headers - cors - databases - executor - files - immutables - inspect_data - jinja_env - max_returned_rows - page_size - plugins_dir - renderers - sql_time_limit_ms - sqlite_extensions - sqlite_functions - static_mounts - template_dir - version_note ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",497170355,Documented internals API for use in plugins, https://github.com/simonw/datasette/issues/498#issuecomment-636360574,https://api.github.com/repos/simonw/datasette/issues/498,636360574,MDEyOklzc3VlQ29tbWVudDYzNjM2MDU3NA==,9599,simonw,2020-05-30T17:26:02Z,2020-05-30T17:26:02Z,OWNER,"I released a plugin that implements an early version of this a while ago: https://github.com/simonw/datasette-search-all I'm still thinking about how a plugin / separate tool could work that builds a single FTS index over the content from multiple tables such that you can run relevance-calculated queries against those multiple tables. Since that's going to be a plugin / separate tool too, I'm closing this issue.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",451513541,Full text search of all tables at once?, https://github.com/simonw/datasette/issues/37#issuecomment-636360861,https://api.github.com/repos/simonw/datasette/issues/37,636360861,MDEyOklzc3VlQ29tbWVudDYzNjM2MDg2MQ==,9599,simonw,2020-05-30T17:29:20Z,2020-05-30T17:29:20Z,OWNER,I'm not going to do this: 2.5 years later I have yet to run into anything that makes me think that JSON serialization performance is worth any extra work.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",268453968,Ability to serialize massive JSON without blocking event loop, https://github.com/simonw/datasette/issues/782#issuecomment-636369978,https://api.github.com/repos/simonw/datasette/issues/782,636369978,MDEyOklzc3VlQ29tbWVudDYzNjM2OTk3OA==,9599,simonw,2020-05-30T18:50:31Z,2020-05-30T18:50:31Z,OWNER,"Here's the default JSON at the moment: https://latest.datasette.io/fixtures/compound_three_primary_keys.json?_size=2 ```json { ""database"": ""fixtures"", ""table"": ""compound_three_primary_keys"", ""is_view"": false, ""human_description_en"": """", ""rows"": [ [ ""a"", ""a"", ""a"", ""a-a-a"" ], [ ""a"", ""a"", ""b"", ""a-a-b"" ] ], ""truncated"": false, ""filtered_table_rows_count"": 1001, ""expanded_columns"": [], ""expandable_columns"": [], ""columns"": [ ""pk1"", ""pk2"", ""pk3"", ""content"" ], ""primary_keys"": [ ""pk1"", ""pk2"", ""pk3"" ], ""units"": {}, ""query"": { ""sql"": ""select pk1, pk2, pk3, content from compound_three_primary_keys order by pk1, pk2, pk3 limit 3"", ""params"": {} }, ""facet_results"": {}, ""suggested_facets"": [ { ""name"": ""pk1"", ""toggle_url"": ""http://latest.datasette.io/fixtures/compound_three_primary_keys.json?_size=2&_facet=pk1"" }, { ""name"": ""pk2"", ""toggle_url"": ""http://latest.datasette.io/fixtures/compound_three_primary_keys.json?_size=2&_facet=pk2"" }, { ""name"": ""pk3"", ""toggle_url"": ""http://latest.datasette.io/fixtures/compound_three_primary_keys.json?_size=2&_facet=pk3"" } ], ""next"": ""a,a,b"", ""next_url"": ""http://latest.datasette.io/fixtures/compound_three_primary_keys.json?_size=2&_next=a%2Ca%2Cb"", ""query_ms"": 17.56119728088379, ""source"": ""tests/fixtures.py"", ""source_url"": ""https://github.com/simonw/datasette/blob/master/tests/fixtures.py"", ""license"": ""Apache License 2.0"", ""license_url"": ""https://github.com/simonw/datasette/blob/master/LICENSE"" } ``` There's a lot of stuff in there. This increases the risk that future minor changes might break existing API consumers. It returns rows as a list of lists of values, and expects you to correlate these with the list of columns. I originally designed it like this because I thought this was a more efficient representation than repeating the column names in a dictionary for every row. With hindsight this was a bad optimization - I _always_ use `?shape=array` because it's more convenient, and gzip encoding of the response means there's no bandwidth saving. Users who want that efficiency should request it using a custom `?_shape=`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",627794879,Redesign default .json format, https://github.com/simonw/datasette/issues/782#issuecomment-636370064,https://api.github.com/repos/simonw/datasette/issues/782,636370064,MDEyOklzc3VlQ29tbWVudDYzNjM3MDA2NA==,9599,simonw,2020-05-30T18:51:19Z,2020-05-30T18:51:19Z,OWNER,"https://latest.datasette.io/fixtures/compound_three_primary_keys.json?_size=2&_shape=array returns this: ```json [ { ""pk1"": ""a"", ""pk2"": ""a"", ""pk3"": ""a"", ""content"": ""a-a-a"" }, { ""pk1"": ""a"", ""pk2"": ""a"", ""pk3"": ""b"", ""content"": ""a-a-b"" } ] ``` There's one big problem with this format: it doesn't provide any space for pagination information.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",627794879,Redesign default .json format, https://github.com/simonw/datasette/issues/699#issuecomment-636376209,https://api.github.com/repos/simonw/datasette/issues/699,636376209,MDEyOklzc3VlQ29tbWVudDYzNjM3NjIwOQ==,9599,simonw,2020-05-30T19:53:28Z,2020-05-30T20:09:10Z,OWNER,"I think there are two hooks here: `actor_from_request(datasette, request)` - returns `None` or a dictionary. - `datasette` is a Datasette instance - useful for things like reading plugin configuration or executing queries - `request` is a [Request object](https://datasette.readthedocs.io/en/latest/internals.html#request-object) - which means ASGI scope can be accessed as `request.scope` A non-None value means the request is authenticated in some way. The shape of that dictionary is entirely undefined. The second hook is for checking permissions. It can look something like this: `permission_allowed(actor, action, resource_type, resource_identifier)` - `actor` = the dictionary that was returned by `actor_from_scope` - `action` = a string representing the action to be performed, e.g. `edit-schema` - `resource_type` = a string representing the type of resource being acted on, e.g. `table` - `resource_identifier` = a string (or maybe tuple?) representing the specific resource, e.g. the table name I don't know if Datasette should provide default implementations of these hooks. It may be that leaving them completely up to plugins is the way to go. I think I need to prototype this quickly to start feeling for how well it might work.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636376893,https://api.github.com/repos/simonw/datasette/issues/699,636376893,MDEyOklzc3VlQ29tbWVudDYzNjM3Njg5Mw==,9599,simonw,2020-05-30T19:57:54Z,2020-05-30T20:09:05Z,OWNER,"`auth_from_scope(datasette, scope)` needs to be able to return an awaitable which is then awaited - so it can execute database queries.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636376974,https://api.github.com/repos/simonw/datasette/issues/699,636376974,MDEyOklzc3VlQ29tbWVudDYzNjM3Njk3NA==,9599,simonw,2020-05-30T19:58:40Z,2020-05-30T20:08:59Z,OWNER,"Maybe call that `actor_from_request(datasette, request)` instead.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636377235,https://api.github.com/repos/simonw/datasette/issues/699,636377235,MDEyOklzc3VlQ29tbWVudDYzNjM3NzIzNQ==,9599,simonw,2020-05-30T20:00:42Z,2020-05-30T20:01:35Z,OWNER,I'm changing `auth` to `actor` and updating the above [design comment](https://github.com/simonw/datasette/issues/699#issuecomment-636376209).,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636377755,https://api.github.com/repos/simonw/datasette/issues/699,636377755,MDEyOklzc3VlQ29tbWVudDYzNjM3Nzc1NQ==,9599,simonw,2020-05-30T20:04:23Z,2020-05-30T20:04:23Z,OWNER,"My usage of the term `subject` here to mean ""the thing I am checking I have permission to interact with, e.g. a database table"" may be misleading. https://stackoverflow.com/questions/4989063/what-is-the-meaning-and-difference-between-subject-user-and-principal for example shows that JAAS (Java Authentication and Authorization Service) defines subject as ""The purpose of the Subject is to represent the authenticated user"".","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636378121,https://api.github.com/repos/simonw/datasette/issues/699,636378121,MDEyOklzc3VlQ29tbWVudDYzNjM3ODEyMQ==,9599,simonw,2020-05-30T20:06:47Z,2020-05-30T20:06:47Z,OWNER,"In AWS IAM world the following terminology is used: https://aws.amazon.com/iam/features/manage-permissions/ > Permissions are granted to IAM **entities** (users, groups, and roles) [...] > > To assign permissions to a user, group, role, or resource, you create a policy that lets you specify: > > * **Actions** – Which AWS service actions you allow. For example, you might allow a user to call the Amazon S3 ListBucket action. Any actions that you don't explicitly allow are denied. > * **Resources** – Which AWS resources you allow the action on. For example, what Amazon S3 buckets will you allow the user to perform the ListBucket action on? Users cannot access any resources that you do not explicitly grant permissions to. > * **Effect** – Whether to allow or deny access. Because access is denied by default, you typically write policies where the effect is to allow. > * **Conditions** – Which conditions must be present for the policy to take effect. For example, you might allow access only to the specific S3 buckets if the user is connecting from a specific IP range or has used multi-factor authentication at login.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636378228,https://api.github.com/repos/simonw/datasette/issues/699,636378228,MDEyOklzc3VlQ29tbWVudDYzNjM3ODIyOA==,9599,simonw,2020-05-30T20:07:25Z,2020-05-30T20:07:25Z,OWNER,"I like ""actor"" better than ""entity"" to mean ""the user or API key that is authenticated for this request"". I'm going to use ""resource"" instead of ""subject"" - updating the design comment again.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636379067,https://api.github.com/repos/simonw/datasette/issues/699,636379067,MDEyOklzc3VlQ29tbWVudDYzNjM3OTA2Nw==,9599,simonw,2020-05-30T20:12:47Z,2020-05-30T20:40:42Z,OWNER,"I could bake some permission checks into default Datasette, which are all treated as allow by default but can then be locked down by plugins. Maybe the following: permission_allowed(request.actor, ""execute-sql"", ""database"", ""name-of-database"") Checks that current user can execute arbitrary SQL queries against a specific database (or use the `?_where=` feature). Equivalent to current [allow_sql](https://datasette.readthedocs.io/en/0.43/config.html#allow-sql) setting. permission_allowed(request.actor, ""download-database"", ""database"", ""name-of-database"") Can the user download the database file? Like [allow_download](https://datasette.readthedocs.io/en/0.43/config.html#allow-download). Maybe one for [allow_csv_stream](https://datasette.readthedocs.io/en/0.43/config.html#allow-csv-stream) too. Having a permission check (defaulting to True) on every single ""view"" would be useful: - view_index - view_database - view_table - view_row - view_query - view_special (for `/-/versions` and so on) ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636381732,https://api.github.com/repos/simonw/datasette/issues/699,636381732,MDEyOklzc3VlQ29tbWVudDYzNjM4MTczMg==,9599,simonw,2020-05-30T20:32:11Z,2020-05-30T20:39:11Z,OWNER,I started sketching this out in the [authentication](https://github.com/simonw/datasette/tree/authentication) branch. Here's the documentation so far: https://github.com/simonw/datasette/blob/8871c20/docs/plugins.rst#actor_from_requestdatasette-request,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636388288,https://api.github.com/repos/simonw/datasette/issues/699,636388288,MDEyOklzc3VlQ29tbWVudDYzNjM4ODI4OA==,9599,simonw,2020-05-30T21:34:50Z,2020-05-30T21:34:50Z,OWNER,"Debugging permissions is going to be important. Optional tooling that supports the following would be useful: - Log every check to `permission_allowed` to the console - optionally with tracebacks showing where in the code the check was made - Log every check to the https://latest.datasette.io/?_trace=1 output - A tool that shows you exactly what permissions the current authenticated user/entity has - A tool showing all available permissions That last one is tricky if permissions are just strings that might be passed to `permission_allowed` - so maybe there needs to be a plugin hook that lets plugins register their permissions, such that they can be introspected later on? A `register_permission_actions()` hook that returns a list of permission action strings (or objects of some sort) perhaps.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636391331,https://api.github.com/repos/simonw/datasette/issues/699,636391331,MDEyOklzc3VlQ29tbWVudDYzNjM5MTMzMQ==,9599,simonw,2020-05-30T22:08:21Z,2020-05-30T22:08:21Z,OWNER,"I'm going to add an awaitable utility method to the Datasette class for checking permissions: await datasette.permission_allowed(actor, action, resource_type, resource_identifier) The second two arguments will be optional.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636392850,https://api.github.com/repos/simonw/datasette/issues/699,636392850,MDEyOklzc3VlQ29tbWVudDYzNjM5Mjg1MA==,9599,simonw,2020-05-30T22:25:19Z,2020-05-30T22:25:19Z,OWNER,The branch is now usable! Next step: write some experimental plugins that exercise some real authentication use-cases with it.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636393204,https://api.github.com/repos/simonw/datasette/issues/699,636393204,MDEyOklzc3VlQ29tbWVudDYzNjM5MzIwNA==,9599,simonw,2020-05-30T22:29:44Z,2020-05-30T22:30:15Z,OWNER,"Robust testing of permissions is really important. I should think about utilities I may be able to add to Datasette's unit testing tools that make it as easy as possible to confirm which permission checks were carried out on a specific HTTP request. That way I can set a good example that any Datasette plugin which makes permission checks can follow.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/datasette/issues/699#issuecomment-636395263,https://api.github.com/repos/simonw/datasette/issues/699,636395263,MDEyOklzc3VlQ29tbWVudDYzNjM5NTI2Mw==,9599,simonw,2020-05-30T22:54:09Z,2020-05-30T22:54:09Z,OWNER,"Idea: add a `/-/actor.json` special page which JSON dumps out the current `request.scope[""actor""]` - so you can easily test how your request has been authenticated.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",582526961,Authentication (and permissions) as a core concept, https://github.com/simonw/sqlite-utils/issues/114#issuecomment-636322089,https://api.github.com/repos/simonw/sqlite-utils/issues/114,636322089,MDEyOklzc3VlQ29tbWVudDYzNjMyMjA4OQ==,9599,simonw,2020-05-30T12:08:43Z,2020-05-30T12:08:43Z,OWNER,"Idea: use a chained API to define a complex transition and then execute it all at once. For example: ```python db[""mytable""].transform().rename(""col1"", ""col_1"") \ .change_type(""col1"", float) \ .execute() ``` ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",621989740,table.transform() method for advanced alter table, https://github.com/simonw/datasette/issues/576#issuecomment-636329095,https://api.github.com/repos/simonw/datasette/issues/576,636329095,MDEyOklzc3VlQ29tbWVudDYzNjMyOTA5NQ==,9599,simonw,2020-05-30T13:11:39Z,2020-05-30T13:25:10Z,OWNER,"I need to document the `.databases` property - both how to get access to specific databases and how to access the first/only database. Idea: `datasette.get_database(name)` method (consistent with existing `.add_database()` and `.remove_database()` methods) - but `name` parameter is optional, returns first database if you omit that.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",497170355,Documented internals API for use in plugins, https://github.com/simonw/datasette/issues/519#issuecomment-636329468,https://api.github.com/repos/simonw/datasette/issues/519,636329468,MDEyOklzc3VlQ29tbWVudDYzNjMyOTQ2OA==,9599,simonw,2020-05-30T13:14:52Z,2020-05-30T13:21:03Z,OWNER,"I've made a lot of progress towards this recently: - Started internals documentation #576 - Improved design of the request object #706 - Redesigned register_output_renderer plugin hook #581","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",459590021,Decide what goes into Datasette 1.0, https://github.com/simonw/datasette/issues/519#issuecomment-636330023,https://api.github.com/repos/simonw/datasette/issues/519,636330023,MDEyOklzc3VlQ29tbWVudDYzNjMzMDAyMw==,9599,simonw,2020-05-30T13:19:24Z,2020-05-30T13:19:24Z,OWNER,"Goals for Datasette 1.0: - Generally signify confidence in the quality/stability of Datasette. I think I'm there already. - Plugin authors can have confidence that their plugins will work for the whole 1.x release cycle - Developers building against Datasette JSON APIs should have confidence in 1.x compatibility - Template authors and CSS theme authors should have that confidence too I think I'm very nearly there for the plugin API. The harder ones are JSON APIs and template authors. For JSON APIs: The default JSON just isn't right. I find myself using `?_shape=array` for almost everything I build. The template part is harder. I think I need to fully document the template variables for every view - and add protective unit tests that match that documentation. The CSS theme part is so hard it may not be possible. How do you guarantee stable HTML that won't break with custom CSS when you'll be adding new features during the 1.x release run? The pattern portfolio from #151 should hopefully help a lot there, but I still don't have a complete idea of what this entails or how feasible it is.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",459590021,Decide what goes into Datasette 1.0, https://github.com/simonw/datasette/issues/576#issuecomment-636330438,https://api.github.com/repos/simonw/datasette/issues/576,636330438,MDEyOklzc3VlQ29tbWVudDYzNjMzMDQzOA==,9599,simonw,2020-05-30T13:23:04Z,2020-05-30T13:23:04Z,OWNER,Need to document `datasette.metadata()` - see #780,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",497170355,Documented internals API for use in plugins, https://github.com/simonw/datasette/issues/576#issuecomment-636330843,https://api.github.com/repos/simonw/datasette/issues/576,636330843,MDEyOklzc3VlQ29tbWVudDYzNjMzMDg0Mw==,9599,simonw,2020-05-30T13:26:38Z,2020-05-30T13:26:38Z,OWNER,There's a bunch of methods on that class which I could add a `_` prefix to and leave undocumented. `datasette.register_renderers()` should be `datasette._register_renderers()` for example.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",497170355,Documented internals API for use in plugins, https://github.com/simonw/datasette/issues/576#issuecomment-636332083,https://api.github.com/repos/simonw/datasette/issues/576,636332083,MDEyOklzc3VlQ29tbWVudDYzNjMzMjA4Mw==,9599,simonw,2020-05-30T13:36:55Z,2020-05-30T13:36:55Z,OWNER,"Here's the current `Datasette` class as introspected using the webserver run by `pydoc -p 8000`: ","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",497170355,Documented internals API for use in plugins, https://github.com/simonw/datasette/issues/576#issuecomment-636332183,https://api.github.com/repos/simonw/datasette/issues/576,636332183,MDEyOklzc3VlQ29tbWVudDYzNjMzMjE4Mw==,9599,simonw,2020-05-30T13:37:51Z,2020-05-30T13:38:35Z,OWNER,"**\_\_init\_\_**(self, files, immutables=None, cache_headers=True, cors=False, inspect_data=None, metadata=None, sqlite_extensions=None, template_dir=None, plugins_dir=None, static_mounts=None, memory=False, config=None, version_note=None, config_dir=None) `Initialize self. See help(type(self)) for accurate signature.` **absolute_url**(self, request, path) **add_database**(self, name, db) **app**(self) `Returns an ASGI app function that serves the whole of [Datasette](http://localhost:8066/datasette.app.html#Datasette)` **app_css_hash**(self) **config**(self, key) **config_dict**(self) **connected_databases**(self) **execute**(self, db_name, sql, params=None, truncate=False, custom_time_limit=None, page_size=None, log_sql_errors=True) **expand_foreign_keys**(self, database, table, column, values) `Returns dict mapping (column, value) -> label` **get_canned_queries**(self, database_name) **get_canned_query**(self, database_name, query_name) **metadata**(self, key=None, database=None, table=None, fallback=True) `Looks up metadata, cascading backwards from specified level.\ Returns None if metadata value is not found.` **plugin_config**(self, plugin_name, database=None, table=None, fallback=True) `Return config for plugin, falling back from specified database/table` **plugins**(self, show_all=False) **prepare_connection**(self, conn, database) **register_custom_units**(self) `Register any custom units defined in the metadata.json with Pint` **register_renderers**(self) `Register output renderers which output data in custom formats.` **remove_database**(self, name) **render_template**(self, templates, context=None, request=None, view_name=None) **table_metadata**(self, database, table) `Fetch table-specific metadata.` **threads**(self) **update_with_inherited_metadata**(self, metadata) **versions**(self)","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",497170355,Documented internals API for use in plugins,