Exemple #1
0
    def add(self, cb, *args, **kwargs):
        """Adds a new periodic callback to the current worker.

        Returns a :py:class:`.Watcher` if added successfully or the value
        ``None`` if not (or raises a ``ValueError`` if the callback is not
        correctly formed and/or decorated).

        :param cb: a callable object/method/function previously decorated
                   with the :py:func:`.periodic` decorator
        :type cb: callable
        """
        if not six.callable(cb):
            raise ValueError("Periodic callback %r must be callable" % cb)
        missing_attrs = _check_attrs(cb)
        if missing_attrs:
            raise ValueError("Periodic callback %r missing required"
                             " attributes %s" % (cb, missing_attrs))
        if not cb._is_periodic:
            return None
        now = self._now_func()
        with self._waiter:
            cb_index = len(self._works)
            cb_metrics = self._INITIAL_METRICS.copy()
            work = Work(utils.get_callback_name(cb), cb, args, kwargs)
            watcher = Watcher(cb_metrics, work)
            self._works.append(work)
            self._watchers.append((cb_metrics, watcher))
            if cb._periodic_run_immediately:
                self._immediates.append(cb_index)
            else:
                next_run = self._initial_schedule_strategy(cb, now)
                self._schedule.push(next_run, cb_index)
            self._waiter.notify_all()
            return watcher
Exemple #2
0
    def add(self, cb, *args, **kwargs):
        """Adds a new periodic callback to the current worker.

        Returns a :py:class:`.Watcher` if added successfully or the value
        ``None`` if not (or raises a ``ValueError`` if the callback is not
        correctly formed and/or decorated).

        :param cb: a callable object/method/function previously decorated
                   with the :py:func:`.periodic` decorator
        :type cb: callable
        """
        if not six.callable(cb):
            raise ValueError("Periodic callback %r must be callable" % cb)
        missing_attrs = _check_attrs(cb)
        if missing_attrs:
            raise ValueError("Periodic callback %r missing required"
                             " attributes %s" % (cb, missing_attrs))
        if not cb._is_periodic:
            return None
        now = self._now_func()
        with self._waiter:
            cb_index = len(self._callables)
            cb_name = utils.get_callback_name(cb)
            cb_metrics = self._INITIAL_METRICS.copy()
            watcher = Watcher(cb_metrics)
            self._callables.append((cb, cb_name, args, kwargs))
            self._watchers.append((cb_metrics, watcher))
            if cb._periodic_run_immediately:
                self._immediates.append(cb_index)
            else:
                next_run = self._initial_schedule_strategy(cb, now)
                self._schedule.push(next_run, cb_index)
            self._waiter.notify_all()
            return watcher
Exemple #3
0
def _on_failure_log(log, cb, kind, spacing, exc_info, traceback=None):
    cb_name = utils.get_callback_name(cb)
    if all(exc_info) or not traceback:
        log.error("Failed to call %s '%s' (it runs every %0.2f"
                  " seconds)", kind, cb_name, spacing, exc_info=exc_info)
    else:
        log.error("Failed to call %s '%s' (it runs every %0.2f"
                  " seconds):\n%s", kind, cb_name, spacing, traceback)
Exemple #4
0
def _on_failure_log(log, cb, kind, spacing, exc_info, traceback=None):
    cb_name = utils.get_callback_name(cb)
    if all(exc_info) or not traceback:
        log.error("Failed to call %s '%s' (it runs every %0.2f"
                  " seconds)",
                  kind,
                  cb_name,
                  spacing,
                  exc_info=exc_info)
    else:
        log.error(
            "Failed to call %s '%s' (it runs every %0.2f"
            " seconds):\n%s", kind, cb_name, spacing, traceback)
Exemple #5
0
    def __init__(self,
                 callables,
                 log=None,
                 executor_factory=None,
                 cond_cls=threading.Condition,
                 event_cls=threading.Event,
                 schedule_strategy='last_started',
                 now_func=utils.now,
                 on_failure=None):
        """Creates a new worker using the given periodic callables.

        :param callables: a iterable of tuple objects previously decorated
                          with the :py:func:`.periodic` decorator, each item
                          in the iterable is expected to be in the format
                          of ``(cb, args, kwargs)`` where ``cb`` is the
                          decorated function and ``args`` and ``kwargs`` are
                          any positional and keyword arguments to send into
                          the callback when it is activated (both ``args``
                          and ``kwargs`` may be provided as none to avoid
                          using them)
        :type callables: iterable
        :param log: logger to use when creating a new worker (defaults
                    to the module logger if none provided), it is currently
                    only used to report callback failures (if they occur)
        :type log: logger
        :param executor_factory: factory callable that can be used to generate
                                 executor objects that will be used to
                                 run the periodic callables (if none is
                                 provided one will be created that uses
                                 the :py:class:`~futurist.SynchronousExecutor`
                                 class)
        :type executor_factory: ExecutorFactory or any callable
        :param cond_cls: callable object that can
                          produce ``threading.Condition``
                          (or compatible/equivalent) objects
        :type cond_cls: callable
        :param event_cls: callable object that can produce ``threading.Event``
                          (or compatible/equivalent) objects
        :type event_cls: callable
        :param schedule_strategy: string to select one of the built-in
                                  strategies that can return the
                                  next time a callable should run
        :type schedule_strategy: string
        :param now_func: callable that can return the current time offset
                         from some point (used in calculating elapsed times
                         and next times to run); preferably this is
                         monotonically increasing
        :type now_func: callable
        :param on_failure: callable that will be called whenever a periodic
                           function fails with an error, it will be provided
                           four positional arguments and one keyword
                           argument, the first positional argument being the
                           callable that failed, the second being the type
                           of activity under which it failed (``IMMEDIATE`` or
                           ``PERIODIC``), the third being the spacing that the
                           callable runs at and the fourth ``exc_info`` tuple
                           of the failure. The keyword argument ``traceback``
                           will also be provided that may be be a string
                           that caused the failure (this is required for
                           executors which run out of process, as those can not
                           *currently* transfer stack frames across process
                           boundaries); if no callable is provided then a
                           default failure logging function will be used
                           instead (do note that
                           any user provided callable should not raise
                           exceptions on being called)
        :type on_failure: callable
        """
        if on_failure is not None and not six.callable(on_failure):
            raise ValueError("On failure callback %r must be"
                             " callable" % on_failure)
        self._tombstone = event_cls()
        self._waiter = cond_cls()
        self._dead = event_cls()
        self._active = event_cls()
        self._cond_cls = cond_cls
        self._watchers = []
        self._works = []
        for (cb, args, kwargs) in callables:
            if not six.callable(cb):
                raise ValueError("Periodic callback %r must be callable" % cb)
            missing_attrs = _check_attrs(cb)
            if missing_attrs:
                raise ValueError("Periodic callback %r missing required"
                                 " attributes %s" % (cb, missing_attrs))
            if cb._is_periodic:
                # Ensure these aren't none and if so replace them with
                # something more appropriate...
                if args is None:
                    args = self._NO_OP_ARGS
                if kwargs is None:
                    kwargs = self._NO_OP_KWARGS.copy()
                cb_metrics = self._INITIAL_METRICS.copy()
                work = Work(utils.get_callback_name(cb), cb, args, kwargs)
                watcher = Watcher(cb_metrics, work)
                self._works.append(work)
                self._watchers.append((cb_metrics, watcher))
        try:
            strategy = self.BUILT_IN_STRATEGIES[schedule_strategy]
            self._schedule_strategy = strategy[0]
            self._initial_schedule_strategy = strategy[1]
        except KeyError:
            valid_strategies = sorted(self.BUILT_IN_STRATEGIES.keys())
            raise ValueError("Scheduling strategy '%s' must be one of"
                             " %s selectable strategies" %
                             (schedule_strategy, valid_strategies))
        self._immediates, self._schedule = _build(
            now_func, self._works, self._initial_schedule_strategy)
        self._log = log or LOG
        if executor_factory is None:
            executor_factory = lambda: futurist.SynchronousExecutor()
        if on_failure is None:
            on_failure = functools.partial(_on_failure_log, self._log)
        self._on_failure = on_failure
        self._executor_factory = executor_factory
        self._now_func = now_func
Exemple #6
0
    def __init__(self, callables, log=None, executor_factory=None,
                 cond_cls=threading.Condition, event_cls=threading.Event,
                 schedule_strategy='last_started', now_func=utils.now,
                 on_failure=None):
        """Creates a new worker using the given periodic callables.

        :param callables: a iterable of tuple objects previously decorated
                          with the :py:func:`.periodic` decorator, each item
                          in the iterable is expected to be in the format
                          of ``(cb, args, kwargs)`` where ``cb`` is the
                          decorated function and ``args`` and ``kwargs`` are
                          any positional and keyword arguments to send into
                          the callback when it is activated (both ``args``
                          and ``kwargs`` may be provided as none to avoid
                          using them)
        :type callables: iterable
        :param log: logger to use when creating a new worker (defaults
                    to the module logger if none provided), it is currently
                    only used to report callback failures (if they occur)
        :type log: logger
        :param executor_factory: factory callable that can be used to generate
                                 executor objects that will be used to
                                 run the periodic callables (if none is
                                 provided one will be created that uses
                                 the :py:class:`~futurist.SynchronousExecutor`
                                 class)
        :type executor_factory: ExecutorFactory or any callable
        :param cond_cls: callable object that can
                          produce ``threading.Condition``
                          (or compatible/equivalent) objects
        :type cond_cls: callable
        :param event_cls: callable object that can produce ``threading.Event``
                          (or compatible/equivalent) objects
        :type event_cls: callable
        :param schedule_strategy: string to select one of the built-in
                                  strategies that can return the
                                  next time a callable should run
        :type schedule_strategy: string
        :param now_func: callable that can return the current time offset
                         from some point (used in calculating elapsed times
                         and next times to run); preferably this is
                         monotonically increasing
        :type now_func: callable
        :param on_failure: callable that will be called whenever a periodic
                           function fails with an error, it will be provided
                           four positional arguments and one keyword
                           argument, the first positional argument being the
                           callable that failed, the second being the type
                           of activity under which it failed (``IMMEDIATE`` or
                           ``PERIODIC``), the third being the spacing that the
                           callable runs at and the fourth ``exc_info`` tuple
                           of the failure. The keyword argument ``traceback``
                           will also be provided that may be be a string
                           that caused the failure (this is required for
                           executors which run out of process, as those can not
                           *currently* transfer stack frames across process
                           boundaries); if no callable is provided then a
                           default failure logging function will be used
                           instead (do note that
                           any user provided callable should not raise
                           exceptions on being called)
        :type on_failure: callable
        """
        if on_failure is not None and not six.callable(on_failure):
            raise ValueError("On failure callback %r must be"
                             " callable" % on_failure)
        self._tombstone = event_cls()
        self._waiter = cond_cls()
        self._dead = event_cls()
        self._active = event_cls()
        self._cond_cls = cond_cls
        self._watchers = []
        self._callables = []
        for (cb, args, kwargs) in callables:
            if not six.callable(cb):
                raise ValueError("Periodic callback %r must be callable" % cb)
            missing_attrs = _check_attrs(cb)
            if missing_attrs:
                raise ValueError("Periodic callback %r missing required"
                                 " attributes %s" % (cb, missing_attrs))
            if cb._is_periodic:
                # Ensure these aren't none and if so replace them with
                # something more appropriate...
                if args is None:
                    args = self._NO_OP_ARGS
                if kwargs is None:
                    kwargs = self._NO_OP_KWARGS
                cb_name = utils.get_callback_name(cb)
                cb_metrics = self._INITIAL_METRICS.copy()
                watcher = Watcher(cb_metrics)
                self._callables.append((cb, cb_name, args, kwargs))
                self._watchers.append((cb_metrics, watcher))
        try:
            strategy = self.BUILT_IN_STRATEGIES[schedule_strategy]
            self._schedule_strategy = strategy[0]
            self._initial_schedule_strategy = strategy[1]
        except KeyError:
            valid_strategies = sorted(self.BUILT_IN_STRATEGIES.keys())
            raise ValueError("Scheduling strategy '%s' must be one of"
                             " %s selectable strategies"
                             % (schedule_strategy, valid_strategies))
        self._immediates, self._schedule = _build(
            now_func, self._callables, self._initial_schedule_strategy)
        self._log = log or LOG
        if executor_factory is None:
            executor_factory = lambda: futurist.SynchronousExecutor()
        if on_failure is None:
            on_failure = functools.partial(_on_failure_log, self._log)
        self._on_failure = on_failure
        self._executor_factory = executor_factory
        self._now_func = now_func