Package rosh :: Package impl :: Module namespace
[frames] | no frames]

Source Code for Module rosh.impl.namespace

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2010, 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: namespace.py 11682 2010-10-22 07:53:07Z kwc $ 
 34  """ 
 35  Generic namespace model for ROS. This powers most of rosh by enabling 
 36  iPython tab-complete on a ROS namespace'd resource (e.g. topics, 
 37  services, parameters). 
 38  """ 
 39   
 40  #TODO: remapping support 
 41   
 42  from __future__ import with_statement 
 43   
 44  import roslib.names 
 45   
46 -class Context(object):
47 """ 48 ROSH context instance. Instead of globals, we need a context 49 instance that implementation can store state in. 50 51 Plugins may store properties on this object as long as they 52 include their name as a prefix. 53 """ 54
55 - def __init__(self):
56 # only guaranteed property 57 self.master = None 58 self.plugin_handlers = {}
59
60 - def add_plugin_handler(self, key, args):
61 self.plugin_handlers[key] = args
62
63 -class NamespaceConfig(object):
64 """ 65 NamespaceConfig is a configuration object for a L{Namespace} instance, 66 storing common data structures and objects that all L{Namespace} 67 instances need. 68 """ 69
70 - def __init__(self, ctx, lock):
71 """ 72 @param ctx: rosh context object 73 @type ctx: dict 74 @param lock: lock for controlling access to cache 75 @param master: ROS master handle, if applicable 76 @type master: rosgraph.MasterApi 77 """ 78 self.cache = {} 79 self.lock = lock 80 self.ctx = ctx 81 self.master = ctx.master
82
83 -class Namespace(object):
84 """ 85 Namespace provides lightweight accessors to a ROS namespace and its data. The behavior of the namespace 86 object is determined by a L{NamespaceConfig} instance. 87 """ 88
89 - def __init__(self, name, config):
90 """ 91 ctor. 92 @param config: Namespace configuration instance. 93 @type config: L{NamespaceConfig} 94 """ 95 self._name = name 96 self._ns = name + '/' 97 self._config = config
98
99 - def _list(self):
100 """ 101 Subclasses should override. 102 """ 103 raise NotImplemented
104
105 - def _props(self):
106 """ 107 Subclasses should override if they have props 108 """ 109 return []
110
111 - def _init(self, *args):
112 """ 113 Subclasses should override. 114 """ 115 raise NotImplemented
116
117 - def _getAttributeNames(self):
118 return list(set([s[len(self._ns):].split('/')[0] for s in self._list()]))
119
120 - def __getattribute__(self, key):
121 if key.startswith('_') or key == 'trait_names': 122 return object.__getattribute__(self, key) 123 else: 124 return self.__getitem__(key)
125
126 - def __iter__(self):
127 return (getattr(self, k) for k in self._getAttributeNames())
128
129 - def _get_entry(self, key):
130 """ 131 By default, creates a new Namespace instance of the correct 132 subclass. Subclasses may wish to override. 133 """ 134 cache = self._config.cache 135 with self._config.lock: 136 if key in cache: 137 obj = cache[key] 138 else: 139 # create a new instance of ourselves. This requires 140 # subclasses to have same constructor args. 141 obj = self.__class__(key, self._config) 142 cache[key] = obj 143 return obj
144
145 - def __getitem__(self, key):
146 """ 147 Dictionary-style accessor 148 """ 149 key = roslib.names.ns_join(self._ns, key) 150 if key in self._config.cache: 151 return self._config.cache[key] 152 else: 153 val = self._get_entry(key) 154 return val
155
156 -class Concept(object):
157
158 - def __init__(self, ctx, lock, nstype):
159 """ 160 @param ctx: Context instance 161 @type ctx: L{Context} 162 @param lock: Resource lock for modifying ctx resources 163 @type lock: threading.Lock 164 @param nstype: Namespace class 165 @type nstype: class 166 """ 167 self._master = ctx.master 168 169 self._config = NamespaceConfig(ctx, lock) 170 self._root = nstype('', self._config) 171 self._nstype = nstype
172
173 - def _init(self, *args):
174 """ 175 Subclasses should override (if necessary) 176 """ 177 raise NotImplemented
178
179 - def _getAttributeNames(self):
180 return self._root._getAttributeNames()
181
182 - def _props(self):
183 """ 184 Subclasses should override if they have props 185 """ 186 return []
187
188 - def __iter__(self):
189 return self._root.__iter__()
190
191 - def __getattribute__(self, key):
192 if key.startswith('_') or key == 'trait_names': 193 return object.__getattribute__(self, key) 194 else: 195 return self._root.__getattribute__(key)
196
197 - def __getitem__(self, key):
198 """ 199 Dictionary-style accessor for topics 200 """ 201 return self._root.__getitem__(key)
202
203 -class ResourceList(object):
204 """ 205 A ResourceList presents a IPython tab-completable list of 206 resources as if they are attributes. The general form of 207 ResourceList only presents a flat list of resources. For a 208 ResourceList that is namespace-aware, use L{NSResourceList}. 209 210 ResourceList can share a config with a Namespace instance, which 211 makes them useful as views against larger pools of resources. 212 """ 213
214 - def __init__(self, config, resources, resource_class):
215 """ 216 @param config: NamespaceConfig instance 217 @param resource_class: Class to instantiate to create new 218 resources if resource has not already been created. 219 @type resource_class: Class 220 """ 221 222 self._resources = resources 223 self._config = config 224 self._resource_class = resource_class
225
226 - def __repr__(self):
227 return '\n'.join(self._resources)
228
229 - def __iter__(self):
230 """ 231 Not thread-safe. If ResourceList is modified during iteration, this will fail 232 """ 233 for r in self._resources: 234 yield self._get_entry(r)
235
236 - def _list(self):
237 return self._resources
238
239 - def _getAttributeNames(self):
240 return self._resources
241
242 - def __getattribute__(self, key):
243 if key.startswith('_') or key == 'trait_names': 244 return object.__getattribute__(self, key) 245 else: 246 return self.__getitem__(key)
247
248 - def __getitem__(self, key):
249 if key in self._config.cache: 250 return self._config.cache[key] 251 else: 252 val = self._get_entry(key) 253 return val
254
255 - def _get_entry(self, key):
256 # in the future we could try and do a redetect, but not high 257 # priority nor common 258 if not key in self._resources: 259 raise AttributeError(key) 260 cache = self._config.cache 261 with self._config.lock: 262 if key in cache: 263 obj = cache[key] 264 else: 265 # create a new instance of resource. resource_class 266 # must obey general resource constructor API. 267 obj = self._resource_class(key, self._config) 268 cache[key] = obj 269 return obj
270
271 -class NSResourceList(object):
272 """ 273 NSResourceList is a constrained version of a Namespace instance and 274 can be used to create a view against a larger Namespace. It can 275 share a config with a Namespace instance. 276 """ 277
278 - def __init__(self, name, config, resources, resource_class):
279 self._ns = name + '/' 280 281 self._resources = resources 282 self._config = config 283 self._resource_class = resource_class
284
285 - def _set_resources(self, resources):
286 """ 287 Change resource list 288 """ 289 self._resources = resources
290
291 - def __repr__(self):
292 return '\n'.join([x for x in self._resources if x.startswith(self._ns)])
293
294 - def _list(self):
295 return self._resources
296
297 - def __iter__(self):
298 """ 299 Not thread-safe. If ResourceList is modified during iteration, this will fail 300 """ 301 for r in self._resources: 302 yield self._get_entry(r)
303
304 - def _getAttributeNames(self):
305 return list(set([s[len(self._ns):].split('/')[0] for s in self._list()]))
306
307 - def __getattribute__(self, key):
308 if key.startswith('_') or key == 'trait_names': 309 return object.__getattribute__(self, key) 310 else: 311 return self.__getitem__(key)
312
313 - def __getitem__(self, key):
314 key = roslib.names.ns_join(self._ns, key) 315 # we additionally constrain key to be in self._resources 316 # because cache is shared with the full Namespace. We use 317 # NSResourceList references for any partial (parent namespace) 318 # references. 319 if key in self._resources and key in self._config.cache: 320 return self._config.cache[key] 321 else: 322 val = self._get_entry(key) 323 return val
324
325 - def _get_entry(self, key):
326 # in the future we could try and do a redetect, but not high 327 # priority nor common 328 res = self._resources 329 cache = self._config.cache 330 with self._config.lock: 331 if key in res: 332 if key in cache: 333 obj = cache[key] 334 else: 335 # create new instance of resource. resource must object construction API 336 obj = self._resource_class(key, self._config) 337 cache[key] = obj 338 else: 339 # descend namespace, do *not* cache in the main cache 340 # as that would pollute the Namespace cache. This may 341 # be a performance issue that we should provide a 342 # separate cache for. 343 if any(r for r in res if r.startswith(key+'/')): 344 obj = self.__class__(key, self._config, res, self._resource_class) 345 else: 346 # do not spawn for keys not within our namespace 347 return None 348 return obj
349 350
351 -def rostype(ns_obj, type_=None):
352 """ 353 Get or set the ROS type of an object. This is generally associated 354 with Namespace instances that have associated types. 355 356 @param type_: (optional) set type of object to type_, if possible 357 @raise TypeError: if type_ is not of the correct type 358 @raise ROSHException: if type cannot be set 359 """ 360 if type_ is None: 361 if hasattr(ns_obj, '_get_type'): 362 return ns_obj._get_type() 363 else: 364 raise ROSHException("object has no ROS type") 365 else: 366 if hasattr(ns_obj, '_set_type'): 367 ns_obj._set_type(type_) 368 else: 369 raise ROSHException("object has no ROS type")
370 371
372 -def info(ns_obj):
373 if hasattr(ns_obj, '_info'): 374 return ns_obj._info() 375 else: 376 raise ROSHException("object not not support _info")
377