mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #9459 from jakobandersen/intersphinx_refac_disable
Intersphinx, refactoring and intersphinx_disable_domains
This commit is contained in:
commit
a8eb1aab72
7
CHANGES
7
CHANGES
@ -47,6 +47,11 @@ Features added
|
|||||||
* #9695: More CSS classes on Javascript domain descriptions
|
* #9695: More CSS classes on Javascript domain descriptions
|
||||||
* #9683: Revert the removal of ``add_stylesheet()`` API. It will be kept until
|
* #9683: Revert the removal of ``add_stylesheet()`` API. It will be kept until
|
||||||
the Sphinx-6.0 release
|
the Sphinx-6.0 release
|
||||||
|
* #2068, add :confval:`intersphinx_disabled_reftypes` for disabling
|
||||||
|
interphinx resolution of cross-references that do not have an explicit
|
||||||
|
inventory specification. Specific types of cross-references can be disabled,
|
||||||
|
e.g., ``std:doc`` or all cross-references in a specific domain,
|
||||||
|
e.g., ``std:*``.
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
@ -85,6 +90,8 @@ Bugs fixed
|
|||||||
* #9733: Fix for logging handler flushing warnings in the middle of the docs
|
* #9733: Fix for logging handler flushing warnings in the middle of the docs
|
||||||
build
|
build
|
||||||
* #9656: Fix warnings without subtype being incorrectly suppressed
|
* #9656: Fix warnings without subtype being incorrectly suppressed
|
||||||
|
* Intersphinx, for unresolved references with an explicit inventory,
|
||||||
|
e.g., ``proj:myFunc``, leave the inventory prefix in the unresolved text.
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
@ -148,6 +148,35 @@ linking:
|
|||||||
exception is raised if the server has not issued a response for timeout
|
exception is raised if the server has not issued a response for timeout
|
||||||
seconds.
|
seconds.
|
||||||
|
|
||||||
|
.. confval:: intersphinx_disabled_reftypes
|
||||||
|
|
||||||
|
.. versionadded:: 4.3
|
||||||
|
|
||||||
|
A list of strings being either:
|
||||||
|
|
||||||
|
- the name of a specific reference type in a domain,
|
||||||
|
e.g., ``std:doc``, ``py:func``, or ``cpp:class``,
|
||||||
|
- the name of a domain, and a wildcard, e.g.,
|
||||||
|
``std:*``, ``py:*``, or ``cpp:*``, or
|
||||||
|
- simply a wildcard ``*``.
|
||||||
|
|
||||||
|
The default value is an empty list.
|
||||||
|
|
||||||
|
When a cross-reference without an explicit inventory specification is being
|
||||||
|
resolved by intersphinx, skip resolution if it matches one of the
|
||||||
|
specifications in this list.
|
||||||
|
|
||||||
|
For example, with ``intersphinx_disabled_reftypes = ['std:doc']``
|
||||||
|
a cross-reference ``:doc:`installation``` will not be attempted to be
|
||||||
|
resolved by intersphinx, but ``:doc:`otherbook:installation``` will be
|
||||||
|
attempted to be resolved in the inventory named ``otherbook`` in
|
||||||
|
:confval:`intersphinx_mapping`.
|
||||||
|
At the same time, all cross-references generated in, e.g., Python,
|
||||||
|
declarations will still be attempted to be resolved by intersphinx.
|
||||||
|
|
||||||
|
If ``*`` is in the list of domains, then no references without an explicit
|
||||||
|
inventory will be resolved by intersphinx.
|
||||||
|
|
||||||
|
|
||||||
Showing all links of an Intersphinx mapping file
|
Showing all links of an Intersphinx mapping file
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
@ -29,11 +29,11 @@ import posixpath
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from os import path
|
from os import path
|
||||||
from typing import IO, Any, Dict, List, Tuple
|
from typing import IO, Any, Dict, List, Optional, Tuple
|
||||||
from urllib.parse import urlsplit, urlunsplit
|
from urllib.parse import urlsplit, urlunsplit
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.nodes import TextElement
|
from docutils.nodes import Element, TextElement
|
||||||
from docutils.utils import relative_path
|
from docutils.utils import relative_path
|
||||||
|
|
||||||
import sphinx
|
import sphinx
|
||||||
@ -41,11 +41,12 @@ from sphinx.addnodes import pending_xref
|
|||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.builders.html import INVENTORY_FILENAME
|
from sphinx.builders.html import INVENTORY_FILENAME
|
||||||
from sphinx.config import Config
|
from sphinx.config import Config
|
||||||
|
from sphinx.domains import Domain
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.util import logging, requests
|
from sphinx.util import logging, requests
|
||||||
from sphinx.util.inventory import InventoryFile
|
from sphinx.util.inventory import InventoryFile
|
||||||
from sphinx.util.typing import Inventory
|
from sphinx.util.typing import Inventory, InventoryItem
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -258,105 +259,211 @@ def load_mappings(app: Sphinx) -> None:
|
|||||||
inventories.main_inventory.setdefault(type, {}).update(objects)
|
inventories.main_inventory.setdefault(type, {}).update(objects)
|
||||||
|
|
||||||
|
|
||||||
def missing_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref,
|
def _create_element_from_result(domain: Domain, inv_name: Optional[str],
|
||||||
contnode: TextElement) -> nodes.reference:
|
data: InventoryItem,
|
||||||
"""Attempt to resolve a missing reference via intersphinx references."""
|
node: pending_xref, contnode: TextElement) -> Element:
|
||||||
target = node['reftarget']
|
proj, version, uri, dispname = data
|
||||||
inventories = InventoryAdapter(env)
|
if '://' not in uri and node.get('refdoc'):
|
||||||
objtypes: List[str] = None
|
# get correct path in case of subdirectories
|
||||||
if node['reftype'] == 'any':
|
uri = path.join(relative_path(node['refdoc'], '.'), uri)
|
||||||
# we search anything!
|
if version:
|
||||||
objtypes = ['%s:%s' % (domain.name, objtype)
|
reftitle = _('(in %s v%s)') % (proj, version)
|
||||||
for domain in env.domains.values()
|
|
||||||
for objtype in domain.object_types]
|
|
||||||
domain = None
|
|
||||||
else:
|
else:
|
||||||
domain = node.get('refdomain')
|
reftitle = _('(in %s)') % (proj,)
|
||||||
if not domain:
|
newnode = nodes.reference('', '', internal=False, refuri=uri, reftitle=reftitle)
|
||||||
|
if node.get('refexplicit'):
|
||||||
|
# use whatever title was given
|
||||||
|
newnode.append(contnode)
|
||||||
|
elif dispname == '-' or \
|
||||||
|
(domain.name == 'std' and node['reftype'] == 'keyword'):
|
||||||
|
# use whatever title was given, but strip prefix
|
||||||
|
title = contnode.astext()
|
||||||
|
if inv_name is not None and title.startswith(inv_name + ':'):
|
||||||
|
newnode.append(contnode.__class__(title[len(inv_name) + 1:],
|
||||||
|
title[len(inv_name) + 1:]))
|
||||||
|
else:
|
||||||
|
newnode.append(contnode)
|
||||||
|
else:
|
||||||
|
# else use the given display name (used for :ref:)
|
||||||
|
newnode.append(contnode.__class__(dispname, dispname))
|
||||||
|
return newnode
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_reference_in_domain_by_target(
|
||||||
|
inv_name: Optional[str], inventory: Inventory,
|
||||||
|
domain: Domain, objtypes: List[str],
|
||||||
|
target: str,
|
||||||
|
node: pending_xref, contnode: TextElement) -> Optional[Element]:
|
||||||
|
for objtype in objtypes:
|
||||||
|
if objtype not in inventory:
|
||||||
|
# Continue if there's nothing of this kind in the inventory
|
||||||
|
continue
|
||||||
|
|
||||||
|
if target in inventory[objtype]:
|
||||||
|
# Case sensitive match, use it
|
||||||
|
data = inventory[objtype][target]
|
||||||
|
elif objtype == 'std:term':
|
||||||
|
# Check for potential case insensitive matches for terms only
|
||||||
|
target_lower = target.lower()
|
||||||
|
insensitive_matches = list(filter(lambda k: k.lower() == target_lower,
|
||||||
|
inventory[objtype].keys()))
|
||||||
|
if insensitive_matches:
|
||||||
|
data = inventory[objtype][insensitive_matches[0]]
|
||||||
|
else:
|
||||||
|
# No case insensitive match either, continue to the next candidate
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# Could reach here if we're not a term but have a case insensitive match.
|
||||||
|
# This is a fix for terms specifically, but potentially should apply to
|
||||||
|
# other types.
|
||||||
|
continue
|
||||||
|
return _create_element_from_result(domain, inv_name, data, node, contnode)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_reference_in_domain(env: BuildEnvironment,
|
||||||
|
inv_name: Optional[str], inventory: Inventory,
|
||||||
|
honor_disabled_refs: bool,
|
||||||
|
domain: Domain, objtypes: List[str],
|
||||||
|
node: pending_xref, contnode: TextElement
|
||||||
|
) -> Optional[Element]:
|
||||||
|
# we adjust the object types for backwards compatibility
|
||||||
|
if domain.name == 'std' and 'cmdoption' in objtypes:
|
||||||
|
# until Sphinx-1.6, cmdoptions are stored as std:option
|
||||||
|
objtypes.append('option')
|
||||||
|
if domain.name == 'py' and 'attribute' in objtypes:
|
||||||
|
# Since Sphinx-2.1, properties are stored as py:method
|
||||||
|
objtypes.append('method')
|
||||||
|
|
||||||
|
# the inventory contains domain:type as objtype
|
||||||
|
objtypes = ["{}:{}".format(domain.name, t) for t in objtypes]
|
||||||
|
|
||||||
|
# now that the objtypes list is complete we can remove the disabled ones
|
||||||
|
if honor_disabled_refs:
|
||||||
|
disabled = env.config.intersphinx_disabled_reftypes
|
||||||
|
objtypes = [o for o in objtypes if o not in disabled]
|
||||||
|
|
||||||
|
# without qualification
|
||||||
|
res = _resolve_reference_in_domain_by_target(inv_name, inventory, domain, objtypes,
|
||||||
|
node['reftarget'], node, contnode)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
|
||||||
|
# try with qualification of the current scope instead
|
||||||
|
full_qualified_name = domain.get_full_qualified_name(node)
|
||||||
|
if full_qualified_name is None:
|
||||||
|
return None
|
||||||
|
return _resolve_reference_in_domain_by_target(inv_name, inventory, domain, objtypes,
|
||||||
|
full_qualified_name, node, contnode)
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_reference(env: BuildEnvironment, inv_name: Optional[str], inventory: Inventory,
|
||||||
|
honor_disabled_refs: bool,
|
||||||
|
node: pending_xref, contnode: TextElement) -> Optional[Element]:
|
||||||
|
# disabling should only be done if no inventory is given
|
||||||
|
honor_disabled_refs = honor_disabled_refs and inv_name is None
|
||||||
|
|
||||||
|
if honor_disabled_refs and '*' in env.config.intersphinx_disabled_reftypes:
|
||||||
|
return None
|
||||||
|
|
||||||
|
typ = node['reftype']
|
||||||
|
if typ == 'any':
|
||||||
|
for domain_name, domain in env.domains.items():
|
||||||
|
if honor_disabled_refs \
|
||||||
|
and (domain_name + ":*") in env.config.intersphinx_disabled_reftypes:
|
||||||
|
continue
|
||||||
|
objtypes = list(domain.object_types)
|
||||||
|
res = _resolve_reference_in_domain(env, inv_name, inventory,
|
||||||
|
honor_disabled_refs,
|
||||||
|
domain, objtypes,
|
||||||
|
node, contnode)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
domain_name = node.get('refdomain')
|
||||||
|
if not domain_name:
|
||||||
# only objects in domains are in the inventory
|
# only objects in domains are in the inventory
|
||||||
return None
|
return None
|
||||||
objtypes = env.get_domain(domain).objtypes_for_role(node['reftype'])
|
if honor_disabled_refs \
|
||||||
|
and (domain_name + ":*") in env.config.intersphinx_disabled_reftypes:
|
||||||
|
return None
|
||||||
|
domain = env.get_domain(domain_name)
|
||||||
|
objtypes = domain.objtypes_for_role(typ)
|
||||||
if not objtypes:
|
if not objtypes:
|
||||||
return None
|
return None
|
||||||
objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes]
|
return _resolve_reference_in_domain(env, inv_name, inventory,
|
||||||
if 'std:cmdoption' in objtypes:
|
honor_disabled_refs,
|
||||||
# until Sphinx-1.6, cmdoptions are stored as std:option
|
domain, objtypes,
|
||||||
objtypes.append('std:option')
|
node, contnode)
|
||||||
if 'py:attribute' in objtypes:
|
|
||||||
# Since Sphinx-2.1, properties are stored as py:method
|
|
||||||
objtypes.append('py:method')
|
|
||||||
|
|
||||||
to_try = [(inventories.main_inventory, target)]
|
|
||||||
if domain:
|
|
||||||
full_qualified_name = env.get_domain(domain).get_full_qualified_name(node)
|
|
||||||
if full_qualified_name:
|
|
||||||
to_try.append((inventories.main_inventory, full_qualified_name))
|
|
||||||
in_set = None
|
|
||||||
if ':' in target:
|
|
||||||
# first part may be the foreign doc set name
|
|
||||||
setname, newtarget = target.split(':', 1)
|
|
||||||
if setname in inventories.named_inventory:
|
|
||||||
in_set = setname
|
|
||||||
to_try.append((inventories.named_inventory[setname], newtarget))
|
|
||||||
if domain:
|
|
||||||
node['reftarget'] = newtarget
|
|
||||||
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))
|
|
||||||
for inventory, target in to_try:
|
|
||||||
for objtype in objtypes:
|
|
||||||
if objtype not in inventory:
|
|
||||||
# Continue if there's nothing of this kind in the inventory
|
|
||||||
continue
|
|
||||||
if target in inventory[objtype]:
|
|
||||||
# Case sensitive match, use it
|
|
||||||
proj, version, uri, dispname = inventory[objtype][target]
|
|
||||||
elif objtype == 'std:term':
|
|
||||||
# Check for potential case insensitive matches for terms only
|
|
||||||
target_lower = target.lower()
|
|
||||||
insensitive_matches = list(filter(lambda k: k.lower() == target_lower,
|
|
||||||
inventory[objtype].keys()))
|
|
||||||
if insensitive_matches:
|
|
||||||
proj, version, uri, dispname = inventory[objtype][insensitive_matches[0]]
|
|
||||||
else:
|
|
||||||
# No case insensitive match either, continue to the next candidate
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
# Could reach here if we're not a term but have a case insensitive match.
|
|
||||||
# This is a fix for terms specifically, but potentially should apply to
|
|
||||||
# other types.
|
|
||||||
continue
|
|
||||||
|
|
||||||
if '://' not in uri and node.get('refdoc'):
|
def inventory_exists(env: BuildEnvironment, inv_name: str) -> bool:
|
||||||
# get correct path in case of subdirectories
|
return inv_name in InventoryAdapter(env).named_inventory
|
||||||
uri = path.join(relative_path(node['refdoc'], '.'), uri)
|
|
||||||
if version:
|
|
||||||
reftitle = _('(in %s v%s)') % (proj, version)
|
|
||||||
else:
|
|
||||||
reftitle = _('(in %s)') % (proj,)
|
|
||||||
newnode = nodes.reference('', '', internal=False, refuri=uri, reftitle=reftitle)
|
|
||||||
if node.get('refexplicit'):
|
|
||||||
# use whatever title was given
|
|
||||||
newnode.append(contnode)
|
|
||||||
elif dispname == '-' or \
|
|
||||||
(domain == 'std' and node['reftype'] == 'keyword'):
|
|
||||||
# use whatever title was given, but strip prefix
|
|
||||||
title = contnode.astext()
|
|
||||||
if in_set and title.startswith(in_set + ':'):
|
|
||||||
newnode.append(contnode.__class__(title[len(in_set) + 1:],
|
|
||||||
title[len(in_set) + 1:]))
|
|
||||||
else:
|
|
||||||
newnode.append(contnode)
|
|
||||||
else:
|
|
||||||
# else use the given display name (used for :ref:)
|
|
||||||
newnode.append(contnode.__class__(dispname, dispname))
|
|
||||||
return newnode
|
|
||||||
# at least get rid of the ':' in the target if no explicit title given
|
|
||||||
if in_set is not None and not node.get('refexplicit', True):
|
|
||||||
if len(contnode) and isinstance(contnode[0], nodes.Text):
|
|
||||||
contnode[0] = nodes.Text(newtarget, contnode[0].rawsource)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
def resolve_reference_in_inventory(env: BuildEnvironment,
|
||||||
|
inv_name: str,
|
||||||
|
node: pending_xref, contnode: TextElement
|
||||||
|
) -> Optional[Element]:
|
||||||
|
"""Attempt to resolve a missing reference via intersphinx references.
|
||||||
|
|
||||||
|
Resolution is tried in the given inventory with the target as is.
|
||||||
|
|
||||||
|
Requires ``inventory_exists(env, inv_name)``.
|
||||||
|
"""
|
||||||
|
assert inventory_exists(env, inv_name)
|
||||||
|
return _resolve_reference(env, inv_name, InventoryAdapter(env).named_inventory[inv_name],
|
||||||
|
False, node, contnode)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_reference_any_inventory(env: BuildEnvironment,
|
||||||
|
honor_disabled_refs: bool,
|
||||||
|
node: pending_xref, contnode: TextElement
|
||||||
|
) -> Optional[Element]:
|
||||||
|
"""Attempt to resolve a missing reference via intersphinx references.
|
||||||
|
|
||||||
|
Resolution is tried with the target as is in any inventory.
|
||||||
|
"""
|
||||||
|
return _resolve_reference(env, None, InventoryAdapter(env).main_inventory,
|
||||||
|
honor_disabled_refs,
|
||||||
|
node, contnode)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_reference_detect_inventory(env: BuildEnvironment,
|
||||||
|
node: pending_xref, contnode: TextElement
|
||||||
|
) -> Optional[Element]:
|
||||||
|
"""Attempt to resolve a missing reference via intersphinx references.
|
||||||
|
|
||||||
|
Resolution is tried first with the target as is in any inventory.
|
||||||
|
If this does not succeed, then the target is split by the first ``:``,
|
||||||
|
to form ``inv_name:newtarget``. If ``inv_name`` is a named inventory, then resolution
|
||||||
|
is tried in that inventory with the new target.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ordinary direct lookup, use data as is
|
||||||
|
res = resolve_reference_any_inventory(env, True, node, contnode)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
|
||||||
|
# try splitting the target into 'inv_name:target'
|
||||||
|
target = node['reftarget']
|
||||||
|
if ':' not in target:
|
||||||
|
return None
|
||||||
|
inv_name, newtarget = target.split(':', 1)
|
||||||
|
if not inventory_exists(env, inv_name):
|
||||||
|
return None
|
||||||
|
node['reftarget'] = newtarget
|
||||||
|
res_inv = resolve_reference_in_inventory(env, inv_name, node, contnode)
|
||||||
|
node['reftarget'] = target
|
||||||
|
return res_inv
|
||||||
|
|
||||||
|
|
||||||
|
def missing_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref,
|
||||||
|
contnode: TextElement) -> Optional[Element]:
|
||||||
|
"""Attempt to resolve a missing reference via intersphinx references."""
|
||||||
|
|
||||||
|
return resolve_reference_detect_inventory(env, node, contnode)
|
||||||
|
|
||||||
|
|
||||||
def normalize_intersphinx_mapping(app: Sphinx, config: Config) -> None:
|
def normalize_intersphinx_mapping(app: Sphinx, config: Config) -> None:
|
||||||
@ -387,6 +494,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_config_value('intersphinx_mapping', {}, True)
|
app.add_config_value('intersphinx_mapping', {}, True)
|
||||||
app.add_config_value('intersphinx_cache_limit', 5, False)
|
app.add_config_value('intersphinx_cache_limit', 5, False)
|
||||||
app.add_config_value('intersphinx_timeout', None, False)
|
app.add_config_value('intersphinx_timeout', None, False)
|
||||||
|
app.add_config_value('intersphinx_disabled_reftypes', [], True)
|
||||||
app.connect('config-inited', normalize_intersphinx_mapping, priority=800)
|
app.connect('config-inited', normalize_intersphinx_mapping, priority=800)
|
||||||
app.connect('builder-inited', load_mappings)
|
app.connect('builder-inited', load_mappings)
|
||||||
app.connect('missing-reference', missing_reference)
|
app.connect('missing-reference', missing_reference)
|
||||||
|
@ -108,6 +108,7 @@ html_static_path = ['{{ dot }}static']
|
|||||||
intersphinx_mapping = {
|
intersphinx_mapping = {
|
||||||
'python': ('https://docs.python.org/3', None),
|
'python': ('https://docs.python.org/3', None),
|
||||||
}
|
}
|
||||||
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if 'sphinx.ext.todo' in extensions %}
|
{%- if 'sphinx.ext.todo' in extensions %}
|
||||||
|
|
||||||
|
@ -70,7 +70,8 @@ OptionSpec = Dict[str, Callable[[str], Any]]
|
|||||||
TitleGetter = Callable[[nodes.Node], str]
|
TitleGetter = Callable[[nodes.Node], str]
|
||||||
|
|
||||||
# inventory data on memory
|
# inventory data on memory
|
||||||
Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]]
|
InventoryItem = Tuple[str, str, str, str]
|
||||||
|
Inventory = Dict[str, Dict[str, InventoryItem]]
|
||||||
|
|
||||||
|
|
||||||
def get_type_hints(obj: Any, globalns: Dict = None, localns: Dict = None) -> Dict[str, Any]:
|
def get_type_hints(obj: Any, globalns: Dict = None, localns: Dict = None) -> Dict[str, Any]:
|
||||||
|
@ -42,6 +42,12 @@ def reference_check(app, *args, **kwds):
|
|||||||
return missing_reference(app, app.env, node, contnode)
|
return missing_reference(app, app.env, node, contnode)
|
||||||
|
|
||||||
|
|
||||||
|
def set_config(app, mapping):
|
||||||
|
app.config.intersphinx_mapping = mapping
|
||||||
|
app.config.intersphinx_cache_limit = 0
|
||||||
|
app.config.intersphinx_disabled_reftypes = []
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('sphinx.ext.intersphinx.InventoryFile')
|
@mock.patch('sphinx.ext.intersphinx.InventoryFile')
|
||||||
@mock.patch('sphinx.ext.intersphinx._read_from_url')
|
@mock.patch('sphinx.ext.intersphinx._read_from_url')
|
||||||
def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status, warning):
|
def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status, warning):
|
||||||
@ -90,13 +96,12 @@ def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status,
|
|||||||
def test_missing_reference(tempdir, app, status, warning):
|
def test_missing_reference(tempdir, app, status, warning):
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(inventory_v2)
|
||||||
app.config.intersphinx_mapping = {
|
set_config(app, {
|
||||||
'https://docs.python.org/': inv_file,
|
'https://docs.python.org/': inv_file,
|
||||||
'py3k': ('https://docs.python.org/py3k/', inv_file),
|
'py3k': ('https://docs.python.org/py3k/', inv_file),
|
||||||
'py3krel': ('py3k', inv_file), # relative path
|
'py3krel': ('py3k', inv_file), # relative path
|
||||||
'py3krelparent': ('../../py3k', inv_file), # relative path, parent dir
|
'py3krelparent': ('../../py3k', inv_file), # relative path, parent dir
|
||||||
}
|
})
|
||||||
app.config.intersphinx_cache_limit = 0
|
|
||||||
|
|
||||||
# load the inventory and check if it's done correctly
|
# load the inventory and check if it's done correctly
|
||||||
normalize_intersphinx_mapping(app, app.config)
|
normalize_intersphinx_mapping(app, app.config)
|
||||||
@ -133,12 +138,12 @@ def test_missing_reference(tempdir, app, status, warning):
|
|||||||
refexplicit=True)
|
refexplicit=True)
|
||||||
assert rn[0].astext() == 'py3k:module2'
|
assert rn[0].astext() == 'py3k:module2'
|
||||||
|
|
||||||
# prefix given, target not found and nonexplicit title: prefix is stripped
|
# prefix given, target not found and nonexplicit title: prefix is not stripped
|
||||||
node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
|
node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
|
||||||
refexplicit=False)
|
refexplicit=False)
|
||||||
rn = missing_reference(app, app.env, node, contnode)
|
rn = missing_reference(app, app.env, node, contnode)
|
||||||
assert rn is None
|
assert rn is None
|
||||||
assert contnode[0].astext() == 'unknown'
|
assert contnode[0].astext() == 'py3k:unknown'
|
||||||
|
|
||||||
# prefix given, target not found and explicit title: nothing is changed
|
# prefix given, target not found and explicit title: nothing is changed
|
||||||
node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
|
node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
|
||||||
@ -169,10 +174,9 @@ def test_missing_reference(tempdir, app, status, warning):
|
|||||||
def test_missing_reference_pydomain(tempdir, app, status, warning):
|
def test_missing_reference_pydomain(tempdir, app, status, warning):
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(inventory_v2)
|
||||||
app.config.intersphinx_mapping = {
|
set_config(app, {
|
||||||
'https://docs.python.org/': inv_file,
|
'https://docs.python.org/': inv_file,
|
||||||
}
|
})
|
||||||
app.config.intersphinx_cache_limit = 0
|
|
||||||
|
|
||||||
# load the inventory and check if it's done correctly
|
# load the inventory and check if it's done correctly
|
||||||
normalize_intersphinx_mapping(app, app.config)
|
normalize_intersphinx_mapping(app, app.config)
|
||||||
@ -210,10 +214,9 @@ def test_missing_reference_pydomain(tempdir, app, status, warning):
|
|||||||
def test_missing_reference_stddomain(tempdir, app, status, warning):
|
def test_missing_reference_stddomain(tempdir, app, status, warning):
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(inventory_v2)
|
||||||
app.config.intersphinx_mapping = {
|
set_config(app, {
|
||||||
'cmd': ('https://docs.python.org/', inv_file),
|
'cmd': ('https://docs.python.org/', inv_file),
|
||||||
}
|
})
|
||||||
app.config.intersphinx_cache_limit = 0
|
|
||||||
|
|
||||||
# load the inventory and check if it's done correctly
|
# load the inventory and check if it's done correctly
|
||||||
normalize_intersphinx_mapping(app, app.config)
|
normalize_intersphinx_mapping(app, app.config)
|
||||||
@ -242,10 +245,9 @@ def test_missing_reference_stddomain(tempdir, app, status, warning):
|
|||||||
def test_missing_reference_cppdomain(tempdir, app, status, warning):
|
def test_missing_reference_cppdomain(tempdir, app, status, warning):
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(inventory_v2)
|
||||||
app.config.intersphinx_mapping = {
|
set_config(app, {
|
||||||
'https://docs.python.org/': inv_file,
|
'https://docs.python.org/': inv_file,
|
||||||
}
|
})
|
||||||
app.config.intersphinx_cache_limit = 0
|
|
||||||
|
|
||||||
# load the inventory and check if it's done correctly
|
# load the inventory and check if it's done correctly
|
||||||
normalize_intersphinx_mapping(app, app.config)
|
normalize_intersphinx_mapping(app, app.config)
|
||||||
@ -269,10 +271,9 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning):
|
|||||||
def test_missing_reference_jsdomain(tempdir, app, status, warning):
|
def test_missing_reference_jsdomain(tempdir, app, status, warning):
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(inventory_v2)
|
||||||
app.config.intersphinx_mapping = {
|
set_config(app, {
|
||||||
'https://docs.python.org/': inv_file,
|
'https://docs.python.org/': inv_file,
|
||||||
}
|
})
|
||||||
app.config.intersphinx_cache_limit = 0
|
|
||||||
|
|
||||||
# load the inventory and check if it's done correctly
|
# load the inventory and check if it's done correctly
|
||||||
normalize_intersphinx_mapping(app, app.config)
|
normalize_intersphinx_mapping(app, app.config)
|
||||||
@ -291,14 +292,75 @@ def test_missing_reference_jsdomain(tempdir, app, status, warning):
|
|||||||
assert rn.astext() == 'baz()'
|
assert rn.astext() == 'baz()'
|
||||||
|
|
||||||
|
|
||||||
|
def test_missing_reference_disabled_domain(tempdir, app, status, warning):
|
||||||
|
inv_file = tempdir / 'inventory'
|
||||||
|
inv_file.write_bytes(inventory_v2)
|
||||||
|
set_config(app, {
|
||||||
|
'inv': ('https://docs.python.org/', inv_file),
|
||||||
|
})
|
||||||
|
|
||||||
|
# load the inventory and check if it's done correctly
|
||||||
|
normalize_intersphinx_mapping(app, app.config)
|
||||||
|
load_mappings(app)
|
||||||
|
|
||||||
|
def case(*, term, doc, py):
|
||||||
|
def assert_(rn, expected):
|
||||||
|
if expected is None:
|
||||||
|
assert rn is None
|
||||||
|
else:
|
||||||
|
assert rn.astext() == expected
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
node, contnode = fake_node('std', 'term', 'a term', 'a term', **kwargs)
|
||||||
|
rn = missing_reference(app, app.env, node, contnode)
|
||||||
|
assert_(rn, 'a term' if term else None)
|
||||||
|
|
||||||
|
node, contnode = fake_node('std', 'term', 'inv:a term', 'a term', **kwargs)
|
||||||
|
rn = missing_reference(app, app.env, node, contnode)
|
||||||
|
assert_(rn, 'a term')
|
||||||
|
|
||||||
|
node, contnode = fake_node('std', 'doc', 'docname', 'docname', **kwargs)
|
||||||
|
rn = missing_reference(app, app.env, node, contnode)
|
||||||
|
assert_(rn, 'docname' if doc else None)
|
||||||
|
|
||||||
|
node, contnode = fake_node('std', 'doc', 'inv:docname', 'docname', **kwargs)
|
||||||
|
rn = missing_reference(app, app.env, node, contnode)
|
||||||
|
assert_(rn, 'docname')
|
||||||
|
|
||||||
|
# an arbitrary ref in another domain
|
||||||
|
node, contnode = fake_node('py', 'func', 'module1.func', 'func()', **kwargs)
|
||||||
|
rn = missing_reference(app, app.env, node, contnode)
|
||||||
|
assert_(rn, 'func()' if py else None)
|
||||||
|
|
||||||
|
node, contnode = fake_node('py', 'func', 'inv:module1.func', 'func()', **kwargs)
|
||||||
|
rn = missing_reference(app, app.env, node, contnode)
|
||||||
|
assert_(rn, 'func()')
|
||||||
|
|
||||||
|
# the base case, everything should resolve
|
||||||
|
assert app.config.intersphinx_disabled_reftypes == []
|
||||||
|
case(term=True, doc=True, py=True)
|
||||||
|
|
||||||
|
# disabled a single ref type
|
||||||
|
app.config.intersphinx_disabled_reftypes = ['std:doc']
|
||||||
|
case(term=True, doc=False, py=True)
|
||||||
|
|
||||||
|
# disabled a whole domain
|
||||||
|
app.config.intersphinx_disabled_reftypes = ['std:*']
|
||||||
|
case(term=False, doc=False, py=True)
|
||||||
|
|
||||||
|
# disabled all domains
|
||||||
|
app.config.intersphinx_disabled_reftypes = ['*']
|
||||||
|
case(term=False, doc=False, py=False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue")
|
@pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue")
|
||||||
def test_inventory_not_having_version(tempdir, app, status, warning):
|
def test_inventory_not_having_version(tempdir, app, status, warning):
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2_not_having_version)
|
inv_file.write_bytes(inventory_v2_not_having_version)
|
||||||
app.config.intersphinx_mapping = {
|
set_config(app, {
|
||||||
'https://docs.python.org/': inv_file,
|
'https://docs.python.org/': inv_file,
|
||||||
}
|
})
|
||||||
app.config.intersphinx_cache_limit = 0
|
|
||||||
|
|
||||||
# load the inventory and check if it's done correctly
|
# load the inventory and check if it's done correctly
|
||||||
normalize_intersphinx_mapping(app, app.config)
|
normalize_intersphinx_mapping(app, app.config)
|
||||||
@ -318,16 +380,15 @@ def test_load_mappings_warnings(tempdir, app, status, warning):
|
|||||||
"""
|
"""
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(inventory_v2)
|
||||||
app.config.intersphinx_mapping = {
|
set_config(app, {
|
||||||
'https://docs.python.org/': inv_file,
|
'https://docs.python.org/': inv_file,
|
||||||
'py3k': ('https://docs.python.org/py3k/', inv_file),
|
'py3k': ('https://docs.python.org/py3k/', inv_file),
|
||||||
'repoze.workflow': ('http://docs.repoze.org/workflow/', inv_file),
|
'repoze.workflow': ('http://docs.repoze.org/workflow/', inv_file),
|
||||||
'django-taggit': ('http://django-taggit.readthedocs.org/en/latest/',
|
'django-taggit': ('http://django-taggit.readthedocs.org/en/latest/',
|
||||||
inv_file),
|
inv_file),
|
||||||
12345: ('http://www.sphinx-doc.org/en/stable/', inv_file),
|
12345: ('http://www.sphinx-doc.org/en/stable/', inv_file),
|
||||||
}
|
})
|
||||||
|
|
||||||
app.config.intersphinx_cache_limit = 0
|
|
||||||
# load the inventory and check if it's done correctly
|
# load the inventory and check if it's done correctly
|
||||||
normalize_intersphinx_mapping(app, app.config)
|
normalize_intersphinx_mapping(app, app.config)
|
||||||
load_mappings(app)
|
load_mappings(app)
|
||||||
@ -337,7 +398,7 @@ def test_load_mappings_warnings(tempdir, app, status, warning):
|
|||||||
def test_load_mappings_fallback(tempdir, app, status, warning):
|
def test_load_mappings_fallback(tempdir, app, status, warning):
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(inventory_v2)
|
||||||
app.config.intersphinx_cache_limit = 0
|
set_config(app, {})
|
||||||
|
|
||||||
# connect to invalid path
|
# connect to invalid path
|
||||||
app.config.intersphinx_mapping = {
|
app.config.intersphinx_mapping = {
|
||||||
|
Loading…
Reference in New Issue
Block a user