Skip to content

Backport PR #19686 on branch v3.4.x (Declare sphinxext.redirect_from parallel_read_safe) #20104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 36 additions & 5 deletions doc/sphinxext/redirect_from.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

from pathlib import Path
from docutils.parsers.rst import Directive
from sphinx.domains import Domain
from sphinx.util import logging

logger = logging.getLogger(__name__)
Expand All @@ -48,32 +49,62 @@
def setup(app):
RedirectFrom.app = app
app.add_directive("redirect-from", RedirectFrom)
app.add_domain(RedirectFromDomain)
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 redirectes."""
return self.data.setdefault('redirects', {})

def clear_doc(self, docnames):
self.redirects.clear()

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(Directive):
required_arguments = 1
redirects = {}

def run(self):
redirected_doc, = self.arguments
env = self.app.env
builder = self.app.builder
domain = env.get_domain('redirect_from')
current_doc = env.path2doc(self.state.document.current_source)
redirected_reldoc, _ = env.relfn2path(redirected_doc, current_doc)
if redirected_reldoc in self.redirects:
if redirected_reldoc in domain.redirects:
raise ValueError(
f"{redirected_reldoc} is already noted as redirecting to "
f"{self.redirects[redirected_reldoc]}")
self.redirects[redirected_reldoc] = current_doc
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 RedirectFrom.redirects.items():
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():
Expand Down