diff options
-rw-r--r-- | coverage/control.py | 29 | ||||
-rw-r--r-- | coverage/files.py | 92 | ||||
-rw-r--r-- | coverage/python.py | 10 | ||||
-rw-r--r-- | coverage/xmlreport.py | 9 | ||||
-rw-r--r-- | tests/farm/html/run_a_xml_1.py | 4 | ||||
-rw-r--r-- | tests/farm/html/run_a_xml_2.py | 4 | ||||
-rw-r--r-- | tests/farm/html/run_y_xml_branch.py | 4 | ||||
-rw-r--r-- | tests/test_files.py | 32 |
8 files changed, 92 insertions, 92 deletions
diff --git a/coverage/control.py b/coverage/control.py index 9256edb2..3cae1d36 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -9,14 +9,14 @@ import socket import sys import traceback -from coverage import env +from coverage import env, files from coverage.annotate import AnnotateReporter from coverage.backward import string_class, iitems from coverage.collector import Collector from coverage.config import CoverageConfig from coverage.data import CoverageData from coverage.debug import DebugControl -from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher +from coverage.files import TreeMatcher, FnmatchMatcher from coverage.files import PathAliases, find_python_files, prep_patterns from coverage.files import ModuleMatcher, abs_file from coverage.html import HtmlReporter @@ -167,7 +167,7 @@ class Coverage(object): # Other instance attributes, set later. self.omit = self.include = self.source = None - self.source_pkgs = self.file_locator = None + self.source_pkgs = None self.data = self.collector = None self.plugins = self.file_tracing_plugins = None self.pylib_dirs = self.cover_dirs = None @@ -214,14 +214,14 @@ class Coverage(object): self._exclude_re = {} self._exclude_regex_stale() - self.file_locator = FileLocator() + files.set_relative_directory() # The source argument can be directories or package names. self.source = [] self.source_pkgs = [] for src in self.config.source or []: if os.path.exists(src): - self.source.append(self.file_locator.canonical_filename(src)) + self.source.append(files.canonical_filename(src)) else: self.source_pkgs.append(src) @@ -468,7 +468,7 @@ class Coverage(object): if filename.endswith("$py.class"): filename = filename[:-9] + ".py" - canonical = self.file_locator.canonical_filename(filename) + canonical = files.canonical_filename(filename) disp.canonical_filename = canonical # Try the plugins, see if they have an opinion about the file. @@ -486,10 +486,9 @@ class Coverage(object): if file_tracer.has_dynamic_source_filename(): disp.has_dynamic_filename = True else: - disp.source_filename = \ - self.file_locator.canonical_filename( - file_tracer.source_filename() - ) + disp.source_filename = files.canonical_filename( + file_tracer.source_filename() + ) break except Exception: self._warn( @@ -740,7 +739,7 @@ class Coverage(object): self._init() aliases = None if self.config.paths: - aliases = PathAliases(self.file_locator) + aliases = PathAliases() for paths in self.config.paths.values(): result = paths[0] for pattern in paths[1:]: @@ -792,7 +791,7 @@ class Coverage(object): # Find files that were never executed at all. for src in self.source: for py_file in find_python_files(src): - py_file = self.file_locator.canonical_filename(py_file) + py_file = files.canonical_filename(py_file) if self.omit_match and self.omit_match.match(py_file): # Turns out this file was omitted, so don't pull it back @@ -872,9 +871,7 @@ class Coverage(object): # The FileReporter can have a name attribute, but if it doesn't, we'll # supply it as the relative path to self.filename. if not hasattr(file_reporter, "name"): - file_reporter.name = self.file_locator.relative_filename( - file_reporter.filename - ) + file_reporter.name = files.relative_filename(file_reporter.filename) return file_reporter @@ -1013,7 +1010,7 @@ class Coverage(object): outfile = open(self.config.xml_output, "w") file_to_close = outfile try: - reporter = XmlReporter(self, self.config, self.file_locator) + reporter = XmlReporter(self, self.config) return reporter.report(morfs, outfile=outfile) except CoverageException: delete_file = True diff --git a/coverage/files.py b/coverage/files.py index a800b7a9..665e2f33 100644 --- a/coverage/files.py +++ b/coverage/files.py @@ -12,55 +12,66 @@ from coverage import env from coverage.misc import CoverageException, join_regex -class FileLocator(object): - """Understand how filenames work.""" +RELATIVE_DIR = None +CANONICAL_FILENAME_CACHE = {} - def __init__(self): - # The absolute path to our current directory. - self.relative_dir = os.path.normcase(abs_file(os.curdir) + os.sep) - # Cache of results of calling the canonical_filename() method, to - # avoid duplicating work. - self.canonical_filename_cache = {} +def set_relative_directory(): + """Set the directory that `relative_filename` will be relative to.""" + global RELATIVE_DIR, CANONICAL_FILENAME_CACHE - def relative_filename(self, filename): - """Return the relative form of `filename`. + # The absolute path to our current directory. + RELATIVE_DIR = os.path.normcase(abs_file(os.curdir) + os.sep) - The filename will be relative to the current directory when the - `FileLocator` was constructed. + # Cache of results of calling the canonical_filename() method, to + # avoid duplicating work. + CANONICAL_FILENAME_CACHE = {} - """ - fnorm = os.path.normcase(filename) - if fnorm.startswith(self.relative_dir): - filename = filename[len(self.relative_dir):] - return filename +def relative_directory(): + """Return the directory that `relative_filename` is relative to.""" + return RELATIVE_DIR - def canonical_filename(self, filename): - """Return a canonical filename for `filename`. +def relative_filename(filename): + """Return the relative form of `filename`. - An absolute path with no redundant components and normalized case. + The filename will be relative to the current directory when the + `set_relative_directory` was called. - """ - if filename not in self.canonical_filename_cache: - if not os.path.isabs(filename): - for path in [os.curdir] + sys.path: - if path is None: - continue - f = os.path.join(path, filename) - if os.path.exists(f): - filename = f - break - cf = abs_file(filename) - self.canonical_filename_cache[filename] = cf - return self.canonical_filename_cache[filename] + """ + fnorm = os.path.normcase(filename) + if fnorm.startswith(RELATIVE_DIR): + filename = filename[len(RELATIVE_DIR):] + return filename + +def canonical_filename(filename): + """Return a canonical filename for `filename`. + + An absolute path with no redundant components and normalized case. + + """ + if filename not in CANONICAL_FILENAME_CACHE: + if not os.path.isabs(filename): + for path in [os.curdir] + sys.path: + if path is None: + continue + f = os.path.join(path, filename) + if os.path.exists(f): + filename = f + break + cf = abs_file(filename) + CANONICAL_FILENAME_CACHE[filename] = cf + return CANONICAL_FILENAME_CACHE[filename] if env.WINDOWS: + _ACTUAL_PATH_CACHE = {} + _ACTUAL_PATH_LIST_CACHE = {} + def actual_path(path): """Get the actual path of `path`, including the correct case.""" - if path in actual_path.cache: - return actual_path.cache[path] + if path in _ACTUAL_PATH_CACHE: + return _ACTUAL_PATH_CACHE[path] head, tail = os.path.split(path) if not tail: @@ -70,26 +81,23 @@ if env.WINDOWS: actpath = tail else: head = actual_path(head) - if head in actual_path.list_cache: - files = actual_path.list_cache[head] + if head in _ACTUAL_PATH_LIST_CACHE: + files = _ACTUAL_PATH_LIST_CACHE[head] else: try: files = os.listdir(head) except OSError: files = [] - actual_path.list_cache[head] = files + _ACTUAL_PATH_LIST_CACHE[head] = files normtail = os.path.normcase(tail) for f in files: if os.path.normcase(f) == normtail: tail = f break actpath = os.path.join(head, tail) - actual_path.cache[path] = actpath + _ACTUAL_PATH_CACHE[path] = actpath return actpath - actual_path.cache = {} - actual_path.list_cache = {} - else: def actual_path(filename): """The actual path for non-Windows platforms.""" diff --git a/coverage/python.py b/coverage/python.py index 69823da7..8dc163df 100644 --- a/coverage/python.py +++ b/coverage/python.py @@ -3,8 +3,7 @@ import os.path import zipimport -from coverage import env -from coverage.files import FileLocator +from coverage import env, files from coverage.misc import contract, NoSource, join_regex from coverage.parser import PythonParser from coverage.phystokens import source_token_lines, source_encoding @@ -85,7 +84,6 @@ class PythonFileReporter(FileReporter): def __init__(self, morf, coverage=None): self.coverage = coverage - file_locator = coverage.file_locator if coverage else FileLocator() if hasattr(morf, '__file__'): filename = morf.__file__ @@ -98,15 +96,13 @@ class PythonFileReporter(FileReporter): elif filename.endswith('$py.class'): # Jython filename = filename[:-9] + ".py" - super(PythonFileReporter, self).__init__( - file_locator.canonical_filename(filename) - ) + super(PythonFileReporter, self).__init__(files.canonical_filename(filename)) if hasattr(morf, '__name__'): name = morf.__name__ name = name.replace(".", os.sep) + ".py" else: - name = file_locator.relative_filename(filename) + name = files.relative_filename(filename) self.name = name self._source = None diff --git a/coverage/xmlreport.py b/coverage/xmlreport.py index 996f19a2..49b73122 100644 --- a/coverage/xmlreport.py +++ b/coverage/xmlreport.py @@ -5,7 +5,7 @@ import sys import time import xml.dom.minidom -from coverage import __url__, __version__ +from coverage import __url__, __version__, files from coverage.report import Reporter @@ -20,10 +20,9 @@ def rate(hit, num): class XmlReporter(Reporter): """A reporter for writing Cobertura-style XML coverage results.""" - def __init__(self, coverage, config, file_locator): + def __init__(self, coverage, config): super(XmlReporter, self).__init__(coverage, config) - self.file_locator = file_locator self.source_paths = set() self.packages = {} self.xml_out = None @@ -122,7 +121,7 @@ class XmlReporter(Reporter): # Create the 'lines' and 'package' XML elements, which # are populated later. Note that a package == a directory. - filename = self.file_locator.relative_filename(fr.filename) + filename = files.relative_filename(fr.filename) filename = filename.replace("\\", "/") dirname = os.path.dirname(filename) or "." parts = dirname.split("/") @@ -130,7 +129,7 @@ class XmlReporter(Reporter): package_name = dirname.replace("/", ".") className = fr.name - self.source_paths.add(self.file_locator.relative_dir.rstrip('/')) + self.source_paths.add(files.relative_directory().rstrip('/')) package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0]) xclass = self.xml_out.createElement("class") diff --git a/tests/farm/html/run_a_xml_1.py b/tests/farm/html/run_a_xml_1.py index 593beae2..eec66174 100644 --- a/tests/farm/html/run_a_xml_1.py +++ b/tests/farm/html/run_a_xml_1.py @@ -2,14 +2,14 @@ source_path = None def html_it(): """Run coverage and make an XML report for a.""" - import coverage + import coverage, coverage.files cov = coverage.coverage() cov.start() import a # pragma: nested cov.stop() # pragma: nested cov.xml_report(a, outfile="../xml_1/coverage.xml") global source_path - source_path = cov.file_locator.relative_dir.rstrip('/') + source_path = coverage.files.relative_directory().rstrip('/') import os if not os.path.exists("xml_1"): diff --git a/tests/farm/html/run_a_xml_2.py b/tests/farm/html/run_a_xml_2.py index 4d691b3b..081c8aa3 100644 --- a/tests/farm/html/run_a_xml_2.py +++ b/tests/farm/html/run_a_xml_2.py @@ -2,14 +2,14 @@ source_path = None def html_it(): """Run coverage and make an XML report for a.""" - import coverage + import coverage, coverage.files cov = coverage.coverage(config_file="run_a_xml_2.ini") cov.start() import a # pragma: nested cov.stop() # pragma: nested cov.xml_report(a) global source_path - source_path = cov.file_locator.relative_dir.rstrip('/') + source_path = coverage.files.relative_directory().rstrip('/') import os if not os.path.exists("xml_2"): diff --git a/tests/farm/html/run_y_xml_branch.py b/tests/farm/html/run_y_xml_branch.py index 59228077..b26f3af8 100644 --- a/tests/farm/html/run_y_xml_branch.py +++ b/tests/farm/html/run_y_xml_branch.py @@ -2,14 +2,14 @@ source_path = None def xml_it(): """Run coverage and make an XML report for y.""" - import coverage + import coverage, coverage.files cov = coverage.coverage(branch=True) cov.start() import y # pragma: nested cov.stop() # pragma: nested cov.xml_report(y, outfile="../xml_branch/coverage.xml") global source_path - source_path = cov.file_locator.relative_dir.rstrip('/') + source_path = coverage.files.relative_directory().rstrip('/') import os if not os.path.exists("xml_branch"): diff --git a/tests/test_files.py b/tests/test_files.py index cd571e08..483ede9f 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -3,8 +3,9 @@ import os import os.path +from coverage import files from coverage.files import ( - FileLocator, TreeMatcher, FnmatchMatcher, ModuleMatcher, PathAliases, + TreeMatcher, FnmatchMatcher, ModuleMatcher, PathAliases, find_python_files, abs_file, actual_path ) from coverage.misc import CoverageException @@ -13,8 +14,8 @@ from coverage import env from tests.coveragetest import CoverageTest -class FileLocatorTest(CoverageTest): - """Tests of `FileLocator`.""" +class FilesTest(CoverageTest): + """Tests of coverage.files.""" def abs_path(self, p): """Return the absolute path for `p`.""" @@ -22,11 +23,11 @@ class FileLocatorTest(CoverageTest): def test_simple(self): self.make_file("hello.py") - fl = FileLocator() - self.assertEqual(fl.relative_filename("hello.py"), "hello.py") + files.set_relative_directory() + self.assertEqual(files.relative_filename("hello.py"), "hello.py") a = self.abs_path("hello.py") self.assertNotEqual(a, "hello.py") - self.assertEqual(fl.relative_filename(a), "hello.py") + self.assertEqual(files.relative_filename(a), "hello.py") def test_peer_directories(self): self.make_file("sub/proj1/file1.py") @@ -35,20 +36,20 @@ class FileLocatorTest(CoverageTest): a2 = self.abs_path("sub/proj2/file2.py") d = os.path.normpath("sub/proj1") os.chdir(d) - fl = FileLocator() - self.assertEqual(fl.relative_filename(a1), "file1.py") - self.assertEqual(fl.relative_filename(a2), a2) + files.set_relative_directory() + self.assertEqual(files.relative_filename(a1), "file1.py") + self.assertEqual(files.relative_filename(a2), a2) def test_filepath_contains_absolute_prefix_twice(self): # https://bitbucket.org/ned/coveragepy/issue/194 # Build a path that has two pieces matching the absolute path prefix. # Technically, this test doesn't do that on Windows, but drive # letters make that impractical to achieve. - fl = FileLocator() + files.set_relative_directory() d = abs_file(os.curdir) trick = os.path.splitdrive(d)[1].lstrip(os.path.sep) rel = os.path.join('sub', trick, 'file1.py') - self.assertEqual(fl.relative_filename(abs_file(rel)), rel) + self.assertEqual(files.relative_filename(abs_file(rel)), rel) class MatcherTest(CoverageTest): @@ -56,11 +57,11 @@ class MatcherTest(CoverageTest): def setUp(self): super(MatcherTest, self).setUp() - self.fl = FileLocator() + files.set_relative_directory() def assertMatches(self, matcher, filepath, matches): """The `matcher` should agree with `matches` about `filepath`.""" - canonical = self.fl.canonical_filename(filepath) + canonical = files.canonical_filename(filepath) self.assertEqual( matcher.match(canonical), matches, "File %s should have matched as %s" % (filepath, matches) @@ -74,10 +75,9 @@ class MatcherTest(CoverageTest): (self.make_file("sub3/file4.py"), True), (self.make_file("sub3/file5.c"), False), ] - fl = FileLocator() trees = [ - fl.canonical_filename("sub"), - fl.canonical_filename("sub3/file4.py"), + files.canonical_filename("sub"), + files.canonical_filename("sub3/file4.py"), ] tm = TreeMatcher(trees) self.assertEqual(tm.info(), trees) |