daggerml.contrib.codecs

View source
 1from __future__ import annotations
 2
 3import importlib
 4from dataclasses import dataclass
 5from tempfile import NamedTemporaryFile
 6from typing import Any
 7
 8from daggerml import Uri
 9from daggerml.api import Dag, apply_codecs
10from daggerml.contrib.adapters import get_adapter
11from daggerml.contrib.s3 import S3Store
12
13
14class PandasDataFrameCodec:
15    def __init__(self, dataframe_type: type[Any]):
16        self._dataframe_type = dataframe_type
17
18    def can_encode(self, value: Any) -> bool:
19        return isinstance(value, self._dataframe_type)
20
21    def encode(self, value: Any, dag: Dag) -> Uri:
22        with NamedTemporaryFile(suffix=".parquet") as tmp:
23            value.to_parquet(tmp.name)
24            return S3Store().put(filepath=tmp.name, suffix=".parquet")
25
26
27class PolarsDataFrameCodec:
28    def __init__(self, dataframe_type: type[Any]):
29        self._dataframe_type = dataframe_type
30
31    def can_encode(self, value: Any) -> bool:
32        return isinstance(value, self._dataframe_type)
33
34    def encode(self, value: Any, dag: Dag) -> Uri:
35        with NamedTemporaryFile(suffix=".parquet") as tmp:
36            value.write_parquet(tmp.name)
37            return S3Store().put(filepath=tmp.name, suffix=".parquet")
38
39
40@dataclass(frozen=True)
41class DelayedRef:
42    name: str
43
44
45@dataclass(frozen=True)
46class DelayedLoad:
47    dagname: str
48    nodename: str | None = None
49
50
51@dataclass(frozen=True)
52class DelayedRunnable:
53    uri: str
54    adapter: str
55    sub: Any | "DelayedRunnable" | None
56    kwargs: dict[str, Any]
57
58
59class DelayedActionCodec:
60    def can_encode(self, value: Any) -> bool:
61        return isinstance(value, (DelayedRef, DelayedLoad, DelayedRunnable))
62
63    def encode(self, value: DelayedRef | DelayedLoad | DelayedRunnable, dag: "Dag"):
64        if isinstance(value, DelayedRef):
65            return apply_codecs(dag[value.name], dag=dag)  # apply_codecs required for `Node` objects.
66        if isinstance(value, DelayedLoad):
67            return apply_codecs(dag.require(value.dagname, name=value.nodename), dag=dag)  # apply_codecs required
68        adapter_spec = get_adapter(value.adapter)
69        # no need for `apply_codecs` because we return a Runnable which is recursed
70        return adapter_spec.resolve_runnable(value.uri, sub=value.sub, kwargs=value.kwargs)
71
72
73def _import_optional(module_name: str) -> Any | None:
74    try:
75        return importlib.import_module(module_name)
76    except ModuleNotFoundError as e:
77        if e.name == module_name:
78            return None
79        raise
80
81
82def literal_codecs() -> list[Any]:
83    codecs: list[Any] = [(1, DelayedActionCodec())]
84    pandas = _import_optional("pandas")
85    if pandas is not None:
86        codecs.append((1, PandasDataFrameCodec(pandas.DataFrame)))
87    polars = _import_optional("polars")
88    if polars is not None:
89        codecs.append((1, PolarsDataFrameCodec(polars.DataFrame)))
90    return codecs

PandasDataFrameCodec

class PandasDataFrameCodec:
View source
15class PandasDataFrameCodec:
16    def __init__(self, dataframe_type: type[Any]):
17        self._dataframe_type = dataframe_type
18
19    def can_encode(self, value: Any) -> bool:
20        return isinstance(value, self._dataframe_type)
21
22    def encode(self, value: Any, dag: Dag) -> Uri:
23        with NamedTemporaryFile(suffix=".parquet") as tmp:
24            value.to_parquet(tmp.name)
25            return S3Store().put(filepath=tmp.name, suffix=".parquet")

PandasDataFrameCodec.__init__

PandasDataFrameCodec(dataframe_type: type[typing.Any])
View source
16    def __init__(self, dataframe_type: type[Any]):
17        self._dataframe_type = dataframe_type

PandasDataFrameCodec.can_encode

def can_encode(self, value: Any) -> bool:
View source
19    def can_encode(self, value: Any) -> bool:
20        return isinstance(value, self._dataframe_type)

PandasDataFrameCodec.encode

def encode(self, value: Any, dag: daggerml.Dag) -> daggerml.Uri:
View source
22    def encode(self, value: Any, dag: Dag) -> Uri:
23        with NamedTemporaryFile(suffix=".parquet") as tmp:
24            value.to_parquet(tmp.name)
25            return S3Store().put(filepath=tmp.name, suffix=".parquet")

PolarsDataFrameCodec

class PolarsDataFrameCodec:
View source
28class PolarsDataFrameCodec:
29    def __init__(self, dataframe_type: type[Any]):
30        self._dataframe_type = dataframe_type
31
32    def can_encode(self, value: Any) -> bool:
33        return isinstance(value, self._dataframe_type)
34
35    def encode(self, value: Any, dag: Dag) -> Uri:
36        with NamedTemporaryFile(suffix=".parquet") as tmp:
37            value.write_parquet(tmp.name)
38            return S3Store().put(filepath=tmp.name, suffix=".parquet")

PolarsDataFrameCodec.__init__

PolarsDataFrameCodec(dataframe_type: type[typing.Any])
View source
29    def __init__(self, dataframe_type: type[Any]):
30        self._dataframe_type = dataframe_type

PolarsDataFrameCodec.can_encode

def can_encode(self, value: Any) -> bool:
View source
32    def can_encode(self, value: Any) -> bool:
33        return isinstance(value, self._dataframe_type)

PolarsDataFrameCodec.encode

def encode(self, value: Any, dag: daggerml.Dag) -> daggerml.Uri:
View source
35    def encode(self, value: Any, dag: Dag) -> Uri:
36        with NamedTemporaryFile(suffix=".parquet") as tmp:
37            value.write_parquet(tmp.name)
38            return S3Store().put(filepath=tmp.name, suffix=".parquet")

DelayedRef

@dataclass(frozen=True)
class DelayedRef:
View source
41@dataclass(frozen=True)
42class DelayedRef:
43    name: str

DelayedRef.__init__

DelayedRef(name: str)

DelayedRef.name

name: str

DelayedLoad

@dataclass(frozen=True)
class DelayedLoad:
View source
46@dataclass(frozen=True)
47class DelayedLoad:
48    dagname: str
49    nodename: str | None = None

DelayedLoad.__init__

DelayedLoad(dagname: str, nodename: str | None = None)

DelayedLoad.dagname

dagname: str

DelayedLoad.nodename

nodename: str | None= None

DelayedRunnable

@dataclass(frozen=True)
class DelayedRunnable:
View source
52@dataclass(frozen=True)
53class DelayedRunnable:
54    uri: str
55    adapter: str
56    sub: Any | "DelayedRunnable" | None
57    kwargs: dict[str, Any]

DelayedRunnable.__init__

DelayedRunnable( uri: str, adapter: str, sub: "Any | 'DelayedRunnable' | None", kwargs: dict[str, typing.Any])

DelayedRunnable.uri

uri: str

DelayedRunnable.adapter

adapter: str

DelayedRunnable.sub

sub: "Any | 'DelayedRunnable' | None"

DelayedRunnable.kwargs

kwargs: dict[str, typing.Any]

DelayedActionCodec

class DelayedActionCodec:
View source
60class DelayedActionCodec:
61    def can_encode(self, value: Any) -> bool:
62        return isinstance(value, (DelayedRef, DelayedLoad, DelayedRunnable))
63
64    def encode(self, value: DelayedRef | DelayedLoad | DelayedRunnable, dag: "Dag"):
65        if isinstance(value, DelayedRef):
66            return apply_codecs(dag[value.name], dag=dag)  # apply_codecs required for `Node` objects.
67        if isinstance(value, DelayedLoad):
68            return apply_codecs(dag.require(value.dagname, name=value.nodename), dag=dag)  # apply_codecs required
69        adapter_spec = get_adapter(value.adapter)
70        # no need for `apply_codecs` because we return a Runnable which is recursed
71        return adapter_spec.resolve_runnable(value.uri, sub=value.sub, kwargs=value.kwargs)

DelayedActionCodec.can_encode

def can_encode(self, value: Any) -> bool:
View source
61    def can_encode(self, value: Any) -> bool:
62        return isinstance(value, (DelayedRef, DelayedLoad, DelayedRunnable))

DelayedActionCodec.encode

def encode( self, value: DelayedRef | DelayedLoad | DelayedRunnable, dag: daggerml.Dag):
View source
64    def encode(self, value: DelayedRef | DelayedLoad | DelayedRunnable, dag: "Dag"):
65        if isinstance(value, DelayedRef):
66            return apply_codecs(dag[value.name], dag=dag)  # apply_codecs required for `Node` objects.
67        if isinstance(value, DelayedLoad):
68            return apply_codecs(dag.require(value.dagname, name=value.nodename), dag=dag)  # apply_codecs required
69        adapter_spec = get_adapter(value.adapter)
70        # no need for `apply_codecs` because we return a Runnable which is recursed
71        return adapter_spec.resolve_runnable(value.uri, sub=value.sub, kwargs=value.kwargs)

literal_codecs

def literal_codecs() -> list[typing.Any]:
View source
83def literal_codecs() -> list[Any]:
84    codecs: list[Any] = [(1, DelayedActionCodec())]
85    pandas = _import_optional("pandas")
86    if pandas is not None:
87        codecs.append((1, PandasDataFrameCodec(pandas.DataFrame)))
88    polars = _import_optional("polars")
89    if polars is not None:
90        codecs.append((1, PolarsDataFrameCodec(polars.DataFrame)))
91    return codecs