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,242 @@
|
|
|
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 functools
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
import time
|
|
23
|
+
import typing
|
|
24
|
+
|
|
25
|
+
import ray
|
|
26
|
+
|
|
27
|
+
from hamilton import base, htypes, lifecycle, node
|
|
28
|
+
from hamilton.execution import executors
|
|
29
|
+
from hamilton.execution.executors import TaskFuture
|
|
30
|
+
from hamilton.execution.grouping import TaskImplementation
|
|
31
|
+
from hamilton.function_modifiers.metadata import RAY_REMOTE_TAG_NAMESPACE
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def raify(fn):
|
|
37
|
+
"""Makes the function into something ray-friendly.
|
|
38
|
+
This is necessary due to https://github.com/ray-project/ray/issues/28146.
|
|
39
|
+
|
|
40
|
+
:param fn: Function to make ray-friendly
|
|
41
|
+
:return: The ray-friendly version
|
|
42
|
+
"""
|
|
43
|
+
if isinstance(fn, functools.partial):
|
|
44
|
+
|
|
45
|
+
def new_fn(*args, **kwargs):
|
|
46
|
+
return fn(*args, **kwargs)
|
|
47
|
+
|
|
48
|
+
return new_fn
|
|
49
|
+
return fn
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def parse_ray_remote_options_from_tags(tags: dict[str, str]) -> dict[str, typing.Any]:
|
|
53
|
+
"""DRY helper to parse ray.remote(**options) from Hamilton Tags
|
|
54
|
+
|
|
55
|
+
Tags are added to nodes via the @ray_remote_options decorator
|
|
56
|
+
|
|
57
|
+
:param tags: Full set of Tags for a Node
|
|
58
|
+
:return: The ray-friendly version
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
ray_tags = {
|
|
62
|
+
tag_name: tag_value
|
|
63
|
+
for tag_name, tag_value in tags.items()
|
|
64
|
+
if tag_name.startswith(f"{RAY_REMOTE_TAG_NAMESPACE}.")
|
|
65
|
+
}
|
|
66
|
+
ray_options = {name.split(".", 1)[1]: json.loads(value) for name, value in ray_tags.items()}
|
|
67
|
+
|
|
68
|
+
return ray_options
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class RayGraphAdapter(
|
|
72
|
+
lifecycle.base.BaseDoRemoteExecute,
|
|
73
|
+
lifecycle.base.BaseDoBuildResult,
|
|
74
|
+
lifecycle.base.BaseDoValidateInput,
|
|
75
|
+
lifecycle.base.BaseDoCheckEdgeTypesMatch,
|
|
76
|
+
lifecycle.base.BasePostGraphExecute,
|
|
77
|
+
abc.ABC,
|
|
78
|
+
):
|
|
79
|
+
"""Class representing what's required to make Hamilton run on Ray.
|
|
80
|
+
|
|
81
|
+
This walks the graph and translates it to run onto `Ray <https://ray.io/>`__.
|
|
82
|
+
|
|
83
|
+
Use `pip install sf-hamilton[ray]` to get the dependencies required to run this.
|
|
84
|
+
|
|
85
|
+
Use this if:
|
|
86
|
+
|
|
87
|
+
* you want to utilize multiple cores on a single machine, or you want to scale to larger data set sizes with\
|
|
88
|
+
a Ray cluster that you can connect to. Note (1): you are still constrained by machine memory size with Ray; you\
|
|
89
|
+
can't just scale to any dataset size. Note (2): serialization costs can outweigh the benefits of parallelism \
|
|
90
|
+
so you should benchmark your code to see if it's worth it.
|
|
91
|
+
|
|
92
|
+
Notes on scaling:
|
|
93
|
+
-----------------
|
|
94
|
+
- Multi-core on single machine ✅
|
|
95
|
+
- Distributed computation on a Ray cluster ✅
|
|
96
|
+
- Scales to any size of data ⛔️; you are LIMITED by the memory on the instance/computer 💻.
|
|
97
|
+
|
|
98
|
+
Function return object types supported:
|
|
99
|
+
---------------------------------------
|
|
100
|
+
- Works for any python object that can be serialized by the Ray framework. ✅
|
|
101
|
+
|
|
102
|
+
Pandas?
|
|
103
|
+
--------
|
|
104
|
+
- ⛔️ Ray DOES NOT do anything special about Pandas.
|
|
105
|
+
|
|
106
|
+
CAVEATS
|
|
107
|
+
-------
|
|
108
|
+
- Serialization costs can outweigh the benefits of parallelism, so you should benchmark your code to see if it's\
|
|
109
|
+
worth it.
|
|
110
|
+
|
|
111
|
+
DISCLAIMER -- this class is experimental, so signature changes are a possibility!
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
result_builder: base.ResultMixin,
|
|
117
|
+
ray_init_config: dict[str, typing.Any] = None,
|
|
118
|
+
shutdown_ray_on_completion: bool = False,
|
|
119
|
+
):
|
|
120
|
+
"""Constructor
|
|
121
|
+
|
|
122
|
+
You have the ability to pass in a ResultMixin object to the constructor to control the return type that gets \
|
|
123
|
+
produce by running on Ray.
|
|
124
|
+
|
|
125
|
+
:param result_builder: Required. An implementation of base.ResultMixin.
|
|
126
|
+
:param ray_init_config: allows to connect to an existing cluster or start a new one with custom configuration (https://docs.ray.io/en/latest/ray-core/api/doc/ray.init.html)
|
|
127
|
+
:param shutdown_ray_on_completion: by default we leave the cluster open, but we can also shut it down
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
self.result_builder = result_builder
|
|
131
|
+
if not self.result_builder:
|
|
132
|
+
raise ValueError(
|
|
133
|
+
"Error: ResultMixin object required. Please pass one in for `result_builder`."
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
self.shutdown_ray_on_completion = shutdown_ray_on_completion
|
|
137
|
+
|
|
138
|
+
if ray_init_config is not None:
|
|
139
|
+
ray.init(**ray_init_config)
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def do_validate_input(node_type: type, input_value: typing.Any) -> bool:
|
|
143
|
+
# NOTE: the type of a raylet is unknown until they are computed
|
|
144
|
+
if isinstance(input_value, ray._raylet.ObjectRef):
|
|
145
|
+
return True
|
|
146
|
+
return htypes.check_input_type(node_type, input_value)
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def do_check_edge_types_match(type_from: type, type_to: type) -> bool:
|
|
150
|
+
return type_from == type_to
|
|
151
|
+
|
|
152
|
+
def do_remote_execute(
|
|
153
|
+
self,
|
|
154
|
+
*,
|
|
155
|
+
execute_lifecycle_for_node: typing.Callable,
|
|
156
|
+
node: node.Node,
|
|
157
|
+
**kwargs: dict[str, typing.Any],
|
|
158
|
+
) -> typing.Any:
|
|
159
|
+
"""Function that is called as we walk the graph to determine how to execute a hamilton function.
|
|
160
|
+
|
|
161
|
+
:param execute_lifecycle_for_node: wrapper function that executes lifecycle hooks and methods
|
|
162
|
+
:param kwargs: the arguments that should be passed to it.
|
|
163
|
+
:return: returns a ray object reference.
|
|
164
|
+
"""
|
|
165
|
+
ray_options = parse_ray_remote_options_from_tags(node.tags)
|
|
166
|
+
return ray.remote(raify(execute_lifecycle_for_node)).options(**ray_options).remote(**kwargs)
|
|
167
|
+
|
|
168
|
+
def do_build_result(self, outputs: dict[str, typing.Any]) -> typing.Any:
|
|
169
|
+
"""Builds the result and brings it back to this running process.
|
|
170
|
+
|
|
171
|
+
:param outputs: the dictionary of key -> Union[ray object reference | value]
|
|
172
|
+
:return: The type of object returned by self.result_builder.
|
|
173
|
+
"""
|
|
174
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
175
|
+
for k, v in outputs.items():
|
|
176
|
+
logger.debug(f"Got output {k}, with type [{type(v)}].")
|
|
177
|
+
# need to wrap our result builder in a remote call and then pass in what we want to build from.
|
|
178
|
+
remote_combine = ray.remote(self.result_builder.build_result).remote(**outputs)
|
|
179
|
+
result = ray.get(remote_combine) # this materializes the object locally
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
def post_graph_execute(self, *args, **kwargs):
|
|
183
|
+
"""We have the option to close the cluster down after execution."""
|
|
184
|
+
|
|
185
|
+
if self.shutdown_ray_on_completion:
|
|
186
|
+
# In case we have Hamilton Tracker to have enough time to properly flush
|
|
187
|
+
time.sleep(5)
|
|
188
|
+
ray.shutdown()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class RayTaskExecutor(executors.TaskExecutor):
|
|
192
|
+
"""Task executor using Ray for the new task-based execution mechanism in Hamilton.
|
|
193
|
+
This is still experimental, so the API might change.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
def __init__(
|
|
197
|
+
self,
|
|
198
|
+
num_cpus: int = None,
|
|
199
|
+
ray_init_config: dict[str, typing.Any] = None,
|
|
200
|
+
skip_init: bool = False,
|
|
201
|
+
):
|
|
202
|
+
"""Creates a ray task executor. Note this will likely take in more parameters. This is
|
|
203
|
+
experimental, so the API will likely change, although we will do our best to make it
|
|
204
|
+
backwards compatible.
|
|
205
|
+
|
|
206
|
+
:param num_cpus: Number of cores to use for initialization, passed directly to ray.init. Defaults to all cores.
|
|
207
|
+
:param ray_init_config: General configuration to pass to ray.init. Defaults to None.
|
|
208
|
+
:param skip_init: Skips ray init if you already have Ray initialized. Default is False.
|
|
209
|
+
"""
|
|
210
|
+
self.num_cpus = num_cpus
|
|
211
|
+
self.ray_init_config = ray_init_config or {}
|
|
212
|
+
self.skip_init = skip_init
|
|
213
|
+
|
|
214
|
+
def init(self):
|
|
215
|
+
if self.skip_init:
|
|
216
|
+
return
|
|
217
|
+
ray.init(num_cpus=self.num_cpus, **self.ray_init_config)
|
|
218
|
+
|
|
219
|
+
def finalize(self):
|
|
220
|
+
if self.skip_init:
|
|
221
|
+
# we assume that if we didn't init it, we don't need to shutdown either.
|
|
222
|
+
return
|
|
223
|
+
ray.shutdown()
|
|
224
|
+
|
|
225
|
+
def submit_task(self, task: TaskImplementation) -> TaskFuture:
|
|
226
|
+
"""Submits a task, wrapping it in a TaskFuture (after getting the corresponding python
|
|
227
|
+
future).
|
|
228
|
+
|
|
229
|
+
:param task: Task to wrap
|
|
230
|
+
:return: A future
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
return executors.TaskFutureWrappingPythonFuture(
|
|
234
|
+
ray.remote(executors.base_execute_task).remote(task=task).future()
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
def can_submit_task(self) -> bool:
|
|
238
|
+
"""For now we can always submit a task -- it might just be delayed.
|
|
239
|
+
|
|
240
|
+
:return: True
|
|
241
|
+
"""
|
|
242
|
+
return True
|
|
@@ -0,0 +1,142 @@
|
|
|
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 collections.abc import Collection
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
import rich.progress
|
|
22
|
+
|
|
23
|
+
from hamilton.execution.grouping import NodeGroupPurpose
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
from typing import override
|
|
27
|
+
except ImportError:
|
|
28
|
+
override = lambda x: x # noqa E731
|
|
29
|
+
|
|
30
|
+
import rich
|
|
31
|
+
from rich.progress import Progress
|
|
32
|
+
|
|
33
|
+
from hamilton.lifecycle import (
|
|
34
|
+
GraphExecutionHook,
|
|
35
|
+
NodeExecutionHook,
|
|
36
|
+
TaskExecutionHook,
|
|
37
|
+
TaskGroupingHook,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class RichProgressBar(TaskExecutionHook, TaskGroupingHook, GraphExecutionHook, NodeExecutionHook):
|
|
42
|
+
"""An adapter that uses rich to show simple progress bars for the graph execution.
|
|
43
|
+
|
|
44
|
+
Note: you need to have rich installed for this to work. If you don't have it installed, you can
|
|
45
|
+
install it with `pip install rich` (or `pip install sf-hamilton[rich]` -- use quotes if you're
|
|
46
|
+
using zsh).
|
|
47
|
+
|
|
48
|
+
.. code-block:: python
|
|
49
|
+
|
|
50
|
+
from hamilton import driver
|
|
51
|
+
from hamilton.plugins import h_rich
|
|
52
|
+
|
|
53
|
+
dr = (
|
|
54
|
+
driver.Builder()
|
|
55
|
+
.with_config({})
|
|
56
|
+
.with_modules(some_modules)
|
|
57
|
+
.with_adapters(h_rich.RichProgressBar())
|
|
58
|
+
.build()
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
and then when you call .execute() or .materialize() you'll get a progress bar!
|
|
62
|
+
|
|
63
|
+
Additionally, this progress bar will also work with task-based execution, showing the progress
|
|
64
|
+
of overall execution as well as the tasks within a parallelized group.
|
|
65
|
+
|
|
66
|
+
.. code-block:: python
|
|
67
|
+
|
|
68
|
+
from hamilton import driver
|
|
69
|
+
from hamilton.execution import executors
|
|
70
|
+
from hamilton.plugins import h_rich
|
|
71
|
+
|
|
72
|
+
dr = (
|
|
73
|
+
driver.Builder()
|
|
74
|
+
.with_modules(__main__)
|
|
75
|
+
.enable_dynamic_execution(allow_experimental_mode=True)
|
|
76
|
+
.with_adapters(RichProgressBar())
|
|
77
|
+
.with_local_executor(executors.SynchronousLocalTaskExecutor())
|
|
78
|
+
.with_remote_executor(executors.SynchronousLocalTaskExecutor())
|
|
79
|
+
.build()
|
|
80
|
+
)
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
run_desc: str = "",
|
|
86
|
+
collect_desc: str = "",
|
|
87
|
+
columns: list[str | rich.progress.ProgressColumn] | None = None,
|
|
88
|
+
**kwargs,
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Create a new Rich Progress Bar adapter.
|
|
91
|
+
|
|
92
|
+
:param run_desc: The description to show for the running phase.
|
|
93
|
+
:param collect_desc: The description to show for the collecting phase (if applicable).
|
|
94
|
+
:param columns: Column configuration for the progress bar. See rich docs for more info.
|
|
95
|
+
:param kwargs: Additional kwargs to pass to rich.progress.Progress. See rich docs for more info.
|
|
96
|
+
"""
|
|
97
|
+
self._group_desc = run_desc or "Running:"
|
|
98
|
+
self._expand_desc = collect_desc or "Collecting:"
|
|
99
|
+
columns = columns or []
|
|
100
|
+
self._progress = Progress(*columns, **kwargs)
|
|
101
|
+
self._task_based = False
|
|
102
|
+
|
|
103
|
+
@override
|
|
104
|
+
def run_before_graph_execution(self, *, execution_path: Collection[str], **kwargs: Any):
|
|
105
|
+
self._progress.add_task(self._group_desc, total=len(execution_path))
|
|
106
|
+
self._progress.start()
|
|
107
|
+
|
|
108
|
+
@override
|
|
109
|
+
def run_after_graph_execution(self, **kwargs: Any):
|
|
110
|
+
self._progress.stop() # in case progress thread is lagging
|
|
111
|
+
|
|
112
|
+
@override
|
|
113
|
+
def run_after_task_grouping(self, *, task_ids: list[str], **kwargs):
|
|
114
|
+
# Change the total of the task group to the number of tasks in the group
|
|
115
|
+
self._progress.update(self._progress.task_ids[0], total=len(task_ids))
|
|
116
|
+
self._task_based = True
|
|
117
|
+
|
|
118
|
+
@override
|
|
119
|
+
def run_after_task_expansion(self, *, parameters: dict[str, Any], **kwargs):
|
|
120
|
+
self._progress.add_task(self._expand_desc, total=len(parameters))
|
|
121
|
+
|
|
122
|
+
@override
|
|
123
|
+
def run_before_task_execution(self, *, purpose: NodeGroupPurpose, **kwargs):
|
|
124
|
+
if purpose == NodeGroupPurpose.GATHER:
|
|
125
|
+
self._progress.advance(self._progress.task_ids[0])
|
|
126
|
+
self._progress.stop_task(self._progress.task_ids[-1])
|
|
127
|
+
|
|
128
|
+
@override
|
|
129
|
+
def run_after_task_execution(self, *, purpose: NodeGroupPurpose, **kwargs):
|
|
130
|
+
if purpose == NodeGroupPurpose.EXECUTE_BLOCK:
|
|
131
|
+
self._progress.advance(self._progress.task_ids[-1])
|
|
132
|
+
else:
|
|
133
|
+
self._progress.advance(self._progress.task_ids[0])
|
|
134
|
+
|
|
135
|
+
@override
|
|
136
|
+
def run_before_node_execution(self, **kwargs):
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
@override
|
|
140
|
+
def run_after_node_execution(self, **kwargs):
|
|
141
|
+
if not self._task_based:
|
|
142
|
+
self._progress.advance(self._progress.task_ids[0])
|