Add paths option in local-plugins config file.

This commit is contained in:
Carl Meyer 2017-10-23 16:49:09 -07:00
parent 8acf55e0f8
commit 423980164b
6 changed files with 84 additions and 6 deletions

View file

@ -267,6 +267,27 @@ example:
These configurations will allow you to select your own custom reporter plugin
that you've designed or will utilize your new check classes.
If your package is installed in the same virtualenv that |Flake8| will run
from, and your local plugins are part of that package, you're all set; |Flake8|
will be able to import your local plugins. However, if you are working on a
project that isn't set up as an installable package, or |Flake8| doesn't run
from the same virtualenv your code runs in, you may need to tell |Flake8| where
to import your local plugins from. You can do this via the ``paths`` option in
the ``local-plugins`` section of your config:
.. code-block:: ini
[flake8:local-plugins]
extension =
MC1 = myflake8plugin:MyChecker1
paths =
./path/to
Relative paths will be interpreted relative to the config file. Multiple paths
can be listed, one per line (or comma separated) as needed. If your local
plugins have any dependencies, it's up to you to ensure they are installed in
whatever Python environment |Flake8| runs in.
.. note::
These plugins otherwise follow the same guidelines as regular plugins.

View file

@ -177,6 +177,8 @@ class Application(object):
self.prelim_opts.isolated,
)
sys.path.extend(self.local_plugins.paths)
if self.check_plugins is None:
self.check_plugins = plugin_manager.Checkers(
self.local_plugins.extension)

View file

@ -54,6 +54,7 @@ class ConfigFileFinder(object):
# caches to avoid double-reading config files
self._local_configs = None
self._local_found_files = []
self._user_config = None
self._cli_configs = {}
@ -120,14 +121,22 @@ class ConfigFileFinder(object):
for filename in self.generate_possible_local_files()
] + [f for f in self.extra_config_files if exists(f)]
def local_configs(self):
"""Parse all local config files into one config object."""
def local_configs_with_files(self):
"""Parse all local config files into one config object.
Return (config, found_config_files) tuple.
"""
if self._local_configs is None:
config, found_files = self._read_config(self.local_config_files())
if found_files:
LOG.debug('Found local configuration files: %s', found_files)
self._local_configs = config
return self._local_configs
self._local_found_files = found_files
return (self._local_configs, self._local_found_files)
def local_configs(self):
"""Parse all local config files into one config object."""
return self.local_configs_with_files()[0]
def user_config_file(self):
"""Find the user-level config file."""
@ -314,7 +323,7 @@ def get_local_plugins(config_finder, cli_config=None, isolated=False):
:rtype:
flake8.options.config.LocalPlugins
"""
local_plugins = LocalPlugins(extension=[], report=[])
local_plugins = LocalPlugins(extension=[], report=[], paths=[])
if isolated:
LOG.debug('Refusing to look for local plugins in configuration'
'files due to user-requested isolation')
@ -324,8 +333,11 @@ def get_local_plugins(config_finder, cli_config=None, isolated=False):
LOG.debug('Reading local plugins only from "%s" specified via '
'--config by the user', cli_config)
config = config_finder.cli_config(cli_config)
config_files = [cli_config]
else:
config = config_finder.local_configs()
config, config_files = config_finder.local_configs_with_files()
base_dirs = {os.path.dirname(cf) for cf in config_files}
section = '%s:local-plugins' % config_finder.program_name
for plugin_type in ['extension', 'report']:
@ -336,7 +348,20 @@ def get_local_plugins(config_finder, cli_config=None, isolated=False):
local_plugins_string,
regexp=utils.LOCAL_PLUGIN_LIST_RE,
))
if config.has_option(section, 'paths'):
raw_paths = utils.parse_comma_separated_list(
config.get(section, 'paths').strip(),
regexp=utils.LOCAL_PLUGIN_LIST_RE,
)
norm_paths = []
for base_dir in base_dirs:
norm_paths.extend(
path for path in
utils.normalize_paths(raw_paths, parent=base_dir)
if os.path.exists(path)
)
local_plugins.paths.extend(norm_paths)
return local_plugins
LocalPlugins = collections.namedtuple('LocalPlugins', 'extension report')
LocalPlugins = collections.namedtuple('LocalPlugins', 'extension report paths')

View file

@ -0,0 +1,5 @@
[flake8:local-plugins]
extension =
XE = aplugin:ExtensionTestPlugin2
paths =
../../integration/subdir/

View file

@ -0,0 +1,16 @@
"""Module that is off sys.path by default, for testing local-plugin-paths."""
class ExtensionTestPlugin2(object):
"""Extension test plugin in its own directory."""
name = 'ExtensionTestPlugin2'
version = '1.0.0'
def __init__(self, tree):
"""Construct an instance of test plugin."""
pass
def run(self):
"""Do nothing."""
pass

View file

@ -3,6 +3,7 @@ from flake8.main import application
LOCAL_PLUGIN_CONFIG = 'tests/fixtures/config_files/local-plugin.ini'
LOCAL_PLUGIN_PATH_CONFIG = 'tests/fixtures/config_files/local-plugin-path.ini'
class ExtensionTestPlugin(object):
@ -56,3 +57,11 @@ def test_local_plugin_can_add_option():
['flake8', '--config', LOCAL_PLUGIN_CONFIG, '--anopt', 'foo'])
assert app.options.anopt == 'foo'
def test_enable_local_plugin_at_non_installed_path():
"""Can add a paths option in local-plugins config section for finding."""
app = application.Application()
app.initialize(['flake8', '--config', LOCAL_PLUGIN_PATH_CONFIG])
assert app.check_plugins['XE'].plugin.name == 'ExtensionTestPlugin2'