www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - optparse.d -- good enough to be std.optparse?

reply derick_eddington nospam.yashmoo.com writes:
I've made a command-line option helper similar to Python's optparse.  It allows
you to specify what D-type you want the option as, short name, long name, help,
and more.  It automatically creates a formatted help message and prints it on
errors.  It checks that clients' usage of the module is proper; and it checks
that the command-line arguments meet what was specified.  It has a compehensive
unittest that demonstrates more.  The source is attached.

: /**
:  * Example usage:
:  */
: int main (char[][] args)
: {
:     int verbosity;
:     char[] outfile;
:     char[][] files;
:
:     // Constructor parameters are used in printing help.
:     // -h / --help is always added by default
:     // and uses program-name and usage.
:     // --version (no short name) is added by
:     // default if version is given.
:     //                                   program-name
:     OptionParser op = new OptionParser(__FILE__[0..$-2],
:     //                 usage                version
:         "[options] --files=f1,fN [outfile]", "0.2");
:
:     // int, short and long, help, metavar,
:     // no default, not mandatory
:     op.addOption!(int)("v", "verbose",
:         "Be more and more and more and more and more "
:         "and more and more verbose.", "LEVEL");
:     // comma-separated string list, short and long, help,
:     // metavar, no default, mandatory
:     op.addOption!(char[][])("fs", "files",
:         "Files to process, and make this a really long help "
:         "string as well to test aligning.", "FILE,FILE,...",
:         null, true);
:     // float, long-only, help, metavar, default, not mandatory
:     op.addOption!(float)(null, "thing", "Do it with this.",
:         "FLOAT", -4.297e21f);
:     // no-value, short-only, with help, no metavar,
:     // no default, not mandatory
:     op.addOption!()("a", null, "Use alternate method.");
:
:     // Parse command-line arguments, including args[0]
:     // program-name (it's skipped).
:     // args will be reset to the remaining non-option
:     // command-line arguments in the order they appeared.
:     Options options = op.parse(args);
:
:     // Check if option was given on the command-line
:     // by using (char[] in Options).
:     // If the option has both short and long names,
:     // either can be used.
:     if ("verbose" in options)
:         // If option was given and it has a value,
:         // retrieve it by indexing Options with short
:         // or long name (or only one if only one)
:         // and do getValue!(T)
:         verbosity = options["verbose"].getValue!(int);
:
:     if (verbosity > 0)
:         writefln("I'll say a little.");
:     if (verbosity > 1)
:         writefln("I'll say a lot more.");
:
:     // args was reset by parse to the remaining
:     // non-option command-line arguments
:     if (args.length == 1)
:         // use the first non-option argument
:         outfile = args[0];
:     else if (args.length > 1)
:         // OptionError.errorExit("message") can be used to
:         // print help and exit process;
:         // useful when checking non-option arguments
:         op.errorExit("more than one output file specified");
:
:     // mandatory options can be assumed to be
:     // there after successful parse
:     assert ("files" in options);
:     files = options["files"].getValue!(char[][]);
:     if (files.length == 0)
:         op.errorExit("need to specifiy at least one input file");
:
:     if (verbosity > 0)
:     {
:         if ("thing" in options)
:             writefln("my thing is: ",
:                 options["thing"].getValue!(float));
:
:         // no-value options are simply tested for being in Options
:         if ("a" in options)
:             writefln("I would process files using alternate method:");
:         else
:             writefln("I would process files using normal method:");
:         foreach (char[] f; files)
:             writefln("\t",f);
:         writefln("and output to ",
:             outfile.length ? outfile : "<stdout>");
:     }
:
:     return 0;
: }

Example command-line output of above program:

---------------------------------------------------------------

$ ./example -h
usage:  optparse [options] --files=f1,fN [outfile]

options:
-h, --help            Show this help message and exit.
--version             Show program's version number and exit.
-vLEVEL, --verbose=LEVEL
Be more and more and more and more and more and more
and more verbose.
-fsFILE,FILE,..., --files=FILE,FILE,...
Files to process, and make this a really long help
string as well to test aligning.
--thing=FLOAT         Do it with this.
-a                    Use alternate method.

---------------------------------------------------------------

$ ./example -v2 --files=somefile,anotherfile,nextfile --thing -4.23e-6 -a
outputfile
I'll say a little.
I'll say a lot more.
my thing is: -4.23e-06
I would process files using alternate method:
somefile
anotherfile
nextfile
and output to outputfile

---------------------------------------------------------------

$ ./example --asdf
optparse: error: no such option: --asdf

[help]

---------------------------------------------------------------

$ ./example
optparse: error: -fs / --files option is mandatory

[help]
May 30 2005
next sibling parent derick_eddington nospam.yashmoo.com writes:
In article <d7g5nd$3i3$1 digitaldaemon.com>, derick_eddington nospam.yashmoo.com
says...
Example command-line output of above program:

---------------------------------------------------------------

$ ./example -h
usage:  optparse [options] --files=f1,fN [outfile]

options:
-h, --help            Show this help message and exit.
--version             Show program's version number and exit.
-vLEVEL, --verbose=LEVEL
Be more and more and more and more and more and more
and more verbose.
-fsFILE,FILE,..., --files=FILE,FILE,...
Files to process, and make this a really long help
string as well to test aligning.
--thing=FLOAT         Do it with this.
-a                    Use alternate method.

Arg, forgot to : these. The help print is nicely formatted...
May 30 2005
prev sibling next sibling parent reply "Kris" <fu bar.com> writes:
Very useful. I'd like to add this to Mango, if that would be OK?

- Kris

<derick_eddington nospam.yashmoo.com> wrote in message
news:d7g5nd$3i3$1 digitaldaemon.com...
 I've made a command-line option helper similar to Python's optparse.  It

 you to specify what D-type you want the option as, short name, long name,

 and more.  It automatically creates a formatted help message and prints it

 errors.  It checks that clients' usage of the module is proper; and it

 that the command-line arguments meet what was specified.  It has a

 unittest that demonstrates more.  The source is attached.

 : /**
 :  * Example usage:
 :  */
 : int main (char[][] args)
 : {
 :     int verbosity;
 :     char[] outfile;
 :     char[][] files;
 :
 :     // Constructor parameters are used in printing help.
 :     // -h / --help is always added by default
 :     // and uses program-name and usage.
 :     // --version (no short name) is added by
 :     // default if version is given.
 :     //                                   program-name
 :     OptionParser op = new OptionParser(__FILE__[0..$-2],
 :     //                 usage                version
 :         "[options] --files=f1,fN [outfile]", "0.2");
 :
 :     // int, short and long, help, metavar,
 :     // no default, not mandatory
 :     op.addOption!(int)("v", "verbose",
 :         "Be more and more and more and more and more "
 :         "and more and more verbose.", "LEVEL");
 :     // comma-separated string list, short and long, help,
 :     // metavar, no default, mandatory
 :     op.addOption!(char[][])("fs", "files",
 :         "Files to process, and make this a really long help "
 :         "string as well to test aligning.", "FILE,FILE,...",
 :         null, true);
 :     // float, long-only, help, metavar, default, not mandatory
 :     op.addOption!(float)(null, "thing", "Do it with this.",
 :         "FLOAT", -4.297e21f);
 :     // no-value, short-only, with help, no metavar,
 :     // no default, not mandatory
 :     op.addOption!()("a", null, "Use alternate method.");
 :
 :     // Parse command-line arguments, including args[0]
 :     // program-name (it's skipped).
 :     // args will be reset to the remaining non-option
 :     // command-line arguments in the order they appeared.
 :     Options options = op.parse(args);
 :
 :     // Check if option was given on the command-line
 :     // by using (char[] in Options).
 :     // If the option has both short and long names,
 :     // either can be used.
 :     if ("verbose" in options)
 :         // If option was given and it has a value,
 :         // retrieve it by indexing Options with short
 :         // or long name (or only one if only one)
 :         // and do getValue!(T)
 :         verbosity = options["verbose"].getValue!(int);
 :
 :     if (verbosity > 0)
 :         writefln("I'll say a little.");
 :     if (verbosity > 1)
 :         writefln("I'll say a lot more.");
 :
 :     // args was reset by parse to the remaining
 :     // non-option command-line arguments
 :     if (args.length == 1)
 :         // use the first non-option argument
 :         outfile = args[0];
 :     else if (args.length > 1)
 :         // OptionError.errorExit("message") can be used to
 :         // print help and exit process;
 :         // useful when checking non-option arguments
 :         op.errorExit("more than one output file specified");
 :
 :     // mandatory options can be assumed to be
 :     // there after successful parse
 :     assert ("files" in options);
 :     files = options["files"].getValue!(char[][]);
 :     if (files.length == 0)
 :         op.errorExit("need to specifiy at least one input file");
 :
 :     if (verbosity > 0)
 :     {
 :         if ("thing" in options)
 :             writefln("my thing is: ",
 :                 options["thing"].getValue!(float));
 :
 :         // no-value options are simply tested for being in Options
 :         if ("a" in options)
 :             writefln("I would process files using alternate method:");
 :         else
 :             writefln("I would process files using normal method:");
 :         foreach (char[] f; files)
 :             writefln("\t",f);
 :         writefln("and output to ",
 :             outfile.length ? outfile : "<stdout>");
 :     }
 :
 :     return 0;
 : }

 Example command-line output of above program:

 ---------------------------------------------------------------

 $ ./example -h
 usage:  optparse [options] --files=f1,fN [outfile]

 options:
 -h, --help            Show this help message and exit.
 --version             Show program's version number and exit.
 -vLEVEL, --verbose=LEVEL
 Be more and more and more and more and more and more
 and more verbose.
 -fsFILE,FILE,..., --files=FILE,FILE,...
 Files to process, and make this a really long help
 string as well to test aligning.
 --thing=FLOAT         Do it with this.
 -a                    Use alternate method.

 ---------------------------------------------------------------

 $ ./example -v2 --files=somefile,anotherfile,nextfile --thing -4.23e-6 -a
 outputfile
 I'll say a little.
 I'll say a lot more.
 my thing is: -4.23e-06
 I would process files using alternate method:
 somefile
 anotherfile
 nextfile
 and output to outputfile

 ---------------------------------------------------------------

 $ ./example --asdf
 optparse: error: no such option: --asdf

 [help]

 ---------------------------------------------------------------

 $ ./example
 optparse: error: -fs / --files option is mandatory

 [help]

May 30 2005
parent reply derick_eddington nospam.yashmoo.com writes:
Certainly.  I'll keep you updated as I enhance and fix bugs.

In article <d7g9q7$7b3$1 digitaldaemon.com>, Kris says...
Very useful. I'd like to add this to Mango, if that would be OK?

- Kris

<derick_eddington nospam.yashmoo.com> wrote in message
news:d7g5nd$3i3$1 digitaldaemon.com...
 I've made a command-line option helper similar to Python's optparse.  It

 you to specify what D-type you want the option as, short name, long name,

 and more.  It automatically creates a formatted help message and prints it

 errors.  It checks that clients' usage of the module is proper; and it

 that the command-line arguments meet what was specified.  It has a

 unittest that demonstrates more.  The source is attached.


May 30 2005
parent reply kris <fu bar.org> writes:
derick_eddington nospam.yashmoo.com wrote:
 Certainly.  I'll keep you updated as I enhance and fix bugs.

Excellent ~ thx!
May 30 2005
next sibling parent James Dunne <james.jdunne gmail.com> writes:
In article <d7gqjo$nth$2 digitaldaemon.com>, kris says...
derick_eddington nospam.yashmoo.com wrote:
 Certainly.  I'll keep you updated as I enhance and fix bugs.


Who doesn't love to enhance bugs? =P
Excellent ~ thx!

Regards, James Dunne
May 31 2005
prev sibling parent derick_eddington nospam.yashmoo.com writes:
I've updated optparse.d:

Option groups for help printout.
Example:

: optionParser = new OptionParser("myapp", "[option [value]]");
: optionParser.addOption!()("v", "verbose");
: optionParser.addOption!(float)("f", "foo", null, null, null, true);
: optionParser.addOption!(int)("x", "xeno", "does this and that", "THING");
: optionParser.addOption!(int[])("a", "apple", "make applesauce", null);
: optionParser.addOption!(long)("l", "long", "be longer", "LONG");
: optionParser.addOption!(double)("d", "double", null, "DOUBLE");
: optionParser.addOption!(uint[])("ui", "uint", null, "UINT,...");
: // make a group, options must have previously been added
: optionParser.makeGroup("number options", "f", "x", "a", "l", "d", "ui");
: optionParser.addOption!(char[])(null, "bar", "make it happen", "BLOB");
: optionParser.addOption!(char[][])("sb", "shabang", "blow up", "IT");
: // make a group, options must have previously been added
: optionParser.makeGroup("string options", "bar", "sb");

Will make help:

: usage:  myapp [option [value]]
:
: options:
:   -h, --help            Show this help message and exit.
:   --version             Show program's version number and exit.
:   -v, --verbose
:
: number options:
:   -f, --foo
:   -x THING, --xeno=THING
:                         does this and that
:   -a, --apple           make applesauce
:   -l LONG, --long=LONG
:                         be longer
:   -d DOUBLE, --double=DOUBLE
:   -ui UINT,..., --uint=UINT,...
:
: string options:
:   --bar=BLOB            make it happen
:   -sb IT, --shabang=IT
:                         blow up

Help string formatting separates short-name from METAVAR by a space for easier
reading.

Smarter use of templates to support more types.

Bug *un-enhanced* :) where "--option= --next-opt" would consume --next-opt as
--option's value instead of causing error because --option has no value.

A few more unittest cases.

Question for potential users of optparse.d:
Would you rather have anal checking of all usage of the module or reduce the
object-file size from 55k to 35k? I tried to use "version" but it's too buggy (I
just wanted version'd else-if...)

--Derick
Jun 01 2005
prev sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
Cool, Derick!

What about using box values there?

Having them and use TypeList instead of

  optionParser = new OptionParser(__FILE__[0..$-2], "[option [value]]", 
"0.0");
  optionParser.addOption!()("v","verbose");
  optionParser.addOption!(float)("f","foo",null,null,null,true);
  optionParser.addOption!(int)("x","xeno","does this and that","THING",123);

will allow to build such declarations in compile time - statically.

Just hypothesis....

Andrew.



<derick_eddington nospam.yashmoo.com> wrote in message 
news:d7g5nd$3i3$1 digitaldaemon.com...
 I've made a command-line option helper similar to Python's optparse.  It 
 allows
 you to specify what D-type you want the option as, short name, long name, 
 help,
 and more.  It automatically creates a formatted help message and prints it 
 on
 errors.  It checks that clients' usage of the module is proper; and it 
 checks
 that the command-line arguments meet what was specified.  It has a 
 compehensive
 unittest that demonstrates more.  The source is attached.

 : /**
 :  * Example usage:
 :  */
 : int main (char[][] args)
 : {
 :     int verbosity;
 :     char[] outfile;
 :     char[][] files;
 :
 :     // Constructor parameters are used in printing help.
 :     // -h / --help is always added by default
 :     // and uses program-name and usage.
 :     // --version (no short name) is added by
 :     // default if version is given.
 :     //                                   program-name
 :     OptionParser op = new OptionParser(__FILE__[0..$-2],
 :     //                 usage                version
 :         "[options] --files=f1,fN [outfile]", "0.2");
 :
 :     // int, short and long, help, metavar,
 :     // no default, not mandatory
 :     op.addOption!(int)("v", "verbose",
 :         "Be more and more and more and more and more "
 :         "and more and more verbose.", "LEVEL");
 :     // comma-separated string list, short and long, help,
 :     // metavar, no default, mandatory
 :     op.addOption!(char[][])("fs", "files",
 :         "Files to process, and make this a really long help "
 :         "string as well to test aligning.", "FILE,FILE,...",
 :         null, true);
 :     // float, long-only, help, metavar, default, not mandatory
 :     op.addOption!(float)(null, "thing", "Do it with this.",
 :         "FLOAT", -4.297e21f);
 :     // no-value, short-only, with help, no metavar,
 :     // no default, not mandatory
 :     op.addOption!()("a", null, "Use alternate method.");
 :
 :     // Parse command-line arguments, including args[0]
 :     // program-name (it's skipped).
 :     // args will be reset to the remaining non-option
 :     // command-line arguments in the order they appeared.
 :     Options options = op.parse(args);
 :
 :     // Check if option was given on the command-line
 :     // by using (char[] in Options).
 :     // If the option has both short and long names,
 :     // either can be used.
 :     if ("verbose" in options)
 :         // If option was given and it has a value,
 :         // retrieve it by indexing Options with short
 :         // or long name (or only one if only one)
 :         // and do getValue!(T)
 :         verbosity = options["verbose"].getValue!(int);
 :
 :     if (verbosity > 0)
 :         writefln("I'll say a little.");
 :     if (verbosity > 1)
 :         writefln("I'll say a lot more.");
 :
 :     // args was reset by parse to the remaining
 :     // non-option command-line arguments
 :     if (args.length == 1)
 :         // use the first non-option argument
 :         outfile = args[0];
 :     else if (args.length > 1)
 :         // OptionError.errorExit("message") can be used to
 :         // print help and exit process;
 :         // useful when checking non-option arguments
 :         op.errorExit("more than one output file specified");
 :
 :     // mandatory options can be assumed to be
 :     // there after successful parse
 :     assert ("files" in options);
 :     files = options["files"].getValue!(char[][]);
 :     if (files.length == 0)
 :         op.errorExit("need to specifiy at least one input file");
 :
 :     if (verbosity > 0)
 :     {
 :         if ("thing" in options)
 :             writefln("my thing is: ",
 :                 options["thing"].getValue!(float));
 :
 :         // no-value options are simply tested for being in Options
 :         if ("a" in options)
 :             writefln("I would process files using alternate method:");
 :         else
 :             writefln("I would process files using normal method:");
 :         foreach (char[] f; files)
 :             writefln("\t",f);
 :         writefln("and output to ",
 :             outfile.length ? outfile : "<stdout>");
 :     }
 :
 :     return 0;
 : }

 Example command-line output of above program:

 ---------------------------------------------------------------

 $ ./example -h
 usage:  optparse [options] --files=f1,fN [outfile]

 options:
 -h, --help            Show this help message and exit.
 --version             Show program's version number and exit.
 -vLEVEL, --verbose=LEVEL
 Be more and more and more and more and more and more
 and more verbose.
 -fsFILE,FILE,..., --files=FILE,FILE,...
 Files to process, and make this a really long help
 string as well to test aligning.
 --thing=FLOAT         Do it with this.
 -a                    Use alternate method.

 ---------------------------------------------------------------

 $ ./example -v2 --files=somefile,anotherfile,nextfile --thing -4.23e-6 -a
 outputfile
 I'll say a little.
 I'll say a lot more.
 my thing is: -4.23e-06
 I would process files using alternate method:
 somefile
 anotherfile
 nextfile
 and output to outputfile

 ---------------------------------------------------------------

 $ ./example --asdf
 optparse: error: no such option: --asdf

 [help]

 ---------------------------------------------------------------

 $ ./example
 optparse: error: -fs / --files option is mandatory

 [help]



 

May 31 2005
parent reply derick_eddington nospam.yashmoo.com writes:
If it could be setup statically at compile-time and have the same conciseness,
flexibility, and ease of checking for options and retrieving their values, that
would be awesome, but I don't follow what you mean about box values and
TypeList.  Could you elaborate?

The only way to parameterize something at compile-time is with templates, right?
But they can only take integral values, not char[] for option names or arbitrary
types for default values.  Statically-initialized structs could be used but then
they still have to be given to an OptionParser and variables for them would also
need to be declared.  I don't think it can be done and retain the same degree of
simpleness of use.


In article <d7iifp$2jhh$1 digitaldaemon.com>, Andrew Fedoniouk says...
Cool, Derick!

What about using box values there?

Having them and use TypeList instead of

  optionParser = new OptionParser(__FILE__[0..$-2], "[option [value]]", 
"0.0");
  optionParser.addOption!()("v","verbose");
  optionParser.addOption!(float)("f","foo",null,null,null,true);
  optionParser.addOption!(int)("x","xeno","does this and that","THING",123);

will allow to build such declarations in compile time - statically.

Just hypothesis....

Andrew.

Jun 01 2005
parent "Andrew Fedoniouk" <news terrainformatica.com> writes:
<derick_eddington nospam.yashmoo.com> wrote in message 
news:d7jvl1$1420$1 digitaldaemon.com...
 If it could be setup statically at compile-time and have the same 
 conciseness,
 flexibility, and ease of checking for options and retrieving their values, 
 that
 would be awesome, but I don't follow what you mean about box values and
 TypeList.  Could you elaborate?

 The only way to parameterize something at compile-time is with templates, 
 right?
 But they can only take integral values, not char[] for option names or 
 arbitrary
 types for default values.  Statically-initialized structs could be used 
 but then
 they still have to be given to an OptionParser and variables for them 
 would also
 need to be declared.  I don't think it can be done and retain the same 
 degree of
 simpleness of use.

Yep. You are right. I was thinking about simple implementation of something close to boost::spirit http://spirit.sourceforge.net/ but it seems like even simple implementation is an overkill for such task. Andrew.
 In article <d7iifp$2jhh$1 digitaldaemon.com>, Andrew Fedoniouk says...
Cool, Derick!

What about using box values there?

Having them and use TypeList instead of

  optionParser = new OptionParser(__FILE__[0..$-2], "[option [value]]",
"0.0");
  optionParser.addOption!()("v","verbose");
  optionParser.addOption!(float)("f","foo",null,null,null,true);
  optionParser.addOption!(int)("x","xeno","does this and 
 that","THING",123);

will allow to build such declarations in compile time - statically.

Just hypothesis....


Andrew.


Jun 01 2005