@@ -264,7 +264,7 @@ def parse_dependencies_from_task_function(
264
264
kwargs = {** signature_defaults , ** task_kwargs }
265
265
kwargs .pop ("produces" , None )
266
266
267
- # Parse products from task decorated with @task and that uses produces .
267
+ # Parse dependencies from task when @task is used .
268
268
if "depends_on" in kwargs :
269
269
has_depends_on_argument = True
270
270
dependencies ["depends_on" ] = tree_map (
@@ -375,7 +375,7 @@ def _find_args_with_node_annotation(func: Callable[..., Any]) -> dict[str, PNode
375
375
_ERROR_MULTIPLE_PRODUCT_DEFINITIONS = """The task uses multiple ways to define \
376
376
products. Products should be defined with either
377
377
378
- - 'typing.Annotated[Path(...), Product] ' (recommended)
378
+ - 'typing.Annotated[Path, Product] = Path (...)' (recommended)
379
379
- '@pytask.mark.task(kwargs={'produces': Path(...)})'
380
380
- as a default argument for 'produces': 'produces = Path(...)'
381
381
- '@pytask.mark.produces(Path(...))' (deprecated)
@@ -384,7 +384,7 @@ def _find_args_with_node_annotation(func: Callable[..., Any]) -> dict[str, PNode
384
384
"""
385
385
386
386
387
- def parse_products_from_task_function (
387
+ def parse_products_from_task_function ( # noqa: C901
388
388
session : Session , task_path : Path | None , task_name : str , node_path : Path , obj : Any
389
389
) -> dict [str , Any ]:
390
390
"""Parse products from task function.
@@ -415,26 +415,14 @@ def parse_products_from_task_function(
415
415
parameters_with_product_annot = _find_args_with_product_annotation (obj )
416
416
parameters_with_node_annot = _find_args_with_node_annotation (obj )
417
417
418
- # Parse products from task decorated with @task and that uses produces.
418
+ # Allow to collect products from ' produces' .
419
419
if "produces" in kwargs :
420
- has_produces_argument = True
421
- collected_products = tree_map_with_path (
422
- lambda p , x : _collect_product (
423
- session ,
424
- node_path ,
425
- task_name ,
426
- NodeInfo (
427
- arg_name = "produces" ,
428
- path = p ,
429
- value = x ,
430
- task_path = task_path ,
431
- task_name = task_name ,
432
- ),
433
- is_string_allowed = True ,
434
- ),
435
- kwargs ["produces" ],
436
- )
437
- out = {"produces" : collected_products }
420
+ if "produces" not in parameters_with_product_annot :
421
+ parameters_with_product_annot .append ("produces" )
422
+ # If there are more parameters with a product annotation, we want to raise an
423
+ # error later to warn about mixing different interfaces.
424
+ if set (parameters_with_product_annot ) - {"produces" }:
425
+ has_produces_argument = True
438
426
439
427
if parameters_with_product_annot :
440
428
out = {}
@@ -473,7 +461,7 @@ def parse_products_from_task_function(
473
461
task_path = task_path ,
474
462
task_name = task_name ,
475
463
),
476
- is_string_allowed = False ,
464
+ convert_string_to_path = parameter_name == "produces" , # noqa: B023
477
465
),
478
466
value ,
479
467
)
@@ -493,7 +481,7 @@ def parse_products_from_task_function(
493
481
task_path = task_path ,
494
482
task_name = task_name ,
495
483
),
496
- is_string_allowed = False ,
484
+ convert_string_to_path = False ,
497
485
),
498
486
parameters_with_node_annot ["return" ],
499
487
)
@@ -514,7 +502,7 @@ def parse_products_from_task_function(
514
502
task_path = task_path ,
515
503
task_name = task_name ,
516
504
),
517
- is_string_allowed = False ,
505
+ convert_string_to_path = False ,
518
506
),
519
507
task_produces ,
520
508
)
@@ -641,7 +629,7 @@ def _collect_product(
641
629
path : Path ,
642
630
task_name : str ,
643
631
node_info : NodeInfo ,
644
- is_string_allowed : bool = False ,
632
+ convert_string_to_path : bool = False ,
645
633
) -> PNode :
646
634
"""Collect products for a task.
647
635
@@ -655,17 +643,9 @@ def _collect_product(
655
643
656
644
"""
657
645
node = node_info .value
658
- # For historical reasons, task.kwargs is like the deco and supports str and Path.
659
- if not isinstance (node , (str , Path )) and is_string_allowed :
660
- msg = (
661
- f"`@pytask.mark.task(kwargs={{'produces': ...}}` can only accept values of "
662
- "type 'str' and 'pathlib.Path' or the same values nested in tuples, lists, "
663
- f"and dictionaries. Here, { node } has type { type (node )} ."
664
- )
665
- raise ValueError (msg )
666
646
667
647
# If we encounter a string and it is allowed, convert it to a path.
668
- if isinstance (node , str ) and is_string_allowed :
648
+ if isinstance (node , str ) and convert_string_to_path :
669
649
node = Path (node )
670
650
node_info = node_info ._replace (value = node )
671
651
0 commit comments