Commit 61375eee authored by Ondřej Nový's avatar Ondřej Nový

Import python-flake8_3.5.0.orig.tar.gz

parent e9c61624
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@gmail.com
License: MIT
Description-Content-Type: UNKNOWN
Description: ========
Flake8
========
......
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
......@@ -9,6 +9,7 @@ with the newest releases first.
==================
.. toctree::
3.5.0
3.4.1
3.4.0
3.3.0
......
......@@ -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.
......@@ -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
......
......@@ -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'),
......
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@gmail.com
License: MIT
Description-Content-Type: UNKNOWN
Description: ========
Flake8
========
......
......@@ -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
......
......@@ -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
......
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
......
......@@ -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())
......
......@@ -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([])
......
......@@ -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
......
......@@ -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
)
......
......@@ -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)
......
......@@ -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():
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
"""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')
"""Option handling and Option management logic."""
import collections