def load(self, path_to_zip_file): if not os.access(path_to_zip_file, os.R_OK): raise PluginNotFound('Cannot access %r' % path_to_zip_file) with zipfile.ZipFile(path_to_zip_file) as zf: plugin_name = self._locate_code(zf, path_to_zip_file) try: ans = None plugin_module = 'calibre_plugins.%s' % plugin_name m = sys.modules.get(plugin_module, None) if m is not None: reload(m) else: m = importlib.import_module(plugin_module) plugin_classes = [] for obj in m.__dict__.itervalues(): if isinstance(obj, type) and issubclass(obj, Plugin) and \ obj.name != 'Trivial Plugin': plugin_classes.append(obj) if not plugin_classes: raise InvalidPlugin( 'No plugin class found in %s:%s' % (as_unicode(path_to_zip_file), plugin_name)) if len(plugin_classes) > 1: plugin_classes.sort(key=lambda c: (getattr( c, '__module__', None) or '').count('.')) ans = plugin_classes[0] if ans.minimum_calibre_version > numeric_version: raise InvalidPlugin( 'The plugin at %s needs a version of calibre >= %s' % (as_unicode(path_to_zip_file), '.'.join( map(unicode, ans.minimum_calibre_version)))) if platform not in ans.supported_platforms: raise InvalidPlugin('The plugin at %s cannot be used on %s' % (as_unicode(path_to_zip_file), platform)) return ans except: with self._lock: del self.loaded_plugins[plugin_name] raise
def initialize_plugin(plugin, path_to_zip_file): try: p = plugin(path_to_zip_file) p.initialize() return p except Exception: print 'Failed to initialize plugin:', plugin.name, plugin.version tb = traceback.format_exc() raise InvalidPlugin((_('Initialization of plugin %s failed with traceback:') %tb) + '\n'+tb)
def _locate_code(self, zf, path_to_zip_file): names = [ x if isinstance(x, unicode) else x.decode('utf-8') for x in zf.namelist() ] names = [x[1:] if x[0] == '/' else x for x in names] plugin_name = None for name in names: name, ext = posixpath.splitext(name) if name.startswith('plugin-import-name-') and ext == '.txt': plugin_name = name.rpartition('-')[-1] if plugin_name is None: c = 0 while True: c += 1 plugin_name = 'dummy%d' % c if plugin_name not in self.loaded_plugins: break else: if self._identifier_pat.match(plugin_name) is None: raise InvalidPlugin( ('The plugin at %r uses an invalid import name: %r' % (path_to_zip_file, plugin_name))) pynames = [x for x in names if x.endswith('.py')] candidates = [ posixpath.dirname(x) for x in pynames if x.endswith('/__init__.py') ] candidates.sort(key=lambda x: x.count('/')) valid_packages = set() for candidate in candidates: parts = candidate.split('/') parent = '.'.join(parts[:-1]) if parent and parent not in valid_packages: continue valid_packages.add('.'.join(parts)) names = OrderedDict() for candidate in pynames: parts = posixpath.splitext(candidate)[0].split('/') package = '.'.join(parts[:-1]) if package and package not in valid_packages: continue name = '.'.join(parts) names[name] = zf.getinfo(candidate) # Legacy plugins if '__init__' not in names: for name in list(names.iterkeys()): if '.' not in name and name.endswith('plugin'): names['__init__'] = names[name] break if '__init__' not in names: raise InvalidPlugin( ('The plugin in %r is invalid. It does not ' 'contain a top-level __init__.py file') % path_to_zip_file) with self._lock: self.loaded_plugins[plugin_name] = (path_to_zip_file, names) return plugin_name