Source code for xcookie.builders.docs

"""
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