summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/source/user/configuration.rst21
-rw-r--r--src/flake8/main/application.py2
-rw-r--r--src/flake8/options/config.py37
-rw-r--r--tests/fixtures/config_files/local-plugin-path.ini5
-rw-r--r--tests/integration/subdir/aplugin.py16
-rw-r--r--tests/integration/test_plugins.py9
6 files changed, 84 insertions, 6 deletions
diff --git a/docs/source/user/configuration.rst b/docs/source/user/configuration.rst
index eacacef..b036eff 100644
--- a/docs/source/user/configuration.rst
+++ b/docs/source/user/configuration.rst
@@ -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.
diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py
index 6c68305..233b9af 100644
--- a/src/flake8/main/application.py
+++ b/src/flake8/main/application.py
@@ -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)
diff --git a/src/flake8/options/config.py b/src/flake8/options/config.py
index 71429af..b5b42fb 100644
--- a/src/flake8/options/config.py
+++ b/src/flake8/options/config.py
@@ -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')
diff --git a/tests/fixtures/config_files/local-plugin-path.ini b/tests/fixtures/config_files/local-plugin-path.ini
new file mode 100644
index 0000000..7368c1e
--- /dev/null
+++ b/tests/fixtures/config_files/local-plugin-path.ini
@@ -0,0 +1,5 @@
+[flake8:local-plugins]
+extension =
+ XE = aplugin:ExtensionTestPlugin2
+paths =
+ ../../integration/subdir/
diff --git a/tests/integration/subdir/aplugin.py b/tests/integration/subdir/aplugin.py
new file mode 100644
index 0000000..98a0464
--- /dev/null
+++ b/tests/integration/subdir/aplugin.py
@@ -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
diff --git a/tests/integration/test_plugins.py b/tests/integration/test_plugins.py
index 6d51a4a..e59eb91 100644
--- a/tests/integration/test_plugins.py
+++ b/tests/integration/test_plugins.py
@@ -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'