comparison examples/trac/trac/core.py @ 39:93b4dcbafd7b trunk

Copy Trac to main branch.
author cmlenz
date Mon, 03 Jul 2006 18:53:27 +0000
parents
children
comparison
equal deleted inserted replaced
38:ee669cb9cccc 39:93b4dcbafd7b
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2003-2005 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.com/license.html.
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://projects.edgewall.com/trac/.
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 class TracError(Exception):
24 """Exception base class for errors in Trac."""
25
26 def __init__(self, message, title=None, show_traceback=False):
27 Exception.__init__(self, message)
28 self.message = message
29 self.title = title
30 self.show_traceback = show_traceback
31
32
33 class Interface(object):
34 """Marker base class for extension point interfaces."""
35
36
37 class ExtensionPoint(property):
38 """Marker class for extension points in components."""
39
40 def __init__(self, interface):
41 """Create the extension point.
42
43 @param interface: the `Interface` subclass that defines the protocol
44 for the extension point
45 """
46 property.__init__(self, self.extensions)
47 self.interface = interface
48 self.__doc__ = 'List of components that implement `%s`' % \
49 self.interface.__name__
50
51 def extensions(self, component):
52 """Return a list of components that declare to implement the extension
53 point interface."""
54 extensions = ComponentMeta._registry.get(self.interface, [])
55 return filter(None, [component.compmgr[cls] for cls in extensions])
56
57 def __repr__(self):
58 """Return a textual representation of the extension point."""
59 return '<ExtensionPoint %s>' % self.interface.__name__
60
61
62 class ComponentMeta(type):
63 """Meta class for components.
64
65 Takes care of component and extension point registration.
66 """
67 _components = []
68 _registry = {}
69
70 def __new__(cls, name, bases, d):
71 """Create the component class."""
72
73 new_class = type.__new__(cls, name, bases, d)
74 if name == 'Component':
75 # Don't put the Component base class in the registry
76 return new_class
77
78 # Only override __init__ for Components not inheriting ComponentManager
79 if True not in [issubclass(x, ComponentManager) for x in bases]:
80 # Allow components to have a no-argument initializer so that
81 # they don't need to worry about accepting the component manager
82 # as argument and invoking the super-class initializer
83 init = d.get('__init__')
84 if not init:
85 # Because we're replacing the initializer, we need to make sure
86 # that any inherited initializers are also called.
87 for init in [b.__init__._original for b in new_class.mro()
88 if issubclass(b, Component)
89 and '__init__' in b.__dict__]:
90 break
91 def maybe_init(self, compmgr, init=init, cls=new_class):
92 if cls not in compmgr.components:
93 compmgr.components[cls] = self
94 if init:
95 init(self)
96 maybe_init._original = init
97 new_class.__init__ = maybe_init
98
99 if d.get('abstract'):
100 # Don't put abstract component classes in the registry
101 return new_class
102
103 ComponentMeta._components.append(new_class)
104 for interface in d.get('_implements', []):
105 ComponentMeta._registry.setdefault(interface, []).append(new_class)
106 for base in [base for base in bases if hasattr(base, '_implements')]:
107 for interface in base._implements:
108 ComponentMeta._registry.setdefault(interface, []).append(new_class)
109
110 return new_class
111
112
113 def implements(*interfaces):
114 """
115 Can be used in the class definiton of `Component` subclasses to declare
116 the extension points that are extended.
117 """
118 import sys
119
120 frame = sys._getframe(1)
121 locals = frame.f_locals
122
123 # Some sanity checks
124 assert locals is not frame.f_globals and '__module__' in frame.f_locals, \
125 'implements() can only be used in a class definition'
126 assert not '_implements' in locals, \
127 'implements() can only be used once in a class definition'
128
129 locals['_implements'] = interfaces
130
131
132 class Component(object):
133 """Base class for components.
134
135 Every component can declare what extension points it provides, as well as
136 what extension points of other components it extends.
137 """
138 __metaclass__ = ComponentMeta
139
140 def __new__(cls, *args, **kwargs):
141 """Return an existing instance of the component if it has already been
142 activated, otherwise create a new instance.
143 """
144 # If this component is also the component manager, just invoke that
145 if issubclass(cls, ComponentManager):
146 self = super(Component, cls).__new__(cls)
147 self.compmgr = self
148 return self
149
150 # The normal case where the component is not also the component manager
151 compmgr = args[0]
152 self = compmgr.components.get(cls)
153 if self is None:
154 self = super(Component, cls).__new__(cls)
155 self.compmgr = compmgr
156 compmgr.component_activated(self)
157 return self
158
159
160 class ComponentManager(object):
161 """The component manager keeps a pool of active components."""
162
163 def __init__(self):
164 """Initialize the component manager."""
165 self.components = {}
166 self.enabled = {}
167 if isinstance(self, Component):
168 self.components[self.__class__] = self
169
170 def __contains__(self, cls):
171 """Return wether the given class is in the list of active components."""
172 return cls in self.components
173
174 def __getitem__(self, cls):
175 """Activate the component instance for the given class, or return the
176 existing the instance if the component has already been activated."""
177 if cls not in self.enabled:
178 self.enabled[cls] = self.is_component_enabled(cls)
179 if not self.enabled[cls]:
180 return None
181 component = self.components.get(cls)
182 if not component:
183 if cls not in ComponentMeta._components:
184 raise TracError, 'Component "%s" not registered' % cls.__name__
185 try:
186 component = cls(self)
187 except TypeError, e:
188 raise TracError, 'Unable to instantiate component %r (%s)' \
189 % (cls, e)
190 return component
191
192 def component_activated(self, component):
193 """Can be overridden by sub-classes so that special initialization for
194 components can be provided.
195 """
196
197 def is_component_enabled(self, cls):
198 """Can be overridden by sub-classes to veto the activation of a
199 component.
200
201 If this method returns False, the component with the given class will
202 not be available.
203 """
204 return True
Copyright (C) 2012-2017 Edgewall Software