DebugClients/Python3/coverage/config.py

changeset 4489
d0d6e4ad31bd
parent 3495
fac17a82b431
child 5051
3586ebd9fac8
--- a/DebugClients/Python3/coverage/config.py	Sun Oct 04 13:35:09 2015 +0200
+++ b/DebugClients/Python3/coverage/config.py	Sun Oct 04 22:37:56 2015 +0200
@@ -1,31 +1,66 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
 """Config file for coverage.py"""
 
-import os, re, sys
-from .backward import string_class, iitems
+import collections
+import os
+import re
+import sys
 
-# In py3, # ConfigParser was renamed to the more-standard configparser
-try:
-    import configparser                             # pylint: disable=F0401
-except ImportError:
-    import ConfigParser as configparser
+from coverage.backward import configparser, iitems, string_class
+from coverage.misc import CoverageException
 
 
 class HandyConfigParser(configparser.RawConfigParser):
     """Our specialization of ConfigParser."""
 
+    def __init__(self, section_prefix):
+        configparser.RawConfigParser.__init__(self)
+        self.section_prefix = section_prefix
+
     def read(self, filename):
-        """Read a filename as UTF-8 configuration data."""
+        """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)
 
-    def get(self, *args, **kwargs):
-        v = configparser.RawConfigParser.get(self, *args, **kwargs)
+    def has_option(self, section, option):
+        section = self.section_prefix + section
+        return configparser.RawConfigParser.has_option(self, section, option)
+
+    def has_section(self, section):
+        section = self.section_prefix + section
+        return configparser.RawConfigParser.has_section(self, section)
+
+    def options(self, section):
+        section = self.section_prefix + section
+        return configparser.RawConfigParser.options(self, section)
+
+    def get_section(self, section):
+        """Get the contents of a section, as a dictionary."""
+        d = {}
+        for opt in self.options(section):
+            d[opt] = self.get(section, opt)
+        return d
+
+    def get(self, section, *args, **kwargs):
+        """Get a value, replacing environment variables also.
+
+        The arguments are the same as `RawConfigParser.get`, but in the found
+        value, ``$WORD`` or ``${WORD}`` are replaced by the value of the
+        environment variable ``WORD``.
+
+        Returns the finished value.
+
+        """
+        section = self.section_prefix + section
+        v = configparser.RawConfigParser.get(self, section, *args, **kwargs)
         def dollar_replace(m):
             """Called for each $replacement."""
             # Only one of the groups will have matched, just get its text.
-            word = [w for w in m.groups() if w is not None][0]
+            word = next(w for w in m.groups() if w is not None)     # pragma: part covered
             if word == "$":
                 return "$"
             else:
@@ -59,28 +94,39 @@
                     values.append(value)
         return values
 
-    def getlinelist(self, section, option):
-        """Read a list of full-line strings.
+    def getregexlist(self, section, option):
+        """Read a list of full-line regexes.
 
         The value of `section` and `option` is treated as a newline-separated
-        list of strings.  Each value is stripped of whitespace.
+        list of regexes.  Each value is stripped of whitespace.
 
         Returns the list of strings.
 
         """
-        value_list = self.get(section, option)
-        return list(filter(None, value_list.split('\n')))
+        line_list = self.get(section, option)
+        value_list = []
+        for value in line_list.splitlines():
+            value = value.strip()
+            try:
+                re.compile(value)
+            except re.error as e:
+                raise CoverageException(
+                    "Invalid [%s].%s value %r: %s" % (section, option, value, e)
+                )
+            if value:
+                value_list.append(value)
+        return value_list
 
 
-# The default line exclusion regexes
+# The default line exclusion regexes.
 DEFAULT_EXCLUDE = [
-    '(?i)# *pragma[: ]*no *cover',
-    ]
+    r'(?i)#\s*pragma[:\s]?\s*no\s*cover',
+]
 
 # The default partial branch regexes, to be modified by the user.
 DEFAULT_PARTIAL = [
-    '(?i)# *pragma[: ]*no *branch',
-    ]
+    r'(?i)#\s*pragma[:\s]?\s*no\s*branch',
+]
 
 # The default partial branch regexes, based on Python semantics.
 # These are any Python branching constructs that can't actually execute all
@@ -88,7 +134,7 @@
 DEFAULT_PARTIAL_ALWAYS = [
     'while (True|1|False|0):',
     'if (True|1|False|0):',
-    ]
+]
 
 
 class CoverageConfig(object):
@@ -106,44 +152,44 @@
 
         # Defaults for [run]
         self.branch = False
+        self.concurrency = None
         self.cover_pylib = False
         self.data_file = ".coverage"
+        self.debug = []
+        self.note = None
         self.parallel = False
+        self.plugins = []
+        self.source = None
         self.timid = False
-        self.source = None
-        self.debug = []
 
         # Defaults for [report]
         self.exclude_list = DEFAULT_EXCLUDE[:]
+        self.fail_under = 0
         self.ignore_errors = False
         self.include = None
         self.omit = None
+        self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
         self.partial_list = DEFAULT_PARTIAL[:]
-        self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
         self.precision = 0
         self.show_missing = False
+        self.skip_covered = False
 
         # Defaults for [html]
+        self.extra_css = None
         self.html_dir = "htmlcov"
-        self.extra_css = None
         self.html_title = "Coverage report"
 
         # Defaults for [xml]
         self.xml_output = "coverage.xml"
+        self.xml_package_depth = 99
 
         # Defaults for [paths]
         self.paths = {}
 
-    def from_environment(self, env_var):
-        """Read configuration from the `env_var` environment variable."""
-        # Timidity: for nose users, read an environment variable.  This is a
-        # cheap hack, since the rest of the command line arguments aren't
-        # recognized, but it solves some users' problems.
-        env = os.environ.get(env_var, '')
-        if env:
-            self.timid = ('--timid' in env)
+        # Options for plugins
+        self.plugin_options = {}
 
-    MUST_BE_LIST = ["omit", "include", "debug"]
+    MUST_BE_LIST = ["omit", "include", "debug", "plugins"]
 
     def from_args(self, **kwargs):
         """Read config values from `kwargs`."""
@@ -153,61 +199,165 @@
                     v = [v]
                 setattr(self, k, v)
 
-    def from_file(self, filename):
+    def from_file(self, filename, section_prefix=""):
         """Read configuration from a .rc file.
 
         `filename` is a file name to read.
 
+        Returns True or False, whether the file could be read.
+
         """
         self.attempted_config_files.append(filename)
 
-        cp = HandyConfigParser()
-        files_read = cp.read(filename)
-        if files_read is not None:  # return value changed in 2.4
-            self.config_files.extend(files_read)
+        cp = HandyConfigParser(section_prefix)
+        try:
+            files_read = cp.read(filename)
+        except configparser.Error 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)
+
+        try:
+            for option_spec in self.CONFIG_FILE_OPTIONS:
+                self._set_attr_from_config_option(cp, *option_spec)
+        except ValueError as err:
+            raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
 
+        # Check that there are no unrecognized options.
+        all_options = collections.defaultdict(set)
         for option_spec in self.CONFIG_FILE_OPTIONS:
-            self.set_attr_from_config_option(cp, *option_spec)
+            section, option = option_spec[1].split(":")
+            all_options[section].add(option)
+
+        for section, options in iitems(all_options):
+            if cp.has_section(section):
+                for unknown in set(cp.options(section)) - options:
+                    if section_prefix:
+                        section = section_prefix + section
+                    raise CoverageException(
+                        "Unrecognized option '[%s] %s=' in config file %s" % (
+                            section, unknown, filename
+                        )
+                    )
 
         # [paths] is special
         if cp.has_section('paths'):
             for option in cp.options('paths'):
                 self.paths[option] = cp.getlist('paths', option)
 
+        # plugins can have options
+        for plugin in self.plugins:
+            if cp.has_section(plugin):
+                self.plugin_options[plugin] = cp.get_section(plugin)
+
+        return True
+
     CONFIG_FILE_OPTIONS = [
+        # These are *args for _set_attr_from_config_option:
+        #   (attr, where, type_="")
+        #
+        #   attr is the attribute to set on the CoverageConfig object.
+        #   where is the section:name to read from the configuration file.
+        #   type_ is the optional type to apply, by using .getTYPE to read the
+        #       configuration value from the file.
+
         # [run]
         ('branch', 'run:branch', 'boolean'),
+        ('concurrency', 'run:concurrency'),
         ('cover_pylib', 'run:cover_pylib', 'boolean'),
         ('data_file', 'run:data_file'),
         ('debug', 'run:debug', 'list'),
         ('include', 'run:include', 'list'),
+        ('note', 'run:note'),
         ('omit', 'run:omit', 'list'),
         ('parallel', 'run:parallel', 'boolean'),
+        ('plugins', 'run:plugins', 'list'),
         ('source', 'run:source', 'list'),
         ('timid', 'run:timid', 'boolean'),
 
         # [report]
-        ('exclude_list', 'report:exclude_lines', 'linelist'),
+        ('exclude_list', 'report:exclude_lines', 'regexlist'),
+        ('fail_under', 'report:fail_under', 'int'),
         ('ignore_errors', 'report:ignore_errors', 'boolean'),
         ('include', 'report:include', 'list'),
         ('omit', 'report:omit', 'list'),
-        ('partial_list', 'report:partial_branches', 'linelist'),
-        ('partial_always_list', 'report:partial_branches_always', 'linelist'),
+        ('partial_always_list', 'report:partial_branches_always', 'regexlist'),
+        ('partial_list', 'report:partial_branches', 'regexlist'),
         ('precision', 'report:precision', 'int'),
         ('show_missing', 'report:show_missing', 'boolean'),
+        ('skip_covered', 'report:skip_covered', 'boolean'),
 
         # [html]
+        ('extra_css', 'html:extra_css'),
         ('html_dir', 'html:directory'),
-        ('extra_css', 'html:extra_css'),
         ('html_title', 'html:title'),
 
         # [xml]
         ('xml_output', 'xml:output'),
-        ]
+        ('xml_package_depth', 'xml:package_depth', 'int'),
+    ]
 
-    def set_attr_from_config_option(self, cp, attr, where, type_=''):
+    def _set_attr_from_config_option(self, cp, attr, where, type_=''):
         """Set an attribute on self if it exists in the ConfigParser."""
         section, option = where.split(":")
         if cp.has_option(section, option):
-            method = getattr(cp, 'get'+type_)
+            method = getattr(cp, 'get' + type_)
             setattr(self, attr, method(section, option))
+
+    def get_plugin_options(self, plugin):
+        """Get a dictionary of options for the plugin named `plugin`."""
+        return self.plugin_options.get(plugin, {})
+
+    def set_option(self, option_name, value):
+        """Set an option in the configuration.
+
+        `option_name` is a colon-separated string indicating the section and
+        option name.  For example, the ``branch`` option in the ``[run]``
+        section of the config file would be indicated with `"run:branch"`.
+
+        `value` is the new value for the option.
+
+        """
+
+        # Check all the hard-coded options.
+        for option_spec in self.CONFIG_FILE_OPTIONS:
+            attr, where = option_spec[:2]
+            if where == option_name:
+                setattr(self, attr, value)
+                return
+
+        # See if it's a plugin option.
+        plugin_name, _, key = option_name.partition(":")
+        if key and plugin_name in self.plugins:
+            self.plugin_options.setdefault(plugin_name, {})[key] = value
+            return
+
+        # If we get here, we didn't find the option.
+        raise CoverageException("No such option: %r" % option_name)
+
+    def get_option(self, option_name):
+        """Get an option from the configuration.
+
+        `option_name` is a colon-separated string indicating the section and
+        option name.  For example, the ``branch`` option in the ``[run]``
+        section of the config file would be indicated with `"run:branch"`.
+
+        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]
+            if where == option_name:
+                return getattr(self, attr)
+
+        # See if it's a plugin option.
+        plugin_name, _, key = option_name.partition(":")
+        if key and plugin_name in self.plugins:
+            return self.plugin_options.get(plugin_name, {}).get(key)
+
+        # If we get here, we didn't find the option.
+        raise CoverageException("No such option: %r" % option_name)

eric ide

mercurial