summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--CHANGES.rst5
-rwxr-xr-xREADME.rst53
-rwxr-xr-xcmd2.py431
-rw-r--r--docs/conf.py75
-rw-r--r--docs/freefeatures.rst72
-rw-r--r--docs/index.rst17
-rw-r--r--docs/pycon2010/fileutil.py12
-rw-r--r--docs/pycon2010/graph.py41
-rw-r--r--docs/pycon2010/pirate.py5
-rw-r--r--docs/pycon2010/pirate2.py6
-rw-r--r--docs/pycon2010/pirate3.py12
-rw-r--r--docs/pycon2010/pirate4.py18
-rw-r--r--docs/pycon2010/pirate5.py19
-rw-r--r--docs/pycon2010/pirate6.py20
-rw-r--r--docs/pycon2010/pirate7.py22
-rw-r--r--docs/pycon2010/pirate8.py25
-rw-r--r--docs/pycon2010/pycon2010.rst64
-rw-r--r--docs/pycon2010/schematic.py32
-rw-r--r--docs/unfreefeatures.rst42
-rwxr-xr-xexample/example.py9
-rw-r--r--ignoreBug.py11
-rwxr-xr-xsetup.py29
-rw-r--r--tests/conftest.py1
-rw-r--r--tests/test_cmd2.py11
-rw-r--r--tests/test_transcript.py5
-rw-r--r--tox.ini1
27 files changed, 555 insertions, 485 deletions
diff --git a/.gitignore b/.gitignore
index 24c540b7..5a438505 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,4 @@ dist
cmd2.egg-info
.idea
.cache
-
+*.pyc \ No newline at end of file
diff --git a/CHANGES.rst b/CHANGES.rst
index b873df3d..1dcc0c2f 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -44,3 +44,8 @@ News
* Fix subprocess.mswindows bug
* Add Python3.6 support
* Drop distutils from setup.py
+
+0.7.0
+-----
+
+* Refactor to use six module for a unified codebase which supports both Python 2 and Python 3
diff --git a/README.rst b/README.rst
index 6a248de2..1f65b4fc 100755
--- a/README.rst
+++ b/README.rst
@@ -40,8 +40,8 @@ Instructions for implementing each feature follow.
- Searchable command history
- All commands will automatically be tracked in the session's history, unless the command is listed in Cmd's excludeFromHistory attribute.
- The history is accessed through the `history`, `list`, and `run` commands
+ All commands will automatically be tracked in the session's history, unless the command is listed in Cmd's excludeFromHistory attribute.
+ The history is accessed through the `history`, `list`, and `run` commands
(and their abbreviations: `hi`, `li`, `l`, `r`).
If you wish to exclude some of your custom commands from the history, append their names
to the list at Cmd.ExcludeFromHistory.
@@ -49,37 +49,37 @@ Instructions for implementing each feature follow.
- Load commands from file, save to file, edit commands in file
Type `help load`, `help save`, `help edit` for details.
-
+
- Multi-line commands
Any command accepts multi-line input when its name is listed in `Cmd.multilineCommands`.
- The program will keep expecting input until a line ends with any of the characters
+ 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).
-
+
- Case-insensitive commands
All commands are case-insensitive, unless `Cmd.caseInsensitive` is set to `False`.
-
+
- Special-character shortcut commands (beyond cmd's "@" and "!")
To create a single-character shortcut for a command, update `Cmd.shortcuts`.
-
+
- Settable environment parameters
- To allow a user to change an environment parameter during program execution,
+ To allow a user to change an environment parameter during program execution,
append the parameter's name to `Cmd.settable`.
-
-- Parsing commands with `optparse` options (flags)
+
+- Parsing commands with `optparse` options (flags)
::
-
+
@options([make_option('-m', '--myoption', action="store_true", help="all about my option")])
def do_myfunc(self, arg, opts):
if opts.myoption:
...
-
+
See Python standard library's `optparse` documentation: http://docs.python.org/lib/optparse-defining-options.html
-
+
cmd2 can be installed with `easy_install cmd2`
Cheese Shop page: http://pypi.python.org/pypi/cmd2
@@ -87,16 +87,16 @@ Cheese Shop page: http://pypi.python.org/pypi/cmd2
Example cmd2 application (example/example.py) ::
'''A sample application for cmd2.'''
-
+
from cmd2 import Cmd, make_option, options, Cmd2TestCase
import unittest, optparse, sys
-
+
class CmdLineApp(Cmd):
multilineCommands = ['orate']
Cmd.shortcuts.update({'&': 'speak'})
maxrepeats = 3
Cmd.settable.append('maxrepeats')
-
+
@options([make_option('-p', '--piglatin', action="store_true", help="atinLay"),
make_option('-s', '--shout', action="store_true", help="N00B EMULATION MODE"),
make_option('-r', '--repeat', type="int", help="output [n] times")
@@ -114,14 +114,14 @@ Example cmd2 application (example/example.py) ::
self.stdout.write('\n')
# self.stdout.write is better than "print", because Cmd can be
# initialized with a non-standard output destination
-
+
do_say = do_speak # now "say" is a synonym for "speak"
do_orate = do_speak # another synonym, but this one takes multi-line input
-
+
class TestMyAppCase(Cmd2TestCase):
CmdApp = CmdLineApp
transcriptFileName = 'exampleSession.txt'
-
+
parser = optparse.OptionParser()
parser.add_option('-t', '--test', dest='unittests', action='store_true', default=False, help='Run unit test suite')
(callopts, callargs) = parser.parse_args()
@@ -133,8 +133,8 @@ Example cmd2 application (example/example.py) ::
app.cmdloop()
The following is a sample session running example.py.
-Thanks to `TestMyAppCase(Cmd2TestCase)`, it also serves as a test
-suite for example.py when saved as `exampleSession.txt`.
+Thanks to `TestMyAppCase(Cmd2TestCase)`, it also serves as a test
+suite for example.py when saved as `exampleSession.txt`.
Running `python example.py -t` will run all the commands in the
transcript against `example.py`, verifying that the output produced
matches the transcript.
@@ -142,12 +142,12 @@ matches the transcript.
example/exampleSession.txt::
(Cmd) help
-
+
Documented commands (type help <topic>):
========================================
- _load edit history li load pause run say shell show
+ _load edit history li load pause run say shell show
ed hi l list orate r save set shortcuts speak
-
+
Undocumented commands:
======================
EOF cmdenvironment eof exit help q quit
@@ -155,14 +155,14 @@ example/exampleSession.txt::
(Cmd) help say
Repeats what you tell me to.
Usage: speak [options] arg
-
+
Options:
-h, --help show this help message and exit
-p, --piglatin atinLay
-s, --shout N00B EMULATION MODE
-r REPEAT, --repeat=REPEAT
output [n] times
-
+
(Cmd) say goodnight, Gracie
goodnight, Gracie
(Cmd) say -ps --repeat=5 goodnight, Gracie
@@ -222,3 +222,4 @@ example/exampleSession.txt::
now: --->
---> say goodbye
goodbye
+
diff --git a/cmd2.py b/cmd2.py
index 237d1bfd..1740f33a 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -1,3 +1,4 @@
+# coding=utf-8
"""Variant on standard library's cmd with extra features.
To use, simply import cmd2.Cmd instead of cmd.Cmd; use precisely as though you
@@ -24,65 +25,62 @@ written to use `self.stdout.write()`,
mercurial repository at http://www.assembla.com/wiki/show/python-cmd2
"""
import cmd
-import re
-import os
-import sys
+import copy
+import datetime
+import doctest
+import glob
import optparse
+import os
+import platform
+import re
import subprocess
+import sys
import tempfile
-import doctest
-import unittest
-import datetime
-import urllib
-import glob
import traceback
-import platform
-import copy
+import unittest
from code import InteractiveConsole
from optparse import make_option
+
import pyparsing
-__version__ = '0.6.9a'
+# next(it) gets next item of iterator it. This is a replacement for calling it.next() in Python 2 and next(it) in Py3
+from six import next
-try:
- raw_input
-except NameError:
- raw_input = input
+# Possible types for text data. This is basestring() in Python 2 and str in Python 3.
+from six import string_types
+
+# raw_input() for Python 2 or input() for Python 3
+from six.moves import input
+
+# itertools.zip() for Python 2 or zip() for Python 3 - produces an iterator in both cases
+from six.moves import zip
+
+# Python 2 urllib2.urlopen() or Python3 urllib.request.urlopen()
+from six.moves.urllib.request import urlopen
# Python 3 compatability hack due to no built-in file keyword in Python 3
-# Due to two occurences of isinstance(<foo>, file) checking to see if something is of file type
+# Due to one occurence of isinstance(<foo>, file) checking to see if something is of file type
try:
file
except NameError:
import io
file = io.TextIOWrapper
-if sys.version_info[0] == 2:
- pyparsing.ParserElement.enablePackrat()
-
-"""
-Packrat is causing Python3 errors that I don't understand.
-
-> /usr/local/Cellar/python3/3.2/lib/python3.2/site-packages/pyparsing-1.5.6-py3.2.egg/pyparsing.py(999)scanString()
--> nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
-(Pdb) n
-NameError: global name 'exc' is not defined
+__version__ = '0.7.0'
-(Pdb) parseFn
-<bound method Or._parseCache of {Python style comment ^ C style comment}>
+# Pyparsing enablePackrat() can greatly speed up parsing, but problems have been seen in Python 3 in the past
+pyparsing.ParserElement.enablePackrat()
-Bug report filed: https://sourceforge.net/tracker/?func=detail&atid=617311&aid=3381439&group_id=97203
-"""
class OptionParser(optparse.OptionParser):
def exit(self, status=0, msg=None):
self.values._exit = True
if msg:
- print (msg)
+ print(msg)
def print_help(self, *args, **kwargs):
try:
- print (self._func.__doc__)
+ print(self._func.__doc__)
except AttributeError:
pass
optparse.OptionParser.print_help(self, *args, **kwargs)
@@ -127,9 +125,10 @@ def _which(editor):
except OSError:
return None
+
optparse.Values.get = _attr_get_
-options_defined = [] # used to distinguish --options from SQL-style --comments
+options_defined = [] # used to distinguish --options from SQL-style --comments
def options(option_list, arg_desc="arg"):
@@ -152,18 +151,20 @@ def options(option_list, arg_desc="arg"):
option_list = [option_list]
for opt in option_list:
options_defined.append(pyparsing.Literal(opt.get_opt_string()))
+
def option_setup(func):
optionParser = OptionParser()
for opt in option_list:
optionParser.add_option(opt)
optionParser.set_usage("%s [options] %s" % (func.__name__[3:], arg_desc))
optionParser._func = func
+
def new_func(instance, arg):
try:
opts, newArgList = optionParser.parse_args(arg.split())
# Must find the remaining args in the original argument list, but
# mustn't include the command itself
- #if hasattr(arg, 'parsed') and newArgList[0] == arg.parsed.command:
+ # if hasattr(arg, 'parsed') and newArgList[0] == arg.parsed.command:
# newArgList = newArgList[1:]
newArgs = remaining_args(arg, newArgList)
if isinstance(arg, ParsedString):
@@ -171,7 +172,7 @@ def options(option_list, arg_desc="arg"):
else:
arg = newArgs
except optparse.OptParseError as e:
- print (e)
+ print(e)
optionParser.print_help()
return
if hasattr(opts, '_exit'):
@@ -181,6 +182,7 @@ def options(option_list, arg_desc="arg"):
new_func.__doc__ = '%s\n%s' % (func.__doc__, optionParser.format_help())
return new_func
+
return option_setup
@@ -196,9 +198,11 @@ Download from http://sourceforge.net/projects/pywin32/"""
errmsg = """Redirecting to or from paste buffer requires xclip
to be installed on operating system.
On Debian/Ubuntu, 'sudo apt-get install xclip' will install it."""
+
def __init__(self):
Exception.__init__(self, self.errmsg)
+
pastebufferr = """Redirecting to or from paste buffer requires %s
to be installed on operating system.
%s"""
@@ -206,14 +210,18 @@ to be installed on operating system.
if sys.platform == "win32":
try:
import win32clipboard
+
+
def get_paste_buffer():
win32clipboard.OpenClipboard(0)
try:
result = win32clipboard.GetClipboardData()
except TypeError:
- result = '' #non-text
+ result = '' # non-text
win32clipboard.CloseClipboard()
return result
+
+
def write_to_paste_buffer(txt):
win32clipboard.OpenClipboard(0)
win32clipboard.EmptyClipboard()
@@ -222,30 +230,41 @@ if sys.platform == "win32":
except ImportError:
def get_paste_buffer(*args):
raise OSError(pastebufferr % ('pywin32', 'Download from http://sourceforge.net/projects/pywin32/'))
+
+
write_to_paste_buffer = get_paste_buffer
elif sys.platform == 'darwin':
can_clip = False
try:
# test for pbcopy - AFAIK, should always be installed on MacOS
- subprocess.check_call('pbcopy -help', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ subprocess.check_call('pbcopy -help', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE)
can_clip = True
except (subprocess.CalledProcessError, OSError, IOError):
pass
if can_clip:
def get_paste_buffer():
- pbcopyproc = subprocess.Popen('pbcopy -help', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ pbcopyproc = subprocess.Popen('pbcopy -help', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE)
return pbcopyproc.stdout.read()
+
+
def write_to_paste_buffer(txt):
- pbcopyproc = subprocess.Popen('pbcopy', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ pbcopyproc = subprocess.Popen('pbcopy', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE)
pbcopyproc.communicate(txt.encode())
else:
def get_paste_buffer(*args):
- raise OSError(pastebufferr % ('pbcopy', 'On MacOS X - error should not occur - part of the default installation'))
+ raise OSError(
+ pastebufferr % ('pbcopy', 'On MacOS X - error should not occur - part of the default installation'))
+
+
write_to_paste_buffer = get_paste_buffer
else:
can_clip = False
try:
- subprocess.check_call('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ subprocess.check_call('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE)
can_clip = True
except AttributeError: # check_call not defined, Python < 2.5
try:
@@ -253,17 +272,21 @@ else:
xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
xclipproc.stdin.write(teststring)
xclipproc.stdin.close()
- xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+ xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE)
if xclipproc.stdout.read() == teststring:
can_clip = True
- except Exception: # hate a bare Exception call, but exception classes vary too much b/t stdlib versions
+ except Exception: # hate a bare Exception call, but exception classes vary too much b/t stdlib versions
pass
except Exception:
- pass # something went wrong with xclip and we cannot use it
+ pass # something went wrong with xclip and we cannot use it
if can_clip:
def get_paste_buffer():
- xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+ xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE)
return xclipproc.stdout.read()
+
+
def write_to_paste_buffer(txt):
xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
xclipproc.stdin.write(txt.encode())
@@ -275,6 +298,8 @@ else:
else:
def get_paste_buffer(*args):
raise OSError(pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"'))
+
+
write_to_paste_buffer = get_paste_buffer
pyparsing.ParserElement.setDefaultWhitespaceChars(' \t')
@@ -295,6 +320,7 @@ class ParsedString(str):
new.parsed.statement['args'] = newargs
return new
+
class StubbornDict(dict):
'''Dictionary that tolerates many input formats.
Create it with stubbornDict(arg) factory function.
@@ -309,8 +335,10 @@ class StubbornDict(dict):
>>> sorted(d.items())
[('girl', 'Frauelein, Maedchen'), ('large', 'gross'), ('plaid', ''), ('plain', ''), ('shoe', 'schuh'), ('small', 'klein')]
'''
+
def update(self, arg):
dict.update(self, StubbornDict.to_dict(arg))
+
append = update
def __iadd__(self, arg):
@@ -393,28 +421,29 @@ def ljust(x, width, fillchar=' '):
x = (x + [fillchar] * width)[:width]
return x
+
class Cmd(cmd.Cmd):
echo = False
- case_insensitive = True # Commands recognized regardless of case
+ case_insensitive = True # Commands recognized regardless of case
continuation_prompt = '> '
- timing = False # Prints elapsed time for each command
+ timing = False # Prints elapsed time for each command
# make sure your terminators are not in legalChars!
legalChars = u'!#$%.:?@_' + pyparsing.alphanums + pyparsing.alphas8bit
shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
excludeFromHistory = '''run r list l history hi ed edit li eof'''.split()
default_to_shell = False
noSpecialParse = 'set ed edit exit'.split()
- defaultExtension = 'txt' # For ``save``, ``load``, etc.
- default_file_name = 'command.txt' # For ``save``, ``load``, etc.
- abbrev = True # Abbreviated commands recognized
+ defaultExtension = 'txt' # For ``save``, ``load``, etc.
+ default_file_name = 'command.txt' # For ``save``, ``load``, etc.
+ abbrev = True # Abbreviated commands recognized
current_script_dir = None
reserved_words = []
- feedback_to_output = False # Do include nonessentials in >, | output
- quiet = False # Do not suppress nonessential output
+ feedback_to_output = False # Do include nonessentials in >, | output
+ quiet = False # Do not suppress nonessential output
debug = False
locals_in_py = True
kept_state = None
- redirector = '>' # for sending output to file
+ redirector = '>' # for sending output to file
settable = stubbornDict('''
prompt
colors Colorized output (*nix only)
@@ -440,7 +469,7 @@ class Cmd(cmd.Cmd):
def perror(self, errmsg, statement=None):
if self.debug:
traceback.print_exc()
- print (str(errmsg))
+ print(str(errmsg))
def pfeedback(self, msg):
"""For printing nonessential feedback. Can be silenced with `quiet`.
@@ -449,7 +478,8 @@ class Cmd(cmd.Cmd):
if self.feedback_to_output:
self.poutput(msg)
else:
- print (msg)
+ print(msg)
+
_STOP_AND_EXIT = True # distinguish end of script file from actual exit
_STOP_SCRIPT_NO_EXIT = -999
editor = os.environ.get('EDITOR')
@@ -461,14 +491,15 @@ class Cmd(cmd.Cmd):
if _which(editor):
break
- colorcodes = {'bold':{True:'\x1b[1m',False:'\x1b[22m'},
- 'cyan':{True:'\x1b[36m',False:'\x1b[39m'},
- 'blue':{True:'\x1b[34m',False:'\x1b[39m'},
- 'red':{True:'\x1b[31m',False:'\x1b[39m'},
- 'magenta':{True:'\x1b[35m',False:'\x1b[39m'},
- 'green':{True:'\x1b[32m',False:'\x1b[39m'},
- 'underline':{True:'\x1b[4m',False:'\x1b[24m'}}
+ colorcodes = {'bold': {True: '\x1b[1m', False: '\x1b[22m'},
+ 'cyan': {True: '\x1b[36m', False: '\x1b[39m'},
+ 'blue': {True: '\x1b[34m', False: '\x1b[39m'},
+ 'red': {True: '\x1b[31m', False: '\x1b[39m'},
+ 'magenta': {True: '\x1b[35m', False: '\x1b[39m'},
+ 'green': {True: '\x1b[32m', False: '\x1b[39m'},
+ 'underline': {True: '\x1b[4m', False: '\x1b[24m'}}
colors = (platform.system() != 'Windows')
+
def colorize(self, val, color):
'''Given a string (``val``), returns that string wrapped in UNIX-style
special characters that turn on (and then off) text color and style.
@@ -485,11 +516,11 @@ class Cmd(cmd.Cmd):
self.stdout.write("""
Commands are %(casesensitive)scase-sensitive.
Commands may be terminated with: %(terminators)s
- Settable parameters: %(settable)s\n""" % \
- { 'casesensitive': (self.case_insensitive and 'not ') or '',
- 'terminators': str(self.terminators),
- 'settable': ' '.join(self.settable)
- })
+ Settable parameters: %(settable)s\n""" %
+ {'casesensitive': (self.case_insensitive and 'not ') or '',
+ 'terminators': str(self.terminators),
+ 'settable': ' '.join(self.settable)
+ })
def do_help(self, arg):
if arg:
@@ -516,13 +547,12 @@ class Cmd(cmd.Cmd):
def do_shortcuts(self, args):
"""Lists single-key shortcuts available."""
result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in sorted(self.shortcuts))
- self.stdout.write("Single-key shortcuts for other commands:\n%s\n" % (result))
+ self.stdout.write("Single-key shortcuts for other commands:\n{}\n".format(result))
prefixParser = pyparsing.Empty()
commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment])
commentGrammars.addParseAction(lambda x: '')
- commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(
- pyparsing.stringEnd ^ '*/')
+ commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/')
terminators = [';']
blankLinesAllowed = False
multilineCommands = []
@@ -533,25 +563,25 @@ class Cmd(cmd.Cmd):
>>> c.multilineCommands = ['multiline']
>>> c.case_insensitive = True
>>> c._init_parser()
- >>> print (c.parser.parseString('').dump())
+ >>> print(c.parser.parseString('').dump())
[]
- >>> print (c.parser.parseString('').dump())
+ >>> print(c.parser.parseString('').dump())
[]
- >>> print (c.parser.parseString('/* empty command */').dump())
+ >>> print(c.parser.parseString('/* empty command */').dump())
[]
- >>> print (c.parser.parseString('plainword').dump())
+ >>> print(c.parser.parseString('plainword').dump())
['plainword', '']
- command: plainword
- statement: ['plainword', '']
- command: plainword
- >>> print (c.parser.parseString('termbare;').dump())
+ >>> print(c.parser.parseString('termbare;').dump())
['termbare', '', ';', '']
- command: termbare
- statement: ['termbare', '', ';']
- command: termbare
- terminator: ;
- terminator: ;
- >>> print (c.parser.parseString('termbare; suffx').dump())
+ >>> print(c.parser.parseString('termbare; suffx').dump())
['termbare', '', ';', 'suffx']
- command: termbare
- statement: ['termbare', '', ';']
@@ -559,19 +589,19 @@ class Cmd(cmd.Cmd):
- terminator: ;
- suffix: suffx
- terminator: ;
- >>> print (c.parser.parseString('barecommand').dump())
+ >>> print(c.parser.parseString('barecommand').dump())
['barecommand', '']
- command: barecommand
- statement: ['barecommand', '']
- command: barecommand
- >>> print (c.parser.parseString('COMmand with args').dump())
+ >>> print(c.parser.parseString('COMmand with args').dump())
['command', 'with args']
- args: with args
- command: command
- statement: ['command', 'with args']
- args: with args
- command: command
- >>> print (c.parser.parseString('command with args and terminator; and suffix').dump())
+ >>> print(c.parser.parseString('command with args and terminator; and suffix').dump())
['command', 'with args and terminator', ';', 'and suffix']
- args: with args and terminator
- command: command
@@ -581,20 +611,20 @@ class Cmd(cmd.Cmd):
- terminator: ;
- suffix: and suffix
- terminator: ;
- >>> print (c.parser.parseString('simple | piped').dump())
+ >>> print(c.parser.parseString('simple | piped').dump())
['simple', '', '|', ' piped']
- command: simple
- pipeTo: piped
- statement: ['simple', '']
- command: simple
- >>> print (c.parser.parseString('double-pipe || is not a pipe').dump())
+ >>> print(c.parser.parseString('double-pipe || is not a pipe').dump())
['double', '-pipe || is not a pipe']
- args: -pipe || is not a pipe
- command: double
- statement: ['double', '-pipe || is not a pipe']
- args: -pipe || is not a pipe
- command: double
- >>> print (c.parser.parseString('command with args, terminator;sufx | piped').dump())
+ >>> print(c.parser.parseString('command with args, terminator;sufx | piped').dump())
['command', 'with args, terminator', ';', 'sufx', '|', ' piped']
- args: with args, terminator
- command: command
@@ -605,7 +635,7 @@ class Cmd(cmd.Cmd):
- terminator: ;
- suffix: sufx
- terminator: ;
- >>> print (c.parser.parseString('output into > afile.txt').dump())
+ >>> print(c.parser.parseString('output into > afile.txt').dump())
['output', 'into', '>', 'afile.txt']
- args: into
- command: output
@@ -614,7 +644,7 @@ class Cmd(cmd.Cmd):
- statement: ['output', 'into']
- args: into
- command: output
- >>> print (c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump())
+ >>> print(c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump())
['output', 'into', ';', 'sufx', '|', ' pipethrume plz', '>', 'afile.txt']
- args: into
- command: output
@@ -627,7 +657,7 @@ class Cmd(cmd.Cmd):
- terminator: ;
- suffix: sufx
- terminator: ;
- >>> print (c.parser.parseString('output to paste buffer >> ').dump())
+ >>> print(c.parser.parseString('output to paste buffer >> ').dump())
['output', 'to paste buffer', '>>', '']
- args: to paste buffer
- command: output
@@ -635,7 +665,7 @@ class Cmd(cmd.Cmd):
- statement: ['output', 'to paste buffer']
- args: to paste buffer
- command: output
- >>> print (c.parser.parseString('ignore the /* commented | > */ stuff;').dump())
+ >>> print(c.parser.parseString('ignore the /* commented | > */ stuff;').dump())
['ignore', 'the /* commented | > */ stuff', ';', '']
- args: the /* commented | > */ stuff
- command: ignore
@@ -644,7 +674,7 @@ class Cmd(cmd.Cmd):
- command: ignore
- terminator: ;
- terminator: ;
- >>> print (c.parser.parseString('has > inside;').dump())
+ >>> print(c.parser.parseString('has > inside;').dump())
['has', '> inside', ';', '']
- args: > inside
- command: has
@@ -653,10 +683,10 @@ class Cmd(cmd.Cmd):
- command: has
- terminator: ;
- terminator: ;
- >>> print (c.parser.parseString('multiline has > inside an unfinished command').dump())
+ >>> print(c.parser.parseString('multiline has > inside an unfinished command').dump())
['multiline', ' has > inside an unfinished command']
- multilineCommand: multiline
- >>> print (c.parser.parseString('multiline has > inside;').dump())
+ >>> print(c.parser.parseString('multiline has > inside;').dump())
['multiline', 'has > inside', ';', '']
- args: has > inside
- multilineCommand: multiline
@@ -665,10 +695,10 @@ class Cmd(cmd.Cmd):
- multilineCommand: multiline
- terminator: ;
- terminator: ;
- >>> print (c.parser.parseString('multiline command /* with comment in progress;').dump())
+ >>> print(c.parser.parseString('multiline command /* with comment in progress;').dump())
['multiline', ' command /* with comment in progress;']
- multilineCommand: multiline
- >>> print (c.parser.parseString('multiline command /* with comment complete */ is done;').dump())
+ >>> print(c.parser.parseString('multiline command /* with comment complete */ is done;').dump())
['multiline', 'command /* with comment complete */ is done', ';', '']
- args: command /* with comment complete */ is done
- multilineCommand: multiline
@@ -677,7 +707,7 @@ class Cmd(cmd.Cmd):
- multilineCommand: multiline
- terminator: ;
- terminator: ;
- >>> print (c.parser.parseString('multiline command ends\n\n').dump())
+ >>> print(c.parser.parseString('multiline command ends\n\n').dump())
['multiline', 'command ends', '\n', '\n']
- args: command ends
- multilineCommand: multiline
@@ -686,7 +716,7 @@ class Cmd(cmd.Cmd):
- multilineCommand: multiline
- terminator: ['\n', '\n']
- terminator: ['\n', '\n']
- >>> print (c.parser.parseString('multiline command "with term; ends" now\n\n').dump())
+ >>> print(c.parser.parseString('multiline command "with term; ends" now\n\n').dump())
['multiline', 'command "with term; ends" now', '\n', '\n']
- args: command "with term; ends" now
- multilineCommand: multiline
@@ -695,7 +725,7 @@ class Cmd(cmd.Cmd):
- multilineCommand: multiline
- terminator: ['\n', '\n']
- terminator: ['\n', '\n']
- >>> print (c.parser.parseString('what if "quoted strings /* seem to " start comments?').dump())
+ >>> print(c.parser.parseString('what if "quoted strings /* seem to " start comments?').dump())
['what', 'if "quoted strings /* seem to " start comments?']
- args: if "quoted strings /* seem to " start comments?
- command: what
@@ -703,21 +733,25 @@ class Cmd(cmd.Cmd):
- args: if "quoted strings /* seem to " start comments?
- command: what
'''
- #outputParser = (pyparsing.Literal('>>') | (pyparsing.WordStart() + '>') | pyparsing.Regex('[^=]>'))('output')
- outputParser = (pyparsing.Literal(self.redirector *2) | \
- (pyparsing.WordStart() + self.redirector) | \
+ # outputParser = (pyparsing.Literal('>>') | (pyparsing.WordStart() + '>') | pyparsing.Regex('[^=]>'))('output')
+ outputParser = (pyparsing.Literal(self.redirector * 2) |
+ (pyparsing.WordStart() + self.redirector) |
pyparsing.Regex('[^=]' + self.redirector))('output')
- terminatorParser = pyparsing.Or([(hasattr(t, 'parseString') and t) or pyparsing.Literal(t) for t in self.terminators])('terminator')
+ terminatorParser = pyparsing.Or(
+ [(hasattr(t, 'parseString') and t) or pyparsing.Literal(t) for t in self.terminators])('terminator')
stringEnd = pyparsing.stringEnd ^ '\nEOF'
- self.multilineCommand = pyparsing.Or([pyparsing.Keyword(c, caseless=self.case_insensitive) for c in self.multilineCommands])('multilineCommand')
+ self.multilineCommand = pyparsing.Or(
+ [pyparsing.Keyword(c, caseless=self.case_insensitive) for c in self.multilineCommands])('multilineCommand')
oneLineCommand = (~self.multilineCommand + pyparsing.Word(self.legalChars))('command')
pipe = pyparsing.Keyword('|', identChars='|')
self.commentGrammars.ignore(pyparsing.quotedString).setParseAction(lambda x: '')
doNotParse = self.commentGrammars | self.commentInProgress | pyparsing.quotedString
afterElements = \
pyparsing.Optional(pipe + pyparsing.SkipTo(outputParser ^ stringEnd, ignore=doNotParse)('pipeTo')) + \
- pyparsing.Optional(outputParser + pyparsing.SkipTo(stringEnd, ignore=doNotParse).setParseAction(lambda x: x[0].strip())('outputTo'))
+ pyparsing.Optional(
+ outputParser + pyparsing.SkipTo(stringEnd, ignore=doNotParse).setParseAction(lambda x: x[0].strip())(
+ 'outputTo'))
if self.case_insensitive:
self.multilineCommand.setParseAction(lambda x: x[0].lower())
oneLineCommand.setParseAction(lambda x: x[0].lower())
@@ -726,14 +760,21 @@ class Cmd(cmd.Cmd):
else:
self.blankLineTerminator = (pyparsing.lineEnd + pyparsing.lineEnd)('terminator')
self.blankLineTerminator.setResultsName('terminator')
- self.blankLineTerminationParser = ((self.multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(self.blankLineTerminator, ignore=doNotParse).setParseAction(lambda x: x[0].strip())('args') + self.blankLineTerminator)('statement')
- self.multilineParser = (((self.multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(terminatorParser, ignore=doNotParse).setParseAction(lambda x: x[0].strip())('args') + terminatorParser)('statement') +
- pyparsing.SkipTo(outputParser ^ pipe ^ stringEnd, ignore=doNotParse).setParseAction(lambda x: x[0].strip())('suffix') + afterElements)
+ self.blankLineTerminationParser = ((self.multilineCommand ^ oneLineCommand) +
+ pyparsing.SkipTo(self.blankLineTerminator, ignore=doNotParse).setParseAction(
+ lambda x: x[0].strip())('args') + self.blankLineTerminator)('statement')
+ self.multilineParser = (((self.multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(terminatorParser,
+ ignore=doNotParse).setParseAction(
+ lambda x: x[0].strip())('args') + terminatorParser)('statement') +
+ pyparsing.SkipTo(outputParser ^ pipe ^ stringEnd, ignore=doNotParse).setParseAction(
+ lambda x: x[0].strip())('suffix') + afterElements)
self.multilineParser.ignore(self.commentInProgress)
- self.singleLineParser = ((oneLineCommand + pyparsing.SkipTo(terminatorParser ^ stringEnd ^ pipe ^ outputParser, ignore=doNotParse).setParseAction(lambda x:x[0].strip())('args'))('statement') +
+ self.singleLineParser = ((oneLineCommand + pyparsing.SkipTo(terminatorParser ^ stringEnd ^ pipe ^ outputParser,
+ ignore=doNotParse).setParseAction(
+ lambda x: x[0].strip())('args'))('statement') +
pyparsing.Optional(terminatorParser) + afterElements)
- #self.multilineParser = self.multilineParser.setResultsName('multilineParser')
- #self.singleLineParser = self.singleLineParser.setResultsName('singleLineParser')
+ # self.multilineParser = self.multilineParser.setResultsName('multilineParser')
+ # self.singleLineParser = self.singleLineParser.setResultsName('singleLineParser')
self.blankLineTerminationParser = self.blankLineTerminationParser.setResultsName('statement')
self.parser = self.prefixParser + (
stringEnd |
@@ -741,7 +782,7 @@ class Cmd(cmd.Cmd):
self.singleLineParser |
self.blankLineTerminationParser |
self.multilineCommand + pyparsing.SkipTo(stringEnd, ignore=doNotParse)
- )
+ )
self.parser.ignore(self.commentGrammars)
inputMark = pyparsing.Literal('<')
@@ -757,6 +798,7 @@ class Cmd(cmd.Cmd):
def preparse(self, raw, **kwargs):
return raw
+
def postparse(self, parseResult):
return parseResult
@@ -786,6 +828,7 @@ class Cmd(cmd.Cmd):
def postparsing_precmd(self, statement):
stop = 0
return stop, statement
+
def postparsing_postcmd(self, stop):
return stop
@@ -795,16 +838,18 @@ class Cmd(cmd.Cmd):
if target in dir(self):
result = target
else:
- if self.abbrev: # accept shortened versions of commands
+ if self.abbrev: # accept shortened versions of commands
funcs = [fname for fname in self.keywords if fname.startswith(arg)]
if len(funcs) == 1:
result = 'do_' + funcs[0]
return result
+
def onecmd_plus_hooks(self, line):
# The outermost level of try/finally nesting can be condensed once
# Python 2.4 support can be dropped.
stop = 0
try:
+ statement = ''
try:
statement = self.complete_statement(line)
(stop, statement) = self.postparsing_precmd(statement)
@@ -828,11 +873,10 @@ class Cmd(cmd.Cmd):
self.perror(str(e), statement)
finally:
return self.postparsing_postcmd(stop)
+
def complete_statement(self, line):
"""Keep accepting lines of input until the command is complete."""
- if (not line) or (
- not pyparsing.Or(self.commentGrammars).
- setParseAction(lambda x: '').transformString(line)):
+ if not line or (not pyparsing.Or(self.commentGrammars).setParseAction(lambda x: '').transformString(line)):
raise EmptyStatement()
statement = self.parsed(line)
while statement.parsed.multilineCommand and (statement.parsed.terminator == ''):
@@ -847,7 +891,8 @@ class Cmd(cmd.Cmd):
if statement.parsed.pipeTo:
self.kept_state = Statekeeper(self, ('stdout',))
self.kept_sys = Statekeeper(sys, ('stdout',))
- self.redirect = subprocess.Popen(statement.parsed.pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+ self.redirect = subprocess.Popen(statement.parsed.pipeTo, shell=True, stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE)
sys.stdout = self.stdout = self.redirect.stdin
elif statement.parsed.output:
if (not statement.parsed.outputTo) and (not can_clip):
@@ -915,7 +960,7 @@ class Cmd(cmd.Cmd):
if self.use_rawinput:
try:
- line = raw_input(prompt)
+ line = input(prompt)
except EOFError:
line = 'EOF'
else:
@@ -925,7 +970,7 @@ class Cmd(cmd.Cmd):
if not len(line):
line = 'EOF'
else:
- if line[-1] == '\n': # this was always true in Cmd
+ if line[-1] == '\n': # this was always true in Cmd
line = line[:-1]
return line
@@ -944,21 +989,21 @@ class Cmd(cmd.Cmd):
import readline
self.old_completer = readline.get_completer()
readline.set_completer(self.complete)
- readline.parse_and_bind(self.completekey+": complete")
+ readline.parse_and_bind(self.completekey + ": complete")
except ImportError:
pass
+ stop = None
try:
if intro is not None:
self.intro = intro
if self.intro:
- self.stdout.write(str(self.intro)+"\n")
- stop = None
+ self.stdout.write(str(self.intro) + "\n")
while not stop:
if self.cmdqueue:
line = self.cmdqueue.pop(0)
else:
line = self.pseudo_raw_input(self.prompt)
- if (self.echo) and (isinstance(self.stdin, file)):
+ if self.echo and isinstance(self.stdin, file):
self.stdout.write(line + '\n')
stop = self.onecmd_plus_hooks(line)
self.postloop()
@@ -972,11 +1017,13 @@ class Cmd(cmd.Cmd):
return stop
def do_EOF(self, arg):
- return self._STOP_SCRIPT_NO_EXIT # End of script; should not exit app
+ return self._STOP_SCRIPT_NO_EXIT # End of script; should not exit app
+
do_eof = do_EOF
def do_quit(self, arg):
return self._STOP_AND_EXIT
+
do_exit = do_quit
do_q = do_quit
@@ -991,11 +1038,12 @@ class Cmd(cmd.Cmd):
| a list of tuples -> interpreted as (value, text), so
that the return value can differ from
the text advertised to the user '''
- if isinstance(options, basestring):
- options = zip(options.split(), options.split())
+ local_opts = options
+ if isinstance(options, string_types):
+ local_opts = list(zip(options.split(), options.split()))
fulloptions = []
- for opt in options:
- if isinstance(opt, basestring):
+ for opt in local_opts:
+ if isinstance(opt, string_types):
fulloptions.append((opt, opt))
else:
try:
@@ -1003,19 +1051,18 @@ class Cmd(cmd.Cmd):
except IndexError:
fulloptions.append((opt[0], opt[0]))
for (idx, (value, text)) in enumerate(fulloptions):
- self.poutput(' %2d. %s\n' % (idx+1, text))
+ self.poutput(' %2d. %s\n' % (idx + 1, text))
while True:
- response = raw_input(prompt)
+ response = input(prompt)
try:
response = int(response)
result = fulloptions[response - 1][0]
break
except ValueError:
- pass # loop and ask again
+ pass # loop and ask again
return result
- @options([make_option('-l', '--long', action="store_true",
- help="describe function of parameter")])
+ @options([make_option('-l', '--long', action="store_true", help="describe function of parameter")])
def do_show(self, arg, opts):
'''Shows value of a parameter.'''
param = arg.strip().lower()
@@ -1032,7 +1079,7 @@ class Cmd(cmd.Cmd):
else:
self.poutput(result[p])
else:
- raise NotImplementedError("Parameter '%s' not supported (type 'show' for list of parameters)." % param)
+ raise LookupError("Parameter '%s' not supported (type 'show' for list of parameters)." % param)
def do_set(self, arg):
'''
@@ -1067,7 +1114,7 @@ class Cmd(cmd.Cmd):
def do_pause(self, arg):
'Displays the specified text then waits for the user to press RETURN.'
- raw_input(arg + '\n')
+ input(arg + '\n')
def do_shell(self, arg):
'execute a command as if at the OS prompt.'
@@ -1091,8 +1138,10 @@ class Cmd(cmd.Cmd):
else:
def quit():
raise EmbeddedConsoleExit
+
def onecmd_plus_hooks(arg):
return self.onecmd_plus_hooks(arg + '\n')
+
def run(arg):
try:
file = open(arg)
@@ -1100,23 +1149,26 @@ class Cmd(cmd.Cmd):
file.close()
except IOError as e:
self.perror(e)
+
self.pystate['quit'] = quit
self.pystate['exit'] = quit
self.pystate['cmd'] = onecmd_plus_hooks
self.pystate['run'] = run
+ keepstate = None
try:
cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
- keepstate = Statekeeper(sys, ('stdin','stdout'))
+ keepstate = Statekeeper(sys, ('stdin', 'stdout'))
sys.stdout = self.stdout
sys.stdin = self.stdin
- interp.interact(banner= "Python %s on %s\n%s\n(%s)\n%s" %
- (sys.version, sys.platform, cprt, self.__class__.__name__, self.do_py.__doc__))
+ interp.interact(banner="Python %s on %s\n%s\n(%s)\n%s" %
+ (sys.version, sys.platform, cprt, self.__class__.__name__, self.do_py.__doc__))
except EmbeddedConsoleExit:
pass
- keepstate.restore()
+ if keepstate is not None:
+ keepstate.restore()
@options([make_option('-s', '--script', action="store_true", help="Script format; no separation lines"),
- ], arg_desc = '(limit on which commands to include)')
+ ], arg_desc='(limit on which commands to include)')
def do_history(self, arg, opts):
"""history [arg]: lists past commands issued
@@ -1134,6 +1186,7 @@ class Cmd(cmd.Cmd):
self.poutput(hi)
else:
self.stdout.write(hi.pr())
+
def last_matching(self, arg):
try:
if arg:
@@ -1142,6 +1195,7 @@ class Cmd(cmd.Cmd):
return self.history[-1]
except IndexError:
return None
+
def do_list(self, arg):
"""list [arg]: lists last command issued
@@ -1189,11 +1243,13 @@ class Cmd(cmd.Cmd):
os.system('%s %s' % (self.editor, filename))
self.do__load(filename)
+
do_edit = do_ed
- saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums)^'*')("idx") +
+ saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums) ^ '*')("idx") +
pyparsing.Optional(pyparsing.Word(legalChars + '/\\'))("fname") +
pyparsing.stringEnd)
+
def do_save(self, arg):
"""`save [N] [filename.ext]`
@@ -1211,26 +1267,26 @@ class Cmd(cmd.Cmd):
if args.idx == '*':
saveme = '\n\n'.join(self.history[:])
elif args.idx:
- saveme = self.history[int(args.idx)-1]
+ saveme = self.history[int(args.idx) - 1]
else:
saveme = self.history[-1]
try:
f = open(os.path.expanduser(fname), 'w')
f.write(saveme)
f.close()
- self.pfeedback('Saved to %s' % (fname))
+ self.pfeedback('Saved to {}'.format(fname))
except Exception as e:
- self.perror('Error saving %s' % (fname))
+ self.perror('Error saving {}'.format(fname))
raise
def read_file_or_url(self, fname):
# TODO: not working on localhost
- if isinstance(fname, file):
+ if os.path.isfile(fname):
result = open(fname, 'r')
else:
match = self.urlre.match(fname)
if match:
- result = urllib.urlopen(match.group(1))
+ result = urlopen(match.group(1))
else:
fname = os.path.expanduser(fname)
try:
@@ -1252,6 +1308,7 @@ class Cmd(cmd.Cmd):
self.do__load('%s %s' % (targetname, args))
urlre = re.compile('(https?://[-\\w\\./]+)')
+
def do_load(self, arg=None):
"""Runs script of command(s) from a file or URL."""
if arg is None:
@@ -1264,8 +1321,8 @@ class Cmd(cmd.Cmd):
except IOError as e:
self.perror('Problem accessing script from %s: \n%s' % (targetname, e))
return
- keepstate = Statekeeper(self, ('stdin','use_rawinput','prompt',
- 'continuation_prompt','current_script_dir'))
+ keepstate = Statekeeper(self, ('stdin', 'use_rawinput', 'prompt',
+ 'continuation_prompt', 'current_script_dir'))
self.stdin = target
self.use_rawinput = False
self.prompt = self.continuation_prompt = ''
@@ -1275,6 +1332,7 @@ class Cmd(cmd.Cmd):
keepstate.restore()
self.lastcmd = ''
return stop and (stop != self._STOP_SCRIPT_NO_EXIT)
+
do__load = do_load # avoid an unfortunate legacy use of do_load from sqlpython
def do_run(self, arg):
@@ -1290,6 +1348,7 @@ class Cmd(cmd.Cmd):
self.pfeedback(runme)
if runme:
stop = self.onecmd_plus_hooks(runme)
+
do_r = do_run
def fileimport(self, statement, source):
@@ -1305,8 +1364,9 @@ class Cmd(cmd.Cmd):
def runTranscriptTests(self, callargs):
class TestMyAppCase(Cmd2TestCase):
CmdApp = self.__class__
+
self.__class__.testfiles = callargs
- sys.argv = [sys.argv[0]] # the --test argument upsets unittest.main()
+ sys.argv = [sys.argv[0]] # the --test argument upsets unittest.main()
testcase = TestMyAppCase()
runner = unittest.TextTestRunner()
result = runner.run(testcase)
@@ -1317,11 +1377,11 @@ class Cmd(cmd.Cmd):
if self.onecmd_plus_hooks(initial_command + '\n'):
return self._STOP_AND_EXIT
- def cmdloop(self):
+ def cmdloop(self, intro=None):
parser = optparse.OptionParser()
parser.add_option('-t', '--test', dest='test',
- action="store_true",
- help='Test against transcript(s) in FILE (wildcards OK)')
+ action="store_true",
+ help='Test against transcript(s) in FILE (wildcards OK)')
(callopts, callargs) = parser.parse_args()
if callopts.test:
self.runTranscriptTests(callargs)
@@ -1329,15 +1389,19 @@ class Cmd(cmd.Cmd):
if not self.run_commands_at_invocation(callargs):
self._cmdloop()
+
class HistoryItem(str):
listformat = '-------------------------[%d]\n%s\n'
+
def __init__(self, instr):
str.__init__(self)
self.lowercase = self.lower()
self.idx = None
+
def pr(self):
return self.listformat % (self.idx, str(self))
+
class History(list):
'''A list of HistoryItems that knows how to respond to user requests.
>>> h = History([HistoryItem('first'), HistoryItem('second'), HistoryItem('third'), HistoryItem('fourth')])
@@ -1360,17 +1424,20 @@ class History(list):
>>> h.search('/IR/')
['first', 'third']
'''
+
def zero_based_index(self, onebased):
result = onebased
if result > 0:
result -= 1
return result
+
def to_index(self, raw):
if raw:
result = self.zero_based_index(int(raw))
else:
result = None
return result
+
def search(self, target):
target = target.strip()
if target[0] == target[-1] == '/' and len(target) > 1:
@@ -1379,7 +1446,9 @@ class History(list):
target = re.escape(target)
pattern = re.compile(target, re.IGNORECASE)
return [s for s in self if pattern.search(s)]
+
spanpattern = re.compile(r'^\s*(?P<start>\-?\d+)?\s*(?P<separator>:|(\.{2,}))?\s*(?P<end>\-?\d+)?\s*$')
+
def span(self, raw):
if raw.lower() in ('*', '-', 'all'):
raw = ':'
@@ -1402,10 +1471,12 @@ class History(list):
return result
rangePattern = re.compile(r'^\s*(?P<start>[\d]+)?\s*\-\s*(?P<end>[\d]+)?\s*$')
+
def append(self, new):
new = HistoryItem(new)
list.append(self, new)
new.idx = len(self)
+
def extend(self, new):
for n in new:
self.append(n)
@@ -1418,7 +1489,7 @@ class History(list):
if getme < 0:
return self[:(-1 * getme)]
else:
- return [self[getme-1]]
+ return [self[getme - 1]]
except IndexError:
return []
except ValueError:
@@ -1436,16 +1507,19 @@ class History(list):
if getme.startswith(r'/') and getme.endswith(r'/'):
finder = re.compile(getme[1:-1], re.DOTALL | re.MULTILINE | re.IGNORECASE)
+
def isin(hi):
return finder.search(hi)
else:
def isin(hi):
- return (getme.lower() in hi.lowercase)
+ return getme.lower() in hi.lowercase
return [itm for itm in self if isin(itm)]
+
class NotSettableError(Exception):
pass
+
def cast(current, new):
"""Tries to force a new value into the same type as the current."""
typ = type(current)
@@ -1458,54 +1532,64 @@ def cast(current, new):
new = new.lower()
except:
pass
- if (new=='on') or (new[0] in ('y','t')):
+ if (new == 'on') or (new[0] in ('y', 't')):
return True
- if (new=='off') or (new[0] in ('n','f')):
+ if (new == 'off') or (new[0] in ('n', 'f')):
return False
else:
try:
return typ(new)
except:
pass
- print ("Problem setting parameter (now %s) to %s; incorrect type?" % (current, new))
+ print("Problem setting parameter (now %s) to %s; incorrect type?" % (current, new))
return current
+
class Statekeeper(object):
def __init__(self, obj, attribs):
self.obj = obj
self.attribs = attribs
if self.obj:
self.save()
+
def save(self):
for attrib in self.attribs:
setattr(self, attrib, getattr(self.obj, attrib))
+
def restore(self):
if self.obj:
for attrib in self.attribs:
setattr(self.obj, attrib, getattr(self, attrib))
+
class Borg(object):
'''All instances of any Borg subclass will share state.
from Python Cookbook, 2nd Ed., recipe 6.16'''
_shared_state = {}
+
def __new__(cls, *a, **k):
obj = object.__new__(cls, *a, **k)
obj.__dict__ = cls._shared_state
return obj
+
class OutputTrap(Borg):
'''Instantiate an OutputTrap to divert/capture ALL stdout output. For use in unit testing.
Call `tearDown()` to return to normal output.'''
+
def __init__(self):
self.contents = ''
self.old_stdout = sys.stdout
sys.stdout = self
+
def write(self, txt):
self.contents += txt
+
def read(self):
result = self.contents
self.contents = ''
return result
+
def tearDown(self):
sys.stdout = self.old_stdout
self.contents = ''
@@ -1516,6 +1600,7 @@ class Cmd2TestCase(unittest.TestCase):
that will execute the commands in a transcript file and expect the results shown.
See example.py'''
CmdApp = None
+
def fetchTranscripts(self):
self.transcripts = {}
for fileset in self.CmdApp.testfiles:
@@ -1524,86 +1609,92 @@ class Cmd2TestCase(unittest.TestCase):
self.transcripts[fname] = iter(tfile.readlines())
tfile.close()
if not len(self.transcripts):
- raise StandardError("No test files found - nothing to test.")
+ raise Exception("No test files found - nothing to test.")
+
def setUp(self):
if self.CmdApp:
self.outputTrap = OutputTrap()
self.cmdapp = self.CmdApp()
self.fetchTranscripts()
- def runTest(self): # was testall
+
+ def runTest(self): # was testall
if self.CmdApp:
its = sorted(self.transcripts.items())
for (fname, transcript) in its:
self._test_transcript(fname, transcript)
+
regexPattern = pyparsing.QuotedString(quoteChar=r'/', escChar='\\', multiline=True, unquoteResults=True)
regexPattern.ignore(pyparsing.cStyleComment)
notRegexPattern = pyparsing.Word(pyparsing.printables)
notRegexPattern.setParseAction(lambda t: re.escape(t[0]))
expectationParser = regexPattern | notRegexPattern
anyWhitespace = re.compile(r'\s', re.DOTALL | re.MULTILINE)
+
def _test_transcript(self, fname, transcript):
lineNum = 0
finished = False
- line = transcript.next()
+ line = next(transcript)
lineNum += 1
tests_run = 0
while not finished:
# Scroll forward to where actual commands begin
while not line.startswith(self.cmdapp.prompt):
try:
- line = transcript.next()
+ line = next(transcript)
except StopIteration:
finished = True
break
lineNum += 1
command = [line[len(self.cmdapp.prompt):]]
- line = transcript.next()
+ line = next(transcript)
# Read the entirety of a multi-line command
while line.startswith(self.cmdapp.continuation_prompt):
command.append(line[len(self.cmdapp.continuation_prompt):])
try:
- line = transcript.next()
+ line = next(transcript)
except StopIteration:
raise (StopIteration,
- 'Transcript broke off while reading command beginning at line %d with\n%s'
- % (command[0]))
+ 'Transcript broke off while reading command beginning at line {} with\n{}'.format(lineNum,
+ command[0])
+ )
lineNum += 1
command = ''.join(command)
# Send the command into the application and capture the resulting output
stop = self.cmdapp.onecmd_plus_hooks(command)
- #TODO: should act on ``stop``
+ # TODO: should act on ``stop``
result = self.outputTrap.read()
# Read the expected result from transcript
if line.startswith(self.cmdapp.prompt):
- message = '\nFile %s, line %d\nCommand was:\n%r\nExpected: (nothing)\nGot:\n%r\n'%\
- (fname, lineNum, command, result)
- self.assert_(not(result.strip()), message)
+ message = '\nFile %s, line %d\nCommand was:\n%r\nExpected: (nothing)\nGot:\n%r\n' % \
+ (fname, lineNum, command, result)
+ self.assert_(not (result.strip()), message)
continue
expected = []
while not line.startswith(self.cmdapp.prompt):
expected.append(line)
try:
- line = transcript.next()
+ line = next(transcript)
except StopIteration:
finished = True
break
lineNum += 1
expected = ''.join(expected)
# Compare actual result to expected
- message = '\nFile %s, line %d\nCommand was:\n%s\nExpected:\n%s\nGot:\n%s\n'%\
- (fname, lineNum, command, expected, result)
+ message = '\nFile %s, line %d\nCommand was:\n%s\nExpected:\n%s\nGot:\n%s\n' % \
+ (fname, lineNum, command, expected, result)
expected = self.expectationParser.transformString(expected)
# checking whitespace is a pain - let's skip it
expected = self.anyWhitespace.sub('', expected)
result = self.anyWhitespace.sub('', result)
- self.assert_(re.match(expected, result, re.MULTILINE | re.DOTALL), message)
+ self.assertTrue(re.match(expected, result, re.MULTILINE | re.DOTALL), message)
def tearDown(self):
if self.CmdApp:
self.outputTrap.tearDown()
+
if __name__ == '__main__':
- doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE)
+ doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)
'''
To make your application transcript-testable, replace
@@ -1627,5 +1718,3 @@ into a file, ``transcript.test``, and invoke the test like::
Wildcards can be used to test against multiple transcript files.
'''
-
-
diff --git a/docs/conf.py b/docs/conf.py
index ee6b5e6a..32d9243c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -11,12 +11,12 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-import sys, os
+# import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.append(os.path.abspath('.'))
+# sys.path.append(os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
@@ -31,7 +31,7 @@ templates_path = ['_templates']
source_suffix = '.rst'
# The encoding of source files.
-#source_encoding = 'utf-8'
+# source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
@@ -51,40 +51,40 @@ release = '0.6.9a'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-#language = None
+# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
-#today = ''
+# today = ''
# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
-#unused_docs = []
+# unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
@@ -96,26 +96,26 @@ html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
-#html_theme_options = {}
+# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
-#html_title = None
+# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-#html_logo = None
+# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-#html_favicon = None
+# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -124,74 +124,73 @@ html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
# If false, no module index is generated.
-#html_use_modindex = True
+# html_use_modindex = True
# If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = ''
+# html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'cmd2doc'
-
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
- ('index', 'cmd2.tex', u'cmd2 Documentation',
- u'Catherine Devlin', 'manual'),
+ ('index', 'cmd2.tex', u'cmd2 Documentation',
+ u'Catherine Devlin', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-#latex_logo = None
+# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+# latex_preamble = ''
# Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
# If false, no module index is generated.
-#latex_use_modindex = True
+# latex_use_modindex = True
# Example configuration for intersphinx: refer to the Python standard library.
diff --git a/docs/freefeatures.rst b/docs/freefeatures.rst
index 87952611..43b39b29 100644
--- a/docs/freefeatures.rst
+++ b/docs/freefeatures.rst
@@ -51,8 +51,8 @@ Commands at invocation
You can send commands to your app as you invoke it by
including them as extra arguments to the program.
-``cmd2`` interprets each argument as a separate
-command, so you should enclose each command in
+``cmd2`` interprets each argument as a separate
+command, so you should enclose each command in
quotation marks if it is more than a one-word command.
::
@@ -60,9 +60,9 @@ quotation marks if it is more than a one-word command.
cat@eee:~/proj/cmd2/example$ python example.py "say hello" "say Gracie" quit
hello
Gracie
- cat@eee:~/proj/cmd2/example$
+ cat@eee:~/proj/cmd2/example$
+
-
Output redirection
==================
@@ -75,7 +75,7 @@ As in a Unix shell, output of a command can be redirected:
ending with a bare ``>``, as in ``mycommand args >``.. Redirecting
to paste buffer requires software to be installed on the operating
system, pywin32_ on Windows or xclip_ on \*nix.
-
+
If your application depends on mathematical syntax, ``>`` may be a bad
choice for redirecting output - it will prevent you from using the
greater-than sign in your actual user commands. You can override your
@@ -83,7 +83,7 @@ app's value of ``self.redirector`` to use a different string for output redirect
class MyApp(cmd2.Cmd):
redirector = '->'
-
+
::
(Cmd) say line1 -> out.txt
@@ -94,7 +94,7 @@ app's value of ``self.redirector`` to use a different string for output redirect
.. _pywin32: http://sourceforge.net/projects/pywin32/
.. _xclip: http://www.cyberciti.biz/faq/xclip-linux-insert-files-command-output-intoclipboard/
-
+
Python
======
@@ -109,34 +109,34 @@ See see :ref:`parameters`)
::
- (Cmd) py print("-".join("spelling"))
- s-p-e-l-l-i-n-g
- (Cmd) py
- Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15)
- [GCC 4.4.1] on linux2
- Type "help", "copyright", "credits" or "license" for more information.
- (CmdLineApp)
-
- py <command>: Executes a Python command.
- py: Enters interactive Python mode.
- End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, 'exit()`.
- Non-python commands can be issued with `cmd("your command")`.
-
- >>> import os
- >>> os.uname()
- ('Linux', 'eee', '2.6.31-19-generic', '#56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010', 'i686')
- >>> cmd("say --piglatin {os}".format(os=os.uname()[0]))
- inuxLay
- >>> self.prompt
- '(Cmd) '
- >>> self.prompt = 'Python was here > '
- >>> quit()
- Python was here >
+ (Cmd) py print("-".join("spelling"))
+ s-p-e-l-l-i-n-g
+ (Cmd) py
+ Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15)
+ [GCC 4.4.1] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ (CmdLineApp)
+
+ py <command>: Executes a Python command.
+ py: Enters interactive Python mode.
+ End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, 'exit()`.
+ Non-python commands can be issued with `cmd("your command")`.
+
+ >>> import os
+ >>> os.uname()
+ ('Linux', 'eee', '2.6.31-19-generic', '#56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010', 'i686')
+ >>> cmd("say --piglatin {os}".format(os=os.uname()[0]))
+ inuxLay
+ >>> self.prompt
+ '(Cmd) '
+ >>> self.prompt = 'Python was here > '
+ >>> quit()
+ Python was here >
Searchable command history
==========================
-All cmd_-based applications have access to previous commands with
+All cmd_-based applications have access to previous commands with
the up- and down- cursor keys.
All cmd_-based applications on systems with the ``readline`` module
@@ -155,7 +155,7 @@ also provide `bash-like history list editing`_.
Quitting the application
========================
-``cmd2`` pre-defines a ``quit`` command for you (with
+``cmd2`` pre-defines a ``quit`` command for you (with
synonyms ``exit`` and simply ``q``).
It's trivial, but it's one less thing for you to remember.
@@ -164,14 +164,14 @@ Abbreviated commands
====================
``cmd2`` apps will accept shortened command names
-so long as there is no ambiguity. Thus, if
+so long as there is no ambiguity. 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 can be turned off with ``app.abbrev`` (see :ref:`parameters`)
-
+
Misc. pre-defined commands
==========================
@@ -196,12 +196,12 @@ a ``cmd2``-based app is copied from the screen and pasted into a text
file, ``transcript.txt``, then a transcript test can be run against it::
python app.py --test transcript.txt
-
+
Any non-whitespace deviations between the output prescribed in ``transcript.txt`` and
the actual output from a fresh run of the application will be reported
as a unit test failure. (Whitespace is ignored during the comparison.)
-Regular expressions can be embedded in the transcript inside paired ``/``
+Regular expressions can be embedded in the transcript inside paired ``/``
slashes. These regular expressions should not include any whitespace
expressions.
diff --git a/docs/index.rst b/docs/index.rst
index 40fb8c81..f98ebb4a 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -20,11 +20,11 @@ The basic use of ``cmd2`` is identical to that of cmd_.
1. Create a subclass of ``cmd2.Cmd``. Define attributes and
``do_*`` methods to control its behavior. Throughout this documentation,
we will assume that you are naming your subclass ``App``::
-
+
from cmd2 import Cmd
class App(Cmd):
# customized attributes and methods here
-
+
2. Instantiate ``App`` and start the command loop::
app = App()
@@ -36,21 +36,20 @@ Resources
* cmd_
* `project bug tracker`_
* `cmd2 project page`_
-* `PyCon 2010 presentation <http://us.pycon.org/2010/conference/talks/#proposal_link_153>`_,
+* `PyCon 2010 presentation <https://github.com/python-cmd2/cmd2/blob/master/docs/pycon2010/pycon2010.rst>`_,
*Easy Command-Line Applications with cmd and cmd2*:
- :doc:`slides <pycon2010/pycon2010>`,
- `video <http://python.mirocommunity.com/video/1533/easy-command-line-applications>`_
-
+ :doc:`slides <pycon2010/pycon2010>`,
+ `video <http://pyvideo.org/pycon-us-2010/pycon-2010--easy-command-line-applications-with-c.html>`_
These docs will refer to ``App`` as your ``cmd2.Cmd``
subclass, and ``app`` as an instance of ``App``. Of
course, in your program, you may name them whatever
you want.
-
+
Contents:
.. toctree::
:maxdepth: 2
-
+
overview
example
freefeatures
@@ -61,7 +60,7 @@ Contents:
Compatibility
=============
-Tested and working with Python 2.5, 2.6, 2.7, 3.1; Jython 2.5
+Tested and working with Python 2.7, 3.5, 3.6
Indices and tables
==================
diff --git a/docs/pycon2010/fileutil.py b/docs/pycon2010/fileutil.py
deleted file mode 100644
index 5e754b5a..00000000
--- a/docs/pycon2010/fileutil.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import glob
-import os.path
-
-for fullfilename in glob.glob('/home/cat/proj/cmd2/*.py'):
- (dirpath, fname) = os.path.split(fullfilename)
- stats = os.stat(fullfilename)
- binds['path'] = dirpath
- binds['name'] = fname
- binds['bytes'] = stats.st_size
- cmd("""INSERT INTO cat.files (path, name, bytes)
- VALUES (%(path)s, %(name)s, %(bytes)s)""")
-quit()
diff --git a/docs/pycon2010/graph.py b/docs/pycon2010/graph.py
deleted file mode 100644
index 96ffde72..00000000
--- a/docs/pycon2010/graph.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from turtle import *
-pu()
-goto(-400,-400)
-
-def label(txt):
- write(txt, font=('Arial', 20, 'italic'))
-hideturtle()
-width(6)
-
-def line(len, _label):
- start = pos()
- pd()
- forward(len)
- pu()
- forward(30)
- pd()
- label(_label)
- pu()
- goto(start)
-
-def tech(x, y, _label):
- pu()
- goto(x, y)
- pd()
- write(_label, font=('Arial', 40, 'bold'))
- pu()
-
-line(600, "Easy to write")
-left(90)
-line(600, "Easy to use")
-
-tech(-360, 160, 'GUI')
-tech(-390, 100, 'AJAX')
-tech(-300, -10, 'webapp')
-tech(190, -380, 'CLU')
-tech(60, -320, 'TUI')
-tech(100, -210, 'cmd')
-tech(80, -80, 'cmd2')
-
-while True:
- pass \ No newline at end of file
diff --git a/docs/pycon2010/pirate.py b/docs/pycon2010/pirate.py
index 98db50ea..bd8b5170 100644
--- a/docs/pycon2010/pirate.py
+++ b/docs/pycon2010/pirate.py
@@ -1,7 +1,10 @@
+# coding=utf-8
from cmd import Cmd
+
class Pirate(Cmd):
pass
+
pirate = Pirate()
-pirate.cmdloop() \ No newline at end of file
+pirate.cmdloop()
diff --git a/docs/pycon2010/pirate2.py b/docs/pycon2010/pirate2.py
index e2c49609..343f94ff 100644
--- a/docs/pycon2010/pirate2.py
+++ b/docs/pycon2010/pirate2.py
@@ -1,18 +1,24 @@
+# coding=utf-8
from cmd import Cmd
+
+
# using ``do_`` methods
class Pirate(Cmd):
gold = 3
+
def do_loot(self, arg):
'Seize booty from a passing ship.'
self.gold += 1
print('Now we gots {0} doubloons'
.format(self.gold))
+
def do_drink(self, arg):
'Drown your sorrrows in rrrum.'
self.gold -= 1
print('Now we gots {0} doubloons'
.format(self.gold))
+
pirate = Pirate()
pirate.cmdloop()
diff --git a/docs/pycon2010/pirate3.py b/docs/pycon2010/pirate3.py
index 7977a8dc..ecc70f3f 100644
--- a/docs/pycon2010/pirate3.py
+++ b/docs/pycon2010/pirate3.py
@@ -1,21 +1,29 @@
+# coding=utf-8
from cmd import Cmd
+
+
# using hook
class Pirate(Cmd):
gold = 3
+
def do_loot(self, arg):
'Seize booty from a passing ship.'
self.gold += 1
+
def do_drink(self, arg):
- 'Drown your sorrrows in rrrum.'
+ 'Drown your sorrrows in rrrum.'
self.gold -= 1
+
def precmd(self, line):
self.initial_gold = self.gold
return line
- def postcmd(self, stop, line):
+
+ def postcmd(self, stop, line):
if self.gold != self.initial_gold:
print('Now we gots {0} doubloons'
.format(self.gold))
+
pirate = Pirate()
pirate.cmdloop()
diff --git a/docs/pycon2010/pirate4.py b/docs/pycon2010/pirate4.py
index 5de9c212..a4e4816d 100644
--- a/docs/pycon2010/pirate4.py
+++ b/docs/pycon2010/pirate4.py
@@ -1,27 +1,35 @@
+# coding=utf-8
from cmd import Cmd
+
+
# using arguments
class Pirate(Cmd):
gold = 3
+
def do_loot(self, arg):
'Seize booty from a passing ship.'
self.gold += 1
+
def do_drink(self, arg):
'''Drown your sorrrows in rrrum.
-
- drink [n] - drink [n] barrel[s] o' rum.'''
+
+ drink [n] - drink [n] barrel[s] o' rum.'''
try:
self.gold -= int(arg)
except:
if arg:
print('''What's "{0}"? I'll take rrrum.'''.format(arg))
- self.gold -= 1
+ self.gold -= 1
+
def precmd(self, line):
self.initial_gold = self.gold
return line
- def postcmd(self, stop, line):
+
+ def postcmd(self, stop, line):
if self.gold != self.initial_gold:
print('Now we gots {0} doubloons'.format(self.gold))
+
pirate = Pirate()
-pirate.cmdloop() \ No newline at end of file
+pirate.cmdloop()
diff --git a/docs/pycon2010/pirate5.py b/docs/pycon2010/pirate5.py
index 7add4635..2167c7f4 100644
--- a/docs/pycon2010/pirate5.py
+++ b/docs/pycon2010/pirate5.py
@@ -1,25 +1,32 @@
+# coding=utf-8
from cmd import Cmd
+
+
# quitting
class Pirate(Cmd):
gold = 3
+
def do_loot(self, arg):
'Seize booty from a passing ship.'
self.gold += 1
+
def do_drink(self, arg):
'''Drown your sorrrows in rrrum.
-
- drink [n] - drink [n] barrel[s] o' rum.'''
+
+ drink [n] - drink [n] barrel[s] o' rum.'''
try:
self.gold -= int(arg)
except:
if arg:
print('''What's "{0}"? I'll take rrrum.'''.format(arg))
- self.gold -= 1
+ self.gold -= 1
+
def precmd(self, line):
self.initial_gold = self.gold
return line
- def postcmd(self, stop, line):
+
+ def postcmd(self, stop, line):
if self.gold != self.initial_gold:
print('Now we gots {0} doubloons'
.format(self.gold))
@@ -27,9 +34,11 @@ class Pirate(Cmd):
print("Off to debtorrr's prison.")
stop = True
return stop
+
def do_quit(self, arg):
print("Quiterrr!")
- return True
+ return True
+
pirate = Pirate()
pirate.cmdloop()
diff --git a/docs/pycon2010/pirate6.py b/docs/pycon2010/pirate6.py
index 4a03fed4..a90c2b52 100644
--- a/docs/pycon2010/pirate6.py
+++ b/docs/pycon2010/pirate6.py
@@ -1,29 +1,37 @@
+# coding=utf-8
from cmd2 import Cmd
+
+
# prompts and defaults
class Pirate(Cmd):
gold = 3
prompt = 'arrr> '
+
def default(self, line):
print('What mean ye by "{0}"?'
.format(line))
+
def do_loot(self, arg):
'Seize booty from a passing ship.'
self.gold += 1
+
def do_drink(self, arg):
'''Drown your sorrrows in rrrum.
-
- drink [n] - drink [n] barrel[s] o' rum.'''
+
+ drink [n] - drink [n] barrel[s] o' rum.'''
try:
self.gold -= int(arg)
except:
if arg:
print('''What's "{0}"? I'll take rrrum.'''.format(arg))
- self.gold -= 1
+ self.gold -= 1
+
def precmd(self, line):
self.initial_gold = self.gold
return line
- def postcmd(self, stop, line):
+
+ def postcmd(self, stop, line):
if self.gold != self.initial_gold:
print('Now we gots {0} doubloons'
.format(self.gold))
@@ -31,9 +39,11 @@ class Pirate(Cmd):
print("Off to debtorrr's prison.")
stop = True
return stop
+
def do_quit(self, arg):
print("Quiterrr!")
- return True
+ return True
+
pirate = Pirate()
pirate.cmdloop()
diff --git a/docs/pycon2010/pirate7.py b/docs/pycon2010/pirate7.py
index 25ff5822..a333070c 100644
--- a/docs/pycon2010/pirate7.py
+++ b/docs/pycon2010/pirate7.py
@@ -1,28 +1,36 @@
+# coding=utf-8
from cmd2 import Cmd
+
+
# prompts and defaults
class Pirate(Cmd):
gold = 3
prompt = 'arrr> '
+
def default(self, line):
print('What mean ye by "{0}"?'.format(line))
+
def do_loot(self, arg):
'Seize booty from a passing ship.'
self.gold += 1
+
def do_drink(self, arg):
'''Drown your sorrrows in rrrum.
-
- drink [n] - drink [n] barrel[s] o' rum.'''
+
+ drink [n] - drink [n] barrel[s] o' rum.'''
try:
self.gold -= int(arg)
except:
if arg:
print('''What's "{0}"? I'll take rrrum.'''.format(arg))
- self.gold -= 1
+ self.gold -= 1
+
def precmd(self, line):
self.initial_gold = self.gold
return line
- def postcmd(self, stop, line):
+
+ def postcmd(self, stop, line):
if self.gold != self.initial_gold:
print('Now we gots {0} doubloons'
.format(self.gold))
@@ -30,17 +38,21 @@ class Pirate(Cmd):
print("Off to debtorrr's prison.")
stop = True
return stop
+
def do_quit(self, arg):
print("Quiterrr!")
- return True
+ return True
+
default_to_shell = True
multilineCommands = ['sing']
terminators = Cmd.terminators + ['...']
songcolor = 'blue'
settable = Cmd.settable + 'songcolor Color to ``sing`` in (red/blue/green/cyan/magenta, bold, underline)'
Cmd.shortcuts.update({'~': 'sing'})
+
def do_sing(self, arg):
print(self.colorize(arg, self.songcolor))
+
pirate = Pirate()
pirate.cmdloop()
diff --git a/docs/pycon2010/pirate8.py b/docs/pycon2010/pirate8.py
index 3e80b241..55d6df5c 100644
--- a/docs/pycon2010/pirate8.py
+++ b/docs/pycon2010/pirate8.py
@@ -1,28 +1,36 @@
+# coding=utf-8
from cmd2 import Cmd, options, make_option
+
+
# prompts and defaults
class Pirate(Cmd):
gold = 3
prompt = 'arrr> '
+
def default(self, line):
print('What mean ye by "{0}"?'.format(line))
+
def do_loot(self, arg):
'Seize booty from a passing ship.'
self.gold += 1
+
def do_drink(self, arg):
'''Drown your sorrrows in rrrum.
-
- drink [n] - drink [n] barrel[s] o' rum.'''
+
+ drink [n] - drink [n] barrel[s] o' rum.'''
try:
self.gold -= int(arg)
except:
if arg:
print('''What's "{0}"? I'll take rrrum.'''.format(arg))
- self.gold -= 1
+ self.gold -= 1
+
def precmd(self, line):
self.initial_gold = self.gold
return line
- def postcmd(self, stop, line):
+
+ def postcmd(self, stop, line):
if self.gold != self.initial_gold:
print('Now we gots {0} doubloons'
.format(self.gold))
@@ -30,21 +38,25 @@ class Pirate(Cmd):
print("Off to debtorrr's prison.")
stop = True
return stop
+
def do_quit(self, arg):
print("Quiterrr!")
- return True
+ return True
+
default_to_shell = True
multilineCommands = ['sing']
terminators = Cmd.terminators + ['...']
songcolor = 'blue'
settable = Cmd.settable + 'songcolor Color to ``sing`` in (red/blue/green/cyan/magenta, bold, underline)'
Cmd.shortcuts.update({'~': 'sing'})
+
def do_sing(self, arg):
print(self.colorize(arg, self.songcolor))
+
@options([make_option('--ho', type='int', default=2,
help="How often to chant 'ho'"),
make_option('-c', '--commas',
- action="store_true",
+ action="store_true",
help="Intersperse commas")])
def do_yo(self, arg, opts):
chant = ['yo'] + ['ho'] * opts.ho
@@ -53,5 +65,6 @@ class Pirate(Cmd):
print('{0} and a bottle of {1}'
.format(chant, arg))
+
pirate = Pirate()
pirate.cmdloop()
diff --git a/docs/pycon2010/pycon2010.rst b/docs/pycon2010/pycon2010.rst
index 0b3b7a46..6c3af676 100644
--- a/docs/pycon2010/pycon2010.rst
+++ b/docs/pycon2010/pycon2010.rst
@@ -11,7 +11,7 @@ Web 2.0
.. image:: web-2-0-logos.gif
:height: 350px
-
+
But first...
============
@@ -20,10 +20,10 @@ But first...
.. image:: akkad.png
:height: 250px
-
+
Sargon the Great
Founder of Akkadian Empire
-
+
.. twenty-third century BC
In between
@@ -31,16 +31,16 @@ In between
.. image:: apple.jpg
:height: 250px
-
+
Command-Line Interface
- Unlike the Akkadian Empire,
+ Unlike the Akkadian Empire,
the CLI will never die.
Defining CLI
============
Also known as
-
+
- "Line-oriented command interpreter"
- "Command-line interface"
- "Shell"
@@ -85,24 +85,24 @@ Examples
.. image:: urwid.png
:height: 250px
-
+
Decide your priorities
======================
.. image:: strategy.png
:height: 350px
-
+
A ``cmd`` app: pirate.py
========================
::
from cmd import Cmd
-
+
class Pirate(Cmd):
pass
-
+
pirate = Pirate()
pirate.cmdloop()
@@ -114,9 +114,9 @@ Fundamental prrrinciple
=======================
.. class:: huge
-
- ``(Cmd) foo a b c``
-
+
+ ``(Cmd) foo a b c``
+
becomes
``self.do_foo('a b c')``
@@ -139,7 +139,7 @@ Fundamental prrrinciple
print('Now we gots {0} doubloons'
.format(self.gold))
-.. do_methods; more help
+.. do_methods; more help
Hooks
=====
@@ -163,16 +163,16 @@ Hooks: pirate3.py
'Seize booty from a passing ship.'
self.gold += 1
def do_drink(self, arg):
- 'Drown your sorrrows in rrrum.'
+ 'Drown your sorrrows in rrrum.'
self.gold -= 1
def precmd(self, line):
self.initial_gold = self.gold
return line
- def postcmd(self, stop, line):
+ def postcmd(self, stop, line):
if self.gold != self.initial_gold:
print('Now we gots {0} doubloons'
.format(self.gold))
-
+
Arguments: pirate4.py
=====================
@@ -180,22 +180,22 @@ Arguments: pirate4.py
def do_drink(self, arg):
'''Drown your sorrrows in rrrum.
-
- drink [n] - drink [n] barrel[s] o' rum.'''
+
+ drink [n] - drink [n] barrel[s] o' rum.'''
try:
self.gold -= int(arg)
except:
if arg:
print('''What's "{0}"? I'll take rrrum.'''
.format(arg))
- self.gold -= 1
-
+ self.gold -= 1
+
quitting: pirate5.py
====================
::
- def postcmd(self, stop, line):
+ def postcmd(self, stop, line):
if self.gold != self.initial_gold:
print('Now we gots {0} doubloons'
.format(self.gold))
@@ -205,7 +205,7 @@ quitting: pirate5.py
return stop
def do_quit(self, arg):
print("Quiterrr!")
- return True
+ return True
prompts, defaults: pirate6.py
=============================
@@ -227,7 +227,7 @@ Other CLI packages
* CMdO
* pycopia
* cmdlin
- * cmd2
+ * cmd2
Demo
====
@@ -258,7 +258,7 @@ Script files
Commands at invocation
-Output redirection
+Output redirection
Python
@@ -273,9 +273,9 @@ But wait, there's more
* Timing
* Echo
* Debug
-
+
Minor changes: pirate7.py
-=========================
+=========================
::
@@ -287,18 +287,18 @@ Minor changes: pirate7.py
Cmd.shortcuts.update({'~': 'sing'})
def do_sing(self, arg):
print(self.colorize(arg, self.songcolor))
-
+
Now how much would you pay?
===========================
options / flags
-Quiet (suppress feedback)
+Quiet (suppress feedback)
BASH-style ``select``
Parsing: terminators, suffixes
-
+
Options: pirate8.py
===================
@@ -307,13 +307,13 @@ Options: pirate8.py
@options([make_option('--ho', type='int', default=2,
help="How often to chant 'ho'"),
make_option('-c', '--commas',
- action="store_true",
+ action="store_true",
help="Intersperse commas")])
def do_yo(self, arg, opts):
chant = ['yo'] + ['ho'] * opts.ho
separator = ', ' if opts.commas else ' '
chant = separator.join(chant)
- print('{0} and a bottle of {1}'
+ print('{0} and a bottle of {1}'
.format(chant, arg))
Serious example: sqlpython
diff --git a/docs/pycon2010/schematic.py b/docs/pycon2010/schematic.py
deleted file mode 100644
index 80774859..00000000
--- a/docs/pycon2010/schematic.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from turtle import *
-hideturtle()
-width(6)
-pensize = 10
-pu()
-goto(0,-400)
-
-def rectangle(x, y, _label):
- pu()
- seth(0)
- backward(x / 2)
- fontsize = 40
- pd()
- for i in range(2):
- forward(x)
- left(90)
- forward(y)
- left(90)
- pu()
- forward(x / 2)
- left(90)
- forward(y / 2 - fontsize)
- pd()
- write(_label, align='center', font=('Arial', fontsize, 'bold'))
-
-rectangle(800, 80, 'cmd')
-pu()
-forward(80)
-rectangle(200, 400, 'cmd2')
-
-while True:
- pass
diff --git a/docs/unfreefeatures.rst b/docs/unfreefeatures.rst
index 1cd0081c..c9149409 100644
--- a/docs/unfreefeatures.rst
+++ b/docs/unfreefeatures.rst
@@ -6,13 +6,13 @@ Multiline commands
==================
Command input may span multiple lines for the
-commands whose names are listed in the
+commands whose names are listed in the
parameter ``app.multilineCommands``. These
commands will be executed only
after the user has entered a *terminator*.
By default, the command terminators is
``;``; replacing or appending to the list
-``app.terminators`` allows different
+``app.terminators`` allows different
terminators. A blank line
is *always* considered a command terminator
(cannot be overridden).
@@ -21,17 +21,17 @@ Parsed statements
=================
``cmd2`` passes ``arg`` to a ``do_`` method (or
-``default`) as a ParsedString, a subclass of
+``default`) as a ParsedString, a subclass of
string that includes an attribute ``parsed``.
``parsed`` is a ``pyparsing.ParseResults``
-object produced by applying a pyparsing_
+object produced by applying a pyparsing_
grammar applied to ``arg``. It may include:
command
Name of the command called
raw
- Full input exactly as typed.
+ Full input exactly as typed.
terminator
Character used to end a multiline command
@@ -68,9 +68,9 @@ process are painfully complex and should not be
considered stable; future ``cmd2`` releases may
change it somewhat (hopefully reducing complexity).
-(Getting ``arg`` as a ``ParsedString`` is
+(Getting ``arg`` as a ``ParsedString`` is
technically "free", in that it requires no application
-changes from the cmd_ standard, but there will
+changes from the cmd_ standard, but there will
be no result unless you change your application
to *use* ``arg.parsed``.)
@@ -83,7 +83,7 @@ to *use* ``arg.parsed``.)
Environment parameters
======================
-Your application can define user-settable parameters
+Your application can define user-settable parameters
which your code can reference. Create them as class attributes
with their default values, and add them (with optional
documentation) to ``settable``.
@@ -111,7 +111,7 @@ documentation) to ``settable``.
(Cmd) set --long
degrees_c: 22 # temperature in Celsius
- sunny: False #
+ sunny: False #
(Cmd) sunbathe
Too dim.
(Cmd) set sunny yes
@@ -135,7 +135,7 @@ a ``do_`` methods accept Unix-style *flags*. It uses optparse_
to parse the flags, and they work the same way as for
that module.
-Flags are defined with the ``options`` decorator,
+Flags are defined with the ``options`` decorator,
which is passed a list of optparse_-style options,
each created with ``make_option``. The method
should accept a second argument, ``opts``, in
@@ -180,19 +180,19 @@ the option's online help.
arg_desc='(from city) (to city)')
def do_travel(self, arg, opts=None):
'Gets you from (from city) to (to city).'
-
+
::
(Cmd) help travel
Gets you from (from city) to (to city).
Usage: travel [options] (from-city) (to-city)
-
+
Options:
-h, --help show this help message and exit
-t, --train by train
-
-.. _optparse:
+
+.. _optparse:
.. _outputters:
@@ -200,17 +200,17 @@ poutput, pfeedback, perror
==========================
Standard ``cmd`` applications produce their output with ``self.stdout.write('output')`` (or with ``print``,
-but ``print`` decreases output flexibility). ``cmd2`` applications can use
+but ``print`` decreases output flexibility). ``cmd2`` applications can use
``self.poutput('output')``, ``self.pfeedback('message')``, and ``self.perror('errmsg')``
instead. These methods have these advantages:
- More concise
- ``.pfeedback()`` destination is controlled by :ref:`quiet` parameter.
-
+
color
=====
-Text output can be colored by wrapping it in the ``colorize`` method.
+Text output can be colored by wrapping it in the ``colorize`` method.
.. automethod:: cmd2.Cmd.colorize
@@ -221,7 +221,7 @@ quiet
Controls whether ``self.pfeedback('message')`` output is suppressed;
useful for non-essential feedback that the user may not always want
-to read. ``quiet`` is only relevant if
+to read. ``quiet`` is only relevant if
``app.pfeedback`` is sometimes used.
``select``
@@ -234,13 +234,13 @@ Presents numbered options to user, as bash ``select``.
.. automethod:: cmd2.Cmd.select
::
-
+
def do_eat(self, arg):
sauce = self.select('sweet salty', 'Sauce? ')
result = '{food} with {sauce} sauce, yum!'
result = result.format(food=arg, sauce=sauce)
self.stdout.write(result + '\n')
-
+
::
(Cmd) eat wheaties
@@ -248,4 +248,4 @@ Presents numbered options to user, as bash ``select``.
2. salty
Sauce? 2
wheaties with salty sauce, yum!
- \ No newline at end of file
+
diff --git a/example/example.py b/example/example.py
index 9d17bbb7..863084cd 100755
--- a/example/example.py
+++ b/example/example.py
@@ -1,4 +1,4 @@
-
+# coding=utf-8
"""A sample application for cmd2.
"""
@@ -15,7 +15,7 @@ class CmdLineApp(Cmd):
@options([make_option('-p', '--piglatin', action="store_true", help="atinLay"),
make_option('-s', '--shout', action="store_true", help="N00B EMULATION MODE"),
make_option('-r', '--repeat', type="int", help="output [n] times")
- ], arg_desc = '(text to say)')
+ ], arg_desc='(text to say)')
def do_speak(self, arg, opts=None):
"""Repeats what you tell me to."""
arg = ''.join(arg)
@@ -30,8 +30,9 @@ class CmdLineApp(Cmd):
# self.stdout.write is better than "print", because Cmd can be
# initialized with a non-standard output destination
- do_say = do_speak # now "say" is a synonym for "speak"
- do_orate = do_speak # another synonym, but this one takes multi-line input
+ do_say = do_speak # now "say" is a synonym for "speak"
+ do_orate = do_speak # another synonym, but this one takes multi-line input
+
c = CmdLineApp()
c.cmdloop()
diff --git a/ignoreBug.py b/ignoreBug.py
deleted file mode 100644
index 37819336..00000000
--- a/ignoreBug.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from pyparsing import *
-
-teststr = 'please /* ignoreme: | oops */ findme: | kthx'
-parser = Word(printables)('leadWord') + SkipTo('|')('statement')
-print parser.parseString(teststr).statement
-parser.ignore(cStyleComment)
-print parser.parseString(teststr).statement
-parser = Combine(parser)
-print parser.parseString(teststr).statement
-parser.ignore(cStyleComment)
-print parser.parseString(teststr).statement
diff --git a/setup.py b/setup.py
index 54e156e9..01d939c2 100755
--- a/setup.py
+++ b/setup.py
@@ -1,24 +1,27 @@
#!/usr/bin/python
-from setuptools import setup, find_packages
-import sys
+# coding=utf-8
+from setuptools import setup
-install_requires = ['pyparsing >= 2.0.1']
+install_requires = ['pyparsing >= 2.0.1', 'six']
+
+tests_require = ['mock', 'pytest']
setup(
name="cmd2",
- version="0.6.9",
+ version="0.7.0",
py_modules=["cmd2"],
- use_2to3=True,
+ use_2to3=False,
# metadata for upload to PyPI
- author = 'Catherine Devlin',
- author_email = 'catherine.devlin@gmail.com',
- description = "Extra features for standard library's cmd module",
- license = 'MIT',
- keywords = 'command prompt console cmd',
- url = 'http://packages.python.org/cmd2/',
- install_requires = install_requires,
- long_description = """Enhancements for standard library's cmd module.
+ author='Catherine Devlin',
+ author_email='catherine.devlin@gmail.com',
+ description="Extra features for standard library's cmd module",
+ license='MIT',
+ keywords='command prompt console cmd',
+ url='http://packages.python.org/cmd2/',
+ install_requires=install_requires,
+ tests_require=tests_require,
+ long_description="""Enhancements for standard library's cmd module.
Drop-in replacement adds several features for command-prompt tools:
diff --git a/tests/conftest.py b/tests/conftest.py
index 55700914..e1204086 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,3 +1,4 @@
+# coding=utf-8
#
# Cmd2 unit/functional testing
#
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 8435ac1a..a7795bb6 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -1,3 +1,4 @@
+# coding=utf-8
#
# Cmd2 unit/functional testing
#
@@ -6,18 +7,14 @@
import mock
import pytest
-
from conftest import run_cmd, _normalize
-import cmd2
+from six import StringIO
-try:
- from StringIO import StringIO
-except ImportError:
- from io import StringIO
+import cmd2
def test_ver():
- assert cmd2.__version__ == '0.6.9a'
+ assert cmd2.__version__ == '0.7.0'
def test_base_help(base_app):
diff --git a/tests/test_transcript.py b/tests/test_transcript.py
index 06cf50b6..dfb37da7 100644
--- a/tests/test_transcript.py
+++ b/tests/test_transcript.py
@@ -1,3 +1,4 @@
+# coding=utf-8
#
# Cmd2 functional testing based on transcript
#
@@ -37,7 +38,7 @@ class CmdLineApp(Cmd):
# self.stdout.write is better than "print", because Cmd can be
# initialized with a non-standard output destination
- do_say = do_speak # now "say" is a synonym for "speak"
+ do_say = do_speak # now "say" is a synonym for "speak"
do_orate = do_speak # another synonym, but this one takes multi-line input
@@ -45,7 +46,7 @@ class CmdLineApp(Cmd):
def _cmdline_app():
c = CmdLineApp()
c.stdout = StdOut()
- #c.shortcuts.update({'&': 'speak', 'h': 'hello'})
+ # c.shortcuts.update({'&': 'speak', 'h': 'hello'})
c.settable.append('maxrepeats Max number of `--repeat`s allowed')
return c
diff --git a/tox.ini b/tox.ini
index 91ce4283..34ec348c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,6 +6,7 @@ deps =
mock
pyparsing
pytest
+ six
commands=
py.test -v --basetemp={envtmpdir} {posargs}
{envpython} cmd2.py