21
21
from _pytask .console import create_summary_panel
22
22
from _pytask .console import get_file
23
23
from _pytask .exceptions import CollectionError
24
+ from _pytask .exceptions import NodeNotCollectedError
24
25
from _pytask .mark_utils import get_all_marks
25
26
from _pytask .mark_utils import has_mark
26
27
from _pytask .node_protocols import PNode
37
38
from _pytask .path import shorten_path
38
39
from _pytask .reports import CollectionReport
39
40
from _pytask .shared import find_duplicates
41
+ from _pytask .task_utils import COLLECTED_TASKS
40
42
from _pytask .task_utils import task as task_decorator
41
43
from _pytask .typing import is_task_function
42
44
from rich .text import Text
@@ -61,6 +63,7 @@ def pytask_collect(session: Session) -> bool:
61
63
62
64
_collect_from_paths (session )
63
65
_collect_from_tasks (session )
66
+ _collect_not_collected_tasks (session )
64
67
65
68
session .tasks .extend (
66
69
i .node
@@ -108,6 +111,9 @@ def _collect_from_tasks(session: Session) -> None:
108
111
path = get_file (raw_task )
109
112
name = raw_task .pytask_meta .name
110
113
114
+ if has_mark (raw_task , "task" ):
115
+ COLLECTED_TASKS [path ].remove (raw_task )
116
+
111
117
# When a task is not a callable, it can be anything or a PTask. Set arbitrary
112
118
# values and it will pass without errors and not collected.
113
119
else :
@@ -126,6 +132,45 @@ def _collect_from_tasks(session: Session) -> None:
126
132
session .collection_reports .append (report )
127
133
128
134
135
+ _FAILED_COLLECTING_TASK = """\
136
+ Failed to collect task '{name}'{path_desc}.
137
+
138
+ This can happen when the task function is defined in another module, imported to a \
139
+ task module and wrapped with the '@task' decorator.
140
+
141
+ To collect this task correctly, wrap the imported function in a lambda expression like
142
+
143
+ task(...)(lambda **x: imported_function(**x)).
144
+ """
145
+
146
+
147
+ def _collect_not_collected_tasks (session : Session ) -> None :
148
+ """Collect tasks that are not collected yet and create failed reports."""
149
+ for path in list (COLLECTED_TASKS ):
150
+ tasks = COLLECTED_TASKS .pop (path )
151
+ for task in tasks :
152
+ name = task .pytask_meta .name # type: ignore[attr-defined]
153
+ node : PTask
154
+ if path :
155
+ node = Task (base_name = name , path = path , function = task )
156
+ path_desc = f" in '{ path } '"
157
+ else :
158
+ node = TaskWithoutPath (name = name , function = task )
159
+ path_desc = ""
160
+ report = CollectionReport (
161
+ outcome = CollectionOutcome .FAIL ,
162
+ node = node ,
163
+ exc_info = (
164
+ NodeNotCollectedError ,
165
+ NodeNotCollectedError (
166
+ _FAILED_COLLECTING_TASK .format (name = name , path_desc = path_desc )
167
+ ),
168
+ None ,
169
+ ),
170
+ )
171
+ session .collection_reports .append (report )
172
+
173
+
129
174
@hookimpl
130
175
def pytask_ignore_collect (path : Path , config : dict [str , Any ]) -> bool :
131
176
"""Ignore a path during the collection."""
0 commit comments