@@ -541,10 +541,8 @@ def _modify_function(func):
541541 num_params = len (inspect .signature (func ).parameters )
542542 empty_args = ([inspect .Parameter .empty ()] * num_params )
543543 workflow_level_promise = scheduled_workflow (* empty_args )
544+ _validate_decorated_function (func , empty_args )
544545 step_level_promise = func (* empty_args )
545- if not isinstance (step_level_promise , PromisedObject ):
546- raise TypeError ("The workflow does not return a 'promise'. Did you use the "
547- "is_fairstep decorator on all the steps?" )
548546
549547 # Description of workflow is the raw function code
550548 description = inspect .getsource (func )
@@ -553,3 +551,22 @@ def _modify_function(func):
553551 is_pplan_plan = is_pplan_plan , derived_from = None )
554552 return workflow_level_promise
555553 return _modify_function
554+
555+
556+ def _validate_decorated_function (func , empty_args ):
557+ """
558+ Validate that a function decorated with is_fairworkflow actually consists of steps that are
559+ decorated with is_fairstep. Call the function using empty arguments to test. NB: This won't
560+ catch all edgecases of users misusing the is_fairworkflow decorator, but at least will
561+ provide more useful error messages in frequently occurring cases.
562+ """
563+ try :
564+ result = func (* empty_args )
565+ except TypeError as e :
566+ raise TypeError ("Marking the function as workflow with `is_fairworkflow` decorator "
567+ "failed. "
568+ "Did you use the is_fairstep decorator on all the steps? "
569+ f"Detailed error message: { e } " )
570+ if not isinstance (result , PromisedObject ):
571+ raise TypeError ("The workflow does not return a 'promise'. Did you use the "
572+ "is_fairstep decorator on all the steps?" )
0 commit comments