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!!!")
"""