diff options
Diffstat (limited to 'coverage/config.py')
-rw-r--r-- | coverage/config.py | 98 |
1 files changed, 87 insertions, 11 deletions
diff --git a/coverage/config.py b/coverage/config.py index c7d6555c..3fa64495 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -21,12 +21,12 @@ class HandyConfigParser(configparser.RawConfigParser): configparser.RawConfigParser.__init__(self) self.section_prefix = section_prefix - def read(self, filename): # pylint: disable=arguments-differ + def read(self, filenames): """Read a file name as UTF-8 configuration data.""" kwargs = {} if sys.version_info >= (3, 2): kwargs['encoding'] = "utf-8" - return configparser.RawConfigParser.read(self, filename, **kwargs) + return configparser.RawConfigParser.read(self, filenames, **kwargs) def has_option(self, section, option): section = self.section_prefix + section @@ -47,7 +47,7 @@ class HandyConfigParser(configparser.RawConfigParser): d[opt] = self.get(section, opt) return d - def get(self, section, *args, **kwargs): + def get(self, section, *args, **kwargs): # pylint: disable=arguments-differ """Get a value, replacing environment variables also. The arguments are the same as `RawConfigParser.get`, but in the found @@ -122,12 +122,12 @@ class HandyConfigParser(configparser.RawConfigParser): # The default line exclusion regexes. DEFAULT_EXCLUDE = [ - r'(?i)#\s*pragma[:\s]?\s*no\s*cover', + r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)', ] # The default partial branch regexes, to be modified by the user. DEFAULT_PARTIAL = [ - r'(?i)#\s*pragma[:\s]?\s*no\s*branch', + r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)', ] # The default partial branch regexes, based on Python semantics. @@ -158,6 +158,7 @@ class CoverageConfig(object): self.cover_pylib = False self.data_file = ".coverage" self.debug = [] + self.disable_warnings = [] self.note = None self.parallel = False self.plugins = [] @@ -191,7 +192,7 @@ class CoverageConfig(object): # Options for plugins self.plugin_options = {} - MUST_BE_LIST = ["omit", "include", "debug", "plugins", "concurrency"] + MUST_BE_LIST = ["concurrency", "debug", "disable_warnings", "include", "omit", "plugins"] def from_args(self, **kwargs): """Read config values from `kwargs`.""" @@ -207,7 +208,8 @@ class CoverageConfig(object): `filename` is a file name to read. - Returns True or False, whether the file could be read. + Returns True or False, whether the file could be read, and it had some + coverage.py settings in it. """ self.attempted_config_files.append(filename) @@ -222,9 +224,12 @@ class CoverageConfig(object): self.config_files.extend(files_read) + any_set = False try: for option_spec in self.CONFIG_FILE_OPTIONS: - self._set_attr_from_config_option(cp, *option_spec) + was_set = self._set_attr_from_config_option(cp, *option_spec) + if was_set: + any_set = True except ValueError as err: raise CoverageException("Couldn't read config file %s: %s" % (filename, err)) @@ -249,13 +254,20 @@ class CoverageConfig(object): if cp.has_section('paths'): for option in cp.options('paths'): self.paths[option] = cp.getlist('paths', option) + any_set = True # plugins can have options for plugin in self.plugins: if cp.has_section(plugin): self.plugin_options[plugin] = cp.get_section(plugin) + any_set = True - return True + # Was this file used as a config file? If no prefix, then it was used. + # If a prefix, then it was only used if we found some settings in it. + if section_prefix: + return any_set + else: + return True CONFIG_FILE_OPTIONS = [ # These are *args for _set_attr_from_config_option: @@ -272,6 +284,7 @@ class CoverageConfig(object): ('cover_pylib', 'run:cover_pylib', 'boolean'), ('data_file', 'run:data_file'), ('debug', 'run:debug', 'list'), + ('disable_warnings', 'run:disable_warnings', 'list'), ('include', 'run:include', 'list'), ('note', 'run:note'), ('omit', 'run:omit', 'list'), @@ -304,11 +317,17 @@ class CoverageConfig(object): ] def _set_attr_from_config_option(self, cp, attr, where, type_=''): - """Set an attribute on self if it exists in the ConfigParser.""" + """Set an attribute on self if it exists in the ConfigParser. + + Returns True if the attribute was set. + + """ section, option = where.split(":") if cp.has_option(section, option): method = getattr(cp, 'get' + type_) setattr(self, attr, method(section, option)) + return True + return False def get_plugin_options(self, plugin): """Get a dictionary of options for the plugin named `plugin`.""" @@ -351,7 +370,6 @@ class CoverageConfig(object): Returns the value of the option. """ - # Check all the hard-coded options. for option_spec in self.CONFIG_FILE_OPTIONS: attr, where = option_spec[:2] @@ -365,3 +383,61 @@ class CoverageConfig(object): # If we get here, we didn't find the option. raise CoverageException("No such option: %r" % option_name) + + +def read_coverage_config(config_file, **kwargs): + """Read the coverage.py configuration. + + Arguments: + config_file: a boolean or string, see the `Coverage` class for the + tricky details. + all others: keyword arguments from the `Coverage` class, used for + setting values in the configuration. + + Returns: + config_file, config: + config_file is the value to use for config_file in other + invocations of coverage. + + config is a CoverageConfig object read from the appropriate + configuration file. + + """ + # Build the configuration from a number of sources: + # 1) defaults: + config = CoverageConfig() + + # 2) from a file: + if config_file: + # Some API users were specifying ".coveragerc" to mean the same as + # True, so make it so. + if config_file == ".coveragerc": + config_file = True + specified_file = (config_file is not True) + if not specified_file: + config_file = ".coveragerc" + + for fname, prefix in [(config_file, ""), + ("setup.cfg", "coverage:"), + ("tox.ini", "coverage:")]: + config_read = config.from_file(fname, section_prefix=prefix) + is_config_file = fname == config_file + + if not config_read and is_config_file and specified_file: + raise CoverageException("Couldn't read '%s' as a config file" % fname) + + if config_read: + break + + # 3) from environment variables: + env_data_file = os.environ.get('COVERAGE_FILE') + if env_data_file: + config.data_file = env_data_file + debugs = os.environ.get('COVERAGE_DEBUG') + if debugs: + config.debug.extend(d.strip() for d in debugs.split(",")) + + # 4) from constructor arguments: + config.from_args(**kwargs) + + return config_file, config |