summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md9
-rwxr-xr-xREADME.md3
-rwxr-xr-xcmd2.py28
-rw-r--r--docs/conf.py2
-rw-r--r--docs/freefeatures.rst15
-rwxr-xr-xexamples/persistent_history.py22
-rwxr-xr-x[-rw-r--r--]examples/submenus.py6
-rwxr-xr-xsetup.py2
-rw-r--r--tests/test_cmd2.py2
9 files changed, 69 insertions, 20 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ad98d0d..110dec68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+## 0.8.1 (TBD, 2018)
+
+* Enhancements
+ * 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
+
## 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
diff --git a/README.md b/README.md
index 94bc6c30..85e26076 100755
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ when using cmd.
Main Features
-------------
-- Searchable command history (`history` command and `<Ctrl>+r`)
+- Searchable command history (`history` command and `<Ctrl>+r`) - optionally persistent
- Text file scripting of your application with `load` (`@`) and `_relative_load` (`@@`)
- Python scripting of your application with ``pyscript``
- Run shell commands with ``!``
@@ -30,6 +30,7 @@ Main Features
- Special-character command shortcuts (beyond cmd's `@` and `!`)
- Settable environment parameters
- Parsing commands with arguments using `argparse`, including support for sub-commands
+- Sub-menu support via the ``AddSubmenu`` decorator
- Unicode character support (*Python 3 only*)
- Good tab-completion of commands, sub-commands, file system paths, and shell commands
- Python 2.7 and 3.4+ support
diff --git a/cmd2.py b/cmd2.py
index a237e1bd..4f077b35 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -24,6 +24,8 @@ is used in place of `print`.
Git repository on GitHub at https://github.com/python-cmd2/cmd2
"""
+import argparse
+import atexit
import cmd
import codecs
import collections
@@ -31,7 +33,6 @@ import datetime
import glob
import io
import optparse
-import argparse
import os
import platform
import re
@@ -112,7 +113,7 @@ if six.PY2 and sys.platform.startswith('lin'):
except ImportError:
pass
-__version__ = '0.8.0'
+__version__ = '0.8.1'
# Pyparsing enablePackrat() can greatly speed up parsing, but problems have been seen in Python 3 in the past
pyparsing.ParserElement.enablePackrat()
@@ -549,6 +550,7 @@ def strip_ansi(text):
def _pop_readline_history(clear_history=True):
"""Returns a copy of readline's history and optionally clears it (default)"""
+ # noinspection PyArgumentList
history = [
readline.get_history_item(i)
for i in range(1, 1 + readline.get_current_history_length())
@@ -689,6 +691,7 @@ class AddSubmenu(object):
)
submenu.cmdloop()
if self.reformat_prompt is not None:
+ # noinspection PyUnboundLocalVariable
self.submenu.prompt = prompt
_push_readline_history(history)
finally:
@@ -761,12 +764,12 @@ class AddSubmenu(object):
_Cmd.complete_help = _complete_submenu_help
# Create bindings in the parent command to the submenus commands.
- setattr(_Cmd, 'do_' + self.command, enter_submenu)
+ setattr(_Cmd, 'do_' + self.command, enter_submenu)
setattr(_Cmd, 'complete_' + self.command, complete_submenu)
# Create additional bindings for aliases
for _alias in self.aliases:
- setattr(_Cmd, 'do_' + _alias, enter_submenu)
+ setattr(_Cmd, 'do_' + _alias, enter_submenu)
setattr(_Cmd, 'complete_' + _alias, complete_submenu)
return _Cmd
@@ -833,12 +836,15 @@ class Cmd(cmd.Cmd):
'quiet': "Don't print nonessential feedback",
'timing': 'Report execution times'}
- def __init__(self, completekey='tab', stdin=None, stdout=None, use_ipython=False, transcript_files=None):
+ def __init__(self, completekey='tab', stdin=None, stdout=None, persistent_history_file='',
+ persistent_history_length=1000, use_ipython=False, transcript_files=None):
"""An easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.
:param completekey: str - (optional) readline name of a completion key, default to Tab
:param stdin: (optional) alternate input file object, if not specified, sys.stdin is used
:param stdout: (optional) alternate output file object, if not specified, sys.stdout is used
+ :param persistent_history_file: str - (optional) file path to load a persistent readline history from
+ :param persistent_history_length: int - (optional) max number of lines which will be written to the history file
:param use_ipython: (optional) should the "ipy" command be included for an embedded IPython shell
:param transcript_files: str - (optional) allows running transcript tests when allow_cli_args is False
"""
@@ -849,6 +855,17 @@ class Cmd(cmd.Cmd):
except AttributeError:
pass
+ # If persistent readline history is enabled, then read history from file and register to write to file at exit
+ if persistent_history_file:
+ persistent_history_file = os.path.expanduser(persistent_history_file)
+ try:
+ readline.read_history_file(persistent_history_file)
+ # default history len is -1 (infinite), which may grow unruly
+ readline.set_history_length(persistent_history_length)
+ except FileNotFoundError:
+ pass
+ atexit.register(readline.write_history_file, persistent_history_file)
+
# Call super class constructor. Need to do it in this way for Python 2 and 3 compatibility
cmd.Cmd.__init__(self, completekey=completekey, stdin=stdin, stdout=stdout)
@@ -2901,6 +2918,7 @@ def namedtuple_with_two_defaults(typename, field_names, default_values=('', ''))
:return: namedtuple type
"""
T = collections.namedtuple(typename, field_names)
+ # noinspection PyUnresolvedReferences
T.__new__.__defaults__ = default_values
return T
diff --git a/docs/conf.py b/docs/conf.py
index d4ef14bf..09d68b9c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -62,7 +62,7 @@ author = 'Catherine Devlin and Todd Leonhardt'
# The short X.Y version.
version = '0.8'
# The full version, including alpha/beta/rc tags.
-release = '0.8.0'
+release = '0.8.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/freefeatures.rst b/docs/freefeatures.rst
index ea40c87c..a439db56 100644
--- a/docs/freefeatures.rst
+++ b/docs/freefeatures.rst
@@ -13,7 +13,7 @@ Script files
============
Text files can serve as scripts for your ``cmd2``-based
-application, with the ``load``, ``_relative_load``, ``edit`` and ``history`` commands.
+application, with the ``load``, ``_relative_load``, and ``edit`` commands.
Both ASCII and UTF-8 encoded unicode text files are supported.
@@ -25,8 +25,6 @@ Simply include one command per line, typed exactly as you would inside a ``cmd2`
.. automethod:: cmd2.Cmd.do_edit
-.. automethod:: cmd2.Cmd.do_history
-
Comments
========
@@ -250,17 +248,22 @@ Searchable command history
==========================
All cmd_-based applications have access to previous commands with
-the up- and down- cursor keys.
+the up- and down- arrow keys.
All cmd_-based applications on systems with the ``readline`` module
-also provide `bash-like history list editing`_.
+also provide `Readline Emacs editing mode`_. With this you can, for example, use **Ctrl-r** to search backward through
+the readline history.
-.. _`bash-like history list editing`: http://www.talug.org/events/20030709/cmdline_history.html
+``cmd2`` adds the option of making this readline history persistent via optional arguments to ``cmd2.Cmd.__init__()``:
+
+.. automethod:: cmd2.Cmd.__init__
``cmd2`` makes a third type of history access available with the **history** command:
.. automethod:: cmd2.Cmd.do_history
+.. _`Readline Emacs editing mode`: http://readline.kablamo.org/emacs.html
+
Quitting the application
========================
diff --git a/examples/persistent_history.py b/examples/persistent_history.py
new file mode 100755
index 00000000..21a2cbf3
--- /dev/null
+++ b/examples/persistent_history.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# coding=utf-8
+"""This example demonstrates how to enable persistent readline history in your cmd2 application.
+
+This will allow end users of your cmd2-based application to use the arrow keys and Ctrl+r in a manner which persists
+across invocations of your cmd2 application. This can make it much easier for them to use your application.
+"""
+import cmd2
+
+
+class Cmd2PersistentHistory(cmd2.Cmd):
+ """Basic example of how to enable persistent readline history within your cmd2 app."""
+ def __init__(self):
+ """"""
+ cmd2.Cmd.__init__(self, persistent_history_file='~/.persistent_history.cmd2', persistent_history_length=500)
+
+ # ... your class code here ...
+
+
+if __name__ == '__main__':
+ app = Cmd2PersistentHistory()
+ app.cmdloop()
diff --git a/examples/submenus.py b/examples/submenus.py
index 52f26e08..1e3da0da 100644..100755
--- a/examples/submenus.py
+++ b/examples/submenus.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+# coding=utf-8
"""
Create a CLI with a nested command structure as follows. The commands 'second' and 'third' navigate the CLI to the scope
of the submenu. Nesting of the submenus is done with the cmd2.AddSubmenu() decorator.
@@ -6,9 +7,6 @@ of the submenu. Nesting of the submenus is done with the cmd2.AddSubmenu() decor
(Top Level)----second----->(2nd Level)----third----->(3rd Level)
| | |
---> say ---> say ---> say
-
-
-
"""
from __future__ import print_function
import sys
@@ -71,7 +69,6 @@ class SecondLevel(cmd2.Cmd):
return [s for s in ['qwe', 'asd', 'zxc'] if s.startswith(text)]
-
@cmd2.AddSubmenu(SecondLevel(),
command='second',
aliases=('second_alias',),
@@ -105,7 +102,6 @@ class TopLevel(cmd2.Cmd):
return [s for s in ['qwe', 'asd', 'zxc'] if s.startswith(text)]
-
if __name__ == '__main__':
root = TopLevel()
diff --git a/setup.py b/setup.py
index 64061ef0..ce871627 100755
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ Setuptools setup file, used to install or test 'cmd2'
import sys
from setuptools import setup
-VERSION = '0.8.0'
+VERSION = '0.8.1'
DESCRIPTION = "cmd2 - a tool for building interactive command line applications in Python"
LONG_DESCRIPTION = """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
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 186def65..5f56803d 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -25,7 +25,7 @@ from conftest import run_cmd, normalize, BASE_HELP, HELP_HISTORY, SHORTCUTS_TXT,
def test_ver():
- assert cmd2.__version__ == '0.8.0'
+ assert cmd2.__version__ == '0.8.1'
def test_empty_statement(base_app):