Package rosgraph :: Module names
[frames] | no frames]

Source Code for Module rosgraph.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 14589 2011-08-07 18:30:21Z kwc $ 
 34   
 35  """ 
 36  Library for manipulating ROS Names. See U{http://ros.org/wiki/Names}. 
 37  """ 
 38   
 39  import os 
 40  import sys 
 41   
 42  from .rosenv import ROS_NAMESPACE 
 43   
 44  #TODO: why are these here? 
 45  MSG_EXT = '.msg' 
 46  SRV_EXT = '.srv' 
 47   
 48  SEP = '/' 
 49  GLOBALNS = '/' 
 50  PRIV_NAME = '~' 
 51  REMAP = ":=" 
 52  ANYTYPE = '*' 
 53   
 54  if sys.hexversion > 0x03000000: #Python3 
55 - def isstring(s):
56 return isinstance(s, str) #Python 3.x
57 else:
58 - def isstring(s):
59 """ 60 Small helper version to check an object is a string in a way that works 61 for both Python 2 and 3 62 """ 63 return isinstance(s, basestring) #Python 2.x
64
65 -def get_ros_namespace(env=None, argv=None):
66 """ 67 @param env: environment dictionary (defaults to os.environ) 68 @type env: dict 69 @param argv: command-line arguments (defaults to sys.argv) 70 @type argv: [str] 71 @return: ROS namespace of current program 72 @rtype: str 73 """ 74 #we force command-line-specified namespaces to be globally scoped 75 if argv is None: 76 argv = sys.argv 77 for a in argv: 78 if a.startswith('__ns:='): 79 return make_global_ns(a[len('__ns:='):]) 80 if env is None: 81 env = os.environ 82 return make_global_ns(env.get(ROS_NAMESPACE, GLOBALNS))
83
84 -def make_caller_id(name):
85 """ 86 Resolve a local name to the caller ID based on ROS environment settings (i.e. ROS_NAMESPACE) 87 88 @param name: local name to calculate caller ID from, e.g. 'camera', 'node' 89 @type name: str 90 @return: caller ID based on supplied local name 91 @rtype: str 92 """ 93 return make_global_ns(ns_join(get_ros_namespace(), name))
94
95 -def make_global_ns(name):
96 """ 97 Convert name to a global name with a trailing namespace separator. 98 99 @param name: ROS resource name. Cannot be a ~name. 100 @type name: str 101 @return str: name as a global name, e.g. 'foo' -> '/foo/'. 102 This does NOT resolve a name. 103 @rtype: str 104 @raise ValueError: if name is a ~name 105 """ 106 if is_private(name): 107 raise ValueError("cannot turn [%s] into a global name"%name) 108 if not is_global(name): 109 name = SEP + name 110 if name[-1] != SEP: 111 name = name + SEP 112 return name
113
114 -def is_global(name):
115 """ 116 Test if name is a global graph resource name. 117 118 @param name: must be a legal name in canonical form 119 @type name: str 120 @return: True if name is a globally referenced name (i.e. /ns/name) 121 @rtype: bool 122 """ 123 return name and name[0] == SEP
124
125 -def is_private(name):
126 """ 127 Test if name is a private graph resource name. 128 129 @param name: must be a legal name in canonical form 130 @type name: str 131 @return bool: True if name is a privately referenced name (i.e. ~name) 132 """ 133 return name and name[0] == PRIV_NAME
134
135 -def namespace(name):
136 """ 137 Get the namespace of name. The namespace is returned with a 138 trailing slash in order to favor easy concatenation and easier use 139 within the global context. 140 141 @param name: name to return the namespace of. Must be a legal 142 name. NOTE: an empty name will return the global namespace. 143 @type name: str 144 @return str: Namespace of name. For example, '/wg/node1' returns '/wg/'. The 145 global namespace is '/'. 146 @rtype: str 147 @raise ValueError: if name is invalid 148 """ 149 "map name to its namespace" 150 if name is None: 151 raise ValueError('name') 152 if not isstring(name): 153 raise TypeError('name') 154 if not name: 155 return SEP 156 elif name[-1] == SEP: 157 name = name[:-1] 158 return name[:name.rfind(SEP)+1] or SEP
159
160 -def ns_join(ns, name):
161 """ 162 Join a namespace and name. If name is unjoinable (i.e. ~private or 163 /global) it will be returned without joining 164 165 @param ns: namespace ('/' and '~' are both legal). If ns is the empty string, name will be returned. 166 @type ns: str 167 @param name str: a legal name 168 @return str: name concatenated to ns, or name if it is 169 unjoinable. 170 @rtype: str 171 """ 172 if is_private(name) or is_global(name): 173 return name 174 if ns == PRIV_NAME: 175 return PRIV_NAME + name 176 if not ns: 177 return name 178 if ns[-1] == SEP: 179 return ns + name 180 return ns + SEP + name
181
182 -def load_mappings(argv):
183 """ 184 Load name mappings encoded in command-line arguments. This will filter 185 out any parameter assignment mappings. 186 187 @param argv: command-line arguments 188 @type argv: [str] 189 @return: name->name remappings. 190 @rtype: dict {str: str} 191 """ 192 mappings = {} 193 for arg in argv: 194 if REMAP in arg: 195 try: 196 src, dst = [x.strip() for x in arg.split(REMAP)] 197 if src and dst: 198 if len(src) > 1 and src[0] == '_' and src[1] != '_': 199 #ignore parameter assignment mappings 200 pass 201 else: 202 mappings[src] = dst 203 except: 204 #TODO: remove 205 sys.stderr.write("ERROR: Invalid remapping argument '%s'\n"%arg) 206 return mappings
207 208 209 ################################################################################ 210 # NAME VALIDATORS 211 212 import re 213 214 #~,/, or ascii char followed by (alphanumeric, _, /) 215 NAME_LEGAL_CHARS_P = re.compile('^[\~\/A-Za-z][\w\/]*$') 234 235 BASE_NAME_LEGAL_CHARS_P = re.compile('^[A-Za-z][\w]*$') #ascii char followed by (alphanumeric, _) 245
246 -def canonicalize_name(name):
247 """ 248 Put name in canonical form. Extra slashes '//' are removed and 249 name is returned without any trailing slash, e.g. /foo/bar 250 @param name: ROS name 251 @type name: str 252 """ 253 if not name or name == SEP: 254 return name 255 elif name[0] == SEP: 256 return '/' + '/'.join([x for x in name.split(SEP) if x]) 257 else: 258 return '/'.join([x for x in name.split(SEP) if x])
259
260 -def resolve_name(name, namespace_, remappings=None):
261 """ 262 Resolve a ROS name to its global, canonical form. Private ~names 263 are resolved relative to the node name. 264 265 @param name: name to resolve. 266 @type name: str 267 @param namespace_: node name to resolve relative to. 268 @type namespace_: str 269 @param remappings: Map of resolved remappings. Use None to indicate no remapping. 270 @return: Resolved name. If name is empty/None, resolve_name 271 returns parent namespace_. If namespace_ is empty/None, 272 @rtype: str 273 """ 274 if not name: #empty string resolves to parent of the namespace_ 275 return namespace(namespace_) 276 277 name = canonicalize_name(name) 278 if name[0] == SEP: #global name 279 resolved_name = name 280 elif is_private(name): #~name 281 # #3044: be careful not to accidentally make rest of name global 282 resolved_name = canonicalize_name(namespace_ + SEP + name[1:]) 283 else: #relative 284 resolved_name = namespace(namespace_) + name 285 286 #Mappings override general namespace-based resolution 287 # - do this before canonicalization as remappings are meant to 288 # match the name as specified in the code 289 if remappings and resolved_name in remappings: 290 return remappings[resolved_name] 291 else: 292 return resolved_name
293
294 -def script_resolve_name(script_name, name):
295 """ 296 Name resolver for scripts. Supports :envvar:`ROS_NAMESPACE`. Does not 297 support remapping arguments. 298 299 :param name: name to resolve, ``str`` 300 :param script_name: name of script. script_name must not 301 contain a namespace., ``str`` 302 :returns: resolved name, ``str`` 303 """ 304 if not name: #empty string resolves to namespace 305 return get_ros_namespace() 306 #Check for global name: /foo/name resolves to /foo/name 307 if is_global(name): 308 return name 309 #Check for private name: ~name resolves to /caller_id/name 310 elif is_private(name): 311 return ns_join(make_caller_id(script_name), name[1:]) 312 return get_ros_namespace() + name
313
314 -def anonymous_name(id):
315 """ 316 Generate a ROS-legal 'anonymous' name 317 318 @param id: prefix for anonymous name 319 @type id: str 320 """ 321 import socket, random 322 name = "%s_%s_%s_%s"%(id, socket.gethostname(), os.getpid(), random.randint(0, sys.maxsize)) 323 # RFC 952 allows hyphens, IP addrs can have '.'s, both 324 # of which are illegal for ROS names. For good 325 # measure, screen ipv6 ':'. 326 name = name.replace('.', '_') 327 name = name.replace('-', '_') 328 return name.replace(':', '_')
329