summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2013-09-26 22:24:01 -0400
committerNed Batchelder <ned@nedbatchelder.com>2013-09-26 22:24:01 -0400
commit9a32586334141664fb393e56d3e1d76545993d79 (patch)
treeb29c5d3190190d7d1dfa9a90f4da729710623868
parente9b9419bc9a7d184070cc39bdf71924599a5e57f (diff)
downloadpython-coveragepy-git-9a32586334141664fb393e56d3e1d76545993d79.tar.gz
Allow relative paths in the [paths] aliases. #267.
-rw-r--r--CHANGES.txt8
-rw-r--r--coverage/files.py17
-rw-r--r--doc/config.rst4
-rw-r--r--tests/test_files.py26
4 files changed, 51 insertions, 4 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 34c9473e..bff92145 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -17,6 +17,13 @@ Change history for Coverage.py
cause them to be incorrectly marked as unexecuted, as described in
`issue 218`_. This is now fixed.
+- When specifying paths to alias together during data combining, you can now
+ specify relative paths, fixing `issue 267`_.
+
+- Most file paths can now be specified with username expansion (``~/src``, or
+ ``~build/src``, for example), and with environment variable expansion
+ (``build/$BUILDNUM/src``).
+
- When running a threaded program under the Python tracer, coverage no longer
issues a spurious warning about the trace function changing: "Trace function
changed, measurement is likely wrong: None." This fixes `issue 164`_.
@@ -34,6 +41,7 @@ Change history for Coverage.py
.. _issue 242: https://bitbucket.org/ned/coveragepy/issue/242/running-a-two-level-package-doesnt-work
.. _issue 218: https://bitbucket.org/ned/coveragepy/issue/218/run-command-does-not-respect-the-omit-flag
.. _issue 255: https://bitbucket.org/ned/coveragepy/issue/255/directory-level-__main__py-not-included-in
+.. _issue 267: https://bitbucket.org/ned/coveragepy/issue/267/relative-path-aliases-dont-work
Version 3.6 --- 5 January 2013
diff --git a/coverage/files.py b/coverage/files.py
index 976db4ed..464535a8 100644
--- a/coverage/files.py
+++ b/coverage/files.py
@@ -3,6 +3,7 @@
from coverage.backward import to_string
from coverage.misc import CoverageException
import fnmatch, os, os.path, re, sys
+import ntpath, posixpath
class FileLocator(object):
"""Understand how filenames work."""
@@ -110,13 +111,20 @@ else:
"""The actual path for non-Windows platforms."""
return filename
+
def abs_file(filename):
"""Return the absolute normalized form of `filename`."""
- path = os.path.abspath(os.path.realpath(filename))
+ path = os.path.expandvars(os.path.expanduser(filename))
+ path = os.path.abspath(os.path.realpath(path))
path = actual_path(path)
return path
+def isabs_anywhere(filename):
+ """Is `filename` an absolute path on any OS?"""
+ return ntpath.isabs(filename) or posixpath.isabs(filename)
+
+
def prep_patterns(patterns):
"""Prepare the file patterns for use in a `FnmatchMatcher`.
@@ -230,6 +238,11 @@ class PathAliases(object):
if pattern.endswith("*"):
raise CoverageException("Pattern must not end with wildcards.")
pattern_sep = sep(pattern)
+
+ # The pattern is meant to match a filepath. Let's make it absolute
+ # unless it already is, or is meant to match any prefix.
+ if not pattern.startswith('*') and not isabs_anywhere(pattern):
+ pattern = abs_file(pattern)
pattern += pattern_sep
# Make a regex from the pattern. fnmatch always adds a \Z or $ to
@@ -237,7 +250,7 @@ class PathAliases(object):
regex_pat = fnmatch.translate(pattern).replace(r'\Z(', '(')
if regex_pat.endswith("$"):
regex_pat = regex_pat[:-1]
- # We want */a/b.py to match on Windows to, so change slash to match
+ # We want */a/b.py to match on Windows too, so change slash to match
# either separator.
regex_pat = regex_pat.replace(r"\/", r"[\\/]")
# We want case-insensitive matching, so add that flag.
diff --git a/doc/config.rst b/doc/config.rst
index b74e9f40..c32d401e 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -9,6 +9,7 @@ Configuration files
:history: 20100824T092900, added ``precision``.
:history: 20110604T184400, updated for 3.5.
:history: 20110827T212700, updated for 3.5.1
+:history: 20130926T222300, updated for 3.6.1
Coverage.py options can be specified in a configuration file. This makes it
@@ -130,7 +131,8 @@ list.
The first value must be an actual file path on the machine where the reporting
will happen, so that source code can be found. The other values can be file
-patterns to match against the paths of collected data.
+patterns to match against the paths of collected data, or they can be absolute
+or relative file paths on the current machine.
See :ref:`cmd_combining` for more information.
diff --git a/tests/test_files.py b/tests/test_files.py
index d247a395..26ae9100 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -1,6 +1,6 @@
"""Tests for files.py"""
-import os
+import os, os.path
from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher
from coverage.files import PathAliases, find_python_files, abs_file
@@ -91,6 +91,8 @@ class MatcherTest(CoverageTest):
class PathAliasesTest(CoverageTest):
"""Tests for coverage/files.py:PathAliases"""
+ run_in_temp_dir = False
+
def test_noop(self):
aliases = PathAliases()
self.assertEqual(aliases.map('/ned/home/a.py'), '/ned/home/a.py')
@@ -155,6 +157,28 @@ class PathAliasesTest(CoverageTest):
mapped = aliases.map(r'/home/ned/foo/src/sub/a.py')
self.assertEqual(mapped, r'.\mysrc\sub\a.py')
+ def test_leading_wildcard(self):
+ aliases = PathAliases()
+ aliases.add('*/d1', './mysrc1')
+ aliases.add('*/d2', './mysrc2')
+ self.assertEqual(aliases.map('/foo/bar/d1/x.py'), './mysrc1/x.py')
+ self.assertEqual(aliases.map('/foo/bar/d2/y.py'), './mysrc2/y.py')
+
+
+class RelativePathAliasesTest(CoverageTest):
+ """Tests for coverage/files.py:PathAliases, with relative files."""
+
+ def test_dot(self):
+ for d in ('.', '..', '../other', '~'):
+ aliases = PathAliases()
+ aliases.add(d, '/the/source')
+ the_file = os.path.join(d, 'a.py')
+ the_file = os.path.expanduser(the_file)
+ the_file = os.path.abspath(the_file)
+
+ assert '~' not in the_file # to be sure the test is pure.
+ self.assertEqual(aliases.map(the_file), '/the/source/a.py')
+
class FindPythonFilesTest(CoverageTest):
"""Tests of `find_python_files`."""