|
32 | 32 |
|
33 | 33 | from pathlib import Path
|
34 | 34 | from docutils.parsers.rst import Directive
|
| 35 | +from sphinx.domains import Domain |
35 | 36 | from sphinx.util import logging
|
36 | 37 |
|
37 | 38 | logger = logging.getLogger(__name__)
|
|
48 | 49 | def setup(app):
|
49 | 50 | RedirectFrom.app = app
|
50 | 51 | app.add_directive("redirect-from", RedirectFrom)
|
| 52 | + app.add_domain(RedirectFromDomain) |
51 | 53 | app.connect("build-finished", _generate_redirects)
|
52 | 54 |
|
| 55 | + metadata = {'parallel_read_safe': True} |
| 56 | + return metadata |
| 57 | + |
| 58 | + |
| 59 | +class RedirectFromDomain(Domain): |
| 60 | + """ |
| 61 | + The sole purpose of this domain is a parallel_read_safe data store for the |
| 62 | + redirects mapping. |
| 63 | + """ |
| 64 | + name = 'redirect_from' |
| 65 | + label = 'redirect_from' |
| 66 | + |
| 67 | + @property |
| 68 | + def redirects(self): |
| 69 | + """The mapping of the redirectes.""" |
| 70 | + return self.data.setdefault('redirects', {}) |
| 71 | + |
| 72 | + def clear_doc(self, docnames): |
| 73 | + self.redirects.clear() |
| 74 | + |
| 75 | + def merge_domaindata(self, docnames, otherdata): |
| 76 | + for src, dst in otherdata['redirects'].items(): |
| 77 | + if src not in self.redirects: |
| 78 | + self.redirects[src] = dst |
| 79 | + elif self.redirects[src] != dst: |
| 80 | + raise ValueError( |
| 81 | + f"Inconsistent redirections from {src} to " |
| 82 | + F"{self.redirects[src]} and {otherdata.redirects[src]}") |
| 83 | + |
53 | 84 |
|
54 | 85 | class RedirectFrom(Directive):
|
55 | 86 | required_arguments = 1
|
56 |
| - redirects = {} |
57 | 87 |
|
58 | 88 | def run(self):
|
59 | 89 | redirected_doc, = self.arguments
|
60 | 90 | env = self.app.env
|
61 | 91 | builder = self.app.builder
|
| 92 | + domain = env.get_domain('redirect_from') |
62 | 93 | current_doc = env.path2doc(self.state.document.current_source)
|
63 | 94 | redirected_reldoc, _ = env.relfn2path(redirected_doc, current_doc)
|
64 |
| - if redirected_reldoc in self.redirects: |
| 95 | + if redirected_reldoc in domain.redirects: |
65 | 96 | raise ValueError(
|
66 | 97 | f"{redirected_reldoc} is already noted as redirecting to "
|
67 |
| - f"{self.redirects[redirected_reldoc]}") |
68 |
| - self.redirects[redirected_reldoc] = current_doc |
| 98 | + f"{domain.redirects[redirected_reldoc]}") |
| 99 | + domain.redirects[redirected_reldoc] = current_doc |
69 | 100 | return []
|
70 | 101 |
|
71 | 102 |
|
72 | 103 | def _generate_redirects(app, exception):
|
73 | 104 | builder = app.builder
|
74 | 105 | if builder.name != "html" or exception:
|
75 | 106 | return
|
76 |
| - for k, v in RedirectFrom.redirects.items(): |
| 107 | + for k, v in app.env.get_domain('redirect_from').redirects.items(): |
77 | 108 | p = Path(app.outdir, k + builder.out_suffix)
|
78 | 109 | html = HTML_TEMPLATE.format(v=builder.get_relative_uri(k, v))
|
79 | 110 | if p.is_file():
|
|
0 commit comments