1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 """
36 Roslaunch XML file parser.
37 """
38
39 from __future__ import print_function
40
41 import itertools
42 import sys
43 import traceback
44 import logging
45
46 from xml.dom.minidom import parse, parseString
47 from xml.dom import Node as DomNode
48
49 from rosgraph.names import make_global_ns, ns_join, is_private, is_legal_name, get_ros_namespace
50
51 from .core import Param, Node, Test, Machine, RLException
52 from . import loader
53 from . import substitution_args
54
55
56 SubstitutionException = substitution_args.SubstitutionException
57 ArgException = substitution_args.ArgException
58
59 NS='ns'
60 CLEAR_PARAMS='clear_params'
61
62 -def _get_text(tag):
63 buff = ''
64 for t in tag.childNodes:
65 if t.nodeType in [t.TEXT_NODE, t.CDATA_SECTION_NODE]:
66 buff += t.data
67 return buff
68
70 """
71 @return True: if tag should be processed according to its if/unless attributes
72 """
73 if_val, unless_val = obj.opt_attrs(tag, context, ['if', 'unless'])
74 if if_val is not None and unless_val is not None:
75 raise XmlParseException("cannot set both 'if' and 'unless' on the same tag")
76 if if_val is not None:
77 if_val = loader.convert_value(if_val, 'bool')
78 if if_val:
79 return True
80 elif unless_val is not None:
81 unless_val = loader.convert_value(unless_val, 'bool')
82 if not unless_val:
83 return True
84 else:
85 return True
86 return False
87
89 """
90 Decorator for evaluating whether or not tag function should run based on if/unless attributes
91 """
92 def call(*args, **kwds):
93
94 if ifunless_test(args[0], args[1], args[2]):
95 return f(*args, **kwds)
96 return call
97
103 """Error with the XML syntax (e.g. invalid attribute/value combinations)"""
104 pass
105
107 """
108 Validate boolean xml attribute.
109 @param v: parameter value or None if no value provided
110 @type v: any
111 @param default: default value
112 @type default: bool
113 @param label: parameter name/label
114 @type label: str
115 @return: boolean value for attribute
116 @rtype: bool
117 @raise XmlParseException: if v is not in correct range or is empty.
118 """
119 if v is None:
120 return default
121 if v.lower() == 'true':
122 return True
123 elif v.lower() == 'false':
124 return False
125 elif not v:
126 raise XmlParseException("bool value for %s must be non-empty"%(label))
127 else:
128 raise XmlParseException("invalid bool value for %s: %s"%(label, v))
129
131 """
132 Validate float xml attribute.
133 @param v: parameter value or None if no value provided
134 @type v: any
135 @param default: default value
136 @type default: float
137 @param label: parameter name/label
138 @type label: str
139 @return: float value for attribute
140 @rtype: float
141 @raise XmlParseException: if v is not in correct range or is empty.
142 """
143 if v is None:
144 return default
145 if not v:
146 raise XmlParseException("bool value for %s must be non-empty"%(label))
147 try:
148 x = float(v)
149 except ValueError:
150 raise XmlParseException("invalid float value for %s: %s"%(label, v))
151 return x
152
153
154
155 _is_default = {'true': True, 'false': False, 'never': False }
156
157 _assignable = {'true': True, 'false': True, 'never': False }
158
159
160
161
162 -class XmlLoader(loader.Loader):
163 """
164 Parser for roslaunch XML format. Loads parsed representation into ROSConfig model.
165 """
166
168 """
169 @param resolve_anon: If True (default), will resolve $(anon foo). If
170 false, will leave these args as-is.
171 @type resolve_anon: bool
172 """
173
174 self.root_context = None
175 self.resolve_anon = resolve_anon
176
178 """
179 Wrapper around substitution_args.resolve_args to set common parameters
180 """
181
182 if args and '$' in args:
183 return substitution_args.resolve_args(args, context=context.resolve_dict, resolve_anon=self.resolve_anon)
184 else:
185 return args
186
188 """
189 Helper routine for fetching and resolving optional tag attributes
190 @param tag DOM tag
191 @param context LoaderContext
192 @param attrs (str): list of attributes to resolve
193 """
194 def tag_value(tag, a):
195 if tag.hasAttribute(a):
196
197
198
199 return tag.getAttribute(a)
200 else:
201 return None
202 return [self.resolve_args(tag_value(tag,a), context) for a in attrs]
203
205 """
206 Helper routine for fetching and resolving required tag attributes
207 @param tag: DOM tag
208 @param attrs: list of attributes to resolve
209 @type attrs: (str)
210 @raise KeyError: if required attribute is missing
211 """
212 return [self.resolve_args(tag.attributes[a].value, context) for a in attrs]
213
215 tag_attrs = tag.attributes.keys()
216 for t_a in tag_attrs:
217 if not t_a in attrs and not t_a in ['if', 'unless']:
218 ros_config.add_config_error("[%s] unknown <%s> attribute '%s'"%(context.filename, tag.tagName, t_a))
219
220
221
222
223 ROSPARAM_OPT_ATTRS = ('command', 'ns', 'file', 'param', 'subst_value')
224 @ifunless
225 - def _rosparam_tag(self, tag, context, ros_config, verbose=True):
226 try:
227 self._check_attrs(tag, context, ros_config, XmlLoader.ROSPARAM_OPT_ATTRS)
228 cmd, ns, file, param, subst_value = self.opt_attrs(tag, context, (XmlLoader.ROSPARAM_OPT_ATTRS))
229 subst_value = _bool_attr(subst_value, False, 'subst_value')
230
231 param = ns_join(ns or '', param or '')
232
233
234 cmd = cmd or 'load'
235 value = _get_text(tag)
236 subst_function = None
237 if subst_value:
238 subst_function = lambda x: self.resolve_args(x, context)
239 self.load_rosparam(context, ros_config, cmd, param, file, value, verbose=verbose, subst_function=subst_function)
240
241 except ValueError as e:
242 raise loader.LoadException("error loading <rosparam> tag: \n\t"+str(e)+"\nXML is %s"%tag.toxml())
243
244 PARAM_ATTRS = ('name', 'value', 'type', 'value', 'textfile', 'binfile', 'command')
245 @ifunless
246 - def _param_tag(self, tag, context, ros_config, force_local=False, verbose=True):
247 """
248 @param force_local: if True, param must be added to context instead of ros_config
249 @type force_local: bool
250 """
251 try:
252 self._check_attrs(tag, context, ros_config, XmlLoader.PARAM_ATTRS)
253
254
255 ptype = (tag.getAttribute('type') or 'auto').lower().strip()
256
257 vals = self.opt_attrs(tag, context, ('value', 'textfile', 'binfile', 'command'))
258 if len([v for v in vals if v is not None]) != 1:
259 raise XmlParseException(
260 "<param> tag must have one and only one of value/textfile/binfile.")
261
262
263
264 name = self.resolve_args(tag.attributes['name'].value.strip(), context)
265 value = self.param_value(verbose, name, ptype, *vals)
266
267 if is_private(name) or force_local:
268 p = Param(name, value)
269 context.add_param(p)
270 else:
271 p = Param(ns_join(context.ns, name), value)
272 ros_config.add_param(Param(ns_join(context.ns, name), value), filename=context.filename, verbose=verbose)
273 return p
274
275 except KeyError as e:
276 raise XmlParseException(
277 "<param> tag is missing required attribute: %s. \n\nParam xml is %s"%(e, tag.toxml()))
278 except ValueError as e:
279 raise XmlParseException(
280 "Invalid <param> tag: %s. \n\nParam xml is %s"%(e, tag.toxml()))
281
282 ARG_ATTRS = ('name', 'value', 'default', 'doc')
283 @ifunless
284 - def _arg_tag(self, tag, context, ros_config, verbose=True):
285 """
286 Process an <arg> tag.
287 """
288 try:
289 self._check_attrs(tag, context, ros_config, XmlLoader.ARG_ATTRS)
290 (name,) = self.reqd_attrs(tag, context, ('name',))
291 value, default, doc = self.opt_attrs(tag, context, ('value', 'default', 'doc'))
292
293 if value is not None and default is not None:
294 raise XmlParseException(
295 "<arg> tag must have one and only one of value/default.")
296
297 context.add_arg(name, value=value, default=default, doc=doc)
298
299 except substitution_args.ArgException as e:
300 raise XmlParseException(
301 "arg '%s' is not defined. \n\nArg xml is %s"%(e, tag.toxml()))
302 except Exception as e:
303 raise XmlParseException(
304 "Invalid <arg> tag: %s. \n\nArg xml is %s"%(e, tag.toxml()))
305
333
334 NODE_ATTRS = ['pkg', 'type', 'machine', 'name', 'args', 'output', \
335 'respawn', 'respawn_delay', 'cwd', NS, CLEAR_PARAMS, \
336 'launch-prefix', 'required']
337 TEST_ATTRS = NODE_ATTRS + ['test-name','time-limit', 'retry']
338
339 @ifunless
340 - def _node_tag(self, tag, context, ros_config, default_machine, is_test=False, verbose=True):
341 """
342 Process XML <node> or <test> tag
343 @param tag: DOM node
344 @type tag: Node
345 @param context: namespace context
346 @type context: L{LoaderContext}
347 @param params: ROS parameter list
348 @type params: [L{Param}]
349 @param clear_params: list of ROS parameter names to clear before setting parameters
350 @type clear_params: [str]
351 @param default_machine: default machine to assign to node
352 @type default_machine: str
353 @param is_test: if set, will load as L{Test} object instead of L{Node} object
354 @type is_test: bool
355 """
356 try:
357 if is_test:
358 self._check_attrs(tag, context, ros_config, XmlLoader.TEST_ATTRS)
359 (name,) = self.opt_attrs(tag, context, ('name',))
360 test_name, time_limit, retry = self._test_attrs(tag, context)
361 if not name:
362 name = test_name
363 else:
364 self._check_attrs(tag, context, ros_config, XmlLoader.NODE_ATTRS)
365 (name,) = self.reqd_attrs(tag, context, ('name',))
366
367 if not is_legal_name(name):
368 ros_config.add_config_error("WARN: illegal <node> name '%s'.\nhttp://ros.org/wiki/Names\nThis will likely cause problems with other ROS tools.\nNode xml is %s"%(name, tag.toxml()))
369
370 child_ns = self._ns_clear_params_attr('node', tag, context, ros_config, node_name=name)
371 param_ns = child_ns.child(name)
372 param_ns.params = []
373
374
375 pkg, node_type = self.reqd_attrs(tag, context, ('pkg', 'type'))
376
377
378 machine, args, output, respawn, respawn_delay, cwd, launch_prefix, \
379 required = self.opt_attrs(tag, context, ('machine', 'args',
380 'output', 'respawn', 'respawn_delay', 'cwd',
381 'launch-prefix', 'required'))
382 if tag.hasAttribute('machine') and not len(machine.strip()):
383 raise XmlParseException("<node> 'machine' must be non-empty: [%s]"%machine)
384 if not machine and default_machine:
385 machine = default_machine.name
386
387 required, respawn = [_bool_attr(*rr) for rr in ((required, False, 'required'),\
388 (respawn, False, 'respawn'))]
389 respawn_delay = _float_attr(respawn_delay, 0.0, 'respawn_delay')
390
391
392
393 remap_context = context.child('')
394
395
396
397 env_context = context.child('')
398
399
400
401 for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]:
402 tag_name = t.tagName.lower()
403 if tag_name == 'remap':
404 r = self._remap_tag(t, context, ros_config)
405 if r is not None:
406 remap_context.add_remap(r)
407 elif tag_name == 'param':
408 self._param_tag(t, param_ns, ros_config, force_local=True, verbose=verbose)
409 elif tag_name == 'rosparam':
410 self._rosparam_tag(t, param_ns, ros_config, verbose=verbose)
411 elif tag_name == 'env':
412 self._env_tag(t, env_context, ros_config)
413 else:
414 ros_config.add_config_error("WARN: unrecognized '%s' child tag in the parent tag element: %s"%(t.tagName, tag.toxml()))
415
416
417
418 for p in itertools.chain(context.params, param_ns.params):
419 pkey = p.key
420 if is_private(pkey):
421
422 pkey = pkey[1:]
423 pkey = param_ns.ns + pkey
424 ros_config.add_param(Param(pkey, p.value), verbose=verbose)
425
426 if not is_test:
427 return Node(pkg, node_type, name=name, namespace=child_ns.ns, machine_name=machine,
428 args=args, respawn=respawn,
429 respawn_delay=respawn_delay,
430 remap_args=remap_context.remap_args(), env_args=env_context.env_args,
431 output=output, cwd=cwd, launch_prefix=launch_prefix,
432 required=required, filename=context.filename)
433 else:
434 return Test(test_name, pkg, node_type, name=name, namespace=child_ns.ns,
435 machine_name=machine, args=args,
436 remap_args=remap_context.remap_args(), env_args=env_context.env_args,
437 time_limit=time_limit, cwd=cwd, launch_prefix=launch_prefix,
438 retry=retry, filename=context.filename)
439 except KeyError as e:
440 raise XmlParseException(
441 "<%s> tag is missing required attribute: %s. Node xml is %s"%(tag.tagName, e, tag.toxml()))
442 except XmlParseException as e:
443 raise XmlParseException(
444 "Invalid <node> tag: %s. \n\nNode xml is %s"%(e, tag.toxml()))
445 except ValueError as e:
446 raise XmlParseException(
447 "Invalid <node> tag: %s. \n\nNode xml is %s"%(e, tag.toxml()))
448
449 MACHINE_ATTRS = ('name', 'address', 'env-loader',
450 'ssh-port', 'user', 'password', 'default', 'timeout')
451 @ifunless
452 - def _machine_tag(self, tag, context, ros_config, verbose=True):
453 try:
454
455 context = context.child(None)
456
457
458 attrs = self.opt_attrs(tag, context,
459 ('ros-root', 'ros-package-path', 'ros-ip', 'ros-hostname'))
460 if any(attrs):
461 raise XmlParseException("<machine>: ros-* attributes are not supported since ROS Fuerte.\nPlease use env-loader instead")
462
463 self._check_attrs(tag, context, ros_config, XmlLoader.MACHINE_ATTRS)
464
465 name, address = self.reqd_attrs(tag, context, ('name', 'address'))
466
467
468 attrs = self.opt_attrs(tag, context,
469 ('env-loader',
470 'ssh-port', 'user', 'password', 'default', 'timeout'))
471 env_loader, ssh_port, user, password, default, timeout = attrs
472
473 ssh_port = int(ssh_port or '22')
474
475
476 default = (default or 'false').lower()
477 try:
478 assignable = _assignable[default]
479 is_default = _is_default[default]
480 except KeyError as e:
481 raise XmlParseException("Invalid value for 'attribute': %s"%default)
482
483
484 for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]:
485 if t.tagName == 'env':
486 raise XmlParseException("<machine>: <env> tag is not supported since ROS Fuerte.\nPlease use env-loader instead")
487 else:
488 ros_config.add_config_error("unrecognized '%s' tag in <%s> tag"%(t.tagName, tag.tagName))
489
490 if timeout:
491 try:
492 timeout = float(timeout)
493 except ValueError:
494 raise XmlParseException("'timeout' be a number: [%s]"%timeout)
495 elif timeout == '':
496 raise XmlParseException("'timeout' cannot be empty")
497 if timeout is not None and timeout <= 0.:
498 raise XmlParseException("'timeout' be a positive number: [%s]"%timeout)
499
500 m = Machine(name, address, env_loader=env_loader,
501 ssh_port=ssh_port, user=user, password=password,
502 assignable=assignable, env_args=context.env_args, timeout=timeout)
503 return (m, is_default)
504 except KeyError as e:
505 raise XmlParseException("<machine> tag is missing required attribute: %s"%e)
506 except SubstitutionException as e:
507 raise XmlParseException(
508 "%s. \n\nMachine xml is %s"%(e, tag.toxml()))
509 except RLException as e:
510 raise XmlParseException(
511 "%s. \n\nMachine xml is %s"%(e, tag.toxml()))
512
513 REMAP_ATTRS = ('from', 'to')
514 @ifunless
521
522 ENV_ATTRS = ('name', 'value')
523 @ifunless
524 - def _env_tag(self, tag, context, ros_config):
532
533 - def _ns_clear_params_attr(self, tag_name, tag, context, ros_config, node_name=None, include_filename=None):
534 """
535 Common processing routine for xml tags with NS and CLEAR_PARAMS attributes
536
537 @param tag: DOM Node
538 @type tag: Node
539 @param context: current namespace context
540 @type context: LoaderContext
541 @param clear_params: list of params to clear
542 @type clear_params: [str]
543 @param node_name: name of node (for use when tag_name == 'node')
544 @type node_name: str
545 @param include_filename: <include> filename if this is an <include> tag. If specified, context will use include rules.
546 @type include_filename: str
547 @return: loader context
548 @rtype: L{LoaderContext}
549 """
550 if tag.hasAttribute(NS):
551 ns = self.resolve_args(tag.getAttribute(NS), context)
552 if not ns:
553 raise XmlParseException("<%s> tag has an empty '%s' attribute"%(tag_name, NS))
554 else:
555 ns = None
556 if include_filename is not None:
557 child_ns = context.include_child(ns, include_filename)
558 else:
559 child_ns = context.child(ns)
560 clear_p = self.resolve_args(tag.getAttribute(CLEAR_PARAMS), context)
561 if clear_p:
562 clear_p = _bool_attr(clear_p, False, 'clear_params')
563 if clear_p:
564 if tag_name == 'node':
565 if not node_name:
566 raise XmlParseException("<%s> tag must have a 'name' attribute to use '%s' attribute"%(tag_name, CLEAR_PARAMS))
567
568 ros_config.add_clear_param(make_global_ns(ns_join(child_ns.ns, node_name)))
569 else:
570 if not ns:
571 raise XmlParseException("'ns' attribute must be set in order to use 'clear_params'")
572 ros_config.add_clear_param(child_ns.ns)
573 return child_ns
574
575 @ifunless
576 - def _launch_tag(self, tag, ros_config, filename=None):
577
578 deprecated = tag.getAttribute('deprecated')
579 if deprecated:
580 if filename:
581 ros_config.add_config_error("[%s] DEPRECATED: %s"%(filename, deprecated))
582 else:
583 ros_config.add_config_error("Deprecation Warning: "+deprecated)
584
585 INCLUDE_ATTRS = ('file', NS, CLEAR_PARAMS, 'pass_all_args')
586 @ifunless
587 - def _include_tag(self, tag, context, ros_config, default_machine, is_core, verbose):
588 self._check_attrs(tag, context, ros_config, XmlLoader.INCLUDE_ATTRS)
589 inc_filename = self.resolve_args(tag.attributes['file'].value, context)
590
591 if tag.hasAttribute('pass_all_args'):
592 pass_all_args = self.resolve_args(tag.attributes['pass_all_args'].value, context)
593 pass_all_args = _bool_attr(pass_all_args, False, 'pass_all_args')
594 else:
595 pass_all_args = False
596
597 child_ns = self._ns_clear_params_attr(tag.tagName, tag, context, ros_config, include_filename=inc_filename)
598
599
600
601 if pass_all_args:
602 if 'arg' in context.resolve_dict:
603 for name, value in context.resolve_dict['arg'].items():
604 child_ns.add_arg(name, value=value)
605
606
607 child_ns.pass_all_args = True
608
609 for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]:
610 tag_name = t.tagName.lower()
611 if tag_name == 'env':
612 self._env_tag(t, child_ns, ros_config)
613 elif tag_name == 'arg':
614 self._arg_tag(t, child_ns, ros_config, verbose=verbose)
615 else:
616 print("WARN: unrecognized '%s' tag in <%s> tag"%(t.tagName, tag.tagName), file=sys.stderr)
617
618
619 loader.process_include_args(child_ns)
620
621 try:
622 launch = self._parse_launch(inc_filename, verbose=verbose)
623 ros_config.add_roslaunch_file(inc_filename)
624 self._launch_tag(launch, ros_config, filename=inc_filename)
625 default_machine = \
626 self._recurse_load(ros_config, launch.childNodes, child_ns, \
627 default_machine, is_core, verbose)
628
629 if not pass_all_args:
630
631 loader.post_process_include_args(child_ns)
632
633 except ArgException as e:
634 raise XmlParseException("included file [%s] requires the '%s' arg to be set"%(inc_filename, str(e)))
635 except XmlParseException as e:
636 raise XmlParseException("while processing %s:\n%s"%(inc_filename, str(e)))
637 if verbose:
638 print("... done importing include file [%s]"%inc_filename)
639 return default_machine
640
641 GROUP_ATTRS = (NS, CLEAR_PARAMS)
642 - def _recurse_load(self, ros_config, tags, context, default_machine, is_core, verbose):
643 """
644 @return: new default machine for current context
645 @rtype: L{Machine}
646 """
647 for tag in [t for t in tags if t.nodeType == DomNode.ELEMENT_NODE]:
648 name = tag.tagName
649 if name == 'group':
650 if ifunless_test(self, tag, context):
651 self._check_attrs(tag, context, ros_config, XmlLoader.GROUP_ATTRS)
652 child_ns = self._ns_clear_params_attr(name, tag, context, ros_config)
653 child_ns.params = list(child_ns.params)
654 default_machine = \
655 self._recurse_load(ros_config, tag.childNodes, child_ns, \
656 default_machine, is_core, verbose)
657 elif name == 'node':
658 n = self._node_tag(tag, context, ros_config, default_machine, verbose=verbose)
659 if n is not None:
660 ros_config.add_node(n, core=is_core, verbose=verbose)
661 elif name == 'test':
662 t = self._node_tag(tag, context, ros_config, default_machine, is_test=True, verbose=verbose)
663 if t is not None:
664 ros_config.add_test(t, verbose=verbose)
665 elif name == 'param':
666 self._param_tag(tag, context, ros_config, verbose=verbose)
667 elif name == 'remap':
668 try:
669 r = self._remap_tag(tag, context, ros_config)
670 if r is not None:
671 context.add_remap(r)
672 except RLException as e:
673 raise XmlParseException("Invalid <remap> tag: %s.\nXML is %s"%(str(e), tag.toxml()))
674 elif name == 'machine':
675 val = self._machine_tag(tag, context, ros_config, verbose=verbose)
676 if val is not None:
677 (m, is_default) = val
678 if is_default:
679 default_machine = m
680 ros_config.add_machine(m, verbose=verbose)
681 elif name == 'rosparam':
682 self._rosparam_tag(tag, context, ros_config, verbose=verbose)
683 elif name == 'master':
684 pass
685 elif name == 'include':
686 val = self._include_tag(tag, context, ros_config, default_machine, is_core, verbose)
687 if val is not None:
688 default_machine = val
689 elif name == 'env':
690 self._env_tag(tag, context, ros_config)
691 elif name == 'arg':
692 self._arg_tag(tag, context, ros_config, verbose=verbose)
693 else:
694 ros_config.add_config_error("unrecognized tag "+tag.tagName)
695 return default_machine
696
697 - def _load_launch(self, launch, ros_config, is_core=False, filename=None, argv=None, verbose=True):
698 """
699 subroutine of launch for loading XML DOM into config. Load_launch assumes that it is
700 creating the root XmlContext, and is thus affected by command-line arguments.
701 @param launch: DOM node of the root <launch> tag in the file
702 @type launch: L{Node}
703 @param ros_config: launch configuration to load XML file into
704 @type ros_config: L{ROSLaunchConfig}
705 @param is_core: (optional) if True, load file using ROS core rules. Default False.
706 @type is_core: bool
707 @param filename: (optional) name of file being loaded
708 @type filename: str
709 @param verbose: (optional) print verbose output. Default False.
710 @type verbose: bool
711 @param argv: (optional) command-line args. Default sys.argv.
712 """
713 if argv is None:
714 argv = sys.argv
715
716 self._launch_tag(launch, ros_config, filename)
717 self.root_context = loader.LoaderContext(get_ros_namespace(argv=argv), filename)
718 loader.load_sysargs_into_context(self.root_context, argv)
719
720 if len(launch.getElementsByTagName('master')) > 0:
721 print("WARNING: ignoring defunct <master /> tag", file=sys.stderr)
722 self._recurse_load(ros_config, launch.childNodes, self.root_context, None, is_core, verbose)
723
725 try:
726 if verbose:
727 print("... loading XML file [%s]"%filename)
728 root = parse(filename).getElementsByTagName('launch')
729 except Exception as e:
730 raise XmlParseException("Invalid roslaunch XML syntax: %s"%e)
731 if len(root) != 1:
732 raise XmlParseException("Invalid roslaunch XML syntax: no root <launch> tag")
733 return root[0]
734
735 - def load(self, filename, ros_config, core=False, argv=None, verbose=True):
736 """
737 load XML file into launch configuration
738 @param filename: XML config file to load
739 @type filename: str
740 @param ros_config: launch configuration to load XML file into
741 @type ros_config: L{ROSLaunchConfig}
742 @param core: if True, load file using ROS core rules
743 @type core: bool
744 @param argv: override command-line arguments (mainly for arg testing)
745 @type argv: [str]
746 """
747 try:
748 launch = self._parse_launch(filename, verbose)
749 ros_config.add_roslaunch_file(filename)
750 self._load_launch(launch, ros_config, is_core=core, filename=filename, argv=argv, verbose=verbose)
751 except ArgException as e:
752 raise XmlParseException("[%s] requires the '%s' arg to be set"%(filename, str(e)))
753 except SubstitutionException as e:
754 raise XmlParseException(str(e))
755
756 - def load_string(self, xml_text, ros_config, core=False, verbose=True):
757 """
758 Load XML text into launch configuration
759 @param xml_text: XML configuration
760 @type xml_text: str
761 @param ros_config: launch configuration to load XML file into
762 @type ros_config: L{ROSLaunchConfig}
763 @param core: if True, load file using ROS core rules
764 @type core: bool
765 """
766 try:
767 if verbose:
768 print("... loading XML")
769 try:
770 if hasattr(xml_text,'encode') and isinstance(xml_text, unicode):
771
772
773
774 xml_text = xml_text.encode('utf-8')
775 except NameError:
776 pass
777 root = parseString(xml_text).getElementsByTagName('launch')
778 except Exception as e:
779 logging.getLogger('roslaunch').error("Invalid roslaunch XML syntax:\nstring[%s]\ntraceback[%s]"%(xml_text, traceback.format_exc()))
780 raise XmlParseException("Invalid roslaunch XML syntax: %s"%e)
781
782 if len(root) != 1:
783 raise XmlParseException("Invalid roslaunch XML syntax: no root <launch> tag")
784 self._load_launch(root[0], ros_config, core, filename='string', verbose=verbose)
785