summaryrefslogtreecommitdiff
path: root/docs/examples/first_app.rst
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2019-07-06 20:31:00 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2019-07-06 20:31:00 -0400
commit3470b8861298b7e2e04f8f49befe98598c5a3b2a (patch)
tree81d63f9ac6e89d4ccc61d9afc6cf3d1054f878f6 /docs/examples/first_app.rst
parent901aa9ed176507071198e84978891c69cc6e9d3b (diff)
parent34975b52e769514b44ee8c13147a22961e68e7a7 (diff)
downloadcmd2-git-3470b8861298b7e2e04f8f49befe98598c5a3b2a.tar.gz
Merge branch 'master' into auto_completer_refactor
Diffstat (limited to 'docs/examples/first_app.rst')
-rw-r--r--docs/examples/first_app.rst322
1 files changed, 322 insertions, 0 deletions
diff --git a/docs/examples/first_app.rst b/docs/examples/first_app.rst
new file mode 100644
index 00000000..ef8e97dc
--- /dev/null
+++ b/docs/examples/first_app.rst
@@ -0,0 +1,322 @@
+First 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``:
+
+* :ref:`features/settings:Settings`
+* :ref:`features/commands:Commands`
+* :ref:`features/argument_processing:Argument Processing`
+* :ref:`features/generating_output:Generating Output`
+* :ref:`features/help:Help`
+* :ref:`features/shortcuts_aliases_macros:Shortcuts`
+* :ref:`features/multiline_commands:Multiline Commands`
+* :ref:`features/history:History`
+
+If you don't want to type as we go, you can download the complete source for
+this example.
+
+
+Basic Application
+-----------------
+
+First we need to create a new ``cmd2`` application. Create a new file
+``first_app.py`` with the following contents::
+
+ #!/usr/bin/env python
+ """A simple cmd2 application."""
+ import cmd2
+
+
+ class FirstApp(cmd2.Cmd):
+ """A simple cmd2 application."""
+
+
+ if __name__ == '__main__':
+ import sys
+ c = FirstApp()
+ sys.exit(c.cmdloop())
+
+We have a new class ``FirstApp`` which is a subclass of
+:ref:`api/cmd:cmd2.Cmd`. When we tell python to run our file like this:
+
+.. code-block:: shell
+
+ $ python first_app.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 first_app.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 ``first_app.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: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 ``Control-p`` to
+move to the previously entered command, and ``Control-n`` to move to the next
+command. You can also search through the command history using ``Control-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`