Skip to content

Commit 094bcd9

Browse files
v-lopezivanpauno
authored andcommitted
Add wildcard loading to ros2 param load (#602)
Signed-off-by: Victor Lopez <victor.lopez@pal-robotics.com>
1 parent bbf12f7 commit 094bcd9

3 files changed

Lines changed: 97 additions & 14 deletions

File tree

ros2param/ros2param/api/__init__.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,21 +137,28 @@ def load_parameter_dict(*, node, node_name, parameter_dict):
137137
print(msg, file=sys.stderr)
138138

139139

140-
def load_parameter_file(*, node, node_name, parameter_file):
140+
def load_parameter_file(*, node, node_name, parameter_file, use_wildcard):
141141
# Remove leading slash and namespaces
142142
with open(parameter_file, 'r') as f:
143143
param_file = yaml.safe_load(f)
144-
if node_name not in param_file:
144+
param_keys = []
145+
if use_wildcard and '/**' in param_file:
146+
param_keys.append('/**')
147+
if node_name in param_file:
148+
param_keys.append(node_name)
149+
150+
if param_keys == []:
145151
raise RuntimeError('Param file does not contain parameters for {}, '
146152
' only for nodes: {}' .format(node_name, param_file.keys()))
147-
148-
value = param_file[node_name]
149-
if type(value) != dict or 'ros__parameters' not in value:
150-
raise RuntimeError('Invalid structure of parameter file for node {}'
151-
'expected same format as provided by ros2 param dump'
152-
.format(node_name))
153-
load_parameter_dict(node=node, node_name=node_name,
154-
parameter_dict=value['ros__parameters'])
153+
param_dict = {}
154+
for k in param_keys:
155+
value = param_file[k]
156+
if type(value) != dict or 'ros__parameters' not in value:
157+
raise RuntimeError('Invalid structure of parameter file for node {}'
158+
'expected same format as provided by ros2 param dump'
159+
.format(k))
160+
param_dict.update(value['ros__parameters'])
161+
load_parameter_dict(node=node, node_name=node_name, parameter_dict=param_dict)
155162

156163

157164
def call_describe_parameters(*, node, node_name, parameter_names=None):

ros2param/ros2param/verb/load.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ def add_arguments(self, parser, cli_name): # noqa: D102
3434
parser.add_argument(
3535
'--include-hidden-nodes', action='store_true',
3636
help='Consider hidden nodes as well')
37-
arg = parser.add_argument(
37+
parser.add_argument(
3838
'parameter_file', help='Parameter file')
39+
parser.add_argument(
40+
'--no-use-wildcard', action='store_true',
41+
help="Do not load parameters in the '/**' namespace into the node")
3942

4043
def main(self, *, args): # noqa: D102
4144
with NodeStrategy(args) as node:
@@ -47,4 +50,5 @@ def main(self, *, args): # noqa: D102
4750
return 'Node not found'
4851

4952
with DirectNode(args) as node:
50-
load_parameter_file(node=node, node_name=node_name, parameter_file=args.parameter_file)
53+
load_parameter_file(node=node, node_name=node_name, parameter_file=args.parameter_file,
54+
use_wildcard=not args.no_use_wildcard)

ros2param/test/test_verb_load.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737

3838
from ros2cli.node.strategy import NodeStrategy
3939

40+
import yaml
41+
4042
TEST_NODE = 'test_node'
4143
TEST_NAMESPACE = '/foo'
4244

@@ -71,6 +73,17 @@
7173
' str_param: Bye World\n'
7274
' use_sim_time: false\n'
7375
)
76+
INPUT_WILDCARD_PARAMETER_FILE = (
77+
'/**:\n'
78+
' ros__parameters:\n'
79+
' str_param: Wildcard\n'
80+
' int_param: 12345\n'
81+
)
82+
INPUT_NODE_OVERLAY_PARAMETER_FILE = (
83+
f'{TEST_NAMESPACE}/{TEST_NODE}:\n'
84+
' ros__parameters:\n'
85+
' str_param: Override\n'
86+
)
7487

7588
# Skip cli tests on Windows while they exhibit pathological behavior
7689
# https://github.com/ros2/build_farmer/issues/248
@@ -191,10 +204,10 @@ def setUp(self):
191204
if timed_out:
192205
self.fail(f'CLI daemon failed to find test node after {TEST_TIMEOUT} seconds')
193206

194-
def _write_param_file(self, tmpdir, filename):
207+
def _write_param_file(self, tmpdir, filename, contents=INPUT_PARAMETER_FILE):
195208
yaml_path = os.path.join(tmpdir, filename)
196209
with open(yaml_path, 'w') as f:
197-
f.write(INPUT_PARAMETER_FILE)
210+
f.write(contents)
198211
return yaml_path
199212

200213
def _output_file(self):
@@ -280,3 +293,62 @@ def test_verb_load(self):
280293
text=param_dump_command.output,
281294
strict=True
282295
)
296+
297+
def test_verb_load_wildcard(self):
298+
with tempfile.TemporaryDirectory() as tmpdir:
299+
# Try param file with only wildcard
300+
filepath = self._write_param_file(tmpdir, 'params.yaml', INPUT_WILDCARD_PARAMETER_FILE)
301+
with self.launch_param_load_command(
302+
arguments=[f'{TEST_NAMESPACE}/{TEST_NODE}', filepath,
303+
'--no-use-wildcard']
304+
) as param_load_command:
305+
assert param_load_command.wait_for_shutdown(timeout=TEST_TIMEOUT)
306+
assert param_load_command.exit_code != launch_testing.asserts.EXIT_OK
307+
assert launch_testing.tools.expect_output(
308+
expected_lines=['Param file does not contain parameters for '
309+
f'{TEST_NAMESPACE}/{TEST_NODE}'],
310+
text=param_load_command.output,
311+
strict=False
312+
)
313+
314+
with self.launch_param_load_command(
315+
arguments=[f'{TEST_NAMESPACE}/{TEST_NODE}', filepath]
316+
) as param_load_command:
317+
assert param_load_command.wait_for_shutdown(timeout=TEST_TIMEOUT)
318+
assert param_load_command.exit_code == launch_testing.asserts.EXIT_OK
319+
assert launch_testing.tools.expect_output(
320+
expected_lines=[''],
321+
text=param_load_command.output,
322+
strict=True
323+
)
324+
# Dump with ros2 param and check that wildcard parameters are loaded
325+
with self.launch_param_dump_command(
326+
arguments=[f'{TEST_NAMESPACE}/{TEST_NODE}', '--print']
327+
) as param_dump_command:
328+
assert param_dump_command.wait_for_shutdown(timeout=TEST_TIMEOUT)
329+
assert param_dump_command.exit_code == launch_testing.asserts.EXIT_OK
330+
loaded_params = yaml.safe_load(param_dump_command.output)
331+
params = loaded_params[f'{TEST_NAMESPACE}/{TEST_NODE}']['ros__parameters']
332+
assert params['str_param'] == 'Wildcard'
333+
assert params['int_param'] == 12345
334+
335+
# Concatenate wildcard + some overlays
336+
filepath = self._write_param_file(tmpdir, 'params.yaml',
337+
INPUT_WILDCARD_PARAMETER_FILE + '\n' +
338+
INPUT_NODE_OVERLAY_PARAMETER_FILE)
339+
with self.launch_param_load_command(
340+
arguments=[f'{TEST_NAMESPACE}/{TEST_NODE}', filepath]
341+
) as param_load_command:
342+
assert param_load_command.wait_for_shutdown(timeout=TEST_TIMEOUT)
343+
assert param_load_command.exit_code == launch_testing.asserts.EXIT_OK
344+
345+
# Dump and check that wildcard parameters were overriden if in node namespace
346+
with self.launch_param_dump_command(
347+
arguments=[f'{TEST_NAMESPACE}/{TEST_NODE}', '--print']
348+
) as param_dump_command:
349+
assert param_dump_command.wait_for_shutdown(timeout=TEST_TIMEOUT)
350+
assert param_dump_command.exit_code == launch_testing.asserts.EXIT_OK
351+
loaded_params = yaml.safe_load(param_dump_command.output)
352+
params = loaded_params[f'{TEST_NAMESPACE}/{TEST_NODE}']['ros__parameters']
353+
assert params['str_param'] == 'Override' # Overriden
354+
assert params['int_param'] == 12345 # Wildcard namespace

0 commit comments

Comments
 (0)