DebugClients/Python3/coverage/config.py

changeset 4489
d0d6e4ad31bd
parent 3495
fac17a82b431
child 5051
3586ebd9fac8
equal deleted inserted replaced
4481:456c58fc64b0 4489:d0d6e4ad31bd
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
3
1 """Config file for coverage.py""" 4 """Config file for coverage.py"""
2 5
3 import os, re, sys 6 import collections
4 from .backward import string_class, iitems 7 import os
5 8 import re
6 # In py3, # ConfigParser was renamed to the more-standard configparser 9 import sys
7 try: 10
8 import configparser # pylint: disable=F0401 11 from coverage.backward import configparser, iitems, string_class
9 except ImportError: 12 from coverage.misc import CoverageException
10 import ConfigParser as configparser
11 13
12 14
13 class HandyConfigParser(configparser.RawConfigParser): 15 class HandyConfigParser(configparser.RawConfigParser):
14 """Our specialization of ConfigParser.""" 16 """Our specialization of ConfigParser."""
15 17
18 def __init__(self, section_prefix):
19 configparser.RawConfigParser.__init__(self)
20 self.section_prefix = section_prefix
21
16 def read(self, filename): 22 def read(self, filename):
17 """Read a filename as UTF-8 configuration data.""" 23 """Read a file name as UTF-8 configuration data."""
18 kwargs = {} 24 kwargs = {}
19 if sys.version_info >= (3, 2): 25 if sys.version_info >= (3, 2):
20 kwargs['encoding'] = "utf-8" 26 kwargs['encoding'] = "utf-8"
21 return configparser.RawConfigParser.read(self, filename, **kwargs) 27 return configparser.RawConfigParser.read(self, filename, **kwargs)
22 28
23 def get(self, *args, **kwargs): 29 def has_option(self, section, option):
24 v = configparser.RawConfigParser.get(self, *args, **kwargs) 30 section = self.section_prefix + section
31 return configparser.RawConfigParser.has_option(self, section, option)
32
33 def has_section(self, section):
34 section = self.section_prefix + section
35 return configparser.RawConfigParser.has_section(self, section)
36
37 def options(self, section):
38 section = self.section_prefix + section
39 return configparser.RawConfigParser.options(self, section)
40
41 def get_section(self, section):
42 """Get the contents of a section, as a dictionary."""
43 d = {}
44 for opt in self.options(section):
45 d[opt] = self.get(section, opt)
46 return d
47
48 def get(self, section, *args, **kwargs):
49 """Get a value, replacing environment variables also.
50
51 The arguments are the same as `RawConfigParser.get`, but in the found
52 value, ``$WORD`` or ``${WORD}`` are replaced by the value of the
53 environment variable ``WORD``.
54
55 Returns the finished value.
56
57 """
58 section = self.section_prefix + section
59 v = configparser.RawConfigParser.get(self, section, *args, **kwargs)
25 def dollar_replace(m): 60 def dollar_replace(m):
26 """Called for each $replacement.""" 61 """Called for each $replacement."""
27 # Only one of the groups will have matched, just get its text. 62 # Only one of the groups will have matched, just get its text.
28 word = [w for w in m.groups() if w is not None][0] 63 word = next(w for w in m.groups() if w is not None) # pragma: part covered
29 if word == "$": 64 if word == "$":
30 return "$" 65 return "$"
31 else: 66 else:
32 return os.environ.get(word, '') 67 return os.environ.get(word, '')
33 68
57 value = value.strip() 92 value = value.strip()
58 if value: 93 if value:
59 values.append(value) 94 values.append(value)
60 return values 95 return values
61 96
62 def getlinelist(self, section, option): 97 def getregexlist(self, section, option):
63 """Read a list of full-line strings. 98 """Read a list of full-line regexes.
64 99
65 The value of `section` and `option` is treated as a newline-separated 100 The value of `section` and `option` is treated as a newline-separated
66 list of strings. Each value is stripped of whitespace. 101 list of regexes. Each value is stripped of whitespace.
67 102
68 Returns the list of strings. 103 Returns the list of strings.
69 104
70 """ 105 """
71 value_list = self.get(section, option) 106 line_list = self.get(section, option)
72 return list(filter(None, value_list.split('\n'))) 107 value_list = []
73 108 for value in line_list.splitlines():
74 109 value = value.strip()
75 # The default line exclusion regexes 110 try:
111 re.compile(value)
112 except re.error as e:
113 raise CoverageException(
114 "Invalid [%s].%s value %r: %s" % (section, option, value, e)
115 )
116 if value:
117 value_list.append(value)
118 return value_list
119
120
121 # The default line exclusion regexes.
76 DEFAULT_EXCLUDE = [ 122 DEFAULT_EXCLUDE = [
77 '(?i)# *pragma[: ]*no *cover', 123 r'(?i)#\s*pragma[:\s]?\s*no\s*cover',
78 ] 124 ]
79 125
80 # The default partial branch regexes, to be modified by the user. 126 # The default partial branch regexes, to be modified by the user.
81 DEFAULT_PARTIAL = [ 127 DEFAULT_PARTIAL = [
82 '(?i)# *pragma[: ]*no *branch', 128 r'(?i)#\s*pragma[:\s]?\s*no\s*branch',
83 ] 129 ]
84 130
85 # The default partial branch regexes, based on Python semantics. 131 # The default partial branch regexes, based on Python semantics.
86 # These are any Python branching constructs that can't actually execute all 132 # These are any Python branching constructs that can't actually execute all
87 # their branches. 133 # their branches.
88 DEFAULT_PARTIAL_ALWAYS = [ 134 DEFAULT_PARTIAL_ALWAYS = [
89 'while (True|1|False|0):', 135 'while (True|1|False|0):',
90 'if (True|1|False|0):', 136 'if (True|1|False|0):',
91 ] 137 ]
92 138
93 139
94 class CoverageConfig(object): 140 class CoverageConfig(object):
95 """Coverage.py configuration. 141 """Coverage.py configuration.
96 142
104 self.attempted_config_files = [] 150 self.attempted_config_files = []
105 self.config_files = [] 151 self.config_files = []
106 152
107 # Defaults for [run] 153 # Defaults for [run]
108 self.branch = False 154 self.branch = False
155 self.concurrency = None
109 self.cover_pylib = False 156 self.cover_pylib = False
110 self.data_file = ".coverage" 157 self.data_file = ".coverage"
158 self.debug = []
159 self.note = None
111 self.parallel = False 160 self.parallel = False
161 self.plugins = []
162 self.source = None
112 self.timid = False 163 self.timid = False
113 self.source = None
114 self.debug = []
115 164
116 # Defaults for [report] 165 # Defaults for [report]
117 self.exclude_list = DEFAULT_EXCLUDE[:] 166 self.exclude_list = DEFAULT_EXCLUDE[:]
167 self.fail_under = 0
118 self.ignore_errors = False 168 self.ignore_errors = False
119 self.include = None 169 self.include = None
120 self.omit = None 170 self.omit = None
171 self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
121 self.partial_list = DEFAULT_PARTIAL[:] 172 self.partial_list = DEFAULT_PARTIAL[:]
122 self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
123 self.precision = 0 173 self.precision = 0
124 self.show_missing = False 174 self.show_missing = False
175 self.skip_covered = False
125 176
126 # Defaults for [html] 177 # Defaults for [html]
178 self.extra_css = None
127 self.html_dir = "htmlcov" 179 self.html_dir = "htmlcov"
128 self.extra_css = None
129 self.html_title = "Coverage report" 180 self.html_title = "Coverage report"
130 181
131 # Defaults for [xml] 182 # Defaults for [xml]
132 self.xml_output = "coverage.xml" 183 self.xml_output = "coverage.xml"
184 self.xml_package_depth = 99
133 185
134 # Defaults for [paths] 186 # Defaults for [paths]
135 self.paths = {} 187 self.paths = {}
136 188
137 def from_environment(self, env_var): 189 # Options for plugins
138 """Read configuration from the `env_var` environment variable.""" 190 self.plugin_options = {}
139 # Timidity: for nose users, read an environment variable. This is a 191
140 # cheap hack, since the rest of the command line arguments aren't 192 MUST_BE_LIST = ["omit", "include", "debug", "plugins"]
141 # recognized, but it solves some users' problems.
142 env = os.environ.get(env_var, '')
143 if env:
144 self.timid = ('--timid' in env)
145
146 MUST_BE_LIST = ["omit", "include", "debug"]
147 193
148 def from_args(self, **kwargs): 194 def from_args(self, **kwargs):
149 """Read config values from `kwargs`.""" 195 """Read config values from `kwargs`."""
150 for k, v in iitems(kwargs): 196 for k, v in iitems(kwargs):
151 if v is not None: 197 if v is not None:
152 if k in self.MUST_BE_LIST and isinstance(v, string_class): 198 if k in self.MUST_BE_LIST and isinstance(v, string_class):
153 v = [v] 199 v = [v]
154 setattr(self, k, v) 200 setattr(self, k, v)
155 201
156 def from_file(self, filename): 202 def from_file(self, filename, section_prefix=""):
157 """Read configuration from a .rc file. 203 """Read configuration from a .rc file.
158 204
159 `filename` is a file name to read. 205 `filename` is a file name to read.
160 206
207 Returns True or False, whether the file could be read.
208
161 """ 209 """
162 self.attempted_config_files.append(filename) 210 self.attempted_config_files.append(filename)
163 211
164 cp = HandyConfigParser() 212 cp = HandyConfigParser(section_prefix)
165 files_read = cp.read(filename) 213 try:
166 if files_read is not None: # return value changed in 2.4 214 files_read = cp.read(filename)
167 self.config_files.extend(files_read) 215 except configparser.Error as err:
168 216 raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
217 if not files_read:
218 return False
219
220 self.config_files.extend(files_read)
221
222 try:
223 for option_spec in self.CONFIG_FILE_OPTIONS:
224 self._set_attr_from_config_option(cp, *option_spec)
225 except ValueError as err:
226 raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
227
228 # Check that there are no unrecognized options.
229 all_options = collections.defaultdict(set)
169 for option_spec in self.CONFIG_FILE_OPTIONS: 230 for option_spec in self.CONFIG_FILE_OPTIONS:
170 self.set_attr_from_config_option(cp, *option_spec) 231 section, option = option_spec[1].split(":")
232 all_options[section].add(option)
233
234 for section, options in iitems(all_options):
235 if cp.has_section(section):
236 for unknown in set(cp.options(section)) - options:
237 if section_prefix:
238 section = section_prefix + section
239 raise CoverageException(
240 "Unrecognized option '[%s] %s=' in config file %s" % (
241 section, unknown, filename
242 )
243 )
171 244
172 # [paths] is special 245 # [paths] is special
173 if cp.has_section('paths'): 246 if cp.has_section('paths'):
174 for option in cp.options('paths'): 247 for option in cp.options('paths'):
175 self.paths[option] = cp.getlist('paths', option) 248 self.paths[option] = cp.getlist('paths', option)
176 249
250 # plugins can have options
251 for plugin in self.plugins:
252 if cp.has_section(plugin):
253 self.plugin_options[plugin] = cp.get_section(plugin)
254
255 return True
256
177 CONFIG_FILE_OPTIONS = [ 257 CONFIG_FILE_OPTIONS = [
258 # These are *args for _set_attr_from_config_option:
259 # (attr, where, type_="")
260 #
261 # attr is the attribute to set on the CoverageConfig object.
262 # where is the section:name to read from the configuration file.
263 # type_ is the optional type to apply, by using .getTYPE to read the
264 # configuration value from the file.
265
178 # [run] 266 # [run]
179 ('branch', 'run:branch', 'boolean'), 267 ('branch', 'run:branch', 'boolean'),
268 ('concurrency', 'run:concurrency'),
180 ('cover_pylib', 'run:cover_pylib', 'boolean'), 269 ('cover_pylib', 'run:cover_pylib', 'boolean'),
181 ('data_file', 'run:data_file'), 270 ('data_file', 'run:data_file'),
182 ('debug', 'run:debug', 'list'), 271 ('debug', 'run:debug', 'list'),
183 ('include', 'run:include', 'list'), 272 ('include', 'run:include', 'list'),
273 ('note', 'run:note'),
184 ('omit', 'run:omit', 'list'), 274 ('omit', 'run:omit', 'list'),
185 ('parallel', 'run:parallel', 'boolean'), 275 ('parallel', 'run:parallel', 'boolean'),
276 ('plugins', 'run:plugins', 'list'),
186 ('source', 'run:source', 'list'), 277 ('source', 'run:source', 'list'),
187 ('timid', 'run:timid', 'boolean'), 278 ('timid', 'run:timid', 'boolean'),
188 279
189 # [report] 280 # [report]
190 ('exclude_list', 'report:exclude_lines', 'linelist'), 281 ('exclude_list', 'report:exclude_lines', 'regexlist'),
282 ('fail_under', 'report:fail_under', 'int'),
191 ('ignore_errors', 'report:ignore_errors', 'boolean'), 283 ('ignore_errors', 'report:ignore_errors', 'boolean'),
192 ('include', 'report:include', 'list'), 284 ('include', 'report:include', 'list'),
193 ('omit', 'report:omit', 'list'), 285 ('omit', 'report:omit', 'list'),
194 ('partial_list', 'report:partial_branches', 'linelist'), 286 ('partial_always_list', 'report:partial_branches_always', 'regexlist'),
195 ('partial_always_list', 'report:partial_branches_always', 'linelist'), 287 ('partial_list', 'report:partial_branches', 'regexlist'),
196 ('precision', 'report:precision', 'int'), 288 ('precision', 'report:precision', 'int'),
197 ('show_missing', 'report:show_missing', 'boolean'), 289 ('show_missing', 'report:show_missing', 'boolean'),
290 ('skip_covered', 'report:skip_covered', 'boolean'),
198 291
199 # [html] 292 # [html]
293 ('extra_css', 'html:extra_css'),
200 ('html_dir', 'html:directory'), 294 ('html_dir', 'html:directory'),
201 ('extra_css', 'html:extra_css'),
202 ('html_title', 'html:title'), 295 ('html_title', 'html:title'),
203 296
204 # [xml] 297 # [xml]
205 ('xml_output', 'xml:output'), 298 ('xml_output', 'xml:output'),
206 ] 299 ('xml_package_depth', 'xml:package_depth', 'int'),
207 300 ]
208 def set_attr_from_config_option(self, cp, attr, where, type_=''): 301
302 def _set_attr_from_config_option(self, cp, attr, where, type_=''):
209 """Set an attribute on self if it exists in the ConfigParser.""" 303 """Set an attribute on self if it exists in the ConfigParser."""
210 section, option = where.split(":") 304 section, option = where.split(":")
211 if cp.has_option(section, option): 305 if cp.has_option(section, option):
212 method = getattr(cp, 'get'+type_) 306 method = getattr(cp, 'get' + type_)
213 setattr(self, attr, method(section, option)) 307 setattr(self, attr, method(section, option))
308
309 def get_plugin_options(self, plugin):
310 """Get a dictionary of options for the plugin named `plugin`."""
311 return self.plugin_options.get(plugin, {})
312
313 def set_option(self, option_name, value):
314 """Set an option in the configuration.
315
316 `option_name` is a colon-separated string indicating the section and
317 option name. For example, the ``branch`` option in the ``[run]``
318 section of the config file would be indicated with `"run:branch"`.
319
320 `value` is the new value for the option.
321
322 """
323
324 # Check all the hard-coded options.
325 for option_spec in self.CONFIG_FILE_OPTIONS:
326 attr, where = option_spec[:2]
327 if where == option_name:
328 setattr(self, attr, value)
329 return
330
331 # See if it's a plugin option.
332 plugin_name, _, key = option_name.partition(":")
333 if key and plugin_name in self.plugins:
334 self.plugin_options.setdefault(plugin_name, {})[key] = value
335 return
336
337 # If we get here, we didn't find the option.
338 raise CoverageException("No such option: %r" % option_name)
339
340 def get_option(self, option_name):
341 """Get an option from the configuration.
342
343 `option_name` is a colon-separated string indicating the section and
344 option name. For example, the ``branch`` option in the ``[run]``
345 section of the config file would be indicated with `"run:branch"`.
346
347 Returns the value of the option.
348
349 """
350
351 # Check all the hard-coded options.
352 for option_spec in self.CONFIG_FILE_OPTIONS:
353 attr, where = option_spec[:2]
354 if where == option_name:
355 return getattr(self, attr)
356
357 # See if it's a plugin option.
358 plugin_name, _, key = option_name.partition(":")
359 if key and plugin_name in self.plugins:
360 return self.plugin_options.get(plugin_name, {}).get(key)
361
362 # If we get here, we didn't find the option.
363 raise CoverageException("No such option: %r" % option_name)

eric ide

mercurial