hwcomponents 1.0.81__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.
@@ -0,0 +1,250 @@
1
+ import glob
2
+ import importlib
3
+ from importlib.machinery import SourceFileLoader
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+ from typing import List, Set, Union
7
+ from hwcomponents._model_wrapper import ComponentModelWrapper, ComponentModel
8
+ import inspect
9
+ import logging
10
+ import copy
11
+ import sys
12
+ import os
13
+ from pkgutil import iter_modules
14
+
15
+ _ALL_ESTIMATORS = None
16
+
17
+
18
+ def installed_models(
19
+ _return_wrappers: bool = False,
20
+ ) -> List[ComponentModelWrapper] | List[ComponentModel]:
21
+ """
22
+ Lists all Python packages installed that are prefixed with "hwcomponents_". Finds
23
+ ComponentModel subclasses in these packages and returns them as
24
+ ComponentModel or ComponentModelWrapper objects.
25
+
26
+ Parameters
27
+ ----------
28
+ _return_wrappers : bool
29
+ Whether to return ComponentModelWrapper objects or
30
+ ComponentModel objects.
31
+
32
+ Returns
33
+ -------
34
+ A list of ComponentModel or ComponentModelWrapper objects.
35
+ """
36
+ # List all Python packages installed that are prefixed with "hwcomponents_"
37
+ global _ALL_ESTIMATORS
38
+ if _ALL_ESTIMATORS is not None:
39
+ return _ALL_ESTIMATORS
40
+
41
+ modules = [p.name for p in iter_modules() if p.name.startswith("hwcomponents_")]
42
+ for m in modules:
43
+ logging.info(f"Importing from module: {m}")
44
+
45
+ models = []
46
+ model_ids = set()
47
+
48
+ # Handle the packages
49
+ for module in modules:
50
+ models.extend(
51
+ get_models_in_module(
52
+ importlib.import_module(module),
53
+ model_ids,
54
+ _return_wrappers,
55
+ )
56
+ )
57
+
58
+ _ALL_ESTIMATORS = models
59
+
60
+ return models
61
+
62
+
63
+ def get_models_in_module(
64
+ module: ModuleType,
65
+ model_ids: Set,
66
+ _return_wrappers: bool = False,
67
+ ) -> List[ComponentModelWrapper] | List[ComponentModel]:
68
+ """
69
+ Finds all ComponentModel subclasses in a module and returns them as
70
+ ComponentModelWrapper objects. Ignores underscore-prefixed classes.
71
+
72
+ Parameters
73
+ ----------
74
+ model_ids : set
75
+ A set of model IDs to avoid duplicates.
76
+ _return_wrappers : bool
77
+ Whether to return ComponentModelWrapper objects or ComponentModel objects.
78
+
79
+ Returns
80
+ -------
81
+ A list of ComponentModelWrapper objects.
82
+
83
+ """
84
+ logging.info(f"Getting models in module: {module.__name__}")
85
+ classes = [
86
+ (x, name) for name in dir(module) if inspect.isclass(x := getattr(module, name))
87
+ ]
88
+ classes = [(x, name) for x, name in classes if not name.startswith("_")]
89
+ found = []
90
+ for x, name in classes:
91
+ superclasses = [c.__name__ for c in inspect.getmro(x)]
92
+
93
+ if (
94
+ any(base in superclasses for base in ["ComponentModel", "Model"])
95
+ and not inspect.isabstract(x)
96
+ and id(x) not in model_ids
97
+ ):
98
+ model_ids.add(id(x))
99
+ if _return_wrappers:
100
+ found.append(ComponentModelWrapper(x, name))
101
+ else:
102
+ found.append(x)
103
+ return found
104
+
105
+
106
+ def get_models(
107
+ *paths_or_packages_or_models: Union[
108
+ str, List[str], List[List[str]], ComponentModel
109
+ ],
110
+ include_installed: bool = True,
111
+ name_must_include: str = "",
112
+ _return_wrappers: bool = False,
113
+ ) -> List[ComponentModelWrapper] | List[ComponentModel]:
114
+ """
115
+ Instantiate a list of model objects for later queries. Searches for models in the
116
+ given paths and packages.
117
+
118
+ Parameters
119
+ ----------
120
+ paths_or_packages_or_models : list
121
+ A list of paths or packages to search for models.
122
+ include_installed : bool
123
+ Whether to include models from installed packages.
124
+ name_must_include : str
125
+ If provided, a model will only be returned if its name includes this string.
126
+ Non-case-sensitive.
127
+ _return_wrappers : bool
128
+ Whether to return ComponentModelWrapper objects or
129
+ ComponentModel objects.
130
+
131
+ Returns
132
+ -------
133
+ A list of ComponentModelWrapper objects or ComponentModel objects.
134
+ """
135
+ model_ids = set()
136
+ n_models = 0
137
+
138
+ packages = []
139
+ paths = []
140
+ models = []
141
+
142
+ flattened = []
143
+
144
+ to_check = list(paths_or_packages_or_models)
145
+
146
+ i = 0
147
+ while i < len(to_check):
148
+ path_or_package = to_check[i]
149
+ i += 1
150
+ if isinstance(path_or_package, (list, tuple)):
151
+ to_check.extend(path_or_package)
152
+ elif isinstance(path_or_package, type) and issubclass(
153
+ path_or_package, ComponentModel
154
+ ):
155
+ models.append(path_or_package)
156
+ elif isinstance(path_or_package, (str, Path)):
157
+ globbed = glob.glob(path_or_package, recursive=True)
158
+ flattened.extend(globbed)
159
+ else:
160
+ raise ValueError(f"Invalid type: {type(path_or_package)}")
161
+
162
+ if _return_wrappers:
163
+ models = [ComponentModelWrapper(m, m.__name__) for m in models]
164
+
165
+ models.extend(installed_models(_return_wrappers) if include_installed else [])
166
+
167
+ for path_or_package in flattened:
168
+ # Check if it's a package first
169
+ try:
170
+ importlib.import_module(path_or_package)
171
+ packages.append(path_or_package)
172
+ except (ImportError, TypeError):
173
+ # If not, check if it's a file
174
+ if os.path.isfile(path_or_package):
175
+ assert path_or_package.endswith(
176
+ ".py"
177
+ ), f"Path {path_or_package} is not a Python file"
178
+ paths.append(path_or_package)
179
+ else:
180
+ raise ValueError(
181
+ f"Path {path_or_package} is not a valid file or package"
182
+ )
183
+
184
+ for package in packages:
185
+ models.extend(
186
+ get_models_in_module(
187
+ importlib.import_module(package),
188
+ model_ids,
189
+ _return_wrappers,
190
+ )
191
+ )
192
+
193
+ # Handle the paths
194
+ paths_globbed = []
195
+ allpaths = []
196
+ for p in paths:
197
+ if isinstance(p, list):
198
+ allpaths.extend(p)
199
+ else:
200
+ allpaths.append(p)
201
+
202
+ for p in allpaths:
203
+ logging.info(f"Checking path: {p}")
204
+ newpaths = []
205
+ if os.path.isfile(p):
206
+ assert p.endswith(".py"), f"Path {p} is not a Python file"
207
+ newpaths.append(p)
208
+ else:
209
+ newpaths += list(glob.glob(p, recursive=True))
210
+ newpaths += list(glob.glob(os.path.join(p, "**"), recursive=True))
211
+ paths_globbed.extend(newpaths)
212
+ if not newpaths:
213
+ raise ValueError(
214
+ f"Path {p} does not have any Python files. Please check the path and "
215
+ f"try again."
216
+ )
217
+
218
+ newpaths = [p.rstrip("/") for p in newpaths]
219
+ newpaths = [p.replace("\\", "/") for p in newpaths]
220
+ newpaths = [p.replace("//", "/") for p in newpaths]
221
+ newpaths = [p for p in newpaths if p.endswith(".py")]
222
+ newpaths = [p for p in newpaths if not p.endswith("setup.py")]
223
+ newpaths = [p for p in newpaths if not p.endswith("__init__.py")]
224
+
225
+ new_models = []
226
+ for path in newpaths:
227
+ logging.info(
228
+ f"Loading models from {path}. Errors below are likely due to the model."
229
+ )
230
+ prev_sys_path = copy.deepcopy(sys.path)
231
+ sys.path.append(os.path.dirname(os.path.abspath(path)))
232
+ python_module = SourceFileLoader(f"model{n_models}", path).load_module()
233
+ new_models += get_models_in_module(
234
+ python_module, model_ids, _return_wrappers
235
+ )
236
+ sys.path = prev_sys_path
237
+ n_models += 1
238
+
239
+ if not new_models:
240
+ raise ValueError(f"No models found in {p}")
241
+
242
+ models.extend(new_models)
243
+
244
+ if _return_wrappers:
245
+ models = [
246
+ m for m in models if name_must_include.lower() in m.model_name.lower()
247
+ ]
248
+ return sorted(models, key=lambda x: x.model_name)
249
+ models = [m for m in models if name_must_include.lower() in m.__name__.lower()]
250
+ return sorted(models, key=lambda x: x.__name__)
@@ -0,0 +1,77 @@
1
+ import argparse
2
+ from hwcomponents.find_models import get_models
3
+
4
+
5
+ def list_components(printfunc=print):
6
+ """
7
+ Lists all available components.
8
+
9
+ Parameters
10
+ ----------
11
+ printfunc : Callable[[str], None]
12
+ The function to use to print the components.
13
+
14
+ Returns
15
+ -------
16
+ None
17
+ """
18
+ printfunc("\n")
19
+ printfunc("Supported Components:")
20
+
21
+ class Entry:
22
+ def __init__(
23
+ self,
24
+ name: str,
25
+ class_name: str,
26
+ init_function: str,
27
+ actions: list[str],
28
+ ):
29
+ self.name = name
30
+ self.class_name = class_name
31
+ self.init_function = init_function
32
+ self.actions = actions
33
+
34
+ def __str__(self):
35
+ return (
36
+ f"{self.class_name}{self.init_function} from class {self.name} \n"
37
+ + "\n".join(f"\t{a}" for a in self.actions)
38
+ )
39
+
40
+ entries = []
41
+ for model in get_models(_return_wrappers=True):
42
+
43
+ def add_entry(name, class_names, init_func, actions):
44
+ class_names = [class_names] if isinstance(class_names, str) else class_names
45
+ entries.append(
46
+ (class_names[0], Entry(name, class_names[0], str(init_func), actions))
47
+ )
48
+ for c in class_names[1:]:
49
+ entries.append(
50
+ (c, f"{c}: alias for {name} from class {class_names[0]}")
51
+ )
52
+
53
+ add_entry(
54
+ model.get_name(),
55
+ model.get_component_names(),
56
+ str(model.init_function),
57
+ model.actions,
58
+ )
59
+
60
+ entries = sorted(entries, key=lambda x: x[0].lower())
61
+ for entry in entries:
62
+ printfunc(entry[1])
63
+
64
+
65
+ def _main():
66
+ parser = argparse.ArgumentParser()
67
+ parser.add_argument(
68
+ "--list", action="store_true", help="List all available components"
69
+ )
70
+ args = parser.parse_args()
71
+
72
+ if args.list:
73
+ list_components()
74
+
75
+
76
+ if __name__ == "__main__":
77
+ _main()