def test_is_legal_name(): from rosgraph.names import is_legal_name failures = [ None, 'foo++', 'foo-bar', '#foo', 'hello\n', '\t', ' name', 'name ', 'f//b', '1name', 'foo\\' ] for f in failures: assert not is_legal_name(f), f tests = [ '', 'f', 'f1', 'f_', 'f/', 'foo', 'foo_bar', 'foo/bar', 'foo/bar/baz', '~f', '~a/b/c', '~/f', '/a/b/c/d', '/' ] for t in tests: assert is_legal_name(t), "[%s]" % t
def get_sys_hostname(): """ If the system hostname is also a valid ROS name, return the hostname. Otherwise, return the first 6 digits of the md5sum of the hostname """ hostname = rosgraph.network.get_host_name() return hostname if is_legal_name(hostname) else hashlib.md5( hostname).hexdigest()[:6]
def test_is_legal_name(): from rosgraph.names import is_legal_name failures = [None, 'foo++', 'foo-bar', '#foo', 'hello\n', '\t', ' name', 'name ', 'f//b', '1name', 'foo\\'] for f in failures: assert not is_legal_name(f), f tests = ['', 'f', 'f1', 'f_', 'f/', 'foo', 'foo_bar', 'foo/bar', 'foo/bar/baz', '~f', '~a/b/c', '~/f', '/a/b/c/d', '/'] for t in tests: assert is_legal_name(t), "[%s]"%t
def test_anonymous_name(): from rosgraph.names import anonymous_name, is_legal_name val = anonymous_name('foo') assert 'foo' in val assert 'foo' != val assert val != anonymous_name('foo') assert not '/' in val assert is_legal_name(val)
def toHTML(cls, text, check_for_ros_names=True, is_node=False, palette=None): ''' Creates a HTML representation of the given text. It could be a node, topic service or group name. :param str text: a name with ROS representation :return: the HTML representation of the given name :rtype: str ''' if text.rfind('@') > 0: # handle host names name, sep, host = text.rpartition('@') result = '' if sep: result = '%s<span style="color:%s;">%s%s</span>' % (name, cls.color_name(palette, QPalette.ButtonText), sep, host) else: result = text elif text.find('{') > -1: # handle group names text = text.strip('{}') ns, sep, name = text.rpartition('/') result = '' if sep: result = '{<span style="color:%s;">%s%s</span>%s}' % (cls.color_name(palette, QPalette.ButtonText), ns, sep, name) else: result = '<span style="color:%s;">{%s}</span>' % (cls.color_name(palette, QPalette.ButtonText), name) # result = '<b>{</b><span style="color:gray;">%s</span><b>}</b>' % (name) # result = '<b>{%s}</b>' % (name) elif text.find('[') > -1: start_idx = text.find('[') end_idx = text.find(']', start_idx) nr_idx = text.find(':') last_part = "" color = cls.color_name(palette, QPalette.ButtonText) if end_idx + 1 < len(text): last_part = text[end_idx + 1:] if nr_idx > -1 and nr_idx < start_idx: result = '%s<b>%s</b><span style="color:%s;">%s</span><b>%s</b>' % (text[0:nr_idx + 1], text[nr_idx + 1:start_idx], color, text[start_idx:end_idx + 1], last_part) else: result = '<b>%s</b><span style="color:%s;">%s</span><b>%s</b>' % (text[0:start_idx], color, text[start_idx:end_idx + 1], last_part) elif text.startswith('<arg_not_set>'): result = '<span style="color:#0000FF;">%s</span>' % (text.replace('<arg_not_set>', '')) elif text.startswith('<arg>'): result = text.replace('<arg>', '') elif check_for_ros_names and not is_legal_name(text): # handle all invalid names (used space in the name) ns, sep, name = text.rpartition('/') result = '' if sep: result = '<span style="color:#FF6600;">%s%s<b>%s</b></span' % (ns, sep, name) else: result = '<span style="color:#FF6600;">%s</span>' % (name) else: # handle all ROS names ns, sep, name = text.rpartition('/') result = '' if sep: result = '<span style="color:%s;">%s%s</span><b>%s</b>' % (cls.color_name(palette, QPalette.ButtonText), ns, sep, name) elif is_node: result = '<b>%s</b>' % name else: result = name return result
def toHTML(cls, text): ''' Creates a HTML representation of the given text. It could be a node, topic service or group name. @param text: a name with ROS representation @type text: C{str} @return: the HTML representation of the given name @rtype: C{str} ''' if text.rfind('@') > 0: # handle host names name, sep, host = text.rpartition('@') result = '' if sep: result = '%s<span style="color:gray;">%s%s</span>' % ( name, sep, host) else: result = text elif text.find('{') > -1: # handle group names text = text.strip('{}') ns, sep, name = text.rpartition('/') result = '' if sep: result = '<b>{</b><span style="color:gray;">%s%s</span><b>%s}</b>' % ( ns, sep, name) else: result = '<b>{%s}</b>' % (name) elif text.find('[') > -1: start_idx = text.find('[') end_idx = text.find(']', start_idx) nr_idx = text.find(':') last_part = "" if end_idx + 1 < len(text): last_part = text[end_idx + 1:] if nr_idx > -1: result = '%s<b>%s</b><span style="color:gray;">%s</span><b>%s</b>' % ( text[0:nr_idx + 1], text[nr_idx + 1:start_idx], text[start_idx:end_idx + 1], last_part) else: result = '<b>%s</b><span style="color:gray;">%s</span><b>%s</b>' % ( text[0:start_idx], text[start_idx:end_idx + 1], last_part) elif not is_legal_name( text): # handle all invalid names (used space in the name) ns, sep, name = text.rpartition('/') result = '' if sep: result = '<span style="color:#FF6600;">%s%s<b>%s</b></span' % ( ns, sep, name) else: result = '<span style="color:#FF6600;">%s</span>' % (name) else: # handle all ROS names ns, sep, name = text.rpartition('/') result = '' if sep: result = '<span style="color:gray;">%s%s</span><b>%s</b>' % ( ns, sep, name) else: result = name return result
def add_remap(self, remap): """ Add a new remap setting to the context. if a remap already exists with the same from key, it will be removed @param remap: remap setting @type remap: (str, str) """ remap = [canonicalize_name(x) for x in remap] if not remap[0] or not remap[1]: raise RLException("remap from/to attributes cannot be empty") if not is_legal_name(remap[0]): raise RLException("remap from [%s] is not a valid ROS name"%remap[0]) if not is_legal_name(remap[1]): raise RLException("remap to [%s] is not a valid ROS name"%remap[1]) matches = [r for r in self._remap_args if r[0] == remap[0]] for m in matches: self._remap_args.remove(m) self._remap_args.append(remap)
def toHTML(cls, text): ''' Creates a HTML representation of the given text. It could be a node, topic service or group name. @param text: a name with ROS representation @type text: C{str} @return: the HTML representation of the given name @rtype: C{str} ''' if text.rfind('@') > 0: # handle host names name, sep, host = text.rpartition('@') result = '' if sep: result = '<div>%s<span style="color:gray;">%s%s</span></div>' % (name, sep, host) else: result = text elif text.find('{') > -1: # handle group names text = text.strip('{}') ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><b>{</b><span style="color:gray;">%s%s</span><b>%s}</b></div>' % (ns, sep, name) else: result = '<div><b>{%s}</b></div>' % (name) elif text.find('[') > -1: nr_idx = text.find(':') pkg_idx = text.find('[', nr_idx) if nr_idx > -1: result = '<div>%s<b>%s</b><span style="color:gray;">%s</span></div>' % (text[0:nr_idx + 1], text[nr_idx + 1:pkg_idx], text[pkg_idx:]) else: result = '<div>%s<span style="color:gray;">%s</span></div>' % (text[0:pkg_idx], text[pkg_idx:]) elif not is_legal_name(text): # handle all invalid names (used space in the name) ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><span style="color:#FF6600;">%s%s<b>%s</b></span></div>' % (ns, sep, name) else: result = '<div><span style="color:#FF6600;">%s</span></div>' % (name) else: # handle all ROS names ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><span style="color:gray;">%s%s</span><b>%s</b></div>' % (ns, sep, name) else: result = name return result
def toHTML(cls, text): ''' Creates a HTML representation of the given text. It could be a node, topic service or group name. @param text: a name with ROS representation @type text: C{str} @return: the HTML representation of the given name @rtype: C{str} ''' if text.rfind('@') > 0: # handle host names name, sep, host = text.rpartition('@') result = '' if sep: result = '<div>%s<span style="color:gray;">%s%s</span></div>' % ( name, sep, host) else: result = text elif text.find('{') > -1: # handle group names text = text.strip('{}') ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><b>{</b><span style="color:gray;">%s%s</span><b>%s}</b></div>' % ( ns, sep, name) else: result = '<div><b>{%s}</b></div>' % (name) elif not is_legal_name( text): # handle all invalid names (used space in the name) ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><span style="color:#FF6600;">%s%s<b>%s</b></span></div>' % ( ns, sep, name) else: result = '<div><span style="color:#FF6600;">%s</span></div>' % ( name) else: # handle all ROS names ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><span style="color:gray;">%s%s</span><b>%s</b></div>' % ( ns, sep, name) else: result = name return result
def toHTML(cls, text): ''' Creates a HTML representation of the topic name. @param topic_name: the topic name @type topic_name: C{str} @return: the HTML representation of the topic name @rtype: C{str} ''' if text.rfind('@') > 0: # handle host names name, sep, host = text.rpartition('@') result = '' if sep: result = '<div>%s<span style="color:gray;">%s%s</span></div>'%(name, sep, host) else: result = text elif text.find('{') > -1: # handle group names text = text.strip('{}') ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><b>{</b><span style="color:gray;">%s%s</span><b>%s}</b></div>'%(ns, sep, name) else: result = '<div><b>{%s}</b></div>'%(name) elif not is_legal_name(text): # handle all invalid names (used space in the name) ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><span style="color:#FF6600;">%s%s<b>%s</b></span></div>'%(ns, sep, name) else: result = '<div><span style="color:#FF6600;">%s</span></div>'%(name) else: # handle all ROS names ns, sep, name = text.rpartition('/') result = '' if sep: result = '<div><span style="color:gray;">%s%s</span><b>%s</b></div>'%(ns, sep, name) else: result = name return result
def get_ros_hostname(): """ Try to get ROS_HOSTNAME environment variable. returns: a ROS compatible hostname, or None. """ ros_hostname = os.environ.get('ROS_HOSTNAME') return ros_hostname if is_legal_name(ros_hostname) else None
def get_sys_hostname(): """ If the system hostname is also a valid ROS name, return the hostname. Otherwise, return the first 6 digits of the md5sum of the hostname """ hostname = rosgraph.network.get_host_name() return hostname if is_legal_name(hostname) else hashlib.md5(hostname).hexdigest()[:6]
def _node_tag(self, tag, context, ros_config, default_machine, is_test=False, verbose=True): """ Process XML <node> or <test> tag @param tag: DOM node @type tag: Node @param context: namespace context @type context: L{LoaderContext} @param params: ROS parameter list @type params: [L{Param}] @param clear_params: list of ROS parameter names to clear before setting parameters @type clear_params: [str] @param default_machine: default machine to assign to node @type default_machine: str @param is_test: if set, will load as L{Test} object instead of L{Node} object @type is_test: bool """ try: if is_test: self._check_attrs(tag, context, ros_config, XmlLoader.TEST_ATTRS) (name, ) = self.opt_attrs(tag, context, ('name', )) test_name, time_limit, retry = self._test_attrs(tag, context) if not name: name = test_name else: self._check_attrs(tag, context, ros_config, XmlLoader.NODE_ATTRS) (name, ) = self.reqd_attrs(tag, context, ('name', )) if not is_legal_name(name): 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())) child_ns = self._ns_clear_params_attr('node', tag, context, ros_config, node_name=name) param_ns = child_ns.child(name) param_ns.params = [ ] # This is necessary because child() does not make a copy of the param list. # required attributes pkg, node_type = self.reqd_attrs(tag, context, ('pkg', 'type')) # optional attributes machine, args, output, respawn, respawn_delay, cwd, launch_prefix, \ required = self.opt_attrs(tag, context, ('machine', 'args', 'output', 'respawn', 'respawn_delay', 'cwd', 'launch-prefix', 'required')) if tag.hasAttribute('machine') and not len(machine.strip()): raise XmlParseException( "<node> 'machine' must be non-empty: [%s]" % machine) if not machine and default_machine: machine = default_machine.name # validate respawn, required required, respawn = [_bool_attr(*rr) for rr in ((required, False, 'required'),\ (respawn, False, 'respawn'))] respawn_delay = _float_attr(respawn_delay, 0.0, 'respawn_delay') # each node gets its own copy of <remap> arguments, which # it inherits from its parent remap_context = context.child('') # each node gets its own copy of <env> arguments, which # it inherits from its parent env_context = context.child('') # nodes can have individual env args set in addition to # the ROS-specific ones. for t in [ c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE ]: tag_name = t.tagName.lower() if tag_name == 'remap': r = self._remap_tag(t, context, ros_config) if r is not None: remap_context.add_remap(r) elif tag_name == 'param': self._param_tag(t, param_ns, ros_config, force_local=True, verbose=verbose) elif tag_name == 'rosparam': self._rosparam_tag(t, param_ns, ros_config, verbose=verbose) elif tag_name == 'env': self._env_tag(t, env_context, ros_config) else: ros_config.add_config_error( "WARN: unrecognized '%s' child tag in the parent tag element: %s" % (t.tagName, tag.toxml())) # #1036 evaluate all ~params in context # TODO: can we get rid of force_local (above), remove this for loop, and just rely on param_tag logic instead? for p in itertools.chain(context.params, param_ns.params): pkey = p.key if is_private(pkey): # strip leading ~, which is optional/inferred pkey = pkey[1:] pkey = param_ns.ns + pkey ros_config.add_param(Param(pkey, p.value), verbose=verbose) if not is_test: return Node(pkg, node_type, name=name, namespace=child_ns.ns, machine_name=machine, args=args, respawn=respawn, respawn_delay=respawn_delay, remap_args=remap_context.remap_args(), env_args=env_context.env_args, output=output, cwd=cwd, launch_prefix=launch_prefix, required=required, filename=context.filename) else: return Test(test_name, pkg, node_type, name=name, namespace=child_ns.ns, machine_name=machine, args=args, remap_args=remap_context.remap_args(), env_args=env_context.env_args, time_limit=time_limit, cwd=cwd, launch_prefix=launch_prefix, retry=retry, filename=context.filename) except KeyError as e: raise XmlParseException( "<%s> tag is missing required attribute: %s. Node xml is %s" % (tag.tagName, e, tag.toxml())) except XmlParseException as e: raise XmlParseException( "Invalid <node> tag: %s. \n\nNode xml is %s" % (e, tag.toxml())) except ValueError as e: raise XmlParseException( "Invalid <node> tag: %s. \n\nNode xml is %s" % (e, tag.toxml()))
def _node_tag(self, tag, context, ros_config, default_machine, is_test=False, verbose=True): """ Process XML <node> or <test> tag @param tag: DOM node @type tag: Node @param context: namespace context @type context: L{LoaderContext} @param params: ROS parameter list @type params: [L{Param}] @param clear_params: list of ROS parameter names to clear before setting parameters @type clear_params: [str] @param default_machine: default machine to assign to node @type default_machine: str @param is_test: if set, will load as L{Test} object instead of L{Node} object @type is_test: bool """ try: if is_test: self._check_attrs(tag, context, ros_config, XmlLoader.TEST_ATTRS) (name,) = self.opt_attrs(tag, context, ('name',)) test_name, time_limit, retry = self._test_attrs(tag, context) if not name: name = test_name else: self._check_attrs(tag, context, ros_config, XmlLoader.NODE_ATTRS) (name,) = self.reqd_attrs(tag, context, ('name',)) if not is_legal_name(name): 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())) child_ns = self._ns_clear_params_attr('node', tag, context, ros_config, node_name=name) param_ns = child_ns.child(name) # required attributes pkg, node_type = self.reqd_attrs(tag, context, ('pkg', 'type')) # optional attributes machine, args, output, respawn, cwd, launch_prefix, required = \ self.opt_attrs(tag, context, ('machine', 'args', 'output', 'respawn', 'cwd', 'launch-prefix', 'required')) if tag.hasAttribute('machine') and not len(machine.strip()): raise XmlParseException("<node> 'machine' must be non-empty: [%s]"%machine) if not machine and default_machine: machine = default_machine.name # validate respawn, required required, respawn = [_bool_attr(*rr) for rr in ((required, False, 'required'),\ (respawn, False, 'respawn'))] # each node gets its own copy of <remap> arguments, which # it inherits from its parent remap_context = context.child('') # nodes can have individual env args set in addition to # the ROS-specific ones. for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]: tag_name = t.tagName.lower() if tag_name == 'remap': r = self._remap_tag(t, context, ros_config) if r is not None: remap_context.add_remap(r) elif tag_name == 'param': self._param_tag(t, param_ns, ros_config, force_local=True, verbose=verbose) elif tag_name == 'rosparam': self._rosparam_tag(t, param_ns, ros_config, verbose=verbose) elif tag_name == 'env': self._env_tag(t, context, ros_config) else: ros_config.add_config_error("WARN: unrecognized '%s' tag in <node> tag. Node xml is %s"%(t.tagName, tag.toxml())) # #1036 evaluate all ~params in context # TODO: can we get rid of force_local (above), remove this for loop, and just rely on param_tag logic instead? for p in itertools.chain(context.params, param_ns.params): pkey = p.key if is_private(pkey): # strip leading ~, which is optional/inferred pkey = pkey[1:] pkey = param_ns.ns + pkey ros_config.add_param(Param(pkey, p.value), verbose=verbose) if not is_test: return Node(pkg, node_type, name=name, namespace=child_ns.ns, machine_name=machine, args=args, respawn=respawn, remap_args=remap_context.remap_args(), env_args=context.env_args, output=output, cwd=cwd, launch_prefix=launch_prefix, required=required, filename=context.filename) else: return Test(test_name, pkg, node_type, name=name, namespace=child_ns.ns, machine_name=machine, args=args, remap_args=remap_context.remap_args(), env_args=context.env_args, time_limit=time_limit, cwd=cwd, launch_prefix=launch_prefix, retry=retry, filename=context.filename) except KeyError as e: raise XmlParseException( "<%s> tag is missing required attribute: %s. Node xml is %s"%(tag.tagName, e, tag.toxml())) except XmlParseException as e: raise XmlParseException( "Invalid <node> tag: %s. \n\nNode xml is %s"%(e, tag.toxml())) except ValueError as e: raise XmlParseException( "Invalid <node> tag: %s. \n\nNode xml is %s"%(e, tag.toxml()))