summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt4
-rw-r--r--coverage/config.py27
-rw-r--r--doc/config.rst5
-rw-r--r--test/test_config.py24
4 files changed, 57 insertions, 3 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 679f1ed8..ca819f2e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -24,6 +24,9 @@ Version 3.6b1
- The HTML report's title can now be set in the configuration file, with the
``--title`` switch on the command line, or via the API.
+- Configuration files now support substitution of environment variables, using
+ syntax like ``${WORD}``. Closes `issue 97`_.
+
- Embarrassingly, the `[xml] output=' setting in the .coveragerc file simply
didn't work. Now it does.
@@ -89,6 +92,7 @@ Version 3.6b1
.. _issue 67: https://bitbucket.org/ned/coveragepy/issue/67/xml-report-filenames-may-be-generated
.. _issue 82: https://bitbucket.org/ned/coveragepy/issue/82/tokenerror-when-generating-html-report
.. _issue 89: https://bitbucket.org/ned/coveragepy/issue/89/on-windows-all-packages-are-reported-in
+.. _issue 97: https://bitbucket.org/ned/coveragepy/issue/97/allow-environment-variables-to-be
.. _issue 111: https://bitbucket.org/ned/coveragepy/issue/111/when-installing-coverage-with-pip-not
.. _issue 137: https://bitbucket.org/ned/coveragepy/issue/137/provide-docs-with-source-distribution
.. _issue 139: https://bitbucket.org/ned/coveragepy/issue/139/easy-check-for-a-certain-coverage-in-tests
diff --git a/coverage/config.py b/coverage/config.py
index 8f1f6710..8edd6c65 100644
--- a/coverage/config.py
+++ b/coverage/config.py
@@ -1,6 +1,6 @@
"""Config file for coverage.py"""
-import os, sys
+import os, re, sys
from coverage.backward import string_class, iitems
# In py3, # ConfigParser was renamed to the more-standard configparser
@@ -10,7 +10,7 @@ except ImportError:
import ConfigParser as configparser
-class HandyConfigParser(configparser.ConfigParser):
+class HandyConfigParser(configparser.RawConfigParser):
"""Our specialization of ConfigParser."""
def read(self, filename):
@@ -18,7 +18,28 @@ class HandyConfigParser(configparser.ConfigParser):
kwargs = {}
if sys.version_info >= (3, 2):
kwargs['encoding'] = "utf-8"
- configparser.ConfigParser.read(self, filename, **kwargs)
+ configparser.RawConfigParser.read(self, filename, **kwargs)
+
+ def get(self, *args, **kwargs):
+ v = configparser.RawConfigParser.get(self, *args, **kwargs)
+ def dollar_replace(m):
+ """Called for each $replacement."""
+ # Only one of the groups will have matched, just get its text.
+ word = [w for w in m.groups() if w is not None][0]
+ if word == "$":
+ return "$"
+ else:
+ return os.environ.get(word, '')
+
+ dollar_pattern = r"""(?x) # Use extended regex syntax
+ \$(?: # A dollar sign, then
+ (?P<v1>\w+) | # a plain word,
+ {(?P<v2>\w+)} | # or a {-wrapped word,
+ (?P<char>[$]) # or a dollar sign.
+ )
+ """
+ v = re.sub(dollar_pattern, dollar_replace, v)
+ return v
def getlist(self, section, option):
"""Read a list of strings.
diff --git a/doc/config.rst b/doc/config.rst
index 159a42f5..f6d8f1fc 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -39,6 +39,11 @@ values on multiple lines.
Boolean values can be specified as ``on``, ``off``, ``true``, ``false``, ``1``,
or ``0`` and are case-insensitive.
+Environment variables can be substituted in by using dollar signs: ``$WORD``
+``${WORD}`` will be replaced with the value of ``WORD`` in the environment.
+A dollar sign can be inserted with ``$$``. Missing environment variables
+will result in empty strings with no error.
+
Many sections and values correspond roughly to commands and options in
the :ref:`command-line interface <cmd>`.
diff --git a/test/test_config.py b/test/test_config.py
index 19e37ab9..1fdc2ce2 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -98,6 +98,30 @@ class ConfigTest(CoverageTest):
""")
self.assertRaises(CoverageException, coverage.coverage)
+ def test_environment_vars_in_config(self):
+ # Config files can have $envvars in them.
+ self.make_file(".coveragerc", """\
+ [run]
+ data_file = $DATA_FILE.fooey
+ branch = $OKAY
+ [report]
+ exclude_lines =
+ the_$$one
+ another${THING}
+ x${THING}y
+ x${NOTHING}y
+ huh$${X}what
+ """)
+ self.set_environ("DATA_FILE", "hello-world")
+ self.set_environ("THING", "ZZZ")
+ self.set_environ("OKAY", "yes")
+ cov = coverage.coverage()
+ self.assertEqual(cov.config.data_file, "hello-world.fooey")
+ self.assertEqual(cov.config.branch, True)
+ self.assertEqual(cov.config.exclude_list,
+ ["the_$one", "anotherZZZ", "xZZZy", "xy", "huh${X}what"]
+ )
+
class ConfigFileTest(CoverageTest):
"""Tests of the config file settings in particular."""