summaryrefslogtreecommitdiff
path: root/docs/argument_processing.rst
blob: ecf59504534209cf5e19623ab579acd6f359ccd1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
.. _decorators:

===================
Argument Processing
===================

``cmd2`` makes it easy to add sophisticated argument processing to your commands using the ``argparse`` python module.
``cmd2`` handles the following for you:

1. Parsing input and quoted strings like the Unix shell
2. Parse the resulting argument list using an instance of ``argparse.ArgumentParser`` that you provide
3. Passes the resulting ``argparse.Namespace`` object to your command function
4. Adds the usage message from the argument parser to your command.
5. Checks if the ``-h/--help`` option is present, and if so, display the help message for the command

These features are all provided by the ``@with_argparser`` decorator which is importable from ``cmd2``.

See the either the argprint_ or argparse_ example to learn more about how to use the various ``cmd2`` argument
processing decorators in your ``cmd2`` applications.

.. _argprint: https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py
.. _argparse: https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_example.py

Using the argument parser decorator
===================================

For each command in the ``cmd2`` subclass which requires argument parsing,
create an instance of ``argparse.ArgumentParser()`` which can parse the
input appropriately for the command. Then decorate the command method with
the ``@with_argparser`` decorator, passing the argument parser as the
first parameter to the decorator. This changes the second argumen to the command method, which will contain the results
of ``ArgumentParser.parse_args()``.

Here's what it looks like::

      import argparse
      from cmd2 import with_argparser

      argparser = argparse.ArgumentParser()
      argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
      argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
      argparser.add_argument('-r', '--repeat', type=int, help='output [n] times')
      argparser.add_argument('word', nargs='?', help='word to say')

      @with_argparser(argparser)
      def do_speak(self, opts)
         """Repeats what you tell me to."""
         arg = opts.word
         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.poutput(arg)

.. note::

   The ``@with_argparser`` decorator sets the ``prog`` variable in
   the argument parser based on the name of the method it is decorating.
   This will override anything you specify in ``prog`` variable when
   creating the argument parser.


Help Messages
=============

By default, cmd2 uses the docstring of the command method when a user asks
for help on the command. When you use the ``@with_argparser``
decorator, the docstring for the ``do_*`` method is used to set the description for the ``argparse.ArgumentParser`` is
With this code::

   import argparse
   from cmd2 import with_argparser

   argparser = argparse.ArgumentParser()
   argparser.add_argument('tag', help='tag')
   argparser.add_argument('content', nargs='+', help='content to surround with tag')
   @with_argparser(argparser)
   def do_tag(self, args):
      """create a html tag"""
      self.stdout.write('<{0}>{1}</{0}>'.format(args.tag, ' '.join(args.content)))
      self.stdout.write('\n')

The ``help tag`` command displays:

.. code-block:: none

   usage: tag [-h] tag content [content ...]

   create a html tag

   positional arguments:
     tag         tag
     content     content to surround with tag

   optional arguments:
     -h, --help  show this help message and exit


If you would prefer you can set the ``description`` while instantiating the ``argparse.ArgumentParser`` and leave the
docstring on your method empty::

   import argparse
   from cmd2 import with_argparser

   argparser = argparse.ArgumentParser(description='create an html tag')
   argparser.add_argument('tag', help='tag')
   argparser.add_argument('content', nargs='+', help='content to surround with tag')
   @with_argparser(argparser)
   def do_tag(self, args):
      self.stdout.write('<{0}>{1}</{0}>'.format(args.tag, ' '.join(args.content)))
      self.stdout.write('\n')

Now when the user enters ``help tag`` they see:

.. code-block:: none

   usage: tag [-h] tag content [content ...]

   create an html tag

   positional arguments:
     tag         tag
     content     content to surround with tag

   optional arguments:
     -h, --help  show this help message and exit


To add additional text to the end of the generated help message, use the ``epilog`` variable::

   import argparse
   from cmd2 import with_argparser

   argparser = argparse.ArgumentParser(description='create an html tag',
                                       epilog='This command can not generate tags with no content, like <br/>.')
   argparser.add_argument('tag', help='tag')
   argparser.add_argument('content', nargs='+', help='content to surround with tag')
   @with_argparser(argparser)
   def do_tag(self, args):
      self.stdout.write('<{0}>{1}</{0}>'.format(args.tag, ' '.join(args.content)))
      self.stdout.write('\n')

Which yields:

.. code-block:: none

   usage: tag [-h] tag content [content ...]

   create an html tag

   positional arguments:
     tag         tag
     content     content to surround with tag

   optional arguments:
     -h, --help  show this help message and exit

   This command can not generate tags with no content, like <br/>


Grouping Commands
=================

By default, the ``help`` command displays::

  Documented commands (type help <topic>):
  ========================================
  alias    findleakers  pyscript    sessions             status       vminfo
  config   help         quit        set                  stop         which
  connect  history      redeploy    shell                thread_dump
  deploy   list         resources   shortcuts            unalias
  edit     load         restart     sslconnectorciphers  undeploy
  expire   py           serverinfo  start                version

If you have a large number of commands, you can optionally group your commands into categories.
Here's the output from the example ``help_categories.py``::

  Documented commands (type help <topic>):

  Application Management
  ======================
  deploy  findleakers  redeploy  sessions  stop
  expire  list         restart   start     undeploy

  Connecting
  ==========
  connect  which

  Server Information
  ==================
  resources  serverinfo  sslconnectorciphers  status  thread_dump  vminfo

  Other
  =====
  alias   edit  history  py        quit  shell      unalias
  config  help  load     pyscript  set   shortcuts  version


There are 2 methods of specifying command categories, using the ``@with_category`` decorator or with the
``categorize()`` function. Once a single command category is detected, the help output switches to a categorized
mode of display. All commands with an explicit category defined default to the category `Other`.

Using the ``@with_category`` decorator::

  @with_category(CMD_CAT_CONNECTING)
  def do_which(self, _):
      """Which command"""
      self.poutput('Which')

Using the ``categorize()`` function:

    You can call with a single function::

        def do_connect(self, _):
            """Connect command"""
            self.poutput('Connect')

        # Tag the above command functions under the category Connecting
        categorize(do_connect, CMD_CAT_CONNECTING)

    Or with an Iterable container of functions::

        def do_undeploy(self, _):
            """Undeploy command"""
            self.poutput('Undeploy')

        def do_stop(self, _):
            """Stop command"""
            self.poutput('Stop')

        def do_findleakers(self, _):
            """Find Leakers command"""
            self.poutput('Find Leakers')

        # Tag the above command functions under the category Application Management
        categorize((do_undeploy,
                    do_stop,
                    do_findleakers), CMD_CAT_APP_MGMT)

The ``help`` command also has a verbose option (``help -v`` or ``help --verbose``) that combines
the help categories with per-command Help Messages::

    Documented commands (type help <topic>):

    Application Management
    ================================================================================
    deploy              Deploy command
    expire              Expire command
    findleakers         Find Leakers command
    list                List command
    redeploy            Redeploy command
    restart             usage: restart [-h] {now,later,sometime,whenever}
    sessions            Sessions command
    start               Start command
    stop                Stop command
    undeploy            Undeploy command

    Connecting
    ================================================================================
    connect             Connect command
    which               Which command

    Server Information
    ================================================================================
    resources              Resources command
    serverinfo             Server Info command
    sslconnectorciphers    SSL Connector Ciphers command is an example of a command that contains
                           multiple lines of help information for the user. Each line of help in a
                           contiguous set of lines will be printed and aligned in the verbose output
                           provided with 'help --verbose'
    status                 Status command
    thread_dump            Thread Dump command
    vminfo                 VM Info command

    Other
    ================================================================================
    alias               Define or display aliases
    config              Config command
    edit                Edit a file in a text editor.
    help                List available commands with "help" or detailed help with "help cmd".
    history             usage: history [-h] [-r | -e | -s | -o FILE | -t TRANSCRIPT] [arg]
    load                Runs commands in script file that is encoded as either ASCII or UTF-8 text.
    py                  Invoke python command, shell, or script
    pyscript            Runs a python script file inside the console
    quit                Exits this application.
    set                 usage: set [-h] [-a] [-l] [settable [settable ...]]
    shell               Execute a command as if at the OS prompt.
    shortcuts           Lists shortcuts (aliases) available.
    unalias             Unsets aliases
    version             Version command


Receiving an argument list
==========================

The default behavior of ``cmd2`` is to pass the user input directly to your
``do_*`` methods as a string. If you don't want to use the full argument parser support outlined above, you can still have ``cmd2`` apply shell parsing rules to the user input and pass you a list of arguments instead of a string. Apply the ``@with_argument_list`` decorator to those methods that should receive an argument list instead of a string::

    from cmd2 import with_argument_list

    class CmdLineApp(cmd2.Cmd):
        """ Example cmd2 application. """

        def do_say(self, cmdline):
            # cmdline contains a string
            pass

        @with_argument_list
        def do_speak(self, arglist):
            # arglist contains a list of arguments
            pass


Using the argument parser decorator and also receiving a a list of unknown positional arguments
===============================================================================================
If you want all unknown arguments to be passed to your command as a list of strings, then
decorate the command method with the ``@with_argparser_and_unknown_args`` decorator.

Here's what it looks like::

    import argparse
    from cmd2 import with_argparser_and_unknown_args

    dir_parser = argparse.ArgumentParser()
    dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line")

    @with_argparser_and_unknown_args(dir_parser)
    def do_dir(self, args, unknown):
        """List contents of current directory."""
        # No arguments for this command
        if unknown:
            self.perror("dir does not take any positional arguments:", traceback_war=False)
            self.do_help('dir')
            self._last_result = CmdResult('', 'Bad arguments')
            return

        # Get the contents as a list
        contents = os.listdir(self.cwd)

        ...

Sub-commands
============
Sub-commands are supported for commands using either the ``@with_argparser`` or
``@with_argparser_and_unknown_args`` decorator.  The syntax for supporting them is based on argparse sub-parsers.

You may add multiple layers of sub-commands for your command. Cmd2 will automatically traverse and tab-complete
sub-commands for all commands using argparse.

See the subcommands_ and tab_autocompletion_ example to learn more about how to use sub-commands in your ``cmd2`` application.

.. _subcommands: https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py
.. _tab_autocompletion: https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py