Example #1
0
def _print_errored_task_report(session: Session,
                               report: ExecutionReport) -> None:
    """Print the traceback and the exception of an errored report."""
    task_name = format_task_id(
        task=report.task,
        editor_url_scheme=session.config["editor_url_scheme"],
        short_name=True,
    )
    text = Text.assemble("Task ", task_name, " failed", style="failed")
    console.rule(text, style=report.outcome.style)

    console.print()

    if report.exc_info and isinstance(report.exc_info[1], Exit):
        console.print(format_exception_without_traceback(report.exc_info))
    else:
        console.print(
            render_exc_info(*report.exc_info, session.config["show_locals"]))

    console.print()
    show_capture = session.config["show_capture"]
    for when, key, content in report.sections:
        if key in ("stdout", "stderr") and show_capture in (
                ShowCapture[key.upper()],
                ShowCapture.ALL,
        ):
            console.rule(f"Captured {key} during {when}", style=None)
            console.print(content)
Example #2
0
def _print_profile_table(
    profile: dict[str, dict[str, Any]], tasks: list[Task], config: dict[str, Any]
) -> None:
    """Print the profile table."""
    name_to_task = {task.name: task for task in tasks}
    info_names = _get_info_names(profile)

    console.print()
    if profile:
        table = Table("Task")
        for name in info_names:
            table.add_column(name, justify="right")

        for task_name, info in profile.items():
            task_id = format_task_id(
                task=name_to_task[task_name],
                editor_url_scheme=config["editor_url_scheme"],
                short_name=True,
            )
            infos = [str(i) for i in info.values()]
            table.add_row(task_id, *infos)

        console.print(table)
    else:
        console.print("No information is stored on the collected tasks.")
Example #3
0
def pytask_collect_log(
    session: Session, reports: list[CollectionReport], tasks: list[Task]
) -> None:
    """Log collection."""
    session.collection_end = time.time()

    console.print(f"Collected {len(tasks)} task{'' if len(tasks) == 1 else 's'}.")

    failed_reports = [r for r in reports if r.outcome == CollectionOutcome.FAIL]
    if failed_reports:
        counts = count_outcomes(reports, CollectionOutcome)

        console.print()
        console.rule(
            Text("Failures during collection", style=CollectionOutcome.FAIL.style),
            style=CollectionOutcome.FAIL.style,
        )

        for report in failed_reports:
            if report.node is None:
                header = "Error"
            else:
                if isinstance(report.node, Task):
                    short_name = format_task_id(
                        report.node, editor_url_scheme="no_link", short_name=True
                    )
                else:
                    short_name = reduce_node_name(report.node, session.config["paths"])
                header = f"Could not collect {short_name}"

            console.rule(
                Text(header, style=CollectionOutcome.FAIL.style),
                style=CollectionOutcome.FAIL.style,
            )

            console.print()

            console.print(
                render_exc_info(*report.exc_info, session.config["show_locals"])
            )

            console.print()

        panel = create_summary_panel(
            counts, CollectionOutcome, "Collected errors and tasks"
        )
        console.print(panel)

        session.hook.pytask_log_session_footer(
            session=session,
            duration=session.collection_end - session.collection_start,
            outcome=CollectionOutcome.FAIL
            if counts[CollectionOutcome.FAIL]
            else CollectionOutcome.SUCCESS,
        )

        raise CollectionError
Example #4
0
def test_format_task_id(
    base_name,
    short_name,
    editor_url_scheme,
    use_short_name,
    relative_to,
    expected,
):
    path = _THIS_FILE

    task = Task(base_name=base_name, path=path, function=task_func)
    if short_name is not None:
        task.short_name = short_name

    result = format_task_id(task, editor_url_scheme, use_short_name, relative_to)
    assert result == expected
Example #5
0
def reduce_names_of_multiple_nodes(names: list[str], dag: nx.DiGraph,
                                   paths: Sequence[str | Path]) -> list[str]:
    """Reduce the names of multiple nodes in the DAG."""
    short_names = []
    for name in names:
        node = dag.nodes[name].get("node") or dag.nodes[name].get("task")

        if isinstance(node, Task):
            short_name = format_task_id(node,
                                        editor_url_scheme="no_link",
                                        short_name=True)
        elif isinstance(node, MetaNode):
            short_name = reduce_node_name(node, paths)
        else:
            raise TypeError(
                f"Requires 'Task' or 'MetaNode' and not {type(node)!r}.")

        short_names.append(short_name)

    return short_names
Example #6
0
    def _generate_table(self, reduce_table: bool, sort_table: bool,
                        add_caption: bool) -> Table | None:
        """Generate the table.

        First, display all completed tasks and, then, all running tasks.

        The number of entries can be limited. All running tasks are always displayed and
        if more entries are requested, the list is filled up with completed tasks.

        """
        n_reports_to_display = self.n_entries_in_table - len(
            self._running_tasks)

        if self.verbose < 2:
            reports = [
                report for report in self._reports
                if report["outcome"] not in (
                    TaskOutcome.SKIP,
                    TaskOutcome.SKIP_UNCHANGED,
                    TaskOutcome.SKIP_PREVIOUS_FAILED,
                    TaskOutcome.PERSISTENCE,
                )
            ]
        else:
            reports = self._reports

        if not reduce_table:
            relevant_reports = reports
        elif n_reports_to_display >= 1:
            relevant_reports = reports[-n_reports_to_display:]
        else:
            relevant_reports = []

        if sort_table:
            relevant_reports = sorted(relevant_reports,
                                      key=lambda report: report["name"])

        if add_caption:
            caption_kwargs = {
                "caption":
                Text(
                    f"Completed: {len(self._reports)}/{self.n_tasks}",
                    style=Style(dim=True, italic=False),
                ),
                "caption_justify":
                "right",
                "caption_style":
                None,
            }
        else:
            caption_kwargs = {}

        table = Table(**caption_kwargs)
        table.add_column("Task", overflow="fold")
        table.add_column("Outcome")
        for report in relevant_reports:
            table.add_row(
                format_task_id(
                    report["task"],
                    editor_url_scheme=self.editor_url_scheme,
                    short_name=True,
                ),
                Text(report["outcome"].symbol, style=report["outcome"].style),
            )
        for task in self._running_tasks.values():
            table.add_row(
                format_task_id(task,
                               editor_url_scheme=self.editor_url_scheme,
                               short_name=True),
                "running",
            )

        # If the table is empty, do not display anything.
        if table.rows == []:
            table = None

        return table
Example #7
0
def _print_collected_tasks(
    dictionary: dict[Path, list[Task]],
    show_nodes: bool,
    editor_url_scheme: str,
    common_ancestor: Path,
) -> None:
    """Print the information on collected tasks.

    Parameters
    ----------
    dictionary : Dict[Path, List["Task"]]
        A dictionary with path on the first level, tasks on the second, dependencies and
        products on the third.
    show_nodes : bool
        Indicator for whether dependencies and products should be displayed.
    editor_url_scheme : str
        The scheme to create an url.
    common_ancestor : Path
        The path common to all tasks and nodes.

    """
    # Have a new line between the number of collected tasks and this info.
    console.print()

    tree = Tree("Collected tasks:", highlight=True)

    for module, tasks in dictionary.items():
        reduced_module = relative_to(module, common_ancestor)
        url_style = create_url_style_for_path(module, editor_url_scheme)
        module_branch = tree.add(
            Text.assemble(PYTHON_ICON, "<Module ",
                          Text(str(reduced_module), style=url_style), ">"))

        for task in tasks:
            reduced_task_name = format_task_id(
                task,
                editor_url_scheme=editor_url_scheme,
                relative_to=common_ancestor)
            task_branch = module_branch.add(
                Text.assemble(TASK_ICON, "<Function ", reduced_task_name,
                              ">"), )

            if show_nodes:
                for node in sorted(tree_just_flatten(task.depends_on),
                                   key=lambda x: x.path):
                    reduced_node_name = relative_to(node.path, common_ancestor)
                    url_style = create_url_style_for_path(
                        node.path, editor_url_scheme)
                    task_branch.add(
                        Text.assemble(
                            FILE_ICON,
                            "<Dependency ",
                            Text(str(reduced_node_name), style=url_style),
                            ">",
                        ))

                for node in sorted(tree_just_flatten(task.produces),
                                   key=lambda x: x.path):
                    reduced_node_name = relative_to(node.path, common_ancestor)
                    url_style = create_url_style_for_path(
                        node.path, editor_url_scheme)
                    task_branch.add(
                        Text.assemble(
                            FILE_ICON,
                            "<Product ",
                            Text(str(reduced_node_name), style=url_style),
                            ">",
                        ))

    console.print(tree)