Plugin Handling¶
Plugin Management¶
Flake8 3.0 added support for two 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
Plugin
instances.
A Plugin
lazily loads the underlying entry-point provided by setuptools.
The entry-point will be loaded either by calling
load_plugin()
or accessing the plugin
attribute. We also use this abstraction to retrieve options that the plugin
wishes to register and parse.
The only public method the PluginManager
provides is
map()
. This will accept a function
(or other callable) and call it with each plugin as the first parameter.
We build atop the PluginManager
with the PluginTypeManager
. It is expected that users of
the PluginTypeManager
will subclass it and specify the namespace
, e.g.,
class ExamplePluginType(flake8.plugin.manager.PluginTypeManager):
namespace = 'example-plugins'
This provides a few extra methods via the PluginManager
‘s map
method.
Finally, we create three classes of plugins:
These are used to interact with each of the types of plugins individually.
Note
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 Trie
. On
top of that we layer our Notifier
class.
Now when Flake8 receives an error or warning, we can easily call the
notify()
method and let plugins act on
that knowledge.
Default Plugins¶
Finally, Flake8 has always provided its own plugin shim for Pyflakes. As
part of that we carry our own shim in-tree and now store that in
flake8.plugins.pyflakes
.
Flake8 also registers plugins for pep8. Each check in pep8 requires
different parameters and it cannot easily be shimmed together like Pyflakes
was. As such, plugins have a concept of a “group”. If you look at our
setup.py
you will see that we register pep8 checks roughly like so:
pep8.<check-name> = pep8:<check-name>
We do this to identify that <check-name>>
is part of a group. This also
enables us to special-case how we handle reporting those checks. Instead of
reporting each check in the --version
output, we report pep8
and check
pep8
the module for a __version__
attribute. We only report it once
to avoid confusing users.
API Documentation¶
-
class
flake8.plugins.manager.
PluginManager
(namespace, verify_requirements=False)¶ Find and manage plugins consistently.
-
__init__
(namespace, verify_requirements=False)¶ Initialize the manager.
Parameters:
-
map
(func, *args, **kwargs)¶ Call
func
with the plugin and *args and **kwargs after.This yields the return value from
func
for each plugin.Parameters: - func (collections.Callable) –
Function to call with each plugin. Signature should at least be:
def myfunc(plugin): pass
Any extra positional or keyword arguments specified with map will be passed along to this function after the plugin. The plugin passed is a
Plugin
. - args – Positional arguments to pass to
func
after each plugin. - kwargs – Keyword arguments to pass to
func
after each plugin.
- func (collections.Callable) –
-
-
class
flake8.plugins.manager.
Plugin
(name, entry_point)¶ Wrap an EntryPoint from setuptools and other logic.
-
__init__
(name, entry_point)¶ “Initialize our Plugin.
Parameters: - name (str) – Name of the entry-point as it was registered with setuptools.
- entry_point (setuptools.EntryPoint) – EntryPoint returned by setuptools.
-
disable
(optmanager)¶ Add the plugin name to the default ignore list.
-
enable
(optmanager)¶ Remove plugin name from the default ignore list.
-
execute
(*args, **kwargs)¶ Call the plugin with *args and **kwargs.
-
group
()¶ Find and parse the group the plugin is in.
-
is_in_a_group
()¶ Determine if this plugin is in a group.
Returns: True if the plugin is in a group, otherwise False. Return type: bool
-
load_plugin
(verify_requirements=False)¶ Retrieve the plugin for this entry-point.
This loads the plugin, stores it on the instance and then returns it. It does not reload it after the first time, it merely returns the cached plugin.
Parameters: verify_requirements (bool) – Whether or not to make setuptools verify that the requirements for the plugin are satisfied. Returns: Nothing
-
off_by_default
¶ Return whether the plugin is ignored by default.
-
parameter_names
¶ List of argument names that need to be passed to the plugin.
-
parameters
¶ List of arguments that need to be passed to the plugin.
-
plugin
¶ The loaded (and cached) plugin associated with the entry-point.
This property implicitly loads the plugin and then caches it.
-
plugin_name
¶ Return the name of the plugin.
-
provide_options
(optmanager, options, extra_args)¶ Pass the parsed options and extra arguments to the plugin.
-
register_options
(optmanager)¶ Register the plugin’s command-line options on the OptionManager.
Parameters: optmanager (flake8.options.manager.OptionManager) – Instantiated OptionManager to register options on. Returns: Nothing
-
version
¶ Return the version of the plugin.
-
-
class
flake8.plugins.manager.
PluginTypeManager
¶ Parent class for most of the specific plugin types.
-
get
(name, default=None)¶ Retrieve the plugin referred to by
name
or return the default.Parameters: - name (str) – Name of the plugin to retrieve.
- default – Default value to return.
Returns: Plugin object referred to by name, if it exists.
Return type: Plugin
-
load_plugins
()¶ Load all plugins of this type that are managed by this manager.
-
names
¶ Proxy attribute to underlying manager.
-
plugins
¶ Proxy attribute to underlying manager.
-
provide_options
(optmanager, options, extra_args)¶ Provide parsed options and extra arguments to the plugins.
-
register_options
(optmanager)¶ Register all of the checkers’ options to the OptionManager.
-
register_plugin_versions
(optmanager)¶ Register the plugins and their versions with the OptionManager.
-
-
class
flake8.plugins.manager.
Checkers
¶ All of the checkers registered through entry-ponits.
-
ast_plugins
¶ List of plugins that expect the AST tree.
-
checks_expecting
(argument_name)¶ Retrieve checks that expect an argument with the specified name.
Find all checker plugins that are expecting a specific argument.
-
logical_line_plugins
¶ List of plugins that expect the logical lines.
-
physical_line_plugins
¶ List of plugins that expect the physical lines.
-
register_options
(optmanager)¶ Register all of the checkers’ options to the OptionManager.
This also ensures that plugins that are not part of a group and are enabled by default are enabled on the option manager.
-
-
class
flake8.plugins.manager.
Listeners
¶ All of the listeners registered through entry-points.
-
build_notifier
()¶ Build a Notifier for our Listeners.
Returns: Object to notify our listeners of certain error codes and warnings. Return type: Notifier
-
-
class
flake8.plugins.manager.
ReportFormatters
¶ All of the report formatters registered through entry-points.
-
class
flake8.plugins.notifier.
Notifier
¶ Object that tracks and notifies listener objects.
-
class
flake8.plugins._trie.
Trie
¶ The object that manages the trie nodes.