DebugClients/Python3/coverage/codeunit.py

changeset 29
391dc0bc4ae5
parent 0
de9c2efb9d02
child 3495
fac17a82b431
equal deleted inserted replaced
28:dde24fc7f7ba 29:391dc0bc4ae5
1 """Code unit (module) handling for Coverage.""" 1 """Code unit (module) handling for Coverage."""
2 2
3 import glob, os 3 import glob, os
4 4
5 from .backward import string_class, StringIO
6 from .misc import CoverageException
7
8
5 def code_unit_factory(morfs, file_locator, omit_prefixes=None): 9 def code_unit_factory(morfs, file_locator, omit_prefixes=None):
6 """Construct a list of CodeUnits from polymorphic inputs. 10 """Construct a list of CodeUnits from polymorphic inputs.
7 11
8 `morfs` is a module or a filename, or a list of same. 12 `morfs` is a module or a filename, or a list of same.
9 `file_locator` is a FileLocator that can help resolve filenames. 13 `file_locator` is a FileLocator that can help resolve filenames.
10 `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes 14 `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
11 will be omitted from the list. 15 will be omitted from the list.
12 16
13 Returns a list of CodeUnit objects. 17 Returns a list of CodeUnit objects.
14 18
15 """ 19 """
16 20
17 # Be sure we have a list. 21 # Be sure we have a list.
18 if not isinstance(morfs, (list, tuple)): 22 if not isinstance(morfs, (list, tuple)):
19 morfs = [morfs] 23 morfs = [morfs]
20 24
21 # On Windows, the shell doesn't expand wildcards. Do it here. 25 # On Windows, the shell doesn't expand wildcards. Do it here.
22 globbed = [] 26 globbed = []
23 for morf in morfs: 27 for morf in morfs:
24 if isinstance(morf, str) and ('?' in morf or '*' in morf): 28 if isinstance(morf, string_class) and ('?' in morf or '*' in morf):
25 globbed.extend(glob.glob(morf)) 29 globbed.extend(glob.glob(morf))
26 else: 30 else:
27 globbed.append(morf) 31 globbed.append(morf)
28 morfs = globbed 32 morfs = globbed
29 33
30 code_units = [CodeUnit(morf, file_locator) for morf in morfs] 34 code_units = [CodeUnit(morf, file_locator) for morf in morfs]
31 35
32 if omit_prefixes: 36 if omit_prefixes:
37 assert not isinstance(omit_prefixes, string_class) # common mistake
33 prefixes = [file_locator.abs_file(p) for p in omit_prefixes] 38 prefixes = [file_locator.abs_file(p) for p in omit_prefixes]
34 filtered = [] 39 filtered = []
35 for cu in code_units: 40 for cu in code_units:
36 for prefix in prefixes: 41 for prefix in prefixes:
37 if cu.name.startswith(prefix): 42 if cu.filename.startswith(prefix):
38 break 43 break
39 else: 44 else:
40 filtered.append(cu) 45 filtered.append(cu)
41 46
42 code_units = filtered 47 code_units = filtered
43 48
44 return code_units 49 return code_units
45 50
46 51
47 class CodeUnit: 52 class CodeUnit(object):
48 """Code unit: a filename or module. 53 """Code unit: a filename or module.
49 54
50 Instance attributes: 55 Instance attributes:
51 56
52 `name` is a human-readable name for this code unit. 57 `name` is a human-readable name for this code unit.
53 `filename` is the os path from which we can read the source. 58 `filename` is the os path from which we can read the source.
54 `relative` is a boolean. 59 `relative` is a boolean.
55 60
56 """ 61 """
57 62
58 def __init__(self, morf, file_locator): 63 def __init__(self, morf, file_locator):
64 self.file_locator = file_locator
65
59 if hasattr(morf, '__file__'): 66 if hasattr(morf, '__file__'):
60 f = morf.__file__ 67 f = morf.__file__
61 else: 68 else:
62 f = morf 69 f = morf
63 # .pyc files should always refer to a .py instead. 70 # .pyc files should always refer to a .py instead.
64 if f.endswith('.pyc'): 71 if f.endswith('.pyc'):
65 f = f[:-1] 72 f = f[:-1]
66 self.filename = file_locator.canonical_filename(f) 73 self.filename = self.file_locator.canonical_filename(f)
67 74
68 if hasattr(morf, '__name__'): 75 if hasattr(morf, '__name__'):
69 n = modname = morf.__name__ 76 n = modname = morf.__name__
70 self.relative = True 77 self.relative = True
71 else: 78 else:
72 n = os.path.splitext(morf)[0] 79 n = os.path.splitext(morf)[0]
73 rel = file_locator.relative_filename(n) 80 rel = self.file_locator.relative_filename(n)
74 if os.path.isabs(n): 81 if os.path.isabs(n):
75 self.relative = (rel != n) 82 self.relative = (rel != n)
76 else: 83 else:
77 self.relative = True 84 self.relative = True
78 n = rel 85 n = rel
81 self.modname = modname 88 self.modname = modname
82 89
83 def __repr__(self): 90 def __repr__(self):
84 return "<CodeUnit name=%r filename=%r>" % (self.name, self.filename) 91 return "<CodeUnit name=%r filename=%r>" % (self.name, self.filename)
85 92
86 def __cmp__(self, other): 93 # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
87 return cmp(self.name, other.name) 94 # of them defined.
95
96 def __lt__(self, other):
97 return self.name < other.name
98
99 def __le__(self, other):
100 return self.name <= other.name
101
102 def __eq__(self, other):
103 return self.name == other.name
104
105 def __ne__(self, other):
106 return self.name != other.name
107
108 def __gt__(self, other):
109 return self.name > other.name
110
111 def __ge__(self, other):
112 return self.name >= other.name
88 113
89 def flat_rootname(self): 114 def flat_rootname(self):
90 """A base for a flat filename to correspond to this code unit. 115 """A base for a flat filename to correspond to this code unit.
91 116
92 Useful for writing files about the code where you want all the files in 117 Useful for writing files about the code where you want all the files in
93 the same directory, but need to differentiate same-named files from 118 the same directory, but need to differentiate same-named files from
94 different directories. 119 different directories.
95 120
96 For example, the file a/b/c.py might return 'a_b_c' 121 For example, the file a/b/c.py might return 'a_b_c'
97 122
98 """ 123 """
99 if self.modname: 124 if self.modname:
100 return self.modname.replace('.', '_') 125 return self.modname.replace('.', '_')
101 else: 126 else:
102 root = os.path.splitdrive(os.path.splitext(self.name)[0])[1] 127 root = os.path.splitdrive(os.path.splitext(self.name)[0])[1]
103 return root.replace('\\', '_').replace('/', '_') 128 return root.replace('\\', '_').replace('/', '_')
104 129
105 def source_file(self): 130 def source_file(self):
106 """Return an open file for reading the source of the code unit.""" 131 """Return an open file for reading the source of the code unit."""
107 return open(self.filename) 132 if os.path.exists(self.filename):
133 # A regular text file: open it.
134 return open(self.filename)
135
136 # Maybe it's in a zip file?
137 source = self.file_locator.get_zip_data(self.filename)
138 if source is not None:
139 return StringIO(source)
140
141 # Couldn't find source.
142 raise CoverageException(
143 "No source for code %r." % self.filename
144 )

eric ide

mercurial