Skip to content

Commit

Permalink
Merge pull request #9131 from RuRo/feature/glob-nitpick-ignore
Browse files Browse the repository at this point in the history
Ignore nitpick warnings with regular expressions using `nitpick_ignore_regex`
  • Loading branch information
tk0miya committed May 5, 2021
2 parents d006c66 + afa80df commit d82c8a7
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 1 deletion.
14 changes: 14 additions & 0 deletions doc/usage/configuration.rst
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions sphinx/config.py
Expand Up @@ -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()
Expand Down
16 changes: 15 additions & 1 deletion sphinx/transforms/post_transforms/__init__.py
Expand Up @@ -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
Expand Down Expand Up @@ -171,14 +172,27 @@ def warn_missing_reference(self, refdoc: str, typ: str, target: str,
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

Expand Down
1 change: 1 addition & 0 deletions tests/roots/test-nitpicky-warnings/conf.py
@@ -0,0 +1 @@
nitpicky = True
7 changes: 7 additions & 0 deletions 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`
74 changes: 74 additions & 0 deletions tests/test_config.py
Expand Up @@ -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

0 comments on commit d82c8a7

Please sign in to comment.