async def start_task(self, desc: TaskDescription, reason: str) -> Optional[RunningTask]: existing = first(lambda x: x.descriptor.id == desc.id and x.is_active, self.tasks.values()) if existing: if desc.on_surpass == TaskSurpassBehaviour.Skip: log.info( f"Task {desc.name} has been triggered. Since the last job is not finished, " f"the execution will be skipped, as defined by the task") return None elif desc.on_surpass == TaskSurpassBehaviour.Replace: log.info( f"New task {desc.name} should replace existing run: {existing.id}." ) existing.end() await self.store_running_task_state(existing) return await self.start_task_directly(desc, reason) elif desc.on_surpass == TaskSurpassBehaviour.Parallel: log.info( f"New task {desc.name} will race with existing run {existing.id}." ) return await self.start_task_directly(desc, reason) elif desc.on_surpass == TaskSurpassBehaviour.Wait: log.info( f"New task {desc.name} with reason {reason} will run when existing run {existing.id} is done." ) self.start_when_done[desc.id] = desc return None else: raise AttributeError( f"Surpass behaviour not handled: {desc.on_surpass}") else: return await self.start_task_directly(desc, reason)
async def delete_job(self, job_id: str) -> Optional[Job]: job: Optional[Job] = first(lambda j: j.id == job_id, self.jobs) if job: self.jobs.remove(job) return job else: return None
async def start_task_by_descriptor_id(self, uid: str) -> Optional[RunningTask]: td = first(lambda t: t.id == uid, self.task_descriptions) if td: return await self.start_task(td, "direct") else: raise NameError(f"No task with such id: {uid}")
def prop_name_kind( path: str ) -> Tuple[str, ResolvedProperty, Optional[str]]: # prop_name, prop, merge_name merge_name = first(lambda name: path.startswith(name + "."), merge_names) # remove merge_name and section part (if existent) from the path lookup = Section.without_section( path[len(merge_name) + 1:] if merge_name else path) # noqa: E203 resolved = model.property_by_path(lookup) def synthetic_path(synth: SyntheticProperty) -> str: before, after = path.rsplit(resolved.prop.name, 1) return f'{before}{".".join(synth.path)}{after}' def escape_part(path_part: str) -> str: return f"`{path_part}`" if path_part.lower( ) in escape_aql_parts else path_part prop_name = synthetic_path( resolved.prop.synthetic) if resolved.prop.synthetic else path # make sure the path does not contain any aql keywords prop_name = ".".join(escape_part(pn) for pn in prop_name.split(".")) return prop_name, resolved, merge_name
def find_term(self, fn: Callable[[Term], bool]) -> Optional[Term]: if fn(self): return self if isinstance(self, CombinedTerm): return self.left.find_term(fn) or self.right.find_term(fn) elif isinstance(self, NotTerm): return self.term.find_term(fn) elif isinstance(self, MergeTerm): return ( self.pre_filter.find_term(fn) or (self.post_filter.find_term(fn) if self.post_filter else None) or first( lambda mq: first(lambda p: p.term.find_term(fn), mq.query. parts), self.merge)) else: return None
def property_by_path(self, path_: str) -> ResolvedProperty: path = PropertyPath.from_path(path_) found: Optional[ResolvedProperty] = first( lambda prop: prop.path.same_as(path), self.__property_kind_by_path) # if the path is not known according to known model: it could be anything. return found if found else ResolvedProperty(path, Property.any_prop(), AnyKind())
def handle_command_results(self, results: Dict[TaskCommand, Any]) -> None: found = first( lambda r: isinstance(r, ExecuteOnCLI) and r.command == self.execute .command, results.keys()) if found: log.info( f"Result of command {self.execute.command} is {results[found]}" ) self.execution_done = True
async def find() -> AnyT: result = first( lambda m: isinstance(m, t) and m.message_type == message_type, all_events) # type: ignore if result: return result # type: ignore elif utc() > stop_at: raise TimeoutError() else: await asyncio.sleep(0.1) return await find()
def ack_for(self, message_type: str, subscriber: Subscriber) -> Optional[Message]: """ Return the ack received ack for the given message_type of the given subscriber or None. """ def relevant_ack(message: Message) -> bool: return (isinstance(message, (ActionDone, ActionError)) and message.message_type == message_type and message.subscriber_id == subscriber.id) return first(relevant_ack, self.received_messages)
async def add_job(self, job: Job) -> None: descriptions = list(self.task_descriptions) existing = first(lambda td: td.id == job.id, descriptions) if existing: if not existing.mutable: raise AttributeError(f"There is an existing job with this {job.id} which can not be deleted!") log.info(f"Job with id {job.id} already exists. Update this job.") descriptions.remove(existing) # store in database await self.job_db.update(job) descriptions.append(job) self.task_descriptions = descriptions await self.update_trigger(job)
def handle_command_results(self, results: Dict[TaskCommand, Any]) -> None: found = first( lambda r: isinstance(r, ExecuteOnCLI) and r.command == self.execute .command, results.keys()) if found: result = results[found] if isinstance(result, Exception): log.warning( f"Command {self.execute.command} failed with error: {result}" ) else: log.info( f"Result of command {self.execute.command} is {result}") self.execution_done = True
async def delete_job(self, job_id: str) -> Optional[Job]: job: Job = first(lambda td: td.id == job_id and isinstance(td, Job), self.task_descriptions) # type: ignore if job: if not job.mutable: raise AttributeError(f"Can not delete job: {job.id} - it is defined in a system file!") # delete all running tasks of this job for task in list(filter(lambda x: x.descriptor.id == job.id, self.tasks.values())): log.info(f"Job: {job_id}: delete running task: {task.id}") await self.delete_running_task(task) await self.job_db.delete(job_id) descriptions = list(self.task_descriptions) descriptions.remove(job) self.task_descriptions = descriptions await self.update_trigger(job, register=False) return job
def prop_name_kind( path: str ) -> Tuple[str, ResolvedProperty, Optional[str]]: # prop_name, prop, merge_name merge_name = first(lambda name: path.startswith(name + "."), merge_names) # remove merge_name and section part (if existent) from the path lookup = Section.without_section( path[len(merge_name) + 1:] if merge_name else path) # noqa: E203 resolved = model.property_by_path(lookup) def synthetic_path(synth: SyntheticProperty) -> str: before, after = path.rsplit(resolved.prop.name, 1) return f'{before}{".".join(synth.path)}{after}' prop_name = synthetic_path( resolved.prop.synthetic) if resolved.prop.synthetic else path return prop_name, resolved, merge_name
def step_by_name(self, name: str) -> Optional[Step]: return first(lambda x: x.name == name, self.steps)