-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlibpython_ui.py
More file actions
110 lines (82 loc) · 3.27 KB
/
libpython_ui.py
File metadata and controls
110 lines (82 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import functools
import os
import gdb
import pygments
import pygments.lexers
import pygments.formatters
import libpython
import libpython_extensions
import tui_windows
def highlight_python(content: bytes) -> bytes:
"""
Applies Python syntax highlighting and prepends line numbers to provided content.
"""
return pygments.highlight(
content, pygments.lexers.PythonLexer(), pygments.formatters.TerminalFormatter(linenos=True)
)
@functools.cache
def get_highlighted_file_content(filename: str) -> str:
"""
Returns the content of the Python source file with syntax highlighting.
"""
with libpython_extensions.PythonSubstitutePath.open(os.fsencode(filename), "r") as f:
content = f.read()
return highlight_python(content)
def get_filename_and_line() -> tuple[str, int]:
"""
Returns the path to the current Python source file and the current line number.
"""
# py-list requires an actual PyEval_EvalFrameEx frame:
frame = libpython.Frame.get_selected_bytecode_frame()
if not frame:
raise gdb.error("Unable to locate gdb frame for python bytecode interpreter")
pyop = frame.get_pyop()
if not pyop or pyop.is_optimized_out():
raise gdb.error(libpython.UNABLE_READ_INFO_PYTHON_FRAME)
filename = pyop.filename()
lineno = pyop.current_line_num()
if lineno is None:
raise gdb.error("Unable to read python frame line number")
return filename, lineno
@tui_windows.register_window("python-source")
class PythonSourceWindow(tui_windows.ScrollableWindow):
title = "Python Source"
def get_content(self):
filename, line = get_filename_and_line()
lines = get_highlighted_file_content(filename).splitlines()
prefixed_lines = [(" > " if i == line else " ") + l for i, l in enumerate(lines, start=1)]
# Set vertical scroll offset to center the current line
half_window_height = self._tui_window.height // 2
self.vscroll_offset = line - half_window_height
return "\n".join(prefixed_lines)
@tui_windows.register_window("python-backtrace")
class PythonBacktraceWindow(tui_windows.ScrollableWindow):
title = "Python Backtrace"
def get_content(self):
return gdb.execute("py-bt", to_string=True)
@tui_windows.register_window("python-locals")
class PythonLocalsWindow(tui_windows.ScrollableWindow):
title = "Local Python Variables"
def get_content(self):
return gdb.execute("py-locals", to_string=True)
@tui_windows.register_window("python-bytecode")
class PythonBytecodeWindow(tui_windows.ScrollableWindow):
title = "Python Bytecode"
def get_lines(self) -> list[str]:
lines = gdb.execute("py-dis", to_string=True).splitlines()
# Set vertical scroll offset to center the current line
for index, line in enumerate(lines, start=1):
if "-->" in line:
half_window_height = self._tui_window.height // 2
self.vscroll_offset = index - half_window_height
return lines
# Define a layout with all Python windows
gdb.execute(
" ".join(
(
"tui new-layout python",
"{-horizontal {python-source 2 status 1 cmd 1} 3",
"{python-locals 1 python-backtrace 1 python-bytecode 1 timeline 1} 2} 1",
)
)
)