mirror of
https://github.com/PyCQA/flake8.git
synced 2026-03-30 18:56:53 +00:00
Merge branch 'remove_unused_broken_flake8_listen' into 'master'
Remove unused and broken flake8.listen plugin type Closes #480 See merge request pycqa/flake8!274
This commit is contained in:
commit
7f50c3acc4
11 changed files with 19 additions and 525 deletions
|
|
@ -4,15 +4,13 @@ Plugin Handling
|
|||
Plugin Management
|
||||
-----------------
|
||||
|
||||
|Flake8| 3.0 added support for two other plugins besides those which define
|
||||
|Flake8| 3.0 added support for other plugins besides those which define
|
||||
new checks. It now supports:
|
||||
|
||||
- extra checks
|
||||
|
||||
- alternative report formatters
|
||||
|
||||
- listeners to auto-correct violations of checks
|
||||
|
||||
To facilitate this, |Flake8| needed a more mature way of managing plugins.
|
||||
Thus, we developed the |PluginManager| which accepts a namespace and will load
|
||||
the plugins for that namespace. A |PluginManager| creates and manages many
|
||||
|
|
@ -38,12 +36,10 @@ the |PTM| will subclass it and specify the ``namespace``, e.g.,
|
|||
|
||||
This provides a few extra methods via the |PluginManager|'s ``map`` method.
|
||||
|
||||
Finally, we create three classes of plugins:
|
||||
Finally, we create two classes of plugins:
|
||||
|
||||
- :class:`~flake8.plugins.manager.Checkers`
|
||||
|
||||
- :class:`~flake8.plugins.manager.Listeners`
|
||||
|
||||
- :class:`~flake8.plugins.manager.ReportFormatters`
|
||||
|
||||
These are used to interact with each of the types of plugins individually.
|
||||
|
|
@ -53,29 +49,6 @@ These are used to interact with each of the types of plugins individually.
|
|||
Our inspiration for our plugin handling comes from the author's extensive
|
||||
experience with ``stevedore``.
|
||||
|
||||
Notifying Listener Plugins
|
||||
--------------------------
|
||||
|
||||
One of the interesting challenges with allowing plugins to be notified each
|
||||
time an error or warning is emitted by a checker is finding listeners quickly
|
||||
and efficiently. It makes sense to allow a listener to listen for a certain
|
||||
class of warnings or just a specific warning. Hence, we need to allow all
|
||||
plugins that listen to a specific warning or class to be notified. For
|
||||
example, someone might register a listener for ``E1`` and another for ``E111``
|
||||
if ``E111`` is triggered by the code, both listeners should be notified.
|
||||
If ``E112`` is returned, then only ``E1`` (and any other listeners) would be
|
||||
notified.
|
||||
|
||||
To implement this goal, we needed an object to store listeners in that would
|
||||
allow for efficient look up - a Trie (or Prefix Tree). Given that none of the
|
||||
existing packages on PyPI allowed for storing data on each node of the trie,
|
||||
it was left up to write our own as :class:`~flake8.plugins._trie.Trie`. On
|
||||
top of that we layer our :class:`~flake8.plugins.notifier.Notifier` class.
|
||||
|
||||
Now when |Flake8| receives an error or warning, we can easily call the
|
||||
:meth:`~flake8.plugins.notifier.Notifier.notify` method and let plugins act on
|
||||
that knowledge.
|
||||
|
||||
Default Plugins
|
||||
---------------
|
||||
|
||||
|
|
@ -103,7 +76,7 @@ API Documentation
|
|||
|
||||
.. autoclass:: flake8.plugins.manager.PluginManager
|
||||
:members:
|
||||
:special-members: __init__, __contains__, __getitem__
|
||||
:special-members: __init__
|
||||
|
||||
.. autoclass:: flake8.plugins.manager.Plugin
|
||||
:members:
|
||||
|
|
@ -115,15 +88,8 @@ API Documentation
|
|||
.. autoclass:: flake8.plugins.manager.Checkers
|
||||
:members:
|
||||
|
||||
.. autoclass:: flake8.plugins.manager.Listeners
|
||||
:members: build_notifier
|
||||
|
||||
.. autoclass:: flake8.plugins.manager.ReportFormatters
|
||||
|
||||
.. autoclass:: flake8.plugins.notifier.Notifier
|
||||
|
||||
.. autoclass:: flake8.plugins._trie.Trie
|
||||
|
||||
.. |PluginManager| replace:: :class:`~flake8.plugins.manager.PluginManager`
|
||||
.. |Plugin| replace:: :class:`~flake8.plugins.manager.Plugin`
|
||||
.. |PTM| replace:: :class:`~flake8.plugins.manager.PluginTypeManager`
|
||||
|
|
|
|||
|
|
@ -86,13 +86,10 @@ grouping of entry-points that flake8 should look in.
|
|||
|
||||
- ``flake8.extension``
|
||||
|
||||
- ``flake8.listen``
|
||||
|
||||
- ``flake8.report``
|
||||
|
||||
If your plugin is one that adds checks to |Flake8|, you will use
|
||||
``flake8.extension``. If your plugin automatically fixes errors in code, you
|
||||
will use ``flake8.listen``. Finally, if your plugin performs extra report
|
||||
``flake8.extension``. If your plugin performs extra report
|
||||
handling (formatting, filtering, etc.) it will use ``flake8.report``.
|
||||
|
||||
If our ``ExamplePlugin`` is something that adds checks, our code would look
|
||||
|
|
|
|||
|
|
@ -57,14 +57,10 @@ class Application(object):
|
|||
self.local_plugins = None
|
||||
#: The instance of :class:`flake8.plugins.manager.Checkers`
|
||||
self.check_plugins = None
|
||||
#: The instance of :class:`flake8.plugins.manager.Listeners`
|
||||
self.listening_plugins = None
|
||||
#: The instance of :class:`flake8.plugins.manager.ReportFormatters`
|
||||
self.formatting_plugins = None
|
||||
#: The user-selected formatter from :attr:`formatting_plugins`
|
||||
self.formatter = None
|
||||
#: The :class:`flake8.plugins.notifier.Notifier` for listening plugins
|
||||
self.listener_trie = None
|
||||
#: The :class:`flake8.style_guide.StyleGuideManager` built from the
|
||||
#: user's options
|
||||
self.guide = None
|
||||
|
|
@ -166,11 +162,11 @@ class Application(object):
|
|||
# type: () -> NoneType
|
||||
"""Find and load the plugins for this application.
|
||||
|
||||
If :attr:`check_plugins`, :attr:`listening_plugins`, or
|
||||
:attr:`formatting_plugins` are ``None`` then this method will update
|
||||
them with the appropriate plugin manager instance. Given the expense
|
||||
of finding plugins (via :mod:`entrypoints`) we want this to be
|
||||
idempotent and so only update those attributes if they are ``None``.
|
||||
If :attr:`check_plugins`, or :attr:`formatting_plugins` are ``None``
|
||||
then this method will update them with the appropriate plugin manager
|
||||
instance. Given the expense of finding plugins (via :mod:`entrypoints`)
|
||||
we want this to be idempotent and so only update those attributes if
|
||||
they are ``None``.
|
||||
"""
|
||||
if self.local_plugins is None:
|
||||
self.local_plugins = config.get_local_plugins(
|
||||
|
|
@ -186,16 +182,12 @@ class Application(object):
|
|||
self.local_plugins.extension
|
||||
)
|
||||
|
||||
if self.listening_plugins is None:
|
||||
self.listening_plugins = plugin_manager.Listeners()
|
||||
|
||||
if self.formatting_plugins is None:
|
||||
self.formatting_plugins = plugin_manager.ReportFormatters(
|
||||
self.local_plugins.report
|
||||
)
|
||||
|
||||
self.check_plugins.load_plugins()
|
||||
self.listening_plugins.load_plugins()
|
||||
self.formatting_plugins.load_plugins()
|
||||
|
||||
def register_plugin_options(self):
|
||||
|
|
@ -203,7 +195,6 @@ class Application(object):
|
|||
"""Register options provided by plugins to our option manager."""
|
||||
self.check_plugins.register_options(self.option_manager)
|
||||
self.check_plugins.register_plugin_versions(self.option_manager)
|
||||
self.listening_plugins.register_options(self.option_manager)
|
||||
self.formatting_plugins.register_options(self.option_manager)
|
||||
|
||||
def parse_configuration_and_cli(self, argv=None):
|
||||
|
|
@ -229,9 +220,6 @@ class Application(object):
|
|||
self.check_plugins.provide_options(
|
||||
self.option_manager, self.options, self.args
|
||||
)
|
||||
self.listening_plugins.provide_options(
|
||||
self.option_manager, self.options, self.args
|
||||
)
|
||||
self.formatting_plugins.provide_options(
|
||||
self.option_manager, self.options, self.args
|
||||
)
|
||||
|
|
@ -264,18 +252,12 @@ class Application(object):
|
|||
|
||||
self.formatter = formatter_class(self.options)
|
||||
|
||||
def make_notifier(self):
|
||||
# type: () -> NoneType
|
||||
"""Initialize our listener Notifier."""
|
||||
if self.listener_trie is None:
|
||||
self.listener_trie = self.listening_plugins.build_notifier()
|
||||
|
||||
def make_guide(self):
|
||||
# type: () -> NoneType
|
||||
"""Initialize our StyleGuide."""
|
||||
if self.guide is None:
|
||||
self.guide = style_guide.StyleGuideManager(
|
||||
self.options, self.listener_trie, self.formatter
|
||||
self.options, self.formatter
|
||||
)
|
||||
|
||||
if self.running_against_diff:
|
||||
|
|
@ -373,7 +355,6 @@ class Application(object):
|
|||
self.register_plugin_options()
|
||||
self.parse_configuration_and_cli(argv)
|
||||
self.make_formatter()
|
||||
self.make_notifier()
|
||||
self.make_guide()
|
||||
self.make_file_checker_manager()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
"""Independent implementation of a Trie tree."""
|
||||
|
||||
__all__ = ("Trie", "TrieNode")
|
||||
|
||||
|
||||
def _iterate_stringlike_objects(string):
|
||||
for i in range(len(string)):
|
||||
yield string[i : i + 1]
|
||||
|
||||
|
||||
class Trie(object):
|
||||
"""The object that manages the trie nodes."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize an empty trie."""
|
||||
self.root = TrieNode(None, None)
|
||||
|
||||
def add(self, path, node_data):
|
||||
"""Add the node data to the path described."""
|
||||
node = self.root
|
||||
for prefix in _iterate_stringlike_objects(path):
|
||||
child = node.find_prefix(prefix)
|
||||
if child is None:
|
||||
child = node.add_child(prefix, [])
|
||||
node = child
|
||||
node.data.append(node_data)
|
||||
|
||||
def find(self, path):
|
||||
"""Find a node based on the path provided."""
|
||||
node = self.root
|
||||
for prefix in _iterate_stringlike_objects(path):
|
||||
child = node.find_prefix(prefix)
|
||||
if child is None:
|
||||
return None
|
||||
node = child
|
||||
return node
|
||||
|
||||
def traverse(self):
|
||||
"""Traverse this tree.
|
||||
|
||||
This performs a depth-first pre-order traversal of children in this
|
||||
tree. It returns the results consistently by first sorting the
|
||||
children based on their prefix and then traversing them in
|
||||
alphabetical order.
|
||||
"""
|
||||
return self.root.traverse()
|
||||
|
||||
|
||||
class TrieNode(object):
|
||||
"""The majority of the implementation details of a Trie."""
|
||||
|
||||
def __init__(self, prefix, data, children=None):
|
||||
"""Initialize a TrieNode with data and children."""
|
||||
self.children = children or {}
|
||||
self.data = data
|
||||
self.prefix = prefix
|
||||
|
||||
def __repr__(self):
|
||||
"""Generate an easy to read representation of the node."""
|
||||
return "TrieNode(prefix={0}, data={1})".format(self.prefix, self.data)
|
||||
|
||||
def find_prefix(self, prefix):
|
||||
"""Find the prefix in the children of this node.
|
||||
|
||||
:returns: A child matching the prefix or None.
|
||||
:rtype: :class:`~TrieNode` or None
|
||||
"""
|
||||
return self.children.get(prefix, None)
|
||||
|
||||
def add_child(self, prefix, data, children=None):
|
||||
"""Create and add a new child node.
|
||||
|
||||
:returns: The newly created node
|
||||
:rtype: :class:`~TrieNode`
|
||||
"""
|
||||
new_node = TrieNode(prefix, data, children)
|
||||
self.children[prefix] = new_node
|
||||
return new_node
|
||||
|
||||
def traverse(self):
|
||||
"""Traverse children of this node.
|
||||
|
||||
This performs a depth-first pre-order traversal of the remaining
|
||||
children in this sub-tree. It returns the results consistently by
|
||||
first sorting the children based on their prefix and then traversing
|
||||
them in alphabetical order.
|
||||
"""
|
||||
if not self.children:
|
||||
return
|
||||
|
||||
for prefix in sorted(self.children):
|
||||
child = self.children[prefix]
|
||||
yield child
|
||||
for child in child.traverse():
|
||||
yield child
|
||||
|
|
@ -6,7 +6,6 @@ import entrypoints
|
|||
|
||||
from flake8 import exceptions
|
||||
from flake8 import utils
|
||||
from flake8.plugins import notifier
|
||||
|
||||
if sys.version_info >= (3, 3):
|
||||
import collections.abc as collections_abc
|
||||
|
|
@ -15,13 +14,7 @@ else:
|
|||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
__all__ = (
|
||||
"Checkers",
|
||||
"Listeners",
|
||||
"Plugin",
|
||||
"PluginManager",
|
||||
"ReportFormatters",
|
||||
)
|
||||
__all__ = ("Checkers", "Plugin", "PluginManager", "ReportFormatters")
|
||||
|
||||
NO_GROUP_FOUND = object()
|
||||
|
||||
|
|
@ -444,24 +437,6 @@ class PluginTypeManager(object):
|
|||
list(self.manager.map(call_provide_options))
|
||||
|
||||
|
||||
class NotifierBuilderMixin(object): # pylint: disable=too-few-public-methods
|
||||
"""Mixin class that builds a Notifier from a PluginManager."""
|
||||
|
||||
def build_notifier(self):
|
||||
"""Build a Notifier for our Listeners.
|
||||
|
||||
:returns:
|
||||
Object to notify our listeners of certain error codes and
|
||||
warnings.
|
||||
:rtype:
|
||||
:class:`~flake8.notifier.Notifier`
|
||||
"""
|
||||
notifier_trie = notifier.Notifier()
|
||||
for name in self.names:
|
||||
notifier_trie.register_listener(name, self.manager[name])
|
||||
return notifier_trie
|
||||
|
||||
|
||||
class Checkers(PluginTypeManager):
|
||||
"""All of the checkers registered through entry-points or config."""
|
||||
|
||||
|
|
@ -542,12 +517,6 @@ class Checkers(PluginTypeManager):
|
|||
return plugins
|
||||
|
||||
|
||||
class Listeners(PluginTypeManager, NotifierBuilderMixin):
|
||||
"""All of the listeners registered through entry-points or config."""
|
||||
|
||||
namespace = "flake8.listen"
|
||||
|
||||
|
||||
class ReportFormatters(PluginTypeManager):
|
||||
"""All of the report formatters registered through entry-points/config."""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
"""Implementation of the class that registers and notifies listeners."""
|
||||
from flake8.plugins import _trie
|
||||
|
||||
|
||||
class Notifier(object):
|
||||
"""Object that tracks and notifies listener objects."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize an empty notifier object."""
|
||||
self.listeners = _trie.Trie()
|
||||
|
||||
def listeners_for(self, error_code):
|
||||
"""Retrieve listeners for an error_code.
|
||||
|
||||
There may be listeners registered for E1, E100, E101, E110, E112, and
|
||||
E126. To get all the listeners for one of E100, E101, E110, E112, or
|
||||
E126 you would also need to incorporate the listeners for E1 (since
|
||||
they're all in the same class).
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flake8 import notifier
|
||||
|
||||
n = notifier.Notifier()
|
||||
# register listeners
|
||||
for listener in n.listeners_for('W102'):
|
||||
listener.notify(...)
|
||||
"""
|
||||
path = error_code
|
||||
while path:
|
||||
node = self.listeners.find(path)
|
||||
listeners = getattr(node, "data", [])
|
||||
for listener in listeners:
|
||||
yield listener
|
||||
path = path[:-1]
|
||||
|
||||
def notify(self, error_code, *args, **kwargs):
|
||||
"""Notify all listeners for the specified error code."""
|
||||
for listener in self.listeners_for(error_code):
|
||||
listener.notify(error_code, *args, **kwargs)
|
||||
|
||||
def register_listener(self, error_code, listener):
|
||||
"""Register a listener for a specific error_code."""
|
||||
self.listeners.add(error_code, listener)
|
||||
|
|
@ -325,19 +325,18 @@ class DecisionEngine(object):
|
|||
class StyleGuideManager(object):
|
||||
"""Manage multiple style guides for a single run."""
|
||||
|
||||
def __init__(self, options, listener_trie, formatter, decider=None):
|
||||
def __init__(self, options, formatter, decider=None):
|
||||
"""Initialize our StyleGuide.
|
||||
|
||||
.. todo:: Add parameter documentation.
|
||||
"""
|
||||
self.options = options
|
||||
self.listener = listener_trie
|
||||
self.formatter = formatter
|
||||
self.stats = statistics.Statistics()
|
||||
self.decider = decider or DecisionEngine(options)
|
||||
self.style_guides = []
|
||||
self.default_style_guide = StyleGuide(
|
||||
options, listener_trie, formatter, decider=decider
|
||||
options, formatter, decider=decider
|
||||
)
|
||||
self.style_guides = list(
|
||||
itertools.chain(
|
||||
|
|
@ -435,15 +434,12 @@ class StyleGuideManager(object):
|
|||
class StyleGuide(object):
|
||||
"""Manage a Flake8 user's style guide."""
|
||||
|
||||
def __init__(
|
||||
self, options, listener_trie, formatter, filename=None, decider=None
|
||||
):
|
||||
def __init__(self, options, formatter, filename=None, decider=None):
|
||||
"""Initialize our StyleGuide.
|
||||
|
||||
.. todo:: Add parameter documentation.
|
||||
"""
|
||||
self.options = options
|
||||
self.listener = listener_trie
|
||||
self.formatter = formatter
|
||||
self.stats = statistics.Statistics()
|
||||
self.decider = decider or DecisionEngine(options)
|
||||
|
|
@ -461,9 +457,7 @@ class StyleGuide(object):
|
|||
filename = filename or self.filename
|
||||
options = copy.deepcopy(self.options)
|
||||
options.ignore.extend(extend_ignore_with or [])
|
||||
return StyleGuide(
|
||||
options, self.listener, self.formatter, filename=filename
|
||||
)
|
||||
return StyleGuide(options, self.formatter, filename=filename)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def processing_file(self, filename):
|
||||
|
|
@ -565,7 +559,6 @@ class StyleGuide(object):
|
|||
):
|
||||
self.formatter.handle(error)
|
||||
self.stats.record(error)
|
||||
self.listener.notify(error.code, error)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
"""Unit tests for the Notifier object."""
|
||||
import pytest
|
||||
|
||||
from flake8.plugins import notifier
|
||||
|
||||
|
||||
class _Listener(object):
|
||||
def __init__(self, error_code):
|
||||
self.error_code = error_code
|
||||
self.was_notified = False
|
||||
|
||||
def notify(self, error_code, *args, **kwargs):
|
||||
assert error_code.startswith(self.error_code)
|
||||
self.was_notified = True
|
||||
|
||||
|
||||
class TestNotifier(object):
|
||||
"""Notifier unit tests."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
"""Set up each TestNotifier instance."""
|
||||
self.notifier = notifier.Notifier()
|
||||
self.listener_map = {}
|
||||
|
||||
def add_listener(error_code):
|
||||
listener = _Listener(error_code)
|
||||
self.listener_map[error_code] = listener
|
||||
self.notifier.register_listener(error_code, listener)
|
||||
|
||||
for i in range(10):
|
||||
add_listener('E{0}'.format(i))
|
||||
for j in range(30):
|
||||
add_listener('E{0}{1:02d}'.format(i, j))
|
||||
|
||||
def test_notify(self):
|
||||
"""Show that we notify a specific error code."""
|
||||
self.notifier.notify('E111', 'extra', 'args')
|
||||
assert self.listener_map['E111'].was_notified is True
|
||||
assert self.listener_map['E1'].was_notified is True
|
||||
|
||||
@pytest.mark.parametrize('code', ['W123', 'W12', 'W1', 'W'])
|
||||
def test_no_listeners_for(self, code):
|
||||
"""Show that we return an empty list of listeners."""
|
||||
assert list(self.notifier.listeners_for(code)) == []
|
||||
|
||||
@pytest.mark.parametrize('code,expected', [
|
||||
('E101', ['E101', 'E1']),
|
||||
('E211', ['E211', 'E2']),
|
||||
])
|
||||
def test_listeners_for(self, code, expected):
|
||||
"""Verify that we retrieve the correct listeners."""
|
||||
assert ([l.error_code
|
||||
for l in self.notifier.listeners_for(code)] == expected)
|
||||
|
|
@ -203,31 +203,3 @@ def test_proxies_getitem_to_managers_plugins_dict(PluginManager): # noqa: N803
|
|||
for i in range(8):
|
||||
key = 'T10%i' % i
|
||||
assert type_mgr[key] is plugins[key]
|
||||
|
||||
|
||||
class FakePluginTypeManager(manager.NotifierBuilderMixin):
|
||||
"""Provide an easy way to test the NotifierBuilderMixin."""
|
||||
|
||||
def __init__(self, manager):
|
||||
"""Initialize with our fake manager."""
|
||||
self.names = sorted(manager)
|
||||
self.manager = manager
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def notifier_builder():
|
||||
"""Create a fake plugin type manager."""
|
||||
return FakePluginTypeManager(manager={
|
||||
'T100': object(),
|
||||
'T101': object(),
|
||||
'T110': object(),
|
||||
})
|
||||
|
||||
|
||||
def test_build_notifier(notifier_builder):
|
||||
"""Verify we properly build a Notifier object."""
|
||||
notifier = notifier_builder.build_notifier()
|
||||
for name in ('T100', 'T101', 'T110'):
|
||||
assert list(notifier.listeners_for(name)) == [
|
||||
notifier_builder.manager[name]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import pytest
|
|||
from flake8 import style_guide
|
||||
from flake8 import utils
|
||||
from flake8.formatting import base
|
||||
from flake8.plugins import notifier
|
||||
|
||||
|
||||
def create_options(**kwargs):
|
||||
|
|
@ -22,35 +21,10 @@ def create_options(**kwargs):
|
|||
return optparse.Values(kwargs)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('select_list,ignore_list,error_code', [
|
||||
(['E111', 'E121'], [], 'E111'),
|
||||
(['E111', 'E121'], [], 'E121'),
|
||||
(['E11', 'E121'], ['E1'], 'E112'),
|
||||
(['E41'], ['E2', 'E12', 'E4'], 'E410'),
|
||||
])
|
||||
def test_handle_error_notifies_listeners(select_list, ignore_list, error_code):
|
||||
"""Verify that error codes notify the listener trie appropriately."""
|
||||
listener_trie = mock.create_autospec(notifier.Notifier, instance=True)
|
||||
formatter = mock.create_autospec(base.BaseFormatter, instance=True)
|
||||
guide = style_guide.StyleGuide(create_options(select=select_list,
|
||||
ignore=ignore_list),
|
||||
listener_trie=listener_trie,
|
||||
formatter=formatter)
|
||||
|
||||
with mock.patch('linecache.getline', return_value=''):
|
||||
guide.handle_error(error_code, 'stdin', 1, 0, 'error found')
|
||||
error = style_guide.Violation(
|
||||
error_code, 'stdin', 1, 1, 'error found', None)
|
||||
listener_trie.notify.assert_called_once_with(error_code, error)
|
||||
formatter.handle.assert_called_once_with(error)
|
||||
|
||||
|
||||
def test_handle_error_does_not_raise_type_errors():
|
||||
"""Verify that we handle our inputs better."""
|
||||
listener_trie = mock.create_autospec(notifier.Notifier, instance=True)
|
||||
formatter = mock.create_autospec(base.BaseFormatter, instance=True)
|
||||
guide = style_guide.StyleGuide(create_options(select=['T111'], ignore=[]),
|
||||
listener_trie=listener_trie,
|
||||
formatter=formatter)
|
||||
|
||||
assert 1 == guide.handle_error(
|
||||
|
|
@ -58,41 +32,11 @@ def test_handle_error_does_not_raise_type_errors():
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('select_list,ignore_list,error_code', [
|
||||
(['E111', 'E121'], [], 'E122'),
|
||||
(['E11', 'E12'], [], 'E132'),
|
||||
(['E2', 'E12'], [], 'E321'),
|
||||
(['E2', 'E12'], [], 'E410'),
|
||||
(['E111', 'E121'], ['E2'], 'E122'),
|
||||
(['E11', 'E12'], ['E13'], 'E132'),
|
||||
(['E1', 'E3'], ['E32'], 'E321'),
|
||||
(['E4'], ['E2', 'E12', 'E41'], 'E410'),
|
||||
(['E111', 'E121'], [], 'E112'),
|
||||
])
|
||||
def test_handle_error_does_not_notify_listeners(select_list, ignore_list,
|
||||
error_code):
|
||||
"""Verify that error codes notify the listener trie appropriately."""
|
||||
listener_trie = mock.create_autospec(notifier.Notifier, instance=True)
|
||||
formatter = mock.create_autospec(base.BaseFormatter, instance=True)
|
||||
guide = style_guide.StyleGuide(create_options(select=select_list,
|
||||
ignore=ignore_list),
|
||||
listener_trie=listener_trie,
|
||||
formatter=formatter)
|
||||
|
||||
with mock.patch('linecache.getline', return_value=''):
|
||||
guide.handle_error(error_code, 'stdin', 1, 1, 'error found')
|
||||
assert listener_trie.notify.called is False
|
||||
assert formatter.handle.called is False
|
||||
|
||||
|
||||
def test_style_guide_manager():
|
||||
"""Verify how the StyleGuideManager creates a default style guide."""
|
||||
listener_trie = mock.create_autospec(notifier.Notifier, instance=True)
|
||||
formatter = mock.create_autospec(base.BaseFormatter, instance=True)
|
||||
options = create_options()
|
||||
guide = style_guide.StyleGuideManager(options,
|
||||
listener_trie=listener_trie,
|
||||
formatter=formatter)
|
||||
guide = style_guide.StyleGuideManager(options, formatter=formatter)
|
||||
assert guide.default_style_guide.options is options
|
||||
assert len(guide.style_guides) == 1
|
||||
|
||||
|
|
@ -114,11 +58,9 @@ PER_FILE_IGNORES_UNPARSED = [
|
|||
])
|
||||
def test_style_guide_applies_to(style_guide_file, filename, expected):
|
||||
"""Verify that we match a file to its style guide."""
|
||||
listener_trie = mock.create_autospec(notifier.Notifier, instance=True)
|
||||
formatter = mock.create_autospec(base.BaseFormatter, instance=True)
|
||||
options = create_options()
|
||||
guide = style_guide.StyleGuide(options,
|
||||
listener_trie=listener_trie,
|
||||
formatter=formatter,
|
||||
filename=style_guide_file)
|
||||
assert guide.applies_to(filename) is expected
|
||||
|
|
@ -126,12 +68,9 @@ def test_style_guide_applies_to(style_guide_file, filename, expected):
|
|||
|
||||
def test_style_guide_manager_pre_file_ignores_parsing():
|
||||
"""Verify how the StyleGuideManager creates a default style guide."""
|
||||
listener_trie = mock.create_autospec(notifier.Notifier, instance=True)
|
||||
formatter = mock.create_autospec(base.BaseFormatter, instance=True)
|
||||
options = create_options(per_file_ignores=PER_FILE_IGNORES_UNPARSED)
|
||||
guide = style_guide.StyleGuideManager(options,
|
||||
listener_trie=listener_trie,
|
||||
formatter=formatter)
|
||||
guide = style_guide.StyleGuideManager(options, formatter=formatter)
|
||||
assert len(guide.style_guides) == 5
|
||||
assert list(map(utils.normalize_path,
|
||||
["first_file.py", "second_file.py", "third_file.py",
|
||||
|
|
@ -150,14 +89,11 @@ def test_style_guide_manager_pre_file_ignores_parsing():
|
|||
def test_style_guide_manager_pre_file_ignores(ignores, violation, filename,
|
||||
handle_error_return):
|
||||
"""Verify how the StyleGuideManager creates a default style guide."""
|
||||
listener_trie = mock.create_autospec(notifier.Notifier, instance=True)
|
||||
formatter = mock.create_autospec(base.BaseFormatter, instance=True)
|
||||
options = create_options(ignore=ignores,
|
||||
select=['E', 'F', 'W'],
|
||||
per_file_ignores=PER_FILE_IGNORES_UNPARSED)
|
||||
guide = style_guide.StyleGuideManager(options,
|
||||
listener_trie=listener_trie,
|
||||
formatter=formatter)
|
||||
guide = style_guide.StyleGuideManager(options, formatter=formatter)
|
||||
assert (guide.handle_error(violation, filename, 1, 1, "Fake text")
|
||||
== handle_error_return)
|
||||
|
||||
|
|
@ -172,12 +108,9 @@ def test_style_guide_manager_pre_file_ignores(ignores, violation, filename,
|
|||
])
|
||||
def test_style_guide_manager_style_guide_for(filename, expected):
|
||||
"""Verify the style guide selection function."""
|
||||
listener_trie = mock.create_autospec(notifier.Notifier, instance=True)
|
||||
formatter = mock.create_autospec(base.BaseFormatter, instance=True)
|
||||
options = create_options(per_file_ignores=PER_FILE_IGNORES_UNPARSED)
|
||||
guide = style_guide.StyleGuideManager(options,
|
||||
listener_trie=listener_trie,
|
||||
formatter=formatter)
|
||||
guide = style_guide.StyleGuideManager(options, formatter=formatter)
|
||||
|
||||
file_guide = guide.style_guide_for(filename)
|
||||
assert file_guide.filename == expected
|
||||
|
|
|
|||
|
|
@ -1,122 +0,0 @@
|
|||
"""Unit test for the _trie module."""
|
||||
from flake8.plugins import _trie as trie
|
||||
|
||||
|
||||
class TestTrie(object):
|
||||
"""Collection of tests for the Trie class."""
|
||||
|
||||
def test_traverse_without_data(self):
|
||||
"""Verify the behaviour when traversing an empty Trie."""
|
||||
tree = trie.Trie()
|
||||
assert list(tree.traverse()) == []
|
||||
|
||||
def test_traverse_with_data(self):
|
||||
"""Verify that traversal of our Trie is depth-first and pre-order."""
|
||||
tree = trie.Trie()
|
||||
tree.add('A', 'A')
|
||||
tree.add('a', 'a')
|
||||
tree.add('AB', 'B')
|
||||
tree.add('Ab', 'b')
|
||||
tree.add('AbC', 'C')
|
||||
tree.add('Abc', 'c')
|
||||
# The trie tree here should look something like
|
||||
#
|
||||
# <root>
|
||||
# / \
|
||||
# A a
|
||||
# / |
|
||||
# B b
|
||||
# / \
|
||||
# C c
|
||||
#
|
||||
# And the traversal should look like:
|
||||
#
|
||||
# A B b C c a
|
||||
expected_order = ['A', 'B', 'b', 'C', 'c', 'a']
|
||||
for expected, actual_node in zip(expected_order, tree.traverse()):
|
||||
assert actual_node.prefix == expected
|
||||
|
||||
def test_find(self):
|
||||
"""Exercise the Trie.find method."""
|
||||
tree = trie.Trie()
|
||||
tree.add('A', 'A')
|
||||
tree.add('a', 'a')
|
||||
tree.add('AB', 'AB')
|
||||
tree.add('Ab', 'Ab')
|
||||
tree.add('AbC', 'AbC')
|
||||
tree.add('Abc', 'Abc')
|
||||
|
||||
assert tree.find('AB').data == ['AB']
|
||||
assert tree.find('AbC').data == ['AbC']
|
||||
assert tree.find('A').data == ['A']
|
||||
assert tree.find('X') is None
|
||||
|
||||
|
||||
class TestTrieNode(object):
|
||||
"""Collection of tests for the TrieNode class."""
|
||||
|
||||
def test_add_child(self):
|
||||
"""Verify we add children appropriately."""
|
||||
node = trie.TrieNode('E', 'E is for Eat')
|
||||
assert node.find_prefix('a') is None
|
||||
added = node.add_child('a', 'a is for Apple')
|
||||
assert node.find_prefix('a') is added
|
||||
|
||||
def test_add_child_overrides_previous_child(self):
|
||||
"""Verify adding a child will replace the previous child."""
|
||||
node = trie.TrieNode('E', 'E is for Eat', children={
|
||||
'a': trie.TrieNode('a', 'a is for Apple')
|
||||
})
|
||||
previous = node.find_prefix('a')
|
||||
assert previous is not None
|
||||
|
||||
added = node.add_child('a', 'a is for Ascertain')
|
||||
assert node.find_prefix('a') is added
|
||||
|
||||
def test_find_prefix(self):
|
||||
"""Verify we can find a child with the specified prefix."""
|
||||
node = trie.TrieNode('E', 'E is for Eat', children={
|
||||
'a': trie.TrieNode('a', 'a is for Apple')
|
||||
})
|
||||
child = node.find_prefix('a')
|
||||
assert child is not None
|
||||
assert child.prefix == 'a'
|
||||
assert child.data == 'a is for Apple'
|
||||
|
||||
def test_find_prefix_returns_none_when_no_children_have_the_prefix(self):
|
||||
"""Verify we receive None from find_prefix for missing children."""
|
||||
node = trie.TrieNode('E', 'E is for Eat', children={
|
||||
'a': trie.TrieNode('a', 'a is for Apple')
|
||||
})
|
||||
assert node.find_prefix('b') is None
|
||||
|
||||
def test_traverse_does_nothing_when_a_node_has_no_children(self):
|
||||
"""Verify traversing a node with no children does nothing."""
|
||||
node = trie.TrieNode('E', 'E is for Eat')
|
||||
assert list(node.traverse()) == []
|
||||
|
||||
def test_traverse(self):
|
||||
"""Verify traversal is depth-first and pre-order."""
|
||||
root = trie.TrieNode(None, None)
|
||||
node = root.add_child('A', 'A')
|
||||
root.add_child('a', 'a')
|
||||
node.add_child('B', 'B')
|
||||
node = node.add_child('b', 'b')
|
||||
node.add_child('C', 'C')
|
||||
node.add_child('c', 'c')
|
||||
# The sub-tree here should look something like
|
||||
#
|
||||
# <root>
|
||||
# / \
|
||||
# A a
|
||||
# / |
|
||||
# B b
|
||||
# / \
|
||||
# C c
|
||||
#
|
||||
# And the traversal should look like:
|
||||
#
|
||||
# A B b C c a
|
||||
expected_order = ['A', 'B', 'b', 'C', 'c', 'a']
|
||||
for expected, actual_node in zip(expected_order, root.traverse()):
|
||||
assert actual_node.prefix == expected
|
||||
Loading…
Add table
Add a link
Reference in a new issue