diff options
-rw-r--r-- | CHANGES.txt | 8 | ||||
-rw-r--r-- | coverage/files.py | 17 | ||||
-rw-r--r-- | doc/config.rst | 4 | ||||
-rw-r--r-- | tests/test_files.py | 26 |
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`.""" |