Package proton :: Module _events
[frames] | no frames]

Source Code for Module proton._events

  1  # 
  2  # Licensed to the Apache Software Foundation (ASF) under one 
  3  # or more contributor license agreements.  See the NOTICE file 
  4  # distributed with this work for additional information 
  5  # regarding copyright ownership.  The ASF licenses this file 
  6  # to you under the Apache License, Version 2.0 (the 
  7  # "License"); you may not use this file except in compliance 
  8  # with the License.  You may obtain a copy of the License at 
  9  # 
 10  #   http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, 
 13  # software distributed under the License is distributed on an 
 14  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 15  # KIND, either express or implied.  See the License for the 
 16  # specific language governing permissions and limitations 
 17  # under the License. 
 18  # 
 19   
 20  from __future__ import absolute_import 
 21   
 22  import threading 
 23   
 24  from cproton import PN_CONNECTION_BOUND, PN_CONNECTION_FINAL, PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_CLOSE, \ 
 25      PN_CONNECTION_LOCAL_OPEN, PN_CONNECTION_REMOTE_CLOSE, PN_CONNECTION_REMOTE_OPEN, PN_CONNECTION_UNBOUND, PN_DELIVERY, \ 
 26      PN_LINK_FINAL, PN_LINK_FLOW, PN_LINK_INIT, PN_LINK_LOCAL_CLOSE, PN_LINK_LOCAL_DETACH, PN_LINK_LOCAL_OPEN, \ 
 27      PN_LINK_REMOTE_CLOSE, PN_LINK_REMOTE_DETACH, PN_LINK_REMOTE_OPEN, PN_PYREF, PN_SESSION_FINAL, PN_SESSION_INIT, \ 
 28      PN_SESSION_LOCAL_CLOSE, PN_SESSION_LOCAL_OPEN, PN_SESSION_REMOTE_CLOSE, PN_SESSION_REMOTE_OPEN, PN_TIMER_TASK, \ 
 29      PN_TRANSPORT, PN_TRANSPORT_CLOSED, PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT_TAIL_CLOSED, \ 
 30      pn_cast_pn_connection, pn_cast_pn_delivery, pn_cast_pn_link, pn_cast_pn_session, pn_cast_pn_transport, \ 
 31      pn_class_name, pn_collector, pn_collector_free, pn_collector_more, pn_collector_peek, pn_collector_pop, \ 
 32      pn_collector_put, pn_collector_release, pn_event_class, pn_event_connection, pn_event_context, pn_event_delivery, \ 
 33      pn_event_link, pn_event_session, pn_event_transport, pn_event_type, pn_event_type_name, pn_py2void, pn_void2py 
 34   
 35  from ._delivery import Delivery 
 36  from ._endpoints import Connection, Link, Session 
 37  from ._transport import Transport 
38 39 40 -class Collector:
41
42 - def __init__(self):
43 self._impl = pn_collector()
44
45 - def put(self, obj, etype):
46 pn_collector_put(self._impl, PN_PYREF, pn_py2void(obj), etype.number)
47
48 - def peek(self):
49 return Event.wrap(pn_collector_peek(self._impl))
50
51 - def more(self):
52 return pn_collector_more(self._impl)
53
54 - def pop(self):
55 ev = self.peek() 56 pn_collector_pop(self._impl)
57
58 - def release(self):
59 pn_collector_release(self._impl)
60
61 - def __del__(self):
62 pn_collector_free(self._impl) 63 del self._impl
64 65 66 if "TypeExtender" not in globals():
67 - class TypeExtender:
68 - def __init__(self, number):
69 self.number = number
70
71 - def next(self):
72 try: 73 return self.number 74 finally: 75 self.number += 1
76
77 78 -class EventType(object):
79 _lock = threading.Lock() 80 _extended = TypeExtender(10000) 81 TYPES = {} 82
83 - def __init__(self, name=None, number=None, method=None):
84 if name is None and number is None: 85 raise TypeError("extended events require a name") 86 try: 87 self._lock.acquire() 88 if name is None: 89 name = pn_event_type_name(number) 90 91 if number is None: 92 number = self._extended.next() 93 94 if method is None: 95 method = "on_%s" % name 96 97 self.name = name 98 self.number = number 99 self.method = method 100 101 self.TYPES[number] = self 102 finally: 103 self._lock.release()
104
105 - def __repr__(self):
106 return "EventType(name=%s, number=%d)" % (self.name, self.number)
107
108 - def __str__(self):
109 return self.name
110
111 112 -def _dispatch(handler, method, *args):
113 m = getattr(handler, method, None) 114 if m: 115 m(*args) 116 elif hasattr(handler, "on_unhandled"): 117 handler.on_unhandled(method, *args)
118
119 120 -class EventBase(object):
121
122 - def __init__(self, type):
123 self._type = type
124 125 @property
126 - def type(self):
127 return self._type
128 129 @property
130 - def handler(self):
131 return None
132
133 - def dispatch(self, handler, type=None):
134 type = type or self._type 135 _dispatch(handler, type.method, self) 136 if hasattr(handler, "handlers"): 137 for h in handler.handlers: 138 self.dispatch(h, type)
139
140 - def __repr__(self):
141 return "%s(%r)" % (self._type, self.context)
142
143 144 -def _core(number, method):
145 return EventType(number=number, method=method)
146
147 148 -def _internal(name):
149 return EventType(name=name)
150 151 152 wrappers = { 153 "pn_void": lambda x: pn_void2py(x), 154 "pn_pyref": lambda x: pn_void2py(x), 155 "pn_connection": lambda x: Connection.wrap(pn_cast_pn_connection(x)), 156 "pn_session": lambda x: Session.wrap(pn_cast_pn_session(x)), 157 "pn_link": lambda x: Link.wrap(pn_cast_pn_link(x)), 158 "pn_delivery": lambda x: Delivery.wrap(pn_cast_pn_delivery(x)), 159 "pn_transport": lambda x: Transport.wrap(pn_cast_pn_transport(x)) 160 }
161 162 163 -class Event(EventBase):
164 TIMER_TASK = _core(PN_TIMER_TASK, "on_timer_task") 165 166 CONNECTION_INIT = _core(PN_CONNECTION_INIT, "on_connection_init") 167 CONNECTION_BOUND = _core(PN_CONNECTION_BOUND, "on_connection_bound") 168 CONNECTION_UNBOUND = _core(PN_CONNECTION_UNBOUND, "on_connection_unbound") 169 CONNECTION_LOCAL_OPEN = _core(PN_CONNECTION_LOCAL_OPEN, "on_connection_local_open") 170 CONNECTION_LOCAL_CLOSE = _core(PN_CONNECTION_LOCAL_CLOSE, "on_connection_local_close") 171 CONNECTION_REMOTE_OPEN = _core(PN_CONNECTION_REMOTE_OPEN, "on_connection_remote_open") 172 CONNECTION_REMOTE_CLOSE = _core(PN_CONNECTION_REMOTE_CLOSE, "on_connection_remote_close") 173 CONNECTION_FINAL = _core(PN_CONNECTION_FINAL, "on_connection_final") 174 175 SESSION_INIT = _core(PN_SESSION_INIT, "on_session_init") 176 SESSION_LOCAL_OPEN = _core(PN_SESSION_LOCAL_OPEN, "on_session_local_open") 177 SESSION_LOCAL_CLOSE = _core(PN_SESSION_LOCAL_CLOSE, "on_session_local_close") 178 SESSION_REMOTE_OPEN = _core(PN_SESSION_REMOTE_OPEN, "on_session_remote_open") 179 SESSION_REMOTE_CLOSE = _core(PN_SESSION_REMOTE_CLOSE, "on_session_remote_close") 180 SESSION_FINAL = _core(PN_SESSION_FINAL, "on_session_final") 181 182 LINK_INIT = _core(PN_LINK_INIT, "on_link_init") 183 LINK_LOCAL_OPEN = _core(PN_LINK_LOCAL_OPEN, "on_link_local_open") 184 LINK_LOCAL_CLOSE = _core(PN_LINK_LOCAL_CLOSE, "on_link_local_close") 185 LINK_LOCAL_DETACH = _core(PN_LINK_LOCAL_DETACH, "on_link_local_detach") 186 LINK_REMOTE_OPEN = _core(PN_LINK_REMOTE_OPEN, "on_link_remote_open") 187 LINK_REMOTE_CLOSE = _core(PN_LINK_REMOTE_CLOSE, "on_link_remote_close") 188 LINK_REMOTE_DETACH = _core(PN_LINK_REMOTE_DETACH, "on_link_remote_detach") 189 LINK_FLOW = _core(PN_LINK_FLOW, "on_link_flow") 190 LINK_FINAL = _core(PN_LINK_FINAL, "on_link_final") 191 192 DELIVERY = _core(PN_DELIVERY, "on_delivery") 193 194 TRANSPORT = _core(PN_TRANSPORT, "on_transport") 195 TRANSPORT_ERROR = _core(PN_TRANSPORT_ERROR, "on_transport_error") 196 TRANSPORT_HEAD_CLOSED = _core(PN_TRANSPORT_HEAD_CLOSED, "on_transport_head_closed") 197 TRANSPORT_TAIL_CLOSED = _core(PN_TRANSPORT_TAIL_CLOSED, "on_transport_tail_closed") 198 TRANSPORT_CLOSED = _core(PN_TRANSPORT_CLOSED, "on_transport_closed") 199 200 # These events are now internal events in the python code 201 REACTOR_INIT = _internal("reactor_init") 202 REACTOR_QUIESCED = _internal("reactor_quiesced") 203 REACTOR_FINAL = _internal("reactor_final") 204 205 SELECTABLE_INIT = _internal("selectable_init") 206 SELECTABLE_UPDATED = _internal("selectable_updated") 207 SELECTABLE_READABLE = _internal("selectable_readable") 208 SELECTABLE_WRITABLE = _internal("selectable_writable") 209 SELECTABLE_EXPIRED = _internal("selectable_expired") 210 SELECTABLE_ERROR = _internal("selectable_error") 211 SELECTABLE_FINAL = _internal("selectable_final") 212 213 @staticmethod
214 - def wrap(impl):
215 if impl is None: 216 return None 217 218 number = pn_event_type(impl) 219 cls = pn_event_class(impl) 220 221 if cls: 222 clsname = pn_class_name(cls) 223 context = wrappers[clsname](pn_event_context(impl)) 224 225 # check for an application defined ApplicationEvent and return that. This 226 # avoids an expensive wrap operation invoked by event.context 227 if cls == PN_PYREF and isinstance(context, EventBase): 228 return context 229 else: 230 clsname = None 231 232 event = Event(impl, number, clsname, context) 233 return event
234
235 - def __init__(self, impl, number, clsname, context):
236 self._type = EventType.TYPES[number] 237 self._clsname = clsname 238 self._context = context 239 240 # Do all this messing around to avoid duplicate wrappers 241 if issubclass(type(context), Delivery): 242 self._delivery = context 243 else: 244 self._delivery = Delivery.wrap(pn_event_delivery(impl)) 245 if self._delivery: 246 self._link = self._delivery.link 247 elif issubclass(type(context), Link): 248 self._link = context 249 else: 250 self._link = Link.wrap(pn_event_link(impl)) 251 if self._link: 252 self._session = self._link.session 253 elif issubclass(type(context), Session): 254 self._session = context 255 else: 256 self._session = Session.wrap(pn_event_session(impl)) 257 if self._session: 258 self._connection = self._session.connection 259 elif issubclass(type(context), Connection): 260 self._connection = context 261 else: 262 self._connection = Connection.wrap(pn_event_connection(impl)) 263 264 if issubclass(type(context), Transport): 265 self._transport = context 266 else: 267 self._transport = Transport.wrap(pn_event_transport(impl))
268 269 @property
270 - def clazz(self):
271 return self._clsname
272 273 @property
274 - def context(self):
275 """Returns the context object associated with the event. The type of this depends on the type of event.""" 276 return self._context
277 278 @property
279 - def handler(self):
280 l = self.link 281 if l: 282 h = l.handler 283 if h: 284 return h 285 s = self.session 286 if s: 287 h = s.handler 288 if h: 289 return h 290 c = self.connection 291 if c: 292 h = c.handler 293 if h: 294 return h 295 c = self.context 296 if not c or not hasattr(c, 'handler'): 297 return None 298 h = c.handler 299 return h
300 301 @property
302 - def reactor(self):
303 """ 304 Deprecated: Returns the container (was reactor) associated with the event. 305 """ 306 return self.container
307 308 @property
309 - def container(self):
310 """ 311 Returns the container associated with the event. 312 """ 313 return self._transport._reactor
314
315 - def __getattr__(self, name):
316 """ 317 This will look for a property of the event as an attached context object of the same 318 type as the property (but lowercase) 319 """ 320 c = self.context 321 # Direct type or subclass of type 322 if type(c).__name__.lower() == name or name in [x.__name__.lower() for x in type(c).__bases__]: 323 return c 324 325 # If the attached object is the wrong type then see if *it* has a property of that name 326 return getattr(c, name, None)
327 328 @property
329 - def transport(self):
330 """Returns the transport associated with the event, or null if none is associated with it.""" 331 return self._transport
332 333 @property
334 - def connection(self):
335 """Returns the connection associated with the event, or null if none is associated with it.""" 336 return self._connection
337 338 @property
339 - def session(self):
340 """Returns the session associated with the event, or null if none is associated with it.""" 341 return self._session
342 343 @property 347 348 @property
349 - def sender(self):
350 """Returns the sender link associated with the event, or null if 351 none is associated with it. This is essentially an alias for 352 link(), that does an additional check on the type of the 353 link.""" 354 l = self.link 355 if l and l.is_sender: 356 return l 357 else: 358 return None
359 360 @property
361 - def receiver(self):
362 """Returns the receiver link associated with the event, or null if 363 none is associated with it. This is essentially an alias for 364 link(), that does an additional check on the type of the link.""" 365 l = self.link 366 if l and l.is_receiver: 367 return l 368 else: 369 return None
370 371 @property
372 - def delivery(self):
373 """Returns the delivery associated with the event, or null if none is associated with it.""" 374 return self._delivery
375
376 377 -class LazyHandlers(object):
378 - def __get__(self, obj, clazz):
379 if obj is None: 380 return self 381 ret = [] 382 obj.__dict__['handlers'] = ret 383 return ret
384
385 386 -class Handler(object):
387 handlers = LazyHandlers() 388 389 # TODO What to do with on_error?
390 - def add(self, handler, on_error=None):
391 """Add a child handler""" 392 self.handlers.append(handler)
393
394 - def on_unhandled(self, method, *args):
395 pass
396