Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/pyramid/registry.py : 69%

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