[Python-modules-commits] [python-flake8] 01/08: Import python-flake8_3.5.0.orig.tar.gz

Ondrej Novy onovy at debian.org
Sat Dec 9 23:33:58 UTC 2017


This is an automated email from the git hooks/post-receive script.

onovy pushed a commit to branch master
in repository python-flake8.

commit 61375eeebdf6b009c9c469323bd4069c9d746270
Author: Ondřej Nový <onovy at debian.org>
Date:   Sun Dec 10 00:11:28 2017 +0100

    Import python-flake8_3.5.0.orig.tar.gz
---
 PKG-INFO                                     |   3 +-
 docs/source/release-notes/3.5.0.rst          |  46 ++++++++++
 docs/source/release-notes/index.rst          |   1 +
 docs/source/user/configuration.rst           |  48 +++++++++++
 setup.cfg                                    |   2 +-
 setup.py                                     |  10 ++-
 src/flake8.egg-info/PKG-INFO                 |   3 +-
 src/flake8.egg-info/SOURCES.txt              |   4 +
 src/flake8.egg-info/entry_points.txt         |   2 +
 src/flake8.egg-info/requires.txt             |   8 +-
 src/flake8/__init__.py                       |   2 +-
 src/flake8/api/legacy.py                     |   5 ++
 src/flake8/checker.py                        |   2 +
 src/flake8/defaults.py                       |   2 +-
 src/flake8/main/application.py               | 112 +++++++++++++++++-------
 src/flake8/main/debug.py                     |  10 ++-
 src/flake8/main/git.py                       |   6 ++
 src/flake8/main/mercurial.py                 |  20 ++++-
 src/flake8/options/aggregator.py             |  13 ++-
 src/flake8/options/config.py                 | 122 ++++++++++++++++++++-------
 src/flake8/options/manager.py                |  17 ++--
 src/flake8/plugins/manager.py                |  60 ++++++++++---
 src/flake8/utils.py                          |  10 ++-
 tests/fixtures/config_files/README.rst       |  10 ++-
 tests/fixtures/config_files/local-plugin.ini |   5 ++
 tests/integration/test_aggregator.py         |   9 +-
 tests/integration/test_plugins.py            |  58 +++++++++++++
 tests/unit/test_application.py               |  59 +++++++------
 tests/unit/test_config_file_finder.py        |  37 +++++++-
 tests/unit/test_debug.py                     |  35 +++++---
 tests/unit/test_get_local_plugins.py         |  41 +++++++++
 tests/unit/test_legacy_api.py                |   4 +
 tests/unit/test_merged_config_parser.py      |  81 +++++++-----------
 tests/unit/test_option_manager.py            |  38 +++++----
 tests/unit/test_plugin_manager.py            |  12 +++
 tests/unit/test_plugin_type_manager.py       |   2 +-
 tests/unit/test_utils.py                     |   1 +
 tests/unit/test_violation.py                 |   1 +
 38 files changed, 682 insertions(+), 219 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index c689934..e118cf0 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,11 +1,12 @@
 Metadata-Version: 1.1
 Name: flake8
-Version: 3.4.1
+Version: 3.5.0
 Summary: the modular source code checker: pep8, pyflakes and co
 Home-page: https://gitlab.com/pycqa/flake8
 Author: Ian Stapleton Cordasco
 Author-email: graffatcolmingov at gmail.com
 License: MIT
+Description-Content-Type: UNKNOWN
 Description: ========
          Flake8
         ========
diff --git a/docs/source/release-notes/3.5.0.rst b/docs/source/release-notes/3.5.0.rst
new file mode 100644
index 0000000..ff3a140
--- /dev/null
+++ b/docs/source/release-notes/3.5.0.rst
@@ -0,0 +1,46 @@
+3.5.0 -- 2017-10-23
+-------------------
+
+You can view the `3.5.0 milestone`_ on GitLab for more details.
+
+New Dependency Information
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Allow for PyFlakes 1.6.0 (See also `GitLab#359`_)
+
+- Start using new PyCodestyle checks for bare excepts and ambiguous identifier
+  (See also `GitLab#361`_)
+
+Features
+~~~~~~~~
+
+- Print out information about configuring VCS hooks (See also `GitLab#335`_)
+
+- Allow users to develop plugins "local" to a repository without using
+  setuptools. See our documentation on local plugins for more information.
+  (See also `GitLab#357`_)
+
+Bugs Fixed
+~~~~~~~~~~
+
+- Catch and helpfully report ``UnicodeDecodeError``\ s when parsing
+  configuration files. (See also `GitLab#358`_)
+
+
+.. all links
+.. _3.5.0 milestone:
+    https://gitlab.com/pycqa/flake8/milestones/20
+
+.. issue links
+.. _GitLab#335:
+    https://gitlab.com/pycqa/flake8/issues/335
+.. _GitLab#357:
+    https://gitlab.com/pycqa/flake8/issues/357
+.. _GitLab#358:
+    https://gitlab.com/pycqa/flake8/issues/358
+.. _GitLab#359:
+    https://gitlab.com/pycqa/flake8/issues/359
+.. _GitLab#361:
+    https://gitlab.com/pycqa/flake8/issues/361
+
+.. merge request links
diff --git a/docs/source/release-notes/index.rst b/docs/source/release-notes/index.rst
index b15d937..99f5dcf 100644
--- a/docs/source/release-notes/index.rst
+++ b/docs/source/release-notes/index.rst
@@ -9,6 +9,7 @@ with the newest releases first.
 ==================
 
 .. toctree::
+    3.5.0
     3.4.1
     3.4.0
     3.3.0
diff --git a/docs/source/user/configuration.rst b/docs/source/user/configuration.rst
index 5e81807..eacacef 100644
--- a/docs/source/user/configuration.rst
+++ b/docs/source/user/configuration.rst
@@ -222,3 +222,51 @@ They use the comments to describe the check but they could also write this as:
 Or they could use each comment to describe **why** they've ignored the check.
 |Flake8| knows how to parse these lists and will appropriately handle
 these situations.
+
+
+Using Local Plugins
+-------------------
+
+.. versionadded:: 3.5.0
+
+|Flake8| allows users to write plugins that live locally in a project. These
+plugins do not need to use setuptools or any of the other overhead associated
+with plugins distributed on PyPI. To use these plugins, users must specify
+them in their configuration file (i.e., ``.flake8``, ``setup.cfg``, or
+``tox.ini``). This must be configured in a separate INI section named
+``flake8:local-plugins``.
+
+Users may configure plugins that check source code, i.e., ``extension``
+plugins, and plugins that report errors, i.e., ``report`` plugins.
+
+An example configuration might look like:
+
+.. code-block:: ini
+
+    [flake8:local-plugins]
+    extension =
+        MC1 = project.flake8.checkers:MyChecker1
+        MC2 = project.flake8.checkers:MyChecker2
+    report =
+        MR1 = project.flake8.reporters:MyReporter1
+        MR2 = project.flake8.reporters:MyReporter2
+
+|Flake8| will also, however, allow for commas to separate the plugins for
+example:
+
+.. code-block:: ini
+
+    [flake8:local-plugins]
+    extension =
+        MC1 = project.flake8.checkers:MyChecker1,
+        MC2 = project.flake8.checkers:MyChecker2
+    report =
+        MR1 = project.flake8.reporters:MyReporter1,
+        MR2 = project.flake8.reporters:MyReporter2
+
+These configurations will allow you to select your own custom reporter plugin
+that you've designed or will utilize your new check classes.
+
+.. note::
+
+    These plugins otherwise follow the same guidelines as regular plugins.
diff --git a/setup.cfg b/setup.cfg
index de6418f..d1c2524 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -8,7 +8,7 @@ universal = 1
 requires-dist = 
 	enum34; python_version<"3.4"
 	configparser; python_version<"3.2"
-	pyflakes >= 1.5.0, < 1.6.0
+	pyflakes >= 1.5.0, < 1.7.0
 	pycodestyle >= 2.0.0, < 2.4.0
 	mccabe >= 0.6.0, < 0.7.0
 
diff --git a/setup.py b/setup.py
index de350a8..aa45e43 100644
--- a/setup.py
+++ b/setup.py
@@ -17,7 +17,11 @@ tests_require = ['mock >= 2.0.0', 'pytest']
 # NOTE(sigmavirus24): When updating these requirements, update them in
 # setup.cfg as well.
 requires = [
-    "pyflakes >= 1.5.0, < 1.6.0",
+    # We document the reasoning for using ranges here:
+    # http://flake8.pycqa.org/en/latest/faq.html#why-does-flake8-use-ranges-for-its-dependencies
+    # And in which releases we will update those ranges here:
+    # http://flake8.pycqa.org/en/latest/internal/releases.html#releasing-flake8
+    "pyflakes >= 1.5.0, < 1.7.0",
     "pycodestyle >= 2.0.0, < 2.4.0",
     "mccabe >= 0.6.0, < 0.7.0",
     "setuptools >= 30",
@@ -108,8 +112,8 @@ setuptools.setup(
             PEP8_PLUGIN('comparison_to_singleton'),
             PEP8_PLUGIN('comparison_negative'),
             PEP8_PLUGIN('comparison_type'),
-            # NOTE(sigmavirus24): Add this back once PyCodestyle 2.1.0 is out
-            # PEP8_PLUGIN('ambiguous_identifier'),
+            PEP8_PLUGIN('ambiguous_identifier'),
+            PEP8_PLUGIN('bare_except'),
             PEP8_PLUGIN('python_3000_has_key'),
             PEP8_PLUGIN('python_3000_raise_comma'),
             PEP8_PLUGIN('python_3000_not_equal'),
diff --git a/src/flake8.egg-info/PKG-INFO b/src/flake8.egg-info/PKG-INFO
index c689934..e118cf0 100644
--- a/src/flake8.egg-info/PKG-INFO
+++ b/src/flake8.egg-info/PKG-INFO
@@ -1,11 +1,12 @@
 Metadata-Version: 1.1
 Name: flake8
-Version: 3.4.1
+Version: 3.5.0
 Summary: the modular source code checker: pep8, pyflakes and co
 Home-page: https://gitlab.com/pycqa/flake8
 Author: Ian Stapleton Cordasco
 Author-email: graffatcolmingov at gmail.com
 License: MIT
+Description-Content-Type: UNKNOWN
 Description: ========
          Flake8
         ========
diff --git a/src/flake8.egg-info/SOURCES.txt b/src/flake8.egg-info/SOURCES.txt
index 159c19f..440bcef 100644
--- a/src/flake8.egg-info/SOURCES.txt
+++ b/src/flake8.egg-info/SOURCES.txt
@@ -76,6 +76,7 @@ docs/source/release-notes/3.2.1.rst
 docs/source/release-notes/3.3.0.rst
 docs/source/release-notes/3.4.0.rst
 docs/source/release-notes/3.4.1.rst
+docs/source/release-notes/3.5.0.rst
 docs/source/release-notes/index.rst
 docs/source/user/configuration.rst
 docs/source/user/error-codes.rst
@@ -132,6 +133,7 @@ tests/fixtures/config_files/cli-specified-without-inline-comments.ini
 tests/fixtures/config_files/cli-specified.ini
 tests/fixtures/config_files/config-with-hyphenated-options.ini
 tests/fixtures/config_files/local-config.ini
+tests/fixtures/config_files/local-plugin.ini
 tests/fixtures/config_files/no-flake8-section.ini
 tests/fixtures/config_files/user-config.ini
 tests/fixtures/diffs/multi_file_diff
@@ -143,6 +145,7 @@ tests/fixtures/example-code/inline-ignores/E501.py
 tests/fixtures/example-code/inline-ignores/E731.py
 tests/integration/test_aggregator.py
 tests/integration/test_checker.py
+tests/integration/test_plugins.py
 tests/unit/test_application.py
 tests/unit/test_base_formatter.py
 tests/unit/test_checker_manager.py
@@ -152,6 +155,7 @@ tests/unit/test_decision_engine.py
 tests/unit/test_file_checker.py
 tests/unit/test_file_processor.py
 tests/unit/test_filenameonly_formatter.py
+tests/unit/test_get_local_plugins.py
 tests/unit/test_git.py
 tests/unit/test_legacy_api.py
 tests/unit/test_merged_config_parser.py
diff --git a/src/flake8.egg-info/entry_points.txt b/src/flake8.egg-info/entry_points.txt
index 2b6ef95..5c15ca6 100644
--- a/src/flake8.egg-info/entry_points.txt
+++ b/src/flake8.egg-info/entry_points.txt
@@ -6,6 +6,8 @@ flake8 = flake8.main.setuptools_command:Flake8
 
 [flake8.extension]
 F = flake8.plugins.pyflakes:FlakesChecker
+pycodestyle.ambiguous_identifier = pycodestyle:ambiguous_identifier
+pycodestyle.bare_except = pycodestyle:bare_except
 pycodestyle.blank_lines = pycodestyle:blank_lines
 pycodestyle.break_around_binary_operator = pycodestyle:break_around_binary_operator
 pycodestyle.comparison_negative = pycodestyle:comparison_negative
diff --git a/src/flake8.egg-info/requires.txt b/src/flake8.egg-info/requires.txt
index c9e0506..66c3062 100644
--- a/src/flake8.egg-info/requires.txt
+++ b/src/flake8.egg-info/requires.txt
@@ -1,7 +1,7 @@
-pyflakes >= 1.5.0, < 1.6.0
-pycodestyle >= 2.0.0, < 2.4.0
-mccabe >= 0.6.0, < 0.7.0
-setuptools >= 30
+pyflakes<1.7.0,>=1.5.0
+pycodestyle<2.4.0,>=2.0.0
+mccabe<0.7.0,>=0.6.0
+setuptools>=30
 
 [:python_version<'3.2']
 configparser
diff --git a/src/flake8/__init__.py b/src/flake8/__init__.py
index 80fe44e..efb711c 100644
--- a/src/flake8/__init__.py
+++ b/src/flake8/__init__.py
@@ -27,7 +27,7 @@ LOG.addHandler(NullHandler())
 # Clean up after LOG config
 del NullHandler
 
-__version__ = '3.4.1'
+__version__ = '3.5.0'
 __version_info__ = tuple(int(i) for i in __version__.split('.') if i.isdigit())
 
 
diff --git a/src/flake8/api/legacy.py b/src/flake8/api/legacy.py
index 2b983c8..b332860 100644
--- a/src/flake8/api/legacy.py
+++ b/src/flake8/api/legacy.py
@@ -6,6 +6,7 @@ In 3.0 we no longer have an "engine" module but we maintain the API from it.
 import logging
 import os.path
 
+import flake8
 from flake8.formatting import base as formatter
 from flake8.main import application as app
 
@@ -26,6 +27,10 @@ def get_style_guide(**kwargs):
         :class:`StyleGuide`
     """
     application = app.Application()
+    application.parse_preliminary_options_and_args([])
+    flake8.configure_logging(
+        application.prelim_opts.verbose, application.prelim_opts.output_file)
+    application.make_config_finder()
     application.find_plugins()
     application.register_plugin_options()
     application.parse_configuration_and_cli([])
diff --git a/src/flake8/checker.py b/src/flake8/checker.py
index 569eafa..6e53cb5 100644
--- a/src/flake8/checker.py
+++ b/src/flake8/checker.py
@@ -209,6 +209,7 @@ class Manager(object):
 
         filename_patterns = self.options.filename
         running_from_vcs = self.options._running_from_vcs
+        running_from_diff = self.options.diff
 
         # NOTE(sigmavirus24): Yes this is a little unsightly, but it's our
         # best solution right now.
@@ -227,6 +228,7 @@ class Manager(object):
             # If it was specified explicitly, the user intended for it to be
             # checked.
             explicitly_provided = (not running_from_vcs and
+                                   not running_from_diff and
                                    (argument == filename))
             return ((file_exists and
                      (explicitly_provided or matches_filename_patterns)) or
diff --git a/src/flake8/defaults.py b/src/flake8/defaults.py
index e8c6bfb..55cb48a 100644
--- a/src/flake8/defaults.py
+++ b/src/flake8/defaults.py
@@ -46,7 +46,7 @@ NOQA_INLINE_REGEXP = re.compile(
     # We do not care about the ``: `` that follows ``noqa``
     # We do not care about the casing of ``noqa``
     # We want a comma-separated list of errors
-    '# noqa(?:: (?P<codes>([A-Z][0-9]+,?)+))?',
+    '# noqa(?:: (?P<codes>([A-Z][0-9]+(?:[,\s]+)?)+))?',
     re.IGNORECASE
 )
 
diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py
index 293ac92..6c68305 100644
--- a/src/flake8/main/application.py
+++ b/src/flake8/main/application.py
@@ -12,7 +12,7 @@ from flake8 import exceptions
 from flake8 import style_guide
 from flake8 import utils
 from flake8.main import options
-from flake8.options import aggregator
+from flake8.options import aggregator, config
 from flake8.options import manager
 from flake8.plugins import manager as plugin_manager
 
@@ -45,34 +45,16 @@ class Application(object):
             prog='flake8', version=flake8.__version__
         )
         options.register_default_options(self.option_manager)
-
-        # We haven't found or registered our plugins yet, so let's defer
-        # printing the version until we aggregate options from config files
-        # and the command-line. First, let's clone our arguments on the CLI,
-        # then we'll attempt to remove ``--version`` so that we can avoid
-        # triggering the "version" action in optparse. If it's not there, we
-        # do not need to worry and we can continue. If it is, we successfully
-        # defer printing the version until just a little bit later.
-        # Similarly we have to defer printing the help text until later.
-        args = sys.argv[:]
-        try:
-            args.remove('--version')
-        except ValueError:
-            pass
-        try:
-            args.remove('--help')
-        except ValueError:
-            pass
-        try:
-            args.remove('-h')
-        except ValueError:
-            pass
-
-        preliminary_opts, _ = self.option_manager.parse_known_args(args)
-        # Set the verbosity of the program
-        flake8.configure_logging(preliminary_opts.verbose,
-                                 preliminary_opts.output_file)
-
+        #: The preliminary options parsed from CLI before plugins are loaded,
+        #: into a :class:`optparse.Values` instance
+        self.prelim_opts = None
+        #: The preliminary arguments parsed from CLI before plugins are loaded
+        self.prelim_args = None
+        #: The instance of :class:`flake8.options.config.ConfigFileFinder`
+        self.config_finder = None
+
+        #: The :class:`flake8.options.config.LocalPlugins` found in config
+        self.local_plugins = None
         #: The instance of :class:`flake8.plugins.manager.Checkers`
         self.check_plugins = None
         #: The instance of :class:`flake8.plugins.manager.Listeners`
@@ -111,6 +93,48 @@ class Application(object):
         #: The parsed diff information
         self.parsed_diff = {}
 
+    def parse_preliminary_options_and_args(self, argv=None):
+        """Get preliminary options and args from CLI, pre-plugin-loading.
+
+        We need to know the values of a few standard options and args now, so
+        that we can find config files and configure logging.
+
+        Since plugins aren't loaded yet, there may be some as-yet-unknown
+        options; we ignore those for now, they'll be parsed later when we do
+        real option parsing.
+
+        Sets self.prelim_opts and self.prelim_args.
+
+        :param list argv:
+            Command-line arguments passed in directly.
+        """
+        # We haven't found or registered our plugins yet, so let's defer
+        # printing the version until we aggregate options from config files
+        # and the command-line. First, let's clone our arguments on the CLI,
+        # then we'll attempt to remove ``--version`` so that we can avoid
+        # triggering the "version" action in optparse. If it's not there, we
+        # do not need to worry and we can continue. If it is, we successfully
+        # defer printing the version until just a little bit later.
+        # Similarly we have to defer printing the help text until later.
+        args = (argv or sys.argv)[:]
+        try:
+            args.remove('--version')
+        except ValueError:
+            pass
+        try:
+            args.remove('--help')
+        except ValueError:
+            pass
+        try:
+            args.remove('-h')
+        except ValueError:
+            pass
+
+        opts, args = self.option_manager.parse_known_args(args)
+        # parse_known_args includes program name and unknown options as args
+        args = [a for a in args[1:] if not a.startswith('-')]
+        self.prelim_opts, self.prelim_args = opts, args
+
     def exit(self):
         # type: () -> NoneType
         """Handle finalization and exiting the program.
@@ -125,6 +149,17 @@ class Application(object):
             raise SystemExit((self.result_count > 0) or
                              self.catastrophic_failure)
 
+    def make_config_finder(self):
+        """Make our ConfigFileFinder based on preliminary opts and args."""
+        if self.config_finder is None:
+            extra_config_files = utils.normalize_paths(
+                self.prelim_opts.append_config)
+            self.config_finder = config.ConfigFileFinder(
+                self.option_manager.program_name,
+                self.prelim_args,
+                extra_config_files,
+            )
+
     def find_plugins(self):
         # type: () -> NoneType
         """Find and load the plugins for this application.
@@ -135,14 +170,23 @@ class Application(object):
         of finding plugins (via :mod:`pkg_resources`) we want this to be
         idempotent and so only update those attributes if they are ``None``.
         """
+        if self.local_plugins is None:
+            self.local_plugins = config.get_local_plugins(
+                self.config_finder,
+                self.prelim_opts.config,
+                self.prelim_opts.isolated,
+            )
+
         if self.check_plugins is None:
-            self.check_plugins = plugin_manager.Checkers()
+            self.check_plugins = plugin_manager.Checkers(
+                self.local_plugins.extension)
 
         if self.listening_plugins is None:
             self.listening_plugins = plugin_manager.Listeners()
 
         if self.formatting_plugins is None:
-            self.formatting_plugins = plugin_manager.ReportFormatters()
+            self.formatting_plugins = plugin_manager.ReportFormatters(
+                self.local_plugins.report)
 
         self.check_plugins.load_plugins()
         self.listening_plugins.load_plugins()
@@ -165,7 +209,7 @@ class Application(object):
         """
         if self.options is None and self.args is None:
             self.options, self.args = aggregator.aggregate_options(
-                self.option_manager, argv
+                self.option_manager, self.config_finder, argv
             )
 
         self.running_against_diff = self.options.diff
@@ -314,6 +358,10 @@ class Application(object):
         """
         # NOTE(sigmavirus24): When updating this, make sure you also update
         # our legacy API calls to these same methods.
+        self.parse_preliminary_options_and_args(argv)
+        flake8.configure_logging(
+            self.prelim_opts.verbose, self.prelim_opts.output_file)
+        self.make_config_finder()
         self.find_plugins()
         self.register_plugin_options()
         self.parse_configuration_and_cli(argv)
diff --git a/src/flake8/main/debug.py b/src/flake8/main/debug.py
index e6ea141..ca3827e 100644
--- a/src/flake8/main/debug.py
+++ b/src/flake8/main/debug.py
@@ -53,8 +53,14 @@ def information(option_manager):
 
 def plugins_from(option_manager):
     """Generate the list of plugins installed."""
-    return [{'plugin': plugin, 'version': version}
-            for (plugin, version) in sorted(option_manager.registered_plugins)]
+    return [
+        {
+            'plugin': plugin.name,
+            'version': plugin.version,
+            'is_local': plugin.local,
+        }
+        for plugin in sorted(option_manager.registered_plugins)
+    ]
 
 
 def dependencies():
diff --git a/src/flake8/main/git.py b/src/flake8/main/git.py
index 3cda3c5..ad55100 100644
--- a/src/flake8/main/git.py
+++ b/src/flake8/main/git.py
@@ -68,6 +68,8 @@ def install():
     pre-commit python script in the hooks sub-directory if one does not
     already exist.
 
+    It will also print a message to stdout about how to configure the hook.
+
     :returns:
         True if successful, False if the git directory doesn't exist.
     :rtype:
@@ -105,6 +107,10 @@ def install():
     # so that git can actually execute it as a hook.
     pre_commit_permissions = stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH
     os.chmod(pre_commit_file, pre_commit_permissions)
+
+    print('git pre-commit hook installed, for configuration options see')
+    print('http://flake8.pycqa.org/en/latest/user/using-hooks.html')
+
     return True
 
 
diff --git a/src/flake8/main/mercurial.py b/src/flake8/main/mercurial.py
index a46f676..344c9f7 100644
--- a/src/flake8/main/mercurial.py
+++ b/src/flake8/main/mercurial.py
@@ -46,7 +46,22 @@ def hook(ui, repo, **kwargs):
 
 
 def install():
-    """Ensure that the mercurial hooks are installed."""
+    """Ensure that the mercurial hooks are installed.
+
+    This searches for the ``.hg/hgrc`` configuration file and will add commit
+    and qrefresh hooks to it, if they do not already exist.
+
+    It will also print a message to stdout about how to configure the hook.
+
+    :returns:
+        True if successful, False if the ``.hg/hgrc`` file doesn't exist.
+    :rtype:
+        bool
+    :raises:
+        flake8.exceptions.MercurialCommitHookAlreadyExists
+    :raises:
+        flake8.exceptions.MercurialQRefreshHookAlreadyExists
+    """
     hgrc = find_hgrc(create_if_missing=True)
     if hgrc is None:
         return False
@@ -80,6 +95,9 @@ def install():
     with open(hgrc, 'w') as fd:
         hgconfig.write(fd)
 
+    print('mercurial hooks installed, for configuration options see')
+    print('http://flake8.pycqa.org/en/latest/user/using-hooks.html')
+
     return True
 
 
diff --git a/src/flake8/options/aggregator.py b/src/flake8/options/aggregator.py
index 4075dc9..5b8ab9c 100644
--- a/src/flake8/options/aggregator.py
+++ b/src/flake8/options/aggregator.py
@@ -5,17 +5,18 @@ applies the user-specified command-line configuration on top of it.
 """
 import logging
 
-from flake8 import utils
 from flake8.options import config
 
 LOG = logging.getLogger(__name__)
 
 
-def aggregate_options(manager, arglist=None, values=None):
+def aggregate_options(manager, config_finder, arglist=None, values=None):
     """Aggregate and merge CLI and config file options.
 
-    :param flake8.option.manager.OptionManager manager:
+    :param flake8.options.manager.OptionManager manager:
         The instance of the OptionManager that we're presently using.
+    :param flake8.options.config.ConfigFileFinder config_finder:
+        The config file finder to use.
     :param list arglist:
         The list of arguments to pass to ``manager.parse_args``. In most cases
         this will be None so ``parse_args`` uses ``sys.argv``. This is mostly
@@ -32,14 +33,12 @@ def aggregate_options(manager, arglist=None, values=None):
     default_values, _ = manager.parse_args([], values=values)
     # Get original CLI values so we can find additional config file paths and
     # see if --config was specified.
-    original_values, original_args = manager.parse_args(arglist)
-    extra_config_files = utils.normalize_paths(original_values.append_config)
+    original_values, _ = manager.parse_args(arglist)
 
     # Make our new configuration file mergerator
     config_parser = config.MergedConfigParser(
         option_manager=manager,
-        extra_config_files=extra_config_files,
-        args=original_args,
+        config_finder=config_finder,
     )
 
     # Get the parsed config
diff --git a/src/flake8/options/config.py b/src/flake8/options/config.py
index a6ac63f..71429af 100644
--- a/src/flake8/options/config.py
+++ b/src/flake8/options/config.py
@@ -1,9 +1,12 @@
 """Config handling logic for Flake8."""
+import collections
 import configparser
 import logging
 import os.path
 import sys
 
+from flake8 import utils
+
 LOG = logging.getLogger(__name__)
 
 __all__ = ('ConfigFileFinder', 'MergedConfigParser')
@@ -49,24 +52,39 @@ class ConfigFileFinder(object):
             args = ['.']
         self.parent = self.tail = os.path.abspath(os.path.commonprefix(args))
 
+        # caches to avoid double-reading config files
+        self._local_configs = None
+        self._user_config = None
+        self._cli_configs = {}
+
     @staticmethod
     def _read_config(files):
         config = configparser.RawConfigParser()
-        try:
-            found_files = config.read(files)
-        except configparser.ParsingError:
-            LOG.exception("There was an error trying to parse a config "
-                          "file. The files we were attempting to parse "
-                          "were: %r", files)
-            found_files = []
+        if isinstance(files, (str, type(u''))):
+            files = [files]
+
+        found_files = []
+        for filename in files:
+            try:
+                found_files.extend(config.read(filename))
+            except UnicodeDecodeError:
+                LOG.exception("There was an error decoding a config file."
+                              "The file with a problem was %s.",
+                              filename)
+            except configparser.ParsingError:
+                LOG.exception("There was an error trying to parse a config "
+                              "file. The file with a problem was %s.",
+                              filename)
         return (config, found_files)
 
     def cli_config(self, files):
         """Read and parse the config file specified on the command-line."""
-        config, found_files = self._read_config(files)
-        if found_files:
-            LOG.debug('Found cli configuration files: %s', found_files)
-        return config
+        if files not in self._cli_configs:
+            config, found_files = self._read_config(files)
+            if found_files:
+                LOG.debug('Found cli configuration files: %s', found_files)
+            self._cli_configs[files] = config
+        return self._cli_configs[files]
 
     def generate_possible_local_files(self):
         """Find and generate all local config files."""
@@ -104,10 +122,12 @@ class ConfigFileFinder(object):
 
     def local_configs(self):
         """Parse all local config files into one config object."""
-        config, found_files = self._read_config(self.local_config_files())
-        if found_files:
-            LOG.debug('Found local configuration files: %s', found_files)
-        return config
+        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
 
     def user_config_file(self):
         """Find the user-level config file."""
@@ -117,10 +137,12 @@ class ConfigFileFinder(object):
 
     def user_config(self):
         """Parse the user config file into a config object."""
-        config, found_files = self._read_config(self.user_config_file())
-        if found_files:
-            LOG.debug('Found user configuration files: %s', found_files)
-        return config
+        if self._user_config is None:
+            config, found_files = self._read_config(self.user_config_file())
+            if found_files:
+                LOG.debug('Found user configuration files: %s', found_files)
+            self._user_config = config
+        return self._user_config
 
 
 class MergedConfigParser(object):
@@ -138,30 +160,23 @@ class MergedConfigParser(object):
     #: :meth:`~configparser.RawConfigParser.getbool` method.
     GETBOOL_ACTIONS = {'store_true', 'store_false'}
 
-    def __init__(self, option_manager, extra_config_files=None, args=None):
+    def __init__(self, option_manager, config_finder):
         """Initialize the MergedConfigParser instance.
 
-        :param flake8.option.manager.OptionManager option_manager:
+        :param flake8.options.manager.OptionManager option_manager:
             Initialized OptionManager.
-        :param list extra_config_files:
-            List of extra config files to parse.
-        :params list args:
-            The extra parsed arguments from the command-line.
+        :param flake8.options.config.ConfigFileFinder config_finder:
+            Initialized ConfigFileFinder.
         """
         #: Our instance of flake8.options.manager.OptionManager
         self.option_manager = option_manager
         #: The prog value for the cli parser
         self.program_name = option_manager.program_name
-        #: Parsed extra arguments
-        self.args = args
         #: Mapping of configuration option names to
         #: :class:`~flake8.options.manager.Option` instances
         self.config_options = option_manager.config_options_dict
-        #: List of extra config files
-        self.extra_config_files = extra_config_files or []
         #: Our instance of our :class:`~ConfigFileFinder`
-        self.config_finder = ConfigFileFinder(self.program_name, self.args,
-                                              self.extra_config_files)
+        self.config_finder = config_finder
 
     def _normalize_value(self, option, value):
         final_value = option.normalize(
@@ -280,3 +295,48 @@ class MergedConfigParser(object):
             return self.parse_cli_config(cli_config)
 
         return self.merge_user_and_local_config()
+
+
+def get_local_plugins(config_finder, cli_config=None, isolated=False):
+    """Get local plugins lists from config files.
+
+    :param flake8.options.config.ConfigFileFinder config_finder:
+        The config file finder to use.
+    :param str cli_config:
+        Value of --config when specified at the command-line. Overrides
+        all other config files.
+    :param bool isolated:
+        Determines if we should parse configuration files at all or not.
+        If running in isolated mode, we ignore all configuration files
+    :returns:
+        LocalPlugins namedtuple containing two lists of plugin strings,
+        one for extension (checker) plugins and one for report plugins.
+    :rtype:
+        flake8.options.config.LocalPlugins
+    """
+    local_plugins = LocalPlugins(extension=[], report=[])
+    if isolated:
+        LOG.debug('Refusing to look for local plugins in configuration'
+                  'files due to user-requested isolation')
+        return local_plugins
+
+    if cli_config:
+        LOG.debug('Reading local plugins only from "%s" specified via '
+                  '--config by the user', cli_config)
+        config = config_finder.cli_config(cli_config)
+    else:
+        config = config_finder.local_configs()
+
+    section = '%s:local-plugins' % config_finder.program_name
+    for plugin_type in ['extension', 'report']:
+        if config.has_option(section, plugin_type):
+            local_plugins_string = config.get(section, plugin_type).strip()
+            plugin_type_list = getattr(local_plugins, plugin_type)
+            plugin_type_list.extend(utils.parse_comma_separated_list(
+                local_plugins_string,
+                regexp=utils.LOCAL_PLUGIN_LIST_RE,
+            ))
+    return local_plugins
+
+
+LocalPlugins = collections.namedtuple('LocalPlugins', 'extension report')
diff --git a/src/flake8/options/manager.py b/src/flake8/options/manager.py
index 7b23732..5b4796f 100644
--- a/src/flake8/options/manager.py
+++ b/src/flake8/options/manager.py
@@ -1,4 +1,5 @@
 """Option handling and Option management logic."""
+import collections
 import logging
 import optparse  # pylint: disable=deprecated-module
 
@@ -153,6 +154,10 @@ class Option(object):
         return self._opt
 
 
+PluginVersion = collections.namedtuple("PluginVersion",
+                                       ["name", "version", "local"])
+
+
 class OptionManager(object):
     """Manage Options and OptionParser while adding post-processing."""
 
@@ -178,9 +183,9 @@ class OptionManager(object):
         self.extended_default_select = set()
 
     @staticmethod
-    def format_plugin(plugin_tuple):
-        """Convert a plugin tuple into a dictionary mapping name to value."""
-        return dict(zip(["name", "version"], plugin_tuple))
+    def format_plugin(plugin):
+        """Convert a PluginVersion into a dictionary mapping name to value."""
+        return {attr: getattr(plugin, attr) for attr in ["name", "version"]}
 
     def add_option(self, *args, **kwargs):
         """Create and register a new option.
@@ -306,7 +311,7 @@ class OptionManager(object):
         self._normalize(options)
         return options, xargs
 
-    def register_plugin(self, name, version):
+    def register_plugin(self, name, version, local=False):
         """Register a plugin relying on the OptionManager.
 
         :param str name:
@@ -314,5 +319,7 @@ class OptionManager(object):
             attribute of the class or function loaded from the entry-point.
         :param str version:
             The version of the checker that we're using.
+        :param bool local:
+            Whether the plugin is local to the project/repository or not.
         """
-        self.registered_plugins.add((name, version))
+        self.registered_plugins.add(PluginVersion(name, version, local))
diff --git a/src/flake8/plugins/manager.py b/src/flake8/plugins/manager.py
index 80a9ef2..503dfbb 100644
--- a/src/flake8/plugins/manager.py
+++ b/src/flake8/plugins/manager.py
@@ -24,7 +24,7 @@ NO_GROUP_FOUND = object()
 class Plugin(object):
     """Wrap an EntryPoint from setuptools and other logic."""
 
-    def __init__(self, name, entry_point):
+    def __init__(self, name, entry_point, local=False):
         """Initialize our Plugin.
 
         :param str name:
@@ -33,9 +33,12 @@ class Plugin(object):
             EntryPoint returned by setuptools.
         :type entry_point:
             setuptools.EntryPoint
+        :param bool local:
+            Is this a repo-local plugin?
         """
         self.name = name
         self.entry_point = entry_point
+        self.local = local
         self._plugin = None
         self._parameters = None
         self._parameter_names = None
@@ -236,11 +239,14 @@ class Plugin(object):
 class PluginManager(object):  # pylint: disable=too-few-public-methods
     """Find and manage plugins consistently."""
 
-    def __init__(self, namespace, verify_requirements=False):
+    def __init__(self, namespace,
+                 verify_requirements=False, local_plugins=None):
         """Initialize the manager.
 
         :param str namespace:
             Namespace of the plugins to manage, e.g., 'flake8.extension'.
+        :param list local_plugins:
+            Plugins from config (as "X = path.to:Plugin" strings).
         :param bool verify_requirements:
             Whether or not to make setuptools verify that the requirements for
             the plugin are satisfied.
@@ -249,15 +255,36 @@ class PluginManager(object):  # pylint: disable=too-few-public-methods
         self.verify_requirements = verify_requirements
         self.plugins = {}
         self.names = []
-        self._load_all_plugins()
+        self._load_local_plugins(local_plugins or [])
+        self._load_entrypoint_plugins()
 
-    def _load_all_plugins(self):
+    def _load_local_plugins(self, local_plugins):
+        """Load local plugins from config.
+
+        :param list local_plugins:
+            Plugins from config (as "X = path.to:Plugin" strings).
+        """
+        for plugin_str in local_plugins:
+            entry_point = pkg_resources.EntryPoint.parse(plugin_str)
+            self._load_plugin_from_entrypoint(entry_point, local=True)
+
+    def _load_entrypoint_plugins(self):
         LOG.info('Loading entry-points for "%s".', self.namespace)
         for entry_point in pkg_resources.iter_entry_points(self.namespace):
-            name = entry_point.name
-            self.plugins[name] = Plugin(name, entry_point)
-            self.names.append(name)
-            LOG.debug('Loaded %r for plugin "%s".', self.plugins[name], name)
+            self._load_plugin_from_entrypoint(entry_point)
+
+    def _load_plugin_from_entrypoint(self, entry_point, local=False):
+        """Load a plugin from a setuptools EntryPoint.
+
+        :param EntryPoint entry_point:
+            EntryPoint to load plugin from.
+        :param bool local:
+            Is this a repo-local plugin?
+        """
+        name = entry_point.name
+        self.plugins[name] = Plugin(name, entry_point, local=local)
+        self.names.append(name)
+        LOG.debug('Loaded %r for plugin "%s".', self.plugins[name], name)
 
     def map(self, func, *args, **kwargs):
         r"""Call ``func`` with the plugin and \*args and \**kwargs after.
@@ -329,9 +356,14 @@ class PluginTypeManager(object):
 
... 867 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-flake8.git



More information about the Python-modules-commits mailing list