Skip to content

Fix auto-completed YAML string for ros2service#1201

Open
leander-dsouza wants to merge 2 commits intoros2:rollingfrom
leander-dsouza:rolling
Open

Fix auto-completed YAML string for ros2service#1201
leander-dsouza wants to merge 2 commits intoros2:rollingfrom
leander-dsouza:rolling

Conversation

@leander-dsouza
Copy link
Contributor

@leander-dsouza leander-dsouza commented Feb 28, 2026

Basic Info

Info Please fill out this column
Ticket(s) this addresses Closes #1194 ; Related #995
Primary OS tested on Ubuntu

Description

  • Renamed YamlCompletionFinder to UnescapedCompletionFinder to better indicate intent.

    This function is further simplified to return the unedited completions to the terminal on all conditions, which would otherwise be filled with special characters from argcomplete.

  • Moved UnescapedCompletionFinder to the helpers.py script for reusability with other methods.

  • Configured ros2service to use this custom completion finder in the same way as configured in Custom Completion Finder for fetching topic prototype. #995 for ros2topic.

How to test

  • Run an example node that hosts a demo service - /add_two_ints:

    demo_service.py:

    import rclpy
    from rclpy.executors import ExternalShutdownException
    from rclpy.node import Node
    
    from example_interfaces.srv import AddTwoInts
    
    
    class DemoService(Node):
    
        def __init__(self):
            super().__init__('demo_service')
            self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
            self.get_logger().info("Service 'add_two_ints' is ready.")
    
        def add_two_ints_callback(self, request, response):
            response.sum = request.a + request.b
            self.get_logger().info(f'Incoming request: a={request.a}, b={request.b} -> sum={response.sum}')
            return response
    
    
    def main(args=None):
        rclpy.init(args=args)
        try:
            node = DemoService()
            rclpy.spin(node)
        except (KeyboardInterrupt, ExternalShutdownException):
            print('Service node stopped cleanly')
        finally:
            node.destroy_node()
            rclpy.shutdown()
    
    
    if __name__ == '__main__':
        main()

    On a separate terminal, execute the test script:

    python3 demo_service.py
  • Before this addition, the autocomplete would fail as follows:

    $ ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts \'a:\ 0b:\ 0\'
    Failed to populate field: Value 'a: 0b: 0' is expected to be a dictionary but is a str
  • After the addition of this PR, this is the expected output:

    $ ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts 'a: 0
    b: 0
    '
    waiting for service to become available...
    requester: making request: example_interfaces.srv.AddTwoInts_Request(a=0, b=0)
    
    response:
    example_interfaces.srv.AddTwoInts_Response(sum=0) 

Is this user-facing behavior change?

Yes, the user-facing behavior change is described in the testing section.

Did you use Generative AI?

No

Additional Information

For distros semantically lower than rolling, a check for ^J is still needed:
For instance, tabbing the given example in kilted or jazzy results in the following:

$ ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts <tab>
'a: 0^Jb: 0^J'  --rate          --stdin         -r 

Signed-off-by: Leander Stephen D'Souza <leanderdsouza1234@gmail.com>
… characters

Signed-off-by: Leander Stephen D'Souza <leanderdsouza1234@gmail.com>
@leander-dsouza leander-dsouza marked this pull request as ready for review February 28, 2026 03:02
@fujitatomoya fujitatomoya self-assigned this Mar 1, 2026
@fujitatomoya
Copy link
Collaborator

@leander-dsouza thanks for coming back to fix this up, i will give it a shot and do review!

Copy link
Collaborator

@fujitatomoya fujitatomoya left a comment

Choose a reason for hiding this comment

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

  • ros2 topic pub <tab><tab> now suggests the available topics, this is unexpected behavior. i do not understand why we need to fix ros2 topic pub behavior here, can you explain why? see #1195 (comment)
root@tomoyafujita-B760M-Pro-RS-D4:~/ros2_ws/colcon_ws# ros2 topic pub <tab><tab>
/parameter_events  /rosout      
  • ros2 service call <tab><tab> prints the all service types. i think it should print the available service list 1st. this is broken because #1194 (comment).
root@tomoyafujita-B760M-Pro-RS-D4:~/ros2_ws/colcon_ws# ros2 service call <tab><tab>
action_msgs/srv/CancelGoal                          map_msgs/srv/SaveMap                                rosbag2_interfaces/srv/Snapshot
/add_two_ints                                       map_msgs/srv/SetMapProjections                      rosbag2_interfaces/srv/SplitBagfile
/add_two_ints_server/describe_parameters            nav_msgs/srv/GetMap                                 rosbag2_interfaces/srv/StartDiscovery
/add_two_ints_server/get_parameters                 nav_msgs/srv/GetPlan                                rosbag2_interfaces/srv/Stop
/add_two_ints_server/get_parameter_types            nav_msgs/srv/LoadMap                                rosbag2_interfaces/srv/StopDiscovery
/add_two_ints_server/get_type_description           nav_msgs/srv/SetMap                                 rosbag2_interfaces/srv/TogglePaused
/add_two_ints_server/list_parameters                rcl_interfaces/srv/DescribeParameters               rosbag2_test_msgdefs/srv/BasicSrv
/add_two_ints_server/set_parameters                 rcl_interfaces/srv/GetLoggerLevels                  rosbag2_test_msgdefs/srv/ComplexSrvIdl
/add_two_ints_server/set_parameters_atomically      rcl_interfaces/srv/GetParameters                    rosbag2_test_msgdefs/srv/ComplexSrvMsg
composition_interfaces/srv/ListNodes                rcl_interfaces/srv/GetParameterTypes                rviz_resource_interfaces/srv/GetResource
composition_interfaces/srv/LoadNode                 rcl_interfaces/srv/ListParameters                   sensor_msgs/srv/SetCameraInfo
composition_interfaces/srv/UnloadNode               rcl_interfaces/srv/SetLoggerLevels                  std_srvs/srv/Empty
diagnostic_msgs/srv/AddDiagnostics                  rcl_interfaces/srv/SetParameters                    std_srvs/srv/SetBool
diagnostic_msgs/srv/SelfTest                        rcl_interfaces/srv/SetParametersAtomically          std_srvs/srv/Trigger
example_interfaces/srv/AddTwoInts                   ros2cli_test_interfaces/srv/ShortVariedMultiNested  test_msgs/srv/Arrays
example_interfaces/srv/SetBool                      rosbag2_interfaces/srv/Burst                        test_msgs/srv/BasicTypes
example_interfaces/srv/Trigger                      rosbag2_interfaces/srv/GetRate                      test_msgs/srv/Empty
lifecycle_msgs/srv/ChangeState                      rosbag2_interfaces/srv/IsDiscoveryRunning           tf2_msgs/srv/FrameGraph
lifecycle_msgs/srv/GetAvailableStates               rosbag2_interfaces/srv/IsPaused                     turtlesim_msgs/srv/Kill
lifecycle_msgs/srv/GetAvailableTransitions          rosbag2_interfaces/srv/Pause                        turtlesim_msgs/srv/SetPen
lifecycle_msgs/srv/GetState                         rosbag2_interfaces/srv/Play                         turtlesim_msgs/srv/Spawn
logging_demo/srv/ConfigLogger                       rosbag2_interfaces/srv/PlayNext                     turtlesim_msgs/srv/TeleportAbsolute
map_msgs/srv/GetMapROI                              rosbag2_interfaces/srv/Record                       turtlesim_msgs/srv/TeleportRelative
map_msgs/srv/GetPointMap                            rosbag2_interfaces/srv/Resume                       type_description_interfaces/srv/GetTypeDescription
map_msgs/srv/GetPointMapROI                         rosbag2_interfaces/srv/Seek                         visualization_msgs/srv/GetInteractiveMarkers
map_msgs/srv/ProjectedMapsInfo                      rosbag2_interfaces/srv/SetRate
  • ros2 service call /add_two_ints <tab><tab> does not suggest service types nor default values at all. this is also still broken.
root@tomoyafujita-B760M-Pro-RS-D4:~/ros2_ws/colcon_ws# ros2 service call /add_two_ints <tab><tab>
build/                         example_qos_overrides.yaml     param_ok.yaml                  test-policy.xml                
DEFAULT_FASTRTPS_PROFILES.xml  install/                       .pytest_cache/                 test.py                        
demo_keystore/                 log/                           ros2/                          test.sh                        
dump.yaml                      param_ng.yaml                  src/                           yaml_sample.py        

@peci1
Copy link
Contributor

peci1 commented Mar 2, 2026

  • ros2 topic pub <tab><tab> now suggests the available topics, this is unexpected behavior. i do not understand why we need to fix ros2 topic pub behavior here, can you explain why? see #1195 (comment)
root@tomoyafujita-B760M-Pro-RS-D4:~/ros2_ws/colcon_ws# ros2 topic pub <tab><tab>
/parameter_events  /rosout      

What's wrong with this? You often want to publish on an existing topic...

@fujitatomoya
Copy link
Collaborator

@peci1 yeah what was i thinking... 😓 and behavior seems to be same, please ignore that comment, sorry!

@peci1
Copy link
Contributor

peci1 commented Mar 2, 2026

I can confirm @fujitatomoya observations in current rolling branch.

I can't confirm the failure from the initial message but I get a different one (similar I'd say):

Details
$ ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts a:\ 0\
b:\ 0\ 
Traceback (most recent call last):
  File "/tmp/ws2/install/ros2cli/bin/ros2", line 33, in <module>
    sys.exit(load_entry_point('ros2cli', 'console_scripts', 'ros2')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/ws2/build/ros2cli/ros2cli/cli.py", line 91, in main
    rc = extension.main(parser=parser, args=args)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/ws2/build/ros2service/ros2service/command/service.py", line 41, in main
    return extension.main(args=args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/ws2/build/ros2service/ros2service/verb/call.py", line 106, in main
    return requester(
           ^^^^^^^^^^
  File "/tmp/ws2/build/ros2service/ros2service/verb/call.py", line 120, in requester
    values_dictionary = yaml.safe_load(values)
                        ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/__init__.py", line 125, in safe_load
    return load(stream, SafeLoader)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/__init__.py", line 81, in load
    return loader.get_single_data()
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/constructor.py", line 49, in get_single_data
    node = self.get_single_node()
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/composer.py", line 36, in get_single_node
    document = self.compose_document()
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/composer.py", line 55, in compose_document
    node = self.compose_node(None, None)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/composer.py", line 84, in compose_node
    node = self.compose_mapping_node(anchor)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/composer.py", line 127, in compose_mapping_node
    while not self.check_event(MappingEndEvent):
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/parser.py", line 98, in check_event
    self.current_event = self.state()
                         ^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/parser.py", line 428, in parse_block_mapping_key
    if self.check_token(KeyToken):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/scanner.py", line 116, in check_token
    self.fetch_more_tokens()
  File "/usr/lib/python3/dist-packages/yaml/scanner.py", line 223, in fetch_more_tokens
    return self.fetch_value()
           ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/yaml/scanner.py", line 577, in fetch_value
    raise ScannerError(None, None,
yaml.scanner.ScannerError: mapping values are not allowed here
  in "<unicode string>", line 1, column 6:
    a: 0b: 0 
         ^

What I can confirm that the prototype autofilled for ros2 service call is now the much friendlier multiline one without backslashes, and the prototype can be right away used.

So I think this PR can be merged and then we'll have to fix the service type suggestions separately.

@leander-dsouza
Copy link
Contributor Author

  • ros2 service call <tab><tab> prints the all service types. i think it should print the available service list 1st. this is broken because #1194 (comment).
  • ros2 service call /add_two_ints <tab><tab> does not suggest service types nor default values at all. this is also still broken.

Thank you for the initial review, Tomoya :)
It is indeed true that the autocomplete does not suggest service types or the available service list.
However, this PR does not aim to solve these issues.

This PR fixes the incorrect formatting of the YAML string for default values once the service name and service type are known.

For instance, tabbing at the specified location now renders - 'a: 0\nb: 0', instead of \'a:\ 0b:\ 0\':

$ ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts <tab>

@fujitatomoya
Copy link
Collaborator

Pulls: #1201
Gist: https://gist.githubusercontent.com/fujitatomoya/e144f5c6d776bb9d6d9785316c753036/raw/50bb459e8d198f18b57e8f2fd58e2b7c5304ceae/ros2.repos
BUILD args: --packages-above-and-dependencies ros2topic ros2service ros2cli
TEST args: --packages-above ros2topic ros2service ros2cli
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/18361

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

Copy link
Collaborator

@fujitatomoya fujitatomoya left a comment

Choose a reason for hiding this comment

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

lgtm with green CI. thanks for the explanation.

@leander-dsouza
Copy link
Contributor Author

lgtm with green CI. thanks for the explanation.

I don't know why the builds are failing in Linux.
These errors seem unrelated to the contents of the PR.

image

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

Successfully merging this pull request may close these issues.

Argument completion for ros2service

3 participants