mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Prototype of rst_docs_for_dataclass mechanism, refs #1510
This commit is contained in:
parent
007294008d
commit
6822378416
5 changed files with 161 additions and 1 deletions
97
datasette/context.py
Normal file
97
datasette/context.py
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
from typing import List, Dict, Optional, Any
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
def doc(documentation):
|
||||||
|
return field(metadata={"doc": documentation})
|
||||||
|
|
||||||
|
|
||||||
|
def is_builtin_type(obj):
|
||||||
|
return isinstance(
|
||||||
|
obj,
|
||||||
|
tuple(
|
||||||
|
x.__class__
|
||||||
|
for x in (int, float, str, bool, bytes, list, tuple, dict, set, frozenset)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def rst_docs_for_dataclass(klass: Any) -> str:
|
||||||
|
"""Generate reStructuredText (reST) docs for a dataclass."""
|
||||||
|
docs = []
|
||||||
|
|
||||||
|
# Class name and docstring
|
||||||
|
docs.append(klass.__name__)
|
||||||
|
docs.append("-" * len(klass.__name__))
|
||||||
|
docs.append("")
|
||||||
|
if klass.__doc__:
|
||||||
|
docs.append(klass.__doc__)
|
||||||
|
docs.append("")
|
||||||
|
|
||||||
|
# Dataclass fields
|
||||||
|
docs.append("Fields")
|
||||||
|
docs.append("~~~~~~")
|
||||||
|
docs.append("")
|
||||||
|
|
||||||
|
for name, field_info in klass.__dataclass_fields__.items():
|
||||||
|
if is_builtin_type(field_info.type):
|
||||||
|
# <class 'int'>
|
||||||
|
type_name = field_info.type.__name__
|
||||||
|
else:
|
||||||
|
# List[str]
|
||||||
|
type_name = str(field_info.type).replace("typing.", "")
|
||||||
|
docs.append(f':{name} - ``{type_name}``: {field_info.metadata.get("doc", "")}')
|
||||||
|
|
||||||
|
return "\n".join(docs)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ForeignKey:
|
||||||
|
incoming: List[Dict]
|
||||||
|
outgoing: List[Dict]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Table:
|
||||||
|
"A table is a useful thing"
|
||||||
|
name: str = doc("The name of the table")
|
||||||
|
columns: List[str] = doc("List of column names in the table")
|
||||||
|
primary_keys: List[str] = doc("List of column names that are primary keys")
|
||||||
|
count: int = doc("Number of rows in the table")
|
||||||
|
hidden: bool = doc(
|
||||||
|
"Should this table default to being hidden in the main database UI?"
|
||||||
|
)
|
||||||
|
fts_table: Optional[str] = doc(
|
||||||
|
"If this table has FTS support, the accompanying FTS table name"
|
||||||
|
)
|
||||||
|
foreign_keys: ForeignKey = doc("List of foreign keys for this table")
|
||||||
|
private: bool = doc("Private tables are not visible to signed-out anonymous users")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class View:
|
||||||
|
name: str
|
||||||
|
private: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Query:
|
||||||
|
title: str
|
||||||
|
sql: str
|
||||||
|
name: str
|
||||||
|
private: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Database:
|
||||||
|
content: str
|
||||||
|
private: bool
|
||||||
|
path: str
|
||||||
|
size: int
|
||||||
|
tables: List[Table]
|
||||||
|
hidden_count: int
|
||||||
|
views: List[View]
|
||||||
|
queries: List[Query]
|
||||||
|
allow_execute_sql: bool
|
||||||
|
table_columns: Dict[str, List[str]]
|
||||||
|
query_ms: float
|
||||||
|
|
@ -31,7 +31,12 @@
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
# ones.
|
# ones.
|
||||||
extensions = ["sphinx.ext.extlinks", "sphinx.ext.autodoc", "sphinx_copybutton"]
|
extensions = [
|
||||||
|
"sphinx.ext.extlinks",
|
||||||
|
"sphinx.ext.autodoc",
|
||||||
|
"sphinx_copybutton",
|
||||||
|
"jsoncontext",
|
||||||
|
]
|
||||||
|
|
||||||
extlinks = {
|
extlinks = {
|
||||||
"issue": ("https://github.com/simonw/datasette/issues/%s", "#%s"),
|
"issue": ("https://github.com/simonw/datasette/issues/%s", "#%s"),
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ Contents
|
||||||
settings
|
settings
|
||||||
introspection
|
introspection
|
||||||
custom_templates
|
custom_templates
|
||||||
|
template_context
|
||||||
plugins
|
plugins
|
||||||
writing_plugins
|
writing_plugins
|
||||||
plugin_hooks
|
plugin_hooks
|
||||||
|
|
|
||||||
28
docs/jsoncontext.py
Normal file
28
docs/jsoncontext.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
from docutils import nodes
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
|
from importlib import import_module
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class JSONContextDirective(SphinxDirective):
|
||||||
|
required_arguments = 1
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
module_path, class_name = self.arguments[0].rsplit(".", 1)
|
||||||
|
try:
|
||||||
|
module = import_module(module_path)
|
||||||
|
dataclass = getattr(module, class_name)
|
||||||
|
except ImportError:
|
||||||
|
warning = f"Unable to import {self.arguments[0]}"
|
||||||
|
return [nodes.error(None, nodes.paragraph(text=warning))]
|
||||||
|
|
||||||
|
doc = json.dumps(
|
||||||
|
dataclass.__annotations__, indent=4, sort_keys=True, default=repr
|
||||||
|
)
|
||||||
|
doc_node = nodes.literal_block(text=doc)
|
||||||
|
|
||||||
|
return [doc_node]
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_directive("jsoncontext", JSONContextDirective)
|
||||||
29
docs/template_context.rst
Normal file
29
docs/template_context.rst
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
.. _template_context:
|
||||||
|
|
||||||
|
Template context
|
||||||
|
================
|
||||||
|
|
||||||
|
This page describes the variables made available to templates used by Datasette to render different pages of the application.
|
||||||
|
|
||||||
|
|
||||||
|
.. [[[cog
|
||||||
|
from datasette.context import rst_docs_for_dataclass, Table
|
||||||
|
cog.out(rst_docs_for_dataclass(Table))
|
||||||
|
.. ]]]
|
||||||
|
Table
|
||||||
|
-----
|
||||||
|
|
||||||
|
A table is a useful thing
|
||||||
|
|
||||||
|
Fields
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
:name - ``str``: The name of the table
|
||||||
|
:columns - ``List[str]``: List of column names in the table
|
||||||
|
:primary_keys - ``List[str]``: List of column names that are primary keys
|
||||||
|
:count - ``int``: Number of rows in the table
|
||||||
|
:hidden - ``bool``: Should this table default to being hidden in the main database UI?
|
||||||
|
:fts_table - ``Optional[str]``: If this table has FTS support, the accompanying FTS table name
|
||||||
|
:foreign_keys - ``ForeignKey``: List of foreign keys for this table
|
||||||
|
:private - ``bool``: Private tables are not visible to signed-out anonymous users
|
||||||
|
.. [[[end]]]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue