|
18 | 18 | import warnings |
19 | 19 | import traceback |
20 | 20 | import inspect |
| 21 | +from labscript_utils import labscript_suite_install_dir, dedent |
| 22 | +from labscript_utils.labconfig import LabConfig |
21 | 23 |
|
22 | 24 | __version__ = '2.5.0' |
23 | 25 |
|
|
27 | 29 | check_version('zprocess', '2.2.7', '3') |
28 | 30 | check_version('numpy', '1.15.1', '2') |
29 | 31 |
|
30 | | -from labscript_utils import labscript_suite_install_dir, dedent |
31 | | - |
32 | | -LABSCRIPT_DEVICES_DIR = os.path.join(labscript_suite_install_dir, 'labscript_devices') |
33 | 32 |
|
34 | 33 | """This file contains the machinery for registering and looking up what BLACS tab and |
35 | 34 | runviewer parser classes belong to a particular labscript device. "labscript device" |
|
46 | 45 | splitting it across multiple files. |
47 | 46 |
|
48 | 47 | The "new" method is more flexible. It allows BLACS tabs and runviewer parsers to be |
49 | | -defined in any importable file within a subfolder of labscript_devices. Classes using |
50 | | -this method can be in files with any name, and do not need class decorators. Instead, |
51 | | -the classes should be registered by creating a file called 'register_classes.py', which |
52 | | -when imported, makes calls to labscript_devices.register_classes() to register |
53 | | -which BLACS tab and runviewer parser class belong to each device. Tab and parser classes |
54 | | -must be passed to register_classes() as fully qualified names, i.e. |
55 | | -"labscript_devices.submodule.ClassName", not by passing in the classes themselves. This |
56 | | -ensures imports can be deferred until the classes are actually needed. When BLACS and |
57 | | -runviewer look up classes with get_BLACS_tab() and get_runviewer_parser(), |
58 | | -populate_registry() will be called in order to find all files called |
59 | | -'register_classes.py' within subfolders (at any depth) of labscript_devices, and they |
60 | | -will be imported to run their code and hence register their classes. |
| 48 | +defined in any importable file within a subfolder of labscript_devices. Additionally, |
| 49 | +the 'user_devices' configuration setting in labconfig can be used to specify a |
| 50 | +comma-delimited list of names of importable packages containing additional labscript |
| 51 | +devices. |
| 52 | +
|
| 53 | +Classes using the new method can be in files with any name, and do not need class |
| 54 | +decorators. Instead, the classes should be registered by creating a file called |
| 55 | +'register_classes.py', which when imported, makes calls to |
| 56 | +labscript_devices.register_classes() to register which BLACS tab and runviewer parser |
| 57 | +class belong to each device. Tab and parser classes must be passed to register_classes() |
| 58 | +as fully qualified names, i.e. "labscript_devices.submodule.ClassName", not by passing |
| 59 | +in the classes themselves. This ensures imports can be deferred until the classes are |
| 60 | +actually needed. When BLACS and runviewer look up classes with get_BLACS_tab() and |
| 61 | +get_runviewer_parser(), populate_registry() will be called in order to find all files |
| 62 | +called 'register_classes.py' within subfolders (at any depth) of labscript_devices, and |
| 63 | +they will be imported to run their code and hence register their classes. |
61 | 64 |
|
62 | 65 | The "new" method does not impose any restrictions on code organisation within subfolders |
63 | 66 | of labscript_devices, and so is preferable as it allows auxiliary utilities or resource |
|
69 | 72 | """ |
70 | 73 |
|
71 | 74 |
|
| 75 | +def _get_import_paths(import_names): |
| 76 | + """For the given list of packages, return all folders containing their submodules. |
| 77 | + If the packages do not exist, ignore them.""" |
| 78 | + paths = [] |
| 79 | + for name in import_names: |
| 80 | + if PY2: |
| 81 | + try: |
| 82 | + _, location, _ = imp.find_module(name) |
| 83 | + except ImportError: |
| 84 | + continue |
| 85 | + paths.append(os.path.dirname(location)) |
| 86 | + else: |
| 87 | + spec = importlib.util.find_spec(name) |
| 88 | + if spec is not None and spec.submodule_search_locations is not None: |
| 89 | + paths.extend(spec.submodule_search_locations) |
| 90 | + return paths |
| 91 | + |
| 92 | + |
| 93 | +def _get_device_dirs(): |
| 94 | + """Return the directory of labscript_devices, and the folders containing |
| 95 | + submodules of any packages listed in the user_devices labconfig setting""" |
| 96 | + try: |
| 97 | + user_devices = LabConfig().get('DEFAULT', 'user_devices') |
| 98 | + except (LabConfig.NoOptionError, LabConfig.NoSectionError): |
| 99 | + user_devices = 'user_devices' |
| 100 | + # Split on commas, remove whitespace: |
| 101 | + user_devices = [s.strip() for s in user_devices.split(',')] |
| 102 | + return _get_import_paths(['labscript_devices'] + user_devices) |
| 103 | + |
| 104 | + |
| 105 | +LABSCRIPT_DEVICES_DIRS = _get_device_dirs() |
| 106 | + |
| 107 | + |
72 | 108 | class ClassRegister(object): |
73 | 109 | """A register for looking up classes by module name. Provides a |
74 | 110 | decorator and a method for looking up classes decorated with it, |
@@ -241,15 +277,16 @@ def populate_registry(): |
241 | 277 | # But they cannot all have the same name, so we import them as |
242 | 278 | # labscript_devices._register_classes_script_<num> with increasing number. |
243 | 279 | module_num = 0 |
244 | | - for folder, _, filenames in os.walk(LABSCRIPT_DEVICES_DIR): |
245 | | - if 'register_classes.py' in filenames: |
246 | | - # The module name is the path to the file, relative to the labscript suite |
247 | | - # install directory: |
248 | | - # Open the file using the import machinery, and import it as module_name. |
249 | | - fp, pathname, desc = imp.find_module('register_classes', [folder]) |
250 | | - module_name = 'labscript_devices._register_classes_script_%d' % module_num |
251 | | - _ = imp.load_module(module_name, fp, pathname, desc) |
252 | | - module_num += 1 |
| 280 | + for devices_dir in LABSCRIPT_DEVICES_DIRS: |
| 281 | + for folder, _, filenames in os.walk(devices_dir): |
| 282 | + if 'register_classes.py' in filenames: |
| 283 | + # The module name is the path to the file, relative to the labscript suite |
| 284 | + # install directory: |
| 285 | + # Open the file using the import machinery, and import it as module_name. |
| 286 | + fp, pathname, desc = imp.find_module('register_classes', [folder]) |
| 287 | + module_name = 'labscript_devices._register_classes_%d' % module_num |
| 288 | + _ = imp.load_module(module_name, fp, pathname, desc) |
| 289 | + module_num += 1 |
253 | 290 |
|
254 | 291 |
|
255 | 292 | if __name__ == '__main__': |
|
0 commit comments