Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@
* Fixed outdated help text for the **edit** command
* Fixed outdated [remove_unused.py](https://github.com/python-cmd2/cmd2/blob/master/examples/remove_unused.py)
* Enhancements
* Added support for sub-menus.
* Added support for sub-menus.
* See [submenus.py](https://github.com/python-cmd2/cmd2/blob/master/examples/submenus.py) for an example of how to use it
* Added option for persistent readline history
* See [persistent_history.py](https://github.com/python-cmd2/cmd2/blob/master/examples/persistent_history.py) for an example
* See the [Searchable command history](http://cmd2.readthedocs.io/en/latest/freefeatures.html#searchable-command-history) section of the documentation for more info
* Improved PyPI packaging by including unit tests, examples, and docs in the tarball
* Improved documentation to make it more obvious that **poutput()** should be used instead of **print()**
* ``exclude_from_help`` and ``excludeFromHistory`` are now instance instead of class attributes

* Added flag and index based tab completion helper functions
* See [tab_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_completion.py)
* Attributes Removed
* ``abbrev`` - Removed support for abbreviated commands
* Good tab completion makes this unnecessary

## 0.8.0 (February 1, 2018)
* Bug Fixes
* Fixed unit tests on Python 3.7 due to changes in how re.escape() behaves in Python 3.7
Expand All @@ -28,7 +33,7 @@
* **with_argparser_and_unknown_args** decorator for argparse-based argument parsing, but allows unknown args
* **do_*** commands get two arguments, the output of argparse.parse_known_args()
* See the [Argument Processing](http://cmd2.readthedocs.io/en/latest/argument_processing.html) section of the documentation for more information on these decorators
* Alternatively, see the [argparse_example.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_example.py)
* Alternatively, see the [argparse_example.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_example.py)
and [arg_print.py](https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py) examples
* Added support for Argpasre sub-commands when using the **with_argument_parser** or **with_argparser_and_unknown_args** decorators
* See [subcommands.py](https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py) for an example of how to use subcommands
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Main Features
- Redirect command output to file with `>`, `>>`; input from file with `<`
- Bare `>`, `>>` with no filename send output to paste buffer (clipboard)
- `py` enters interactive Python console (opt-in `ipy` for IPython console)
- Multi-line, case-insensitive, and abbreviated commands
- Multi-line and case-insensitive commands
- Special-character command shortcuts (beyond cmd's `@` and `!`)
- Settable environment parameters
- Parsing commands with arguments using `argparse`, including support for sub-commands
Expand Down Expand Up @@ -171,7 +171,6 @@ class CmdLineApp(Cmd):
MUMBLE_LAST = ['right?']

def __init__(self):
self.abbrev = True
self.multilineCommands = ['orate']
self.maxrepeats = 3

Expand Down Expand Up @@ -248,7 +247,6 @@ example/transcript_regex.txt:
# The regex for editor will match whatever program you use.
# regexes on prompts just make the trailing space obvious
(Cmd) set
abbrev: True
colors: /(True|False)/
continuation_prompt: >/ /
debug: False
Expand Down
16 changes: 3 additions & 13 deletions cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ class Cmd(cmd.Cmd):
commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment])
commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/')
legalChars = u'!#$%.:?@_-' + pyparsing.alphanums + pyparsing.alphas8bit
multilineCommands = [] # NOTE: Multiline commands can never be abbreviated, even if abbrev is True
multilineCommands = []
prefixParser = pyparsing.Empty()
redirector = '>' # for sending output to file
shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
Expand All @@ -1008,7 +1008,6 @@ class Cmd(cmd.Cmd):
reserved_words = []

# Attributes which ARE dynamically settable at runtime
abbrev = False # Abbreviated commands recognized
colors = (platform.system() != 'Windows')
continuation_prompt = '> '
debug = False
Expand All @@ -1029,8 +1028,7 @@ class Cmd(cmd.Cmd):

# To make an attribute settable with the "do_set" command, add it to this ...
# This starts out as a dictionary but gets converted to an OrderedDict sorted alphabetically by key
settable = {'abbrev': 'Accept abbreviated commands',
'colors': 'Colorized output (*nix only)',
settable = {'colors': 'Colorized output (*nix only)',
'continuation_prompt': 'On 2nd+ line of input',
'debug': 'Show full error stack on error',
'echo': 'Echo command issued into output',
Expand Down Expand Up @@ -1076,7 +1074,7 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, persistent_histor

# Commands to exclude from the help menu or history command
self.exclude_from_help = ['do_eof', 'do_eos', 'do__relative_load']
self.excludeFromHistory = '''history histor histo hist his hi h edit edi ed e eof eo eos'''.split()
self.excludeFromHistory = '''history edit eof eos'''.split()

self._finalize_app_parameters()

Expand Down Expand Up @@ -1664,21 +1662,13 @@ def _restore_output(self, statement):
def _func_named(self, arg):
"""Gets the method name associated with a given command.

If self.abbrev is False, it is always just looks for do_arg. However, if self.abbrev is True,
it allows abbreviated command names and looks for any commands which start with do_arg.

:param arg: str - command to look up method name which implements it
:return: str - method name which implements the given command
"""
result = None
target = 'do_' + arg
if target in dir(self):
result = target
else:
if self.abbrev: # accept shortened versions of commands
funcs = [func for func in self.keywords if func.startswith(arg) and func not in self.multilineCommands]
if len(funcs) == 1:
result = 'do_' + funcs[0]
return result

def onecmd(self, line):
Expand Down
17 changes: 0 additions & 17 deletions docs/freefeatures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -271,23 +271,6 @@ Quitting the application
It's trivial, but it's one less thing for you to remember.


Abbreviated commands
====================

``cmd2`` apps will accept shortened command names
so long as there is no ambiguity if the ``abbrev`` settable parameter is set to ``True``.
Thus, if ``do_divide`` is defined, then ``divid``, ``div``,
or even ``d`` will suffice, so long as there are
no other commands defined beginning with *divid*,
*div*, or *d*.

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

.. warning::

Due to the way the parsing logic works for multiline commands, abbreviations
will not be accepted for multiline commands.

Misc. pre-defined commands
==========================

Expand Down
3 changes: 1 addition & 2 deletions docs/settingchanges.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ comments, is viewable from within a running application
with::

(Cmd) set --long
abbrev: False # Accept abbreviated commands
colors: True # Colorized output (*nix only)
continuation_prompt: > # On 2nd+ line of input
debug: False # Show full error stack on error
Expand All @@ -130,5 +129,5 @@ with::

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

set abbrev True
set colors False

1 change: 0 additions & 1 deletion examples/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class CmdLineApp(Cmd):
MUMBLE_LAST = ['right?']

def __init__(self):
self.abbrev = True
self.multilineCommands = ['orate']
self.maxrepeats = 3

Expand Down
1 change: 0 additions & 1 deletion examples/transcripts/exampleSession.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# The regex for editor will match whatever program you use.
# regexes on prompts just make the trailing space obvious
(Cmd) set
abbrev: False
colors: /(True|False)/
continuation_prompt: >/ /
debug: False
Expand Down
1 change: 0 additions & 1 deletion examples/transcripts/transcript_regex.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# The regex for editor will match whatever program you use.
# regexes on prompts just make the trailing space obvious
(Cmd) set
abbrev: True
colors: /(True|False)/
continuation_prompt: >/ /
debug: False
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
- Redirect command output to file with `>`, `>>`; input from file with `<`
- Bare `>`, `>>` with no filename send output to paste buffer (clipboard)
- `py` enters interactive Python console (opt-in `ipy` for IPython console)
- Multi-line, case-insensitive, and abbreviated commands
- Multi-line and case-insensitive commands
- Special-character command shortcuts (beyond cmd's `@` and `!`)
- Settable environment parameters
- Parsing commands with arguments using `argparse`, including support for sub-commands
Expand Down
4 changes: 1 addition & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@
if sys.platform.startswith('win'):
expect_colors = False
# Output from the show command with default settings
SHOW_TXT = """abbrev: False
colors: {}
SHOW_TXT = """colors: {}
continuation_prompt: >
debug: False
echo: False
Expand All @@ -71,7 +70,6 @@
else:
color_str = 'False'
SHOW_LONG = """
abbrev: False # Accept abbreviated commands
colors: {} # Colorized output (*nix only)
continuation_prompt: > # On 2nd+ line of input
debug: False # Show full error stack on error
Expand Down
1 change: 0 additions & 1 deletion tests/scripts/postcmds.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
set abbrev off
set colors off
1 change: 0 additions & 1 deletion tests/scripts/precmds.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
set abbrev on
set colors on
80 changes: 28 additions & 52 deletions tests/test_cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,12 +470,10 @@ def test_load_nested_loads(base_app, request):
expected = """
%s
_relative_load precmds.txt
set abbrev on
set colors on
help
shortcuts
_relative_load postcmds.txt
set abbrev off
set colors off""" % initial_load
assert run_cmd(base_app, 'history -s') == normalize(expected)

Expand All @@ -494,12 +492,10 @@ def test_base_runcmds_plus_hooks(base_app, request):
'load ' + postfilepath])
expected = """
load %s
set abbrev on
set colors on
help
shortcuts
load %s
set abbrev off
set colors off""" % (prefilepath, postfilepath)
assert run_cmd(base_app, 'history -s') == normalize(expected)

Expand Down Expand Up @@ -796,6 +792,30 @@ def test_base_py_interactive(base_app):
m.assert_called_once()


def test_exclude_from_history(base_app, monkeypatch):
# Mock out the os.system call so we don't actually open an editor
m = mock.MagicMock(name='system')
monkeypatch.setattr("os.system", m)

# Run edit command
run_cmd(base_app, 'edit')

# Run history command
run_cmd(base_app, 'history')

# Verify that the history is empty
out = run_cmd(base_app, 'history')
assert out == []

# Now run a command which isn't excluded from the history
run_cmd(base_app, 'help')
# And verify we have a history now ...
out = run_cmd(base_app, 'history')
expected = normalize("""-------------------------[1]
help""")
assert out == expected


def test_base_cmdloop_with_queue():
# Create a cmd2.Cmd() instance and make sure basic settings are like we want for test
app = cmd2.Cmd()
Expand Down Expand Up @@ -1275,49 +1295,6 @@ def test_cmdresult(cmdresult_app):
assert cmdresult_app._last_result == cmd2.CmdResult('', arg)


@pytest.fixture
def abbrev_app():
app = cmd2.Cmd()
app.abbrev = True
app.stdout = StdOut()
return app

def test_exclude_from_history(abbrev_app, monkeypatch):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to add back a much simpler version of this test

# Run all variants of run
run_cmd(abbrev_app, 'run')
run_cmd(abbrev_app, 'ru')
run_cmd(abbrev_app, 'r')

# Mock out the os.system call so we don't actually open an editor
m = mock.MagicMock(name='system')
monkeypatch.setattr("os.system", m)

# Run all variants of edit
run_cmd(abbrev_app, 'edit')
run_cmd(abbrev_app, 'edi')
run_cmd(abbrev_app, 'ed')

# Run all variants of history
run_cmd(abbrev_app, 'history')
run_cmd(abbrev_app, 'histor')
run_cmd(abbrev_app, 'histo')
run_cmd(abbrev_app, 'hist')
run_cmd(abbrev_app, 'his')
run_cmd(abbrev_app, 'hi')

# Verify that the history is empty
out = run_cmd(abbrev_app, 'history')
assert out == []

# Now run a command which isn't excluded from the history
run_cmd(abbrev_app, 'help')
# And verify we have a history now ...
out = run_cmd(abbrev_app, 'history')
expected = normalize("""-------------------------[1]
help""")
assert out == expected


def test_is_text_file_bad_input(base_app):
# Test with a non-existent file
file_is_valid = base_app.is_text_file('does_not_exist.txt')
Expand Down Expand Up @@ -1421,7 +1398,7 @@ def test_pseudo_raw_input_piped_rawinput_true_echo_true(capsys):
app, out = piped_rawinput_true(capsys, True, command)
out = out.splitlines()
assert out[0] == '{}{}'.format(app.prompt, command)
assert out[1] == 'abbrev: False'
assert out[1].startswith('colors:')

# using the decorator puts the original function at six.moves.input
# back when this method returns
Expand All @@ -1431,7 +1408,7 @@ def test_pseudo_raw_input_piped_rawinput_true_echo_false(capsys):
command = 'set'
app, out = piped_rawinput_true(capsys, False, command)
firstline = out.splitlines()[0]
assert firstline == 'abbrev: False'
assert firstline.startswith('colors:')
assert not '{}{}'.format(app.prompt, command) in out

# the next helper function and two tests check for piped
Expand All @@ -1442,7 +1419,6 @@ def piped_rawinput_false(capsys, echo, command):
app = cmd2.Cmd(stdin=fakein)
app.use_rawinput = False
app.echo = echo
app.abbrev = False
app._cmdloop()
out, err = capsys.readouterr()
return (app, out)
Expand All @@ -1452,13 +1428,13 @@ def test_pseudo_raw_input_piped_rawinput_false_echo_true(capsys):
app, out = piped_rawinput_false(capsys, True, command)
out = out.splitlines()
assert out[0] == '{}{}'.format(app.prompt, command)
assert out[1] == 'abbrev: False'
assert out[1].startswith('colors:')

def test_pseudo_raw_input_piped_rawinput_false_echo_false(capsys):
command = 'set'
app, out = piped_rawinput_false(capsys, False, command)
firstline = out.splitlines()[0]
assert firstline == 'abbrev: False'
assert firstline.startswith('colors:')
assert not '{}{}'.format(app.prompt, command) in out

#
Expand Down
6 changes: 0 additions & 6 deletions tests/test_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,12 +326,6 @@ def test_parse_multiline_ignores_terminators_in_comments(parser):
assert results.terminator[0] == '\n'
assert results.terminator[1] == '\n'

def test_parse_abbreviated_multiline_not_allowed(parser):
line = 'multilin command\n'
results = parser.parseString(line)
assert results.command == 'multilin'
assert results.multilineCommand == ''

# Unicode support is only present in cmd2 for Python 3
@pytest.mark.skipif(sys.version_info < (3,0), reason="cmd2 unicode support requires python3")
def test_parse_command_with_unicode_args(parser):
Expand Down
3 changes: 1 addition & 2 deletions tests/test_transcript.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class CmdLineApp(Cmd):
MUMBLE_LAST = ['right?']

def __init__(self, *args, **kwargs):
self.abbrev = True
self.multilineCommands = ['orate']
self.maxrepeats = 3
self.redirector = '->'
Expand Down Expand Up @@ -159,7 +158,7 @@ def test_base_with_transcript(_cmdline_app):
OODNIGHT, GRACIEGAY
OODNIGHT, GRACIEGAY
OODNIGHT, GRACIEGAY
(Cmd) hi
(Cmd) history
-------------------------[1]
help
-------------------------[2]
Expand Down
1 change: 0 additions & 1 deletion tests/transcripts/regex_set.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# Regexes on prompts just make the trailing space obvious

(Cmd) set
abbrev: True
colors: /(True|False)/
continuation_prompt: >/ /
debug: False
Expand Down