combining.py 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. from apscheduler.triggers.base import BaseTrigger
  2. from apscheduler.util import obj_to_ref, ref_to_obj
  3. class BaseCombiningTrigger(BaseTrigger):
  4. __slots__ = ('triggers', 'jitter')
  5. def __init__(self, triggers, jitter=None):
  6. self.triggers = triggers
  7. self.jitter = jitter
  8. def __getstate__(self):
  9. return {
  10. 'version': 1,
  11. 'triggers': [(obj_to_ref(trigger.__class__), trigger.__getstate__())
  12. for trigger in self.triggers],
  13. 'jitter': self.jitter
  14. }
  15. def __setstate__(self, state):
  16. if state.get('version', 1) > 1:
  17. raise ValueError(
  18. 'Got serialized data for version %s of %s, but only versions up to 1 can be '
  19. 'handled' % (state['version'], self.__class__.__name__))
  20. self.jitter = state['jitter']
  21. self.triggers = []
  22. for clsref, state in state['triggers']:
  23. cls = ref_to_obj(clsref)
  24. trigger = cls.__new__(cls)
  25. trigger.__setstate__(state)
  26. self.triggers.append(trigger)
  27. def __repr__(self):
  28. return '<{}({}{})>'.format(self.__class__.__name__, self.triggers,
  29. ', jitter={}'.format(self.jitter) if self.jitter else '')
  30. class AndTrigger(BaseCombiningTrigger):
  31. """
  32. Always returns the earliest next fire time that all the given triggers can agree on.
  33. The trigger is considered to be finished when any of the given triggers has finished its
  34. schedule.
  35. Trigger alias: ``and``
  36. :param list triggers: triggers to combine
  37. :param int|None jitter: delay the job execution by ``jitter`` seconds at most
  38. """
  39. __slots__ = ()
  40. def get_next_fire_time(self, previous_fire_time, now):
  41. while True:
  42. fire_times = [trigger.get_next_fire_time(previous_fire_time, now)
  43. for trigger in self.triggers]
  44. if None in fire_times:
  45. return None
  46. elif min(fire_times) == max(fire_times):
  47. return self._apply_jitter(fire_times[0], self.jitter, now)
  48. else:
  49. now = max(fire_times)
  50. def __str__(self):
  51. return 'and[{}]'.format(', '.join(str(trigger) for trigger in self.triggers))
  52. class OrTrigger(BaseCombiningTrigger):
  53. """
  54. Always returns the earliest next fire time produced by any of the given triggers.
  55. The trigger is considered finished when all the given triggers have finished their schedules.
  56. Trigger alias: ``or``
  57. :param list triggers: triggers to combine
  58. :param int|None jitter: delay the job execution by ``jitter`` seconds at most
  59. .. note:: Triggers that depends on the previous fire time, such as the interval trigger, may
  60. seem to behave strangely since they are always passed the previous fire time produced by
  61. any of the given triggers.
  62. """
  63. __slots__ = ()
  64. def get_next_fire_time(self, previous_fire_time, now):
  65. fire_times = [trigger.get_next_fire_time(previous_fire_time, now)
  66. for trigger in self.triggers]
  67. fire_times = [fire_time for fire_time in fire_times if fire_time is not None]
  68. if fire_times:
  69. return self._apply_jitter(min(fire_times), self.jitter, now)
  70. else:
  71. return None
  72. def __str__(self):
  73. return 'or[{}]'.format(', '.join(str(trigger) for trigger in self.triggers))