cmd2 2.6.2__py3-none-any.whl → 3.0.0b1__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 +41 -38
- cmd2/argparse_completer.py +80 -81
- cmd2/argparse_custom.py +359 -151
- cmd2/clipboard.py +1 -1
- cmd2/cmd2.py +1272 -845
- cmd2/colors.py +270 -0
- cmd2/command_definition.py +13 -5
- cmd2/constants.py +0 -6
- cmd2/decorators.py +41 -104
- cmd2/exceptions.py +1 -1
- cmd2/history.py +7 -11
- cmd2/parsing.py +12 -17
- cmd2/plugin.py +1 -2
- cmd2/py_bridge.py +15 -10
- cmd2/rich_utils.py +451 -0
- cmd2/rl_utils.py +12 -8
- cmd2/string_utils.py +166 -0
- cmd2/styles.py +72 -0
- cmd2/terminal_utils.py +144 -0
- cmd2/transcript.py +7 -9
- cmd2/utils.py +88 -508
- {cmd2-2.6.2.dist-info → cmd2-3.0.0b1.dist-info}/METADATA +23 -44
- cmd2-3.0.0b1.dist-info/RECORD +27 -0
- cmd2/ansi.py +0 -1093
- cmd2/table_creator.py +0 -1122
- cmd2-2.6.2.dist-info/RECORD +0 -24
- {cmd2-2.6.2.dist-info → cmd2-3.0.0b1.dist-info}/WHEEL +0 -0
- {cmd2-2.6.2.dist-info → cmd2-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
- {cmd2-2.6.2.dist-info → cmd2-3.0.0b1.dist-info}/top_level.txt +0 -0
cmd2/__init__.py
CHANGED
@@ -1,24 +1,17 @@
|
|
1
1
|
"""Import certain things for backwards compatibility."""
|
2
2
|
|
3
|
-
import argparse
|
4
3
|
import contextlib
|
5
4
|
import importlib.metadata as importlib_metadata
|
6
|
-
import sys
|
7
5
|
|
8
6
|
with contextlib.suppress(importlib_metadata.PackageNotFoundError):
|
9
7
|
__version__ = importlib_metadata.version(__name__)
|
10
8
|
|
11
|
-
from .
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
EightBitFg,
|
16
|
-
Fg,
|
17
|
-
RgbBg,
|
18
|
-
RgbFg,
|
19
|
-
TextStyle,
|
20
|
-
style,
|
9
|
+
from . import (
|
10
|
+
plugin,
|
11
|
+
rich_utils,
|
12
|
+
string_utils,
|
21
13
|
)
|
14
|
+
from .argparse_completer import set_default_ap_completer_type
|
22
15
|
from .argparse_custom import (
|
23
16
|
Cmd2ArgumentParser,
|
24
17
|
Cmd2AttributeWrapper,
|
@@ -26,21 +19,22 @@ from .argparse_custom import (
|
|
26
19
|
register_argparse_argument_parameter,
|
27
20
|
set_default_argument_parser_type,
|
28
21
|
)
|
29
|
-
|
30
|
-
# Check if user has defined a module that sets a custom value for argparse_custom.DEFAULT_ARGUMENT_PARSER.
|
31
|
-
# Do this before loading cmd2.Cmd class so its commands use the custom parser.
|
32
|
-
cmd2_parser_module = getattr(argparse, 'cmd2_parser_module', None)
|
33
|
-
if cmd2_parser_module is not None:
|
34
|
-
import importlib
|
35
|
-
|
36
|
-
importlib.import_module(cmd2_parser_module)
|
37
|
-
|
38
|
-
from . import plugin
|
39
|
-
from .argparse_completer import set_default_ap_completer_type
|
40
22
|
from .cmd2 import Cmd
|
41
|
-
from .
|
42
|
-
from .
|
43
|
-
|
23
|
+
from .colors import Color
|
24
|
+
from .command_definition import (
|
25
|
+
CommandSet,
|
26
|
+
with_default_category,
|
27
|
+
)
|
28
|
+
from .constants import (
|
29
|
+
COMMAND_NAME,
|
30
|
+
DEFAULT_SHORTCUTS,
|
31
|
+
)
|
32
|
+
from .decorators import (
|
33
|
+
as_subcommand_to,
|
34
|
+
with_argparser,
|
35
|
+
with_argument_list,
|
36
|
+
with_category,
|
37
|
+
)
|
44
38
|
from .exceptions import (
|
45
39
|
Cmd2ArgparseError,
|
46
40
|
CommandSetRegistrationError,
|
@@ -50,33 +44,33 @@ from .exceptions import (
|
|
50
44
|
)
|
51
45
|
from .parsing import Statement
|
52
46
|
from .py_bridge import CommandResult
|
53
|
-
from .
|
47
|
+
from .rich_utils import RichPrintKwargs
|
48
|
+
from .string_utils import stylize
|
49
|
+
from .styles import Cmd2Style
|
50
|
+
from .utils import (
|
51
|
+
CompletionMode,
|
52
|
+
CustomCompletionSettings,
|
53
|
+
Settable,
|
54
|
+
categorize,
|
55
|
+
)
|
54
56
|
|
55
57
|
__all__: list[str] = [ # noqa: RUF022
|
56
58
|
'COMMAND_NAME',
|
57
59
|
'DEFAULT_SHORTCUTS',
|
58
|
-
# ANSI Exports
|
59
|
-
'Cursor',
|
60
|
-
'Bg',
|
61
|
-
'Fg',
|
62
|
-
'EightBitBg',
|
63
|
-
'EightBitFg',
|
64
|
-
'RgbBg',
|
65
|
-
'RgbFg',
|
66
|
-
'TextStyle',
|
67
|
-
'style',
|
68
60
|
# Argparse Exports
|
69
61
|
'Cmd2ArgumentParser',
|
70
62
|
'Cmd2AttributeWrapper',
|
71
63
|
'CompletionItem',
|
72
64
|
'register_argparse_argument_parameter',
|
73
|
-
'set_default_argument_parser_type',
|
74
65
|
'set_default_ap_completer_type',
|
66
|
+
'set_default_argument_parser_type',
|
75
67
|
# Cmd2
|
76
68
|
'Cmd',
|
77
69
|
'CommandResult',
|
78
70
|
'CommandSet',
|
79
71
|
'Statement',
|
72
|
+
# Colors
|
73
|
+
"Color",
|
80
74
|
# Decorators
|
81
75
|
'with_argument_list',
|
82
76
|
'with_argparser',
|
@@ -87,9 +81,18 @@ __all__: list[str] = [ # noqa: RUF022
|
|
87
81
|
'Cmd2ArgparseError',
|
88
82
|
'CommandSetRegistrationError',
|
89
83
|
'CompletionError',
|
84
|
+
'PassThroughException',
|
90
85
|
'SkipPostcommandHooks',
|
91
86
|
# modules
|
92
87
|
'plugin',
|
88
|
+
'rich_utils',
|
89
|
+
'string_utils',
|
90
|
+
# Rich Utils
|
91
|
+
'RichPrintKwargs',
|
92
|
+
# String Utils
|
93
|
+
'stylize',
|
94
|
+
# Styles,
|
95
|
+
"Cmd2Style",
|
93
96
|
# Utilities
|
94
97
|
'categorize',
|
95
98
|
'CompletionMode',
|
cmd2/argparse_completer.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""Module
|
1
|
+
"""Module defines the ArgparseCompleter class which provides argparse-based tab completion to cmd2 apps.
|
2
2
|
|
3
3
|
See the header of argparse_custom.py for instructions on how to use these features.
|
4
4
|
"""
|
@@ -9,25 +9,24 @@ import numbers
|
|
9
9
|
from collections import (
|
10
10
|
deque,
|
11
11
|
)
|
12
|
+
from collections.abc import Sequence
|
12
13
|
from typing import (
|
14
|
+
IO,
|
13
15
|
TYPE_CHECKING,
|
14
|
-
Optional,
|
15
|
-
Union,
|
16
16
|
cast,
|
17
17
|
)
|
18
18
|
|
19
|
-
from .
|
20
|
-
|
21
|
-
widest_line,
|
22
|
-
)
|
23
|
-
from .constants import (
|
24
|
-
INFINITY,
|
25
|
-
)
|
19
|
+
from .constants import INFINITY
|
20
|
+
from .rich_utils import Cmd2GeneralConsole
|
26
21
|
|
27
22
|
if TYPE_CHECKING: # pragma: no cover
|
28
|
-
from .cmd2 import
|
29
|
-
|
30
|
-
|
23
|
+
from .cmd2 import Cmd
|
24
|
+
|
25
|
+
from rich.box import SIMPLE_HEAD
|
26
|
+
from rich.table import (
|
27
|
+
Column,
|
28
|
+
Table,
|
29
|
+
)
|
31
30
|
|
32
31
|
from .argparse_custom import (
|
33
32
|
ChoicesCallable,
|
@@ -35,20 +34,12 @@ from .argparse_custom import (
|
|
35
34
|
CompletionItem,
|
36
35
|
generate_range_error,
|
37
36
|
)
|
38
|
-
from .command_definition import
|
39
|
-
|
40
|
-
|
41
|
-
from .exceptions import (
|
42
|
-
CompletionError,
|
43
|
-
)
|
44
|
-
from .table_creator import (
|
45
|
-
Column,
|
46
|
-
HorizontalAlignment,
|
47
|
-
SimpleTable,
|
48
|
-
)
|
37
|
+
from .command_definition import CommandSet
|
38
|
+
from .exceptions import CompletionError
|
39
|
+
from .styles import Cmd2Style
|
49
40
|
|
50
|
-
# If no descriptive
|
51
|
-
|
41
|
+
# If no descriptive headers are supplied, then this will be used instead
|
42
|
+
DEFAULT_DESCRIPTIVE_HEADERS: Sequence[str | Column] = ('Description',)
|
52
43
|
|
53
44
|
# Name of the choice/completer function argument that, if present, will be passed a dictionary of
|
54
45
|
# command line tokens up through the token being completed mapped to their argparse destination name.
|
@@ -104,8 +95,8 @@ class _ArgumentState:
|
|
104
95
|
|
105
96
|
def __init__(self, arg_action: argparse.Action) -> None:
|
106
97
|
self.action = arg_action
|
107
|
-
self.min:
|
108
|
-
self.max:
|
98
|
+
self.min: int | str
|
99
|
+
self.max: float | int | str
|
109
100
|
self.count = 0
|
110
101
|
self.is_remainder = self.action.nargs == argparse.REMAINDER
|
111
102
|
|
@@ -140,7 +131,7 @@ class _UnfinishedFlagError(CompletionError):
|
|
140
131
|
:param flag_arg_state: information about the unfinished flag action.
|
141
132
|
"""
|
142
133
|
arg = f'{argparse._get_action_name(flag_arg_state.action)}'
|
143
|
-
err = f'{generate_range_error(cast(int, flag_arg_state.min), cast(
|
134
|
+
err = f'{generate_range_error(cast(int, flag_arg_state.min), cast(int | float, flag_arg_state.max))}'
|
144
135
|
error = f"Error: argument {arg}: {err} ({flag_arg_state.count} entered)"
|
145
136
|
super().__init__(error)
|
146
137
|
|
@@ -162,7 +153,7 @@ class ArgparseCompleter:
|
|
162
153
|
"""Automatic command line tab completion based on argparse parameters."""
|
163
154
|
|
164
155
|
def __init__(
|
165
|
-
self, parser: argparse.ArgumentParser, cmd2_app: 'Cmd', *, parent_tokens:
|
156
|
+
self, parser: argparse.ArgumentParser, cmd2_app: 'Cmd', *, parent_tokens: dict[str, list[str]] | None = None
|
166
157
|
) -> None:
|
167
158
|
"""Create an ArgparseCompleter.
|
168
159
|
|
@@ -202,7 +193,7 @@ class ArgparseCompleter:
|
|
202
193
|
self._subcommand_action = action
|
203
194
|
|
204
195
|
def complete(
|
205
|
-
self, text: str, line: str, begidx: int, endidx: int, tokens: list[str], *, cmd_set:
|
196
|
+
self, text: str, line: str, begidx: int, endidx: int, tokens: list[str], *, cmd_set: CommandSet | None = None
|
206
197
|
) -> list[str]:
|
207
198
|
"""Complete text using argparse metadata.
|
208
199
|
|
@@ -227,10 +218,10 @@ class ArgparseCompleter:
|
|
227
218
|
skip_remaining_flags = False
|
228
219
|
|
229
220
|
# _ArgumentState of the current positional
|
230
|
-
pos_arg_state:
|
221
|
+
pos_arg_state: _ArgumentState | None = None
|
231
222
|
|
232
223
|
# _ArgumentState of the current flag
|
233
|
-
flag_arg_state:
|
224
|
+
flag_arg_state: _ArgumentState | None = None
|
234
225
|
|
235
226
|
# Non-reusable flags that we've parsed
|
236
227
|
matched_flags: list[str] = []
|
@@ -522,7 +513,7 @@ class ArgparseCompleter:
|
|
522
513
|
|
523
514
|
return matches
|
524
515
|
|
525
|
-
def _format_completions(self, arg_state: _ArgumentState, completions:
|
516
|
+
def _format_completions(self, arg_state: _ArgumentState, completions: list[str] | list[CompletionItem]) -> list[str]:
|
526
517
|
"""Format CompletionItems into hint table."""
|
527
518
|
# Nothing to do if we don't have at least 2 completions which are all CompletionItems
|
528
519
|
if len(completions) < 2 or not all(isinstance(c, CompletionItem) for c in completions):
|
@@ -537,7 +528,7 @@ class ArgparseCompleter:
|
|
537
528
|
if not self._cmd2_app.matches_sorted:
|
538
529
|
# If all orig_value types are numbers, then sort by that value
|
539
530
|
if all_nums:
|
540
|
-
completion_items.sort(key=lambda c: c.orig_value)
|
531
|
+
completion_items.sort(key=lambda c: c.orig_value)
|
541
532
|
|
542
533
|
# Otherwise sort as strings
|
543
534
|
else:
|
@@ -547,8 +538,6 @@ class ArgparseCompleter:
|
|
547
538
|
|
548
539
|
# Check if there are too many CompletionItems to display as a table
|
549
540
|
if len(completions) <= self._cmd2_app.max_completion_items:
|
550
|
-
four_spaces = 4 * ' '
|
551
|
-
|
552
541
|
# If a metavar was defined, use that instead of the dest field
|
553
542
|
destination = arg_state.action.metavar if arg_state.action.metavar else arg_state.action.dest
|
554
543
|
|
@@ -561,39 +550,45 @@ class ArgparseCompleter:
|
|
561
550
|
tuple_index = min(len(destination) - 1, arg_state.count)
|
562
551
|
destination = destination[tuple_index]
|
563
552
|
|
564
|
-
|
565
|
-
if
|
566
|
-
|
567
|
-
|
568
|
-
# Replace tabs with 4 spaces so we can calculate width
|
569
|
-
desc_header = desc_header.replace('\t', four_spaces)
|
570
|
-
|
571
|
-
# Calculate needed widths for the token and description columns of the table
|
572
|
-
token_width = style_aware_wcswidth(destination)
|
573
|
-
desc_width = widest_line(desc_header)
|
574
|
-
|
575
|
-
for item in completion_items:
|
576
|
-
token_width = max(style_aware_wcswidth(item), token_width)
|
577
|
-
|
578
|
-
# Replace tabs with 4 spaces so we can calculate width
|
579
|
-
item.description = item.description.replace('\t', four_spaces)
|
580
|
-
desc_width = max(widest_line(item.description), desc_width)
|
553
|
+
desc_headers = cast(Sequence[str | Column] | None, arg_state.action.get_descriptive_headers()) # type: ignore[attr-defined]
|
554
|
+
if desc_headers is None:
|
555
|
+
desc_headers = DEFAULT_DESCRIPTIVE_HEADERS
|
581
556
|
|
582
|
-
|
583
|
-
|
584
|
-
|
557
|
+
# Build all headers for the hint table
|
558
|
+
headers: list[Column] = []
|
559
|
+
headers.append(
|
585
560
|
Column(
|
586
561
|
destination.upper(),
|
587
|
-
|
588
|
-
|
589
|
-
|
562
|
+
justify="right" if all_nums else "left",
|
563
|
+
no_wrap=True,
|
564
|
+
)
|
565
|
+
)
|
566
|
+
for desc_header in desc_headers:
|
567
|
+
header = (
|
568
|
+
desc_header
|
569
|
+
if isinstance(desc_header, Column)
|
570
|
+
else Column(
|
571
|
+
desc_header,
|
572
|
+
overflow="fold",
|
573
|
+
)
|
590
574
|
)
|
575
|
+
headers.append(header)
|
576
|
+
|
577
|
+
# Build the hint table
|
578
|
+
hint_table = Table(
|
579
|
+
*headers,
|
580
|
+
box=SIMPLE_HEAD,
|
581
|
+
show_edge=False,
|
582
|
+
border_style=Cmd2Style.TABLE_BORDER,
|
591
583
|
)
|
592
|
-
|
584
|
+
for item in completion_items:
|
585
|
+
hint_table.add_row(item, *item.descriptive_data)
|
593
586
|
|
594
|
-
|
595
|
-
|
596
|
-
|
587
|
+
# Generate the hint table string
|
588
|
+
console = Cmd2GeneralConsole()
|
589
|
+
with console.capture() as capture:
|
590
|
+
console.print(hint_table, end="")
|
591
|
+
self._cmd2_app.formatted_completions = capture.get()
|
597
592
|
|
598
593
|
# Return sorted list of completions
|
599
594
|
return cast(list[str], completions)
|
@@ -624,24 +619,28 @@ class ArgparseCompleter:
|
|
624
619
|
break
|
625
620
|
return []
|
626
621
|
|
627
|
-
def
|
628
|
-
"""Supports cmd2's help command in the
|
622
|
+
def print_help(self, tokens: list[str], file: IO[str] | None = None) -> None:
|
623
|
+
"""Supports cmd2's help command in the printing of help text.
|
629
624
|
|
630
625
|
:param tokens: arguments passed to help command
|
631
|
-
:
|
626
|
+
:param file: optional file object where the argparse should write help text
|
627
|
+
If not supplied, argparse will write to sys.stdout.
|
632
628
|
"""
|
633
|
-
# If our parser has subcommands, we must examine the tokens and check if they are subcommands
|
629
|
+
# If our parser has subcommands, we must examine the tokens and check if they are subcommands.
|
634
630
|
# If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter.
|
635
|
-
if self._subcommand_action is not None:
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
631
|
+
if tokens and self._subcommand_action is not None:
|
632
|
+
parser = cast(
|
633
|
+
argparse.ArgumentParser | None,
|
634
|
+
self._subcommand_action.choices.get(tokens[0]),
|
635
|
+
)
|
640
636
|
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
637
|
+
if parser:
|
638
|
+
completer_type = self._cmd2_app._determine_ap_completer_type(parser)
|
639
|
+
completer = completer_type(parser, self._cmd2_app)
|
640
|
+
completer.print_help(tokens[1:])
|
641
|
+
return
|
642
|
+
|
643
|
+
self._parser.print_help(file=file)
|
645
644
|
|
646
645
|
def _complete_arg(
|
647
646
|
self,
|
@@ -652,7 +651,7 @@ class ArgparseCompleter:
|
|
652
651
|
arg_state: _ArgumentState,
|
653
652
|
consumed_arg_values: dict[str, list[str]],
|
654
653
|
*,
|
655
|
-
cmd_set:
|
654
|
+
cmd_set: CommandSet | None = None,
|
656
655
|
) -> list[str]:
|
657
656
|
"""Tab completion routine for an argparse argument.
|
658
657
|
|
@@ -660,7 +659,7 @@ class ArgparseCompleter:
|
|
660
659
|
:raises CompletionError: if the completer or choices function this calls raises one.
|
661
660
|
"""
|
662
661
|
# Check if the arg provides choices to the user
|
663
|
-
arg_choices:
|
662
|
+
arg_choices: list[str] | ChoicesCallable
|
664
663
|
if arg_state.action.choices is not None:
|
665
664
|
arg_choices = list(arg_state.action.choices)
|
666
665
|
if not arg_choices:
|
@@ -722,12 +721,12 @@ class ArgparseCompleter:
|
|
722
721
|
if not arg_choices.is_completer:
|
723
722
|
choices_func = arg_choices.choices_provider
|
724
723
|
if isinstance(choices_func, ChoicesProviderFuncWithTokens):
|
725
|
-
completion_items = choices_func(*args, **kwargs)
|
724
|
+
completion_items = choices_func(*args, **kwargs)
|
726
725
|
else: # pragma: no cover
|
727
726
|
# This won't hit because runtime checking doesn't check function argument types and will always
|
728
727
|
# resolve true above. Mypy, however, does see the difference and gives an error that can't be
|
729
728
|
# ignored. Mypy issue #5485 discusses this problem
|
730
|
-
completion_items = choices_func(*args)
|
729
|
+
completion_items = choices_func(*args)
|
731
730
|
# else case is already covered above
|
732
731
|
else:
|
733
732
|
completion_items = arg_choices
|