summaryrefslogtreecommitdiff
path: root/coverage/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'coverage/config.py')
-rw-r--r--coverage/config.py105
1 files changed, 60 insertions, 45 deletions
diff --git a/coverage/config.py b/coverage/config.py
index 7b8f2bd0..2a281875 100644
--- a/coverage/config.py
+++ b/coverage/config.py
@@ -1,15 +1,16 @@
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
"""Config file for coverage.py"""
import collections
import os
import re
-import sys
+from coverage import env
from coverage.backward import configparser, iitems, string_class
from coverage.misc import contract, CoverageException, isolate_module
+from coverage.misc import substitute_variables
os = isolate_module(os)
@@ -33,7 +34,7 @@ class HandyConfigParser(configparser.RawConfigParser):
def read(self, filenames):
"""Read a file name as UTF-8 configuration data."""
kwargs = {}
- if sys.version_info >= (3, 2):
+ if env.PYVERSION >= (3, 2):
kwargs['encoding'] = "utf-8"
return configparser.RawConfigParser.read(self, filenames, **kwargs)
@@ -85,23 +86,7 @@ class HandyConfigParser(configparser.RawConfigParser):
raise configparser.NoOptionError
v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs)
- def dollar_replace(m):
- """Called for each $replacement."""
- # Only one of the groups will have matched, just get its text.
- word = next(w for w in m.groups() if w is not None) # pragma: part covered
- if word == "$":
- return "$"
- else:
- return os.environ.get(word, '')
-
- dollar_pattern = r"""(?x) # Use extended regex syntax
- \$(?: # A dollar sign, then
- (?P<v1>\w+) | # a plain word,
- {(?P<v2>\w+)} | # or a {-wrapped word,
- (?P<char>[$]) # or a dollar sign.
- )
- """
- v = re.sub(dollar_pattern, dollar_replace, v)
+ v = substitute_variables(v)
return v
def getlist(self, section, option):
@@ -175,8 +160,13 @@ class CoverageConfig(object):
def __init__(self):
"""Initialize the configuration attributes to their defaults."""
# Metadata about the config.
+ # We tried to read these config files.
self.attempted_config_files = []
- self.config_files = []
+ # We did read these config files, but maybe didn't find any content for us.
+ self.config_files_read = []
+ # The file that gave us our configuration.
+ self.config_file = None
+ self._config_contents = None
# Defaults for [run] and [report]
self._include = None
@@ -185,10 +175,12 @@ class CoverageConfig(object):
# Defaults for [run]
self.branch = False
self.concurrency = None
+ self.context = None
self.cover_pylib = False
self.data_file = ".coverage"
self.debug = []
self.disable_warnings = []
+ self.dynamic_context = None
self.note = None
self.parallel = False
self.plugins = []
@@ -262,7 +254,7 @@ class CoverageConfig(object):
if not files_read:
return False
- self.config_files.extend(files_read)
+ self.config_files_read.extend(files_read)
any_set = False
try:
@@ -305,9 +297,16 @@ class CoverageConfig(object):
# then it was used. If we're piggybacking on someone else's file,
# then it was only used if we found some settings in it.
if our_file:
- return True
+ used = True
else:
- return any_set
+ used = any_set
+
+ if used:
+ self.config_file = filename
+ with open(filename) as f:
+ self._config_contents = f.read()
+
+ return used
CONFIG_FILE_OPTIONS = [
# These are *args for _set_attr_from_config_option:
@@ -321,10 +320,12 @@ class CoverageConfig(object):
# [run]
('branch', 'run:branch', 'boolean'),
('concurrency', 'run:concurrency', 'list'),
+ ('context', 'run:context'),
('cover_pylib', 'run:cover_pylib', 'boolean'),
('data_file', 'run:data_file'),
('debug', 'run:debug', 'list'),
('disable_warnings', 'run:disable_warnings', 'list'),
+ ('dynamic_context', 'run:dynamic_context'),
('note', 'run:note'),
('parallel', 'run:parallel', 'boolean'),
('plugins', 'run:plugins', 'list'),
@@ -425,6 +426,34 @@ class CoverageConfig(object):
raise CoverageException("No such option: %r" % option_name)
+def config_files_to_try(config_file):
+ """What config files should we try to read?
+
+ Returns a list of tuples:
+ (filename, is_our_file, was_file_specified)
+ """
+
+ # 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:
+ # No file was specified. Check COVERAGE_RCFILE.
+ config_file = os.environ.get('COVERAGE_RCFILE')
+ if config_file:
+ specified_file = True
+ if not specified_file:
+ # Still no file specified. Default to .coveragerc
+ config_file = ".coveragerc"
+ files_to_try = [
+ (config_file, True, specified_file),
+ ("setup.cfg", False, False),
+ ("tox.ini", False, False),
+ ]
+ return files_to_try
+
+
def read_coverage_config(config_file, **kwargs):
"""Read the coverage.py configuration.
@@ -435,10 +464,7 @@ def read_coverage_config(config_file, **kwargs):
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:
config is a CoverageConfig object read from the appropriate
configuration file.
@@ -449,25 +475,14 @@ def read_coverage_config(config_file, **kwargs):
# 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, our_file in [(config_file, True),
- ("setup.cfg", False),
- ("tox.ini", False)]:
- config_read = config.from_file(fname, our_file=our_file)
- 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)
+ files_to_try = config_files_to_try(config_file)
+ for fname, our_file, specified_file in files_to_try:
+ config_read = config.from_file(fname, our_file=our_file)
if config_read:
break
+ if specified_file:
+ raise CoverageException("Couldn't read '%s' as a config file" % fname)
# 3) from environment variables:
env_data_file = os.environ.get('COVERAGE_FILE')
@@ -486,4 +501,4 @@ def read_coverage_config(config_file, **kwargs):
config.html_dir = os.path.expanduser(config.html_dir)
config.xml_output = os.path.expanduser(config.xml_output)
- return config_file, config
+ return config