diff options
| author | Anthony Sottile <asottile@umich.edu> | 2018-12-10 17:47:05 +0000 |
|---|---|---|
| committer | Anthony Sottile <asottile@umich.edu> | 2018-12-10 17:47:05 +0000 |
| commit | cad4e5be6fd0bf025d2789f08fb6c9ba832e782f (patch) | |
| tree | 5a987994369ff6ff22cc9191a4faec4fb5ad00c8 | |
| parent | 9a9237a3386acb4a28b3eeb237e24cce973f17f6 (diff) | |
| parent | 2803d0a81045fb144cd874807809d5b6fcb91786 (diff) | |
| download | flake8-cad4e5be6fd0bf025d2789f08fb6c9ba832e782f.tar.gz | |
Merge branch 'physical-line-multiple' into 'master'
Allow physical checks to return multiple results
See merge request pycqa/flake8!269
| -rw-r--r-- | src/flake8/checker.py | 39 | ||||
| -rw-r--r-- | tests/integration/test_checker.py | 88 |
2 files changed, 109 insertions, 18 deletions
diff --git a/src/flake8/checker.py b/src/flake8/checker.py index 8b1655b..b0e0644 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -541,21 +541,38 @@ class FileChecker(object): self.processor.next_logical_line() def run_physical_checks(self, physical_line, override_error_line=None): - """Run all checks for a given physical line.""" + """Run all checks for a given physical line. + + A single physical check may return multiple errors. + """ for plugin in self.checks["physical_line_plugins"]: self.processor.update_checker_state_for(plugin) result = self.run_check(plugin, physical_line=physical_line) - if result is not None: - column_offset, text = result - error_code = self.report( - error_code=None, - line_number=self.processor.line_number, - column=column_offset, - text=text, - line=(override_error_line or physical_line), - ) - self.processor.check_physical_error(error_code, physical_line) + if result is not None: + # This is a single result if first element is an int + column_offset = None + try: + column_offset = result[0] + except (IndexError, TypeError): + pass + + if isinstance(column_offset, int): + # If we only have a single result, convert to a collection + result = (result,) + + for result_single in result: + column_offset, text = result_single + error_code = self.report( + error_code=None, + line_number=self.processor.line_number, + column=column_offset, + text=text, + line=(override_error_line or physical_line), + ) + self.processor.check_physical_error( + error_code, physical_line + ) def process_tokens(self): """Process tokens and trigger checks. diff --git a/tests/integration/test_checker.py b/tests/integration/test_checker.py index 3d10bfb..0a3703c 100644 --- a/tests/integration/test_checker.py +++ b/tests/integration/test_checker.py @@ -5,8 +5,17 @@ import pytest from flake8 import checker from flake8.plugins import manager +PHYSICAL_LINE = "# Physical line content" EXPECTED_REPORT = (1, 1, 'T000 Expected Message') +EXPECTED_REPORT_PHYSICAL_LINE = (1, 'T000 Expected Message') +EXPECTED_RESULT_PHYSICAL_LINE = ( + 'T000', + 0, + 1, + 'Expected Message', + PHYSICAL_LINE, +) class PluginClass(object): @@ -43,13 +52,48 @@ def plugin_func_list(tree): return [EXPECTED_REPORT + (type(plugin_func_list), )] -@pytest.mark.parametrize('plugin_target', [ - PluginClass, - plugin_func_gen, - plugin_func_list, -]) -def test_handle_file_plugins(plugin_target): - """Test the FileChecker class handling different file plugin types.""" +@plugin_func +def plugin_func_physical_ret(physical_line): + """Expect report from a physical_line. Single return.""" + return EXPECTED_REPORT_PHYSICAL_LINE + + +@plugin_func +def plugin_func_physical_none(physical_line): + """Expect report from a physical_line. No results.""" + return None + + +@plugin_func +def plugin_func_physical_list_single(physical_line): + """Expect report from a physical_line. List of single result.""" + return [EXPECTED_REPORT_PHYSICAL_LINE] + + +@plugin_func +def plugin_func_physical_list_multiple(physical_line): + """Expect report from a physical_line. List of multiple results.""" + return [EXPECTED_REPORT_PHYSICAL_LINE] * 2 + + +@plugin_func +def plugin_func_physical_gen_single(physical_line): + """Expect report from a physical_line. Generator of single result.""" + yield EXPECTED_REPORT_PHYSICAL_LINE + + +@plugin_func +def plugin_func_physical_gen_multiple(physical_line): + """Expect report from a physical_line. Generator of multiple results.""" + for _ in range(3): + yield EXPECTED_REPORT_PHYSICAL_LINE + + +def mock_file_checker_with_plugin(plugin_target): + """Get a mock FileChecker class with plugin_target registered. + + Useful as a starting point for mocking reports/results. + """ # Mock an entry point returning the plugin target entry_point = mock.Mock(spec=['load']) entry_point.name = plugin_target.name @@ -67,6 +111,17 @@ def test_handle_file_plugins(plugin_target): checks.to_dictionary(), mock.MagicMock() ) + return file_checker + + +@pytest.mark.parametrize('plugin_target', [ + PluginClass, + plugin_func_gen, + plugin_func_list, +]) +def test_handle_file_plugins(plugin_target): + """Test the FileChecker class handling different file plugin types.""" + file_checker = mock_file_checker_with_plugin(plugin_target) # Do not actually build an AST file_checker.processor.build_ast = lambda: True @@ -81,6 +136,25 @@ def test_handle_file_plugins(plugin_target): text=EXPECTED_REPORT[2]) +@pytest.mark.parametrize('plugin_target,len_results', [ + (plugin_func_physical_ret, 1), + (plugin_func_physical_none, 0), + (plugin_func_physical_list_single, 1), + (plugin_func_physical_list_multiple, 2), + (plugin_func_physical_gen_single, 1), + (plugin_func_physical_gen_multiple, 3), +]) +def test_line_check_results(plugin_target, len_results): + """Test the FileChecker class handling results from line checks.""" + file_checker = mock_file_checker_with_plugin(plugin_target) + + # Results will be store in an internal array + file_checker.run_physical_checks(PHYSICAL_LINE) + assert file_checker.results == [ + EXPECTED_RESULT_PHYSICAL_LINE + ] * len_results + + PLACEHOLDER_CODE = 'some_line = "of" * code' |
