diff options
-rw-r--r-- | CHANGES.rst | 8 | ||||
-rwxr-xr-x | README.rst | 3 | ||||
-rwxr-xr-x | cmd2.py | 22 | ||||
-rw-r--r-- | docs/freefeatures.rst | 26 | ||||
-rwxr-xr-x | examples/argparse_example.py | 88 |
5 files changed, 135 insertions, 12 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index fdfad54a..827d492e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,10 +1,14 @@ News ==== -0.7.1a +0.7.1 ------ -Placeholder +*Release date: TBD + +* Fixed a bug where ``-`` wasn't being treated as a legal character +* Added CONTRIBUTING.md and CODE_OF_CONDUCT.md files +* Fixed a bug where the allow_cli_args attribute wasn't properly disabling parsing of args at invocation when False 0.7.0 ----- @@ -69,8 +69,7 @@ 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
- (and their abbreviations: `hi`, `li`, `l`, `r`).
+ The history is accessed through the ``history``, ``list``, and ``run`` commands.
If you wish to exclude some of your custom commands from the history, append their names
to the list at Cmd.ExcludeFromHistory.
@@ -566,13 +566,14 @@ class Cmd(cmd.Cmd): timing Report execution times ''') - def __init__(self, completekey='tab', stdin=None, stdout=None, use_ipython=False): + def __init__(self, completekey='tab', stdin=None, stdout=None, use_ipython=False, transcript_files=None): """An easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package. :param completekey: str - (optional) readline name of a completion key, default to Tab :param stdin: (optional) alternate input file object, if not specified, sys.stdin is used :param stdout: (optional) alternate output file object, if not specified, sys.stdout is used :param use_ipython: (optional) should the "ipy" command be included for an embedded IPython shell + :param transcript_files: str - (optional) allows running transcript tests when allow_cli_args is False """ # If use_ipython is False, make sure the do_ipy() method doesn't exit if not use_ipython: @@ -592,6 +593,7 @@ class Cmd(cmd.Cmd): if fname.startswith('do_')] self._init_parser() self._temp_filename = None + self._transcript_files = transcript_files def poutput(self, msg): """Convenient shortcut for self.stdout.write(); adds newline if necessary.""" @@ -1491,13 +1493,17 @@ Script should contain one command per line, just like command would be typed in return self._STOP_AND_EXIT 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)') - (callopts, callargs) = parser.parse_args() - if callopts.test: - self.runTranscriptTests(callargs) + if self.allow_cli_args: + parser = optparse.OptionParser() + parser.add_option('-t', '--test', dest='test', + action="store_true", + help='Test against transcript(s) in FILE (wildcards OK)') + (callopts, callargs) = parser.parse_args() + if callopts.test: + self._transcript_files = callargs + + if self._transcript_files is not None: + self.runTranscriptTests(self._transcript_files) else: # Always run the preloop first self.preloop() diff --git a/docs/freefeatures.rst b/docs/freefeatures.rst index 2c41c213..0f6a0e84 100644 --- a/docs/freefeatures.rst +++ b/docs/freefeatures.rst @@ -63,6 +63,18 @@ quotation marks if it is more than a one-word command. Gracie cat@eee:~/proj/cmd2/example$ +.. note:: + + if you wish to disable cmd2's consumption of command-line arguments, you can do so by setting the ``allow_cli_args`` + attribute of your ``cmd2.Cmd`` class instance to ``False``. This would be useful, for example, if you wish to use + someting like Argparse_ to parse the overall command line arguments for your application:: + + from cmd2 import Cmd + class App(Cmd): + def __init__(self): + self.allow_cli_args = False + +.. _Argparse: https://docs.python.org/3/library/argparse.html Output redirection ================== @@ -242,3 +254,17 @@ Regular expressions can be embedded in the transcript inside paired ``/`` slashes. These regular expressions should not include any whitespace expressions. +.. note:: + + If you have set ``allow_cli_args`` to False in order to disable parsing of command line arguments at invocaiton, + then the use of ``-t`` or ``--test`` to run transcript testing is automatically disabled. In this case, you can + alternatively provide a value for the optional ``transcript_files`` when constructing the instance of your + ``cmd2.Cmd`` derived class in order to cause a transcript test to run:: + + from cmd2 import Cmd + class App(Cmd): + # customized attributes and methods here + + if __name__ == '__main__': + app = App(transcript_files='exampleSession.txt') + app.cmdloop() diff --git a/examples/argparse_example.py b/examples/argparse_example.py new file mode 100755 index 00000000..8f833578 --- /dev/null +++ b/examples/argparse_example.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# coding=utf-8 +"""A sample application for cmd2 showing how to use Argparse to process command line arguments for your application. +It doubles as an example of how you can still do transcript testing even if allow_cli_args is false. + +Thanks to cmd2's built-in transtript testing capability, it also serves as a test suite for argparse_example.py when +used with the exampleSession.txt transcript. + +Running `python argparse_example.py -t exampleSession.txt` will run all the commands in the transcript against +argparse_example.py, verifying that the output produced matches the transcript. +""" +import argparse + +from cmd2 import Cmd, make_option, options + + +class CmdLineApp(Cmd): + """ Example cmd2 application. """ + multilineCommands = ['orate'] + Cmd.shortcuts.update({'&': 'speak'}) + maxrepeats = 3 + Cmd.settable.append('maxrepeats') + + # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist + # default_to_shell = True + + def __init__(self, ip_addr=None, port=None, transcript_files=None): + # Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell + Cmd.__init__(self, use_ipython=False, transcript_files=transcript_files) + + # Disable cmd's usage of command-line arguments as commands to be run at invocation + self.allow_cli_args = False + + # Example of args set from the command-line (but they aren't being used here) + self._ip = ip_addr + self._port = port + + @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") + ]) + def do_speak(self, arg, opts=None): + """Repeats what you tell me to.""" + arg = ''.join(arg) + if opts.piglatin: + arg = '%s%say' % (arg[1:], arg[0]) + if opts.shout: + arg = arg.upper() + repetitions = opts.repeat or 1 + for i in range(min(repetitions, self.maxrepeats)): + self.stdout.write(arg) + 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 + + +if __name__ == '__main__': + # You can do your custom Argparse parsing here to meet your application's needs + parser = argparse.ArgumentParser(description='Process the arguments however you like.') + + # Add a few arguments which aren't really used, but just to get the gist + parser.add_argument('-p', '--port', type=int, help='TCP port') + parser.add_argument('-i', '--ip', type=str, help='IPv4 address') + + # Add an argument which enables transcript testing + parser.add_argument('-t', '--test', type=str, help='Test against transcript in FILE (wildcards OK)') + args = parser.parse_args() + + port = None + if args.port: + port = args.port + + ip_addr = None + if args.ip: + ip_addr = args.ip + + transcript = None + if args.test: + transcripts = [args.test] + + # Instantiate your cmd2 applicaiton + c = CmdLineApp(transcript_files=transcripts) + + # And run your cmd2 application + c.cmdloop() |