File: redirect_from.py

package info (click to toggle)
matplotlib 3.10.1%2Bdfsg1-4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 78,352 kB
  • sloc: python: 147,118; cpp: 62,988; objc: 1,679; ansic: 1,426; javascript: 786; makefile: 104; sh: 53
file content (127 lines) | stat: -rw-r--r-- 4,135 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""
Redirecting old docs to new location
====================================

If an rst file is moved or its content subsumed in a different file, it
is desirable 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::

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta http-equiv="refresh" content="0; url=new-page.html">
      </head>
    </html>

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 sphinx.util.docutils import SphinxDirective
from sphinx.domains import Domain
from sphinx.util import logging

logger = logging.getLogger(__name__)


HTML_TEMPLATE = """<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="refresh" content="0; url={v}">
  </head>
</html>
"""


def setup(app):
    app.add_directive("redirect-from", RedirectFrom)
    app.add_domain(RedirectFromDomain)
    app.connect("builder-inited", _clear_redirects)
    app.connect("build-finished", _generate_redirects)

    metadata = {'parallel_read_safe': True}
    return metadata


class RedirectFromDomain(Domain):
    """
    The sole purpose of this domain is a parallel_read_safe data store for the
    redirects mapping.
    """
    name = 'redirect_from'
    label = 'redirect_from'

    @property
    def redirects(self):
        """The mapping of the redirects."""
        return self.data.setdefault('redirects', {})

    def clear_doc(self, docname):
        self.redirects.pop(docname, None)

    def merge_domaindata(self, docnames, otherdata):
        for src, dst in otherdata['redirects'].items():
            if src not in self.redirects:
                self.redirects[src] = dst
            elif self.redirects[src] != dst:
                raise ValueError(
                    f"Inconsistent redirections from {src} to "
                    f"{self.redirects[src]} and {otherdata['redirects'][src]}")


class RedirectFrom(SphinxDirective):
    required_arguments = 1

    def run(self):
        redirected_doc, = self.arguments
        domain = self.env.get_domain('redirect_from')
        current_doc = self.env.path2doc(self.state.document.current_source)
        redirected_reldoc, _ = self.env.relfn2path(redirected_doc, current_doc)
        if redirected_reldoc in domain.redirects:
            raise ValueError(
                f"{redirected_reldoc} is already noted as redirecting to "
                f"{domain.redirects[redirected_reldoc]}")
        domain.redirects[redirected_reldoc] = current_doc
        return []


def _generate_redirects(app, exception):
    builder = app.builder
    if builder.name != "html" or exception:
        return
    for k, v in app.env.get_domain('redirect_from').redirects.items():
        p = Path(app.outdir, k + builder.out_suffix)
        html = HTML_TEMPLATE.format(v=builder.get_relative_uri(k, v))
        if p.is_file():
            if p.read_text() != html:
                logger.warning('A redirect-from directive is trying to '
                               'create %s, but that file already exists '
                               '(perhaps you need to run "make clean")', p)
        else:
            logger.info('making refresh html file: %s redirect to %s', k, v)
            p.parent.mkdir(parents=True, exist_ok=True)
            p.write_text(html, encoding='utf-8')


def _clear_redirects(app):
    domain = app.env.get_domain('redirect_from')
    if domain.redirects:
        logger.info('clearing cached redirects')
        domain.redirects.clear()