def test_priorityqueue_enqueue_dequeue(self): """Enqueue followed by dequeue should give the same result""" p = PriorityQueue() self.assertRaises(IndexError, p.dequeue) p.enqueue(42) p.enqueue(41) p.enqueue(43) assert([p.dequeue(), p.dequeue(), p.dequeue()] == [41, 42, 43])
def test_priorityqueue_empty(self): """Must be empty on construction""" p = PriorityQueue() assert len(p) == 0 assert p.items == [] # Still empty after enqueue/dequeue p.enqueue(42) p.dequeue() assert len(p) == 0
def test_priorityqueue_empty(self): """Must be empty on construction""" p = PriorityQueue() assert(len(p) == 0) assert(p.items == []) # Still empty after enqueue/dequeue p.enqueue(42) p.dequeue() assert(len(p) == 0)
def test_priorityqueue_length(self): """Test that length is n after n invocations""" p = PriorityQueue() assert len(p) == 0 for n in range(42): p.enqueue(n) assert len(p) == 42 p.dequeue() assert len(p) == 41 p.remove(10) assert len(p) == 40 for n in range(len(p)): p.dequeue() assert len(p) == 0
def test_priorityqueue_length(self): """Test that length is n after n invocations""" p = PriorityQueue() assert(len(p) == 0) for n in range(42): p.enqueue(n) assert(len(p) == 42) p.dequeue() assert(len(p) == 41) p.remove(10) assert(len(p) == 40) for n in range(len(p)): p.dequeue() assert(len(p) == 0)
class Trampoline(object): def __init__(self): self.queue = PriorityQueue(4) def enqueue(self, item): return self.queue.enqueue(item) def dispose(self): self.queue = None def run(self): #print "Trampoline:run" while self.queue.length > 0: item = self.queue.dequeue() if not item.is_cancelled(): diff = item.duetime - Scheduler.now() while diff > timedelta(0): seconds = diff.seconds + diff.microseconds / 1E6 + diff.days * 86400 log.info("Trampoline:run(), Sleeping: %s" % seconds) time.sleep(seconds) diff = item.duetime - Scheduler.now() if not item.is_cancelled(): #print("item.invoke") item.invoke()
def test_priorityqueue_sort_stability(self): """Items with same value should be returned in the order they were added""" p = PriorityQueue() p.enqueue(TestItem(43, "high")) p.enqueue(TestItem(42, "first")) p.enqueue(TestItem(42, "second")) p.enqueue(TestItem(42, "last")) p.enqueue(TestItem(41, "low")) assert len(p) == 5 assert p.dequeue() == TestItem(41, "low") assert p.dequeue() == TestItem(42, "first") assert p.dequeue() == TestItem(42, "second") assert p.dequeue() == TestItem(42, "last") assert p.dequeue() == TestItem(43, "high")
def run(cls, queue: PriorityQueue) -> None: while queue: item = queue.dequeue() if not item.is_cancelled(): diff = item.duetime - item.scheduler.now while diff > timedelta(0): seconds = diff.seconds + diff.microseconds / 1E6 + diff.days * 86400 log.warning("Do not schedule blocking work!") time.sleep(seconds) diff = item.duetime - item.scheduler.now if not item.is_cancelled(): item.invoke()
class Trampoline(object): def __init__(self, scheduler): self.queue = PriorityQueue(4) self.scheduler = scheduler def enqueue(self, item): return self.queue.enqueue(item) def dispose(self): self.queue = None def run(self): while len(self.queue): item = self.queue.dequeue() if not item.is_cancelled(): diff = item.duetime - self.scheduler.now() while diff > timedelta(0): seconds = diff.seconds + diff.microseconds / 1E6 + diff.days * 86400 log.warning("Do not schedule blocking work!") time.sleep(seconds) diff = item.duetime - self.scheduler.now() if not item.is_cancelled(): item.invoke()
def test_priorityqueue_remove(self): """Remove item from queue""" p = PriorityQueue() assert(p.remove(42) == False) p.enqueue(42) p.enqueue(41) p.enqueue(43) assert p.remove(42) == True assert [p.dequeue(), p.dequeue()] == [41, 43] p.enqueue(42) p.enqueue(41) p.enqueue(43) assert p.remove(41) == True assert [p.dequeue(), p.dequeue()] == [42, 43] p.enqueue(42) p.enqueue(41) p.enqueue(43) assert p.remove(43) == True assert [p.dequeue(), p.dequeue()] == [41, 42]
def test_priorityqueue_remove(self): """Remove item from queue""" p = PriorityQueue() assert(p.remove(42) == False) p.enqueue(42) p.enqueue(41) p.enqueue(43) assert(p.remove(42) == True) assert([p.dequeue(), p.dequeue()] == [41, 43]) p.enqueue(42) p.enqueue(41) p.enqueue(43) assert(p.remove(41) == True) assert([p.dequeue(), p.dequeue()] == [42, 43]) p.enqueue(42) p.enqueue(41) p.enqueue(43) assert(p.remove(43) == True) assert([p.dequeue(), p.dequeue()] == [41, 42])
class VirtualTimeScheduler(SchedulerBase): """Virtual Scheduler. This scheduler should work with either datetime/timespan or ticks as int/int""" def __init__(self, initial_clock=0.0) -> None: """Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer. Args: initial_clock: Initial value for the clock. comparer: Comparer to determine causality of events based on absolute time. """ self.clock = initial_clock self.is_enabled = False self.queue = PriorityQueue(1024) super().__init__() @property def now(self) -> datetime: """Gets the schedulers absolute time clock value as datetime offset.""" return self.to_datetime(self.clock) def schedule(self, action, state=None): """Schedules an action to be executed.""" return self.schedule_absolute(self.clock, action, state) def schedule_relative(self, duetime: typing.RelativeTime, action: typing.ScheduledAction, state: Any = None): """Schedules an action to be executed at duetime. Args: duetime: Relative time after which to execute the action. action: Action to be executed. state: [Optional] State passed to the action to be executed. Returns: The disposable object used to cancel the scheduled action (best effort) """ runat = self.add(self.clock, self.to_seconds(duetime)) return self.schedule_absolute(duetime=runat, action=action, state=state) def schedule_absolute(self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction, state: typing.TState = None): """Schedules an action to be executed at duetime.""" si: ScheduledItem[typing.TState] = ScheduledItem( self, state, action, duetime) self.queue.enqueue(si) return si.disposable def start(self) -> None: """Starts the virtual time scheduler.""" if self.is_enabled: return self.is_enabled = True spinning = 0 while self.is_enabled: item = self.get_next() if not item: break if item.duetime > self.clock: self.clock = item.duetime spinning = 0 if spinning > MAX_SPINNING: self.clock += 1.0 spinning = 0 item.invoke() spinning += 1 self.is_enabled = False def stop(self) -> None: """Stops the virtual time scheduler.""" self.is_enabled = False def advance_to(self, time: float) -> None: """Advances the schedulers clock to the specified time, running all work til that point. Args: time: Absolute time to advance the schedulers clock to. """ if self.clock > time: raise ArgumentOutOfRangeException() if self.clock == time: return if self.is_enabled: return self.is_enabled = True while self.is_enabled: item = self.get_next() if not item: break if item.duetime > time: self.queue.enqueue(item) break if item.duetime > self.clock: self.clock = item.duetime item.invoke() self.is_enabled = False self.clock = time def advance_by(self, time: float) -> None: """Advances the schedulers clock by the specified relative time, running all work scheduled for that timespan. Args: time: Relative time to advance the schedulers clock by. """ log.debug("VirtualTimeScheduler.advance_by(time=%s)", time) dt = self.add(self.clock, time) if self.clock > dt: raise ArgumentOutOfRangeException() self.advance_to(dt) def sleep(self, time: float) -> None: """Advances the schedulers clock by the specified relative time. Args: time: Relative time to advance the schedulers clock by. """ dt = self.add(self.clock, time) if self.clock > dt: raise ArgumentOutOfRangeException() self.clock = dt def get_next(self) -> Optional[ScheduledItem]: """Returns the next scheduled item to be executed.""" while self.queue: item = self.queue.dequeue() if not item.is_cancelled(): return item return None @staticmethod def add(absolute, relative): raise NotImplementedError
class VirtualTimeScheduler(Scheduler): """Virtual Scheduler""" def __init__(self, initial_clock=0, comparer=None): """Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer. Keyword arguments: initial_clock -- Initial value for the clock. comparer -- Comparer to determine causality of events based on absolute time. """ self.clock = initial_clock self.comparer = comparer self.is_enabled = False self.queue = PriorityQueue(1024) def now(self): #return self.clock return self.to_datetime_offset(self.clock) def schedule(self, action, state=None): return self.schedule_absolute(self.clock, action, state) def schedule_relative(self, duetime, action, state=None): """Schedules an action to be executed at duetime. Return the disposable object used to cancel the scheduled action (best effort) Keyword arguments: due_time -- Relative time after which to execute the action. action -- Action to be executed. state -- [Optional] State passed to the action to be executed. """ log.debug( "VirtualTimeScheduler.schedule_relative(duetime=%s, state=%s)" % (duetime, state)) runat = self.add(self.clock, self.to_relative(duetime)) return self.schedule_absolute(runat, action, state) def schedule_absolute(self, duetime, action, state=None): log.debug( "VirtualTimeScheduler.schedule_absolute(duetime=%s, state=%s)" % (duetime, state)) def run(scheduler, state1): #print ("VirtualTimeScheduler:schedule_absolute:run(%s)" % repr(state1)) self.queue.remove(si) #print ("running action", action.__doc__) return action(scheduler, state1) si = ScheduledItem(self, state, run, duetime, self.comparer) self.queue.enqueue(si) return si.disposable def schedule_periodic(self, period, action, state=None): scheduler = SchedulePeriodicRecursive(self, period, action, state) return scheduler.start() def start(self): """Starts the virtual time scheduler.""" next = None if not self.is_enabled: self.is_enabled = True while self.is_enabled: #print (self.clock) next = self.get_next() if next: if self.comparer(next.duetime, self.clock) > 0: self.clock = next.duetime log.info("VirtualTimeScheduler.start(), clock: %s" % self.clock) #else: # print ("skipping", next.duetime, self.clock) #print ("Invoke: ", self.clock, next.action) next.invoke() else: self.is_enabled = False def stop(self): """Stops the virtual time scheduler.""" self.is_enabled = False def advance_to(self, time): """Advances the scheduler's clock to the specified time, running all work till that point. Keyword arguments: time -- Absolute time to advance the scheduler's clock to. """ print("advance_to()") next = None if self.comparer(self.clock, time) >= 0: raise ArgumentOutOfRangeException() if not self.is_enabled: self.is_enabled = True while self.is_enabled: next = self.get_next() if next and self.comparer(next.duetime, time) <= 0: if self.comparer(next.duetime, self.clock) > 0: self.clock = next.duetime next.invoke() else: self.is_enabled = False self.clock = time def advance_by(self, time): """Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan. Keyword arguments: time -- Relative time to advance the scheduler's clock by. """ log.debug("VirtualTimeScheduler.advance_by(time=%s)" % time) dt = self.add(self.clock, time) if self.comparer(self.clock, dt) >= 0: raise ArgumentOutOfRangeException() return self.advance_to(dt) def sleep(self, time): """Advances the scheduler's clock by the specified relative time. Keyword arguments: time -- Relative time to advance the scheduler's clock by. """ dt = self.add(self.clock, time) if self.comparer(self.clock, dt) >= 0: raise ArgumentOutOfRangeException() self.clock = dt def get_next(self): """Returns the next scheduled item to be executed.""" while self.queue.length > 0: next = self.queue.peek() if next.is_cancelled(): self.queue.dequeue() else: return next return None
class PyGameScheduler(SchedulerBase): """A scheduler that schedules works for PyGame. http://www.pygame.org/docs/ref/time.html http://www.pygame.org/docs/ref/event.html""" def __init__(self, event_id=None): global pygame import pygame self.timer = None self.event_id = event_id or pygame.USEREVENT self.queue = PriorityQueue() def schedule(self, action: typing.ScheduledAction, state: Any = None) -> typing.Disposable: """Schedules an action to be executed.""" log.debug("PyGameScheduler.schedule(state=%s)", state) return self.schedule_relative(0, action, state) def run(self) -> None: while self.queue: item = self.queue.peek() diff = item.duetime - self.now if diff > timedelta(0): break item = self.queue.dequeue() if not item.is_cancelled(): item.invoke() def schedule_relative(self, duetime: typing.RelativeTime, action: typing.ScheduledAction, state: typing.TState = None) -> typing.Disposable: """Schedules an action to be executed after duetime. Args: duetime: Relative time after which to execute the action. action: Action to be executed. Returns: The disposable object used to cancel the scheduled action (best effort). """ dt = self.now + self.to_timedelta(duetime) si: ScheduledItem[typing.TState] = ScheduledItem(self, state, action, dt) self.queue.enqueue(si) return si.disposable def schedule_absolute(self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction, state: typing.TState = None) -> typing.Disposable: """Schedules an action to be executed at duetime. Args: duetime: Absolute time after which to execute the action. action: Action to be executed. Returns: The disposable object used to cancel the scheduled action (best effort).""" return self.schedule_relative(duetime - self.now, action, state) @property def now(self) -> datetime: """Represents a notion of time for this scheduler. Tasks being scheduled on a scheduler will adhere to the time denoted by this property.""" return datetime.utcnow()
class PyGameScheduler(SchedulerBase): """A scheduler that schedules works for PyGame. http://www.pygame.org/docs/ref/time.html http://www.pygame.org/docs/ref/event.html""" def __init__(self, event_id=None): global pygame import pygame self.timer = None self.event_id = event_id or pygame.USEREVENT self.queue = PriorityQueue() def schedule(self, action: typing.ScheduledAction, state: Any = None) -> typing.Disposable: """Schedules an action to be executed.""" log.debug("PyGameScheduler.schedule(state=%s)", state) return self.schedule_relative(0, action, state) def run(self) -> None: while self.queue: item = self.queue.peek() diff = item.duetime - self.now if diff > timedelta(0): break item = self.queue.dequeue() if not item.is_cancelled(): item.invoke() def schedule_relative(self, duetime: typing.RelativeTime, action: typing.ScheduledAction, state: typing.TState = None) -> typing.Disposable: """Schedules an action to be executed after duetime. Args: duetime: Relative time after which to execute the action. action: Action to be executed. Returns: The disposable object used to cancel the scheduled action (best effort). """ dt = self.now + self.to_timedelta(duetime) si: ScheduledItem[typing.TState] = ScheduledItem( self, state, action, dt) self.queue.enqueue(si) return si.disposable def schedule_absolute(self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction, state: typing.TState = None) -> typing.Disposable: """Schedules an action to be executed at duetime. Args: duetime: Absolute time after which to execute the action. action: Action to be executed. Returns: The disposable object used to cancel the scheduled action (best effort).""" return self.schedule_relative(duetime - self.now, action, state) @property def now(self) -> datetime: """Represents a notion of time for this scheduler. Tasks being scheduled on a scheduler will adhere to the time denoted by this property.""" return datetime.utcnow()
class VirtualTimeScheduler(Scheduler): """Virtual Scheduler. This scheduler should work with either datetime/timespan or ticks as int/int""" def __init__(self, initial_clock=0, comparer=None): """Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer. Keyword arguments: initial_clock -- Initial value for the clock. comparer -- Comparer to determine causality of events based on absolute time. """ self.clock = initial_clock self.comparer = comparer self.is_enabled = False self.queue = PriorityQueue(1024) super(VirtualTimeScheduler, self).__init__() def local_now(self): return self.to_datetime(self.clock) def schedule_now(self, state, action): return self.schedule_absolute_with_state(state, self.clock, action) def now(self): """Gets the scheduler's absolute time clock value as datetime offset.""" return self.to_datetime(self.clock) def schedule(self, action, state=None): return self.schedule_absolute(self.clock, action, state) def schedule_relative(self, duetime, action, state=None): """Schedules an action to be executed at duetime. Return the disposable object used to cancel the scheduled action (best effort) Keyword arguments: due_time -- Relative time after which to execute the action. action -- Action to be executed. state -- [Optional] State passed to the action to be executed. """ log.debug("VirtualTimeScheduler.schedule_relative(duetime=%s, state=%s)" % (duetime, state)) runat = self.add(self.clock, self.to_relative(duetime)) return self.schedule_absolute(duetime=runat, action=action, state=state) def schedule_absolute(self, duetime, action, state=None): """Schedules an action to be executed at duetime.""" def run(scheduler, state1): self.queue.remove(si) return action(scheduler, state1) si = ScheduledItem(self, state, run, duetime, self.comparer) self.queue.enqueue(si) return si.disposable def schedule_periodic(self, period, action, state=None): scheduler = SchedulePeriodicRecursive(self, period, action, state) return scheduler.start() def start(self): """Starts the virtual time scheduler.""" next = None if not self.is_enabled: self.is_enabled = True while self.is_enabled: next = self.get_next() if next: if self.comparer(next.duetime, self.clock) > 0: self.clock = next.duetime log.info("VirtualTimeScheduler.start(), clock: %s", self.clock) next.invoke() else: self.is_enabled = False def stop(self): """Stops the virtual time scheduler.""" self.is_enabled = False def advance_to(self, time): """Advances the scheduler's clock to the specified time, running all work til that point. Keyword arguments: time -- Absolute time to advance the scheduler's clock to. """ next = None due_to_clock = self.comparer(self.clock, time) if due_to_clock > 0: raise ArgumentOutOfRangeException() if not due_to_clock: return if not self.is_enabled: self.is_enabled = True while self.is_enabled: next = self.get_next() if next and self.comparer(next.duetime, time) <= 0: if self.comparer(next.duetime, self.clock) > 0: self.clock = next.duetime next.invoke() else: self.is_enabled = False self.clock = time def advance_by(self, time): """Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan. Keyword arguments: time -- Relative time to advance the scheduler's clock by. """ log.debug("VirtualTimeScheduler.advance_by(time=%s)", time) dt = self.add(self.clock, time) if self.comparer(self.clock, dt) > 0: raise ArgumentOutOfRangeException() return self.advance_to(dt) def sleep(self, time): """Advances the scheduler's clock by the specified relative time. Keyword arguments: time -- Relative time to advance the scheduler's clock by. """ dt = self.add(self.clock, time) if self.comparer(self.clock, dt) >= 0: raise ArgumentOutOfRangeException() self.clock = dt def get_next(self): """Returns the next scheduled item to be executed.""" while self.queue.length > 0: next = self.queue.peek() if next.is_cancelled(): self.queue.dequeue() else: return next return None def add(self, absolute, relative): raise NotImplementedError
class PyGameScheduler(Scheduler): """A scheduler that schedules works for PyGame. http://www.pygame.org/docs/ref/time.html http://www.pygame.org/docs/ref/event.html""" def __init__(self, event_id=None): global pygame import pygame self.timer = None self.event_id = event_id or pygame.USEREVENT self.queue = PriorityQueue() def schedule(self, action, state=None): """Schedules an action to be executed.""" log.debug("PyGameScheduler.schedule(state=%s)", state) return self.schedule_relative(0, action, state) def run(self): while self.queue.length: item = self.queue.peek() diff = item.duetime - self.now() if diff > timedelta(0): break item = self.queue.dequeue() if not item.is_cancelled(): item.invoke() def schedule_relative(self, duetime, action, state=None): """Schedules an action to be executed after duetime. Keyword arguments: duetime -- {timedelta} Relative time after which to execute the action. action -- {Function} Action to be executed. Returns {Disposable} The disposable object used to cancel the scheduled action (best effort).""" dt = self.now() + self.to_timedelta(duetime) si = ScheduledItem(self, state, action, dt) self.queue.enqueue(si) return si.disposable def schedule_absolute(self, duetime, action, state=None): """Schedules an action to be executed at duetime. Keyword arguments: duetime -- {datetime} Absolute time after which to execute the action. action -- {Function} Action to be executed. Returns {Disposable} The disposable object used to cancel the scheduled action (best effort).""" return self.schedule_relative(duetime - self.now(), action, state) def now(self): """Represents a notion of time for this scheduler. Tasks being scheduled on a scheduler will adhere to the time denoted by this property.""" return self.to_datetime(pygame.time.get_ticks())
class PyGameScheduler(Scheduler): """A scheduler that schedules works for PyGame. http://www.pygame.org/docs/ref/time.html http://www.pygame.org/docs/ref/event.html""" def __init__(self, event_id=None): global pygame import pygame self.timer = None self.event_id = event_id or pygame.USEREVENT self.queue = PriorityQueue() def schedule(self, action, state=None): """Schedules an action to be executed.""" log.debug("PyGameScheduler.schedule(state=%s)", state) return self.schedule_relative(0, action, state) def run(self): while len(self.queue): item = self.queue.peek() diff = item.duetime - self.now() if diff > timedelta(0): break item = self.queue.dequeue() if not item.is_cancelled(): item.invoke() def schedule_relative(self, duetime, action, state=None): """Schedules an action to be executed after duetime. Keyword arguments: duetime -- {timedelta} Relative time after which to execute the action. action -- {Function} Action to be executed. Returns {Disposable} The disposable object used to cancel the scheduled action (best effort).""" dt = self.now() + self.to_timedelta(duetime) si = ScheduledItem(self, state, action, dt) self.queue.enqueue(si) return si.disposable def schedule_absolute(self, duetime, action, state=None): """Schedules an action to be executed at duetime. Keyword arguments: duetime -- {datetime} Absolute time after which to execute the action. action -- {Function} Action to be executed. Returns {Disposable} The disposable object used to cancel the scheduled action (best effort).""" return self.schedule_relative(duetime - self.now(), action, state) def now(self): """Represents a notion of time for this scheduler. Tasks being scheduled on a scheduler will adhere to the time denoted by this property.""" return self.to_datetime(pygame.time.get_ticks())
class TrampolineScheduler(RxBPSchedulerBase, Scheduler): def __init__(self): """Gets a scheduler that schedules work as soon as possible on the current thread.""" super().__init__() self._idle = True self.queue = PriorityQueue() self.lock = threading.RLock() def sleep(self, seconds: float) -> None: time.sleep(seconds) @property def idle(self) -> bool: return self._idle @property def is_order_guaranteed(self) -> bool: return True def schedule(self, action: typing.ScheduledAction, state: Optional[typing.TState] = None) -> typing.Disposable: """Schedules an action to be executed. Args: action: Action to be executed. state: [Optional] state to be given to the action function. Returns: The disposable object used to cancel the scheduled action (best effort). """ return self.schedule_absolute(self.now, action, state=state) def schedule_relative( self, duetime: typing.RelativeTime, action: typing.ScheduledAction, state: Optional[typing.TState] = None) -> typing.Disposable: """Schedules an action to be executed after duetime. Args: duetime: Relative time after which to execute the action. action: Action to be executed. state: [Optional] state to be given to the action function. Returns: The disposable object used to cancel the scheduled action (best effort). """ # duetime = SchedulerBase.normalize(self.to_timedelta(duetime)) duetime = self.to_timedelta(duetime) return self.schedule_absolute(self.now + duetime, action, state=state) def schedule_absolute( self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction, state: Optional[typing.TState] = None) -> typing.Disposable: """Schedules an action to be executed at duetime. Args: duetime: Absolute time after which to execute the action. action: Action to be executed. state: [Optional] state to be given to the action function. """ duetime = self.to_datetime(duetime) if duetime > self.now: log.warning("Do not schedule imperative work!") si = ScheduledItem(self, state, action, duetime) with self.lock: self.queue.enqueue(si) if self._idle: self._idle = False start_trampoline = True else: start_trampoline = False if start_trampoline: while True: try: while self.queue: item: ScheduledItem = self.queue.peek() if item.is_cancelled(): with self.lock: self.queue.dequeue() else: diff = item.duetime - item.scheduler.now if diff <= datetime.timedelta(0): item.invoke() with self.lock: self.queue.dequeue() else: time.sleep(diff.total_seconds()) except: traceback.print_exc() finally: with self.lock: if not self.queue: self._idle = True # self.queue.clear() break return si.disposable
class VirtualTimeScheduler(SchedulerBase): """Virtual Scheduler. This scheduler should work with either datetime/timespan or ticks as int/int""" def __init__(self, initial_clock=0): """Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer. Keyword arguments: initial_clock -- Initial value for the clock. comparer -- Comparer to determine causality of events based on absolute time. """ self.clock = initial_clock self.is_enabled = False self.queue = PriorityQueue(1024) super(VirtualTimeScheduler, self).__init__() @property def now(self): """Gets the schedulers absolute time clock value as datetime offset.""" return self.to_datetime(self.clock) def schedule(self, action, state=None): """Schedules an action to be executed.""" return self.schedule_absolute(self.clock, action, state) def schedule_relative(self, duetime, action, state=None): """Schedules an action to be executed at duetime. Return the disposable object used to cancel the scheduled action (best effort) Keyword arguments: duetime -- Relative time after which to execute the action. action -- Action to be executed. state -- [Optional] State passed to the action to be executed.""" runat = self.add(self.clock, self.to_relative(duetime)) return self.schedule_absolute(duetime=runat, action=action, state=state) def schedule_absolute(self, duetime, action, state=None): """Schedules an action to be executed at duetime.""" si = ScheduledItem(self, state, action, duetime) self.queue.enqueue(si) return si.disposable def schedule_periodic(self, period, action, state=None): scheduler = SchedulePeriodic(self, period, action, state) return scheduler.start() def start(self): """Starts the virtual time scheduler.""" if self.is_enabled: return self.is_enabled = True while self.is_enabled: next = self.get_next() if not next: break if next.duetime > self.clock: self.clock = next.duetime next.invoke() self.is_enabled = False def stop(self): """Stops the virtual time scheduler.""" self.is_enabled = False def advance_to(self, time): """Advances the schedulers clock to the specified time, running all work til that point. Keyword arguments: time -- Absolute time to advance the schedulers clock to.""" if self.clock > time: raise ArgumentOutOfRangeException() if self.clock == time: return if self.is_enabled: return self.is_enabled = True while self.is_enabled: next = self.get_next() if not next: break if next.duetime > time: self.queue.enqueue(next) break if next.duetime > self.clock: self.clock = next.duetime next.invoke() self.is_enabled = False self.clock = time def advance_by(self, time): """Advances the schedulers clock by the specified relative time, running all work scheduled for that timespan. Keyword arguments: time -- Relative time to advance the schedulers clock by.""" log.debug("VirtualTimeScheduler.advance_by(time=%s)", time) dt = self.add(self.clock, time) if self.clock > dt: raise ArgumentOutOfRangeException() return self.advance_to(dt) def sleep(self, time): """Advances the schedulers clock by the specified relative time. Keyword arguments: time -- Relative time to advance the schedulers clock by.""" dt = self.add(self.clock, time) if self.clock > dt: raise ArgumentOutOfRangeException() self.clock = dt def get_next(self): """Returns the next scheduled item to be executed.""" while len(self.queue): next = self.queue.dequeue() if not next.is_cancelled(): return next return None @staticmethod def add(absolute, relative): raise NotImplementedError