Package rospy :: Module names

Source Code for Module rospy.names

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2008, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32  # 
 33  # Revision $Id$ 
 34   
 35  """ 
 36  Support for ROS Names 
 37   
 38  See: U{http://www.ros.org/wiki/Names} 
 39  """ 
 40   
 41  import sys 
 42  import os 
 43   
 44  from rosgraph.names import namespace, get_ros_namespace, ns_join, make_global_ns, load_mappings, \ 
 45       SEP, GLOBALNS, REMAP, ANYTYPE, \ 
 46       is_global, is_private 
 47  import rosgraph.names 
 48   
 49  from rospy.exceptions import ROSException 
 50  from rospy.impl.validators import ParameterInvalid 
 51   
 52  TOPIC_ANYTYPE = ANYTYPE #indicates that a subscriber will connect any datatype given to it 
 53  SERVICE_ANYTYPE = ANYTYPE #indicates that a service client does not have a fixed type 
 54   
 55  import struct 
 56   
 57  if sys.hexversion > 0x03000000: #Python3 
58 - def isstring(s):
59 return isinstance(s, str) #Python 3.x
60 else:
61 - def isstring(s):
62 return isinstance(s, basestring) #Python 2.x
63
64 -def canonicalize_name(name):
65 """ 66 Put name in canonical form. Double slashes '//' are removed and 67 name is returned without any trailing slash, e.g. /foo/bar 68 @param name: ROS name 69 @type name: str 70 """ 71 if not name or name == SEP: 72 return name 73 elif name[0] == SEP: 74 return '/' + '/'.join([x for x in name.split(SEP) if x]) 75 else: 76 return '/'.join([x for x in name.split(SEP) if x])
77 ##if len(name) > 1 and name[-1] == SEP: 78 ## return name[:-1] 79 ##return name 80 81 # Mappings override name resolution by substituting fully-qualified 82 # names in for local name references. They override any name 83 # reference, with exception of '.local' names. We load remapping args 84 # as soon as client API is referenced so that they are initialized 85 # before Topic constructors are invoked. 86 _mappings = load_mappings(sys.argv) 87 _resolved_mappings = {} 88
89 -def reload_mappings(argv):
90 """ 91 Re-initialize the name remapping table. 92 93 @param argv: Command line arguments to this program. ROS reads 94 these arguments to find renaming params. 95 @type argv: [str] 96 """ 97 global _mappings 98 _mappings = load_mappings(argv)
99 100 # #1810
101 -def initialize_mappings(node_name):
102 """ 103 Initialize the remapping table based on provide node name. 104 105 @param node_name: name of node (caller ID) 106 @type node_name: str 107 """ 108 global _resolved_mappings 109 _resolved_mappings = {} 110 for m,v in _mappings.items(): 111 # resolve both parts of the mappings. use the rosgraph.names 112 # version of resolve_name to avoid circular mapping. 113 if m.startswith('__'): # __name, __log, etc... 114 _resolved_mappings[m] = v 115 else: 116 _resolved_mappings[rosgraph.names.resolve_name(m, node_name)] = rosgraph.names.resolve_name(v, node_name)
117
118 -def resolve_name_without_node_name(name):
119 """ 120 The need for this function is complicated -- Topics and Services can be created before init_node is called. 121 In general, this is okay, unless the name is a ~name, in which 122 case we have to raise an ValueError 123 124 @param name: ROS name to resolve 125 @type name: str 126 @raise ValueError: if name is a ~name 127 @raise ROSInitException: if name is remapped to a ~name 128 """ 129 if is_private(name): 130 raise ValueError("~name topics cannot be created before init_node() has been called") 131 132 # we use the underlying rosgraph.names.resolve_name to avoid dependencies on nodename/remappings 133 fake_caller_id = ns_join(get_namespace(), 'node') 134 fake_resolved = rosgraph.names.resolve_name(name, fake_caller_id) 135 136 for m, v in _mappings.items(): 137 if rosgraph.names.resolve_name(m, fake_caller_id) == fake_resolved: 138 if is_private(name): 139 raise ROSInitException("due to the way this node is written, %s cannot be remapped to a ~name. \nThe declaration of topics/services must be moved after the call to init_node()"%name) 140 else: 141 return rosgraph.names.resolve_name(v, fake_caller_id) 142 return fake_resolved
143
144 -def get_mappings():
145 """ 146 Get mapping table with unresolved names 147 148 @return: command-line remappings {name: name} 149 @rtype: {str: str} 150 """ 151 return _mappings
152
153 -def get_resolved_mappings():
154 """ 155 Get mapping table with resolved names 156 157 @return: command-line remappings {name: name} 158 @rtype: {str: str} 159 """ 160 return _resolved_mappings
161 162 #TODO: port to a wrapped call to rosgraph.names.resolve_name
163 -def resolve_name(name, caller_id=None):
164 """ 165 Resolve a ROS name to its global, canonical form. Private ~names 166 are resolved relative to the node name. 167 168 @param name: name to resolve. 169 @type name: str 170 @param caller_id: node name to resolve relative to. To 171 resolve to local namespace, omit this parameter (or use None) 172 @type caller_id: str 173 @return: Resolved name. If name is empty/None, resolve_name 174 returns parent namespace. If namespace is empty/None, 175 @rtype: str 176 """ 177 if not caller_id: 178 caller_id = get_name() 179 if not name: #empty string resolves to namespace 180 return namespace(caller_id) 181 182 name = str(name) # enforce string conversion else struct.pack might raise UnicodeDecodeError (see #3998) 183 name = canonicalize_name(name) 184 if name[0] == SEP: #global name 185 resolved_name = name 186 elif is_private(name): #~name 187 resolved_name = ns_join(caller_id, name[1:]) 188 else: #relative 189 resolved_name = namespace(caller_id) + name 190 191 #Mappings override general namespace-based resolution 192 # - do this before canonicalization as remappings are meant to 193 # match the name as specified in the code 194 if resolved_name in _resolved_mappings: 195 return _resolved_mappings[resolved_name] 196 else: 197 return resolved_name
198 199
200 -def remap_name(name, caller_id=None, resolved=True):
201 """ 202 Remap a ROS name. This API should be used to instead of 203 resolve_name for APIs in which you don't wish to resolve the name 204 unless it is remapped. 205 @param name: name to remap 206 @type name: str 207 208 @param resolved: if True (default), use resolved names in remappings, which is the standard for ROS. 209 @type resolved: bool 210 211 @return: Remapped name 212 @rtype: str 213 """ 214 if not caller_id: 215 caller_id = get_caller_id() 216 if name in _mappings: 217 return rosgraph.names.resolve_name(_mappings[name], caller_id) 218 return name
219
220 -def scoped_name(caller_id, name):
221 """ 222 Convert the global caller_id to a relative name within the namespace. For example, for 223 namespace '/foo' and name '/foo/bar/name', the return value will 224 be 'bar/name' 225 226 WARNING: scoped_name does not validate that name is actually within 227 the supplied namespace. 228 @param caller_id: caller ID, in canonical form 229 @type caller_id: str 230 @param name: name to scope 231 @type name: str 232 @return: name scoped to the caller_id's namespace. 233 @rtype: str 234 """ 235 if not is_global(caller_id): 236 raise ROSException("caller_id must be global") 237 return canonicalize_name(name)[len(namespace(caller_id)):]
238 239 240 ################################################### 241 # Name validators ############################ 242 243 #Technically XMLRPC will never send a None, but I don't want to code masterslave.py to be 244 #XML-RPC specific in this way. 245
246 -def valid_name_validator_resolved(param_name, param_value, caller_id):
247 if not param_value or not isstring(param_value): 248 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name) 249 #TODO: actual validation of chars 250 # I added the colon check as the common error will be to send an URI instead of name 251 if ':' in param_value or ' ' in param_value: 252 raise ParameterInvalid("ERROR: parameter [%s] contains illegal chars"%param_name) 253 #don't use our own resolve_name because we do not want to remap 254 return rosgraph.names.resolve_name(param_value, caller_id, remappings=None)
255
256 -def valid_name_validator_unresolved(param_name, param_value, caller_id):
257 if not param_value or not isstring(param_value): 258 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name) 259 #TODO: actual validation of chars 260 # I added the colon check as the common error will be to send an URI instead of name 261 if ':' in param_value or ' ' in param_value: 262 raise ParameterInvalid("ERROR: parameter [%s] contains illegal chars"%param_name) 263 return param_value
264
265 -def valid_name(param_name, resolve=True):
266 """ 267 Validator that resolves names and also ensures that they are not empty 268 @param param_name: name 269 @type param_name: str 270 @param resolve: if True/omitted, the name will be resolved to 271 a global form. Otherwise, no resolution occurs. 272 @type resolve: bool 273 @return: resolved parameter value 274 @rtype: str 275 """ 276 def validator(param_value, caller_id): 277 if resolve: 278 return valid_name_validator_resolved(param_name, param_value, caller_id) 279 return valid_name_validator_unresolved(param_name, param_value, caller_id)
280 return validator 281
282 -def global_name(param_name):
283 """ 284 Validator that checks for valid, global graph resource name. 285 @return: parameter value 286 @rtype: str 287 """ 288 def validator(param_value, caller_id): 289 if not param_value or not isstring(param_value): 290 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name) 291 #TODO: actual validation of chars 292 if not is_global(param_value): 293 raise ParameterInvalid("ERROR: parameter [%s] must be a globally referenced name"%param_name) 294 return param_value
295 return validator 296 297 ######################################################### 298 #Global Namespace Routines 299 # - Global state, e.g. singletons and namespace 300 301 _caller_namespace = get_ros_namespace() 302 _caller_id = _caller_namespace+'unnamed' #default for non-node. 303
304 -def get_namespace():
305 """ 306 Get namespace of local node. 307 @return: fully-qualified name of local node or '' if not applicable 308 @rtype: str 309 """ 310 return _caller_namespace
311
312 -def get_name():
313 """ 314 Get fully resolved name of local node. If this is not a node, 315 use empty string 316 @return: fully-qualified name of local node or '' if not applicable 317 @rtype: str 318 """ 319 return _caller_id
320 321 # backwards compatibility 322 get_caller_id = get_name 323
324 -def _set_caller_id(caller_id):
325 """ 326 Internal API. 327 Set the global name (i.e. caller_id) and namespace. Methods can 328 check what the name of the current node is by calling get_caller_id. 329 330 The caller_id is important as it is the first parameter to any API 331 call on a remote node. Invoked by ROSNode constructor 332 @param caller_id: new caller ID 333 @type caller_id: str 334 """ 335 global _caller_id, _caller_namespace 336 _caller_id = caller_id 337 _caller_namespace = namespace(caller_id)
338