Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug when setting launch arguments of included descriptions #749

Open
jdlangs opened this issue Dec 14, 2023 · 3 comments
Open

Bug when setting launch arguments of included descriptions #749

jdlangs opened this issue Dec 14, 2023 · 3 comments
Assignees

Comments

@jdlangs
Copy link

jdlangs commented Dec 14, 2023

Bug report

Required Info:

  • Operating System:
    • Ubuntu
  • Installation type:
    • Source
  • Version or commit hash:
    • rolling

Steps to reproduce issue

Build this package and run ros2 launch launch_args_ex top.launch.py bottom1:=true bottom2:=true

top.launch.py

import os
from ament_index_python import get_package_share_directory
import launch
from launch.actions import IncludeLaunchDescription

def generate_launch_description():
    return launch.LaunchDescription([
        IncludeLaunchDescription(
            os.path.join(
                get_package_share_directory('launch_args_ex'),
                'launch',
                'bottom.launch.py',
            ),
        ),
    ])

bottom.launch.py

import launch
from launch.actions import DeclareLaunchArgument

def generate_launch_description():
    return launch.LaunchDescription([
        DeclareLaunchArgument("bottom1", description="Inner argument 1"),
        DeclareLaunchArgument("bottom2", description="Inner argument 2"),
    ])

Expected behavior

The bottom1 and bottom2 arguments receive values passed from the command-line.

Actual behavior

❯ ros2 launch launch_args_ex top.launch.py bottom1:=true bottom2:=true                                                                                                                     
[INFO] [launch]: All log files can be found below ...
[INFO] [launch]: Default logging verbosity is set to INFO
[ERROR] [launch]: Caught exception in launch (see debug for traceback): Included launch description missing required argument 'bottom1' (description: 'Inner argument 1'), given: []

What's weird is if you don't set one of the args, it now sees that the other one was given:

❯ ros2 launch launch_args_ex top.launch.py bottom2:=true                                                                                                                                                           ─╯
[INFO] [launch]: All log files can be found below ...
[INFO] [launch]: Default logging verbosity is set to INFO
[ERROR] [launch]: Caught exception in launch (see debug for traceback): Included launch description missing required argument 'bottom1' (description: 'Inner argument 1'), given: [bottom2]

Here's a full debug output as well:

❯ ros2 launch launch_args_ex top.launch.py bottom1:=true bottom2:=true -d                                                                                                                                          ─╯
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.IncludeLaunchDescription'
[INFO] [launch]: All log files can be found below ...
[INFO] [launch]: Default logging verbosity is set to DEBUG
[DEBUG] [launch]: processing event: '<launch.events.include_launch_description.IncludeLaunchDescription object at 0x7fe55b1ae700>'
[DEBUG] [launch]: processing event: '<launch.events.include_launch_description.IncludeLaunchDescription object at 0x7fe55b1ae700>' ✓ '<launch.event_handlers.on_include_launch_description.OnIncludeLaunchDescription object at 0x7fe55b183f10>'
[DEBUG] [launch]: An exception was raised in an async action/event
[DEBUG] [launch]: Traceback (most recent call last):
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/launch_service.py", line 337, in run_async
    raise completed_tasks_exceptions[0]
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/launch_service.py", line 230, in _process_one_event
    await self.__process_event(next_event)
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/launch_service.py", line 250, in __process_event
    visit_all_entities_and_collect_futures(entity, self.__context))
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  [Previous line repeated 1 more time]
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 38, in visit_all_entities_and_collect_futures
    sub_entities = entity.visit(context)
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/action.py", line 109, in visit
    return self.execute(context)
  File "/home/josh/ros2_dev/install/launch/lib/python3.8/site-packages/launch/actions/include_launch_description.py", line 173, in execute
    raise RuntimeError(
RuntimeError: Included launch description missing required argument 'bottom1' (description: 'Inner argument 1'), given: []

[ERROR] [launch]: Caught exception in launch (see debug for traceback): Included launch description missing required argument 'bottom1' (description: 'Inner argument 1'), given: []
[DEBUG] [launch.launch_context]: emitting event: 'launch.events.Shutdown'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7fe55b183df0>'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7fe55b183df0>' ✓ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7fe55b183430>'
@sloretz
Copy link
Contributor

sloretz commented Dec 26, 2023

@methylDragon Would you be willing to look into the behavior and figure out where the bug is? 🧇

It seems like passing launch_arguments to IncludeLaunchDescription in top.launch.py might be part of the solution, but if I understand correctly that wouldn't explain why setting bottom2 only seems to have been recognized by bottom.launch.py.

@jellehierck
Copy link

As a workaround I currently push required arguments explicitly to included launch files in top.launch.py.

IncludeLaunchDescription(
    PythonLaunchDescriptionSource(
        [
            str(Path(get_package_share_directory("launch_args_ex")) / "launch" / "bottom.launch.py"),
        ]
    ),
    launch_arguments={"bottom1": LaunchConfiguration("bottom1"), "bottom2": LaunchConfiguration("bottom2")}.items(),  # <-- Push required arguments explicitly
)

Starting the launch file with ros2 launch launch_args_ex top.launch.py bottom1:=true bottom2:=true now works.

I would much rather that the required arguments check takes into account the context of the top launch file automatically.

@jellehierck
Copy link

After sleeping a night on this and doing some testing this morning, I want to make some remarks on my previous comment.

As a workaround I currently push required arguments explicitly to included launch files in top.launch.py.

IncludeLaunchDescription(
    PythonLaunchDescriptionSource(
        [
            str(Path(get_package_share_directory("launch_args_ex")) / "launch" / "bottom.launch.py"),
        ]
    ),
    launch_arguments={"bottom1": LaunchConfiguration("bottom1"), "bottom2": LaunchConfiguration("bottom2")}.items(),  # <-- Push required arguments explicitly
)

Starting the launch file with ros2 launch launch_args_ex top.launch.py bottom1:=true bottom2:=true now works.

A caveat with this approach is that you will get a different, less descriptive error message if you do not start the launch file with one of the required arguments:

Running ros2 launch launch_args_ex top.launch.py using top.launch.py from the original bug report (without workaround) gives a description of the launch argument:

[ERROR] [launch]: Caught exception in launch (see debug for traceback): Included launch description missing required argument 'bottom1' (description: 'Inner argument 1'), given: []

Running ros2 launch launch_args_ex top.launch.py with my workaround gives a non-descriptive error message:

[ERROR] [launch]: Caught exception in launch (see debug for traceback): launch configuration 'bottom1' does not exist

Something to be aware of when using the workaround.

I would much rather that the required arguments check takes into account the context of the top launch file automatically.

Actually, after sleeping a night on this I do not think I completely agree with myself anymore. I guess my main confusion is how the required arguments check seems to behave differently from how the arguments are evaluated.

If we use top.launch.py from the original bug report but adjust bottom.launch.py to have default values (and a LogInfo to print the argument values):

# bottom.launch.py
import launch
from launch.actions import DeclareLaunchArgument, LogInfo
from launch.substitutions import LaunchConfiguration

def generate_launch_description():
    return launch.LaunchDescription(
        [
            DeclareLaunchArgument("bottom1", description="Inner argument 1", default_value="false"),
            DeclareLaunchArgument("bottom2", description="Inner argument 2", default_value="false"),
            LogInfo(msg=["bottom1 = ", LaunchConfiguration("bottom1")]),
            LogInfo(msg=["bottom2 = ", LaunchConfiguration("bottom2")]),
        ]
    )

Then running ros2 launch launch_args_ex top.launch.py gives the following result (as expected):

[INFO] [launch.user]: bottom1 = false
[INFO] [launch.user]: bottom2 = false

Running ros2 launch launch_args_ex top.launch.py bottom1:=true gives the following result (bottom1 is now evaluated as true, as expected):

[INFO] [launch.user]: bottom1 = true
[INFO] [launch.user]: bottom2 = false

So the launch arguments passed to top.launch.py are passed to the included bottom.launch.py and evaluated.

Then the question becomes, why doesn't this also work for the required arguments check, i.e. when we do not define default values for bottom.launch.py?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants