DebugClients/Python/coverage/codeunit.py

changeset 3497
7f51ab29a1a2
parent 790
2c0ea0163ef4
child 3499
f2d4b02c7e88
equal deleted inserted replaced
3493:71f15675e89f 3497:7f51ab29a1a2
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 5 from .backward import open_source, string_class, StringIO
6 from .misc import CoverageException 6 from .misc import CoverageException
7 7
8 8
9 def code_unit_factory(morfs, file_locator, omit_prefixes=None): 9 def code_unit_factory(morfs, file_locator):
10 """Construct a list of CodeUnits from polymorphic inputs. 10 """Construct a list of CodeUnits from polymorphic inputs.
11 11
12 `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.
13
13 `file_locator` is a FileLocator that can help resolve filenames. 14 `file_locator` is a FileLocator that can help resolve filenames.
14 `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
15 will be omitted from the list.
16 15
17 Returns a list of CodeUnit objects. 16 Returns a list of CodeUnit objects.
18 17
19 """ 18 """
20
21 # Be sure we have a list. 19 # Be sure we have a list.
22 if not isinstance(morfs, (list, tuple)): 20 if not isinstance(morfs, (list, tuple)):
23 morfs = [morfs] 21 morfs = [morfs]
24 22
25 # On Windows, the shell doesn't expand wildcards. Do it here. 23 # On Windows, the shell doesn't expand wildcards. Do it here.
31 globbed.append(morf) 29 globbed.append(morf)
32 morfs = globbed 30 morfs = globbed
33 31
34 code_units = [CodeUnit(morf, file_locator) for morf in morfs] 32 code_units = [CodeUnit(morf, file_locator) for morf in morfs]
35 33
36 if omit_prefixes:
37 assert not isinstance(omit_prefixes, string_class) # common mistake
38 prefixes = [file_locator.abs_file(p) for p in omit_prefixes]
39 filtered = []
40 for cu in code_units:
41 for prefix in prefixes:
42 if cu.filename.startswith(prefix):
43 break
44 else:
45 filtered.append(cu)
46
47 code_units = filtered
48
49 return code_units 34 return code_units
50 35
51 36
52 class CodeUnit(object): 37 class CodeUnit(object):
53 """Code unit: a filename or module. 38 """Code unit: a filename or module.
57 `name` is a human-readable name for this code unit. 42 `name` is a human-readable name for this code unit.
58 `filename` is the os path from which we can read the source. 43 `filename` is the os path from which we can read the source.
59 `relative` is a boolean. 44 `relative` is a boolean.
60 45
61 """ 46 """
62
63 def __init__(self, morf, file_locator): 47 def __init__(self, morf, file_locator):
64 self.file_locator = file_locator 48 self.file_locator = file_locator
65 49
66 if hasattr(morf, '__file__'): 50 if hasattr(morf, '__file__'):
67 f = morf.__file__ 51 f = morf.__file__
68 else: 52 else:
69 f = morf 53 f = morf
70 # .pyc files should always refer to a .py instead. 54 # .pyc files should always refer to a .py instead.
71 if f.endswith('.pyc'): 55 if f.endswith('.pyc') or f.endswith('.pyo'):
72 f = f[:-1] 56 f = f[:-1]
57 elif f.endswith('$py.class'): # Jython
58 f = f[:-9] + ".py"
73 self.filename = self.file_locator.canonical_filename(f) 59 self.filename = self.file_locator.canonical_filename(f)
74 60
75 if hasattr(morf, '__name__'): 61 if hasattr(morf, '__name__'):
76 n = modname = morf.__name__ 62 n = modname = morf.__name__
77 self.relative = True 63 self.relative = True
93 # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all 79 # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
94 # of them defined. 80 # of them defined.
95 81
96 def __lt__(self, other): 82 def __lt__(self, other):
97 return self.name < other.name 83 return self.name < other.name
98
99 def __le__(self, other): 84 def __le__(self, other):
100 return self.name <= other.name 85 return self.name <= other.name
101
102 def __eq__(self, other): 86 def __eq__(self, other):
103 return self.name == other.name 87 return self.name == other.name
104
105 def __ne__(self, other): 88 def __ne__(self, other):
106 return self.name != other.name 89 return self.name != other.name
107
108 def __gt__(self, other): 90 def __gt__(self, other):
109 return self.name > other.name 91 return self.name > other.name
110
111 def __ge__(self, other): 92 def __ge__(self, other):
112 return self.name >= other.name 93 return self.name >= other.name
113 94
114 def flat_rootname(self): 95 def flat_rootname(self):
115 """A base for a flat filename to correspond to this code unit. 96 """A base for a flat filename to correspond to this code unit.
122 103
123 """ 104 """
124 if self.modname: 105 if self.modname:
125 return self.modname.replace('.', '_') 106 return self.modname.replace('.', '_')
126 else: 107 else:
127 root = os.path.splitdrive(os.path.splitext(self.name)[0])[1] 108 root = os.path.splitdrive(self.name)[1]
128 return root.replace('\\', '_').replace('/', '_') 109 return root.replace('\\', '_').replace('/', '_').replace('.', '_')
129 110
130 def source_file(self): 111 def source_file(self):
131 """Return an open file for reading the source of the code unit.""" 112 """Return an open file for reading the source of the code unit."""
132 if os.path.exists(self.filename): 113 if os.path.exists(self.filename):
133 # A regular text file: open it. 114 # A regular text file: open it.
134 return open(self.filename) 115 return open_source(self.filename)
135 116
136 # Maybe it's in a zip file? 117 # Maybe it's in a zip file?
137 source = self.file_locator.get_zip_data(self.filename) 118 source = self.file_locator.get_zip_data(self.filename)
138 if source is not None: 119 if source is not None:
139 return StringIO(source) 120 return StringIO(source)
140 121
141 # Couldn't find source. 122 # Couldn't find source.
142 raise CoverageException( 123 raise CoverageException(
143 "No source for code %r." % self.filename 124 "No source for code '%s'." % self.filename
144 ) 125 )
145 126
146 # 127 def should_be_python(self):
147 # eflag: FileType = Python2 128 """Does it seem like this file should contain Python?
129
130 This is used to decide if a file reported as part of the exection of
131 a program was really likely to have contained Python in the first
132 place.
133
134 """
135 # Get the file extension.
136 _, ext = os.path.splitext(self.filename)
137
138 # Anything named *.py* should be Python.
139 if ext.startswith('.py'):
140 return True
141 # A file with no extension should be Python.
142 if not ext:
143 return True
144 # Everything else is probably not Python.
145 return False

eric ide

mercurial