170 |
159 |
171 The attributes of this class are the various settings that control the |
160 The attributes of this class are the various settings that control the |
172 operation of coverage.py. |
161 operation of coverage.py. |
173 |
162 |
174 """ |
163 """ |
|
164 # pylint: disable=too-many-instance-attributes |
|
165 |
175 def __init__(self): |
166 def __init__(self): |
176 """Initialize the configuration attributes to their defaults.""" |
167 """Initialize the configuration attributes to their defaults.""" |
177 # Metadata about the config. |
168 # Metadata about the config. |
|
169 # We tried to read these config files. |
178 self.attempted_config_files = [] |
170 self.attempted_config_files = [] |
179 self.config_files = [] |
171 # We did read these config files, but maybe didn't find any content for us. |
|
172 self.config_files_read = [] |
|
173 # The file that gave us our configuration. |
|
174 self.config_file = None |
|
175 self._config_contents = None |
180 |
176 |
181 # Defaults for [run] and [report] |
177 # Defaults for [run] and [report] |
182 self._include = None |
178 self._include = None |
183 self._omit = None |
179 self._omit = None |
184 |
180 |
185 # Defaults for [run] |
181 # Defaults for [run] |
186 self.branch = False |
182 self.branch = False |
|
183 self.command_line = None |
187 self.concurrency = None |
184 self.concurrency = None |
|
185 self.context = None |
188 self.cover_pylib = False |
186 self.cover_pylib = False |
189 self.data_file = ".coverage" |
187 self.data_file = ".coverage" |
190 self.debug = [] |
188 self.debug = [] |
191 self.disable_warnings = [] |
189 self.disable_warnings = [] |
|
190 self.dynamic_context = None |
192 self.note = None |
191 self.note = None |
193 self.parallel = False |
192 self.parallel = False |
194 self.plugins = [] |
193 self.plugins = [] |
195 self.source = None |
194 self.relative_files = False |
196 self.run_include = None |
195 self.run_include = None |
197 self.run_omit = None |
196 self.run_omit = None |
|
197 self.source = None |
198 self.timid = False |
198 self.timid = False |
|
199 self._crash = None |
199 |
200 |
200 # Defaults for [report] |
201 # Defaults for [report] |
201 self.exclude_list = DEFAULT_EXCLUDE[:] |
202 self.exclude_list = DEFAULT_EXCLUDE[:] |
202 self.fail_under = 0.0 |
203 self.fail_under = 0.0 |
203 self.ignore_errors = False |
204 self.ignore_errors = False |
204 self.report_include = None |
205 self.report_include = None |
205 self.report_omit = None |
206 self.report_omit = None |
206 self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:] |
207 self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:] |
207 self.partial_list = DEFAULT_PARTIAL[:] |
208 self.partial_list = DEFAULT_PARTIAL[:] |
208 self.precision = 0 |
209 self.precision = 0 |
|
210 self.report_contexts = None |
209 self.show_missing = False |
211 self.show_missing = False |
210 self.skip_covered = False |
212 self.skip_covered = False |
|
213 self.skip_empty = False |
211 |
214 |
212 # Defaults for [html] |
215 # Defaults for [html] |
213 self.extra_css = None |
216 self.extra_css = None |
214 self.html_dir = "htmlcov" |
217 self.html_dir = "htmlcov" |
215 self.html_title = "Coverage report" |
218 self.html_title = "Coverage report" |
|
219 self.show_contexts = False |
216 |
220 |
217 # Defaults for [xml] |
221 # Defaults for [xml] |
218 self.xml_output = "coverage.xml" |
222 self.xml_output = "coverage.xml" |
219 self.xml_package_depth = 99 |
223 self.xml_package_depth = 99 |
220 |
224 |
|
225 # Defaults for [json] |
|
226 self.json_output = "coverage.json" |
|
227 self.json_pretty_print = False |
|
228 self.json_show_contexts = False |
|
229 |
221 # Defaults for [paths] |
230 # Defaults for [paths] |
222 self.paths = {} |
231 self.paths = collections.OrderedDict() |
223 |
232 |
224 # Options for plugins |
233 # Options for plugins |
225 self.plugin_options = {} |
234 self.plugin_options = {} |
226 |
235 |
227 MUST_BE_LIST = [ |
236 MUST_BE_LIST = [ |
318 # type_ is the optional type to apply, by using .getTYPE to read the |
343 # type_ is the optional type to apply, by using .getTYPE to read the |
319 # configuration value from the file. |
344 # configuration value from the file. |
320 |
345 |
321 # [run] |
346 # [run] |
322 ('branch', 'run:branch', 'boolean'), |
347 ('branch', 'run:branch', 'boolean'), |
|
348 ('command_line', 'run:command_line'), |
323 ('concurrency', 'run:concurrency', 'list'), |
349 ('concurrency', 'run:concurrency', 'list'), |
|
350 ('context', 'run:context'), |
324 ('cover_pylib', 'run:cover_pylib', 'boolean'), |
351 ('cover_pylib', 'run:cover_pylib', 'boolean'), |
325 ('data_file', 'run:data_file'), |
352 ('data_file', 'run:data_file'), |
326 ('debug', 'run:debug', 'list'), |
353 ('debug', 'run:debug', 'list'), |
327 ('disable_warnings', 'run:disable_warnings', 'list'), |
354 ('disable_warnings', 'run:disable_warnings', 'list'), |
|
355 ('dynamic_context', 'run:dynamic_context'), |
328 ('note', 'run:note'), |
356 ('note', 'run:note'), |
329 ('parallel', 'run:parallel', 'boolean'), |
357 ('parallel', 'run:parallel', 'boolean'), |
330 ('plugins', 'run:plugins', 'list'), |
358 ('plugins', 'run:plugins', 'list'), |
|
359 ('relative_files', 'run:relative_files', 'boolean'), |
331 ('run_include', 'run:include', 'list'), |
360 ('run_include', 'run:include', 'list'), |
332 ('run_omit', 'run:omit', 'list'), |
361 ('run_omit', 'run:omit', 'list'), |
333 ('source', 'run:source', 'list'), |
362 ('source', 'run:source', 'list'), |
334 ('timid', 'run:timid', 'boolean'), |
363 ('timid', 'run:timid', 'boolean'), |
|
364 ('_crash', 'run:_crash'), |
335 |
365 |
336 # [report] |
366 # [report] |
337 ('exclude_list', 'report:exclude_lines', 'regexlist'), |
367 ('exclude_list', 'report:exclude_lines', 'regexlist'), |
338 ('fail_under', 'report:fail_under', 'float'), |
368 ('fail_under', 'report:fail_under', 'float'), |
339 ('ignore_errors', 'report:ignore_errors', 'boolean'), |
369 ('ignore_errors', 'report:ignore_errors', 'boolean'), |
340 ('partial_always_list', 'report:partial_branches_always', 'regexlist'), |
370 ('partial_always_list', 'report:partial_branches_always', 'regexlist'), |
341 ('partial_list', 'report:partial_branches', 'regexlist'), |
371 ('partial_list', 'report:partial_branches', 'regexlist'), |
342 ('precision', 'report:precision', 'int'), |
372 ('precision', 'report:precision', 'int'), |
|
373 ('report_contexts', 'report:contexts', 'list'), |
343 ('report_include', 'report:include', 'list'), |
374 ('report_include', 'report:include', 'list'), |
344 ('report_omit', 'report:omit', 'list'), |
375 ('report_omit', 'report:omit', 'list'), |
345 ('show_missing', 'report:show_missing', 'boolean'), |
376 ('show_missing', 'report:show_missing', 'boolean'), |
346 ('skip_covered', 'report:skip_covered', 'boolean'), |
377 ('skip_covered', 'report:skip_covered', 'boolean'), |
|
378 ('skip_empty', 'report:skip_empty', 'boolean'), |
347 ('sort', 'report:sort'), |
379 ('sort', 'report:sort'), |
348 |
380 |
349 # [html] |
381 # [html] |
350 ('extra_css', 'html:extra_css'), |
382 ('extra_css', 'html:extra_css'), |
351 ('html_dir', 'html:directory'), |
383 ('html_dir', 'html:directory'), |
352 ('html_title', 'html:title'), |
384 ('html_title', 'html:title'), |
|
385 ('show_contexts', 'html:show_contexts', 'boolean'), |
353 |
386 |
354 # [xml] |
387 # [xml] |
355 ('xml_output', 'xml:output'), |
388 ('xml_output', 'xml:output'), |
356 ('xml_package_depth', 'xml:package_depth', 'int'), |
389 ('xml_package_depth', 'xml:package_depth', 'int'), |
|
390 |
|
391 # [json] |
|
392 ('json_output', 'json:output'), |
|
393 ('json_pretty_print', 'json:pretty_print', 'boolean'), |
|
394 ('json_show_contexts', 'json:show_contexts', 'boolean'), |
357 ] |
395 ] |
358 |
396 |
359 def _set_attr_from_config_option(self, cp, attr, where, type_=''): |
397 def _set_attr_from_config_option(self, cp, attr, where, type_=''): |
360 """Set an attribute on self if it exists in the ConfigParser. |
398 """Set an attribute on self if it exists in the ConfigParser. |
361 |
399 |
423 |
461 |
424 # If we get here, we didn't find the option. |
462 # If we get here, we didn't find the option. |
425 raise CoverageException("No such option: %r" % option_name) |
463 raise CoverageException("No such option: %r" % option_name) |
426 |
464 |
427 |
465 |
|
466 def config_files_to_try(config_file): |
|
467 """What config files should we try to read? |
|
468 |
|
469 Returns a list of tuples: |
|
470 (filename, is_our_file, was_file_specified) |
|
471 """ |
|
472 |
|
473 # Some API users were specifying ".coveragerc" to mean the same as |
|
474 # True, so make it so. |
|
475 if config_file == ".coveragerc": |
|
476 config_file = True |
|
477 specified_file = (config_file is not True) |
|
478 if not specified_file: |
|
479 # No file was specified. Check COVERAGE_RCFILE. |
|
480 config_file = os.environ.get('COVERAGE_RCFILE') |
|
481 if config_file: |
|
482 specified_file = True |
|
483 if not specified_file: |
|
484 # Still no file specified. Default to .coveragerc |
|
485 config_file = ".coveragerc" |
|
486 files_to_try = [ |
|
487 (config_file, True, specified_file), |
|
488 ("setup.cfg", False, False), |
|
489 ("tox.ini", False, False), |
|
490 ("pyproject.toml", False, False), |
|
491 ] |
|
492 return files_to_try |
|
493 |
|
494 |
428 def read_coverage_config(config_file, **kwargs): |
495 def read_coverage_config(config_file, **kwargs): |
429 """Read the coverage.py configuration. |
496 """Read the coverage.py configuration. |
430 |
497 |
431 Arguments: |
498 Arguments: |
432 config_file: a boolean or string, see the `Coverage` class for the |
499 config_file: a boolean or string, see the `Coverage` class for the |
433 tricky details. |
500 tricky details. |
434 all others: keyword arguments from the `Coverage` class, used for |
501 all others: keyword arguments from the `Coverage` class, used for |
435 setting values in the configuration. |
502 setting values in the configuration. |
436 |
503 |
437 Returns: |
504 Returns: |
438 config_file, config: |
505 config: |
439 config_file is the value to use for config_file in other |
|
440 invocations of coverage. |
|
441 |
|
442 config is a CoverageConfig object read from the appropriate |
506 config is a CoverageConfig object read from the appropriate |
443 configuration file. |
507 configuration file. |
444 |
508 |
445 """ |
509 """ |
446 # Build the configuration from a number of sources: |
510 # Build the configuration from a number of sources: |
447 # 1) defaults: |
511 # 1) defaults: |
448 config = CoverageConfig() |
512 config = CoverageConfig() |
449 |
513 |
450 # 2) from a file: |
514 # 2) from a file: |
451 if config_file: |
515 if config_file: |
452 # Some API users were specifying ".coveragerc" to mean the same as |
516 files_to_try = config_files_to_try(config_file) |
453 # True, so make it so. |
517 |
454 if config_file == ".coveragerc": |
518 for fname, our_file, specified_file in files_to_try: |
455 config_file = True |
|
456 specified_file = (config_file is not True) |
|
457 if not specified_file: |
|
458 config_file = ".coveragerc" |
|
459 |
|
460 for fname, our_file in [(config_file, True), |
|
461 ("setup.cfg", False), |
|
462 ("tox.ini", False)]: |
|
463 config_read = config.from_file(fname, our_file=our_file) |
519 config_read = config.from_file(fname, our_file=our_file) |
464 is_config_file = fname == config_file |
|
465 |
|
466 if not config_read and is_config_file and specified_file: |
|
467 raise CoverageException("Couldn't read '%s' as a config file" % fname) |
|
468 |
|
469 if config_read: |
520 if config_read: |
470 break |
521 break |
471 |
522 if specified_file: |
|
523 raise CoverageException("Couldn't read '%s' as a config file" % fname) |
|
524 |
|
525 # $set_env.py: COVERAGE_DEBUG - Options for --debug. |
472 # 3) from environment variables: |
526 # 3) from environment variables: |
473 env_data_file = os.environ.get('COVERAGE_FILE') |
527 env_data_file = os.environ.get('COVERAGE_FILE') |
474 if env_data_file: |
528 if env_data_file: |
475 config.data_file = env_data_file |
529 config.data_file = env_data_file |
476 debugs = os.environ.get('COVERAGE_DEBUG') |
530 debugs = os.environ.get('COVERAGE_DEBUG') |