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,417 @@
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
+ from __future__ import annotations
19
+
20
+ import dataclasses
21
+ import textwrap
22
+
23
+ try:
24
+ import pandas # noqa: F401
25
+
26
+ HAS_PANDAS = True
27
+ except ImportError:
28
+ HAS_PANDAS = False
29
+
30
+ try:
31
+ import numpy # noqa: F401
32
+
33
+ HAS_NUMPY = True
34
+ except ImportError:
35
+ HAS_NUMPY = False
36
+
37
+ try:
38
+ import polars # noqa: F401
39
+
40
+ HAS_POLARS = True
41
+ except ImportError:
42
+ HAS_POLARS = False
43
+
44
+ try:
45
+ import graphviz # noqa: F401
46
+
47
+ HAS_GRAPHVIZ = True
48
+ except ImportError:
49
+ HAS_GRAPHVIZ = False
50
+
51
+ AVAILABLE_LIBS: dict[str, bool] = {
52
+ "pandas": HAS_PANDAS,
53
+ "numpy": HAS_NUMPY,
54
+ "polars": HAS_POLARS,
55
+ "graphviz": HAS_GRAPHVIZ,
56
+ }
57
+
58
+
59
+ @dataclasses.dataclass(frozen=True)
60
+ class ScaffoldTemplate:
61
+ """A scaffold template with its library requirements."""
62
+
63
+ name: str
64
+ description: str
65
+ code: str
66
+ requires: frozenset[str] = frozenset()
67
+
68
+
69
+ ALL_TEMPLATES: list[ScaffoldTemplate] = [
70
+ ScaffoldTemplate(
71
+ name="basic_pure_python",
72
+ description="Simple data processing pipeline using only built-in Python types.",
73
+ requires=frozenset(),
74
+ code=textwrap.dedent('''\
75
+ """Basic Hamilton module example (pure Python, no external libraries)."""
76
+
77
+
78
+ def raw_value(raw_value_input: int) -> int:
79
+ """Pass-through for raw input value."""
80
+ return raw_value_input
81
+
82
+
83
+ def doubled(raw_value: int) -> int:
84
+ """Double the value."""
85
+ return raw_value * 2
86
+
87
+
88
+ def message(doubled: int) -> str:
89
+ """Create a summary message."""
90
+ return f"Result: {doubled}"
91
+
92
+
93
+ # --- Driver script ---
94
+ # from hamilton import driver
95
+ # import my_module
96
+ #
97
+ # dr = driver.Builder().with_modules(my_module).build()
98
+ # result = dr.execute(
99
+ # ["message", "doubled"],
100
+ # inputs={"raw_value_input": 5},
101
+ # )
102
+ # print(result)
103
+ '''),
104
+ ),
105
+ ScaffoldTemplate(
106
+ name="basic",
107
+ description="Simple data processing pipeline with pandas.",
108
+ requires=frozenset({"pandas"}),
109
+ code=textwrap.dedent('''\
110
+ """Basic Hamilton module example."""
111
+ import pandas as pd
112
+
113
+
114
+ def raw_data(raw_data_input: pd.DataFrame) -> pd.DataFrame:
115
+ """Pass-through for raw input data."""
116
+ return raw_data_input
117
+
118
+
119
+ def cleaned(raw_data: pd.DataFrame) -> pd.DataFrame:
120
+ """Drop rows with missing values."""
121
+ return raw_data.dropna()
122
+
123
+
124
+ def row_count(cleaned: pd.DataFrame) -> int:
125
+ """Count rows after cleaning."""
126
+ return len(cleaned)
127
+
128
+
129
+ # --- Driver script ---
130
+ # from hamilton import driver
131
+ # import my_module
132
+ #
133
+ # dr = driver.Builder().with_modules(my_module).build()
134
+ # result = dr.execute(
135
+ # ["row_count", "cleaned"],
136
+ # inputs={"raw_data_input": pd.DataFrame({"a": [1, 2, None], "b": [4, None, 6]})},
137
+ # )
138
+ # print(result)
139
+ '''),
140
+ ),
141
+ ScaffoldTemplate(
142
+ name="parameterized",
143
+ description="Using @parameterize to create multiple nodes from one function.",
144
+ requires=frozenset({"pandas"}),
145
+ code=textwrap.dedent('''\
146
+ """Hamilton module using @parameterize to create multiple nodes."""
147
+ import pandas as pd
148
+
149
+ from hamilton.function_modifiers import parameterize, value
150
+
151
+
152
+ @parameterize(
153
+ weekly_mean={"window": value(7)},
154
+ monthly_mean={"window": value(30)},
155
+ )
156
+ def rolling_mean(time_series: pd.Series, window: int) -> pd.Series:
157
+ """Compute a rolling mean with a given window size."""
158
+ return time_series.rolling(window).mean()
159
+
160
+
161
+ def time_series(time_series_input: pd.Series) -> pd.Series:
162
+ """Pass-through for time series input."""
163
+ return time_series_input
164
+
165
+
166
+ # --- Driver script ---
167
+ # from hamilton import driver
168
+ # import my_module
169
+ #
170
+ # dr = driver.Builder().with_modules(my_module).build()
171
+ # result = dr.execute(
172
+ # ["weekly_mean", "monthly_mean"],
173
+ # inputs={"time_series_input": pd.Series(range(60))},
174
+ # )
175
+ '''),
176
+ ),
177
+ ScaffoldTemplate(
178
+ name="config_based",
179
+ description="Conditional logic with @config.when.",
180
+ requires=frozenset({"pandas"}),
181
+ code=textwrap.dedent('''\
182
+ """Hamilton module using @config.when for conditional logic."""
183
+ import pandas as pd
184
+
185
+ from hamilton.function_modifiers import config
186
+
187
+
188
+ @config.when(env="production")
189
+ def data_source__prod(db_connection_string: str) -> pd.DataFrame:
190
+ """Load data from production database."""
191
+ # In real code: pd.read_sql("SELECT * FROM table", db_connection_string)
192
+ return pd.DataFrame({"value": [1, 2, 3]})
193
+
194
+
195
+ @config.when(env="development")
196
+ def data_source__dev() -> pd.DataFrame:
197
+ """Return sample data for development."""
198
+ return pd.DataFrame({"value": [10, 20, 30]})
199
+
200
+
201
+ def processed(data_source: pd.DataFrame) -> pd.DataFrame:
202
+ """Process the data source."""
203
+ return data_source.assign(doubled=data_source["value"] * 2)
204
+
205
+
206
+ # --- Driver script ---
207
+ # from hamilton import driver
208
+ # import my_module
209
+ #
210
+ # dr = (
211
+ # driver.Builder()
212
+ # .with_modules(my_module)
213
+ # .with_config({"env": "development"})
214
+ # .build()
215
+ # )
216
+ # result = dr.execute(["processed"])
217
+ '''),
218
+ ),
219
+ ScaffoldTemplate(
220
+ name="data_pipeline",
221
+ description="ETL workflow: ingest -> clean -> transform -> aggregate.",
222
+ requires=frozenset({"pandas"}),
223
+ code=textwrap.dedent('''\
224
+ """Hamilton data pipeline: ingest -> clean -> transform -> aggregate."""
225
+ import pandas as pd
226
+
227
+
228
+ def raw_data(raw_data_input: pd.DataFrame) -> pd.DataFrame:
229
+ """Ingest raw data."""
230
+ return raw_data_input
231
+
232
+
233
+ def cleaned_data(raw_data: pd.DataFrame) -> pd.DataFrame:
234
+ """Remove nulls and duplicates."""
235
+ return raw_data.dropna().drop_duplicates()
236
+
237
+
238
+ def spend(cleaned_data: pd.DataFrame) -> pd.Series:
239
+ """Extract the spend column."""
240
+ return cleaned_data["spend"].abs()
241
+
242
+
243
+ def avg_spend(spend: pd.Series) -> float:
244
+ """Average spend across all records."""
245
+ return spend.mean()
246
+
247
+
248
+ def total_spend(spend: pd.Series) -> float:
249
+ """Total spend across all records."""
250
+ return spend.sum()
251
+
252
+
253
+ # --- Driver script ---
254
+ # from hamilton import driver
255
+ # import my_module
256
+ #
257
+ # dr = driver.Builder().with_modules(my_module).build()
258
+ # result = dr.execute(
259
+ # ["avg_spend", "total_spend"],
260
+ # inputs={"raw_data_input": pd.DataFrame({"spend": [-10, 20, -30]})},
261
+ # )
262
+ '''),
263
+ ),
264
+ ScaffoldTemplate(
265
+ name="ml_pipeline",
266
+ description="Feature engineering and train/test split with pandas and numpy.",
267
+ requires=frozenset({"pandas", "numpy"}),
268
+ code=textwrap.dedent('''\
269
+ """Hamilton ML pipeline: features -> train/test split -> model -> metrics."""
270
+ import pandas as pd
271
+ import numpy as np
272
+
273
+
274
+ def feature_matrix(feature_matrix_input: pd.DataFrame) -> pd.DataFrame:
275
+ """Input feature matrix."""
276
+ return feature_matrix_input
277
+
278
+
279
+ def target(target_input: pd.Series) -> pd.Series:
280
+ """Input target variable."""
281
+ return target_input
282
+
283
+
284
+ def train_fraction() -> float:
285
+ """Fraction of data for training."""
286
+ return 0.8
287
+
288
+
289
+ def train_indices(
290
+ feature_matrix: pd.DataFrame, train_fraction: float
291
+ ) -> np.ndarray:
292
+ """Random train indices."""
293
+ n = len(feature_matrix)
294
+ idx = np.arange(n)
295
+ np.random.shuffle(idx)
296
+ return idx[: int(n * train_fraction)]
297
+
298
+
299
+ def test_indices(
300
+ feature_matrix: pd.DataFrame, train_indices: np.ndarray
301
+ ) -> np.ndarray:
302
+ """Test indices (complement of train)."""
303
+ all_idx = set(range(len(feature_matrix)))
304
+ return np.array(sorted(all_idx - set(train_indices)))
305
+
306
+
307
+ def train_X(feature_matrix: pd.DataFrame, train_indices: np.ndarray) -> pd.DataFrame:
308
+ """Training features."""
309
+ return feature_matrix.iloc[train_indices]
310
+
311
+
312
+ def test_X(feature_matrix: pd.DataFrame, test_indices: np.ndarray) -> pd.DataFrame:
313
+ """Test features."""
314
+ return feature_matrix.iloc[test_indices]
315
+
316
+
317
+ def train_y(target: pd.Series, train_indices: np.ndarray) -> pd.Series:
318
+ """Training target."""
319
+ return target.iloc[train_indices]
320
+
321
+
322
+ def test_y(target: pd.Series, test_indices: np.ndarray) -> pd.Series:
323
+ """Test target."""
324
+ return target.iloc[test_indices]
325
+
326
+
327
+ # --- Driver script ---
328
+ # from hamilton import driver
329
+ # import my_module
330
+ #
331
+ # dr = driver.Builder().with_modules(my_module).build()
332
+ # result = dr.execute(
333
+ # ["train_X", "test_X", "train_y", "test_y"],
334
+ # inputs={
335
+ # "feature_matrix_input": pd.DataFrame({"a": range(100), "b": range(100)}),
336
+ # "target_input": pd.Series(range(100)),
337
+ # },
338
+ # )
339
+ '''),
340
+ ),
341
+ ScaffoldTemplate(
342
+ name="data_quality",
343
+ description="Data validation with @check_output.",
344
+ requires=frozenset({"pandas", "numpy"}),
345
+ code=textwrap.dedent('''\
346
+ """Hamilton module with data quality checks using @check_output."""
347
+ import pandas as pd
348
+ import numpy as np
349
+
350
+ from hamilton.function_modifiers import check_output
351
+
352
+
353
+ @check_output(
354
+ data_type=np.float64,
355
+ range=(0, None),
356
+ )
357
+ def spend(spend_raw: pd.Series) -> pd.Series:
358
+ """Clean spend: ensure non-negative floats."""
359
+ return spend_raw.abs().astype(float)
360
+
361
+
362
+ @check_output(
363
+ data_type=np.float64,
364
+ )
365
+ def revenue(revenue_raw: pd.Series) -> pd.Series:
366
+ """Clean revenue data."""
367
+ return revenue_raw.astype(float)
368
+
369
+
370
+ def profit(revenue: pd.Series, spend: pd.Series) -> pd.Series:
371
+ """Profit = revenue - spend."""
372
+ return revenue - spend
373
+
374
+
375
+ # --- Driver script ---
376
+ # from hamilton import driver
377
+ # import my_module
378
+ #
379
+ # dr = driver.Builder().with_modules(my_module).build()
380
+ # result = dr.execute(
381
+ # ["profit"],
382
+ # inputs={
383
+ # "spend_raw": pd.Series([10, 20, 30]),
384
+ # "revenue_raw": pd.Series([100, 200, 300]),
385
+ # },
386
+ # )
387
+ '''),
388
+ ),
389
+ ]
390
+
391
+
392
+ def get_available_templates(
393
+ preferred_libraries: set[str] | None = None,
394
+ ) -> dict[str, ScaffoldTemplate]:
395
+ """Return templates filtered by library availability.
396
+
397
+ If *preferred_libraries* is provided, only templates whose requirements
398
+ are a subset of the preferred set are returned. Otherwise, falls back
399
+ to auto-detection via ``AVAILABLE_LIBS``.
400
+ """
401
+ if preferred_libraries is not None:
402
+ return {t.name: t for t in ALL_TEMPLATES if t.requires <= preferred_libraries}
403
+ return {
404
+ t.name: t
405
+ for t in ALL_TEMPLATES
406
+ if all(AVAILABLE_LIBS.get(lib, False) for lib in t.requires)
407
+ }
408
+
409
+
410
+ def get_capabilities(
411
+ preferred_libraries: set[str] | None = None,
412
+ ) -> dict:
413
+ """Return capabilities, optionally filtered by user-specified libraries."""
414
+ return {
415
+ "libraries": dict(AVAILABLE_LIBS),
416
+ "available_scaffolds": sorted(get_available_templates(preferred_libraries).keys()),
417
+ }