Handle determining plugin versions and names

Since we now treat pep8 checks each as an individual plugin, we need a way to
represent pep8 as a single plugin in the version output (like we currently
do). As such we need to be a bit wiser in how we tell the OptionManager about
plugins and their versions and we only do this for certain plugins.
This commit is contained in:
Ian Cordasco 2016-05-10 14:11:47 -05:00
parent 44d994dab9
commit abdc957f17
3 changed files with 97 additions and 15 deletions

View file

@ -282,6 +282,7 @@ class Application(object):
# type: () -> NoneType
"""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)

View file

@ -167,7 +167,7 @@ class OptionManager(object):
@staticmethod
def format_plugin(plugin_tuple):
"""Convert a plugin tuple into a dictionary mapping name to value."""
return dict(zip(["entry", "name", "version"], plugin_tuple))
return dict(zip(["name", "version"], plugin_tuple))
def add_option(self, *args, **kwargs):
"""Create and register a new option.
@ -213,7 +213,7 @@ class OptionManager(object):
def generate_epilog(self):
"""Create an epilog with the version and name of each of plugin."""
plugin_version_format = '%(name)s(%(entry)s): %(version)s'
plugin_version_format = '%(name)s: %(version)s'
self.parser.epilog = 'Installed plugins: ' + self.generate_versions(
plugin_version_format
)
@ -229,17 +229,13 @@ class OptionManager(object):
return options, xargs
def register_plugin(self, entry_point_name, name, version):
def register_plugin(self, name, version):
"""Register a plugin relying on the OptionManager.
:param str entry_point_name:
The name of the entry-point loaded with pkg_resources. For
example, if the entry-point looks like: ``C90 = mccabe.Checker``
then the ``entry_point_name`` would be ``C90``.
:param str name:
The name of the checker itself. This will be the ``name``
attribute of the class or function loaded from the entry-point.
:param str version:
The version of the checker that we're using.
"""
self.registered_plugins.add((entry_point_name, name, version))
self.registered_plugins.add((name, version))

View file

@ -18,6 +18,8 @@ __all__ = (
'ReportFormatters',
)
NO_GROUP_FOUND = object()
class Plugin(object):
"""Wrap an EntryPoint from setuptools and other logic."""
@ -36,6 +38,9 @@ class Plugin(object):
self.entry_point = entry_point
self._plugin = None
self._parameters = None
self._group = None
self._plugin_name = None
self._version = None
def __repr__(self):
"""Provide an easy to read description of the current plugin."""
@ -43,6 +48,28 @@ class Plugin(object):
self.name, self.entry_point
)
def is_in_a_group(self):
"""Determine if this plugin is in a group.
:returns:
True if the plugin is in a group, otherwise False.
:rtype:
bool
"""
return self.group() is not None
def group(self):
"""Find and parse the group the plugin is in."""
if self._group is None:
name = self.name.split('.', 1)
if len(name) > 1:
self._group = name[0]
else:
self._group = NO_GROUP_FOUND
if self._group is NO_GROUP_FOUND:
return None
return self._group
@property
def parameters(self):
"""List of arguments that need to be passed to the plugin."""
@ -61,8 +88,25 @@ class Plugin(object):
@property
def version(self):
"""Return the version attribute on the plugin."""
return self.plugin.version
"""Return the version of the plugin."""
if self._version is None:
if self.is_in_a_group():
self._version = version_for(self)
else:
self._version = self.plugin.version
return self._version
@property
def plugin_name(self):
"""Return the name of the plugin."""
if self._plugin_name is None:
if self.is_in_a_group():
self._plugin_name = self.group()
else:
self._plugin_name = self.plugin.name
return self._plugin_name
def execute(self, *args, **kwargs):
r"""Call the plugin with \*args and \*\*kwargs."""
@ -136,11 +180,6 @@ class Plugin(object):
self.name, optmanager
)
add_options(optmanager)
optmanager.register_plugin(
entry_point_name=self.name,
name=self.plugin.name,
version=self.plugin.version
)
class PluginManager(object): # pylint: disable=too-few-public-methods
@ -193,6 +232,46 @@ class PluginManager(object): # pylint: disable=too-few-public-methods
for name in self.names:
yield func(self.plugins[name], *args, **kwargs)
def versions(self):
# () -> (str, str)
"""Generate the versions of plugins.
:returns:
Tuples of the plugin_name and version
:rtype:
tuple
"""
plugins_seen = set()
for entry_point_name in self.names:
plugin = self.plugins[entry_point_name]
plugin_name = plugin.plugin_name
if plugin.plugin_name in plugins_seen:
continue
plugins_seen.add(plugin_name)
yield (plugin_name, plugin.version)
def version_for(plugin):
# (Plugin) -> Union[str, NoneType]
"""Determine the version of a plugin by it's module.
:param plugin:
The loaded plugin
:type plugin:
Plugin
:returns:
version string for the module
:rtype:
str
"""
module_name = plugin.plugin.__module__
try:
module = __import__(module_name)
except ImportError:
return None
return getattr(module, '__version__', None)
class PluginTypeManager(object):
"""Parent class for most of the specific plugin types."""
@ -264,6 +343,12 @@ class PluginTypeManager(object):
self.plugins_loaded = True
return plugins
def register_plugin_versions(self, optmanager):
"""Register the plugins and their versions with the OptionManager."""
self.load_plugins()
for (plugin_name, version) in self.manager.versions():
optmanager.register_plugin(name=plugin_name, version=version)
def register_options(self, optmanager):
"""Register all of the checkers' options to the OptionManager."""
self.load_plugins()