def _verify_list_filter(self, filter: List, argument_name: str) -> None: # Check this because filter is user input if not isinstance(filter, list): raise UserError(f"'{argument_name}' should be a list.") if not filter: raise UserError(f"'{argument_name}' should be non-empty.")
def parents(self) -> None: self._verify_entrypoint_selected() current_trace_frame = self.trace_tuples[ self.current_trace_frame_index ].trace_frame # Don't allow calling from the leaf node in a trace. Instead, call # parents() from the placeholder of the caller of the leaf node. if self._is_leaf(current_trace_frame): raise UserError("Try running from a non-leaf node.") with self.db.make_session() as session: parent_trace_frames = self._next_trace_frames( session, current_trace_frame, backwards=True ) if len(parent_trace_frames) == 0: print( f"No parents calling [{current_trace_frame.callee} " f": {current_trace_frame.callee_port}]." ) return parent_trace_frame = self._select_parent_trace_frame(parent_trace_frames) if not parent_trace_frame: return self._update_trace_tuples_new_parent(parent_trace_frame) self.trace()
def _verify_entrypoint_selected(self) -> None: assert self.current_issue_id == -1 or self.current_frame_id == -1 if self.current_issue_id == -1 and self.current_frame_id == -1: raise UserError( "Use 'set_issue(ID)' or 'set_frame(ID)' to select an" " entrypoint first.")
def jump(self, selected_number: int) -> None: """Jump to a specific trace frame in a trace. Parameters: selected_number: int the trace frame number from trace() output """ self._verify_entrypoint_selected() if selected_number < 1 or selected_number > len(self.trace_tuples): raise UserError( "Trace frame number out of bounds " f"(expected 1-{len(self.trace_tuples)} but got {selected_number})." ) self.current_trace_frame_index = selected_number - 1 self.trace()
def _select_parent_trace_frame( self, parent_trace_frames: List[TraceFrame] ) -> Optional[TraceFrame]: for i, parent in enumerate(parent_trace_frames): print(f"[{i + 1}] {parent.caller} : {parent.caller_port}") try: parent_number = click.prompt( f"\nParent number (1-{len(parent_trace_frames)})", type=int ) if parent_number < 1 or parent_number > len(parent_trace_frames): raise UserError("Out of bounds.") print() return parent_trace_frames[parent_number - 1] # pyre-ignore except click.Abort: print("\nParent not selected.") pass
def branch(self, selected_number: int) -> None: """Selects a branch when there are multiple possible traces to follow. The trace output that follows includes the new branch and its children frames. Parameters: selected_number: int branch number from expand() output """ self._verify_entrypoint_selected() self._verify_multiple_branches() with self.db.make_session() as session: branches = self._get_trace_frame_branches(session) if selected_number < 1 or selected_number > len(branches): raise UserError( "Branch number out of bounds " f"(expected 1-{len(branches)} but got {selected_number})." ) new_navigation = self._navigate_trace_frames( session, branches, selected_number - 1 ) new_trace_tuples = self._create_trace_tuples(new_navigation) if self._is_before_root(): new_trace_tuples.reverse() self.trace_tuples = ( new_trace_tuples + self.trace_tuples[self.current_trace_frame_index + 1 :] ) # If length of prefix changes, it will change some indices trace_frame_index_delta = ( len(new_navigation) - self.current_trace_frame_index - 1 ) self.current_trace_frame_index += trace_frame_index_delta else: self.trace_tuples = ( self.trace_tuples[: self.current_trace_frame_index] + new_trace_tuples ) self.trace()
def frames( self, *, callers: Optional[List[str]] = None, kind: Optional[TraceKind] = None ): """Display trace frames independent of the current issue. Parameters (all optional): callers: list[str] filter traces by this caller name kind: precondition|postcondition the type of trace frames to show Sample usage: frames(callers=["module.function"], kind=postcondition) String filters support LIKE wildcards (%, _) from SQL: % matches anything (like .* in regex) _ matches 1 character (like . in regex) """ with self.db.make_session() as session: query = session.query(TraceFrame).filter( TraceFrame.run_id == self.current_run_id ) if callers is not None: self._verify_list_filter(callers, "callers") query = self._add_list_filter_to_query( callers, query, TraceFrame.caller ) if kind is not None: if kind not in {TraceKind.PRECONDITION, TraceKind.POSTCONDITION}: raise UserError( "Try 'frames(kind=postcondition)'" " or 'frames(kind=precondition)'." ) query = query.filter(TraceFrame.kind == kind) trace_frames = ( query.group_by(TraceFrame.id) .order_by(TraceFrame.caller, TraceFrame.callee) .all() ) self._output_trace_frames(self._group_trace_frames(trace_frames))
def _verify_multiple_branches(self) -> None: current_trace_tuple = self.trace_tuples[self.current_trace_frame_index] if current_trace_tuple.branches < 2: raise UserError("This trace frame has no alternate branches to take.")