diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 61279a1..eaaa906 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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 `: + 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 ^^^^^^^^^ diff --git a/autoapi/extension.py b/autoapi/extension.py index 3782a4d..0a89f72 100644 --- a/autoapi/extension.py +++ b/autoapi/extension.py @@ -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) diff --git a/autoapi/mappers/base.py b/autoapi/mappers/base.py index 5c7dcb7..c2a15c0 100644 --- a/autoapi/mappers/base.py +++ b/autoapi/mappers/base.py @@ -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 diff --git a/autoapi/mappers/python/objects.py b/autoapi/mappers/python/objects.py index 95030bc..7150d09 100644 --- a/autoapi/mappers/python/objects.py +++ b/autoapi/mappers/python/objects.py @@ -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 diff --git a/autoapi/templates/python/class.rst b/autoapi/templates/python/class.rst index 8c8fe11..7efc34f 100644 --- a/autoapi/templates/python/class.rst +++ b/autoapi/templates/python/class.rst @@ -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 %} diff --git a/autoapi/templates/python/module.rst b/autoapi/templates/python/module.rst index 296afe6..2137ac4 100644 --- a/autoapi/templates/python/module.rst +++ b/autoapi/templates/python/module.rst @@ -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 %} diff --git a/docs/conf.py b/docs/conf.py index fab5df5..b0e7403 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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), } diff --git a/docs/reference/config.rst b/docs/reference/config.rst index 77a10e0..2ada243 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -3,79 +3,79 @@ Configuration Options .. confval:: autoapi_dirs - **Required** + **Required** - 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`. - Relative paths should be relative to the root of the documentation directory - (ie the directory with the ``conf.py`` file). + 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`. + Relative paths should be relative to the root of the documentation directory + (ie the directory with the ``conf.py`` file). - For Python, if a package directory is specified, - the package directory itself will be included in the relative path of the children. - If an ordinary directory is specified, - that directory will not be included in the relative path. + For Python, if a package directory is specified, + the package directory itself will be included in the relative path of the children. + If an ordinary directory is specified, + that directory will not be included in the relative path. .. confval:: autoapi_type - Default: ``python`` + Default: ``python`` - Set the type of files you are documenting. - This depends on the programming language that you are using: + Set the type of files you are documenting. + This depends on the programming language that you are using: - ========== ================ - Language ``autoapi_type`` - ========== ================ - Python ``'python'`` - Go ``'go'`` - Javascript ``'javascript'`` - .NET ``'dotnet'`` - ========== ================ + ========== ================ + Language ``autoapi_type`` + ========== ================ + Python ``'python'`` + Go ``'go'`` + Javascript ``'javascript'`` + .NET ``'dotnet'`` + ========== ================ .. confval:: autoapi_template_dir - Default: ``''`` + Default: ``''`` - A directory that has user-defined templates to override our default templates. - The path can either be absolute, - or relative to the root of the documentation directory - (ie the directory with the ``conf.py`` file). - An path relative to where `sphinx-build` is run - is allowed for backwards compatibility only - and will be removed in a future version. + A directory that has user-defined templates to override our default templates. + The path can either be absolute, + or relative to the root of the documentation directory + (ie the directory with the ``conf.py`` file). + An path relative to where `sphinx-build` is run + is allowed for backwards compatibility only + and will be removed in a future version. - You can view the default templates in the - `autoapi/templates `_ - directory of the package. + You can view the default templates in the + `autoapi/templates `_ + directory of the package. .. confval:: autoapi_file_patterns - Default: Varies by Language + Default: Varies by Language - A list containing the file patterns to look for when generating documentation. - Patterns should be listed in order of preference. - For example, - if ``autoapi_file_patterns`` is set to the default value - and a `.py` file and a `.pyi` file are found, - then the `.py` will be read. + A list containing the file patterns to look for when generating documentation. + Patterns should be listed in order of preference. + For example, + if ``autoapi_file_patterns`` is set to the default value + and a `.py` file and a `.pyi` file are found, + then the `.py` will be read. - The defaults by language are: + The defaults by language are: - ========== ============================================ - Language ``autoapi_file_patterns`` - ========== ============================================ - Python ``['*.py', '*.pyi']`` - Go ``['*.go']`` - Javascript ``['*.js']`` - .NET ``['project.json', '*.csproj', '*.vbproj']`` - ========== ============================================ + ========== ============================================ + Language ``autoapi_file_patterns`` + ========== ============================================ + Python ``['*.py', '*.pyi']`` + Go ``['*.go']`` + Javascript ``['*.js']`` + .NET ``['project.json', '*.csproj', '*.vbproj']`` + ========== ============================================ .. confval:: autoapi_generate_api_docs - Default: ``True`` + Default: ``True`` - Whether to generate API documentation. - If this is ``False``, documentation should be generated though the - :doc:`directives`. + Whether to generate API documentation. + If this is ``False``, documentation should be generated though the + :doc:`directives`. Customisation Options @@ -83,119 +83,142 @@ Customisation Options .. confval:: autoapi_options - Default: [ - ``'members'``, - ``'undoc-members'``, - ``'private-members'``, - ``'show-inheritance'``, - ``'show-module-summary'``, - ``'special-members'``, - ``'imported-members'``, - ] + Default: [ + ``'members'``, + ``'undoc-members'``, + ``'private-members'``, + ``'show-inheritance'``, + ``'show-module-summary'``, + ``'special-members'``, + ``'imported-members'``, + ] - Options for display of the generated documentation. + Options for display of the generated documentation. - * ``members``: Display children of an object - * ``inherited-members``: Display children of an object - that have been inherited from a base class. - * ``undoc-members``: Display objects that have no docstring - * ``private-members``: Display private 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-diagram``: Display an inheritance diagram in - generated class documentation. - It makes use of the :mod:`sphinx.ext.inheritance_diagram` extension, - and requires `Graphviz `_ to be installed. - * ``show-module-summary``: Whether to include autosummary directives - in generated module documentation. - * ``imported-members``: Display objects imported from the same - top level package or module. - The default module template does not include imported objects, - even with this option enabled. - The default package template does. + * ``members``: Display children of an object + * ``inherited-members``: Display children of an object + that have been inherited from a base class. + * ``undoc-members``: Display objects that have no docstring + * ``private-members``: Display private 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-diagram``: Display an inheritance diagram in + generated class documentation. + It makes use of the :mod:`sphinx.ext.inheritance_diagram` extension, + and requires `Graphviz `_ to be installed. + * ``show-module-summary``: Whether to include autosummary directives + in generated module documentation. + * ``imported-members``: Display objects imported from the same + top level package or module. + The default module template does not include imported objects, + even with this option enabled. + The default package template does. .. confval:: autoapi_ignore - Default: Varies By Language + Default: Varies By Language - A list of patterns to ignore when finding files. - The defaults by language are: + A list of patterns to ignore when finding files. + The defaults by language are: - ========== ============================================ - Language ``autoapi_file_patterns`` - ========== ============================================ - Python ``['*migrations*']`` - Go ``[]`` - Javascript ``[]`` - .NET ``['*toc.yml', '*index.yml']`` - ========== ============================================ + ========== ============================================ + Language ``autoapi_file_patterns`` + ========== ============================================ + Python ``['*migrations*']`` + Go ``[]`` + Javascript ``[]`` + .NET ``['*toc.yml', '*index.yml']`` + ========== ============================================ .. confval:: autoapi_root - Default: ``autoapi`` + Default: ``autoapi`` - Path to output the generated AutoAPI files into, - including the generated index page. - This path must be relative to the root of the documentation directory - (ie the directory with the ``conf.py`` file). - This can be used to place the generated documentation - anywhere in your documentation hierarchy. + Path to output the generated AutoAPI files into, + including the generated index page. + This path must be relative to the root of the documentation directory + (ie the directory with the ``conf.py`` file). + This can be used to place the generated documentation + anywhere in your documentation hierarchy. .. confval:: autoapi_add_toctree_entry - Default: ``True`` + Default: ``True`` - Whether to insert the generated documentation into the TOC tree. - If this is ``False``, the default AutoAPI index page is not generated - and you will need to include the generated documentation - in a TOC tree entry yourself. + Whether to insert the generated documentation into the TOC tree. + If this is ``False``, the default AutoAPI index page is not generated + and you will need to include the generated documentation + in a TOC tree entry yourself. .. 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. - * ``both``: Use the concatentation of the class docstring and the - ``__init__`` docstring. - * ``init``: Use only the ``__init__`` docstring. + * ``class``: Use only the class docstring. + * ``both``: Use the concatentation of the class docstring and the + ``__init__`` docstring. + * ``init``: Use only the ``__init__`` docstring. - If the class does not have an ``__init__`` or the ``__init__`` - docstring is empty and the class defines a ``__new__`` with a docstring, - the ``__new__`` docstring is used instead of the ``__init__`` docstring. + If the class does not have an ``__init__`` or the ``__init__`` + docstring is empty and the class defines a ``__new__`` with a docstring, + the ``__new__`` docstring is used instead of the ``__init__`` docstring. .. 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 - Default: ``False`` + Default: ``False`` - This changes the package detection behaviour to be compatible with :pep:`420`, - but directories in :confval:`autoapi_dirs` - are no longer searched recursively for packages. - Instead, when this is ``True``, - :confval:`autoapi_dirs` should point directly to - the directories of implicit namespaces - and the directories of packages. + This changes the package detection behaviour to be compatible with :pep:`420`, + but directories in :confval:`autoapi_dirs` + are no longer searched recursively for packages. + Instead, when this is ``True``, + :confval:`autoapi_dirs` should point directly to + the directories of implicit namespaces + and the directories of packages. - If searching is still required, - this should be done manually in the ``conf.py``. + If searching is still required, + 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 @@ -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) - (Python only) - Emitted when a template has to decide whether a member should be included - in the documentation. - Usually the member is skipped if a handler returns ``True``, - and included otherwise. - Handlers should return ``None`` to fall back to the default skipping - behaviour of AutoAPI or another attached handler. + (Python only) + Emitted when a template has to decide whether a member should be included + in the documentation. + Usually the member is skipped if a handler returns ``True``, + and included otherwise. + Handlers should return ``None`` to fall back to the default skipping + behaviour of AutoAPI or another attached handler. - :param app: The Sphinx application object. - :param what: The type of the object which the docstring belongs to. - This can be one of: - ``"attribute"``, ``"class"``, ``"data"``, ``"exception"``, - ``"function"``, ``"method"``, ``"module"``, ``"package"``. - :type what: str - :param name: The fully qualified name of the object. - :type name: str - :param obj: The object itself. - :type obj: PythonPythonMapper - :param skip: Whether AutoAPI will skip this member if the handler - does not override the decision. - :type skip: bool - :param options: The options given to the directive. + :param app: The Sphinx application object. + :param what: The type of the object which the docstring belongs to. + This can be one of: + ``"attribute"``, ``"class"``, ``"data"``, ``"exception"``, + ``"function"``, ``"method"``, ``"module"``, ``"package"``. + :type what: str + :param name: The fully qualified name of the object. + :type name: str + :param obj: The object itself. + :type obj: PythonPythonMapper + :param skip: Whether AutoAPI will skip this member if the handler + does not override the decision. + :type skip: bool + :param options: The options given to the directive. Advanced Options @@ -234,11 +257,11 @@ Advanced Options .. confval:: autoapi_keep_files - Default: ``False`` + Default: ``False`` - Keep the AutoAPI generated files on the filesystem after the run. - Useful for debugging or transitioning to manual documentation. + Keep the AutoAPI generated files on the filesystem after the run. + Useful for debugging or transitioning to manual documentation. - Keeping files will also allow AutoAPI to use incremental builds. - Providing none of the source files have changed, - AutoAPI will skip parsing the source code and regenerating the API documentation. + Keeping files will also allow AutoAPI to use incremental builds. + Providing none of the source files have changed, + AutoAPI will skip parsing the source code and regenerating the API documentation. diff --git a/docs/reference/templates.rst b/docs/reference/templates.rst index 5be55df..5439bed 100644 --- a/docs/reference/templates.rst +++ b/docs/reference/templates.rst @@ -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:: diff --git a/tests/python/test_pyintegration.py b/tests/python/test_pyintegration.py index aa10fb3..82c7f97 100644 --- a/tests/python/test_pyintegration.py +++ b/tests/python/test_pyintegration.py @@ -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