acoustic-carpenter-78188
05/11/2023, 11:49 PMworkflow decorator is hinted as always returning a WorkflowBase, which is not true when _workflow_function is `None`; this leads to a spurious type error depending on how workflow is called; similar to #1631, I propose using typing.overload to differentiate the return type of workflow based on the value of _workflow_function.
Type
☑︎ Bug Fix
☐ Feature
☐ Plugin
Are all requirements met?
☑︎ Code completed
☐ Smoke tested
☐ Unit tests added
☐ Code documentation added
☐ Any pending items have an associated Issue
Complete description
Here is an example of the typing bug this fix addresses:
import flytekit
import flytekit.remote
@flytekit.workflow
def my_workflow1() -> int:
return 0
@flytekit.workflow()
def my_workflow2() -> int:
return 0
my_workflow3 = flytekit.workflow(lambda: 0)
remote = flytekit.remote.FlyteRemote(...) # type: ignore
# before
reveal_type(my_workflow1) # Type of "my_workflow1" is "WorkflowBase"
remote.register_workflow(my_workflow1) # OK
reveal_type(my_workflow2) # Type of "my_workflow2" is "Tuple[Promise] | Promise | VoidPromise | Tuple | None"
remote.register_workflow(my_workflow2) # error: Argument of type "Tuple[Promise] | Promise | VoidPromise | Tuple | None" cannot be assigned to parameter "entity" of type "WorkflowBase" in function "register_workflow"
reveal_type(my_workflow3) # Type of "my_workflow3" is "WorkflowBase"
remote.register_workflow(my_workflow3) # OK
# after
reveal_type(my_workflow1) # Type of "my_workflow1" is "PythonFunctionWorkflow"
remote.register_workflow(my_workflow1) # OK
reveal_type(my_workflow2) # Type of "my_workflow2" is "PythonFunctionWorkflow"
remote.register_workflow(my_workflow2) # OK
reveal_type(my_workflow3) # Type of "my_workflow3" is "PythonFunctionWorkflow"
remote.register_workflow(my_workflow3) # OK
I am proposing to change the return type from WorkflowBase to PythonFunctionWorkflow. This is the class being instantiated and seems to match what is done by task, which returns PythonFunctionTask instead of the base class PythonTask which is expected by FlyteRemote.register_task.
I am also proposing we use the condition if _workflow_function is not None: instead of if _workflow_function:. This is a recommendation by the Google Python Style Guide: https://google.github.io/styleguide/pyguide.html#2144-decision
Always use if foo is None: (or is not None) to check for a None value. E.g., when testing whether a variable or argument that defaults to None was set to some other value. The other value might be a value that’s false in a boolean context!Tracking Issue N/A Follow-up issue N/A flyteorg/flytekit Codecov: 30.00% of diff hit (target 71.24%) ✅ 29 other checks have passed 29/30 successful checks
acoustic-carpenter-78188
05/12/2023, 8:07 PM