3030from _pytask .mark_utils import has_mark
3131from _pytask .node_protocols import PNode
3232from _pytask .node_protocols import PPathNode
33+ from _pytask .node_protocols import PProvisionalNode
3334from _pytask .node_protocols import PTask
35+ from _pytask .nodes import DirectoryNode
3436from _pytask .nodes import PathNode
3537from _pytask .nodes import PythonNode
3638from _pytask .nodes import Task
@@ -299,6 +301,8 @@ def pytask_collect_task(
299301 raise ValueError (msg )
300302
301303 path_nodes = Path .cwd () if path is None else path .parent
304+
305+ # Collect dependencies and products.
302306 dependencies = parse_dependencies_from_task_function (
303307 session , path , name , path_nodes , obj
304308 )
@@ -309,6 +313,9 @@ def pytask_collect_task(
309313 markers = get_all_marks (obj )
310314 collection_id = obj .pytask_meta ._id if hasattr (obj , "pytask_meta" ) else None
311315 after = obj .pytask_meta .after if hasattr (obj , "pytask_meta" ) else []
316+ is_generator = (
317+ obj .pytask_meta .is_generator if hasattr (obj , "pytask_meta" ) else False
318+ )
312319
313320 # Get the underlying function to avoid having different states of the function,
314321 # e.g. due to pytask_meta, in different layers of the wrapping.
@@ -321,7 +328,11 @@ def pytask_collect_task(
321328 depends_on = dependencies ,
322329 produces = products ,
323330 markers = markers ,
324- attributes = {"collection_id" : collection_id , "after" : after },
331+ attributes = {
332+ "collection_id" : collection_id ,
333+ "after" : after ,
334+ "is_generator" : is_generator ,
335+ },
325336 )
326337 return Task (
327338 base_name = name ,
@@ -330,41 +341,25 @@ def pytask_collect_task(
330341 depends_on = dependencies ,
331342 produces = products ,
332343 markers = markers ,
333- attributes = {"collection_id" : collection_id , "after" : after },
344+ attributes = {
345+ "collection_id" : collection_id ,
346+ "after" : after ,
347+ "is_generator" : is_generator ,
348+ },
334349 )
335350 if isinstance (obj , PTask ):
336351 return obj
337352 return None
338353
339354
340- _TEMPLATE_ERROR : str = """\
341- The provided path of the dependency/product is
342-
343- {}
344-
345- , but the path of the file on disk is
346-
347- {}
348-
349- Case-sensitive file systems would raise an error because the upper and lower case \
350- format of the paths does not match.
351-
352- Please, align the names to ensure reproducibility on case-sensitive file systems \
353- (often Linux or macOS) or disable this error with 'check_casing_of_paths = false' in \
354- the pyproject.toml file.
355-
356- Hint: If parts of the path preceding your project directory are not properly \
357- formatted, check whether you need to call `.resolve()` on `SRC`, `BLD` or other paths \
358- created from the `__file__` attribute of a module.
359- """
360-
361-
362355_TEMPLATE_ERROR_DIRECTORY : str = """\
363356 The path '{path}' points to a directory, although only files are allowed."""
364357
365358
366359@hookimpl (trylast = True )
367- def pytask_collect_node (session : Session , path : Path , node_info : NodeInfo ) -> PNode : # noqa: C901, PLR0912
360+ def pytask_collect_node ( # noqa: C901, PLR0912
361+ session : Session , path : Path , node_info : NodeInfo
362+ ) -> PNode | PProvisionalNode :
368363 """Collect a node of a task as a :class:`pytask.PNode`.
369364
370365 Strings are assumed to be paths. This might be a strict assumption, but since this
@@ -384,6 +379,21 @@ def pytask_collect_node(session: Session, path: Path, node_info: NodeInfo) -> PN
384379 """
385380 node = node_info .value
386381
382+ if isinstance (node , DirectoryNode ):
383+ if node .root_dir is None :
384+ node .root_dir = path
385+ if (
386+ not node .name
387+ or node .name == node .root_dir .joinpath (node .pattern ).as_posix ()
388+ ):
389+ short_root_dir = shorten_path (
390+ node .root_dir , session .config ["paths" ] or (session .config ["root" ],)
391+ )
392+ node .name = Path (short_root_dir , node .pattern ).as_posix ()
393+
394+ if isinstance (node , PProvisionalNode ):
395+ return node
396+
387397 if isinstance (node , PythonNode ):
388398 node .node_info = node_info
389399 if not node .name :
@@ -418,9 +428,11 @@ def pytask_collect_node(session: Session, path: Path, node_info: NodeInfo) -> PN
418428 raise ValueError (_TEMPLATE_ERROR_DIRECTORY .format (path = node .path ))
419429
420430 if isinstance (node , PNode ):
431+ if not node .name :
432+ node .name = create_name_of_python_node (node_info )
421433 return node
422434
423- if isinstance (node , UPath ):
435+ if isinstance (node , UPath ): # pragma: no cover
424436 if not node .protocol :
425437 node = Path (node )
426438 else :
@@ -459,6 +471,28 @@ def pytask_collect_node(session: Session, path: Path, node_info: NodeInfo) -> PN
459471 return PythonNode (value = node , name = node_name , node_info = node_info )
460472
461473
474+ _TEMPLATE_ERROR : str = """\
475+ The provided path of the dependency/product is
476+
477+ {}
478+
479+ , but the path of the file on disk is
480+
481+ {}
482+
483+ Case-sensitive file systems would raise an error because the upper and lower case \
484+ format of the paths does not match.
485+
486+ Please, align the names to ensure reproducibility on case-sensitive file systems \
487+ (often Linux or macOS) or disable this error with 'check_casing_of_paths = false' in \
488+ your pytask configuration file.
489+
490+ Hint: If parts of the path preceding your project directory are not properly \
491+ formatted, check whether you need to call `.resolve()` on `SRC`, `BLD` or other paths \
492+ created from the `__file__` attribute of a module.
493+ """
494+
495+
462496def _raise_error_if_casing_of_path_is_wrong (
463497 path : Path , check_casing_of_paths : bool
464498) -> None :
0 commit comments