Skip to content

optenv with single argument not supported in eval expression #187

@romainreignier

Description

@romainreignier

According to the documentation, the optenv substitution argument can be used without the default_value supplied.
In an eval expression, the substitution arguments are converted into Python functions.
In rosmon, the optenv is defined as a function with two arguments using make_handler2 here:

local["optenv"] = make_handler2(substitutions::optenv);

If optenv is used with a single argument, we have the following error:

Could not load launch file: /home/rre/open_mower_ros/src/open_mower/launch/include/_gps.launch:14: Substitution error: Caught Python exception while evaluating $(eval optenv('OM_GPS_BAUDRATE')!=''):
ArgumentError: Python argument types in
    None.None(str)
did not match C++ signature:
    None(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)

This usage has been found in this open source project.

The following unit test allows to catch it:

diff --git a/rosmon_core/test/xml/test_subst.cpp b/rosmon_core/test/xml/test_subst.cpp
index 5044769..0ee92e3 100644
--- a/rosmon_core/test/xml/test_subst.cpp
+++ b/rosmon_core/test/xml/test_subst.cpp
@@ -302,6 +302,21 @@ TEST_CASE("eval", "[subst]")
                CHECK(value == 2*20);
        }
 
+       SECTION("optenv single argument")
+       {
+               LaunchConfig config;
+               config.parseString(R"EOF(
+                       <launch>
+                               <param name="test" value="$(eval 'test' + optenv('PATH'))"/>
+                       </launch>
+               )EOF");
+
+               config.evaluateParameters();
+
+               auto value = getTypedParam<std::string>(config.parameters(), "/test");
+               CHECK(value == std::string("test") + getenv("PATH"));
+       }
+
        SECTION("python errors")
        {
                using Catch::Matchers::Contains;

With the following output:

$ ./test_xml_loading eval
Filters: eval
Loaded launch file in 0.053113s
Loaded launch file in 0.057872s
Loaded launch file in 0.000252s
Loaded launch file in 0.000169s

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test_xml_loading is a Catch v2.13.7 host application.
Run with -? for options

-------------------------------------------------------------------------------
eval
  optenv single argument
-------------------------------------------------------------------------------
/home/rre/open_mower_ros/src/rosmon/rosmon_core/test/xml/test_subst.cpp:305
...............................................................................

/home/rre/open_mower_ros/src/rosmon/rosmon_core/test/xml/test_subst.cpp:305: FAILED:
due to unexpected exception with message:
  [string]:3: Substitution error: Caught Python exception while evaluating $
  (eval 'test' + optenv('PATH')):
  ArgumentError: Python argument types in
      None.None(str)
  did not match C++ signature:
      None(std::__cxx11::basic_string<char, std::char_traits<char>, std::
  allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>,
  std::allocator<char> >)

Loaded launch file in 0.000046s
===============================================================================
test cases:  1 |  0 passed | 1 failed
assertions: 13 | 12 passed | 1 failed

To resolve the issue, we need to find how to have a variable number of arguments for a Python function with Boost::Python, like *args.

Edit: Overloading and default arguments is actually part of the BoostPython tutorial.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions