Package trac :: Module core

Source Code for Module trac.core

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (C) 2003-2009 Edgewall Software 
  4  # Copyright (C) 2003-2004 Jonas Borgström <jonas@edgewall.com> 
  5  # Copyright (C) 2004-2005 Christopher Lenz <cmlenz@gmx.de> 
  6  # All rights reserved. 
  7  # 
  8  # This software is licensed as described in the file COPYING, which 
  9  # you should have received as part of this distribution. The terms 
 10  # are also available at http://trac.edgewall.org/wiki/TracLicense. 
 11  # 
 12  # This software consists of voluntary contributions made by many 
 13  # individuals. For the exact contribution history, see the revision 
 14  # history and logs, available at http://trac.edgewall.org/log/. 
 15  # 
 16  # Author: Jonas Borgström <jonas@edgewall.com> 
 17  #         Christopher Lenz <cmlenz@gmx.de> 
 18   
 19  __all__ = ['Component', 'ExtensionPoint', 'implements', 'Interface', 
 20             'TracError'] 
21 22 23 -def N_(string):
24 """No-op translation marker, inlined here to avoid importing from 25 `trac.util`. 26 """ 27 return string
28
29 30 -class TracError(Exception):
31 """Exception base class for errors in Trac.""" 32 33 title = N_('Trac Error') 34
35 - def __init__(self, message, title=None, show_traceback=False):
36 """If message is a genshi.builder.tag object, everything up to the 37 first <p> will be displayed in the red box, and everything after will 38 be displayed below the red box. 39 If title is given, it will be displayed as the large header above the 40 error message. 41 """ 42 from trac.util.translation import gettext 43 Exception.__init__(self, message) 44 self._message = message 45 self.title = title or gettext(self.title) 46 self.show_traceback = show_traceback
47 48 message = property(lambda self: self._message, 49 lambda self, v: setattr(self, '_message', v)) 50
51 - def __unicode__(self):
52 return unicode(self.message)
53
54 55 -class Interface(object):
56 """Marker base class for extension point interfaces."""
57
58 59 -class ExtensionPoint(property):
60 """Marker class for extension points in components.""" 61
62 - def __init__(self, interface):
63 """Create the extension point. 64 65 @param interface: the `Interface` subclass that defines the protocol 66 for the extension point 67 """ 68 property.__init__(self, self.extensions) 69 self.interface = interface 70 self.__doc__ = 'List of components that implement `%s`' % \ 71 self.interface.__name__
72
73 - def extensions(self, component):
74 """Return a list of components that declare to implement the extension 75 point interface. 76 """ 77 extensions = ComponentMeta._registry.get(self.interface, ()) 78 return filter(None, [component.compmgr[cls] for cls in extensions])
79
80 - def __repr__(self):
81 """Return a textual representation of the extension point.""" 82 return '<ExtensionPoint %s>' % self.interface.__name__
83
84 85 -class ComponentMeta(type):
86 """Meta class for components. 87 88 Takes care of component and extension point registration. 89 """ 90 _components = [] 91 _registry = {} 92
93 - def __new__(mcs, name, bases, d):
94 """Create the component class.""" 95 96 new_class = type.__new__(mcs, name, bases, d) 97 if name == 'Component': 98 # Don't put the Component base class in the registry 99 return new_class 100 101 # Only override __init__ for Components not inheriting ComponentManager 102 if True not in [issubclass(x, ComponentManager) for x in bases]: 103 # Allow components to have a no-argument initializer so that 104 # they don't need to worry about accepting the component manager 105 # as argument and invoking the super-class initializer 106 init = d.get('__init__') 107 if not init: 108 # Because we're replacing the initializer, we need to make sure 109 # that any inherited initializers are also called. 110 for init in [b.__init__._original for b in new_class.mro() 111 if issubclass(b, Component) 112 and '__init__' in b.__dict__]: 113 break 114 def maybe_init(self, compmgr, init=init, cls=new_class): 115 if cls not in compmgr.components: 116 compmgr.components[cls] = self 117 if init: 118 try: 119 init(self) 120 except: 121 del compmgr.components[cls] 122 raise
123 maybe_init._original = init 124 new_class.__init__ = maybe_init 125 126 if d.get('abstract'): 127 # Don't put abstract component classes in the registry 128 return new_class 129 130 ComponentMeta._components.append(new_class) 131 registry = ComponentMeta._registry 132 for cls in new_class.__mro__: 133 for interface in cls.__dict__.get('_implements', ()): 134 classes = registry.setdefault(interface, []) 135 if new_class not in classes: 136 classes.append(new_class) 137 138 return new_class
139
140 141 -class Component(object):
142 """Base class for components. 143 144 Every component can declare what extension points it provides, as well as 145 what extension points of other components it extends. 146 """ 147 __metaclass__ = ComponentMeta 148
149 - def __new__(cls, *args, **kwargs):
150 """Return an existing instance of the component if it has already been 151 activated, otherwise create a new instance. 152 """ 153 # If this component is also the component manager, just invoke that 154 if issubclass(cls, ComponentManager): 155 self = super(Component, cls).__new__(cls) 156 self.compmgr = self 157 return self 158 159 # The normal case where the component is not also the component manager 160 compmgr = args[0] 161 self = compmgr.components.get(cls) 162 if self is None: 163 self = super(Component, cls).__new__(cls) 164 self.compmgr = compmgr 165 compmgr.component_activated(self) 166 return self
167 168 @staticmethod
169 - def implements(*interfaces):
170 """Can be used in the class definiton of `Component` subclasses to 171 declare the extension points that are extended. 172 """ 173 import sys 174 175 frame = sys._getframe(1) 176 locals_ = frame.f_locals 177 178 # Some sanity checks 179 assert locals_ is not frame.f_globals and '__module__' in locals_, \ 180 'implements() can only be used in a class definition' 181 182 locals_.setdefault('_implements', []).extend(interfaces)
183 184 185 implements = Component.implements
186 187 188 -class ComponentManager(object):
189 """The component manager keeps a pool of active components.""" 190
191 - def __init__(self):
192 """Initialize the component manager.""" 193 self.components = {} 194 self.enabled = {} 195 if isinstance(self, Component): 196 self.components[self.__class__] = self
197
198 - def __contains__(self, cls):
199 """Return wether the given class is in the list of active components.""" 200 return cls in self.components
201
202 - def __getitem__(self, cls):
203 """Activate the component instance for the given class, or return the 204 existing instance if the component has already been activated. 205 """ 206 if not self.is_enabled(cls): 207 return None 208 component = self.components.get(cls) 209 if not component: 210 if cls not in ComponentMeta._components: 211 raise TracError('Component "%s" not registered' % cls.__name__) 212 try: 213 component = cls(self) 214 except TypeError, e: 215 raise TracError('Unable to instantiate component %r (%s)' % 216 (cls, e)) 217 return component
218
219 - def is_enabled(self, cls):
220 """Return whether the given component class is enabled.""" 221 if cls not in self.enabled: 222 self.enabled[cls] = self.is_component_enabled(cls) 223 return self.enabled[cls]
224
225 - def disable_component(self, component):
226 """Force a component to be disabled. 227 228 The argument `component` can be a class or an instance. 229 """ 230 if not isinstance(component, type): 231 component = component.__class__ 232 self.enabled[component] = False 233 self.components[component] = None
234
235 - def component_activated(self, component):
236 """Can be overridden by sub-classes so that special initialization for 237 components can be provided. 238 """
239
240 - def is_component_enabled(self, cls):
241 """Can be overridden by sub-classes to veto the activation of a 242 component. 243 244 If this method returns `False`, the component was disabled explicitly. 245 If it returns `None`, the component was neither enabled nor disabled 246 explicitly. In both cases, the component with the given class will not 247 be available. 248 """ 249 return True
250