mirror of
https://github.com/PyCQA/flake8.git
synced 2026-03-30 02:46:52 +00:00
Updates to config/options aggregation
- Adds another flag to the Option class - Adds normalization to the config parsing - Removes dead code from _parse_config - Handles user specifying --append-config, --config, and --isolated but does not handle their mutual exclusion
This commit is contained in:
parent
07a29e45db
commit
67aff6d2ec
3 changed files with 148 additions and 18 deletions
51
flake8/options/aggregator.py
Normal file
51
flake8/options/aggregator.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
"""Aggregation function for CLI specified options and config file options.
|
||||
|
||||
This holds the logic that uses the collected and merged config files and
|
||||
applies the user-specified command-line configuration on top of it.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from flake8.options import config
|
||||
from flake8 import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def aggregate_options(manager, arglist=None, values=None):
|
||||
"""Function that aggregates the CLI and Config options."""
|
||||
# Get defaults from the option parser
|
||||
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()
|
||||
extra_config_files = utils.normalize_paths(original_values.append_config)
|
||||
|
||||
# Make our new configuration file mergerator
|
||||
config_parser = config.MergedConfigParser(
|
||||
option_manager=manager,
|
||||
extra_config_files=extra_config_files,
|
||||
args=original_args,
|
||||
)
|
||||
|
||||
# Get the parsed config
|
||||
parsed_config = config_parser.parse(original_values.config,
|
||||
original_values.isolated)
|
||||
|
||||
# Merge values parsed from config onto the default values returned
|
||||
for config_name, value in parsed_config.items():
|
||||
dest_name = config_name
|
||||
# If the config name is somehow different from the destination name,
|
||||
# fetch the destination name from our Option
|
||||
if not hasattr(default_values, config_name):
|
||||
dest_name = config_parser.config_options[config_name].dest
|
||||
|
||||
LOG.debug('Overriding default value of (%s) for "%s" with (%s)',
|
||||
getattr(default_values, dest_name, None),
|
||||
dest_name,
|
||||
value)
|
||||
# Override the default values with the config values
|
||||
setattr(default_values, dest_name, value)
|
||||
|
||||
# Finally parse the command-line options
|
||||
final_values, args = manager.parse_args(arglist, default_values)
|
||||
return final_values, args
|
||||
|
|
@ -11,11 +11,16 @@ from . import utils
|
|||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
__all__('ConfigFileFinder', 'MergedConfigParser')
|
||||
|
||||
|
||||
class ConfigFileFinder(object):
|
||||
PROJECT_FILENAMES = ('setup.cfg', 'tox.ini')
|
||||
|
||||
def __init__(self, program_name, args):
|
||||
def __init__(self, program_name, args, extra_config_files):
|
||||
# The values of --append-config from the CLI
|
||||
self.extra_config_files = extra_config_files
|
||||
|
||||
# Platform specific settings
|
||||
self.is_windows = sys.platform == 'win32'
|
||||
self.xdg_home = os.environ.get('XDG_CONFIG_HOME',
|
||||
|
|
@ -27,8 +32,6 @@ class ConfigFileFinder(object):
|
|||
|
||||
# List of filenames to find in the local/project directory
|
||||
self.project_filenames = ('setup.cfg', 'tox.ini', self.program_config)
|
||||
# List of filenames to find "globally"
|
||||
self.global_filenames = (self.program_config,)
|
||||
|
||||
self.local_directory = os.curdir
|
||||
|
||||
|
|
@ -42,6 +45,12 @@ class ConfigFileFinder(object):
|
|||
found_files = config.read(files)
|
||||
return (config, found_files)
|
||||
|
||||
def cli_config(self, files):
|
||||
config, found_files = self._read_config(files)
|
||||
if found_files:
|
||||
LOG.debug('Found cli configuration files: %s', found_files)
|
||||
return config
|
||||
|
||||
def generate_possible_local_config_files(self):
|
||||
tail = self.tail
|
||||
parent = self.parent
|
||||
|
|
@ -57,7 +66,7 @@ class ConfigFileFinder(object):
|
|||
filename
|
||||
for filename in self.generate_possible_local_config_files()
|
||||
if os.path.exists(filename)
|
||||
]
|
||||
] + self.extra_config_files
|
||||
|
||||
def local_configs(self):
|
||||
config, found_files = self._read_config(self.local_config_files())
|
||||
|
|
@ -81,20 +90,33 @@ class MergedConfigParser(object):
|
|||
GETINT_METHODS = set(['int', 'count'])
|
||||
GETBOOL_METHODS = set(['store_true', 'store_false'])
|
||||
|
||||
def __init__(self, option_manager, args=None):
|
||||
def __init__(self, option_manager, extra_config_files=None, args=None):
|
||||
# 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
|
||||
# Our instance of our ConfigFileFinder
|
||||
self.config_finder = ConfigFileFinder(self.program_name, self.args)
|
||||
# Mapping of configuration option names to
|
||||
# flake8.options.manager.Option instances
|
||||
self.config_options = option_manager.config_options_dict
|
||||
self.extra_config_files = extra_config_files or []
|
||||
|
||||
@staticmethod
|
||||
def _normalize_value(option, value):
|
||||
if option.normalize_paths:
|
||||
final_value = utils.normalize_paths(value)
|
||||
elif option.comma_separated_list:
|
||||
final_value = utils.parse_comma_separated_list(value)
|
||||
else:
|
||||
final_value = value
|
||||
LOG.debug('%r has been normalized to %r for option "%s"',
|
||||
value, final_value, option.config_name)
|
||||
return final_value
|
||||
|
||||
def _parse_config(self, config_parser):
|
||||
config = self.config_finder.local_configs()
|
||||
if not config.has_section(self.program_name):
|
||||
LOG.debug('Local configuration files have no %s section',
|
||||
self.program_name)
|
||||
return {}
|
||||
|
||||
config_dict = {}
|
||||
for option_name in config_parser.options(self.program_name):
|
||||
if option_name not in self.config_options:
|
||||
|
|
@ -113,10 +135,7 @@ class MergedConfigParser(object):
|
|||
value = method(self.program_name, option_name)
|
||||
LOG.debug('Option "%s" returned value: %r', option_name, value)
|
||||
|
||||
final_value = value
|
||||
if option.comma_separated_list:
|
||||
final_value = utils.parse_comma_separated_list(value)
|
||||
|
||||
final_value = self._normalize_value(value)
|
||||
config_dict[option_name] = final_value
|
||||
|
||||
def is_configured_by(self, config):
|
||||
|
|
@ -129,7 +148,7 @@ class MergedConfigParser(object):
|
|||
if not self.is_configured_by(config):
|
||||
LOG.debug('Local configuration files have no %s section',
|
||||
self.program_name)
|
||||
return
|
||||
return {}
|
||||
|
||||
LOG.debug('Parsing local configuration files.')
|
||||
return self._parse_config(config)
|
||||
|
|
@ -140,18 +159,51 @@ class MergedConfigParser(object):
|
|||
if not self.is_configured_by(config):
|
||||
LOG.debug('User configuration files have no %s section',
|
||||
self.program_name)
|
||||
return
|
||||
return {}
|
||||
|
||||
LOG.debug('Parsing user configuration files.')
|
||||
return self._parse_config(config)
|
||||
|
||||
def parse(self):
|
||||
def parse_cli_config(self, config_path):
|
||||
"""Parse and return the file specified by --config."""
|
||||
config = self.config_finder.cli_config(config_path)
|
||||
if not self.is_configured_by(config):
|
||||
LOG.debug('CLI configuration files have no %s section',
|
||||
self.program_name)
|
||||
return {}
|
||||
|
||||
LOG.debug('Parsing CLI configuration files.')
|
||||
return self._parse_config(config)
|
||||
|
||||
def parse(self, cli_config=None, isolated=False):
|
||||
"""Parse and return the local and user config files.
|
||||
|
||||
First this copies over the parsed local configuration and then
|
||||
iterates over the options in the user configuration and sets them if
|
||||
they were not set by the local configuration file.
|
||||
|
||||
: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:
|
||||
Dictionary of parsed configuration options
|
||||
:rtype:
|
||||
dict
|
||||
"""
|
||||
if isolated:
|
||||
LOG.debug('Refusing to parse configuration files due to user-'
|
||||
'requested isolation')
|
||||
return {}
|
||||
|
||||
if cli_config:
|
||||
LOG.debug('Ignoring user and locally found configuration files. '
|
||||
'Reading only configuration from "%s" specified via '
|
||||
'--config by the user', cli_config)
|
||||
return self.parse_cli_config(cli_config)
|
||||
|
||||
user_config = self.parse_user_config()
|
||||
config = self.parse_local_config()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class Option(object):
|
||||
"""Our wrapper around an optparse.Option object to add features."""
|
||||
def __init__(self, short_option_name=None, long_option_name=None,
|
||||
# Options below here are taken from the optparse.Option class
|
||||
action=None, default=None, type=None, dest=None,
|
||||
|
|
@ -13,6 +14,7 @@ class Option(object):
|
|||
metavar=None,
|
||||
# Options below here are specific to Flake8
|
||||
parse_from_config=False, comma_separated_list=False,
|
||||
normalize_paths=False,
|
||||
):
|
||||
"""Initialize an Option instance wrapping optparse.Option.
|
||||
|
||||
|
|
@ -57,6 +59,9 @@ class Option(object):
|
|||
:param bool comma_separated_list:
|
||||
Whether the option is a comma separated list when parsing from a
|
||||
config file.
|
||||
:param bool normalize_paths:
|
||||
Whether the option is expecting a path or list of paths and should
|
||||
attempt to normalize the paths to absolute paths.
|
||||
"""
|
||||
self.short_option_name = short_option_name
|
||||
self.long_option_name = long_option_name
|
||||
|
|
@ -72,11 +77,16 @@ class Option(object):
|
|||
'help': help,
|
||||
'metavar': metavar,
|
||||
}
|
||||
# Set attributes for our option arguments
|
||||
for key, value in self.option_kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
# Set our custom attributes
|
||||
self.parse_from_config = parse_from_config
|
||||
self.comma_separated_list = comma_separated_list
|
||||
self.normalize_paths = normalize_paths
|
||||
|
||||
self.config_name = None
|
||||
if parse_from_config:
|
||||
if not long_option_name:
|
||||
raise ValueError('When specifying parse_from_config=True, '
|
||||
|
|
@ -108,6 +118,16 @@ class OptionManager(object):
|
|||
self.version = version
|
||||
|
||||
def add_option(self, *args, **kwargs):
|
||||
"""Create and register a new option.
|
||||
|
||||
See parameters for :class:`~flake8.options.manager.Option` for
|
||||
acceptable arguments to this method.
|
||||
|
||||
.. note::
|
||||
|
||||
``short_option_name`` and ``long_option_name`` may be specified
|
||||
positionally as they are with optparse normally.
|
||||
"""
|
||||
option = Option(*args, **kwargs)
|
||||
self.parser.add_option(option.to_optparse())
|
||||
self.options.append(option)
|
||||
|
|
@ -116,4 +136,11 @@ class OptionManager(object):
|
|||
LOG.debug('Registered option "%s".', option)
|
||||
|
||||
def parse_args(self, args=None, values=None):
|
||||
"""Simple proxy to calling the OptionParser's parse_args method.
|
||||
|
||||
.. todo::
|
||||
|
||||
Normalize values based on our extra attributes from
|
||||
:class:`~flake8.options.manager.OptionManager`.
|
||||
"""
|
||||
return self.parser.parse_args(args, values)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue