first comit
This commit is contained in:
164
venv/lib/python3.10/site-packages/traitlets/config/sphinxdoc.py
Normal file
164
venv/lib/python3.10/site-packages/traitlets/config/sphinxdoc.py
Normal file
@@ -0,0 +1,164 @@
|
||||
"""Machinery for documenting traitlets config options with Sphinx.
|
||||
|
||||
This includes:
|
||||
|
||||
- A Sphinx extension defining directives and roles for config options.
|
||||
- A function to generate an rst file given an Application instance.
|
||||
|
||||
To make this documentation, first set this module as an extension in Sphinx's
|
||||
conf.py::
|
||||
|
||||
extensions = [
|
||||
# ...
|
||||
'traitlets.config.sphinxdoc',
|
||||
]
|
||||
|
||||
Autogenerate the config documentation by running code like this before
|
||||
Sphinx builds::
|
||||
|
||||
from traitlets.config.sphinxdoc import write_doc
|
||||
from myapp import MyApplication
|
||||
|
||||
writedoc('config/options.rst', # File to write
|
||||
'MyApp config options', # Title
|
||||
MyApplication()
|
||||
)
|
||||
|
||||
The generated rST syntax looks like this::
|
||||
|
||||
.. configtrait:: Application.log_datefmt
|
||||
|
||||
Description goes here.
|
||||
|
||||
Cross reference like this: :configtrait:`Application.log_datefmt`.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
from collections import defaultdict
|
||||
from textwrap import dedent
|
||||
|
||||
from traitlets import HasTraits, Undefined
|
||||
from traitlets.config.application import Application
|
||||
from traitlets.utils.text import indent
|
||||
|
||||
|
||||
def setup(app: t.Any) -> dict[str, t.Any]:
|
||||
"""Registers the Sphinx extension.
|
||||
|
||||
You shouldn't need to call this directly; configure Sphinx to use this
|
||||
module instead.
|
||||
"""
|
||||
app.add_object_type("configtrait", "configtrait", objname="Config option")
|
||||
return {"parallel_read_safe": True, "parallel_write_safe": True}
|
||||
|
||||
|
||||
def interesting_default_value(dv: t.Any) -> bool:
|
||||
if (dv is None) or (dv is Undefined):
|
||||
return False
|
||||
if isinstance(dv, (str, list, tuple, dict, set)):
|
||||
return bool(dv)
|
||||
return True
|
||||
|
||||
|
||||
def format_aliases(aliases: list[str]) -> str:
|
||||
fmted = []
|
||||
for a in aliases:
|
||||
dashes = "-" if len(a) == 1 else "--"
|
||||
fmted.append(f"``{dashes}{a}``")
|
||||
return ", ".join(fmted)
|
||||
|
||||
|
||||
def class_config_rst_doc(cls: type[HasTraits], trait_aliases: dict[str, t.Any]) -> str:
|
||||
"""Generate rST documentation for this class' config options.
|
||||
|
||||
Excludes traits defined on parent classes.
|
||||
"""
|
||||
lines = []
|
||||
classname = cls.__name__
|
||||
for _, trait in sorted(cls.class_traits(config=True).items()):
|
||||
ttype = trait.__class__.__name__
|
||||
|
||||
fullname = classname + "." + (trait.name or "")
|
||||
lines += [".. configtrait:: " + fullname, ""]
|
||||
|
||||
help = trait.help.rstrip() or "No description"
|
||||
lines.append(indent(dedent(help)) + "\n")
|
||||
|
||||
# Choices or type
|
||||
if "Enum" in ttype:
|
||||
# include Enum choices
|
||||
lines.append(indent(":options: " + ", ".join("``%r``" % x for x in trait.values))) # type:ignore[attr-defined]
|
||||
else:
|
||||
lines.append(indent(":trait type: " + ttype))
|
||||
|
||||
# Default value
|
||||
# Ignore boring default values like None, [] or ''
|
||||
if interesting_default_value(trait.default_value):
|
||||
try:
|
||||
dvr = trait.default_value_repr()
|
||||
except Exception:
|
||||
dvr = None # ignore defaults we can't construct
|
||||
if dvr is not None:
|
||||
if len(dvr) > 64:
|
||||
dvr = dvr[:61] + "..."
|
||||
# Double up backslashes, so they get to the rendered docs
|
||||
dvr = dvr.replace("\\n", "\\\\n")
|
||||
lines.append(indent(":default: ``%s``" % dvr))
|
||||
|
||||
# Command line aliases
|
||||
if trait_aliases[fullname]:
|
||||
fmt_aliases = format_aliases(trait_aliases[fullname])
|
||||
lines.append(indent(":CLI option: " + fmt_aliases))
|
||||
|
||||
# Blank line
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def reverse_aliases(app: Application) -> dict[str, list[str]]:
|
||||
"""Produce a mapping of trait names to lists of command line aliases."""
|
||||
res = defaultdict(list)
|
||||
for alias, trait in app.aliases.items():
|
||||
res[trait].append(alias)
|
||||
|
||||
# Flags also often act as aliases for a boolean trait.
|
||||
# Treat flags which set one trait to True as aliases.
|
||||
for flag, (cfg, _) in app.flags.items():
|
||||
if len(cfg) == 1:
|
||||
classname = next(iter(cfg))
|
||||
cls_cfg = cfg[classname]
|
||||
if len(cls_cfg) == 1:
|
||||
traitname = next(iter(cls_cfg))
|
||||
if cls_cfg[traitname] is True:
|
||||
res[classname + "." + traitname].append(flag)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def write_doc(path: str, title: str, app: Application, preamble: str | None = None) -> None:
|
||||
"""Write a rst file documenting config options for a traitlets application.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : str
|
||||
The file to be written
|
||||
title : str
|
||||
The human-readable title of the document
|
||||
app : traitlets.config.Application
|
||||
An instance of the application class to be documented
|
||||
preamble : str
|
||||
Extra text to add just after the title (optional)
|
||||
"""
|
||||
trait_aliases = reverse_aliases(app)
|
||||
with open(path, "w") as f:
|
||||
f.write(title + "\n")
|
||||
f.write(("=" * len(title)) + "\n")
|
||||
f.write("\n")
|
||||
if preamble is not None:
|
||||
f.write(preamble + "\n\n")
|
||||
|
||||
for c in app._classes_inc_parents():
|
||||
f.write(class_config_rst_doc(c, trait_aliases))
|
||||
f.write("\n")
|
||||
Reference in New Issue
Block a user