apache-hamilton 1.90.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- apache_hamilton-1.90.0.dev0.dist-info/METADATA +407 -0
- apache_hamilton-1.90.0.dev0.dist-info/RECORD +151 -0
- apache_hamilton-1.90.0.dev0.dist-info/WHEEL +4 -0
- apache_hamilton-1.90.0.dev0.dist-info/entry_points.txt +9 -0
- apache_hamilton-1.90.0.dev0.dist-info/licenses/DISCLAIMER +10 -0
- apache_hamilton-1.90.0.dev0.dist-info/licenses/LICENSE +228 -0
- apache_hamilton-1.90.0.dev0.dist-info/licenses/NOTICE +5 -0
- hamilton/__init__.py +24 -0
- hamilton/ad_hoc_utils.py +132 -0
- hamilton/async_driver.py +465 -0
- hamilton/base.py +466 -0
- hamilton/caching/__init__.py +16 -0
- hamilton/caching/adapter.py +1475 -0
- hamilton/caching/cache_key.py +70 -0
- hamilton/caching/fingerprinting.py +287 -0
- hamilton/caching/stores/__init__.py +16 -0
- hamilton/caching/stores/base.py +242 -0
- hamilton/caching/stores/file.py +140 -0
- hamilton/caching/stores/memory.py +297 -0
- hamilton/caching/stores/sqlite.py +282 -0
- hamilton/caching/stores/utils.py +40 -0
- hamilton/cli/__init__.py +16 -0
- hamilton/cli/__main__.py +328 -0
- hamilton/cli/commands.py +126 -0
- hamilton/cli/logic.py +338 -0
- hamilton/common/__init__.py +76 -0
- hamilton/contrib/__init__.py +41 -0
- hamilton/data_quality/__init__.py +16 -0
- hamilton/data_quality/base.py +198 -0
- hamilton/data_quality/default_validators.py +560 -0
- hamilton/data_quality/pandera_validators.py +121 -0
- hamilton/dataflows/__init__.py +726 -0
- hamilton/dataflows/template/README.md +25 -0
- hamilton/dataflows/template/__init__.py +54 -0
- hamilton/dataflows/template/author.md +28 -0
- hamilton/dataflows/template/requirements.txt +0 -0
- hamilton/dataflows/template/tags.json +7 -0
- hamilton/dataflows/template/valid_configs.jsonl +1 -0
- hamilton/dev_utils/__init__.py +16 -0
- hamilton/dev_utils/deprecation.py +204 -0
- hamilton/driver.py +2112 -0
- hamilton/execution/__init__.py +16 -0
- hamilton/execution/debugging_utils.py +56 -0
- hamilton/execution/executors.py +502 -0
- hamilton/execution/graph_functions.py +421 -0
- hamilton/execution/grouping.py +430 -0
- hamilton/execution/state.py +539 -0
- hamilton/experimental/__init__.py +27 -0
- hamilton/experimental/databackend.py +61 -0
- hamilton/experimental/decorators/__init__.py +16 -0
- hamilton/experimental/decorators/parameterize_frame.py +233 -0
- hamilton/experimental/h_async.py +29 -0
- hamilton/experimental/h_cache.py +413 -0
- hamilton/experimental/h_dask.py +28 -0
- hamilton/experimental/h_databackends.py +174 -0
- hamilton/experimental/h_ray.py +28 -0
- hamilton/experimental/h_spark.py +32 -0
- hamilton/function_modifiers/README +40 -0
- hamilton/function_modifiers/__init__.py +121 -0
- hamilton/function_modifiers/adapters.py +900 -0
- hamilton/function_modifiers/base.py +859 -0
- hamilton/function_modifiers/configuration.py +310 -0
- hamilton/function_modifiers/delayed.py +202 -0
- hamilton/function_modifiers/dependencies.py +246 -0
- hamilton/function_modifiers/expanders.py +1230 -0
- hamilton/function_modifiers/macros.py +1634 -0
- hamilton/function_modifiers/metadata.py +434 -0
- hamilton/function_modifiers/recursive.py +908 -0
- hamilton/function_modifiers/validation.py +289 -0
- hamilton/function_modifiers_base.py +31 -0
- hamilton/graph.py +1153 -0
- hamilton/graph_types.py +264 -0
- hamilton/graph_utils.py +41 -0
- hamilton/htypes.py +450 -0
- hamilton/io/__init__.py +32 -0
- hamilton/io/data_adapters.py +216 -0
- hamilton/io/default_data_loaders.py +224 -0
- hamilton/io/materialization.py +500 -0
- hamilton/io/utils.py +158 -0
- hamilton/lifecycle/__init__.py +67 -0
- hamilton/lifecycle/api.py +833 -0
- hamilton/lifecycle/base.py +1130 -0
- hamilton/lifecycle/default.py +802 -0
- hamilton/log_setup.py +47 -0
- hamilton/models.py +92 -0
- hamilton/node.py +449 -0
- hamilton/plugins/README.md +48 -0
- hamilton/plugins/__init__.py +16 -0
- hamilton/plugins/dask_extensions.py +47 -0
- hamilton/plugins/dlt_extensions.py +161 -0
- hamilton/plugins/geopandas_extensions.py +49 -0
- hamilton/plugins/h_dask.py +331 -0
- hamilton/plugins/h_ddog.py +522 -0
- hamilton/plugins/h_diskcache.py +163 -0
- hamilton/plugins/h_experiments/__init__.py +22 -0
- hamilton/plugins/h_experiments/__main__.py +62 -0
- hamilton/plugins/h_experiments/cache.py +39 -0
- hamilton/plugins/h_experiments/data_model.py +68 -0
- hamilton/plugins/h_experiments/hook.py +219 -0
- hamilton/plugins/h_experiments/server.py +375 -0
- hamilton/plugins/h_kedro.py +152 -0
- hamilton/plugins/h_logging.py +454 -0
- hamilton/plugins/h_mcp/__init__.py +28 -0
- hamilton/plugins/h_mcp/__main__.py +33 -0
- hamilton/plugins/h_mcp/_helpers.py +129 -0
- hamilton/plugins/h_mcp/_templates.py +417 -0
- hamilton/plugins/h_mcp/server.py +328 -0
- hamilton/plugins/h_mlflow.py +335 -0
- hamilton/plugins/h_narwhals.py +134 -0
- hamilton/plugins/h_openlineage.py +400 -0
- hamilton/plugins/h_opentelemetry.py +167 -0
- hamilton/plugins/h_pandas.py +257 -0
- hamilton/plugins/h_pandera.py +117 -0
- hamilton/plugins/h_polars.py +304 -0
- hamilton/plugins/h_polars_lazyframe.py +282 -0
- hamilton/plugins/h_pyarrow.py +56 -0
- hamilton/plugins/h_pydantic.py +127 -0
- hamilton/plugins/h_ray.py +242 -0
- hamilton/plugins/h_rich.py +142 -0
- hamilton/plugins/h_schema.py +493 -0
- hamilton/plugins/h_slack.py +100 -0
- hamilton/plugins/h_spark.py +1380 -0
- hamilton/plugins/h_threadpool.py +125 -0
- hamilton/plugins/h_tqdm.py +122 -0
- hamilton/plugins/h_vaex.py +129 -0
- hamilton/plugins/huggingface_extensions.py +236 -0
- hamilton/plugins/ibis_extensions.py +93 -0
- hamilton/plugins/jupyter_magic.py +622 -0
- hamilton/plugins/kedro_extensions.py +117 -0
- hamilton/plugins/lightgbm_extensions.py +99 -0
- hamilton/plugins/matplotlib_extensions.py +108 -0
- hamilton/plugins/mlflow_extensions.py +216 -0
- hamilton/plugins/numpy_extensions.py +105 -0
- hamilton/plugins/pandas_extensions.py +1763 -0
- hamilton/plugins/plotly_extensions.py +150 -0
- hamilton/plugins/polars_extensions.py +71 -0
- hamilton/plugins/polars_implementations.py +25 -0
- hamilton/plugins/polars_lazyframe_extensions.py +302 -0
- hamilton/plugins/polars_post_1_0_0_extensions.py +877 -0
- hamilton/plugins/polars_pre_1_0_0_extension.py +836 -0
- hamilton/plugins/pydantic_extensions.py +98 -0
- hamilton/plugins/pyspark_pandas_extensions.py +47 -0
- hamilton/plugins/sklearn_plot_extensions.py +129 -0
- hamilton/plugins/spark_extensions.py +105 -0
- hamilton/plugins/vaex_extensions.py +51 -0
- hamilton/plugins/xgboost_extensions.py +91 -0
- hamilton/plugins/yaml_extensions.py +89 -0
- hamilton/registry.py +254 -0
- hamilton/settings.py +18 -0
- hamilton/telemetry.py +50 -0
- hamilton/version.py +18 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
import dataclasses
|
|
19
|
+
import pathlib
|
|
20
|
+
from collections.abc import Collection
|
|
21
|
+
from typing import IO, Any
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import plotly.graph_objects
|
|
25
|
+
except ImportError as e:
|
|
26
|
+
raise NotImplementedError("Plotly is not installed.") from e
|
|
27
|
+
|
|
28
|
+
from hamilton import registry
|
|
29
|
+
from hamilton.io import utils
|
|
30
|
+
from hamilton.io.data_adapters import DataSaver
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclasses.dataclass
|
|
34
|
+
class PlotlyStaticWriter(DataSaver):
|
|
35
|
+
"""Write Plotly figure as static image format
|
|
36
|
+
ref: https://plotly.com/python/static-image-export/
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
path: str | pathlib.Path | IO
|
|
40
|
+
format: str | None = None
|
|
41
|
+
width: int | None = None
|
|
42
|
+
height: int | None = None
|
|
43
|
+
scale: int | float | None = None
|
|
44
|
+
validate: bool = True
|
|
45
|
+
engine: str = "auto"
|
|
46
|
+
|
|
47
|
+
def _get_saving_kwargs(self) -> dict:
|
|
48
|
+
kwargs = {}
|
|
49
|
+
if self.format is not None:
|
|
50
|
+
kwargs["format"] = self.format
|
|
51
|
+
if self.width is not None:
|
|
52
|
+
kwargs["width"] = self.width
|
|
53
|
+
if self.height is not None:
|
|
54
|
+
kwargs["height"] = self.height
|
|
55
|
+
if self.scale is not None:
|
|
56
|
+
kwargs["scale"] = self.scale
|
|
57
|
+
if self.validate is not None:
|
|
58
|
+
kwargs["validate"] = self.validate
|
|
59
|
+
if self.engine is not None:
|
|
60
|
+
kwargs["engine"] = self.engine
|
|
61
|
+
|
|
62
|
+
return kwargs
|
|
63
|
+
|
|
64
|
+
def save_data(self, data: plotly.graph_objects.Figure) -> dict[str, Any]:
|
|
65
|
+
data.write_image(file=self.path, **self._get_saving_kwargs())
|
|
66
|
+
return utils.get_file_metadata(self.path)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def applicable_types(cls) -> Collection[type]:
|
|
70
|
+
return [plotly.graph_objects.Figure]
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def name(cls) -> str:
|
|
74
|
+
return "plotly"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclasses.dataclass
|
|
78
|
+
class PlotlyInteractiveWriter(DataSaver):
|
|
79
|
+
"""Write Plotly figure as interactive HTML + JS
|
|
80
|
+
ref: https://plotly.com/python/interactive-html-export/
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
path: str | pathlib.Path | IO
|
|
84
|
+
config: dict | None = None
|
|
85
|
+
auto_play: bool = True
|
|
86
|
+
include_plotlyjs: bool | str = (
|
|
87
|
+
True # or "cdn", "directory", "require", "False", "other string .js"
|
|
88
|
+
)
|
|
89
|
+
include_mathjax: bool | str = False # "cdn", "string .js"
|
|
90
|
+
post_script: str | list[str] | None = None
|
|
91
|
+
full_html: bool = True
|
|
92
|
+
animation_opts: dict | None = None
|
|
93
|
+
default_width: int | float | str = "100%"
|
|
94
|
+
default_height: int | float | str = "100%"
|
|
95
|
+
validate: bool = True
|
|
96
|
+
auto_open: bool = True
|
|
97
|
+
div_id: str | None = None
|
|
98
|
+
|
|
99
|
+
def _get_saving_kwargs(self) -> dict:
|
|
100
|
+
kwargs = {}
|
|
101
|
+
if self.config is not None:
|
|
102
|
+
kwargs["config"] = self.config
|
|
103
|
+
if self.auto_play is not None:
|
|
104
|
+
kwargs["auto_play"] = self.auto_play
|
|
105
|
+
if self.include_plotlyjs is not None:
|
|
106
|
+
kwargs["include_plotlyjs"] = self.include_plotlyjs
|
|
107
|
+
if self.include_mathjax is not None:
|
|
108
|
+
kwargs["include_mathjax"] = self.include_mathjax
|
|
109
|
+
if self.post_script is not None:
|
|
110
|
+
kwargs["post_script"] = self.post_script
|
|
111
|
+
if self.full_html is not None:
|
|
112
|
+
kwargs["full_html"] = self.full_html
|
|
113
|
+
if self.animation_opts is not None:
|
|
114
|
+
kwargs["animation_opts"] = self.animation_opts
|
|
115
|
+
if self.default_width is not None:
|
|
116
|
+
kwargs["default_width"] = self.default_width
|
|
117
|
+
if self.default_height is not None:
|
|
118
|
+
kwargs["default_height"] = self.default_height
|
|
119
|
+
if self.validate is not None:
|
|
120
|
+
kwargs["validate"] = self.validate
|
|
121
|
+
if self.auto_open is not None:
|
|
122
|
+
kwargs["auto_open"] = self.auto_open
|
|
123
|
+
if self.div_id is not None:
|
|
124
|
+
kwargs["div_id"] = self.div_id
|
|
125
|
+
return kwargs
|
|
126
|
+
|
|
127
|
+
def save_data(self, data: plotly.graph_objects.Figure) -> dict[str, Any]:
|
|
128
|
+
data.write_html(file=self.path, **self._get_saving_kwargs())
|
|
129
|
+
return utils.get_file_metadata(self.path)
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def applicable_types(cls) -> Collection[type]:
|
|
133
|
+
return [plotly.graph_objects.Figure]
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def name(cls) -> str:
|
|
137
|
+
return "html"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def register_data_loaders():
|
|
141
|
+
for loader in [
|
|
142
|
+
PlotlyStaticWriter,
|
|
143
|
+
PlotlyInteractiveWriter,
|
|
144
|
+
]:
|
|
145
|
+
registry.register_adapter(loader)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
register_data_loaders()
|
|
149
|
+
|
|
150
|
+
COLUMN_FRIENDLY_DF_TYPE = False
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from packaging import version
|
|
22
|
+
|
|
23
|
+
from hamilton import registry
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
from xlsxwriter.workbook import Workbook
|
|
29
|
+
except ImportError:
|
|
30
|
+
Workbook = type
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
import polars as pl
|
|
34
|
+
except ImportError as e:
|
|
35
|
+
raise NotImplementedError("Polars is not installed.") from e
|
|
36
|
+
|
|
37
|
+
pl_version = version.Version(pl.__version__)
|
|
38
|
+
if pl_version < version.Version("1.0.0"):
|
|
39
|
+
from hamilton.plugins.polars_pre_1_0_0_extension import register_data_loaders
|
|
40
|
+
|
|
41
|
+
logger.warning(
|
|
42
|
+
"Using pre-1.0.0 Polars integration -- we will stop supporting this in Hamilton 2.0, so please upgrade your version of polars! "
|
|
43
|
+
f"Current version: {pl_version}, minimum required version: 1.0.0."
|
|
44
|
+
)
|
|
45
|
+
else:
|
|
46
|
+
from hamilton.plugins.polars_post_1_0_0_extensions import register_data_loaders
|
|
47
|
+
|
|
48
|
+
register_data_loaders()
|
|
49
|
+
|
|
50
|
+
DATAFRAME_TYPE = pl.DataFrame
|
|
51
|
+
COLUMN_TYPE = pl.Series
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def register_types():
|
|
55
|
+
"""Function to register the types for this extension."""
|
|
56
|
+
registry.register_types("polars", DATAFRAME_TYPE, COLUMN_TYPE)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@registry.get_column.register(pl.DataFrame)
|
|
60
|
+
def get_column_polars(df: pl.DataFrame, column_name: str) -> pl.Series:
|
|
61
|
+
return df[column_name]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@registry.fill_with_scalar.register(pl.DataFrame)
|
|
65
|
+
def fill_with_scalar_polars(df: pl.DataFrame, column_name: str, scalar_value: Any) -> pl.DataFrame:
|
|
66
|
+
if not isinstance(scalar_value, pl.Series):
|
|
67
|
+
scalar_value = [scalar_value]
|
|
68
|
+
return df.with_columns(pl.Series(name=column_name, values=scalar_value))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
register_types()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
|
|
20
|
+
from hamilton.plugins.h_polars import PolarsDataFrameResult
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
logger.warning("This module is deprecated. Please use hamilton.plugins.h_polars instead.")
|
|
24
|
+
|
|
25
|
+
__all__ = ["PolarsDataFrameResult"]
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
import dataclasses
|
|
19
|
+
from collections.abc import Collection, Mapping, Sequence
|
|
20
|
+
from io import BytesIO
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import (
|
|
23
|
+
Any,
|
|
24
|
+
BinaryIO,
|
|
25
|
+
TextIO,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
import polars as pl
|
|
30
|
+
except ImportError as e:
|
|
31
|
+
raise NotImplementedError("Polars is not installed.") from e
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# for polars <0.16.0 we need to determine whether type_aliases exist.
|
|
35
|
+
has_alias = False
|
|
36
|
+
if hasattr(pl, "type_aliases"):
|
|
37
|
+
has_alias = True
|
|
38
|
+
|
|
39
|
+
# for polars 0.18.0 we need to check what to do.
|
|
40
|
+
if has_alias and hasattr(pl.type_aliases, "CsvEncoding"):
|
|
41
|
+
from polars.type_aliases import CsvEncoding
|
|
42
|
+
else:
|
|
43
|
+
CsvEncoding = type
|
|
44
|
+
|
|
45
|
+
# import these types to make type hinting work
|
|
46
|
+
from polars.datatypes import DataType, DataTypeClass # noqa: F401
|
|
47
|
+
|
|
48
|
+
from hamilton import registry
|
|
49
|
+
from hamilton.io import utils
|
|
50
|
+
from hamilton.io.data_adapters import DataLoader
|
|
51
|
+
|
|
52
|
+
DATAFRAME_TYPE = pl.LazyFrame
|
|
53
|
+
COLUMN_TYPE = pl.Expr
|
|
54
|
+
# COLUMN_FRIENDLY_DF_TYPE = False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def register_types():
|
|
58
|
+
"""Function to register the types for this extension."""
|
|
59
|
+
registry.register_types("polars_lazyframe", DATAFRAME_TYPE, COLUMN_TYPE)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@registry.get_column.register(pl.LazyFrame)
|
|
63
|
+
def get_column_polars_lazyframe(df: pl.LazyFrame, column_name: str) -> pl.Expr:
|
|
64
|
+
# TODO: figure out if we can validate this here already or need to wait to the end
|
|
65
|
+
# when query.collect() resolves the lazy frame
|
|
66
|
+
# df.collect_schema().names() gives a list of names but it can be expensive
|
|
67
|
+
# https://docs.pola.rs/api/python/stable/reference/lazyframe/api/polars.LazyFrame.columns.html
|
|
68
|
+
# https://docs.pola.rs/api/python/stable/reference/lazyframe/api/polars.LazyFrame.collect_schema.html#polars.LazyFrame.collect_schema
|
|
69
|
+
return pl.col(column_name)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@registry.fill_with_scalar.register(pl.LazyFrame)
|
|
73
|
+
def fill_with_scalar_polars_lazyframe(
|
|
74
|
+
df: pl.LazyFrame, column_name: str, scalar_value: Any
|
|
75
|
+
) -> pl.LazyFrame:
|
|
76
|
+
if not isinstance(scalar_value, pl.Expr):
|
|
77
|
+
scalar_value = pl.lit(scalar_value)
|
|
78
|
+
return df.with_columns(scalar_value.alias(column_name))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
register_types()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclasses.dataclass
|
|
85
|
+
class PolarsScanCSVReader(DataLoader):
|
|
86
|
+
"""Class specifically to handle loading CSV files with Polars.
|
|
87
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_csv.html
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
file: str | TextIO | BytesIO | Path | BinaryIO | bytes
|
|
91
|
+
# kwargs:
|
|
92
|
+
has_header: bool = True
|
|
93
|
+
columns: Sequence[int] | Sequence[str] = None
|
|
94
|
+
new_columns: Sequence[str] = None
|
|
95
|
+
separator: str = ","
|
|
96
|
+
comment_char: str = None
|
|
97
|
+
quote_char: str = '"'
|
|
98
|
+
skip_rows: int = 0
|
|
99
|
+
dtypes: Mapping[str, Any] | Sequence[Any] = None
|
|
100
|
+
null_values: str | Sequence[str] | dict[str, str] = None
|
|
101
|
+
missing_utf8_is_empty_string: bool = False
|
|
102
|
+
ignore_errors: bool = False
|
|
103
|
+
try_parse_dates: bool = False
|
|
104
|
+
n_threads: int = None
|
|
105
|
+
infer_schema_length: int = 100
|
|
106
|
+
batch_size: int = 8192
|
|
107
|
+
n_rows: int = None
|
|
108
|
+
encoding: CsvEncoding | str = "utf8"
|
|
109
|
+
low_memory: bool = False
|
|
110
|
+
rechunk: bool = True
|
|
111
|
+
use_pyarrow: bool = False
|
|
112
|
+
storage_options: dict[str, Any] = None
|
|
113
|
+
skip_rows_after_header: int = 0
|
|
114
|
+
row_count_name: str = None
|
|
115
|
+
row_count_offset: int = 0
|
|
116
|
+
eol_char: str = "\n"
|
|
117
|
+
raise_if_empty: bool = True
|
|
118
|
+
|
|
119
|
+
def _get_loading_kwargs(self):
|
|
120
|
+
kwargs = {}
|
|
121
|
+
if self.has_header is not None:
|
|
122
|
+
kwargs["has_header"] = self.has_header
|
|
123
|
+
if self.columns is not None:
|
|
124
|
+
kwargs["columns"] = self.columns
|
|
125
|
+
if self.new_columns is not None:
|
|
126
|
+
kwargs["new_columns"] = self.new_columns
|
|
127
|
+
if self.separator is not None:
|
|
128
|
+
kwargs["separator"] = self.separator
|
|
129
|
+
if self.comment_char is not None:
|
|
130
|
+
kwargs["comment_char"] = self.comment_char
|
|
131
|
+
if self.quote_char is not None:
|
|
132
|
+
kwargs["quote_char"] = self.quote_char
|
|
133
|
+
if self.skip_rows is not None:
|
|
134
|
+
kwargs["skip_rows"] = self.skip_rows
|
|
135
|
+
if self.dtypes is not None:
|
|
136
|
+
kwargs["dtypes"] = self.dtypes
|
|
137
|
+
if self.null_values is not None:
|
|
138
|
+
kwargs["null_values"] = self.null_values
|
|
139
|
+
if self.missing_utf8_is_empty_string is not None:
|
|
140
|
+
kwargs["missing_utf8_is_empty_string"] = self.missing_utf8_is_empty_string
|
|
141
|
+
if self.ignore_errors is not None:
|
|
142
|
+
kwargs["ignore_errors"] = self.ignore_errors
|
|
143
|
+
if self.try_parse_dates is not None:
|
|
144
|
+
kwargs["try_parse_dates"] = self.try_parse_dates
|
|
145
|
+
if self.n_threads is not None:
|
|
146
|
+
kwargs["n_threads"] = self.n_threads
|
|
147
|
+
if self.infer_schema_length is not None:
|
|
148
|
+
kwargs["infer_schema_length"] = self.infer_schema_length
|
|
149
|
+
if self.n_rows is not None:
|
|
150
|
+
kwargs["n_rows"] = self.n_rows
|
|
151
|
+
if self.encoding is not None:
|
|
152
|
+
kwargs["encoding"] = self.encoding
|
|
153
|
+
if self.low_memory is not None:
|
|
154
|
+
kwargs["low_memory"] = self.low_memory
|
|
155
|
+
if self.rechunk is not None:
|
|
156
|
+
kwargs["rechunk"] = self.rechunk
|
|
157
|
+
if self.storage_options is not None:
|
|
158
|
+
kwargs["storage_options"] = self.storage_options
|
|
159
|
+
if self.skip_rows_after_header is not None:
|
|
160
|
+
kwargs["skip_rows_after_header"] = self.skip_rows_after_header
|
|
161
|
+
if self.row_count_name is not None:
|
|
162
|
+
kwargs["row_count_name"] = self.row_count_name
|
|
163
|
+
if self.row_count_offset is not None:
|
|
164
|
+
kwargs["row_count_offset"] = self.row_count_offset
|
|
165
|
+
if self.eol_char is not None:
|
|
166
|
+
kwargs["eol_char"] = self.eol_char
|
|
167
|
+
if self.raise_if_empty is not None:
|
|
168
|
+
kwargs["raise_if_empty"] = self.raise_if_empty
|
|
169
|
+
return kwargs
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def applicable_types(cls) -> Collection[type]:
|
|
173
|
+
return [DATAFRAME_TYPE]
|
|
174
|
+
|
|
175
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
176
|
+
df = pl.scan_csv(self.file, **self._get_loading_kwargs())
|
|
177
|
+
|
|
178
|
+
metadata = utils.get_file_and_dataframe_metadata(self.file, df)
|
|
179
|
+
return df, metadata
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def name(cls) -> str:
|
|
183
|
+
return "csv"
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@dataclasses.dataclass
|
|
187
|
+
class PolarsScanParquetReader(DataLoader):
|
|
188
|
+
"""Class specifically to handle loading parquet files with polars
|
|
189
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_parquet.html
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
file: str | TextIO | BytesIO | Path | BinaryIO | bytes
|
|
193
|
+
# kwargs:
|
|
194
|
+
columns: list[int] | list[str] = None
|
|
195
|
+
n_rows: int = None
|
|
196
|
+
use_pyarrow: bool = False
|
|
197
|
+
memory_map: bool = True
|
|
198
|
+
storage_options: dict[str, Any] = None
|
|
199
|
+
parallel: Any = "auto"
|
|
200
|
+
row_count_name: str = None
|
|
201
|
+
row_count_offset: int = 0
|
|
202
|
+
low_memory: bool = False
|
|
203
|
+
use_statistics: bool = True
|
|
204
|
+
rechunk: bool = True
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def applicable_types(cls) -> Collection[type]:
|
|
208
|
+
return [DATAFRAME_TYPE]
|
|
209
|
+
|
|
210
|
+
def _get_loading_kwargs(self):
|
|
211
|
+
kwargs = {}
|
|
212
|
+
if self.columns is not None:
|
|
213
|
+
kwargs["columns"] = self.columns
|
|
214
|
+
if self.n_rows is not None:
|
|
215
|
+
kwargs["n_rows"] = self.n_rows
|
|
216
|
+
if self.storage_options is not None:
|
|
217
|
+
kwargs["storage_options"] = self.storage_options
|
|
218
|
+
if self.parallel is not None:
|
|
219
|
+
kwargs["parallel"] = self.parallel
|
|
220
|
+
if self.row_count_name is not None:
|
|
221
|
+
kwargs["row_count_name"] = self.row_count_name
|
|
222
|
+
if self.row_count_offset is not None:
|
|
223
|
+
kwargs["row_count_offset"] = self.row_count_offset
|
|
224
|
+
if self.low_memory is not None:
|
|
225
|
+
kwargs["low_memory"] = self.low_memory
|
|
226
|
+
if self.use_statistics is not None:
|
|
227
|
+
kwargs["use_statistics"] = self.use_statistics
|
|
228
|
+
if self.rechunk is not None:
|
|
229
|
+
kwargs["rechunk"] = self.rechunk
|
|
230
|
+
return kwargs
|
|
231
|
+
|
|
232
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
233
|
+
df = pl.scan_parquet(self.file, **self._get_loading_kwargs())
|
|
234
|
+
metadata = utils.get_file_and_dataframe_metadata(self.file, df)
|
|
235
|
+
return df, metadata
|
|
236
|
+
|
|
237
|
+
@classmethod
|
|
238
|
+
def name(cls) -> str:
|
|
239
|
+
return "parquet"
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@dataclasses.dataclass
|
|
243
|
+
class PolarsScanFeatherReader(DataLoader):
|
|
244
|
+
"""
|
|
245
|
+
Class specifically to handle loading Feather/Arrow IPC files with Polars.
|
|
246
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_ipc.html
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
source: str | BinaryIO | BytesIO | Path | bytes
|
|
250
|
+
# kwargs:
|
|
251
|
+
columns: list[str] | list[int] | None = None
|
|
252
|
+
n_rows: int | None = None
|
|
253
|
+
use_pyarrow: bool = False
|
|
254
|
+
memory_map: bool = True
|
|
255
|
+
storage_options: dict[str, Any] | None = None
|
|
256
|
+
row_count_name: str | None = None
|
|
257
|
+
row_count_offset: int = 0
|
|
258
|
+
rechunk: bool = True
|
|
259
|
+
|
|
260
|
+
@classmethod
|
|
261
|
+
def applicable_types(cls) -> Collection[type]:
|
|
262
|
+
return [DATAFRAME_TYPE]
|
|
263
|
+
|
|
264
|
+
def _get_loading_kwargs(self):
|
|
265
|
+
kwargs = {}
|
|
266
|
+
if self.columns is not None:
|
|
267
|
+
kwargs["columns"] = self.columns
|
|
268
|
+
if self.n_rows is not None:
|
|
269
|
+
kwargs["n_rows"] = self.n_rows
|
|
270
|
+
if self.memory_map is not None:
|
|
271
|
+
kwargs["memory_map"] = self.memory_map
|
|
272
|
+
if self.storage_options is not None:
|
|
273
|
+
kwargs["storage_options"] = self.storage_options
|
|
274
|
+
if self.row_count_name is not None:
|
|
275
|
+
kwargs["row_count_name"] = self.row_count_name
|
|
276
|
+
if self.row_count_offset is not None:
|
|
277
|
+
kwargs["row_count_offset"] = self.row_count_offset
|
|
278
|
+
if self.rechunk is not None:
|
|
279
|
+
kwargs["rechunk"] = self.rechunk
|
|
280
|
+
return kwargs
|
|
281
|
+
|
|
282
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
283
|
+
df = pl.scan_ipc(self.source, **self._get_loading_kwargs())
|
|
284
|
+
metadata = utils.get_file_metadata(self.source)
|
|
285
|
+
return df, metadata
|
|
286
|
+
|
|
287
|
+
@classmethod
|
|
288
|
+
def name(cls) -> str:
|
|
289
|
+
return "feather"
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def register_data_loaders():
|
|
293
|
+
"""Function to register the data loaders for this extension."""
|
|
294
|
+
for loader in [
|
|
295
|
+
PolarsScanCSVReader,
|
|
296
|
+
PolarsScanParquetReader,
|
|
297
|
+
PolarsScanFeatherReader,
|
|
298
|
+
]:
|
|
299
|
+
registry.register_adapter(loader)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
register_data_loaders()
|