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

1from webob import Response as WebobResponse 

2 

3from functools import update_wrapper 

4 

5from zope.interface import Interface 

6 

7from pyramid.interfaces import IResponse, ITraverser, IResourceURL 

8 

9from pyramid.util import takes_one_arg 

10 

11from pyramid.config.actions import action_method 

12 

13 

14class AdaptersConfiguratorMixin(object): 

15 @action_method 

16 def add_subscriber(self, subscriber, iface=None, **predicates): 

17 """Add an event :term:`subscriber` for the event stream 

18 implied by the supplied ``iface`` interface. 

19 

20 The ``subscriber`` argument represents a callable object (or a 

21 :term:`dotted Python name` which identifies a callable); it will be 

22 called with a single object ``event`` whenever :app:`Pyramid` emits 

23 an :term:`event` associated with the ``iface``, which may be an 

24 :term:`interface` or a class or a :term:`dotted Python name` to a 

25 global object representing an interface or a class. 

26 

27 Using the default ``iface`` value, ``None`` will cause the subscriber 

28 to be registered for all event types. See :ref:`events_chapter` for 

29 more information about events and subscribers. 

30 

31 Any number of predicate keyword arguments may be passed in 

32 ``**predicates``. Each predicate named will narrow the set of 

33 circumstances in which the subscriber will be invoked. Each named 

34 predicate must have been registered via 

35 :meth:`pyramid.config.Configurator.add_subscriber_predicate` before it 

36 can be used. See :ref:`subscriber_predicates` for more information. 

37 

38 .. versionadded:: 1.4 

39 The ``**predicates`` argument. 

40 """ 

41 dotted = self.maybe_dotted 

42 subscriber, iface = dotted(subscriber), dotted(iface) 

43 if iface is None: 

44 iface = (Interface,) 

45 if not isinstance(iface, (tuple, list)): 

46 iface = (iface,) 

47 

48 def register(): 

49 predlist = self.get_predlist('subscriber') 

50 order, preds, phash = predlist.make(self, **predicates) 

51 

52 derived_predicates = [self._derive_predicate(p) for p in preds] 

53 derived_subscriber = self._derive_subscriber( 

54 subscriber, derived_predicates 

55 ) 

56 

57 intr.update( 

58 { 

59 'phash': phash, 

60 'order': order, 

61 'predicates': preds, 

62 'derived_predicates': derived_predicates, 

63 'derived_subscriber': derived_subscriber, 

64 } 

65 ) 

66 

67 self.registry.registerHandler(derived_subscriber, iface) 

68 

69 intr = self.introspectable( 

70 'subscribers', 

71 id(subscriber), 

72 self.object_description(subscriber), 

73 'subscriber', 

74 ) 

75 

76 intr['subscriber'] = subscriber 

77 intr['interfaces'] = iface 

78 

79 self.action(None, register, introspectables=(intr,)) 

80 return subscriber 

81 

82 def _derive_predicate(self, predicate): 

83 derived_predicate = predicate 

84 

85 if eventonly(predicate): 

86 

87 def derived_predicate(*arg): 

88 return predicate(arg[0]) 

89 

90 # seems pointless to try to fix __doc__, __module__, etc as 

91 # predicate will invariably be an instance 

92 

93 return derived_predicate 

94 

95 def _derive_subscriber(self, subscriber, predicates): 

96 derived_subscriber = subscriber 

97 

98 if eventonly(subscriber): 

99 

100 def derived_subscriber(*arg): 

101 return subscriber(arg[0]) 

102 

103 if hasattr(subscriber, '__name__'): 

104 update_wrapper(derived_subscriber, subscriber) 

105 

106 if not predicates: 

107 return derived_subscriber 

108 

109 def subscriber_wrapper(*arg): 

110 # We need to accept *arg and pass it along because zope subscribers 

111 # are designed awkwardly. Notification via 

112 # registry.adapter.subscribers will always call an associated 

113 # subscriber with all of the objects involved in the subscription 

114 # lookup, despite the fact that the event sender always has the 

115 # option to attach those objects to the event object itself, and 

116 # almost always does. 

117 # 

118 # The "eventonly" jazz sprinkled in this function and related 

119 # functions allows users to define subscribers and predicates which 

120 # accept only an event argument without needing to accept the rest 

121 # of the adaptation arguments. Had I been smart enough early on to 

122 # use .subscriptions to find the subscriber functions in order to 

123 # call them manually with a single "event" argument instead of 

124 # relying on .subscribers to both find and call them implicitly 

125 # with all args, the eventonly hack would not have been required. 

126 # At this point, though, using .subscriptions and manual execution 

127 # is not possible without badly breaking backwards compatibility. 

128 if all((predicate(*arg) for predicate in predicates)): 

129 return derived_subscriber(*arg) 

130 

131 if hasattr(subscriber, '__name__'): 

132 update_wrapper(subscriber_wrapper, subscriber) 

133 

134 return subscriber_wrapper 

135 

136 @action_method 

137 def add_subscriber_predicate( 

138 self, name, factory, weighs_more_than=None, weighs_less_than=None 

139 ): 

140 """ 

141 .. versionadded:: 1.4 

142 

143 Adds a subscriber predicate factory. The associated subscriber 

144 predicate can later be named as a keyword argument to 

145 :meth:`pyramid.config.Configurator.add_subscriber` in the 

146 ``**predicates`` anonymous keyword argument dictionary. 

147 

148 ``name`` should be the name of the predicate. It must be a valid 

149 Python identifier (it will be used as a ``**predicates`` keyword 

150 argument to :meth:`~pyramid.config.Configurator.add_subscriber`). 

151 

152 ``factory`` should be a :term:`predicate factory` or :term:`dotted 

153 Python name` which refers to a predicate factory. 

154 

155 See :ref:`subscriber_predicates` for more information. 

156 

157 """ 

158 self._add_predicate( 

159 'subscriber', 

160 name, 

161 factory, 

162 weighs_more_than=weighs_more_than, 

163 weighs_less_than=weighs_less_than, 

164 ) 

165 

166 @action_method 

167 def add_response_adapter(self, adapter, type_or_iface): 

168 """ When an object of type (or interface) ``type_or_iface`` is 

169 returned from a view callable, Pyramid will use the adapter 

170 ``adapter`` to convert it into an object which implements the 

171 :class:`pyramid.interfaces.IResponse` interface. If ``adapter`` is 

172 None, an object returned of type (or interface) ``type_or_iface`` 

173 will itself be used as a response object. 

174 

175 ``adapter`` and ``type_or_interface`` may be Python objects or 

176 strings representing dotted names to importable Python global 

177 objects. 

178 

179 See :ref:`using_iresponse` for more information.""" 

180 adapter = self.maybe_dotted(adapter) 

181 type_or_iface = self.maybe_dotted(type_or_iface) 

182 

183 def register(): 

184 reg = self.registry 

185 if adapter is None: 

186 reg.registerSelfAdapter((type_or_iface,), IResponse) 

187 else: 

188 reg.registerAdapter(adapter, (type_or_iface,), IResponse) 

189 

190 discriminator = (IResponse, type_or_iface) 

191 intr = self.introspectable( 

192 'response adapters', 

193 discriminator, 

194 self.object_description(adapter), 

195 'response adapter', 

196 ) 

197 intr['adapter'] = adapter 

198 intr['type'] = type_or_iface 

199 self.action(discriminator, register, introspectables=(intr,)) 

200 

201 def add_default_response_adapters(self): 

202 # cope with WebOb response objects that aren't decorated with IResponse 

203 self.add_response_adapter(None, WebobResponse) 

204 

205 @action_method 

206 def add_traverser(self, adapter, iface=None): 

207 """ 

208 The superdefault :term:`traversal` algorithm that :app:`Pyramid` uses 

209 is explained in :ref:`traversal_algorithm`. Though it is rarely 

210 necessary, this default algorithm can be swapped out selectively for 

211 a different traversal pattern via configuration. The section 

212 entitled :ref:`changing_the_traverser` details how to create a 

213 traverser class. 

214 

215 For example, to override the superdefault traverser used by Pyramid, 

216 you might do something like this: 

217 

218 .. code-block:: python 

219 

220 from myapp.traversal import MyCustomTraverser 

221 config.add_traverser(MyCustomTraverser) 

222 

223 This would cause the Pyramid superdefault traverser to never be used; 

224 instead all traversal would be done using your ``MyCustomTraverser`` 

225 class, no matter which object was returned by the :term:`root 

226 factory` of this application. Note that we passed no arguments to 

227 the ``iface`` keyword parameter. The default value of ``iface``, 

228 ``None`` represents that the registered traverser should be used when 

229 no other more specific traverser is available for the object returned 

230 by the root factory. 

231 

232 However, more than one traversal algorithm can be active at the same 

233 time. The traverser used can depend on the result of the :term:`root 

234 factory`. For instance, if your root factory returns more than one 

235 type of object conditionally, you could claim that an alternate 

236 traverser adapter should be used against one particular class or 

237 interface returned by that root factory. When the root factory 

238 returned an object that implemented that class or interface, a custom 

239 traverser would be used. Otherwise, the default traverser would be 

240 used. The ``iface`` argument represents the class of the object that 

241 the root factory might return or an :term:`interface` that the object 

242 might implement. 

243 

244 To use a particular traverser only when the root factory returns a 

245 particular class: 

246 

247 .. code-block:: python 

248 

249 config.add_traverser(MyCustomTraverser, MyRootClass) 

250 

251 When more than one traverser is active, the "most specific" traverser 

252 will be used (the one that matches the class or interface of the 

253 value returned by the root factory most closely). 

254 

255 Note that either ``adapter`` or ``iface`` can be a :term:`dotted 

256 Python name` or a Python object. 

257 

258 See :ref:`changing_the_traverser` for more information. 

259 """ 

260 iface = self.maybe_dotted(iface) 

261 adapter = self.maybe_dotted(adapter) 

262 

263 def register(iface=iface): 

264 if iface is None: 

265 iface = Interface 

266 self.registry.registerAdapter(adapter, (iface,), ITraverser) 

267 

268 discriminator = ('traverser', iface) 

269 intr = self.introspectable( 

270 'traversers', 

271 discriminator, 

272 'traverser for %r' % iface, 

273 'traverser', 

274 ) 

275 intr['adapter'] = adapter 

276 intr['iface'] = iface 

277 self.action(discriminator, register, introspectables=(intr,)) 

278 

279 @action_method 

280 def add_resource_url_adapter(self, adapter, resource_iface=None): 

281 """ 

282 .. versionadded:: 1.3 

283 

284 When you add a traverser as described in 

285 :ref:`changing_the_traverser`, it's convenient to continue to use the 

286 :meth:`pyramid.request.Request.resource_url` API. However, since the 

287 way traversal is done may have been modified, the URLs that 

288 ``resource_url`` generates by default may be incorrect when resources 

289 are returned by a custom traverser. 

290 

291 If you've added a traverser, you can change how 

292 :meth:`~pyramid.request.Request.resource_url` generates a URL for a 

293 specific type of resource by calling this method. 

294 

295 The ``adapter`` argument represents a class that implements the 

296 :class:`~pyramid.interfaces.IResourceURL` interface. The class 

297 constructor should accept two arguments in its constructor (the 

298 resource and the request) and the resulting instance should provide 

299 the attributes detailed in that interface (``virtual_path`` and 

300 ``physical_path``, in particular). 

301 

302 The ``resource_iface`` argument represents a class or interface that 

303 the resource should possess for this url adapter to be used when 

304 :meth:`pyramid.request.Request.resource_url` looks up a resource url 

305 adapter. If ``resource_iface`` is not passed, or it is passed as 

306 ``None``, the url adapter will be used for every type of resource. 

307 

308 See :ref:`changing_resource_url` for more information. 

309 """ 

310 adapter = self.maybe_dotted(adapter) 

311 resource_iface = self.maybe_dotted(resource_iface) 

312 

313 def register(resource_iface=resource_iface): 

314 if resource_iface is None: 

315 resource_iface = Interface 

316 self.registry.registerAdapter( 

317 adapter, (resource_iface, Interface), IResourceURL 

318 ) 

319 

320 discriminator = ('resource url adapter', resource_iface) 

321 intr = self.introspectable( 

322 'resource url adapters', 

323 discriminator, 

324 'resource url adapter for resource iface %r' % resource_iface, 

325 'resource url adapter', 

326 ) 

327 intr['adapter'] = adapter 

328 intr['resource_iface'] = resource_iface 

329 self.action(discriminator, register, introspectables=(intr,)) 

330 

331 

332def eventonly(callee): 

333 return takes_one_arg(callee, argname='event')