Skip to content

Commit

Permalink
Merge pull request #85 from melexis/exclude_sphinx_deprecation
Browse files Browse the repository at this point in the history
Add flag to exclude Sphinx deprecation warnings
  • Loading branch information
Letme authored Oct 7, 2019
2 parents 9a4a71f + 3f4a722 commit 27ca146
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 29 deletions.
8 changes: 8 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,14 @@ An example configuration for the sphinx checker is given below:
}
}
Exclude Sphinx deprecation warnings
-----------------------------------

There is a special flag `--exclude-sphinx-deprecation` that lets the sphinx checker exclude
Sphinx deprecation warnings. These warnings match the following regular expression:
`RemovedInSphinx\\d+Warning`. Using this flag results in the same behavior as adding this
regex to the configuration file as value for the `exclude` key for the sphinx checker.


=======================
Issues and new Features
Expand Down
7 changes: 6 additions & 1 deletion src/mlx/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def config_parser_json(self, config):
self.activate_checker(checker)
checker.set_maximum(int(config[checker.name]['max']))
checker.set_minimum(int(config[checker.name]['min']))
checker.set_exclude_patterns(config[checker.name].get("exclude"))
checker.add_patterns(config[checker.name].get("exclude"), checker.exclude_patterns)
print("Config parsing for {name} completed".format(name=checker.name))
except KeyError as err:
print("Incomplete config. Missing: {key}".format(key=err))
Expand All @@ -196,6 +196,8 @@ def warnings_wrapper(args):
group2 = parser.add_argument_group('Configuration file with options')
group2.add_argument('--config', dest='configfile', action='store', required=False,
help='Config file in JSON format provides toggle of checkers and their limits')
group2.add_argument('--include-sphinx-deprecation', dest='include_sphinx_deprecation', action='store_true',
help="Sphinx checker will include warnings matching (RemovedInSphinx\\d+Warning) regex")
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true')
parser.add_argument('--command', dest='command', action='store_true',
help='Treat program arguments as command to execute to obtain data')
Expand Down Expand Up @@ -230,6 +232,9 @@ def warnings_wrapper(args):
warnings.set_maximum(args.maxwarnings)
warnings.set_minimum(args.minwarnings)

if args.include_sphinx_deprecation and 'sphinx' in warnings.activated_checkers.keys():
warnings.get_checker('sphinx').include_sphinx_deprecation()

if args.command:
cmd = args.logfile
if args.flags:
Expand Down
67 changes: 44 additions & 23 deletions src/mlx/warnings_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
DOXYGEN_WARNING_REGEX = r"(?:((?:[/.]|[A-Za-z]).+?):(-?\d+):\s*([Ww]arning|[Ee]rror)|<.+>:-?\d+(?::\s*([Ww]arning|[Ee]rror))?): ((?!notes).+(?:(?!\s*(?:[Nn]otice|[Ww]arning|[Ee]rror): )[^/<\n][^:\n][^/\n].+)*)|\s*(\b[Nn]otice|\b[Ww]arning|\b[Ee]rror): (?!notes)(.+)\n?"
doxy_pattern = re.compile(DOXYGEN_WARNING_REGEX)

SPHINX_WARNING_REGEX = r"(?m)^(?:(.+?:(?:\d+|None)?):?\s*)?(DEBUG|INFO|WARNING|ERROR|SEVERE|(?:\w+Sphinx\d+Warning)):\s*(.+)$"
SPHINX_WARNING_REGEX = r"(?m)^(?:(.+?:(?:\d+|None)?):?\s*)?(DEBUG|INFO|WARNING|ERROR|SEVERE):\s*(.+)$"
sphinx_pattern = re.compile(SPHINX_WARNING_REGEX)

PYTHON_XMLRUNNER_REGEX = r"(\s*(ERROR|FAILED) (\[\d+.\d\d\ds\]: \s*(.+)))\n?"
Expand All @@ -33,6 +33,7 @@ def __init__(self, verbose=False):
self.verbose = verbose
self.reset()
self.exclude_patterns = []
self.include_patterns = []

def reset(self):
''' Reset function (resets min, max and counter values) '''
Expand All @@ -49,17 +50,18 @@ def check(self, content):
'''
return

def set_exclude_patterns(self, exclude_regexes):
''' Abstract setter function for the exclude patterns list[re.Pattern]
def add_patterns(self, regexes, pattern_container):
''' Raises an Exception to explain that this feature is not available for the targeted checker
Args:
exclude_regexes (list|None): List of regexes to ignore certain matched warning messages
regexes (list[str]|None): List of regexes to add
pattern_container (list[re.Pattern]): Target storage container for patterns
Raises:
Exception: Feature of regexes to exclude warnings is only configurable for RegexChecker classes
Exception: Feature of regexes to include/exclude warnings is only configurable for the RegexChecker classes
'''
if exclude_regexes:
raise Exception("Feature of regexes to exclude warnings is not configurable for the {}."
if regexes:
raise Exception("Feature of regexes to include/exclude warnings is not configurable for the {}."
.format(self.__class__.__name__))

def set_maximum(self, maximum):
Expand Down Expand Up @@ -148,19 +150,19 @@ class RegexChecker(WarningsChecker):
name = 'regex'
pattern = None

def set_exclude_patterns(self, exclude_regexes):
''' Setter function for the exclude patterns list[re.Pattern]
def add_patterns(self, regexes, pattern_container):
''' Adds regexes as patterns to the specified container
Args:
exclude_regexes (list|None): List of regexes to ignore certain matched warning messages
regexes (list[str]|None): List of regexes to add
pattern_container (list[re.Pattern]): Target storage container for patterns
'''
self.exclude_patterns = []
if exclude_regexes:
if not isinstance(exclude_regexes, list):
raise TypeError("Excpected a list value for exclude key in configuration file; got {}"
.format(exclude_regexes.__class__.__name__))
for regex in exclude_regexes:
self.exclude_patterns.append(re.compile(regex))
if regexes:
if not isinstance(regexes, list):
raise TypeError("Expected a list value for exclude key in configuration file; got {}"
.format(regexes.__class__.__name__))
for regex in regexes:
pattern_container.append(re.compile(regex))

def check(self, content):
''' Function for counting the number of warnings in a specific text
Expand All @@ -177,25 +179,44 @@ def check(self, content):
self.print_when_verbose(match_string)

def _is_excluded(self, content):
''' Checks if the specific text must be excluded based on the configured regexes for exclusion.
''' Checks if the specific text must be excluded based on the configured regexes for exclusion and inclusion.
Inclusion has priority over exclusion.
Args:
content (str): The content to parse
Returns:
bool: True for exclusion, False for inclusion
'''
for pattern in self.exclude_patterns:
if pattern.search(content):
self.print_when_verbose("Excluded {!r} because of configured regex {!r}"
.format(content, pattern.pattern))
return True
matching_exclude_pattern = self._search_patterns(content, self.exclude_patterns)
if not self._search_patterns(content, self.include_patterns) and matching_exclude_pattern:
self.print_when_verbose("Excluded {!r} because of configured regex {!r}"
.format(content, matching_exclude_pattern))
return True
return False

@staticmethod
def _search_patterns(content, patterns):
''' Returns the regex of the first pattern that matches specified content, None if nothing matches '''
for pattern in patterns:
if pattern.search(content):
return pattern.pattern
return None


class SphinxChecker(RegexChecker):
name = 'sphinx'
pattern = sphinx_pattern
sphinx_deprecation_regex = r"(?m)^(?:(.+?:(?:\d+|None)?):?\s*)?(DEBUG|INFO|WARNING|ERROR|SEVERE|(?:\w+Sphinx\d+Warning)):\s*(.+)$"
sphinx_deprecation_regex_in_match = "RemovedInSphinx\\d+Warning"

def include_sphinx_deprecation(self):
'''
Adds the pattern for sphinx_deprecation_regex to the list patterns to include and alters the main pattern
'''
self.pattern = re.compile(self.sphinx_deprecation_regex)
self.add_patterns([self.sphinx_deprecation_regex_in_match], self.include_patterns)


class DoxyChecker(RegexChecker):
Expand Down
4 changes: 4 additions & 0 deletions tests/sphinx_double_deprecation_warning.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/usr/local/lib/python3.7/dist-packages/sphinx/util/docutils.py:286: RemovedInSphinx30Warning: function based directive support is now deprecated. Use class based directive instead.
RemovedInSphinx30Warning)
/usr/local/lib/python3.7/dist-packages/sphinx_rtd_theme/search.html:20: RemovedInSphinx30Warning: To modify script_files in the theme is deprecated. Please insert a <script> tag directly in your theme instead.
{{ super() }}
18 changes: 13 additions & 5 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,28 @@ def test_configfile_parsing_exclude(self):
self.assertEqual(warnings.return_count(), 0)
deprecation_warning = 'sphinx/application.py:402: RemovedInSphinx20Warning: app.info() is now deprecated. Use sphinx.util.logging instead.'
warnings.check(deprecation_warning)
self.assertEqual(warnings.return_count(), 0) # ignored because of configured "exclude" regex
warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'")
self.assertEqual(warnings.return_count(), 0)
toctree_warning = "/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'"
warnings.check(toctree_warning)
self.assertEqual(warnings.return_count(), 0) # ignored because of configured "exclude" regex
warnings.check("home/bljah/test/index.rst:5: WARNING: this warning should not get excluded")
self.assertEqual(warnings.return_count(), 1)
warnings.check('This should not be treated as warning2')
self.assertEqual(warnings.return_count(), 1)
warnings.check('ERROR [0.000s]: test_some_error_test (something.anything.somewhere)')
self.assertEqual(warnings.return_count(), 1)
excluded_deprecation_warning = "Excluded {!r} because of configured regex {!r}".format(deprecation_warning, "RemovedInSphinx\\d+Warning")
self.assertIn(excluded_deprecation_warning, verbose_output.getvalue())
excluded_toctree_warning = "Excluded {!r} because of configured regex {!r}".format(toctree_warning, "WARNING: toctree")
self.assertIn(excluded_toctree_warning, verbose_output.getvalue())
warning_echo = "home/bljah/test/index.rst:5: WARNING: this warning should not get excluded"
self.assertIn(warning_echo, verbose_output.getvalue())

def test_configfile_parsing_include_priority(self):
warnings = WarningsPlugin(verbose=True, config_file="tests/config_example_exclude.json")
warnings.get_checker('sphinx').include_sphinx_deprecation()
deprecation_warning = 'sphinx/application.py:402: RemovedInSphinx20Warning: app.info() is now deprecated. Use sphinx.util.logging instead.'
warnings.check(deprecation_warning)
self.assertEqual(warnings.return_count(), 1)

def test_partial_sphinx_config_parsing(self):
warnings = WarningsPlugin()
tmpjson = {
Expand Down Expand Up @@ -118,7 +126,7 @@ def test_partial_junit_config_parsing_exclude_regex(self):
with self.assertRaises(Exception) as exc:
warnings.config_parser_json(tmpjson)
self.assertEqual(str(exc.exception),
"Feature of regexes to exclude warnings is not configurable for the JUnitChecker.")
"Feature of regexes to include/exclude warnings is not configurable for the JUnitChecker.")

def test_partial_xmlrunner_config_parsing(self):
warnings = WarningsPlugin()
Expand Down
12 changes: 12 additions & 0 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,15 @@ def test_configfile_exclude_commandline(self):
with self.assertRaises(SystemExit) as ex:
warnings_wrapper(['--config', 'tests/config_example.json', '--junit', 'tests/junit_single_fail.xml'])
self.assertEqual(2, ex.exception.code)

def test_sphinx_deprecation(self):
retval = warnings_wrapper(['--sphinx', 'tests/sphinx_double_deprecation_warning.txt'])
self.assertEqual(0, retval)

def test_exclude_sphinx_deprecation(self):
retval = warnings_wrapper(['--sphinx', '--include-sphinx-deprecation', 'tests/sphinx_double_deprecation_warning.txt'])
self.assertEqual(2, retval)

def test_ignore_sphinx_deprecation_flag(self):
retval = warnings_wrapper(['--junit', '--include-sphinx-deprecation', 'tests/junit*.xml'])
self.assertEqual(self.junit_warning_cnt, retval)
11 changes: 11 additions & 0 deletions tests/test_sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ def test_multiline(self):
self.assertRegex(fake_out.getvalue(), duterr2)

def test_deprecation_warning(self):
duterr1 = "/usr/local/lib/python3.5/dist-packages/sphinx/application.py:402: RemovedInSphinx20Warning: "\
"app.info() is now deprecated. Use sphinx.util.logging instead. RemovedInSphinx20Warning\n"
dut = "This should not be treated as warning2\n"
dut += duterr1
with patch('sys.stdout', new=StringIO()) as fake_out:
self.warnings.check(dut)
self.assertEqual(self.warnings.return_count(), 0)
self.assertNotEqual(fake_out.getvalue(), duterr1)

def test_deprecation_warning_included(self):
self.warnings.get_checker('sphinx').include_sphinx_deprecation()
duterr1 = "/usr/local/lib/python3.5/dist-packages/sphinx/application.py:402: RemovedInSphinx20Warning: "\
"app.info() is now deprecated. Use sphinx.util.logging instead. RemovedInSphinx20Warning\n"
dut = "This1 should not be treated as warning\n"
Expand Down

0 comments on commit 27ca146

Please sign in to comment.