diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2022-11-30 18:44:40 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2022-12-01 10:21:45 -0500 |
commit | 2adeb8bb5c2a12a080d7a528e35e5df9ffe7785f (patch) | |
tree | 2f95b6017927c5a1dfeb62c6f0169f022ca0d401 | |
parent | c2e35658d8311fd2b5d1460c2cb56762d5fe0ec7 (diff) | |
download | python-coveragepy-git-2adeb8bb5c2a12a080d7a528e35e5df9ffe7785f.tar.gz |
fix: when checking source existence for remapping, zipfiles are ok
-rw-r--r-- | coverage/files.py | 31 | ||||
-rw-r--r-- | coverage/python.py | 27 | ||||
-rw-r--r-- | tests/test_files.py | 31 |
3 files changed, 73 insertions, 16 deletions
diff --git a/coverage/files.py b/coverage/files.py index 63e9afb2..5f222419 100644 --- a/coverage/files.py +++ b/coverage/files.py @@ -153,6 +153,35 @@ def abs_file(path): return actual_path(os.path.abspath(os.path.realpath(path))) +def zip_location(filename): + """Split a filename into a zipfile / inner name pair. + + Only return a pair if the zipfile exists. No check is made if the inner + name is in the zipfile. + + """ + for ext in ['.zip', '.egg', '.pex']: + zipbase, extension, inner = filename.partition(ext + sep(filename)) + if extension: + zipfile = zipbase + ext + if os.path.exists(zipfile): + return zipfile, inner + return None + + +def source_exists(path): + """Determine if a source file path exists.""" + if os.path.exists(path): + return True + + if zip_location(path): + # If zip_location returns anything, then it's a zipfile that + # exists. That's good enough for us. + return True + + return False + + def python_reported_file(filename): """Return the string as Python would describe this file name.""" if env.PYBEHAVIOR.report_absolute_files: @@ -408,7 +437,7 @@ class PathAliases: result = result.rstrip(r"\/") + result_sep self.aliases.append((original_pattern, regex, result)) - def map(self, path, exists=os.path.exists): + def map(self, path, exists=source_exists): """Map `path` through the aliases. `path` is checked against all of the patterns. The first pattern to diff --git a/coverage/python.py b/coverage/python.py index c8b8e774..b3232085 100644 --- a/coverage/python.py +++ b/coverage/python.py @@ -9,7 +9,7 @@ import zipimport from coverage import env from coverage.exceptions import CoverageException, NoSource -from coverage.files import canonical_filename, relative_filename +from coverage.files import canonical_filename, relative_filename, zip_location from coverage.misc import contract, expensive, isolate_module, join_regex from coverage.parser import PythonParser from coverage.phystokens import source_token_lines, source_encoding @@ -79,19 +79,18 @@ def get_zip_bytes(filename): an empty string if the file is empty. """ - markers = ['.zip'+os.sep, '.egg'+os.sep, '.pex'+os.sep] - for marker in markers: - if marker in filename: - parts = filename.split(marker) - try: - zi = zipimport.zipimporter(parts[0]+marker[:-1]) - except zipimport.ZipImportError: - continue - try: - data = zi.get_data(parts[1]) - except OSError: - continue - return data + zipfile_inner = zip_location(filename) + if zipfile_inner is not None: + zipfile, inner = zipfile_inner + try: + zi = zipimport.zipimporter(zipfile) + except zipimport.ZipImportError: + return None + try: + data = zi.get_data(inner) + except OSError: + return None + return data return None diff --git a/tests/test_files.py b/tests/test_files.py index 4baac072..9e49628d 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -69,7 +69,7 @@ class FilesTest(CoverageTest): assert files.canonical_filename('sub/proj1/file1.py') == self.abs_path('file1.py') @pytest.mark.parametrize( - ["curdir", "sep"], [ + "curdir, sep", [ ("/", "/"), ("X:\\", "\\"), ] @@ -81,6 +81,21 @@ class FilesTest(CoverageTest): files.set_relative_directory() assert files.relative_directory() == curdir + @pytest.mark.parametrize( + "to_make, to_check, answer", [ + ("a/b/c/foo.py", "a/b/c/foo.py", True), + ("a/b/c/foo.py", "a/b/c/bar.py", False), + ("src/files.zip", "src/files.zip/foo.py", True), + ("src/files.egg", "src/files.egg/foo.py", True), + ("src/files.pex", "src/files.pex/foo.py", True), + ("src/files.zip", "src/morefiles.zip/foo.py", False), + ("src/files.pex", "src/files.pex/zipfiles/files.zip/foo.py", True), + ] + ) + def test_source_exists(self, to_make, to_check, answer): + self.make_file(to_make, "") + assert files.source_exists(to_check) == answer + @pytest.mark.parametrize("original, flat", [ ("abc.py", "abc_py"), @@ -117,6 +132,7 @@ def globs_to_regex_params( Everything is yielded so that `test_globs_to_regex` can call `globs_to_regex` once and check one result. + """ pat_id = "|".join(patterns) for text in matches: @@ -578,6 +594,19 @@ class PathAliasesTest(CoverageTest): self.assert_mapped(aliases, the_file, '/the/source/a.py') +class PathAliasesRealFilesTest(CoverageTest): + """Tests for coverage/files.py:PathAliases using real files.""" + + def test_aliasing_zip_files(self): + self.make_file("src/zipfiles/code.zip", "fake zip, doesn't matter") + aliases = PathAliases() + aliases.add("*/d1", "./src") + aliases.add("*/d2", "./src") + + expected = files.canonical_filename("src/zipfiles/code.zip/p1.py") + assert aliases.map("tox/d1/zipfiles/code.zip/p1.py") == expected + + class FindPythonFilesTest(CoverageTest): """Tests of `find_python_files`.""" |