Can edit the Jinja environment

Closes #200
This commit is contained in:
Ashley Whetter 2020-11-13 22:47:10 -08:00
parent 06a1969d11
commit a8d07b610f
10 changed files with 250 additions and 165 deletions

View File

@ -21,6 +21,10 @@ Features
Basic incremental build support is enabled ``autoapi_keep_files`` is enabled. Basic incremental build support is enabled ``autoapi_keep_files`` is enabled.
Providing none of the source files have changed, Providing none of the source files have changed,
AutoAPI will skip parsing the source code and regenerating the API documentation. AutoAPI will skip parsing the source code and regenerating the API documentation.
* `#200 <https://github.com/readthedocs/sphinx-autoapi/issues/200>`:
Can pass a callback that edits the Jinja Environment object before
template rendering begins.
This allows custom filters, tests, and globals to be added to the environment.
Bug Fixes Bug Fixes
^^^^^^^^^ ^^^^^^^^^

View File

@ -302,6 +302,7 @@ def setup(app):
app.add_config_value("autoapi_python_use_implicit_namespaces", False, "html") app.add_config_value("autoapi_python_use_implicit_namespaces", False, "html")
app.add_config_value("autoapi_python_class_content", "class", "html") app.add_config_value("autoapi_python_class_content", "class", "html")
app.add_config_value("autoapi_generate_api_docs", True, "html") app.add_config_value("autoapi_generate_api_docs", True, "html")
app.add_config_value("autoapi_prepare_jinja_env", None, "html")
app.add_autodocumenter(documenters.AutoapiFunctionDocumenter) app.add_autodocumenter(documenters.AutoapiFunctionDocumenter)
app.add_autodocumenter(documenters.AutoapiPropertyDocumenter) app.add_autodocumenter(documenters.AutoapiPropertyDocumenter)
app.add_autodocumenter(documenters.AutoapiDecoratorDocumenter) app.add_autodocumenter(documenters.AutoapiDecoratorDocumenter)

View File

@ -193,6 +193,8 @@ class SphinxMapperBase(object):
return "\n".join(prepare_docstring(value)) return "\n".join(prepare_docstring(value))
self.jinja_env.filters["prepare_docstring"] = _wrapped_prepare self.jinja_env.filters["prepare_docstring"] = _wrapped_prepare
if self.app.config.autoapi_prepare_jinja_env:
self.app.config.autoapi_prepare_jinja_env(self.jinja_env)
self.url_root = url_root self.url_root = url_root

View File

@ -107,7 +107,7 @@ class PythonPythonMapper(PythonMapperBase):
"""Whether this object should be displayed in documentation. """Whether this object should be displayed in documentation.
This attribute depends on the configuration options given in This attribute depends on the configuration options given in
:confval:`autoapi_options`. :confval:`autoapi_options` and the result of :event:`autoapi-skip-member`.
:type: bool :type: bool
""" """
@ -161,6 +161,8 @@ class PythonPythonMapper(PythonMapperBase):
class PythonFunction(PythonPythonMapper): class PythonFunction(PythonPythonMapper):
"""The representation of a function."""
type = "function" type = "function"
is_callable = True is_callable = True
member_order = 40 member_order = 40
@ -191,6 +193,8 @@ class PythonFunction(PythonPythonMapper):
class PythonMethod(PythonFunction): class PythonMethod(PythonFunction):
"""The representation of a method."""
type = "method" type = "method"
is_callable = True is_callable = True
member_order = 50 member_order = 50
@ -255,6 +259,8 @@ class PythonAttribute(PythonData):
class TopLevelPythonPythonMapper(PythonPythonMapper): class TopLevelPythonPythonMapper(PythonPythonMapper):
"""A common base class for modules and packages."""
_RENDER_LOG_LEVEL = "VERBOSE" _RENDER_LOG_LEVEL = "VERBOSE"
def __init__(self, obj, **kwargs): def __init__(self, obj, **kwargs):
@ -297,14 +303,20 @@ class TopLevelPythonPythonMapper(PythonPythonMapper):
class PythonModule(TopLevelPythonPythonMapper): class PythonModule(TopLevelPythonPythonMapper):
"""The representation of a module."""
type = "module" type = "module"
class PythonPackage(TopLevelPythonPythonMapper): class PythonPackage(TopLevelPythonPythonMapper):
"""The representation of a package."""
type = "package" type = "package"
class PythonClass(PythonPythonMapper): class PythonClass(PythonPythonMapper):
"""The representation of a class."""
type = "class" type = "class"
member_order = 30 member_order = 30
@ -390,5 +402,7 @@ class PythonClass(PythonPythonMapper):
class PythonException(PythonClass): class PythonException(PythonClass):
"""The representation of an exception class."""
type = "exception" type = "exception"
member_order = 20 member_order = 20

View File

@ -26,7 +26,7 @@
{% set visible_classes = obj.classes|rejectattr("inherited")|selectattr("display")|list %} {% set visible_classes = obj.classes|rejectattr("inherited")|selectattr("display")|list %}
{% endif %} {% endif %}
{% for klass in visible_classes %} {% for klass in visible_classes %}
{{ klass.rendered|indent(3) }} {{ klass.render()|indent(3) }}
{% endfor %} {% endfor %}
{% if "inherited-members" in autoapi_options %} {% if "inherited-members" in autoapi_options %}
{% set visible_attributes = obj.attributes|selectattr("display")|list %} {% set visible_attributes = obj.attributes|selectattr("display")|list %}
@ -34,7 +34,7 @@
{% set visible_attributes = obj.attributes|rejectattr("inherited")|selectattr("display")|list %} {% set visible_attributes = obj.attributes|rejectattr("inherited")|selectattr("display")|list %}
{% endif %} {% endif %}
{% for attribute in visible_attributes %} {% for attribute in visible_attributes %}
{{ attribute.rendered|indent(3) }} {{ attribute.render()|indent(3) }}
{% endfor %} {% endfor %}
{% if "inherited-members" in autoapi_options %} {% if "inherited-members" in autoapi_options %}
{% set visible_methods = obj.methods|selectattr("display")|list %} {% set visible_methods = obj.methods|selectattr("display")|list %}
@ -42,6 +42,6 @@
{% set visible_methods = obj.methods|rejectattr("inherited")|selectattr("display")|list %} {% set visible_methods = obj.methods|rejectattr("inherited")|selectattr("display")|list %}
{% endif %} {% endif %}
{% for method in visible_methods %} {% for method in visible_methods %}
{{ method.rendered|indent(3) }} {{ method.render()|indent(3) }}
{% endfor %} {% endfor %}
{% endif %} {% endif %}

View File

@ -92,7 +92,7 @@ Functions
{% endblock %} {% endblock %}
{% endif %} {% endif %}
{% for obj_item in visible_children %} {% for obj_item in visible_children %}
{{ obj_item.rendered|indent(0) }} {{ obj_item.render()|indent(0) }}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -27,6 +27,7 @@ autoapi_dirs = ['../autoapi']
autoapi_generate_api_docs = False autoapi_generate_api_docs = False
intersphinx_mapping = { intersphinx_mapping = {
'jinja': ('https://jinja.palletsprojects.com/en/master/', None),
'sphinx': ('https://www.sphinx-doc.org/en/master/', None), 'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
'python': ('https://docs.python.org/3/', None), 'python': ('https://docs.python.org/3/', None),
} }

View File

@ -3,79 +3,79 @@ Configuration Options
.. confval:: autoapi_dirs .. confval:: autoapi_dirs
**Required** **Required**
Paths (relative or absolute) to the source code that you wish to generate your API documentation from. Paths (relative or absolute) to the source code that you wish to generate your API documentation from.
The paths are searched recursively for files matching :confval:`autoapi_file_patterns`. The paths are searched recursively for files matching :confval:`autoapi_file_patterns`.
Relative paths should be relative to the root of the documentation directory Relative paths should be relative to the root of the documentation directory
(ie the directory with the ``conf.py`` file). (ie the directory with the ``conf.py`` file).
For Python, if a package directory is specified, For Python, if a package directory is specified,
the package directory itself will be included in the relative path of the children. the package directory itself will be included in the relative path of the children.
If an ordinary directory is specified, If an ordinary directory is specified,
that directory will not be included in the relative path. that directory will not be included in the relative path.
.. confval:: autoapi_type .. confval:: autoapi_type
Default: ``python`` Default: ``python``
Set the type of files you are documenting. Set the type of files you are documenting.
This depends on the programming language that you are using: This depends on the programming language that you are using:
========== ================ ========== ================
Language ``autoapi_type`` Language ``autoapi_type``
========== ================ ========== ================
Python ``'python'`` Python ``'python'``
Go ``'go'`` Go ``'go'``
Javascript ``'javascript'`` Javascript ``'javascript'``
.NET ``'dotnet'`` .NET ``'dotnet'``
========== ================ ========== ================
.. confval:: autoapi_template_dir .. confval:: autoapi_template_dir
Default: ``''`` Default: ``''``
A directory that has user-defined templates to override our default templates. A directory that has user-defined templates to override our default templates.
The path can either be absolute, The path can either be absolute,
or relative to the root of the documentation directory or relative to the root of the documentation directory
(ie the directory with the ``conf.py`` file). (ie the directory with the ``conf.py`` file).
An path relative to where `sphinx-build` is run An path relative to where `sphinx-build` is run
is allowed for backwards compatibility only is allowed for backwards compatibility only
and will be removed in a future version. and will be removed in a future version.
You can view the default templates in the You can view the default templates in the
`autoapi/templates <https://github.com/readthedocs/sphinx-autoapi/tree/master/autoapi/templates>`_ `autoapi/templates <https://github.com/readthedocs/sphinx-autoapi/tree/master/autoapi/templates>`_
directory of the package. directory of the package.
.. confval:: autoapi_file_patterns .. confval:: autoapi_file_patterns
Default: Varies by Language Default: Varies by Language
A list containing the file patterns to look for when generating documentation. A list containing the file patterns to look for when generating documentation.
Patterns should be listed in order of preference. Patterns should be listed in order of preference.
For example, For example,
if ``autoapi_file_patterns`` is set to the default value if ``autoapi_file_patterns`` is set to the default value
and a `.py` file and a `.pyi` file are found, and a `.py` file and a `.pyi` file are found,
then the `.py` will be read. then the `.py` will be read.
The defaults by language are: The defaults by language are:
========== ============================================ ========== ============================================
Language ``autoapi_file_patterns`` Language ``autoapi_file_patterns``
========== ============================================ ========== ============================================
Python ``['*.py', '*.pyi']`` Python ``['*.py', '*.pyi']``
Go ``['*.go']`` Go ``['*.go']``
Javascript ``['*.js']`` Javascript ``['*.js']``
.NET ``['project.json', '*.csproj', '*.vbproj']`` .NET ``['project.json', '*.csproj', '*.vbproj']``
========== ============================================ ========== ============================================
.. confval:: autoapi_generate_api_docs .. confval:: autoapi_generate_api_docs
Default: ``True`` Default: ``True``
Whether to generate API documentation. Whether to generate API documentation.
If this is ``False``, documentation should be generated though the If this is ``False``, documentation should be generated though the
:doc:`directives`. :doc:`directives`.
Customisation Options Customisation Options
@ -83,119 +83,142 @@ Customisation Options
.. confval:: autoapi_options .. confval:: autoapi_options
Default: [ Default: [
``'members'``, ``'members'``,
``'undoc-members'``, ``'undoc-members'``,
``'private-members'``, ``'private-members'``,
``'show-inheritance'``, ``'show-inheritance'``,
``'show-module-summary'``, ``'show-module-summary'``,
``'special-members'``, ``'special-members'``,
``'imported-members'``, ``'imported-members'``,
] ]
Options for display of the generated documentation. Options for display of the generated documentation.
* ``members``: Display children of an object * ``members``: Display children of an object
* ``inherited-members``: Display children of an object * ``inherited-members``: Display children of an object
that have been inherited from a base class. that have been inherited from a base class.
* ``undoc-members``: Display objects that have no docstring * ``undoc-members``: Display objects that have no docstring
* ``private-members``: Display private objects (eg. ``_foo`` in Python) * ``private-members``: Display private objects (eg. ``_foo`` in Python)
* ``special-members``: Display special objects (eg. ``__foo__`` in Python) * ``special-members``: Display special objects (eg. ``__foo__`` in Python)
* ``show-inheritance``: Display a list of base classes below the class signature. * ``show-inheritance``: Display a list of base classes below the class signature.
* ``show-inheritance-diagram``: Display an inheritance diagram in * ``show-inheritance-diagram``: Display an inheritance diagram in
generated class documentation. generated class documentation.
It makes use of the :mod:`sphinx.ext.inheritance_diagram` extension, It makes use of the :mod:`sphinx.ext.inheritance_diagram` extension,
and requires `Graphviz <https://graphviz.org/>`_ to be installed. and requires `Graphviz <https://graphviz.org/>`_ to be installed.
* ``show-module-summary``: Whether to include autosummary directives * ``show-module-summary``: Whether to include autosummary directives
in generated module documentation. in generated module documentation.
* ``imported-members``: Display objects imported from the same * ``imported-members``: Display objects imported from the same
top level package or module. top level package or module.
The default module template does not include imported objects, The default module template does not include imported objects,
even with this option enabled. even with this option enabled.
The default package template does. The default package template does.
.. confval:: autoapi_ignore .. confval:: autoapi_ignore
Default: Varies By Language Default: Varies By Language
A list of patterns to ignore when finding files. A list of patterns to ignore when finding files.
The defaults by language are: The defaults by language are:
========== ============================================ ========== ============================================
Language ``autoapi_file_patterns`` Language ``autoapi_file_patterns``
========== ============================================ ========== ============================================
Python ``['*migrations*']`` Python ``['*migrations*']``
Go ``[]`` Go ``[]``
Javascript ``[]`` Javascript ``[]``
.NET ``['*toc.yml', '*index.yml']`` .NET ``['*toc.yml', '*index.yml']``
========== ============================================ ========== ============================================
.. confval:: autoapi_root .. confval:: autoapi_root
Default: ``autoapi`` Default: ``autoapi``
Path to output the generated AutoAPI files into, Path to output the generated AutoAPI files into,
including the generated index page. including the generated index page.
This path must be relative to the root of the documentation directory This path must be relative to the root of the documentation directory
(ie the directory with the ``conf.py`` file). (ie the directory with the ``conf.py`` file).
This can be used to place the generated documentation This can be used to place the generated documentation
anywhere in your documentation hierarchy. anywhere in your documentation hierarchy.
.. confval:: autoapi_add_toctree_entry .. confval:: autoapi_add_toctree_entry
Default: ``True`` Default: ``True``
Whether to insert the generated documentation into the TOC tree. Whether to insert the generated documentation into the TOC tree.
If this is ``False``, the default AutoAPI index page is not generated If this is ``False``, the default AutoAPI index page is not generated
and you will need to include the generated documentation and you will need to include the generated documentation
in a TOC tree entry yourself. in a TOC tree entry yourself.
.. confval:: autoapi_python_class_content .. confval:: autoapi_python_class_content
Default: ``class`` Default: ``class``
Which docstring to insert into the content of a class. Which docstring to insert into the content of a class.
* ``class``: Use only the class docstring. * ``class``: Use only the class docstring.
* ``both``: Use the concatentation of the class docstring and the * ``both``: Use the concatentation of the class docstring and the
``__init__`` docstring. ``__init__`` docstring.
* ``init``: Use only the ``__init__`` docstring. * ``init``: Use only the ``__init__`` docstring.
If the class does not have an ``__init__`` or the ``__init__`` If the class does not have an ``__init__`` or the ``__init__``
docstring is empty and the class defines a ``__new__`` with a docstring, docstring is empty and the class defines a ``__new__`` with a docstring,
the ``__new__`` docstring is used instead of the ``__init__`` docstring. the ``__new__`` docstring is used instead of the ``__init__`` docstring.
.. confval:: autoapi_member_order .. confval:: autoapi_member_order
Default: ``bysource`` Default: ``bysource``
The order to document members. The order to document members. This option can have the following values:
* ``alphabetical``: Order members by their name, case sensitively.
* ``bysource``: Order members by the order that they were defined in the source code.
* ``groupwise``: Order members by their type then alphabetically,
ordering the types as follows:
* Submodules and subpackages
* Attributes
* Exceptions
* Classes
* Functions
* Methods
* ``alphabetical``: Order members by their name, case sensitively.
* ``bysource``: Order members by the order that they were defined in the source code.
* ``groupwise``: Order members by their type then alphabetically, in the order:
* Submodules and subpackages
* Attributes
* Exceptions
* Classes
* Functions
* Methods
.. confval:: autoapi_python_use_implicit_namespaces .. confval:: autoapi_python_use_implicit_namespaces
Default: ``False`` Default: ``False``
This changes the package detection behaviour to be compatible with :pep:`420`, This changes the package detection behaviour to be compatible with :pep:`420`,
but directories in :confval:`autoapi_dirs` but directories in :confval:`autoapi_dirs`
are no longer searched recursively for packages. are no longer searched recursively for packages.
Instead, when this is ``True``, Instead, when this is ``True``,
:confval:`autoapi_dirs` should point directly to :confval:`autoapi_dirs` should point directly to
the directories of implicit namespaces the directories of implicit namespaces
and the directories of packages. and the directories of packages.
If searching is still required, If searching is still required,
this should be done manually in the ``conf.py``. this should be done manually in the ``conf.py``.
.. confval:: autoapi_prepare_jinja_env
Default: ``None``
A callback that is called shortly after the Jinja environment is created.
It passed the Jinja environment for editing before template rendering begins.
The callback should have the following signature:
.. py:function:: prepare_jinja_env(jinja_env: jinja2.Environment) -> None
:noindex:
Events Events
@ -205,28 +228,28 @@ The following events allow you to control the behaviour of AutoAPI.
.. event:: autoapi-skip-member (app, what, name, obj, skip, options) .. event:: autoapi-skip-member (app, what, name, obj, skip, options)
(Python only) (Python only)
Emitted when a template has to decide whether a member should be included Emitted when a template has to decide whether a member should be included
in the documentation. in the documentation.
Usually the member is skipped if a handler returns ``True``, Usually the member is skipped if a handler returns ``True``,
and included otherwise. and included otherwise.
Handlers should return ``None`` to fall back to the default skipping Handlers should return ``None`` to fall back to the default skipping
behaviour of AutoAPI or another attached handler. behaviour of AutoAPI or another attached handler.
:param app: The Sphinx application object. :param app: The Sphinx application object.
:param what: The type of the object which the docstring belongs to. :param what: The type of the object which the docstring belongs to.
This can be one of: This can be one of:
``"attribute"``, ``"class"``, ``"data"``, ``"exception"``, ``"attribute"``, ``"class"``, ``"data"``, ``"exception"``,
``"function"``, ``"method"``, ``"module"``, ``"package"``. ``"function"``, ``"method"``, ``"module"``, ``"package"``.
:type what: str :type what: str
:param name: The fully qualified name of the object. :param name: The fully qualified name of the object.
:type name: str :type name: str
:param obj: The object itself. :param obj: The object itself.
:type obj: PythonPythonMapper :type obj: PythonPythonMapper
:param skip: Whether AutoAPI will skip this member if the handler :param skip: Whether AutoAPI will skip this member if the handler
does not override the decision. does not override the decision.
:type skip: bool :type skip: bool
:param options: The options given to the directive. :param options: The options given to the directive.
Advanced Options Advanced Options
@ -234,11 +257,11 @@ Advanced Options
.. confval:: autoapi_keep_files .. confval:: autoapi_keep_files
Default: ``False`` Default: ``False``
Keep the AutoAPI generated files on the filesystem after the run. Keep the AutoAPI generated files on the filesystem after the run.
Useful for debugging or transitioning to manual documentation. Useful for debugging or transitioning to manual documentation.
Keeping files will also allow AutoAPI to use incremental builds. Keeping files will also allow AutoAPI to use incremental builds.
Providing none of the source files have changed, Providing none of the source files have changed,
AutoAPI will skip parsing the source code and regenerating the API documentation. AutoAPI will skip parsing the source code and regenerating the API documentation.

View File

@ -22,6 +22,22 @@ We provide :samp:`base/base.rst` as an incredibly basic output of every object::
.. {language}:{type}:: {name} .. {language}:{type}:: {name}
Custom Filters, Tests, and Globals
----------------------------------
The :confval:`autoapi_prepare_jinja_env` configuration option allows you
to pass a callback that can edit the :class:`jinja2.Environment` object
before rendering begins.
This callback, among other things, can be used to add custom filters,
tests, and/or globals to the Jinja environment. For example:
.. code-block:: python
def autoapi_prepare_jinja_env(jinja_env):
jinja_env.filters["my_custom_filter"] = lambda value: value.upper()
Context Context
------- -------
@ -30,10 +46,13 @@ This contains:
* ``autoapi_options``: The value of the :confval:`autoapi_options` * ``autoapi_options``: The value of the :confval:`autoapi_options`
configuration option. configuration option.
* ``include_summaries``: The value of the :confval:`autoapi_include_summaries`
configuration option.
* ``obj``: A Python object derived from :class:`PythonMapperBase`. * ``obj``: A Python object derived from :class:`PythonMapperBase`.
* ``sphinx_version``: The contents of :attr:`sphinx.version_info`. * ``sphinx_version``: The contents of :attr:`sphinx.version_info`.
This object has a number of standard attributes you can reliably access per language. The object in ``obj`` has a number of standard attributes
that you can reliably access per language.
.. warning:: .. warning::

View File

@ -753,3 +753,24 @@ class TestImplicitNamespacePackage(object):
example_file = example_handle.read() example_file = example_handle.read()
assert "namespace.example.second_sub_method" in example_file assert "namespace.example.second_sub_method" in example_file
def test_custom_jinja_filters(builder):
confoverrides = {
"autoapi_prepare_jinja_env": (
lambda jinja_env: jinja_env.filters.update(
{
"prepare_docstring": (
lambda docstring: "This is using custom filters."
)
}
)
),
}
builder("pyexample", confoverrides=confoverrides)
example_path = "_build/text/autoapi/example/index.txt"
with io.open(example_path, encoding="utf8") as example_handle:
example_file = example_handle.read()
assert "This is using custom filters." in example_file