eric7/DebugClients/Python/coverage/parser.py

branch
eric7
changeset 8527
2bd1325d727e
parent 8312
800c432b34c8
child 8775
0802ae193343
equal deleted inserted replaced
8526:587202572b10 8527:2bd1325d727e
203 203
204 # Find the starts of the executable statements. 204 # Find the starts of the executable statements.
205 if not empty: 205 if not empty:
206 self.raw_statements.update(self.byte_parser._find_statements()) 206 self.raw_statements.update(self.byte_parser._find_statements())
207 207
208 # The first line of modules can lie and say 1 always, even if the first
209 # line of code is later. If so, map 1 to the actual first line of the
210 # module.
211 if env.PYBEHAVIOR.module_firstline_1 and self._multiline:
212 self._multiline[1] = min(self.raw_statements)
213
208 def first_line(self, line): 214 def first_line(self, line):
209 """Return the first line number of the statement including `line`.""" 215 """Return the first line number of the statement including `line`."""
210 if line < 0: 216 if line < 0:
211 line = -self._multiline.get(-line, -line) 217 line = -self._multiline.get(-line, -line)
212 else: 218 else:
218 statement. 224 statement.
219 225
220 Returns a set of the first lines. 226 Returns a set of the first lines.
221 227
222 """ 228 """
223 return set(self.first_line(l) for l in lines) 229 return {self.first_line(l) for l in lines}
224 230
225 def translate_lines(self, lines): 231 def translate_lines(self, lines):
226 """Implement `FileReporter.translate_lines`.""" 232 """Implement `FileReporter.translate_lines`."""
227 return self.first_lines(lines) 233 return self.first_lines(lines)
228 234
330 start, end = end, start 336 start, end = end, start
331 337
332 fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)]) 338 fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)])
333 339
334 msgs = [] 340 msgs = []
335 for fragment_pair in fragment_pairs: 341 for smsg, emsg in fragment_pairs:
336 smsg, emsg = fragment_pair
337
338 if emsg is None: 342 if emsg is None:
339 if end < 0: 343 if end < 0:
340 # Hmm, maybe we have a one-line callable, let's check. 344 # Hmm, maybe we have a one-line callable, let's check.
341 if (-end, end) in self._missing_arc_fragments: 345 if (-end, end) in self._missing_arc_fragments:
342 return self.missing_arc_description(-end, end) 346 return self.missing_arc_description(-end, end)
387 The iteration includes `self` as its first value. 391 The iteration includes `self` as its first value.
388 392
389 """ 393 """
390 return (ByteParser(self.text, code=c) for c in code_objects(self.code)) 394 return (ByteParser(self.text, code=c) for c in code_objects(self.code))
391 395
392 def _bytes_lines(self): 396 def _line_numbers(self):
393 """Map byte offsets to line numbers in `code`. 397 """Yield the line numbers possible in this code object.
394 398
395 Uses co_lnotab described in Python/compile.c to map byte offsets to 399 Uses co_lnotab described in Python/compile.c to find the
396 line numbers. Produces a sequence: (b0, l0), (b1, l1), ... 400 line numbers. Produces a sequence: l0, l1, ...
397 401 """
398 Only byte offsets that correspond to line numbers are included in the 402 if hasattr(self.code, "co_lines"):
399 results. 403 for _, _, line in self.code.co_lines():
400 404 if line is not None:
401 """ 405 yield line
402 # Adapted from dis.py in the standard library. 406 else:
403 byte_increments = bytes_to_ints(self.code.co_lnotab[0::2]) 407 # Adapted from dis.py in the standard library.
404 line_increments = bytes_to_ints(self.code.co_lnotab[1::2]) 408 byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
405 409 line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
406 last_line_num = None 410
407 line_num = self.code.co_firstlineno 411 last_line_num = None
408 byte_num = 0 412 line_num = self.code.co_firstlineno
409 for byte_incr, line_incr in zip(byte_increments, line_increments): 413 byte_num = 0
410 if byte_incr: 414 for byte_incr, line_incr in zip(byte_increments, line_increments):
411 if line_num != last_line_num: 415 if byte_incr:
412 yield (byte_num, line_num) 416 if line_num != last_line_num:
413 last_line_num = line_num 417 yield line_num
414 byte_num += byte_incr 418 last_line_num = line_num
415 if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80: 419 byte_num += byte_incr
416 line_incr -= 0x100 420 if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
417 line_num += line_incr 421 line_incr -= 0x100
418 if line_num != last_line_num: 422 line_num += line_incr
419 yield (byte_num, line_num) 423 if line_num != last_line_num:
424 yield line_num
420 425
421 def _find_statements(self): 426 def _find_statements(self):
422 """Find the statements in `self.code`. 427 """Find the statements in `self.code`.
423 428
424 Produce a sequence of line numbers that start statements. Recurses 429 Produce a sequence of line numbers that start statements. Recurses
425 into all code objects reachable from `self.code`. 430 into all code objects reachable from `self.code`.
426 431
427 """ 432 """
428 for bp in self.child_parsers(): 433 for bp in self.child_parsers():
429 # Get all of the lineno information from this code. 434 # Get all of the lineno information from this code.
430 for _, l in bp._bytes_lines(): 435 for l in bp._line_numbers():
431 yield l 436 yield l
432 437
433 438
434 # 439 #
435 # AST analysis 440 # AST analysis
518 523
519 @contract(text='unicode', statements=set) 524 @contract(text='unicode', statements=set)
520 def __init__(self, text, statements, multiline): 525 def __init__(self, text, statements, multiline):
521 self.root_node = ast.parse(neuter_encoding_declaration(text)) 526 self.root_node = ast.parse(neuter_encoding_declaration(text))
522 # TODO: I think this is happening in too many places. 527 # TODO: I think this is happening in too many places.
523 self.statements = set(multiline.get(l, l) for l in statements) 528 self.statements = {multiline.get(l, l) for l in statements}
524 self.multiline = multiline 529 self.multiline = multiline
525 530
526 if AST_DUMP: # pragma: debugging 531 if AST_DUMP: # pragma: debugging
527 # Dump the AST so that failing tests have helpful output. 532 # Dump the AST so that failing tests have helpful output.
528 print("Statements: {}".format(self.statements)) 533 print("Statements: {}".format(self.statements))
617 return self.line_for_node(node.elts[0]) 622 return self.line_for_node(node.elts[0])
618 else: 623 else:
619 return node.lineno 624 return node.lineno
620 625
621 def _line__Module(self, node): 626 def _line__Module(self, node):
622 if node.body: 627 if env.PYBEHAVIOR.module_firstline_1:
628 return 1
629 elif node.body:
623 return self.line_for_node(node.body[0]) 630 return self.line_for_node(node.body[0])
624 else: 631 else:
625 # Empty modules have no line number, they always start at 1. 632 # Empty modules have no line number, they always start at 1.
626 return 1 633 return 1
627 634
628 # The node types that just flow to the next node with no complications. 635 # The node types that just flow to the next node with no complications.
629 OK_TO_DEFAULT = set([ 636 OK_TO_DEFAULT = {
630 "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global", 637 "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global",
631 "Import", "ImportFrom", "Nonlocal", "Pass", "Print", 638 "Import", "ImportFrom", "Nonlocal", "Pass", "Print",
632 ]) 639 }
633 640
634 @contract(returns='ArcStarts') 641 @contract(returns='ArcStarts')
635 def add_arcs(self, node): 642 def add_arcs(self, node):
636 """Add the arcs for `node`. 643 """Add the arcs for `node`.
637 644
659 if 0: 666 if 0:
660 if node_name not in self.OK_TO_DEFAULT: 667 if node_name not in self.OK_TO_DEFAULT:
661 print("*** Unhandled: {}".format(node)) 668 print("*** Unhandled: {}".format(node))
662 669
663 # Default for simple statements: one exit from this node. 670 # Default for simple statements: one exit from this node.
664 return set([ArcStart(self.line_for_node(node))]) 671 return {ArcStart(self.line_for_node(node))}
665 672
666 @one_of("from_start, prev_starts") 673 @one_of("from_start, prev_starts")
667 @contract(returns='ArcStarts') 674 @contract(returns='ArcStarts')
668 def add_body_arcs(self, body, from_start=None, prev_starts=None): 675 def add_body_arcs(self, body, from_start=None, prev_starts=None):
669 """Add arcs for the body of a compound statement. 676 """Add arcs for the body of a compound statement.
675 682
676 Returns a set of ArcStarts, the exits from this body. 683 Returns a set of ArcStarts, the exits from this body.
677 684
678 """ 685 """
679 if prev_starts is None: 686 if prev_starts is None:
680 prev_starts = set([from_start]) 687 prev_starts = {from_start}
681 for body_node in body: 688 for body_node in body:
682 lineno = self.line_for_node(body_node) 689 lineno = self.line_for_node(body_node)
683 first_line = self.multiline.get(lineno, lineno) 690 first_line = self.multiline.get(lineno, lineno)
684 if first_line not in self.statements: 691 if first_line not in self.statements:
685 body_node = self.find_non_missing_node(body_node) 692 body_node = self.find_non_missing_node(body_node)
888 for lineno in range(last+1, body_start): 895 for lineno in range(last+1, body_start):
889 if lineno in self.statements: 896 if lineno in self.statements:
890 self.add_arc(last, lineno) 897 self.add_arc(last, lineno)
891 last = lineno 898 last = lineno
892 # The body is handled in collect_arcs. 899 # The body is handled in collect_arcs.
893 return set([ArcStart(last)]) 900 return {ArcStart(last)}
894 901
895 _handle__ClassDef = _handle_decorated 902 _handle__ClassDef = _handle_decorated
896 903
897 @contract(returns='ArcStarts') 904 @contract(returns='ArcStarts')
898 def _handle__Continue(self, node): 905 def _handle__Continue(self, node):
982 try_block.handler_start = None 989 try_block.handler_start = None
983 if node.handlers: 990 if node.handlers:
984 # If there are `except` clauses, then raises in the try body 991 # If there are `except` clauses, then raises in the try body
985 # will already jump to them. Start this set over for raises in 992 # will already jump to them. Start this set over for raises in
986 # `except` and `else`. 993 # `except` and `else`.
987 try_block.raise_from = set([]) 994 try_block.raise_from = set()
988 else: 995 else:
989 self.block_stack.pop() 996 self.block_stack.pop()
990 997
991 handler_exits = set() 998 handler_exits = set()
992 999
1077 causes = [] 1084 causes = []
1078 for start in sorted(starts): 1085 for start in sorted(starts):
1079 if start.cause is not None: 1086 if start.cause is not None:
1080 causes.append(start.cause.format(lineno=start.lineno)) 1087 causes.append(start.cause.format(lineno=start.lineno))
1081 cause = " or ".join(causes) 1088 cause = " or ".join(causes)
1082 exits = set(ArcStart(xit.lineno, cause) for xit in exits) 1089 exits = {ArcStart(xit.lineno, cause) for xit in exits}
1083 return exits 1090 return exits
1084 1091
1085 @contract(returns='ArcStarts') 1092 @contract(returns='ArcStarts')
1086 def _handle__TryExcept(self, node): 1093 def _handle__TryExcept(self, node):
1087 # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get 1094 # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
1107 1114
1108 return self._handle__Try(node) 1115 return self._handle__Try(node)
1109 1116
1110 @contract(returns='ArcStarts') 1117 @contract(returns='ArcStarts')
1111 def _handle__While(self, node): 1118 def _handle__While(self, node):
1119 start = to_top = self.line_for_node(node.test)
1112 constant_test = self.is_constant_expr(node.test) 1120 constant_test = self.is_constant_expr(node.test)
1113 start = to_top = self.line_for_node(node.test) 1121 top_is_body0 = False
1114 if constant_test and (env.PY3 or constant_test == "Num"): 1122 if constant_test and (env.PY3 or constant_test == "Num"):
1123 top_is_body0 = True
1124 if env.PYBEHAVIOR.keep_constant_test:
1125 top_is_body0 = False
1126 if top_is_body0:
1115 to_top = self.line_for_node(node.body[0]) 1127 to_top = self.line_for_node(node.body[0])
1116 self.block_stack.append(LoopBlock(start=to_top)) 1128 self.block_stack.append(LoopBlock(start=to_top))
1117 from_start = ArcStart(start, cause="the condition on line {lineno} was never true") 1129 from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
1118 exits = self.add_body_arcs(node.body, from_start=from_start) 1130 exits = self.add_body_arcs(node.body, from_start=from_start)
1119 for xit in exits: 1131 for xit in exits:

eric ide

mercurial