From 156f90369f007928f26c6a4980b1ac0cc0691e53 Mon Sep 17 00:00:00 2001 From: Ian Stapleton Cordasco Date: Tue, 8 Aug 2017 08:54:40 -0500 Subject: [PATCH] Update our plugin registration for debugging This allows us to report whether or not a plugin is local when users provide `flake8 --bug-report` output. --- src/flake8/main/debug.py | 10 ++++++-- src/flake8/options/manager.py | 17 ++++++++++---- src/flake8/plugins/manager.py | 2 -- tests/unit/test_debug.py | 35 ++++++++++++++++++++-------- tests/unit/test_option_manager.py | 38 ++++++++++++++++--------------- tests/unit/test_plugin.py | 11 --------- 6 files changed, 65 insertions(+), 48 deletions(-) diff --git a/src/flake8/main/debug.py b/src/flake8/main/debug.py index e6ea141..ca3827e 100644 --- a/src/flake8/main/debug.py +++ b/src/flake8/main/debug.py @@ -53,8 +53,14 @@ def information(option_manager): def plugins_from(option_manager): """Generate the list of plugins installed.""" - return [{'plugin': plugin, 'version': version} - for (plugin, version) in sorted(option_manager.registered_plugins)] + return [ + { + 'plugin': plugin.name, + 'version': plugin.version, + 'is_local': plugin.local, + } + for plugin in sorted(option_manager.registered_plugins) + ] def dependencies(): diff --git a/src/flake8/options/manager.py b/src/flake8/options/manager.py index 7b23732..5b4796f 100644 --- a/src/flake8/options/manager.py +++ b/src/flake8/options/manager.py @@ -1,4 +1,5 @@ """Option handling and Option management logic.""" +import collections import logging import optparse # pylint: disable=deprecated-module @@ -153,6 +154,10 @@ class Option(object): return self._opt +PluginVersion = collections.namedtuple("PluginVersion", + ["name", "version", "local"]) + + class OptionManager(object): """Manage Options and OptionParser while adding post-processing.""" @@ -178,9 +183,9 @@ class OptionManager(object): self.extended_default_select = set() @staticmethod - def format_plugin(plugin_tuple): - """Convert a plugin tuple into a dictionary mapping name to value.""" - return dict(zip(["name", "version"], plugin_tuple)) + def format_plugin(plugin): + """Convert a PluginVersion into a dictionary mapping name to value.""" + return {attr: getattr(plugin, attr) for attr in ["name", "version"]} def add_option(self, *args, **kwargs): """Create and register a new option. @@ -306,7 +311,7 @@ class OptionManager(object): self._normalize(options) return options, xargs - def register_plugin(self, name, version): + def register_plugin(self, name, version, local=False): """Register a plugin relying on the OptionManager. :param str name: @@ -314,5 +319,7 @@ class OptionManager(object): attribute of the class or function loaded from the entry-point. :param str version: The version of the checker that we're using. + :param bool local: + Whether the plugin is local to the project/repository or not. """ - self.registered_plugins.add((name, version)) + self.registered_plugins.add(PluginVersion(name, version, local)) diff --git a/src/flake8/plugins/manager.py b/src/flake8/plugins/manager.py index 699b1aa..503dfbb 100644 --- a/src/flake8/plugins/manager.py +++ b/src/flake8/plugins/manager.py @@ -115,8 +115,6 @@ class Plugin(object): self._version = version_for(self) else: self._version = self.plugin.version - if self.local: - self._version += ' [local]' return self._version diff --git a/tests/unit/test_debug.py b/tests/unit/test_debug.py index b416378..7253bcc 100644 --- a/tests/unit/test_debug.py +++ b/tests/unit/test_debug.py @@ -4,6 +4,7 @@ import pytest import setuptools from flake8.main import debug +from flake8.options import manager def test_dependencies(): @@ -15,11 +16,18 @@ def test_dependencies(): @pytest.mark.parametrize('plugins, expected', [ ([], []), - ([('pycodestyle', '2.0.0')], [{'plugin': 'pycodestyle', - 'version': '2.0.0'}]), - ([('pycodestyle', '2.0.0'), ('mccabe', '0.5.9')], - [{'plugin': 'mccabe', 'version': '0.5.9'}, - {'plugin': 'pycodestyle', 'version': '2.0.0'}]), + ([manager.PluginVersion('pycodestyle', '2.0.0', False)], + [{'plugin': 'pycodestyle', 'version': '2.0.0', 'is_local': False}]), + ([manager.PluginVersion('pycodestyle', '2.0.0', False), + manager.PluginVersion('mccabe', '0.5.9', False)], + [{'plugin': 'mccabe', 'version': '0.5.9', 'is_local': False}, + {'plugin': 'pycodestyle', 'version': '2.0.0', 'is_local': False}]), + ([manager.PluginVersion('pycodestyle', '2.0.0', False), + manager.PluginVersion('my-local', '0.0.1', True), + manager.PluginVersion('mccabe', '0.5.9', False)], + [{'plugin': 'mccabe', 'version': '0.5.9', 'is_local': False}, + {'plugin': 'my-local', 'version': '0.0.1', 'is_local': True}, + {'plugin': 'pycodestyle', 'version': '2.0.0', 'is_local': False}]), ]) def test_plugins_from(plugins, expected): """Test that we format plugins appropriately.""" @@ -34,8 +42,10 @@ def test_information(system, pyversion, pyimpl): """Verify that we return all the information we care about.""" expected = { 'version': '3.1.0', - 'plugins': [{'plugin': 'mccabe', 'version': '0.5.9'}, - {'plugin': 'pycodestyle', 'version': '2.0.0'}], + 'plugins': [{'plugin': 'mccabe', 'version': '0.5.9', + 'is_local': False}, + {'plugin': 'pycodestyle', 'version': '2.0.0', + 'is_local': False}], 'dependencies': [{'dependency': 'setuptools', 'version': setuptools.__version__}], 'platform': { @@ -45,8 +55,10 @@ def test_information(system, pyversion, pyimpl): }, } option_manager = mock.Mock( - registered_plugins={('pycodestyle', '2.0.0'), - ('mccabe', '0.5.9')}, + registered_plugins={ + manager.PluginVersion('pycodestyle', '2.0.0', False), + manager.PluginVersion('mccabe', '0.5.9', False), + }, version='3.1.0', ) assert expected == debug.information(option_manager) @@ -75,7 +87,10 @@ def test_print_information_no_plugins(dumps, information, print_mock): @mock.patch('json.dumps', return_value='{}') def test_print_information(dumps, information, print_mock): """Verify we print and exit only when we have plugins.""" - plugins = [('pycodestyle', '2.0.0'), ('mccabe', '0.5.9')] + plugins = [ + manager.PluginVersion('pycodestyle', '2.0.0', False), + manager.PluginVersion('mccabe', '0.5.9', False), + ] option_manager = mock.Mock(registered_plugins=set(plugins)) with pytest.raises(SystemExit): debug.print_information( diff --git a/tests/unit/test_option_manager.py b/tests/unit/test_option_manager.py index 950f191..0f5c058 100644 --- a/tests/unit/test_option_manager.py +++ b/tests/unit/test_option_manager.py @@ -121,7 +121,9 @@ def test_parse_args_normalize_paths(optmanager): def test_format_plugin(): """Verify that format_plugin turns a tuple into a dictionary.""" - plugin = manager.OptionManager.format_plugin(('Testing', '0.0.0')) + plugin = manager.OptionManager.format_plugin( + manager.PluginVersion('Testing', '0.0.0', False) + ) assert plugin['name'] == 'Testing' assert plugin['version'] == '0.0.0' @@ -129,9 +131,9 @@ def test_format_plugin(): def test_generate_versions(optmanager): """Verify a comma-separated string is generated of registered plugins.""" optmanager.registered_plugins = [ - ('Testing 100', '0.0.0'), - ('Testing 101', '0.0.0'), - ('Testing 300', '0.0.0'), + manager.PluginVersion('Testing 100', '0.0.0', False), + manager.PluginVersion('Testing 101', '0.0.0', False), + manager.PluginVersion('Testing 300', '0.0.0', True), ] assert (optmanager.generate_versions() == 'Testing 100: 0.0.0, Testing 101: 0.0.0, Testing 300: 0.0.0') @@ -140,11 +142,11 @@ def test_generate_versions(optmanager): def test_plugins_are_sorted_in_generate_versions(optmanager): """Verify we sort before joining strings in generate_versions.""" optmanager.registered_plugins = [ - ('pyflakes', '1.5.0'), - ('mccabe', '0.7.0'), - ('pycodestyle', '2.2.0'), - ('flake8-docstrings', '0.6.1'), - ('flake8-bugbear', '2016.12.1'), + manager.PluginVersion('pyflakes', '1.5.0', False), + manager.PluginVersion('mccabe', '0.7.0', False), + manager.PluginVersion('pycodestyle', '2.2.0', False), + manager.PluginVersion('flake8-docstrings', '0.6.1', False), + manager.PluginVersion('flake8-bugbear', '2016.12.1', False), ] assert (optmanager.generate_versions() == 'flake8-bugbear: 2016.12.1, ' @@ -157,9 +159,9 @@ def test_plugins_are_sorted_in_generate_versions(optmanager): def test_generate_versions_with_format_string(optmanager): """Verify a comma-separated string is generated of registered plugins.""" optmanager.registered_plugins.update([ - ('Testing', '0.0.0'), - ('Testing', '0.0.0'), - ('Testing', '0.0.0'), + manager.PluginVersion('Testing', '0.0.0', False), + manager.PluginVersion('Testing', '0.0.0', False), + manager.PluginVersion('Testing', '0.0.0', False), ]) assert ( optmanager.generate_versions() == 'Testing: 0.0.0' @@ -172,9 +174,9 @@ def test_update_version_string(optmanager): assert optmanager.parser.version == TEST_VERSION optmanager.registered_plugins = [ - ('Testing 100', '0.0.0'), - ('Testing 101', '0.0.0'), - ('Testing 300', '0.0.0'), + manager.PluginVersion('Testing 100', '0.0.0', False), + manager.PluginVersion('Testing 101', '0.0.0', False), + manager.PluginVersion('Testing 300', '0.0.0', False), ] optmanager.update_version_string() @@ -190,9 +192,9 @@ def test_generate_epilog(optmanager): assert optmanager.parser.epilog is None optmanager.registered_plugins = [ - ('Testing 100', '0.0.0'), - ('Testing 101', '0.0.0'), - ('Testing 300', '0.0.0'), + manager.PluginVersion('Testing 100', '0.0.0', False), + manager.PluginVersion('Testing 101', '0.0.0', False), + manager.PluginVersion('Testing 300', '0.0.0', False), ] expected_value = ( diff --git a/tests/unit/test_plugin.py b/tests/unit/test_plugin.py index d6bc27f..84f676a 100644 --- a/tests/unit/test_plugin.py +++ b/tests/unit/test_plugin.py @@ -111,17 +111,6 @@ def test_version_proxies_to_the_plugin(): assert plugin.version == 'a.b.c' -def test_local_plugin_version(): - """Verify that local plugins have [local] appended to version.""" - entry_point = mock.Mock(spec=['require', 'resolve', 'load']) - plugin_obj = mock.Mock(spec_set=['version']) - plugin_obj.version = 'a.b.c' - plugin = manager.Plugin('T000', entry_point, local=True) - plugin._plugin = plugin_obj - - assert plugin.version == 'a.b.c [local]' - - def test_register_options(): """Verify we call add_options on the plugin only if it exists.""" # Set up our mocks and Plugin object