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.
Providing none of the source files have changed,
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
^^^^^^^^^

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_class_content", "class", "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.AutoapiPropertyDocumenter)
app.add_autodocumenter(documenters.AutoapiDecoratorDocumenter)

View File

@ -193,6 +193,8 @@ class SphinxMapperBase(object):
return "\n".join(prepare_docstring(value))
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

View File

@ -107,7 +107,7 @@ class PythonPythonMapper(PythonMapperBase):
"""Whether this object should be displayed in documentation.
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
"""
@ -161,6 +161,8 @@ class PythonPythonMapper(PythonMapperBase):
class PythonFunction(PythonPythonMapper):
"""The representation of a function."""
type = "function"
is_callable = True
member_order = 40
@ -191,6 +193,8 @@ class PythonFunction(PythonPythonMapper):
class PythonMethod(PythonFunction):
"""The representation of a method."""
type = "method"
is_callable = True
member_order = 50
@ -255,6 +259,8 @@ class PythonAttribute(PythonData):
class TopLevelPythonPythonMapper(PythonPythonMapper):
"""A common base class for modules and packages."""
_RENDER_LOG_LEVEL = "VERBOSE"
def __init__(self, obj, **kwargs):
@ -297,14 +303,20 @@ class TopLevelPythonPythonMapper(PythonPythonMapper):
class PythonModule(TopLevelPythonPythonMapper):
"""The representation of a module."""
type = "module"
class PythonPackage(TopLevelPythonPythonMapper):
"""The representation of a package."""
type = "package"
class PythonClass(PythonPythonMapper):
"""The representation of a class."""
type = "class"
member_order = 30
@ -390,5 +402,7 @@ class PythonClass(PythonPythonMapper):
class PythonException(PythonClass):
"""The representation of an exception class."""
type = "exception"
member_order = 20

View File

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

View File

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

View File

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

View File

@ -170,18 +170,28 @@ Customisation Options
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, in the order:
* ``groupwise``: Order members by their type then alphabetically,
ordering the types as follows:
* Submodules and subpackages
* Attributes
* Exceptions
* Classes
* Functions
* Methods
.. confval:: autoapi_python_use_implicit_namespaces
Default: ``False``
@ -198,6 +208,19 @@ Customisation Options
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
~~~~~~

View File

@ -22,6 +22,22 @@ We provide :samp:`base/base.rst` as an incredibly basic output of every object::
.. {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
-------
@ -30,10 +46,13 @@ This contains:
* ``autoapi_options``: The value of the :confval:`autoapi_options`
configuration option.
* ``include_summaries``: The value of the :confval:`autoapi_include_summaries`
configuration option.
* ``obj``: A Python object derived from :class:`PythonMapperBase`.
* ``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::

View File

@ -753,3 +753,24 @@ class TestImplicitNamespacePackage(object):
example_file = example_handle.read()
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