Jump to page: 1 2 3
Thread overview
New library: argparse, for parsing CLI arguments
Oct 13, 2021
Andrey Zherikov
Oct 13, 2021
Imperatorn
Oct 13, 2021
WebFreak001
Oct 13, 2021
Andrey Zherikov
Oct 13, 2021
WebFreak001
Oct 13, 2021
Andrey Zherikov
Oct 13, 2021
WebFreak001
Oct 13, 2021
Andrey Zherikov
Oct 17, 2021
Andrey Zherikov
Oct 19, 2021
Andrey Zherikov
Oct 19, 2021
Andrey Zherikov
Oct 13, 2021
SealabJaster
Oct 14, 2021
Andrey Zherikov
Oct 14, 2021
Andrey Zherikov
Oct 14, 2021
Bill Baxter
Oct 14, 2021
Andrey Zherikov
Oct 14, 2021
Bill Baxter
Oct 14, 2021
Andrey Zherikov
Oct 14, 2021
Paul Backus
Oct 14, 2021
Andrey Zherikov
Oct 19, 2021
Mathias LANG
Oct 19, 2021
Andrey Zherikov
October 13, 2021

Hi everyone,

I'm happy to announce that I've published a CLI argument parsing library - argparse. It's been around for some time already so please take a look and provide your feedback if you haven't done so.

The reasoning to create one more CLI parsing library is that the existing libraries do not provide enough flexibility in parsing that I'm looking for and/or they depend on other libraries. As a result argparse supports wide variety of data types including enums, callbacks and arrays as well as a fully customized argument parsing. It also doesn't depend on anything besides the standard library.

Since it's in active development (activeness depends on my availability of course), I have few things to do before making the first major release so stay tuned and/or contribute if you'd like to.

October 13, 2021

On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov wrote:

>

Hi everyone,

I'm happy to announce that I've published a CLI argument parsing library - argparse. It's been around for some time already so please take a look and provide your feedback if you haven't done so.

[...]

Oh, that's pretty nice actually

October 13, 2021

On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov wrote:

>

[...]
It also doesn't depend on anything besides the standard library.
[...]

if you want to drop the dependency on std.typecons : Nullable you could use https://code.dlang.org/packages/expected, where you can additionally return error values with instead of returning null on error.

Doesn't cast to bool but can check for .hasError which is more explicit.

October 13, 2021

On Wednesday, 13 October 2021 at 11:59:06 UTC, WebFreak001 wrote:

>

On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov wrote:

>

[...]
It also doesn't depend on anything besides the standard library.
[...]

if you want to drop the dependency on std.typecons : Nullable you could use https://code.dlang.org/packages/expected, where you can additionally return error values with instead of returning null on error.

Doesn't cast to bool but can check for .hasError which is more explicit.

This will break "doesn't depend on anything besides std" unfortunately.

October 13, 2021

On Wednesday, 13 October 2021 at 12:11:03 UTC, Andrey Zherikov wrote:

>

On Wednesday, 13 October 2021 at 11:59:06 UTC, WebFreak001 wrote:

>

On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov wrote:

>

[...]
It also doesn't depend on anything besides the standard library.
[...]

if you want to drop the dependency on std.typecons : Nullable you could use https://code.dlang.org/packages/expected, where you can additionally return error values with instead of returning null on error.

Doesn't cast to bool but can check for .hasError which is more explicit.

This will break "doesn't depend on anything besides std" unfortunately.

well... if you ask me that expected package should be part of the stdlib, it has decent download statistics too :p

At least would be better than creating your own Result type if you do decide to drop Nullable for something with error information I think

October 13, 2021

On 10/13/21 7:27 AM, Andrey Zherikov wrote:

>

Hi everyone,

I'm happy to announce that I've published a CLI argument parsing library - argparse. It's been around for some time already so please take a look and provide your feedback if you haven't done so.

The reasoning to create one more CLI parsing library is that the existing libraries do not provide enough flexibility in parsing that I'm looking for and/or they depend on other libraries. As a result argparse supports wide variety of data types including enums, callbacks and arrays as well as a fully customized argument parsing. It also doesn't depend on anything besides the standard library.

Since it's in active development (activeness depends on my availability of course), I have few things to do before making the first major release so stay tuned and/or contribute if you'd like to.

I was literally just thinking about how something like this would be useful yesterday. It's amazing when the universe just listens to me and provides what I was hoping for!

One nitpick -- you should be able to opt in using the name of the field member instead of having to write @NamedArgument. e.g. your string unused parameter requires a @NamedArgument("unused") which seems unnecessary.

-Steve

October 13, 2021

On Wednesday, 13 October 2021 at 14:36:30 UTC, Steven Schveighoffer wrote:

>

One nitpick -- you should be able to opt in using the name of the field member instead of having to write @NamedArgument. e.g. your string unused parameter requires a @NamedArgument("unused") which seems unnecessary.

I think unused word confuses a bit. I meant the argument that is skipped in command line so it has a default value ("some default value").
So having this is totally fine:

struct Params
{
    string s;

    @NamedArgument("num")
    int n;
}

I'll rename unused to remove this confusion.

October 13, 2021

On 10/13/21 11:50 AM, Andrey Zherikov wrote:

>

On Wednesday, 13 October 2021 at 14:36:30 UTC, Steven Schveighoffer wrote:

>

One nitpick -- you should be able to opt in using the name of the field member instead of having to write @NamedArgument. e.g. your string unused parameter requires a @NamedArgument("unused") which seems unnecessary.

I think unused word confuses a bit. I meant the argument that is skipped in command line so it has a default value ("some default value").
So having this is totally fine:

struct Params
{
     string s;

     @NamedArgument("num")
     int n;
}

I'll rename unused to remove this confusion.

No, it's not a confusion about unused. The array parameter has the same issue.

I meant that for named parameters, one shouldn't have to attribute them for them to be considered part of the parameters.

e.g. (to replace your current code):

struct Params
{
    // Positional arguments are required by default
    @PositionalArgument(0) // override the default of a named argument
    string name;

    // Named argments are optional by default
    string unused = "some default value";

    // Numeric types are converted automatically
    int num;

    // Boolean flags are supported
    bool flag;

    // Enums are also supported
    enum Enum { unset, foo, boo };
    @NamedArgument("enum") // required since enum is a keyword
    Enum enumValue;

    // Use array to store multiple values
    int[] array;

    // Callback with no args (flag)
    void cb() {}

    // Callback with single value
    void cb1(string value) { assert(value == "cb-value"); }

    // Callback with zero or more values
    void cb2(string[] value) { assert(value == ["cb-v1","cb-v2"]); }
}

The point is that I shouldn't have to tell the library the name of something that I've already given a name to.

Having them named differently on the command line than the actual field name should still be a possibility (and required in some cases, e.g. the enum case above), but honestly, the Params struct exists solely to accept command line parameters, there's no compelling need to use alternate names for the command line and the field name. If the library automatically does the right thing by default, then your code becomes simpler and more beautiful.

Not to detract from your library, because I think it's an awesome design to model using structs (one I use all the time), but the API developer in me frowns at lack of DRY. Try to focus on requiring the smallest amount of machinery/attributes possible. Every time you require extraneous pieces to get things to work, it adds another place where errors/confusion can happen.

-Steve

October 13, 2021

On Wednesday, 13 October 2021 at 16:24:52 UTC, Steven Schveighoffer wrote:

>

On 10/13/21 11:50 AM, Andrey Zherikov wrote:

>

On Wednesday, 13 October 2021 at 14:36:30 UTC, Steven [...]

No, it's not a confusion about unused. The array parameter has the same issue.

I meant that for named parameters, one shouldn't have to attribute them for them to be considered part of the parameters.

e.g. (to replace your current code):

struct Params
{
    // Positional arguments are required by default
    @PositionalArgument(0) // override the default of a named argument
    string name;

    // Named argments are optional by default
    string unused = "some default value";

    // Numeric types are converted automatically
    int num;

    // Boolean flags are supported
    bool flag;

    // Enums are also supported
    enum Enum { unset, foo, boo };
    @NamedArgument("enum") // required since enum is a keyword
    Enum enumValue;

    // Use array to store multiple values
    int[] array;

    // Callback with no args (flag)
    void cb() {}

    // Callback with single value
    void cb1(string value) { assert(value == "cb-value"); }

    // Callback with zero or more values
    void cb2(string[] value) { assert(value == ["cb-v1","cb-v2"]); }
}

The point is that I shouldn't have to tell the library the name of something that I've already given a name to.

Having them named differently on the command line than the actual field name should still be a possibility (and required in some cases, e.g. the enum case above), but honestly, the Params struct exists solely to accept command line parameters, there's no compelling need to use alternate names for the command line and the field name. If the library automatically does the right thing by default, then your code becomes simpler and more beautiful.

Not to detract from your library, because I think it's an awesome design to model using structs (one I use all the time), but the API developer in me frowns at lack of DRY. Try to focus on requiring the smallest amount of machinery/attributes possible. Every time you require extraneous pieces to get things to work, it adds another place where errors/confusion can happen.

-Steve

This should probably rather be:

struct Params
{
    // Positional arguments are required by default
    @PositionalArgument(0) // override the default of a named argument
    string name;

    // Named argments are optional by default
    @NamedArgument
    string unused = "some default value";

    // Numeric types are converted automatically
    @NamedArgument
    int num;

    // Boolean flags are supported
    @NamedArgument
    bool flag;

    // Enums are also supported
    enum Enum { unset, foo, boo };
    @NamedArgument("enum") // required since enum is a keyword
    Enum enumValue;

    // Use array to store multiple values
    @NamedArgument
    int[] array;

    // Callback with no args (flag)
    @NamedArgument
    void cb() {}

    // Callback with single value
    @NamedArgument
    void cb1(string value) { assert(value == "cb-value"); }

    // Callback with zero or more values
    @NamedArgument
    void cb2(string[] value) { assert(value == ["cb-v1","cb-v2"]); }
}

as otherwise the definition could be ambiguous (like are the parameters positional with automatic count or named by default?)

If you don't like the repetition you could also then make it @NamedArgument { [all my variables] } But I'm not a fan of having everything included even without UDA

October 13, 2021

On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov wrote:

>

...

Guess you got fed up with me not updating JCLI :3

Looks great either way, I really like what you've done with the .Parse!().PreValidate!() chaining, looks clean.

Also happy to see your penchant for documentation is still holding strong!

« First   ‹ Prev
1 2 3