def pytask_execute_task(task: Task) -> bool: """Execute task.""" kwargs = {**task.kwargs} func_arg_names = set(inspect.signature(task.function).parameters) for arg_name in ("depends_on", "produces"): if arg_name in func_arg_names: attribute = getattr(task, arg_name) kwargs[arg_name] = tree_map(lambda x: x.value, attribute) task.execute(**kwargs) return True
def task_capture(self, when: str, task: Task) -> Generator[None, None, None]: """Pipe captured stdout and stderr into report sections.""" self.resume() try: yield finally: self.suspend(in_=False) out, err = self.read() task.add_report_section(when, "stdout", out) task.add_report_section(when, "stderr", err)
def pytask_collect_task( session: Session, path: Path, name: str, obj: Any ) -> Task | None: """Collect a task which is a function. There is some discussion on how to detect functions in this `thread <https://stackoverflow.com/q/624926/7523785>`_. :class:`types.FunctionType` does not detect built-ins which is not possible anyway. """ if (name.startswith("task_") or has_mark(obj, "task")) and callable(obj): dependencies = parse_nodes(session, path, name, obj, depends_on) products = parse_nodes(session, path, name, obj, produces) markers = obj.pytask_meta.markers if hasattr(obj, "pytask_meta") else [] kwargs = obj.pytask_meta.kwargs if hasattr(obj, "pytask_meta") else {} # Get the underlying function to avoid having different states of the function, # e.g. due to pytask_meta, in different layers of the wrapping. unwrapped = inspect.unwrap(obj) return Task( base_name=name, path=path, function=unwrapped, depends_on=dependencies, produces=products, markers=markers, kwargs=kwargs, ) else: return None
def wrap_function_for_post_mortem_debugging(session: Session, task: Task) -> None: """Wrap the function for post-mortem debugging.""" task_function = task.function @functools.wraps(task_function) def wrapper(*args: Any, **kwargs: Any) -> None: capman = session.config["pm"].get_plugin("capturemanager") live_manager = session.config["pm"].get_plugin("live_manager") try: task_function(*args, **kwargs) except Exception: # Order is important! Pausing the live object before the capturemanager # would flush the table to stdout and it will be visible in the captured # output. capman.suspend(in_=True) out, err = capman.read() live_manager.pause() if out or err: console.print() if out: console.rule("Captured stdout", style=None) console.print(out) if err: console.rule("Captured stderr", style=None) console.print(err) exc_info = remove_internal_traceback_frames_from_exc_info( sys.exc_info()) console.print() console.rule("Traceback", characters=">", style=None) console.print( render_exc_info(*exc_info, session.config["show_locals"])) post_mortem(exc_info[2]) live_manager.resume() capman.resume() raise task.function = wrapper
def pytask_collect_task_protocol( session: Session, path: Path, name: str, obj: Any ) -> CollectionReport | None: """Start protocol for collecting a task.""" try: session.hook.pytask_collect_task_setup( session=session, path=path, name=name, obj=obj ) task = session.hook.pytask_collect_task( session=session, path=path, name=name, obj=obj ) if task is not None: session.hook.pytask_collect_task_teardown(session=session, task=task) return CollectionReport(outcome=CollectionOutcome.SUCCESS, node=task) except Exception: task = Task(base_name=name, path=path, function=None) return CollectionReport.from_exception( outcome=CollectionOutcome.FAIL, exc_info=sys.exc_info(), node=task ) else: return None
def wrap_function_for_tracing(session: Session, task: Task) -> None: """Wrap the task function for tracing.""" _pdb = PytaskPDB._init_pdb("runcall") task_function = task.function # We can't just return `partial(pdb.runcall, task_function)` because (on python < # 3.7.4) runcall's first param is `func`, which means we'd get an exception if one # of the kwargs to task_function was called `func`. @functools.wraps(task_function) def wrapper(*args: Any, **kwargs: Any) -> None: capman = session.config["pm"].get_plugin("capturemanager") live_manager = session.config["pm"].get_plugin("live_manager") # Order is important! Pausing the live object before the capturemanager would # flush the table to stdout and it will be visible in the captured output. capman.suspend(in_=True) out, err = capman.read() live_manager.stop() if out or err: console.print() if out: console.rule("Captured stdout", style=None) console.print(out) if err: console.rule("Captured stderr", style=None) console.print(err) _pdb.runcall(task_function, *args, **kwargs) live_manager.resume() capman.resume() task.function = wrapper
def pytask_execute_task(task: Task) -> Generator[None, None, None]: """Attach the duration of the execution to the task.""" start = time.time() yield end = time.time() task.attributes["duration"] = (start, end)