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,289 @@
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 abc
19
+ import asyncio
20
+ from collections import defaultdict
21
+ from collections.abc import Callable, Collection
22
+ from typing import Any
23
+
24
+ from hamilton import node
25
+ from hamilton.data_quality import base as dq_base
26
+ from hamilton.data_quality import default_validators
27
+ from hamilton.function_modifiers import base
28
+
29
+ """Decorators that validate artifacts of a node"""
30
+
31
+ IS_DATA_VALIDATOR_TAG = "hamilton.data_quality.contains_dq_results"
32
+ DATA_VALIDATOR_ORIGINAL_OUTPUT_TAG = "hamilton.data_quality.source_node"
33
+
34
+
35
+ class BaseDataValidationDecorator(base.NodeTransformer):
36
+ @abc.abstractmethod
37
+ def get_validators(self, node_to_validate: node.Node) -> list[dq_base.DataValidator]:
38
+ """Returns a list of validators used to transform the nodes.
39
+
40
+ :param node_to_validate: Nodes to which the output of the validator will apply
41
+ :return: A list of validators to apply to the node.
42
+ """
43
+ pass
44
+
45
+ def transform_node(
46
+ self, node_: node.Node, config: dict[str, Any], fn: Callable
47
+ ) -> Collection[node.Node]:
48
+ raw_node = node.Node(
49
+ name=node_.name
50
+ + "_raw", # TODO -- make this unique -- this will break with multiple validation decorators, which we *don't* want
51
+ typ=node_.type,
52
+ doc_string=node_.documentation,
53
+ callabl=node_.callable,
54
+ node_source=node_.node_role,
55
+ input_types=node_.input_types,
56
+ tags=node_.tags,
57
+ )
58
+ validators = self.get_validators(node_)
59
+ validator_nodes = []
60
+ validator_name_map = {}
61
+ validator_name_count = defaultdict(int)
62
+ for validator in validators:
63
+ if dq_base.is_async_validator(validator):
64
+
65
+ async def validation_function(
66
+ validator_to_call: dq_base.DataValidator = validator, **kwargs
67
+ ):
68
+ result = list(kwargs.values())[0] # This should just have one kwarg
69
+ return await validator_to_call.validate(result)
70
+
71
+ else:
72
+
73
+ def validation_function(
74
+ validator_to_call: dq_base.DataValidator = validator, **kwargs
75
+ ):
76
+ result = list(kwargs.values())[0] # This should just have one kwarg
77
+ validation_result = validator_to_call.validate(result)
78
+ if asyncio.iscoroutine(validation_result):
79
+ validation_result.close()
80
+ raise TypeError(
81
+ f"Validator '{validator_to_call.name()}' returned a coroutine. "
82
+ f"Use AsyncDriver for async validators."
83
+ )
84
+ return validation_result
85
+
86
+ validator_node_name = node_.name + "_" + validator.name()
87
+ validator_name_count[validator_node_name] = (
88
+ validator_name_count[validator_node_name] + 1
89
+ )
90
+ if validator_name_count[validator_node_name] > 1:
91
+ validator_node_name = (
92
+ validator_node_name + "_" + str(validator_name_count[validator_node_name] - 1)
93
+ )
94
+ validator_node = node.Node(
95
+ name=validator_node_name, # TODO -- determine a good approach towards naming this
96
+ typ=dq_base.ValidationResult,
97
+ doc_string=validator.description(),
98
+ callabl=validation_function,
99
+ node_source=node.NodeType.STANDARD,
100
+ input_types={raw_node.name: (node_.type, node.DependencyType.REQUIRED)},
101
+ tags={
102
+ **node_.tags,
103
+ **{
104
+ IS_DATA_VALIDATOR_TAG: True,
105
+ DATA_VALIDATOR_ORIGINAL_OUTPUT_TAG: node_.name,
106
+ },
107
+ },
108
+ )
109
+ validator_name_map[validator_node_name] = validator
110
+ validator_nodes.append(validator_node)
111
+
112
+ def final_node_callable(
113
+ validator_nodes=validator_nodes, validator_name_map=validator_name_map, **kwargs
114
+ ):
115
+ """Callable for the final node. First calls the action on every node, then
116
+
117
+ :param validator_nodes:
118
+ :param validator_name_map:
119
+ :param kwargs:
120
+ :return: returns the original node output
121
+ """
122
+ failures = []
123
+ for validator_node in validator_nodes:
124
+ validator: dq_base.DataValidator = validator_name_map[validator_node.name]
125
+ validation_result: dq_base.ValidationResult = kwargs[validator_node.name]
126
+ if validator.importance == dq_base.DataValidationLevel.WARN:
127
+ dq_base.act_warn(node_.name, validation_result, validator)
128
+ else:
129
+ failures.append((validation_result, validator))
130
+ dq_base.act_fail_bulk(node_.name, failures)
131
+ return kwargs[raw_node.name]
132
+
133
+ final_node = node.Node(
134
+ name=node_.name,
135
+ typ=node_.type,
136
+ doc_string=node_.documentation,
137
+ callabl=final_node_callable,
138
+ node_source=node_.node_role,
139
+ input_types={
140
+ raw_node.name: (node_.type, node.DependencyType.REQUIRED),
141
+ **{
142
+ validator_node.name: (validator_node.type, node.DependencyType.REQUIRED)
143
+ for validator_node in validator_nodes
144
+ },
145
+ },
146
+ tags=node_.tags,
147
+ )
148
+ return [*validator_nodes, final_node, raw_node]
149
+
150
+ def validate(self, fn: Callable):
151
+ pass
152
+
153
+
154
+ class check_output_custom(BaseDataValidationDecorator):
155
+ """Class to use if you want to implement your own custom validators.
156
+
157
+ Come chat to us in slack if you're interested in this!
158
+ """
159
+
160
+ def __init__(self, *validators: dq_base.DataValidator, target_: base.TargetType = None):
161
+ """Creates a check_output_custom decorator. This allows passing of custom validators that implement the \
162
+ DataValidator interface.
163
+
164
+ :param validators: Validator to use.
165
+ :param target\\_: The nodes to check the output of. For more detail read the docs in\
166
+ function_modifiers.base.NodeTransformer, but your options are:
167
+
168
+ 1. **None**: This will check just the "final node" (the node that is returned by the decorated function).
169
+ 2. **... (Ellipsis)**: This will check all nodes in the subDAG created by this.
170
+ 3. **string**: This will check the node with the given name.
171
+ 4. **Collection[str]**: This will check all nodes specified in the list.
172
+
173
+ In all likelihood, you *don't* want ``...``, but the others are useful.
174
+
175
+ Note: you cannot stack `@check_output_custom` decorators. If you want to use multiple custom validators, \
176
+ you should pass them all in as arguments to a single `@check_output_custom` decorator.
177
+
178
+ """
179
+ super(check_output_custom, self).__init__(target=target_)
180
+ self.validators = list(validators)
181
+
182
+ def get_validators(self, node_to_validate: node.Node) -> list[dq_base.DataValidator]:
183
+ return self.validators
184
+
185
+
186
+ class check_output(BaseDataValidationDecorator):
187
+ """The ``@check_output`` decorator enables you to add simple data quality checks to your code.
188
+
189
+ For example:
190
+
191
+ .. code-block:: python
192
+
193
+ import pandas as pd
194
+ import numpy as np
195
+ from hamilton.function_modifiers import check_output
196
+
197
+ @check_output(
198
+ data_type=np.int64,
199
+ data_in_range=(0,100),
200
+ importance="warn",
201
+ )
202
+ def some_int_data_between_0_and_100() -> pd.Series:
203
+ ...
204
+
205
+ The check_output decorator takes in arguments that each correspond to one of the default validators. These \
206
+ arguments tell it to add the default validator to the list. The above thus creates two validators, one that checks \
207
+ the datatype of the series, and one that checks whether the data is in a certain range.
208
+
209
+ Pandera example that shows how to use the check_output decorator with a Pandera schema:
210
+
211
+ .. code-block:: python
212
+
213
+ import pandas as pd
214
+ import pandera as pa
215
+ from hamilton.function_modifiers import check_output
216
+ from hamilton.function_modifiers import extract_columns
217
+
218
+ schema = pa.DataFrameSchema(...)
219
+
220
+ @extract_columns('col1', 'col2')
221
+ @check_output(schema=schema, target_="builds_dataframe", importance="fail")
222
+ def builds_dataframe(...) -> pd.DataFrame:
223
+ ...
224
+
225
+ """
226
+
227
+ def get_validators(self, node_to_validate: node.Node) -> list[dq_base.DataValidator]:
228
+ try:
229
+ return default_validators.resolve_default_validators(
230
+ node_to_validate.type,
231
+ importance=self.importance,
232
+ available_validators=self.default_validator_candidates,
233
+ **self.default_validator_kwargs,
234
+ )
235
+ except ValueError as e:
236
+ raise ValueError(
237
+ f"Could not resolve validators for @check_output for function [{node_to_validate.name}]. "
238
+ f"Please check that `target_` is set correctly if you're using that argument.\n"
239
+ f"Actual error: {e}"
240
+ ) from e
241
+
242
+ def __init__(
243
+ self,
244
+ importance: str = dq_base.DataValidationLevel.WARN.value,
245
+ default_validator_candidates: list[type[dq_base.BaseDefaultValidator]] = None,
246
+ target_: base.TargetType = None,
247
+ **default_validator_kwargs: Any,
248
+ ):
249
+ """Creates the check_output validator.
250
+
251
+ This constructs the default validator class.
252
+
253
+ Note: that this creates a whole set of default validators.
254
+ TODO -- enable construction of custom validators using check_output.custom(\\*validators).
255
+
256
+ :param importance: For the default validator, how important is it that this passes.
257
+ :param default_validator_candidates: List of validators to be considerred for this check.
258
+ :param default_validator_kwargs: keyword arguments to be passed to the validator.
259
+ :param target\\_: a target specifying which nodes to decorate. See the docs in check_output_custom\
260
+ for a quick overview and the docs in function_modifiers.base.NodeTransformer for more detail.
261
+ """
262
+ super(check_output, self).__init__(target=target_)
263
+ self.importance = importance
264
+ self.default_validator_kwargs = default_validator_kwargs
265
+ self.default_validator_candidates = default_validator_candidates
266
+ # We need to wait until we actually have the function in order to construct the validators
267
+ # So, we'll just store the constructor arguments for now and check it in validation
268
+
269
+ @staticmethod
270
+ def _validate_constructor_args(
271
+ *validator: dq_base.DataValidator, importance: str = None, **default_validator_kwargs: Any
272
+ ):
273
+ if len(validator) != 0:
274
+ if importance is not None or len(default_validator_kwargs) > 0:
275
+ raise ValueError(
276
+ "Can provide *either* a list of custom validators or arguments for the default validator. "
277
+ "Instead received both."
278
+ )
279
+ else:
280
+ if importance is None:
281
+ raise ValueError("Must supply an importance level if using the default validator.")
282
+
283
+ def validate(self, fn: Callable):
284
+ """Validates that the check_output node works on the function on which it was called
285
+
286
+ :param fn: Function to validate
287
+ :raises: InvalidDecoratorException if the decorator is not valid for the function's return type
288
+ """
289
+ pass
@@ -0,0 +1,31 @@
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
+ # Quick hack to make this a backwards compatible refactor
19
+ # This allows us to define everything within the function_modifiers directory, and just refer to that
20
+ # While maintaining old imports
21
+ import logging
22
+
23
+ from hamilton.function_modifiers.base import * # noqa F403
24
+
25
+ logger = logging.getLogger(__name__)
26
+ logger.warning(
27
+ "Import of this module is deprecated, and will be removed in a 2.0 release. In fact, "
28
+ "this is not a public-facing API, so if you're hitting this message either we're internally "
29
+ "importing the wrong one or you're doing something fancy. Either way, \n"
30
+ "please replace with `from hamilton.function_modifiers import base as fm_base`."
31
+ )