Skip to content

Commit

Permalink
intersphinx: Allow strict prefix matching
Browse files Browse the repository at this point in the history
Strictly using prefix for intersphinx links gives a better control on
external links - only links explicitely declared will point to the
external documentation.

Fixes sphinx-doc#2068
  • Loading branch information
nijel committed Mar 13, 2021
1 parent c817c20 commit 350ef53
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 0 deletions.
14 changes: 14 additions & 0 deletions doc/usage/extensions/intersphinx.rst
Expand Up @@ -148,6 +148,20 @@ linking:
exception is raised if the server has not issued a response for timeout
seconds.

.. confval:: intersphinx_strict_prefix

.. versionadded:: 4.0.0

Turn on strict matching of references in Sphinx - all intersphinx links
needs to have a prefix. This disables looking up the non-prefixed targets in
intersphinx and will fail the build if such target is not present.

.. hint::

Strictly using prefix for intersphinx links gives a better control on
external links - only links explicitely declared will point to the
external documentation.


Showing all links of an Intersphinx mapping file
------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions sphinx/ext/intersphinx.py
Expand Up @@ -314,6 +314,8 @@ def missing_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref,
full_qualified_name = env.get_domain(domain).get_full_qualified_name(node)
if full_qualified_name:
to_try.append((inventories.named_inventory[setname], full_qualified_name))
elif app.config.intersphinx_strict_prefix:
return None
for inventory, target in to_try:
for objtype in objtypes:
if objtype not in inventory or target not in inventory[objtype]:
Expand Down Expand Up @@ -379,6 +381,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('intersphinx_mapping', {}, True)
app.add_config_value('intersphinx_cache_limit', 5, False)
app.add_config_value('intersphinx_timeout', None, False)
app.add_config_value('intersphinx_strict_prefix', False, True)
app.connect('config-inited', normalize_intersphinx_mapping, priority=800)
app.connect('builder-inited', load_mappings)
app.connect('missing-reference', missing_reference)
Expand All @@ -399,6 +402,7 @@ def inspect_main(argv: List[str]) -> None:

class MockConfig:
intersphinx_timeout = None # type: int
intersphinx_strict_prefix = False
tls_verify = False
user_agent = None

Expand Down
15 changes: 15 additions & 0 deletions tests/test_ext_intersphinx.py
Expand Up @@ -97,6 +97,7 @@ def test_missing_reference(tempdir, app, status, warning):
'py3krelparent': ('../../py3k', inv_file), # relative path, parent dir
}
app.config.intersphinx_cache_limit = 0
app.config.intersphinx_strict_prefix = False

# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
Expand Down Expand Up @@ -165,6 +166,13 @@ def test_missing_reference(tempdir, app, status, warning):
rn = reference_check(app, 'std', 'doc', 'docname', 'docname')
assert rn['refuri'] == 'https://docs.python.org/docname.html'

# check resolution when strict prefix is enabled
app.config.intersphinx_strict_prefix = True
rn = reference_check(app, 'py', 'func', 'module1.func', 'foo')
assert rn is None
rn = reference_check(app, 'py', 'func', 'py3k:module1.func', 'foo')
assert rn is not None


def test_missing_reference_pydomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
Expand All @@ -173,6 +181,7 @@ def test_missing_reference_pydomain(tempdir, app, status, warning):
'https://docs.python.org/': inv_file,
}
app.config.intersphinx_cache_limit = 0
app.config.intersphinx_strict_prefix = False

# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
Expand Down Expand Up @@ -212,6 +221,7 @@ def test_missing_reference_stddomain(tempdir, app, status, warning):
'cmd': ('https://docs.python.org/', inv_file),
}
app.config.intersphinx_cache_limit = 0
app.config.intersphinx_strict_prefix = False

# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
Expand Down Expand Up @@ -244,6 +254,7 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning):
'https://docs.python.org/': inv_file,
}
app.config.intersphinx_cache_limit = 0
app.config.intersphinx_strict_prefix = False

# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
Expand Down Expand Up @@ -271,6 +282,7 @@ def test_missing_reference_jsdomain(tempdir, app, status, warning):
'https://docs.python.org/': inv_file,
}
app.config.intersphinx_cache_limit = 0
app.config.intersphinx_strict_prefix = False

# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
Expand All @@ -297,6 +309,7 @@ def test_inventory_not_having_version(tempdir, app, status, warning):
'https://docs.python.org/': inv_file,
}
app.config.intersphinx_cache_limit = 0
app.config.intersphinx_strict_prefix = False

# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
Expand Down Expand Up @@ -326,6 +339,7 @@ def test_load_mappings_warnings(tempdir, app, status, warning):
}

app.config.intersphinx_cache_limit = 0
app.config.intersphinx_strict_prefix = False
# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
load_mappings(app)
Expand All @@ -336,6 +350,7 @@ def test_load_mappings_fallback(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
app.config.intersphinx_cache_limit = 0
app.config.intersphinx_strict_prefix = False

# connect to invalid path
app.config.intersphinx_mapping = {
Expand Down

0 comments on commit 350ef53

Please sign in to comment.