diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4493ffa..ff3d3c1d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -80,7 +80,8 @@ repos: rev: 1.5.0 hooks: - id: interrogate - args: [-v, --fail-under=40, src, tests] + args: [-v, --fail-under=75] + exclude: ^(tests/|docs/) - repo: https://github.com/codespell-project/codespell rev: v2.1.0 hooks: @@ -90,6 +91,8 @@ repos: rev: "0.47" hooks: - id: check-manifest + args: [--no-build-isolation] + additional_dependencies: [setuptools-scm, toml] - repo: https://github.com/guilatrova/tryceratops rev: v1.0.1 hooks: @@ -108,7 +111,6 @@ repos: types-setuptools ] pass_filenames: false - language_version: "3.9" - repo: meta hooks: - id: check-hooks-apply diff --git a/MANIFEST.in b/MANIFEST.in index 1b7a4eb9..91be5481 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,8 @@ include CITATION include LICENSE +recursive-include src py.typed + exclude .coveragerc exclude *.yaml exclude *.yml diff --git a/docs/rtd_environment.yml b/docs/rtd_environment.yml index 76e3cf83..e040a05b 100644 --- a/docs/rtd_environment.yml +++ b/docs/rtd_environment.yml @@ -27,6 +27,7 @@ dependencies: - pony >=0.7.15 - pexpect - rich + - typing-extensions - pip: - ../ diff --git a/docs/source/changes.rst b/docs/source/changes.rst index 169b26b6..5027c30c 100644 --- a/docs/source/changes.rst +++ b/docs/source/changes.rst @@ -10,6 +10,7 @@ all releases are available on `PyPI `_ and 0.1.9 - 2022-xx-xx ------------------ +- :gh:`197` publishes types, and adds classes and functions to the main namespace. - :gh:`217` enhances the tutorial on how to set up a project. - :gh:`218` removes ``depends_on`` and ``produces`` from the task function when parsed. - :gh:`219` removes some leftovers from pytest in :class:`~_pytask.mark.Mark`. diff --git a/environment.yml b/environment.yml index 57f54b7f..721e5640 100644 --- a/environment.yml +++ b/environment.yml @@ -23,6 +23,7 @@ dependencies: - pluggy - pony >=0.7.15 - rich + - typing-extensions # Misc - black diff --git a/pyproject.toml b/pyproject.toml index 1c8d5e6c..2e39790f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ [build-system] requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.0"] +build-backend = "setuptools.build_meta" [tool.setuptools_scm] diff --git a/setup.cfg b/setup.cfg index 68da70ff..a9f4ff5a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -42,6 +42,7 @@ install_requires = pluggy pony>=0.7.15 rich + typing-extensions python_requires = >=3.7 include_package_data = True package_dir = diff --git a/setup.py b/setup.py deleted file mode 100644 index c21a9ee0..00000000 --- a/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import annotations - -from setuptools import setup - - -if __name__ == "__main__": - setup() diff --git a/src/_pytask/click.py b/src/_pytask/click.py index 56828bbc..b90ee215 100644 --- a/src/_pytask/click.py +++ b/src/_pytask/click.py @@ -1,3 +1,4 @@ +"""This module contains code related to click.""" from __future__ import annotations from typing import Any @@ -18,11 +19,16 @@ class OptionHighlighter(RegexHighlighter): + """A highlighter for help texts.""" + highlights = [_SWITCH_REGEX, _OPTION_REGEX, _METAVAR_REGEX] class ColoredGroup(DefaultGroup): + """A subclass which colors groups with default commands.""" + def format_help(self: DefaultGroup, ctx: Any, formatter: Any) -> None: # noqa: U100 + """Format the help text.""" highlighter = OptionHighlighter() console.print( @@ -71,6 +77,7 @@ class ColoredCommand(click.Command): def format_help( self: click.Command, ctx: Any, formatter: Any # noqa: U100 ) -> None: + """Format the help text.""" console.print( f"[b]pytask[/b] [dim]v{version}[/]\n", justify="center", highlight=False ) @@ -91,6 +98,7 @@ def format_help( def print_options(group_or_command: click.Command | DefaultGroup, ctx: Any) -> None: + """Print options formatted with a table in a panel.""" highlighter = OptionHighlighter() options_table = Table(highlight=True, box=None, show_header=False) diff --git a/src/_pytask/compat.py b/src/_pytask/compat.py index 733f1294..bfeafa3d 100644 --- a/src/_pytask/compat.py +++ b/src/_pytask/compat.py @@ -10,6 +10,9 @@ from packaging.version import parse as parse_version +__all__ = ["check_for_optional_program", "import_optional_dependency"] + + _MINIMUM_VERSIONS: dict[str, str] = {} """Dict[str, str]: A mapping from packages to their minimum versions.""" diff --git a/src/_pytask/outcomes.py b/src/_pytask/outcomes.py index 839cea11..521e51c0 100644 --- a/src/_pytask/outcomes.py +++ b/src/_pytask/outcomes.py @@ -13,6 +13,20 @@ from _pytask.report import ExecutionReport +__all__ = [ + "count_outcomes", + "CollectionOutcome", + "Exit", + "ExitCode", + "Persisted", + "PytaskOutcome", + "Skipped", + "SkippedUnchanged", + "SkippedAncestorFailed", + "TaskOutcome", +] + + class CollectionOutcome(Enum): """Outcomes of collected files or tasks. @@ -30,6 +44,7 @@ class CollectionOutcome(Enum): @property def symbol(self) -> str: + """The symbol of an outcome.""" symbols = { CollectionOutcome.SUCCESS: ".", CollectionOutcome.FAIL: "F", @@ -39,6 +54,7 @@ def symbol(self) -> str: @property def description(self) -> str: + """A description of an outcome used in the summary panel.""" descriptions = { CollectionOutcome.SUCCESS: "Succeeded", CollectionOutcome.FAIL: "Failed", @@ -48,6 +64,7 @@ def description(self) -> str: @property def style(self) -> str: + """Return the style of an outcome.""" styles = { CollectionOutcome.SUCCESS: "success", CollectionOutcome.FAIL: "failed", @@ -57,6 +74,7 @@ def style(self) -> str: @property def style_textonly(self) -> str: + """Return the style of an outcome when only the text is colored.""" styles_textonly = { CollectionOutcome.SUCCESS: "success.textonly", CollectionOutcome.FAIL: "failed.textonly", diff --git a/src/_pytask/py.typed b/src/_pytask/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/src/_pytask/resolve_dependencies.py b/src/_pytask/resolve_dependencies.py index 33824c32..67f71777 100644 --- a/src/_pytask/resolve_dependencies.py +++ b/src/_pytask/resolve_dependencies.py @@ -1,3 +1,4 @@ +"""This module contains code related to resolving dependencies.""" from __future__ import annotations import itertools diff --git a/src/_pytask/session.py b/src/_pytask/session.py index 8264767a..1412743b 100644 --- a/src/_pytask/session.py +++ b/src/_pytask/session.py @@ -1,3 +1,4 @@ +"""This module contains code related to the session object.""" from __future__ import annotations from typing import Any @@ -13,9 +14,9 @@ # Location was moved from pluggy v0.13.1 to v1.0.0. try: - from pluggy.hooks import _HookRelay -except ImportError: from pluggy._hooks import _HookRelay +except ImportError: + from pluggy.hooks import _HookRelay if TYPE_CHECKING: diff --git a/src/_pytask/task.py b/src/_pytask/task.py index 627b9714..67dcf628 100644 --- a/src/_pytask/task.py +++ b/src/_pytask/task.py @@ -1,3 +1,4 @@ +"""This module contain hooks related to the ``@pytask.mark.task`` decorator.""" from __future__ import annotations from pathlib import Path @@ -12,6 +13,7 @@ @hookimpl def pytask_parse_config(config: dict[str, Any]) -> None: + """Parse the configuration.""" config["markers"]["task"] = "Mark a function as a task regardless of its name." diff --git a/src/_pytask/task_utils.py b/src/_pytask/task_utils.py index e9927866..81c9ec65 100644 --- a/src/_pytask/task_utils.py +++ b/src/_pytask/task_utils.py @@ -1,3 +1,4 @@ +"""This module contains utilities related to the ``@pytask.mark.task`` decorator.""" from __future__ import annotations from typing import Any @@ -8,10 +9,12 @@ def task(name: str | None = None) -> str: + """Parse inputs of the ``@pytask.mark.task`` decorator.""" return name def parse_task_marker(obj: Callable[..., Any]) -> str: + """Parse the ``@pytask.mark.task`` decorator.""" obj, task_markers = remove_markers_from_func(obj, "task") if len(task_markers) != 1: diff --git a/src/pytask/__init__.py b/src/pytask/__init__.py index d469dfa8..c71ead8a 100644 --- a/src/pytask/__init__.py +++ b/src/pytask/__init__.py @@ -4,9 +4,64 @@ from _pytask import __version__ from _pytask.build import main from _pytask.cli import cli +from _pytask.compat import check_for_optional_program +from _pytask.compat import import_optional_dependency from _pytask.config import hookimpl +from _pytask.console import console from _pytask.graph import build_dag +from _pytask.mark import Mark from _pytask.mark import MARK_GEN as mark # noqa: N811 +from _pytask.mark import MarkDecorator +from _pytask.mark import MarkGenerator +from _pytask.mark_utils import get_marks_from_obj +from _pytask.mark_utils import get_specific_markers_from_task +from _pytask.mark_utils import has_marker +from _pytask.mark_utils import remove_markers_from_func +from _pytask.nodes import FilePathNode +from _pytask.nodes import MetaNode +from _pytask.nodes import MetaTask +from _pytask.nodes import PythonFunctionTask +from _pytask.outcomes import CollectionOutcome +from _pytask.outcomes import count_outcomes +from _pytask.outcomes import Exit +from _pytask.outcomes import ExitCode +from _pytask.outcomes import Persisted +from _pytask.outcomes import Skipped +from _pytask.outcomes import SkippedAncestorFailed +from _pytask.outcomes import SkippedUnchanged +from _pytask.outcomes import TaskOutcome +from _pytask.session import Session -__all__ = ["__version__", "build_dag", "cli", "hookimpl", "main", "mark"] +__all__ = [ + "__version__", + "build_dag", + "check_for_optional_program", + "cli", + "console", + "count_outcomes", + "get_marks_from_obj", + "get_specific_markers_from_task", + "has_marker", + "hookimpl", + "import_optional_dependency", + "main", + "mark", + "remove_markers_from_func", + "CollectionOutcome", + "Exit", + "ExitCode", + "FilePathNode", + "Mark", + "MarkDecorator", + "MarkGenerator", + "MetaNode", + "MetaTask", + "Persisted", + "PythonFunctionTask", + "Session", + "Skipped", + "SkippedAncestorFailed", + "SkippedUnchanged", + "TaskOutcome", +] diff --git a/src/pytask/py.typed b/src/pytask/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_build.py b/tests/test_build.py index 0ebc058a..bb1e753a 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -3,8 +3,8 @@ import textwrap import pytest -from _pytask.outcomes import ExitCode from pytask import cli +from pytask import ExitCode @pytest.mark.end_to_end diff --git a/tests/test_capture.py b/tests/test_capture.py index 05c7f8f2..1e4faf85 100644 --- a/tests/test_capture.py +++ b/tests/test_capture.py @@ -19,8 +19,8 @@ from _pytask.capture import CaptureManager from _pytask.capture import CaptureResult from _pytask.capture import MultiCapture -from _pytask.outcomes import ExitCode from pytask import cli +from pytask import ExitCode @pytest.mark.unit diff --git a/tests/test_clean.py b/tests/test_clean.py index fecbf4eb..c94bcbe1 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -3,8 +3,8 @@ import textwrap import pytest -from _pytask.outcomes import ExitCode from pytask import cli +from pytask import ExitCode @pytest.fixture() diff --git a/tests/test_cli.py b/tests/test_cli.py index c43730b0..9fbbaaa6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3,9 +3,9 @@ import subprocess import pytest -from _pytask.outcomes import ExitCode from pytask import __version__ from pytask import cli +from pytask import ExitCode @pytest.mark.end_to_end diff --git a/tests/test_collect.py b/tests/test_collect.py index f1c724bc..8c8c749e 100644 --- a/tests/test_collect.py +++ b/tests/test_collect.py @@ -2,6 +2,7 @@ import sys import textwrap +import warnings from contextlib import ExitStack as does_not_raise # noqa: N813 from pathlib import Path @@ -10,12 +11,12 @@ from _pytask.collect import pytask_collect_node from _pytask.exceptions import NodeNotCollectedError from _pytask.nodes import create_task_name -from _pytask.nodes import PythonFunctionTask -from _pytask.outcomes import CollectionOutcome -from _pytask.outcomes import ExitCode -from _pytask.session import Session from pytask import cli +from pytask import CollectionOutcome +from pytask import ExitCode from pytask import main +from pytask import PythonFunctionTask +from pytask import Session @pytest.mark.end_to_end @@ -176,11 +177,11 @@ def test_pytask_collect_node_does_not_raise_error_if_path_is_not_normalized( if is_absolute: collected_node = tmp_path / collected_node - with pytest.warns(None) as record: + with warnings.catch_warnings() as record: result = pytask_collect_node(session, task_path, collected_node) + assert not record assert str(result.path) == str(real_node) - assert not record @pytest.mark.unit diff --git a/tests/test_collect_command.py b/tests/test_collect_command.py index 8c6b555f..bff56476 100644 --- a/tests/test_collect_command.py +++ b/tests/test_collect_command.py @@ -7,9 +7,9 @@ import pytest from _pytask.collect_command import _find_common_ancestor_of_all_nodes from _pytask.collect_command import _print_collected_tasks -from _pytask.nodes import MetaNode -from _pytask.nodes import MetaTask from pytask import cli +from pytask import MetaNode +from pytask import MetaTask @pytest.mark.end_to_end diff --git a/tests/test_compat.py b/tests/test_compat.py index f5d6fa47..b6cadd59 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -6,8 +6,8 @@ import pytest from _pytask.compat import _MINIMUM_VERSIONS -from _pytask.compat import check_for_optional_program -from _pytask.compat import import_optional_dependency +from pytask import check_for_optional_program +from pytask import import_optional_dependency @pytest.mark.unit diff --git a/tests/test_console.py b/tests/test_console.py index 96fda556..5bb6956e 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -6,16 +6,16 @@ import pytest from _pytask.console import _get_file from _pytask.console import _get_source_lines -from _pytask.console import console from _pytask.console import create_summary_panel from _pytask.console import create_url_style_for_path from _pytask.console import create_url_style_for_task from _pytask.console import format_task_id from _pytask.console import render_to_string from _pytask.nodes import create_task_name -from _pytask.nodes import PythonFunctionTask -from _pytask.outcomes import CollectionOutcome -from _pytask.outcomes import TaskOutcome +from pytask import CollectionOutcome +from pytask import console +from pytask import PythonFunctionTask +from pytask import TaskOutcome from rich.console import Console from rich.style import Style from rich.text import Span diff --git a/tests/test_dag.py b/tests/test_dag.py index e42df47c..774a6d17 100644 --- a/tests/test_dag.py +++ b/tests/test_dag.py @@ -10,8 +10,8 @@ from _pytask.dag import node_and_neighbors from _pytask.dag import task_and_descending_tasks from _pytask.dag import TopologicalSorter -from _pytask.mark import Mark -from _pytask.nodes import MetaTask +from pytask import Mark +from pytask import MetaTask @attr.s diff --git a/tests/test_database.py b/tests/test_database.py index 9a12176d..34cf9fd4 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -6,9 +6,9 @@ import pytest from _pytask.database import create_database from _pytask.database import State -from _pytask.outcomes import ExitCode from pony import orm from pytask import cli +from pytask import ExitCode @pytest.mark.end_to_end diff --git a/tests/test_debugging.py b/tests/test_debugging.py index 69ece020..e24da1d8 100644 --- a/tests/test_debugging.py +++ b/tests/test_debugging.py @@ -8,8 +8,8 @@ import pytest from _pytask.debugging import _pdbcls_callback -from _pytask.outcomes import ExitCode from pytask import cli +from pytask import ExitCode try: import pexpect diff --git a/tests/test_execute.py b/tests/test_execute.py index ed025d39..3798020a 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -6,10 +6,10 @@ import pytest from _pytask.exceptions import NodeNotFoundError -from _pytask.outcomes import ExitCode -from _pytask.outcomes import TaskOutcome from pytask import cli +from pytask import ExitCode from pytask import main +from pytask import TaskOutcome @pytest.mark.end_to_end diff --git a/tests/test_graph.py b/tests/test_graph.py index 908aec79..b610a20b 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -4,8 +4,8 @@ import textwrap import pytest -from _pytask.outcomes import ExitCode from pytask import cli +from pytask import ExitCode try: import pydot # noqa: F401 diff --git a/tests/test_live.py b/tests/test_live.py index 6679bb31..2e6ac028 100644 --- a/tests/test_live.py +++ b/tests/test_live.py @@ -7,11 +7,11 @@ from _pytask.live import _parse_n_entries_in_table from _pytask.live import LiveExecution from _pytask.live import LiveManager -from _pytask.nodes import PythonFunctionTask -from _pytask.outcomes import ExitCode -from _pytask.outcomes import TaskOutcome from _pytask.report import ExecutionReport from pytask import cli +from pytask import ExitCode +from pytask import PythonFunctionTask +from pytask import TaskOutcome @pytest.mark.unit diff --git a/tests/test_mark.py b/tests/test_mark.py index 706b6361..b02690b4 100644 --- a/tests/test_mark.py +++ b/tests/test_mark.py @@ -5,10 +5,10 @@ import pytask import pytest -from _pytask.mark import MarkGenerator -from _pytask.outcomes import ExitCode from pytask import cli +from pytask import ExitCode from pytask import main +from pytask import MarkGenerator @pytest.mark.unit diff --git a/tests/test_mark_utils.py b/tests/test_mark_utils.py index c78ef15f..ae8145ad 100644 --- a/tests/test_mark_utils.py +++ b/tests/test_mark_utils.py @@ -3,11 +3,11 @@ import attr import pytask import pytest -from _pytask.mark_utils import get_marks_from_obj -from _pytask.mark_utils import get_specific_markers_from_task -from _pytask.mark_utils import has_marker -from _pytask.mark_utils import remove_markers_from_func -from _pytask.nodes import MetaTask +from pytask import get_marks_from_obj +from pytask import get_specific_markers_from_task +from pytask import has_marker +from pytask import MetaTask +from pytask import remove_markers_from_func @attr.s diff --git a/tests/test_nodes.py b/tests/test_nodes.py index ac603697..16956e90 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -13,11 +13,11 @@ from _pytask.nodes import _extract_nodes_from_function_markers from _pytask.nodes import create_task_name from _pytask.nodes import depends_on -from _pytask.nodes import FilePathNode -from _pytask.nodes import MetaNode -from _pytask.nodes import MetaTask from _pytask.nodes import produces from _pytask.shared import reduce_node_name +from pytask import FilePathNode +from pytask import MetaNode +from pytask import MetaTask @pytest.mark.unit diff --git a/tests/test_outcomes.py b/tests/test_outcomes.py index 1fc4c82e..e756112f 100644 --- a/tests/test_outcomes.py +++ b/tests/test_outcomes.py @@ -1,11 +1,11 @@ from __future__ import annotations import pytest -from _pytask.outcomes import CollectionOutcome -from _pytask.outcomes import count_outcomes -from _pytask.outcomes import TaskOutcome from _pytask.report import CollectionReport from _pytask.report import ExecutionReport +from pytask import CollectionOutcome +from pytask import count_outcomes +from pytask import TaskOutcome @pytest.mark.unit diff --git a/tests/test_parametrize.py b/tests/test_parametrize.py index de2b4257..d212cb79 100644 --- a/tests/test_parametrize.py +++ b/tests/test_parametrize.py @@ -7,15 +7,15 @@ import _pytask.parametrize import pytask import pytest -from _pytask.mark import Mark -from _pytask.outcomes import ExitCode from _pytask.parametrize import _arg_value_to_id_component from _pytask.parametrize import _parse_arg_names from _pytask.parametrize import _parse_parametrize_markers from _pytask.parametrize import pytask_parametrize_task from _pytask.pluginmanager import get_plugin_manager from pytask import cli +from pytask import ExitCode from pytask import main +from pytask import Mark class DummySession: diff --git a/tests/test_persist.py b/tests/test_persist.py index dab7f2bd..6ee17784 100644 --- a/tests/test_persist.py +++ b/tests/test_persist.py @@ -5,12 +5,12 @@ import pytest from _pytask.database import create_database from _pytask.database import State -from _pytask.outcomes import Persisted -from _pytask.outcomes import SkippedUnchanged -from _pytask.outcomes import TaskOutcome from _pytask.persist import pytask_execute_task_process_report from pony import orm from pytask import main +from pytask import Persisted +from pytask import SkippedUnchanged +from pytask import TaskOutcome class DummyClass: diff --git a/tests/test_profile.py b/tests/test_profile.py index ff7a6efb..531d3085 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -6,10 +6,10 @@ import pytest from _pytask.cli import cli from _pytask.database import create_database -from _pytask.outcomes import ExitCode from _pytask.profile import _to_human_readable_size from _pytask.profile import Runtime from pony import orm +from pytask import ExitCode from pytask import main diff --git a/tests/test_resolve_dependencies.py b/tests/test_resolve_dependencies.py index 62c83ae9..a556dfa6 100644 --- a/tests/test_resolve_dependencies.py +++ b/tests/test_resolve_dependencies.py @@ -9,12 +9,12 @@ import pytest from _pytask.exceptions import NodeNotFoundError from _pytask.exceptions import ResolvingDependenciesError -from _pytask.nodes import FilePathNode -from _pytask.nodes import PythonFunctionTask -from _pytask.outcomes import ExitCode from _pytask.resolve_dependencies import _check_if_root_nodes_are_available from _pytask.resolve_dependencies import pytask_resolve_dependencies_create_dag from pytask import cli +from pytask import ExitCode +from pytask import FilePathNode +from pytask import PythonFunctionTask @attr.s diff --git a/tests/test_skipping.py b/tests/test_skipping.py index 202dc21e..f2509d57 100644 --- a/tests/test_skipping.py +++ b/tests/test_skipping.py @@ -4,15 +4,15 @@ from contextlib import ExitStack as does_not_raise # noqa: N813 import pytest -from _pytask.mark import Mark -from _pytask.outcomes import ExitCode -from _pytask.outcomes import Skipped -from _pytask.outcomes import SkippedAncestorFailed -from _pytask.outcomes import SkippedUnchanged -from _pytask.outcomes import TaskOutcome from _pytask.skipping import pytask_execute_task_setup from pytask import cli +from pytask import ExitCode from pytask import main +from pytask import Mark +from pytask import Skipped +from pytask import SkippedAncestorFailed +from pytask import SkippedUnchanged +from pytask import TaskOutcome class DummyClass: diff --git a/tests/test_task.py b/tests/test_task.py index 77241c2a..145ea69b 100644 --- a/tests/test_task.py +++ b/tests/test_task.py @@ -4,7 +4,7 @@ import pytest from _pytask.nodes import create_task_name -from _pytask.outcomes import ExitCode +from pytask import ExitCode from pytask import main diff --git a/tests/test_traceback.py b/tests/test_traceback.py index acf550f9..dc140f04 100644 --- a/tests/test_traceback.py +++ b/tests/test_traceback.py @@ -3,8 +3,8 @@ import textwrap import pytest -from _pytask.outcomes import ExitCode from pytask import cli +from pytask import ExitCode @pytest.mark.end_to_end