Skip to content

Commit 797db72

Browse files
drammocksnwnde
authored andcommitted
Fix redirects (mne-tools#12099)
1 parent 7021f2f commit 797db72

File tree

2 files changed

+147
-123
lines changed

2 files changed

+147
-123
lines changed

doc/_templates/sidebar-quicklinks.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ <h5 class="card-header font-weight-bold">Version {{ release }}</h5>
33
<div class="card-body">
44
<ul class="list-group list-group-flush list-unstyled quicklinks">
55
<li><a href="{{ pathto('auto_tutorials/index.html', 1) }}"><i class="text-muted fas fa-book fa-fw"></i> Tutorials</a></li>
6-
<li><a href="{{ pathto('whats_new.html', 1) }}"><i class="text-muted fas fa-newspaper fa-fw"></i> Changelog</a></li>
7-
<li><a href="{{ pathto('overview/get_help.html', 1) }}"><i class="text-muted fas fa-circle-question fa-fw"></i> Get help</a></li>
8-
<li><a href="{{ pathto('overview/cite.html', 1) }}"><i class="text-muted fas fa-quote-left fa-fw"></i> Cite</a></li>
9-
<li><a href="{{ pathto('install/contributing.html', 1) }}"><i class="text-muted fas fa-code-branch fa-fw"></i> Contribute</a></li>
6+
<li><a href="{{ pathto('development/whats_new.html', 1) }}"><i class="text-muted fas fa-newspaper fa-fw"></i> Changelog</a></li>
7+
<li><a href="{{ pathto('help/index.html', 1) }}"><i class="text-muted fas fa-circle-question fa-fw"></i> Get help</a></li>
8+
<li><a href="{{ pathto('documentation/cite.html', 1) }}"><i class="text-muted fas fa-quote-left fa-fw"></i> Cite</a></li>
9+
<li><a href="{{ pathto('development/contributing.html', 1) }}"><i class="text-muted fas fa-code-branch fa-fw"></i> Contribute</a></li>
1010
</ul>
1111
</div>
1212
</div>

doc/conf.py

Lines changed: 143 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import gc
1010
from importlib.metadata import metadata
1111
import os
12+
from pathlib import Path
1213
import subprocess
1314
import sys
1415
import time
@@ -60,8 +61,8 @@
6061
# (Sphinx looks at variable changes and rewrites all files if some change)
6162
copyright = (
6263
f'2012–{td.year}, MNE Developers. Last updated <time datetime="{td.isoformat()}" class="localized">{td.strftime("%Y-%m-%d %H:%M %Z")}</time>\n' # noqa: E501
63-
'<script type="text/javascript">$(function () { $("time.localized").each(function () { var el = $(this); el.text(new Date(el.attr("datetime")).toLocaleString([], {dateStyle: "medium", timeStyle: "long"})); }); } )</script>'
64-
) # noqa: E501
64+
'<script type="text/javascript">$(function () { $("time.localized").each(function () { var el = $(this); el.text(new Date(el.attr("datetime")).toLocaleString([], {dateStyle: "medium", timeStyle: "long"})); }); } )</script>' # noqa: E501
65+
)
6566
if os.getenv("MNE_FULL_DATE", "false").lower() != "true":
6667
copyright = f"2012–{td.year}, MNE Developers. Last updated locally."
6768

@@ -1636,87 +1637,70 @@ def reset_warnings(gallery_conf, fname):
16361637
custom_redirects = {
16371638
# Custom redirects (one HTML path to another, relative to outdir)
16381639
# can be added here as fr->to key->value mappings
1639-
"install/contributing.html": "development/contributing.html",
1640-
"overview/roadmap.html": "development/roadmap.html",
1641-
"whats_new.html": "development/whats_new.html",
1642-
f"{tu}/evoked/plot_eeg_erp.html": f"{tu}/evoked/30_eeg_erp.html",
1643-
f"{tu}/evoked/plot_whitened.html": f"{tu}/evoked/40_whitened.html",
1644-
f"{tu}/misc/plot_modifying_data_inplace.html": f"{tu}/intro/15_inplace.html",
1645-
f"{tu}/misc/plot_report.html": f"{tu}/intro/70_report.html",
1646-
f"{tu}/misc/plot_seeg.html": f"{tu}/clinical/20_seeg.html",
1647-
f"{tu}/misc/plot_ecog.html": f"{tu}/clinical/30_ecog.html",
1648-
f"{tu}/{ml}/plot_receptive_field.html": f"{tu}/{ml}/30_strf.html",
1649-
f"{tu}/{ml}/plot_sensors_decoding.html": f"{tu}/{ml}/50_decoding.html",
1650-
f"{tu}/{sm}/plot_background_freesurfer.html": f"{tu}/{fw}/10_background_freesurfer.html", # noqa E501
1651-
f"{tu}/{sm}/plot_source_alignment.html": f"{tu}/{fw}/20_source_alignment.html",
1652-
f"{tu}/{sm}/plot_forward.html": f"{tu}/{fw}/30_forward.html",
1653-
f"{tu}/{sm}/plot_eeg_no_mri.html": f"{tu}/{fw}/35_eeg_no_mri.html",
1654-
f"{tu}/{sm}/plot_background_freesurfer_mne.html": f"{tu}/{fw}/50_background_freesurfer_mne.html", # noqa E501
1655-
f"{tu}/{sm}/plot_fix_bem_in_blender.html": f"{tu}/{fw}/80_fix_bem_in_blender.html",
1656-
f"{tu}/{sm}/plot_compute_covariance.html": f"{tu}/{fw}/90_compute_covariance.html",
1657-
f"{tu}/{sm}/plot_object_source_estimate.html": f"{tu}/{nv}/10_stc_class.html",
1658-
f"{tu}/{sm}/plot_dipole_fit.html": f"{tu}/{nv}/20_dipole_fit.html",
1659-
f"{tu}/{sm}/plot_mne_dspm_source_localization.html": f"{tu}/{nv}/30_mne_dspm_loreta.html", # noqa E501
1660-
f"{tu}/{sm}/plot_dipole_orientations.html": f"{tu}/{nv}/35_dipole_orientations.html", # noqa E501
1661-
f"{tu}/{sm}/plot_mne_solutions.html": f"{tu}/{nv}/40_mne_fixed_free.html",
1662-
f"{tu}/{sm}/plot_beamformer_lcmv.html": f"{tu}/{nv}/50_beamformer_lcmv.html",
1663-
f"{tu}/{sm}/plot_visualize_stc.html": f"{tu}/{nv}/60_visualize_stc.html",
1664-
f"{tu}/{sm}/plot_eeg_mri_coords.html": f"{tu}/{nv}/70_eeg_mri_coords.html",
1665-
f"{tu}/{sd}/plot_brainstorm_phantom_elekta.html": f"{tu}/{nv}/80_brainstorm_phantom_elekta.html", # noqa E501
1666-
f"{tu}/{sd}/plot_brainstorm_phantom_ctf.html": f"{tu}/{nv}/85_brainstorm_phantom_ctf.html", # noqa E501
1667-
f"{tu}/{sd}/plot_phantom_4DBTi.html": f"{tu}/{nv}/90_phantom_4DBTi.html",
1668-
f"{tu}/{sd}/plot_brainstorm_auditory.html": f"{tu}/io/60_ctf_bst_auditory.html",
1669-
f"{tu}/{sd}/plot_sleep.html": f"{tu}/clinical/60_sleep.html",
1670-
f"{tu}/{di}/plot_background_filtering.html": f"{tu}/{pr}/25_background_filtering.html", # noqa E501
1671-
f"{tu}/{di}/plot_background_statistics.html": f"{tu}/{sn}/10_background_stats.html",
1672-
f"{tu}/{sn}/plot_stats_cluster_erp.html": f"{tu}/{sn}/20_erp_stats.html",
1673-
f"{tu}/{sn}/plot_stats_cluster_1samp_test_time_frequency.html": f"{tu}/{sn}/40_cluster_1samp_time_freq.html", # noqa E501
1674-
f"{tu}/{sn}/plot_stats_cluster_time_frequency.html": f"{tu}/{sn}/50_cluster_between_time_freq.html", # noqa E501
1675-
f"{tu}/{sn}/plot_stats_spatio_temporal_cluster_sensors.html": f"{tu}/{sn}/75_cluster_ftest_spatiotemporal.html", # noqa E501
1676-
f"{tu}/{sr}/plot_stats_cluster_spatio_temporal.html": f"{tu}/{sr}/20_cluster_1samp_spatiotemporal.html", # noqa E501
1677-
f"{tu}/{sr}/plot_stats_cluster_spatio_temporal_2samp.html": f"{tu}/{sr}/30_cluster_ftest_spatiotemporal.html", # noqa E501
1678-
f"{tu}/{sr}/plot_stats_cluster_spatio_temporal_repeated_measures_anova.html": f"{tu}/{sr}/60_cluster_rmANOVA_spatiotemporal.html", # noqa E501
1679-
f"{tu}/{sr}/plot_stats_cluster_time_frequency_repeated_measures_anova.html": f"{tu}/{sn}/70_cluster_rmANOVA_time_freq.html", # noqa E501
1680-
f"{tu}/{tf}/plot_sensors_time_frequency.html": f"{tu}/{tf}/20_sensors_time_frequency.html", # noqa E501
1681-
f"{tu}/{tf}/plot_ssvep.html": f"{tu}/{tf}/50_ssvep.html",
1682-
f"{tu}/{si}/plot_creating_data_structures.html": f"{tu}/{si}/10_array_objs.html",
1683-
f"{tu}/{si}/plot_point_spread.html": f"{tu}/{si}/70_point_spread.html",
1684-
f"{tu}/{si}/plot_dics.html": f"{tu}/{si}/80_dics.html",
1685-
f"{tu}/{tf}/plot_eyetracking.html": f"{tu}/{pr}/90_eyetracking_data.html",
1686-
f"{ex}/{co}/mne_inverse_label_connectivity.html": f"{mne_conn}/{ex}/mne_inverse_label_connectivity.html", # noqa E501
1687-
f"{ex}/{co}/cwt_sensor_connectivity.html": f"{mne_conn}/{ex}/cwt_sensor_connectivity.html", # noqa E501
1688-
f"{ex}/{co}/mixed_source_space_connectivity.html": f"{mne_conn}/{ex}/mixed_source_space_connectivity.html", # noqa E501
1689-
f"{ex}/{co}/mne_inverse_coherence_epochs.html": f"{mne_conn}/{ex}/mne_inverse_coherence_epochs.html", # noqa E501
1690-
f"{ex}/{co}/mne_inverse_connectivity_spectrum.html": f"{mne_conn}/{ex}/mne_inverse_connectivity_spectrum.html", # noqa E501
1691-
f"{ex}/{co}/mne_inverse_envelope_correlation_volume.html": f"{mne_conn}/{ex}/mne_inverse_envelope_correlation_volume.html", # noqa E501
1692-
f"{ex}/{co}/mne_inverse_envelope_correlation.html": f"{mne_conn}/{ex}/mne_inverse_envelope_correlation.html", # noqa E501
1693-
f"{ex}/{co}/mne_inverse_psi_visual.html": f"{mne_conn}/{ex}/mne_inverse_psi_visual.html", # noqa E501
1694-
f"{ex}/{co}/sensor_connectivity.html": f"{mne_conn}/{ex}/sensor_connectivity.html",
1695-
f"{ex}/{vi}/publication_figure.html": f"{tu}/{vi}/10_publication_figure.html",
1696-
f"{ex}/{vi}/sensor_noise_level.html": f"{tu}/{pr}/50_artifact_correction_ssp.html",
1640+
"install/contributing": "development/contributing",
1641+
"overview/cite": "documentation/cite",
1642+
"overview/get_help": "help/index",
1643+
"overview/roadmap": "development/roadmap",
1644+
"whats_new": "development/whats_new",
1645+
f"{tu}/evoked/plot_eeg_erp": f"{tu}/evoked/30_eeg_erp",
1646+
f"{tu}/evoked/plot_whitened": f"{tu}/evoked/40_whitened",
1647+
f"{tu}/misc/plot_modifying_data_inplace": f"{tu}/intro/15_inplace",
1648+
f"{tu}/misc/plot_report": f"{tu}/intro/70_report",
1649+
f"{tu}/misc/plot_seeg": f"{tu}/clinical/20_seeg",
1650+
f"{tu}/misc/plot_ecog": f"{tu}/clinical/30_ecog",
1651+
f"{tu}/{ml}/plot_receptive_field": f"{tu}/{ml}/30_strf",
1652+
f"{tu}/{ml}/plot_sensors_decoding": f"{tu}/{ml}/50_decoding",
1653+
f"{tu}/{sm}/plot_background_freesurfer": f"{tu}/{fw}/10_background_freesurfer",
1654+
f"{tu}/{sm}/plot_source_alignment": f"{tu}/{fw}/20_source_alignment",
1655+
f"{tu}/{sm}/plot_forward": f"{tu}/{fw}/30_forward",
1656+
f"{tu}/{sm}/plot_eeg_no_mri": f"{tu}/{fw}/35_eeg_no_mri",
1657+
f"{tu}/{sm}/plot_background_freesurfer_mne": f"{tu}/{fw}/50_background_freesurfer_mne", # noqa E501
1658+
f"{tu}/{sm}/plot_fix_bem_in_blender": f"{tu}/{fw}/80_fix_bem_in_blender",
1659+
f"{tu}/{sm}/plot_compute_covariance": f"{tu}/{fw}/90_compute_covariance",
1660+
f"{tu}/{sm}/plot_object_source_estimate": f"{tu}/{nv}/10_stc_class",
1661+
f"{tu}/{sm}/plot_dipole_fit": f"{tu}/{nv}/20_dipole_fit",
1662+
f"{tu}/{sm}/plot_mne_dspm_source_localization": f"{tu}/{nv}/30_mne_dspm_loreta",
1663+
f"{tu}/{sm}/plot_dipole_orientations": f"{tu}/{nv}/35_dipole_orientations",
1664+
f"{tu}/{sm}/plot_mne_solutions": f"{tu}/{nv}/40_mne_fixed_free",
1665+
f"{tu}/{sm}/plot_beamformer_lcmv": f"{tu}/{nv}/50_beamformer_lcmv",
1666+
f"{tu}/{sm}/plot_visualize_stc": f"{tu}/{nv}/60_visualize_stc",
1667+
f"{tu}/{sm}/plot_eeg_mri_coords": f"{tu}/{nv}/70_eeg_mri_coords",
1668+
f"{tu}/{sd}/plot_brainstorm_phantom_elekta": f"{tu}/{nv}/80_brainstorm_phantom_elekta", # noqa E501
1669+
f"{tu}/{sd}/plot_brainstorm_phantom_ctf": f"{tu}/{nv}/85_brainstorm_phantom_ctf",
1670+
f"{tu}/{sd}/plot_phantom_4DBTi": f"{tu}/{nv}/90_phantom_4DBTi",
1671+
f"{tu}/{sd}/plot_brainstorm_auditory": f"{tu}/io/60_ctf_bst_auditory",
1672+
f"{tu}/{sd}/plot_sleep": f"{tu}/clinical/60_sleep",
1673+
f"{tu}/{di}/plot_background_filtering": f"{tu}/{pr}/25_background_filtering",
1674+
f"{tu}/{di}/plot_background_statistics": f"{tu}/{sn}/10_background_stats",
1675+
f"{tu}/{sn}/plot_stats_cluster_erp": f"{tu}/{sn}/20_erp_stats",
1676+
f"{tu}/{sn}/plot_stats_cluster_1samp_test_time_frequency": f"{tu}/{sn}/40_cluster_1samp_time_freq", # noqa E501
1677+
f"{tu}/{sn}/plot_stats_cluster_time_frequency": f"{tu}/{sn}/50_cluster_between_time_freq", # noqa E501
1678+
f"{tu}/{sn}/plot_stats_spatio_temporal_cluster_sensors": f"{tu}/{sn}/75_cluster_ftest_spatiotemporal", # noqa E501
1679+
f"{tu}/{sr}/plot_stats_cluster_spatio_temporal": f"{tu}/{sr}/20_cluster_1samp_spatiotemporal", # noqa E501
1680+
f"{tu}/{sr}/plot_stats_cluster_spatio_temporal_2samp": f"{tu}/{sr}/30_cluster_ftest_spatiotemporal", # noqa E501
1681+
f"{tu}/{sr}/plot_stats_cluster_spatio_temporal_repeated_measures_anova": f"{tu}/{sr}/60_cluster_rmANOVA_spatiotemporal", # noqa E501
1682+
f"{tu}/{sr}/plot_stats_cluster_time_frequency_repeated_measures_anova": f"{tu}/{sn}/70_cluster_rmANOVA_time_freq", # noqa E501
1683+
f"{tu}/{tf}/plot_sensors_time_frequency": f"{tu}/{tf}/20_sensors_time_frequency",
1684+
f"{tu}/{tf}/plot_ssvep": f"{tu}/{tf}/50_ssvep",
1685+
f"{tu}/{si}/plot_creating_data_structures": f"{tu}/{si}/10_array_objs",
1686+
f"{tu}/{si}/plot_point_spread": f"{tu}/{si}/70_point_spread",
1687+
f"{tu}/{si}/plot_dics": f"{tu}/{si}/80_dics",
1688+
f"{tu}/{tf}/plot_eyetracking": f"{tu}/{pr}/90_eyetracking_data",
1689+
f"{ex}/{co}/mne_inverse_label_connectivity": f"{mne_conn}/{ex}/mne_inverse_label_connectivity", # noqa E501
1690+
f"{ex}/{co}/cwt_sensor_connectivity": f"{mne_conn}/{ex}/cwt_sensor_connectivity",
1691+
f"{ex}/{co}/mixed_source_space_connectivity": f"{mne_conn}/{ex}/mixed_source_space_connectivity", # noqa E501
1692+
f"{ex}/{co}/mne_inverse_coherence_epochs": f"{mne_conn}/{ex}/mne_inverse_coherence_epochs", # noqa E501
1693+
f"{ex}/{co}/mne_inverse_connectivity_spectrum": f"{mne_conn}/{ex}/mne_inverse_connectivity_spectrum", # noqa E501
1694+
f"{ex}/{co}/mne_inverse_envelope_correlation_volume": f"{mne_conn}/{ex}/mne_inverse_envelope_correlation_volume", # noqa E501
1695+
f"{ex}/{co}/mne_inverse_envelope_correlation": f"{mne_conn}/{ex}/mne_inverse_envelope_correlation", # noqa E501
1696+
f"{ex}/{co}/mne_inverse_psi_visual": f"{mne_conn}/{ex}/mne_inverse_psi_visual",
1697+
f"{ex}/{co}/sensor_connectivity": f"{mne_conn}/{ex}/sensor_connectivity",
1698+
f"{ex}/{vi}/publication_figure": f"{tu}/{vi}/10_publication_figure",
1699+
f"{ex}/{vi}/sensor_noise_level": f"{tu}/{pr}/50_artifact_correction_ssp",
16971700
}
16981701

1699-
1700-
def check_existing_redirect(path):
1701-
"""Make sure existing HTML files are redirects, before overwriting."""
1702-
if os.path.isfile(path):
1703-
with open(path, "r") as fid:
1704-
for _ in range(8):
1705-
next(fid)
1706-
line = fid.readline()
1707-
assert "Page Redirection" in line, line
1708-
1709-
1710-
def make_redirects(app, exception):
1711-
"""Make HTML redirects."""
1712-
# https://www.sphinx-doc.org/en/master/extdev/appapi.html
1713-
# Adapted from sphinxcontrib/redirects (BSD-2-Clause)
1714-
if not (
1715-
isinstance(app.builder, sphinx.builders.html.StandaloneHTMLBuilder)
1716-
and exception is None
1717-
):
1718-
return
1719-
TEMPLATE = """\
1702+
# Adapted from sphinxcontrib/redirects (BSD-2-Clause)
1703+
REDIRECT_TEMPLATE = """\
17201704
<!DOCTYPE HTML>
17211705
<html lang="en-US">
17221706
<head>
@@ -1730,66 +1714,104 @@ def make_redirects(app, exception):
17301714
<body>
17311715
If you are not redirected automatically, follow this <a href='{to}'>link</a>.
17321716
</body>
1733-
</html>""" # noqa: E501
1734-
sphinx_gallery_conf = app.config["sphinx_gallery_conf"]
1735-
for src_dir, out_dir in zip(
1736-
sphinx_gallery_conf["examples_dirs"], sphinx_gallery_conf["gallery_dirs"]
1737-
):
1738-
root = os.path.abspath(os.path.join(app.srcdir, src_dir))
1717+
</html>"""
1718+
1719+
1720+
def check_existing_redirect(path):
1721+
"""Make sure existing HTML files are redirects, before overwriting."""
1722+
if path.is_file():
1723+
with open(path, "r") as fid:
1724+
for _ in range(8):
1725+
next(fid)
1726+
line = fid.readline()
1727+
if "Page Redirection" not in line:
1728+
raise RuntimeError(
1729+
"Attempted overwrite of HTML file with a redirect, where the "
1730+
"original file was not already a redirect."
1731+
)
1732+
1733+
1734+
def _check_valid_builder(app, exception):
1735+
valid_builder = isinstance(app.builder, sphinx.builders.html.StandaloneHTMLBuilder)
1736+
return valid_builder and exception is None
1737+
1738+
1739+
def make_gallery_redirects(app, exception):
1740+
"""Make HTML redirects for our sphinx gallery pages."""
1741+
if not _check_valid_builder(app, exception):
1742+
return
1743+
sg_conf = app.config["sphinx_gallery_conf"]
1744+
for src_dir, out_dir in zip(sg_conf["examples_dirs"], sg_conf["gallery_dirs"]):
1745+
root = (Path(app.srcdir) / src_dir).resolve()
17391746
fnames = [
1740-
os.path.join(os.path.relpath(dirpath, root), fname)
1741-
for dirpath, _, fnames in os.walk(root)
1742-
for fname in fnames
1743-
if fname in needed_plot_redirects
1747+
pyfile.relative_to(root)
1748+
for pyfile in root.rglob(r"**/*.py")
1749+
if pyfile.name in needed_plot_redirects
17441750
]
17451751
# plot_ redirects
17461752
for fname in fnames:
1747-
dirname = os.path.join(app.outdir, out_dir, os.path.dirname(fname))
1748-
to_fname = os.path.splitext(os.path.basename(fname))[0] + ".html"
1753+
dirname = Path(app.outdir) / out_dir / fname.parent
1754+
to_fname = fname.with_suffix(".html").name
17491755
fr_fname = f"plot_{to_fname}"
1750-
to_path = os.path.join(dirname, to_fname)
1751-
fr_path = os.path.join(dirname, fr_fname)
1752-
assert os.path.isfile(to_path), (fname, to_path)
1756+
to_path = dirname / to_fname
1757+
fr_path = dirname / fr_fname
1758+
assert to_path.is_file(), (fname, to_path)
17531759
with open(fr_path, "w") as fid:
1754-
fid.write(TEMPLATE.format(to=to_fname))
1760+
fid.write(REDIRECT_TEMPLATE.format(to=to_fname))
17551761
sphinx_logger.info(
17561762
f"Added {len(fnames):3d} HTML plot_* redirects for {out_dir}"
17571763
)
1758-
# API redirects
1764+
1765+
1766+
def make_api_redirects(app, exception):
1767+
"""Make HTML redirects for our API pages."""
1768+
if not _check_valid_builder(app, exception):
1769+
return
1770+
17591771
for page in api_redirects:
17601772
fname = f"{page}.html"
1761-
fr_path = os.path.join(app.outdir, fname)
1762-
to_path = os.path.join(app.outdir, "api", fname)
1773+
fr_path = Path(app.outdir) / fname
1774+
to_path = Path(app.outdir) / "api" / fname
17631775
# allow overwrite if existing file is just a redirect
17641776
check_existing_redirect(fr_path)
17651777
with open(fr_path, "w") as fid:
1766-
fid.write(TEMPLATE.format(to=to_path))
1778+
fid.write(REDIRECT_TEMPLATE.format(to=to_path))
17671779
sphinx_logger.info(f"Added {len(api_redirects):3d} HTML API redirects")
1768-
# custom redirects
1769-
for fr, to in custom_redirects.items():
1770-
if not to.startswith("http"):
1771-
assert os.path.isfile(os.path.join(app.outdir, to)), to
1772-
# handle links to sibling folders
1773-
path_parts = to.split("/")
1774-
if tu in path_parts:
1775-
path_parts = [".."] + path_parts[(path_parts.index(tu) + 1) :]
1776-
to = os.path.join(*path_parts)
1777-
assert to.endswith("html"), to
1778-
fr_path = os.path.join(app.outdir, fr)
1779-
assert fr_path.endswith("html"), fr_path
1780-
# allow overwrite if existing file is just a redirect
1780+
1781+
1782+
def make_custom_redirects(app, exception):
1783+
"""Make HTML redirects for miscellaneous pages."""
1784+
if not _check_valid_builder(app, exception):
1785+
return
1786+
1787+
for _fr, _to in custom_redirects.items():
1788+
fr = f"{_fr}.html"
1789+
to = f"{_to}.html"
1790+
fr_path = Path(app.outdir) / fr
17811791
check_existing_redirect(fr_path)
1782-
# handle folders that no longer exist
1783-
if fr_path.split("/")[-2] in (
1792+
if to.startswith("http"):
1793+
to_path = to
1794+
else:
1795+
to_path = Path(app.outdir) / to
1796+
assert to_path.is_file(), to_path
1797+
# recreate folders that no longer exist
1798+
defunct_gallery_folders = (
17841799
"misc",
17851800
"discussions",
17861801
"source-modeling",
17871802
"sample-datasets",
17881803
"connectivity",
1804+
)
1805+
parts = fr_path.relative_to(Path(app.outdir)).parts
1806+
if (
1807+
len(parts) > 1 # whats_new violates this
1808+
and parts[1] in defunct_gallery_folders
1809+
and not fr_path.parent.exists()
17891810
):
1790-
os.makedirs(os.path.dirname(fr_path), exist_ok=True)
1811+
os.makedirs(fr_path.parent, exist_ok=True)
1812+
# write the redirect
17911813
with open(fr_path, "w") as fid:
1792-
fid.write(TEMPLATE.format(to=to))
1814+
fid.write(REDIRECT_TEMPLATE.format(to=to_path))
17931815
sphinx_logger.info(f"Added {len(custom_redirects):3d} HTML custom redirects")
17941816

17951817

@@ -1818,5 +1840,7 @@ def setup(app):
18181840
app.connect("autodoc-process-docstring", append_attr_meth_examples)
18191841
report_scraper.app = app
18201842
app.connect("builder-inited", report_scraper.copyfiles)
1821-
app.connect("build-finished", make_redirects)
1843+
app.connect("build-finished", make_gallery_redirects)
1844+
app.connect("build-finished", make_api_redirects)
1845+
app.connect("build-finished", make_custom_redirects)
18221846
app.connect("build-finished", make_version)

0 commit comments

Comments
 (0)