Skip to content

Commit

Permalink
starter docs (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
nstarman committed May 19, 2023
1 parent b552270 commit 92781ce
Show file tree
Hide file tree
Showing 15 changed files with 529 additions and 35 deletions.
3 changes: 0 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,5 @@ repos:
rev: "v1.2.0"
hooks:
- id: mypy
additional_dependencies: [
"git+https://github.com/python-cosmology/cosmology-api.git#egg=cosmology-api"
]
args:
- --strict
6 changes: 6 additions & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
**Main authors:** Nathaniel Starkman, Nicolas Tessore

All contributors (alphabetical last name):

* Nathaniel Starkman (`@nstarman <https://github.com/nstarman>`_)
* Nicolas Tessore (`@ntessore <https://github.com/ntessore>`_)
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
api/reference
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
51 changes: 51 additions & 0 deletions docs/_ext/sphinx_ext_autosummary_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Monkey-patching autosummary to emit the autosummary-gather-context event."""
from inspect import signature
from typing import TYPE_CHECKING

from sphinx.errors import ExtensionError
from sphinx.ext.autosummary.generate import (
generate_autosummary_content,
generate_autosummary_docs,
)

if TYPE_CHECKING:
from sphinx.application import Sphinx

GENERATE_SIGNATURE = signature(generate_autosummary_content)


def generate_autosummary_content_with_context(*args: object, **kwargs: object) -> str:
"""Wrap generate_autosummary_content to emit autosummary-gather-context."""
ba = GENERATE_SIGNATURE.bind_partial(*args, **kwargs)
app = ba.arguments["app"]
if app:
name = ba.arguments["name"]
obj = ba.arguments["obj"]
parent = ba.arguments["parent"]
context = ba.arguments["context"]
results = app.emit("autosummary-gather-context", name, obj, parent, context)
for extra_context in results:
if extra_context:
context.update(extra_context)
return generate_autosummary_content(*args, **kwargs) # type: ignore[arg-type]


def setup(app: "Sphinx") -> None:
"""Monkey-patch the autosummary-gather-context event if not present."""
# try connecting a mock callback to autosummary-gather-context
try:
listener_id = app.connect("autosummary-gather-context", lambda: None)
except ExtensionError:
listener_id = None

if listener_id is not None:
# event exists, all good
app.disconnect(listener_id)
else:
# patch generate_autosummary_docs to call the wrapper above
generate_autosummary_docs.__globals__[
"generate_autosummary_content"
] = generate_autosummary_content_with_context

# register the new event
app.add_event("autosummary-gather-context")
84 changes: 84 additions & 0 deletions docs/_ext/sphinx_ext_cosmology_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# ruff: noqa
import sys
from types import ModuleType
from inspect import getmembers, getmro
from sphinx.util.inspect import signature, stringify_signature

# get_overloads is a Python 3.11 feature
try:
from typing import get_overloads
except ImportError:
get_overloads = None

import cosmology
import cosmology.api

DOCS_MODULE_NAME = "cosmology.docs"


class cosmo(cosmology.api.StandardCosmology):
"""This is a mock cosmology class used for documenting all members
of the cosmology.api protocols.
"""


COSMOLOGY_BASES = [cls for cls in getmro(cosmo) if cls is not cosmo]


def context_callback(app, name, obj, parent, context):
"""Callback function to provide extra context for reference."""
# add protocols to context if obj is a method or property of Cosmology
extra_context = None
if parent is cosmo:
_, _, membername = name.rpartition(".")
protocols = [
f"{cls.__qualname__}.{membername}"
for cls in COSMOLOGY_BASES
if hasattr(cls, membername)
]
extra_context = {"protocols": protocols}
return extra_context


def signature_callback(app, what, name, obj, options, sig, return_annotation):
"""Callback function to provide overloaded signatures."""
if what in ("function", "method") and callable(obj):
overloads = get_overloads(obj)
if overloads:
kwargs = {}
if app.config.autodoc_typehints in ("none", "description"):
kwargs["show_annotation"] = False
if app.config.autodoc_typehints_format == "short":
kwargs["unqualified_typehints"] = True
type_aliases = app.config.autodoc_type_aliases
bound_method = what == "method"
sigs = []
for overload in overloads:
overload_sig = signature(
overload, bound_method=bound_method, type_aliases=type_aliases
)
sigs.append(stringify_signature(overload_sig, **kwargs))
return "\n".join(sigs), None


def setup(app):
"""Initialise the cosmology documentation extension."""
# create a mock `cosmology.docs` module containing `Cosmology`
docs_module = ModuleType(DOCS_MODULE_NAME)
docs_module.cosmo = cosmo
cosmo.__module__ = DOCS_MODULE_NAME
sys.modules[DOCS_MODULE_NAME] = docs_module

# change the autosummary_filename_map for all members of Cosmology
filename_map = {}
filename_prefix = f"{cosmo.__module__}.{cosmo.__name__}"
for name, _obj in getmembers(cosmo):
filename_map[f"{filename_prefix}.{name}"] = name
app.config.autosummary_filename_map.update(filename_map)

# register a callback for extra autosummary context
app.connect("autosummary-gather-context", context_callback)

# register a callback to show overloaded signatures
if get_overloads:
app.connect("autodoc-process-signature", signature_callback)
Empty file added docs/_static/.gitignore
Empty file.
20 changes: 20 additions & 0 deletions docs/_templates/autosummary/reference.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{ name | escape | underline }}

{% if protocols -%}
.. rubric:: Protocols

.. currentmodule:: cosmology
{% for protocol in protocols %}
{% if loop.first %}.. {{ objtype }}:: {{ protocol }}
{%- else %}{% for c in objtype %} {% endfor %} {{ protocol }}
{%- endif %}
{%- endfor %}
:nocontentsentry:

{% endif -%}

.. rubric:: Reference

.. currentmodule:: {{ module }}

.. auto{{ objtype }}:: {{ objname }}
6 changes: 6 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

Changelog
=========

.. include:: ../CHANGES.rst
:start-line: 2
177 changes: 177 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# ruff: noqa

"""Configuration file for the Sphinx documentation builder.
This file only contains a selection of the most common options. For a full
list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
"""


import os
import sys
import tomli

sys.path.append(os.path.abspath("../src"))


# -- Project information -----------------------------------------------------


def read_pyproject():
"""Get author information from package metadata."""
with open(os.path.abspath("../pyproject.toml"), "rb") as f:
toml = tomli.load(f)

project = dict(toml["project"])
version = project["version"]
authors = ", ".join(d["name"] for d in project["authors"])

return version, authors


package_version, package_authors = read_pyproject()

project = "cosmology"
author = package_authors
copyright = f"2023, {author}"


# The full version, including alpha/beta/rc tags.
release = package_version
# The short X.Y version.
version = release.partition("-")[0]


# -- General configuration ---------------------------------------------------

sys.path.append(os.path.abspath("./_ext"))

# By default, highlight as Python 3.
highlight_language = "python3"

# 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.autosummary",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"numpydoc",
"sphinx_copybutton",
"sphinx_ext_autosummary_context",
"sphinx_ext_cosmology_api",
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

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

# This is added to the end of RST files - a good place to put substitutions to
# be used globally.
rst_epilog = """
.. |author| replace:: {author}
.. _Python: http://www.python.org
"""

intersphinx_mapping = {
"python": (
"https://docs.python.org/3/",
(None, "http://data.astropy.org/intersphinx/python3.inv"),
),
"numpy": (
"https://numpy.org/doc/stable/",
(None, "http://data.astropy.org/intersphinx/numpy.inv"),
),
"scipy": (
"https://docs.scipy.org/doc/scipy/reference/",
(None, "https://docs.scipy.org/doc/scipy/objects.inv"),
),
}


# -- 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 = "furo"

# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = f"{project} v{release}"

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


# -- autodoc extension -------------------------------------------------------

# The default options for autodoc directives. They are applied to all autodoc
# directives automatically. It must be a dictionary which maps option names to
# the values.
autodoc_default_options = {
"members": False,
"inherited-members": False,
"show-inheritance": True,
}


add_module_names = False

# autodoc_class_signature = "separated"

autosummary_generate = True


# -- numpydoc extension ------------------------------------------------------

# Whether to show all members of a class in the Methods and Attributes sections
# automatically. True by default.
numpydoc_show_class_members = True

# Whether to show all inherited members of a class in the Methods and
# Attributes sections automatically. If it's false, inherited members won't
# shown. True by default.
numpydoc_show_inherited_class_members = True

# Whether to create a Sphinx table of contents for the lists of class methods
# and attributes. If a table of contents is made, Sphinx expects each entry to
# have a separate page. True by default.
numpydoc_class_members_toctree = False

# A regular expression matching citations which should be mangled to avoid
# conflicts due to duplication across the documentation. Defaults to '[\w-]+'.
# > numpydoc_citation_re = '[\w-]+'

# Whether to format the Attributes section of a class page in the same way as
# the Parameter section. If it's False, the Attributes section will be
# formatted as the Methods section using an autosummary table. True by default.
numpydoc_attributes_as_param_list = False

# Whether to create cross-references for the parameter types in the
# Parameters, Other Parameters, Returns and Yields sections of the docstring.
numpydoc_xref_param_type = True

# Words not to cross-reference. Most likely, these are common words used in
# parameter type descriptions that may be confused for classes of the same
# name. This can be overwritten or modified in packages and is provided here
# for convenience.
numpydoc_xref_ignore = {
"or",
"default",
"optional",
"positional-only",
"keyword-only",
}

# -- copybutton extension ------------------------------------------------------

copybutton_exclude = ".linenos, .gp, .go"
Loading

0 comments on commit 92781ce

Please sign in to comment.