Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(242)

Unified Diff: tools/telemetry/third_party/coverage/coverage/files.py

Issue 1366913004: Add coverage Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/third_party/coverage/coverage/files.py
diff --git a/third_party/pycoverage/coverage/files.py b/tools/telemetry/third_party/coverage/coverage/files.py
similarity index 59%
copy from third_party/pycoverage/coverage/files.py
copy to tools/telemetry/third_party/coverage/coverage/files.py
index 464535a81653ca833ba33b462467941e373c0927..2b8727d43db44e087899f4733d89c6c8edabf3e8 100644
--- a/third_party/pycoverage/coverage/files.py
+++ b/tools/telemetry/third_party/coverage/coverage/files.py
@@ -1,111 +1,123 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""File wrangling."""
-from coverage.backward import to_string
-from coverage.misc import CoverageException
-import fnmatch, os, os.path, re, sys
-import ntpath, posixpath
+import fnmatch
+import ntpath
+import os
+import os.path
+import posixpath
+import re
+import sys
-class FileLocator(object):
- """Understand how filenames work."""
+from coverage import env
+from coverage.backward import unicode_class
+from coverage.misc import CoverageException, join_regex
- 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 = {}
+RELATIVE_DIR = None
+CANONICAL_FILENAME_CACHE = {}
- def relative_filename(self, filename):
- """Return the relative form of `filename`.
- The filename will be relative to the current directory when the
- `FileLocator` was constructed.
+def set_relative_directory():
+ """Set the directory that `relative_filename` will be relative to."""
+ global RELATIVE_DIR, CANONICAL_FILENAME_CACHE
- """
- fnorm = os.path.normcase(filename)
- if fnorm.startswith(self.relative_dir):
- filename = filename[len(self.relative_dir):]
- return filename
+ # The absolute path to our current directory.
+ RELATIVE_DIR = os.path.normcase(abs_file(os.curdir) + os.sep)
- def canonical_filename(self, filename):
- """Return a canonical filename for `filename`.
+ # Cache of results of calling the canonical_filename() method, to
+ # avoid duplicating work.
+ CANONICAL_FILENAME_CACHE = {}
- An absolute path with no redundant components and normalized case.
+def relative_directory():
+ """Return the directory that `relative_filename` is relative to."""
+ return RELATIVE_DIR
- """
- 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]
-
- def get_zip_data(self, filename):
- """Get data from `filename` if it is a zip file path.
-
- Returns the string data read from the zip file, or None if no zip file
- could be found or `filename` isn't in it. The data returned will be
- an empty string if the file is empty.
+def relative_filename(filename):
+ """Return the relative form of `filename`.
- """
- import zipimport
- markers = ['.zip'+os.sep, '.egg'+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 IOError:
+ The file name will be relative to the current directory when the
+ `set_relative_directory` was called.
+
+ """
+ fnorm = os.path.normcase(filename)
+ if fnorm.startswith(RELATIVE_DIR):
+ filename = filename[len(RELATIVE_DIR):]
+ return filename
+
+def canonical_filename(filename):
+ """Return a canonical file name 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
- return to_string(data)
- return None
+ 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]
+
+
+def flat_rootname(filename):
+ """A base for a flat file name to correspond to this file.
+
+ Useful for writing files about the code where you want all the files in
+ the same directory, but need to differentiate same-named files from
+ different directories.
+
+ For example, the file a/b/c.py will return 'a_b_c_py'
+
+ """
+ name = ntpath.splitdrive(filename)[1]
+ return re.sub(r"[\\/.:]", "_", name)
-if sys.platform == 'win32':
+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 env.PY2 and isinstance(path, unicode_class):
+ path = path.encode(sys.getfilesystemencoding())
+ if path in _ACTUAL_PATH_CACHE:
+ return _ACTUAL_PATH_CACHE[path]
head, tail = os.path.split(path)
if not tail:
- actpath = head
+ # This means head is the drive spec: normalize it.
+ actpath = head.upper()
elif not head:
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."""
@@ -137,7 +149,7 @@ def prep_patterns(patterns):
"""
prepped = []
for p in patterns or []:
- if p.startswith("*") or p.startswith("?"):
+ if p.startswith(("*", "?")):
prepped.append(p)
else:
prepped.append(abs_file(p))
@@ -147,7 +159,7 @@ def prep_patterns(patterns):
class TreeMatcher(object):
"""A matcher for files in a tree."""
def __init__(self, directories):
- self.dirs = directories[:]
+ self.dirs = list(directories)
def __repr__(self):
return "<TreeMatcher %r>" % self.dirs
@@ -156,10 +168,6 @@ class TreeMatcher(object):
"""A list of strings for displaying when dumping state."""
return self.dirs
- def add(self, directory):
- """Add another directory to the list we match for."""
- self.dirs.append(directory)
-
def match(self, fpath):
"""Does `fpath` indicate a file in one of our trees?"""
for d in self.dirs:
@@ -173,10 +181,49 @@ class TreeMatcher(object):
return False
+class ModuleMatcher(object):
+ """A matcher for modules in a tree."""
+ def __init__(self, module_names):
+ self.modules = list(module_names)
+
+ def __repr__(self):
+ return "<ModuleMatcher %r>" % (self.modules)
+
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.modules
+
+ def match(self, module_name):
+ """Does `module_name` indicate a module in one of our packages?"""
+ if not module_name:
+ return False
+
+ for m in self.modules:
+ if module_name.startswith(m):
+ if module_name == m:
+ return True
+ if module_name[len(m)] == '.':
+ # This is a module in the package
+ return True
+
+ return False
+
+
class FnmatchMatcher(object):
- """A matcher for files by filename pattern."""
+ """A matcher for files by file name pattern."""
def __init__(self, pats):
self.pats = pats[:]
+ # fnmatch is platform-specific. On Windows, it does the Windows thing
+ # of treating / and \ as equivalent. But on other platforms, we need to
+ # take care of that ourselves.
+ fnpats = (fnmatch.translate(p) for p in pats)
+ fnpats = (p.replace(r"\/", r"[\\/]") for p in fnpats)
+ if env.WINDOWS:
+ # Windows is also case-insensitive. BTW: the regex docs say that
+ # flags like (?i) have to be at the beginning, but fnmatch puts
+ # them at the end, and having two there seems to work fine.
+ fnpats = (p + "(?i)" for p in fnpats)
+ self.re = re.compile(join_regex(fnpats))
def __repr__(self):
return "<FnmatchMatcher %r>" % self.pats
@@ -186,11 +233,8 @@ class FnmatchMatcher(object):
return self.pats
def match(self, fpath):
- """Does `fpath` match one of our filename patterns?"""
- for pat in self.pats:
- if fnmatch.fnmatch(fpath, pat):
- return True
- return False
+ """Does `fpath` match one of our file name patterns?"""
+ return self.re.match(fpath) is not None
def sep(s):
@@ -213,12 +257,9 @@ class PathAliases(object):
A `PathAliases` object tracks a list of pattern/result pairs, and can
map a path through those aliases to produce a unified path.
- `locator` is a FileLocator that is used to canonicalize the results.
-
"""
- def __init__(self, locator=None):
+ def __init__(self):
self.aliases = []
- self.locator = locator
def add(self, pattern, result):
"""Add the `pattern`/`result` pair to the list of aliases.
@@ -245,11 +286,10 @@ class PathAliases(object):
pattern = abs_file(pattern)
pattern += pattern_sep
- # Make a regex from the pattern. fnmatch always adds a \Z or $ to
+ # Make a regex from the pattern. fnmatch always adds a \Z to
# match the whole string, which we don't want.
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 too, so change slash to match
# either separator.
regex_pat = regex_pat.replace(r"\/", r"[\\/]")
@@ -272,6 +312,10 @@ class PathAliases(object):
The separator style in the result is made to match that of the result
in the alias.
+ Returns the mapped path. If a mapping has happened, this is a
+ canonical path. If no mapping has happened, it is the original value
+ of `path` unchanged.
+
"""
for regex, result, pattern_sep, result_sep in self.aliases:
m = regex.match(path)
@@ -279,8 +323,7 @@ class PathAliases(object):
new = path.replace(m.group(0), result)
if pattern_sep != result_sep:
new = new.replace(pattern_sep, result_sep)
- if self.locator:
- new = self.locator.canonical_filename(new)
+ new = canonical_filename(new)
return new
return path
@@ -291,7 +334,7 @@ def find_python_files(dirname):
To be importable, the files have to be in a directory with a __init__.py,
except for `dirname` itself, which isn't required to have one. The
assumption is that `dirname` was specified directly, so the user knows
- best, but subdirectories are checked for a __init__.py to be sure we only
+ best, but sub-directories are checked for a __init__.py to be sure we only
find the importable files.
"""

Powered by Google App Engine
This is Rietveld 408576698