xmyshell 0.2.16__tar.gz → 0.2.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xmyshell-0.2.16/xmyshell.egg-info → xmyshell-0.2.18}/PKG-INFO +10 -1
- {xmyshell-0.2.16 → xmyshell-0.2.18}/README.md +9 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/pyproject.toml +1 -1
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/environment.py +1 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/kernel.py +1 -1
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/main.py +0 -2
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/meta.py +3 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18/xmyshell.egg-info}/PKG-INFO +10 -1
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell.egg-info/SOURCES.txt +0 -1
- xmyshell-0.2.16/xmyshell/lexer.py +0 -259
- {xmyshell-0.2.16 → xmyshell-0.2.18}/LICENSE +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/setup.cfg +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/__init__.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/__main__.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/completer.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/data/commands.json +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/init.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/injection.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/keybindings.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/stubs/__init__.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/stubs/__init__.pyi +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/stubs/py.typed +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/themes.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/update.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell/utils.py +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell.egg-info/dependency_links.txt +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell.egg-info/entry_points.txt +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell.egg-info/requires.txt +0 -0
- {xmyshell-0.2.16 → xmyshell-0.2.18}/xmyshell.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xmyshell
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.18
|
|
4
4
|
Summary: XmyShell is a shell-like tool built on top of prompt_toolkit which provides a persistent Python runtime.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -163,6 +163,15 @@ The exit code of the last command.
|
|
|
163
163
|
- `exec_duration`
|
|
164
164
|
The duration which last command took.
|
|
165
165
|
|
|
166
|
+
- `last_error`
|
|
167
|
+
The error raised from last Python operation (if exists).
|
|
168
|
+
|
|
169
|
+
- `last_traceback`
|
|
170
|
+
The traceback of last Python operation (if exists).
|
|
171
|
+
|
|
172
|
+
- `ans`
|
|
173
|
+
The result from last print command.
|
|
174
|
+
|
|
166
175
|
- `SHOW_WELCOME`
|
|
167
176
|
Toggle whether to show welcome message when staring.
|
|
168
177
|
|
|
@@ -153,6 +153,15 @@ The exit code of the last command.
|
|
|
153
153
|
- `exec_duration`
|
|
154
154
|
The duration which last command took.
|
|
155
155
|
|
|
156
|
+
- `last_error`
|
|
157
|
+
The error raised from last Python operation (if exists).
|
|
158
|
+
|
|
159
|
+
- `last_traceback`
|
|
160
|
+
The traceback of last Python operation (if exists).
|
|
161
|
+
|
|
162
|
+
- `ans`
|
|
163
|
+
The result from last print command.
|
|
164
|
+
|
|
156
165
|
- `SHOW_WELCOME`
|
|
157
166
|
Toggle whether to show welcome message when staring.
|
|
158
167
|
|
|
@@ -189,10 +189,10 @@ def xmyshell_raw_command(cmd_line: str) -> int | None:
|
|
|
189
189
|
result = eval(expr, dict(os.environ), namespace)
|
|
190
190
|
print(result)
|
|
191
191
|
namespace["last_error"] = None
|
|
192
|
+
namespace["ans"] = result
|
|
192
193
|
return 0
|
|
193
194
|
except Exception as e:
|
|
194
195
|
import traceback
|
|
195
|
-
|
|
196
196
|
namespace["last_error"] = e
|
|
197
197
|
namespace["last_traceback"] = traceback.format_exc()
|
|
198
198
|
pyerror(f"{type(e).__name__}: {e}")
|
|
@@ -19,7 +19,6 @@ from .keybindings import (
|
|
|
19
19
|
)
|
|
20
20
|
from .init import xmyshell_init
|
|
21
21
|
from .kernel import pyeval, xmyshell
|
|
22
|
-
from .lexer import XmyShellLexer
|
|
23
22
|
from .injection import inject
|
|
24
23
|
|
|
25
24
|
def xmyshell_main() -> None:
|
|
@@ -35,7 +34,6 @@ def xmyshell_main() -> None:
|
|
|
35
34
|
complete_in_thread=True,
|
|
36
35
|
complete_while_typing=True,
|
|
37
36
|
key_bindings=bindings,
|
|
38
|
-
lexer=XmyShellLexer(),
|
|
39
37
|
)
|
|
40
38
|
session.default_buffer.on_cursor_position_changed += on_cursor_position_changed
|
|
41
39
|
session.default_buffer.on_text_changed += on_text_changed
|
|
@@ -57,6 +57,9 @@ Helper Functions:
|
|
|
57
57
|
Built-in variables:
|
|
58
58
|
exit_code - The exit code of the last command.
|
|
59
59
|
exec_duration - The duration which last command took.
|
|
60
|
+
last_error - The error raised from last Python operation (if exists).
|
|
61
|
+
last_traceback - The traceback of last Python operation (if exists).
|
|
62
|
+
ans - The result from last print command.
|
|
60
63
|
HOME_DIR - The home directory, similar to "~".
|
|
61
64
|
PROFILE - The path of config file.
|
|
62
65
|
SHOW_WELCOME - Toggle whether to show welcome message.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xmyshell
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.18
|
|
4
4
|
Summary: XmyShell is a shell-like tool built on top of prompt_toolkit which provides a persistent Python runtime.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -163,6 +163,15 @@ The exit code of the last command.
|
|
|
163
163
|
- `exec_duration`
|
|
164
164
|
The duration which last command took.
|
|
165
165
|
|
|
166
|
+
- `last_error`
|
|
167
|
+
The error raised from last Python operation (if exists).
|
|
168
|
+
|
|
169
|
+
- `last_traceback`
|
|
170
|
+
The traceback of last Python operation (if exists).
|
|
171
|
+
|
|
172
|
+
- `ans`
|
|
173
|
+
The result from last print command.
|
|
174
|
+
|
|
166
175
|
- `SHOW_WELCOME`
|
|
167
176
|
Toggle whether to show welcome message when staring.
|
|
168
177
|
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
import keyword
|
|
3
|
-
import inspect
|
|
4
|
-
from collections.abc import Callable
|
|
5
|
-
from prompt_toolkit.lexers import Lexer
|
|
6
|
-
from prompt_toolkit.formatted_text import FormattedText
|
|
7
|
-
from .environment import namespace
|
|
8
|
-
|
|
9
|
-
def _is_number_literal(s: str) -> bool:
|
|
10
|
-
try:
|
|
11
|
-
import ast # lazy
|
|
12
|
-
result = ast.literal_eval(s)
|
|
13
|
-
return isinstance(result, (int, float, complex))
|
|
14
|
-
except (ValueError, SyntaxError):
|
|
15
|
-
return False
|
|
16
|
-
|
|
17
|
-
def _shell_split_keep_sep(text: str) -> list[str]:
|
|
18
|
-
parts: list[str] = []
|
|
19
|
-
quote: str | None = None
|
|
20
|
-
i = 0
|
|
21
|
-
n = len(text)
|
|
22
|
-
seg_start = 0
|
|
23
|
-
|
|
24
|
-
def flush(end: int) -> None:
|
|
25
|
-
nonlocal seg_start
|
|
26
|
-
if end > seg_start:
|
|
27
|
-
parts.append(text[seg_start:end])
|
|
28
|
-
seg_start = end
|
|
29
|
-
|
|
30
|
-
while i < n:
|
|
31
|
-
ch = text[i]
|
|
32
|
-
if quote is None:
|
|
33
|
-
if ch in " \t":
|
|
34
|
-
flush(i)
|
|
35
|
-
j = i
|
|
36
|
-
while j < n and text[j] in " \t":
|
|
37
|
-
j += 1
|
|
38
|
-
parts.append(text[i:j])
|
|
39
|
-
seg_start = j
|
|
40
|
-
i = j
|
|
41
|
-
continue
|
|
42
|
-
elif ch == "\\" and i + 1 < n:
|
|
43
|
-
i += 2
|
|
44
|
-
continue
|
|
45
|
-
elif ch in ("'", '"'):
|
|
46
|
-
quote = ch
|
|
47
|
-
else:
|
|
48
|
-
if ch == quote:
|
|
49
|
-
quote = None
|
|
50
|
-
elif ch == "\\" and quote == '"' and i + 1 < n:
|
|
51
|
-
i += 2
|
|
52
|
-
continue
|
|
53
|
-
i += 1
|
|
54
|
-
flush(n)
|
|
55
|
-
return parts
|
|
56
|
-
|
|
57
|
-
def _split_with_braces_and_pipes(line: str) -> list[str]:
|
|
58
|
-
parts = []
|
|
59
|
-
outer_buf = []
|
|
60
|
-
inner_buf = []
|
|
61
|
-
depth = 0
|
|
62
|
-
i = 0
|
|
63
|
-
n = len(line)
|
|
64
|
-
|
|
65
|
-
while i < n:
|
|
66
|
-
ch = line[i]
|
|
67
|
-
|
|
68
|
-
if i + 1 < n and ((ch == '{' and line[i+1] == '{') or (ch == '}' and line[i+1] == '}')):
|
|
69
|
-
seq = line[i:i+2]
|
|
70
|
-
if depth == 0:
|
|
71
|
-
outer_buf.extend(seq)
|
|
72
|
-
else:
|
|
73
|
-
inner_buf.extend(seq)
|
|
74
|
-
i += 2
|
|
75
|
-
continue
|
|
76
|
-
|
|
77
|
-
if ch == '{':
|
|
78
|
-
if depth == 0:
|
|
79
|
-
if outer_buf:
|
|
80
|
-
parts.append(''.join(outer_buf))
|
|
81
|
-
outer_buf = []
|
|
82
|
-
parts.append('{')
|
|
83
|
-
depth = 1
|
|
84
|
-
inner_buf = []
|
|
85
|
-
else:
|
|
86
|
-
inner_buf.append(ch)
|
|
87
|
-
depth += 1
|
|
88
|
-
i += 1
|
|
89
|
-
|
|
90
|
-
elif ch == '}':
|
|
91
|
-
if depth == 0:
|
|
92
|
-
outer_buf.append(ch)
|
|
93
|
-
else:
|
|
94
|
-
depth -= 1
|
|
95
|
-
if depth == 0:
|
|
96
|
-
parts.append(''.join(inner_buf))
|
|
97
|
-
parts.append('}')
|
|
98
|
-
inner_buf = []
|
|
99
|
-
else:
|
|
100
|
-
inner_buf.append(ch)
|
|
101
|
-
i += 1
|
|
102
|
-
|
|
103
|
-
elif ch == '|':
|
|
104
|
-
if depth == 0:
|
|
105
|
-
if outer_buf:
|
|
106
|
-
parts.append(''.join(outer_buf))
|
|
107
|
-
outer_buf = []
|
|
108
|
-
parts.append('|')
|
|
109
|
-
else:
|
|
110
|
-
inner_buf.append(ch)
|
|
111
|
-
i += 1
|
|
112
|
-
|
|
113
|
-
else:
|
|
114
|
-
if depth == 0:
|
|
115
|
-
outer_buf.append(ch)
|
|
116
|
-
else:
|
|
117
|
-
inner_buf.append(ch)
|
|
118
|
-
i += 1
|
|
119
|
-
|
|
120
|
-
if outer_buf:
|
|
121
|
-
parts.append(''.join(outer_buf))
|
|
122
|
-
|
|
123
|
-
if depth > 0 and inner_buf:
|
|
124
|
-
parts.append(''.join(inner_buf))
|
|
125
|
-
|
|
126
|
-
return parts
|
|
127
|
-
|
|
128
|
-
def _lex_cmd(cmd: str, first_word: bool = True) -> list[tuple[str, str]]:
|
|
129
|
-
parts: list[str] = _shell_split_keep_sep(cmd)
|
|
130
|
-
result: list[tuple[str, str]] = []
|
|
131
|
-
for part in parts:
|
|
132
|
-
if part == "":
|
|
133
|
-
continue
|
|
134
|
-
if part.isspace():
|
|
135
|
-
result.append(("", part))
|
|
136
|
-
continue
|
|
137
|
-
if first_word:
|
|
138
|
-
result.append(("ansigreen bold", part))
|
|
139
|
-
first_word = False
|
|
140
|
-
continue
|
|
141
|
-
if part.startswith("-"):
|
|
142
|
-
result.append(("ansibrightblue", part))
|
|
143
|
-
continue
|
|
144
|
-
# if part in keyword.kwlist:
|
|
145
|
-
# result.append(("ansibrightmagenta bold", part))
|
|
146
|
-
# continue
|
|
147
|
-
if _is_number_literal(part):
|
|
148
|
-
result.append(("ansibrightblue", part))
|
|
149
|
-
continue
|
|
150
|
-
result.append(("", part))
|
|
151
|
-
return result
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
STYLE_KEYWORD = "ansibrightmagenta bold"
|
|
155
|
-
STYLE_NUMBER = "ansiblue"
|
|
156
|
-
STYLE_VARIABLE = "ansibrightcyan"
|
|
157
|
-
STYLE_FUNCTION = "ansibrightblue"
|
|
158
|
-
STYLE_CLASS = "ansibrightred"
|
|
159
|
-
|
|
160
|
-
TOKEN_PATTERN = re.compile(r'[a-zA-Z_]\w*|\d+\.?\d*')
|
|
161
|
-
|
|
162
|
-
def _lex_python(text: str) -> list[tuple[str, str]]:
|
|
163
|
-
result = []
|
|
164
|
-
pos = 0
|
|
165
|
-
|
|
166
|
-
for match in TOKEN_PATTERN.finditer(text):
|
|
167
|
-
start, end = match.span()
|
|
168
|
-
if start > pos:
|
|
169
|
-
result.append(("", text[pos:start]))
|
|
170
|
-
|
|
171
|
-
token = match.group()
|
|
172
|
-
|
|
173
|
-
if token.isdigit() or (token.replace('.', '').isdigit() and token.count('.') <= 1):
|
|
174
|
-
result.append((STYLE_NUMBER, token))
|
|
175
|
-
elif keyword.iskeyword(token):
|
|
176
|
-
result.append((STYLE_KEYWORD, token))
|
|
177
|
-
else:
|
|
178
|
-
if token in namespace:
|
|
179
|
-
obj = namespace[token]
|
|
180
|
-
if inspect.isclass(obj):
|
|
181
|
-
style = STYLE_CLASS
|
|
182
|
-
elif callable(obj):
|
|
183
|
-
style = STYLE_FUNCTION
|
|
184
|
-
else:
|
|
185
|
-
style = STYLE_VARIABLE
|
|
186
|
-
else:
|
|
187
|
-
style = STYLE_VARIABLE
|
|
188
|
-
result.append((style, token))
|
|
189
|
-
|
|
190
|
-
pos = end
|
|
191
|
-
|
|
192
|
-
if pos < len(text):
|
|
193
|
-
result.append(("", text[pos:]))
|
|
194
|
-
|
|
195
|
-
return result
|
|
196
|
-
|
|
197
|
-
def _lex_sh(line: str) -> list[tuple[str, str]]:
|
|
198
|
-
result: list[tuple[str, str]] = []
|
|
199
|
-
parts: list[str] = _split_with_braces_and_pipes(line)
|
|
200
|
-
in_expr: bool = False
|
|
201
|
-
first_word: bool = True
|
|
202
|
-
for part in parts:
|
|
203
|
-
if part.startswith("|"):
|
|
204
|
-
first_word = True
|
|
205
|
-
result.append(("ansicyan", part))
|
|
206
|
-
elif part == "{":
|
|
207
|
-
result.append(("ansimagenta", "{"))
|
|
208
|
-
in_expr = True
|
|
209
|
-
first_word = False
|
|
210
|
-
elif part == "}":
|
|
211
|
-
result.append(("ansimagenta", "}"))
|
|
212
|
-
in_expr = False
|
|
213
|
-
elif in_expr:
|
|
214
|
-
result.extend(_lex_python(part))
|
|
215
|
-
else:
|
|
216
|
-
result.extend(_lex_cmd(part, first_word))
|
|
217
|
-
return result
|
|
218
|
-
|
|
219
|
-
def _lex_line(line: str) -> list[tuple[str, str]]:
|
|
220
|
-
|
|
221
|
-
result: list[tuple[str, str]] = []
|
|
222
|
-
|
|
223
|
-
target_var = None
|
|
224
|
-
if "=>" in line:
|
|
225
|
-
line, target_var = line.split("=>", 1)
|
|
226
|
-
|
|
227
|
-
leading_spaces = line[:len(line) - len(line.lstrip())]
|
|
228
|
-
_splited = line.lstrip().split(None, 1)
|
|
229
|
-
_cmd = _splited[0] if _splited else ""
|
|
230
|
-
if _cmd in ["pyexec", "print", "pwd", "help"]:
|
|
231
|
-
sh_cmd = None
|
|
232
|
-
if "|>" in line:
|
|
233
|
-
line, sh_cmd = line.split("|>", 1)
|
|
234
|
-
result.append(("", leading_spaces))
|
|
235
|
-
result.append(("ansigreen bold", _cmd))
|
|
236
|
-
result.extend(_lex_python(line[len(leading_spaces) + len(_cmd):]))
|
|
237
|
-
if sh_cmd is not None:
|
|
238
|
-
result.append(("ansiblue", "|>"))
|
|
239
|
-
result.extend(_lex_sh(sh_cmd))
|
|
240
|
-
else:
|
|
241
|
-
result.extend(_lex_sh(line))
|
|
242
|
-
|
|
243
|
-
if target_var is not None:
|
|
244
|
-
result.append(("ansiblue", "=>"))
|
|
245
|
-
result.append(("ansibrightcyan", target_var))
|
|
246
|
-
|
|
247
|
-
return result
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
class XmyShellLexer(Lexer):
|
|
251
|
-
def lex_document(self, document) -> Callable[[int], FormattedText]:
|
|
252
|
-
def _lex(line_number: int) -> FormattedText:
|
|
253
|
-
lines = document.text.splitlines()
|
|
254
|
-
if line_number >= len(lines):
|
|
255
|
-
return FormattedText([("", "")])
|
|
256
|
-
line = lines[line_number]
|
|
257
|
-
return FormattedText(_lex_line(line))
|
|
258
|
-
|
|
259
|
-
return _lex
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|