Skip to content

Commit 88b45e1

Browse files
committed
First stage of attribute refactoring
The following are now arguments to cmd2.Cmd.__init__() instead of class attributes: * allow_redirection * multiline_commands * terminators * shortcuts Added a couple read-only properties for convenience of cmd2.Cmd accessing immutable members from self.statement_parser
1 parent 23fad46 commit 88b45e1

File tree

14 files changed

+50
-53
lines changed

14 files changed

+50
-53
lines changed

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ Instructions for implementing each feature follow.
185185

186186
- Multi-line commands
187187

188-
Any command accepts multi-line input when its name is listed in `Cmd.multiline_commands`.
189-
The program will keep expecting input until a line ends with any of the characters
190-
in `Cmd.terminators` . The default terminators are `;` and `/n` (empty newline).
188+
Any command accepts multi-line input when its name is listed the `multiline_commands` optional argument to
189+
`cmd2.Cmd.__init`. The program will keep expecting input until a line ends with any of the characters listed in the
190+
`terminators` optional argument to `cmd2.Cmd.__init__()` . The default terminators are `;` and `/n` (empty newline).
191191

192192
- Special-character shortcut commands (beyond cmd's "@" and "!")
193193

@@ -239,14 +239,13 @@ class CmdLineApp(cmd2.Cmd):
239239
MUMBLE_LAST = ['right?']
240240

241241
def __init__(self):
242-
self.multiline_commands = ['orate']
243242
self.maxrepeats = 3
244243

245244
# Add stuff to shortcuts before calling base class initializer
246245
self.shortcuts.update({'&': 'speak'})
247246

248247
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
249-
super().__init__(use_ipython=False)
248+
super().__init__(use_ipython=False, multiline_commands=['orate'])
250249

251250
# Make maxrepeats settable at runtime
252251
self.settable['maxrepeats'] = 'max repetitions for speak command'

cmd2/cmd2.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -293,14 +293,10 @@ class Cmd(cmd.Cmd):
293293
294294
Line-oriented command interpreters are often useful for test harnesses, internal tools, and rapid prototypes.
295295
"""
296-
# Attributes used to configure the StatementParser, best not to change these at runtime
297-
multiline_commands = []
298-
shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
299-
terminators = [constants.MULTILINE_TERMINATOR]
296+
DEFAULT_SHORTCUTS = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
300297

301298
# Attributes which are NOT dynamically settable at runtime
302299
allow_cli_args = True # Should arguments passed on the command-line be processed as commands?
303-
allow_redirection = True # Should output redirection and pipes be allowed
304300
default_to_shell = False # Attempt to run unrecognized commands as shell commands
305301
quit_on_sigint = False # Quit the loop on interrupt instead of just resetting prompt
306302
reserved_words = []
@@ -338,7 +334,9 @@ class Cmd(cmd.Cmd):
338334

339335
def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, persistent_history_file: str = '',
340336
persistent_history_length: int = 1000, startup_script: Optional[str] = None, use_ipython: bool = False,
341-
transcript_files: Optional[List[str]] = None) -> None:
337+
transcript_files: Optional[List[str]] = None, allow_redirection: bool = True,
338+
multiline_commands: Optional[List[str]] = None, terminators: Optional[List[str]] = None,
339+
shortcuts: Optional[Dict[str, str]] = None) -> None:
342340
"""An easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.
343341
344342
:param completekey: (optional) readline name of a completion key, default to Tab
@@ -349,6 +347,9 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, persistent
349347
:param startup_script: (optional) file path to a a script to load and execute at startup
350348
:param use_ipython: (optional) should the "ipy" command be included for an embedded IPython shell
351349
:param transcript_files: (optional) allows running transcript tests when allow_cli_args is False
350+
:param allow_redirection: (optional) should output redirection and pipes be allowed
351+
:param multiline_commands: (optional) list of commands allowed to accept multi-line input
352+
:param shortcuts: (optional) dictionary containing shortcuts for commands
352353
"""
353354
# If use_ipython is False, make sure the do_ipy() method doesn't exit
354355
if not use_ipython:
@@ -377,21 +378,21 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, persistent
377378
self.aliases = dict()
378379
self.macros = dict()
379380

380-
self._finalize_app_parameters()
381-
382381
self.initial_stdout = sys.stdout
383382
self.history = History()
384383
self.pystate = {}
385384
self.py_history = []
386385
self.pyscript_name = 'app'
387386
self.keywords = self.reserved_words + self.get_all_commands()
388-
self.statement_parser = StatementParser(
389-
allow_redirection=self.allow_redirection,
390-
terminators=self.terminators,
391-
multiline_commands=self.multiline_commands,
392-
aliases=self.aliases,
393-
shortcuts=self.shortcuts,
394-
)
387+
388+
if shortcuts is None:
389+
shortcuts = self.DEFAULT_SHORTCUTS
390+
shortcuts = sorted(shortcuts.items(), reverse=True)
391+
self.statement_parser = StatementParser(allow_redirection=allow_redirection,
392+
terminators=terminators,
393+
multiline_commands=multiline_commands,
394+
aliases=self.aliases,
395+
shortcuts=shortcuts)
395396
self._transcript_files = transcript_files
396397

397398
# Used to enable the ability for a Python script to quit the application
@@ -545,10 +546,15 @@ def visible_prompt(self) -> str:
545546
"""
546547
return utils.strip_ansi(self.prompt)
547548

548-
def _finalize_app_parameters(self) -> None:
549-
"""Finalize the shortcuts"""
550-
# noinspection PyUnresolvedReferences
551-
self.shortcuts = sorted(self.shortcuts.items(), reverse=True)
549+
@property
550+
def allow_redirection(self) -> bool:
551+
"""Read-only property to get whether or not redirection of stdout is allowed."""
552+
return self.statement_parser.allow_redirection
553+
554+
@property
555+
def shortcuts(self) -> List[Tuple[str, str]]:
556+
"""Read-only property to access the shortcuts stored in the StatementParser."""
557+
return self.statement_parser.shortcuts
552558

553559
def decolorized_write(self, fileobj: IO, msg: str) -> None:
554560
"""Write a string to a fileobject, stripping ANSI escape sequences if necessary
@@ -2792,7 +2798,8 @@ def cmdenvironment(self) -> str:
27922798
Commands may be terminated with: {}
27932799
Arguments at invocation allowed: {}
27942800
Output redirection and pipes allowed: {}"""
2795-
return read_only_settings.format(str(self.terminators), self.allow_cli_args, self.allow_redirection)
2801+
return read_only_settings.format(str(self.statement_parser.terminators), self.allow_cli_args,
2802+
self.allow_redirection)
27962803

27972804
def show(self, args: argparse.Namespace, parameter: str = '') -> None:
27982805
"""Shows current settings of parameters.

docs/settingchanges.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ To define more shortcuts, update the dict ``App.shortcuts`` with the
4444
Shortcuts need to be created by updating the ``shortcuts`` dictionary attribute prior to calling the
4545
``cmd2.Cmd`` super class ``__init__()`` method. Moreover, that super class init method needs to be called after
4646
updating the ``shortcuts`` attribute This warning applies in general to many other attributes which are not
47-
settable at runtime such as ``multiline_commands``, etc.
47+
settable at runtime.
4848

4949

5050
Aliases

docs/unfreefeatures.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ Multiline commands
77

88
Command input may span multiple lines for the
99
commands whose names are listed in the
10-
parameter ``app.multiline_commands``. These
10+
``multiline_commands`` argument to ``cmd2.Cmd.__init__()``. These
1111
commands will be executed only
1212
after the user has entered a *terminator*.
1313
By default, the command terminator is
14-
``;``; replacing or appending to the list
15-
``app.terminators`` allows different
14+
``;``; specifying the ``terminators`` optional argument to ``cmd2.Cmd.__init__()`` allows different
1615
terminators. A blank line
1716
is *always* considered a command terminator
1817
(cannot be overridden).

examples/arg_print.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(self):
2424
# Make sure to call this super class __init__ *after* setting and/or updating shortcuts
2525
super().__init__()
2626
# NOTE: It is critical that the super class __init__ method be called AFTER updating certain parameters which
27-
# are not settable at runtime. This includes the shortcuts, multiline_commands, etc.
27+
# are not settable at runtime. This includes the shortcuts, etc.
2828

2929
def do_aprint(self, statement):
3030
"""Print the argument string this basic command is called with."""

examples/cmd_as_argument.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,13 @@ class CmdLineApp(cmd2.Cmd):
3030

3131
def __init__(self):
3232
self.allow_cli_args = False
33-
self.multiline_commands = ['orate']
3433
self.maxrepeats = 3
3534

3635
# Add stuff to shortcuts before calling base class initializer
3736
self.shortcuts.update({'&': 'speak'})
3837

3938
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
40-
super().__init__(use_ipython=False)
39+
super().__init__(use_ipython=False, multiline_commands=['orate'])
4140

4241
# Make maxrepeats settable at runtime
4342
self.settable['maxrepeats'] = 'max repetitions for speak command'

examples/colors.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,13 @@ class CmdLineApp(cmd2.Cmd):
6363
MUMBLE_LAST = ['right?']
6464

6565
def __init__(self):
66-
self.multiline_commands = ['orate']
6766
self.maxrepeats = 3
6867

6968
# Add stuff to shortcuts before calling base class initializer
7069
self.shortcuts.update({'&': 'speak'})
7170

7271
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
73-
super().__init__(use_ipython=True)
72+
super().__init__(use_ipython=True, multiline_commands=['orate'])
7473

7574
# Make maxrepeats settable at runtime
7675
self.settable['maxrepeats'] = 'max repetitions for speak command'

examples/decorator_example.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@
1919
class CmdLineApp(cmd2.Cmd):
2020
""" Example cmd2 application. """
2121
def __init__(self, ip_addr=None, port=None, transcript_files=None):
22-
self.multiline_commands = ['orate']
2322
self.shortcuts.update({'&': 'speak'})
2423
self.maxrepeats = 3
2524

2625
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
27-
super().__init__(use_ipython=False, transcript_files=transcript_files)
26+
super().__init__(use_ipython=False, transcript_files=transcript_files, multiline_commands=['orate'])
2827

2928
# Make maxrepeats settable at runtime
3029
self.settable['maxrepeats'] = 'Max number of `--repeat`s allowed'

examples/example.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,13 @@ class CmdLineApp(cmd2.Cmd):
2727
MUMBLE_LAST = ['right?']
2828

2929
def __init__(self):
30-
self.multiline_commands = ['orate']
3130
self.maxrepeats = 3
3231

3332
# Add stuff to shortcuts before calling base class initializer
3433
self.shortcuts.update({'&': 'speak'})
3534

3635
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
37-
super().__init__(use_ipython=False)
36+
super().__init__(use_ipython=False, multiline_commands=['orate'])
3837

3938
# Make maxrepeats settable at runtime
4039
self.settable['maxrepeats'] = 'max repetitions for speak command'

examples/pirate.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from colorama import Fore
1212

1313
import cmd2
14+
from cmd2.constants import MULTILINE_TERMINATOR
1415

1516
COLORS = {
1617
'black': Fore.BLACK,
@@ -28,15 +29,13 @@ class Pirate(cmd2.Cmd):
2829
"""A piratical example cmd2 application involving looting and drinking."""
2930
def __init__(self):
3031
self.default_to_shell = True
31-
self.multiline_commands = ['sing']
32-
self.terminators = self.terminators + ['...']
3332
self.songcolor = Fore.BLUE
3433

3534
# Add stuff to shortcuts before calling base class initializer
3635
self.shortcuts.update({'~': 'sing'})
3736

3837
"""Initialize the base class as well as this one"""
39-
super().__init__()
38+
super().__init__(multiline_commands=['sing'], terminators=[MULTILINE_TERMINATOR, '...'])
4039

4140
# Make songcolor settable at runtime
4241
self.settable['songcolor'] = 'Color to ``sing`` in (black/red/green/yellow/blue/magenta/cyan/white)'

0 commit comments

Comments
 (0)