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: names.py 14634 2011-08-10 17:57:16Z kwc $ 
 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   
 45  from roslib.names import namespace, get_ros_namespace, ns_join, make_global_ns, load_mappings, \ 
 46       SEP, GLOBALNS, TYPE_SEPARATOR, REMAP, ANYTYPE, \ 
 47       is_global, is_private 
 48  import roslib.names 
 49   
 50  from rospy.exceptions import ROSException 
 51  from rospy.impl.validators import ParameterInvalid 
 52   
 53  TOPIC_ANYTYPE = ANYTYPE #indicates that a subscriber will connect any datatype given to it 
 54  SERVICE_ANYTYPE = ANYTYPE #indicates that a service client does not have a fixed type 
 55   
 56  import struct 
 57   
 58  if sys.hexversion > 0x03000000: #Python3 
59 - def isstring(s):
60 return isinstance(s, str) #Python 3.x
61 else:
62 - def isstring(s):
63 return isinstance(s, basestring) #Python 2.x
64
65 -def canonicalize_name(name):
66 """ 67 Put name in canonical form. Double slashes '//' are removed and 68 name is returned without any trailing slash, e.g. /foo/bar 69 @param name: ROS name 70 @type name: str 71 """ 72 if not name or name == SEP: 73 return name 74 elif name[0] == SEP: 75 return '/' + '/'.join([x for x in name.split(SEP) if x]) 76 else: 77 return '/'.join([x for x in name.split(SEP) if x])
78 ##if len(name) > 1 and name[-1] == SEP: 79 ## return name[:-1] 80 ##return name 81 82 # Mappings override name resolution by substituting fully-qualified 83 # names in for local name references. They override any name 84 # reference, with exception of '.local' names. We load remapping args 85 # as soon as client API is referenced so that they are initialized 86 # before Topic constructors are invoked. 87 _mappings = load_mappings(sys.argv) 88 _resolved_mappings = {} 89
90 -def reload_mappings(argv):
91 """ 92 Re-initialize the name remapping table. 93 94 @param argv: Command line arguments to this program. ROS reads 95 these arguments to find renaming params. 96 @type argv: [str] 97 """ 98 global _mappings 99 _mappings = load_mappings(argv)
100 101 # #1810
102 -def initialize_mappings(node_name):
103 """ 104 Initialize the remapping table based on provide node name. 105 106 @param node_name: name of node (caller ID) 107 @type node_name: str 108 """ 109 global _resolved_mappings 110 _resolved_mappings = {} 111 for m,v in _mappings.items(): 112 # resolve both parts of the mappings. use the roslib.names 113 # version of resolve_name to avoid circular mapping. 114 if m.startswith('__'): # __name, __log, etc... 115 _resolved_mappings[m] = v 116 else: 117 _resolved_mappings[roslib.names.resolve_name(m, node_name)] = roslib.names.resolve_name(v, node_name)
118
119 -def resolve_name_without_node_name(name):
120 """ 121 The need for this function is complicated -- Topics and Services can be created before init_node is called. 122 In general, this is okay, unless the name is a ~name, in which 123 case we have to raise an ValueError 124 125 @param name: ROS name to resolve 126 @type name: str 127 @raise ValueError: if name is a ~name 128 @raise ROSInitException: if name is remapped to a ~name 129 """ 130 if is_private(name): 131 raise ValueError("~name topics cannot be created before init_node() has been called") 132 133 # we use the underlying roslib.names.resolve_name to avoid dependencies on nodename/remappings 134 fake_caller_id = ns_join(get_namespace(), 'node') 135 fake_resolved = roslib.names.resolve_name(name, fake_caller_id) 136 137 for m, v in _mappings.items(): 138 if roslib.names.resolve_name(m, fake_caller_id) == fake_resolved: 139 if is_private(name): 140 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) 141 else: 142 return roslib.names.resolve_name(v, fake_caller_id) 143 return fake_resolved
144
145 -def get_mappings():
146 """ 147 Get mapping table with unresolved names 148 149 @return: command-line remappings {name: name} 150 @rtype: {str: str} 151 """ 152 return _mappings
153
154 -def get_resolved_mappings():
155 """ 156 Get mapping table with resolved names 157 158 @return: command-line remappings {name: name} 159 @rtype: {str: str} 160 """ 161 return _resolved_mappings
162 163 #TODO: port to a wrapped call to roslib.names.resolve_name
164 -def resolve_name(name, caller_id=None):
165 """ 166 Resolve a ROS name to its global, canonical form. Private ~names 167 are resolved relative to the node name. 168 169 @param name: name to resolve. 170 @type name: str 171 @param caller_id: node name to resolve relative to. To 172 resolve to local namespace, omit this parameter (or use None) 173 @type caller_id: str 174 @return: Resolved name. If name is empty/None, resolve_name 175 returns parent namespace. If namespace is empty/None, 176 @rtype: str 177 """ 178 if not caller_id: 179 caller_id = get_name() 180 if not name: #empty string resolves to namespace 181 return namespace(caller_id) 182 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 roslib.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 roslib.names.resolve_name(param_value, caller_id, remappings=None)
255 -def valid_name_validator_unresolved(param_name, param_value, caller_id):
256 if not param_value or not isstring(param_value): 257 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name) 258 #TODO: actual validation of chars 259 # I added the colon check as the common error will be to send an URI instead of name 260 if ':' in param_value or ' ' in param_value: 261 raise ParameterInvalid("ERROR: parameter [%s] contains illegal chars"%param_name) 262 return param_value
263
264 -def valid_name(param_name, resolve=True):
265 """ 266 Validator that resolves names and also ensures that they are not empty 267 @param param_name: name 268 @type param_name: str 269 @param resolve: if True/omitted, the name will be resolved to 270 a global form. Otherwise, no resolution occurs. 271 @type resolve: bool 272 @return: resolved parameter value 273 @rtype: str 274 """ 275 def validator(param_value, caller_id): 276 if resolve: 277 return valid_name_validator_resolved(param_name, param_value, caller_id) 278 return valid_name_validator_unresolved(param_name, param_value, caller_id)
279 return validator 280
281 -def global_name(param_name):
282 """ 283 Validator that checks for valid, global graph resource name. 284 @return: parameter value 285 @rtype: str 286 """ 287 def validator(param_value, caller_id): 288 if not param_value or not isstring(param_value): 289 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name) 290 #TODO: actual validation of chars 291 if not is_global(param_value): 292 raise ParameterInvalid("ERROR: parameter [%s] must be a globally referenced name"%param_name) 293 return param_value
294 return validator 295 296 ######################################################### 297 #Global Namespace Routines 298 # - Global state, e.g. singletons and namespace 299 300 _caller_namespace = get_ros_namespace() 301 _caller_id = _caller_namespace+'unnamed' #default for non-node. 302
303 -def get_namespace():
304 """ 305 Get namespace of local node. 306 @return: fully-qualified name of local node or '' if not applicable 307 @rtype: str 308 """ 309 return _caller_namespace
310
311 -def get_name():
312 """ 313 Get fully resolved name of local node. If this is not a node, 314 use empty string 315 @return: fully-qualified name of local node or '' if not applicable 316 @rtype: str 317 """ 318 return _caller_id
319 320 # backwards compatibility 321 get_caller_id = get_name 322
323 -def _set_caller_id(caller_id):
324 """ 325 Internal API. 326 Set the global name (i.e. caller_id) and namespace. Methods can 327 check what the name of the current node is by calling get_caller_id. 328 329 The caller_id is important as it is the first parameter to any API 330 call on a remote node. Invoked by ROSNode constructor 331 @param caller_id: new caller ID 332 @type caller_id: str 333 """ 334 global _caller_id, _caller_namespace 335 _caller_id = caller_id 336 _caller_namespace = namespace(caller_id)
337