Máté Csákvári
02/08/2024, 9:21 AM@dataclass_json
@dataclass
class Fruit:
name: int # was str
@task
def print_message(message: int):
print(message)
return
@task
def dataclass_task() -> Fruit:
return Fruit(name=10)
@workflow
def dataclass_wf():
fruit_instance = dataclass_task()
print_message(message=fruit_instance.name)
Running this workflow results in:
TypeTransformerFailedError: Error encountered while executing 'dataclass_wf':
Failed to convert inputs of task 'access-test.print_message':
Error converting input 'message' at position 0:
Cannot convert literal <FlyteLiteral scalar { primitive { float_value: 10 } }> to <class 'int'>
This is happening because when a new promise is created from a dataclass instance by accessing one of its attributes, that promise has a pb struct representation with a float_value inside. That promise will be resolved in flytekit.core.promise:translate_inputs_to_literals to a float literal in flyte's type system. Later when that value is about to be converted to an integer using the TypeEngine with the registered SimpleTransformer for ints, it will fail to access the underlying data because its now stored inside lv.scalar.primitive.float_value instead of lv.scalar.primitive.integer:
TypeEngine.register(
SimpleTransformer(
"int",
int,
_type_models.LiteralType(simple=_type_models.SimpleType.INTEGER),
lambda x: Literal(scalar=Scalar(primitive=Primitive(integer=x))),
lambda x: x.scalar.primitive.integer # <- This doesn't exist. x.scalar.primite.float_value does.
)
)
If I replace that accessor for the transformer with:
def _fix_int_access(lv: Literal) -> int:
if lv.scalar.primitive.integer is None:
return int(lv.scalar.primitive.float_value)
return lv.scalar.primitive.integer
then it works fine. Also the unit test suite completes successfuly with this change.
I see that this behaviour was encountered before when dataclasses are passed inside promises and these float values are converted back to integer after the python dataclass has been reconstructed. This is unfortunately not covered by that fix.
I can also see a different fix for this right where this conversion first takes place in flytekit.core.promise:
try:
if type(v) is Promise:
v = resolve_attr_path_in_promise(v) # <-- HERE we can convert the Literal to contain an integer instead of a float_value
result[k] = TypeEngine.to_literal(ctx, v, t, var.type)
except TypeTransformerFailedError as exc:
raise TypeTransformerFailedError(f"Failed argument '{k}': {exc}") from exc
The straightforward fix here worked also, but messed up some of the unit tests which I haven't investigated further yet.
I'm not sure which one would be the better solution, but I think fixing it right where the discrepancy first manifests between the type annotations and the underlying data in the flyte's type system seems better for me.
I'm happy to create an issue and a PR but I first wanted to ask if this approach is the right way to go about this.Kevin Su
02/08/2024, 10:27 AMKevin Su
02/08/2024, 10:27 AMMáté Csákvári
02/08/2024, 11:29 AMByron Hsu
02/09/2024, 6:25 AMMáté Csákvári
02/09/2024, 7:56 AMByron Hsu
02/09/2024, 8:31 AMMáté Csákvári
02/09/2024, 8:32 AMByron Hsu
02/09/2024, 8:34 AMByron Hsu
02/09/2024, 8:35 AMMáté Csákvári
02/09/2024, 8:36 AMByron Hsu
02/09/2024, 8:54 AMMáté Csákvári
02/09/2024, 8:58 AMAustin Liu (Austinnn)
03/18/2024, 1:32 PMAustin Liu (Austinnn)
03/18/2024, 1:33 PMdata class
annotations
?Máté Csákvári
03/18/2024, 2:17 PMMáté Csákvári
03/18/2024, 2:19 PMAustin Liu (Austinnn)
03/19/2024, 9:46 AMMáté Csákvári
03/19/2024, 9:55 AMAustin Liu (Austinnn)
03/19/2024, 10:02 AMresolve_attr_path_in_promise()
at translate_inputs_to_literals()
.
Adding something like,
new_st = int(new_st) if t==int else new_st
before to_literal_type()
in resolving dataclass
remaining path.
It works in local mode.Austin Liu (Austinnn)
03/19/2024, 10:03 AMpython_val
?Máté Csákvári
03/19/2024, 10:05 AMAustin Liu (Austinnn)
03/19/2024, 10:07 AMMáté Csákvári
03/19/2024, 10:08 AMAustin Liu (Austinnn)
03/19/2024, 10:11 AM