DebugClients/Python/coverage/python.py

changeset 5051
3586ebd9fac8
parent 4489
d0d6e4ad31bd
equal deleted inserted replaced
5047:04e5dfbd3f3d 5051:3586ebd9fac8
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
3 3
4 """Python source expertise for coverage.py""" 4 """Python source expertise for coverage.py"""
5 5
6 import os.path 6 import os.path
7 import types
7 import zipimport 8 import zipimport
8 9
9 from coverage import env, files 10 from coverage import env, files
10 from coverage.misc import contract, expensive, NoSource, join_regex 11 from coverage.misc import (
12 contract, CoverageException, expensive, NoSource, join_regex, isolate_module,
13 )
11 from coverage.parser import PythonParser 14 from coverage.parser import PythonParser
12 from coverage.phystokens import source_token_lines, source_encoding 15 from coverage.phystokens import source_token_lines, source_encoding
13 from coverage.plugin import FileReporter 16 from coverage.plugin import FileReporter
17
18 os = isolate_module(os)
14 19
15 20
16 @contract(returns='bytes') 21 @contract(returns='bytes')
17 def read_python_source(filename): 22 def read_python_source(filename):
18 """Read the Python source text from `filename`. 23 """Read the Python source text from `filename`.
46 break 51 break
47 else: 52 else:
48 # Couldn't find source. 53 # Couldn't find source.
49 raise NoSource("No source for code: '%s'." % filename) 54 raise NoSource("No source for code: '%s'." % filename)
50 55
56 # Replace \f because of http://bugs.python.org/issue19035
57 source = source.replace(b'\f', b' ')
51 source = source.decode(source_encoding(source), "replace") 58 source = source.decode(source_encoding(source), "replace")
52 59
53 # Python code should always end with a line with a newline. 60 # Python code should always end with a line with a newline.
54 if source and source[-1] != '\n': 61 if source and source[-1] != '\n':
55 source += '\n' 62 source += '\n'
88 def __init__(self, morf, coverage=None): 95 def __init__(self, morf, coverage=None):
89 self.coverage = coverage 96 self.coverage = coverage
90 97
91 if hasattr(morf, '__file__'): 98 if hasattr(morf, '__file__'):
92 filename = morf.__file__ 99 filename = morf.__file__
100 elif isinstance(morf, types.ModuleType):
101 # A module should have had .__file__, otherwise we can't use it.
102 # This could be a PEP-420 namespace package.
103 raise CoverageException("Module {0} has no file".format(morf))
93 else: 104 else:
94 filename = morf 105 filename = morf
106
107 filename = files.unicode_filename(filename)
95 108
96 # .pyc files should always refer to a .py instead. 109 # .pyc files should always refer to a .py instead.
97 if filename.endswith(('.pyc', '.pyo')): 110 if filename.endswith(('.pyc', '.pyo')):
98 filename = filename[:-1] 111 filename = filename[:-1]
99 elif filename.endswith('$py.class'): # Jython 112 elif filename.endswith('$py.class'): # Jython
102 super(PythonFileReporter, self).__init__(files.canonical_filename(filename)) 115 super(PythonFileReporter, self).__init__(files.canonical_filename(filename))
103 116
104 if hasattr(morf, '__name__'): 117 if hasattr(morf, '__name__'):
105 name = morf.__name__ 118 name = morf.__name__
106 name = name.replace(".", os.sep) + ".py" 119 name = name.replace(".", os.sep) + ".py"
120 name = files.unicode_filename(name)
107 else: 121 else:
108 name = files.relative_filename(filename) 122 name = files.relative_filename(filename)
109 self.relname = name 123 self.relname = name
110 124
111 self._source = None 125 self._source = None
112 self._parser = None 126 self._parser = None
113 self._statements = None 127 self._statements = None
114 self._excluded = None 128 self._excluded = None
115 129
130 @contract(returns='unicode')
116 def relative_filename(self): 131 def relative_filename(self):
117 return self.relname 132 return self.relname
118 133
119 @property 134 @property
120 def parser(self): 135 def parser(self):
122 if self._parser is None: 137 if self._parser is None:
123 self._parser = PythonParser( 138 self._parser = PythonParser(
124 filename=self.filename, 139 filename=self.filename,
125 exclude=self.coverage._exclude_regex('exclude'), 140 exclude=self.coverage._exclude_regex('exclude'),
126 ) 141 )
142 self._parser.parse_source()
127 return self._parser 143 return self._parser
128 144
129 @expensive
130 def lines(self): 145 def lines(self):
131 """Return the line numbers of statements in the file.""" 146 """Return the line numbers of statements in the file."""
132 if self._statements is None: 147 return self.parser.statements
133 self._statements, self._excluded = self.parser.parse_source() 148
134 return self._statements
135
136 @expensive
137 def excluded_lines(self): 149 def excluded_lines(self):
138 """Return the line numbers of statements in the file.""" 150 """Return the line numbers of statements in the file."""
139 if self._excluded is None: 151 return self.parser.excluded
140 self._statements, self._excluded = self.parser.parse_source()
141 return self._excluded
142 152
143 def translate_lines(self, lines): 153 def translate_lines(self, lines):
144 return self.parser.translate_lines(lines) 154 return self.parser.translate_lines(lines)
145 155
146 def translate_arcs(self, arcs): 156 def translate_arcs(self, arcs):
159 return self.parser.arcs() 169 return self.parser.arcs()
160 170
161 @expensive 171 @expensive
162 def exit_counts(self): 172 def exit_counts(self):
163 return self.parser.exit_counts() 173 return self.parser.exit_counts()
174
175 def missing_arc_description(self, start, end, executed_arcs=None):
176 return self.parser.missing_arc_description(start, end, executed_arcs)
164 177
165 @contract(returns='unicode') 178 @contract(returns='unicode')
166 def source(self): 179 def source(self):
167 if self._source is None: 180 if self._source is None:
168 self._source = get_python_source(self.filename) 181 self._source = get_python_source(self.filename)
188 # Everything else is probably not Python. 201 # Everything else is probably not Python.
189 return False 202 return False
190 203
191 def source_token_lines(self): 204 def source_token_lines(self):
192 return source_token_lines(self.source()) 205 return source_token_lines(self.source())
206
207 #
208 # eflag: FileType = Python2

eric ide

mercurial