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,836 @@
|
|
|
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, IOBase, TextIOWrapper
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import (
|
|
23
|
+
Any,
|
|
24
|
+
BinaryIO,
|
|
25
|
+
Literal,
|
|
26
|
+
TextIO,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from xlsxwriter.workbook import Workbook
|
|
31
|
+
except ImportError:
|
|
32
|
+
Workbook = type
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
import polars as pl
|
|
36
|
+
except ImportError as e:
|
|
37
|
+
raise NotImplementedError("Polars is not installed.") from e
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# for polars <0.16.0 we need to determine whether type_aliases exist.
|
|
41
|
+
has_alias = False
|
|
42
|
+
if hasattr(pl, "type_aliases"):
|
|
43
|
+
has_alias = True
|
|
44
|
+
|
|
45
|
+
# for polars 0.18.0 we need to check what to do.
|
|
46
|
+
if has_alias and hasattr(pl.type_aliases, "CsvEncoding"):
|
|
47
|
+
from polars.type_aliases import CsvEncoding, SchemaDefinition
|
|
48
|
+
else:
|
|
49
|
+
CsvEncoding = type
|
|
50
|
+
if has_alias and hasattr(pl.type_aliases, "CsvQuoteStyle"):
|
|
51
|
+
from polars.type_aliases import CsvQuoteStyle
|
|
52
|
+
else:
|
|
53
|
+
CsvQuoteStyle = type
|
|
54
|
+
if has_alias and hasattr(pl.type_aliases, "IpcCompression"):
|
|
55
|
+
from polars.type_aliases import IpcCompression
|
|
56
|
+
else:
|
|
57
|
+
IpcCompression = type
|
|
58
|
+
|
|
59
|
+
from hamilton import registry
|
|
60
|
+
from hamilton.io import utils
|
|
61
|
+
from hamilton.io.data_adapters import DataLoader, DataSaver
|
|
62
|
+
|
|
63
|
+
DATAFRAME_TYPE = pl.DataFrame
|
|
64
|
+
COLUMN_TYPE = pl.Series
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@registry.get_column.register(pl.DataFrame)
|
|
68
|
+
def get_column_polars(df: pl.DataFrame, column_name: str) -> pl.Series:
|
|
69
|
+
return df[column_name]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@registry.fill_with_scalar.register(pl.DataFrame)
|
|
73
|
+
def fill_with_scalar_polars(df: pl.DataFrame, column_name: str, scalar_value: Any) -> pl.DataFrame:
|
|
74
|
+
if not isinstance(scalar_value, pl.Series):
|
|
75
|
+
scalar_value = [scalar_value]
|
|
76
|
+
return df.with_columns(pl.Series(name=column_name, values=scalar_value))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclasses.dataclass
|
|
80
|
+
class PolarsCSVReader(DataLoader):
|
|
81
|
+
"""Class specifically to handle loading CSV files with Polars.
|
|
82
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_csv.html
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
file: str | TextIO | BytesIO | Path | BinaryIO | bytes
|
|
86
|
+
# kwargs:
|
|
87
|
+
has_header: bool = True
|
|
88
|
+
include_header: bool = True
|
|
89
|
+
columns: Sequence[int] | Sequence[str] = None
|
|
90
|
+
new_columns: Sequence[str] = None
|
|
91
|
+
separator: str = ","
|
|
92
|
+
comment_char: str = None
|
|
93
|
+
quote_char: str = '"'
|
|
94
|
+
skip_rows: int = 0
|
|
95
|
+
dtypes: Mapping[str, Any] | Sequence[Any] = None
|
|
96
|
+
null_values: str | Sequence[str] | dict[str, str] = None
|
|
97
|
+
missing_utf8_is_empty_string: bool = False
|
|
98
|
+
ignore_errors: bool = False
|
|
99
|
+
try_parse_dates: bool = False
|
|
100
|
+
n_threads: int = None
|
|
101
|
+
infer_schema_length: int = 100
|
|
102
|
+
batch_size: int = 8192
|
|
103
|
+
n_rows: int = None
|
|
104
|
+
encoding: CsvEncoding | str = "utf8"
|
|
105
|
+
low_memory: bool = False
|
|
106
|
+
rechunk: bool = True
|
|
107
|
+
use_pyarrow: bool = False
|
|
108
|
+
storage_options: dict[str, Any] = None
|
|
109
|
+
skip_rows_after_header: int = 0
|
|
110
|
+
row_count_name: str = None
|
|
111
|
+
row_count_offset: int = 0
|
|
112
|
+
sample_size: int = 1024
|
|
113
|
+
eol_char: str = "\n"
|
|
114
|
+
raise_if_empty: bool = True
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def applicable_types(cls) -> Collection[type]:
|
|
118
|
+
return [DATAFRAME_TYPE]
|
|
119
|
+
|
|
120
|
+
def _get_loading_kwargs(self):
|
|
121
|
+
kwargs = {}
|
|
122
|
+
if self.has_header is not None:
|
|
123
|
+
kwargs["has_header"] = self.has_header
|
|
124
|
+
if self.columns is not None:
|
|
125
|
+
kwargs["columns"] = self.columns
|
|
126
|
+
if self.new_columns is not None:
|
|
127
|
+
kwargs["new_columns"] = self.new_columns
|
|
128
|
+
if self.separator is not None:
|
|
129
|
+
kwargs["separator"] = self.separator
|
|
130
|
+
if self.comment_char is not None:
|
|
131
|
+
kwargs["comment_char"] = self.comment_char
|
|
132
|
+
if self.quote_char is not None:
|
|
133
|
+
kwargs["quote_char"] = self.quote_char
|
|
134
|
+
if self.skip_rows is not None:
|
|
135
|
+
kwargs["skip_rows"] = self.skip_rows
|
|
136
|
+
if self.dtypes is not None:
|
|
137
|
+
kwargs["dtypes"] = self.dtypes
|
|
138
|
+
if self.null_values is not None:
|
|
139
|
+
kwargs["null_values"] = self.null_values
|
|
140
|
+
if self.missing_utf8_is_empty_string is not None:
|
|
141
|
+
kwargs["missing_utf8_is_empty_string"] = self.missing_utf8_is_empty_string
|
|
142
|
+
if self.ignore_errors is not None:
|
|
143
|
+
kwargs["ignore_errors"] = self.ignore_errors
|
|
144
|
+
if self.try_parse_dates is not None:
|
|
145
|
+
kwargs["try_parse_dates"] = self.try_parse_dates
|
|
146
|
+
if self.n_threads is not None:
|
|
147
|
+
kwargs["n_threads"] = self.n_threads
|
|
148
|
+
if self.infer_schema_length is not None:
|
|
149
|
+
kwargs["infer_schema_length"] = self.infer_schema_length
|
|
150
|
+
if self.batch_size is not None:
|
|
151
|
+
kwargs["batch_size"] = self.batch_size
|
|
152
|
+
if self.n_rows is not None:
|
|
153
|
+
kwargs["n_rows"] = self.n_rows
|
|
154
|
+
if self.encoding is not None:
|
|
155
|
+
kwargs["encoding"] = self.encoding
|
|
156
|
+
if self.low_memory is not None:
|
|
157
|
+
kwargs["low_memory"] = self.low_memory
|
|
158
|
+
if self.rechunk is not None:
|
|
159
|
+
kwargs["rechunk"] = self.rechunk
|
|
160
|
+
if self.use_pyarrow is not None:
|
|
161
|
+
kwargs["use_pyarrow"] = self.use_pyarrow
|
|
162
|
+
if self.storage_options is not None:
|
|
163
|
+
kwargs["storage_options"] = self.storage_options
|
|
164
|
+
if self.skip_rows_after_header is not None:
|
|
165
|
+
kwargs["skip_rows_after_header"] = self.skip_rows_after_header
|
|
166
|
+
if self.row_count_name is not None:
|
|
167
|
+
kwargs["row_count_name"] = self.row_count_name
|
|
168
|
+
if self.row_count_offset is not None:
|
|
169
|
+
kwargs["row_count_offset"] = self.row_count_offset
|
|
170
|
+
if self.eol_char is not None:
|
|
171
|
+
kwargs["eol_char"] = self.eol_char
|
|
172
|
+
if self.raise_if_empty is not None:
|
|
173
|
+
kwargs["raise_if_empty"] = self.raise_if_empty
|
|
174
|
+
return kwargs
|
|
175
|
+
|
|
176
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
177
|
+
df = pl.read_csv(self.file, **self._get_loading_kwargs())
|
|
178
|
+
|
|
179
|
+
metadata = utils.get_file_and_dataframe_metadata(self.file, df)
|
|
180
|
+
return df, metadata
|
|
181
|
+
|
|
182
|
+
@classmethod
|
|
183
|
+
def name(cls) -> str:
|
|
184
|
+
return "csv"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclasses.dataclass
|
|
188
|
+
class PolarsCSVWriter(DataSaver):
|
|
189
|
+
"""Class specifically to handle saving CSV files with Polars.
|
|
190
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.DataFrame.write_csv.html
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
file: BytesIO | TextIOWrapper | str | Path
|
|
194
|
+
# kwargs:
|
|
195
|
+
has_header: bool = True
|
|
196
|
+
separator: str = ","
|
|
197
|
+
line_terminator: str = "\n"
|
|
198
|
+
quote: str = '"'
|
|
199
|
+
batch_size: int = 1024
|
|
200
|
+
datetime_format: str = None
|
|
201
|
+
date_format: str = None
|
|
202
|
+
time_format: str = None
|
|
203
|
+
float_precision: int = None
|
|
204
|
+
null_value: str = None
|
|
205
|
+
quote_style: CsvQuoteStyle = None
|
|
206
|
+
|
|
207
|
+
@classmethod
|
|
208
|
+
def applicable_types(cls) -> Collection[type]:
|
|
209
|
+
return [DATAFRAME_TYPE, pl.LazyFrame]
|
|
210
|
+
|
|
211
|
+
def _get_saving_kwargs(self):
|
|
212
|
+
kwargs = {}
|
|
213
|
+
if self.separator is not None:
|
|
214
|
+
kwargs["separator"] = self.separator
|
|
215
|
+
if self.has_header is not None:
|
|
216
|
+
kwargs["has_header"] = self.has_header
|
|
217
|
+
if self.separator is not None:
|
|
218
|
+
kwargs["separator"] = self.separator
|
|
219
|
+
if self.line_terminator is not None:
|
|
220
|
+
kwargs["line_terminator"] = self.line_terminator
|
|
221
|
+
if self.quote is not None:
|
|
222
|
+
kwargs["quote"] = self.quote
|
|
223
|
+
if self.batch_size is not None:
|
|
224
|
+
kwargs["batch_size"] = self.batch_size
|
|
225
|
+
if self.datetime_format is not None:
|
|
226
|
+
kwargs["datetime_format"] = self.datetime_format
|
|
227
|
+
if self.date_format is not None:
|
|
228
|
+
kwargs["date_format"] = self.date_format
|
|
229
|
+
if self.time_format is not None:
|
|
230
|
+
kwargs["time_format"] = self.time_format
|
|
231
|
+
if self.float_precision is not None:
|
|
232
|
+
kwargs["float_precision"] = self.float_precision
|
|
233
|
+
if self.null_value is not None:
|
|
234
|
+
kwargs["null_value"] = self.null_value
|
|
235
|
+
if self.quote_style is not None:
|
|
236
|
+
kwargs["quote_style"] = self.quote_style
|
|
237
|
+
return kwargs
|
|
238
|
+
|
|
239
|
+
def save_data(self, data: DATAFRAME_TYPE | pl.LazyFrame) -> dict[str, Any]:
|
|
240
|
+
if isinstance(data, pl.LazyFrame):
|
|
241
|
+
data = data.collect()
|
|
242
|
+
data.write_csv(self.file, **self._get_saving_kwargs())
|
|
243
|
+
return utils.get_file_and_dataframe_metadata(self.file, data)
|
|
244
|
+
|
|
245
|
+
@classmethod
|
|
246
|
+
def name(cls) -> str:
|
|
247
|
+
return "csv"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@dataclasses.dataclass
|
|
251
|
+
class PolarsParquetReader(DataLoader):
|
|
252
|
+
"""Class specifically to handle loading parquet files with polars
|
|
253
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_parquet.html
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
file: str | TextIO | BytesIO | Path | BinaryIO | bytes
|
|
257
|
+
# kwargs:
|
|
258
|
+
columns: list[int] | list[str] = None
|
|
259
|
+
n_rows: int = None
|
|
260
|
+
use_pyarrow: bool = False
|
|
261
|
+
memory_map: bool = True
|
|
262
|
+
storage_options: dict[str, Any] = None
|
|
263
|
+
parallel: Any = "auto"
|
|
264
|
+
row_count_name: str = None
|
|
265
|
+
row_count_offset: int = 0
|
|
266
|
+
low_memory: bool = False
|
|
267
|
+
pyarrow_options: dict[str, Any] = None
|
|
268
|
+
use_statistics: bool = True
|
|
269
|
+
rechunk: bool = True
|
|
270
|
+
|
|
271
|
+
@classmethod
|
|
272
|
+
def applicable_types(cls) -> Collection[type]:
|
|
273
|
+
return [DATAFRAME_TYPE]
|
|
274
|
+
|
|
275
|
+
def _get_loading_kwargs(self):
|
|
276
|
+
kwargs = {}
|
|
277
|
+
if self.columns is not None:
|
|
278
|
+
kwargs["columns"] = self.columns
|
|
279
|
+
if self.n_rows is not None:
|
|
280
|
+
kwargs["n_rows"] = self.n_rows
|
|
281
|
+
if self.use_pyarrow is not None:
|
|
282
|
+
kwargs["use_pyarrow"] = self.use_pyarrow
|
|
283
|
+
if self.memory_map is not None:
|
|
284
|
+
kwargs["memory_map"] = self.memory_map
|
|
285
|
+
if self.storage_options is not None:
|
|
286
|
+
kwargs["storage_options"] = self.storage_options
|
|
287
|
+
if self.parallel is not None:
|
|
288
|
+
kwargs["parallel"] = self.parallel
|
|
289
|
+
if self.row_count_name is not None:
|
|
290
|
+
kwargs["row_count_name"] = self.row_count_name
|
|
291
|
+
if self.row_count_offset is not None:
|
|
292
|
+
kwargs["row_count_offset"] = self.row_count_offset
|
|
293
|
+
if self.low_memory is not None:
|
|
294
|
+
kwargs["low_memory"] = self.low_memory
|
|
295
|
+
if self.pyarrow_options is not None:
|
|
296
|
+
kwargs["pyarrow_options"] = self.pyarrow_options
|
|
297
|
+
if self.use_statistics is not None:
|
|
298
|
+
kwargs["use_statistics"] = self.use_statistics
|
|
299
|
+
if self.rechunk is not None:
|
|
300
|
+
kwargs["rechunk"] = self.rechunk
|
|
301
|
+
return kwargs
|
|
302
|
+
|
|
303
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
304
|
+
df = pl.read_parquet(self.file, **self._get_loading_kwargs())
|
|
305
|
+
metadata = utils.get_file_and_dataframe_metadata(self.file, df)
|
|
306
|
+
return df, metadata
|
|
307
|
+
|
|
308
|
+
@classmethod
|
|
309
|
+
def name(cls) -> str:
|
|
310
|
+
return "parquet"
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@dataclasses.dataclass
|
|
314
|
+
class PolarsParquetWriter(DataSaver):
|
|
315
|
+
"""Class specifically to handle saving CSV files with Polars.
|
|
316
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.DataFrame.write_parquet.html
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
file: BytesIO | TextIOWrapper | str | Path
|
|
320
|
+
# kwargs:
|
|
321
|
+
compression: Any = "zstd"
|
|
322
|
+
compression_level: int = None
|
|
323
|
+
statistics: bool = False
|
|
324
|
+
row_group_size: int = None
|
|
325
|
+
use_pyarrow: bool = False
|
|
326
|
+
pyarrow_options: dict[str, Any] = None
|
|
327
|
+
|
|
328
|
+
@classmethod
|
|
329
|
+
def applicable_types(cls) -> Collection[type]:
|
|
330
|
+
return [DATAFRAME_TYPE, pl.LazyFrame]
|
|
331
|
+
|
|
332
|
+
def _get_saving_kwargs(self):
|
|
333
|
+
kwargs = {}
|
|
334
|
+
if self.compression is not None:
|
|
335
|
+
kwargs["compression"] = self.compression
|
|
336
|
+
if self.compression is not None:
|
|
337
|
+
kwargs["compression_level"] = self.compression_level
|
|
338
|
+
if self.compression is not None:
|
|
339
|
+
kwargs["statistics"] = self.statistics
|
|
340
|
+
if self.compression is not None:
|
|
341
|
+
kwargs["row_group_size"] = self.row_group_size
|
|
342
|
+
if self.compression is not None:
|
|
343
|
+
kwargs["use_pyarrow"] = self.use_pyarrow
|
|
344
|
+
if self.compression is not None:
|
|
345
|
+
kwargs["pyarrow_options"] = self.pyarrow_options
|
|
346
|
+
return kwargs
|
|
347
|
+
|
|
348
|
+
def save_data(self, data: DATAFRAME_TYPE | pl.LazyFrame) -> dict[str, Any]:
|
|
349
|
+
if isinstance(data, pl.LazyFrame):
|
|
350
|
+
data = data.collect()
|
|
351
|
+
|
|
352
|
+
data.write_parquet(self.file, **self._get_saving_kwargs())
|
|
353
|
+
|
|
354
|
+
return utils.get_file_and_dataframe_metadata(self.file, data)
|
|
355
|
+
|
|
356
|
+
@classmethod
|
|
357
|
+
def name(cls) -> str:
|
|
358
|
+
return "parquet"
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
@dataclasses.dataclass
|
|
362
|
+
class PolarsFeatherReader(DataLoader):
|
|
363
|
+
"""
|
|
364
|
+
Class specifically to handle loading Feather/Arrow IPC files with Polars.
|
|
365
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_ipc.html
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
source: str | BinaryIO | BytesIO | Path | bytes
|
|
369
|
+
# kwargs:
|
|
370
|
+
columns: list[str] | list[int] | None = None
|
|
371
|
+
n_rows: int | None = None
|
|
372
|
+
use_pyarrow: bool = False
|
|
373
|
+
memory_map: bool = True
|
|
374
|
+
storage_options: dict[str, Any] | None = None
|
|
375
|
+
row_count_name: str | None = None
|
|
376
|
+
row_count_offset: int = 0
|
|
377
|
+
rechunk: bool = True
|
|
378
|
+
|
|
379
|
+
@classmethod
|
|
380
|
+
def applicable_types(cls) -> Collection[type]:
|
|
381
|
+
return [DATAFRAME_TYPE]
|
|
382
|
+
|
|
383
|
+
def _get_loading_kwargs(self):
|
|
384
|
+
kwargs = {}
|
|
385
|
+
if self.columns is not None:
|
|
386
|
+
kwargs["columns"] = self.columns
|
|
387
|
+
if self.n_rows is not None:
|
|
388
|
+
kwargs["n_rows"] = self.n_rows
|
|
389
|
+
if self.use_pyarrow is not None:
|
|
390
|
+
kwargs["use_pyarrow"] = self.use_pyarrow
|
|
391
|
+
if self.memory_map is not None:
|
|
392
|
+
kwargs["memory_map"] = self.memory_map
|
|
393
|
+
if self.storage_options is not None:
|
|
394
|
+
kwargs["storage_options"] = self.storage_options
|
|
395
|
+
if self.row_count_name is not None:
|
|
396
|
+
kwargs["row_count_name"] = self.row_count_name
|
|
397
|
+
if self.row_count_offset is not None:
|
|
398
|
+
kwargs["row_count_offset"] = self.row_count_offset
|
|
399
|
+
if self.rechunk is not None:
|
|
400
|
+
kwargs["rechunk"] = self.rechunk
|
|
401
|
+
return kwargs
|
|
402
|
+
|
|
403
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
404
|
+
df = pl.read_ipc(self.source, **self._get_loading_kwargs())
|
|
405
|
+
metadata = utils.get_file_metadata(self.source)
|
|
406
|
+
return df, metadata
|
|
407
|
+
|
|
408
|
+
@classmethod
|
|
409
|
+
def name(cls) -> str:
|
|
410
|
+
return "feather"
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
@dataclasses.dataclass
|
|
414
|
+
class PolarsFeatherWriter(DataSaver):
|
|
415
|
+
"""
|
|
416
|
+
Class specifically to handle saving Feather/Arrow IPC files with Polars.
|
|
417
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.DataFrame.write_ipc.html
|
|
418
|
+
"""
|
|
419
|
+
|
|
420
|
+
file: BinaryIO | BytesIO | str | Path | None = None
|
|
421
|
+
# kwargs:
|
|
422
|
+
compression: IpcCompression = "uncompressed"
|
|
423
|
+
|
|
424
|
+
@classmethod
|
|
425
|
+
def applicable_types(cls) -> Collection[type]:
|
|
426
|
+
return [DATAFRAME_TYPE, pl.LazyFrame]
|
|
427
|
+
|
|
428
|
+
def _get_saving_kwargs(self):
|
|
429
|
+
kwargs = {}
|
|
430
|
+
if self.compression is not None:
|
|
431
|
+
kwargs["compression"] = self.compression
|
|
432
|
+
return kwargs
|
|
433
|
+
|
|
434
|
+
def save_data(self, data: DATAFRAME_TYPE | pl.LazyFrame) -> dict[str, Any]:
|
|
435
|
+
if isinstance(data, pl.LazyFrame):
|
|
436
|
+
data = data.collect()
|
|
437
|
+
data.write_ipc(self.file, **self._get_saving_kwargs())
|
|
438
|
+
return utils.get_file_and_dataframe_metadata(self.file, data)
|
|
439
|
+
|
|
440
|
+
@classmethod
|
|
441
|
+
def name(cls) -> str:
|
|
442
|
+
return "feather"
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@dataclasses.dataclass
|
|
446
|
+
class PolarsAvroReader(DataLoader):
|
|
447
|
+
"""Class specifically to handle loading Avro files with polars
|
|
448
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_avro.html
|
|
449
|
+
"""
|
|
450
|
+
|
|
451
|
+
file: str | TextIO | BytesIO | Path | BinaryIO | bytes
|
|
452
|
+
# kwargs:
|
|
453
|
+
columns: list[int] | list[str] | None = None
|
|
454
|
+
n_rows: int | None = None
|
|
455
|
+
|
|
456
|
+
@classmethod
|
|
457
|
+
def applicable_types(cls) -> Collection[type]:
|
|
458
|
+
return [DATAFRAME_TYPE]
|
|
459
|
+
|
|
460
|
+
def _get_loading_kwargs(self):
|
|
461
|
+
kwargs = {}
|
|
462
|
+
if self.columns is not None:
|
|
463
|
+
kwargs["columns"] = self.columns
|
|
464
|
+
if self.n_rows is not None:
|
|
465
|
+
kwargs["n_rows"] = self.n_rows
|
|
466
|
+
return kwargs
|
|
467
|
+
|
|
468
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
469
|
+
df = pl.read_avro(self.file, **self._get_loading_kwargs())
|
|
470
|
+
metadata = utils.get_file_and_dataframe_metadata(self.file, df)
|
|
471
|
+
return df, metadata
|
|
472
|
+
|
|
473
|
+
@classmethod
|
|
474
|
+
def name(cls) -> str:
|
|
475
|
+
return "avro"
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
@dataclasses.dataclass
|
|
479
|
+
class PolarsAvroWriter(DataSaver):
|
|
480
|
+
"""Class specifically to handle saving Avro files with Polars.
|
|
481
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.DataFrame.write_avro.html
|
|
482
|
+
"""
|
|
483
|
+
|
|
484
|
+
file: BytesIO | TextIOWrapper | str | Path
|
|
485
|
+
# kwargs:
|
|
486
|
+
compression: Any = "uncompressed"
|
|
487
|
+
|
|
488
|
+
@classmethod
|
|
489
|
+
def applicable_types(cls) -> Collection[type]:
|
|
490
|
+
return [DATAFRAME_TYPE, pl.LazyFrame]
|
|
491
|
+
|
|
492
|
+
def _get_saving_kwargs(self):
|
|
493
|
+
kwargs = {}
|
|
494
|
+
if self.compression is not None:
|
|
495
|
+
kwargs["compression"] = self.compression
|
|
496
|
+
return kwargs
|
|
497
|
+
|
|
498
|
+
def save_data(self, data: DATAFRAME_TYPE | pl.LazyFrame) -> dict[str, Any]:
|
|
499
|
+
if isinstance(data, pl.LazyFrame):
|
|
500
|
+
data = data.collect()
|
|
501
|
+
|
|
502
|
+
data.write_avro(self.file, **self._get_saving_kwargs())
|
|
503
|
+
return utils.get_file_and_dataframe_metadata(self.file, data)
|
|
504
|
+
|
|
505
|
+
@classmethod
|
|
506
|
+
def name(cls) -> str:
|
|
507
|
+
return "avro"
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
@dataclasses.dataclass
|
|
511
|
+
class PolarsJSONReader(DataLoader):
|
|
512
|
+
"""
|
|
513
|
+
Class specifically to handle loading JSON files with Polars.
|
|
514
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_json.html
|
|
515
|
+
"""
|
|
516
|
+
|
|
517
|
+
source: str | Path | IOBase | bytes
|
|
518
|
+
schema: SchemaDefinition = None
|
|
519
|
+
schema_overrides: SchemaDefinition = None
|
|
520
|
+
|
|
521
|
+
@classmethod
|
|
522
|
+
def applicable_types(cls) -> Collection[type]:
|
|
523
|
+
return [DATAFRAME_TYPE]
|
|
524
|
+
|
|
525
|
+
def _get_loading_kwargs(self):
|
|
526
|
+
kwargs = {}
|
|
527
|
+
if self.schema is not None:
|
|
528
|
+
kwargs["schema"] = self.schema
|
|
529
|
+
if self.schema_overrides is not None:
|
|
530
|
+
kwargs["schema_overrides"] = self.schema_overrides
|
|
531
|
+
return kwargs
|
|
532
|
+
|
|
533
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
534
|
+
df = pl.read_json(self.source, **self._get_loading_kwargs())
|
|
535
|
+
metadata = utils.get_file_metadata(self.source)
|
|
536
|
+
return df, metadata
|
|
537
|
+
|
|
538
|
+
@classmethod
|
|
539
|
+
def name(cls) -> str:
|
|
540
|
+
return "json"
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
@dataclasses.dataclass
|
|
544
|
+
class PolarsJSONWriter(DataSaver):
|
|
545
|
+
"""
|
|
546
|
+
Class specifically to handle saving JSON files with Polars.
|
|
547
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.DataFrame.write_json.html
|
|
548
|
+
"""
|
|
549
|
+
|
|
550
|
+
file: IOBase | str | Path
|
|
551
|
+
pretty: bool = False
|
|
552
|
+
row_oriented: bool = False
|
|
553
|
+
|
|
554
|
+
@classmethod
|
|
555
|
+
def applicable_types(cls) -> Collection[type]:
|
|
556
|
+
return [DATAFRAME_TYPE, pl.LazyFrame]
|
|
557
|
+
|
|
558
|
+
def _get_saving_kwargs(self):
|
|
559
|
+
kwargs = {}
|
|
560
|
+
if self.pretty is not None:
|
|
561
|
+
kwargs["pretty"] = self.pretty
|
|
562
|
+
if self.row_oriented is not None:
|
|
563
|
+
kwargs["row_oriented"] = self.row_oriented
|
|
564
|
+
return kwargs
|
|
565
|
+
|
|
566
|
+
def save_data(self, data: DATAFRAME_TYPE | pl.LazyFrame) -> dict[str, Any]:
|
|
567
|
+
if isinstance(data, pl.LazyFrame):
|
|
568
|
+
data = data.collect()
|
|
569
|
+
|
|
570
|
+
data.write_json(self.file, **self._get_saving_kwargs())
|
|
571
|
+
return utils.get_file_and_dataframe_metadata(self.file, data)
|
|
572
|
+
|
|
573
|
+
@classmethod
|
|
574
|
+
def name(cls) -> str:
|
|
575
|
+
return "json"
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
@dataclasses.dataclass
|
|
579
|
+
class PolarsSpreadsheetReader(DataLoader):
|
|
580
|
+
"""
|
|
581
|
+
Class specifically to handle loading Spreadsheet files with Polars.
|
|
582
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_excel.html
|
|
583
|
+
"""
|
|
584
|
+
|
|
585
|
+
source: str | Path | IOBase | bytes
|
|
586
|
+
# kwargs:
|
|
587
|
+
sheet_id: int | Sequence[int] | None = None
|
|
588
|
+
sheet_name: str | list[str] | tuple[str] | None = None
|
|
589
|
+
engine: Literal["xlsx2csv", "openpyxl", "pyxlsb", "odf", "xlrd", "xlsxwriter"] = "xlsx2csv"
|
|
590
|
+
engine_options: dict[str, Any] | None = None
|
|
591
|
+
read_options: dict[str, Any] | None = None
|
|
592
|
+
schema_overrides: dict[str, Any] | None = None
|
|
593
|
+
raise_if_empty: bool = True
|
|
594
|
+
|
|
595
|
+
@classmethod
|
|
596
|
+
def applicable_types(cls) -> Collection[type]:
|
|
597
|
+
return [DATAFRAME_TYPE]
|
|
598
|
+
|
|
599
|
+
def _get_loading_kwargs(self):
|
|
600
|
+
kwargs = {}
|
|
601
|
+
if self.sheet_id is not None:
|
|
602
|
+
kwargs["sheet_id"] = self.sheet_id
|
|
603
|
+
if self.sheet_name is not None:
|
|
604
|
+
kwargs["sheet_name"] = self.sheet_name
|
|
605
|
+
if self.engine is not None:
|
|
606
|
+
kwargs["engine"] = self.engine
|
|
607
|
+
if self.engine_options is not None:
|
|
608
|
+
kwargs["engine_options"] = self.engine_options
|
|
609
|
+
if self.read_options is not None:
|
|
610
|
+
kwargs["read_options"] = self.read_options
|
|
611
|
+
if self.schema_overrides is not None:
|
|
612
|
+
kwargs["schema_overrides"] = self.schema_overrides
|
|
613
|
+
if self.raise_if_empty is not None:
|
|
614
|
+
kwargs["raise_if_empty"] = self.raise_if_empty
|
|
615
|
+
return kwargs
|
|
616
|
+
|
|
617
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
618
|
+
df = pl.read_excel(self.source, **self._get_loading_kwargs())
|
|
619
|
+
metadata = utils.get_file_metadata(self.source)
|
|
620
|
+
return df, metadata
|
|
621
|
+
|
|
622
|
+
@classmethod
|
|
623
|
+
def name(cls) -> str:
|
|
624
|
+
return "spreadsheet"
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
@dataclasses.dataclass
|
|
628
|
+
class PolarsSpreadsheetWriter(DataSaver):
|
|
629
|
+
"""
|
|
630
|
+
Class specifically to handle saving Spreadsheet files with Polars.
|
|
631
|
+
Should map to https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.DataFrame.write_excel.html
|
|
632
|
+
"""
|
|
633
|
+
|
|
634
|
+
# importing here because this is where it's used. Can move later.
|
|
635
|
+
# but yeah the polars type aliases weren't resolving well in python 3.9
|
|
636
|
+
# so stripped/reduced them appropriately.
|
|
637
|
+
from polars.datatypes import DataType, DataTypeClass
|
|
638
|
+
from polars.type_aliases import ColumnTotalsDefinition, RowTotalsDefinition
|
|
639
|
+
|
|
640
|
+
workbook: Workbook | BytesIO | Path | str
|
|
641
|
+
worksheet: str | None = None
|
|
642
|
+
# kwargs:
|
|
643
|
+
position: tuple[int, int] | str = "A1"
|
|
644
|
+
table_style: str | dict[str, Any] | None = None
|
|
645
|
+
table_name: str | None = None
|
|
646
|
+
column_formats: Mapping[str | tuple[str, ...], str | Mapping[str, str]] | None = None
|
|
647
|
+
dtype_formats: dict[DataType | DataTypeClass, str] | None = None
|
|
648
|
+
conditional_formats: (
|
|
649
|
+
Mapping[str | Collection[str], str | Mapping[str, Any] | Sequence[str | Mapping[str, Any]]]
|
|
650
|
+
| None
|
|
651
|
+
) = None
|
|
652
|
+
header_format: dict[str, Any] | None = None
|
|
653
|
+
column_totals: ColumnTotalsDefinition | None = None
|
|
654
|
+
column_widths: Mapping[str, tuple[str, ...] | int] | int | None = None
|
|
655
|
+
row_totals: RowTotalsDefinition | None = None
|
|
656
|
+
row_heights: dict[int | tuple[int, ...], int] | int | None = None
|
|
657
|
+
sparklines: dict[str, Sequence[str] | dict[str, Any]] | None = None
|
|
658
|
+
formulas: dict[str, str | dict[str, str]] | None = None
|
|
659
|
+
float_precision: int = 3
|
|
660
|
+
include_header: bool = True
|
|
661
|
+
autofilter: bool = True
|
|
662
|
+
autofit: bool = False
|
|
663
|
+
hidden_columns: Sequence[str] | str | None = None
|
|
664
|
+
hide_gridlines: bool = None
|
|
665
|
+
sheet_zoom: int | None = None
|
|
666
|
+
freeze_panes: (
|
|
667
|
+
str | tuple[int, int] | tuple[str, int, int] | tuple[int, int, int, int] | None
|
|
668
|
+
) = None
|
|
669
|
+
|
|
670
|
+
@classmethod
|
|
671
|
+
def applicable_types(cls) -> Collection[type]:
|
|
672
|
+
return [DATAFRAME_TYPE, pl.LazyFrame]
|
|
673
|
+
|
|
674
|
+
def _get_saving_kwargs(self):
|
|
675
|
+
kwargs = {}
|
|
676
|
+
if self.position is not None:
|
|
677
|
+
kwargs["position"] = self.position
|
|
678
|
+
if self.table_style is not None:
|
|
679
|
+
kwargs["table_style"] = self.table_style
|
|
680
|
+
if self.table_name is not None:
|
|
681
|
+
kwargs["table_name"] = self.table_name
|
|
682
|
+
if self.column_formats is not None:
|
|
683
|
+
kwargs["column_formats"] = self.column_formats
|
|
684
|
+
if self.dtype_formats is not None:
|
|
685
|
+
kwargs["dtype_formats"] = self.dtype_formats
|
|
686
|
+
if self.conditional_formats is not None:
|
|
687
|
+
kwargs["conditional_formats"] = self.conditional_formats
|
|
688
|
+
if self.header_format is not None:
|
|
689
|
+
kwargs["header_format"] = self.header_format
|
|
690
|
+
if self.column_totals is not None:
|
|
691
|
+
kwargs["column_totals"] = self.column_totals
|
|
692
|
+
if self.column_widths is not None:
|
|
693
|
+
kwargs["column_widths"] = self.column_widths
|
|
694
|
+
if self.row_totals is not None:
|
|
695
|
+
kwargs["row_totals"] = self.row_totals
|
|
696
|
+
if self.row_heights is not None:
|
|
697
|
+
kwargs["row_heights"] = self.row_heights
|
|
698
|
+
if self.sparklines is not None:
|
|
699
|
+
kwargs["sparklines"] = self.sparklines
|
|
700
|
+
if self.formulas is not None:
|
|
701
|
+
kwargs["formulas"] = self.formulas
|
|
702
|
+
if self.float_precision is not None:
|
|
703
|
+
kwargs["float_precision"] = self.float_precision
|
|
704
|
+
if self.include_header is not None:
|
|
705
|
+
kwargs["include_header"] = self.include_header
|
|
706
|
+
if self.autofilter is not None:
|
|
707
|
+
kwargs["autofilter"] = self.autofilter
|
|
708
|
+
if self.autofit is not None:
|
|
709
|
+
kwargs["autofit"] = self.autofit
|
|
710
|
+
if self.hidden_columns is not None:
|
|
711
|
+
kwargs["hidden_columns"] = self.hidden_columns
|
|
712
|
+
if self.hide_gridlines is not None:
|
|
713
|
+
kwargs["hide_gridlines"] = self.hide_gridlines
|
|
714
|
+
if self.sheet_zoom is not None:
|
|
715
|
+
kwargs["sheet_zoom"] = self.sheet_zoom
|
|
716
|
+
if self.freeze_panes is not None:
|
|
717
|
+
kwargs["freeze_panes"] = self.freeze_panes
|
|
718
|
+
return kwargs
|
|
719
|
+
|
|
720
|
+
def save_data(self, data: DATAFRAME_TYPE | pl.LazyFrame) -> dict[str, Any]:
|
|
721
|
+
if isinstance(data, pl.LazyFrame):
|
|
722
|
+
data = data.collect()
|
|
723
|
+
|
|
724
|
+
data.write_excel(self.workbook, self.worksheet, **self._get_saving_kwargs())
|
|
725
|
+
return utils.get_file_and_dataframe_metadata(self.workbook, data)
|
|
726
|
+
|
|
727
|
+
@classmethod
|
|
728
|
+
def name(cls) -> str:
|
|
729
|
+
return "spreadsheet"
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
@dataclasses.dataclass
|
|
733
|
+
class PolarsDatabaseReader(DataLoader):
|
|
734
|
+
"""
|
|
735
|
+
Class specifically to handle loading DataFrame from a database.
|
|
736
|
+
"""
|
|
737
|
+
|
|
738
|
+
query: str
|
|
739
|
+
connection: str
|
|
740
|
+
# kwargs:
|
|
741
|
+
iter_batches: bool = False
|
|
742
|
+
batch_size: int | None = None
|
|
743
|
+
schema_overrides: dict[str, Any] | None = None
|
|
744
|
+
infer_schema_length: int | None = None
|
|
745
|
+
execute_options: dict[str, Any] | None = None
|
|
746
|
+
|
|
747
|
+
@classmethod
|
|
748
|
+
def applicable_types(cls) -> Collection[type]:
|
|
749
|
+
return [DATAFRAME_TYPE]
|
|
750
|
+
|
|
751
|
+
def _get_loading_kwargs(self):
|
|
752
|
+
kwargs = {}
|
|
753
|
+
if self.iter_batches is not None:
|
|
754
|
+
kwargs["iter_batches"] = self.iter_batches
|
|
755
|
+
if self.batch_size is not None:
|
|
756
|
+
kwargs["batch_size"] = self.batch_size
|
|
757
|
+
if self.schema_overrides is not None:
|
|
758
|
+
kwargs["schema_overrides"] = self.schema_overrides
|
|
759
|
+
if self.infer_schema_length is not None:
|
|
760
|
+
kwargs["infer_schema_length"] = self.infer_schema_length
|
|
761
|
+
if self.execute_options is not None:
|
|
762
|
+
kwargs["execute_options"] = self.execute_options
|
|
763
|
+
return kwargs
|
|
764
|
+
|
|
765
|
+
def load_data(self, type_: type) -> tuple[DATAFRAME_TYPE, dict[str, Any]]:
|
|
766
|
+
df = pl.read_database(
|
|
767
|
+
query=self.query,
|
|
768
|
+
connection=self.connection,
|
|
769
|
+
**self._get_loading_kwargs(),
|
|
770
|
+
)
|
|
771
|
+
metadata = utils.get_file_and_dataframe_metadata(self.query, df)
|
|
772
|
+
return df, metadata
|
|
773
|
+
|
|
774
|
+
@classmethod
|
|
775
|
+
def name(cls) -> str:
|
|
776
|
+
return "database"
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
@dataclasses.dataclass
|
|
780
|
+
class PolarsDatabaseWriter(DataSaver):
|
|
781
|
+
"""
|
|
782
|
+
Class specifically to handle saving DataFrame to a database.
|
|
783
|
+
"""
|
|
784
|
+
|
|
785
|
+
table_name: str
|
|
786
|
+
connection: str
|
|
787
|
+
if_table_exists: Literal["fail", "replace", "append"] = "fail"
|
|
788
|
+
engine: Literal["auto", "sqlalchemy", "adbc"] = "sqlalchemy"
|
|
789
|
+
|
|
790
|
+
@classmethod
|
|
791
|
+
def applicable_types(cls) -> Collection[type]:
|
|
792
|
+
return [DATAFRAME_TYPE, pl.LazyFrame]
|
|
793
|
+
|
|
794
|
+
def _get_saving_kwargs(self):
|
|
795
|
+
kwargs = {}
|
|
796
|
+
if self.if_table_exists is not None:
|
|
797
|
+
kwargs["if_table_exists"] = self.if_table_exists
|
|
798
|
+
if self.engine is not None:
|
|
799
|
+
kwargs["engine"] = self.engine
|
|
800
|
+
return kwargs
|
|
801
|
+
|
|
802
|
+
def save_data(self, data: DATAFRAME_TYPE | pl.LazyFrame) -> dict[str, Any]:
|
|
803
|
+
if isinstance(data, pl.LazyFrame):
|
|
804
|
+
data = data.collect()
|
|
805
|
+
|
|
806
|
+
data.write_database(
|
|
807
|
+
table_name=self.table_name,
|
|
808
|
+
connection=self.connection,
|
|
809
|
+
**self._get_saving_kwargs(),
|
|
810
|
+
)
|
|
811
|
+
return utils.get_file_and_dataframe_metadata(self.table_name, data)
|
|
812
|
+
|
|
813
|
+
@classmethod
|
|
814
|
+
def name(cls) -> str:
|
|
815
|
+
return "database"
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def register_data_loaders():
|
|
819
|
+
"""Function to register the data loaders for this extension."""
|
|
820
|
+
for loader in [
|
|
821
|
+
PolarsCSVReader,
|
|
822
|
+
PolarsCSVWriter,
|
|
823
|
+
PolarsParquetReader,
|
|
824
|
+
PolarsParquetWriter,
|
|
825
|
+
PolarsFeatherReader,
|
|
826
|
+
PolarsFeatherWriter,
|
|
827
|
+
PolarsAvroReader,
|
|
828
|
+
PolarsAvroWriter,
|
|
829
|
+
PolarsJSONReader,
|
|
830
|
+
PolarsJSONWriter,
|
|
831
|
+
PolarsDatabaseReader,
|
|
832
|
+
PolarsDatabaseWriter,
|
|
833
|
+
PolarsSpreadsheetReader,
|
|
834
|
+
PolarsSpreadsheetWriter,
|
|
835
|
+
]:
|
|
836
|
+
registry.register_adapter(loader)
|