Package rosh :: Module plugin
[frames] | no frames]

Source Code for Module rosh.plugin

  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: plugin.py 11463 2010-10-08 19:00:27Z kwc $ 
 34   
 35  import roslib 
 36  import roslib.packages 
 37  from rosh.impl.exceptions import ROSHException, InvalidPlugin, NoPlugin 
 38   
 39  # TODO: possibly store other data in here, like plugin help, etc... 
40 -class PluginContext(object):
41
42 - def __init__(self, rosh_globals, rosh_context, rosh_lock):
43 # global symbol table for rosh module 44 self.rosh_globals = rosh_globals 45 # namespace ctx object of rosh 46 self.ctx = rosh_context 47 # common lock object for initialization 48 self.rosh_lock = rosh_lock 49 50 self._apis = {}
51
52 - def _register_api(self, plugin_api_id, callback):
53 self._apis[plugin_api_id] = callback
54
55 - def _register_handler(self, plugin_api_id, args):
56 callback = self._apis[plugin_api_id] 57 callback(*args)
58
59 -class PluginData(object):
60
61 - def __init__(self):
62 self.apis = {} 63 self.handlers = {}
64
65 - def add_handler(self, plugin_api_id, args):
66 """ 67 @param plugin_api_id: identifier for API to register with 68 @type plugin_api_id: str 69 @param args: arguments for plugin API 70 @type args: [any] 71 """ 72 if plugin_api_id not in self.handlers: 73 self.handlers[plugin_api_id] = [] 74 self.handlers[plugin_api_id].append(args)
75
76 - def add_api(self, plugin_api_id, callback):
77 """ 78 Register plugin API that downstream plugins can add handlers for. 79 80 @param plugin_api_id: identifier for this API 81 @type plugin_api_id: str 82 @param callback: callback function to invoke when handlers register with this API 83 @type callback: fn 84 """ 85 self.apis[plugin_api_id] = callback
86
87 -def reentrant_load(loaded_symbols, globals_):
88 """ 89 Utility routine to allow plugins that have already loaded to 90 initialize a new globals_ dictionary. 91 """ 92 if loaded_symbols is not None: 93 if globals_ is not None: 94 globals_.update(loaded_symbols) 95 return True 96 else: 97 return False
98
99 -def globals_load(plugin_context, globals_, symbols):
100 """ 101 Utility routine for plugins to load their symbols into the 102 appropriate global symbol tables 103 """ 104 for g in [plugin_context.rosh_globals, globals_]: 105 if g is not None: 106 g.update(symbols)
107
108 -def load_plugin(name, plugin_context, globals_=None):
109 """ 110 Load ROSH plugin from another ROS package. 111 112 load_plugin() loads the plugin into the rosh global symbol 113 table. If globals_ is specified, plugin symbols will also be 114 loaded into the provided dictionary. 115 116 @param globals_: global symbol table to additionally load plugin to. 117 @type globals_: dict 118 @raise NoPlugin: if plugin does not exist 119 @raise InvalidPlugin: if plugin fails to load properly 120 """ 121 122 # remap friendly names to actual implementation. this is a bit of 123 # abstraction leakage and should instead be dynamically registered 124 # (though much more expensive). 125 126 #TODO: this is a hack until rosh_testing is a proper framework/plugin" 127 if name == 'rosh.impl.testing': 128 import rosh.impl.testing 129 rosh.impl.testing.load_rosh_plugin('rosh.impl.ros_testing', plugin_context, globals_) 130 return 131 132 try: 133 # make sure plugin exists for accurate error reporting 134 roslib.packages.get_pkg_dir(name) 135 except roslib.packages.InvalidROSPkgException, e: 136 raise NoPlugin("invalid plugin [%s]: no ROS package named [%s]"%(name, name)) 137 try: 138 roslib.load_manifest(name) 139 except roslib.packages.InvalidROSPkgException, e: 140 raise InvalidPlugin("Failed to locate dependencies of plugin [%s]: \n[%s]"%(name, str(e))) 141 try: 142 m = __import__(name) 143 except ImportError: 144 raise InvalidPlugin("invalid plugin [%s]: python module [%s] import failed"%(name, name)) 145 try: 146 loader = getattr(m, 'rosh_plugin_load') 147 except AttributeError, e: 148 raise InvalidPlugin("invalid plugin [%s]: plugin is missing rosh_plugin_load() entry point"%(name)) 149 150 try: 151 plugin_data = loader(plugin_context, globals_) 152 except Exception, e: 153 raise InvalidPlugin("invalid plugin [%s]: plugin raised Exception on load: %s"%(name, e)) 154 errors = [] 155 if plugin_data is not None: 156 157 # Wire up plugins. Although the plugins could do this 158 # themselves, de-coupling provides better protection against 159 # future implementation changes and is also easier to write 160 # tests for. 161 162 # - We load as much as we can, record the errors, then 163 # re-raise at the end. 164 for plugin_api_id, callback in plugin_data.apis.iteritems(): 165 try: 166 plugin_context._register_api(plugin_api_id, callback) 167 except Exception, e: 168 errors.append(e) 169 170 for plugin_api_id, args in plugin_data.handlers.iteritems(): 171 try: 172 plugin_context._register_handler(plugin_api_id, args) 173 except Exception, e: 174 errors.append(e) 175 176 if errors: 177 error_str = [str(e) for e in errors] 178 raise ROSHException("errors loading plugin [%s]: \n%s"%(name, '\n'.join(error_str)))
179