Skip to content

Commit 6588289

Browse files
authored
Merge pull request #169 from python-cmd2/refactor_parser_config
Fixed a few bugs and examples
2 parents d092e61 + 2c3bd0a commit 6588289

File tree

12 files changed

+57
-49
lines changed

12 files changed

+57
-49
lines changed

CHANGES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ News
66

77
*Release date: TBD*
88

9+
* Bug Fixes
10+
* `case_insensitive` is no longer a runtime-settable parameter, but it was still listed as such
11+
* Fixed a recursive loop bug when abbreviated commands are enabled and it could get stuck in the editor forever
12+
* Added additional command abbreviations to the "exclude from history" list
13+
* Fixed argparse_example.py and pirate.py examples and transcript_regex.txt transcript
14+
* Enhancements
15+
- Organized all attributes used to configure the ParserManager into a single location
16+
- Set the default value of `abbrev` to `False` (which controls whether or not abbreviated commands are allowed)
17+
- With good tab-completion of command names, using abbreviated commands isn't a particularly useful feature
18+
- And it can create problems
19+
920

1021
0.7.4
1122
-----

cmd2.py

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -400,32 +400,29 @@ class Cmd(cmd.Cmd):
400400
401401
Line-oriented command interpreters are often useful for test harnesses, internal tools, and rapid prototypes.
402402
"""
403-
# Attributes which are NOT dynamically settable at runtime
404-
allow_cli_args = True # Should arguments passed on the command-line be processed as commands?
405-
allow_redirection = True # Should output redirection and pipes be allowed
403+
# Attributes used to configure the ParserManager (all are not dynamically settable at runtime)
406404
blankLinesAllowed = False
407-
commentGrammars = pyparsing.Or(
408-
[pyparsing.pythonStyleComment, pyparsing.cStyleComment]
409-
)
405+
case_insensitive = True # Commands recognized regardless of case
406+
commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment])
410407
commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/')
411-
412-
default_to_shell = False # Attempt to run unrecognized commands as shell commands
413-
excludeFromHistory = '''run r list l history hi ed edit li eof'''.split()
414-
exclude_from_help = ['do_eof'] # Commands to exclude from the help menu
415-
416-
# make sure your terminators are not in legalChars!
417408
legalChars = u'!#$%.:?@_-' + pyparsing.alphanums + pyparsing.alphas8bit
418409
multilineCommands = [] # NOTE: Multiline commands can never be abbreviated, even if abbrev is True
419410
prefixParser = pyparsing.Empty()
420-
redirector = '>' # for sending output to file
421-
reserved_words = []
411+
redirector = '>' # for sending output to file
422412
shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
423-
terminators = [';']
413+
terminators = [';'] # make sure your terminators are not in legalChars!
414+
415+
# Attributes which are NOT dynamically settable at runtime
416+
allow_cli_args = True # Should arguments passed on the command-line be processed as commands?
417+
allow_redirection = True # Should output redirection and pipes be allowed
418+
default_to_shell = False # Attempt to run unrecognized commands as shell commands
419+
excludeFromHistory = '''run ru r history histor histo hist his hi h edit edi ed e eof eo'''.split()
420+
exclude_from_help = ['do_eof'] # Commands to exclude from the help menu
421+
reserved_words = []
424422

425423
# Attributes which ARE dynamically settable at runtime
426-
abbrev = True # Abbreviated commands recognized
424+
abbrev = False # Abbreviated commands recognized
427425
autorun_on_edit = False # Should files automatically run after editing (doesn't apply to commands)
428-
case_insensitive = True # Commands recognized regardless of case
429426
colors = (platform.system() != 'Windows')
430427
continuation_prompt = '> '
431428
debug = False
@@ -448,7 +445,6 @@ class Cmd(cmd.Cmd):
448445
# This starts out as a dictionary but gets converted to an OrderedDict sorted alphabetically by key
449446
settable = {'abbrev': 'Accept abbreviated commands',
450447
'autorun_on_edit': 'Automatically run files after editing',
451-
'case_insensitive': 'Upper- and lower-case both OK',
452448
'colors': 'Colorized output (*nix only)',
453449
'continuation_prompt': 'On 2nd+ line of input',
454450
'debug': 'Show full error stack on error',

docs/freefeatures.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,13 @@ Abbreviated commands
265265
====================
266266

267267
``cmd2`` apps will accept shortened command names
268-
so long as there is no ambiguity. Thus, if
269-
``do_divide`` is defined, then ``divid``, ``div``,
268+
so long as there is no ambiguity if the ``abrev`` settable parameter is set to ``True``.
269+
Thus, if ``do_divide`` is defined, then ``divid``, ``div``,
270270
or even ``d`` will suffice, so long as there are
271271
no other commands defined beginning with *divid*,
272272
*div*, or *d*.
273273

274-
This behavior can be turned off with ``app.abbrev`` (see :ref:`parameters`)
274+
This behavior is disabled by default, but can be turned on with ``app.abbrev`` (see :ref:`parameters`)
275275

276276
.. warning::
277277

docs/settingchanges.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,8 @@ comments, is viewable from within a running application
103103
with::
104104

105105
(Cmd) set --long
106-
abbrev: True # Accept abbreviated commands
106+
abbrev: False # Accept abbreviated commands
107107
autorun_on_edit: False # Automatically run files after editing
108-
case_insensitive: True # upper- and lower-case both OK
109108
colors: True # Colorized output (*nix only)
110109
continuation_prompt: > # On 2nd+ line of input
111110
debug: False # Show full error stack on error
@@ -119,5 +118,5 @@ with::
119118

120119
Any of these user-settable parameters can be set while running your app with the ``set`` command like so::
121120

122-
set abbrev False
121+
set abbrev True
123122

examples/argparse_example.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"""A sample application for cmd2 showing how to use Argparse to process command line arguments for your application.
44
It doubles as an example of how you can still do transcript testing even if allow_cli_args is false.
55
6-
Thanks to cmd2's built-in transcript testing capability, it also serves as a test suite for argparse_example.py when
6+
Thanks to cmd2's built-in transcript testing capability, it also serves as a test suite for argparse_example.py when
77
used with the exampleSession.txt transcript.
88
9-
Running `python argparse_example.py -t exampleSession.txt` will run all the commands in the transcript against
9+
Running `python argparse_example.py -t exampleSession.txt` will run all the commands in the transcript against
1010
argparse_example.py, verifying that the output produced matches the transcript.
1111
"""
1212
import argparse
@@ -16,15 +16,14 @@
1616

1717
class CmdLineApp(Cmd):
1818
""" Example cmd2 application. """
19-
multilineCommands = ['orate']
20-
Cmd.shortcuts.update({'&': 'speak'})
21-
maxrepeats = 3
22-
Cmd.settable.append('maxrepeats')
19+
def __init__(self, ip_addr=None, port=None, transcript_files=None):
20+
self.multilineCommands = ['orate']
21+
self.shortcuts.update({'&': 'speak'})
22+
self.maxrepeats = 3
2323

24-
# Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist
25-
# default_to_shell = True
24+
# Add stuff to settable and/or shortcuts before calling base class initializer
25+
self.settable['maxrepeats'] = 'Max number of `--repeat`s allowed'
2626

27-
def __init__(self, ip_addr=None, port=None, transcript_files=None):
2827
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
2928
Cmd.__init__(self, use_ipython=False, transcript_files=transcript_files)
3029

@@ -35,6 +34,9 @@ def __init__(self, ip_addr=None, port=None, transcript_files=None):
3534
self._ip = ip_addr
3635
self._port = port
3736

37+
# Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist
38+
# self.default_to_shell = True
39+
3840
@options([make_option('-p', '--piglatin', action="store_true", help="atinLay"),
3941
make_option('-s', '--shout', action="store_true", help="N00B EMULATION MODE"),
4042
make_option('-r', '--repeat', type="int", help="output [n] times")

examples/example.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class CmdLineApp(Cmd):
1919
# default_to_shell = True
2020

2121
def __init__(self):
22+
self.abbrev = True
2223
self.multilineCommands = ['orate']
2324
self.maxrepeats = 3
2425

examples/pirate.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@
1111

1212
class Pirate(Cmd):
1313
"""A piratical example cmd2 application involving looting and drinking."""
14-
default_to_shell = True
15-
multilineCommands = ['sing']
16-
terminators = Cmd.terminators + ['...']
17-
songcolor = 'blue'
18-
settable = Cmd.settable + 'songcolor Color to ``sing`` in (red/blue/green/cyan/magenta, bold, underline)'
19-
Cmd.shortcuts.update({'~': 'sing'})
20-
2114
def __init__(self):
15+
self.default_to_shell = True
16+
self.multilineCommands = ['sing']
17+
self.terminators = Cmd.terminators + ['...']
18+
self.songcolor = 'blue'
19+
20+
# Add stuff to settable and/or shortcuts before calling base class initializer
21+
self.settable['songcolor'] = 'Color to ``sing`` in (red/blue/green/cyan/magenta, bold, underline)'
22+
self.shortcuts.update({'~': 'sing'})
23+
2224
"""Initialize the base class as well as this one"""
2325
Cmd.__init__(self)
2426
# prompts and defaults

examples/transcript_regex.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
(Cmd) set
44
abbrev: True
55
autorun_on_edit: False
6-
case_insensitive: True
76
colors: /(True|False)/
87
continuation_prompt: >
98
debug: False
109
echo: False
1110
editor: /([^\s]+)/
12-
feedback_to_output: False
11+
feedback_to_output: True
1312
locals_in_py: True
1413
maxrepeats: 3
1514
prompt: (Cmd)

tests/conftest.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@
4747
if sys.platform.startswith('win'):
4848
expect_colors = False
4949
# Output from the show command with default settings
50-
SHOW_TXT = """abbrev: True
50+
SHOW_TXT = """abbrev: False
5151
autorun_on_edit: False
52-
case_insensitive: True
5352
colors: {}
5453
continuation_prompt: >
5554
debug: False
@@ -67,9 +66,8 @@
6766
else:
6867
color_str = 'False'
6968
SHOW_LONG = """
70-
abbrev: True # Accept abbreviated commands
69+
abbrev: False # Accept abbreviated commands
7170
autorun_on_edit: False # Automatically run files after editing
72-
case_insensitive: True # Upper- and lower-case both OK
7371
colors: {} # Colorized output (*nix only)
7472
continuation_prompt: > # On 2nd+ line of input
7573
debug: False # Show full error stack on error

tests/test_cmd2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def test_set_not_supported(base_app, capsys):
9999
""")
100100
assert normalize(str(err)) == expected
101101

102-
def test_set_abbreviated(base_app):
102+
def test_set_quiet(base_app):
103103
out = run_cmd(base_app, 'set quie True')
104104
expected = normalize("""
105105
quiet - was: False
@@ -649,7 +649,7 @@ def test_edit_no_editor(base_app, capsys):
649649
base_app.editor = None
650650

651651
# Make sure we get an exception, but cmd2 handles it
652-
run_cmd(base_app, 'ed')
652+
run_cmd(base_app, 'edit')
653653
out, err = capsys.readouterr()
654654

655655
expected = _expected_no_editor_error()

0 commit comments

Comments
 (0)