"""
Template code for building docs including conf.py
"""
import ubelt as ub
[docs]
class DocsBuilder:
"""
Helper to build sphinx docs related files
"""
def __init__(docs_builder, config):
"""
Args:
config (XCookieConfig.shrinkuser()):
"""
docs_builder.config = config
@property
def docs_dpath(docs_builder):
repodir = ub.Path(docs_builder.config['repodir']).absolute()
return repodir / 'docs'
@property
def docs_auto_outdir(docs_builder):
# FIXME:
# return docs_builder.docs_dpath / 'source'
return docs_builder.docs_dpath / 'source/auto'
[docs]
def sphinx_apidoc_invocation(docs_builder, shrinkuser=False):
"""
Instructions to invoke sphinx-apidoc.
Xcookie calls this, but also writes it to the conf.py docstring for
transparency.
"""
repodir = ub.Path(docs_builder.config['repodir']).absolute()
docs_outdir = docs_builder.docs_auto_outdir
if shrinkuser:
repodir = repodir.shrinkuser()
docs_outdir = docs_outdir.shrinkuser()
rel_mod_dpath = (
ub.Path(docs_builder.config['rel_mod_parent_dpath'])
/ docs_builder.config['mod_name']
)
mod_abspath = repodir / rel_mod_dpath
exclude_pattern = None
if docs_builder.config['mod_name'] in {'mkinit', 'xdoctest'}:
# Hack: todo make this configurable
exclude_pattern = '_tokenize.py'
if exclude_pattern is None:
invocation = f'sphinx-apidoc --private --separate --force --output-dir {docs_outdir} {mod_abspath}'
else:
invocation = f"sphinx-apidoc --private --separate --force --output-dir {docs_outdir} {mod_abspath} '{exclude_pattern}'"
return invocation
[docs]
def build_docs_index(self):
import ubelt as ub
parts = []
tags = set(self.config['tags'])
mod_name = self.config['mod_name']
pkg_name = self.config['pkg_name']
repo_name = self.config['repo_name']
# FIXME: some of these should use package names
if {'gitlab', 'kitware'}.issubset(tags):
parts.append(
ub.codeblock(
f"""
:gitlab_url: https://gitlab.kitware.com/computer-vision/{repo_name}
"""
)
)
if {'github'}.issubset(tags):
parts.append(
ub.codeblock(
f"""
:github_url: {self.config['url']}
"""
)
)
logo_part = ub.codeblock(
"""
.. The large version wont work because github strips rst image rescaling. https://i.imgur.com/AcWVroL.png
# TODO: Add a logo
.. image:: https://i.imgur.com/PoYIsWE.png
:height: 100px
:align: left
"""
)
this_fpath = str(__file__)
parts.append(logo_part)
parts.append(f'.. Autogenerated by templates in {this_fpath}')
title = f"Welcome to {pkg_name}'s documentation!"
underline = '=' * len(title)
title_part = title + '\n' + underline
parts.append(title_part)
init_part = ub.codeblock(
f"""
.. The __init__ files contains the top-level documentation overview
.. automodule:: {mod_name}.__init__
:show-inheritance:
"""
)
parts.append(init_part)
if 0:
usefulness_part = ub.codeblock(
"""
.. # Computed function usefulness
.. include:: function_usefulness.rst
"""
)
parts.append(usefulness_part)
sidebar_part = ub.codeblock(
f"""
.. toctree::
:maxdepth: 5
auto/{mod_name}
auto/modules
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
"""
)
parts.append(sidebar_part)
text = '\n\n'.join(parts)
return text
[docs]
def build_docs_conf(self):
import datetime as datetime_mod
docs_builder = DocsBuilder(self.config)
author = self.config['author']
if isinstance(author, list):
author_str = ' '.join(author)
else:
author_str = author
fmtkw = {
'repo_name': self.config['repo_name'],
'repo_dashname': self.config['repo_name'].replace('_', '-'),
'mod_name': self.config['mod_name'],
'rel_mod_dpath': str(self.rel_mod_dpath),
# 'repo_url': self.remote_info['url'],
'repo_url': self.config.url,
'author': author_str,
'year': datetime_mod.datetime.now().year,
'repodir_wrt_home': self.repodir.shrinkuser(),
}
fmtkw['invoke_apidoc'] = docs_builder.sphinx_apidoc_invocation(
shrinkuser=True
)
fmtkw['docs_dpath_wrt_home'] = docs_builder.docs_dpath.shrinkuser()
fmtkw['rel_add_dir'] = docs_builder.docs_auto_outdir.relative_to(
docs_builder.docs_dpath
)
text = ub.codeblock(
r'''
"""
Notes:
Based on template code in:
~/code/xcookie/xcookie/builders/docs.py
~/code/xcookie/xcookie/rc/conf_ext.py
http://docs.readthedocs.io/en/latest/getting_started.html
pip install sphinx sphinx-autobuild sphinx_rtd_theme sphinxcontrib-napoleon
cd {repodir_wrt_home}
mkdir -p docs
cd docs
sphinx-quickstart
# need to edit the conf.py
# Remove any old auto docs folder and regenerate it.
rm -rf {docs_dpath_wrt_home}/source/auto
cd {docs_dpath_wrt_home}
{invoke_apidoc}
git add {rel_add_dir}/*.rst
# Note: the module should importable before running this
# (e.g. install it in developer mode or munge the PYTHONPATH)
make html
Also:
To turn on PR checks
https://docs.readthedocs.io/en/stable/guides/autobuild-docs-for-pull-requests.html
https://readthedocs.org/dashboard/{repo_dashname}/advanced/
ensure your github account is connected to readthedocs
https://readthedocs.org/accounts/social/connections/
### For gitlab
To enable the read-the-docs go to https://readthedocs.org/dashboard/ and login
The user will need to enable the repo on their readthedocs account:
https://readthedocs.org/dashboard/import/manual/?
Enter the following information:
Set the Repository NAME: {repo_name}
Set the Repository URL: {repo_url}
Make sure you have a .readthedocs.yml file
For gitlab you also need to setup an integrations. Navigate to:
https://readthedocs.org/dashboard/{repo_dashname}/integrations/create/
Then add gitlab incoming webhook and copy the URL (make sure
you copy the real url and not the text so https is included),
specifically:
In the "Integration type:" dropdown menu, select
"Gitlab incoming webhook"
Click "Add integration"
Copy the text in the "Webhook URL" box to be used later.
Copy the text in the "Secret" box to be used later.
Then go to
{repo_url}/hooks
Click "Add new webhook".
Copy the text previously saved from the "Webhook URL" box
in the readthedocs form into the "URL" box in the gitlab
form.
Copy the text previously saved from the "Secret" box
in the readthedocs form into the "Secret token" box in the
gitlab form.
For trigger permissions select the following checkboxes:
push events,
tag push events,
merge request events
release events
Click the "Add webhook" button.
See Docs for more details https://docs.readthedocs.io/en/stable/integrations.html
Will also need to activate the main branch:
https://readthedocs.org/projects/{repo_dashname}/versions/
"""
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/stable/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
import sphinx_rtd_theme
from os.path import exists
from os.path import dirname
from os.path import join
def parse_version(fpath):
"""
Statically parse the version number from a python file
"""
import ast
if not exists(fpath):
raise ValueError('fpath={{!r}} does not exist'.format(fpath))
with open(fpath, 'r') as file_:
sourcecode = file_.read()
pt = ast.parse(sourcecode)
class VersionVisitor(ast.NodeVisitor):
def visit_Assign(self, node):
for target in node.targets:
if getattr(target, 'id', None) == '__version__':
self.version = node.value.s
visitor = VersionVisitor()
visitor.visit(pt)
return visitor.version
project = '{repo_name}'
copyright = '{year}, {author}'
author = '{author}'
modname = '{mod_name}'
repo_dpath = dirname(dirname(dirname(__file__)))
mod_dpath = join(repo_dpath, '{rel_mod_dpath}')
src_dpath = dirname(mod_dpath)
modpath = join(mod_dpath, '__init__.py')
release = parse_version(modpath)
version = '.'.join(release.split('.')[0:2])
# Hack to ensure the module is importable
# sys.path.insert(0, os.path.abspath(src_dpath))
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
# 'autoapi.extension',
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.intersphinx',
'sphinx.ext.napoleon',
'sphinx.ext.todo',
'sphinx.ext.viewcode',
'myst_parser', # For markdown docs
'sphinx.ext.imgconverter', # For building latexpdf
'sphinx.ext.githubpages',
# 'sphinxcontrib.redirects',
'sphinxcontrib.jquery', # Fix for search
'sphinx_reredirects',
]
todo_include_todos = True
napoleon_google_docstring = True
napoleon_use_param = False
napoleon_use_ivar = True
#autoapi_type = 'python'
#autoapi_dirs = [mod_dpath]
autodoc_inherit_docstrings = False
# Hack for geowatch, todo configure
autosummary_mock_imports = [
'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_24_and_lt_4_xx',
'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_22_and_lt_4_24',
'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_21_and_lt_4_22',
'geowatch.tasks.fusion.datamodules.temporal_sampling.affinity_sampling',
'geowatch.tasks.depth_pcd.model',
'geowatch.tasks.cold.export_change_map',
]
autodoc_default_options = {{ # Document callable classes
'special-members': '__call__'}}
autodoc_member_order = 'bysource'
autoclass_content = 'both'
# autodoc_mock_imports = ['torch', 'torchvision', 'visdom']
# autoapi_modules = {{
# modname: {{
# 'override': False,
# 'output': 'auto'
# }}
# }}
# autoapi_dirs = [f'../../src/{{modname}}']
# autoapi_keep_files = True
# References:
# https://stackoverflow.com/questions/21538983/specifying-targets-for-intersphinx-links-to-numpy-scipy-and-matplotlib
intersphinx_mapping = {{
# 'pytorch': ('http://pytorch.org/docs/master/', None),
'python': ('https://docs.python.org/3', None),
'click': ('https://click.palletsprojects.com/', None),
# 'xxhash': ('https://pypi.org/project/xxhash/', None),
# 'pygments': ('https://pygments.org/docs/', None),
# 'tqdm': ('https://tqdm.github.io/', None),
# Requires that the repo have objects.inv
'kwarray': ('https://kwarray.readthedocs.io/en/latest/', None),
'kwimage': ('https://kwimage.readthedocs.io/en/latest/', None),
# 'kwplot': ('https://kwplot.readthedocs.io/en/latest/', None),
'ndsampler': ('https://ndsampler.readthedocs.io/en/latest/', None),
'ubelt': ('https://ubelt.readthedocs.io/en/latest/', None),
'xdoctest': ('https://xdoctest.readthedocs.io/en/latest/', None),
'networkx': ('https://networkx.org/documentation/stable/', None),
'scriptconfig': ('https://scriptconfig.readthedocs.io/en/latest/', None),
'rich': ('https://rich.readthedocs.io/en/latest/', None),
'numpy': ('https://numpy.org/doc/stable/', None),
'sympy': ('https://docs.sympy.org/latest/', None),
'scikit-learn': ('https://scikit-learn.org/stable/', None),
'pandas': ('https://pandas.pydata.org/docs/', None),
'matplotlib': ('https://matplotlib.org/stable/', None),
'pytest': ('https://docs.pytest.org/en/latest/', None),
'platformdirs': ('https://platformdirs.readthedocs.io/en/latest/', None),
'timerit': ('https://timerit.readthedocs.io/en/latest/', None),
'progiter': ('https://progiter.readthedocs.io/en/latest/', None),
'dateutil': ('https://dateutil.readthedocs.io/en/latest/', None),
# 'pytest._pytest.doctest': ('https://docs.pytest.org/en/latest/_modules/_pytest/doctest.html', None),
# 'colorama': ('https://pypi.org/project/colorama/', None),
# 'cv2' : ('http://docs.opencv.org/2.4/', None),
# 'h5py' : ('http://docs.h5py.org/en/latest/', None)
}}
__dev_note__ = """
python -m sphinx.ext.intersphinx https://docs.python.org/3/objects.inv
python -m sphinx.ext.intersphinx https://kwcoco.readthedocs.io/en/latest/objects.inv
python -m sphinx.ext.intersphinx https://networkx.org/documentation/stable/objects.inv
python -m sphinx.ext.intersphinx https://kwarray.readthedocs.io/en/latest/objects.inv
python -m sphinx.ext.intersphinx https://kwimage.readthedocs.io/en/latest/objects.inv
python -m sphinx.ext.intersphinx https://ubelt.readthedocs.io/en/latest/objects.inv
python -m sphinx.ext.intersphinx https://networkx.org/documentation/stable/objects.inv
sphobjinv suggest -t 90 -u https://readthedocs.org/projects/pytest/reference/objects.inv
"signal.convolve2d"
python -m sphinx.ext.intersphinx https://pygments-doc.readthedocs.io/en/latest/objects.inv
"""
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = ['.rst', '.md']
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {{
'collapse_navigation': False,
'display_version': True,
'navigation_depth': -1,
# 'logo_only': True,
}}
# html_logo = '.static/{repo_name}.svg'
# html_favicon = '.static/{repo_name}.ico'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {{}}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = project + 'doc'
# -- Options for LaTeX output ------------------------------------------------
# References:
# https://tex.stackexchange.com/questions/546246/centos-8-the-font-freeserif-cannot-be-found
"""
# https://www.sphinx-doc.org/en/master/usage/builders/index.html#sphinx.builders.latex.LaTeXBuilder
# https://tex.stackexchange.com/a/570691/83399
sudo apt install fonts-freefont-otf texlive-luatex texlive-latex-extra texlive-fonts-recommended texlive-latex-recommended tex-gyre latexmk
make latexpdf LATEXMKOPTS="-shell-escape --synctex=-1 -src-specials -interaction=nonstopmode"
make latexpdf LATEXMKOPTS="-lualatex -interaction=nonstopmode"
make LATEXMKOPTS="-lualatex -interaction=nonstopmode"
"""
# latex_engine = 'lualatex'
# latex_engine = 'xelatex'
latex_elements = {{
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, '{repo_name}.tex', '{repo_name} Documentation',
'{author}', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, '{repo_name}', '{repo_name} Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, '{repo_name}', '{repo_name} Documentation',
author, '{repo_name}', 'One line description of project.',
'Miscellaneous'),
]
# -- Extension configuration -------------------------------------------------
'''
).format(**fmtkw)
from xcookie import rc
util_text = rc.resource_fpath('conf_ext.py').read_text()
if self.config.render_doc_images:
util_text = util_text.replace(
'render_doc_images = 0', 'render_doc_images = 1'
)
if self.config['repo_name'] == 'kwcoco':
util_text = util_text.replace(
'HACK_FOR_KWCOCO = 0', 'HACK_FOR_KWCOCO = 1'
)
text = text + '\n' + util_text
from xcookie.util.util_code_format import format_code
text = format_code(text)
return text
[docs]
def build_docs_requirements(self):
import ubelt as ub
docs_requierments = ub.codeblock(
"""
sphinx>=5.0.1
sphinx-autobuild>=2021.3.14
sphinx_rtd_theme >= 1.2.1
sphinxcontrib-napoleon>=0.7
sphinx-autoapi>=1.8.4
Pygments>=2.9.0
myst_parser>=0.18.0
sphinx-reredirects>=0.0.1
xdoctest>=1.1.2
"""
)
if self.config.render_doc_images:
if self.config['mod_name'] != 'kwplot':
if 'cv2' in self.tags:
docs_requierments += '\nkwplot>=0.4.15'
else:
docs_requierments += '\nkwplot[headless]>=0.4.15'
return docs_requierments