Documentation (Sphinx)

To generate this documentation, we use the Python documentation generator Sphinx (see Using Sphinx).

The generation process is divided into two stages:

  1. 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 the docs/src/ref-ext directory.

  2. sphinx-build generates the html documentation out of the .rst files located in the sphinx directory. It merges the static .rst files directly in the sphinx directory together with the files generated in step 1 in docs/src/ref-ext and the simplified .rst files in docs/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 in docs/src/ref-ext to the directory docs/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:

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.

Extensions

We use the following sphinx extensions:

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.author: Final[str] = 'Tür an Tür Digitalfabrik gGmbH'[source]

The project author

conf.autodoc_typehints: Final[str] = 'both'[source]

This value controls whether the types of undocumented parameters and return values are documented

conf.copyright: Final[str] = '2024 Tür an Tür Digitalfabrik gGmbH'[source]

The copyright notice

conf.django_settings: Final[str] = 'integreat_cms.core.sphinx_settings'[source]

The path to the django settings module (see sphinxcontrib-django)

conf.django_version: Final[str] = '4.2'[source]

The “major.minor” version of 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',
]

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.github_repository: Final[str] = 'integreat-cms'[source]

GitHub repository name

conf.github_url: Final[str] = 'https://github.com/digitalfabrik/integreat-cms'[source]

GitHub URL

conf.github_username: Final[str] = 'digitalfabrik'[source]

GitHub username

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_last_updated_fmt: Final[str] = '%b %d, %Y'[source]

Include last updated timestamp

The logo shown in the menu bar

Do not include links to the documentation source (.rst files) in build

conf.html_show_sphinx: Final[bool] = False[source]

Do not include a link to sphinx

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), 'pylint': ('https://pylint.readthedocs.io/en/latest/', 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),
    'pylint': ('https://pylint.readthedocs.io/en/latest/', 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.intersphinx_timeout: Final[int] = 5[source]

The intersphinx request timeout in seconds

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.

Parameters:
  • domain (str) – The programming language of the given object (e.g. py, c, cpp or javascript)

  • info (dict) – Information about the given object. For a python object, it has the keys module and fullname.

Returns:

The URL of the given module on GitHub

Return type:

str | None

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 is True

[
    ('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.project: Final[str] = 'integreat-cms'[source]

The project name

conf.release = '2024.12.1'[source]

The full version, including alpha/beta/rc tags

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.

conf.setup(app: Sphinx) None[source]

Connect to the django-configured event of sphinxcontrib_django to monkeypatch application.

Parameters:

app (Sphinx) – The Sphinx application object

Return type:

None

conf.templates_path: Final[list[str]] = ['templates'][source]

The path for patched template files

['templates']