Skip to content

Commit c6e7237

Browse files
StructuredDatasetTransformerEngine should derive default protocol from raw output prefix (#1107)
Signed-off-by: Yee Hing Tong <wild-endeavor@users.noreply.github.com>
1 parent c5a9468 commit c6e7237

8 files changed

Lines changed: 88 additions & 26 deletions

File tree

flytekit/core/data_persistence.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ def __init__(
318318
self._raw_output_prefix = raw_output_prefix
319319
self._data_config = data_config if data_config else DataConfig.auto()
320320

321+
@property
322+
def raw_output_prefix(self) -> str:
323+
return self._raw_output_prefix
324+
321325
@property
322326
def data_config(self) -> DataConfig:
323327
return self._data_config

flytekit/types/structured/basic_dfs.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,9 @@ def decode(
105105
return pq.read_table(local_dir)
106106

107107

108-
for protocol in [LOCAL, S3]:
109-
StructuredDatasetTransformerEngine.register(PandasToParquetEncodingHandler(protocol))
110-
StructuredDatasetTransformerEngine.register(ParquetToPandasDecodingHandler(protocol))
111-
StructuredDatasetTransformerEngine.register(ArrowToParquetEncodingHandler(protocol))
112-
StructuredDatasetTransformerEngine.register(ParquetToArrowDecodingHandler(protocol))
113-
114-
# Don't override the default for GCS.
115-
StructuredDatasetTransformerEngine.register(PandasToParquetEncodingHandler(GCS), default_for_type=False)
116-
StructuredDatasetTransformerEngine.register(ParquetToPandasDecodingHandler(GCS), default_for_type=False)
117-
StructuredDatasetTransformerEngine.register(ArrowToParquetEncodingHandler(GCS), default_for_type=False)
118-
StructuredDatasetTransformerEngine.register(ParquetToArrowDecodingHandler(GCS), default_for_type=False)
108+
# Don't override default protocol
109+
for protocol in [LOCAL, S3, GCS]:
110+
StructuredDatasetTransformerEngine.register(PandasToParquetEncodingHandler(protocol), default_for_type=False)
111+
StructuredDatasetTransformerEngine.register(ParquetToPandasDecodingHandler(protocol), default_for_type=False)
112+
StructuredDatasetTransformerEngine.register(ArrowToParquetEncodingHandler(protocol), default_for_type=False)
113+
StructuredDatasetTransformerEngine.register(ParquetToArrowDecodingHandler(protocol), default_for_type=False)

flytekit/types/structured/structured_dataset.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -471,10 +471,7 @@ def to_literal(
471471
# 3. This is the third and probably most common case. The python StructuredDataset object wraps a dataframe
472472
# that we will need to invoke an encoder for. Figure out which encoder to call and invoke it.
473473
df_type = type(python_val.dataframe)
474-
if python_val.uri is None:
475-
protocol = self.DEFAULT_PROTOCOLS[df_type]
476-
else:
477-
protocol = protocol_prefix(python_val.uri)
474+
protocol = self._protocol_from_type_or_prefix(ctx, df_type, python_val.uri)
478475
return self.encode(
479476
ctx,
480477
python_val,
@@ -485,13 +482,31 @@ def to_literal(
485482
)
486483

487484
# Otherwise assume it's a dataframe instance. Wrap it with some defaults
488-
fmt = self.DEFAULT_FORMATS[python_type]
489-
protocol = self.DEFAULT_PROTOCOLS[python_type]
485+
if python_type in self.DEFAULT_FORMATS:
486+
fmt = self.DEFAULT_FORMATS[python_type]
487+
else:
488+
logger.debug(f"No default format for type {python_type}, using system default.")
489+
fmt = StructuredDataset.DEFAULT_FILE_FORMAT
490+
protocol = self._protocol_from_type_or_prefix(ctx, python_type)
490491
meta = StructuredDatasetMetadata(structured_dataset_type=expected.structured_dataset_type if expected else None)
491492

492493
sd = StructuredDataset(dataframe=python_val, metadata=meta)
493494
return self.encode(ctx, sd, python_type, protocol, fmt, sdt)
494495

496+
def _protocol_from_type_or_prefix(self, ctx: FlyteContext, df_type: Type, uri: Optional[str] = None) -> str:
497+
"""
498+
Get the protocol from the default, if missing, then look it up from the uri if provided, if not then look
499+
up from the provided context's file access.
500+
"""
501+
if df_type in self.DEFAULT_PROTOCOLS:
502+
return self.DEFAULT_PROTOCOLS[df_type]
503+
else:
504+
protocol = protocol_prefix(uri or ctx.file_access.raw_output_prefix)
505+
logger.debug(
506+
f"No default protocol for type {df_type} found, using {protocol} from output prefix {ctx.file_access.raw_output_prefix}"
507+
)
508+
return protocol
509+
495510
def encode(
496511
self,
497512
ctx: FlyteContext,

plugins/flytekit-polars/flytekitplugins/polars/sd_transformers.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,10 @@ def decode(
6262
return pl.read_parquet(path)
6363

6464

65-
for protocol in [LOCAL, S3]:
65+
for protocol in [LOCAL, S3, GCS]:
6666
StructuredDatasetTransformerEngine.register(
67-
PolarsDataFrameToParquetEncodingHandler(protocol), default_for_type=True
67+
PolarsDataFrameToParquetEncodingHandler(protocol), default_for_type=False
6868
)
6969
StructuredDatasetTransformerEngine.register(
70-
ParquetToPolarsDataFrameDecodingHandler(protocol), default_for_type=True
70+
ParquetToPolarsDataFrameDecodingHandler(protocol), default_for_type=False
7171
)
72-
StructuredDatasetTransformerEngine.register(PolarsDataFrameToParquetEncodingHandler(GCS), default_for_type=False)
73-
StructuredDatasetTransformerEngine.register(ParquetToPolarsDataFrameDecodingHandler(GCS), default_for_type=False)

plugins/flytekit-spark/flytekitplugins/spark/sd_transformers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ def decode(
4949

5050

5151
for protocol in ["/", "s3"]:
52-
StructuredDatasetTransformerEngine.register(SparkToParquetEncodingHandler(protocol), default_for_type=True)
53-
StructuredDatasetTransformerEngine.register(ParquetToSparkDecodingHandler(protocol), default_for_type=True)
52+
StructuredDatasetTransformerEngine.register(SparkToParquetEncodingHandler(protocol), default_for_type=False)
53+
StructuredDatasetTransformerEngine.register(ParquetToSparkDecodingHandler(protocol), default_for_type=False)

tests/flytekit/unit/core/test_data_persistence.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ def test_get_random_remote_path():
55
fp = FileAccessProvider("/tmp", "s3://my-bucket")
66
path = fp.get_random_remote_path()
77
assert path.startswith("s3://my-bucket")
8+
assert fp.raw_output_prefix == "s3://my-bucket"
89

910

1011
def test_is_remote():
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from dataclasses import dataclass
2+
from typing import List
3+
4+
from dataclasses_json import dataclass_json
5+
6+
from flytekit.core.task import task
7+
from flytekit.core.workflow import workflow
8+
9+
10+
def test_dataclass():
11+
@dataclass_json
12+
@dataclass
13+
class AppParams(object):
14+
snapshotDate: str
15+
region: str
16+
preprocess: bool
17+
listKeys: List[str]
18+
19+
@task
20+
def t1() -> AppParams:
21+
ap = AppParams(snapshotDate="4/5/2063", region="us-west-3", preprocess=False, listKeys=["a", "b"])
22+
return ap
23+
24+
@workflow
25+
def wf() -> AppParams:
26+
return t1()
27+
28+
res = wf()
29+
assert res.region == "us-west-3"

tests/flytekit/unit/core/test_structured_dataset.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import tempfile
12
import typing
23

34
import pytest
45

56
import flytekit.configuration
67
from flytekit.configuration import Image, ImageConfig
78
from flytekit.core.context_manager import FlyteContext, FlyteContextManager
9+
from flytekit.core.data_persistence import FileAccessProvider
810
from flytekit.core.type_engine import TypeEngine
911
from flytekit.models import literals
1012
from flytekit.models.literals import StructuredDatasetMetadata
@@ -20,8 +22,8 @@
2022

2123
from flytekit import kwtypes, task
2224
from flytekit.types.structured.structured_dataset import (
25+
LOCAL,
2326
PARQUET,
24-
S3,
2527
StructuredDataset,
2628
StructuredDatasetDecoder,
2729
StructuredDatasetEncoder,
@@ -336,7 +338,7 @@ def test_to_python_value_without_incoming_columns():
336338
def test_format_correct():
337339
class TempEncoder(StructuredDatasetEncoder):
338340
def __init__(self):
339-
super().__init__(pd.DataFrame, S3, "avro")
341+
super().__init__(pd.DataFrame, LOCAL, "avro")
340342

341343
def encode(
342344
self,
@@ -375,3 +377,21 @@ def t1() -> Annotated[StructuredDataset, "avro"]:
375377
return StructuredDataset(dataframe=df)
376378

377379
assert t1().file_format == "avro"
380+
381+
382+
def test_protocol_detection():
383+
# We've don't register defaults to the transformer engine
384+
assert pd.DataFrame not in StructuredDatasetTransformerEngine.DEFAULT_PROTOCOLS
385+
e = StructuredDatasetTransformerEngine()
386+
ctx = FlyteContextManager.current_context()
387+
protocol = e._protocol_from_type_or_prefix(ctx, pd.DataFrame)
388+
assert protocol == "/"
389+
390+
with tempfile.TemporaryDirectory() as tmp_dir:
391+
fs = FileAccessProvider(local_sandbox_dir=tmp_dir, raw_output_prefix="s3://fdsa")
392+
ctx2 = ctx.with_file_access(fs).build()
393+
protocol = e._protocol_from_type_or_prefix(ctx2, pd.DataFrame)
394+
assert protocol == "s3"
395+
396+
protocol = e._protocol_from_type_or_prefix(ctx2, pd.DataFrame, "bq://foo")
397+
assert protocol == "bq"

0 commit comments

Comments
 (0)