From 3bcb006d88cf69c10a8633f60e4a8e48191c4a4b Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 25 Feb 2021 21:06:20 +0100 Subject: [PATCH] Backport PR #19456: Doc implement reredirects --- doc/api/backend_gtk3_api.rst | 4 ++ doc/api/backend_qt_api.rst | 5 ++ doc/api/backend_wx_api.rst | 2 + doc/conf.py | 1 + doc/devel/documenting_mpl.rst | 34 +++++++++++ doc/sphinxext/redirect_from.py | 87 +++++++++++++++++++++++++++ tutorials/introductory/customizing.py | 2 + 7 files changed, 135 insertions(+) create mode 100644 doc/sphinxext/redirect_from.py diff --git a/doc/api/backend_gtk3_api.rst b/doc/api/backend_gtk3_api.rst index e53794e3d285..5e17df66d602 100644 --- a/doc/api/backend_gtk3_api.rst +++ b/doc/api/backend_gtk3_api.rst @@ -1,6 +1,10 @@ **NOTE** These backends are not documented here, to avoid adding a dependency to building the docs. +.. redirect-from:: /api/backend_gtk3agg_api +.. redirect-from:: /api/backend_gtk3cairo_api + + :mod:`matplotlib.backends.backend_gtk3agg` ========================================== diff --git a/doc/api/backend_qt_api.rst b/doc/api/backend_qt_api.rst index 33c5ead63bb7..90fe9bb95539 100644 --- a/doc/api/backend_qt_api.rst +++ b/doc/api/backend_qt_api.rst @@ -1,6 +1,11 @@ **NOTE** These backends are not documented here, to avoid adding a dependency to building the docs. +.. redirect-from:: /api/backend_qt4agg_api +.. redirect-from:: /api/backend_qt4cairo_api +.. redirect-from:: /api/backend_qt5agg_api +.. redirect-from:: /api/backend_qt5cairo_api + :mod:`matplotlib.backends.backend_qt4agg` ========================================= diff --git a/doc/api/backend_wx_api.rst b/doc/api/backend_wx_api.rst index e45358c9c558..3ae3bc502e69 100644 --- a/doc/api/backend_wx_api.rst +++ b/doc/api/backend_wx_api.rst @@ -1,6 +1,8 @@ **NOTE** These backends are not documented here, to avoid adding a dependency to building the docs. +.. redirect-from:: /api/backend_wxagg_api + :mod:`matplotlib.backends.backend_wxagg` ======================================== diff --git a/doc/conf.py b/doc/conf.py index 629657b8c60b..eba9fcb35c92 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -63,6 +63,7 @@ 'sphinxext.missing_references', 'sphinxext.mock_gui_toolkits', 'sphinxext.skip_deprecated', + 'sphinxext.redirect_from', 'sphinx_copybutton', ] diff --git a/doc/devel/documenting_mpl.rst b/doc/devel/documenting_mpl.rst index 0bd0816a631a..01985f700a4e 100644 --- a/doc/devel/documenting_mpl.rst +++ b/doc/devel/documenting_mpl.rst @@ -889,6 +889,40 @@ should ideally be named similar to :file:`imshow_mynewexample.py`. Miscellaneous ============= +Moving documentation +-------------------- + +Sometimes it is desirable to move or consolidate documentation. With no +action this will lead to links either going dead (404) or pointing to old +versions of the documentation. Preferable is to replace the old page +with an html refresh that immediately redirects the viewer to the new +page. So, for example we move ``/doc/topic/old_info.rst`` to +``/doc/topic/new_info.rst``. We remove ``/doc/topic/old_info.rst`` and +in ``/doc/topic/new_info.rst`` we insert a ``redirect-from`` directive that +tells sphinx to still make the old file with the html refresh/redirect in it +(probably near the top of the file to make it noticeable) + +.. code-block:: rst + + .. redirect-from:: /topic/old_info + +In the built docs this will yield an html file +``/build/html/topic/old_info.html`` that has a refresh to ``new_info.html``. +If the two files are in different subdirectories: + +.. code-block:: rst + + .. redirect-from:: /old_topic/old_info2 + +will yield an html file ``/build/html/old_topic/old_info2.html`` that has a +(relative) refresh to ``../topic/new_info.html``. + +Use the full path for this directive, relative to the doc root at +``http://matplotlib.org/stable/``. So ``/old_topic/old_info2`` would be +found by users at ``http://matplotlib.org/stable/old_topic/old_info2``. +For clarity, do not use relative links. + + Adding animations ----------------- diff --git a/doc/sphinxext/redirect_from.py b/doc/sphinxext/redirect_from.py new file mode 100644 index 000000000000..d8aa487b03d0 --- /dev/null +++ b/doc/sphinxext/redirect_from.py @@ -0,0 +1,87 @@ +""" +Redirecting old docs to new location +==================================== + +If an rst file is moved or its content subsumed in a different file, it +is desireable to redirect the old file to the new or existing file. This +extension enables this with a simple html refresh. + +For example suppose ``doc/topic/old-page.rst`` is removed and its content +included in ``doc/topic/new-page.rst``. We use the ``redirect-from`` +directive in ``doc/topic/new-page.rst``:: + + .. redirect-from:: /topic/old-page + +This creates in the build directory a file ``build/html/topic/old-page.html`` +that contains a relative refresh:: + + + + + + + +If you need to redirect across subdirectory trees, that works as well. For +instance if ``doc/topic/subdir1/old-page.rst`` is now found at +``doc/topic/subdir2/new-page.rst`` then ``new-page.rst`` just lists the +full path:: + + .. redirect-from:: /topic/subdir1/old-page.rst + +""" + +from pathlib import Path +from docutils.parsers.rst import Directive +from sphinx.util import logging + +logger = logging.getLogger(__name__) + + +HTML_TEMPLATE = """ + + + + +""" + + +def setup(app): + RedirectFrom.app = app + app.add_directive("redirect-from", RedirectFrom) + app.connect("build-finished", _generate_redirects) + + +class RedirectFrom(Directive): + required_arguments = 1 + redirects = {} + + def run(self): + redirected_doc, = self.arguments + env = self.app.env + builder = self.app.builder + current_doc = env.path2doc(self.state.document.current_source) + redirected_reldoc, _ = env.relfn2path(redirected_doc, current_doc) + if redirected_reldoc in self.redirects: + raise ValueError( + f"{redirected_reldoc} is already noted as redirecting to " + f"{self.redirects[redirected_reldoc]}") + self.redirects[redirected_reldoc] = builder.get_relative_uri( + redirected_reldoc, current_doc) + return [] + + +def _generate_redirects(app, exception): + builder = app.builder + if builder.name != "html" or exception: + return + for k, v in RedirectFrom.redirects.items(): + p = Path(app.outdir, k + builder.out_suffix) + if p.is_file(): + logger.warning(f'A redirect-from directive is trying to create ' + f'{p}, but that file already exists (perhaps ' + f'you need to run "make clean")') + else: + p.parent.mkdir(parents=True, exist_ok=True) + with p.open("x") as file: + logger.info(f'making refresh html file: {k} redirect to {v}') + file.write(HTML_TEMPLATE.format(v=v)) diff --git a/tutorials/introductory/customizing.py b/tutorials/introductory/customizing.py index ab761fb8bcaf..993692bc8dd0 100644 --- a/tutorials/introductory/customizing.py +++ b/tutorials/introductory/customizing.py @@ -1,4 +1,6 @@ """ +.. redirect-from:: /users/customizing + Customizing Matplotlib with style sheets and rcParams =====================================================