Skip to content

Commit 854e84e

Browse files
committed
add plugin manager
1 parent bdd5e8e commit 854e84e

File tree

1 file changed

+370
-0
lines changed

1 file changed

+370
-0
lines changed
Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
import importlib
2+
from importlib.metadata import entry_points
3+
import importlib.resources
4+
import pkgutil
5+
import os
6+
import inspect
7+
8+
from navigate.tools.file_functions import load_yaml_file
9+
from navigate.tools.common_functions import load_module_from_file
10+
from navigate.model.features import feature_related_functions
11+
12+
def register_features(module):
13+
"""Register features
14+
15+
Parameters
16+
----------
17+
module : module
18+
A python module contains features
19+
"""
20+
for c in dir(module):
21+
if inspect.isclass(getattr(module, c)):
22+
setattr(feature_related_functions, c, getattr(module, c))
23+
24+
class PluginPackageManager:
25+
26+
@staticmethod
27+
def get_plugins():
28+
"""Get plugins
29+
30+
Returns
31+
-------
32+
plugins : dict
33+
plugins dict
34+
"""
35+
plugins = {}
36+
for entry_point in entry_points().get("navigate.plugins", []):
37+
plugin_package_name = entry_point.module.split(".")[0]
38+
if plugin_package_name in plugins:
39+
print(
40+
f"*** Warning: plugin {plugin_package_name} exists!"
41+
"Can't load twice!"
42+
"Please double-check the installed plugins!"
43+
)
44+
continue
45+
plugins[plugin_package_name] = importlib.resources.files(plugin_package_name)
46+
return plugins
47+
48+
@staticmethod
49+
def load_controller(package_name, package_path, controller_name):
50+
"""Load controller
51+
52+
Parameters
53+
----------
54+
package_name : str
55+
package name
56+
package_path : str
57+
package path
58+
controller_name : str
59+
controller name
60+
61+
Returns
62+
-------
63+
controller_class : class
64+
controller class
65+
"""
66+
controller_file_name = "_".join(controller_name.lower().split()) + "_controller"
67+
controller_class_name = "".join(controller_name.title().split()) + "Controller"
68+
try:
69+
controller_module = importlib.import_module(f"{package_name}.controller.{controller_file_name}")
70+
return getattr(controller_module, controller_class_name)
71+
except (ImportError, AttributeError):
72+
return None
73+
74+
@staticmethod
75+
def load_view(package_name, package_path, frame_name):
76+
"""Load view
77+
78+
Parameters
79+
----------
80+
package_name : str
81+
package name
82+
package_path : str
83+
package path
84+
frame_name : str
85+
frame name
86+
87+
Returns
88+
-------
89+
frame_class : class
90+
frame class
91+
"""
92+
frame_file_name = "_".join(frame_name.lower().split()) + "_frame"
93+
frame_class_name = "".join(frame_name.title().split()) + "Frame"
94+
try:
95+
view_module = importlib.import_module(f"{package_name}.view.{frame_file_name}")
96+
return getattr(view_module, frame_class_name)
97+
except (ImportError, AttributeError):
98+
return None
99+
100+
@staticmethod
101+
def load_feature_lists(package_name, package_path, register_func):
102+
"""Load feature lists
103+
104+
Parameters
105+
----------
106+
package_name : str
107+
package name
108+
package_path : str
109+
package path
110+
register_func : func
111+
the function to handle feature lists
112+
"""
113+
try:
114+
module = importlib.import_module(f"{package_name}.feature_list")
115+
register_func(importlib.resources.files(package_name).joinpath("feature_list.py"), module)
116+
except (ImportError, AttributeError):
117+
pass
118+
119+
@staticmethod
120+
def load_features(package_name, package_path):
121+
"""Load features
122+
123+
Parameters
124+
----------
125+
package_name : str
126+
package name
127+
package_path : str
128+
package path
129+
"""
130+
for _, module_name, is_pkg in pkgutil.iter_modules([importlib.resources.files(package_name).joinpath("model/features")]):
131+
if not is_pkg:
132+
full_module_name = f"{package_name}.model.features.{module_name}"
133+
try:
134+
module = importlib.import_module(full_module_name)
135+
except (ImportError, AttributeError):
136+
continue
137+
register_features(module)
138+
139+
@staticmethod
140+
def load_acquisition_modes(package_name, package_path, acquisition_modes, register_func):
141+
"""Load acquisition modes
142+
143+
Parameters
144+
----------
145+
package_name : str
146+
package name
147+
package_path : str
148+
package path
149+
acquisition_modes : []
150+
list of acquisition mode configurations
151+
register_func : func
152+
the function to register acquisition modes
153+
"""
154+
for acquisition_mode_config in acquisition_modes:
155+
acquisition_file = acquisition_mode_config["file_name"][:-3]
156+
try:
157+
module = importlib.import_module(f"{package_name}.{acquisition_file}")
158+
except (ImportError, AttributeError):
159+
continue
160+
if module:
161+
register_func(acquisition_mode_config["name"], module)
162+
163+
@staticmethod
164+
def load_devices(package_name, package_path, register_func):
165+
"""Load devices
166+
167+
Parameters
168+
----------
169+
package_name : str
170+
package name
171+
package_path : str
172+
package path
173+
register_func : func
174+
the function to register devices
175+
"""
176+
for _, module_name, is_pkg in pkgutil.iter_modules([importlib.resources.files(package_name).joinpath("model/devices")]):
177+
if is_pkg:
178+
full_module_name = f"{package_name}.model.devices.{module_name}.device_startup_functions"
179+
try:
180+
module = importlib.import_module(full_module_name)
181+
except (ImportError, AttributeError):
182+
continue
183+
register_func(module_name, module)
184+
185+
186+
class PluginFileManager:
187+
188+
def __init__(self, plugins_path, plugins_config_path):
189+
self.plugins_path = plugins_path
190+
self.plugins_config_path = plugins_config_path
191+
192+
def get_plugins(self):
193+
"""Get plugins
194+
195+
Returns
196+
-------
197+
plugins : dict
198+
plugins dict
199+
"""
200+
plugins = {}
201+
202+
folder_names = os.listdir(self.plugins_path)
203+
full_path_names = dict(
204+
[(f, os.path.join(self.plugins_path, f)) for f in folder_names]
205+
)
206+
# add plugins from plugins_config.yml
207+
plugins_config = load_yaml_file(self.plugins_config_path)
208+
if plugins_config:
209+
full_path_names.update(plugins_config)
210+
211+
for plugin_name, plugin_path in full_path_names.items():
212+
if plugin_path and os.path.exists(plugin_path) and os.path.isdir(plugin_path):
213+
plugins[plugin_name] = plugin_path
214+
215+
return plugins
216+
217+
@staticmethod
218+
def load_controller(plugin_name, plugin_path, controller_name):
219+
"""Load controller
220+
221+
Parameters
222+
----------
223+
plugin_name : str
224+
plugin name
225+
plugin_path : str
226+
plugin path
227+
controller_name : str
228+
controller name
229+
230+
Returns
231+
-------
232+
controller_class : class
233+
controller class
234+
"""
235+
controller_file_name = "_".join(controller_name.lower().split()) + "_controller.py"
236+
controller_class_name = "".join(controller_name.title().split()) + "Controller"
237+
controller_file_path = os.path.join(
238+
plugin_path,
239+
"controller",
240+
controller_file_name
241+
)
242+
if os.path.exists(controller_file_path) and os.path.isfile(controller_file_path):
243+
module = load_module_from_file(
244+
controller_class_name, controller_file_path
245+
)
246+
return getattr(module, controller_class_name)
247+
return None
248+
249+
@staticmethod
250+
def load_view(plugin_name, plugin_path, frame_name):
251+
"""Load view
252+
253+
Parameters
254+
----------
255+
plugin_name : str
256+
plugin name
257+
plugin_path : str
258+
plugin path
259+
frame_name : str
260+
frame name
261+
262+
Returns
263+
-------
264+
frame_class : class
265+
tkinter frame class
266+
"""
267+
frame_file_name = "_".join(frame_name.lower().split()) + "_frame.py"
268+
frame_class_name = "".join(frame_name.title().split()) + "Frame"
269+
frame_file_path = os.path.join(
270+
plugin_path,
271+
"view",
272+
frame_file_name
273+
)
274+
if os.path.exists(frame_file_path) and os.path.isfile(frame_file_path):
275+
module = load_module_from_file(
276+
frame_class_name, frame_file_path
277+
)
278+
return getattr(module, frame_class_name)
279+
return None
280+
281+
@staticmethod
282+
def load_feature_lists(plugin_name, plugin_path, register_func):
283+
"""Load feature lists
284+
285+
Parameters
286+
----------
287+
plugin_name : str
288+
plugin name
289+
plugin_path : str
290+
plugin path
291+
register_func : func
292+
the function to handle feature lists
293+
"""
294+
plugin_feature_list = os.path.join(plugin_path, "feature_list.py")
295+
if os.path.exists(plugin_feature_list):
296+
module = load_module_from_file("feature_list_temp", plugin_feature_list)
297+
register_func(plugin_feature_list, module)
298+
299+
@staticmethod
300+
def load_features(plugin_name, plugin_path):
301+
"""Load features
302+
303+
Parameters
304+
----------
305+
plugin_name : str
306+
plugin name
307+
plugin_path : str
308+
plugin path
309+
"""
310+
features_dir = os.path.join(plugin_path, "model", "features")
311+
features = []
312+
if os.path.exists(features_dir):
313+
features = os.listdir(features_dir)
314+
for feature in features:
315+
feature_file = os.path.join(features_dir, feature)
316+
if os.path.isfile(feature_file):
317+
module = load_module_from_file(feature, feature_file)
318+
register_features(module)
319+
320+
@staticmethod
321+
def load_acquisition_modes(plugin_name, plugin_path, acquisition_modes, register_func):
322+
"""Load acquisition modes
323+
324+
Parameters
325+
----------
326+
plugin_name : str
327+
plugin name
328+
plugin_path : str
329+
plugin path
330+
acquisition_modes : []
331+
list of acquisition mode configurations
332+
register_func : func
333+
the function to register acquisition modes
334+
"""
335+
for acquisition_mode_config in acquisition_modes:
336+
acquisition_file = acquisition_mode_config["file_name"]
337+
full_path_name = os.path.join(plugin_path, acquisition_file)
338+
if os.path.exists(full_path_name):
339+
module = load_module_from_file(acquisition_file[:-3], full_path_name)
340+
if module:
341+
register_func(acquisition_mode_config["name"], module)
342+
343+
@staticmethod
344+
def load_devices(plugin_name, plugin_path, register_func):
345+
"""Load devices
346+
347+
Parameters
348+
----------
349+
plugin_name : str
350+
plugin name
351+
plugin_path : str
352+
plugin path
353+
register_func : func
354+
the function to register devices
355+
"""
356+
device_dir = os.path.join(plugin_path, "model", "devices")
357+
if os.path.exists(device_dir) and os.path.isdir(device_dir):
358+
devices = os.listdir(device_dir)
359+
for device in devices:
360+
device_path = os.path.join(device_dir, device)
361+
if not os.path.isdir(device_path):
362+
continue
363+
try:
364+
module = load_module_from_file(
365+
"device_module",
366+
os.path.join(device_path, "device_startup_functions.py")
367+
)
368+
except FileNotFoundError:
369+
continue
370+
register_func(device, module)

0 commit comments

Comments
 (0)