cmd2 2.5.11__py3-none-any.whl → 2.6.0__py3-none-any.whl
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.
- cmd2/__init__.py +11 -22
- cmd2/ansi.py +78 -91
- cmd2/argparse_completer.py +109 -132
- cmd2/argparse_custom.py +199 -217
- cmd2/clipboard.py +2 -6
- cmd2/cmd2.py +447 -521
- cmd2/command_definition.py +34 -44
- cmd2/constants.py +1 -3
- cmd2/decorators.py +47 -58
- cmd2/exceptions.py +23 -46
- cmd2/history.py +29 -43
- cmd2/parsing.py +59 -84
- cmd2/plugin.py +6 -10
- cmd2/py_bridge.py +20 -26
- cmd2/rl_utils.py +45 -69
- cmd2/table_creator.py +83 -106
- cmd2/transcript.py +55 -59
- cmd2/utils.py +143 -176
- {cmd2-2.5.11.dist-info → cmd2-2.6.0.dist-info}/METADATA +34 -17
- cmd2-2.6.0.dist-info/RECORD +24 -0
- {cmd2-2.5.11.dist-info → cmd2-2.6.0.dist-info}/WHEEL +1 -1
- cmd2-2.5.11.dist-info/RECORD +0 -24
- {cmd2-2.5.11.dist-info → cmd2-2.6.0.dist-info/licenses}/LICENSE +0 -0
- {cmd2-2.5.11.dist-info → cmd2-2.6.0.dist-info}/top_level.txt +0 -0
cmd2/py_bridge.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
while maintaining a reasonable degree of isolation between the two.
|
1
|
+
"""Bridges calls made inside of a Python environment to the Cmd2 host app.
|
2
|
+
|
3
|
+
Maintains a reasonable degree of isolation between the two.
|
5
4
|
"""
|
6
5
|
|
7
6
|
import sys
|
@@ -13,7 +12,6 @@ from typing import (
|
|
13
12
|
IO,
|
14
13
|
TYPE_CHECKING,
|
15
14
|
Any,
|
16
|
-
List,
|
17
15
|
NamedTuple,
|
18
16
|
Optional,
|
19
17
|
TextIO,
|
@@ -30,7 +28,7 @@ if TYPE_CHECKING: # pragma: no cover
|
|
30
28
|
|
31
29
|
|
32
30
|
class CommandResult(NamedTuple):
|
33
|
-
"""Encapsulates the results from a cmd2 app command
|
31
|
+
"""Encapsulates the results from a cmd2 app command.
|
34
32
|
|
35
33
|
:stdout: str - output captured from stdout while this command is executing
|
36
34
|
:stderr: str - output captured from stderr while this command is executing
|
@@ -71,20 +69,17 @@ class CommandResult(NamedTuple):
|
|
71
69
|
data: Any = None
|
72
70
|
|
73
71
|
def __bool__(self) -> bool:
|
74
|
-
"""
|
75
|
-
|
72
|
+
"""Return True if the command succeeded, otherwise False."""
|
76
73
|
# If data was set, then use it to determine success
|
77
74
|
if self.data is not None:
|
78
75
|
return bool(self.data)
|
79
76
|
|
80
77
|
# Otherwise check if stderr was filled out
|
81
|
-
|
82
|
-
return not self.stderr
|
78
|
+
return not self.stderr
|
83
79
|
|
84
80
|
|
85
81
|
class PyBridge:
|
86
|
-
"""
|
87
|
-
Provides a Python API wrapper for application commands.
|
82
|
+
"""Provides a Python API wrapper for application commands.
|
88
83
|
|
89
84
|
:param cmd2_app: app being controlled by this PyBridge.
|
90
85
|
:param add_to_history: If True, then add all commands run by this PyBridge to history.
|
@@ -92,6 +87,7 @@ class PyBridge:
|
|
92
87
|
"""
|
93
88
|
|
94
89
|
def __init__(self, cmd2_app: 'cmd2.Cmd', *, add_to_history: bool = True) -> None:
|
90
|
+
"""Initialize PyBridge instances."""
|
95
91
|
self._cmd2_app = cmd2_app
|
96
92
|
self._add_to_history = add_to_history
|
97
93
|
self.cmd_echo = False
|
@@ -99,15 +95,15 @@ class PyBridge:
|
|
99
95
|
# Tells if any of the commands run via __call__ returned True for stop
|
100
96
|
self.stop = False
|
101
97
|
|
102
|
-
def __dir__(self) ->
|
103
|
-
"""Return a custom set of attribute names"""
|
104
|
-
attributes:
|
98
|
+
def __dir__(self) -> list[str]:
|
99
|
+
"""Return a custom set of attribute names."""
|
100
|
+
attributes: list[str] = []
|
105
101
|
attributes.insert(0, 'cmd_echo')
|
106
102
|
return attributes
|
107
103
|
|
108
104
|
def __call__(self, command: str, *, echo: Optional[bool] = None) -> CommandResult:
|
109
|
-
"""
|
110
|
-
|
105
|
+
"""Provide functionality to call application commands by calling PyBridge.
|
106
|
+
|
111
107
|
ex: app('help')
|
112
108
|
:param command: command line being run
|
113
109
|
:param echo: If provided, this temporarily overrides the value of self.cmd_echo while the
|
@@ -131,23 +127,21 @@ class PyBridge:
|
|
131
127
|
stop = False
|
132
128
|
try:
|
133
129
|
self._cmd2_app.stdout = cast(TextIO, copy_cmd_stdout)
|
134
|
-
with redirect_stdout(cast(IO[str], copy_cmd_stdout)):
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
)
|
130
|
+
with redirect_stdout(cast(IO[str], copy_cmd_stdout)), redirect_stderr(cast(IO[str], copy_stderr)):
|
131
|
+
stop = self._cmd2_app.onecmd_plus_hooks(
|
132
|
+
command,
|
133
|
+
add_to_history=self._add_to_history,
|
134
|
+
py_bridge_call=True,
|
135
|
+
)
|
141
136
|
finally:
|
142
137
|
with self._cmd2_app.sigint_protection:
|
143
138
|
self._cmd2_app.stdout = cast(IO[str], copy_cmd_stdout.inner_stream)
|
144
139
|
self.stop = stop or self.stop
|
145
140
|
|
146
141
|
# Save the result
|
147
|
-
|
142
|
+
return CommandResult(
|
148
143
|
stdout=copy_cmd_stdout.getvalue(),
|
149
144
|
stderr=copy_stderr.getvalue(),
|
150
145
|
stop=stop,
|
151
146
|
data=self._cmd2_app.last_result,
|
152
147
|
)
|
153
|
-
return result
|
cmd2/rl_utils.py
CHANGED
@@ -1,15 +1,11 @@
|
|
1
|
-
|
2
|
-
"""
|
3
|
-
Imports the proper Readline for the platform and provides utility functions for it
|
4
|
-
"""
|
1
|
+
"""Imports the proper Readline for the platform and provides utility functions for it."""
|
5
2
|
|
3
|
+
import contextlib
|
6
4
|
import sys
|
7
5
|
from enum import (
|
8
6
|
Enum,
|
9
7
|
)
|
10
|
-
from typing import
|
11
|
-
Union,
|
12
|
-
)
|
8
|
+
from typing import Union
|
13
9
|
|
14
10
|
#########################################################################################################################
|
15
11
|
# NOTE ON LIBEDIT:
|
@@ -32,14 +28,12 @@ try:
|
|
32
28
|
import gnureadline as readline # type: ignore[import]
|
33
29
|
except ImportError:
|
34
30
|
# Note: If this actually fails, you should install gnureadline on Linux/Mac or pyreadline3 on Windows.
|
35
|
-
|
31
|
+
with contextlib.suppress(ImportError):
|
36
32
|
import readline # type: ignore[no-redef]
|
37
|
-
except ImportError: # pragma: no cover
|
38
|
-
pass
|
39
33
|
|
40
34
|
|
41
35
|
class RlType(Enum):
|
42
|
-
"""Readline library types we support"""
|
36
|
+
"""Readline library types we support."""
|
43
37
|
|
44
38
|
GNU = 1
|
45
39
|
PYREADLINE = 2
|
@@ -72,30 +66,30 @@ if 'pyreadline3' in sys.modules:
|
|
72
66
|
if sys.stdout is not None and sys.stdout.isatty(): # pragma: no cover
|
73
67
|
|
74
68
|
def enable_win_vt100(handle: HANDLE) -> bool:
|
75
|
-
"""
|
76
|
-
|
69
|
+
"""Enable VT100 character sequences in a Windows console.
|
70
|
+
|
77
71
|
This only works on Windows 10 and up
|
78
72
|
:param handle: the handle on which to enable vt100
|
79
|
-
:return: True if vt100 characters are enabled for the handle
|
73
|
+
:return: True if vt100 characters are enabled for the handle.
|
80
74
|
"""
|
81
|
-
|
75
|
+
enable_virtual_terminal_processing = 0x0004
|
82
76
|
|
83
77
|
# Get the current mode for this handle in the console
|
84
78
|
cur_mode = DWORD(0)
|
85
79
|
readline.rl.console.GetConsoleMode(handle, byref(cur_mode))
|
86
80
|
|
87
|
-
|
81
|
+
ret_val = False
|
88
82
|
|
89
83
|
# Check if ENABLE_VIRTUAL_TERMINAL_PROCESSING is already enabled
|
90
|
-
if (cur_mode.value &
|
91
|
-
|
84
|
+
if (cur_mode.value & enable_virtual_terminal_processing) != 0:
|
85
|
+
ret_val = True
|
92
86
|
|
93
|
-
elif readline.rl.console.SetConsoleMode(handle, cur_mode.value |
|
87
|
+
elif readline.rl.console.SetConsoleMode(handle, cur_mode.value | enable_virtual_terminal_processing):
|
94
88
|
# Restore the original mode when we exit
|
95
89
|
atexit.register(readline.rl.console.SetConsoleMode, handle, cur_mode)
|
96
|
-
|
90
|
+
ret_val = True
|
97
91
|
|
98
|
-
return
|
92
|
+
return ret_val
|
99
93
|
|
100
94
|
# Enable VT100 sequences for stdout and stderr
|
101
95
|
STD_OUT_HANDLE = -11
|
@@ -107,15 +101,15 @@ if 'pyreadline3' in sys.modules:
|
|
107
101
|
############################################################################################################
|
108
102
|
# pyreadline3 is incomplete in terms of the Python readline API. Add the missing functions we need.
|
109
103
|
############################################################################################################
|
110
|
-
# readline.remove_history_item()
|
111
|
-
|
112
|
-
getattr(readline, 'remove_history_item')
|
113
|
-
except AttributeError:
|
104
|
+
# Add missing `readline.remove_history_item()`
|
105
|
+
if not hasattr(readline, 'remove_history_item'):
|
114
106
|
|
115
107
|
def pyreadline_remove_history_item(pos: int) -> None:
|
116
|
-
"""
|
117
|
-
|
118
|
-
|
108
|
+
"""Remove the specified item number from the pyreadline3 history.
|
109
|
+
|
110
|
+
An implementation of remove_history_item() for pyreadline3.
|
111
|
+
|
112
|
+
:param pos: The 0-based position in history to remove.
|
119
113
|
"""
|
120
114
|
# Save of the current location of the history cursor
|
121
115
|
saved_cursor = readline.rl.mode._history.history_cursor
|
@@ -159,10 +153,9 @@ else:
|
|
159
153
|
|
160
154
|
|
161
155
|
def rl_force_redisplay() -> None: # pragma: no cover
|
162
|
-
"""
|
163
|
-
|
164
|
-
|
165
|
-
printing to the screen
|
156
|
+
"""Causes readline to display the prompt and input text wherever the cursor is and start reading input from this location.
|
157
|
+
|
158
|
+
This is the proper way to restore the input line after printing to the screen.
|
166
159
|
"""
|
167
160
|
if not sys.stdout.isatty():
|
168
161
|
return
|
@@ -181,34 +174,25 @@ def rl_force_redisplay() -> None: # pragma: no cover
|
|
181
174
|
|
182
175
|
|
183
176
|
def rl_get_point() -> int: # pragma: no cover
|
184
|
-
"""
|
185
|
-
Returns the offset of the current cursor position in rl_line_buffer
|
186
|
-
"""
|
177
|
+
"""Return the offset of the current cursor position in rl_line_buffer."""
|
187
178
|
if rl_type == RlType.GNU:
|
188
179
|
return ctypes.c_int.in_dll(readline_lib, "rl_point").value
|
189
180
|
|
190
|
-
|
181
|
+
if rl_type == RlType.PYREADLINE:
|
191
182
|
return int(readline.rl.mode.l_buffer.point)
|
192
183
|
|
193
|
-
|
194
|
-
return 0
|
184
|
+
return 0
|
195
185
|
|
196
186
|
|
197
187
|
def rl_get_prompt() -> str: # pragma: no cover
|
198
|
-
"""Get Readline's prompt"""
|
188
|
+
"""Get Readline's prompt."""
|
199
189
|
if rl_type == RlType.GNU:
|
200
190
|
encoded_prompt = ctypes.c_char_p.in_dll(readline_lib, "rl_prompt").value
|
201
|
-
if encoded_prompt is None
|
202
|
-
prompt = ''
|
203
|
-
else:
|
204
|
-
prompt = encoded_prompt.decode(encoding='utf-8')
|
191
|
+
prompt = '' if encoded_prompt is None else encoded_prompt.decode(encoding='utf-8')
|
205
192
|
|
206
193
|
elif rl_type == RlType.PYREADLINE:
|
207
194
|
prompt_data: Union[str, bytes] = readline.rl.prompt
|
208
|
-
if isinstance(prompt_data, bytes)
|
209
|
-
prompt = prompt_data.decode(encoding='utf-8')
|
210
|
-
else:
|
211
|
-
prompt = prompt_data
|
195
|
+
prompt = prompt_data.decode(encoding='utf-8') if isinstance(prompt_data, bytes) else prompt_data
|
212
196
|
|
213
197
|
else:
|
214
198
|
prompt = ''
|
@@ -217,27 +201,22 @@ def rl_get_prompt() -> str: # pragma: no cover
|
|
217
201
|
|
218
202
|
|
219
203
|
def rl_get_display_prompt() -> str: # pragma: no cover
|
220
|
-
"""
|
221
|
-
Get Readline's currently displayed prompt.
|
204
|
+
"""Get Readline's currently displayed prompt.
|
222
205
|
|
223
206
|
In GNU Readline, the displayed prompt sometimes differs from the prompt.
|
224
207
|
This occurs in functions that use the prompt string as a message area, such as incremental search.
|
225
208
|
"""
|
226
209
|
if rl_type == RlType.GNU:
|
227
210
|
encoded_prompt = ctypes.c_char_p.in_dll(readline_lib, "rl_display_prompt").value
|
228
|
-
if encoded_prompt is None
|
229
|
-
prompt = ''
|
230
|
-
else:
|
231
|
-
prompt = encoded_prompt.decode(encoding='utf-8')
|
211
|
+
prompt = '' if encoded_prompt is None else encoded_prompt.decode(encoding='utf-8')
|
232
212
|
return rl_unescape_prompt(prompt)
|
233
|
-
|
234
|
-
return rl_get_prompt()
|
213
|
+
return rl_get_prompt()
|
235
214
|
|
236
215
|
|
237
216
|
def rl_set_prompt(prompt: str) -> None: # pragma: no cover
|
238
|
-
"""
|
239
|
-
|
240
|
-
:param prompt: the new prompt value
|
217
|
+
"""Set Readline's prompt.
|
218
|
+
|
219
|
+
:param prompt: the new prompt value.
|
241
220
|
"""
|
242
221
|
escaped_prompt = rl_escape_prompt(prompt)
|
243
222
|
|
@@ -250,8 +229,7 @@ def rl_set_prompt(prompt: str) -> None: # pragma: no cover
|
|
250
229
|
|
251
230
|
|
252
231
|
def rl_escape_prompt(prompt: str) -> str:
|
253
|
-
"""
|
254
|
-
Overcome bug in GNU Readline in relation to calculation of prompt length in presence of ANSI escape codes
|
232
|
+
"""Overcome bug in GNU Readline in relation to calculation of prompt length in presence of ANSI escape codes.
|
255
233
|
|
256
234
|
:param prompt: original prompt
|
257
235
|
:return: prompt safe to pass to GNU Readline
|
@@ -278,12 +256,11 @@ def rl_escape_prompt(prompt: str) -> str:
|
|
278
256
|
|
279
257
|
return result
|
280
258
|
|
281
|
-
|
282
|
-
return prompt
|
259
|
+
return prompt
|
283
260
|
|
284
261
|
|
285
262
|
def rl_unescape_prompt(prompt: str) -> str:
|
286
|
-
"""Remove escape characters from a Readline prompt"""
|
263
|
+
"""Remove escape characters from a Readline prompt."""
|
287
264
|
if rl_type == RlType.GNU:
|
288
265
|
escape_start = "\x01"
|
289
266
|
escape_end = "\x02"
|
@@ -293,16 +270,16 @@ def rl_unescape_prompt(prompt: str) -> str:
|
|
293
270
|
|
294
271
|
|
295
272
|
def rl_in_search_mode() -> bool: # pragma: no cover
|
296
|
-
"""Check if readline is doing either an incremental (e.g. Ctrl-r) or non-incremental (e.g. Esc-p) search"""
|
273
|
+
"""Check if readline is doing either an incremental (e.g. Ctrl-r) or non-incremental (e.g. Esc-p) search."""
|
297
274
|
if rl_type == RlType.GNU:
|
298
275
|
# GNU Readline defines constants that we can use to determine if in search mode.
|
299
276
|
# RL_STATE_ISEARCH 0x0000080
|
300
277
|
# RL_STATE_NSEARCH 0x0000100
|
301
|
-
|
278
|
+
in_search_mode = 0x0000180
|
302
279
|
|
303
280
|
readline_state = ctypes.c_int.in_dll(readline_lib, "rl_readline_state").value
|
304
|
-
return bool(
|
305
|
-
|
281
|
+
return bool(in_search_mode & readline_state)
|
282
|
+
if rl_type == RlType.PYREADLINE:
|
306
283
|
from pyreadline3.modes.emacs import ( # type: ignore[import]
|
307
284
|
EmacsMode,
|
308
285
|
)
|
@@ -317,5 +294,4 @@ def rl_in_search_mode() -> bool: # pragma: no cover
|
|
317
294
|
readline.rl.mode._process_non_incremental_search_keyevent,
|
318
295
|
)
|
319
296
|
return readline.rl.mode.process_keyevent_queue[-1] in search_funcs
|
320
|
-
|
321
|
-
return False
|
297
|
+
return False
|