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.
This commit is contained in:
Ian Cordasco 2016-02-23 23:20:34 -06:00
parent 1cd5fea730
commit 24d2689a05
3 changed files with 80 additions and 0 deletions

View file

@ -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."""

View file

@ -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

View file

@ -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']