commit | author | age
|
e49638
|
1 |
import operator |
c15cbc
|
2 |
import threading |
e49638
|
3 |
|
CM |
4 |
from zope.interface import implementer |
0dde01
|
5 |
from zope.interface.registry import Components |
857de3
|
6 |
|
e6c2d2
|
7 |
from pyramid.compat import text_ |
398465
|
8 |
from pyramid.decorator import reify |
e49638
|
9 |
|
3b5ccb
|
10 |
from pyramid.interfaces import ( |
e49638
|
11 |
IIntrospector, |
CM |
12 |
IIntrospectable, |
5e20f9
|
13 |
ISettings, |
3edad5
|
14 |
) |
5e20f9
|
15 |
|
MM |
16 |
from pyramid.path import ( |
|
17 |
CALLER_PACKAGE, |
|
18 |
caller_package, |
|
19 |
) |
e6c2d2
|
20 |
|
CM |
21 |
empty = text_('') |
e35dc1
|
22 |
|
17b8bc
|
23 |
class Registry(Components, dict): |
5e20f9
|
24 |
""" A registry object is an :term:`application registry`. |
MM |
25 |
|
|
26 |
It is used by the framework itself to perform mappings of URLs to view |
|
27 |
callables, as well as servicing other various framework duties. A registry |
|
28 |
has its own internal API, but this API is rarely used by Pyramid |
|
29 |
application developers (it's usually only used by developers of the |
|
30 |
Pyramid framework and Pyramid addons). But it has a number of attributes |
|
31 |
that may be useful to application developers within application code, |
|
32 |
such as ``settings``, which is a dictionary containing application |
|
33 |
deployment settings. |
a1365e
|
34 |
|
CM |
35 |
For information about the purpose and usage of the application registry, |
|
36 |
see :ref:`zca_chapter`. |
|
37 |
|
5e20f9
|
38 |
The registry may be used both as an :class:`pyramid.interfaces.IDict` and |
MM |
39 |
as a Zope component registry. |
|
40 |
These two ways of storing configuration are independent. |
|
41 |
Applications will tend to prefer to store information as key-values |
|
42 |
whereas addons may prefer to use the component registry to avoid naming |
|
43 |
conflicts and to provide more complex lookup mechanisms. |
|
44 |
|
a1365e
|
45 |
The application registry is usually accessed as ``request.registry`` in |
5e20f9
|
46 |
application code. By the time a registry is used to handle requests it |
MM |
47 |
should be considered frozen and read-only. Any changes to its internal |
|
48 |
state should be done with caution and concern for thread-safety. |
a1365e
|
49 |
|
CM |
50 |
""" |
2bb9b7
|
51 |
|
CM |
52 |
# for optimization purposes, if no listeners are listening, don't try |
|
53 |
# to notify them |
7292d4
|
54 |
has_listeners = False |
3b5ccb
|
55 |
|
5a972b
|
56 |
_settings = None |
17b8bc
|
57 |
|
f6af10
|
58 |
def __init__(self, package_name=CALLER_PACKAGE, *args, **kw): |
c15cbc
|
59 |
# add a registry-instance-specific lock, which is used when the lookup |
CM |
60 |
# cache is mutated |
|
61 |
self._lock = threading.Lock() |
|
62 |
# add a view lookup cache |
|
63 |
self._clear_view_lookup_cache() |
5e20f9
|
64 |
if package_name is CALLER_PACKAGE: |
MM |
65 |
package_name = caller_package().__name__ |
f6af10
|
66 |
Components.__init__(self, package_name, *args, **kw) |
5e20f9
|
67 |
dict.__init__(self) |
c15cbc
|
68 |
|
CM |
69 |
def _clear_view_lookup_cache(self): |
|
70 |
self._view_lookup_cache = {} |
|
71 |
|
79ef3d
|
72 |
def __nonzero__(self): |
CM |
73 |
# defeat bool determination via dict.__len__ |
|
74 |
return True |
|
75 |
|
398465
|
76 |
@reify |
HB |
77 |
def package_name(self): |
|
78 |
return self.__name__ |
|
79 |
|
2bb9b7
|
80 |
def registerSubscriptionAdapter(self, *arg, **kw): |
CM |
81 |
result = Components.registerSubscriptionAdapter(self, *arg, **kw) |
7292d4
|
82 |
self.has_listeners = True |
2bb9b7
|
83 |
return result |
c9aece
|
84 |
|
e6c2d2
|
85 |
def registerSelfAdapter(self, required=None, provided=None, name=empty, |
CM |
86 |
info=empty, event=True): |
d868ff
|
87 |
# registerAdapter analogue which always returns the object itself |
CM |
88 |
# when required is matched |
|
89 |
return self.registerAdapter(lambda x: x, required=required, |
|
90 |
provided=provided, name=name, |
|
91 |
info=info, event=event) |
|
92 |
|
c209f8
|
93 |
def queryAdapterOrSelf(self, object, interface, default=None): |
d868ff
|
94 |
# queryAdapter analogue which returns the object if it implements |
CM |
95 |
# the interface, otherwise it will return an adaptation to the |
|
96 |
# interface |
1a6fc7
|
97 |
if not interface.providedBy(object): |
c209f8
|
98 |
return self.queryAdapter(object, interface, default=default) |
d868ff
|
99 |
return object |
CM |
100 |
|
2bb9b7
|
101 |
def registerHandler(self, *arg, **kw): |
CM |
102 |
result = Components.registerHandler(self, *arg, **kw) |
7292d4
|
103 |
self.has_listeners = True |
2bb9b7
|
104 |
return result |
CM |
105 |
|
971537
|
106 |
def notify(self, *events): |
7292d4
|
107 |
if self.has_listeners: |
2bb9b7
|
108 |
# iterating over subscribers assures they get executed |
58fdd1
|
109 |
[ _ for _ in self.subscribers(events, None) ] |
3fd912
|
110 |
|
5a972b
|
111 |
# backwards compatibility for code that wants to look up a settings |
CM |
112 |
# object via ``registry.getUtility(ISettings)`` |
|
113 |
def _get_settings(self): |
|
114 |
return self._settings |
|
115 |
|
|
116 |
def _set_settings(self, settings): |
|
117 |
self.registerUtility(settings, ISettings) |
|
118 |
self._settings = settings |
|
119 |
|
|
120 |
settings = property(_get_settings, _set_settings) |
|
121 |
|
e49638
|
122 |
@implementer(IIntrospector) |
CM |
123 |
class Introspector(object): |
|
124 |
def __init__(self): |
|
125 |
self._refs = {} |
|
126 |
self._categories = {} |
|
127 |
self._counter = 0 |
3b5ccb
|
128 |
|
e49638
|
129 |
def add(self, intr): |
CM |
130 |
category = self._categories.setdefault(intr.category_name, {}) |
|
131 |
category[intr.discriminator] = intr |
|
132 |
category[intr.discriminator_hash] = intr |
|
133 |
intr.order = self._counter |
|
134 |
self._counter += 1 |
3b5ccb
|
135 |
|
e49638
|
136 |
def get(self, category_name, discriminator, default=None): |
CM |
137 |
category = self._categories.setdefault(category_name, {}) |
|
138 |
intr = category.get(discriminator, default) |
|
139 |
return intr |
3b5ccb
|
140 |
|
79f34b
|
141 |
def get_category(self, category_name, default=None, sort_key=None): |
57a0d7
|
142 |
if sort_key is None: |
CM |
143 |
sort_key = operator.attrgetter('order') |
79f34b
|
144 |
category = self._categories.get(category_name) |
CM |
145 |
if category is None: |
|
146 |
return default |
e49638
|
147 |
values = category.values() |
57a0d7
|
148 |
values = sorted(set(values), key=sort_key) |
79f34b
|
149 |
return [ |
25c64c
|
150 |
{'introspectable': intr, |
JA |
151 |
'related': self.related(intr)} |
|
152 |
for intr in values |
|
153 |
] |
e49638
|
154 |
|
57a0d7
|
155 |
def categorized(self, sort_key=None): |
e49638
|
156 |
L = [] |
CM |
157 |
for category_name in self.categories(): |
79f34b
|
158 |
L.append((category_name, self.get_category(category_name, |
CM |
159 |
sort_key=sort_key))) |
e49638
|
160 |
return L |
57a0d7
|
161 |
|
CM |
162 |
def categories(self): |
|
163 |
return sorted(self._categories.keys()) |
e49638
|
164 |
|
CM |
165 |
def remove(self, category_name, discriminator): |
|
166 |
intr = self.get(category_name, discriminator) |
|
167 |
if intr is None: |
|
168 |
return |
57a0d7
|
169 |
L = self._refs.pop(intr, []) |
e49638
|
170 |
for d in L: |
CM |
171 |
L2 = self._refs[d] |
57a0d7
|
172 |
L2.remove(intr) |
e49638
|
173 |
category = self._categories[intr.category_name] |
CM |
174 |
del category[intr.discriminator] |
|
175 |
del category[intr.discriminator_hash] |
|
176 |
|
|
177 |
def _get_intrs_by_pairs(self, pairs): |
|
178 |
introspectables = [] |
|
179 |
for pair in pairs: |
|
180 |
category_name, discriminator = pair |
|
181 |
intr = self._categories.get(category_name, {}).get(discriminator) |
|
182 |
if intr is None: |
|
183 |
raise KeyError((category_name, discriminator)) |
|
184 |
introspectables.append(intr) |
|
185 |
return introspectables |
|
186 |
|
|
187 |
def relate(self, *pairs): |
|
188 |
introspectables = self._get_intrs_by_pairs(pairs) |
|
189 |
relatable = ((x,y) for x in introspectables for y in introspectables) |
|
190 |
for x, y in relatable: |
|
191 |
L = self._refs.setdefault(x, []) |
|
192 |
if x is not y and y not in L: |
|
193 |
L.append(y) |
|
194 |
|
|
195 |
def unrelate(self, *pairs): |
|
196 |
introspectables = self._get_intrs_by_pairs(pairs) |
|
197 |
relatable = ((x,y) for x in introspectables for y in introspectables) |
|
198 |
for x, y in relatable: |
|
199 |
L = self._refs.get(x, []) |
|
200 |
if y in L: |
|
201 |
L.remove(y) |
|
202 |
|
|
203 |
def related(self, intr): |
|
204 |
category_name, discriminator = intr.category_name, intr.discriminator |
|
205 |
intr = self._categories.get(category_name, {}).get(discriminator) |
|
206 |
if intr is None: |
|
207 |
raise KeyError((category_name, discriminator)) |
|
208 |
return self._refs.get(intr, []) |
|
209 |
|
|
210 |
@implementer(IIntrospectable) |
|
211 |
class Introspectable(dict): |
|
212 |
|
35ad08
|
213 |
order = 0 # mutated by introspector.add |
57a9d6
|
214 |
action_info = None # mutated by self.register |
e49638
|
215 |
|
CM |
216 |
def __init__(self, category_name, discriminator, title, type_name): |
|
217 |
self.category_name = category_name |
|
218 |
self.discriminator = discriminator |
|
219 |
self.title = title |
|
220 |
self.type_name = type_name |
35ad08
|
221 |
self._relations = [] |
e49638
|
222 |
|
CM |
223 |
def relate(self, category_name, discriminator): |
35ad08
|
224 |
self._relations.append((True, category_name, discriminator)) |
e49638
|
225 |
|
CM |
226 |
def unrelate(self, category_name, discriminator): |
35ad08
|
227 |
self._relations.append((False, category_name, discriminator)) |
e49638
|
228 |
|
d98612
|
229 |
def _assert_resolved(self): |
CM |
230 |
assert undefer(self.discriminator) is self.discriminator |
|
231 |
|
e49638
|
232 |
@property |
CM |
233 |
def discriminator_hash(self): |
d98612
|
234 |
self._assert_resolved() |
e49638
|
235 |
return hash(self.discriminator) |
CM |
236 |
|
|
237 |
def __hash__(self): |
d98612
|
238 |
self._assert_resolved() |
e49638
|
239 |
return hash((self.category_name,) + (self.discriminator,)) |
CM |
240 |
|
|
241 |
def __repr__(self): |
d98612
|
242 |
self._assert_resolved() |
e49638
|
243 |
return '<%s category %r, discriminator %r>' % (self.__class__.__name__, |
CM |
244 |
self.category_name, |
|
245 |
self.discriminator) |
|
246 |
|
|
247 |
def __nonzero__(self): |
|
248 |
return True |
|
249 |
|
|
250 |
__bool__ = __nonzero__ # py3 |
3b5ccb
|
251 |
|
57a9d6
|
252 |
def register(self, introspector, action_info): |
d98612
|
253 |
self.discriminator = undefer(self.discriminator) |
57a9d6
|
254 |
self.action_info = action_info |
CM |
255 |
introspector.add(self) |
|
256 |
for relate, category_name, discriminator in self._relations: |
d98612
|
257 |
discriminator = undefer(discriminator) |
57a9d6
|
258 |
if relate: |
CM |
259 |
method = introspector.relate |
|
260 |
else: |
|
261 |
method = introspector.unrelate |
|
262 |
method( |
|
263 |
(self.category_name, self.discriminator), |
|
264 |
(category_name, discriminator) |
|
265 |
) |
|
266 |
|
d98612
|
267 |
class Deferred(object): |
CM |
268 |
""" Can be used by a third-party configuration extender to wrap a |
|
269 |
:term:`discriminator` during configuration if an immediately hashable |
|
270 |
discriminator cannot be computed because it relies on unresolved values. |
|
271 |
The function should accept no arguments and should return a hashable |
|
272 |
discriminator.""" |
|
273 |
def __init__(self, func): |
|
274 |
self.func = func |
|
275 |
|
78f6ef
|
276 |
@reify |
MM |
277 |
def value(self): |
71e92d
|
278 |
result = self.func() |
KK |
279 |
del self.func |
|
280 |
return result |
d98612
|
281 |
|
78f6ef
|
282 |
def resolve(self): |
MM |
283 |
return self.value |
|
284 |
|
d98612
|
285 |
def undefer(v): |
CM |
286 |
""" Function which accepts an object and returns it unless it is a |
|
287 |
:class:`pyramid.registry.Deferred` instance. If it is an instance of |
|
288 |
that class, its ``resolve`` method is called, and the result of the |
|
289 |
method is returned.""" |
|
290 |
if isinstance(v, Deferred): |
|
291 |
v = v.resolve() |
|
292 |
return v |
|
293 |
|
|
294 |
class predvalseq(tuple): |
|
295 |
""" A subtype of tuple used to represent a sequence of predicate values """ |
|
296 |
|
59c5df
|
297 |
global_registry = Registry('global') |