Mercurial > genshi > mirror
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 |