summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2021-05-05 23:43:46 +0900
committerGitHub <noreply@github.com>2021-05-05 23:43:46 +0900
commitd82c8a78900e48c037827d3c522acecd5a0b530c (patch)
treee07705635736640d687e42d53427246d66ccad10
parentd006c66e237f1a5bc4e95b25a5176fa8dc9de65a (diff)
parentafa80dfe03c22e4278ea57648c4db4640fdc36e8 (diff)
downloadsphinx-git-d82c8a78900e48c037827d3c522acecd5a0b530c.tar.gz
Merge pull request #9131 from RuRo/feature/glob-nitpick-ignore
Ignore nitpick warnings with regular expressions using `nitpick_ignore_regex`
-rw-r--r--doc/usage/configuration.rst14
-rw-r--r--sphinx/config.py1
-rw-r--r--sphinx/transforms/post_transforms/__init__.py16
-rw-r--r--tests/roots/test-nitpicky-warnings/conf.py1
-rw-r--r--tests/roots/test-nitpicky-warnings/index.rst7
-rw-r--r--tests/test_config.py74
6 files changed, 112 insertions, 1 deletions
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index 7b7a0a1d4..95a4b6267 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -419,6 +419,20 @@ General configuration
.. versionadded:: 1.1
+.. confval:: nitpick_ignore_regex
+
+ An extended version of :confval:`nitpick_ignore`, which instead interprets
+ the ``type`` and ``target`` strings as regular expressions. Note, that the
+ regular expression must match the whole string (as if the ``^`` and ``$``
+ markers were inserted).
+
+ For example, ``(r'py:.*', r'foo.*bar\.B.*')`` will ignore nitpicky warnings
+ for all python entities that start with ``'foo'`` and have ``'bar.B'`` in
+ them, such as ``('py:const', 'foo_package.bar.BAZ_VALUE')`` or
+ ``('py:class', 'food.bar.Barman')``.
+
+ .. versionadded:: 4.1
+
.. confval:: numfig
If true, figures, tables and code-blocks are automatically numbered if they
diff --git a/sphinx/config.py b/sphinx/config.py
index 5e265f576..1ba98d007 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -131,6 +131,7 @@ class Config:
'manpages_url': (None, 'env', []),
'nitpicky': (False, None, []),
'nitpick_ignore': ([], None, []),
+ 'nitpick_ignore_regex': ([], None, []),
'numfig': (False, 'env', []),
'numfig_secnum_depth': (1, 'env', []),
'numfig_format': ({}, 'env', []), # will be initialized in init_numfig_format()
diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py
index e2899d994..281407983 100644
--- a/sphinx/transforms/post_transforms/__init__.py
+++ b/sphinx/transforms/post_transforms/__init__.py
@@ -8,6 +8,7 @@
:license: BSD, see LICENSE for details.
"""
+import re
from typing import Any, Dict, List, Optional, Tuple, Type, cast
from docutils import nodes
@@ -171,14 +172,27 @@ class ReferencesResolver(SphinxPostTransform):
warn = node.get('refwarn')
if self.config.nitpicky:
warn = True
+ dtype = '%s:%s' % (domain.name, typ) if domain else typ
if self.config.nitpick_ignore:
- dtype = '%s:%s' % (domain.name, typ) if domain else typ
if (dtype, target) in self.config.nitpick_ignore:
warn = False
# for "std" types also try without domain name
if (not domain or domain.name == 'std') and \
(typ, target) in self.config.nitpick_ignore:
warn = False
+ if self.config.nitpick_ignore_regex:
+ def matches_ignore(entry_type: str, entry_target: str) -> bool:
+ for ignore_type, ignore_target in self.config.nitpick_ignore_regex:
+ if re.fullmatch(ignore_type, entry_type) and \
+ re.fullmatch(ignore_target, entry_target):
+ return True
+ return False
+ if matches_ignore(dtype, target):
+ warn = False
+ # for "std" types also try without domain name
+ if (not domain or domain.name == 'std') and \
+ matches_ignore(typ, target):
+ warn = False
if not warn:
return
diff --git a/tests/roots/test-nitpicky-warnings/conf.py b/tests/roots/test-nitpicky-warnings/conf.py
new file mode 100644
index 000000000..2db221cc6
--- /dev/null
+++ b/tests/roots/test-nitpicky-warnings/conf.py
@@ -0,0 +1 @@
+nitpicky = True
diff --git a/tests/roots/test-nitpicky-warnings/index.rst b/tests/roots/test-nitpicky-warnings/index.rst
new file mode 100644
index 000000000..e73840d4f
--- /dev/null
+++ b/tests/roots/test-nitpicky-warnings/index.rst
@@ -0,0 +1,7 @@
+test-nitpicky-warnings
+======================
+
+:py:const:`prefix.anything.postfix`
+:py:class:`prefix.anything`
+:py:class:`anything.postfix`
+:js:class:`prefix.anything.postfix`
diff --git a/tests/test_config.py b/tests/test_config.py
index a48e7ce30..b9b4f612a 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -311,3 +311,77 @@ def test_check_enum_for_list_failed(logger):
config.init_values()
check_confval_types(None, config)
assert logger.warning.called
+
+
+nitpick_warnings = [
+ "WARNING: py:const reference target not found: prefix.anything.postfix",
+ "WARNING: py:class reference target not found: prefix.anything",
+ "WARNING: py:class reference target not found: anything.postfix",
+ "WARNING: js:class reference target not found: prefix.anything.postfix",
+]
+
+
+@pytest.mark.sphinx(testroot='nitpicky-warnings')
+def test_nitpick_base(app, status, warning):
+ app.builder.build_all()
+
+ warning = warning.getvalue().strip().split('\n')
+ assert len(warning) == len(nitpick_warnings)
+ for actual, expected in zip(warning, nitpick_warnings):
+ assert expected in actual
+
+
+@pytest.mark.sphinx(testroot='nitpicky-warnings', confoverrides={
+ 'nitpick_ignore': [
+ ('py:const', 'prefix.anything.postfix'),
+ ('py:class', 'prefix.anything'),
+ ('py:class', 'anything.postfix'),
+ ('js:class', 'prefix.anything.postfix'),
+ ],
+})
+def test_nitpick_ignore(app, status, warning):
+ app.builder.build_all()
+ assert not len(warning.getvalue().strip())
+
+
+@pytest.mark.sphinx(testroot='nitpicky-warnings', confoverrides={
+ 'nitpick_ignore_regex': [
+ (r'py:.*', r'.*postfix'),
+ (r'.*:class', r'prefix.*'),
+ ]
+})
+def test_nitpick_ignore_regex1(app, status, warning):
+ app.builder.build_all()
+ assert not len(warning.getvalue().strip())
+
+
+@pytest.mark.sphinx(testroot='nitpicky-warnings', confoverrides={
+ 'nitpick_ignore_regex': [
+ (r'py:.*', r'prefix.*'),
+ (r'.*:class', r'.*postfix'),
+ ]
+})
+def test_nitpick_ignore_regex2(app, status, warning):
+ app.builder.build_all()
+ assert not len(warning.getvalue().strip())
+
+
+@pytest.mark.sphinx(testroot='nitpicky-warnings', confoverrides={
+ 'nitpick_ignore_regex': [
+ # None of these should match
+ (r'py:', r'.*'),
+ (r':class', r'.*'),
+ (r'', r'.*'),
+ (r'.*', r'anything'),
+ (r'.*', r'prefix'),
+ (r'.*', r'postfix'),
+ (r'.*', r''),
+ ]
+})
+def test_nitpick_ignore_regex_fullmatch(app, status, warning):
+ app.builder.build_all()
+
+ warning = warning.getvalue().strip().split('\n')
+ assert len(warning) == len(nitpick_warnings)
+ for actual, expected in zip(warning, nitpick_warnings):
+ assert expected in actual