DebugClients/Python3/coverage/config.py

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

eric ide

mercurial