Documentation (Sphinx)
To generate this documentation, we use the Python documentation generator Sphinx (see Using Sphinx).
The generation process is divided into two stages:
sphinx-apidoc is a tool which scans the whole source code of our application in the
integreat_cms
directory and generates.rst
files out of the Python docstrings. These files are then placed in thedocs/src/ref-ext
directory.sphinx-build generates the html documentation out of the
.rst
files located in the sphinx directory. It merges the static.rst
files directly in thesphinx
directory together with the files generated in step 1 indocs/src/ref-ext
and the simplified.rst
files indocs/src/ref
.
Developer Tools
Generate this documentation with tools/make_docs.sh:
./tools/make_docs.sh [--clean]
Apart from the two steps above, this tool also performs the additional tasks:
Patch the footer template which allows custom URLs in the copyright notice.
Move the translation file to make sure the documentation doesn’t get partially translated.
Modify the autogenerated
.rst
files to improve the readability by removing module paths in headings.Patch the autogenerated
~integreat_cms.cms.rst
file to make sure decorated functions are included in the documentation.Copy all
.rst
files indocs/src/ref-ext
to the directorydocs/src/ref
and remove all undocumented and inherited members from the simplified version.Patch the breadcrumbs template which allows links from the simple view to the extended view and vice versa
Remove all temporary build files when running in CircleCI context to exclude them from the contents hosted on GitHub Pages.
If the --clean
parameter is provided, the script will clean all temporary documentation files in the docs/src/ref/
and docs/src/ref-ext/
directories as well as the compiled html output in docs/dist
. Existing outdated documentation
files can cause the generation script to fail if e.g. source files were added or deleted.
Docstrings
Please add docstrings in the sphinx format (see Writing docstrings):
"""
[Summary]
:param [ParamName]: [ParamDescription], defaults to [DefaultParamVal]
...
:return: [ReturnDescription]
:raises [ErrorType]: [ErrorDescription]
...
"""
Hint
In the model documentation, the parameters are not required, because they are automatically derived from the model field type.
Whenever you want to document module/class attributes which are not parameters (i.e. because they are not passed to the
__init__()
-function or contain static values), use inline comments:
#: Description of attribute
attribute = "value of this attribute"
See the configuration files settings
and conf
or constants
for examples.
Cross-referencing
Whenever you want to reference another module, class or function inside a static documentation page or within a docstring, use the according MOVED: Domains and have a look at Cross-referencing Python objects.
Example:
:class:`~integreat_cms.cms.models.regions.region.Region`
becomes Region
Many other python projects also use Sphinx for their documentation. Whenever you use e.g. a library function or want
to reference external classes or documentation pages, you can do this with intersphinx
.
All available documentations are listed in the dict intersphinx_mapping
.
If you want to reference another documentation which is not yet listed, just add it to this dict. After that, you can
reference them with the normal directives plus the key specified in the intersphinx_mapping
dict.
Example:
:doc:`python:howto/regex
becomes Regular Expression HOWTO
Note
If the cross-referenced documentation uses custom text roles,
the usage of these custom roles will only work after these roles have been registered in an extension’s setup()
(see sphinx.application.Sphinx
).
Hint
If you don’t know which references to the external documentation are possible, you can use the tool sphobjinv:
sphobjinv suggest -u https://docs.python.org/3.9/objects.inv "regex"
Remote inventory found.
:std:doc:`howto/regex`
:std:label:`regex-howto`
Substitutions
Whenever you use GitHub-specific strings, please use Substitutions:
|github-username|
becomes digitalfabrik|github-repository|
becomes integreat-cms|github-pages-url|
becomes https://digitalfabrik.github.io/integreat-cms
If you want to provide new substitutions, add them to rst_epilog
.
GitHub Pages
The CircleCI job deploy-documentation
uses the GitHub user DigitalfabrikMember to
push all changes to the documentation on the develop
branch to our repository’s gh-pages
branch.
This branch is then automatically deployed to https://digitalfabrik.github.io/integreat-cms.
External Links
Whenever you use multiple external links to the same website, consider defining a shortcut in extlinks
.
Currently, the following shortcuts exists:
:github:
, e.g.:github:`issues`
is rendered to issues:github-issue:
, e.g.:github-issue:`1928`
is rendered to #1928:github-source:
, e.g.:github-source:`setup.py`
is rendered to setup.py:django-source:
, e.g.:django-source:`django/shortcuts.py`
is rendered to django/shortcuts.py
Extensions
We use the following sphinx extensions:
sphinx.ext.autodoc
allows to use directives likeautomodule
in.rst
files to automatically import the modules and pull in documentation from docstrings in a semi-automatic way.sphinx.ext.extlinks
allows to use shortcuts to external links defined inextlinks
.sphinx.ext.githubpages
creates a.nojekyll
file on generated HTML directory to publish the document on GitHub Pages.sphinx.ext.intersphinx
allows to reference other project’s documentations.sphinx.ext.linkcode
adds external links to the source code of documented modules, seelinkcode_resolve()
.sphinxcontrib_django
adds improvements to the docstrings of Django models and forms.sphinx_rtd_theme is the theme we use for our documentation: Read the Docs Sphinx Theme
sphinx_last_updated_by_git determines the “last updated” date on the generated html documentation by the modification date of the source files in git.
Configuration
Configuration file for the Sphinx documentation builder.
This file only contains the options which deviate from the default values. For a full list see the documentation: Configuration
- conf.autodoc_typehints: Final[str] = 'both'[source]
This value controls whether the types of undocumented parameters and return values are documented
- conf.django_settings: Final[str] = 'integreat_cms.core.sphinx_settings'[source]
The path to the django settings module (see sphinxcontrib-django)
- conf.extensions: Final[list[str]] = ['sphinx.ext.autodoc', 'sphinx.ext.extlinks', 'sphinx.ext.githubpages', 'sphinx.ext.intersphinx', 'sphinx.ext.linkcode', 'sphinxcontrib_django', 'sphinx_rtd_theme', 'sphinx_last_updated_by_git'][source]
All enabled sphinx extensions (see Extensions)
[ 'sphinx.ext.autodoc', 'sphinx.ext.extlinks', 'sphinx.ext.githubpages', 'sphinx.ext.intersphinx', 'sphinx.ext.linkcode', 'sphinxcontrib_django', 'sphinx_rtd_theme', 'sphinx_last_updated_by_git', ]
- conf.extlinks: Final[dict[str, tuple[str, str]]] = {'django-source': ('https://github.com/django/django/blob/stable/4.2.x/%s', '%s'), 'github': ('https://github.com/digitalfabrik/integreat-cms/%s', '%s'), 'github-issue': ('https://github.com/digitalfabrik/integreat-cms/issues/%s', '#%s'), 'github-source': ('https://github.com/digitalfabrik/integreat-cms/blob/develop/%s', '%s')}[source]
Markup to shorten external links (see sphinx.ext.extlinks – Markup to shorten external links)
{ 'django-source': ( 'https://github.com/django/django/blob/stable/4.2.x/%s', '%s', ), 'github': ('https://github.com/digitalfabrik/integreat-cms/%s', '%s'), 'github-issue': ( 'https://github.com/digitalfabrik/integreat-cms/issues/%s', '#%s', ), 'github-source': ( 'https://github.com/digitalfabrik/integreat-cms/blob/develop/%s', '%s', ), }
- conf.github_pages_url: Final[str] = 'https://digitalfabrik.github.io/integreat-cms'[source]
GitHub pages URL (target of gh-pages branch)
- conf.html_baseurl: Final[str] = 'https://digitalfabrik.github.io/integreat-cms'[source]
The url where the docs should be published (via gh-pages)
- conf.html_favicon: Final[str] = '../../integreat_cms/static/src/logos/integreat/integreat-icon.svg'[source]
The favicon of the html doc files
- conf.html_logo: Final[str] = '../../integreat_cms/static/src/logos/integreat/integreat-logo-white.svg'[source]
The logo shown in the menu bar
- conf.html_show_sourcelink: Final[bool] = False[source]
Do not include links to the documentation source (.rst files) in build
- conf.html_theme: Final[str] = 'sphinx_rtd_theme'[source]
The theme to use for HTML and HTML Help pages.
- conf.html_theme_options: Final[dict[str, bool]] = {'collapse_navigation': False, 'logo_only': False}[source]
Do not show the project name, only the logo
{'collapse_navigation': False, 'logo_only': False}
- conf.intersphinx_mapping: Final[dict[str, tuple[str, str | None]]] = {'aiohttp': ('https://docs.aiohttp.org/en/stable/', None), 'dateutil': ('https://dateutil.readthedocs.io/en/stable/', None), 'db_mutex': ('https://django-db-mutex.readthedocs.io/en/latest/', None), 'django': ('https://docs.djangoproject.com/en/4.2/', 'https://docs.djangoproject.com/en/4.2/_objects/'), 'django-debug-toolbar': ('https://django-debug-toolbar.readthedocs.io/en/latest/', None), 'django-import-export': ('https://django-import-export.readthedocs.io/en/latest/', None), 'django-polymorphic': ('https://django-polymorphic.readthedocs.io/en/latest/', None), 'django-treebeard': ('https://django-treebeard.readthedocs.io/en/latest/', None), 'geopy': ('https://geopy.readthedocs.io/en/stable/', None), 'lxml': ('https://lxml.de/apidoc/', None), 'pytest': ('https://docs.pytest.org/en/latest/', None), 'pytest-cov': ('https://pytest-cov.readthedocs.io/en/latest/', None), 'pytest-django': ('https://pytest-django.readthedocs.io/en/latest/', None), 'pytest-httpserver': ('https://pytest-httpserver.readthedocs.io/en/latest/', None), 'pytest-xdist': ('https://pytest-xdist.readthedocs.io/en/latest/', None), 'python': ('https://docs.python.org/3.11/', None), 'requests': ('https://requests.readthedocs.io/en/latest/', None), 'requests-mock': ('https://requests-mock.readthedocs.io/en/latest/', None), 'setuptools': ('https://setuptools.pypa.io/en/latest/', None), 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), 'sphinx-rtd-theme': ('https://sphinx-rtd-theme.readthedocs.io/en/latest/', None), 'sphinx-rtd-tutorial': ('https://sphinx-rtd-tutorial.readthedocs.io/en/latest/', None), 'sphinxcontrib-django': ('https://sphinxcontrib-django.readthedocs.io/en/latest/', None), 'twine': ('https://twine.readthedocs.io/en/latest/', None), 'wsgi': ('https://wsgi.readthedocs.io/en/latest/', None), 'xhtml2pdf': ('https://xhtml2pdf.readthedocs.io/en/latest/', None)}[source]
Enable cross-references to other documentations
{ 'aiohttp': ('https://docs.aiohttp.org/en/stable/', None), 'dateutil': ('https://dateutil.readthedocs.io/en/stable/', None), 'db_mutex': ('https://django-db-mutex.readthedocs.io/en/latest/', None), 'django': ( 'https://docs.djangoproject.com/en/4.2/', 'https://docs.djangoproject.com/en/4.2/_objects/', ), 'django-debug-toolbar': ( 'https://django-debug-toolbar.readthedocs.io/en/latest/', None, ), 'django-import-export': ( 'https://django-import-export.readthedocs.io/en/latest/', None, ), 'django-polymorphic': ( 'https://django-polymorphic.readthedocs.io/en/latest/', None, ), 'django-treebeard': ( 'https://django-treebeard.readthedocs.io/en/latest/', None, ), 'geopy': ('https://geopy.readthedocs.io/en/stable/', None), 'lxml': ('https://lxml.de/apidoc/', None), 'pytest': ('https://docs.pytest.org/en/latest/', None), 'pytest-cov': ('https://pytest-cov.readthedocs.io/en/latest/', None), 'pytest-django': ('https://pytest-django.readthedocs.io/en/latest/', None), 'pytest-httpserver': ( 'https://pytest-httpserver.readthedocs.io/en/latest/', None, ), 'pytest-xdist': ('https://pytest-xdist.readthedocs.io/en/latest/', None), 'python': ('https://docs.python.org/3.11/', None), 'requests': ('https://requests.readthedocs.io/en/latest/', None), 'requests-mock': ('https://requests-mock.readthedocs.io/en/latest/', None), 'setuptools': ('https://setuptools.pypa.io/en/latest/', None), 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), 'sphinx-rtd-theme': ( 'https://sphinx-rtd-theme.readthedocs.io/en/latest/', None, ), 'sphinx-rtd-tutorial': ( 'https://sphinx-rtd-tutorial.readthedocs.io/en/latest/', None, ), 'sphinxcontrib-django': ( 'https://sphinxcontrib-django.readthedocs.io/en/latest/', None, ), 'twine': ('https://twine.readthedocs.io/en/latest/', None), 'wsgi': ('https://wsgi.readthedocs.io/en/latest/', None), 'xhtml2pdf': ('https://xhtml2pdf.readthedocs.io/en/latest/', None), }
- conf.linkcode_resolve(domain: str, info: dict) str | None [source]
This function adds source code links to all modules (see
sphinx.ext.linkcode
). It links all classes and functions to their source files on GitHub including line numbers.
- conf.modindex_common_prefix: Final[list[str]] = ['integreat_cms'][source]
A list of prefixes that are ignored for sorting the Python module index
['integreat_cms']
- conf.nitpick_ignore: list[tuple[str, str]] = [('py:attr', 'django.contrib.auth.models.Group.role'), ('py:attr', 'django.contrib.auth.models.Group.user_set'), ('py:attr', 'django.contrib.auth.models.Permission.user_set'), ('py:attr', 'django.contrib.contenttypes.models.ContentType.polymorphic_cms.feedback_set+'), ('py:attr', 'linkcheck.models.Link.+'), ('py:attr', 'linkcheck.models.Link.event_translation'), ('py:attr', 'linkcheck.models.Link.event_translations'), ('py:attr', 'linkcheck.models.Link.imprint_translation'), ('py:attr', 'linkcheck.models.Link.page_translation'), ('py:attr', 'linkcheck.models.Link.page_translations'), ('py:attr', 'linkcheck.models.Link.poi_translation'), ('py:attr', 'linkcheck.models.Link.poi_translations'), ('py:class', '_io.StringIO'), ('py:class', 'argparse.ArgumentTypeError'), ('py:class', 'builtins.AssertionError'), ('py:class', 'builtins.int'), ('py:class', 'builtins.int'), ('py:class', 'django.contrib.admin.checks.ModelAdminChecks'), ('py:class', 'django.contrib.admin.helpers.ActionForm'), ('py:class', 'django.contrib.auth.base_user.BaseUserManager'), ('py:class', 'django.contrib.auth.context_processors.PermWrapper'), ('py:class', 'django.contrib.auth.forms.UsernameField'), ('py:class', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher'), ('py:class', 'django.contrib.auth.tokens.PasswordResetTokenGenerator'), ('py:class', 'django.core.handlers.WSGIHandler'), ('py:class', 'django.core.mail.EmailMultiAlternatives'), ('py:class', 'django.core.management.base.BaseCommand'), ('py:class', 'django.core.management.base.CommandError'), ('py:class', 'django.core.management.base.CommandParser'), ('py:class', 'django.core.serializers.base.DeserializationError'), ('py:class', 'django.core.serializers.base.DeserializedObject'), ('py:class', 'django.core.serializers.base.ProgressBar'), ('py:class', 'django.core.serializers.base.SerializationError'), ('py:class', 'django.core.serializers.xml_serializer.Deserializer'), ('py:class', 'django.core.serializers.xml_serializer.Serializer'), ('py:class', 'django.db.models.base.ModelBase'), ('py:class', 'django.forms.BaseInlineFormSet'), ('py:class', 'django.forms.BaseModelFormSet'), ('py:class', 'django.forms.formsets.POICategoryTranslationFormFormSet'), ('py:class', 'django.forms.models.ModelChoiceIterator'), ('py:class', 'django.forms.models.ModelFormMetaclass'), ('py:class', 'django.forms.widgets.LanguageTreeNodeForm'), ('py:class', 'django.forms.widgets.PageForm'), ('py:class', 'django.test.client.AsyncClient'), ('py:class', 'django.test.client.Client'), ('py:class', 'django.utils.datastructures.MultiValueDict'), ('py:class', 'django.utils.functional.Promise'), ('py:class', 'django.utils.xmlutils.SimplerXMLGenerator'), ('py:class', 'linkcheck.apps.LinkcheckConfig'), ('py:class', 'linkcheck.Linklist'), ('py:class', 'linkcheck.models.Link'), ('py:class', 'linkcheck.models.Url'), ('py:class', 'NoneType'), ('py:class', 'lxml.html.Element'), ('py:class', 'polymorphic.query.PolymorphicQuerySet'), ('py:class', 'PolymorphicQuerySet'), ('py:class', 'pytest_django.fixtures.SettingsWrapper'), ('py:class', 'requests_mock.mocker.Mocker'), ('py:class', 'webauthn.WebAuthnUser'), ('py:class', 'xml.dom.minidom.Element'), ('py:class', 'django.contrib.auth.context_processors.PermWrapper'), ('py:func', 'django.contrib.sitemaps.Sitemap._urls'), ('py:func', 'django.utils.text.capfirst'), ('py:class', '-- reverse *IN PLACE*'), ('py:class', 'integer -- return first index of value.'), ('py:class', 'integer -- return number of occurrences of value'), ('py:class', '-- insert object before index'), ('py:class', '-- remove first occurrence of value.'), ('py:class', '-- size of D in memory, in bytes'), ('py:class', '-- return a reverse iterator over the deque')][source]
A list of (type, target) tuples that should be ignored when
nitpicky
isTrue
[ ('py:attr', 'django.contrib.auth.models.Group.role'), ('py:attr', 'django.contrib.auth.models.Group.user_set'), ('py:attr', 'django.contrib.auth.models.Permission.user_set'), ( 'py:attr', 'django.contrib.contenttypes.models.ContentType.polymorphic_cms.feedback_set+', ), ('py:attr', 'linkcheck.models.Link.+'), ('py:attr', 'linkcheck.models.Link.event_translation'), ('py:attr', 'linkcheck.models.Link.event_translations'), ('py:attr', 'linkcheck.models.Link.imprint_translation'), ('py:attr', 'linkcheck.models.Link.page_translation'), ('py:attr', 'linkcheck.models.Link.page_translations'), ('py:attr', 'linkcheck.models.Link.poi_translation'), ('py:attr', 'linkcheck.models.Link.poi_translations'), ('py:class', '_io.StringIO'), ('py:class', 'argparse.ArgumentTypeError'), ('py:class', 'builtins.AssertionError'), ('py:class', 'builtins.int'), ('py:class', 'builtins.int'), ('py:class', 'django.contrib.admin.checks.ModelAdminChecks'), ('py:class', 'django.contrib.admin.helpers.ActionForm'), ('py:class', 'django.contrib.auth.base_user.BaseUserManager'), ('py:class', 'django.contrib.auth.context_processors.PermWrapper'), ('py:class', 'django.contrib.auth.forms.UsernameField'), ('py:class', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher'), ('py:class', 'django.contrib.auth.tokens.PasswordResetTokenGenerator'), ('py:class', 'django.core.handlers.WSGIHandler'), ('py:class', 'django.core.mail.EmailMultiAlternatives'), ('py:class', 'django.core.management.base.BaseCommand'), ('py:class', 'django.core.management.base.CommandError'), ('py:class', 'django.core.management.base.CommandParser'), ('py:class', 'django.core.serializers.base.DeserializationError'), ('py:class', 'django.core.serializers.base.DeserializedObject'), ('py:class', 'django.core.serializers.base.ProgressBar'), ('py:class', 'django.core.serializers.base.SerializationError'), ('py:class', 'django.core.serializers.xml_serializer.Deserializer'), ('py:class', 'django.core.serializers.xml_serializer.Serializer'), ('py:class', 'django.db.models.base.ModelBase'), ('py:class', 'django.forms.BaseInlineFormSet'), ('py:class', 'django.forms.BaseModelFormSet'), ('py:class', 'django.forms.formsets.POICategoryTranslationFormFormSet'), ('py:class', 'django.forms.models.ModelChoiceIterator'), ('py:class', 'django.forms.models.ModelFormMetaclass'), ('py:class', 'django.forms.widgets.LanguageTreeNodeForm'), ('py:class', 'django.forms.widgets.PageForm'), ('py:class', 'django.test.client.AsyncClient'), ('py:class', 'django.test.client.Client'), ('py:class', 'django.utils.datastructures.MultiValueDict'), ('py:class', 'django.utils.functional.Promise'), ('py:class', 'django.utils.xmlutils.SimplerXMLGenerator'), ('py:class', 'linkcheck.apps.LinkcheckConfig'), ('py:class', 'linkcheck.Linklist'), ('py:class', 'linkcheck.models.Link'), ('py:class', 'linkcheck.models.Url'), ('py:class', 'NoneType'), ('py:class', 'lxml.html.Element'), ('py:class', 'polymorphic.query.PolymorphicQuerySet'), ('py:class', 'PolymorphicQuerySet'), ('py:class', 'pytest_django.fixtures.SettingsWrapper'), ('py:class', 'requests_mock.mocker.Mocker'), ('py:class', 'webauthn.WebAuthnUser'), ('py:class', 'xml.dom.minidom.Element'), ('py:class', 'django.contrib.auth.context_processors.PermWrapper'), ('py:func', 'django.contrib.sitemaps.Sitemap._urls'), ('py:func', 'django.utils.text.capfirst'), ('py:class', '-- reverse *IN PLACE*'), ('py:class', 'integer -- return first index of value.'), ('py:class', 'integer -- return number of occurrences of value'), ('py:class', '-- insert object before index'), ('py:class', '-- remove first occurrence of value.'), ('py:class', '-- size of D in memory, in bytes'), ('py:class', '-- return a reverse iterator over the deque'), ]
- conf.nitpicky: Final[bool] = False[source]
Warn about all references where the target cannot be found
- conf.rst_epilog: Final[str] = '\n.. |github-username| replace:: digitalfabrik\n.. |github-repository| replace:: integreat-cms\n.. |github-pages-url| replace:: https://digitalfabrik.github.io/integreat-cms\n'[source]
A string of reStructuredText that will be included at the end of every source file that is read. Used for substitutions.