--- a/DebugClients/Python/coverage/codeunit.py Thu Jan 07 13:42:05 2010 +0000 +++ b/DebugClients/Python/coverage/codeunit.py Thu Jan 07 13:42:51 2010 +0000 @@ -2,60 +2,67 @@ import glob, os +from coverage.backward import string_class, StringIO +from coverage.misc import CoverageException + + def code_unit_factory(morfs, file_locator, omit_prefixes=None): """Construct a list of CodeUnits from polymorphic inputs. - + `morfs` is a module or a filename, or a list of same. `file_locator` is a FileLocator that can help resolve filenames. `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes will be omitted from the list. - + Returns a list of CodeUnit objects. - + """ # Be sure we have a list. if not isinstance(morfs, (list, tuple)): morfs = [morfs] - + # On Windows, the shell doesn't expand wildcards. Do it here. globbed = [] for morf in morfs: - if isinstance(morf, basestring) and ('?' in morf or '*' in morf): + if isinstance(morf, string_class) and ('?' in morf or '*' in morf): globbed.extend(glob.glob(morf)) else: globbed.append(morf) morfs = globbed code_units = [CodeUnit(morf, file_locator) for morf in morfs] - + if omit_prefixes: + assert not isinstance(omit_prefixes, string_class) # common mistake prefixes = [file_locator.abs_file(p) for p in omit_prefixes] filtered = [] for cu in code_units: for prefix in prefixes: - if cu.name.startswith(prefix): + if cu.filename.startswith(prefix): break else: filtered.append(cu) - + code_units = filtered return code_units -class CodeUnit: +class CodeUnit(object): """Code unit: a filename or module. - + Instance attributes: - + `name` is a human-readable name for this code unit. `filename` is the os path from which we can read the source. `relative` is a boolean. - + """ def __init__(self, morf, file_locator): + self.file_locator = file_locator + if hasattr(morf, '__file__'): f = morf.__file__ else: @@ -63,14 +70,14 @@ # .pyc files should always refer to a .py instead. if f.endswith('.pyc'): f = f[:-1] - self.filename = file_locator.canonical_filename(f) + self.filename = self.file_locator.canonical_filename(f) if hasattr(morf, '__name__'): n = modname = morf.__name__ self.relative = True else: n = os.path.splitext(morf)[0] - rel = file_locator.relative_filename(n) + rel = self.file_locator.relative_filename(n) if os.path.isabs(n): self.relative = (rel != n) else: @@ -83,18 +90,36 @@ def __repr__(self): return "<CodeUnit name=%r filename=%r>" % (self.name, self.filename) - def __cmp__(self, other): - return cmp(self.name, other.name) + # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all + # of them defined. + + def __lt__(self, other): + return self.name < other.name + + def __le__(self, other): + return self.name <= other.name + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __gt__(self, other): + return self.name > other.name + + def __ge__(self, other): + return self.name >= other.name def flat_rootname(self): """A base for a flat filename to correspond to this code unit. - + 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 might return 'a_b_c' - + """ if self.modname: return self.modname.replace('.', '_') @@ -104,4 +129,16 @@ def source_file(self): """Return an open file for reading the source of the code unit.""" - return open(self.filename) + if os.path.exists(self.filename): + # A regular text file: open it. + return open(self.filename) + + # Maybe it's in a zip file? + source = self.file_locator.get_zip_data(self.filename) + if source is not None: + return StringIO(source) + + # Couldn't find source. + raise CoverageException( + "No source for code %r." % self.filename + )