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
184 context.resolve_dict['filename'] = context.filename
185 return substitution_args.resolve_args(args, context=context.resolve_dict, resolve_anon=self.resolve_anon)
186 else:
187 return args
188
190 """
191 Helper routine for fetching and resolving optional tag attributes
192 @param tag DOM tag
193 @param context LoaderContext
194 @param attrs (str): list of attributes to resolve
195 """
196 def tag_value(tag, a):
197 if tag.hasAttribute(a):
198
199
200
201 return tag.getAttribute(a)
202 else:
203 return None
204 return [self.resolve_args(tag_value(tag,a), context) for a in attrs]
205
207 """
208 Helper routine for fetching and resolving required tag attributes
209 @param tag: DOM tag
210 @param attrs: list of attributes to resolve
211 @type attrs: (str)
212 @raise KeyError: if required attribute is missing
213 """
214 return [self.resolve_args(tag.attributes[a].value, context) for a in attrs]
215
217 tag_attrs = tag.attributes.keys()
218 for t_a in tag_attrs:
219 if not t_a in attrs and not t_a in ['if', 'unless']:
220 ros_config.add_config_error("[%s] unknown <%s> attribute '%s'"%(context.filename, tag.tagName, t_a))
221
222
223
224
225 ROSPARAM_OPT_ATTRS = ('command', 'ns', 'file', 'param', 'subst_value')
226 @ifunless
227 - def _rosparam_tag(self, tag, context, ros_config, verbose=True):
228 try:
229 self._check_attrs(tag, context, ros_config, XmlLoader.ROSPARAM_OPT_ATTRS)
230 cmd, ns, file, param, subst_value = self.opt_attrs(tag, context, (XmlLoader.ROSPARAM_OPT_ATTRS))
231 subst_value = _bool_attr(subst_value, False, 'subst_value')
232
233 param = ns_join(ns or '', param or '')
234
235
236 cmd = cmd or 'load'
237 value = _get_text(tag)
238 subst_function = None
239 if subst_value:
240 subst_function = lambda x: self.resolve_args(x, context)
241 self.load_rosparam(context, ros_config, cmd, param, file, value, verbose=verbose, subst_function=subst_function)
242
243 except ValueError as e:
244 raise loader.LoadException("error loading <rosparam> tag: \n\t"+str(e)+"\nXML is %s"%tag.toxml())
245
246 PARAM_ATTRS = ('name', 'value', 'type', 'value', 'textfile', 'binfile', 'command')
247 @ifunless
248 - def _param_tag(self, tag, context, ros_config, force_local=False, verbose=True):
249 """
250 @param force_local: if True, param must be added to context instead of ros_config
251 @type force_local: bool
252 """
253 try:
254 self._check_attrs(tag, context, ros_config, XmlLoader.PARAM_ATTRS)
255
256
257 ptype = (tag.getAttribute('type') or 'auto').lower().strip()
258
259 vals = self.opt_attrs(tag, context, ('value', 'textfile', 'binfile', 'command'))
260 if len([v for v in vals if v is not None]) != 1:
261 raise XmlParseException(
262 "<param> tag must have one and only one of value/textfile/binfile.")
263
264
265
266 name = self.resolve_args(tag.attributes['name'].value.strip(), context)
267 value = self.param_value(verbose, name, ptype, *vals)
268
269 if is_private(name) or force_local:
270 p = Param(name, value)
271 context.add_param(p)
272 else:
273 p = Param(ns_join(context.ns, name), value)
274 ros_config.add_param(Param(ns_join(context.ns, name), value), filename=context.filename, verbose=verbose)
275 return p
276
277 except KeyError as e:
278 raise XmlParseException(
279 "<param> tag is missing required attribute: %s. \n\nParam xml is %s"%(e, tag.toxml()))
280 except ValueError as e:
281 raise XmlParseException(
282 "Invalid <param> tag: %s. \n\nParam xml is %s"%(e, tag.toxml()))
283
284 ARG_ATTRS = ('name', 'value', 'default', 'doc')
285 @ifunless
286 - def _arg_tag(self, tag, context, ros_config, verbose=True):
287 """
288 Process an <arg> tag.
289 """
290 try:
291 self._check_attrs(tag, context, ros_config, XmlLoader.ARG_ATTRS)
292 (name,) = self.reqd_attrs(tag, context, ('name',))
293 value, default, doc = self.opt_attrs(tag, context, ('value', 'default', 'doc'))
294
295 if value is not None and default is not None:
296 raise XmlParseException(
297 "<arg> tag must have one and only one of value/default.")
298
299 context.add_arg(name, value=value, default=default, doc=doc)
300
301 except substitution_args.ArgException as e:
302 raise XmlParseException(
303 "arg '%s' is not defined. \n\nArg xml is %s"%(e, tag.toxml()))
304 except Exception as e:
305 raise XmlParseException(
306 "Invalid <arg> tag: %s. \n\nArg xml is %s"%(e, tag.toxml()))
307
335
336 NODE_ATTRS = ['pkg', 'type', 'machine', 'name', 'args', 'output', \
337 'respawn', 'respawn_delay', 'cwd', NS, CLEAR_PARAMS, \
338 'launch-prefix', 'required']
339 TEST_ATTRS = NODE_ATTRS + ['test-name','time-limit', 'retry']
340
341 @ifunless
342 - def _node_tag(self, tag, context, ros_config, default_machine, is_test=False, verbose=True):
343 """
344 Process XML <node> or <test> tag
345 @param tag: DOM node
346 @type tag: Node
347 @param context: namespace context
348 @type context: L{LoaderContext}
349 @param params: ROS parameter list
350 @type params: [L{Param}]
351 @param clear_params: list of ROS parameter names to clear before setting parameters
352 @type clear_params: [str]
353 @param default_machine: default machine to assign to node
354 @type default_machine: str
355 @param is_test: if set, will load as L{Test} object instead of L{Node} object
356 @type is_test: bool
357 """
358 try:
359 if is_test:
360 self._check_attrs(tag, context, ros_config, XmlLoader.TEST_ATTRS)
361 (name,) = self.opt_attrs(tag, context, ('name',))
362 test_name, time_limit, retry = self._test_attrs(tag, context)
363 if not name:
364 name = test_name
365 else:
366 self._check_attrs(tag, context, ros_config, XmlLoader.NODE_ATTRS)
367 (name,) = self.reqd_attrs(tag, context, ('name',))
368
369 if not is_legal_name(name):
370 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()))
371
372 child_ns = self._ns_clear_params_attr('node', tag, context, ros_config, node_name=name)
373 param_ns = child_ns.child(name)
374 param_ns.params = []
375
376
377 pkg, node_type = self.reqd_attrs(tag, context, ('pkg', 'type'))
378
379
380 machine, args, output, respawn, respawn_delay, cwd, launch_prefix, \
381 required = self.opt_attrs(tag, context, ('machine', 'args',
382 'output', 'respawn', 'respawn_delay', 'cwd',
383 'launch-prefix', 'required'))
384 if tag.hasAttribute('machine') and not len(machine.strip()):
385 raise XmlParseException("<node> 'machine' must be non-empty: [%s]"%machine)
386 if not machine and default_machine:
387 machine = default_machine.name
388
389 required, respawn = [_bool_attr(*rr) for rr in ((required, False, 'required'),\
390 (respawn, False, 'respawn'))]
391 respawn_delay = _float_attr(respawn_delay, 0.0, 'respawn_delay')
392
393
394
395 remap_context = context.child('')
396
397
398
399 env_context = context.child('')
400
401
402
403 for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]:
404 tag_name = t.tagName.lower()
405 if tag_name == 'remap':
406 r = self._remap_tag(t, context, ros_config)
407 if r is not None:
408 remap_context.add_remap(r)
409 elif tag_name == 'param':
410 self._param_tag(t, param_ns, ros_config, force_local=True, verbose=verbose)
411 elif tag_name == 'rosparam':
412 self._rosparam_tag(t, param_ns, ros_config, verbose=verbose)
413 elif tag_name == 'env':
414 self._env_tag(t, env_context, ros_config)
415 else:
416 ros_config.add_config_error("WARN: unrecognized '%s' child tag in the parent tag element: %s"%(t.tagName, tag.toxml()))
417
418
419
420 for p in itertools.chain(context.params, param_ns.params):
421 pkey = p.key
422 if is_private(pkey):
423
424 pkey = pkey[1:]
425 pkey = param_ns.ns + pkey
426 ros_config.add_param(Param(pkey, p.value), verbose=verbose)
427
428 if not is_test:
429 return Node(pkg, node_type, name=name, namespace=child_ns.ns, machine_name=machine,
430 args=args, respawn=respawn,
431 respawn_delay=respawn_delay,
432 remap_args=remap_context.remap_args(), env_args=env_context.env_args,
433 output=output, cwd=cwd, launch_prefix=launch_prefix,
434 required=required, filename=context.filename)
435 else:
436 return Test(test_name, pkg, node_type, name=name, namespace=child_ns.ns,
437 machine_name=machine, args=args,
438 remap_args=remap_context.remap_args(), env_args=env_context.env_args,
439 time_limit=time_limit, cwd=cwd, launch_prefix=launch_prefix,
440 retry=retry, filename=context.filename)
441 except KeyError as e:
442 raise XmlParseException(
443 "<%s> tag is missing required attribute: %s. Node xml is %s"%(tag.tagName, e, tag.toxml()))
444 except XmlParseException as e:
445 raise XmlParseException(
446 "Invalid <node> tag: %s. \n\nNode xml is %s"%(e, tag.toxml()))
447 except ValueError as e:
448 raise XmlParseException(
449 "Invalid <node> tag: %s. \n\nNode xml is %s"%(e, tag.toxml()))
450
451 MACHINE_ATTRS = ('name', 'address', 'env-loader',
452 'ssh-port', 'user', 'password', 'default', 'timeout')
453 @ifunless
454 - def _machine_tag(self, tag, context, ros_config, verbose=True):
455 try:
456
457 context = context.child(None)
458
459
460 attrs = self.opt_attrs(tag, context,
461 ('ros-root', 'ros-package-path', 'ros-ip', 'ros-hostname'))
462 if any(attrs):
463 raise XmlParseException("<machine>: ros-* attributes are not supported since ROS Fuerte.\nPlease use env-loader instead")
464
465 self._check_attrs(tag, context, ros_config, XmlLoader.MACHINE_ATTRS)
466
467 name, address = self.reqd_attrs(tag, context, ('name', 'address'))
468
469
470 attrs = self.opt_attrs(tag, context,
471 ('env-loader',
472 'ssh-port', 'user', 'password', 'default', 'timeout'))
473 env_loader, ssh_port, user, password, default, timeout = attrs
474
475 ssh_port = int(ssh_port or '22')
476
477
478 default = (default or 'false').lower()
479 try:
480 assignable = _assignable[default]
481 is_default = _is_default[default]
482 except KeyError as e:
483 raise XmlParseException("Invalid value for 'attribute': %s"%default)
484
485
486 for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]:
487 if t.tagName == 'env':
488 raise XmlParseException("<machine>: <env> tag is not supported since ROS Fuerte.\nPlease use env-loader instead")
489 else:
490 ros_config.add_config_error("unrecognized '%s' tag in <%s> tag"%(t.tagName, tag.tagName))
491
492 if timeout:
493 try:
494 timeout = float(timeout)
495 except ValueError:
496 raise XmlParseException("'timeout' be a number: [%s]"%timeout)
497 elif timeout == '':
498 raise XmlParseException("'timeout' cannot be empty")
499 if timeout is not None and timeout <= 0.:
500 raise XmlParseException("'timeout' be a positive number: [%s]"%timeout)
501
502 m = Machine(name, address, env_loader=env_loader,
503 ssh_port=ssh_port, user=user, password=password,
504 assignable=assignable, env_args=context.env_args, timeout=timeout)
505 return (m, is_default)
506 except KeyError as e:
507 raise XmlParseException("<machine> tag is missing required attribute: %s"%e)
508 except SubstitutionException as e:
509 raise XmlParseException(
510 "%s. \n\nMachine xml is %s"%(e, tag.toxml()))
511 except RLException as e:
512 raise XmlParseException(
513 "%s. \n\nMachine xml is %s"%(e, tag.toxml()))
514
515 REMAP_ATTRS = ('from', 'to')
516 @ifunless
523
524 ENV_ATTRS = ('name', 'value')
525 @ifunless
526 - def _env_tag(self, tag, context, ros_config):
534
535 - def _ns_clear_params_attr(self, tag_name, tag, context, ros_config, node_name=None, include_filename=None):
536 """
537 Common processing routine for xml tags with NS and CLEAR_PARAMS attributes
538
539 @param tag: DOM Node
540 @type tag: Node
541 @param context: current namespace context
542 @type context: LoaderContext
543 @param clear_params: list of params to clear
544 @type clear_params: [str]
545 @param node_name: name of node (for use when tag_name == 'node')
546 @type node_name: str
547 @param include_filename: <include> filename if this is an <include> tag. If specified, context will use include rules.
548 @type include_filename: str
549 @return: loader context
550 @rtype: L{LoaderContext}
551 """
552 if tag.hasAttribute(NS):
553 ns = self.resolve_args(tag.getAttribute(NS), context)
554 if not ns:
555 raise XmlParseException("<%s> tag has an empty '%s' attribute"%(tag_name, NS))
556 else:
557 ns = None
558 if include_filename is not None:
559 child_ns = context.include_child(ns, include_filename)
560 else:
561 child_ns = context.child(ns)
562 clear_p = self.resolve_args(tag.getAttribute(CLEAR_PARAMS), context)
563 if clear_p:
564 clear_p = _bool_attr(clear_p, False, 'clear_params')
565 if clear_p:
566 if tag_name == 'node':
567 if not node_name:
568 raise XmlParseException("<%s> tag must have a 'name' attribute to use '%s' attribute"%(tag_name, CLEAR_PARAMS))
569
570 ros_config.add_clear_param(make_global_ns(ns_join(child_ns.ns, node_name)))
571 else:
572 if not ns:
573 raise XmlParseException("'ns' attribute must be set in order to use 'clear_params'")
574 ros_config.add_clear_param(child_ns.ns)
575 return child_ns
576
577 @ifunless
578 - def _launch_tag(self, tag, ros_config, filename=None):
579
580 deprecated = tag.getAttribute('deprecated')
581 if deprecated:
582 if filename:
583 ros_config.add_config_error("[%s] DEPRECATED: %s"%(filename, deprecated))
584 else:
585 ros_config.add_config_error("Deprecation Warning: "+deprecated)
586
587 INCLUDE_ATTRS = ('file', NS, CLEAR_PARAMS, 'pass_all_args')
588 @ifunless
589 - def _include_tag(self, tag, context, ros_config, default_machine, is_core, verbose):
590 self._check_attrs(tag, context, ros_config, XmlLoader.INCLUDE_ATTRS)
591 inc_filename = self.resolve_args(tag.attributes['file'].value, context)
592
593 if tag.hasAttribute('pass_all_args'):
594 pass_all_args = self.resolve_args(tag.attributes['pass_all_args'].value, context)
595 pass_all_args = _bool_attr(pass_all_args, False, 'pass_all_args')
596 else:
597 pass_all_args = False
598
599 child_ns = self._ns_clear_params_attr(tag.tagName, tag, context, ros_config, include_filename=inc_filename)
600
601
602
603 if pass_all_args:
604 if 'arg' in context.resolve_dict:
605 for name, value in context.resolve_dict['arg'].items():
606 child_ns.add_arg(name, value=value)
607
608
609 child_ns.pass_all_args = True
610
611 for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]:
612 tag_name = t.tagName.lower()
613 if tag_name == 'env':
614 self._env_tag(t, child_ns, ros_config)
615 elif tag_name == 'arg':
616 self._arg_tag(t, child_ns, ros_config, verbose=verbose)
617 else:
618 print("WARN: unrecognized '%s' tag in <%s> tag"%(t.tagName, tag.tagName), file=sys.stderr)
619
620
621 loader.process_include_args(child_ns)
622
623 try:
624 launch = self._parse_launch(inc_filename, verbose=verbose)
625 ros_config.add_roslaunch_file(inc_filename)
626 self._launch_tag(launch, ros_config, filename=inc_filename)
627 default_machine = \
628 self._recurse_load(ros_config, launch.childNodes, child_ns, \
629 default_machine, is_core, verbose)
630
631
632 loader.post_process_include_args(child_ns)
633
634 except ArgException as e:
635 raise XmlParseException("included file [%s] requires the '%s' arg to be set"%(inc_filename, str(e)))
636 except XmlParseException as e:
637 raise XmlParseException("while processing %s:\n%s"%(inc_filename, str(e)))
638 if verbose:
639 print("... done importing include file [%s]"%inc_filename)
640 return default_machine
641
642 GROUP_ATTRS = (NS, CLEAR_PARAMS)
643 - def _recurse_load(self, ros_config, tags, context, default_machine, is_core, verbose):
644 """
645 @return: new default machine for current context
646 @rtype: L{Machine}
647 """
648 for tag in [t for t in tags if t.nodeType == DomNode.ELEMENT_NODE]:
649 name = tag.tagName
650 if name == 'group':
651 if ifunless_test(self, tag, context):
652 self._check_attrs(tag, context, ros_config, XmlLoader.GROUP_ATTRS)
653 child_ns = self._ns_clear_params_attr(name, tag, context, ros_config)
654 child_ns.params = list(child_ns.params)
655 default_machine = \
656 self._recurse_load(ros_config, tag.childNodes, child_ns, \
657 default_machine, is_core, verbose)
658 elif name == 'node':
659 n = self._node_tag(tag, context, ros_config, default_machine, verbose=verbose)
660 if n is not None:
661 ros_config.add_node(n, core=is_core, verbose=verbose)
662 elif name == 'test':
663 t = self._node_tag(tag, context, ros_config, default_machine, is_test=True, verbose=verbose)
664 if t is not None:
665 ros_config.add_test(t, verbose=verbose)
666 elif name == 'param':
667 self._param_tag(tag, context, ros_config, verbose=verbose)
668 elif name == 'remap':
669 try:
670 r = self._remap_tag(tag, context, ros_config)
671 if r is not None:
672 context.add_remap(r)
673 except RLException as e:
674 raise XmlParseException("Invalid <remap> tag: %s.\nXML is %s"%(str(e), tag.toxml()))
675 elif name == 'machine':
676 val = self._machine_tag(tag, context, ros_config, verbose=verbose)
677 if val is not None:
678 (m, is_default) = val
679 if is_default:
680 default_machine = m
681 ros_config.add_machine(m, verbose=verbose)
682 elif name == 'rosparam':
683 self._rosparam_tag(tag, context, ros_config, verbose=verbose)
684 elif name == 'master':
685 pass
686 elif name == 'include':
687 val = self._include_tag(tag, context, ros_config, default_machine, is_core, verbose)
688 if val is not None:
689 default_machine = val
690 elif name == 'env':
691 self._env_tag(tag, context, ros_config)
692 elif name == 'arg':
693 self._arg_tag(tag, context, ros_config, verbose=verbose)
694 else:
695 ros_config.add_config_error("unrecognized tag "+tag.tagName)
696 return default_machine
697
698 - def _load_launch(self, launch, ros_config, is_core=False, filename=None, argv=None, verbose=True):
699 """
700 subroutine of launch for loading XML DOM into config. Load_launch assumes that it is
701 creating the root XmlContext, and is thus affected by command-line arguments.
702 @param launch: DOM node of the root <launch> tag in the file
703 @type launch: L{Node}
704 @param ros_config: launch configuration to load XML file into
705 @type ros_config: L{ROSLaunchConfig}
706 @param is_core: (optional) if True, load file using ROS core rules. Default False.
707 @type is_core: bool
708 @param filename: (optional) name of file being loaded
709 @type filename: str
710 @param verbose: (optional) print verbose output. Default False.
711 @type verbose: bool
712 @param argv: (optional) command-line args. Default sys.argv.
713 """
714 if argv is None:
715 argv = sys.argv
716
717 self._launch_tag(launch, ros_config, filename)
718 self.root_context = loader.LoaderContext(get_ros_namespace(), filename)
719 loader.load_sysargs_into_context(self.root_context, argv)
720
721 if len(launch.getElementsByTagName('master')) > 0:
722 print("WARNING: ignoring defunct <master /> tag", file=sys.stderr)
723 self._recurse_load(ros_config, launch.childNodes, self.root_context, None, is_core, verbose)
724
726 try:
727 if verbose:
728 print("... loading XML file [%s]"%filename)
729 root = parse(filename).getElementsByTagName('launch')
730 except Exception as e:
731 raise XmlParseException("Invalid roslaunch XML syntax: %s"%e)
732 if len(root) != 1:
733 raise XmlParseException("Invalid roslaunch XML syntax: no root <launch> tag")
734 return root[0]
735
736 - def load(self, filename, ros_config, core=False, argv=None, verbose=True):
737 """
738 load XML file into launch configuration
739 @param filename: XML config file to load
740 @type filename: str
741 @param ros_config: launch configuration to load XML file into
742 @type ros_config: L{ROSLaunchConfig}
743 @param core: if True, load file using ROS core rules
744 @type core: bool
745 @param argv: override command-line arguments (mainly for arg testing)
746 @type argv: [str]
747 """
748 try:
749 launch = self._parse_launch(filename, verbose)
750 ros_config.add_roslaunch_file(filename)
751 self._load_launch(launch, ros_config, is_core=core, filename=filename, argv=argv, verbose=verbose)
752 except ArgException as e:
753 raise XmlParseException("[%s] requires the '%s' arg to be set"%(filename, str(e)))
754 except SubstitutionException as e:
755 raise XmlParseException(str(e))
756
757 - def load_string(self, xml_text, ros_config, core=False, verbose=True):
758 """
759 Load XML text into launch configuration
760 @param xml_text: XML configuration
761 @type xml_text: str
762 @param ros_config: launch configuration to load XML file into
763 @type ros_config: L{ROSLaunchConfig}
764 @param core: if True, load file using ROS core rules
765 @type core: bool
766 """
767 try:
768 if verbose:
769 print("... loading XML")
770 try:
771 if hasattr(xml_text,'encode') and isinstance(xml_text, unicode):
772
773
774
775 xml_text = xml_text.encode('utf-8')
776 except NameError:
777 pass
778 root = parseString(xml_text).getElementsByTagName('launch')
779 except Exception as e:
780 logging.getLogger('roslaunch').error("Invalid roslaunch XML syntax:\nstring[%s]\ntraceback[%s]"%(xml_text, traceback.format_exc()))
781 raise XmlParseException("Invalid roslaunch XML syntax: %s"%e)
782
783 if len(root) != 1:
784 raise XmlParseException("Invalid roslaunch XML syntax: no root <launch> tag")
785 self._load_launch(root[0], ros_config, core, filename='string', verbose=verbose)
786