diff --git a/docs/source/_static/md/markers.md b/docs/source/_static/md/markers.md index 99e8c202..478ffe10 100644 --- a/docs/source/_static/md/markers.md +++ b/docs/source/_static/md/markers.md @@ -29,12 +29,6 @@ $ pytask markers │ pytask.mark.skipif │ Skip a task and all its dependent tasks │ │ │ if a condition is met. │ │ │ │ -│ pytask.mark.task │ Mark a function as a task regardless of │ -│ │ its name. Or mark tasks which are │ -│ │ repeated in a loop. See this tutorial │ -│ │ for more information: │ -│ │ https://bit.ly/3DWrXS3. │ -│ │ │ │ pytask.mark.try_first │ Try to execute a task a early as │ │ │ possible. │ │ │ │ diff --git a/docs/source/changes.md b/docs/source/changes.md index 2e53e5ac..9c89f1f8 100644 --- a/docs/source/changes.md +++ b/docs/source/changes.md @@ -11,6 +11,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and {meth}`~pytask.TaskWithoutPath.execute`. Thanks to {user}`Ostheer`. - {pull}`551` removes the deprecated `@pytask.mark.depends_on` and `@pytask.mark.produces`. +- {pull}`552` removes the deprecated `@pytask.mark.task`. ## 0.4.5 - 2024-01-09 diff --git a/docs/source/reference_guides/api.md b/docs/source/reference_guides/api.md index e7c07527..70feb034 100644 --- a/docs/source/reference_guides/api.md +++ b/docs/source/reference_guides/api.md @@ -96,27 +96,6 @@ plugins process. The following marks are available by default. Skip a task. -.. function:: pytask.mark.task(name, *, id, kwargs) - - The task decorator allows to mark any task function regardless of its name as a task - or assigns a new task name. - - It also allows to repeat tasks in for-loops by adding a specific ``id`` or keyword - arguments via ``kwargs``. - - .. deprecated:: 0.4.0 - - Will be removed in v0.5.0. Use :func:`~pytask.task` instead. - - :type name: str | None - :param name: The name of the task. - :type id: str | None - :param id: An id for the task if it is part of a parametrization. - :type kwargs: dict[Any, Any] | None - :param kwargs: - A dictionary containing keyword arguments which are passed to the task when it - is executed. - .. function:: pytask.mark.try_first Indicate that the task should be executed as soon as possible. diff --git a/docs/source/tutorials/write_a_task.md b/docs/source/tutorials/write_a_task.md index 8165ffc2..9d949077 100644 --- a/docs/source/tutorials/write_a_task.md +++ b/docs/source/tutorials/write_a_task.md @@ -129,11 +129,6 @@ def create_random_data(): ... ``` -```{warning} -Since v0.4 users should use {func}`@task ` over -{func}`@pytask.mark.task ` which will be removed in v0.5. -``` - ## Customize task module names Use the configuration value {confval}`task_files` if you prefer a different naming diff --git a/src/_pytask/collect_utils.py b/src/_pytask/collect_utils.py index d39bc21b..32a15ecd 100644 --- a/src/_pytask/collect_utils.py +++ b/src/_pytask/collect_utils.py @@ -70,9 +70,8 @@ def parse_dependencies_from_task_function( kwargs[name] = parameters_with_node_annot.pop(name) else: msg = ( - f"The value for the parameter {name!r} is defined twice in " - "'@pytask.mark.task(kwargs=...)' and in the type annotation. Choose " - "only one option." + f"The value for the parameter {name!r} is defined twice, in " + "'@task(kwargs=...)' and in the type annotation. Choose only one way." ) raise ValueError(msg) @@ -209,9 +208,9 @@ def parse_products_from_task_function( and parameter_name in parameters_with_node_annot ): msg = ( - f"The value for the parameter {parameter_name!r} is defined twice " - "in '@pytask.mark.task(kwargs=...)' and in the type annotation. " - "Choose only one option." + f"The value for the parameter {parameter_name!r} is defined twice, " + "in '@task(kwargs=...)' and in the type annotation. Choose only " + "one way." ) raise ValueError(msg) diff --git a/src/_pytask/mark/__init__.py b/src/_pytask/mark/__init__.py index 22055cea..a8ffe2ab 100644 --- a/src/_pytask/mark/__init__.py +++ b/src/_pytask/mark/__init__.py @@ -133,9 +133,7 @@ def from_task(cls, task: PTask) -> KeywordMatcher: mapped_names = {task.name} # Add the names attached to the current function through direct assignment. - function_obj = task.function - if function_obj: - mapped_names.update(function_obj.__dict__) + mapped_names.update(task.function.__dict__) # Add the markers to the keywords as we no longer handle them correctly. mapped_names.update(mark.name for mark in task.markers) @@ -149,7 +147,7 @@ def __call__(self, subname: str) -> bool: return any(subname in name for name in names) -def select_by_keyword(session: Session, dag: nx.DiGraph) -> set[str]: +def select_by_keyword(session: Session, dag: nx.DiGraph) -> set[str] | None: """Deselect tests by keywords.""" keywordexpr = session.config["expression"] if not keywordexpr: @@ -204,7 +202,7 @@ def __call__(self, name: str) -> bool: return name in self.own_mark_names -def select_by_mark(session: Session, dag: nx.DiGraph) -> set[str]: +def select_by_mark(session: Session, dag: nx.DiGraph) -> set[str] | None: """Deselect tests by marks.""" matchexpr = session.config["marker_expression"] if not matchexpr: diff --git a/src/_pytask/mark/__init__.pyi b/src/_pytask/mark/__init__.pyi deleted file mode 100644 index 3b25e360..00000000 --- a/src/_pytask/mark/__init__.pyi +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Any -from typing_extensions import deprecated -from _pytask.mark.expression import Expression -from _pytask.mark.expression import ParseError -from _pytask.mark.structures import Mark -from _pytask.mark.structures import MarkDecorator -from _pytask.tree_util import PyTree - -from _pytask.session import Session -import networkx as nx - -def select_by_after_keyword(session: Session, after: str) -> set[str]: ... -def select_by_keyword(session: Session, dag: nx.DiGraph) -> set[str]: ... -def select_by_mark(session: Session, dag: nx.DiGraph) -> set[str]: ... - -class MarkGenerator: - @deprecated( - "'@pytask.mark.task' is deprecated starting pytask v0.4.0 and will be removed in v0.5.0. Use '@task' from 'from pytask import task' instead.", # noqa: E501 - category=FutureWarning, - stacklevel=1, - ) - @staticmethod - def task( - name: str | None = None, - *, - id: str | None = None, # noqa: A002 - kwargs: dict[Any, Any] | None = None, - produces: PyTree[Any] | None = None, - ) -> None: ... - def __getattr__(self, name: str) -> MarkDecorator | Any: ... - -MARK_GEN = MarkGenerator() - -__all__ = [ - "Expression", - "MARK_GEN", - "Mark", - "MarkDecorator", - "MarkGenerator", - "ParseError", - "select_by_keyword", - "select_by_mark", - "select_by_after_keyword", -] diff --git a/src/_pytask/mark/structures.py b/src/_pytask/mark/structures.py index 1efbc8c2..74ec21cb 100644 --- a/src/_pytask/mark/structures.py +++ b/src/_pytask/mark/structures.py @@ -196,6 +196,13 @@ def __getattr__(self, name: str) -> MarkDecorator | Any: if name in ("depends_on", "produces"): raise RuntimeError(_DEPRECATION_DECORATOR.format(name)) + if name == "task": + msg = ( + "'@pytask.mark.task' is removed. Use '@task' with 'from pytask import " + "task' instead." + ) + raise RuntimeError(msg) + # If the name is not in the set of known marks after updating, # then it really is time to issue a warning or an error. if self.config is not None and name not in self.config["markers"]: @@ -217,19 +224,6 @@ def __getattr__(self, name: str) -> MarkDecorator | Any: stacklevel=2, ) - if name == "task": - from _pytask.task_utils import task - - warnings.warn( - "'@pytask.mark.task' is deprecated starting pytask v0.4.0 and will be " - "removed in v0.5.0. Use '@task' with 'from pytask import task' " - "instead.", - category=FutureWarning, - stacklevel=1, - ) - - return task - return MarkDecorator(Mark(name, (), {})) diff --git a/src/_pytask/task.py b/src/_pytask/task.py index 895b2d2d..fc029ab8 100644 --- a/src/_pytask/task.py +++ b/src/_pytask/task.py @@ -1,4 +1,4 @@ -"""Contain hooks related to the ``@pytask.mark.task`` decorator.""" +"""Contain hooks related to the :func:`@task `.""" from __future__ import annotations from typing import Any diff --git a/src/_pytask/task_utils.py b/src/_pytask/task_utils.py index 5ef1102f..4609d3ee 100644 --- a/src/_pytask/task_utils.py +++ b/src/_pytask/task_utils.py @@ -1,4 +1,4 @@ -"""Contains utilities related to the ``@pytask.mark.task`` decorator.""" +"""Contains utilities related to the :func:`@task `.""" from __future__ import annotations import functools @@ -31,9 +31,9 @@ COLLECTED_TASKS: dict[Path | None, list[Callable[..., Any]]] = defaultdict(list) """A container for collecting tasks. -Tasks marked by the ``@pytask.mark.task`` decorator can be generated in a loop where one -iteration overwrites the previous task. To retrieve the tasks later, use this dictionary -mapping from paths of modules to a list of tasks per module. +Tasks marked by the :func:`@task ` decorator can be generated in a loop +where one iteration overwrites the previous task. To retrieve the tasks later, use this +dictionary mapping from paths of modules to a list of tasks per module. """ @@ -96,8 +96,7 @@ def wrapper(func: Callable[..., Any]) -> Callable[..., Any]: for arg, arg_name in ((name, "name"), (id, "id")): if not (isinstance(arg, str) or arg is None): msg = ( - f"Argument {arg_name!r} of @pytask.mark.task must be a str, but it " - f"is {arg!r}." + f"Argument {arg_name!r} of @task must be a str, but it is {arg!r}." ) raise ValueError(msg) @@ -231,7 +230,7 @@ def _parse_task(task: Callable[..., Any]) -> tuple[str, Callable[..., Any]]: if meta.name is None and task.__name__ == "_": msg = ( - "A task function either needs 'name' passed by the ``@pytask.mark.task`` " + "A task function either needs 'name' passed by the ``@task`` " "decorator or the function name of the task function must not be '_'." ) raise ValueError(msg) @@ -255,7 +254,7 @@ def _parse_task_kwargs(kwargs: Any) -> dict[str, Any]: if attrs.has(type(kwargs)): return attrs.asdict(kwargs) msg = ( - "'@pytask.mark.task(kwargs=...) needs to be a dictionary, namedtuple or an " + "'@task(kwargs=...) needs to be a dictionary, namedtuple or an " "instance of an attrs class." ) raise ValueError(msg) diff --git a/tests/test_mark.py b/tests/test_mark.py index 74eb916c..d9c961c5 100644 --- a/tests/test_mark.py +++ b/tests/test_mark.py @@ -173,11 +173,11 @@ def task_no_2(): ) def test_keyword_option_parametrize(tmp_path, expr: str, expected_passed: str) -> None: source = """ - import pytask + from pytask import task for arg in [None, 1.3, "2-3"]: - @pytask.mark.task + @task def task_func(arg=arg): pass """ @@ -373,7 +373,7 @@ def task_write_text(): ... @pytest.mark.end_to_end() -@pytest.mark.parametrize("name", ["parametrize", "depends_on", "produces"]) +@pytest.mark.parametrize("name", ["parametrize", "depends_on", "produces", "task"]) def test_error_with_depreacated_markers(runner, tmp_path, name): source = f""" from pytask import mark diff --git a/tests/test_task.py b/tests/test_task.py index 968480e8..fdaeee5c 100644 --- a/tests/test_task.py +++ b/tests/test_task.py @@ -437,7 +437,7 @@ def task_example(): result = runner.invoke(cli, [tmp_path.as_posix()]) assert result.exit_code == ExitCode.COLLECTION_FAILED - assert "Argument 'id' of @pytask.mark.task" in result.output + assert "Argument 'id' of @task" in result.output @pytest.mark.end_to_end() diff --git a/tests/test_task_utils.py b/tests/test_task_utils.py index 666af242..1eac0218 100644 --- a/tests/test_task_utils.py +++ b/tests/test_task_utils.py @@ -50,8 +50,8 @@ class ExampleAttrs: (ExampleNT(), does_not_raise(), {"a": 1}), (ExampleNT, pytest.raises(TypeError, match=r"(_asdict\(\) missing 1)"), None), (ExampleAttrs(), does_not_raise(), {"b": "wonderful"}), - (ExampleAttrs, pytest.raises(ValueError, match="@pytask.mark.task"), None), - (1, pytest.raises(ValueError, match="@pytask.mark.task"), None), + (ExampleAttrs, pytest.raises(ValueError, match="@task"), None), + (1, pytest.raises(ValueError, match="@task"), None), ], ) def test_parse_task_kwargs(kwargs, expectation, expected): diff --git a/tests/test_warnings.py b/tests/test_warnings.py index 17288c32..90233aba 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -166,11 +166,11 @@ def warn_now(): def test_multiple_occurrences_of_warning_are_reduced(tmp_path, runner): source = """ import warnings - import pytask + from pytask import task for i in range(10): - @pytask.mark.task + @task def task_example(): warnings.warn("warning!!!") """