From 24d2689a0519cb453eb67d6a0ef37f60efc8f0fd Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Tue, 23 Feb 2016 23:20:34 -0600 Subject: [PATCH] Distinguish check types via plugin type manager Flake8 and pep8 has historically supported three types of checks: - Plugins that accept the physical line - Plugins that accept the logical line - Plugins that accept the AST tree The logical place to make this distinction is on the Checkers plugin type manager class. This adds the foundation for finding plugins that fall into each class. --- flake8/plugins/manager.py | 20 ++++++++++++++++++++ flake8/utils.py | 38 ++++++++++++++++++++++++++++++++++++++ tests/unit/test_utils.py | 22 ++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/flake8/plugins/manager.py b/flake8/plugins/manager.py index 21171ef..21639f9 100644 --- a/flake8/plugins/manager.py +++ b/flake8/plugins/manager.py @@ -6,6 +6,7 @@ import pkg_resources from flake8 import exceptions from flake8.plugins import notifier +from flake8 import utils LOG = logging.getLogger(__name__) @@ -296,6 +297,25 @@ class Checkers(PluginTypeManager): namespace = 'flake8.extension' + def checks_expecting(self, argument_name): + """Retrieve checks that expect an argument with the specified name. + + Find all checker plugins that are expecting a specific argument. + """ + for plugin in self.plugins.values(): + parameters = utils.parameters_for(plugin) + if argument_name in parameters: + yield plugin + + @property + def physical_line_plugins(self): + """List of plugins that expect the physical lines.""" + plugins = getattr(self, '_physical_line_plugins', []) + if not plugins: + plugins = list(self.checks_expecting('physical_line')) + self._physical_line_plugins = plugins + return plugins + class Listeners(PluginTypeManager, NotifierBuilderMixin): """All of the listeners registered through entry-points.""" diff --git a/flake8/utils.py b/flake8/utils.py index fbc90bd..cd1cf72 100644 --- a/flake8/utils.py +++ b/flake8/utils.py @@ -1,5 +1,6 @@ """Utility methods for flake8.""" import fnmatch as _fnmatch +import inspect import io import os import sys @@ -144,3 +145,40 @@ def fnmatch(filename, patterns, default=True): if not patterns: return default return any(_fnmatch.fnmatch(filename, pattern) for pattern in patterns) + + +def parameters_for(plugin): + # type: (flake8.plugins.manager.Plugin) -> List[str] + """Return the parameters for the plugin. + + This will inspect the plugin and return either the function parameters + if the plugin is a function or the parameters for ``__init__`` after + ``self`` if the plugin is a class. + + :param plugin: + The internal plugin object. + :type plugin: + flake8.plugins.manager.Plugin + :returns: + Parameters to the plugin. + :rtype: + list(str) + """ + func = plugin.plugin + is_class = not inspect.isfunction(func) + if is_class: # The plugin is a class + func = plugin.plugin.__init__ + + if sys.version_info < (3, 3): + parameters = inspect.getargspec(func)[0] + else: + parameters = [ + parameter.name + for parameter in inspect.signature(func).parameters.values() + if parameter.kind == parameter.POSITIONAL_OR_KEYWORD + ] + + if is_class: + parameters.remove('self') + + return parameters diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 34b4909..615f327 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -4,6 +4,7 @@ import mock import pytest +from flake8.plugins import manager as plugin_manager from flake8 import utils @@ -86,3 +87,24 @@ def test_filenames_from_a_single_file(): assert len(filenames) == 1 assert ['flake8/__init__.py'] == filenames + + +def test_parameters_for_class_plugin(): + """Verify that we can retrieve the parameters for a class plugin.""" + class FakeCheck(object): + def __init__(self, tree): + pass + + plugin = plugin_manager.Plugin('plugin-name', object()) + plugin._plugin = FakeCheck + assert utils.parameters_for(plugin) == ['tree'] + + +def test_parameters_for_function_plugin(): + """Verify that we retrieve the parameters for a function plugin.""" + def fake_plugin(physical_line, self, tree): + pass + + plugin = plugin_manager.Plugin('plugin-name', object()) + plugin._plugin = fake_plugin + assert utils.parameters_for(plugin) == ['physical_line', 'self', 'tree']