diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2014-07-05 10:00:49 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2014-07-05 10:00:49 -0400 |
commit | 29d841f46a3c3ef4642e6b4aff9f98bc99fac1ee (patch) | |
tree | fbff345985a48b90a85384b442d75f943f86c5f2 | |
parent | 327f3686b64294eadec21bf482e3ab59d6a465b9 (diff) | |
download | python-coveragepy-git-29d841f46a3c3ef4642e6b4aff9f98bc99fac1ee.tar.gz |
Read options from setup.cfg if .coveragerc not around. #304
-rw-r--r-- | CHANGES.txt | 5 | ||||
-rw-r--r-- | coverage/config.py | 36 | ||||
-rw-r--r-- | coverage/control.py | 9 | ||||
-rw-r--r-- | tests/test_config.py | 144 |
4 files changed, 133 insertions, 61 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 7ecdbc66..2e516959 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,10 @@ Change history for Coverage.py - Python versions supported are now CPython 2.6, 2.7, 3.2, 3.3, and 3.4, and PyPy 2.2. +- Options are now also read from a setup.cfg file, if any. Sections are + prefixed with "coverage:", so the ``[run]`` options will be read from the + ``[coverage:run]`` section of setup.cfg. Finishes `issue 304`_. + - The ``report`` command can now show missing branches when reporting on branch coverage. Thanks, Steve Leonard. @@ -37,6 +41,7 @@ Change history for Coverage.py .. _issue 284: https://bitbucket.org/ned/coveragepy/issue/284/fail-under-should-show-more-precision .. _issue 285: https://bitbucket.org/ned/coveragepy/issue/285/xml-report-fails-if-output-file-directory .. _issue 303: https://bitbucket.org/ned/coveragepy/issue/303/unicodedecodeerror +.. _issue 304: https://bitbucket.org/ned/coveragepy/issue/304/attempt-to-get-configuration-from-setupcfg .. _issue 305: https://bitbucket.org/ned/coveragepy/issue/305/pendingdeprecationwarning-the-imp-module diff --git a/coverage/config.py b/coverage/config.py index e5e35856..064bc1ca 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -13,6 +13,11 @@ except ImportError: class HandyConfigParser(configparser.RawConfigParser): """Our specialization of ConfigParser.""" + def __init__(self, section_prefix): + # pylint: disable=super-init-not-called + configparser.RawConfigParser.__init__(self) + self.section_prefix = section_prefix + def read(self, filename): """Read a filename as UTF-8 configuration data.""" kwargs = {} @@ -20,7 +25,19 @@ class HandyConfigParser(configparser.RawConfigParser): kwargs['encoding'] = "utf-8" return configparser.RawConfigParser.read(self, filename, **kwargs) - def 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(self, section, *args, **kwargs): """Get a value, replacing environment variables also. The arguments are the same as `RawConfigParser.get`, but in the found @@ -30,7 +47,8 @@ class HandyConfigParser(configparser.RawConfigParser): Returns the finished value. """ - v = configparser.RawConfigParser.get(self, *args, **kwargs) + 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. @@ -164,18 +182,22 @@ class CoverageConfig(object): 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() + cp = HandyConfigParser(section_prefix) files_read = cp.read(filename) - if files_read is not None: # return value changed in 2.4 - self.config_files.extend(files_read) + if not files_read: + return False + + self.config_files.extend(files_read) for option_spec in self.CONFIG_FILE_OPTIONS: self.set_attr_from_config_option(cp, *option_spec) @@ -185,6 +207,8 @@ class CoverageConfig(object): for option in cp.options('paths'): self.paths[option] = cp.getlist('paths', option) + return True + CONFIG_FILE_OPTIONS = [ # These are *args for set_attr_from_config_option: # (attr, where, type_="") diff --git a/coverage/control.py b/coverage/control.py index 19b68ca0..cb917e52 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -99,17 +99,22 @@ class coverage(object): # 1: defaults: self.config = CoverageConfig() - # 2: from the coveragerc file: + # 2: from the .coveragerc or setup.cfg file: if config_file: + did_read_rc = should_read_setupcfg = False if config_file is True: config_file = ".coveragerc" + should_read_setupcfg = True try: - self.config.from_file(config_file) + did_read_rc = self.config.from_file(config_file) except ValueError as err: raise CoverageException( "Couldn't read config file %s: %s" % (config_file, err) ) + if not did_read_rc and should_read_setupcfg: + self.config.from_file("setup.cfg", section_prefix="coverage:") + # 3: from environment variables: self.config.from_environment('COVERAGE_OPTIONS') env_data_file = os.environ.get('COVERAGE_FILE') diff --git a/tests/test_config.py b/tests/test_config.py index 7fa31208..7409f4aa 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -125,58 +125,71 @@ class ConfigTest(CoverageTest): class ConfigFileTest(CoverageTest): """Tests of the config file settings in particular.""" - def test_config_file_settings(self): - # This sample file tries to use lots of variation of syntax... - self.make_file(".coveragerc", """\ - # This is a settings file for coverage.py - [run] - timid = yes - data_file = something_or_other.dat - branch = 1 - cover_pylib = TRUE - parallel = on - include = a/ , b/ - - [report] - ; these settings affect reporting. - exclude_lines = - if 0: - - pragma:?\\s+no cover - another_tab - - ignore_errors = TRUE - omit = - one, another, some_more, - yet_more - precision = 3 - - partial_branches = - pragma:?\\s+no branch - partial_branches_always = - if 0: - while True: - - show_missing= TruE - - [html] - - directory = c:\\tricky\\dir.somewhere - extra_css=something/extra.css - title = Title & nums # nums! - [xml] - output=mycov.xml - - [paths] - source = - . - /home/ned/src/ - - other = other, /home/ned/other, c:\\Ned\\etc - - """) - cov = coverage.coverage() - + # This sample file tries to use lots of variation of syntax... + # The {section} placeholder lets us nest these settings in another file. + LOTSA_SETTINGS = """\ + # This is a settings file for coverage.py + [{section}run] + timid = yes + data_file = something_or_other.dat + branch = 1 + cover_pylib = TRUE + parallel = on + include = a/ , b/ + + [{section}report] + ; these settings affect reporting. + exclude_lines = + if 0: + + pragma:?\\s+no cover + another_tab + + ignore_errors = TRUE + omit = + one, another, some_more, + yet_more + precision = 3 + + partial_branches = + pragma:?\\s+no branch + partial_branches_always = + if 0: + while True: + + show_missing= TruE + + [{section}html] + + directory = c:\\tricky\\dir.somewhere + extra_css=something/extra.css + title = Title & nums # nums! + [{section}xml] + output=mycov.xml + + [{section}paths] + source = + . + /home/ned/src/ + + other = other, /home/ned/other, c:\\Ned\\etc + + """ + + # Just some sample setup.cfg text from the docs. + SETUP_CFG = """\ + [bdist_rpm] + release = 1 + packager = Jane Packager <janep@pysoft.com> + doc_files = CHANGES.txt + README.txt + USAGE.txt + doc/ + examples/ + """ + + def assert_config_settings_are_correct(self, cov): + """Check that `cov` has all the settings from LOTSA_SETTINGS.""" self.assertTrue(cov.config.timid) self.assertEqual(cov.config.data_file, "something_or_other.dat") self.assertTrue(cov.config.branch) @@ -211,8 +224,33 @@ class ConfigFileTest(CoverageTest): 'other': ['other', '/home/ned/other', 'c:\\Ned\\etc'] }) + def test_config_file_settings(self): + self.make_file(".coveragerc", self.LOTSA_SETTINGS.format(section="")) + cov = coverage.coverage() + self.assert_config_settings_are_correct(cov) + + def test_config_file_settings_in_setupcfg(self): + nested = self.LOTSA_SETTINGS.format(section="coverage:") + self.make_file("setup.cfg", nested + "\n" + self.SETUP_CFG) + cov = coverage.coverage() + self.assert_config_settings_are_correct(cov) + + def test_setupcfg_only_if_not_coveragerc(self): + self.make_file(".coveragerc", """\ + [run] + include = foo + """) + self.make_file("setup.cfg", """\ + [run] + omit = bar + branch = true + """) + cov = coverage.coverage() + self.assertEqual(cov.config.include, ["foo"]) + self.assertEqual(cov.config.omit, None) + self.assertEqual(cov.config.branch, False) + def test_one(self): - # This sample file tries to use lots of variation of syntax... self.make_file(".coveragerc", """\ [html] title = tabblo & «ταБЬℓσ» # numbers |