dhellmann / CommandLineApp (http://doughellmann.com/projects/CommandLineApp/)

The CommandLineApp class makes creating command line applications in Python as simple as defining callbacks to handle options when they appear in 'sys.argv'.

Changed (Δ1.4 KB):

raw changeset »

.hgignore (1 lines added, 0 lines removed)

docs/source/PyMagArticle/Listing2.py (4 lines added, 2 lines removed)

docs/source/PyMagArticle/index.rst (86 lines added, 18 lines removed)

pavement.py (11 lines added, 11 lines removed)

Up to file-list .hgignore:

@@ -4,3 +4,4 @@ setup.py
4
4
MANIFEST
5
5
CommandLineApp.egg-info
6
6
*.zip
7
docs/build

Up to file-list docs/source/PyMagArticle/Listing2.py:

@@ -9,6 +9,8 @@ import commandlineapp
9
9
class csvcat(commandlineapp.CommandLineApp):
10
10
    """Concatenate comma separated value files.
11
11
    """
12
    
13
    _app_name = 'csvcat'
12
14
13
15
    EXAMPLES_DESCRIPTION = '''
14
16
To concatenate 2 files, including all columns and headers:
@@ -49,7 +51,7 @@ To concatenate 2 files, including only t
49
51
        """
50
52
        self.dialect = name
51
53
        return
52
    optionHandler_d = optionHandler_dialect
54
    option_handler_d = option_handler_dialect
53
55
54
56
    columns = []
55
57
    def option_handler_columns(self, *col):
@@ -58,7 +60,7 @@ To concatenate 2 files, including only t
58
60
        """
59
61
        self.columns.extend([int(c) for c in col])
60
62
        return
61
    optionHandler_c = optionHandler_columns
63
    option_handler_c = option_handler_columns
62
64
63
65
    def getPrintableColumns(self, row):
64
66
        """Return only the part of the row which should be printed.

Up to file-list docs/source/PyMagArticle/index.rst:

@@ -41,11 +41,79 @@ Listing 1
41
41
42
42
::
43
43
44
	$ python Listing2.py --help
45
	Traceback (most recent call last):
46
	  File "Listing2.py", line 7, in <module>
47
	    import commandlineapp
48
	ImportError: No module named commandlineapp
44
	$ python docs/source/PyMagArticle/Listing2.py --help
45
	Concatenate comma separated value files.
46
	
47
	
48
	SYNTAX:
49
	
50
	  csvcat [<options>] filename [filename...]
51
	
52
	    -c col[,col...], --columns=col[,col...]
53
	    -d name, --dialect=name
54
	    --debug
55
	    -h
56
	    --help
57
	    --quiet
58
	    --skip-headers
59
	    -v
60
	    --verbose=level
61
	
62
	
63
	ARGUMENTS:
64
	
65
	    The names of comma separated value files, such as might be
66
	    exported from a spreadsheet or database program.
67
	
68
	
69
	OPTIONS:
70
	
71
	    -c col[,col...], --columns=col[,col...]
72
	        Limit the output to the specified columns. Columns are
73
	        identified by number, starting with 0.
74
	
75
	    -d name, --dialect=name
76
	        Specify the output dialect name. Defaults to "excel".
77
	
78
	    --debug
79
	        Set debug mode to see tracebacks.
80
	
81
	    -h
82
	        Displays abbreviated help message.
83
	
84
	    --help
85
	        Displays verbose help message.
86
	
87
	    --quiet
88
	        Turn on quiet mode.
89
	
90
	    --skip-headers
91
	        Treat the first line of each file as a header, and only
92
	        include one copy in the output.
93
	
94
	    -v
95
	        Increment the verbose level.
96
	
97
	        Higher levels are more verbose. The default is 1.
98
	
99
	    --verbose=level
100
	        Set the verbose level.
101
	
102
	EXAMPLES:
103
	
104
	
105
	To concatenate 2 files, including all columns and headers:
106
	
107
	  $ csvcat file1.csv file2.csv
108
	
109
	To concatenate 2 files, skipping the headers in the second file:
110
	
111
	  $ csvcat --skip-headers file1.csv file2.csv
112
	
113
	To concatenate 2 files, including only the first and third columns:
114
	
115
	  $ csvcat --col 0,2 file1.csv file2.csv
116
	
49
117
50
118
.. [[[end]]]
51
119
@@ -57,7 +125,7 @@ Listing 2
57
125
58
126
The program description is taken from the docstring of the **csvcat** class.  Before it is printed, the text is split into paragraphs and reformatted using **textwrap**, to ensure that it is no wider than 80 columns of text.
59
127
60
The program description is followed by a syntax summary for the program.  The options listed in the syntax section correspond to methods with names that begin with ``optionHandler_``.  For example, ``optionHandler_skip_headers()`` indicates that **csvcat** should accept a ``--skip-headers`` option on the command line.
128
The program description is followed by a syntax summary for the program.  The options listed in the syntax section correspond to methods with names that begin with ``option_handler_``.  For example, ``option_handler_skip_headers()`` indicates that **csvcat** should accept a ``--skip-headers`` option on the command line.
61
129
62
130
The names of any non-optional arguments to the program appear in the syntax summary.  In this case, **csvcat** needs the names of the files containing the input data.  At least one file name is necessary, and multiple names can be given, as indicated by the fact that the ``filename`` argument to ``main()`` (line 78) uses the variable argument notation: ``*filename``.  A longer description of the arguments, taken from the docstring of the ``main()`` method (lines 79-82), follows the syntax summary.  As with the general program summary, the description of the arguments is reformatted with **textwrap** to fit the screen.
63
131
@@ -66,15 +134,15 @@ Options and Their Arguments
66
134
67
135
Following the argument description is a detailed explanation of all of the options to the program.  **CommandLineApp** examines each option handler method to build the option description, including the name of the option, alternative names for the same option, and the name and description of any arguments the option accepts.  There are three variations of option handlers, based on the arguments used by the option. 
68
136
69
The simplest kind of option does not take an argument at all, and is used as a "switch" to turn a feature on or off.  The method ``optionHandler_skip_headers`` (lines 38-43) is an example of such a switch.  The method takes no argument, so **CommandLineApp** recognizes that the option being defined does not take an argument either.  To create the option name, the prefix is stripped from the method name, and the underscore is converted to a dash (``-``); ``optionHandler_skip_headers`` becomes ``--skip-headers``.
137
The simplest kind of option does not take an argument at all, and is used as a "switch" to turn a feature on or off.  The method ``option_handler_skip_headers`` (lines 38-43) is an example of such a switch.  The method takes no argument, so **CommandLineApp** recognizes that the option being defined does not take an argument either.  To create the option name, the prefix is stripped from the method name, and the underscore is converted to a dash (``-``); ``option_handler_skip_headers`` becomes ``--skip-headers``.
70
138
71
Other options accept a single argument.  For example, the ``--dialect`` option requires the name of the CSV output dialect.  The method ``optionHandler_dialect`` (lines 46-51) takes one argument, called ``name``.  The suggested syntax for the option, as seen in Listing 1, is ``--dialect=name``.  The name of the method's argument is used as the name of the argument to the option in the help text.
139
Other options accept a single argument.  For example, the ``--dialect`` option requires the name of the CSV output dialect.  The method ``option_handler_dialect`` (lines 46-51) takes one argument, called ``name``.  The suggested syntax for the option, as seen in Listing 1, is ``--dialect=name``.  The name of the method's argument is used as the name of the argument to the option in the help text.
72
140
73
The ``-d`` option has the same meaning as ``--dialect``, because ``optionHandler_d`` is an alias for ``optionHandler_dialect`` (line 52).  **CommandLineApp** recognizes aliases, and combines the forms in the documentation so the alternative forms ``-d name`` and ``--dialect=name`` are described together.
141
The ``-d`` option has the same meaning as ``--dialect``, because ``option_handler_d`` is an alias for ``option_handler_dialect`` (line 52).  **CommandLineApp** recognizes aliases, and combines the forms in the documentation so the alternative forms ``-d name`` and ``--dialect=name`` are described together.
74
142
75
143
It is often useful for an option to take multiple arguments, as with ``--columns``.  The user could repeat the option on the command line, but it is more compact to allow them to list multiple values in one argument list.  When **CommandLineApp** sees an option handler method that takes a variable argument list, it treats the corresponding option as accepting a list of arguments.  When the option appears on the command line, the string argument is split on any commas and the resulting list of strings is passed to the option handler method.  
76
144
77
For example, ``optionHandler_columns`` (lines 55-60) takes a variable length argument named ``col``.  The option ``--columns`` can be followed by several column numbers, separated by commas.  The option handler is called with the list of values pre-parsed.  In the syntax description, the argument is shown repeating: ``--columns=col[,col...]``.
145
For example, ``option_handler_columns`` (lines 55-60) takes a variable length argument named ``col``.  The option ``--columns`` can be followed by several column numbers, separated by commas.  The option handler is called with the list of values pre-parsed.  In the syntax description, the argument is shown repeating: ``--columns=col[,col...]``.
78
146
79
147
For all cases, the docstring from the option handler method serves as the help text for the option.  The text of the docstring is reformatted using **textwrap** so both the code and help output are easy to read without extra effort on the part of the developer.
80
148
@@ -197,43 +265,43 @@ Option Definitions
197
265
198
266
The standard library module **inspect** provides functions for performing introspection operations on classes and objects at runtime.  The API supports basic querying and type checking so it is possible, for example, to get a list of the methods of a class, including all inherited methods.  
199
267
200
``CommandLineApp.scanForOptions()`` uses **inspect** to scan an application class for option handler methods (lines 251-260).  All of the methods of the class are retrieved with ``inspect.getmembers()``, and those whose name starts with ``optionHandler_`` are added to the list of supported options.  Since most command line options use dashes instead of underscores, but method names cannot contain dashes, the underscores in the option handler method names are converted to dashes when creating the option name.
268
``CommandLineApp.scanForOptions()`` uses **inspect** to scan an application class for option handler methods (lines 251-260).  All of the methods of the class are retrieved with ``inspect.getmembers()``, and those whose name starts with ``option_handler_`` are added to the list of supported options.  Since most command line options use dashes instead of underscores, but method names cannot contain dashes, the underscores in the option handler method names are converted to dashes when creating the option name.
201
269
202
270
The ``__init__()`` method of the **OptionDef** class (lines 440-469) does all of the work of determining the command line switch name and what type of arguments the switch takes.  The option handler method is examined with ``inspect.getargspec()``, and the result is used to initialize the **OptionDef**.
203
271
204
272
An "argspec" for a function is a tuple made up of four values: a list of the names of all regular arguments to the function, including ``self`` if the function is a method; the name of the argument to receive the variable argument values, if any; the name of the argument to receive the keyword arguments, if any; and a list of the default values for the arguments, in they order they appear in the list of option names.
205
273
206
The argspecs for the option handlers in **csvcat** illustrate the variations of interest to **OptionDef**.  First, ``optionHandler_skip_headers``:
274
The argspecs for the option handlers in **csvcat** illustrate the variations of interest to **OptionDef**.  First, ``option_handler_skip_headers``:
207
275
208
276
::
209
277
210
278
    >>> import Listing2
211
279
    >>> import inspect
212
280
    >>> print inspect.getargspec(
213
    ... Listing2.csvcat.optionHandler_skip_headers)
281
    ... Listing2.csvcat.option_handler_skip_headers)
214
282
    (['self'], None, None, None)
215
283
216
284
Since the only positional argument to the method is ``self``, and there is no variable argument name given, the option handler is treated as a simple command line switch without any arguments.
217
285
218
The ``optionHandler_dialect``, on the other hand, does include an additional argument:
286
The ``option_handler_dialect``, on the other hand, does include an additional argument:
219
287
220
288
::
221
289
222
290
    >>> print inspect.getargspec(
223
    ... Listing2.csvcat.optionHandler_dialect)
291
    ... Listing2.csvcat.option_handler_dialect)
224
292
    (['self', 'name'], None, None, None)
225
293
226
294
The ``name`` argument is listed in the argspec as a single regular argument.  The result, when a program is run, is that while the options are being processed by **CommandLineApp** and **OptionDef**, the value for ``name`` is passed directly to the option handler method (line 497).
227
295
228
The ``optionHandler_columns`` method illustrates variable argument handling:
296
The ``option_handler_columns`` method illustrates variable argument handling:
229
297
230
298
::
231
299
232
300
    >>> print inspect.getargspec(
233
    ... Listing2.csvcat.optionHandler_columns)
301
    ... Listing2.csvcat.option_handler_columns)
234
302
    (['self'], 'col', None, None)
235
303
236
The ``col`` argument from ``optionHandler_columns`` is named in the argspec as the variable argument identifier.  Since ``optionHandler_columns`` accepts variable arguments, the **OptionDef** splits the argument value into a list of strings, and the list is passed to the option handler method (lines 494-495) using the variable argument syntax.
304
The ``col`` argument from ``option_handler_columns`` is named in the argspec as the variable argument identifier.  Since ``option_handler_columns`` accepts variable arguments, the **OptionDef** splits the argument value into a list of strings, and the list is passed to the option handler method (lines 494-495) using the variable argument syntax.
237
305
238
306
The other variable argument configuration, using unidentified keyword arguments, does not make sense for an option handler.  The user of the command line program has no standard way to specify named arguments to options, so they are not supported by **OptionDef**.
239
307

Up to file-list pavement.py:

@@ -118,12 +118,19 @@ def run_script(input_file, script_name,
118
118
       If False, the output is passed to rstrip() then one newline is added.  If
119
119
       True, newlines are added to the output until it ends in 2.
120
120
    """
121
    # rundir = path(input_file).dirname()
122
    # if interpreter:
123
    #     cmd = '%(interpreter)s %(script_name)s' % vars()
124
    # else:
125
    #     cmd = script_name
126
    # real_cmd = 'cd %(rundir)s; %(cmd)s 2>&1' % vars()
121
127
    rundir = path(input_file).dirname()
128
    full_script_name = rundir / script_name
122
129
    if interpreter:
123
        cmd = '%(interpreter)s %(script_name)s' % vars()
130
        cmd = '%(interpreter)s %(full_script_name)s' % vars()
124
131
    else:
125
        cmd = script_name
126
    real_cmd = 'cd %(rundir)s; %(cmd)s 2>&1' % vars()
132
        cmd = full_script_name
133
    real_cmd = cmd
127
134
    try:
128
135
        output_text = sh(real_cmd, capture=True, ignore_error=ignore_error)
129
136
    except Exception, err:
@@ -163,14 +170,7 @@ def sdist():
163
170
    pass
164
171
165
172
@task
166
def copy_src_to_listing(options):
167
    #destdir=path(options.sphinx.docroot) / options.sphinx.sourcedir / 'PyMagArticle'
168
    destdir='docs/source/PyMagArticle'
169
    sh('cp commandlineapp.py %s' % destdir)
170
    return
171
172
@task
173
@needs(['copy_src_to_listing', 'cog', 'paver.doctools.html'])
173
@needs(['cog', 'paver.doctools.html'])
174
174
def html(options):
175
175
    """Run sphinx to produce the documentation.
176
176
    """