Writing Plugins For Flake8 2 and 3¶
Plugins have existed for Flake8 2.x for a few years. There are a number of these on PyPI already. While it did not seem reasonable for Flake8 to attempt to provide a backwards compatible shim for them, we did decide to try to document the easiest way to write a plugin that’s compatible across both versions.
If your plugin does not register options, it should Just Work.
The only two breaking changes in Flake8 3.0 is the fact that we no
longer check the option parser for a list of strings to parse from a config
file and we no longer patch pep8 or pycodestyle’s
functions. On Flake8 2.x, to have an option parsed from the configuration
files that Flake8 finds and parses you would have to do something like:
parser.add_option('-X', '--example-flag', type='string', help='...') parser.config_options.append('example-flag')
For Flake8 3.0, we have added three arguments to the
add_option() method you will call
on the parser you receive:
True, Flake8 will parse the option from the config files Flake8 finds.
True, Flake8 will split the string intelligently and handle extra whitespace. The parsed value will be a list.
True, Flake8 will:
- remove trailing path separators (i.e.,
- return the absolute path for values that have the separator in them
- remove trailing path separators (i.e.,
All three of these options can be combined or used separately.
Parsing Options from Configuration Files¶
The example from Flake8 2.x now looks like:
parser.add_option('-X', '--example-flag', type='string', parse_from_config=True, help='...')
Parsing Comma-Separated Lists¶
Now let’s imagine that the option we want to add is expecting a comma-separatd
list of values from the user (e.g.,
--select E123,W503,F405). Flake8 2.x
often forced users to parse these lists themselves since pep8 special-cased
certain flags and left others on their own. Flake8 3.0 adds
comma_separated_list so that the parsed option is already a list for
plugin authors. When combined with
parse_from_config this means that users
can also do something like:
example-flag = first, second, third, fourth, fifth
And Flake8 will just return the list:
["first", "second", "third", "fourth", "fifth"]
Normalizing Values that Are Paths¶
Finally, let’s imagine that our new option wants a path or list of paths. To
ensure that these paths are semi-normalized (the way Flake8 2.x used to
work) we need only pass
normalize_paths=True. If you have specified
comma_separated_list=True then this will parse the value as a list of
paths that have been normalized. Otherwise, this will parse the value
as a single path.
Option Handling on Flake8 2 and 3¶
To ease the transition, the Flake8 maintainers have released
flake8-polyfill provides a convenience function to help users
transition between Flake8 2 and 3 without issue. For example, if your plugin
has to work on Flake8 2.x and 3.x but you want to take advantage of some of
the new options to
add_option, you can do
from flake8_polyfill import options class MyPlugin(object): @classmethod def add_options(cls, parser): options.register( parser, '--application-names', default='', type='string', help='Names of the applications to be checked.', parse_from_config=True, comma_separated_list=True, ) options.register( parser, '--style-name', default='', type='string', help='The name of the style convention you want to use', parse_from_config=True, ) options.register( parser, '--application-paths', default='', type='string', help='Locations of the application code', parse_from_config=True, comma_separated_list=True, normalize_paths=True, ) @classmethod def parse_options(cls, parsed_options): cls.application_names = parsed_options.application_names cls.style_name = parsed_options.style_name cls.application_paths = parsed_options.application_paths
flake8-polyfill will handle these extra options using callbacks to the option
parser. The project has direct replications of the functions that Flake8
uses to provide the same functionality. This means that the values you receive
should be identically parsed whether you’re using Flake8 2.x or 3.x.
register(parser, *args, **kwargs)¶
Register an option for the Option Parser provided by Flake8.
- parser – The option parser being used by Flake8 to handle command-line options.
- *args – Positional arguments that you might otherwise pass to
- **kwargs – Keyword arguments you might otherwise pass to
Standard In Handling on Flake8 2.5, 2.6, and 3¶
After releasing Flake8 2.6, handling standard-in became a bit trickier for
some plugins. Flake8 2.5 and earlier had started monkey-patching pep8’s
stdin_get_value function. 2.6 switched to pycodestyle and only
monkey-patched that. 3.0 has its own internal implementation and uses that but
does not directly provide anything for plugins using pep8 and pycodestyle’s
flake8-polyfill provides this functionality for
plugin developers via it’s
If a plugin needs to read the content from stdin, it can do the following:
from flake8_polyfill import stdin stdin.monkey_patch('pep8') # To monkey-patch only pep8 stdin.monkey_patch('pycodestyle') # To monkey-patch only pycodestyle stdin.monkey_patch('all') # To monkey-patch both pep8 and pycodestyle
Further, when using
flake8-polyfill does not require both packages to be
installed but will attempt to monkey-patch both and will silently ignore the
fact that pep8 or pycodestyle is not installed.
Monkey-patch the specified module with the appropriate stdin.
On Flake8 2.5 and lower, Flake8 would would monkey-patch
pep8.stdin_get_valuefor everyone. This avoided problems where stdin might be exhausted.
On Flake8 2.6, Flake8 stopped patching
pep8and started monkey-patching
On Flake8 3.x, Flake8 has no need to monkey patch either
This function accepts three parameters:
“all” is a special value that will monkey-patch both “pep8” and “pycodestyle”.
Parameters: which (str) – The name of the module to patch. Returns: Nothing. Return type: NoneType