@@ -3851,3 +3851,145 @@ async def test_dict_transformer_annotated_type():
38513851
38523852 literal3 = await TypeEngine .async_to_literal (ctx , nested_dict , nested_dict_type , expected_type )
38533853 assert literal3 .map .literals ["outer" ].map .literals ["inner" ].scalar .primitive .integer == 42
3854+
3855+ @pytest .fixture (autouse = True )
3856+ def clear_type_engine_cache ():
3857+ """Clear TypeEngine cache before and after each test"""
3858+ TypeEngine ._LITERAL_CACHE .clear ()
3859+ yield
3860+ TypeEngine ._LITERAL_CACHE .clear ()
3861+
3862+ def test_type_engine_cache_with_list ():
3863+ ctx = FlyteContext .current_context ()
3864+ python_val = [1 , 2 , 3 , 4 , 5 ]
3865+ python_type = typing .List [int ]
3866+ expected = TypeEngine .to_literal_type (python_type )
3867+ list_transformer = TypeEngine .get_transformer (typing .List [int ])
3868+ with mock .patch .object (list_transformer , 'async_to_literal' ,
3869+ wraps = list_transformer .async_to_literal ) as mock_async_to_literal :
3870+
3871+ # First call
3872+ literal1 = TypeEngine .to_literal (ctx , python_val , python_type , expected )
3873+
3874+ key = TypeEngine ._get_literal_cache_key (python_val , python_type )
3875+ assert key is not None
3876+ assert key in TypeEngine ._LITERAL_CACHE
3877+
3878+ # Second call with same DataFrame
3879+ literal2 = TypeEngine .to_literal (ctx , python_val , python_type , expected )
3880+
3881+ # Verify async_to_literal was only called once
3882+ assert mock_async_to_literal .call_count == 1
3883+
3884+ assert literal1 is literal2
3885+
3886+ # Test with different data - should not use cache
3887+ different_val = [2 , 1 , 3 , 4 , 5 ]
3888+ literal3 = TypeEngine .to_literal (ctx , different_val , python_type , expected )
3889+ key_different = TypeEngine ._get_literal_cache_key (different_val , python_type )
3890+
3891+ assert key_different is not key
3892+ assert key_different is not None
3893+ assert key_different in TypeEngine ._LITERAL_CACHE
3894+
3895+ # Verify different literals are different objects
3896+ assert literal1 is not literal3
3897+
3898+ # Add many different values to test cache size limit
3899+ for i in range (200 ): # More than the default maxsize of 128
3900+ test_val = [i , i + 1 , i + 2 ]
3901+ test_type = typing .List [int ]
3902+ test_expected = TypeEngine .to_literal_type (test_type )
3903+ TypeEngine .to_literal (ctx , test_val , test_type , test_expected )
3904+
3905+ # Cache should not exceed maxsize
3906+ assert len (TypeEngine ._LITERAL_CACHE ) == 128
3907+
3908+ def test_type_engine_cache_with_dict ():
3909+ ctx = FlyteContext .current_context ()
3910+ python_val = {"a" : [1 , 2 , 3 ]}
3911+ python_type = typing .Dict [str , typing .List [int ]]
3912+ expected = TypeEngine .to_literal_type (python_type )
3913+ dict_transformer = TypeEngine .get_transformer (typing .Dict [str , typing .List [int ]])
3914+ with mock .patch .object (dict_transformer , 'async_to_literal' ,
3915+ wraps = dict_transformer .async_to_literal ) as mock_async_to_literal :
3916+
3917+ # First call
3918+ literal1 = TypeEngine .to_literal (ctx , python_val , python_type , expected )
3919+
3920+ key = TypeEngine ._get_literal_cache_key (python_val , python_type )
3921+ assert key is not None
3922+ assert key in TypeEngine ._LITERAL_CACHE
3923+
3924+ # Second call with same DataFrame
3925+ literal2 = TypeEngine .to_literal (ctx , python_val , python_type , expected )
3926+
3927+ # Verify async_to_literal was only called once
3928+ assert mock_async_to_literal .call_count == 1
3929+
3930+ assert literal1 is literal2
3931+
3932+ def test_make_key_with_annotated_types ():
3933+ # Test with Annotated type
3934+ annotated_val = [1 , 2 , 3 ]
3935+ annotated_type = typing .Annotated [typing .List [int ], "test_annotation" ]
3936+
3937+ key = TypeEngine ._get_literal_cache_key (annotated_val , annotated_type )
3938+ key_without_annotation = TypeEngine ._get_literal_cache_key (annotated_val , typing .List [int ])
3939+ # Should handle Annotated types correctly
3940+ assert key is not None
3941+ assert key_without_annotation is not None
3942+ assert key != key_without_annotation
3943+
3944+ def test_type_engine_cache_with_pandas ():
3945+ pd = pytest .importorskip ("pandas" )
3946+ ctx = FlyteContext .current_context ()
3947+ # Create DataFrame
3948+ df = pd .DataFrame ({"a" : [1 , 2 , 3 ], "b" : [4 , 5 , 6 ]})
3949+ df_type = pd .DataFrame
3950+ df_expected = TypeEngine .to_literal_type (df_type )
3951+
3952+ # Get the transformer for DataFrame
3953+ df_transformer = TypeEngine ._REGISTRY [pd .DataFrame ]
3954+
3955+ # Mock the async_to_literal method with wraps to track calls
3956+ with mock .patch .object (df_transformer , 'async_to_literal' ,
3957+ wraps = df_transformer .async_to_literal ) as mock_async_to_literal :
3958+
3959+ # First call
3960+ literal1 = TypeEngine .to_literal (ctx , df , df_type , df_expected )
3961+
3962+ # Second call with same DataFrame
3963+ literal2 = TypeEngine .to_literal (ctx , df , df_type , df_expected )
3964+
3965+ # Verify async_to_literal was called
3966+ assert mock_async_to_literal .call_count == 1
3967+
3968+ assert literal1 is literal2
3969+
3970+ def test_type_engine_cache_with_flytefile ():
3971+
3972+ transformer = TypeEngine .get_transformer (FlyteFile )
3973+ ctx = FlyteContext .current_context ()
3974+
3975+ temp_dir = tempfile .mkdtemp (prefix = "temp_example_" )
3976+ file_path = os .path .join (temp_dir , "file.txt" )
3977+ with open (file_path , "w" ) as file1 :
3978+ file1 .write ("hello world" )
3979+
3980+ lt = TypeEngine .to_literal_type (FlyteFile )
3981+
3982+ # Mock the file upload
3983+ with mock .patch .object (transformer , 'async_to_literal' ,
3984+ wraps = transformer .async_to_literal ) as mock_async_to_literal :
3985+
3986+ # Test 1: Upload local file to remote
3987+ lv1 = TypeEngine .to_literal (ctx , file_path , FlyteFile , lt )
3988+
3989+ # Second call with same DataFrame
3990+ lv2 = TypeEngine .to_literal (ctx , file_path , FlyteFile , lt )
3991+
3992+ # Verify async_to_literal was called
3993+ assert mock_async_to_literal .call_count == 1
3994+
3995+ assert lv1 is lv2
0 commit comments