/
simplified-command-line-processing-with-dyn-optionspy.html
150 lines (126 loc) · 7.75 KB
/
simplified-command-line-processing-with-dyn-optionspy.html
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
<!---
;;
;; This softeware is Copyright (c) 2009 A.F. Haffmans
;;
;; This file is part of cl-bliky.
;;
;; cl-bliky is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; cl-bliky is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with cl-bliky. If not, see <http://www.gnu.org/licenses/>.
;;
;;
--->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title> Mohegan SkunkWorks </title>
<link rel="stylesheet" href="/style.css" type="text/css"/>
</head>
<body>
<div id="blog-title">
<a href="/"> <h1> Mohegan SkunkWorks </h1> </a>
</div>
<div id="mainClm">
<div id="post">
<div id="post-type" style="visibility:hidden">
<p>"post"</p>
</div>
<div id="post-timestamp" >
<p>Sat, 29 Aug 2009 19:15:49 EST </p>
</div>
<div id="post-header">
<h2> Simplified command line processing with dyn-options.py </h2>
</div>
<div id="post-intro">
<P>Am I the only one in the world who feels that using python's <EM>getopt</EM> is a bit of a struggle ? It involves a lot of boiler plate. Tedious refactoring is required each time you add or change an option. This is not specific to Python, as most languages have a similar facility to parse the command line, which is similarly annoying. <BR> <BR> I decided to create an easier way to process command line options, by transforming the command line into an immutable (read-only) object. The result is <A HREF="http://github.com/fons/dyn_options/tree/master">dyn_options</A>. <BR> <BR> <A HREF="http://github.com/fons/dyn_options/tree/master">dyn_options</A> considers every string on the command line which starts with either - or -- (i.e. a single or double dash) an option flag. The value of the option flag is a concatenation of everything that follows it, until the next flag is encountered. A simple option flag is one without explicit values and is considered a boolean flag, set to <EM>True</EM>. <A HREF="http://github.com/fons/dyn_options/tree/master">dyn_options</A> creates a read-only object, with attributes and values set to the command line option flags and values respectively. <BR> <BR> So, '--opt4 hello world' will be converted to an option flag called <EM>opt4</EM>, with a value of <EM>hello world</EM>. This makes dealing with spaces on the command line a lot easier. </P>
</div>
<div id="post-body">
<H3>Using <A HREF="http://github.com/fons/dyn_options/tree/master">dyn_options</A></H3><P>Here is how an option object is created : </P><PRE><CODE> import dyn_options
option = dyn_options.create_option(argv, option_defaults())
</CODE></PRE><P>If you have defaults, <EM>option_defaults()</EM> should return a dictionary of key-value pairs, with the key corresponding to the option, and its value to the desired default value. </P><P>An easy way to check whether an option is set, is to do something like : </P><PRE><CODE> if option.some_flag :
do_something
.......
</CODE></PRE><H3>A few examples</H3><P> You can play around with <EM> example.py</EM> to test how various options are handled. Here's the source: </P> <PRE><CODE> #!/usr/bin/env python
import sys
import dyn_options
def option_defaults() :
return dict( [("opt1", "opt1_default"), ("help", False)])
def main(argv) :
option = dyn_options.create_option(argv, option_defaults())
print "using defaults :", option
option = dyn_options.create_option(argv)
print "no defaults :", option
if option.opt4 :
print "opt4 is set :", option.opt4
else :
print "opt4 is not set"
if __name__ == '__main__':
sys.exit(main(sys.argv))
</CODE></PRE><P>I create two different <EM>option</EM> objects. The first one has defaults; The second one doesn't. The output for </P><PRE><CODE> ./example.py --opt2 --opt4 hello world </CODE></PRE><P>is this: </P><PRE><CODE> using defaults : options :
#) help ==> False
#) program ==> ./example.py
#) opt4 ==> hello world
#) opt1 ==> opt1_default
#) opt2 ==> True
no defaults : options :
#) program ==> ./example.py
#) opt4 ==> hello world
#) opt2 ==> True
opt4 is set : hello world </CODE></PRE><P>When <EM> option</EM> is initialized with the dictionary returned by <EM> option_defaults()</EM> ,<BR> <EM> opt1</EM> is set to the default value specified for it in the dictionary. In the second case, when no defaults are supplied, it's not set. </P> <P> Here's the output for : <CODE> ./example.py --opt1 new_value</CODE> </P> <PRE><CODE> using defaults : options :
#) help ==> False
#) program ==> ./example.py
#) opt1 ==> new_value
no defaults : options :
#) program ==> ./example.py
#) opt1 ==> new_value
opt4 is not set </CODE></PRE><P>As you can see, the value of <EM>opt1</EM> is now the one provided on the command line, rather than the default. <BR> <BR><BR>
</P><H3>Immutable/Read-Only</H3><P>This is one of the tests in <EM> dyn<EM>options</EM>test.py</EM> . It verifies that the <EM> option</EM> remains unchanged, after it's been created. </P> <PRE><CODE> def test4() :
"""
option is immutable
"""
L=['./dyn_options.py', '--opt1', 'opt1_value', '-opt2', 'opt2_value', '-opt3']
option = dyn_options.create_option(L, option_defaults())
try :
assert option.opt1 == "opt1_value"
assert option.opt2 == "opt2_value"
assert option.opt3 == True
assert option.help == False
#Try to override...
option.help = True
assert option.help == False
option.opt2 == "new_opt2_value"
assert option.opt2 == "opt2_value"
#Try to add new attribute
option.opt55 = "opt55_value"
assert option.opt55 == False
except AssertionError :
traceback.print_exc()
print "Failed test4 : parsing ", str(L)
print "generated : ", option
print "internals : ", option.__repr__()
return -1
print "pass test4"
return 0 </CODE></PRE><P> You can't set additional attributes, nor override existing ones. This seems reasonable to me. Once your options are set, they should remain so. Notice that I don't throw an exception when try to override the value of option <EM>opt2</EM>. <BR> <BR><BR>
</P> <H3> Internals</H3><P> How does all of this work ? Very simple : The command line is converted to a dictionary, which in turn is used to initialize the internal dictionary <STRONG>dict</STRONG> of the <EM> options</EM> object. I also override the <STRONG>getattr</STRONG> and <STRONG>setattr</STRONG> methods. Those are used to 'get' and 'set' elements of the internal dictionary, and need to be overridden to make the object read-only. </P> <P> Enjoy. </P>
</div>
<div id="post-timestamp-id" style="visibility:hidden">
<p>3460576549 </p>
</div>
</div>
<div id="footer">
<p>
Powered by <a href="http://www.github.com/fons/cl-bliky"> cl-bliky </a>
</p>
</div>
</div><!-- end of main area -->
</body>
</html>