diff --git a/flake8/main/cli.py b/flake8/main/cli.py index d8a95f9..e168bef 100644 --- a/flake8/main/cli.py +++ b/flake8/main/cli.py @@ -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) diff --git a/flake8/options/manager.py b/flake8/options/manager.py index cb4c831..bf144c9 100644 --- a/flake8/options/manager.py +++ b/flake8/options/manager.py @@ -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)) diff --git a/flake8/plugins/manager.py b/flake8/plugins/manager.py index 872a4f4..56ee813 100644 --- a/flake8/plugins/manager.py +++ b/flake8/plugins/manager.py @@ -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()