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
hamilton/graph_types.py
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
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
|
+
"""Module for external-facing graph constructs. These help the user navigate/manage the graph as needed."""
|
|
19
|
+
|
|
20
|
+
import ast
|
|
21
|
+
import functools
|
|
22
|
+
import hashlib
|
|
23
|
+
import inspect
|
|
24
|
+
import logging
|
|
25
|
+
import typing
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
|
|
28
|
+
from hamilton import htypes, node
|
|
29
|
+
from hamilton.htypes import get_type_as_string
|
|
30
|
+
|
|
31
|
+
# This is a little ugly -- its just required for graph build, and works
|
|
32
|
+
# This indicates a larger smell though -- we need to have the right level of
|
|
33
|
+
# hierarchy to ensure we don't have to deal with this.
|
|
34
|
+
# The larger problem is that we have a few interfaces that are referred to by
|
|
35
|
+
# The core system (in defaults), and we have not managed to disentangle it yet.
|
|
36
|
+
if typing.TYPE_CHECKING:
|
|
37
|
+
from hamilton import graph
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _remove_docs_and_comments(source: str) -> str:
|
|
43
|
+
"""Remove the docs and comments from a source code string.
|
|
44
|
+
|
|
45
|
+
The use of `ast.unparse()` requires Python 3.9
|
|
46
|
+
|
|
47
|
+
1. Parsing then unparsing the AST of the source code will
|
|
48
|
+
create a code object and convert it back to a string. In the
|
|
49
|
+
process, comments are stripped.
|
|
50
|
+
|
|
51
|
+
2. walk the AST to check if first element after `def` is a
|
|
52
|
+
docstring. If so, edit AST to skip the docstring
|
|
53
|
+
|
|
54
|
+
NOTE. The ast parsing will fail if `source` has syntax errors. For the
|
|
55
|
+
majority of cases this is caught upstream (e.g., by calling `import`).
|
|
56
|
+
The foreseeable edge case is if `source` is the result of `inspect.getsource`
|
|
57
|
+
on a nested function, method, or callable where `def` isn't at column 0.
|
|
58
|
+
Standard usage of Hamilton requires users to define functions/nodes at the top
|
|
59
|
+
level of a module, and therefore no issues should arise.
|
|
60
|
+
"""
|
|
61
|
+
parsed = ast.parse(source)
|
|
62
|
+
for n in ast.walk(parsed):
|
|
63
|
+
if not isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
if not len(n.body):
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
# check if 1st node is a docstring
|
|
70
|
+
if not isinstance(n.body[0], ast.Expr):
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
# In Python 3.8+, string literals (including docstrings) are ast.Constant nodes
|
|
74
|
+
# ast.Str is deprecated and will be removed in Python 3.14
|
|
75
|
+
if not hasattr(n.body[0], "value"):
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
value = n.body[0].value
|
|
79
|
+
is_docstring = isinstance(value, ast.Constant) and isinstance(value.value, str)
|
|
80
|
+
|
|
81
|
+
if not is_docstring:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
# skip docstring
|
|
85
|
+
n.body = n.body[1:]
|
|
86
|
+
|
|
87
|
+
return ast.unparse(parsed)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def hash_source_code(source: str | typing.Callable, strip: bool = False) -> str:
|
|
91
|
+
"""Hashes the source code of a function (str).
|
|
92
|
+
|
|
93
|
+
The `strip` parameter requires Python 3.9
|
|
94
|
+
|
|
95
|
+
If strip, try to remove docs and comments from source code string. Since
|
|
96
|
+
they don't impact function behavior, they shouldn't influence the hash.
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(source, typing.Callable):
|
|
99
|
+
source = inspect.getsource(source)
|
|
100
|
+
|
|
101
|
+
source = source.strip()
|
|
102
|
+
|
|
103
|
+
if strip:
|
|
104
|
+
try:
|
|
105
|
+
# could fail if source is indented code.
|
|
106
|
+
# see `remove_docs_and_comments` docstring for details.
|
|
107
|
+
source = _remove_docs_and_comments(source)
|
|
108
|
+
except Exception:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
return hashlib.sha256(source.encode()).hexdigest()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class HamiltonNode:
|
|
116
|
+
"""External facing API for hamilton Nodes. Having this as a dataclass allows us
|
|
117
|
+
to hide the internals of the system but expose what the user might need.
|
|
118
|
+
Furthermore, we can always add attributes and maintain backwards compatibility."""
|
|
119
|
+
|
|
120
|
+
name: str
|
|
121
|
+
type: type
|
|
122
|
+
tags: dict[str, str | list[str]]
|
|
123
|
+
is_external_input: bool
|
|
124
|
+
originating_functions: tuple[typing.Callable, ...] | None
|
|
125
|
+
documentation: str | None
|
|
126
|
+
required_dependencies: set[str]
|
|
127
|
+
optional_dependencies: set[str]
|
|
128
|
+
optional_dependencies_default_values: dict[str, typing.Any]
|
|
129
|
+
|
|
130
|
+
def as_dict(self, include_optional_dependencies_default_values: bool = False) -> dict:
|
|
131
|
+
"""Create a dictionary representation of the Node that is JSON serializable.
|
|
132
|
+
|
|
133
|
+
:param include_optional_dependencies_default_values: Include optional dependencies default values in the output.
|
|
134
|
+
Note: optional values could be anything and might not be JSON serializable.
|
|
135
|
+
"""
|
|
136
|
+
dict_representation = {
|
|
137
|
+
"name": self.name,
|
|
138
|
+
"tags": self.tags,
|
|
139
|
+
"output_type": (get_type_as_string(self.type) or ""),
|
|
140
|
+
"required_dependencies": sorted(self.required_dependencies),
|
|
141
|
+
"optional_dependencies": sorted(self.optional_dependencies),
|
|
142
|
+
"source": (
|
|
143
|
+
inspect.getsource(self.originating_functions[0])
|
|
144
|
+
if self.originating_functions
|
|
145
|
+
else None
|
|
146
|
+
),
|
|
147
|
+
"documentation": self.documentation,
|
|
148
|
+
"version": self.version,
|
|
149
|
+
}
|
|
150
|
+
if include_optional_dependencies_default_values:
|
|
151
|
+
dict_representation["optional_dependencies_default_values"] = (
|
|
152
|
+
self.optional_dependencies_default_values
|
|
153
|
+
)
|
|
154
|
+
return dict_representation
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def from_node(n: node.Node) -> "HamiltonNode":
|
|
158
|
+
"""Creates a HamiltonNode from a Node (Hamilton's internal representation).
|
|
159
|
+
|
|
160
|
+
:param n: Node to create the Variable from.
|
|
161
|
+
:return: HamiltonNode created from the Node.
|
|
162
|
+
"""
|
|
163
|
+
return HamiltonNode(
|
|
164
|
+
name=n.name,
|
|
165
|
+
type=n.type,
|
|
166
|
+
tags=n.tags,
|
|
167
|
+
is_external_input=n.user_defined,
|
|
168
|
+
originating_functions=n.originating_functions,
|
|
169
|
+
documentation=n.documentation,
|
|
170
|
+
required_dependencies={
|
|
171
|
+
dep
|
|
172
|
+
for dep, (type_, dep_type) in n.input_types.items()
|
|
173
|
+
if dep_type == node.DependencyType.REQUIRED
|
|
174
|
+
},
|
|
175
|
+
optional_dependencies={
|
|
176
|
+
dep
|
|
177
|
+
for dep, (type_, dep_type) in n.input_types.items()
|
|
178
|
+
if dep_type == node.DependencyType.OPTIONAL
|
|
179
|
+
},
|
|
180
|
+
optional_dependencies_default_values={
|
|
181
|
+
name: value for name, value in n.default_parameter_values.items()
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
@functools.cached_property
|
|
186
|
+
def version(self) -> str | None:
|
|
187
|
+
"""Generate a hash of the node originating function source code.
|
|
188
|
+
|
|
189
|
+
Note that this will be `None` if the node is an external input/has no
|
|
190
|
+
originating functions.
|
|
191
|
+
|
|
192
|
+
The option `strip=True` means docstring and comments are ignored
|
|
193
|
+
when hashing the function.
|
|
194
|
+
"""
|
|
195
|
+
if self.originating_functions is None or len(self.originating_functions) == 0:
|
|
196
|
+
if self.is_external_input:
|
|
197
|
+
# return the name of the config node. (we could add type but skipping for now)
|
|
198
|
+
return self.name
|
|
199
|
+
return None # this shouldn't happen often.
|
|
200
|
+
try:
|
|
201
|
+
# return hash of first function. It could be that others are Hamilton framework code.
|
|
202
|
+
return hash_source_code(self.originating_functions[0], strip=True)
|
|
203
|
+
except OSError: # TODO -- ensure we can get the node hash in a databricks environment when using jupyter magic
|
|
204
|
+
logger.warning(
|
|
205
|
+
f"Failed to hash source code for node {self.name}. Certain environments (such as databricks) do not allow it."
|
|
206
|
+
" In this case, version will be None."
|
|
207
|
+
)
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
def __repr__(self):
|
|
211
|
+
return f'Node("{self.name}": {htypes.get_type_as_string(self.type)})'
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@dataclass
|
|
215
|
+
class HamiltonGraph:
|
|
216
|
+
"""External facing API for Hamilton Graphs. Currently a list of nodes that
|
|
217
|
+
allow you to trace forward/backwards in the graph. Will likely be adding some more capabilities:
|
|
218
|
+
1. More metadata -- config + modules
|
|
219
|
+
2. More utility functions -- make it easy to walk/do an action at each node
|
|
220
|
+
For now, you have to implement walking on your own if you care about order.
|
|
221
|
+
|
|
222
|
+
Note that you do not construct this class directly -- instead, you will get this at various points in the API.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
nodes: list[HamiltonNode]
|
|
226
|
+
# store the original graph for internal use
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def from_graph(fn_graph: "graph.FunctionGraph") -> "HamiltonGraph":
|
|
230
|
+
"""Creates a HamiltonGraph from a FunctionGraph (Hamilton's internal representation).
|
|
231
|
+
|
|
232
|
+
:param fn_graph: FunctionGraph to convert
|
|
233
|
+
:return: HamiltonGraph created from the FunctionGraph
|
|
234
|
+
"""
|
|
235
|
+
return HamiltonGraph(
|
|
236
|
+
nodes=[HamiltonNode.from_node(n) for n in fn_graph.nodes.values()],
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
@functools.cached_property
|
|
240
|
+
def version(self) -> str:
|
|
241
|
+
"""Generate a hash of the dataflow based on the collection of node hashes.
|
|
242
|
+
|
|
243
|
+
Node hashes are in a sorted list, then concatenated as a string before hashing.
|
|
244
|
+
To find differences between dataflows, you need to inspect the node level.
|
|
245
|
+
"""
|
|
246
|
+
sorted_node_versions = sorted([n.version for n in self.nodes if n.version is not None])
|
|
247
|
+
return hashlib.sha256(str(sorted_node_versions).encode()).hexdigest()
|
|
248
|
+
|
|
249
|
+
@functools.cached_property
|
|
250
|
+
def __nodes_lookup(self) -> dict[str, HamiltonNode]:
|
|
251
|
+
"""Cache the mapping {node_name: node} for faster `__getitem__`"""
|
|
252
|
+
return {n.name: n for n in self.nodes}
|
|
253
|
+
|
|
254
|
+
def __getitem__(self, key: str) -> HamiltonNode:
|
|
255
|
+
"""Get an HamiltonNode by name
|
|
256
|
+
|
|
257
|
+
:param key: Hamilton node name
|
|
258
|
+
:return: Hamilton node
|
|
259
|
+
"""
|
|
260
|
+
return self.__nodes_lookup[key]
|
|
261
|
+
|
|
262
|
+
def filter_nodes(self, filter: typing.Callable[[HamiltonNode], bool]) -> list[HamiltonNode]:
|
|
263
|
+
"""Return Hamilton nodes matching the filter criteria"""
|
|
264
|
+
return [n for n in self.nodes if filter(n) is True]
|
hamilton/graph_utils.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
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 inspect
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from types import ModuleType
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def is_submodule(child: ModuleType, parent: ModuleType):
|
|
24
|
+
return parent.__name__ in child.__name__
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def find_functions(function_module: ModuleType) -> list[tuple[str, Callable]]:
|
|
28
|
+
"""Function to determine the set of functions we want to build a graph from.
|
|
29
|
+
|
|
30
|
+
This iterates through the function module and grabs all function definitions.
|
|
31
|
+
:return: list of tuples of (func_name, function).
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def valid_fn(fn):
|
|
35
|
+
return (
|
|
36
|
+
inspect.isfunction(fn)
|
|
37
|
+
and not fn.__name__.startswith("_")
|
|
38
|
+
and is_submodule(inspect.getmodule(fn), function_module)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return [f for f in inspect.getmembers(function_module, predicate=valid_fn)]
|