summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2014-07-05 10:00:49 -0400
committerNed Batchelder <ned@nedbatchelder.com>2014-07-05 10:00:49 -0400
commit29d841f46a3c3ef4642e6b4aff9f98bc99fac1ee (patch)
treefbff345985a48b90a85384b442d75f943f86c5f2
parent327f3686b64294eadec21bf482e3ab59d6a465b9 (diff)
downloadpython-coveragepy-git-29d841f46a3c3ef4642e6b4aff9f98bc99fac1ee.tar.gz
Read options from setup.cfg if .coveragerc not around. #304
-rw-r--r--CHANGES.txt5
-rw-r--r--coverage/config.py36
-rw-r--r--coverage/control.py9
-rw-r--r--tests/test_config.py144
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