FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
cmd2/cmd2/cmd2.py at main · python-cmd2/cmd2 · GitHub
cmd2/cmd2/cmd2.py at main · python-cmd2/cmd2 · GitHub
Skip to content
Navigation Menu
Sign in
Appearance settings
AI CODE CREATION
GitHub Copilot
Write better code with AI
GitHub Copilot app
Direct agents from issue to merge
MCP Registry
Integrate external tools
DEVELOPER WORKFLOWS
Actions
Automate any workflow
Codespaces
Instant dev environments
Issues
Plan and track work
Code Review
Manage code changes
Code Quality
Enforce quality at merge
APPLICATION SECURITY
GitHub Advanced Security
Find and fix vulnerabilities
Code security
Secure your code as you build
Secret protection
Stop leaks before they start
EXPLORE
Why GitHub
Documentation
Blog
Changelog
Marketplace
View all features
BY COMPANY SIZE
Enterprises
Small and medium teams
Startups
Nonprofits
BY USE CASE
App Modernization
DevSecOps
DevOps
CI/CD
View all use cases
BY INDUSTRY
Healthcare
Financial services
Manufacturing
Government
View all industries
View all solutions
EXPLORE BY TOPIC
AI
Software Development
DevOps
Security
View all topics
EXPLORE BY TYPE
Customer stories
Events & webinars
Ebooks & reports
Business insights
GitHub Skills
SUPPORT & SERVICES
Documentation
Customer support
Community forum
Trust center
Partners
View all resources
COMMUNITY
GitHub Sponsors
Fund open source developers
PROGRAMS
Security Lab
Maintainer Community
GitHub Stars
Archive Program
REPOSITORIES
Topics
Trending
Collections
ENTERPRISE SOLUTIONS
Enterprise platform
AI-powered developer platform
AVAILABLE ADD-ONS
GitHub Advanced Security
Enterprise-grade security features
Copilot for Business
Enterprise-grade AI features
Premium Support
Enterprise-grade 24/7 support
Pricing
Sign in
Sign up
Appearance settings
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
Uh oh!
There was an error while loading.
Please reload this page
.
python-cmd2
/
cmd2
Public
Notifications
You must be signed in to change notification settings
Fork
132
Star
686
Code
Issues
1
Pull requests
2
Discussions
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Discussions
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
cmd2
/
cmd2
/
cmd2.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
6080 lines (5064 loc) · 258 KB
Breadcrumbs
cmd2
/
cmd2
/
cmd2.py
Copy path
File metadata and controls
6080 lines (5064 loc) · 258 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python.
cmd2 is a tool for building interactive command line applications in Python. Its goal is to make it quick and easy for
developers to build feature-rich and user-friendly interactive command line applications. It provides a simple API which
is an extension of Python's built-in cmd module. cmd2 provides a wealth of features on top of cmd to make your life easier
and eliminates much of the boilerplate code which would be necessary when using cmd.
Extra features include:
- Searchable command history (commands: "history")
- Run commands from file, save to file, edit commands in file
- Multi-line commands
- Special-character shortcut commands (beyond cmd's "?" and "!")
- Settable environment parameters
- Parsing commands with `argparse` argument parsers (flags)
- Redirection to file or paste buffer (clipboard) with > or >>
- Bash-style ``select`` available
Note: cmd2 redirection only captures output directed to self.stdout (e.g., via self.poutput()).
Standard print() calls write directly to sys.stdout and are not captured. However, print() calls
within pyscripts and the interactive Python shell are treated as command output and sent to
self.stdout, allowing them to be captured.
GitHub: https://github.com/python-cmd2/cmd2
Documentation: https://cmd2.readthedocs.io/
"""
# This module has many imports, quite a few of which are only
# infrequently utilized. To reduce the initial overhead of
# import this module, many of these imports are lazy-loaded
# i.e. we only import the module when we use it.
import
argparse
import
contextlib
import
copy
import
dataclasses
import
datetime
import
functools
import
glob
import
inspect
import
os
import
pydoc
import
re
import
sys
import
tempfile
import
threading
import
time
from
code
import
InteractiveConsole
from
collections
import
deque
from
collections
.
abc
import
(
Callable
,
Iterable
,
Mapping
,
Sequence
,
)
from
dataclasses
import
(
dataclass
,
field
,
)
from
types
import
FrameType
from
typing
import
(
IO
,
TYPE_CHECKING
,
Any
,
ClassVar
,
NamedTuple
,
TextIO
,
TypeVar
,
cast
,
)
from
prompt_toolkit
import
(
filters
,
print_formatted_text
,
)
from
prompt_toolkit
.
application
import
create_app_session
,
get_app
from
prompt_toolkit
.
auto_suggest
import
AutoSuggestFromHistory
from
prompt_toolkit
.
completion
import
Completer
,
DummyCompleter
from
prompt_toolkit
.
formatted_text
import
ANSI
,
AnyFormattedText
from
prompt_toolkit
.
history
import
InMemoryHistory
from
prompt_toolkit
.
input
import
DummyInput
,
create_input
from
prompt_toolkit
.
key_binding
import
KeyBindings
from
prompt_toolkit
.
key_binding
.
key_processor
import
KeyPress
,
KeyPressEvent
from
prompt_toolkit
.
keys
import
Keys
from
prompt_toolkit
.
output
import
DummyOutput
,
create_output
from
prompt_toolkit
.
patch_stdout
import
patch_stdout
from
prompt_toolkit
.
shortcuts
import
CompleteStyle
,
PromptSession
,
choice
,
set_title
from
prompt_toolkit
.
styles
import
DynamicStyle
from
rich
.
console
import
(
Group
,
JustifyMethod
,
RenderableType
,
)
from
rich
.
highlighter
import
ReprHighlighter
from
rich
.
pretty
import
Pretty
from
rich
.
rule
import
Rule
from
rich
.
style
import
(
Style
,
StyleType
,
)
from
rich
.
table
import
(
Column
,
Table
,
)
from
rich
.
text
import
Text
from
rich
.
traceback
import
Traceback
from
.
import
(
argparse_completer
,
argparse_utils
,
constants
,
plugin
,
utils
,
)
from
.
import
rich_utils
as
ru
from
.
import
string_utils
as
su
from
.
argparse_utils
import
(
ArgparseCommandSpec
,
Cmd2ArgumentParser
,
ParserSource
,
SubcommandRecord
,
SubcommandSpec
,
)
from
.
clipboard
import
(
get_paste_buffer
,
write_to_paste_buffer
,
)
from
.
command_set
import
CommandSet
from
.
completion
import
(
Choices
,
CompletionItem
,
Completions
,
)
from
.
constants
import
(
COMMAND_FUNC_PREFIX
,
COMPLETER_FUNC_PREFIX
,
HELP_FUNC_PREFIX
,
)
from
.
decorators
import
(
as_subcommand_to
,
with_argparser
,
)
from
.
exceptions
import
(
Cmd2ShlexError
,
CommandSetRegistrationError
,
CompletionError
,
EmbeddedConsoleExit
,
EmptyStatement
,
IncompleteStatement
,
MacroError
,
PassThroughException
,
RedirectionError
,
SkipPostcommandHooks
,
)
from
.
history
import
(
History
,
HistoryItem
,
)
from
.
parsing
import
(
Macro
,
MacroArg
,
Statement
,
StatementParser
,
shlex_split
,
)
from
.
rich_utils
import
(
Cmd2BaseConsole
,
Cmd2ExceptionConsole
,
Cmd2GeneralConsole
,
Cmd2SimpleTable
,
TextGroup
,
)
from
.
styles
import
Cmd2Style
from
.
theme
import
get_pt_theme
from
.
types
import
(
BoundCommandFunc
,
BoundCompleter
,
CmdOrSet
,
CmdOrSetT
,
UnboundChoicesProvider
,
UnboundCompleter
,
)
try
:
if
sys
.
platform
==
"win32"
:
from
prompt_toolkit
.
output
.
win32
import
NoConsoleScreenBufferError
# type: ignore[attr-defined]
else
:
# Trigger the except block for non-Windows platforms
raise
ImportError
# noqa: TRY301
except
ImportError
:
class
NoConsoleScreenBufferError
(
Exception
):
# type: ignore[no-redef]
"""Dummy exception to use when prompt_toolkit.output.win32.NoConsoleScreenBufferError is not available."""
def
__init__
(
self
,
msg
:
str
=
""
)
->
None
:
"""Initialize NoConsoleScreenBufferError custom exception instance."""
super
().
__init__
(
msg
)
from
.
pt_utils
import
(
Cmd2Completer
,
Cmd2History
,
Cmd2Lexer
,
pt_filter_style
,
pt_resolve_color_depth
,
)
from
.
utils
import
(
Settable
,
get_defining_class
,
get_types
,
strip_doc_annotations
,
suggest_similar
,
)
if
TYPE_CHECKING
:
# pragma: no cover
from
prompt_toolkit
.
buffer
import
Buffer
class
_SavedCmd2Env
:
"""cmd2 environment settings that are backed up when entering an interactive Python shell."""
def
__init__
(
self
)
->
None
:
self
.
history
:
list
[
str
]
=
[]
self
.
completer
:
Callable
[[
str
,
int
],
str
|
None
]
|
None
=
None
class
DisabledCommand
(
NamedTuple
):
"""Stores data about a disabled command.
This data is used to restore its functions when the command is enabled.
"""
command_func
:
BoundCommandFunc
[...]
help_func
:
Callable
[[],
Any
]
|
None
completer_func
:
BoundCompleter
|
None
class
CommandParsers
:
"""Create and store all command method argument parsers for a given Cmd instance.
Parser creation and retrieval are accomplished through the get() method.
"""
def
__init__
(
self
,
cmd_app
:
"Cmd"
)
->
None
:
"""Initialize CommandParsers.
:param cmd_app: the Cmd instance whose parsers are being managed
"""
self
.
_cmd_app
=
cmd_app
# Keyed by the fully qualified method names. This is more reliable than
# the methods themselves, since wrapping a method will change its address.
self
.
_parsers
:
dict
[
str
,
Cmd2ArgumentParser
]
=
{}
@
staticmethod
def
_fully_qualified_name
(
command_method
:
BoundCommandFunc
[...])
->
str
:
"""Return the fully qualified name of a method or None if a method wasn't passed in."""
try
:
return
f"
{
command_method
.
__module__
}
.
{
command_method
.
__qualname__
}
"
except
AttributeError
:
return
""
def
__contains__
(
self
,
command_method
:
BoundCommandFunc
[...])
->
bool
:
"""Return whether a given method's parser is in self.
If the parser does not yet exist, it will be created if applicable.
This is basically for checking if a method is argarse-based.
"""
parser
=
self
.
get
(
command_method
)
return
bool
(
parser
)
def
get
(
self
,
command_method
:
BoundCommandFunc
[...])
->
Cmd2ArgumentParser
|
None
:
"""Return a given method's parser or None if the method is not argparse-based.
If the parser does not yet exist, it will be created.
"""
full_method_name
=
self
.
_fully_qualified_name
(
command_method
)
if
not
full_method_name
:
return
None
if
full_method_name
not
in
self
.
_parsers
:
if
not
command_method
.
__name__
.
startswith
(
COMMAND_FUNC_PREFIX
):
return
None
command
=
command_method
.
__name__
[
len
(
COMMAND_FUNC_PREFIX
) :]
spec
:
ArgparseCommandSpec
|
None
=
getattr
(
command_method
,
constants
.
ARGPARSE_COMMAND_ATTR_SPEC
,
None
)
if
spec
is
None
:
return
None
owner
=
self
.
_cmd_app
.
find_commandset_for_command
(
command
)
or
self
.
_cmd_app
parser
=
self
.
_cmd_app
.
_build_parser
(
owner
,
spec
.
parser_source
)
# To ensure accurate usage strings, recursively update 'prog' values
# within the parser to match the command name.
parser
.
update_prog
(
command
)
# If the description has not been set, then use the method docstring if one exists
if
parser
.
description
is
None
and
command_method
.
__doc__
:
parser
.
description
=
strip_doc_annotations
(
command_method
.
__doc__
)
self
.
_parsers
[
full_method_name
]
=
parser
return
self
.
_parsers
.
get
(
full_method_name
)
def
remove
(
self
,
command_method
:
BoundCommandFunc
[...])
->
None
:
"""Remove a given method's parser if it exists."""
full_method_name
=
self
.
_fully_qualified_name
(
command_method
)
if
full_method_name
in
self
.
_parsers
:
del
self
.
_parsers
[
full_method_name
]
@
dataclass
(
kw_only
=
True
)
class
AsyncAlert
:
"""Contents of an asynchronous alert which display while user is at prompt.
:param msg: an optional printable object (including Rich renderables) to be
printed above the prompt.
:param soft_wrap: Enable soft wrap mode. This only applies with msg is not None.
Defaults to True. See print_to() docstring for more details on
this parameter.
:param prompt: an optional string to dynamically replace the current prompt.
:ivar timestamp: monotonic creation time of the alert. If an alert was created
before the current prompt was rendered, its prompt data is ignored
to avoid a stale display, but its msg data will still be displayed.
"""
msg
:
Any
|
None
=
None
soft_wrap
:
bool
=
True
prompt
:
str
|
None
=
None
timestamp
:
float
=
field
(
default_factory
=
time
.
monotonic
,
init
=
False
)
@
dataclass
class
_ConsoleCache
(
threading
.
local
):
"""Thread-local storage for cached Rich consoles used by core print methods."""
stdout
:
Cmd2BaseConsole
|
None
=
None
stderr
:
Cmd2BaseConsole
|
None
=
None
class
Cmd
:
"""An easy but powerful framework for writing line-oriented command interpreters.
Extends the Python Standard Library's cmd package by adding a lot of useful features
to the out of the box configuration.
Line-oriented command interpreters are often useful for test harnesses, internal tools, and rapid prototypes.
"""
DEFAULT_COMPLETEKEY
:
ClassVar
[
str
]
=
"tab"
DEFAULT_EDITOR
:
ClassVar
[
str
|
None
]
=
utils
.
find_editor
()
DEFAULT_PROMPT
:
ClassVar
[
str
]
=
"(Cmd) "
# Default category for commands defined in this class which have
# not been explicitly categorized with the @with_category decorator.
# This value is inherited by subclasses but they can set their own
# DEFAULT_CATEGORY to place their commands into a custom category.
# Subclasses can also reassign cmd2.Cmd.DEFAULT_CATEGORY to rename
# the category used for the framework's built-in commands.
DEFAULT_CATEGORY
:
ClassVar
[
str
]
=
"Cmd2 Commands"
# Header for table listing help topics not related to a command.
MISC_HEADER
:
ClassVar
[
str
]
=
"Miscellaneous Help Topics"
def
__init__
(
self
,
completekey
:
str
|
None
=
None
,
stdin
:
TextIO
|
None
=
None
,
stdout
:
TextIO
|
None
=
None
,
*
,
allow_cli_args
:
bool
=
True
,
allow_clipboard
:
bool
=
True
,
allow_redirection
:
bool
=
True
,
auto_load_commands
:
bool
=
False
,
auto_suggest
:
bool
=
True
,
complete_in_thread
:
bool
=
True
,
command_sets
:
Iterable
[
CommandSet
[
Any
]]
|
None
=
None
,
enable_bottom_toolbar
:
bool
=
False
,
enable_rprompt
:
bool
=
False
,
include_ipy
:
bool
=
False
,
include_py
:
bool
=
False
,
intro
:
RenderableType
=
""
,
multiline_commands
:
Iterable
[
str
]
|
None
=
None
,
persistent_history_file
:
str
=
""
,
persistent_history_length
:
int
=
1000
,
refresh_interval
:
float
=
0.0
,
shortcuts
:
Mapping
[
str
,
str
]
|
None
=
None
,
silence_startup_script
:
bool
=
False
,
startup_script
:
str
=
""
,
suggest_similar_command
:
bool
=
False
,
terminators
:
Iterable
[
str
]
|
None
=
None
,
)
->
None
:
"""Easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.
:param completekey: name of a completion key, default to 'tab'. (If None or an empty string, 'tab' is used)
:param stdin: alternate input file object, if not specified, sys.stdin is used
:param stdout: alternate output file object, if not specified, sys.stdout is used
:param allow_cli_args: if ``True``, then [cmd2.Cmd.__init__][] will process command
line arguments as either commands to be run. This should be
set to ``False`` if your application parses its own command line
arguments.
:param allow_clipboard: If False, cmd2 will disable clipboard interactions
:param allow_redirection: If ``False``, prevent output redirection and piping to shell
commands. This parameter prevents redirection and piping, but
does not alter parsing behavior. A user can still type
redirection and piping tokens, and they will be parsed as such
but they won't do anything.
:param auto_load_commands: If True, cmd2 will check for all subclasses of `CommandSet`
that are currently loaded by Python and automatically
instantiate and register all commands. If False, CommandSets
must be manually installed with `register_command_set`.
:param auto_suggest: If True, cmd2 will provide fish shell style auto-suggestions
based on history. User can press right-arrow key to accept the
provided suggestion.
:param complete_in_thread: if ``True``, then completion will run in a separate thread.
:param command_sets: Provide CommandSet instances to load during cmd2 initialization.
This allows CommandSets with custom constructor parameters to be
loaded. This also allows the a set of CommandSets to be provided
when `auto_load_commands` is set to False
:param enable_bottom_toolbar: if ``True``, enables a bottom toolbar while at the main prompt.
Override ``get_bottom_toolbar()`` to define its content.
:param enable_rprompt: if ``True``, enables a right prompt while at the main prompt.
Override ``get_rprompt()`` to define its content.
:param include_ipy: should the "ipy" command be included for an embedded IPython shell
:param include_py: should the "py" command be included for an embedded Python shell
:param intro: introduction to display at startup
:param multiline_commands: Iterable of commands allowed to accept multi-line input
:param persistent_history_file: file path to load a persistent cmd2 command history from
:param persistent_history_length: max number of history items to write
to the persistent history file
:param refresh_interval: How often, in seconds, to refresh the UI. Defaults to 0.0.
prompt-toolkit already refreshes the UI every time a key is pressed.
Set this value if you need the UI to update automatically without
user input (e.g., for displaying a clock or background status
updates in the bottom toolbar).
:param shortcuts: Mapping containing shortcuts for commands. If not supplied,
then defaults to constants.DEFAULT_SHORTCUTS. If you do not want
any shortcuts, pass None and an empty dictionary will be created.
:param silence_startup_script: if ``True``, then the startup script's output will be
suppressed. Anything written to stderr will still display.
:param startup_script: file path to a script to execute at startup
:param suggest_similar_command: if ``True``, then when a command is not found,
[cmd2.Cmd][] will look for similar commands and suggest them.
:param terminators: Iterable of characters that terminate a command. These are mainly
intended for terminating multiline commands, but will also
terminate single-line commands. If not supplied, the default
is a semicolon. If your app only contains single-line commands
and you want terminators to be treated as literals by the parser,
then set this to None.
"""
# Check if py or ipy need to be disabled in this instance
if
not
include_py
:
setattr
(
self
,
"do_py"
,
None
)
# noqa: B010
if
not
include_ipy
:
setattr
(
self
,
"do_ipy"
,
None
)
# noqa: B010
# initialize plugin system
# needs to be done before we most of the other stuff below
self
.
_initialize_plugin_system
()
# Configure a few defaults
self
.
prompt
:
str
=
self
.
DEFAULT_PROMPT
self
.
intro
=
intro
if
not
completekey
:
completekey
=
self
.
DEFAULT_COMPLETEKEY
# What to use for standard input
if
stdin
is
not
None
:
self
.
stdin
=
stdin
else
:
self
.
stdin
=
sys
.
stdin
# Standard output stream. The interactive UI remains attached to this initial
# stream even when self.stdout is temporarily swapped during command output
# redirection.
if
stdout
is
not
None
:
self
.
stdout
=
stdout
else
:
self
.
stdout
=
sys
.
stdout
# Attributes which should NOT be dynamically settable via the set command at runtime
self
.
allow_redirection
=
allow_redirection
# Security setting to prevent redirection of stdout
# If True, cmd2 treats redirected input (pipes/files) as an interactive session.
# It will display the prompt before reading each line to synchronize with
# automation tools (like Pexpect) and will skip echoing the input to prevent
# duplicate prompts in the output.
self
.
interactive_pipe
=
False
# Attributes which ARE dynamically settable via the set command at runtime
self
.
debug
=
False
self
.
echo
=
False
self
.
editor
=
self
.
DEFAULT_EDITOR
self
.
quiet
=
False
# Do not suppress nonessential output
self
.
scripts_add_to_history
=
True
# Scripts and pyscripts add commands to history
self
.
timing
=
False
# Prints elapsed time for each command
# Default settings for Rich tracebacks created by format_exception().
# This dictionary can contain any parameter accepted by the
# rich.traceback.Traceback class. You can modify it to adjust
# the detail and layout of tracebacks.
self
.
traceback_kwargs
:
dict
[
str
,
Any
]
=
{
"width"
:
100
,
"code_width"
:
None
,
# Show all code characters
"show_locals"
:
False
,
"max_frames"
:
100
,
"word_wrap"
:
True
,
# Wrap long lines of code instead of truncate
"indent_guides"
:
True
,
}
# Cached Rich consoles used by core print methods.
self
.
_console_cache
=
_ConsoleCache
()
# The maximum number of items to display in a completion table. If the number of completion
# suggestions exceeds this number, then no table will appear.
self
.
max_completion_table_items
:
int
=
50
# The maximum number of completion results to display in a single column (CompleteStyle.COLUMN).
# If the number of results exceeds this, CompleteStyle.MULTI_COLUMN will be used.
self
.
max_column_completion_results
:
int
=
7
# A dictionary mapping settable names to their Settable instance
self
.
_settables
:
dict
[
str
,
Settable
]
=
{}
self
.
_always_prefix_settables
:
bool
=
False
# CommandSet containers
self
.
_installed_command_sets
:
set
[
CommandSet
[
Any
]]
=
set
()
self
.
_cmd_to_command_sets
:
dict
[
str
,
CommandSet
[
Any
]]
=
{}
self
.
build_settables
()
# Use as prompt for multiline commands on the 2nd+ line of input
self
.
continuation_prompt
:
str
=
"> "
# Allow access to your application in embedded Python shells and pyscripts via self
self
.
self_in_py
=
False
# Commands to exclude from the help menu and completion
self
.
hidden_commands
=
[
"_eof"
,
"_relative_run_script"
]
# Initialize history from a persistent history file (if present)
self
.
persistent_history_file
=
""
self
.
_persistent_history_length
=
persistent_history_length
self
.
_initialize_history
(
persistent_history_file
)
# Create the main PromptSession
self
.
main_session
=
self
.
_create_main_session
(
auto_suggest
=
auto_suggest
,
complete_in_thread
=
complete_in_thread
,
completekey
=
completekey
,
enable_bottom_toolbar
=
enable_bottom_toolbar
,
enable_rprompt
=
enable_rprompt
,
refresh_interval
=
refresh_interval
,
)
# The session currently holding focus (either the main REPL or a command's
# custom prompt). Completion and UI logic should reference this variable
# to ensure they modify the correct session state.
self
.
active_session
=
self
.
main_session
# Commands to exclude from the history command
self
.
exclude_from_history
=
[
"_eof"
,
"history"
]
# Dictionary of macro names and their values
self
.
macros
:
dict
[
str
,
Macro
]
=
{}
# Keeps track of typed command history in the Python shell
self
.
_py_history
:
list
[
str
]
=
[]
# The name by which Python environments refer to the PyBridge to call app commands
self
.
py_bridge_name
=
"app"
# Defines app-specific variables/functions available in Python shells and pyscripts
self
.
py_locals
:
dict
[
str
,
Any
]
=
{}
# True if running inside a Python shell or pyscript, False otherwise
self
.
_in_py
=
False
self
.
statement_parser
:
StatementParser
=
StatementParser
(
terminators
=
terminators
,
multiline_commands
=
multiline_commands
,
shortcuts
=
shortcuts
)
# Stores results from the last command run to enable usage of results in Python shells and pyscripts
self
.
last_result
:
Any
=
None
# Used by run_script command to store current script dir as a LIFO queue to support _relative_run_script command
self
.
_script_dir
:
list
[
str
]
=
[]
# Context manager used to protect critical sections in the main thread from stopping due to a KeyboardInterrupt
self
.
sigint_protection
:
utils
.
ContextFlag
=
utils
.
ContextFlag
()
# If the current command created a process to pipe to, then this will be a ProcReader object.
# Otherwise it will be None. It's used to know when a pipe process can be killed and/or waited upon.
self
.
_cur_pipe_proc_reader
:
utils
.
ProcReader
|
None
=
None
# Used to keep track of whether we are redirecting or piping output
self
.
_redirecting
=
False
# Set text which prints right before all of the help tables are listed.
self
.
doc_leader
=
""
# The error that prints when no help information can be found
self
.
help_error
=
"No help on {}"
# The error that prints when a non-existent command is run
self
.
default_error
=
"{} is not a recognized command, alias, or macro."
# If non-empty, this string will be displayed if a broken pipe error occurs
self
.
broken_pipe_warning
=
""
# Commands that will run at the beginning of the command loop
self
.
_startup_commands
:
list
[
str
]
=
[]
# Store initial termios settings to restore after each command.
# This is a faster way of accomplishing what "stty sane" does.
self
.
_initial_termios_settings
=
None
if
not
sys
.
platform
.
startswith
(
"win"
)
and
self
.
stdin
.
isatty
():
try
:
import
io
import
termios
self
.
_initial_termios_settings
=
termios
.
tcgetattr
(
self
.
stdin
.
fileno
())
except
(
ImportError
,
io
.
UnsupportedOperation
,
termios
.
error
):
# This can happen if termios isn't available or stdin is a pseudo-TTY
self
.
_initial_termios_settings
=
None
# If a startup script is provided and exists, then execute it in the startup commands
if
startup_script
:
startup_script
=
os
.
path
.
abspath
(
os
.
path
.
expanduser
(
startup_script
))
if
os
.
path
.
exists
(
startup_script
):
script_cmd
=
f"run_script
{
su
.
quote
(
startup_script
)
}
"
if
silence_startup_script
:
script_cmd
+=
f"
{
constants
.
REDIRECTION_OVERWRITE
}
{
os
.
devnull
}
"
self
.
_startup_commands
.
append
(
script_cmd
)
# Check for command line args
if
allow_cli_args
:
parser
=
argparse_utils
.
DEFAULT_ARGUMENT_PARSER
()
_callopts
,
callargs
=
parser
.
parse_known_args
()
# If commands were supplied at invocation, then add them to the command queue
if
callargs
:
self
.
_startup_commands
.
extend
(
callargs
)
# Set the pager(s) for use when displaying output using a pager
if
sys
.
platform
.
startswith
(
"win"
):
self
.
pager
=
self
.
pager_chop
=
"more"
else
:
# Here is the meaning of the various flags we are using with the less command:
# -S causes lines longer than the screen width to be chopped (truncated) rather than wrapped
# -R causes ANSI "style" escape sequences to be output in raw form (i.e. colors are displayed)
# -X disables sending the termcap initialization and deinitialization strings to the terminal
# -F causes less to automatically exit if the entire file can be displayed on the first screen
self
.
pager
=
"less -RXF"
self
.
pager_chop
=
"less -SRXF"
# This boolean flag stores whether cmd2 will allow clipboard related features
self
.
allow_clipboard
=
allow_clipboard
# This determines the value returned by cmdloop() when exiting the application
self
.
exit_code
=
0
# Commands disabled during specific application states
# Key: Command name | Value: DisabledCommand object
# NOTE: Use disable_command() and enable_command() to modify this dictionary.
self
.
disabled_commands
:
dict
[
str
,
DisabledCommand
]
=
{}
# Categories of commands to be disabled
# Key: Category name | Value: Message to display
# NOTE: Use disable_category() and enable_category() to modify this dictionary.
self
.
disabled_categories
:
dict
[
str
,
str
]
=
{}
# Command parsers for this Cmd instance.
self
.
command_parsers
:
CommandParsers
=
CommandParsers
(
self
)
# Members related to printing asynchronous alerts
self
.
_alert_queue
:
deque
[
AsyncAlert
]
=
deque
()
self
.
_alert_condition
=
threading
.
Condition
()
self
.
_alert_allowed
=
False
self
.
_alert_shutdown
=
False
self
.
_alert_thread
:
threading
.
Thread
|
None
=
None
self
.
_alert_prompt_timestamp
:
float
=
0.0
# Uses time.monotonic()
# Add functions decorated to be subcommands
self
.
_register_subcommands
(
self
)
############################################################################################################
# The following code block loads CommandSets, verifies command names, and registers subcommands.
# This block should appear after all attributes have been created since the registration code
# depends on them and it's possible a module's on_register() method may need to access some.
############################################################################################################
# Load modular commands
if
command_sets
:
for
command_set
in
command_sets
:
self
.
register_command_set
(
command_set
)
if
auto_load_commands
:
self
.
_autoload_commands
()
# Verify commands don't have invalid names (like starting with a shortcut)
for
cur_cmd
in
self
.
get_all_commands
():
valid
,
errmsg
=
self
.
statement_parser
.
is_valid_command
(
cur_cmd
)
if
not
valid
:
raise
ValueError
(
f"Invalid command name '
{
cur_cmd
}
':
{
errmsg
}
"
)
self
.
suggest_similar_command
=
suggest_similar_command
self
.
default_suggestion_message
=
"Did you mean {}?"
# the current command being executed
self
.
current_command
:
Statement
|
None
=
None
def
_should_continue_multiline
(
self
)
->
bool
:
"""Return whether prompt-toolkit should continue prompting the user for a multiline command."""
buffer
:
Buffer
=
get_app
().
current_buffer
line
:
str
=
buffer
.
text
used_macros
=
[]
# Continue until all macros are resolved
while
True
:
try
:
statement
=
self
.
_check_statement_complete
(
line
)
except
IncompleteStatement
:
# The statement (or the resolved macro) is incomplete.
# Keep prompting the user.
return
True
except
(
Cmd2ShlexError
,
EmptyStatement
):
# These are "finished" states (even if they are errors).
# Submit so the main loop can handle the exception.
return
False
# Check if this command matches a macro and wasn't already processed to avoid an infinite loop
if
statement
.
command
in
self
.
macros
and
statement
.
command
not
in
used_macros
:
used_macros
.
append
(
statement
.
command
)
try
:
line
=
self
.
_resolve_macro
(
statement
)
except
MacroError
:
# Resolve failed. Submit to let the main loop handle the error.
return
False
else
:
# No macro found or already processed. The statement is complete.
return
False
def
_create_key_bindings
(
self
,
completekey
:
str
)
->
KeyBindings
:
"""Create and configure custom key bindings for the PromptSession."""
key_bindings
=
KeyBindings
()
if
completekey
!=
self
.
DEFAULT_COMPLETEKEY
:
@
key_bindings
.
add
(
completekey
)
def
_trigger_completion
(
event
:
KeyPressEvent
)
->
None
:
# pragma: no cover
"""Trigger completion using the custom completion key."""
b
=
event
.
current_buffer
if
b
.
complete_state
:
b
.
complete_next
()
else
:
b
.
start_completion
(
select_first
=
False
)
@
key_bindings
.
add
(
"enter"
,
filter
=
filters
.
completion_is_selected
)
def
_accept_completion
(
event
:
KeyPressEvent
)
->
None
:
# pragma: no cover
"""Accept a selected completion on Enter without submitting the command."""
event
.
current_buffer
.
complete_state
=
None
@
key_bindings
.
add
(
Keys
.
BracketedPaste
)
def
_handle_bracketed_paste
(
event
:
KeyPressEvent
)
->
None
:
"""Handle bracketed paste by feeding lines as keystrokes separated by Enter.
By default, prompt_toolkit inserts pasted text as a single buffer blob.
Translating newlines into Enter keystrokes allows multiple pasted commands
to execute sequentially and multiline commands to continue as expected.
"""
data
=
event
.
data
.
replace
(
"
\r
\n
"
,
"
\n
"
).
replace
(
"
\r
"
,
"
\n
"
)
if
"
\n
"
not
in
data
:
event
.
current_buffer
.
insert_text
(
data
)
return
key_presses
=
[]
for
i
,
line
in
enumerate
(
data
.
split
(
"
\n
"
)):
if
i
>
0
:
key_presses
.
append
(
KeyPress
(
Keys
.
ControlM
,
"
\r
"
))
if
line
:
key_presses
.
append
(
KeyPress
(
Keys
.
Any
,
line
))
event
.
key_processor
.
feed_multiple
(
key_presses
)
return
key_bindings
def
_create_main_session
(
self
,
*
,
auto_suggest
:
bool
,
complete_in_thread
:
bool
,
completekey
:
str
,
enable_bottom_toolbar
:
bool
,
enable_rprompt
:
bool
,
refresh_interval
:
float
,
)
->
PromptSession
[
str
]:
"""Create and return the main PromptSession for the application.
Builds an interactive session if self.stdin and self.stdout are TTYs.
Otherwise, uses dummy drivers to support non-interactive streams like
pipes or files.
"""
# Base configuration
kwargs
:
dict
[
str
,
Any
]
=
{
"auto_suggest"
:
AutoSuggestFromHistory
()
if
auto_suggest
else
None
,
"bottom_toolbar"
:
self
.
get_bottom_toolbar
if
enable_bottom_toolbar
else
None
,
"color_depth"
:
pt_resolve_color_depth
(),
"complete_style"
:
CompleteStyle
.
MULTI_COLUMN
,
"complete_in_thread"
:
complete_in_thread
,
"complete_while_typing"
:
False
,
"completer"
:
Cmd2Completer
(
self
),
"enable_suspend"
:
True
,
"history"
:
Cmd2History
(
item
.
raw
for
item
in
self
.
history
),
"key_bindings"
:
self
.
_create_key_bindings
(
completekey
),
"lexer"
:
Cmd2Lexer
(
self
),
"multiline"
:
filters
.
Condition
(
self
.
_should_continue_multiline
),
"prompt_continuation"
:
self
.
continuation_prompt
,
"refresh_interval"
:
refresh_interval
,
"rprompt"
:
self
.
get_rprompt
if
enable_rprompt
else
None
,
"style"
:
DynamicStyle
(
get_pt_theme
),
}
if
self
.
stdin
.
isatty
()
and
self
.
stdout
.
isatty
():
try
:
if
self
.
stdin
!=
sys
.
stdin
:
kwargs
[
"input"
]
=
create_input
(
stdin
=
self
.
stdin
)
if
self
.
stdout
!=
sys
.
stdout
:
kwargs
[
"output"
]
=
create_output
(
stdout
=
self
.
stdout
)
return
PromptSession
(
**
kwargs
)
except
(
NoConsoleScreenBufferError
,
AttributeError
,
ValueError
):
# Fallback to dummy input/output if PromptSession initialization fails.
# This can happen in some CI environments (like GitHub Actions on Windows)
# where isatty() is True but there is no real console.
pass
# Fallback to dummy drivers for non-interactive environments.
kwargs
.
update
(
{
"input"
:
DummyInput
(),
"output"
:
DummyOutput
(),
}
)
return
PromptSession
(
**
kwargs
)
def
find_commandsets
(
self
,
commandset_type
:
type
[
CommandSet
[
Any
]],
*
,
subclass_match
:
bool
=
False
)
->
list
[
CommandSet
[
Any
]]:
"""Find all CommandSets that match the provided CommandSet type.
By default, locates a CommandSet that is an exact type match but may optionally return all CommandSets that
are sub-classes of the provided type
:param commandset_type: CommandSet sub-class type to search for
:param subclass_match: If True, return all sub-classes of provided type, otherwise only search for exact match
:return: Matching CommandSets
"""
return
[
cmdset
for
cmdset
in
self
.
_installed_command_sets
if
type
(
cmdset
)
==
commandset_type
or
(
subclass_match
and
isinstance
(
cmdset
,
commandset_type
))
# noqa: E721
]
def
find_commandset_for_command
(
self
,
command_name
:
str
)
->
CommandSet
[
Any
]
|
None
:
"""Find the CommandSet that registered the command name.
:param command_name: command name to search
:return: CommandSet that provided the command
"""
return
self
.
_cmd_to_command_sets
.
get
(
command_name
)
def
_autoload_commands
(
self
)
->
None
:
"""Load modular command definitions."""
# Search for all subclasses of CommandSet, instantiate them if they weren't already provided in the constructor
all_commandset_defs
=
CommandSet
.
__subclasses__
()
existing_commandset_types
=
[
type
(
command_set
)
for
command_set
in
self
.
_installed_command_sets
]
def
load_commandset_by_type
(
commandset_types
:
Sequence
[
type
[
CommandSet
[
Any
]]])
->
None
:
for
cmdset_type
in
commandset_types
:
# check if the type has sub-classes. We will only auto-load leaf class types.
subclasses
=
cmdset_type
.
__subclasses__
()
if
subclasses
:
load_commandset_by_type
(
subclasses
)
else
:
init_sig
=
inspect
.
signature
(
cmdset_type
.
__init__
)
if
not
(
cmdset_type
in
existing_commandset_types
or
len
(
init_sig
.
parameters
)
!=
1
or
"self"
not
in
init_sig
.
parameters
):
cmdset
=
cmdset_type
()
self
.
register_command_set
(
cmdset
)
load_commandset_by_type
(
all_commandset_defs
)
def
register_command_set
(
self
,
cmdset
:
CommandSet
[
Any
])
->
None
:
"""Installs a CommandSet, loading all commands defined in the CommandSet.
:param cmdset: CommandSet to load
"""
existing_commandset_types
=
[
type
(
command_set
)
for
command_set
in
self
.
_installed_command_sets
]
if
type
(
cmdset
)
in
existing_commandset_types
:
raise
CommandSetRegistrationError
(
"CommandSet "
+
type
(
cmdset
).
__name__
+
" is already installed"
)
all_settables
=
self
.
settables
if
self
.
always_prefix_settables
:
if
not
cmdset
.
settable_prefix
.
strip
():
raise
CommandSetRegistrationError
(
"CommandSet settable prefix must not be empty"
)
for
key
in
cmdset
.
settables
:
prefixed_name
=
f"
{
cmdset
.
settable_prefix
}
.
{
key
}
"
if
prefixed_name
in
all_settables
:
raise
CommandSetRegistrationError
(
f"Duplicate settable:
{
key
}
"
)
else
:
for
key
in
cmdset
.
settables
:
if
key
in
all_settables
:
raise
CommandSetRegistrationError
(
f"Duplicate settable
{
key
}
is already registered"
)
cmdset
.
on_register
(
self
)
methods
=
cast
(
list
[
tuple
[
str
,
BoundCommandFunc
[...]]],
inspect
.
getmembers
(
cmdset
,
predicate
=
lambda
meth
: (
# type: ignore[arg-type]
isinstance
(
meth
,
Callable
)
# type: ignore[arg-type]
and
hasattr
(
meth
,
"__name__"
)
and
meth
.
__name__
.
startswith
(
COMMAND_FUNC_PREFIX
)
),
),
)
installed_attributes
=
[]
try
:
for
cmd_func_name
,
command_method
in
methods
:
command
=
cmd_func_name
[
len
(
COMMAND_FUNC_PREFIX
) :]
self
.
_install_command_function
(
cmd_func_name
,
command_method
,
type
(
cmdset
).
__name__
)
installed_attributes
.
append
(
cmd_func_name
)
completer_func_name
=
COMPLETER_FUNC_PREFIX
+
command
cmd_completer
=
getattr
(
cmdset
,
completer_func_name
,
None
)
if
cmd_completer
is
not
None
:
self
.
_install_completer_function
(
command
,
cmd_completer
)
installed_attributes
.
append
(
completer_func_name
)
help_func_name
=
HELP_FUNC_PREFIX
+
command
cmd_help
=
getattr
(
cmdset
,
help_func_name
,
None
)
if
cmd_help
is
not
None
:
self
.
_install_help_function
(
command
,
cmd_help
)
installed_attributes
.
append
(
help_func_name
)
self
.
_cmd_to_command_sets
[
command
]
=
cmdset
# If this command is in a disabled category, then disable it
command_category
=
self
.
_get_command_category
(
command_method
)
if
command_category
in
self
.
disabled_categories
:
message_to_print
=
self
.
disabled_categories
[
command_category
]
self
.
disable_command
(
command
,
message_to_print
)
self
.
_installed_command_sets
.
add
(
cmdset
)
self
.
_register_subcommands
(
cmdset
)
cmdset
.
on_registered
()
except
Exception
:
cmdset
.
on_unregister
()
for
attrib
in
installed_attributes
:
delattr
(
self
,
attrib
)
if
cmdset
in
self
.
_installed_command_sets
:
self
.
_installed_command_sets
.
remove
(
cmdset
)
if
cmdset
in
self
.
_cmd_to_command_sets
.
values
():
self
.
_cmd_to_command_sets
=
{
key
:
val
for
key
,
val
in
self
.
_cmd_to_command_sets
.
items
()
if
val
is
not
cmdset
}
cmdset
.
on_unregistered
()
raise
def
_build_parser
(
self
,
owner
:
CmdOrSet
,
parser_source
:
ParserSource
[
Any
],
)
->
Cmd2ArgumentParser
:
"""Build argument parser for a command/subcommand.
:param owner: the object that owns the command. If parser_source requires
a class argument (like a classmethod), this object's class is passed.
:param parser_source: an existing Cmd2ArgumentParser instance or a factory
(callable, staticmethod, or classmethod) that returns one.
:return: new parser
:raises TypeError: if parser_source is an invalid type or if the factory fails
to return a Cmd2ArgumentParser
"""
# Handle existing parser
if
isinstance
(
parser_source
,
argparse
.
ArgumentParser
):
if
not
isinstance
(
parser_source
,
Cmd2ArgumentParser
):
raise
TypeError
(
f"The parser must be an instance of 'Cmd2ArgumentParser' (or subclass). "
f"Received: '
{
type
(
parser_source
).
__name__
}
'."
)
return
copy
.
deepcopy
(
parser_source
)
# Handle factories
View remainder of file in raw view
Footer
© 2026 GitHub, Inc.
Footer navigation
Terms
Privacy
Security
Status
Community
Docs
Contact
You can’t perform that action at this time.
Back
|
FazBrowse Home
|
New Git URL