summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Lykke Andersen <Jakob@caput.dk>2021-10-31 14:44:45 +0100
committerJakob Lykke Andersen <Jakob@caput.dk>2021-10-31 14:44:45 +0100
commit4c19d9f65a261ff5d378563110fa2af7c2d64752 (patch)
tree3a6e936c05ffe948c3742b1d8bc97d5c3d1238a6
parentb32d4b500a5d92b3c5cbbecbdc1a2ce4186b5b6d (diff)
parenta8eb1aab72a514b7257c780c0c57432732a702fb (diff)
downloadsphinx-git-4c19d9f65a261ff5d378563110fa2af7c2d64752.tar.gz
Merge branch '4.x'
-rw-r--r--CHANGES8
-rw-r--r--doc/usage/extensions/intersphinx.rst29
-rw-r--r--sphinx/ext/intersphinx.py298
-rw-r--r--sphinx/templates/quickstart/conf.py_t1
-rw-r--r--sphinx/util/logging.py6
-rw-r--r--sphinx/util/typing.py3
-rw-r--r--tests/test_ext_intersphinx.py109
-rw-r--r--tests/test_util_logging.py13
8 files changed, 342 insertions, 125 deletions
diff --git a/CHANGES b/CHANGES
index f5db8d733..f16fa5b12 100644
--- a/CHANGES
+++ b/CHANGES
@@ -68,6 +68,11 @@ Features added
* #9695: More CSS classes on Javascript domain descriptions
* #9683: Revert the removal of ``add_stylesheet()`` API. It will be kept until
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
----------
@@ -105,6 +110,9 @@ Bugs fixed
* #9688: Fix :rst:dir:`code`` does not recognize ``:class:`` option
* #9733: Fix for logging handler flushing warnings in the middle of the docs
build
+* #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
--------
diff --git a/doc/usage/extensions/intersphinx.rst b/doc/usage/extensions/intersphinx.rst
index 478ddb7ae..a3e65bed6 100644
--- a/doc/usage/extensions/intersphinx.rst
+++ b/doc/usage/extensions/intersphinx.rst
@@ -148,6 +148,35 @@ linking:
exception is raised if the server has not issued a response for timeout
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
------------------------------------------------
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 4795d1ae2..d5f92d6fb 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -29,11 +29,11 @@ import posixpath
import sys
import time
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 docutils import nodes
-from docutils.nodes import TextElement
+from docutils.nodes import Element, TextElement
from docutils.utils import relative_path
import sphinx
@@ -41,11 +41,12 @@ from sphinx.addnodes import pending_xref
from sphinx.application import Sphinx
from sphinx.builders.html import INVENTORY_FILENAME
from sphinx.config import Config
+from sphinx.domains import Domain
from sphinx.environment import BuildEnvironment
from sphinx.locale import _, __
from sphinx.util import logging, requests
from sphinx.util.inventory import InventoryFile
-from sphinx.util.typing import Inventory
+from sphinx.util.typing import Inventory, InventoryItem
logger = logging.getLogger(__name__)
@@ -258,105 +259,211 @@ def load_mappings(app: Sphinx) -> None:
inventories.main_inventory.setdefault(type, {}).update(objects)
-def missing_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref,
- contnode: TextElement) -> nodes.reference:
- """Attempt to resolve a missing reference via intersphinx references."""
- target = node['reftarget']
- inventories = InventoryAdapter(env)
- objtypes: List[str] = None
- if node['reftype'] == 'any':
- # we search anything!
- objtypes = ['%s:%s' % (domain.name, objtype)
- for domain in env.domains.values()
- for objtype in domain.object_types]
- domain = None
+def _create_element_from_result(domain: Domain, inv_name: Optional[str],
+ data: InventoryItem,
+ node: pending_xref, contnode: TextElement) -> Element:
+ proj, version, uri, dispname = data
+ if '://' not in uri and node.get('refdoc'):
+ # get correct path in case of subdirectories
+ uri = path.join(relative_path(node['refdoc'], '.'), uri)
+ if version:
+ reftitle = _('(in %s v%s)') % (proj, version)
else:
- domain = node.get('refdomain')
- if not domain:
+ 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.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
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:
return None
- objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes]
- if 'std:cmdoption' in objtypes:
- # until Sphinx-1.6, cmdoptions are stored as std:option
- objtypes.append('std:option')
- 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
+ return _resolve_reference_in_domain(env, inv_name, inventory,
+ honor_disabled_refs,
+ domain, objtypes,
+ node, contnode)
- if '://' not in uri and node.get('refdoc'):
- # get correct path in case of subdirectories
- 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 inventory_exists(env: BuildEnvironment, inv_name: str) -> bool:
+ return inv_name in InventoryAdapter(env).named_inventory
+
+
+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:
@@ -387,6 +494,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_disabled_reftypes', [], True)
app.connect('config-inited', normalize_intersphinx_mapping, priority=800)
app.connect('builder-inited', load_mappings)
app.connect('missing-reference', missing_reference)
diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t
index f1da41c4a..07a1aa867 100644
--- a/sphinx/templates/quickstart/conf.py_t
+++ b/sphinx/templates/quickstart/conf.py_t
@@ -108,6 +108,7 @@ html_static_path = ['{{ dot }}static']
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
}
+
{%- endif %}
{%- if 'sphinx.ext.todo' in extensions %}
diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py
index d4d843e58..bdf77dc07 100644
--- a/sphinx/util/logging.py
+++ b/sphinx/util/logging.py
@@ -369,8 +369,10 @@ def is_suppressed_warning(type: str, subtype: str, suppress_warnings: List[str])
target, subtarget = warning_type, None
if target == type:
- if (subtype is None or subtarget is None or
- subtarget == subtype or subtarget == '*'):
+ if ((subtype is None and subtarget is None) or
+ subtarget is None or
+ subtarget == subtype or
+ subtarget == '*'):
return True
return False
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py
index a2ab5f931..e1972d86d 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -70,7 +70,8 @@ OptionSpec = Dict[str, Callable[[str], Any]]
TitleGetter = Callable[[nodes.Node], str]
# 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]:
diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py
index 28b5e63b1..e820730a1 100644
--- a/tests/test_ext_intersphinx.py
+++ b/tests/test_ext_intersphinx.py
@@ -42,6 +42,12 @@ def reference_check(app, *args, **kwds):
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._read_from_url')
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):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
- app.config.intersphinx_mapping = {
+ set_config(app, {
'https://docs.python.org/': inv_file,
'py3k': ('https://docs.python.org/py3k/', inv_file),
'py3krel': ('py3k', inv_file), # relative path
'py3krelparent': ('../../py3k', inv_file), # relative path, parent dir
- }
- app.config.intersphinx_cache_limit = 0
+ })
# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
@@ -133,12 +138,12 @@ def test_missing_reference(tempdir, app, status, warning):
refexplicit=True)
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',
refexplicit=False)
rn = missing_reference(app, app.env, node, contnode)
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
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):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
- app.config.intersphinx_mapping = {
+ set_config(app, {
'https://docs.python.org/': inv_file,
- }
- app.config.intersphinx_cache_limit = 0
+ })
# load the inventory and check if it's done correctly
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):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
- app.config.intersphinx_mapping = {
+ set_config(app, {
'cmd': ('https://docs.python.org/', inv_file),
- }
- app.config.intersphinx_cache_limit = 0
+ })
# load the inventory and check if it's done correctly
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):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
- app.config.intersphinx_mapping = {
+ set_config(app, {
'https://docs.python.org/': inv_file,
- }
- app.config.intersphinx_cache_limit = 0
+ })
# load the inventory and check if it's done correctly
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):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
- app.config.intersphinx_mapping = {
+ set_config(app, {
'https://docs.python.org/': inv_file,
- }
- app.config.intersphinx_cache_limit = 0
+ })
# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
@@ -291,14 +292,75 @@ def test_missing_reference_jsdomain(tempdir, app, status, warning):
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")
def test_inventory_not_having_version(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2_not_having_version)
- app.config.intersphinx_mapping = {
+ set_config(app, {
'https://docs.python.org/': inv_file,
- }
- app.config.intersphinx_cache_limit = 0
+ })
# load the inventory and check if it's done correctly
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.write_bytes(inventory_v2)
- app.config.intersphinx_mapping = {
+ set_config(app, {
'https://docs.python.org/': inv_file,
'py3k': ('https://docs.python.org/py3k/', inv_file),
'repoze.workflow': ('http://docs.repoze.org/workflow/', inv_file),
'django-taggit': ('http://django-taggit.readthedocs.org/en/latest/',
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
normalize_intersphinx_mapping(app, app.config)
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):
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
- app.config.intersphinx_cache_limit = 0
+ set_config(app, {})
# connect to invalid path
app.config.intersphinx_mapping = {
diff --git a/tests/test_util_logging.py b/tests/test_util_logging.py
index a03f62b01..5abcd02ef 100644
--- a/tests/test_util_logging.py
+++ b/tests/test_util_logging.py
@@ -131,6 +131,7 @@ def test_is_suppressed_warning():
assert is_suppressed_warning("ref", "option", suppress_warnings) is True
assert is_suppressed_warning("files", "image", suppress_warnings) is True
assert is_suppressed_warning("files", "stylesheet", suppress_warnings) is True
+ assert is_suppressed_warning("rest", None, suppress_warnings) is False
assert is_suppressed_warning("rest", "syntax", suppress_warnings) is False
assert is_suppressed_warning("rest", "duplicated_labels", suppress_warnings) is True
@@ -143,33 +144,39 @@ def test_suppress_warnings(app, status, warning):
app.config.suppress_warnings = []
warning.truncate(0)
+ logger.warning('message0', type='test')
logger.warning('message1', type='test', subtype='logging')
logger.warning('message2', type='test', subtype='crash')
logger.warning('message3', type='actual', subtype='logging')
+ assert 'message0' in warning.getvalue()
assert 'message1' in warning.getvalue()
assert 'message2' in warning.getvalue()
assert 'message3' in warning.getvalue()
- assert app._warncount == 3
+ assert app._warncount == 4
app.config.suppress_warnings = ['test']
warning.truncate(0)
+ logger.warning('message0', type='test')
logger.warning('message1', type='test', subtype='logging')
logger.warning('message2', type='test', subtype='crash')
logger.warning('message3', type='actual', subtype='logging')
+ assert 'message0' not in warning.getvalue()
assert 'message1' not in warning.getvalue()
assert 'message2' not in warning.getvalue()
assert 'message3' in warning.getvalue()
- assert app._warncount == 4
+ assert app._warncount == 5
app.config.suppress_warnings = ['test.logging']
warning.truncate(0)
+ logger.warning('message0', type='test')
logger.warning('message1', type='test', subtype='logging')
logger.warning('message2', type='test', subtype='crash')
logger.warning('message3', type='actual', subtype='logging')
+ assert 'message0' in warning.getvalue()
assert 'message1' not in warning.getvalue()
assert 'message2' in warning.getvalue()
assert 'message3' in warning.getvalue()
- assert app._warncount == 6
+ assert app._warncount == 8
def test_warningiserror(app, status, warning):