--- a/eric7/DebugClients/Python/coverage/files.py Fri Nov 19 19:28:47 2021 +0100 +++ b/eric7/DebugClients/Python/coverage/files.py Sat Nov 20 16:47:38 2021 +0100 @@ -13,8 +13,8 @@ import sys from coverage import env -from coverage.backward import unicode_class -from coverage.misc import contract, CoverageException, join_regex, isolate_module +from coverage.exceptions import CoverageException +from coverage.misc import contract, human_sorted, isolate_module, join_regex os = isolate_module(os) @@ -48,7 +48,7 @@ fnorm = os.path.normcase(filename) if fnorm.startswith(RELATIVE_DIR): filename = filename[len(RELATIVE_DIR):] - return unicode_filename(filename) + return filename @contract(returns='unicode') @@ -77,7 +77,7 @@ return CANONICAL_FILENAME_CACHE[filename] -MAX_FLAT = 200 +MAX_FLAT = 100 @contract(filename='unicode', returns='unicode') def flat_rootname(filename): @@ -87,15 +87,16 @@ 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' + For example, the file a/b/c.py will return 'd_86bbcbe134d28fd2_c_py' """ - name = ntpath.splitdrive(filename)[1] - name = re.sub(r"[\\/.:]", "_", name) - if len(name) > MAX_FLAT: - h = hashlib.sha1(name.encode('UTF-8')).hexdigest() - name = name[-(MAX_FLAT-len(h)-1):] + '_' + h - return name + dirname, basename = ntpath.split(filename) + if dirname: + fp = hashlib.new("sha3_256", dirname.encode("UTF-8")).hexdigest()[:16] + prefix = f"d_{fp}_" + else: + prefix = "" + return prefix + basename.replace(".", "_") if env.WINDOWS: @@ -105,8 +106,6 @@ def actual_path(path): """Get the actual path of `path`, including the correct case.""" - if env.PY2 and isinstance(path, unicode_class): - path = path.encode(sys.getfilesystemencoding()) if path in _ACTUAL_PATH_CACHE: return _ACTUAL_PATH_CACHE[path] @@ -143,21 +142,6 @@ return filename -if env.PY2: - @contract(returns='unicode') - def unicode_filename(filename): - """Return a Unicode version of `filename`.""" - if isinstance(filename, str): - encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() - filename = filename.decode(encoding, "replace") - return filename -else: - @contract(filename='unicode', returns='unicode') - def unicode_filename(filename): - """Return a Unicode version of `filename`.""" - return filename - - @contract(returns='unicode') def abs_file(path): """Return the absolute normalized form of `path`.""" @@ -167,7 +151,6 @@ pass path = os.path.abspath(path) path = actual_path(path) - path = unicode_filename(path) return path @@ -207,7 +190,7 @@ return prepped -class TreeMatcher(object): +class TreeMatcher: """A matcher for files in a tree. Construct with a list of paths, either files or directories. Paths match @@ -215,18 +198,21 @@ somewhere in a subtree rooted at one of the directories. """ - def __init__(self, paths): - self.paths = list(paths) + def __init__(self, paths, name="unknown"): + self.original_paths = human_sorted(paths) + self.paths = list(map(os.path.normcase, paths)) + self.name = name def __repr__(self): - return "<TreeMatcher %r>" % self.paths + return f"<TreeMatcher {self.name} {self.original_paths!r}>" def info(self): """A list of strings for displaying when dumping state.""" - return self.paths + return self.original_paths def match(self, fpath): """Does `fpath` indicate a file in one of our trees?""" + fpath = os.path.normcase(fpath) for p in self.paths: if fpath.startswith(p): if fpath == p: @@ -238,13 +224,14 @@ return False -class ModuleMatcher(object): +class ModuleMatcher: """A matcher for modules in a tree.""" - def __init__(self, module_names): + def __init__(self, module_names, name="unknown"): self.modules = list(module_names) + self.name = name def __repr__(self): - return "<ModuleMatcher %r>" % (self.modules) + return f"<ModuleMatcher {self.name} {self.modules!r}>" def info(self): """A list of strings for displaying when dumping state.""" @@ -266,14 +253,15 @@ return False -class FnmatchMatcher(object): +class FnmatchMatcher: """A matcher for files by file name pattern.""" - def __init__(self, pats): + def __init__(self, pats, name="unknown"): self.pats = list(pats) self.re = fnmatches_to_regex(self.pats, case_insensitive=env.WINDOWS) + self.name = name def __repr__(self): - return "<FnmatchMatcher %r>" % self.pats + return f"<FnmatchMatcher {self.name} {self.pats!r}>" def info(self): """A list of strings for displaying when dumping state.""" @@ -327,7 +315,7 @@ return compiled -class PathAliases(object): +class PathAliases: """A collection of aliases for paths. When combining data files from remote machines, often the paths to source @@ -338,13 +326,15 @@ map a path through those aliases to produce a unified path. """ - def __init__(self): + def __init__(self, relative=False): self.aliases = [] + self.relative = relative def pprint(self): # pragma: debugging """Dump the important parts of the PathAliases, for debugging.""" + print(f"Aliases (relative={self.relative}):") for regex, result in self.aliases: - print("{!r} --> {!r}".format(regex.pattern, result)) + print(f"{regex.pattern!r} --> {result!r}") def add(self, pattern, result): """Add the `pattern`/`result` pair to the list of aliases. @@ -405,7 +395,8 @@ if m: new = path.replace(m.group(0), result) new = new.replace(sep(path), sep(result)) - new = canonical_filename(new) + if not self.relative: + new = canonical_filename(new) return new return path