Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pythonbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ jobs:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip==21.2.4 setuptools wheel
python -m pip install --upgrade pip setuptools wheel
pip install -r doc-requirements.txt
- name: Build the documentation
run: make -C docs html
36 changes: 5 additions & 31 deletions dataset_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
import pyspark.sql.dataframe
from pyspark.sql import SparkSession

from flytekit import FlyteContext, kwtypes, task, workflow
from flytekit.models import literals
Expand All @@ -27,8 +25,8 @@
)
from flytekit.types.structured.utils import get_filesystem

PANDAS_PATH = "/tmp/pandas"
NUMPY_PATH = "/tmp/numpy"
PANDAS_PATH = "s3://flyte-batch/my-s3-bucket/test-data/pandas"
NUMPY_PATH = "s3://flyte-batch/my-s3-bucket/test-data/numpy"
BQ_PATH = "bq://photo-313016:flyte.new_table3"

# https://github.com/flyteorg/flyte/issues/523
Expand Down Expand Up @@ -60,7 +58,7 @@ def t2(dataframe: pd.DataFrame) -> Annotated[pd.DataFrame, arrow_schema]:
@task
def t3(dataset: StructuredDataset[my_cols]) -> StructuredDataset[my_cols]:
# s3 (parquet) -> pandas -> s3 (parquet)
print("Pandas dataframe")
print("Pandas dataframe:")
print(dataset.open(pd.DataFrame).all())
# In the example, we download dataset when we open it.
# Here we won't upload anything, since we're returning just the input object.
Expand Down Expand Up @@ -148,6 +146,8 @@ def decode(

FLYTE_DATASET_TRANSFORMER.register_handler(NumpyEncodingHandlers(np.ndarray, "/", "parquet"))
FLYTE_DATASET_TRANSFORMER.register_handler(NumpyDecodingHandlers(np.ndarray, "/", "parquet"))
FLYTE_DATASET_TRANSFORMER.register_handler(NumpyEncodingHandlers(np.ndarray, "s3://", "parquet"))
FLYTE_DATASET_TRANSFORMER.register_handler(NumpyDecodingHandlers(np.ndarray, "s3://", "parquet"))


@task
Expand All @@ -163,17 +163,6 @@ def t10(dataset: StructuredDataset[my_cols]) -> np.ndarray:
return np_array


@task
def t11(dataframe: pyspark.sql.dataframe.DataFrame) -> StructuredDataset[my_cols]:
return StructuredDataset(dataframe)


@task
def t12(dataset: StructuredDataset[my_cols]) -> pyspark.sql.dataframe.DataFrame:
spark_df = dataset.open(pyspark.sql.dataframe.DataFrame).all()
return spark_df


@task
def generate_pandas() -> pd.DataFrame:
return pd.DataFrame({"Name": ["Tom", "Joseph"], "Age": [20, 22]})
Expand All @@ -189,24 +178,11 @@ def generate_arrow() -> pa.Table:
return pa.Table.from_pandas(pd.DataFrame({"Name": ["Tom", "Joseph"], "Age": [20, 22]}))


@task
def generate_spark_dataframe() -> pyspark.sql.dataframe.DataFrame:
data = [
{"Category": "A", "ID": 1, "Value": 121.44, "Truth": True},
{"Category": "B", "ID": 2, "Value": 300.01, "Truth": False},
{"Category": "C", "ID": 3, "Value": 10.99, "Truth": None},
{"Category": "E", "ID": 4, "Value": 33.87, "Truth": True},
]
spark = SparkSession.builder.getOrCreate()
return spark.createDataFrame(data)


@workflow()
def wf():
df = generate_pandas()
np_array = generate_numpy()
arrow_df = generate_arrow()
spark_df = generate_spark_dataframe()
t1(dataframe=df)
t1a(dataframe=df)
t2(dataframe=df)
Expand All @@ -220,8 +196,6 @@ def wf():
t8a(dataframe=arrow_df)
t9(dataframe=np_array)
t10(dataset=StructuredDataset(uri=NUMPY_PATH))
dataset = t11(dataframe=spark_df)
t12(dataset=dataset)


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion dev-requirements.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-c requirements.txt

git+git://github.com/flyteorg/pytest-flyte@main#egg=pytest-flyte
git+https://github.com/flyteorg/pytest-flyte@main#egg=pytest-flyte
coverage[toml]
joblib
mock
Expand Down
14 changes: 2 additions & 12 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile with python 3.9
# To update, run:
#
# make dev-requirements.txt
# pip-compile dev-requirements.in
#
-e file:.#egg=flytekit
# via
Expand Down Expand Up @@ -77,7 +77,6 @@ cryptography==36.0.1
# via
# -c requirements.txt
# paramiko
# secretstorage
dataclasses-json==0.5.6
# via
# -c requirements.txt
Expand Down Expand Up @@ -138,11 +137,6 @@ importlib-metadata==4.10.0
# keyring
iniconfig==1.1.1
# via pytest
jeepney==0.7.1
# via
# -c requirements.txt
# keyring
# secretstorage
jinja2==3.0.3
# via
# -c requirements.txt
Expand Down Expand Up @@ -262,7 +256,7 @@ pytest==6.2.5
# pytest-flyte
pytest-docker==0.10.3
# via pytest-flyte
pytest-flyte @ git+git://github.com/flyteorg/pytest-flyte@main
pytest-flyte @ git+https://github.com/flyteorg/pytest-flyte@main
# via -r dev-requirements.in
python-dateutil==2.8.1
# via
Expand Down Expand Up @@ -315,10 +309,6 @@ retry==0.9.2
# via
# -c requirements.txt
# flytekit
secretstorage==3.3.1
# via
# -c requirements.txt
# keyring
six==1.16.0
# via
# -c requirements.txt
Expand Down
6 changes: 5 additions & 1 deletion flytekit/core/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,11 @@ def transform_function_to_interface(fn: Callable, docstring: Optional[Docstring]
For now the fancy object, maybe in the future a dumb object.

"""
type_hints = typing.get_type_hints(fn)
try:
# include_extras can only be used in python >= 3.9
type_hints = typing.get_type_hints(fn, include_extras=True)
except TypeError:
type_hints = typing.get_type_hints(fn)
signature = inspect.signature(fn)
return_annotation = type_hints.get("return", None)

Expand Down
28 changes: 27 additions & 1 deletion flytekit/core/type_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@
Primitive,
Scalar,
Schema,
StructuredDatasetMetadata,
)
from flytekit.models.types import LiteralType, SimpleType
from flytekit.models.types import LiteralType, SimpleType, StructuredDatasetType

T = typing.TypeVar("T")
DEFINITIONS = "definitions"
Expand Down Expand Up @@ -280,6 +281,7 @@ def _serialize_flyte_type(self, python_val: T, python_type: Type[T]):
from flytekit.types.directory.types import FlyteDirectory
from flytekit.types.file import FlyteFile
from flytekit.types.schema.types import FlyteSchema
from flytekit.types.structured.structured_dataset import StructuredDataset

for f in dataclasses.fields(python_type):
v = python_val.__getattribute__(f.name)
Expand All @@ -288,6 +290,7 @@ def _serialize_flyte_type(self, python_val: T, python_type: Type[T]):
issubclass(field_type, FlyteSchema)
or issubclass(field_type, FlyteFile)
or issubclass(field_type, FlyteDirectory)
or issubclass(field_type, StructuredDataset)
):
lv = TypeEngine.to_literal(FlyteContext.current_context(), v, field_type, None)
# dataclass_json package will extract the "path" from FlyteFile, FlyteDirectory, and write it to a
Expand All @@ -300,6 +303,13 @@ def _serialize_flyte_type(self, python_val: T, python_type: Type[T]):
# as determined by the transformer.
if issubclass(field_type, FlyteFile) or issubclass(field_type, FlyteDirectory):
python_val.__setattr__(f.name, field_type(path=lv.scalar.blob.uri))
elif issubclass(field_type, StructuredDataset):
python_val.__setattr__(
f.name,
field_type(
uri=lv.scalar.structured_dataset.uri,
),
)

elif dataclasses.is_dataclass(field_type):
self._serialize_flyte_type(v, field_type)
Expand All @@ -308,6 +318,7 @@ def _deserialize_flyte_type(self, python_val: T, expected_python_type: Type) ->
from flytekit.types.directory.types import FlyteDirectory, FlyteDirToMultipartBlobTransformer
from flytekit.types.file.file import FlyteFile, FlyteFilePathTransformer
from flytekit.types.schema.types import FlyteSchema, FlyteSchemaTransformer
from flytekit.types.structured.structured_dataset import StructuredDataset, StructuredDatasetTransformerEngine

if not dataclasses.is_dataclass(expected_python_type):
return python_val
Expand Down Expand Up @@ -353,6 +364,21 @@ def _deserialize_flyte_type(self, python_val: T, expected_python_type: Type) ->
),
expected_python_type,
)
elif issubclass(expected_python_type, StructuredDataset):
return StructuredDatasetTransformerEngine().to_python_value(
FlyteContext.current_context(),
Literal(
scalar=Scalar(
structured_dataset=StructuredDataset(
metadata=StructuredDatasetMetadata(
structured_dataset_type=StructuredDatasetType(format=python_val.file_format)
),
uri=python_val.uri,
)
)
),
expected_python_type,
)
else:
for f in dataclasses.fields(expected_python_type):
value = python_val.__getattribute__(f.name)
Expand Down
2 changes: 1 addition & 1 deletion flytekit/models/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def external_schema_bytes(self) -> bytes:
def to_flyte_idl(self) -> _types_pb2.StructuredDatasetType:
return _types_pb2.StructuredDatasetType(
columns=[c.to_flyte_idl() for c in self.columns] if self.columns else None,
format=self._format,
format=self.format,
external_schema_type=self.external_schema_type if self.external_schema_type else None,
external_schema_bytes=self.external_schema_bytes if self.external_schema_bytes else None,
)
Expand Down
6 changes: 1 addition & 5 deletions flytekit/types/structured/basic_dfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,7 @@ def decode(
path = flyte_value.uri
local_dir = ctx.file_access.get_random_local_directory()
ctx.file_access.get_data(path, local_dir, is_multipart=True)
frames = [pandas.read_parquet(os.path.join(local_dir, f)) for f in os.listdir(local_dir)]
if len(frames) == 1:
return frames[0]
elif len(frames) > 1:
return pandas.concat(frames, copy=True)
return pd.read_parquet(local_dir)


class ArrowToParquetEncodingHandler(StructuredDatasetEncoder):
Expand Down
Loading