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 venusian 

2 

3from zope.interface import implementer, Interface 

4 

5from pyramid.interfaces import ( 

6 IContextFound, 

7 INewRequest, 

8 INewResponse, 

9 IApplicationCreated, 

10 IBeforeRender, 

11 IBeforeTraversal, 

12) 

13 

14 

15class subscriber(object): 

16 """ Decorator activated via a :term:`scan` which treats the function 

17 being decorated as an event subscriber for the set of interfaces passed 

18 as ``*ifaces`` and the set of predicate terms passed as ``**predicates`` 

19 to the decorator constructor. 

20 

21 For example: 

22 

23 .. code-block:: python 

24 

25 from pyramid.events import NewRequest 

26 from pyramid.events import subscriber 

27 

28 @subscriber(NewRequest) 

29 def mysubscriber(event): 

30 event.request.foo = 1 

31 

32 More than one event type can be passed as a constructor argument. The 

33 decorated subscriber will be called for each event type. 

34 

35 .. code-block:: python 

36 

37 from pyramid.events import NewRequest, NewResponse 

38 from pyramid.events import subscriber 

39 

40 @subscriber(NewRequest, NewResponse) 

41 def mysubscriber(event): 

42 print(event) 

43 

44 When the ``subscriber`` decorator is used without passing an arguments, 

45 the function it decorates is called for every event sent: 

46 

47 .. code-block:: python 

48 

49 from pyramid.events import subscriber 

50 

51 @subscriber() 

52 def mysubscriber(event): 

53 print(event) 

54 

55 This method will have no effect until a :term:`scan` is performed 

56 against the package or module which contains it, ala: 

57 

58 .. code-block:: python 

59 

60 from pyramid.config import Configurator 

61 config = Configurator() 

62 config.scan('somepackage_containing_subscribers') 

63 

64 Any ``**predicate`` arguments will be passed along to 

65 :meth:`pyramid.config.Configurator.add_subscriber`. See 

66 :ref:`subscriber_predicates` for a description of how predicates can 

67 narrow the set of circumstances in which a subscriber will be called. 

68 

69 Two additional keyword arguments which will be passed to the 

70 :term:`venusian` ``attach`` function are ``_depth`` and ``_category``. 

71 

72 ``_depth`` is provided for people who wish to reuse this class from another 

73 decorator. The default value is ``0`` and should be specified relative to 

74 the ``subscriber`` invocation. It will be passed in to the 

75 :term:`venusian` ``attach`` function as the depth of the callstack when 

76 Venusian checks if the decorator is being used in a class or module 

77 context. It's not often used, but it can be useful in this circumstance. 

78 

79 ``_category`` sets the decorator category name. It can be useful in 

80 combination with the ``category`` argument of ``scan`` to control which 

81 views should be processed. 

82 

83 See the :py:func:`venusian.attach` function in Venusian for more 

84 information about the ``_depth`` and ``_category`` arguments. 

85 

86 .. versionchanged:: 1.9.1 

87 Added the ``_depth`` and ``_category`` arguments. 

88 

89 """ 

90 

91 venusian = venusian # for unit testing 

92 

93 def __init__(self, *ifaces, **predicates): 

94 self.ifaces = ifaces 

95 self.predicates = predicates 

96 self.depth = predicates.pop('_depth', 0) 

97 self.category = predicates.pop('_category', 'pyramid') 

98 

99 def register(self, scanner, name, wrapped): 

100 config = scanner.config 

101 for iface in self.ifaces or (Interface,): 

102 config.add_subscriber(wrapped, iface, **self.predicates) 

103 

104 def __call__(self, wrapped): 

105 self.venusian.attach( 

106 wrapped, 

107 self.register, 

108 category=self.category, 

109 depth=self.depth + 1, 

110 ) 

111 return wrapped 

112 

113 

114@implementer(INewRequest) 

115class NewRequest(object): 

116 """ An instance of this class is emitted as an :term:`event` 

117 whenever :app:`Pyramid` begins to process a new request. The 

118 event instance has an attribute, ``request``, which is a 

119 :term:`request` object. This event class implements the 

120 :class:`pyramid.interfaces.INewRequest` interface.""" 

121 

122 def __init__(self, request): 

123 self.request = request 

124 

125 

126@implementer(INewResponse) 

127class NewResponse(object): 

128 """ An instance of this class is emitted as an :term:`event` 

129 whenever any :app:`Pyramid` :term:`view` or :term:`exception 

130 view` returns a :term:`response`. 

131 

132 The instance has two attributes:``request``, which is the request 

133 which caused the response, and ``response``, which is the response 

134 object returned by a view or renderer. 

135 

136 If the ``response`` was generated by an :term:`exception view`, the 

137 request will have an attribute named ``exception``, which is the 

138 exception object which caused the exception view to be executed. If the 

139 response was generated by a 'normal' view, this attribute of the request 

140 will be ``None``. 

141 

142 This event will not be generated if a response cannot be created due to 

143 an exception that is not caught by an exception view (no response is 

144 created under this circumstace). 

145 

146 This class implements the 

147 :class:`pyramid.interfaces.INewResponse` interface. 

148 

149 .. note:: 

150 

151 Postprocessing a response is usually better handled in a WSGI 

152 :term:`middleware` component than in subscriber code that is 

153 called by a :class:`pyramid.interfaces.INewResponse` event. 

154 The :class:`pyramid.interfaces.INewResponse` event exists 

155 almost purely for symmetry with the 

156 :class:`pyramid.interfaces.INewRequest` event. 

157 """ 

158 

159 def __init__(self, request, response): 

160 self.request = request 

161 self.response = response 

162 

163 

164@implementer(IBeforeTraversal) 

165class BeforeTraversal(object): 

166 """ 

167 An instance of this class is emitted as an :term:`event` after the 

168 :app:`Pyramid` :term:`router` has attempted to find a :term:`route` object 

169 but before any traversal or view code is executed. The instance has an 

170 attribute, ``request``, which is the request object generated by 

171 :app:`Pyramid`. 

172 

173 Notably, the request object **may** have an attribute named 

174 ``matched_route``, which is the matched route if found. If no route 

175 matched, this attribute is not available. 

176 

177 This class implements the :class:`pyramid.interfaces.IBeforeTraversal` 

178 interface. 

179 """ 

180 

181 def __init__(self, request): 

182 self.request = request 

183 

184 

185@implementer(IContextFound) 

186class ContextFound(object): 

187 """ An instance of this class is emitted as an :term:`event` after 

188 the :app:`Pyramid` :term:`router` finds a :term:`context` 

189 object (after it performs traversal) but before any view code is 

190 executed. The instance has an attribute, ``request``, which is 

191 the request object generated by :app:`Pyramid`. 

192 

193 Notably, the request object will have an attribute named 

194 ``context``, which is the context that will be provided to the 

195 view which will eventually be called, as well as other attributes 

196 attached by context-finding code. 

197 

198 This class implements the 

199 :class:`pyramid.interfaces.IContextFound` interface. 

200 

201 .. note:: 

202 

203 As of :app:`Pyramid` 1.0, for backwards compatibility purposes, this 

204 event may also be imported as :class:`pyramid.events.AfterTraversal`. 

205 """ 

206 

207 def __init__(self, request): 

208 self.request = request 

209 

210 

211AfterTraversal = ContextFound # b/c as of 1.0 

212 

213 

214@implementer(IApplicationCreated) 

215class ApplicationCreated(object): 

216 """ An instance of this class is emitted as an :term:`event` when 

217 the :meth:`pyramid.config.Configurator.make_wsgi_app` is 

218 called. The instance has an attribute, ``app``, which is an 

219 instance of the :term:`router` that will handle WSGI requests. 

220 This class implements the 

221 :class:`pyramid.interfaces.IApplicationCreated` interface. 

222 

223 .. note:: 

224 

225 For backwards compatibility purposes, this class can also be imported as 

226 :class:`pyramid.events.WSGIApplicationCreatedEvent`. This was the name 

227 of the event class before :app:`Pyramid` 1.0. 

228 """ 

229 

230 def __init__(self, app): 

231 self.app = app 

232 self.object = app 

233 

234 

235WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.0) 

236 

237 

238@implementer(IBeforeRender) 

239class BeforeRender(dict): 

240 """ 

241 Subscribers to this event may introspect and modify the set of 

242 :term:`renderer globals` before they are passed to a :term:`renderer`. 

243 This event object itself has a dictionary-like interface that can be used 

244 for this purpose. For example:: 

245 

246 from pyramid.events import subscriber 

247 from pyramid.events import BeforeRender 

248 

249 @subscriber(BeforeRender) 

250 def add_global(event): 

251 event['mykey'] = 'foo' 

252 

253 An object of this type is sent as an event just before a :term:`renderer` 

254 is invoked. 

255 

256 If a subscriber adds a key via ``__setitem__`` that already exists in 

257 the renderer globals dictionary, it will overwrite the older value there. 

258 This can be problematic because event subscribers to the BeforeRender 

259 event do not possess any relative ordering. For maximum interoperability 

260 with other third-party subscribers, if you write an event subscriber meant 

261 to be used as a BeforeRender subscriber, your subscriber code will need to 

262 ensure no value already exists in the renderer globals dictionary before 

263 setting an overriding value (which can be done using ``.get`` or 

264 ``__contains__`` of the event object). 

265 

266 The dictionary returned from the view is accessible through the 

267 :attr:`rendering_val` attribute of a :class:`~pyramid.events.BeforeRender` 

268 event. 

269 

270 Suppose you return ``{'mykey': 'somevalue', 'mykey2': 'somevalue2'}`` from 

271 your view callable, like so:: 

272 

273 from pyramid.view import view_config 

274 

275 @view_config(renderer='some_renderer') 

276 def myview(request): 

277 return {'mykey': 'somevalue', 'mykey2': 'somevalue2'} 

278 

279 :attr:`rendering_val` can be used to access these values from the 

280 :class:`~pyramid.events.BeforeRender` object:: 

281 

282 from pyramid.events import subscriber 

283 from pyramid.events import BeforeRender 

284 

285 @subscriber(BeforeRender) 

286 def read_return(event): 

287 # {'mykey': 'somevalue'} is returned from the view 

288 print(event.rendering_val['mykey']) 

289 

290 In other words, :attr:`rendering_val` is the (non-system) value returned 

291 by a view or passed to ``render*`` as ``value``. This feature is new in 

292 Pyramid 1.2. 

293 

294 For a description of the values present in the renderer globals dictionary, 

295 see :ref:`renderer_system_values`. 

296 

297 .. seealso:: 

298 

299 See also :class:`pyramid.interfaces.IBeforeRender`. 

300 """ 

301 

302 def __init__(self, system, rendering_val=None): 

303 dict.__init__(self, system) 

304 self.rendering_val = rendering_val