python-cmd2 / cmd2 Goto Github PK
View Code? Open in Web Editor NEWcmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python
Home Page: https://cmd2.readthedocs.io/en/stable/
License: MIT License
cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python
Home Page: https://cmd2.readthedocs.io/en/stable/
License: MIT License
Originally reported by: Tyler Abair (Bitbucket: tabair, GitHub: tabair)
Cmd2 has a cool feature where it will process all CLI args passed to a script as commands, but there is no option to turn this feature off.
A quick work-around that I'm using at the moment is to save sys.argv to a tmp variable, and then set sys.argv = [tmp[0]] just before calling Cmd.cmdloop() and restore it after, but it'd be much nicer if I could just set an instance variable on my Cmd object to turn this feature off instead.
Using cmd2 as drop in replacement breaks using https://docs.python.org/3/library/cmd.html#cmd.Cmd.emptyline, as cmd2 completely ignores empty lines (onecmd_plus_hooks catches EmptyStatements and just returns 0). Either this is a bug or needs a note in the docs.
Originally reported by: Anonymous
When an option that is not in the list is inserted, say:
the operation is closed because the list is out of index, it would be nicer to do nothing until a correct option is entered.
Originally reported by: Bhairav Shah (Bitbucket: bhairav, GitHub: bhairav)
In the cmdloop method, the following line is present:
#!python
(callopts, callargs) = parser.parse_args()
Digging deeper into optparse, parse_args() calls the below line:
#!python
rargs = self._get_args(args)
Which in turn has this:
#!python
def _get_args(self, args):
if args is None:
return sys.argv[1:]
else:
return args[:] # don't modify caller's list
While it may make sense for _get_args in optparse to use the sys.argv arguments, would it make sense for cmd2 to do the same? Thinking that cmd2 doesn't need to allow reading from normal shell commandline because it is supposed to be an embedded interpreter within the program.
Wondering if cmdloop in cmd2 can be fixed in one of two ways:
#!python
(callopts, callargs) = parser.parse_args(args=[])
OR
#!python
cmdloop(args=[])
I tested with option 1, and was able to make my program work by not having it complain with the following kind of error:
error: no such option: -f
Thanks.
The same error happens with either Python 3.6.0 or 2.7.13:
=================================== FAILURES ===================================
____________________________ test_input_redirection ____________________________
base_app = <cmd2.Cmd object at 0x7fdd39ddab38>
request = <FixtureRequest for <Function 'test_input_redirection'>>
@pytest.mark.skipif(sys.platform.startswith('linux') and getpass.getuser() == 'travis',
reason="Unit test passes on Ubuntu 16.04 and Debian 8.7, but fails on TravisCI Linux containers")
def test_input_redirection(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
filename = os.path.join(test_dir, 'redirect.txt')
# NOTE: File 'redirect.txt" contains 1 word "history"
# Verify that redirecting input from a file works
out = run_cmd(base_app, 'help < {}'.format(filename))
expected = normalize(HELP_HISTORY)
> assert out == expected
E assert [] == ['history [arg]: lists past ...lar expression search', ...]
E Right contains more items, first extra item: 'history [arg]: lists past commands issued'
E Use -v to get the full diff
tests/test_cmd2.py:361: AssertionError
===================== 1 failed, 77 passed in 37.79 seconds =====================
Originally reported by: Anonymous
I find it efficient to list a summary of help for all documented commands. Thought you might find this worth including. Thanks for a nice tool! --Ken Bannister
#!python
self.doc_header = 'Commands (type "help all" or "help <topic>"):'
def help_all(self):
"""Lists first line of help for all documented commands"""
names = self.get_names()
names.sort()
for name in names:
if name[:3] == 'do_':
try:
doc = getattr(self, name).__doc__
if doc:
maxlen = 65
ellipsis = ''
if len(doc) > maxlen:
ellipsis = '...'
self.stdout.write('{0} - {1}{2}\n'.format(
name[3:80-maxlen], doc[:maxlen], ellipsis))
except AttributeError:
pass
Consider checking if ipython is installed and if so, using an embedded IPython shell instead of a basic Python one for the built-in "py" command.
(Cmd) help
Documented commands (type help <topic>):
========================================
_relative_load edit help list orate py run say shell show
cmdenvironment eof history load pause quit save set shortcuts speak
(Cmd) ^[[A
when I press arrow up, I expect to show the previous command (help), but just display ^[[A
. I guess it displayed what it read from keyboard event without processing.
Originally reported by: Michael Haberler (Bitbucket: mhaberler, GitHub: mhaberler)
I wish there were a way to register cmd2 input with event loops like zmq.loop, asyncio, poll, gobject/gevent etc without occupying the main loop
the assumption cmd2 owns the inner loop of a program is very restrictive - even Gnu readline can be used from an event loop (C, not Python though)
using threading is an overkill for the purpose, and brings its own set of problems
Currently, when piping the output of a command to a shell command with "|", if the command piped to returns a non-zero value, the cmd2 throws an exception.
We shouldn't throw an exception in this case.
The output redirection feature where command output can be piped (|) as input to operating-system commands, as in "help | wc" is broken for Python 3.x but still works for Python 2.7. This feature's expected behavior is documented here:
https://cmd2.readthedocs.io/en/latest/freefeatures.html#output-redirection
Not only is it broken, but it causes an unhandled exception. It appears to be a str (unicode) vs bytes issue.
When "debug" is set to True, this is the exception I got:
(Cmd) help | wc
Traceback (most recent call last):
File "/Users/toddleonhardt/src/cmd2/cmd2.py", line 872, in onecmd_plus_hooks
stop = self.onecmd(statement)
File "/Users/toddleonhardt/src/cmd2/cmd2.py", line 955, in onecmd
stop = func(statement)
File "/Users/toddleonhardt/src/cmd2/cmd2.py", line 543, in do_help
cmd.Cmd.do_help(self, arg)
File "/Users/toddleonhardt/anaconda/lib/python3.5/cmd.py", line 333, in do_help
self.stdout.write("%s\n"%str(self.doc_leader))
TypeError: a bytes-like object is required, not 'str'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/toddleonhardt/src/cmd2/cmd2.py", line 877, in onecmd_plus_hooks
self.restore_output(statement)
File "/Users/toddleonhardt/src/cmd2/cmd2.py", line 928, in restore_output
self.kept_state.stdout.write(result or '')
TypeError: write() argument must be str, not bytes
Transcript testing creates a 2nd instance of the class derived from cmd2.Cmd. This appears to be unecessary and done primarily for redirecting stdout.
This should probably be handled in a better and more efficient way which doesn't require creating a second instance.
Pypi links to https://pythonhosted.org/cmd2/, which links to http://trac-hg.assembla.com/python-cmd2/report/1. The docs in this repository link to bitbucket. Assuming github is now the canonical place (at least it has the latest updates), it should be mentioned in the docs, and the pypi page should be updated.
If the bitbucket is still the canonical place, I think that should be mentioned in the main readme.
Currently even if CLI args are disabled, cmd2 still tries to use OptParse to look for the "-t" flag which enables transcript testing. This interferes with using something like ArgParse to handle command line arguments for the overall application.
The when the allow_cli_args attribute is False, it should disable ALL processing of command-line arguments, including the "-t" one passed for transcript testing.
Originally reported by: Kent Duncan (Bitbucket: slamduncan, GitHub: slamduncan)
When adding to the shortcut list, if one shortcut is the start of another shortcut than it will override it and pass the remainder of the longer shortcut in arg.
Example:
Cmd.shortcuts.update({
'p': 'first_sc',
'pcon' : 'second_sc',
})
When it's running if you type in pcon it will call do_first_sc with the arg 'con'.
It would be nice for commands which interact with the local file system such as edit, load, and shell to have built-in tab completion based on file system paths.
Currently arguments for commands using the options decorator are split using shlex.split(), however arguments for commands which do not use the options decorator are not split in this fashion.
Ideally, arguments for all commands would go through a more similar processing chain.
The real question, is where is the best place in the code to do that?
It would be nice to keep them in sync :)
Python Prompt Toolkit provides some really powerful and cool features:
https://github.com/jonathanslenders/python-prompt-toolkit
It would be nice if a cmd2 user could optionally replace the default prompt with a prompt from python-prompt-toolkit.
This would at a minimum require replacing the simple string prompt with a function which prints a prompt and returns the text the user entered. It would be somewhat of a departure from cmd. But it would open up the door for easily incorporating things like syntax-highlighting, input validation, and a bottom toolbar.
I'm not sure it would really be possible to make cmd2 and python-prompt-toolkit play nicely together, but if it is, it would be a great thing.
Mac OS X ships with libedit instead of libreadline. libedit doesn't work well with Python's readline module. So a user either needs to use homebrew to install GNU readline or they need to use pip to install the gnureadline module which is a version of Python's readline module which is statically linked to the GNU readline library.
So to help with Mac OS X compatibility for users that have gnureadline installed instead of libreadline, it would be good to have an import setup like so:
try:
import gnureadline as readline
except ImportError:
import readline
Originally reported by: Anonymous
Using python3.3 with a cmd2.py modified by 2to3.
There are two instances were cmd2 checks whether some object is of type file. There is apparently no builtin type file.
I fixed the issue this way:
try:
import io
file = io.TextIOWrapper
except ImportError:
pass # Python2
But only tried it on python3 version.
Also your documentation claims that cmd has shortcuts @ and !: there doesn't appear to be any @, but there is a ? shortcut.
Originally reported by: Tomaz Muraus (Bitbucket: kami, GitHub: kami)
Latest version (0.6.5) introduced a regression which breaks cmd2 under Python 2.6.
#!bash
kami ~/cmd2 $ python2.6 setup.py
Traceback (most recent call last):
File "setup.py", line 11, in <module>
if sys.version_info.major < 3:
AttributeError: 'tuple' object has no attribute 'major'
In Python 2.6, sys.version_info is a tuple and not an object like thing so doing sys.version_info.major won't work.
I'm including a patch which fixes it and works under all supported versions (2.6, 2.7, 3.x).
Currently all of the unit tests which verify how the command line is parsed are tightly coupled with the details of the underlying pyparsing implementation.
These unit tests should be refactored in a way that they relate to how cmd2 parses the command line, but are not directly dependent upon this parsing being done by pyparsing in particular.
Once they are more generic, they could be used to help safely refactor cmd2 to use a different library for doing this parsing.
Currently entering an empty line completely bypasses all precmd and postcmd hooks, so there is no way to do something like update the prompt based on asynchronous notifications from a background thread when the user just presses enter.
postparsing_postcmd() should always be called, even for an empty line.
optparse has been deprecated since Python 3.2 and all development on it has ceased:
https://docs.python.org/3.6/library/optparse.html
We should investigate replacing the usage of optparse with argparse or a similar library such as docopt or click.
Since the APIs aren't identical, it won't be a trivial drop-in replacement.
Python's built-in "shlex" module for simple lexical analysis is probably good enough to handle our command-line parsing in cmd2 and it is implemented in C, so its performance would probably be a lot better than pyparsing, which is implemented in pure Python.
ANTLR and PLY also look like potentially superior alternatives to pyparsing, which is slow, dated, and at least in our case requires an overly-complicated grammar.
But shlex should be preferred since it is built-in to Python and not 3rd-party.
shlex:
https://docs.python.org/3.6/library/shlex.html
pyparsing:
http://pyparsing.wikispaces.com
ANTLR:
https://github.com/antlr/antlr4
PLY:
https://github.com/dabeaz/ply
This is a long-term "down the road" idea.
Originally reported by: paramite (Bitbucket: paramite, GitHub: paramite)
During editor command check cmd2 generates output which is not required (and in some cases disturbing). Patch attached. Please check bug [1] in case you wonder why this is needed.
[1] https://bugzilla.redhat.com/show_bug.cgi?id=889774
In general, all cmd2 commands are scriptable within a Python script via "cmd('command args')". However, since when running within a Python script we are actually within the do_py command, quit doesn't actually quit the cmd2 application.
The current behavior is non-intuitive. Issuing a cmd('quit') should really quit.
Add code like the following to do_quit() and do_py():
def do_quit(self):
self._should_quit = True
def do_py(self):
...
return self._should_quit
Originally reported by: Anonymous
as options may be added at end of line (optparse) ex : speak -p aaaaa bbbbb -r2 ( instead of speak -p -r2....., it happens in def : remaining_args(..) that the `\s_$ entails an attribute error because no matching
pattern = '\s+'.join(re.escape(a) for a in newArgList) + '\s_$'
Bypassed by removing '\s*$ in above.
regards
Many REPL frameworks have support for multi-level commands, where the command has sub-commands.
While this can be done with cmd2, there isn't any built-in support to make it easy.
Consider adding some built-in support to make it easy to have nested commands or subcommands.
Originally reported by: Stuart Axon (Bitbucket: stuaxo, GitHub: stuaxo)
I have a homebuild command parser I'm using on my socket server, I'd like to use something like cmd2 instead.
Ideally, all the command history etc would be available - but that seems a bit ambitious ..
Is there a method I can pass a line of text + then cmd2 call the correct functions + return the feedback ?
That way I could use cmd2 for my ordinary shell and my socket shell.
The current built-in scripting capability is very nice for automating the execution of a series of commands.
However, there isn't any built-in way to run a command IF certain criteria are met (based on say whether or not a previous command "succeeded".
This may be beyond the scope of what cmd2 is intended to provide. But if there was an easy way to add this functionality, it would be useful.
The ability to enter a 2nd interactive Python console within an existing one via "cmd('py')" isn't helpful and could be harmful.
Recommend detecting if running in an interactive console, bypassing running a new one, and printing an error with something like:
def do_py(self):
if self._in_py:
self.perror("Recursively entering interactive Python consoles is not allowed.", traceback_war=False)
return
self._in_py = True
....
self._in_py = False
return self._should_quit
Originally reported by: Alex Gaynor (Bitbucket: alex_gaynor, GitHub: Unknown)
A bunch of tools are starting to use these to introspect which python versions are supported by a package. It'd be awesome for cmd2's setup.py to advertise it's supported versions, e.g.: https://github.com/django/django/blob/master/setup.py#L58
Adding more, better, and more complicated / in depth examples which demonstrate the wealth of features present in cmd2 and the various ways cmd2 can be used in an application would likely be most helpful for new users.
Originally reported by: Anonymous
#!python
import sys
from cmd2 import Cmd, make_option, options
class DemoApp(Cmd):
"""Simple command processor example."""
@options([make_option('-n', '--name', action="store", help="your name"),
])
def do_hello(self, command, opts):
if opts.name:
sys.stdout.write('Hello %s\n' % opts.name)
else:
sys.stdout.write('Hello Nobody\n')
if __name__ == '__main__':
DemoApp().cmdloop()
if you pass:
hello 'Catherine Devlin'
it is printed:
Hello 'Catherine
the same with double quotes
If at any point the cmd2 prompt gets changed to involve anything involving color via ANSI color escape codes, then the behavior of readline when the up and down arrows are pressed gets really messed up.
This is due to a bug in how GNU Readline calculates the prompt length when the Python3 input() or Python2 raw_input() methods are called. This article has more info:
https://bugs.python.org/issue17337
Ideally, we can make cmd2 protect against this case, even though it is fundamentally a bug in GNU Readline.
When running with transcript testing, preloop() isn't getting called before the testing and postloop() isn't being called afterwards.
These should be called to handle required initialization and cleanup, respectively.
The documentation states the following:
Regular expressions can be embedded in the transcript inside paired / slashes. These regular expressions should not include any whitespace expressions.
But the example we have for using transcript testing doesn't include any regular expressions.
Add an example for how to this to make it easier for users to take advantage of this feature.
Also, we don't appear to have any unit tests which use a transcript with a regular expression embedded within it. So we should add one.
If the feature doesn't work, we should either fix it or change the documentation.
There is currently a bug in the py command where the "cmd" function present in the embedded Python console isn't defined until after an interactive python session is entered once.
The fix is very easy, just move the relevant code a little bit earlier in the do_py() function.
Originally reported by: Atsushi Odagiri (Bitbucket: aodag, GitHub: aodag)
I download eggs and sdist from https://pypi.python.org/pypi/cmd2 .
I think the egg for 2.7 includes sources applied 2to3.
#!
Traceback (most recent call last):
File "bin/shirly", line 40, in <module>
import shirly.script
File "/home/aodag/works/shirly/src/shirly/script.py", line 2, in <module>
from cliff.app import App
File "/home/aodag/.buildout/eggs/cliff-1.4-py2.7.egg/cliff/app.py", line 11, in <module>
from .interactive import InteractiveApp
File "/home/aodag/.buildout/eggs/cliff-1.4-py2.7.egg/cliff/interactive.py", line 9, in <module>
import cmd2
File "/home/aodag/.buildout/eggs/cmd2-0.6.6-py2.7.egg/cmd2.py", line 36, in <module>
import urllib.request, urllib.parse, urllib.error
ImportError: No module named request
All of the existing doctest tests work for older versions of pyparsing. But not with pyparsing 2.1.10 because how the ParseResults.dump() function treats strings changed (it now puts them in quotes).
I don't see an easy way to get the doctests to work for all versions of pyparsing.
The best option is to convert the existing doctest tests to pytest unit tests.
Originally reported by: Michał Górny (Bitbucket: mgorny, GitHub: mgorny)
The code reads:
try:
from setuptools import setup, find_packages
except ImportError:
from distutils.core import setup
def find_packages():
return ['sqlpython']
^^^^^^^^^^^
which means that anyone trying to install it without setuptools is going to get wrong package name. Aside of that, setuptools-specific keys will trigger warnings in Python 2, lack of 2to3 conversion in Python 3 and fatal errors in Python 3.3.
I suggest just importing setuptools
and forgetting about distutils compatibility.
For the commands which support local file system path completion, in the case where there is a single result and that result is a directory, it would be convenient if the completion function automatically added in the trailing path separator.
Originally reported by: Tyler Abair (Bitbucket: tabair, GitHub: tabair)
Cmd2's redirection with '>' and piping with '|' is a terrific feature, but I really want to disable redirection and piping sometimes. Unfortunately, there is no option I can set to make the '>' and '|' characters behave as plain characters.
I can't find a work-around to this without editing the source code. I did find that setting Cmd.redirrector to a non-printable character like '0x03' will stop redirection, but I can't find any way to touch piping. Furthermore, this is a TERRIBLE workaround to stop redirection, because a malicious user is capable of sending even non-printable characters through.
Originally reported by: Bhairav Shah (Bitbucket: bhairav, GitHub: bhairav)
Have an issue with getting dictionary values. Python 2.7. Sample code:
#!python
a = {}
a[1] = "blah"
class MyCmd(cmd.Cmd):
def do_printdict(self, line):
global a
print "a is %s" % a
val = a[1]
print "val for 1 is: %s" % val
val = a[2]
print "val for 2 is: %s" % val
my_cmd = MyCmd()
my_cmd.cmdloop()
If I import the original cmd, I get the following output:
#!python
(Cmd) printdict
a is {1: 'blah'}
val for 1 is: blah
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/cmd.py", line 142, in cmdloop
stop = self.onecmd(line)
File "/usr/lib/python2.7/cmd.py", line 221, in onecmd
return func(arg)
File "<stdin>", line 7, in do_printdict
KeyError: 2
If I import cmd2, I get the following output:
#!python
(Cmd) printdict
a is {1: 'blah'}
val for 1 is: blah
2
The original cmd seems to be doing it right, giving the KeyError. Not sure what is happening with cmd2.
Thanks.
Originally reported by: Barry Warsaw (Bitbucket: warsaw, GitHub: warsaw)
Actually, subprocess.mswindows is an internal-only API so ideally it shouldn't be used at all. However, in Python 3.5 the attribute has been renamed to subprocess._mswindows, thus breaking cmd2.
You can just use (sys.platform == 'win32')
which is the definition of subprocess.mswindows anyway.
AttributeError: module 'subprocess' has no attribute 'mswindows'
This has been changed to _mswindows. Otherwise everything seems to work in 3.5. Still testing though
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.