summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2019-03-13 23:33:58 -0400
committerTodd Leonhardt <todd.leonhardt@gmail.com>2019-03-13 23:33:58 -0400
commit88b45e12b0c2c9e7f8e386504f2d6808f396560b (patch)
tree66209a092dc11528f73a9cb1a991c620be09a815
parent23fad46d5187600d52b95125bb1629e491cb9e6f (diff)
downloadcmd2-git-88b45e12b0c2c9e7f8e386504f2d6808f396560b.tar.gz
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
-rwxr-xr-xREADME.md9
-rw-r--r--cmd2/cmd2.py47
-rw-r--r--docs/settingchanges.rst2
-rw-r--r--docs/unfreefeatures.rst5
-rwxr-xr-xexamples/arg_print.py2
-rwxr-xr-xexamples/cmd_as_argument.py3
-rwxr-xr-xexamples/colors.py3
-rwxr-xr-xexamples/decorator_example.py3
-rwxr-xr-xexamples/example.py3
-rwxr-xr-xexamples/pirate.py5
-rwxr-xr-xexamples/plumbum_colors.py3
-rw-r--r--tests/test_cmd2.py9
-rw-r--r--tests/test_completion.py6
-rw-r--r--tests/test_transcript.py3
14 files changed, 50 insertions, 53 deletions
diff --git a/README.md b/README.md
index 6b33ab84..65a24b8b 100755
--- a/README.md
+++ b/README.md
@@ -185,9 +185,9 @@ Instructions for implementing each feature follow.
- Multi-line commands
- Any command accepts multi-line input when its name is listed in `Cmd.multiline_commands`.
- The program will keep expecting input until a line ends with any of the characters
- in `Cmd.terminators` . The default terminators are `;` and `/n` (empty newline).
+ Any command accepts multi-line input when its name is listed the `multiline_commands` optional argument to
+ `cmd2.Cmd.__init`. The program will keep expecting input until a line ends with any of the characters listed in the
+ `terminators` optional argument to `cmd2.Cmd.__init__()` . The default terminators are `;` and `/n` (empty newline).
- Special-character shortcut commands (beyond cmd's "@" and "!")
@@ -239,14 +239,13 @@ class CmdLineApp(cmd2.Cmd):
MUMBLE_LAST = ['right?']
def __init__(self):
- self.multiline_commands = ['orate']
self.maxrepeats = 3
# Add stuff to shortcuts before calling base class initializer
self.shortcuts.update({'&': 'speak'})
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- super().__init__(use_ipython=False)
+ super().__init__(use_ipython=False, multiline_commands=['orate'])
# Make maxrepeats settable at runtime
self.settable['maxrepeats'] = 'max repetitions for speak command'
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index cd8ff110..2da8f561 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -293,14 +293,10 @@ class Cmd(cmd.Cmd):
Line-oriented command interpreters are often useful for test harnesses, internal tools, and rapid prototypes.
"""
- # Attributes used to configure the StatementParser, best not to change these at runtime
- multiline_commands = []
- shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
- terminators = [constants.MULTILINE_TERMINATOR]
+ DEFAULT_SHORTCUTS = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
# Attributes which are NOT dynamically settable at runtime
allow_cli_args = True # Should arguments passed on the command-line be processed as commands?
- allow_redirection = True # Should output redirection and pipes be allowed
default_to_shell = False # Attempt to run unrecognized commands as shell commands
quit_on_sigint = False # Quit the loop on interrupt instead of just resetting prompt
reserved_words = []
@@ -338,7 +334,9 @@ class Cmd(cmd.Cmd):
def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, persistent_history_file: str = '',
persistent_history_length: int = 1000, startup_script: Optional[str] = None, use_ipython: bool = False,
- transcript_files: Optional[List[str]] = None) -> None:
+ transcript_files: Optional[List[str]] = None, allow_redirection: bool = True,
+ multiline_commands: Optional[List[str]] = None, terminators: Optional[List[str]] = None,
+ shortcuts: Optional[Dict[str, str]] = None) -> None:
"""An easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.
:param completekey: (optional) readline name of a completion key, default to Tab
@@ -349,6 +347,9 @@ class Cmd(cmd.Cmd):
:param startup_script: (optional) file path to a a script to load and execute at startup
:param use_ipython: (optional) should the "ipy" command be included for an embedded IPython shell
:param transcript_files: (optional) allows running transcript tests when allow_cli_args is False
+ :param allow_redirection: (optional) should output redirection and pipes be allowed
+ :param multiline_commands: (optional) list of commands allowed to accept multi-line input
+ :param shortcuts: (optional) dictionary containing shortcuts for commands
"""
# If use_ipython is False, make sure the do_ipy() method doesn't exit
if not use_ipython:
@@ -377,21 +378,21 @@ class Cmd(cmd.Cmd):
self.aliases = dict()
self.macros = dict()
- self._finalize_app_parameters()
-
self.initial_stdout = sys.stdout
self.history = History()
self.pystate = {}
self.py_history = []
self.pyscript_name = 'app'
self.keywords = self.reserved_words + self.get_all_commands()
- self.statement_parser = StatementParser(
- allow_redirection=self.allow_redirection,
- terminators=self.terminators,
- multiline_commands=self.multiline_commands,
- aliases=self.aliases,
- shortcuts=self.shortcuts,
- )
+
+ if shortcuts is None:
+ shortcuts = self.DEFAULT_SHORTCUTS
+ shortcuts = sorted(shortcuts.items(), reverse=True)
+ self.statement_parser = StatementParser(allow_redirection=allow_redirection,
+ terminators=terminators,
+ multiline_commands=multiline_commands,
+ aliases=self.aliases,
+ shortcuts=shortcuts)
self._transcript_files = transcript_files
# Used to enable the ability for a Python script to quit the application
@@ -545,10 +546,15 @@ class Cmd(cmd.Cmd):
"""
return utils.strip_ansi(self.prompt)
- def _finalize_app_parameters(self) -> None:
- """Finalize the shortcuts"""
- # noinspection PyUnresolvedReferences
- self.shortcuts = sorted(self.shortcuts.items(), reverse=True)
+ @property
+ def allow_redirection(self) -> bool:
+ """Read-only property to get whether or not redirection of stdout is allowed."""
+ return self.statement_parser.allow_redirection
+
+ @property
+ def shortcuts(self) -> List[Tuple[str, str]]:
+ """Read-only property to access the shortcuts stored in the StatementParser."""
+ return self.statement_parser.shortcuts
def decolorized_write(self, fileobj: IO, msg: str) -> None:
"""Write a string to a fileobject, stripping ANSI escape sequences if necessary
@@ -2792,7 +2798,8 @@ class Cmd(cmd.Cmd):
Commands may be terminated with: {}
Arguments at invocation allowed: {}
Output redirection and pipes allowed: {}"""
- return read_only_settings.format(str(self.terminators), self.allow_cli_args, self.allow_redirection)
+ return read_only_settings.format(str(self.statement_parser.terminators), self.allow_cli_args,
+ self.allow_redirection)
def show(self, args: argparse.Namespace, parameter: str = '') -> None:
"""Shows current settings of parameters.
diff --git a/docs/settingchanges.rst b/docs/settingchanges.rst
index e1c437e4..c9345bc6 100644
--- a/docs/settingchanges.rst
+++ b/docs/settingchanges.rst
@@ -44,7 +44,7 @@ To define more shortcuts, update the dict ``App.shortcuts`` with the
Shortcuts need to be created by updating the ``shortcuts`` dictionary attribute prior to calling the
``cmd2.Cmd`` super class ``__init__()`` method. Moreover, that super class init method needs to be called after
updating the ``shortcuts`` attribute This warning applies in general to many other attributes which are not
- settable at runtime such as ``multiline_commands``, etc.
+ settable at runtime.
Aliases
diff --git a/docs/unfreefeatures.rst b/docs/unfreefeatures.rst
index 97953215..071a15b2 100644
--- a/docs/unfreefeatures.rst
+++ b/docs/unfreefeatures.rst
@@ -7,12 +7,11 @@ Multiline commands
Command input may span multiple lines for the
commands whose names are listed in the
-parameter ``app.multiline_commands``. These
+``multiline_commands`` argument to ``cmd2.Cmd.__init__()``. These
commands will be executed only
after the user has entered a *terminator*.
By default, the command terminator is
-``;``; replacing or appending to the list
-``app.terminators`` allows different
+``;``; specifying the ``terminators`` optional argument to ``cmd2.Cmd.__init__()`` allows different
terminators. A blank line
is *always* considered a command terminator
(cannot be overridden).
diff --git a/examples/arg_print.py b/examples/arg_print.py
index 18d21787..a7b938e3 100755
--- a/examples/arg_print.py
+++ b/examples/arg_print.py
@@ -24,7 +24,7 @@ class ArgumentAndOptionPrinter(cmd2.Cmd):
# Make sure to call this super class __init__ *after* setting and/or updating shortcuts
super().__init__()
# NOTE: It is critical that the super class __init__ method be called AFTER updating certain parameters which
- # are not settable at runtime. This includes the shortcuts, multiline_commands, etc.
+ # are not settable at runtime. This includes the shortcuts, etc.
def do_aprint(self, statement):
"""Print the argument string this basic command is called with."""
diff --git a/examples/cmd_as_argument.py b/examples/cmd_as_argument.py
index dcec81c8..48397a42 100755
--- a/examples/cmd_as_argument.py
+++ b/examples/cmd_as_argument.py
@@ -30,14 +30,13 @@ class CmdLineApp(cmd2.Cmd):
def __init__(self):
self.allow_cli_args = False
- self.multiline_commands = ['orate']
self.maxrepeats = 3
# Add stuff to shortcuts before calling base class initializer
self.shortcuts.update({'&': 'speak'})
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- super().__init__(use_ipython=False)
+ super().__init__(use_ipython=False, multiline_commands=['orate'])
# Make maxrepeats settable at runtime
self.settable['maxrepeats'] = 'max repetitions for speak command'
diff --git a/examples/colors.py b/examples/colors.py
index 62df54e6..6f4912be 100755
--- a/examples/colors.py
+++ b/examples/colors.py
@@ -63,14 +63,13 @@ class CmdLineApp(cmd2.Cmd):
MUMBLE_LAST = ['right?']
def __init__(self):
- self.multiline_commands = ['orate']
self.maxrepeats = 3
# Add stuff to shortcuts before calling base class initializer
self.shortcuts.update({'&': 'speak'})
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- super().__init__(use_ipython=True)
+ super().__init__(use_ipython=True, multiline_commands=['orate'])
# Make maxrepeats settable at runtime
self.settable['maxrepeats'] = 'max repetitions for speak command'
diff --git a/examples/decorator_example.py b/examples/decorator_example.py
index 5d127619..873d37cd 100755
--- a/examples/decorator_example.py
+++ b/examples/decorator_example.py
@@ -19,12 +19,11 @@ import cmd2
class CmdLineApp(cmd2.Cmd):
""" Example cmd2 application. """
def __init__(self, ip_addr=None, port=None, transcript_files=None):
- self.multiline_commands = ['orate']
self.shortcuts.update({'&': 'speak'})
self.maxrepeats = 3
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- super().__init__(use_ipython=False, transcript_files=transcript_files)
+ super().__init__(use_ipython=False, transcript_files=transcript_files, multiline_commands=['orate'])
# Make maxrepeats settable at runtime
self.settable['maxrepeats'] = 'Max number of `--repeat`s allowed'
diff --git a/examples/example.py b/examples/example.py
index 04727ec6..145ddb79 100755
--- a/examples/example.py
+++ b/examples/example.py
@@ -27,14 +27,13 @@ class CmdLineApp(cmd2.Cmd):
MUMBLE_LAST = ['right?']
def __init__(self):
- self.multiline_commands = ['orate']
self.maxrepeats = 3
# Add stuff to shortcuts before calling base class initializer
self.shortcuts.update({'&': 'speak'})
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- super().__init__(use_ipython=False)
+ super().__init__(use_ipython=False, multiline_commands=['orate'])
# Make maxrepeats settable at runtime
self.settable['maxrepeats'] = 'max repetitions for speak command'
diff --git a/examples/pirate.py b/examples/pirate.py
index 32330404..a58f9a46 100755
--- a/examples/pirate.py
+++ b/examples/pirate.py
@@ -11,6 +11,7 @@ import argparse
from colorama import Fore
import cmd2
+from cmd2.constants import MULTILINE_TERMINATOR
COLORS = {
'black': Fore.BLACK,
@@ -28,15 +29,13 @@ class Pirate(cmd2.Cmd):
"""A piratical example cmd2 application involving looting and drinking."""
def __init__(self):
self.default_to_shell = True
- self.multiline_commands = ['sing']
- self.terminators = self.terminators + ['...']
self.songcolor = Fore.BLUE
# Add stuff to shortcuts before calling base class initializer
self.shortcuts.update({'~': 'sing'})
"""Initialize the base class as well as this one"""
- super().__init__()
+ super().__init__(multiline_commands=['sing'], terminators=[MULTILINE_TERMINATOR, '...'])
# Make songcolor settable at runtime
self.settable['songcolor'] = 'Color to ``sing`` in (black/red/green/yellow/blue/magenta/cyan/white)'
diff --git a/examples/plumbum_colors.py b/examples/plumbum_colors.py
index 3e5031d7..3867081b 100755
--- a/examples/plumbum_colors.py
+++ b/examples/plumbum_colors.py
@@ -66,14 +66,13 @@ class CmdLineApp(cmd2.Cmd):
MUMBLE_LAST = ['right?']
def __init__(self):
- self.multiline_commands = ['orate']
self.maxrepeats = 3
# Add stuff to shortcuts before calling base class initializer
self.shortcuts.update({'&': 'speak'})
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
- super().__init__(use_ipython=True)
+ super().__init__(use_ipython=True, multiline_commands=['orate'])
# Make maxrepeats settable at runtime
self.settable['maxrepeats'] = 'max repetitions for speak command'
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index b3942203..368ca221 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -105,7 +105,7 @@ def test_base_show_readonly(base_app):
Commands may be terminated with: {}
Arguments at invocation allowed: {}
Output redirection and pipes allowed: {}
-""".format(base_app.terminators, base_app.allow_cli_args, base_app.allow_redirection))
+""".format(base_app.statement_parser.terminators, base_app.allow_cli_args, base_app.allow_redirection))
assert out == expected
@@ -557,9 +557,9 @@ def test_feedback_to_output_false(base_app, capsys):
os.remove(filename)
-def test_allow_redirection(base_app):
+def test_disallow_redirection(base_app):
# Set allow_redirection to False
- base_app.allow_redirection = False
+ base_app.statement_parser.allow_redirection = False
filename = 'test_allow_redirect.txt'
@@ -1264,8 +1264,7 @@ def test_which_editor_bad():
class MultilineApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
- self.multiline_commands = ['orate']
- super().__init__(*args, **kwargs)
+ super().__init__(*args, multiline_commands=['orate'], **kwargs)
orate_parser = argparse.ArgumentParser()
orate_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
diff --git a/tests/test_completion.py b/tests/test_completion.py
index da7fae65..a7c26928 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -635,7 +635,7 @@ def test_tokens_for_completion_redirect(cmd2_app):
endidx = len(line)
begidx = endidx - len(text)
- cmd2_app.allow_redirection = True
+ cmd2_app.statement_parser.allow_redirection = True
expected_tokens = ['command', '|', '<', '>>', 'file']
expected_raw_tokens = ['command', '|', '<', '>>', 'file']
@@ -649,7 +649,7 @@ def test_tokens_for_completion_quoted_redirect(cmd2_app):
endidx = len(line)
begidx = endidx - len(text)
- cmd2_app.allow_redirection = True
+ cmd2_app.statement_parser.redirection = True
expected_tokens = ['command', '>file']
expected_raw_tokens = ['command', '">file']
@@ -663,7 +663,7 @@ def test_tokens_for_completion_redirect_off(cmd2_app):
endidx = len(line)
begidx = endidx - len(text)
- cmd2_app.allow_redirection = False
+ cmd2_app.statement_parser.allow_redirection = False
expected_tokens = ['command', '>file']
expected_raw_tokens = ['command', '>file']
diff --git a/tests/test_transcript.py b/tests/test_transcript.py
index f93642b8..4a03ebe0 100644
--- a/tests/test_transcript.py
+++ b/tests/test_transcript.py
@@ -29,10 +29,9 @@ class CmdLineApp(cmd2.Cmd):
MUMBLE_LAST = ['right?']
def __init__(self, *args, **kwargs):
- self.multiline_commands = ['orate']
self.maxrepeats = 3
- super().__init__(*args, **kwargs)
+ super().__init__(*args, multiline_commands=['orate'], **kwargs)
# Make maxrepeats settable at runtime
self.settable['maxrepeats'] = 'Max number of `--repeat`s allowed'