Skip to content
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Breaking changes
Major changes
.............

- Keep the per-page signature anchor and permalink when ``:noindex:`` is set; only the global route registration is skipped. [:pull:`112` by @ggiesen]
- Added support for Python 3.10 up to 3.14.[:pull:`85` by @stevepiercy]
- Adjusted a unit test regular expression for :file:`bottle_test.py`. [:pull:`85` by @stevepiercy]
- Use MDN documentation for information about HTTP status codes instead of ``w3.org``. [:pull:`78` by @jamesrobson-secondmind]
Expand Down
19 changes: 18 additions & 1 deletion src/sphinxcontrib/httpdomain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,23 @@ class HTTPResource(ObjectDescription):
}

method = NotImplemented
noindex = False

def run(self):
# ObjectDescription.run() skips add_target_and_index() entirely when
# the noindex option is set, which would drop the per-page anchor
# along with the global registration. Anchors are per-document ids
# and cannot collide across pages, so pop the option before running
# the base class and gate only the registration in
# add_target_and_index().
self.noindex = 'noindex' in self.options
self.options.pop('noindex', None)
nodes = super().run()
if self.noindex:
for node in nodes:
if isinstance(node, addnodes.desc):
node['noindex'] = node['no-index'] = True
return nodes

def handle_signature(self, sig, signode):
method = self.method.upper() + ' '
Expand Down Expand Up @@ -343,7 +360,7 @@ def needs_arglist(self):

def add_target_and_index(self, name_cls, sig, signode):
signode['ids'].append(http_resource_anchor(*name_cls[1:]))
if 'noindex' not in self.options:
if not self.noindex:
self.env.domaindata['http'][self.method][sig] = (
self.env.docname,
self.options.get('synopsis', ''),
Expand Down
70 changes: 70 additions & 0 deletions test/noindex_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import pathlib
import tempfile
import unittest

from sphinx import addnodes
from sphinx.application import Sphinx


class NoindexTestCase(unittest.TestCase):
"""The noindex option must skip only the global route registration.

The per-page anchor on the signature (and with it the HTML permalink)
must survive, so pages that document a route already indexed by another
page keep working deep links.
"""

def build(self):
self.tmp = tempfile.TemporaryDirectory()
src = pathlib.Path(self.tmp.name, 'src')
src.mkdir()
(src / 'conf.py').write_text(
"extensions = ['sphinxcontrib.httpdomain']\n"
)
(src / 'index.rst').write_text(
'Index\n'
'=====\n'
'\n'
'.. toctree::\n'
'\n'
' other\n'
'\n'
'.. http:post:: /demo\n'
'\n'
' Canonical description.\n'
)
(src / 'other.rst').write_text(
'Other\n'
'=====\n'
'\n'
'.. http:post:: /demo\n'
' :noindex:\n'
'\n'
' Alternative description of the same route.\n'
)
out = pathlib.Path(self.tmp.name, 'out')
app = Sphinx(
str(src), str(src), str(out), str(out / '.doctrees'), 'html',
status=None,
)
app.build()
return app

def tearDown(self):
self.tmp.cleanup()

def test_noindex_skips_registration(self):
app = self.build()
entries = app.env.domaindata['http']['post']
self.assertIn('/demo', entries)
self.assertEqual(entries['/demo'][0], 'index')

def test_noindex_keeps_anchor(self):
app = self.build()
doctree = app.env.get_doctree('other')
ids = [
anchor
for node in doctree.findall(addnodes.desc_signature)
for anchor in node['ids']
]
self.assertEqual(ids, ['post--demo'])