eric6/DebugClients/Python/coverage/config.py

changeset 7427
362cd1b6f81a
parent 6942
2602857055c5
child 7702
f8b97639deb5
diff -r dc171b1d8261 -r 362cd1b6f81a eric6/DebugClients/Python/coverage/config.py
--- a/eric6/DebugClients/Python/coverage/config.py	Wed Feb 19 19:38:36 2020 +0100
+++ b/eric6/DebugClients/Python/coverage/config.py	Sat Feb 22 14:27:42 2020 +0100
@@ -1,15 +1,20 @@
 # 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 copy
 import os
+import os.path
 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
+
+from coverage.tomlconfig import TomlConfigParser, TomlDecodeError
 
 os = isolate_module(os)
 
@@ -30,11 +35,11 @@
         if our_file:
             self.section_prefixes.append("")
 
-    def read(self, filenames):
+    def read(self, filenames, encoding=None):
         """Read a file name as UTF-8 configuration data."""
         kwargs = {}
-        if sys.version_info >= (3, 2):
-            kwargs['encoding'] = "utf-8"
+        if env.PYVERSION >= (3, 2):
+            kwargs['encoding'] = encoding or "utf-8"
         return configparser.RawConfigParser.read(self, filenames, **kwargs)
 
     def has_option(self, section, option):
@@ -85,23 +90,7 @@
             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, os.environ)
         return v
 
     def getlist(self, section, option):
@@ -172,11 +161,18 @@
     operation of coverage.py.
 
     """
+    # pylint: disable=too-many-instance-attributes
+
     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
@@ -184,18 +180,23 @@
 
         # Defaults for [run]
         self.branch = False
+        self.command_line = None
         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 = []
-        self.source = None
+        self.relative_files = False
         self.run_include = None
         self.run_omit = None
+        self.source = None
         self.timid = False
+        self._crash = None
 
         # Defaults for [report]
         self.exclude_list = DEFAULT_EXCLUDE[:]
@@ -206,20 +207,28 @@
         self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
         self.partial_list = DEFAULT_PARTIAL[:]
         self.precision = 0
+        self.report_contexts = None
         self.show_missing = False
         self.skip_covered = False
+        self.skip_empty = False
 
         # Defaults for [html]
         self.extra_css = None
         self.html_dir = "htmlcov"
         self.html_title = "Coverage report"
+        self.show_contexts = False
 
         # Defaults for [xml]
         self.xml_output = "coverage.xml"
         self.xml_package_depth = 99
 
+        # Defaults for [json]
+        self.json_output = "coverage.json"
+        self.json_pretty_print = False
+        self.json_show_contexts = False
+
         # Defaults for [paths]
-        self.paths = {}
+        self.paths = collections.OrderedDict()
 
         # Options for plugins
         self.plugin_options = {}
@@ -252,17 +261,22 @@
         coverage.py settings in it.
 
         """
+        _, ext = os.path.splitext(filename)
+        if ext == '.toml':
+            cp = TomlConfigParser(our_file)
+        else:
+            cp = HandyConfigParser(our_file)
+
         self.attempted_config_files.append(filename)
 
-        cp = HandyConfigParser(our_file)
         try:
             files_read = cp.read(filename)
-        except configparser.Error as err:
+        except (configparser.Error, TomlDecodeError) as err:
             raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
         if not files_read:
             return False
 
-        self.config_files.extend(files_read)
+        self.config_files_read.extend(map(os.path.abspath, files_read))
 
         any_set = False
         try:
@@ -305,9 +319,20 @@
         # 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 = os.path.abspath(filename)
+            with open(filename) as f:
+                self._config_contents = f.read()
+
+        return used
+
+    def copy(self):
+        """Return a copy of the configuration."""
+        return copy.deepcopy(self)
 
     CONFIG_FILE_OPTIONS = [
         # These are *args for _set_attr_from_config_option:
@@ -320,18 +345,23 @@
 
         # [run]
         ('branch', 'run:branch', 'boolean'),
+        ('command_line', 'run:command_line'),
         ('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'),
+        ('relative_files', 'run:relative_files', 'boolean'),
         ('run_include', 'run:include', 'list'),
         ('run_omit', 'run:omit', 'list'),
         ('source', 'run:source', 'list'),
         ('timid', 'run:timid', 'boolean'),
+        ('_crash', 'run:_crash'),
 
         # [report]
         ('exclude_list', 'report:exclude_lines', 'regexlist'),
@@ -340,20 +370,28 @@
         ('partial_always_list', 'report:partial_branches_always', 'regexlist'),
         ('partial_list', 'report:partial_branches', 'regexlist'),
         ('precision', 'report:precision', 'int'),
+        ('report_contexts', 'report:contexts', 'list'),
         ('report_include', 'report:include', 'list'),
         ('report_omit', 'report:omit', 'list'),
         ('show_missing', 'report:show_missing', 'boolean'),
         ('skip_covered', 'report:skip_covered', 'boolean'),
+        ('skip_empty', 'report:skip_empty', 'boolean'),
         ('sort', 'report:sort'),
 
         # [html]
         ('extra_css', 'html:extra_css'),
         ('html_dir', 'html:directory'),
         ('html_title', 'html:title'),
+        ('show_contexts', 'html:show_contexts', 'boolean'),
 
         # [xml]
         ('xml_output', 'xml:output'),
         ('xml_package_depth', 'xml:package_depth', 'int'),
+
+        # [json]
+        ('json_output', 'json:output'),
+        ('json_pretty_print', 'json:pretty_print', 'boolean'),
+        ('json_show_contexts', 'json:show_contexts', 'boolean'),
     ]
 
     def _set_attr_from_config_option(self, cp, attr, where, type_=''):
@@ -425,6 +463,35 @@
         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),
+        ("pyproject.toml", False, False),
+    ]
+    return files_to_try
+
+
 def read_coverage_config(config_file, **kwargs):
     """Read the coverage.py configuration.
 
@@ -435,10 +502,7 @@
             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,26 +513,16 @@
 
     # 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"
+        files_to_try = config_files_to_try(config_file)
 
-        for fname, our_file in [(config_file, True),
-                                ("setup.cfg", False),
-                                ("tox.ini", False)]:
+        for fname, our_file, specified_file in files_to_try:
             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:
+            if config_read:
+                break
+            if specified_file:
                 raise CoverageException("Couldn't read '%s' as a config file" % fname)
 
-            if config_read:
-                break
-
+    # $set_env.py: COVERAGE_DEBUG - Options for --debug.
     # 3) from environment variables:
     env_data_file = os.environ.get('COVERAGE_FILE')
     if env_data_file:
@@ -485,5 +539,9 @@
     config.data_file = os.path.expanduser(config.data_file)
     config.html_dir = os.path.expanduser(config.html_dir)
     config.xml_output = os.path.expanduser(config.xml_output)
+    config.paths = collections.OrderedDict(
+        (k, [os.path.expanduser(f) for f in v])
+        for k, v in config.paths.items()
+    )
 
-    return config_file, config
+    return config

eric ide

mercurial