summaryrefslogtreecommitdiff
path: root/docs/examples/example.rst
diff options
context:
space:
mode:
authorkotfu <kotfu@kotfu.net>2019-07-05 15:05:51 -0600
committerkotfu <kotfu@kotfu.net>2019-07-05 15:05:51 -0600
commit9e64f600b65d99ea5a83051372372512be698b1b (patch)
tree7409d3646ff4a0fa349b8e71daa5959cbde342bf /docs/examples/example.rst
parent4c97ab03825151fd85a875617fde7d0534c9cb72 (diff)
downloadcmd2-git-9e64f600b65d99ea5a83051372372512be698b1b.tar.gz
Added a walkthrough of constructing a simple application to the overview
For #709
Diffstat (limited to 'docs/examples/example.rst')
-rw-r--r--docs/examples/example.rst301
1 files changed, 301 insertions, 0 deletions
diff --git a/docs/examples/example.rst b/docs/examples/example.rst
new file mode 100644
index 00000000..58ae9cdb
--- /dev/null
+++ b/docs/examples/example.rst
@@ -0,0 +1,301 @@
+Simple Application
+==================
+
+.. _cmd: https://docs.python.org/3/library/cmd.html
+
+Here's a quick walkthrough of a simple application which demonstrates 8 features of
+``cmd2``:
+
+* Settings
+* Commands
+* Argument Parsing
+* Generating Output
+* Help
+* Shortcuts
+* Multiline Commands
+* History
+
+If you don't want to type as we go, you can download the complete source for
+this example app.
+
+
+Basic Application
+-----------------
+
+First we need to create a new ``cmd2`` application. Create a new file ``example.py`` with the following contents::
+
+ #!/usr/bin/env python
+ """
+ A sample application for cmd2.
+ """
+ import cmd2
+
+
+ class CmdLineApp(cmd2.Cmd):
+ """Example cmd2 application. """
+
+
+ if __name__ == '__main__':
+ import sys
+ c = CmdLineApp()
+ sys.exit(c.cmdloop())
+
+We have a new class ``CmdLineApp`` which is a subclass of
+:ref:`api/cmd:cmd2.Cmd`. When we tell python to run our file like this:
+
+.. code-block:: shell
+
+ $ python example.py
+
+it creates an instance of our class, and calls the ``cmdloop()`` method. This
+method accepts user input and runs commands based on that input. Because we
+subclassed ``cmd2.Cmd``, our new app already has a bunch of features built in.
+
+Congratulations, you have a working ``cmd2`` app. You can run it, and then type
+``quit`` to exit.
+
+
+Create a New Setting
+--------------------
+
+Before we create our first command, we are going to add a setting to this app.
+``cmd2`` includes robust support for :ref:`features/settings:Settings`. You
+configure settings during object initialization, so we need to add an
+initializer to our class::
+
+ def __init__(self):
+ super().__init__()
+
+ # Make maxrepeats settable at runtime
+ self.maxrepeats = 3
+ self.settable['maxrepeats'] = 'max repetitions for speak command'
+
+In that initializer, the first thing to do is to make sure we initialize
+``cmd2``. That's what the ``super().__init__()`` line does. Then we create an
+attribute to hold our setting, and then add a description of our setting to the
+``settable`` dictionary. If our attribute name isn't in ``settable``, then it
+won't be treated as a setting. Now if you run the script, and enter the ``set``
+command to see the settings, like this:
+
+.. code-block:: shell
+
+ $ python example.py
+ (Cmd) set
+
+you will see our ``maxrepeats`` setting show up with it's default value of ``3``.
+
+
+Create A Command
+----------------
+
+Now we will create our first command, called ``speak`` which will echo back
+whatever we tell it to say. We are going to use an :ref:`argument processor
+<features/argument_processing:Argument Processing>` so the ``speak`` command can
+shout and talk piglatin. We will also use some built in methods for
+:ref:`generating output <features/generating_output:Generating Output>`. Add
+this code to ``example.py``, so that the ``speak_parser`` attribute and the
+``do_speak()`` method are part of the ``CmdLineApp()`` class::
+
+ speak_parser = argparse.ArgumentParser()
+ speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
+ speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
+ speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
+ speak_parser.add_argument('words', nargs='+', help='words to say')
+
+ @cmd2.with_argparser(speak_parser)
+ def do_speak(self, args):
+ """Repeats what you tell me to."""
+ words = []
+ for word in args.words:
+ if args.piglatin:
+ word = '%s%say' % (word[1:], word[0])
+ if args.shout:
+ word = word.upper()
+ words.append(word)
+ repetitions = args.repeat or 1
+ for _ in range(min(repetitions, self.maxrepeats)):
+ # .poutput handles newlines, and accommodates output redirection too
+ self.poutput(' '.join(words))
+
+Up at the top of the script, you'll also need to add::
+
+ import argparse
+
+There's a bit to unpack here, so let's walk through it. We created
+``speak_parser``, which uses the `argparse
+<https://docs.python.org/3/library/argparse.html>`_ module from the Python
+standard library to parse command line input from a user. There is nothing thus far
+that is specific to ``cmd2``.
+
+There is also a new method called ``do_speak()``. In both cmd_ and ``cmd2``,
+methods that start with ``do_`` become new commands, so by defining this method
+we have created a command called ``speak``.
+
+Note the ``@cmd2.with_argparser`` decorator on the ``do_speak()`` method. This
+decorator does 3 useful things for us:
+
+1. It tells ``cmd2`` to process all input for the ``speak`` command using the
+ argparser we defined. If the user input doesn't meet the requirements defined by
+ the argparser, then an error will be displayed for the user.
+2. It alters our ``do_speak`` method so that instead of receiving the raw user
+ input as a parameter, we receive the namespace from the argparser.
+3. It creates a help message for us based on the argparser.
+
+You can see in the body of the method how we use the namespace from the
+argparser (passed in as the variable ``args``). We build an array of words which
+we will output, honoring both the ``--piglatin`` and ``--shout`` options.
+
+At the end of the method, we use our ``maxrepeats`` setting as an upper limit to
+the number of times we will print the output.
+
+The last thing you'll notice is that we used the ``self.poutput()`` method to
+display our output. ``poutput()`` is a method provided by ``cmd2``, which I
+strongly recommend you use anytime you want to :ref:`generate output <features/generating_output:Generating Output>`. It provides the following benefits:
+
+1. Allows the user to redirect output to a text file or pipe it to a shell process
+2. Gracefully handles ``BrokenPipeWarning`` exceptions for redirected output
+3. Makes the output show up in a :ref:`transcript <features/transcripts:Transcripts>`
+4. Honors the setting to strip embedded ansi sequences (typically used for background and foreground colors)
+
+Go run the script again, and try out the ``speak`` command. Try typing ``help speak``, and you
+will see a lovely usage message describing the various options for the command.
+
+With those few lines of code, we created a :ref:`command
+<features/commands:Commands>`, used an :ref:`Argument Processor <features/argument_processing:Argument Processing>`, added a nice :ref:`help message <features/help:Help>` for our users, and :ref:`generated some output <features/generating_output:Generating Output>`.
+
+
+Shortcuts
+---------
+
+``cmd2`` has several capabilities to simplify repetitive user input: :ref:`Shortcuts, Aliases, and Macros <features/shortcuts_aliases_macros:Shortcuts, Aliases, and Macros>`. Let's add a shortcut to our application. Shortcuts are character strings that can be used instead of a command name. For example, ``cmd2`` has support for a shortcut ``!`` which runs the ``shell`` command. So instead of typing this:
+
+.. code-block:: shell
+
+ (Cmd) shell ls -al
+
+you can type this:
+
+.. code-block:: shell
+
+ (Cmd) !ls -al
+
+Let's add a shortcut for our ``speak`` command. Change the ``__init__()`` method so it looks like this::
+
+ def __init__(self):
+ shortcuts = cmd2.DEFAULT_SHORTCUTS
+ shortcuts.update({'&': 'speak'})
+ super().__init__(shortcuts=shortcuts)
+
+ # Make maxrepeats settable at runtime
+ self.maxrepeats = 3
+ self.settable['maxrepeats'] = 'max repetitions for speak command'
+
+Shortcuts are passed to the ``cmd2`` initializer, and if you want the built-in
+shortcuts of ``cmd2`` you have to pass them. These shortcuts are defined as a
+dictionary, with the key being the shortcut, and the value containing the
+command. When using the default shortcuts and also adding your own, it's a good
+idea to use the ``.update()`` method to modify the dictionary. This way if you
+add a shortcut that happens to already be in the default set, yours will
+override, and you won't get any errors at runtime.
+
+Run your app again, and type:
+
+.. code-block:: shell
+
+ (Cmd) shortcuts
+
+to see the list of all of the shortcuts, including the one for speak that we just created.
+
+
+Multiline Commands
+------------------
+
+Some use cases benefit from the ability to have commands that span more than one
+line. For example, you might want the ability for your user to type in a SQL
+command, which can often span lines and which are terminated with a semicolon.
+Let's add a :ref:`multiline command <features/multiline_commands:Multiline
+Commands>` to our application. First we'll create a new command called
+``orate``. This code shows both the definition of our ``speak`` command, and the
+``orate`` command::
+
+ @cmd2.with_argparser(speak_parser)
+ def do_speak(self, args):
+ """Repeats what you tell me to."""
+ words = []
+ for word in args.words:
+ if args.piglatin:
+ word = '%s%say' % (word[1:], word[0])
+ if args.shout:
+ word = word.upper()
+ words.append(word)
+ repetitions = args.repeat or 1
+ for _ in range(min(repetitions, self.maxrepeats)):
+ # .poutput handles newlines, and accommodates output redirection too
+ self.poutput(' '.join(words))
+
+ # orate is a synonym for speak which takes multiline input
+ do_orate = do_speak
+
+With the new command created, we need to tell ``cmd2`` to treat that command as
+a multi-line command. Modify the super initialization line to look like this::
+
+ super().__init__(multiline_commands=['orate'], shortcuts=shortcuts)
+
+Now when you run the example, you can type something like this:
+
+.. code-block:: shell
+
+ (Cmd) orate O for a Muse of fire, that would ascend
+ > The brightest heaven of invention,
+ > A kingdom for a stage, princes to act
+ > And monarchs to behold the swelling scene! ;
+
+Notice the prompt changes to indicate that input is still ongoing. ``cmd2`` will
+continue prompting for input until it sees an unquoted semicolon (the default
+multi-line command termination character).
+
+
+History
+-------
+
+``cmd2`` tracks the history of the commands that users enter. As a developer,
+you don't need to do anything to enable this functionality, you get it for free.
+If you want the history of commands to persist between invocations of your
+application, you'll need to do a little work. The
+:ref:`features/history:Command History` page has all the details.
+
+Users can access command history using two methods:
+
+- the `readline <https://docs.python.org/3/library/readline.html>`_ library which provides a python interface to the `GNU readline library <https://tiswww.case.edu/php/chet/readline/rltop.html>`_.
+- the ``history`` command which is built-in to ``cmd2``
+
+From the prompt in a ``cmd2``-based application, you can press ``<CNTL>-p`` to
+move to the previously entered command, and ``<CNTL>-n`` to move to the next
+command. You can also search through the command history usint ``<CNTL>-r``. The
+`GNU Readline User Manual
+<https://tiswww.case.edu/php/chet/readline/rluserman.html>`_ has all the
+details, including all the available commands, and instructions for customizing
+the key bindings.
+
+The ``history`` command allows a user to view the command history, and select
+commands from history by number, range, string search, or regular expression.
+With the selected commands, users can:
+
+- re-run the commands
+- edit the selected commands in a text editor, and run them after the text editor exits
+- save the commands to a file
+- run the commands, saving both the commands and their output to a file
+
+Learn more about the ``history`` command by typing ``history -h`` at any ``cmd2`` input prompt, or by exploring :ref:`Command History For Users <features/history:For Users>`.
+
+
+Conclusion
+----------
+
+You've just created a simple, but functional command line application. With minimal work
+on your part, the application leverages many robust features of ``cmd2``. To learn more
+you can:
+
+- Dive into all of the :doc:`../features/index` that ``cmd2`` provides
+- Look at more :doc:`../examples/index`
+- Browse the :doc:`../api/index`