#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Sphinx configuration for documentation for ``berhoel`` python packages.
"""
# Standard library imports.
import enum
from typing import Union
from pathlib import Path
# Third party library imports.
import tomli
import sphinx_bootstrap_theme
import cmakelists_parsing.parsing as cmp
__date__ = "2023/01/01 21:14:27 hoel"
__author__ = "Berthold Höllmann"
__copyright__ = "Copyright © 2022 by Berthold Höllmann"
__credits__ = ["Berthold Höllmann"]
__maintainer__ = "Berthold Höllmann"
__email__ = "berhoel@gmail.com"
try:
# Local library imports.
from ._version import __version__
except ImportError:
__version__ = "0.0.0.invalid0"
__all__ = [
"ProjectTypes",
"configuration",
"extensions",
"templates_path",
"language",
"exclude_patterns",
"html_theme",
"html_theme_path",
"html_static_path",
"latex_elements",
"latex_show_urls",
"latex_engine",
"intersphinx_mapping",
"todo_include_todos",
"napoleon_google_docstring",
"napoleon_numpy_docstring",
"napoleon_include_init_with_doc",
"napoleon_include_private_with_doc",
"napoleon_include_special_with_doc",
"napoleon_use_admonition_for_examples",
"napoleon_use_admonition_for_notes",
"napoleon_use_admonition_for_references",
"napoleon_use_ivar",
"napoleon_use_param",
"napoleon_use_rtype",
"autodoc_default_options",
"favicons",
"get_html_theme_path",
"setup",
"sitemap_url_scheme",
]
[docs]class ProjectTypes(enum.Enum):
POETRY = enum.auto()
CMAKE = enum.auto()
# -- process project configuration -------------------------------------------
[docs]class _ProjectInfo:
[docs] def __init__(self):
self.project, self.release, self.author = None, None, None
[docs] def configuration(self):
res = {"project": self.project, "release": self.release, "author": self.author}
if hasattr(self, "html_baseurl"):
res.update({"html_baseurl": self.html_baseurl})
return res
[docs]class PyProject_toml(_ProjectInfo):
"""Provide project information from pyproject.toml file."""
[docs] def __init__(self, cfg_file: Union[Path, str] = None):
"""Process pyproject.toml file for project information.
:param `cfg_file`: path to pyproject.toml file for current project.
(default: search for appropriate file)
:type cfg_file: str|Path
"""
if cfg_file is None:
base_path = Path().absolute()
py_project = base_path / "pyproject.toml"
while not py_project.is_file():
if base_path == base_path.parent:
raise SystemError("pyproject.toml not found")
base_path = base_path.parent
py_project = base_path / "pyproject.toml"
self._toml_inst = tomli.load((base_path / "pyproject.toml").open("rb"))
else:
self._toml_inst = tomli.load(
(cfg_file if isinstance(cfg_file, Path) else Path(cfg_file)).open("rb")
)
@property
def project(self):
return self._toml_inst["tool"]["poetry"]["name"]
@property
def release(self):
return self._toml_inst["tool"]["poetry"]["version"]
@property
def author(self):
return ", ".join(self._toml_inst["tool"]["poetry"]["authors"])
# -- Option for sphinx_sitemap extension ----------------------------------------------
@property
def html_baseurl(self):
return self._toml_inst["tool"]["poetry"]["homepage"]
[docs]class CMakeLists_txt(_ProjectInfo):
"""Provide project information from CMakeLists.txt file."""
[docs] def __init__(self, cfg_file: Union[Path, str] = None):
"""Process CMakeLists.txt file for project information.
:param `cfg_file`: path to CMakeLists.txt file for current project.
(default: search for appropriate file)
:type cfg_file: str|Path
"""
if cfg_file is None:
base_path = Path().absolute()
cmake_project = base_path / "CMakeLists.txt"
while not cmake_project.is_file() or not self._has_project(cmake_project):
if base_path == base_path.parent:
raise SystemError("CMakeLists.txt not found")
base_path = base_path.parent
cmake_project = base_path / "CMakeLists.txt"
self._cmake_lists = cmp.parse((base_path / "CMakeLists.txt").read_text())
else:
self._cmake_lists = cmp.parse(
(cfg_file if isinstance(cfg_file, Path) else Path(cfg_file)).read_text()
)
[docs] @staticmethod
def _has_project(cfg_file):
"Check if CMakeLists.txt file has 'project' command."
for line in cmp.parse(cfg_file.read_text()):
if hasattr(line, "name") and line.name == "project":
return True
return False
@property
def project(self):
for line in self._cmake_lists:
if hasattr(line, "name") and line.name == "project":
return line.body[0].contents
raise SystemError("No project command found in CMakeLists.txt")
@property
def release(self):
for line in self._cmake_lists:
if hasattr(line, "name") and line.name == "project":
content = iter(line.body[1:])
for entry in content:
if entry.contents == "VERSION":
return next(content).contents
raise SystemError(
"No version number found in CMakeLists.txt project command"
)
raise SystemError("No project command found in CMakeLists.txt")
@property
def author(self):
return "Berthold Höllmann <berthold@xn--hllmanns-n4a.de>"
[docs]def configuration(
project_type: ProjectTypes = ProjectTypes.POETRY,
cfg_file: Union[Path, str] = None,
):
"""Get configuration class appropriate to build system for current project.
.. Keyword Arguments:
:param project_type: Type of builkd system configuration file (poetry or CMake)
(default project_types.POETRY)
:param cfg_file: Path to build system configuration file. Will be search if
not given. (default None)
.. Types:
:type project_type: project_types
:type cfg_file: str | Path
"""
if project_type == ProjectTypes.POETRY:
return PyProject_toml(cfg_file)
if project_type == ProjectTypes.CMAKE:
return CMakeLists_txt(cfg_file)
raise SystemError("Unknown project type")
# -- Path setup --------------------------------------------------------------
[docs]def get_html_theme_path():
return [Path(__file__).parent]
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.viewcode",
"sphinx.ext.todo",
"sphinx.ext.autodoc",
"sphinx.ext.intersphinx",
"sphinx.ext.coverage",
"sphinx.ext.mathjax",
"sphinx.ext.autosummary",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
"sphinx-favicon",
"sphinx_sitemap",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# 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 = [
"_build",
"Thumbs.db",
".DS_Store",
"**/flycheck_*.py",
"tests",
]
# -- Options for HTML output -------------------------------------------------
# Activate the theme.
html_theme = "berhoel_sphinx_theme"
html_theme_path = [Path(__file__).parent]
html_theme_path.extend(sphinx_bootstrap_theme.get_html_theme_path())
# (Optional) Logo. Should be small enough to fit the navbar (ideally 24x24).
# Path should be relative to the ``_static`` files directory.
# html_logo = "_static/logo.svg"
# 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"]
# -- Options for LaTeX/PDF output -------------------------------------------------
latex_elements = {
"fontenc": "\\usepackage{fontspec}",
"fontpkg": """\
\\setmainfont{DejaVu Serif}
\\setsansfont{DejaVu Sans}
\\setmonofont{DejaVu Sans Mono}""",
"geometry": "\\usepackage[vmargin=2.5cm, hmargin=3cm]{geometry}",
"preamble": """\
\\usepackage[titles]{tocloft}
\\cftsetpnumwidth {1.25cm}\\cftsetrmarg{1.5cm}
\\setlength{\\cftchapnumwidth}{0.75cm}
\\setlength{\\cftsecindent}{\\cftchapnumwidth}
\\setlength{\\cftsecnumwidth}{1.25cm}""",
"fncychap": "\\usepackage[Bjornstrup]{fncychap}",
"printindex": "\\footnotesize\\raggedright\\printindex",
}
latex_show_urls = "footnote"
latex_engine = "xelatex"
# -- Extension configuration -------------------------------------------------
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {"https://docs.python.org/3/": None}
# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# Napoleon settings
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = False
napoleon_include_private_with_doc = False
napoleon_include_special_with_doc = True
napoleon_use_admonition_for_examples = False
napoleon_use_admonition_for_notes = False
napoleon_use_admonition_for_references = False
napoleon_use_ivar = False
napoleon_use_param = True
napoleon_use_rtype = True
# -- Options for autodoc extension ----------------------------------------------
autodoc_default_options = {
"private-members": True,
"member-order": "bysource",
"special-members": "__init__,__str__,__call__,__enter__,__exit__",
"ignore-module-all": True,
}
# -- Options for favicon extension ----------------------------------------------
favicons = [
{
"rel": "icon",
"static-file": "bhLogo.svg", # => use `_static/icon.svg`
"type": "image/ico",
},
]
[docs]def setup(app):
# app.add_css_file("fonts/stylesheet.css") # also can be a full URL
app.add_css_file("bhoel-sphinx.css") # also can be a full URL
app.add_css_file("https://www.höllmanns.de/fonts/jetbrains-mono.css")
app.add_css_file("https://www.höllmanns.de/fonts/orelega-one.css")
app.add_css_file("https://www.höllmanns.de/fonts/roboto.css")
# -- Options for sphinx_sitemap extension ----------------------------------------------
sitemap_url_scheme = "{link}"
# Local Variables:
# mode: python
# compile-command: "poetry run tox"
# time-stamp-pattern: "30/__date__ = \"%:y/%02m/%02d %02H:%02M:%02S %u\""
# End: