summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/control.py29
-rw-r--r--coverage/files.py92
-rw-r--r--coverage/python.py10
-rw-r--r--coverage/xmlreport.py9
-rw-r--r--tests/farm/html/run_a_xml_1.py4
-rw-r--r--tests/farm/html/run_a_xml_2.py4
-rw-r--r--tests/farm/html/run_y_xml_branch.py4
-rw-r--r--tests/test_files.py32
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)