def __init__(self, interval): """ Set up the ticker """ self.interval = interval self.subscriptions = {} # set up a twisted asynchronous repeat call self.task = ExtendedLoopingCall(self._callback)
def __init__(self, interval): """ Set up the ticker Args: interval (int): The stepping interval. """ self.interval = interval self.subscriptions = {} self._is_ticking = False self._to_remove = [] self._to_add = [] # set up a twisted asynchronous repeat call self.task = ExtendedLoopingCall(self._callback)
class Ticker(object): """ Represents a repeatedly running task that calls hooks repeatedly. Overload `_callback` to change the way it operates. """ @inlineCallbacks def _callback(self): """ This will be called repeatedly every `self.interval` seconds. `self.subscriptions` contain tuples of (obj, args, kwargs) for each subscribing object. If overloading, this callback is expected to handle all subscriptions when it is triggered. It should not return anything and should not traceback on poorly designed hooks. The callback should ideally work under @inlineCallbacks so it can yield appropriately. The _hook_key, which is passed down through the handler via kwargs is used here to identify which hook method to call. """ self._to_add = [] self._to_remove = [] self._is_ticking = True for store_key, (args, kwargs) in self.subscriptions.iteritems(): callback = yield kwargs.pop("_callback", "at_tick") obj = yield kwargs.pop("_obj", None) try: if callable(callback): # call directly yield callback(*args, **kwargs) continue # try object method if not obj or not obj.pk: # object was deleted between calls self._to_remove.append(store_key) continue else: yield _GA(obj, callback)(*args, **kwargs) except ObjectDoesNotExist: log_trace("Removing ticker.") self._to_remove.append(store_key) except Exception: log_trace() finally: # make sure to re-store kwargs["_callback"] = callback kwargs["_obj"] = obj # cleanup - we do this here to avoid changing the subscription dict while it loops self._is_ticking = False for store_key in self._to_remove: self.remove(store_key) for store_key, (args, kwargs) in self._to_add: self.add(store_key, *args, **kwargs) self._to_remove = [] self._to_add = [] def __init__(self, interval): """ Set up the ticker Args: interval (int): The stepping interval. """ self.interval = interval self.subscriptions = {} self._is_ticking = False self._to_remove = [] self._to_add = [] # set up a twisted asynchronous repeat call self.task = ExtendedLoopingCall(self._callback) def validate(self, start_delay=None): """ Start/stop the task depending on how many subscribers we have using it. Args: start_delay (int): Time to way before starting. """ subs = self.subscriptions if self.task.running: if not subs: self.task.stop() elif subs: self.task.start(self.interval, now=False, start_delay=start_delay) def add(self, store_key, *args, **kwargs): """ Sign up a subscriber to this ticker. Args: store_key (str): Unique storage hash for this ticker subscription. args (any, optional): Arguments to call the hook method with. Kwargs: _start_delay (int): If set, this will be used to delay the start of the trigger instead of `interval`. """ if self._is_ticking: # protects the subscription dict from # updating while it is looping self._to_add.append((store_key, (args, kwargs))) else: start_delay = kwargs.pop("_start_delay", None) self.subscriptions[store_key] = (args, kwargs) self.validate(start_delay=start_delay) def remove(self, store_key): """ Unsubscribe object from this ticker Args: store_key (str): Unique store key. """ if self._is_ticking: # this protects the subscription dict from # updating while it is looping self._to_remove.append(store_key) else: self.subscriptions.pop(store_key, False) self.validate() def stop(self): """ Kill the Task, regardless of subscriptions. """ self.subscriptions = {} self.validate()
class Ticker(object): """ Represents a repeatedly running task that calls hooks repeatedly. Overload `_callback` to change the way it operates. """ @inlineCallbacks def _callback(self): """ This will be called repeatedly every `self.interval` seconds. `self.subscriptions` contain tuples of (obj, args, kwargs) for each subscribing object. If overloading, this callback is expected to handle all subscriptions when it is triggered. It should not return anything and should not traceback on poorly designed hooks. The callback should ideally work under @inlineCallbacks so it can yield appropriately. The _hook_key, which is passed down through the handler via kwargs is used here to identify which hook method to call. """ for store_key, (obj, args, kwargs) in self.subscriptions.items(): hook_key = yield kwargs.pop("_hook_key", "at_tick") if not obj or not obj.pk: # object was deleted between calls self.remove(store_key) continue try: yield _GA(obj, hook_key)(*args, **kwargs) except ObjectDoesNotExist: log_trace() self.remove(store_key) except Exception: log_trace() finally: # make sure to re-store kwargs["_hook_key"] = hook_key def __init__(self, interval): """ Set up the ticker Args: interval (int): The stepping interval. """ self.interval = interval self.subscriptions = {} # set up a twisted asynchronous repeat call self.task = ExtendedLoopingCall(self._callback) def validate(self, start_delay=None): """ Start/stop the task depending on how many subscribers we have using it. Args: start_delay (int): Time to way before starting. """ subs = self.subscriptions if None in subs.values(): # clean out objects that may have been deleted subs = dict((store_key, obj) for store_key, obj in subs if obj) self.subscriptions = subs if self.task.running: if not subs: self.task.stop() elif subs: self.task.start(self.interval, now=False, start_delay=start_delay) def add(self, store_key, obj, *args, **kwargs): """ Sign up a subscriber to this ticker. Args: store_key (str): Unique storage hash for this ticker subscription. obj (Object): Object subscribing to this ticker. args (any, optional): Arguments to call the hook method with. Kwargs: _start_delay (int): If set, this will be used to delay the start of the trigger instead of `interval`. _hooK_key (str): This carries the name of the hook method to call. It is passed on as-is from this method. """ start_delay = kwargs.pop("_start_delay", None) self.subscriptions[store_key] = (obj, args, kwargs) self.validate(start_delay=start_delay) def remove(self, store_key): """ Unsubscribe object from this ticker Args: store_key (str): Unique store key. """ self.subscriptions.pop(store_key, False) self.validate() def stop(self): """ Kill the Task, regardless of subscriptions. """ self.subscriptions = {} self.validate()
class Ticker(object): """ Represents a repeatedly running task that calls hooks repeatedly. Overload `_callback` to change the way it operates. """ @inlineCallbacks def _callback(self): """ This will be called repeatedly every `self.interval` seconds. `self.subscriptions` contain tuples of (obj, args, kwargs) for each subscribing object. If overloading, this callback is expected to handle all subscriptions when it is triggered. It should not return anything and should not traceback on poorly designed hooks. The callback should ideally work under @inlineCallbacks so it can yield appropriately. """ for store_key, (obj, args, kwargs) in self.subscriptions.items(): hook_key = yield kwargs.get("_hook_key", "at_tick") if not obj or not obj.pk: # object was deleted between calls self.remove(store_key) continue try: yield _GA(obj, hook_key)(*args, **kwargs) except ObjectDoesNotExist: log_trace() self.remove(store_key) except Exception: log_trace() def __init__(self, interval): """ Set up the ticker """ self.interval = interval self.subscriptions = {} # set up a twisted asynchronous repeat call self.task = ExtendedLoopingCall(self._callback) def validate(self, start_delay=None): """ Start/stop the task depending on how many subscribers we have using it. """ subs = self.subscriptions if None in subs.values(): # clean out objects that may have been deleted subs = dict((store_key, obj) for store_key, obj in subs if obj) self.subscriptions = subs if self.task.running: if not subs: self.task.stop() elif subs: self.task.start(self.interval, now=False, start_delay=start_delay) def add(self, store_key, obj, *args, **kwargs): """ Sign up a subscriber to this ticker. If kwargs contains a keyword _start_delay, this will be used to delay the start of the trigger instead of interval. """ start_delay = kwargs.pop("_start_delay", None) self.subscriptions[store_key] = (obj, args, kwargs) self.validate(start_delay=start_delay) def remove(self, store_key): """ Unsubscribe object from this ticker """ self.subscriptions.pop(store_key, False) self.validate() def stop(self): """ Kill the Task, regardless of subscriptions """ self.subscriptions = {} self.validate()