sierra-research 1.3.6__py3-none-any.whl → 1.5.0__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.
Files changed (254) hide show
  1. sierra/__init__.py +3 -3
  2. sierra/core/__init__.py +3 -3
  3. sierra/core/batchroot.py +223 -0
  4. sierra/core/cmdline.py +681 -1057
  5. sierra/core/compare.py +11 -0
  6. sierra/core/config.py +96 -88
  7. sierra/core/engine.py +306 -0
  8. sierra/core/execenv.py +380 -0
  9. sierra/core/expdef.py +11 -0
  10. sierra/core/experiment/__init__.py +1 -0
  11. sierra/core/experiment/bindings.py +150 -101
  12. sierra/core/experiment/definition.py +414 -245
  13. sierra/core/experiment/spec.py +83 -85
  14. sierra/core/exproot.py +44 -0
  15. sierra/core/generators/__init__.py +10 -0
  16. sierra/core/generators/experiment.py +528 -0
  17. sierra/core/generators/generator_factory.py +138 -137
  18. sierra/core/graphs/__init__.py +23 -0
  19. sierra/core/graphs/bcbridge.py +94 -0
  20. sierra/core/graphs/heatmap.py +245 -324
  21. sierra/core/graphs/pathset.py +27 -0
  22. sierra/core/graphs/schema.py +77 -0
  23. sierra/core/graphs/stacked_line.py +341 -0
  24. sierra/core/graphs/summary_line.py +506 -0
  25. sierra/core/logging.py +3 -2
  26. sierra/core/models/__init__.py +3 -1
  27. sierra/core/models/info.py +19 -0
  28. sierra/core/models/interface.py +52 -122
  29. sierra/core/pipeline/__init__.py +2 -5
  30. sierra/core/pipeline/pipeline.py +228 -126
  31. sierra/core/pipeline/stage1/__init__.py +10 -0
  32. sierra/core/pipeline/stage1/pipeline_stage1.py +45 -31
  33. sierra/core/pipeline/stage2/__init__.py +10 -0
  34. sierra/core/pipeline/stage2/pipeline_stage2.py +8 -11
  35. sierra/core/pipeline/stage2/runner.py +401 -0
  36. sierra/core/pipeline/stage3/__init__.py +12 -0
  37. sierra/core/pipeline/stage3/gather.py +321 -0
  38. sierra/core/pipeline/stage3/pipeline_stage3.py +37 -84
  39. sierra/core/pipeline/stage4/__init__.py +12 -2
  40. sierra/core/pipeline/stage4/pipeline_stage4.py +36 -354
  41. sierra/core/pipeline/stage5/__init__.py +12 -0
  42. sierra/core/pipeline/stage5/pipeline_stage5.py +33 -208
  43. sierra/core/pipeline/yaml.py +48 -0
  44. sierra/core/plugin.py +529 -62
  45. sierra/core/proc.py +11 -0
  46. sierra/core/prod.py +11 -0
  47. sierra/core/ros1/__init__.py +5 -1
  48. sierra/core/ros1/callbacks.py +22 -21
  49. sierra/core/ros1/cmdline.py +59 -88
  50. sierra/core/ros1/generators.py +159 -175
  51. sierra/core/ros1/variables/__init__.py +3 -0
  52. sierra/core/ros1/variables/exp_setup.py +122 -116
  53. sierra/core/startup.py +106 -76
  54. sierra/core/stat_kernels.py +4 -5
  55. sierra/core/storage.py +13 -32
  56. sierra/core/trampoline.py +30 -0
  57. sierra/core/types.py +116 -71
  58. sierra/core/utils.py +103 -106
  59. sierra/core/variables/__init__.py +1 -1
  60. sierra/core/variables/base_variable.py +12 -17
  61. sierra/core/variables/batch_criteria.py +387 -481
  62. sierra/core/variables/builtin.py +135 -0
  63. sierra/core/variables/exp_setup.py +19 -39
  64. sierra/core/variables/population_size.py +72 -76
  65. sierra/core/variables/variable_density.py +44 -68
  66. sierra/core/vector.py +1 -1
  67. sierra/main.py +256 -88
  68. sierra/plugins/__init__.py +119 -0
  69. sierra/plugins/compare/__init__.py +14 -0
  70. sierra/plugins/compare/graphs/__init__.py +19 -0
  71. sierra/plugins/compare/graphs/cmdline.py +120 -0
  72. sierra/plugins/compare/graphs/comparator.py +291 -0
  73. sierra/plugins/compare/graphs/inter_controller.py +531 -0
  74. sierra/plugins/compare/graphs/inter_scenario.py +297 -0
  75. sierra/plugins/compare/graphs/namecalc.py +53 -0
  76. sierra/plugins/compare/graphs/outputroot.py +73 -0
  77. sierra/plugins/compare/graphs/plugin.py +147 -0
  78. sierra/plugins/compare/graphs/preprocess.py +172 -0
  79. sierra/plugins/compare/graphs/schema.py +37 -0
  80. sierra/plugins/engine/__init__.py +14 -0
  81. sierra/plugins/engine/argos/__init__.py +18 -0
  82. sierra/plugins/{platform → engine}/argos/cmdline.py +144 -151
  83. sierra/plugins/{platform/argos/variables → engine/argos/generators}/__init__.py +5 -0
  84. sierra/plugins/engine/argos/generators/engine.py +394 -0
  85. sierra/plugins/engine/argos/plugin.py +393 -0
  86. sierra/plugins/{platform/argos/generators → engine/argos/variables}/__init__.py +5 -0
  87. sierra/plugins/engine/argos/variables/arena_shape.py +183 -0
  88. sierra/plugins/engine/argos/variables/cameras.py +240 -0
  89. sierra/plugins/engine/argos/variables/constant_density.py +112 -0
  90. sierra/plugins/engine/argos/variables/exp_setup.py +82 -0
  91. sierra/plugins/{platform → engine}/argos/variables/physics_engines.py +83 -87
  92. sierra/plugins/engine/argos/variables/population_constant_density.py +178 -0
  93. sierra/plugins/engine/argos/variables/population_size.py +115 -0
  94. sierra/plugins/engine/argos/variables/population_variable_density.py +123 -0
  95. sierra/plugins/engine/argos/variables/rendering.py +108 -0
  96. sierra/plugins/engine/ros1gazebo/__init__.py +18 -0
  97. sierra/plugins/engine/ros1gazebo/cmdline.py +175 -0
  98. sierra/plugins/{platform/ros1robot → engine/ros1gazebo}/generators/__init__.py +5 -0
  99. sierra/plugins/engine/ros1gazebo/generators/engine.py +125 -0
  100. sierra/plugins/engine/ros1gazebo/plugin.py +404 -0
  101. sierra/plugins/engine/ros1gazebo/variables/__init__.py +15 -0
  102. sierra/plugins/engine/ros1gazebo/variables/population_size.py +214 -0
  103. sierra/plugins/engine/ros1robot/__init__.py +18 -0
  104. sierra/plugins/engine/ros1robot/cmdline.py +159 -0
  105. sierra/plugins/{platform/ros1gazebo → engine/ros1robot}/generators/__init__.py +4 -0
  106. sierra/plugins/engine/ros1robot/generators/engine.py +95 -0
  107. sierra/plugins/engine/ros1robot/plugin.py +410 -0
  108. sierra/plugins/{hpc/local → engine/ros1robot/variables}/__init__.py +5 -0
  109. sierra/plugins/engine/ros1robot/variables/population_size.py +146 -0
  110. sierra/plugins/execenv/__init__.py +11 -0
  111. sierra/plugins/execenv/hpc/__init__.py +18 -0
  112. sierra/plugins/execenv/hpc/adhoc/__init__.py +18 -0
  113. sierra/plugins/execenv/hpc/adhoc/cmdline.py +30 -0
  114. sierra/plugins/execenv/hpc/adhoc/plugin.py +131 -0
  115. sierra/plugins/execenv/hpc/cmdline.py +137 -0
  116. sierra/plugins/execenv/hpc/local/__init__.py +18 -0
  117. sierra/plugins/execenv/hpc/local/cmdline.py +31 -0
  118. sierra/plugins/execenv/hpc/local/plugin.py +145 -0
  119. sierra/plugins/execenv/hpc/pbs/__init__.py +18 -0
  120. sierra/plugins/execenv/hpc/pbs/cmdline.py +30 -0
  121. sierra/plugins/execenv/hpc/pbs/plugin.py +121 -0
  122. sierra/plugins/execenv/hpc/slurm/__init__.py +18 -0
  123. sierra/plugins/execenv/hpc/slurm/cmdline.py +30 -0
  124. sierra/plugins/execenv/hpc/slurm/plugin.py +133 -0
  125. sierra/plugins/execenv/prefectserver/__init__.py +18 -0
  126. sierra/plugins/execenv/prefectserver/cmdline.py +66 -0
  127. sierra/plugins/execenv/prefectserver/dockerremote/__init__.py +18 -0
  128. sierra/plugins/execenv/prefectserver/dockerremote/cmdline.py +66 -0
  129. sierra/plugins/execenv/prefectserver/dockerremote/plugin.py +132 -0
  130. sierra/plugins/execenv/prefectserver/flow.py +66 -0
  131. sierra/plugins/execenv/prefectserver/local/__init__.py +18 -0
  132. sierra/plugins/execenv/prefectserver/local/cmdline.py +29 -0
  133. sierra/plugins/execenv/prefectserver/local/plugin.py +133 -0
  134. sierra/plugins/{hpc/adhoc → execenv/robot}/__init__.py +1 -0
  135. sierra/plugins/execenv/robot/turtlebot3/__init__.py +18 -0
  136. sierra/plugins/execenv/robot/turtlebot3/plugin.py +204 -0
  137. sierra/plugins/expdef/__init__.py +14 -0
  138. sierra/plugins/expdef/json/__init__.py +14 -0
  139. sierra/plugins/expdef/json/plugin.py +504 -0
  140. sierra/plugins/expdef/xml/__init__.py +14 -0
  141. sierra/plugins/expdef/xml/plugin.py +386 -0
  142. sierra/{core/hpc → plugins/proc}/__init__.py +1 -1
  143. sierra/plugins/proc/collate/__init__.py +15 -0
  144. sierra/plugins/proc/collate/cmdline.py +47 -0
  145. sierra/plugins/proc/collate/plugin.py +271 -0
  146. sierra/plugins/proc/compress/__init__.py +18 -0
  147. sierra/plugins/proc/compress/cmdline.py +47 -0
  148. sierra/plugins/proc/compress/plugin.py +123 -0
  149. sierra/plugins/proc/decompress/__init__.py +18 -0
  150. sierra/plugins/proc/decompress/plugin.py +96 -0
  151. sierra/plugins/proc/imagize/__init__.py +15 -0
  152. sierra/plugins/proc/imagize/cmdline.py +49 -0
  153. sierra/plugins/proc/imagize/plugin.py +270 -0
  154. sierra/plugins/proc/modelrunner/__init__.py +16 -0
  155. sierra/plugins/proc/modelrunner/plugin.py +250 -0
  156. sierra/plugins/proc/statistics/__init__.py +15 -0
  157. sierra/plugins/proc/statistics/cmdline.py +64 -0
  158. sierra/plugins/proc/statistics/plugin.py +390 -0
  159. sierra/plugins/{hpc → prod}/__init__.py +1 -0
  160. sierra/plugins/prod/graphs/__init__.py +18 -0
  161. sierra/plugins/prod/graphs/cmdline.py +269 -0
  162. sierra/plugins/prod/graphs/collate.py +279 -0
  163. sierra/plugins/prod/graphs/inter/__init__.py +13 -0
  164. sierra/plugins/prod/graphs/inter/generate.py +83 -0
  165. sierra/plugins/prod/graphs/inter/heatmap.py +86 -0
  166. sierra/plugins/prod/graphs/inter/line.py +134 -0
  167. sierra/plugins/prod/graphs/intra/__init__.py +15 -0
  168. sierra/plugins/prod/graphs/intra/generate.py +202 -0
  169. sierra/plugins/prod/graphs/intra/heatmap.py +74 -0
  170. sierra/plugins/prod/graphs/intra/line.py +114 -0
  171. sierra/plugins/prod/graphs/plugin.py +103 -0
  172. sierra/plugins/prod/graphs/targets.py +63 -0
  173. sierra/plugins/prod/render/__init__.py +18 -0
  174. sierra/plugins/prod/render/cmdline.py +72 -0
  175. sierra/plugins/prod/render/plugin.py +282 -0
  176. sierra/plugins/storage/__init__.py +5 -0
  177. sierra/plugins/storage/arrow/__init__.py +18 -0
  178. sierra/plugins/storage/arrow/plugin.py +38 -0
  179. sierra/plugins/storage/csv/__init__.py +9 -0
  180. sierra/plugins/storage/csv/plugin.py +12 -5
  181. sierra/version.py +3 -2
  182. sierra_research-1.5.0.dist-info/METADATA +238 -0
  183. sierra_research-1.5.0.dist-info/RECORD +186 -0
  184. {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info}/WHEEL +1 -2
  185. sierra/core/experiment/xml.py +0 -454
  186. sierra/core/generators/controller_generator_parser.py +0 -34
  187. sierra/core/generators/exp_creator.py +0 -351
  188. sierra/core/generators/exp_generators.py +0 -142
  189. sierra/core/graphs/scatterplot2D.py +0 -109
  190. sierra/core/graphs/stacked_line_graph.py +0 -249
  191. sierra/core/graphs/stacked_surface_graph.py +0 -220
  192. sierra/core/graphs/summary_line_graph.py +0 -369
  193. sierra/core/hpc/cmdline.py +0 -142
  194. sierra/core/models/graphs.py +0 -87
  195. sierra/core/pipeline/stage2/exp_runner.py +0 -286
  196. sierra/core/pipeline/stage3/imagizer.py +0 -149
  197. sierra/core/pipeline/stage3/run_collator.py +0 -317
  198. sierra/core/pipeline/stage3/statistics_calculator.py +0 -478
  199. sierra/core/pipeline/stage4/graph_collator.py +0 -319
  200. sierra/core/pipeline/stage4/inter_exp_graph_generator.py +0 -240
  201. sierra/core/pipeline/stage4/intra_exp_graph_generator.py +0 -317
  202. sierra/core/pipeline/stage4/model_runner.py +0 -168
  203. sierra/core/pipeline/stage4/rendering.py +0 -283
  204. sierra/core/pipeline/stage4/yaml_config_loader.py +0 -103
  205. sierra/core/pipeline/stage5/inter_scenario_comparator.py +0 -328
  206. sierra/core/pipeline/stage5/intra_scenario_comparator.py +0 -989
  207. sierra/core/platform.py +0 -493
  208. sierra/core/plugin_manager.py +0 -369
  209. sierra/core/root_dirpath_generator.py +0 -241
  210. sierra/plugins/hpc/adhoc/plugin.py +0 -125
  211. sierra/plugins/hpc/local/plugin.py +0 -81
  212. sierra/plugins/hpc/pbs/__init__.py +0 -9
  213. sierra/plugins/hpc/pbs/plugin.py +0 -126
  214. sierra/plugins/hpc/slurm/__init__.py +0 -9
  215. sierra/plugins/hpc/slurm/plugin.py +0 -130
  216. sierra/plugins/platform/__init__.py +0 -9
  217. sierra/plugins/platform/argos/__init__.py +0 -9
  218. sierra/plugins/platform/argos/generators/platform_generators.py +0 -383
  219. sierra/plugins/platform/argos/plugin.py +0 -337
  220. sierra/plugins/platform/argos/variables/arena_shape.py +0 -145
  221. sierra/plugins/platform/argos/variables/cameras.py +0 -243
  222. sierra/plugins/platform/argos/variables/constant_density.py +0 -136
  223. sierra/plugins/platform/argos/variables/exp_setup.py +0 -113
  224. sierra/plugins/platform/argos/variables/population_constant_density.py +0 -175
  225. sierra/plugins/platform/argos/variables/population_size.py +0 -102
  226. sierra/plugins/platform/argos/variables/population_variable_density.py +0 -132
  227. sierra/plugins/platform/argos/variables/rendering.py +0 -104
  228. sierra/plugins/platform/ros1gazebo/__init__.py +0 -9
  229. sierra/plugins/platform/ros1gazebo/cmdline.py +0 -213
  230. sierra/plugins/platform/ros1gazebo/generators/platform_generators.py +0 -137
  231. sierra/plugins/platform/ros1gazebo/plugin.py +0 -335
  232. sierra/plugins/platform/ros1gazebo/variables/__init__.py +0 -10
  233. sierra/plugins/platform/ros1gazebo/variables/population_size.py +0 -204
  234. sierra/plugins/platform/ros1robot/__init__.py +0 -9
  235. sierra/plugins/platform/ros1robot/cmdline.py +0 -175
  236. sierra/plugins/platform/ros1robot/generators/platform_generators.py +0 -112
  237. sierra/plugins/platform/ros1robot/plugin.py +0 -373
  238. sierra/plugins/platform/ros1robot/variables/__init__.py +0 -10
  239. sierra/plugins/platform/ros1robot/variables/population_size.py +0 -146
  240. sierra/plugins/robot/__init__.py +0 -9
  241. sierra/plugins/robot/turtlebot3/__init__.py +0 -9
  242. sierra/plugins/robot/turtlebot3/plugin.py +0 -194
  243. sierra_research-1.3.6.data/data/share/man/man1/sierra-cli.1 +0 -2349
  244. sierra_research-1.3.6.data/data/share/man/man7/sierra-examples.7 +0 -488
  245. sierra_research-1.3.6.data/data/share/man/man7/sierra-exec-envs.7 +0 -331
  246. sierra_research-1.3.6.data/data/share/man/man7/sierra-glossary.7 +0 -285
  247. sierra_research-1.3.6.data/data/share/man/man7/sierra-platforms.7 +0 -358
  248. sierra_research-1.3.6.data/data/share/man/man7/sierra-usage.7 +0 -725
  249. sierra_research-1.3.6.data/data/share/man/man7/sierra.7 +0 -78
  250. sierra_research-1.3.6.dist-info/METADATA +0 -500
  251. sierra_research-1.3.6.dist-info/RECORD +0 -133
  252. sierra_research-1.3.6.dist-info/top_level.txt +0 -1
  253. {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info}/entry_points.txt +0 -0
  254. {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info/licenses}/LICENSE +0 -0
sierra/core/plugin.py CHANGED
@@ -1,112 +1,579 @@
1
1
  # Copyright 2021 John Harwell, All rights reserved.
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- """Sanity checks for verifying selected plugins.
5
-
6
- Checks that selected plugins implement the necessary classes and
7
- functions. Currently checkes: ``--storage-medium``, ``--exec-env``, and
8
- ``--platform``.
4
+ """SIERRA plugin management to make SIERRA OPEN/CLOSED.
9
5
 
6
+ Also contains checks that selected plugins implement the necessary classes and
7
+ functions. Currently checkes: ``--storage``, ``--execenv``, and ``--engine``.
10
8
  """
11
9
 
12
10
  # Core packages
13
- import inspect
11
+ # Core packages
12
+ import importlib.util
13
+ import importlib
14
+ import typing as tp
15
+ import sys
14
16
  import logging
17
+ import pathlib
18
+ import inspect
15
19
 
16
20
  # 3rd party packages
17
21
 
22
+ # 3rd party packages
23
+ import json
24
+
18
25
  # Project packages
26
+ from sierra.core import types
27
+
28
+
29
+ class BasePluginManager:
30
+ """Base class for common functionality."""
31
+
32
+ def __init__(self) -> None:
33
+ self.logger = logging.getLogger(__name__)
34
+ self.loaded = {} # type: tp.Dict[str, tp.Dict]
35
+
36
+ def available_plugins(self):
37
+ raise NotImplementedError
38
+
39
+ def loaded_plugins(self):
40
+ return self.loaded
41
+
42
+ def load_plugin(self, name: str) -> None:
43
+ """Load a plugin module."""
44
+ plugins = self.available_plugins()
45
+ if name not in plugins:
46
+ self.logger.fatal("Cannot locate plugin %s", name)
47
+ self.logger.fatal(
48
+ "Loaded plugins: %s\n",
49
+ json.dumps(self.loaded, default=lambda x: "<ModuleSpec>", indent=4),
50
+ )
51
+ raise Exception(f"Cannot locate plugin '{name}'")
52
+
53
+ init = importlib.util.module_from_spec(plugins[name]["init_spec"])
54
+ plugins[name]["init_spec"].loader.exec_module(init)
55
+
56
+ if not hasattr(init, "sierra_plugin_type"):
57
+ self.logger.warning(
58
+ "Cannot load plugin %s: __init__.py does not define sierra_plugin_type()",
59
+ name,
60
+ )
61
+ return
62
+
63
+ plugin_type = init.sierra_plugin_type()
64
+
65
+ # The name of the module is only needed for pipeline plugins, not
66
+ # project plugins.
67
+ if plugins[name]["module_spec"] is None and plugin_type == "pipeline":
68
+ if not hasattr(init, "sierra_plugin_module"):
69
+ self.logger.warning(
70
+ "Cannot load plugin %s: __init__.py does not define sierra_plugin_module()",
71
+ name,
72
+ )
73
+ return
74
+
75
+ modname = init.sierra_plugin_module()
76
+ fpath = (
77
+ plugins[name]["parent_dir"] / name.replace(".", "/") / f"{modname}.py"
78
+ )
79
+ plugins[name]["module_spec"] = importlib.util.spec_from_file_location(
80
+ modname, fpath
81
+ )
82
+
83
+ if plugin_type == "pipeline":
84
+ self._load_pipeline_plugin(name)
85
+
86
+ elif plugin_type == "project":
87
+ self._load_project_plugin(name)
88
+ elif plugin_type == "model":
89
+ if not hasattr(init, "sierra_models"):
90
+ self.logger.warning(
91
+ "Cannot load plugin %s: __init__.py does not define sierra_models()",
92
+ name,
93
+ )
94
+ return
95
+
96
+ self._load_model_plugin(name)
97
+ else:
98
+ self.logger.warning(
99
+ "Unknown plugin type '%s' for %s: cannot load", plugin_type, name
100
+ )
101
+
102
+ def get_plugin(self, name: str) -> dict:
103
+ try:
104
+ return self.loaded[name]
105
+ except KeyError:
106
+ self.logger.fatal("No such plugin %s", name)
107
+ self.logger.fatal(
108
+ "Loaded plugins: %s\n",
109
+ json.dumps(self.loaded, default=lambda x: "<ModuleSpec>", indent=4),
110
+ )
111
+ raise
112
+
113
+ def get_plugin_module(self, name: str) -> types.ModuleType:
114
+ try:
115
+ return self.loaded[name]["module"]
116
+ except KeyError:
117
+ self.logger.fatal("No such plugin %s", name)
118
+ self.logger.fatal(
119
+ "Loaded plugins: %s\n",
120
+ json.dumps(self.loaded, default=lambda x: "<ModuleSpec>", indent=4),
121
+ )
122
+ raise
123
+
124
+ def has_plugin(self, name: str) -> bool:
125
+ return name in self.loaded
126
+
127
+ def _load_pipeline_plugin(self, name: str) -> None:
128
+ if name in self.loaded:
129
+ self.logger.warning("Pipeline plugin %s already loaded", name)
130
+ return
131
+
132
+ plugins = self.available_plugins()
133
+
134
+ # The parent directory of the plugin must be on sys.path so it can be
135
+ # imported, so we put in on there if it isn't.
136
+ new = str(plugins[name]["parent_dir"])
137
+ if new not in sys.path:
138
+ sys.path = [new] + sys.path[0:]
139
+ self.logger.debug("Updated sys.path with %s", [new])
140
+
141
+ module = importlib.util.module_from_spec(plugins[name]["module_spec"])
142
+ plugins[name]["module_spec"].loader.exec_module(module)
143
+
144
+ # When importing with importlib, the module is not automatically added
145
+ # to sys.modules. This means that trying to pickle anything in it will
146
+ # fail with a rather cryptic 'AttributeError', so we explicitly add the
147
+ # last path component of the plugin name--which is the actual name of
148
+ # the module the plugin lives in--to sys.modules so that pickling will
149
+ # work.
150
+ sys_modname = name.split(".")[1]
151
+ if sys_modname not in sys.modules:
152
+ sys.modules[sys_modname] = module
153
+
154
+ self.loaded[name] = {
155
+ "spec": plugins[name]["module_spec"],
156
+ "parent_dir": plugins[name]["parent_dir"],
157
+ "module": module,
158
+ "type": "pipeline",
159
+ }
160
+ self.logger.debug(
161
+ "Loaded pipeline plugin %s from %s -> %s",
162
+ name,
163
+ plugins[name]["parent_dir"],
164
+ name,
165
+ )
166
+
167
+ def _load_project_plugin(self, name: str) -> None:
168
+ if name in self.loaded:
169
+ self.logger.warning("Project plugin %s already loaded", name)
170
+ return
19
171
 
172
+ plugins = self.available_plugins()
20
173
 
21
- def storage_sanity_checks(module) -> None:
174
+ # The parent directory of the plugin must be on sys.path so it can be
175
+ # imported, so we put in on there if it isn't.
176
+ new = str(plugins[name]["parent_dir"])
177
+ if new not in sys.path:
178
+ sys.path = [new] + sys.path[0:]
179
+ self.logger.debug("Updated sys.path with %s", [new])
180
+
181
+ self.loaded[name] = {
182
+ "spec": plugins[name]["module_spec"],
183
+ "parent_dir": plugins[name]["parent_dir"],
184
+ "type": "project",
185
+ }
186
+
187
+ self.logger.debug(
188
+ ("Loaded project plugin %s from %s -> %s"),
189
+ name,
190
+ plugins[name]["parent_dir"],
191
+ name,
192
+ )
193
+
194
+ def _load_model_plugin(self, name: str) -> None:
195
+ if name in self.loaded:
196
+ self.logger.warning("Model plugin %s already loaded", name)
197
+ return
198
+
199
+ plugins = self.available_plugins()
200
+
201
+ # The parent directory of the plugin must be on sys.path so it can be
202
+ # imported, so we put in on there if it isn't.
203
+ new = str(plugins[name]["parent_dir"])
204
+ if new not in sys.path:
205
+ sys.path = [new] + sys.path[0:]
206
+ self.logger.debug("Updated sys.path with %s", [new])
207
+
208
+ self.loaded[name] = {
209
+ "spec": plugins[name]["module_spec"],
210
+ "parent_dir": plugins[name]["parent_dir"],
211
+ "type": "model",
212
+ }
213
+
214
+ self.logger.debug(
215
+ ("Loaded model plugin %s from %s -> %s"),
216
+ name,
217
+ plugins[name]["parent_dir"],
218
+ name,
219
+ )
220
+
221
+
222
+ class DirectoryPluginManager(BasePluginManager):
223
+ """Container for managing directory-based plugins."""
224
+
225
+ def __init__(self) -> None:
226
+ super().__init__()
227
+ self.plugins = {} # type: tp.Dict[str, tp.Dict]
228
+
229
+ def initialize(self, project: str, search_path: tp.List[pathlib.Path]) -> None:
230
+ self.logger.debug(
231
+ "Initializing with plugin search path %s", [str(p) for p in search_path]
232
+ )
233
+
234
+ for path in search_path:
235
+ if not path.exists():
236
+ self.logger.warning(
237
+ "Non-existent path '%s' on SIERRA_PLUGIN_PATH", path
238
+ )
239
+ continue
240
+
241
+ self.logger.debug("Searching for plugins in '%s'", path)
242
+
243
+ def recursive_search(root: pathlib.Path) -> None:
244
+ try:
245
+ for f in root.iterdir():
246
+ if not f.is_dir():
247
+ continue
248
+ recursive_search(f)
249
+
250
+ plugin = f / "plugin.py"
251
+ init = f / "__init__.py"
252
+ cookie = f / ".sierraplugin"
253
+
254
+ if not (init.exists() and (plugin.exists() or cookie.exists())):
255
+ continue
256
+
257
+ name = f"{f.parent.name}.{f.name}"
258
+
259
+ if plugin.exists():
260
+ module_spec = importlib.util.spec_from_file_location(
261
+ f.name, plugin
262
+ )
263
+ else:
264
+ module_spec = None
265
+
266
+ init_spec = importlib.util.spec_from_file_location(
267
+ "__init__", init
268
+ )
269
+
270
+ self.logger.debug("Found plugin in '%s' -> %s", f, name)
271
+
272
+ self.plugins[name] = {
273
+ "parent_dir": root.parent,
274
+ "module_spec": module_spec,
275
+ "init_spec": init_spec,
276
+ }
277
+ except FileNotFoundError:
278
+ self.logger.warning(
279
+ "Malformed plugin in %s: not loading", f.relative_to(path)
280
+ )
281
+
282
+ recursive_search(path)
283
+
284
+ def available_plugins(self):
285
+ return self.plugins
286
+
287
+
288
+ def module_exists(name: str) -> bool:
22
289
  """
23
- Check the selected ``--storage-medium`` plugin.
290
+ Check if a module exists before trying to import it.
24
291
  """
25
- logging.trace("Verifying --storage-medium plugin interface") # type: ignore
292
+ try:
293
+ _ = __import__(name)
294
+ except ImportError:
295
+ return False
296
+ else:
297
+ return True
298
+
299
+
300
+ def module_load(name: str) -> types.ModuleType:
301
+ """
302
+ Import the specified module.
303
+ """
304
+ return __import__(name, fromlist=["*"])
305
+
26
306
 
27
- functions = ['df_read',
28
- 'df_write']
307
+ def bc_load(cmdopts: types.Cmdopts, category: str):
308
+ """
309
+ Load the specified :term:`Batch Criteria`.
310
+ """
311
+ path = f"variables.{category}"
312
+ return module_load_tiered(
313
+ project=cmdopts["project"], engine=cmdopts["engine"], path=path
314
+ )
315
+
316
+
317
+ def module_load_tiered(
318
+ path: str, project: tp.Optional[str] = None, engine: tp.Optional[str] = None
319
+ ) -> types.ModuleType:
320
+ """Attempt to load the specified python module with tiered precedence.
321
+
322
+ Generally, the precedence is project -> project submodule -> engine module
323
+ -> SIERRA core module, to allow users to override SIERRA core functionality
324
+ with ease. Specifically:
325
+
326
+ #. Check if the requested module directly exists. If it does, return
327
+ it.
328
+
329
+ #. Check if the requested module is a part of a project (i.e.,
330
+ ``<project>.<path>`` exists). If it does, return it. This requires
331
+ that :envvar:`SIERRA_PLUGIN_PATH` to be set properly.
332
+
333
+ #. Check if the requested module is provided by the engine plugin (i.e.,
334
+ ``sierra.engine.<engine>.<path>`` exists). If it does, return it.
335
+
336
+ #. Check if the requested module is part of the SIERRA core (i.e.,
337
+ ``sierra.core.<path>`` exists). If it does, return it.
338
+
339
+ If no match was found using any of these, throw an error.
340
+ """
341
+ # First, see if the requested module is a project/directly exists as
342
+ # specified.
343
+ if module_exists(path):
344
+ logging.trace("Using direct path %s", path) # type: ignore
345
+ return module_load(path)
346
+
347
+ # Next, check if the requested module is part of the project plugin
348
+ if project is not None:
349
+ component_path = f"{project}.{path}"
350
+ if module_exists(component_path):
351
+ logging.trace(
352
+ "Using project component path %s", component_path # type: ignore
353
+ )
354
+ return module_load(component_path)
355
+ else:
356
+ logging.trace(
357
+ "Project component path %s does not exist", # type: ignore
358
+ component_path,
359
+ )
360
+
361
+ # If that didn't work, check the engine plugin
362
+ if engine is not None:
363
+ engine_path = f"{engine}.{path}"
364
+ if module_exists(engine_path):
365
+ logging.trace("Using engine component path %s", engine_path) # type: ignore
366
+ return module_load(engine_path)
367
+
368
+ # If that didn't work, then check the SIERRA core
369
+ core_path = f"sierra.core.{path}"
370
+ if module_exists(core_path):
371
+ logging.trace("Using SIERRA core path %s", core_path) # type: ignore
372
+ return module_load(core_path)
373
+ else:
374
+ logging.trace("SIERRA core path %s does not exist", core_path) # type: ignore
375
+
376
+ # Module does not exist
377
+ error = (
378
+ f"project: '{project}' "
379
+ f"engine: '{engine}' "
380
+ f"path: '{path}' "
381
+ f"sys.path: {sys.path}"
382
+ )
383
+ raise ImportError(error)
384
+
385
+
386
+ def storage_sanity_checks(medium: str, module) -> None:
387
+ """
388
+ Check the selected ``--storage`` plugin.
389
+ """
390
+ logging.trace("Verifying --storage=%s plugin interface", medium) # type: ignore
391
+
392
+ functions = ["df_read", "df_write", "suffixes"]
29
393
  in_module = inspect.getmembers(module, inspect.isfunction)
30
394
 
31
395
  for f in functions:
32
- assert (any(f in name for (name, _) in in_module)),\
33
- f"Storage medium plugin does not define {f}"
396
+ assert any(
397
+ f == name for (name, _) in in_module
398
+ ), f"Storage medium {medium} does not define {f}()"
34
399
 
35
400
 
36
- def exec_env_sanity_checks(module) -> None:
401
+ def expdef_sanity_checks(expdef: str, module) -> None:
37
402
  """
38
- Check the selected ``--exec-env`` plugin.
403
+ Check the selected ``--expdef`` plugin.
39
404
  """
40
- logging.trace("Verifying --exec-env plugin interface") # type: ignore
405
+ logging.trace("Verifying --expdef=%s plugin interface", expdef) # type: ignore
406
+
407
+ functions = ["root_querypath", "unpickle"]
408
+ module_funcs = inspect.getmembers(module, inspect.isfunction)
409
+ module_classes = inspect.getmembers(module, inspect.isclass)
410
+ classes = ["ExpDef", "Writer"]
411
+
412
+ for c in classes:
413
+ assert any(
414
+ c == name for (name, _) in module_classes
415
+ ), f"Expdef plugin {expdef} does not define {c}"
416
+
417
+ for f in functions:
418
+ assert any(
419
+ f == name for (name, _) in module_funcs
420
+ ), f"Expdef {expdef} does not define {f}()"
421
+
422
+
423
+ def proc_sanity_checks(proc: str, module) -> None:
424
+ """
425
+ Check the selected ``--proc`` plugins.
426
+ """
427
+ logging.trace("Verifying --proc=%s plugin interface", proc) # type: ignore
428
+
429
+ functions = ["proc_batch_exp"]
430
+ in_module = inspect.getmembers(module, inspect.isfunction)
431
+
432
+ for f in functions:
433
+ assert any(
434
+ f == name for (name, _) in in_module
435
+ ), f"Processing plugin {proc} does not define {f}()"
436
+
437
+
438
+ def prod_sanity_checks(prod: str, module) -> None:
439
+ """
440
+ Check the selected ``--prod`` plugins.
441
+ """
442
+ logging.trace("Verifying --prod=%s plugin interface", prod) # type: ignore
443
+
444
+ functions = ["proc_batch_exp"]
445
+ in_module = inspect.getmembers(module, inspect.isfunction)
446
+
447
+ for f in functions:
448
+ assert any(
449
+ f == name for (name, _) in in_module
450
+ ), f"Product plugin {prod} does not define {f}()"
451
+
452
+
453
+ def compare_sanity_checks(compare: str, module) -> None:
454
+ """
455
+ Check the selected ``--compare`` plugins.
456
+ """
457
+ logging.trace("Verifying --compare=%s plugin interface", compare) # type: ignore
458
+
459
+ functions = ["proc_exps"]
460
+ in_module = inspect.getmembers(module, inspect.isfunction)
461
+
462
+ for f in functions:
463
+ assert any(
464
+ f == name for (name, _) in in_module
465
+ ), f"Comparison plugin {compare} does not define {f}()"
466
+
467
+
468
+ def execenv_sanity_checks(execenv: str, module) -> None:
469
+ """
470
+ Check the selected ``--execenv`` plugin.
471
+ """
472
+ logging.trace("Verifying --execenv=%s plugin interface", execenv) # type: ignore
41
473
 
42
474
  in_module = inspect.getmembers(module, inspect.isclass)
43
475
 
44
- opt_classes = ['ParsedCmdlineConfigurer',
45
- 'ExpRunShellCmdsGenerator',
46
- 'ExpShellCmdsGenerator',
47
- 'ExecEnvChecker']
476
+ opt_functions = ["cmdline_postparse_configure", "execenv_check"]
477
+ opt_classes = ["ExpRunShellCmdsGenerator", "ExpShellCmdsGenerator"]
48
478
 
49
479
  for c in opt_classes:
50
- if not any(c in name for (name, _) in in_module):
51
- logging.debug(("Execution environment plugin '%s' does not define "
52
- "'%s'"),
53
- module.__name__,
54
- c)
480
+ if not any(c == name for (name, _) in in_module):
481
+ logging.debug(
482
+ (
483
+ "Execution environment plugin %s does not define "
484
+ "%s--some SIERRA functionality may not be "
485
+ "available. See docs for details."
486
+ ),
487
+ execenv,
488
+ c,
489
+ )
55
490
 
491
+ for f in opt_functions:
492
+ if not any(f in name for (name, _) in in_module):
493
+ logging.debug(
494
+ ("Execution environment plugin %s does not define %s()."),
495
+ execenv,
496
+ f,
497
+ )
56
498
 
57
- def platform_sanity_checks(module) -> None:
499
+
500
+ def engine_sanity_checks(engine: str, module) -> None:
58
501
  """
59
- Check the selected ``--platform`` plugin.
502
+ Check the selected ``--engine`` plugin.
60
503
  """
61
- logging.trace("Verifying --platform plugin interface") # type: ignore
504
+ logging.trace("Verifying --engine=%s plugin interface", engine) # type: ignore
62
505
 
63
- req_classes = ['ExpConfigurer',
64
- 'CmdlineParserGenerator'
65
- ]
506
+ req_classes = [
507
+ "ExpConfigurer",
508
+ ]
66
509
 
67
- req_functions = ['population_size_from_def',
68
- 'population_size_from_pickle',
69
- ]
510
+ req_functions = [
511
+ "population_size_from_def",
512
+ "population_size_from_pickle",
513
+ ]
70
514
 
71
- opt_classes = ['ParsedCmdlineConfigurer',
72
- 'ExpRunShellCmdsGenerator',
73
- 'ExpShellCmdsGenerator',
74
- 'ExecEnvChecker']
515
+ opt_classes = ["ExpRunShellCmdsGenerator", "ExpShellCmdsGenerator"]
75
516
 
76
- opt_functions = ['robot_prefix_extract',
77
- 'arena_dims_from_criteria']
517
+ opt_functions = [
518
+ "cmdline_postparse_configure",
519
+ "execenv_check",
520
+ "agent_prefix_extract",
521
+ "arena_dims_from_criteria",
522
+ ]
78
523
 
79
524
  in_module = inspect.getmembers(module, inspect.isclass)
80
525
 
81
526
  for c in req_classes:
82
- assert (any(c in name for (name, _) in in_module)),\
83
- f"Platform plugin '{module.__name__}' does not define '{c}'"
527
+ assert any(
528
+ c == name for (name, _) in in_module
529
+ ), f"Engine plugin {engine} does not define {c}"
84
530
 
85
531
  for f in opt_classes:
86
532
  if not any(f in name for (name, _) in in_module):
87
- logging.debug(("Platform plugin '%s' not define define '%s'"
88
- "--some SIERRA functionality may not be available. "
89
- "See docs for details."),
90
- module.__name__,
91
- f)
533
+ logging.debug(
534
+ (
535
+ "Engine plugin %s does not define %s"
536
+ "--some SIERRA functionality may not be available. "
537
+ "See docs for details."
538
+ ),
539
+ engine,
540
+ f,
541
+ )
92
542
 
93
543
  in_module = inspect.getmembers(module, inspect.isfunction)
94
544
 
95
545
  for f in req_functions:
96
- assert (any(f in name for (name, _) in in_module)),\
97
- f"Platform plugin '{module.__name__}' does not define '{f}()'"
546
+ assert any(
547
+ f == name for (name, _) in in_module
548
+ ), f"Engine plugin {engine} does not define {f}()"
98
549
 
99
550
  for f in opt_functions:
100
- if not any(f in name for (name, _) in in_module):
101
- logging.debug(("Platform plugin '%s' not define define '%s()'"
102
- "--some SIERRA functionality may not be available. "
103
- "See docs for details."),
104
- module.__name__,
105
- f)
106
-
107
-
108
- __api__ = {
109
- 'storage_sanity_checks',
110
- 'exec_env_sanity_checks',
111
- 'platform_sanity_checks'
112
- }
551
+ if not any(f == name for (name, _) in in_module):
552
+ logging.debug(
553
+ (
554
+ "Engine plugin %s does not define %s()"
555
+ "--some SIERRA functionality may not be available. "
556
+ "See docs for details."
557
+ ),
558
+ engine,
559
+ f,
560
+ )
561
+
562
+
563
+ # Singletons
564
+ pipeline = DirectoryPluginManager()
565
+ models = DirectoryPluginManager()
566
+
567
+ __all__ = [
568
+ "DirectoryPluginManager",
569
+ "module_exists",
570
+ "module_load",
571
+ "bc_load",
572
+ "module_load_tiered",
573
+ "storage_sanity_checks",
574
+ "execenv_sanity_checks",
575
+ "engine_sanity_checks",
576
+ "proc_sanity_checks",
577
+ "prod_sanity_checks",
578
+ "compare_sanity_checks",
579
+ ]
sierra/core/proc.py ADDED
@@ -0,0 +1,11 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Trampoline bindings for the various ``--proc`` plugins that come with SIERRA."""
5
+
6
+ # Core packages
7
+
8
+ # 3rd party packages
9
+
10
+ # Project packages
11
+ from sierra.core.trampoline import cmdline_parser # noqa: F401
sierra/core/prod.py ADDED
@@ -0,0 +1,11 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Trampoline bindings for the various ``--prod`` plugins that come with SIERRA."""
5
+
6
+ # Core packages
7
+
8
+ # 3rd party packages
9
+
10
+ # Project packages
11
+ from sierra.core.trampoline import cmdline_parser # noqa: F401
@@ -1,10 +1,14 @@
1
1
  # Copyright 2021 John Harwell, All rights reserved.
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
+ """Container module for things related to :term:`ROS1`."""
4
5
 
5
6
  # Core packages
6
7
 
7
8
  # 3rd party packages
8
9
 
9
10
  # Project packages
10
- from . import cmdline, callbacks, variables, generators
11
+ from . import cmdline as cmdline
12
+ from . import callbacks as callbacks
13
+ from . import variables as variables
14
+ from . import generators as generators