• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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