DebugClients/Python/coverage/parser.py

changeset 6649
f1b3a73831c9
parent 6219
d6c795b5ce33
equal deleted inserted replaced
6648:c09e6c6006eb 6649:f1b3a73831c9
407 if byte_incr: 407 if byte_incr:
408 if line_num != last_line_num: 408 if line_num != last_line_num:
409 yield (byte_num, line_num) 409 yield (byte_num, line_num)
410 last_line_num = line_num 410 last_line_num = line_num
411 byte_num += byte_incr 411 byte_num += byte_incr
412 if env.PYVERSION >= (3, 6) and line_incr >= 0x80:
413 line_incr -= 0x100
412 line_num += line_incr 414 line_num += line_incr
413 if line_num != last_line_num: 415 if line_num != last_line_num:
414 yield (byte_num, line_num) 416 yield (byte_num, line_num)
415 417
416 def _find_statements(self): 418 def _find_statements(self):
501 def __init__(self, body): 503 def __init__(self, body):
502 self.body = body 504 self.body = body
503 self.lineno = body[0].lineno 505 self.lineno = body[0].lineno
504 506
505 507
508 # TODO: some add_arcs methods here don't add arcs, they return them. Rename them.
509 # TODO: the cause messages have too many commas.
510 # TODO: Shouldn't the cause messages join with "and" instead of "or"?
511
506 class AstArcAnalyzer(object): 512 class AstArcAnalyzer(object):
507 """Analyze source text with an AST to find executable code paths.""" 513 """Analyze source text with an AST to find executable code paths."""
508 514
509 @contract(text='unicode', statements=set) 515 @contract(text='unicode', statements=set)
510 def __init__(self, text, statements, multiline): 516 def __init__(self, text, statements, multiline):
542 node_name = node.__class__.__name__ 548 node_name = node.__class__.__name__
543 code_object_handler = getattr(self, "_code_object__" + node_name, None) 549 code_object_handler = getattr(self, "_code_object__" + node_name, None)
544 if code_object_handler is not None: 550 if code_object_handler is not None:
545 code_object_handler(node) 551 code_object_handler(node)
546 552
553 @contract(start=int, end=int)
547 def add_arc(self, start, end, smsg=None, emsg=None): 554 def add_arc(self, start, end, smsg=None, emsg=None):
548 """Add an arc, including message fragments to use if it is missing.""" 555 """Add an arc, including message fragments to use if it is missing."""
549 if self.debug: # pragma: debugging 556 if self.debug: # pragma: debugging
550 print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg)) 557 print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg))
551 print(short_stack(limit=6)) 558 print(short_stack(limit=6))
570 if handler is not None: 577 if handler is not None:
571 return handler(node) 578 return handler(node)
572 else: 579 else:
573 return node.lineno 580 return node.lineno
574 581
582 def _line_decorated(self, node):
583 """Compute first line number for things that can be decorated (classes and functions)."""
584 lineno = node.lineno
585 if env.PYBEHAVIOR.trace_decorated_def:
586 if node.decorator_list:
587 lineno = node.decorator_list[0].lineno
588 return lineno
589
575 def _line__Assign(self, node): 590 def _line__Assign(self, node):
576 return self.line_for_node(node.value) 591 return self.line_for_node(node.value)
592
593 _line__ClassDef = _line_decorated
577 594
578 def _line__Dict(self, node): 595 def _line__Dict(self, node):
579 # Python 3.5 changed how dict literals are made. 596 # Python 3.5 changed how dict literals are made.
580 if env.PYVERSION >= (3, 5) and node.keys: 597 if env.PYVERSION >= (3, 5) and node.keys:
581 if node.keys[0] is not None: 598 if node.keys[0] is not None:
584 # Unpacked dict literals `{**{'a':1}}` have None as the key, 601 # Unpacked dict literals `{**{'a':1}}` have None as the key,
585 # use the value in that case. 602 # use the value in that case.
586 return node.values[0].lineno 603 return node.values[0].lineno
587 else: 604 else:
588 return node.lineno 605 return node.lineno
606
607 _line__FunctionDef = _line_decorated
589 608
590 def _line__List(self, node): 609 def _line__List(self, node):
591 if node.elts: 610 if node.elts:
592 return self.line_for_node(node.elts[0]) 611 return self.line_for_node(node.elts[0])
593 else: 612 else:
688 node = missing_fn(node) 707 node = missing_fn(node)
689 else: 708 else:
690 node = None 709 node = None
691 return node 710 return node
692 711
712 # Missing nodes: _missing__*
713 #
714 # Entire statements can be optimized away by Python. They will appear in
715 # the AST, but not the bytecode. These functions are called (by
716 # find_non_missing_node) to find a node to use instead of the missing
717 # node. They can return None if the node should truly be gone.
718
693 def _missing__If(self, node): 719 def _missing__If(self, node):
694 # If the if-node is missing, then one of its children might still be 720 # If the if-node is missing, then one of its children might still be
695 # here, but not both. So return the first of the two that isn't missing. 721 # here, but not both. So return the first of the two that isn't missing.
696 # Use a NodeList to hold the clauses as a single node. 722 # Use a NodeList to hold the clauses as a single node.
697 non_missing = self.find_non_missing_node(NodeList(node.body)) 723 non_missing = self.find_non_missing_node(NodeList(node.body))
715 return None 741 return None
716 if len(non_missing_children) == 1: 742 if len(non_missing_children) == 1:
717 return non_missing_children[0] 743 return non_missing_children[0]
718 return NodeList(non_missing_children) 744 return NodeList(non_missing_children)
719 745
746 def _missing__While(self, node):
747 body_nodes = self.find_non_missing_node(NodeList(node.body))
748 if not body_nodes:
749 return None
750 # Make a synthetic While-true node.
751 new_while = ast.While()
752 new_while.lineno = body_nodes.lineno
753 new_while.test = ast.Name()
754 new_while.test.lineno = body_nodes.lineno
755 new_while.test.id = "True"
756 new_while.body = body_nodes.body
757 new_while.orelse = None
758 return new_while
759
720 def is_constant_expr(self, node): 760 def is_constant_expr(self, node):
721 """Is this a compile-time constant?""" 761 """Is this a compile-time constant?"""
722 node_name = node.__class__.__name__ 762 node_name = node.__class__.__name__
723 if node_name in ["NameConstant", "Num"]: 763 if node_name in ["Constant", "NameConstant", "Num"]:
724 return "Num" 764 return "Num"
725 elif node_name == "Name": 765 elif node_name == "Name":
726 if node.id in ["True", "False", "None", "__debug__"]: 766 if node.id in ["True", "False", "None", "__debug__"]:
727 return "Name" 767 return "Name"
728 return None 768 return None
803 843
804 844
805 # Handlers: _handle__* 845 # Handlers: _handle__*
806 # 846 #
807 # Each handler deals with a specific AST node type, dispatched from 847 # Each handler deals with a specific AST node type, dispatched from
808 # add_arcs. Each deals with a particular kind of node type, and returns 848 # add_arcs. Handlers return the set of exits from that node, and can
809 # the set of exits from that node. These functions mirror the Python 849 # also call self.add_arc to record arcs they find. These functions mirror
810 # semantics of each syntactic construct. See the docstring for add_arcs to 850 # the Python semantics of each syntactic construct. See the docstring
811 # understand the concept of exits from a node. 851 # for add_arcs to understand the concept of exits from a node.
812 852
813 @contract(returns='ArcStarts') 853 @contract(returns='ArcStarts')
814 def _handle__Break(self, node): 854 def _handle__Break(self, node):
815 here = self.line_for_node(node) 855 here = self.line_for_node(node)
816 break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed") 856 break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed")
818 return set() 858 return set()
819 859
820 @contract(returns='ArcStarts') 860 @contract(returns='ArcStarts')
821 def _handle_decorated(self, node): 861 def _handle_decorated(self, node):
822 """Add arcs for things that can be decorated (classes and functions).""" 862 """Add arcs for things that can be decorated (classes and functions)."""
823 last = self.line_for_node(node) 863 main_line = last = node.lineno
824 if node.decorator_list: 864 if node.decorator_list:
865 if env.PYBEHAVIOR.trace_decorated_def:
866 last = None
825 for dec_node in node.decorator_list: 867 for dec_node in node.decorator_list:
826 dec_start = self.line_for_node(dec_node) 868 dec_start = self.line_for_node(dec_node)
827 if dec_start != last: 869 if last is not None and dec_start != last:
828 self.add_arc(last, dec_start) 870 self.add_arc(last, dec_start)
829 last = dec_start 871 last = dec_start
872 if env.PYBEHAVIOR.trace_decorated_def:
873 self.add_arc(last, main_line)
874 last = main_line
830 # The definition line may have been missed, but we should have it 875 # The definition line may have been missed, but we should have it
831 # in `self.statements`. For some constructs, `line_for_node` is 876 # in `self.statements`. For some constructs, `line_for_node` is
832 # not what we'd think of as the first line in the statement, so map 877 # not what we'd think of as the first line in the statement, so map
833 # it to the first one. 878 # it to the first one.
834 if node.body: 879 if node.body:
966 ) 1011 )
967 1012
968 final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from) 1013 final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from)
969 1014
970 if try_block.break_from: 1015 if try_block.break_from:
971 self.process_break_exits( 1016 if env.PYBEHAVIOR.finally_jumps_back:
972 self._combine_finally_starts(try_block.break_from, final_exits) 1017 for break_line in try_block.break_from:
973 ) 1018 lineno = break_line.lineno
1019 cause = break_line.cause.format(lineno=lineno)
1020 for final_exit in final_exits:
1021 self.add_arc(final_exit.lineno, lineno, cause)
1022 breaks = try_block.break_from
1023 else:
1024 breaks = self._combine_finally_starts(try_block.break_from, final_exits)
1025 self.process_break_exits(breaks)
1026
974 if try_block.continue_from: 1027 if try_block.continue_from:
975 self.process_continue_exits( 1028 if env.PYBEHAVIOR.finally_jumps_back:
976 self._combine_finally_starts(try_block.continue_from, final_exits) 1029 for continue_line in try_block.continue_from:
977 ) 1030 lineno = continue_line.lineno
1031 cause = continue_line.cause.format(lineno=lineno)
1032 for final_exit in final_exits:
1033 self.add_arc(final_exit.lineno, lineno, cause)
1034 continues = try_block.continue_from
1035 else:
1036 continues = self._combine_finally_starts(try_block.continue_from, final_exits)
1037 self.process_continue_exits(continues)
1038
978 if try_block.raise_from: 1039 if try_block.raise_from:
979 self.process_raise_exits( 1040 self.process_raise_exits(
980 self._combine_finally_starts(try_block.raise_from, final_exits) 1041 self._combine_finally_starts(try_block.raise_from, final_exits)
981 ) 1042 )
1043
982 if try_block.return_from: 1044 if try_block.return_from:
983 self.process_return_exits( 1045 if env.PYBEHAVIOR.finally_jumps_back:
984 self._combine_finally_starts(try_block.return_from, final_exits) 1046 for return_line in try_block.return_from:
985 ) 1047 lineno = return_line.lineno
1048 cause = return_line.cause.format(lineno=lineno)
1049 for final_exit in final_exits:
1050 self.add_arc(final_exit.lineno, lineno, cause)
1051 returns = try_block.return_from
1052 else:
1053 returns = self._combine_finally_starts(try_block.return_from, final_exits)
1054 self.process_return_exits(returns)
986 1055
987 if exits: 1056 if exits:
988 # The finally clause's exits are only exits for the try block 1057 # The finally clause's exits are only exits for the try block
989 # as a whole if the try block had some exits to begin with. 1058 # as a whole if the try block had some exits to begin with.
990 exits = final_exits 1059 exits = final_exits

eric ide

mercurial