-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathdiagnose.py
187 lines (150 loc) · 5.44 KB
/
diagnose.py
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
"""Textual CLI command code to print diagnostic information."""
from __future__ import annotations
import os
import platform
import sys
from functools import singledispatch
from typing import Any
from importlib.metadata import version
from rich.console import Console, ConsoleDimensions
def _section(title: str, values: dict[str, str]) -> None:
"""Print a collection of named values within a titled section.
Args:
title: The title for the section.
values: The values to print out.
"""
max_name = max(map(len, values.keys()))
max_value = max(map(len, values.values()))
print(f"## {title}")
print()
print(f"| {'Name':{max_name}} | {'Value':{max_value}} |")
print(f"|-{'-' * max_name}-|-{'-'*max_value}-|")
for name, value in values.items():
print(f"| {name:{max_name}} | {value:{max_value}} |")
print()
def _versions() -> None:
"""Print useful version numbers."""
_section("Versions", {"Textual": version("textual"), "Rich": version("rich")})
def _python() -> None:
"""Print information about Python."""
_section(
"Python",
{
"Version": platform.python_version(),
"Implementation": platform.python_implementation(),
"Compiler": platform.python_compiler(),
"Executable": sys.executable,
},
)
def _os() -> None:
_section(
"Operating System",
{
"System": platform.system(),
"Release": platform.release(),
"Version": platform.version(),
},
)
def _guess_term() -> str:
"""Try and guess which terminal is being used.
Returns:
The best guess at the name of the terminal.
"""
# Look in a couple of generic locations for the name of the terminal.
term_program = os.environ.get("TERM_PROGRAM") or os.environ.get("TERMINAL_NAME")
if term_program is None:
# Seems we couldn't get it that way. Let's check for some of the
# more common terminal signatures.
if "ALACRITTY_WINDOW_ID" in os.environ:
term_program = "Alacritty"
elif "KITTY_PID" in os.environ:
term_program = "Kitty"
elif "WT_SESSION" in os.environ:
term_program = "Windows Terminal"
elif "INSIDE_EMACS" in os.environ and os.environ["INSIDE_EMACS"]:
term_program = (
f"GNU Emacs {' '.join(os.environ['INSIDE_EMACS'].split(','))}"
)
elif "JEDITERM_SOURCE_ARGS" in os.environ:
term_program = "PyCharm"
elif "GNOME_TERMINAL_SCREEN" in os.environ:
term_program = "GNOME Terminal"
elif "XTERM_VERSION" in os.environ:
term_program = os.environ.get("XTERM_VERSION") or "XTerm"
elif "TERMINATOR_UUID" in os.environ:
term_program = "Terminator"
elif "KONSOLE_VERSION" in os.environ:
term_program = (
f"Konsole {os.environ.get('KONSOLE_VERSION')}"
if os.environ.get("KONSOLE_VERSION")
else "Konsole"
)
else:
# See if we can pull out some sort of version information too.
term_version = os.environ.get("TERM_PROGRAM_VERSION") or os.environ.get(
"TERMINAL_VERSION_STRING"
)
if term_version is not None:
term_program = f"{term_program} ({term_version})"
# Seems we can't work this out.
if term_program is None:
term_program = "*Unknown*"
# Check for running under screen. As you look at this you might think
# "what about tmux too?" -- good point; but we'll be picking up tmux as
# the terminal type, because of how it takes over TERM_PROGRAM.
if "STY" in os.environ:
term_program += " (inside screen)"
return term_program
def _env(var_name: str) -> str:
"""Get a representation of an environment variable.
Args:
var_name: The name of the variable to get.
Returns:
The value, or an indication that it isn't set.
"""
return os.environ.get(var_name, "*Not set*")
def _term() -> None:
"""Print information about the terminal."""
_section(
"Terminal",
{
"Terminal Application": _guess_term(),
"TERM": _env("TERM"),
"COLORTERM": _env("COLORTERM"),
"FORCE_COLOR": _env("FORCE_COLOR"),
"NO_COLOR": _env("NO_COLOR"),
},
)
@singledispatch
def _str_rich(value: Any) -> str:
"""Convert a rich console option to a string.
Args:
value: The value to convert to a string.
Returns:
The string version of the value for output
"""
return str(value)
@_str_rich.register
def _(value: ConsoleDimensions) -> str:
return f"width={value.width}, height={value.height}"
def _console() -> None:
"""Print The Rich console options."""
_section(
"Rich Console options",
{k: _str_rich(v) for k, v in Console().options.__dict__.items()},
)
def diagnose() -> None:
"""Print information about Textual and its environment to help diagnose problems."""
print(
"<!-- This is valid Markdown, please paste the following directly in to a GitHub issue -->"
)
print("# Textual Diagnostics")
print()
_versions()
_python()
_os()
_term()
_console()
# TODO: Recommended changes. Given all of the above, make any useful
# recommendations to the user (eg: don't use Windows console, use
# Windows Terminal; don't use macOS Terminal.app, etc).