Plugins have existed for 2.x for a few years. There are a number of these on PyPI already. While it did not seem reasonable for 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.
Note
If your plugin does not register options, it should Just Work.
The only two breaking changes in 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 stdin_get_value
functions. On 2.x, to have an option parsed from the configuration files that 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 3.0, we have added three arguments to the ~flake8.options.manager.OptionManager.add_option
method you will call on the parser you receive:
parse_from_config
which expectsTrue
orFalse
When
True
, will parse the option from the config files finds.comma_separated_list
which expectsTrue
orFalse
When
True
, will split the string intelligently and handle extra whitespace. The parsed value will be a list.normalize_paths
which expectsTrue
orFalse
When
True
, will:- remove trailing path separators (i.e.,
os.path.sep
) - 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.
The example from 2.x now looks like:
parser.add_option('-X', '--example-flag', type='string',
parse_from_config=True,
help='...')
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
). 2.x often forced users to parse these lists themselves since pep8 special-cased certain flags and left others on their own. 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 will just return the list:
["first", "second", "third", "fourth", "fifth"]
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 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.
To ease the transition, the maintainers have released flake8-polyfill. 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 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.
flake8_polyfill.options.register
After releasing 2.6, handling standard-in became a bit trickier for some plugins. 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 stdin_get_value
function. flake8-polyfill
provides this functionality for plugin developers via its flake8_polyfill.stdin
module.
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 all
, 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.
flake8_polyfill.stdin.monkey_patch