Hide keyboard shortcuts

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 

3 

4from zope.interface import implementer 

5from zope.interface.registry import Components 

6 

7from pyramid.compat import text_ 

8from pyramid.decorator import reify 

9 

10from pyramid.interfaces import IIntrospector, IIntrospectable, ISettings 

11 

12from pyramid.path import CALLER_PACKAGE, caller_package 

13 

14empty = text_('') 

15 

16 

17class Registry(Components, dict): 

18 """ A registry object is an :term:`application registry`. 

19 

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. 

28 

29 For information about the purpose and usage of the application registry, 

30 see :ref:`zca_chapter`. 

31 

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. 

38 

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. 

43 

44 """ 

45 

46 # for optimization purposes, if no listeners are listening, don't try 

47 # to notify them 

48 has_listeners = False 

49 

50 _settings = None 

51 

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) 

62 

63 def _clear_view_lookup_cache(self): 

64 self._view_lookup_cache = {} 

65 

66 def __nonzero__(self): 

67 # defeat bool determination via dict.__len__ 

68 return True 

69 

70 @reify 

71 def package_name(self): 

72 return self.__name__ 

73 

74 def registerSubscriptionAdapter(self, *arg, **kw): 

75 result = Components.registerSubscriptionAdapter(self, *arg, **kw) 

76 self.has_listeners = True 

77 return result 

78 

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 ) 

92 

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 

100 

101 def registerHandler(self, *arg, **kw): 

102 result = Components.registerHandler(self, *arg, **kw) 

103 self.has_listeners = True 

104 return result 

105 

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)] 

110 

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 

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 

122 

123@implementer(IIntrospector) 

124class Introspector(object): 

125 def __init__(self): 

126 self._refs = {} 

127 self._categories = {} 

128 self._counter = 0 

129 

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 

136 

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 

141 

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 ] 

154 

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 

165 

166 def categories(self): 

167 return sorted(self._categories.keys()) 

168 

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] 

180 

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 

190 

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) 

198 

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) 

206 

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, []) 

213 

214 

215@implementer(IIntrospectable) 

216class Introspectable(dict): 

217 

218 order = 0 # mutated by introspector.add 

219 action_info = None # mutated by self.register 

220 

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 = [] 

227 

228 def relate(self, category_name, discriminator): 

229 self._relations.append((True, category_name, discriminator)) 

230 

231 def unrelate(self, category_name, discriminator): 

232 self._relations.append((False, category_name, discriminator)) 

233 

234 def _assert_resolved(self): 

235 assert undefer(self.discriminator) is self.discriminator 

236 

237 @property 

238 def discriminator_hash(self): 

239 self._assert_resolved() 

240 return hash(self.discriminator) 

241 

242 def __hash__(self): 

243 self._assert_resolved() 

244 return hash((self.category_name,) + (self.discriminator,)) 

245 

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 ) 

253 

254 def __nonzero__(self): 

255 return True 

256 

257 __bool__ = __nonzero__ # py3 

258 

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 ) 

273 

274 

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.""" 

281 

282 def __init__(self, func): 

283 self.func = func 

284 

285 @reify 

286 def value(self): 

287 result = self.func() 

288 del self.func 

289 return result 

290 

291 def resolve(self): 

292 return self.value 

293 

294 

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 

303 

304 

305class predvalseq(tuple): 

306 """ A subtype of tuple used to represent a sequence of predicate values """ 

307 

308 

309global_registry = Registry('global')