From: Bardur A. <oca...@sc...> - 2004-08-13 16:52:28
|
Hi all, As promised, here's my take on how option parsing should be done (i.e. Python optparse-style). Since there's quite a lot of it, I've uploaded the code to http://imada.sdu.dk/~bardur/optparse-20040813.tar.gz Currently I'm using an everso slightly modified Getopt as the backend to actually parse the command line, but I'll change away from that and implement my own because of a couple of limitations in that parser which prevent me from implementing some of the optparse stuff (nargs>1 support comes to mind). There is a complete example in the tarball, but here's a sample from that file (revised syntax, sorry but I've gotten so used to it that I daren't try my hand at 'old' syntax): open OptParse; [...] let op = new optparser ~version:"1.0" () and verboseness = ref 0 and title = (StdOpt.int_option ~metavar:"NUM" ~default:1 ()) and vol_id = (StdOpt.str_option ~metavar:"TITLE" ~default:"DEFLABEL" ()) and super_seekrit = (StdOpt.store_true ()) in (do { op#add ~help:"Increase verboseness; this help message is extra long just to see if wrapped lines work properly when the corresponding options overflow the 'option area'." ["-v";"--verbose";"--talkative"] (StdOpt.count_option ~increment:1 verboseness); op#add ~help:"Decrease verboseness" ["-q"; "--quiet"] (StdOpt.count_option ~increment:(-1) verboseness); op#add ~help:"Select title # to keep" ["-T"; "--title"] title; op#add ~help:"set the Volume ID (label) of the file system" ["-V"] vol_id; op#add ~help:"set the super-seekrit option" ~show:False ["-S"] super_seekrit; op#add ~help:"set a random string option to something, by the way this is a very long help text, but then that's OK. It will just get wrapped neatly." ["-X"] some_string; printf "verboseness: %d\n" verboseness.val; printf "title: %d\n" (Opt.get title); printf "vol_id: %s\n" (Opt.get vol_id); printf "super-seekrit: %b\n" (Opt.get super_seekrit); flush stdout; }) When run with the --help option (which is automatically added unless explicitly suppressed), it produces the following output: usage: sample1 [options] options: --version show program's version and exit -h, --help show this help message and exit -v, --talkative, --verbose Increase verboseness; this help message is extra long just to see if wrapped lines work properly when the corresponding options overflow the 'option area'. -q, --quiet Decrease verboseness -TNUM, --title=NUM Select title # to keep -VTITLE set the Volume ID (label) of the file system -XSTR set a random string option to something, by the way this is a very long help text, but then that's OK. It will just get wrapped neatly. (output is currently always formatted for a terminal width of 79; I'll see if we can't get the terminal width from some built-in library...) There are a couple of issues of style with _using_ the code: - An earlier version used labeled arguments ~short_name, ~short_names, ~long_name, ~long_names to construct the option strings. As an example, here's what the "quiet" declaration above would look like: op#add ~help:"Decrease verboseness" ~short_name:'q' ~long_name:"quiet" (StdOpt.count_option ~increment:(-1) verboseness); This just seems cleaner to me (and is very slightly more type safe), but I thought I'd ask you all for opinions on that. - I feel it's quite ugly to use names like "int_option" and "str_option", but the only alternative I can see (if we want polymorphism and type safety) is: Get rid of "int_option", et.al. and have just one value_option which (as its only required parameter) a a module with a signature that includes an "of_string" function. This would look like: let int_opt1 = value_option ... Integer in ... which just looks cleaner to me (and obviates the need for a separate module namespace for xxx_option functions to avoid polluting the namespace too much). There are practical problems, though: Almost no relevant modules actually have an "of_string", there is no Integer module, etc. One semi-serious problem with relying on these "built-in" conversion is that the "Failure _" exceptions they raise have values which are useless for displaying to the user, so one must either accept a generic "could not read value '$ARG'" message, or just accept the useless messages from the built-in functions. I'm not too familiar with modules, so it may be possible to wrap the standard modules using something similar to how ExtString does it and provide more useful exception messages... Ideas, anyone? There are also a couple of implementation issues which I'd like to get general consensus on: - Option groups: Should they be supported? See http://www.python.org/doc/current/lib/optparse-generating-help.html for an example of how that would look (output-wise). AFAIK, they serve only cosmetic purposes in optparse.py. - Formatting: Should the interface to the help text formatter be exposed to the user? (This would require exporting a bit more of the internals, but would probably not be _too_ bad). Sooo... what you think? A serious contenter for Arg/Getopt for (eventual) inclusion in ExtLib? Any and all constructive criticism would be greatly appreciated! :) -- Bardur Arantsson <ba...@im...> <ba...@sc...> - Head of Safety? Five buttons? I'm in! Arnold Rimmer | Red Dwarf |