Make Plugin.load_plugin raise a Flake8 exception

Let's catch exceptions, log them, and re-raise as a FailedToLoad
exception. This also refactors the actually loading of plugins into a
private method on the Plugin class.
This commit is contained in:
Ian Cordasco 2016-01-22 09:04:52 -06:00
parent 6546cf41d4
commit 3b64ff2a1f
3 changed files with 62 additions and 12 deletions

25
flake8/exceptions.py Normal file
View file

@ -0,0 +1,25 @@
"""Exception classes for all of Flake8."""
class Flake8Exception(Exception):
"""Plain Flake8 exception."""
pass
class FailedToLoadPlugin(Flake8Exception):
"""Exception raised when a plugin fails to load."""
FORMAT = 'Flake8 failed to load plugin "%(name)s" due to %(exc)s.'
def __init__(self, *args, **kwargs):
"""Initialize our FailedToLoadPlugin exception."""
self.plugin = kwargs.pop('plugin')
self.ep_name = self.plugin.name
self.original_exception = kwargs.pop('exception')
super(FailedToLoadPlugin, self).__init__(*args, **kwargs)
def __str__(self):
"""Return a nice string for our exception."""
return self.FORMAT % {'name': self.ep_name,
'exc': self.original_exception}

View file

@ -5,6 +5,7 @@ import logging
import pkg_resources
from flake8 import _trie
from flake8 import exceptions
LOG = logging.getLogger(__name__)
@ -58,6 +59,21 @@ class Plugin(object):
r"""Call the plugin with \*args and \*\*kwargs."""
return self.plugin(*args, **kwargs)
def _load(self, verify_requirements):
# Avoid relying on hasattr() here.
resolve = getattr(self.entry_point, 'resolve', None)
require = getattr(self.entry_point, 'require', None)
if resolve and require:
if verify_requirements:
LOG.debug('Verifying plugin "%s"\'s requirements.',
self.name)
require()
self._plugin = resolve()
else:
self._plugin = self.entry_point.load(
require=verify_requirements
)
def load_plugin(self, verify_requirements=False):
"""Retrieve the plugin for this entry-point.
@ -73,19 +89,16 @@ class Plugin(object):
"""
if self._plugin is None:
LOG.debug('Loading plugin "%s" from entry-point.', self.name)
# Avoid relying on hasattr() here.
resolve = getattr(self.entry_point, 'resolve', None)
require = getattr(self.entry_point, 'require', None)
if resolve and require:
if verify_requirements:
LOG.debug('Verifying plugin "%s"\'s requirements.',
self.name)
require()
self._plugin = resolve()
else:
self._plugin = self.entry_point.load(
require=verify_requirements
try:
self._load(verify_requirements)
except Exception as load_exception:
LOG.exception(load_exception, exc_info=True)
failed_to_load = exceptions.FailedToLoadPlugin(
plugin=self,
exception=load_exception,
)
LOG.critical(str(failed_to_load))
raise failed_to_load
def provide_options(self, optmanager, options, extra_args):
"""Pass the parsed options and extra arguments to the plugin."""

View file

@ -1,6 +1,8 @@
"""Tests for flake8.plugins.manager.Plugin."""
import mock
import pytest
from flake8 import exceptions
from flake8.plugins import manager
@ -48,6 +50,16 @@ def test_load_plugin_only_calls_require_when_verifying_requirements():
entry_point.resolve.assert_called_once_with()
def test_load_plugin_catches_and_reraises_exceptions():
"""Verify we raise our own FailedToLoadPlugin."""
entry_point = mock.Mock(spec=['require', 'resolve'])
entry_point.resolve.side_effect = ValueError('Test failure')
plugin = manager.Plugin('T000', entry_point)
with pytest.raises(exceptions.FailedToLoadPlugin):
plugin.load_plugin()
def test_plugin_property_loads_plugin_on_first_use():
"""Verify that we load our plugin when we first try to use it."""
entry_point = mock.Mock(spec=['require', 'resolve', 'load'])