From 8e957490d84b1f84bad0ec6f67d9370d38c408c0 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 23 May 2024 16:02:41 -0400 Subject: [PATCH 1/2] Promote mpltype Sphinx role to a public extension When projects derive from our types, but don't override all the docstrings, they may want to use these extensions so that their docs build. --- .../next_api_changes/development/28289-ES.rst | 14 ++++++++++ doc/conf.py | 2 +- lib/matplotlib/sphinxext/meson.build | 1 + .../matplotlib/sphinxext/roles.py | 28 ++++++++++--------- pyproject.toml | 4 +-- 5 files changed, 33 insertions(+), 16 deletions(-) create mode 100644 doc/api/next_api_changes/development/28289-ES.rst rename doc/sphinxext/custom_roles.py => lib/matplotlib/sphinxext/roles.py (72%) diff --git a/doc/api/next_api_changes/development/28289-ES.rst b/doc/api/next_api_changes/development/28289-ES.rst new file mode 100644 index 000000000000..e68e5ea81203 --- /dev/null +++ b/doc/api/next_api_changes/development/28289-ES.rst @@ -0,0 +1,14 @@ +Documentation-specific custom Sphinx roles are now semi-public +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For third-party packages that derive types from Matplotlib, our use of custom roles may +prevent Sphinx from building their docs. These custom Sphinx roles are now public solely +for the purposes of use within projects that derive from Matplotlib types, and may be +added to Sphinx via ``conf.py``:: + + extensions = [ + 'matplotlib.sphinxext.roles', + # Other extensions. + ] + +Any other use of these roles is not supported. diff --git a/doc/conf.py b/doc/conf.py index c9a475aecf9c..ff42246526b6 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -116,9 +116,9 @@ def _parse_skip_subdirs_file(): 'sphinx_gallery.gen_gallery', 'matplotlib.sphinxext.mathmpl', 'matplotlib.sphinxext.plot_directive', + 'matplotlib.sphinxext.roles', 'matplotlib.sphinxext.figmpl_directive', 'sphinxcontrib.inkscapeconverter', - 'sphinxext.custom_roles', 'sphinxext.github', 'sphinxext.math_symbol_table', 'sphinxext.missing_references', diff --git a/lib/matplotlib/sphinxext/meson.build b/lib/matplotlib/sphinxext/meson.build index 5dc7388384eb..35bb96fecbe1 100644 --- a/lib/matplotlib/sphinxext/meson.build +++ b/lib/matplotlib/sphinxext/meson.build @@ -3,6 +3,7 @@ python_sources = [ 'figmpl_directive.py', 'mathmpl.py', 'plot_directive.py', + 'roles.py', ] typing_sources = [ diff --git a/doc/sphinxext/custom_roles.py b/lib/matplotlib/sphinxext/roles.py similarity index 72% rename from doc/sphinxext/custom_roles.py rename to lib/matplotlib/sphinxext/roles.py index d76c92709865..3f6afd9812ef 100644 --- a/doc/sphinxext/custom_roles.py +++ b/lib/matplotlib/sphinxext/roles.py @@ -2,10 +2,11 @@ from docutils import nodes +import matplotlib from matplotlib import rcParamsDefault -class QueryReference(nodes.Inline, nodes.TextElement): +class _QueryReference(nodes.Inline, nodes.TextElement): """ Wraps a reference or pending reference to add a query string. @@ -19,7 +20,7 @@ def to_query_string(self): return '&'.join(f'{name}={value}' for name, value in self.attlist()) -def visit_query_reference_node(self, node): +def _visit_query_reference_node(self, node): """ Resolve *node* into query strings on its ``reference`` children. @@ -33,14 +34,14 @@ def visit_query_reference_node(self, node): self.visit_literal(node) -def depart_query_reference_node(self, node): +def _depart_query_reference_node(self, node): """ Act as if this is a `~docutils.nodes.literal`. """ self.depart_literal(node) -def rcparam_role(name, rawtext, text, lineno, inliner, options={}, content=[]): +def _rcparam_role(name, rawtext, text, lineno, inliner, options=None, content=None): # Generate a pending cross-reference so that Sphinx will ensure this link # isn't broken at some point in the future. title = f'rcParams["{text}"]' @@ -48,7 +49,7 @@ def rcparam_role(name, rawtext, text, lineno, inliner, options={}, content=[]): ref_nodes, messages = inliner.interpreted(title, f'{title} <{target}>', 'ref', lineno) - qr = QueryReference(rawtext, highlight=text) + qr = _QueryReference(rawtext, highlight=text) qr += ref_nodes node_list = [qr] @@ -64,7 +65,7 @@ def rcparam_role(name, rawtext, text, lineno, inliner, options={}, content=[]): return node_list, messages -def mpltype_role(name, rawtext, text, lineno, inliner, options={}, content=[]): +def _mpltype_role(name, rawtext, text, lineno, inliner, options=None, content=None): mpltype = text type_to_link_target = { 'color': 'colors_def', @@ -78,12 +79,13 @@ def mpltype_role(name, rawtext, text, lineno, inliner, options={}, content=[]): def setup(app): - app.add_role("rc", rcparam_role) - app.add_role("mpltype", mpltype_role) + app.add_role("rc", _rcparam_role) + app.add_role("mpltype", _mpltype_role) app.add_node( - QueryReference, - html=(visit_query_reference_node, depart_query_reference_node), - latex=(visit_query_reference_node, depart_query_reference_node), - text=(visit_query_reference_node, depart_query_reference_node), + _QueryReference, + html=(_visit_query_reference_node, _depart_query_reference_node), + latex=(_visit_query_reference_node, _depart_query_reference_node), + text=(_visit_query_reference_node, _depart_query_reference_node), ) - return {"parallel_read_safe": True, "parallel_write_safe": True} + return {"version": matplotlib.__version__, + "parallel_read_safe": True, "parallel_write_safe": True} diff --git a/pyproject.toml b/pyproject.toml index a9fb7df68450..52bbe308c0f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -283,11 +283,11 @@ ignore_directives = [ "include" ] ignore_roles = [ - # sphinxext.custom_roles - "rc", # matplotlib.sphinxext.mathmpl "mathmpl", "math-stix", + # matplotlib.sphinxext.roles + "rc", # sphinxext.github "ghissue", "ghpull", From 3fe382dd89313be3feeb7620cde64c2c0dbe0ba4 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sun, 2 Jun 2024 01:00:03 +0200 Subject: [PATCH 2/2] Document sphinxext.roles --- doc/api/index.rst | 1 + .../next_api_changes/development/28289-ES.rst | 11 +--- doc/api/sphinxext_roles.rst | 7 +++ lib/matplotlib/sphinxext/roles.py | 56 +++++++++++++++++++ 4 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 doc/api/sphinxext_roles.rst diff --git a/doc/api/index.rst b/doc/api/index.rst index e55a0ed3c5b2..70c3b5343e7a 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -126,6 +126,7 @@ Alphabetical list of modules: sphinxext_mathmpl_api.rst sphinxext_plot_directive_api.rst sphinxext_figmpl_directive_api.rst + sphinxext_roles.rst spines_api.rst style_api.rst table_api.rst diff --git a/doc/api/next_api_changes/development/28289-ES.rst b/doc/api/next_api_changes/development/28289-ES.rst index e68e5ea81203..f891c63a64bf 100644 --- a/doc/api/next_api_changes/development/28289-ES.rst +++ b/doc/api/next_api_changes/development/28289-ES.rst @@ -3,12 +3,5 @@ Documentation-specific custom Sphinx roles are now semi-public For third-party packages that derive types from Matplotlib, our use of custom roles may prevent Sphinx from building their docs. These custom Sphinx roles are now public solely -for the purposes of use within projects that derive from Matplotlib types, and may be -added to Sphinx via ``conf.py``:: - - extensions = [ - 'matplotlib.sphinxext.roles', - # Other extensions. - ] - -Any other use of these roles is not supported. +for the purposes of use within projects that derive from Matplotlib types. See +:mod:`matplotlib.sphinxext.roles` for details. diff --git a/doc/api/sphinxext_roles.rst b/doc/api/sphinxext_roles.rst new file mode 100644 index 000000000000..99959ff05d14 --- /dev/null +++ b/doc/api/sphinxext_roles.rst @@ -0,0 +1,7 @@ +============================== +``matplotlib.sphinxext.roles`` +============================== + +.. automodule:: matplotlib.sphinxext.roles + :no-undoc-members: + :private-members: _rcparam_role, _mpltype_role diff --git a/lib/matplotlib/sphinxext/roles.py b/lib/matplotlib/sphinxext/roles.py index 3f6afd9812ef..301adcd8a5f5 100644 --- a/lib/matplotlib/sphinxext/roles.py +++ b/lib/matplotlib/sphinxext/roles.py @@ -1,3 +1,40 @@ +""" +Custom roles for the Matplotlib documentation. + +.. warning:: + + These roles are considered semi-public. They are only intended to be used in + the Matplotlib documentation. + +However, it can happen that downstream packages end up pulling these roles into +their documentation, which will result in documentation build errors. The following +describes the exact mechanism and how to fix the errors. + +There are two ways, Matplotlib docstrings can end up in downstream documentation. +You have to subclass a Matplotlib class and either use the ``:inherited-members:`` +option in your autodoc configuration, or you have to override a method without +specifying a new docstring; the new method will inherit the original docstring and +still render in your autodoc. If the docstring contains one of the custom sphinx +roles, you'll see one of the following error messages: + +.. code-block:: none + + Unknown interpreted text role "mpltype". + Unknown interpreted text role "rc". + +To fix this, you can add this module as extension to your sphinx :file:`conf.py`:: + + extensions = [ + 'matplotlib.sphinxext.roles', + # Other extensions. + ] + +.. warning:: + + Direct use of these roles in other packages is not officially supported. We + reserve the right to modify or remove these roles without prior notification. +""" + from urllib.parse import urlsplit, urlunsplit from docutils import nodes @@ -42,6 +79,13 @@ def _depart_query_reference_node(self, node): def _rcparam_role(name, rawtext, text, lineno, inliner, options=None, content=None): + """ + Sphinx role ``:rc:`` to highlight and link ``rcParams`` entries. + + Usage: Give the desired ``rcParams`` key as parameter. + + :code:`:rc:`figure.dpi`` will render as: :rc:`figure.dpi` + """ # Generate a pending cross-reference so that Sphinx will ensure this link # isn't broken at some point in the future. title = f'rcParams["{text}"]' @@ -66,6 +110,18 @@ def _rcparam_role(name, rawtext, text, lineno, inliner, options=None, content=No def _mpltype_role(name, rawtext, text, lineno, inliner, options=None, content=None): + """ + Sphinx role ``:mpltype:`` for custom matplotlib types. + + In Matplotlib, there are a number of type-like concepts that do not have a + direct type representation; example: color. This role allows to properly + highlight them in the docs and link to their definition. + + Currently supported values: + + - :code:`:mpltype:`color`` will render as: :mpltype:`color` + + """ mpltype = text type_to_link_target = { 'color': 'colors_def',