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.
- sierra/__init__.py +3 -3
- sierra/core/__init__.py +3 -3
- sierra/core/batchroot.py +223 -0
- sierra/core/cmdline.py +681 -1057
- sierra/core/compare.py +11 -0
- sierra/core/config.py +96 -88
- sierra/core/engine.py +306 -0
- sierra/core/execenv.py +380 -0
- sierra/core/expdef.py +11 -0
- sierra/core/experiment/__init__.py +1 -0
- sierra/core/experiment/bindings.py +150 -101
- sierra/core/experiment/definition.py +414 -245
- sierra/core/experiment/spec.py +83 -85
- sierra/core/exproot.py +44 -0
- sierra/core/generators/__init__.py +10 -0
- sierra/core/generators/experiment.py +528 -0
- sierra/core/generators/generator_factory.py +138 -137
- sierra/core/graphs/__init__.py +23 -0
- sierra/core/graphs/bcbridge.py +94 -0
- sierra/core/graphs/heatmap.py +245 -324
- sierra/core/graphs/pathset.py +27 -0
- sierra/core/graphs/schema.py +77 -0
- sierra/core/graphs/stacked_line.py +341 -0
- sierra/core/graphs/summary_line.py +506 -0
- sierra/core/logging.py +3 -2
- sierra/core/models/__init__.py +3 -1
- sierra/core/models/info.py +19 -0
- sierra/core/models/interface.py +52 -122
- sierra/core/pipeline/__init__.py +2 -5
- sierra/core/pipeline/pipeline.py +228 -126
- sierra/core/pipeline/stage1/__init__.py +10 -0
- sierra/core/pipeline/stage1/pipeline_stage1.py +45 -31
- sierra/core/pipeline/stage2/__init__.py +10 -0
- sierra/core/pipeline/stage2/pipeline_stage2.py +8 -11
- sierra/core/pipeline/stage2/runner.py +401 -0
- sierra/core/pipeline/stage3/__init__.py +12 -0
- sierra/core/pipeline/stage3/gather.py +321 -0
- sierra/core/pipeline/stage3/pipeline_stage3.py +37 -84
- sierra/core/pipeline/stage4/__init__.py +12 -2
- sierra/core/pipeline/stage4/pipeline_stage4.py +36 -354
- sierra/core/pipeline/stage5/__init__.py +12 -0
- sierra/core/pipeline/stage5/pipeline_stage5.py +33 -208
- sierra/core/pipeline/yaml.py +48 -0
- sierra/core/plugin.py +529 -62
- sierra/core/proc.py +11 -0
- sierra/core/prod.py +11 -0
- sierra/core/ros1/__init__.py +5 -1
- sierra/core/ros1/callbacks.py +22 -21
- sierra/core/ros1/cmdline.py +59 -88
- sierra/core/ros1/generators.py +159 -175
- sierra/core/ros1/variables/__init__.py +3 -0
- sierra/core/ros1/variables/exp_setup.py +122 -116
- sierra/core/startup.py +106 -76
- sierra/core/stat_kernels.py +4 -5
- sierra/core/storage.py +13 -32
- sierra/core/trampoline.py +30 -0
- sierra/core/types.py +116 -71
- sierra/core/utils.py +103 -106
- sierra/core/variables/__init__.py +1 -1
- sierra/core/variables/base_variable.py +12 -17
- sierra/core/variables/batch_criteria.py +387 -481
- sierra/core/variables/builtin.py +135 -0
- sierra/core/variables/exp_setup.py +19 -39
- sierra/core/variables/population_size.py +72 -76
- sierra/core/variables/variable_density.py +44 -68
- sierra/core/vector.py +1 -1
- sierra/main.py +256 -88
- sierra/plugins/__init__.py +119 -0
- sierra/plugins/compare/__init__.py +14 -0
- sierra/plugins/compare/graphs/__init__.py +19 -0
- sierra/plugins/compare/graphs/cmdline.py +120 -0
- sierra/plugins/compare/graphs/comparator.py +291 -0
- sierra/plugins/compare/graphs/inter_controller.py +531 -0
- sierra/plugins/compare/graphs/inter_scenario.py +297 -0
- sierra/plugins/compare/graphs/namecalc.py +53 -0
- sierra/plugins/compare/graphs/outputroot.py +73 -0
- sierra/plugins/compare/graphs/plugin.py +147 -0
- sierra/plugins/compare/graphs/preprocess.py +172 -0
- sierra/plugins/compare/graphs/schema.py +37 -0
- sierra/plugins/engine/__init__.py +14 -0
- sierra/plugins/engine/argos/__init__.py +18 -0
- sierra/plugins/{platform → engine}/argos/cmdline.py +144 -151
- sierra/plugins/{platform/argos/variables → engine/argos/generators}/__init__.py +5 -0
- sierra/plugins/engine/argos/generators/engine.py +394 -0
- sierra/plugins/engine/argos/plugin.py +393 -0
- sierra/plugins/{platform/argos/generators → engine/argos/variables}/__init__.py +5 -0
- sierra/plugins/engine/argos/variables/arena_shape.py +183 -0
- sierra/plugins/engine/argos/variables/cameras.py +240 -0
- sierra/plugins/engine/argos/variables/constant_density.py +112 -0
- sierra/plugins/engine/argos/variables/exp_setup.py +82 -0
- sierra/plugins/{platform → engine}/argos/variables/physics_engines.py +83 -87
- sierra/plugins/engine/argos/variables/population_constant_density.py +178 -0
- sierra/plugins/engine/argos/variables/population_size.py +115 -0
- sierra/plugins/engine/argos/variables/population_variable_density.py +123 -0
- sierra/plugins/engine/argos/variables/rendering.py +108 -0
- sierra/plugins/engine/ros1gazebo/__init__.py +18 -0
- sierra/plugins/engine/ros1gazebo/cmdline.py +175 -0
- sierra/plugins/{platform/ros1robot → engine/ros1gazebo}/generators/__init__.py +5 -0
- sierra/plugins/engine/ros1gazebo/generators/engine.py +125 -0
- sierra/plugins/engine/ros1gazebo/plugin.py +404 -0
- sierra/plugins/engine/ros1gazebo/variables/__init__.py +15 -0
- sierra/plugins/engine/ros1gazebo/variables/population_size.py +214 -0
- sierra/plugins/engine/ros1robot/__init__.py +18 -0
- sierra/plugins/engine/ros1robot/cmdline.py +159 -0
- sierra/plugins/{platform/ros1gazebo → engine/ros1robot}/generators/__init__.py +4 -0
- sierra/plugins/engine/ros1robot/generators/engine.py +95 -0
- sierra/plugins/engine/ros1robot/plugin.py +410 -0
- sierra/plugins/{hpc/local → engine/ros1robot/variables}/__init__.py +5 -0
- sierra/plugins/engine/ros1robot/variables/population_size.py +146 -0
- sierra/plugins/execenv/__init__.py +11 -0
- sierra/plugins/execenv/hpc/__init__.py +18 -0
- sierra/plugins/execenv/hpc/adhoc/__init__.py +18 -0
- sierra/plugins/execenv/hpc/adhoc/cmdline.py +30 -0
- sierra/plugins/execenv/hpc/adhoc/plugin.py +131 -0
- sierra/plugins/execenv/hpc/cmdline.py +137 -0
- sierra/plugins/execenv/hpc/local/__init__.py +18 -0
- sierra/plugins/execenv/hpc/local/cmdline.py +31 -0
- sierra/plugins/execenv/hpc/local/plugin.py +145 -0
- sierra/plugins/execenv/hpc/pbs/__init__.py +18 -0
- sierra/plugins/execenv/hpc/pbs/cmdline.py +30 -0
- sierra/plugins/execenv/hpc/pbs/plugin.py +121 -0
- sierra/plugins/execenv/hpc/slurm/__init__.py +18 -0
- sierra/plugins/execenv/hpc/slurm/cmdline.py +30 -0
- sierra/plugins/execenv/hpc/slurm/plugin.py +133 -0
- sierra/plugins/execenv/prefectserver/__init__.py +18 -0
- sierra/plugins/execenv/prefectserver/cmdline.py +66 -0
- sierra/plugins/execenv/prefectserver/dockerremote/__init__.py +18 -0
- sierra/plugins/execenv/prefectserver/dockerremote/cmdline.py +66 -0
- sierra/plugins/execenv/prefectserver/dockerremote/plugin.py +132 -0
- sierra/plugins/execenv/prefectserver/flow.py +66 -0
- sierra/plugins/execenv/prefectserver/local/__init__.py +18 -0
- sierra/plugins/execenv/prefectserver/local/cmdline.py +29 -0
- sierra/plugins/execenv/prefectserver/local/plugin.py +133 -0
- sierra/plugins/{hpc/adhoc → execenv/robot}/__init__.py +1 -0
- sierra/plugins/execenv/robot/turtlebot3/__init__.py +18 -0
- sierra/plugins/execenv/robot/turtlebot3/plugin.py +204 -0
- sierra/plugins/expdef/__init__.py +14 -0
- sierra/plugins/expdef/json/__init__.py +14 -0
- sierra/plugins/expdef/json/plugin.py +504 -0
- sierra/plugins/expdef/xml/__init__.py +14 -0
- sierra/plugins/expdef/xml/plugin.py +386 -0
- sierra/{core/hpc → plugins/proc}/__init__.py +1 -1
- sierra/plugins/proc/collate/__init__.py +15 -0
- sierra/plugins/proc/collate/cmdline.py +47 -0
- sierra/plugins/proc/collate/plugin.py +271 -0
- sierra/plugins/proc/compress/__init__.py +18 -0
- sierra/plugins/proc/compress/cmdline.py +47 -0
- sierra/plugins/proc/compress/plugin.py +123 -0
- sierra/plugins/proc/decompress/__init__.py +18 -0
- sierra/plugins/proc/decompress/plugin.py +96 -0
- sierra/plugins/proc/imagize/__init__.py +15 -0
- sierra/plugins/proc/imagize/cmdline.py +49 -0
- sierra/plugins/proc/imagize/plugin.py +270 -0
- sierra/plugins/proc/modelrunner/__init__.py +16 -0
- sierra/plugins/proc/modelrunner/plugin.py +250 -0
- sierra/plugins/proc/statistics/__init__.py +15 -0
- sierra/plugins/proc/statistics/cmdline.py +64 -0
- sierra/plugins/proc/statistics/plugin.py +390 -0
- sierra/plugins/{hpc → prod}/__init__.py +1 -0
- sierra/plugins/prod/graphs/__init__.py +18 -0
- sierra/plugins/prod/graphs/cmdline.py +269 -0
- sierra/plugins/prod/graphs/collate.py +279 -0
- sierra/plugins/prod/graphs/inter/__init__.py +13 -0
- sierra/plugins/prod/graphs/inter/generate.py +83 -0
- sierra/plugins/prod/graphs/inter/heatmap.py +86 -0
- sierra/plugins/prod/graphs/inter/line.py +134 -0
- sierra/plugins/prod/graphs/intra/__init__.py +15 -0
- sierra/plugins/prod/graphs/intra/generate.py +202 -0
- sierra/plugins/prod/graphs/intra/heatmap.py +74 -0
- sierra/plugins/prod/graphs/intra/line.py +114 -0
- sierra/plugins/prod/graphs/plugin.py +103 -0
- sierra/plugins/prod/graphs/targets.py +63 -0
- sierra/plugins/prod/render/__init__.py +18 -0
- sierra/plugins/prod/render/cmdline.py +72 -0
- sierra/plugins/prod/render/plugin.py +282 -0
- sierra/plugins/storage/__init__.py +5 -0
- sierra/plugins/storage/arrow/__init__.py +18 -0
- sierra/plugins/storage/arrow/plugin.py +38 -0
- sierra/plugins/storage/csv/__init__.py +9 -0
- sierra/plugins/storage/csv/plugin.py +12 -5
- sierra/version.py +3 -2
- sierra_research-1.5.0.dist-info/METADATA +238 -0
- sierra_research-1.5.0.dist-info/RECORD +186 -0
- {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info}/WHEEL +1 -2
- sierra/core/experiment/xml.py +0 -454
- sierra/core/generators/controller_generator_parser.py +0 -34
- sierra/core/generators/exp_creator.py +0 -351
- sierra/core/generators/exp_generators.py +0 -142
- sierra/core/graphs/scatterplot2D.py +0 -109
- sierra/core/graphs/stacked_line_graph.py +0 -249
- sierra/core/graphs/stacked_surface_graph.py +0 -220
- sierra/core/graphs/summary_line_graph.py +0 -369
- sierra/core/hpc/cmdline.py +0 -142
- sierra/core/models/graphs.py +0 -87
- sierra/core/pipeline/stage2/exp_runner.py +0 -286
- sierra/core/pipeline/stage3/imagizer.py +0 -149
- sierra/core/pipeline/stage3/run_collator.py +0 -317
- sierra/core/pipeline/stage3/statistics_calculator.py +0 -478
- sierra/core/pipeline/stage4/graph_collator.py +0 -319
- sierra/core/pipeline/stage4/inter_exp_graph_generator.py +0 -240
- sierra/core/pipeline/stage4/intra_exp_graph_generator.py +0 -317
- sierra/core/pipeline/stage4/model_runner.py +0 -168
- sierra/core/pipeline/stage4/rendering.py +0 -283
- sierra/core/pipeline/stage4/yaml_config_loader.py +0 -103
- sierra/core/pipeline/stage5/inter_scenario_comparator.py +0 -328
- sierra/core/pipeline/stage5/intra_scenario_comparator.py +0 -989
- sierra/core/platform.py +0 -493
- sierra/core/plugin_manager.py +0 -369
- sierra/core/root_dirpath_generator.py +0 -241
- sierra/plugins/hpc/adhoc/plugin.py +0 -125
- sierra/plugins/hpc/local/plugin.py +0 -81
- sierra/plugins/hpc/pbs/__init__.py +0 -9
- sierra/plugins/hpc/pbs/plugin.py +0 -126
- sierra/plugins/hpc/slurm/__init__.py +0 -9
- sierra/plugins/hpc/slurm/plugin.py +0 -130
- sierra/plugins/platform/__init__.py +0 -9
- sierra/plugins/platform/argos/__init__.py +0 -9
- sierra/plugins/platform/argos/generators/platform_generators.py +0 -383
- sierra/plugins/platform/argos/plugin.py +0 -337
- sierra/plugins/platform/argos/variables/arena_shape.py +0 -145
- sierra/plugins/platform/argos/variables/cameras.py +0 -243
- sierra/plugins/platform/argos/variables/constant_density.py +0 -136
- sierra/plugins/platform/argos/variables/exp_setup.py +0 -113
- sierra/plugins/platform/argos/variables/population_constant_density.py +0 -175
- sierra/plugins/platform/argos/variables/population_size.py +0 -102
- sierra/plugins/platform/argos/variables/population_variable_density.py +0 -132
- sierra/plugins/platform/argos/variables/rendering.py +0 -104
- sierra/plugins/platform/ros1gazebo/__init__.py +0 -9
- sierra/plugins/platform/ros1gazebo/cmdline.py +0 -213
- sierra/plugins/platform/ros1gazebo/generators/platform_generators.py +0 -137
- sierra/plugins/platform/ros1gazebo/plugin.py +0 -335
- sierra/plugins/platform/ros1gazebo/variables/__init__.py +0 -10
- sierra/plugins/platform/ros1gazebo/variables/population_size.py +0 -204
- sierra/plugins/platform/ros1robot/__init__.py +0 -9
- sierra/plugins/platform/ros1robot/cmdline.py +0 -175
- sierra/plugins/platform/ros1robot/generators/platform_generators.py +0 -112
- sierra/plugins/platform/ros1robot/plugin.py +0 -373
- sierra/plugins/platform/ros1robot/variables/__init__.py +0 -10
- sierra/plugins/platform/ros1robot/variables/population_size.py +0 -146
- sierra/plugins/robot/__init__.py +0 -9
- sierra/plugins/robot/turtlebot3/__init__.py +0 -9
- sierra/plugins/robot/turtlebot3/plugin.py +0 -194
- sierra_research-1.3.6.data/data/share/man/man1/sierra-cli.1 +0 -2349
- sierra_research-1.3.6.data/data/share/man/man7/sierra-examples.7 +0 -488
- sierra_research-1.3.6.data/data/share/man/man7/sierra-exec-envs.7 +0 -331
- sierra_research-1.3.6.data/data/share/man/man7/sierra-glossary.7 +0 -285
- sierra_research-1.3.6.data/data/share/man/man7/sierra-platforms.7 +0 -358
- sierra_research-1.3.6.data/data/share/man/man7/sierra-usage.7 +0 -725
- sierra_research-1.3.6.data/data/share/man/man7/sierra.7 +0 -78
- sierra_research-1.3.6.dist-info/METADATA +0 -500
- sierra_research-1.3.6.dist-info/RECORD +0 -133
- sierra_research-1.3.6.dist-info/top_level.txt +0 -1
- {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info}/entry_points.txt +0 -0
- {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,386 @@
|
|
1
|
+
# Copyright 2024 John Harwell, All rights reserved.
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MIT
|
4
|
+
"""Plugin for parsing and manipulating template input files in XML format."""
|
5
|
+
|
6
|
+
# Core packages
|
7
|
+
import pathlib
|
8
|
+
import logging
|
9
|
+
import xml.etree.ElementTree as ET
|
10
|
+
import typing as tp
|
11
|
+
|
12
|
+
# 3rd party packages
|
13
|
+
import implements
|
14
|
+
|
15
|
+
# Project packages
|
16
|
+
from sierra.core.experiment import definition
|
17
|
+
from sierra.core import types
|
18
|
+
|
19
|
+
|
20
|
+
class Writer:
|
21
|
+
"""Write the XML experiment to the filesystem according to configuration.
|
22
|
+
|
23
|
+
More than one file may be written, as specified.
|
24
|
+
"""
|
25
|
+
|
26
|
+
def __init__(self, tree: ET.ElementTree) -> None:
|
27
|
+
self.tree = tree
|
28
|
+
self.root = tree.getroot()
|
29
|
+
self.logger = logging.getLogger(__name__)
|
30
|
+
|
31
|
+
def __call__(
|
32
|
+
self, write_config: definition.WriterConfig, base_opath: pathlib.Path
|
33
|
+
) -> None:
|
34
|
+
for config in write_config.values:
|
35
|
+
self._write_with_config(base_opath, config)
|
36
|
+
|
37
|
+
def _write_with_config(
|
38
|
+
self, base_opath: tp.Union[pathlib.Path, str], config: dict
|
39
|
+
) -> None:
|
40
|
+
tree, src_root, opath = self._prepare_tree(pathlib.Path(base_opath), config)
|
41
|
+
|
42
|
+
if tree is None:
|
43
|
+
self.logger.warning(
|
44
|
+
"Cannot write non-existent tree@'%s' to '%s'", src_root, opath
|
45
|
+
)
|
46
|
+
return
|
47
|
+
|
48
|
+
self.logger.trace("Write tree@%s to %s", src_root, opath) # type: ignore
|
49
|
+
|
50
|
+
# Renaming tree root is not required
|
51
|
+
if "rename_to" in config and config["rename_to"] is not None:
|
52
|
+
tree.tag = config["rename_to"]
|
53
|
+
self.logger.trace(
|
54
|
+
"Rename tree root -> %s", config["rename_to"] # type: ignore
|
55
|
+
)
|
56
|
+
|
57
|
+
# Adding new children not required
|
58
|
+
if all(
|
59
|
+
k in config and config[k] is not None
|
60
|
+
for k in ["new_children_parent", "new_children"]
|
61
|
+
):
|
62
|
+
self._add_new_children(config, tree)
|
63
|
+
|
64
|
+
# Grafts are not required
|
65
|
+
if all(
|
66
|
+
k in config and config[k] is not None
|
67
|
+
for k in ["child_grafts_parent", "child_grafts"]
|
68
|
+
):
|
69
|
+
self._add_grafts(config, tree)
|
70
|
+
|
71
|
+
to_write = ET.ElementTree(tree)
|
72
|
+
|
73
|
+
ET.indent(to_write.getroot(), space="\t", level=0)
|
74
|
+
ET.indent(to_write, space="\t", level=0)
|
75
|
+
to_write.write(opath, encoding="utf-8")
|
76
|
+
|
77
|
+
def _add_grafts(self, config: dict, tree: ET.Element) -> None:
|
78
|
+
|
79
|
+
graft_parent = tree.find(config["child_grafts_parent"])
|
80
|
+
assert graft_parent is not None, f"Bad parent '{graft_parent}' for grafting"
|
81
|
+
|
82
|
+
for g in config["child_grafts"]:
|
83
|
+
self.logger.trace(
|
84
|
+
"Graft tree@'%s' as child under '%s'", g, graft_parent # type: ignore
|
85
|
+
)
|
86
|
+
elt = self.root.find(g)
|
87
|
+
graft_parent.append(elt)
|
88
|
+
|
89
|
+
def _add_new_children(self, config: dict, tree: ET.ElementTree) -> None:
|
90
|
+
"""Given the experiment definition, add new children as configured.
|
91
|
+
|
92
|
+
We operate on the whole definition in-situ, rather than creating a new
|
93
|
+
subtree with all the children because that is less error prone in terms
|
94
|
+
of grafting the new subtree back into the experiment definition.
|
95
|
+
"""
|
96
|
+
|
97
|
+
parent = tree.find(config["new_children_parent"])
|
98
|
+
|
99
|
+
for spec in config["new_children"]:
|
100
|
+
if spec.as_root_elt:
|
101
|
+
# Special case: Adding children to an empty tree
|
102
|
+
tree = ET.Element(spec.path, spec.attr)
|
103
|
+
continue
|
104
|
+
|
105
|
+
elt = parent.find(spec.path)
|
106
|
+
|
107
|
+
assert elt is not None, (
|
108
|
+
f"Could not find parent '{spec.path}' of new child element '{spec.tag}' "
|
109
|
+
"to add"
|
110
|
+
)
|
111
|
+
|
112
|
+
ET.SubElement(elt, spec.tag, spec.attr)
|
113
|
+
|
114
|
+
self.logger.trace(
|
115
|
+
"Create child element '%s' under '%s'", # type: ignore
|
116
|
+
spec.tag,
|
117
|
+
spec.path,
|
118
|
+
)
|
119
|
+
|
120
|
+
def _prepare_tree(
|
121
|
+
self, base_opath: pathlib.Path, config: dict
|
122
|
+
) -> tp.Tuple[tp.Optional[ET.Element], str, pathlib.Path]:
|
123
|
+
assert "src_parent" in config, "'src_parent' key is required"
|
124
|
+
assert (
|
125
|
+
"src_tag" in config and config["src_tag"] is not None
|
126
|
+
), "'src_tag' key is required"
|
127
|
+
|
128
|
+
if config["src_parent"] is None:
|
129
|
+
src_root = config["src_tag"]
|
130
|
+
else:
|
131
|
+
src_root = "{0}/{1}".format(config["src_parent"], config["src_tag"])
|
132
|
+
|
133
|
+
tree_out = self.tree.getroot().find(src_root)
|
134
|
+
|
135
|
+
# Customizing the output write path is not required
|
136
|
+
opath = base_opath
|
137
|
+
if "opath_leaf" in config and config["opath_leaf"] is not None:
|
138
|
+
opath = base_opath.with_name(base_opath.name + str(config["opath_leaf"]))
|
139
|
+
|
140
|
+
self.logger.trace(
|
141
|
+
"Preparing subtree write of '%s' to '%s', root='%s'",
|
142
|
+
tree_out,
|
143
|
+
opath,
|
144
|
+
tree_out,
|
145
|
+
)
|
146
|
+
|
147
|
+
return (tree_out, src_root, opath)
|
148
|
+
|
149
|
+
|
150
|
+
def root_querypath() -> str:
|
151
|
+
return "."
|
152
|
+
|
153
|
+
|
154
|
+
@implements.implements(definition.BaseExpDef)
|
155
|
+
class ExpDef:
|
156
|
+
"""Read, write, and modify parsed XML files into experiment definitions."""
|
157
|
+
|
158
|
+
def __init__(
|
159
|
+
self,
|
160
|
+
input_fpath: pathlib.Path,
|
161
|
+
write_config: tp.Optional[definition.WriterConfig] = None,
|
162
|
+
) -> None:
|
163
|
+
|
164
|
+
self.write_config = write_config
|
165
|
+
self.input_fpath = input_fpath
|
166
|
+
self.tree = ET.parse(self.input_fpath)
|
167
|
+
self.root = self.tree.getroot()
|
168
|
+
self.element_adds = definition.ElementAddList()
|
169
|
+
self.attr_chgs = definition.AttrChangeSet()
|
170
|
+
|
171
|
+
self.logger = logging.getLogger(__name__)
|
172
|
+
|
173
|
+
def n_mods(self) -> tp.Tuple[int, int]:
|
174
|
+
return len(self.element_adds), len(self.attr_chgs)
|
175
|
+
|
176
|
+
def write_config_set(self, config: definition.WriterConfig) -> None:
|
177
|
+
"""Set the write config for the object.
|
178
|
+
|
179
|
+
Provided for cases in which the configuration is dependent on whether or
|
180
|
+
not certain tags/element are present in the input file.
|
181
|
+
|
182
|
+
"""
|
183
|
+
self.write_config = config
|
184
|
+
|
185
|
+
def write(self, base_opath: pathlib.Path) -> None:
|
186
|
+
assert self.write_config is not None, "Can't write without write config"
|
187
|
+
|
188
|
+
writer = Writer(self.tree)
|
189
|
+
writer(self.write_config, base_opath)
|
190
|
+
|
191
|
+
def flatten(self, keys: tp.List[str]) -> None:
|
192
|
+
raise NotImplementedError("The XML expdef plugin does not support flattening")
|
193
|
+
|
194
|
+
def attr_get(self, path: str, attr: str) -> tp.Optional[tp.Union[str, int, float]]:
|
195
|
+
el = self.root.find(path)
|
196
|
+
if el is not None and attr in el.attrib:
|
197
|
+
return el.attrib[attr]
|
198
|
+
return None
|
199
|
+
|
200
|
+
def attr_change(
|
201
|
+
self,
|
202
|
+
path: str,
|
203
|
+
attr: str,
|
204
|
+
value: tp.Union[str, int, float],
|
205
|
+
noprint: bool = False,
|
206
|
+
) -> bool:
|
207
|
+
el = self.root.find(path)
|
208
|
+
if el is None:
|
209
|
+
if not noprint:
|
210
|
+
self.logger.warning("Parent element '%s' not found", path)
|
211
|
+
return False
|
212
|
+
|
213
|
+
if attr not in el.attrib:
|
214
|
+
if not noprint:
|
215
|
+
self.logger.warning("Attribute '%s' not found in path '%s'", attr, path)
|
216
|
+
return False
|
217
|
+
|
218
|
+
el.attrib[attr] = value
|
219
|
+
self.logger.trace(
|
220
|
+
"Modify attr: '%s/%s' = '%s'", path, attr, value # type: ignore
|
221
|
+
)
|
222
|
+
|
223
|
+
self.attr_chgs.add(definition.AttrChange(path, attr, str(value)))
|
224
|
+
return True
|
225
|
+
|
226
|
+
def attr_add(
|
227
|
+
self,
|
228
|
+
path: str,
|
229
|
+
attr: str,
|
230
|
+
value: tp.Union[str, int, float],
|
231
|
+
noprint: bool = False,
|
232
|
+
) -> bool:
|
233
|
+
el = self.root.find(path)
|
234
|
+
if el is None:
|
235
|
+
if not noprint:
|
236
|
+
self.logger.warning("Parent element '%s' not found", path)
|
237
|
+
return False
|
238
|
+
|
239
|
+
if attr in el.attrib:
|
240
|
+
if not noprint:
|
241
|
+
self.logger.warning("Attribute '%s' already in path '%s'", attr, path)
|
242
|
+
return False
|
243
|
+
|
244
|
+
el.set(attr, value)
|
245
|
+
self.logger.trace(
|
246
|
+
"Add new attribute: '%s/%s' = '%s'", path, attr, value # type: ignore
|
247
|
+
)
|
248
|
+
self.attr_chgs.add(definition.AttrChange(path, attr, str(value)))
|
249
|
+
return True
|
250
|
+
|
251
|
+
def has_element(self, path: str) -> bool:
|
252
|
+
return self.root.find(path) is not None
|
253
|
+
|
254
|
+
def has_attr(self, path: str, attr: str) -> bool:
|
255
|
+
el = self.root.find(path)
|
256
|
+
if el is None:
|
257
|
+
return False
|
258
|
+
return attr in el.attrib
|
259
|
+
|
260
|
+
def element_change(self, path: str, tag: str, value: str) -> bool:
|
261
|
+
el = self.root.find(path)
|
262
|
+
if el is None:
|
263
|
+
self.logger.warning("Parent element '%s' not found", path)
|
264
|
+
return False
|
265
|
+
|
266
|
+
for child in el:
|
267
|
+
if child.tag == tag:
|
268
|
+
child.tag = value
|
269
|
+
self.logger.trace(
|
270
|
+
"Modify element: '%s/%s' = '%s'", path, tag, value # type: ignore
|
271
|
+
)
|
272
|
+
return True
|
273
|
+
|
274
|
+
self.logger.warning("No such element '%s' found in '%s'", tag, path)
|
275
|
+
return False
|
276
|
+
|
277
|
+
def element_remove(self, path: str, tag: str, noprint: bool = False) -> bool:
|
278
|
+
parent = self.root.find(path)
|
279
|
+
|
280
|
+
if parent is None:
|
281
|
+
if not noprint:
|
282
|
+
self.logger.warning("Parent node '%s' not found", path)
|
283
|
+
return False
|
284
|
+
|
285
|
+
victim = parent.find(tag)
|
286
|
+
if victim is None:
|
287
|
+
if not noprint:
|
288
|
+
self.logger.warning("No victim '%s' found in parent '%s'", tag, path)
|
289
|
+
return False
|
290
|
+
|
291
|
+
parent.remove(victim)
|
292
|
+
return True
|
293
|
+
|
294
|
+
def element_remove_all(self, path: str, tag: str, noprint: bool = False) -> bool:
|
295
|
+
|
296
|
+
parent = self.root.find(path)
|
297
|
+
|
298
|
+
if parent is None:
|
299
|
+
if not noprint:
|
300
|
+
self.logger.warning("Parent element '%s' not found", path)
|
301
|
+
return False
|
302
|
+
|
303
|
+
victims = parent.findall(tag)
|
304
|
+
if not victims:
|
305
|
+
if not noprint:
|
306
|
+
self.logger.warning(
|
307
|
+
"No victims matching '%s' found in parent '%s'", tag, path
|
308
|
+
)
|
309
|
+
return False
|
310
|
+
|
311
|
+
for victim in victims:
|
312
|
+
parent.remove(victim)
|
313
|
+
self.logger.trace(
|
314
|
+
"Remove matching element: '%s/%s'", path, tag # type: ignore
|
315
|
+
)
|
316
|
+
|
317
|
+
return True
|
318
|
+
|
319
|
+
def element_add(
|
320
|
+
self,
|
321
|
+
path: str,
|
322
|
+
tag: str,
|
323
|
+
attr: tp.Optional[types.StrDict] = None,
|
324
|
+
allow_dup: bool = True,
|
325
|
+
noprint: bool = False,
|
326
|
+
) -> bool:
|
327
|
+
"""
|
328
|
+
Add tag name as a child element of enclosing parent.
|
329
|
+
"""
|
330
|
+
parent = self.root.find(path)
|
331
|
+
|
332
|
+
if parent is None:
|
333
|
+
if not noprint:
|
334
|
+
self.logger.warning("Parent element '%s' not found", path)
|
335
|
+
return False
|
336
|
+
|
337
|
+
if not allow_dup:
|
338
|
+
if parent.find(tag) is not None:
|
339
|
+
if not noprint:
|
340
|
+
self.logger.warning(
|
341
|
+
"Child element '%s' already in parent '%s'", tag, path
|
342
|
+
)
|
343
|
+
return False
|
344
|
+
|
345
|
+
ET.SubElement(parent, tag, attrib=attr if attr else {})
|
346
|
+
self.logger.trace(
|
347
|
+
"Add new unique element: '%s/%s' = '%s'", # type: ignore
|
348
|
+
path,
|
349
|
+
tag,
|
350
|
+
str(attr),
|
351
|
+
)
|
352
|
+
else:
|
353
|
+
# Use ET.Element instead of ET.SubElement so that child nodes with
|
354
|
+
# the same 'tag' don't overwrite each other.
|
355
|
+
child = ET.Element(tag, attrib=attr if attr else {})
|
356
|
+
parent.append(child)
|
357
|
+
self.logger.trace(
|
358
|
+
"Add new element: '%s/%s' = '%s'", path, tag, str(attr) # type: ignore
|
359
|
+
)
|
360
|
+
|
361
|
+
self.element_adds.append(definition.ElementAdd(path, tag, attr, allow_dup))
|
362
|
+
return True
|
363
|
+
|
364
|
+
|
365
|
+
def unpickle(
|
366
|
+
fpath: pathlib.Path,
|
367
|
+
) -> tp.Optional[tp.Union[definition.AttrChangeSet, definition.ElementAddList]]:
|
368
|
+
"""Unickle all XML modifications from the pickle file at the path.
|
369
|
+
|
370
|
+
You don't know how many there are, so go until you get an exception.
|
371
|
+
|
372
|
+
"""
|
373
|
+
try:
|
374
|
+
return definition.AttrChangeSet.unpickle(fpath)
|
375
|
+
except (EOFError, TypeError):
|
376
|
+
pass
|
377
|
+
|
378
|
+
try:
|
379
|
+
return definition.ElementAddList.unpickle(fpath)
|
380
|
+
except EOFError:
|
381
|
+
pass
|
382
|
+
|
383
|
+
raise NotImplementedError
|
384
|
+
|
385
|
+
|
386
|
+
__all__ = ["ExpDef", "unpickle"]
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2025 John Harwell, All rights reserved.
|
3
|
+
#
|
4
|
+
# SPDX-License Identifier: MIT
|
5
|
+
#
|
6
|
+
"""Container module for the :term:`Data Collation` processing plugin."""
|
7
|
+
# Core packages
|
8
|
+
|
9
|
+
# 3rd party packages
|
10
|
+
|
11
|
+
# Project packages
|
12
|
+
|
13
|
+
|
14
|
+
def sierra_plugin_type() -> str:
|
15
|
+
return "pipeline"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2025 John Harwell, All rights reserved.
|
3
|
+
#
|
4
|
+
# SPDX-License Identifier: MIT
|
5
|
+
#
|
6
|
+
|
7
|
+
# Core packages
|
8
|
+
import typing as tp
|
9
|
+
import argparse
|
10
|
+
|
11
|
+
# 3rd party packages
|
12
|
+
|
13
|
+
# Project packages
|
14
|
+
from sierra.core import types
|
15
|
+
from sierra.plugins import PluginCmdline
|
16
|
+
|
17
|
+
|
18
|
+
def build(
|
19
|
+
parents: tp.List[argparse.ArgumentParser], stages: tp.List[int]
|
20
|
+
) -> PluginCmdline:
|
21
|
+
"""
|
22
|
+
Get a cmdline parser supporting the ``proc.collate`` processing plugin.
|
23
|
+
"""
|
24
|
+
cmdline = PluginCmdline(parents, stages)
|
25
|
+
cmdline.multistage.add_argument(
|
26
|
+
"--skip-collate",
|
27
|
+
help="""
|
28
|
+
Specify that no collation of data across experiments within a batch
|
29
|
+
(stage 4) or across runs within an experiment (stage 3) should be
|
30
|
+
performed. Useful if collation takes a long time and multiple
|
31
|
+
types of stage 4 outputs are desired. Collation is generally
|
32
|
+
idempotent unless you change the stage3 options (YMMV).
|
33
|
+
"""
|
34
|
+
+ cmdline.stage_usage_doc([3, 4]),
|
35
|
+
action="store_true",
|
36
|
+
)
|
37
|
+
return cmdline
|
38
|
+
|
39
|
+
|
40
|
+
def to_cmdopts(args: argparse.Namespace) -> types.Cmdopts:
|
41
|
+
return {
|
42
|
+
"skip_collate": args.skip_collate,
|
43
|
+
}
|
44
|
+
|
45
|
+
|
46
|
+
def sphinx_cmdline_multistage():
|
47
|
+
return build([], [3, 4]).parser
|