From 991b4299d65da1b3bc1a4b17c2c72d0918162166 Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop Date: Sun, 3 Mar 2024 21:13:45 +0300 Subject: [PATCH] Update argparse. Otherwise .choices doesn't work properly. See https://github.com/p-ranav/argparse/issues/307 --- .../argparse/include/argparse/argparse.hpp | 15 ++- thirdparty/argparse/module/argparse.cppm | 3 +- thirdparty/argparse/test/test_choices.cpp | 68 ++++++++++++- .../argparse/test/test_const_correct.cpp | 27 ------ .../argparse/test/test_value_semantics.cpp | 96 ------------------- 5 files changed, 80 insertions(+), 129 deletions(-) delete mode 100644 thirdparty/argparse/test/test_const_correct.cpp delete mode 100644 thirdparty/argparse/test/test_value_semantics.cpp diff --git a/thirdparty/argparse/include/argparse/argparse.hpp b/thirdparty/argparse/include/argparse/argparse.hpp index 0c85127da4..ec06ba2376 100644 --- a/thirdparty/argparse/include/argparse/argparse.hpp +++ b/thirdparty/argparse/include/argparse/argparse.hpp @@ -30,11 +30,12 @@ SOFTWARE. */ #pragma once +#include + #ifndef ARGPARSE_MODULE_USE_STD_MODULE #include #include #include -#include #include #include #include @@ -657,7 +658,7 @@ public: } template - auto action(F &&callable, Args &&...bound_args) + auto action(F &&callable, Args &&... bound_args) -> std::enable_if_t, Argument &> { using action_type = std::conditional_t< @@ -783,7 +784,7 @@ public: } template - Argument &choices(T &&first, U &&...rest) { + Argument &choices(T &&first, U &&... rest) { add_choice(std::forward(first)); choices(std::forward(rest)...); return *this; @@ -845,8 +846,14 @@ public: if (m_choices.has_value()) { // Check each value in (start, end) and make sure // it is in the list of allowed choices/options + std::size_t i = 0; + auto max_number_of_args = m_num_args_range.get_max(); for (auto it = start; it != end; ++it) { + if (i == max_number_of_args) { + break; + } find_value_in_choices_or_throw(it); + i += 1; } } @@ -1546,7 +1553,7 @@ public: // Parameter packed add_parents method // Accepts a variadic number of ArgumentParser objects template - ArgumentParser &add_parents(const Targs &...f_args) { + ArgumentParser &add_parents(const Targs &... f_args) { for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) { for (const auto &argument : parent_parser.m_positional_arguments) { auto it = m_positional_arguments.insert( diff --git a/thirdparty/argparse/module/argparse.cppm b/thirdparty/argparse/module/argparse.cppm index 625283e1ca..8613b44dea 100644 --- a/thirdparty/argparse/module/argparse.cppm +++ b/thirdparty/argparse/module/argparse.cppm @@ -33,12 +33,13 @@ module; #ifndef ARGPARSE_MODULE_USE_STD_MODULE #include -#endif +#endif export module argparse; #ifdef ARGPARSE_MODULE_USE_STD_MODULE import std; +import std.compat; extern "C++" { #include diff --git a/thirdparty/argparse/test/test_choices.cpp b/thirdparty/argparse/test/test_choices.cpp index 678aba825d..4fc4383a0f 100644 --- a/thirdparty/argparse/test/test_choices.cpp +++ b/thirdparty/argparse/test/test_choices.cpp @@ -23,6 +23,72 @@ TEST_CASE("Parse argument that is in the fixed number of allowed choices" * program.parse_args({"test", "red"}); } +TEST_CASE("Parse argument that is in the fixed number of allowed choices, with " + "other positional argument" * + test_suite("choices")) { + argparse::ArgumentParser program("test"); + program.add_argument("--input") + .default_value(std::string{"baz"}) + .choices("foo", "bar", "baz"); + program.add_argument("--value").scan<'i', int>().default_value(0); + + REQUIRE_NOTHROW( + program.parse_args({"test", "--input", "foo", "--value", "1"})); + REQUIRE(program.get("--input") == "foo"); + REQUIRE(program.get("--value") == 1); +} + +TEST_CASE( + "Parse nargs argument that is in the fixed number of allowed choices, with " + "other positional argument" * + test_suite("choices")) { + argparse::ArgumentParser program("test"); + program.add_argument("--input") + .default_value(std::string{"baz"}) + .choices("foo", "bar", "baz") + .nargs(2); + program.add_argument("--value").scan<'i', int>().default_value(0); + + REQUIRE_NOTHROW( + program.parse_args({"test", "--input", "foo", "bar", "--value", "1"})); + REQUIRE((program.get>("--input") == + std::vector{"foo", "bar"})); + REQUIRE(program.get("--value") == 1); +} + +TEST_CASE("Parse argument that is in the fixed number of allowed choices, with " + "other positional argument (reversed)" * + test_suite("choices")) { + argparse::ArgumentParser program("test"); + program.add_argument("--input") + .default_value(std::string{"baz"}) + .choices("foo", "bar", "baz"); + program.add_argument("--value").scan<'i', int>().default_value(0); + + REQUIRE_NOTHROW( + program.parse_args({"test", "--value", "1", "--input", "foo"})); + REQUIRE(program.get("--input") == "foo"); + REQUIRE(program.get("--value") == 1); +} + +TEST_CASE( + "Parse nargs argument that is in the fixed number of allowed choices, with " + "other positional argument (reversed)" * + test_suite("choices")) { + argparse::ArgumentParser program("test"); + program.add_argument("--input") + .default_value(std::string{"baz"}) + .choices("foo", "bar", "baz") + .nargs(2); + program.add_argument("--value").scan<'i', int>().default_value(0); + + REQUIRE_NOTHROW( + program.parse_args({"test", "--value", "1", "--input", "foo", "bar"})); + REQUIRE((program.get>("--input") == + std::vector{"foo", "bar"})); + REQUIRE(program.get("--value") == 1); +} + TEST_CASE("Parse argument that is in the fixed number of allowed choices, with " "invalid default" * test_suite("choices")) { @@ -88,4 +154,4 @@ TEST_CASE("Parse multiple arguments that are not in fixed number of allowed " program.parse_args({"test", "6", "7"}), "Invalid argument \"6\" - allowed options: {1, 2, 3, 4, 5}", std::runtime_error); -} \ No newline at end of file +} diff --git a/thirdparty/argparse/test/test_const_correct.cpp b/thirdparty/argparse/test/test_const_correct.cpp deleted file mode 100644 index 1acfdd40b6..0000000000 --- a/thirdparty/argparse/test/test_const_correct.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include - -using doctest::test_suite; - -TEST_CASE("ArgumentParser is const-correct after construction and parsing" * - test_suite("value_semantics")) { - GIVEN("a parser") { - argparse::ArgumentParser parser("test"); - parser.add_argument("--foo", "-f").help("I am foo"); - parser.add_description("A description"); - parser.add_epilog("An epilog"); - - WHEN("becomes const-qualified") { - parser.parse_args({"./main", "--foo", "baz"}); - const auto const_parser = std::move(parser); - - THEN("only const methods are accessible") { - REQUIRE(const_parser.help().str().size() > 0); - REQUIRE(const_parser.present("--foo")); - REQUIRE(const_parser.is_used("-f")); - REQUIRE(const_parser.get("-f") == "baz"); - REQUIRE(const_parser["-f"] == std::string("baz")); - } - } - } -} diff --git a/thirdparty/argparse/test/test_value_semantics.cpp b/thirdparty/argparse/test/test_value_semantics.cpp deleted file mode 100644 index 2cc0f88777..0000000000 --- a/thirdparty/argparse/test/test_value_semantics.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include - -using doctest::test_suite; - -TEST_CASE("ArgumentParser is MoveConstructible and MoveAssignable" * - test_suite("value_semantics")) { - GIVEN("a parser that has two arguments") { - argparse::ArgumentParser parser("test"); - parser.add_argument("foo"); - parser.add_argument("-f"); - - WHEN("move construct a new parser from it") { - auto new_parser = std::move(parser); - - THEN("the old parser replaces the new parser") { - new_parser.parse_args({"test", "bar", "-f", "nul"}); - - REQUIRE(new_parser.get("foo") == "bar"); - REQUIRE(new_parser.get("-f") == "nul"); - } - } - - WHEN("move assign a parser prvalue to it") { - parser = argparse::ArgumentParser("test"); - - THEN("the old parser is replaced") { - REQUIRE_THROWS_AS(parser.parse_args({"test", "bar"}), - std::runtime_error); - REQUIRE_THROWS_AS(parser.parse_args({"test", "-f", "nul"}), - std::runtime_error); - REQUIRE_NOTHROW(parser.parse_args({"test"})); - } - } - } -} - -TEST_CASE("ArgumentParser is CopyConstructible and CopyAssignable" * - test_suite("value_semantics")) { - GIVEN("a parser that has two arguments") { - argparse::ArgumentParser parser("test"); - parser.add_argument("foo"); - parser.add_argument("-f"); - - WHEN("copy construct a new parser from it") { - auto new_parser = parser; - - THEN("the new parser has the old parser's capability") { - new_parser.parse_args({"test", "bar", "-f", "nul"}); - - REQUIRE(new_parser.get("foo") == "bar"); - REQUIRE(new_parser.get("-f") == "nul"); - - AND_THEN("but does not share states with the old parser") { - REQUIRE_THROWS_AS(parser.get("foo"), std::logic_error); - REQUIRE_THROWS_AS(parser.get("-f"), std::logic_error); - } - } - - AND_THEN("the old parser works as a distinct copy") { - new_parser.parse_args({"test", "toe", "-f", "/"}); - - REQUIRE(new_parser.get("foo") == "toe"); - REQUIRE(new_parser.get("-f") == "/"); - } - } - - WHEN("copy assign a parser lvalue to it") { - argparse::ArgumentParser optional_parser("test"); - optional_parser.add_argument("-g"); - parser = optional_parser; - - THEN("the old parser is replaced") { - REQUIRE_THROWS_AS(parser.parse_args({"test", "bar"}), - std::runtime_error); - REQUIRE_THROWS_AS(parser.parse_args({"test", "-f", "nul"}), - std::runtime_error); - REQUIRE_NOTHROW(parser.parse_args({"test"})); - REQUIRE_NOTHROW(parser.parse_args({"test", "-g", "nul"})); - - AND_THEN("but does not share states with the other parser") { - REQUIRE(parser.get("-g") == "nul"); - REQUIRE_THROWS_AS(optional_parser.get("-g"), std::logic_error); - } - } - - AND_THEN("the other parser works as a distinct copy") { - REQUIRE_NOTHROW(optional_parser.parse_args({"test"})); - REQUIRE_NOTHROW(optional_parser.parse_args({"test", "-g", "nul"})); - REQUIRE_THROWS_AS( - optional_parser.parse_args({"test", "bar", "-g", "nul"}), - std::runtime_error); - } - } - } -}