def execute(self): summary = { 'started': datetime.now(), } summary.update(self.config.get_summary_information()) found_files = find_python(self.config.ignores, self.config.paths, self.config.explicit_file_mode, self.config.workdir) # Run the tools messages = [] for tool in self.config.get_tools(found_files): try: messages += tool.run(found_files) except Exception: # pylint: disable=broad-except if self.config.die_on_tool_error: raise else: for name, cls in tools.TOOLS.items(): if cls == tool.__class__: toolname = name break else: toolname = 'Unknown' loc = Location(self.config.workdir, None, None, None, None) msg = 'Tool %s failed to run (exception was raised)' % ( toolname, ) message = Message( toolname, 'failure', loc, message=msg, ) messages.append(message) messages = self.process_messages(found_files, messages) summary['message_count'] = len(messages) summary['completed'] = datetime.now() # Timedelta.total_seconds() is not available # on Python<=2.6 so we calculate it ourselves # See issue #60 and http://stackoverflow.com/a/3694895 delta = (summary['completed'] - summary['started']) total_seconds = (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 1e6) / 1e6 summary['time_taken'] = '%0.2f' % total_seconds external_config = [] for tool, configured_by in self.config.configured_by.items(): if configured_by is not None: external_config.append((tool, configured_by)) if len(external_config) > 0: summary['external_config'] = ', '.join(['%s: %s' % info for info in external_config]) self.summary = summary self.messages = messages
def test_vulture_find_dead_code(self): root = os.path.join(os.path.dirname(__file__), "testpath", "testfile.py") found_files = find_python([], [root], explicit_file_mode=True) self.vulture_tool.configure(self.config, found_files) messages = self.vulture_tool.run(found_files) self.assertTrue( any(message.code in ["unused-variable", "unused-import"] for message in messages))
def test_absolute_path_is_computed_correctly(self): pylint_tool, config = _get_pylint_tool_and_prospector_config() root = os.path.join(os.path.dirname(__file__), "testpath", "test.py") root_sep_split = root.split(os.path.sep) root_os_split = os.path.split(root) found_files = find_python([], [root], explicit_file_mode=True) pylint_tool.configure(config, found_files) self.assertNotEqual(pylint_tool._args, [os.path.join(*root_sep_split)]) self.assertEqual(pylint_tool._args, [os.path.join(*root_os_split)])
def test_hardcoded_password_string(self): root = os.path.join(os.path.dirname(__file__), "testpath", "testfile.py") found_files = find_python([], [root], explicit_file_mode=True) self.bandit_tool.configure(self.config, found_files) messages = self.bandit_tool.run(found_files) self.assertTrue( any(message.code in ["B107", "B105", "B106"] for message in messages))
def execute(self): summary = { 'started': datetime.now(), 'libraries': self.libraries, 'strictness': self.strictness, 'profiles': self.profiles, 'adaptors': [adaptor.name for adaptor in self.adaptors], 'tools': self.tools_to_run, } # Find the files and packages in a common way, so that each tool # gets the same list. found_files = find_python(self.ignores, self.path) # Prep the tools. for tool in self.tool_runners: tool.prepare(found_files, self.config, self.adaptors) # Run the tools messages = [] for tool in self.tool_runners: try: messages += tool.run() except Exception: # pylint: disable=W0703 if self.config.die_on_tool_error: raise else: for name, cls in tools.TOOLS.items(): if cls == tool.__class__: toolname = name break else: toolname = 'Unknown' loc = Location(self.path, None, None, None, None) msg = 'Tool %s failed to run (exception was raised)' % ( toolname, ) message = Message( toolname, 'failure', loc, message=msg, ) messages.append(message) messages = self.process_messages(messages) summary['message_count'] = len(messages) summary['completed'] = datetime.now() delta = (summary['completed'] - summary['started']) summary['time_taken'] = '%0.2f' % delta.total_seconds() self.summary = summary self.messages = messages
def test_pycodestyle_space_and_tabs(self): root = os.path.join(os.path.dirname(__file__), "testpath", "test_space_tab.py") found_files = find_python([], [root], explicit_file_mode=True) self.pep8_tool.configure(self.config, found_files) messages = self.pep8_tool.run([]) self.assertTrue(any(message.code == "E101" for message in messages)) self.assertTrue(any(message.code == "E111" for message in messages)) self.assertTrue(any(message.code == "W191" for message in messages)) self.assertTrue(all(message.source == "pep8" for message in messages))
def _run_test(self, name, expected): root = os.path.join(os.path.dirname(__file__), 'testdata', name) files = find_python([], root) expected = [os.path.join(root, e).rstrip(os.path.sep) for e in expected] actual = files.get_minimal_syspath() expected.sort(key=lambda x: len(x)) self.assertEqual(actual, expected)
def _assert_find_files(name, expected, explicit_file_mode=False): root = _TEST_DIR / name files = find_python([], [str(root)], explicit_file_mode=explicit_file_mode) expected = [os.path.relpath(os.path.join(str(root), e).rstrip(os.path.sep)) for e in expected] expected.append(files.rootpath) actual = files.get_minimal_syspath() expected.sort(key=lambda x: len(x)) assert actual == expected
def test_absolute_path_is_computed_correctly(self): root = os.path.join(os.path.dirname(__file__), 'testpath', 'test.py') root_sep_split = root.split(os.path.sep) root_os_split = os.path.split(root) found_files = find_python([], [root], explicit_file_mode=True) self.pylint_tool.configure(self.config, found_files) self.assertNotEqual(self.pylint_tool._args, [os.path.join(*root_sep_split)]) self.assertEqual(self.pylint_tool._args, [os.path.join(*root_os_split)])
def test_use_prospector_default_path_finder(self): workdir = "tests/tools/pylint/testpath/absolute-import/" with patch("os.getcwd", return_value=os.path.realpath(workdir)): pylint_tool, config = _get_pylint_tool_and_prospector_config( argv_patch=["", "-P", "prospector-default-finder"]) root = os.path.join(os.path.dirname(__file__), "testpath", "absolute-import", "pkg") found_files = find_python([], [root], False) pylint_tool.configure(config, found_files) messages = pylint_tool.run(found_files) self.assertEqual(messages[0].code, "no-name-in-module")
def test_wont_throw_false_positive_relative_beyond_top_level(self): with patch( "os.getcwd", return_value=os.path.realpath("tests/tools/pylint/testpath/")): pylint_tool, config = _get_pylint_tool_and_prospector_config() root = os.path.join(os.path.dirname(__file__), "testpath", "src", "mcve", "foobar.py") found_files = find_python([], [root], explicit_file_mode=True) pylint_tool.configure(config, found_files) messages = pylint_tool.run(found_files) self.assertListEqual(messages, [])
def _run_test(self, name, expected): root = os.path.join(os.path.dirname(__file__), 'testdata', name) files = find_python([], [root], explicit_file_mode=False) expected = [ os.path.join(root, e).rstrip(os.path.sep) for e in expected ] actual = files.get_minimal_syspath() expected.sort(key=lambda x: len(x)) self.assertEqual(actual, expected)
def test_find_pycodestyle_section_in_config(self): workdir = os.path.join(os.path.dirname(__file__), "testsettings", "pycodestyle") root = os.path.join(os.path.dirname(__file__), "testsettings", "pycodestyle", "testfile.py") found_files = find_python([], [root], explicit_file_mode=True, workdir=workdir) configured_by, _ = self.pep8_tool.configure(self.config, found_files) expected_config_path = os.path.join(workdir, "setup.cfg") self.assertEqual(configured_by, "Configuration found at %s" % expected_config_path)
def test_no_duplicates_in_checkpath(self): """ This checks that the pylint tool will not generate a list of packages and subpackages - if there is a hierarchy there is no need to duplicate sub-packages in the list to be checked """ root = THIS_DIR / "duplicates_test" files = find_python([], [str(root)], explicit_file_mode=False) tool, config = _get_pylint_tool_and_prospector_config() check_paths = tool._get_pylint_check_paths(files) assert len(check_paths) == 1 assert [str(Path(p).relative_to(root)) for p in check_paths] == ["pkg1"]
def test_will_throw_useless_suppression(self): with patch( "os.getcwd", return_value=os.path.realpath("tests/tools/pylint/testpath/")): pylint_tool, config = _get_pylint_tool_and_prospector_config( argv_patch=["", "-t", "pylint"]) root = os.path.join(os.path.dirname(__file__), "testpath", "test_useless_suppression.py") found_files = find_python([], [root], explicit_file_mode=True) pylint_tool.configure(config, found_files) messages = pylint_tool.run(found_files) assert any( m.code == "useless-suppression" for m in messages), "There should be at least one useless suppression"
def test_wont_throw_useless_suppression(self): with patch( "os.getcwd", return_value=os.path.realpath("tests/tools/pylint/testpath/")): pylint_tool, config = _get_pylint_tool_and_prospector_config( argv_patch=["", "-t", "pylint"]) root = os.path.join(os.path.dirname(__file__), "testpath", "test_useless_suppression.py") found_files = find_python([], [root], explicit_file_mode=True) pylint_tool.configure(config, found_files) messages = pylint_tool.run(found_files) for message in messages: if message.code == "useless-suppression": self.fail("useless-suppression was thrown")
def _assert_find_files(self, name, expected, explicit_file_mode=False): root = os.path.join(os.path.dirname(__file__), "testdata", name) files = find_python([], [root], explicit_file_mode=explicit_file_mode) expected = [ os.path.relpath(os.path.join(root, e).rstrip(os.path.sep)) for e in expected ] expected.append(files.rootpath) actual = files.get_minimal_syspath() expected.sort(key=lambda x: len(x)) self.assertEqual(actual, expected)
def test_use_pylint_default_path_finder(self): workdir = os.path.realpath( "tests/tools/pylint/testpath/absolute-import/") pylint_tool, config = _get_pylint_tool_and_prospector_config( argv_patch=[ "", "-P", os.path.join(workdir, ".prospector", "pylint-default-finder.yml") ]) root = os.path.join(os.path.dirname(__file__), "testpath", "absolute-import", "pkg") found_files = find_python([], [root], False, workdir) pylint_tool.configure(config, found_files) messages = pylint_tool.run(found_files) self.assertListEqual(messages, [])
def test_pylint_config(self): """Verifies that prospector will configure pylint with any pylint-specific configuration if found""" def _has_message(msg_list, code): return any([ message.code == code and message.source == "pylint" for message in msg_list ]) for config_type in ("pylintrc", "pylintrc2", "pyproject", "setup.cfg"): root = THIS_DIR / "pylint_configs" / config_type with patch("os.getcwd", return_value=root.absolute()): pylint_tool, config = _get_pylint_tool_and_prospector_config() self.assertEqual(Path(config.workdir).absolute(), root.absolute()) found_files = find_python([], [str(root)], explicit_file_mode=False, workdir=str(root)) pylint_tool.configure(config, found_files) messages = pylint_tool.run(found_files) self.assertTrue(_has_message(messages, "line-too-long"), msg=config_type)
def execute(self): summary = { 'started': datetime.now(), } summary.update(self.config.get_summary_information()) found_files = find_python(self.config.ignores, self.config.paths, self.config.explicit_file_mode, self.config.workdir) # Run the tools messages = [] for tool in self.config.get_tools(found_files): for name, cls in tools.TOOLS.items(): if cls == tool.__class__: toolname = name break else: toolname = 'Unknown' try: # Tools can output to stdout/stderr in unexpected places, for example, # pep257 emits warnings about __all__ and as pyroma exec's the setup.py # file, it will execute any print statements in that, etc etc... with capture_output(hide=not self.config.direct_tool_stdout) as capture: messages += tool.run(found_files) if self.config.include_tool_stdout: loc = Location(self.config.workdir, None, None, None, None) if capture.get_hidden_stderr(): msg = 'stderr from %s:\n%s' % (toolname, capture.get_hidden_stderr()) messages.append(Message(toolname, 'hidden-output', loc, message=msg)) if capture.get_hidden_stdout(): msg = 'stdout from %s:\n%s' % (toolname, capture.get_hidden_stdout()) messages.append(Message(toolname, 'hidden-output', loc, message=msg)) except Exception: # pylint: disable=broad-except if self.config.die_on_tool_error: raise else: loc = Location(self.config.workdir, None, None, None, None) msg = 'Tool %s failed to run (exception was raised)' % ( toolname, ) message = Message( toolname, 'failure', loc, message=msg, ) messages.append(message) messages = self.process_messages(found_files, messages) summary['message_count'] = len(messages) summary['completed'] = datetime.now() # Timedelta.total_seconds() is not available # on Python<=2.6 so we calculate it ourselves # See issue #60 and http://stackoverflow.com/a/3694895 delta = (summary['completed'] - summary['started']) total_seconds = (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 1e6) / 1e6 summary['time_taken'] = '%0.2f' % total_seconds external_config = [] for tool, configured_by in self.config.configured_by.items(): if configured_by is not None: external_config.append((tool, configured_by)) if len(external_config) > 0: summary['external_config'] = ', '.join(['%s: %s' % info for info in external_config]) self.summary = summary self.messages = self.messages + messages
def execute(self): summary = { 'started': datetime.now(), } summary.update(self.config.get_summary_information()) found_files = find_python(self.config.ignores, self.config.paths, self.config.explicit_file_mode, self.config.workdir) # Run the tools messages = [] for tool in self.config.get_tools(found_files): for name, cls in tools.TOOLS.items(): if cls == tool.__class__: toolname = name break else: toolname = 'Unknown' try: # Tools can output to stdout/stderr in unexpected places, for example, # pep257 emits warnings about __all__ and as pyroma exec's the setup.py # file, it will execute any print statements in that, etc etc... with capture_output( hide=not self.config.direct_tool_stdout) as capture: messages += tool.run(found_files) if self.config.include_tool_stdout: loc = Location(self.config.workdir, None, None, None, None) if capture.get_hidden_stderr(): msg = 'stderr from %s:\n%s' % ( toolname, capture.get_hidden_stderr()) messages.append( Message(toolname, 'hidden-output', loc, message=msg)) if capture.get_hidden_stdout(): msg = 'stdout from %s:\n%s' % ( toolname, capture.get_hidden_stdout()) messages.append( Message(toolname, 'hidden-output', loc, message=msg)) except FatalProspectorException as fatal: sys.stderr.write(fatal.message) sys.exit(2) except Exception: # pylint: disable=broad-except if self.config.die_on_tool_error: raise else: loc = Location(self.config.workdir, None, None, None, None) msg = 'Tool %s failed to run (exception was raised)' % ( toolname, ) message = Message( toolname, 'failure', loc, message=msg, ) messages.append(message) messages = self.process_messages(found_files, messages) summary['message_count'] = len(messages) summary['completed'] = datetime.now() # Timedelta.total_seconds() is not available # on Python<=2.6 so we calculate it ourselves # See issue #60 and http://stackoverflow.com/a/3694895 delta = (summary['completed'] - summary['started']) total_seconds = (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 1e6) / 1e6 summary['time_taken'] = '%0.2f' % total_seconds external_config = [] for tool, configured_by in self.config.configured_by.items(): if configured_by is not None: external_config.append((tool, configured_by)) if len(external_config) > 0: summary['external_config'] = ', '.join( ['%s: %s' % info for info in external_config]) self.summary = summary self.messages = self.messages + messages
def test_unrecognised_options(self): finder = find_python([], [], True, Path(__file__).parent.absolute()) self.assertRaises(BadToolConfig, self._get_config("mypy_bad_options").get_tools, finder)
def execute(self): deprecated_names = self.config.replace_deprecated_tool_names() summary = { "started": datetime.now(), } summary.update(self.config.get_summary_information()) found_files = find_python( self.config.ignores, self.config.paths, self.config.explicit_file_mode, self.config.workdir, ) messages = [] # see if any old tool names are run for deprecated_name in deprecated_names: loc = Location(self.config.workdir, None, None, None, None) new_name = DEPRECATED_TOOL_NAMES[deprecated_name] msg = ( f"Tool {deprecated_name} has been renamed to {new_name}. " f"The old name {deprecated_name} is now deprecated and will be removed in Prospector 2.0. " f"Please update your prospector configuration.") message = Message( "prospector", "Deprecation", loc, message=msg, ) messages.append(message) warnings.warn(msg, category=DeprecationWarning) # Run the tools for tool in self.config.get_tools(found_files): for name, cls in tools.TOOLS.items(): if cls == tool.__class__: toolname = name break else: toolname = "Unknown" try: # Tools can output to stdout/stderr in unexpected places, for example, # pydocstyle emits warnings about __all__ and as pyroma exec's the setup.py # file, it will execute any print statements in that, etc etc... with CaptureOutput( hide=not self.config.direct_tool_stdout) as capture: messages += tool.run(found_files) if self.config.include_tool_stdout: loc = Location(self.config.workdir, None, None, None, None) if capture.get_hidden_stderr(): msg = f"stderr from {toolname}:\n{capture.get_hidden_stderr()}" messages.append( Message(toolname, "hidden-output", loc, message=msg)) if capture.get_hidden_stdout(): msg = f"stdout from {toolname}:\n{capture.get_hidden_stdout()}" messages.append( Message(toolname, "hidden-output", loc, message=msg)) except FatalProspectorException as fatal: sys.stderr.write(fatal.message) sys.exit(2) except Exception as ex: # pylint:disable=broad-except if self.config.die_on_tool_error: raise FatalProspectorException from ex loc = Location(self.config.workdir, None, None, None, None) msg = ( f"Tool {toolname} failed to run " f"(exception was raised, re-run prospector with -X to see the stacktrace)" ) message = Message( toolname, "failure", loc, message=msg, ) messages.append(message) messages = self.process_messages(found_files, messages) summary["message_count"] = len(messages) summary["completed"] = datetime.now() # Timedelta.total_seconds() is not available # on Python<=2.6 so we calculate it ourselves # See issue #60 and http://stackoverflow.com/a/3694895 delta = summary["completed"] - summary["started"] total_seconds = (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 1e6) / 1e6 summary["time_taken"] = "%0.2f" % total_seconds external_config = [] for tool, configured_by in self.config.configured_by.items(): if configured_by is not None: external_config.append((tool, configured_by)) if len(external_config) > 0: summary["external_config"] = ", ".join( ["%s: %s" % info for info in external_config]) self.summary = summary self.messages = self.messages + messages
def test_good_options(self): finder = find_python([], [], True, Path(__file__).parent.absolute()) self._get_config("mypy_good_options").get_tools(finder)
def execute(self): summary = { 'started': datetime.now(), } summary.update(self.config.get_summary_information()) found_files = find_python(self.config.ignores, self.config.paths, self.config.explicit_file_mode, self.config.workdir) # Run the tools messages = [] for tool in self.config.get_tools(found_files): try: messages += tool.run(found_files) except Exception: # pylint: disable=broad-except if self.config.die_on_tool_error: raise else: for name, cls in tools.TOOLS.items(): if cls == tool.__class__: toolname = name break else: toolname = 'Unknown' loc = Location(self.config.workdir, None, None, None, None) msg = 'Tool %s failed to run (exception was raised)' % ( toolname, ) message = Message( toolname, 'failure', loc, message=msg, ) messages.append(message) messages = self.process_messages(found_files, messages) summary['message_count'] = len(messages) summary['completed'] = datetime.now() # Timedelta.total_seconds() is not available # on Python<=2.6 so we calculate it ourselves # See issue #60 and http://stackoverflow.com/a/3694895 delta = (summary['completed'] - summary['started']) total_seconds = (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 1e6) / 1e6 summary['time_taken'] = '%0.2f' % total_seconds external_config = [] for tool, configured_by in self.config.configured_by.items(): if configured_by is not None: external_config.append((tool, configured_by)) if len(external_config) > 0: summary['external_config'] = ', '.join( ['%s: %s' % info for info in external_config]) self.summary = summary self.messages = messages