import time import datetime from jenkinsapi.exceptions import UnknownQueueItem, TimeOut class Invocation(object): """ Represents the state and consequences of a single attempt to start a job. This class provides a context manager which is intended to watch the state of the job before and after the invoke. It will detect whether a process got queued, launched or whether nothing at all happened. An instance of this object will be returned by job.invoke() """ def __init__(self, job): self.job = job self.initial_builds = None self.queue_item = None self.build_number = None def __enter__(self): """ Start watching the job """ self.job.poll() self.initial_builds = set(self.job.get_build_dict().keys()) def __exit__(self, type, value, traceback): """ Finish watching the job - it will track which new queue items or builds have been created as a consequence of invoking the job. """ self.job.poll() newly_created_builds = set(self.job.get_build_dict().keys()) if newly_created_builds: self.build_number = newly_created_builds.pop() else: try: self.queue_item = self.job.get_queue_item() self.build_number = self.job.get_next_build_number() except UnknownQueueItem: pass def get_build_number(self): """ If this job is building or complete then provide it's build-number """ self.build_number = self.job.get_last_buildnumber() return self.build_number def get_build(self): return self.job[self.get_build_number()] def block_until_not_queued(self, timeout, delay): self.__block(lambda: self.is_queued(), False, timeout, delay) def block_until_completed(self, timeout, delay): self.__block(lambda: self.is_running(), False, timeout, delay) @staticmethod def __block(fn, expectation, timeout, delay=2): startTime = datetime.datetime.now() endTime = startTime + datetime.timedelta(seconds=timeout) while True: if fn() == expectation: break else: time.sleep(delay) if datetime.datetime.now() > endTime: raise TimeOut() def block(self, until='completed', timeout=200, delay=2): """ Block this item until a condition is met. Setting until to 'running' blocks the item until it is running (i.e. it's no longer queued) """ assert until in ['completed', 'not_queued'], 'Unknown block condition: %s' % until self.block_until_not_queued(timeout, delay) if until == 'completed': self.block_until_completed(timeout, delay) def stop(self): """ Stop this item, whether it is on the queue or blocked. """ self.get_build().stop() def is_queued(self): """ Returns True if this item is on the queue """ return self.job.is_queued() def is_running(self): """ Returns True if this item is executing now Returns False if this item has completed or has not yet executed. """ try: return self.get_build().is_running() except KeyError: # This item has not yet executed return False def is_queued_or_running(self): return self.is_queued() or self.is_running() def get_queue_item(self): """ If the item is queued it will return that QueueItem, otherwise it will raise an exception. """ return self.queue_item