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 Warning: do not use this library. It is unstable and most of the routines
37 here have been superceded by other libraries (e.g. genmsg). These
38 routines will likely be *deleted* in future releases.
39 """
40
41 import os
42 import re
43 import sys
44
45
46 PRN_SEPARATOR = '/'
47 TYPE_SEPARATOR = PRN_SEPARATOR
48 SEP = '/'
49 GLOBALNS = '/'
50 PRIV_NAME = '~'
51 REMAP = ':='
52 ANYTYPE = '*'
53
54 if sys.hexversion > 0x03000000:
56 return isinstance(s, str)
57 else:
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)
64
65
67 """
68 @param env: environment dictionary (defaults to os.environ)
69 @type env: dict
70 @param argv: command-line arguments (defaults to sys.argv)
71 @type argv: [str]
72 @return: ROS namespace of current program
73 @rtype: str
74 """
75
76 if argv is None:
77 argv = sys.argv
78 for a in argv:
79 if a.startswith('__ns:='):
80 return make_global_ns(a[len('__ns:='):])
81 if env is None:
82 env = os.environ
83 return make_global_ns(env.get('ROS_NAMESPACE', GLOBALNS))
84
85
87 """
88 Resolve a local name to the caller ID based on ROS environment settings (i.e. ROS_NAMESPACE)
89
90 @param name: local name to calculate caller ID from, e.g. 'camera', 'node'
91 @type name: str
92 @return: caller ID based on supplied local name
93 @rtype: str
94 """
95 return make_global_ns(ns_join(get_ros_namespace(), name))
96
97
99 """
100 Convert name to a global name with a trailing namespace separator.
101
102 @param name: ROS resource name. Cannot be a ~name.
103 @type name: str
104 @return str: name as a global name, e.g. 'foo' -> '/foo/'.
105 This does NOT resolve a name.
106 @rtype: str
107 @raise ValueError: if name is a ~name
108 """
109 if is_private(name):
110 raise ValueError('cannot turn [%s] into a global name' % name)
111 if not is_global(name):
112 name = SEP + name
113 if name[-1] != SEP:
114 name = name + SEP
115 return name
116
117
119 """
120 Test if name is a global graph resource name.
121
122 @param name: must be a legal name in canonical form
123 @type name: str
124 @return: True if name is a globally referenced name (i.e. /ns/name)
125 @rtype: bool
126 """
127 return name and name[0] == SEP
128
129
131 """
132 Test if name is a private graph resource name.
133
134 @param name: must be a legal name in canonical form
135 @type name: str
136 @return bool: True if name is a privately referenced name (i.e. ~name)
137 """
138 return name and name[0] == PRIV_NAME
139
140
142 """
143 Get the namespace of name. The namespace is returned with a
144 trailing slash in order to favor easy concatenation and easier use
145 within the global context.
146
147 @param name: name to return the namespace of. Must be a legal
148 name. NOTE: an empty name will return the global namespace.
149 @type name: str
150 @return str: Namespace of name. For example, '/wg/node1' returns '/wg/'. The
151 global namespace is '/'.
152 @rtype: str
153 @raise ValueError: if name is invalid
154 """
155 'map name to its namespace'
156 if name is None:
157 raise ValueError('name')
158 if not isstring(name):
159 raise TypeError('name')
160 if not name:
161 return SEP
162 elif name[-1] == SEP:
163 name = name[:-1]
164 return name[:name.rfind(SEP)+1] or SEP
165
166
168 """
169 Join a namespace and name. If name is unjoinable (i.e. ~private or
170 /global) it will be returned without joining
171
172 @param ns: namespace ('/' and '~' are both legal). If ns is the empty string, name will be returned.
173 @type ns: str
174 @param name str: a legal name
175 @return str: name concatenated to ns, or name if it is
176 unjoinable.
177 @rtype: str
178 """
179 if is_private(name) or is_global(name):
180 return name
181 if ns == PRIV_NAME:
182 return PRIV_NAME + name
183 if not ns:
184 return name
185 if ns[-1] == SEP:
186 return ns + name
187 return ns + SEP + name
188
189
191 """
192 Load name mappings encoded in command-line arguments. This will filter
193 out any parameter assignment mappings (see roslib.param.load_param_mappings()).
194
195 @param argv: command-line arguments
196 @type argv: [str]
197 @return: name->name remappings.
198 @rtype: dict {str: str}
199 """
200 mappings = {}
201 for arg in argv:
202 if REMAP in arg:
203 try:
204 src, dst = [x.strip() for x in arg.split(REMAP)]
205 if src and dst:
206 if len(src) > 1 and src[0] == '_' and src[1] != '_':
207
208 pass
209 else:
210 mappings[src] = dst
211 except Exception:
212 sys.stderr.write("ERROR: Invalid remapping argument '%s'\n" % arg)
213 return mappings
214
215
216
217
218
219
221 """
222 Convert package name + resource into a fully qualified resource name
223
224 @param res_pkg_name: name of package resource is located in
225 @type res_pkg_name: str
226 @param name: resource base name
227 @type name: str
228 @param my_pkg: name of package resource is being referred to
229 in. If specified, name will be returned in local form if
230 res_pkg_name is my_pkg
231 @type my_pkg: str
232 @return: name for resource
233 @rtype: str
234 """
235 if res_pkg_name != my_pkg:
236 return res_pkg_name+PRN_SEPARATOR+name
237 return name
238
239
241 """
242 pkg/typeName -> typeName, typeName -> typeName
243
244 Convert fully qualified resource name into the package-less resource name
245 @param name: package resource name, e.g. 'std_msgs/String'
246 @type name: str
247 @return: resource name sans package-name scope
248 @rtype: str
249 """
250
251 return name[name.rfind(PRN_SEPARATOR)+1:]
252
253
255 """
256 pkg/typeName -> pkg, typeName -> None
257
258 @param name: package resource name, e.g. 'std_msgs/String'
259 @type name: str
260 @return: package name of resource
261 @rtype: str
262 """
263
264 if PRN_SEPARATOR not in name:
265 return None
266 return name[:name.find(PRN_SEPARATOR)]
267
268
270 """
271 Split a name into its package and resource name parts, e.g. 'std_msgs/String -> std_msgs, String'
272
273 @param name: package resource name, e.g. 'std_msgs/String'
274 @type name: str
275 @return: package name, resource name
276 @rtype: str
277 @raise ValueError: if name is invalid
278 """
279 if PRN_SEPARATOR in name:
280 val = tuple(name.split(PRN_SEPARATOR))
281 if len(val) != 2:
282 raise ValueError('invalid name [%s]' % name)
283 else:
284 return val
285 else:
286 return '', name
287
288
290
291 if not isstring(name) or not name or len(name) > 255:
292 return False
293 return is_legal_resource_name(name)
294
295
296
297
298
299
300 RESOURCE_NAME_LEGAL_CHARS_P = re.compile(r'^[A-Za-z][\w_\/]*$')
301
302
304 """
305 Check if name is a legal ROS name for filesystem resources
306 (alphabetical character followed by alphanumeric, underscore, or
307 forward slashes). This constraint is currently not being enforced,
308 but may start getting enforced in later versions of ROS.
309
310 @param name: Name
311 @type name: str
312 """
313
314 if name is None:
315 return False
316 m = RESOURCE_NAME_LEGAL_CHARS_P.match(name)
317
318 return m is not None and m.group(0) == name and '//' not in name
319
320
321
322 NAME_LEGAL_CHARS_P = re.compile(r'^[\~\/A-Za-z][\w_\/]*$')
323
324
326 """
327 Check if name is a legal ROS name for graph resources
328 (alphabetical character followed by alphanumeric, underscore, or
329 forward slashes). This constraint is currently not being enforced,
330 but may start getting enforced in later versions of ROS.
331
332 @param name: Name
333 @type name: str
334 """
335
336 if name is None:
337 return False
338
339 if name == '':
340 return True
341 m = NAME_LEGAL_CHARS_P.match(name)
342 return m is not None and m.group(0) == name and '//' not in name
343
344
345 BASE_NAME_LEGAL_CHARS_P = re.compile(r'^[A-Za-z][\w_]*$')
346
347
349 """
350 Validates that name is a legal base name for a graph resource. A base name has
351 no namespace context, e.g. "node_name".
352 """
353 if name is None:
354 return False
355 m = BASE_NAME_LEGAL_CHARS_P.match(name)
356 return m is not None and m.group(0) == name
357
358
359 BASE_RESOURCE_NAME_LEGAL_CHARS_P = re.compile(r'^[A-Za-z][\w_]*$')
360
361
363 """
364 Validates that name is a legal resource base name. A base name has
365 no package context, e.g. "String".
366 """
367
368 if name is None:
369 return False
370 m = BASE_NAME_LEGAL_CHARS_P.match(name)
371 return m is not None and m.group(0) == name
372
373
375 """
376 Put name in canonical form. Extra slashes '//' are removed and
377 name is returned without any trailing slash, e.g. /foo/bar
378 @param name: ROS name
379 @type name: str
380 """
381 if not name or name == SEP:
382 return name
383 elif name[0] == SEP:
384 return '/' + '/'.join([x for x in name.split(SEP) if x])
385 else:
386 return '/'.join([x for x in name.split(SEP) if x])
387
388
390 """
391 Resolve a ROS name to its global, canonical form. Private ~names
392 are resolved relative to the node name.
393
394 @param name: name to resolve.
395 @type name: str
396 @param namespace_: node name to resolve relative to.
397 @type namespace_: str
398 @param remappings: Map of resolved remappings. Use None to indicate no remapping.
399 @return: Resolved name. If name is empty/None, resolve_name
400 returns parent namespace_. If namespace_ is empty/None,
401 @rtype: str
402 """
403 if not name:
404 return namespace(namespace_)
405
406 name = canonicalize_name(name)
407 if name[0] == SEP:
408 resolved_name = name
409 elif is_private(name):
410
411 resolved_name = canonicalize_name(namespace_ + SEP + name[1:])
412 else:
413 resolved_name = namespace(namespace_) + name
414
415
416
417
418 if remappings and resolved_name in remappings:
419 return remappings[resolved_name]
420 else:
421 return resolved_name
422
423
425 """
426 Generate a ROS-legal 'anonymous' name
427
428 @param id: prefix for anonymous name
429 @type id: str
430 """
431 import random
432 import socket
433 name = '%s_%s_%s_%s' % (id, socket.gethostname(), os.getpid(), random.randint(0, sys.maxsize))
434
435
436
437 name = name.replace('.', '_')
438 name = name.replace('-', '_')
439 return name.replace(':', '_')
440