1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Copyright (c) 2024 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18from collections.abc import Sequence 19from pathlib import Path 20from typing import Iterable, List, Optional, Set 21 22import trio 23from rich import box 24from rich.columns import Columns 25from rich.console import Group, RenderableType 26from rich.panel import Panel 27from rich.pretty import Pretty 28from rich.syntax import Syntax 29from rich.table import Column, Table 30from rich.text import Text 31 32from .debug_types import DEFAULT_DEPTH, Frame, Paused, Scope 33 34 35def source_file(src_file: Path, **kwargs) -> Syntax: 36 return Syntax.from_path( 37 path=str(src_file), 38 lexer="ts", 39 start_line=0, 40 line_numbers=True, 41 **kwargs, 42 ) 43 44 45async def _add_rows(table: Table, scope: Scope): 46 table.add_row( 47 f":red_triangle_pointed_down: [table.header]{scope.data.type_}[/table.header]", 48 Text(f"{scope.data.object_.description}", style="repr.str"), 49 ) 50 props = await scope.mirror_variables() 51 if len(props): 52 for k, v in props.items(): 53 table.add_row(k, Pretty(v)) 54 else: 55 table.add_row("none", "", style="italic") 56 57 58async def frame_layout( 59 frame: Frame, 60 scopes: Optional[Set[str]] = None, 61 skip_scopes: Sequence[str] = (), 62) -> RenderableType: 63 table = Table( 64 Column("name"), 65 Column("value"), 66 box=box.SIMPLE, 67 show_edge=False, 68 ) 69 this = frame.this() 70 if this is not None: 71 table.add_row("this", Pretty(await this.mirror_value(depth=DEFAULT_DEPTH))) 72 else: 73 await trio.lowlevel.checkpoint() 74 for scope in frame.scopes(): 75 if (scopes is None or scope.data.type_ in scopes) and (scope.data.type_ not in skip_scopes): 76 await _add_rows(table, scope) 77 else: 78 table.add_row( 79 f":red_triangle_pointed_up: [table.header]{scope.data.type_}[/table.header]", 80 f"[repr.str]{scope.data.object_.description}[/repr.str]", 81 ) 82 83 title = "[inspect.class]%s[/inspect.class] [repr.filename]%s[/repr.filename]:[repr.number]%s[/repr.number]" % ( 84 frame.data.function_name, 85 Path(frame.data.url).name, 86 frame.data.location.line_number, 87 ) 88 return Panel(table, title=title, title_align="left") 89 90 91async def frames_layout( 92 call_frames: Iterable[Frame], 93 scopes: Optional[Set[str]] = None, 94 skip_scopes: Sequence[str] = (), 95) -> List[RenderableType]: 96 97 await trio.lowlevel.checkpoint() 98 return [await frame_layout(f, scopes, skip_scopes) for f in call_frames] 99 100 101async def paused_layout( 102 paused: Paused, 103 url: Path, 104 scopes: Optional[Set[str]] = None, 105 skip_scopes: Sequence[str] = (), 106) -> RenderableType: 107 frames = await frames_layout(paused.frames(), scopes=scopes, skip_scopes=skip_scopes) 108 highlight_lines = [f.location.line_number for f in paused.data.call_frames] 109 code = source_file(url, highlight_lines=highlight_lines) 110 return Columns([code, Group(*frames)]) 111