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