From 6544e14724132c073499cdfc835f5181f3a257c8 Mon Sep 17 00:00:00 2001 From: Old-Ding <35417409+Old-Ding@users.noreply.github.com> Date: Sat, 11 Jul 2026 12:42:06 +0800 Subject: [PATCH 1/2] Show IDL interfaces in their original format Bypass the ROS message line parser for .idl resources while preserving existing message expansion and CLI trailing newline behavior. Signed-off-by: Old-Ding <35417409+Old-Ding@users.noreply.github.com> --- ros2interface/ros2interface/verb/show.py | 15 ++++++-- ros2interface/test/test_show.py | 48 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 ros2interface/test/test_show.py diff --git a/ros2interface/ros2interface/verb/show.py b/ros2interface/ros2interface/verb/show.py index aec3eb641..500db2d90 100644 --- a/ros2interface/ros2interface/verb/show.py +++ b/ros2interface/ros2interface/verb/show.py @@ -104,7 +104,10 @@ def _is_nested(self) -> bool: return False -def _get_interface_lines(interface_identifier: str) -> typing.Iterable[InterfaceTextLine]: +def _get_interface_lines( + interface_identifier: str, + file_path: str, +) -> typing.Iterable[InterfaceTextLine]: parts: typing.List[str] = interface_identifier.split('/') if len(parts) != 3: raise ValueError( @@ -112,7 +115,6 @@ def _get_interface_lines(interface_identifier: str) -> typing.Iterable[Interface ) pkg_name, _, msg_name = parts - file_path = get_interface_path(interface_identifier) with open(file_path) as file_handler: for line in file_handler: yield InterfaceTextLine( @@ -147,7 +149,14 @@ def _show_interface( is_show_nested_comments: bool = False, indent_level: int = 0 ): - for line in _get_interface_lines(interface_identifier): + file_path = get_interface_path(interface_identifier) + if file_path.endswith('.idl'): + with open(file_path) as file_handler: + content = file_handler.read() + print(content, end='' if content.endswith('\n') else '\n') + return + + for line in _get_interface_lines(interface_identifier, file_path): _print_interface_line( line, is_show_comments=is_show_comments, indent_level=indent_level) diff --git a/ros2interface/test/test_show.py b/ros2interface/test/test_show.py new file mode 100644 index 000000000..b68871c49 --- /dev/null +++ b/ros2interface/test/test_show.py @@ -0,0 +1,48 @@ +# Copyright 2026 Old-Ding +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for the interface show verb.""" + +from ros2interface.verb import show + + +def test_show_msg_interface(tmp_path, monkeypatch, capsys): + msg_text = 'uint32 value\n' + msg_path = tmp_path / 'Basic.msg' + msg_path.write_text(msg_text) + monkeypatch.setattr(show, 'get_interface_path', lambda _: str(msg_path)) + + show._show_interface('test_interfaces/msg/Basic') + + assert capsys.readouterr().out == msg_text + + +def test_show_idl_interface(tmp_path, monkeypatch, capsys): + idl_text = """\ +module test_interfaces { + module msg { + struct IdlOnly { + uint32 value; + }; + }; +}; +""" + idl_path = tmp_path / 'IdlOnly.idl' + monkeypatch.setattr(show, 'get_interface_path', lambda _: str(idl_path)) + + for file_text in (idl_text, idl_text.rstrip('\n')): + idl_path.write_text(file_text) + show._show_interface('test_interfaces/msg/IdlOnly') + + assert capsys.readouterr().out == idl_text From 1aea8236edaa5ef0e14816fc69d3676e6154787c Mon Sep 17 00:00:00 2001 From: Old-Ding <35417409+Old-Ding@users.noreply.github.com> Date: Wed, 15 Jul 2026 19:12:17 +0800 Subject: [PATCH 2/2] ros2interface: indent nested IDL output Signed-off-by: Old-Ding <35417409+Old-Ding@users.noreply.github.com> --- ros2cli_test_interfaces/CMakeLists.txt | 2 + ros2cli_test_interfaces/msg/IdlOnly.idl | 7 +++ .../msg/ShortVariedIdlNested.msg | 1 + ros2interface/ros2interface/verb/show.py | 6 ++- ros2interface/test/test_cli.py | 41 ++++++++++++++++ ros2interface/test/test_show.py | 48 ------------------- 6 files changed, 56 insertions(+), 49 deletions(-) create mode 100644 ros2cli_test_interfaces/msg/IdlOnly.idl create mode 100644 ros2cli_test_interfaces/msg/ShortVariedIdlNested.msg delete mode 100644 ros2interface/test/test_show.py diff --git a/ros2cli_test_interfaces/CMakeLists.txt b/ros2cli_test_interfaces/CMakeLists.txt index b3c905927..f7e2008c8 100644 --- a/ros2cli_test_interfaces/CMakeLists.txt +++ b/ros2cli_test_interfaces/CMakeLists.txt @@ -10,7 +10,9 @@ find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "action/ShortVariedMultiNested.action" + "msg/IdlOnly.idl" "msg/ShortVaried.msg" + "msg/ShortVariedIdlNested.msg" "msg/ShortVariedMultiNested.msg" "msg/ShortVariedNested.msg" "srv/ShortVariedMultiNested.srv" diff --git a/ros2cli_test_interfaces/msg/IdlOnly.idl b/ros2cli_test_interfaces/msg/IdlOnly.idl new file mode 100644 index 000000000..e1ea063a1 --- /dev/null +++ b/ros2cli_test_interfaces/msg/IdlOnly.idl @@ -0,0 +1,7 @@ +module ros2cli_test_interfaces { + module msg { + struct IdlOnly { + uint32 value; + }; + }; +}; diff --git a/ros2cli_test_interfaces/msg/ShortVariedIdlNested.msg b/ros2cli_test_interfaces/msg/ShortVariedIdlNested.msg new file mode 100644 index 000000000..30b45757a --- /dev/null +++ b/ros2cli_test_interfaces/msg/ShortVariedIdlNested.msg @@ -0,0 +1 @@ +IdlOnly idl_only diff --git a/ros2interface/ros2interface/verb/show.py b/ros2interface/ros2interface/verb/show.py index 500db2d90..94aeb7d09 100644 --- a/ros2interface/ros2interface/verb/show.py +++ b/ros2interface/ros2interface/verb/show.py @@ -153,7 +153,11 @@ def _show_interface( if file_path.endswith('.idl'): with open(file_path) as file_handler: content = file_handler.read() - print(content, end='' if content.endswith('\n') else '\n') + indent_string = indent_level * '\t' + for line in content.splitlines(): + print(f'{indent_string}{line}' if line else '') + if not content or not content.endswith('\n'): + print() return for line in _get_interface_lines(interface_identifier, file_path): diff --git a/ros2interface/test/test_cli.py b/ros2interface/test/test_cli.py index cc654b48c..047283440 100644 --- a/ros2interface/test/test_cli.py +++ b/ros2interface/test/test_cli.py @@ -302,6 +302,47 @@ def test_show_message(self): strict=True ) + def test_show_idl_message(self): + with self.launch_interface_command( + arguments=['show', 'ros2cli_test_interfaces/msg/IdlOnly'] + ) as interface_command: + assert interface_command.wait_for_shutdown(timeout=2) + assert interface_command.exit_code == launch_testing.asserts.EXIT_OK + assert launch_testing.tools.expect_output( + expected_lines=[ + 'module ros2cli_test_interfaces {', + ' module msg {', + ' struct IdlOnly {', + ' uint32 value;', + ' };', + ' };', + '};', + ], + text=interface_command.output, + strict=True + ) + + def test_show_message_with_idl_nested_type(self): + with self.launch_interface_command( + arguments=['show', 'ros2cli_test_interfaces/msg/ShortVariedIdlNested'] + ) as interface_command: + assert interface_command.wait_for_shutdown(timeout=2) + assert interface_command.exit_code == launch_testing.asserts.EXIT_OK + assert launch_testing.tools.expect_output( + expected_lines=[ + 'IdlOnly idl_only', + '\tmodule ros2cli_test_interfaces {', + '\t module msg {', + '\t struct IdlOnly {', + '\t uint32 value;', + '\t };', + '\t };', + '\t};', + ], + text=interface_command.output, + strict=True + ) + def test_show_message_with_all_comments(self): with self.launch_interface_command( arguments=[ diff --git a/ros2interface/test/test_show.py b/ros2interface/test/test_show.py deleted file mode 100644 index b68871c49..000000000 --- a/ros2interface/test/test_show.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2026 Old-Ding -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests for the interface show verb.""" - -from ros2interface.verb import show - - -def test_show_msg_interface(tmp_path, monkeypatch, capsys): - msg_text = 'uint32 value\n' - msg_path = tmp_path / 'Basic.msg' - msg_path.write_text(msg_text) - monkeypatch.setattr(show, 'get_interface_path', lambda _: str(msg_path)) - - show._show_interface('test_interfaces/msg/Basic') - - assert capsys.readouterr().out == msg_text - - -def test_show_idl_interface(tmp_path, monkeypatch, capsys): - idl_text = """\ -module test_interfaces { - module msg { - struct IdlOnly { - uint32 value; - }; - }; -}; -""" - idl_path = tmp_path / 'IdlOnly.idl' - monkeypatch.setattr(show, 'get_interface_path', lambda _: str(idl_path)) - - for file_text in (idl_text, idl_text.rstrip('\n')): - idl_path.write_text(file_text) - show._show_interface('test_interfaces/msg/IdlOnly') - - assert capsys.readouterr().out == idl_text