Skip to content
This repository has been archived by the owner on Jan 19, 2024. It is now read-only.

fix for parsing error handling of an empty command argument #48

Merged
merged 4 commits into from
Apr 27, 2023

Conversation

jolly-fellow
Copy link
Contributor

Example: antler-proj build ~/path/proj ""
corresponding to issue #19

Copy link
Contributor

@mikelik mikelik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it's a hack, but it works. I would just simplify catching the exception

tools/main.cpp Outdated
Comment on lines 77 to 80
catch (CLI::ExtrasError &e) {
if (std::string_view{"The following argument was not expected: "} == e.what()) {
return app.exit(CLI::ExtrasError("Empty argument is encountered in the command line", e.get_exit_code()));
}
Copy link
Contributor

@mikelik mikelik Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to https://github.com/AntelopeIO/antler-proj/blob/main/tools/CLI11.hpp#L902 ExtrasError always contain either The following arguments were or The following arguments was

Suggested change
catch (CLI::ExtrasError &e) {
if (std::string_view{"The following argument was not expected: "} == e.what()) {
return app.exit(CLI::ExtrasError("Empty argument is encountered in the command line", e.get_exit_code()));
}
catch (const CLI::ExtrasError& e) {
return app.exit(CLI::ExtrasError("Empty argument is encountered in the command line", e.get_exit_code()));
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to https://github.com/AntelopeIO/antler-proj/blob/main/tools/CLI11.hpp#L902 ExtrasError always contain either The following arguments were or The following arguments was

But we should not change the behavior in case if this exception thrown for non empty arguments. In this case all these arguments should be printed as "The following arguments were not expected: arg1 arg2 etc." or "The following argument was not expected: arg"
We should change the message only in case if it prints "The following argument was not expected: " which happens only if we have one "" (empty argument) in the command line.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. This is bigger hack than I thought.
Isn't there any other way to fix it?
I.e. checking arguments before parsing if any of them is ""

Copy link
Contributor

@ScottBailey ScottBailey Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are "extra" arguments - so they aren't caught by the standard validators.

I've suggested a change (below) that could address this.

Copy link
Contributor Author

@jolly-fellow jolly-fellow Apr 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. This is bigger hack than I thought. Isn't there any other way to fix it? I.e. checking arguments before parsing if any of them is ""

This is the best solution which I can invent without changing of the CLI11.hpp code.
The problem is not with empty arguments of options, the problem with empty options.
The parser recognize the subcommand, then find all its options and their arguments, and all other text which it can find the parser consider as "extra" arguments, i.e. trash which it shows in the text of the exception when prints the error. All it works if these extra arguments have symbols. But it seems authors of the parser don't know that it possible to declare an empty argument in the command line, so the parser just don't handle this case at all and print nothing instead of name of an argument. In other words everything which is not looks like a known subcommand or option of a subcommand or a parameter of an option considers as an "extra" argument.

tools/main.cpp Outdated
return app.exit(CLI::ExtrasError("Empty argument is encountered in the command line", e.get_exit_code()));
}
}
catch (const CLI::ParseError &e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other references in code are similar to this:

Suggested change
catch (const CLI::ParseError &e) {
catch (const CLI::ParseError& e) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - @mikelik is suggesting the "correct" style.

@jolly-fellow jolly-fellow marked this pull request as ready for review April 25, 2023 14:29
tools/main.cpp Outdated
Comment on lines 76 to 81
// This hack fix parsing error handling of an empty command argument. Example: antler-proj build ~/path/proj ""
catch (CLI::ExtrasError &e) {
if (std::string_view{"The following argument was not expected: "} == e.what()) {
return app.exit(CLI::ExtrasError("Empty argument is encountered in the command line", e.get_exit_code()));
}
}
Copy link
Contributor

@ScottBailey ScottBailey Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing an app.exit(e).

Also, I agree with @mikelik - we should handle both "was" and "were". Here's a possibility:

Suggested change
// This hack fix parsing error handling of an empty command argument. Example: antler-proj build ~/path/proj ""
catch (CLI::ExtrasError &e) {
if (std::string_view{"The following argument was not expected: "} == e.what()) {
return app.exit(CLI::ExtrasError("Empty argument is encountered in the command line", e.get_exit_code()));
}
}
// Handle empty command argument. Example: `antler-proj build ~/path/proj ""`
catch (CLI::ExtrasError& e) {
std::string what = e.what();
// trim space
while(!what.empty() && what.back() == ' ')
what.pop_back();
if( what == "The following argument was not expected:" || what == "The following arguments were not expected:") {
return app.exit(CLI::ExtrasError("Empty argument(s) encountered in the command line.", e.get_exit_code()));
}
app.exit(e);
}

Copy link
Contributor

@dimas1185 dimas1185 Apr 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scott is right, if one would pass 2+ empty arguments (which is awkward but possible due to errors in scripts) - it will fail.
But his solution seems to me too verbose and hard to understand from glance. here is mine:

Suggested change
// This hack fix parsing error handling of an empty command argument. Example: antler-proj build ~/path/proj ""
catch (CLI::ExtrasError &e) {
if (std::string_view{"The following argument was not expected: "} == e.what()) {
return app.exit(CLI::ExtrasError("Empty argument is encountered in the command line", e.get_exit_code()));
}
}
// This hack fix parsing error handling of an empty command argument. Example: antler-proj build ~/path/proj ""
catch (CLI::ExtrasError &e) {
const std::regex rex("The following argument(s)? (was|were)? not expected:\\s+$", std::regex_constants::ECMAScript);
if (std::regex_search(e.what(), rex)) {
return app.exit(CLI::ExtrasError("Unexpected empty argument is encountered in the command line", e.get_exit_code()));
}
}

Copy link
Contributor Author

@jolly-fellow jolly-fellow Apr 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scott is right, if one would pass 2+ empty arguments (which is awkward but possible due to errors in scripts) - it will fail. But his solution seems to me too verbose and hard to understand from glance. here is mine:

I'm not against of such comprehensive check but if you try to test the cases with more than one empty argument you will see that they processed by the parser earlier and as I understand such exception will be never thrown for antler-proj command syntax.
And I prefer @mikelik and @ScottBailey solutions. Don't want to include a complex library for such simple cases.

Copy link
Contributor

@ScottBailey ScottBailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must add app.exit(e) to correctly report other errors. Should behave the same when 1 empty argument or 2+ are provided.

Where's the test? A simple one could be added in tools/tests.

@BenjaminGormanPMP BenjaminGormanPMP linked an issue Apr 26, 2023 that may be closed by this pull request
@@ -57,7 +57,7 @@ int main(int argc, char** argv) {
app.add_flag_callback("-V,--version",
[&app]() {
std::cout << app.get_name() << " v" << antler::system::version::full() << std::endl;
std::exit(0); // Succesfull exit MUST happen here.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now I feel relief that native English speakers also mess double letters in that word lol

@jolly-fellow
Copy link
Contributor Author

I have added @dimas1185 update from main to avoid a merge collision.

tools/main.cpp Outdated Show resolved Hide resolved
Co-authored-by: mikelik <mikelik@users.noreply.github.com>
Copy link
Contributor

@ScottBailey ScottBailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@ScottBailey ScottBailey merged commit 83b0f1b into main Apr 27, 2023
@ScottBailey ScottBailey deleted the empty_param_error_fix branch April 27, 2023 16:09
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Incorrect processing of an empty parameter.
4 participants