Package entropy :: Package core

Source Code for Package entropy.core

  1  # -*- coding: utf-8 -*- 
  2  """ 
  3   
  4      @author: Fabio Erculiani <lxnay@sabayonlinux.org> 
  5      @contact: lxnay@sabayonlinux.org 
  6      @copyright: Fabio Erculiani 
  7      @license: GPL-2 
  8   
  9      B{Entropy Framework core module}. 
 10   
 11      This module contains base classes used by entropy.client, 
 12      entropy.server and entropy.services. 
 13   
 14      "Singleton" is a class that is inherited from singleton objects. 
 15   
 16  """ 
 17  import sys 
 18  import os 
 19  import inspect 
 20  from entropy.const import etpConst 
 21   
22 -class Singleton(object):
23 24 """ 25 If your class wants to become a sexy Singleton, 26 subclass this and replace __init__ with init_singleton 27 """ 28 29 __is_destroyed = False 30 __is_singleton = True
31 - def __new__(cls, *args, **kwds):
32 instance = cls.__dict__.get("__it__") 33 if instance is not None: 34 if not instance.is_destroyed(): 35 return instance 36 cls.__it__ = instance = object.__new__(cls) 37 instance.init_singleton(*args, **kwds) 38 return instance
39
40 - def is_destroyed(self):
41 """ 42 In our world, Singleton instances may be destroyed, 43 this is done by setting a private bool var __is_destroyed 44 45 @rtype: bool 46 @return: instance status, if destroyed or not 47 """ 48 return self.__is_destroyed
49
50 - def is_singleton(self):
51 """ 52 Return if the instance is a singleton 53 54 @rtype: bool 55 @return: class singleton property, if singleton or not 56 """ 57 return self.__is_singleton
58 59
60 -class EntropyPluginFactory:
61 62 """ 63 Generic Entropy Components Plugin Factory (loader). 64 """ 65 66 _PLUGIN_SUFFIX = "_plugin" 67 _PYTHON_EXTENSION = ".py" 68
69 - def __init__(self, base_plugin_class, plugin_package_module, 70 default_plugin_name = None, fallback_plugin_name = None):
71 """ 72 Entropy Generic Plugin Factory constructor. 73 MANDATORY: every plugin module/package(name) must end with _plugin 74 suffix. 75 76 Base plugin classes must have the following class attributes set: 77 78 - BASE_PLUGIN_API_VERSION: integer describing API revision in use 79 in class 80 81 Subclasses of Base plugin class must have the following class 82 attributes set: 83 84 - PLUGIN_API_VERSION: integer describing the currently implemented 85 plugin API revision, must match with BASE_PLUGIN_API_VERSION 86 above otherwise plugin won't be loaded and a warning will be 87 printed. 88 89 Moreover, plugin classes must be "Python new-style classes", otherwise 90 parser won't be able to determine if classes have subclasses and thus 91 pick the proper object (one with no subclasses!!). 92 See: http://www.python.org/doc/newstyle -- in other words, you have 93 to inherit the built-in "object" class (yeah, it's called object). 94 So, even if using normal classes could work, if you start doing nasty 95 things (nested inherittance of plugin classes), behaviour cannot 96 be guaranteed. 97 If it's not clear, let me repeat once again, valid plugin classes 98 must not have subclasses around! Think about it, it's an obvious thing. 99 100 If plugin class features a "PLUGIN_DISABLED" class attribute with 101 a boolean value of True, such plugin will be ignored. 102 103 @param base_plugin_class: Base EntropyPlugin-based class that valid 104 plugin classes must inherit from. 105 @type base_plugin_class: class 106 @param plugin_package_module: every plugin repository must work as 107 Python package, the value of this argument must be a valid 108 Python package module that can be scanned looking for valid 109 Entropy Plugin classes. 110 @type plugin_package_module: Python module 111 @keyword default_plugin_name: identifier of the default plugin to load 112 @type default_plugin_name: string 113 @keyword fallback_plugin_name: identifier of the fallback plugin to load 114 if default is not available 115 @type fallback_plugin_name: string 116 @raise AttributeError: when passed plugin_package_module is not a 117 valid Python package module 118 """ 119 self.__modfile = plugin_package_module.__file__ 120 self.__base_class = base_plugin_class 121 self.__plugin_package_module = plugin_package_module 122 self.__default_plugin_name = default_plugin_name 123 self.__fallback_plugin_name = fallback_plugin_name 124 self.__cache = None
125 126
127 - def get_available_plugins(self):
128 """ 129 Return currently available EntropyPlugin plugin classes. 130 Note: Entropy plugins can either be Python packages or modules and 131 their name MUST end with PluginFactory._PLUGIN_SUFFIX ("_plugin"). 132 133 @return: dictionary composed by Entropy plugin id as key and Entropy 134 Python module as value 135 @rtype: dict 136 """ 137 if self.__cache is not None: 138 return self.__cache.copy() 139 140 available = {} 141 base_api = self.__base_class.BASE_PLUGIN_API_VERSION 142 143 pkg_modname = self.__plugin_package_module.__name__ 144 for modname in os.listdir(os.path.dirname(self.__modfile)): 145 146 if modname.startswith("__"): 147 continue # python stuff 148 if not (modname.endswith(EntropyPluginFactory._PYTHON_EXTENSION) \ 149 or "." not in modname): 150 continue # not something we want to load 151 152 if modname.endswith(EntropyPluginFactory._PYTHON_EXTENSION): 153 modname = modname[:-len(EntropyPluginFactory._PYTHON_EXTENSION)] 154 155 if not modname.endswith(EntropyPluginFactory._PLUGIN_SUFFIX): 156 continue 157 158 # remove suffix 159 modname_clean = modname[:-len(EntropyPluginFactory._PLUGIN_SUFFIX)] 160 161 modpath = "%s.%s" % (pkg_modname, modname,) 162 163 try: 164 module = __import__(modpath) 165 except ImportError, err: 166 sys.stderr.write("!!! Entropy Plugin warning, cannot " \ 167 "load module: %s | %s !!!\n" % (modpath, err,)) 168 continue 169 170 for obj in sys.modules[modpath].__dict__.values(): 171 172 if not inspect.isclass(obj): 173 continue 174 175 if not issubclass(obj, self.__base_class): 176 continue 177 178 if hasattr(obj, '__subclasses__'): 179 # new style class 180 if obj.__subclasses__(): # only lower classes taken 181 continue 182 else: 183 sys.stderr.write("!!! Entropy Plugin warning: " \ 184 "%s is not a new style class !!!\n" % (obj,)) 185 186 if obj is self.__base_class: 187 # in this case, obj is our base class, 188 # so we are very sure that obj is not valid 189 continue 190 191 if not hasattr(obj, "PLUGIN_API_VERSION"): 192 sys.stderr.write("!!! Entropy Plugin warning: " \ 193 "no PLUGIN_API_VERSION in %s !!!\n" % (obj,)) 194 continue 195 196 if obj.PLUGIN_API_VERSION != base_api: 197 sys.stderr.write("!!! Entropy Plugin warning: " \ 198 "PLUGIN_API_VERSION mismatch in %s !!!\n" % (obj,)) 199 continue 200 201 if hasattr(obj, 'PLUGIN_DISABLED'): 202 if obj.PLUGIN_DISABLED: 203 # this plugin has been disabled 204 continue 205 206 available[modname_clean] = obj 207 208 self.__cache = available.copy() 209 return available
210
211 - def get_default_plugin(self):
212 """ 213 Return currently configured Entropy Plugin class. 214 215 @return: Entropy plugin class 216 @rtype: entropy.core.EntropyPlugin 217 @raise KeyError: if default plugin is not set or not found 218 """ 219 available = self.get_available_plugins() 220 plugin = self.__default_plugin_name 221 fallback = self.__fallback_plugin_name 222 klass = available.get(plugin) 223 224 if klass is None: 225 import warnings 226 warnings.warn("%s: %s" % ( 227 "selected Plugin not available, using fallback", plugin,)) 228 klass = available.get(fallback) 229 230 if klass is None: 231 raise KeyError 232 233 return klass
234