dynamic-systems-testing-framework 1.0.1__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.
- dstf/__init__.py +41 -0
- dstf/__main__.py +10 -0
- dstf/authoring/__init__.py +13 -0
- dstf/authoring/builtin/__init__.py +32 -0
- dstf/authoring/builtin/accept.py +205 -0
- dstf/authoring/builtin/edit_criteria.py +479 -0
- dstf/authoring/builtin/edit_family.py +228 -0
- dstf/authoring/builtin/edit_on_plot.py +1354 -0
- dstf/authoring/builtin/edit_tree.py +1399 -0
- dstf/authoring/builtin/enqueue.py +144 -0
- dstf/authoring/capabilities.py +463 -0
- dstf/authoring/errors.py +49 -0
- dstf/authoring/family_rows.py +526 -0
- dstf/authoring/forms.py +631 -0
- dstf/authoring/layout.py +104 -0
- dstf/authoring/live_rows.py +337 -0
- dstf/authoring/live_runs.py +385 -0
- dstf/authoring/needle.py +127 -0
- dstf/authoring/picker_rows.py +92 -0
- dstf/authoring/queue_rows.py +354 -0
- dstf/authoring/session.py +3016 -0
- dstf/authoring/shell/__init__.py +9 -0
- dstf/authoring/shell/actions.py +702 -0
- dstf/authoring/shell/app.py +53 -0
- dstf/authoring/shell/drawer_controls.py +190 -0
- dstf/authoring/shell/family_fold.py +468 -0
- dstf/authoring/shell/figure_hook.py +249 -0
- dstf/authoring/shell/grips.js +1000 -0
- dstf/authoring/shell/header.py +783 -0
- dstf/authoring/shell/live_view.py +93 -0
- dstf/authoring/shell/picker.py +267 -0
- dstf/authoring/shell/placing.py +122 -0
- dstf/authoring/shell/plot_card.py +1171 -0
- dstf/authoring/shell/queue_panel.py +433 -0
- dstf/authoring/shell/rail.py +593 -0
- dstf/authoring/shell/shell.py +528 -0
- dstf/authoring/shell/stats_card.py +501 -0
- dstf/authoring/shell/strip.py +204 -0
- dstf/authoring/shell/style.py +721 -0
- dstf/authoring/shell/suite.py +277 -0
- dstf/authoring/shell/suite_grid.js +414 -0
- dstf/authoring/shell/suite_grid.py +373 -0
- dstf/authoring/shell/tab_theme.py +110 -0
- dstf/authoring/shell/tabstate.py +146 -0
- dstf/authoring/shell/test_list.py +179 -0
- dstf/authoring/shell/test_page.py +1404 -0
- dstf/authoring/shell/tree_card.py +1171 -0
- dstf/authoring/shell/variables_drawer.py +589 -0
- dstf/authoring/shell/widgets.py +188 -0
- dstf/authoring/situation.py +287 -0
- dstf/authoring/spec_document.py +215 -0
- dstf/authoring/spec_preview.py +83 -0
- dstf/authoring/suite_rows.py +99 -0
- dstf/authoring/tree_move.py +516 -0
- dstf/authoring/tree_pointer.py +33 -0
- dstf/authoring/tree_text.py +61 -0
- dstf/authoring/typed_number.js +64 -0
- dstf/authoring/typed_number.py +76 -0
- dstf/authoring/unscorable.py +141 -0
- dstf/authoring/variable_rows.py +216 -0
- dstf/backends/__init__.py +283 -0
- dstf/backends/base.py +1117 -0
- dstf/backends/common/__init__.py +1 -0
- dstf/backends/common/json_driver.py +484 -0
- dstf/backends/common/live_mat.py +369 -0
- dstf/backends/common/mat_reader.py +632 -0
- dstf/backends/common/proc_kill.py +238 -0
- dstf/backends/common/proc_output.py +20 -0
- dstf/backends/common/result_files.py +109 -0
- dstf/backends/common/result_variables.py +329 -0
- dstf/backends/common/statistics.py +39 -0
- dstf/backends/cross_backend.py +193 -0
- dstf/backends/dymola/__init__.py +5 -0
- dstf/backends/dymola/backend.py +776 -0
- dstf/backends/dymola/interface_loader.py +241 -0
- dstf/backends/dymola/log_parser.py +396 -0
- dstf/backends/dymola/persistent_backend.py +949 -0
- dstf/backends/fmpy/__init__.py +11 -0
- dstf/backends/fmpy/backend.py +443 -0
- dstf/backends/julia/__init__.py +27 -0
- dstf/backends/julia/backend.py +156 -0
- dstf/backends/julia/persistent_backend.py +541 -0
- dstf/backends/julia/run_persistent.jl +167 -0
- dstf/backends/julia/run_test.jl +146 -0
- dstf/backends/openmodelica/__init__.py +21 -0
- dstf/backends/openmodelica/backend.py +368 -0
- dstf/backends/openmodelica/init_xml.py +76 -0
- dstf/backends/openmodelica/log_parser.py +119 -0
- dstf/backends/openmodelica/mos_generator.py +228 -0
- dstf/backends/openmodelica/persistent_backend.py +1045 -0
- dstf/backends/openmodelica/session_loader.py +63 -0
- dstf/backends/persistent.py +1811 -0
- dstf/backends/pool.py +602 -0
- dstf/backends/progress.py +395 -0
- dstf/backends/python/__init__.py +11 -0
- dstf/backends/python/backend.py +141 -0
- dstf/backends/python/run_test.py +141 -0
- dstf/backends/run_metadata.py +103 -0
- dstf/backends/types.py +310 -0
- dstf/cli/__init__.py +12 -0
- dstf/cli/author_cmd.py +61 -0
- dstf/cli/baseline_cmds.py +341 -0
- dstf/cli/common.py +231 -0
- dstf/cli/diag_cmds.py +350 -0
- dstf/cli/main.py +561 -0
- dstf/cli/review.py +398 -0
- dstf/cli/run_cmds.py +589 -0
- dstf/cli/spec_cmds.py +123 -0
- dstf/comparison/__init__.py +13 -0
- dstf/comparison/algorithms.py +2127 -0
- dstf/comparison/builtins.py +291 -0
- dstf/comparison/comparator.py +1189 -0
- dstf/comparison/geometry.py +164 -0
- dstf/comparison/handles.py +1194 -0
- dstf/comparison/metric_tree.py +332 -0
- dstf/comparison/mode_schema.py +176 -0
- dstf/comparison/modes.py +788 -0
- dstf/comparison/registry.py +230 -0
- dstf/comparison/seeds.py +281 -0
- dstf/comparison/sides.py +158 -0
- dstf/comparison/snap.py +62 -0
- dstf/comparison/strict_keys.py +246 -0
- dstf/comparison/summaries.py +354 -0
- dstf/comparison/tree_eval.py +868 -0
- dstf/comparison/tree_spec.py +678 -0
- dstf/comparison/types.py +194 -0
- dstf/comparison/validator.py +232 -0
- dstf/config.py +815 -0
- dstf/discovery/__init__.py +0 -0
- dstf/discovery/json_recognizer.py +702 -0
- dstf/discovery/library_state.py +248 -0
- dstf/discovery/mo_parser.py +794 -0
- dstf/discovery/patch_apply.py +308 -0
- dstf/discovery/patch_envelope.py +63 -0
- dstf/discovery/plots.py +318 -0
- dstf/discovery/recognizer.py +103 -0
- dstf/discovery/scan_cache.py +154 -0
- dstf/discovery/spec_parser.py +538 -0
- dstf/discovery/spec_shape.py +122 -0
- dstf/discovery/spec_update.py +493 -0
- dstf/discovery/test_registry.py +488 -0
- dstf/discovery/unit_tests.py +95 -0
- dstf/figures/__init__.py +14 -0
- dstf/figures/assemble.py +48 -0
- dstf/figures/decimate.py +150 -0
- dstf/figures/families.py +289 -0
- dstf/figures/palette.py +170 -0
- dstf/figures/plotly.py +1413 -0
- dstf/figures/view.py +233 -0
- dstf/pipeline.py +435 -0
- dstf/plugins.py +104 -0
- dstf/reporting/__init__.py +0 -0
- dstf/reporting/console_report.py +187 -0
- dstf/reporting/context_builder.py +979 -0
- dstf/reporting/dashboard_render.py +534 -0
- dstf/reporting/junit_report.py +192 -0
- dstf/reporting/overlay_loader.py +708 -0
- dstf/reporting/report_suite.py +1059 -0
- dstf/reporting/rows.py +458 -0
- dstf/reporting/schema_export.py +413 -0
- dstf/reporting/stats_rows.py +321 -0
- dstf/reporting/status.py +388 -0
- dstf/reporting/templates/axes.js +61 -0
- dstf/reporting/templates/dashboard.html +284 -0
- dstf/reporting/templates/dashboard.js +604 -0
- dstf/reporting/templates/filters.js +100 -0
- dstf/reporting/templates/interactive.html +420 -0
- dstf/reporting/templates/report.js +116 -0
- dstf/reporting/templates/vendor/plotly.min.js +8 -0
- dstf/reporting/theme.py +324 -0
- dstf/reporting/tree_rows.py +73 -0
- dstf/reporting/verdicts.py +124 -0
- dstf/storage/__init__.py +0 -0
- dstf/storage/accept.py +216 -0
- dstf/storage/baseline.py +43 -0
- dstf/storage/batch_manifest.py +204 -0
- dstf/storage/ref_index.py +295 -0
- dstf/storage/reference_store.py +661 -0
- dstf/storage/roles.py +576 -0
- dstf/storage/run_stats.py +85 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/METADATA +266 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/RECORD +185 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/WHEEL +4 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/entry_points.txt +2 -0
- dynamic_systems_testing_framework-1.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""Backends. Concrete today: Dymola, FMPy, OpenModelica, Julia, Python. Pluggable via ``@register``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from ..storage.batch_manifest import BatchManifest
|
|
8
|
+
from .base import Backend
|
|
9
|
+
from .types import (
|
|
10
|
+
Capability,
|
|
11
|
+
TestResult,
|
|
12
|
+
TestRunResult,
|
|
13
|
+
VariableResult,
|
|
14
|
+
resolve_variable_patterns,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ..config import Config
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"Backend",
|
|
22
|
+
"BackendUnavailableError",
|
|
23
|
+
"VariableResult",
|
|
24
|
+
"TestResult",
|
|
25
|
+
"TestRunResult",
|
|
26
|
+
"BatchManifest",
|
|
27
|
+
"resolve_variable_patterns",
|
|
28
|
+
"get_backend",
|
|
29
|
+
"get_backend_class",
|
|
30
|
+
"select_backend",
|
|
31
|
+
"dataset_kind_for",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Backend registry
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
_REGISTRY: dict[str, type[Backend]] = {}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _validate_capabilities(cls: type[Backend], name: str) -> None:
|
|
43
|
+
"""Assert declared :class:`Capability` flags have matching method overrides.
|
|
44
|
+
|
|
45
|
+
A capability declaration is a promise the framework can rely on. Without
|
|
46
|
+
this check, a backend can claim ``FMU_EXPORT`` and not override
|
|
47
|
+
:meth:`export_fmu` — the framework gates on the flag, calls the method,
|
|
48
|
+
and gets the base ``NotImplementedError`` at runtime instead of at
|
|
49
|
+
registration. We catch this drift at import time so the typo or
|
|
50
|
+
copy-paste-and-forget can't survive past `pytest --collect-only`.
|
|
51
|
+
|
|
52
|
+
Every capability has a load-bearing method, and all four are checked:
|
|
53
|
+
|
|
54
|
+
- ``FMU_EXPORT`` → ``export_fmu`` must be overridden.
|
|
55
|
+
- ``BATCH_FALLBACK`` → either ``run_tests`` or ``run_single_test`` must
|
|
56
|
+
be overridden (a backend can ship its own batched orchestration *or*
|
|
57
|
+
use the default per-test loop).
|
|
58
|
+
- ``PERSISTENT_WORKERS`` → ``persistent_backend_cls`` must be overridden
|
|
59
|
+
to return a non-None class.
|
|
60
|
+
- ``LIVE_RESULTS`` → ``live_result_path`` must be overridden.
|
|
61
|
+
- ``CANCEL`` → ``cancel_test`` must be overridden (a persistent variant's
|
|
62
|
+
cancel is its worker's, and is covered by tests like its other flags).
|
|
63
|
+
|
|
64
|
+
Only ``@register``-decorated classes are checked. Persistent variants
|
|
65
|
+
are reached via ``persistent_backend_cls``, not the registry, so their
|
|
66
|
+
declarations are covered by tests instead (see
|
|
67
|
+
``tests/test_capability_gating.py::TestFmuExportGate``).
|
|
68
|
+
"""
|
|
69
|
+
caps = cls.capabilities
|
|
70
|
+
if Capability.FMU_EXPORT in caps and cls.export_fmu is Backend.export_fmu:
|
|
71
|
+
raise TypeError(
|
|
72
|
+
f"@register('{name}'): {cls.__name__} declares "
|
|
73
|
+
f"Capability.FMU_EXPORT but does not override export_fmu(). "
|
|
74
|
+
f"Either implement the method or drop the capability flag."
|
|
75
|
+
)
|
|
76
|
+
if Capability.CANCEL in caps and cls.cancel_test is Backend.cancel_test:
|
|
77
|
+
raise TypeError(
|
|
78
|
+
f"@register('{name}'): {cls.__name__} declares "
|
|
79
|
+
f"Capability.CANCEL but does not override cancel_test(). "
|
|
80
|
+
f"Either implement the method or drop the capability flag."
|
|
81
|
+
)
|
|
82
|
+
if Capability.BATCH_FALLBACK in caps and (
|
|
83
|
+
cls.run_tests is Backend.run_tests
|
|
84
|
+
and cls.run_single_test is Backend.run_single_test
|
|
85
|
+
):
|
|
86
|
+
raise TypeError(
|
|
87
|
+
f"@register('{name}'): {cls.__name__} declares "
|
|
88
|
+
f"Capability.BATCH_FALLBACK but overrides neither "
|
|
89
|
+
f"run_tests() nor run_single_test(). Implement one or "
|
|
90
|
+
f"drop the capability flag."
|
|
91
|
+
)
|
|
92
|
+
if (
|
|
93
|
+
Capability.PERSISTENT_WORKERS in caps
|
|
94
|
+
# review 2026-07-06 (finding 28): classmethods must be compared via
|
|
95
|
+
# __func__ — `cls.persistent_backend_cls is Backend.
|
|
96
|
+
# persistent_backend_cls` compares per-access bound-method wrappers
|
|
97
|
+
# and is always False, which left this check permanently inert.
|
|
98
|
+
and cls.persistent_backend_cls.__func__
|
|
99
|
+
is Backend.persistent_backend_cls.__func__
|
|
100
|
+
):
|
|
101
|
+
raise TypeError(
|
|
102
|
+
f"@register('{name}'): {cls.__name__} declares "
|
|
103
|
+
f"Capability.PERSISTENT_WORKERS but does not override "
|
|
104
|
+
f"persistent_backend_cls(). Return your persistent-backend "
|
|
105
|
+
f"class (lazy-imported) or drop the capability flag."
|
|
106
|
+
)
|
|
107
|
+
if (
|
|
108
|
+
Capability.LIVE_RESULTS in caps
|
|
109
|
+
and cls.live_result_path.__func__ is Backend.live_result_path.__func__
|
|
110
|
+
):
|
|
111
|
+
raise TypeError(
|
|
112
|
+
f"@register('{name}'): {cls.__name__} declares "
|
|
113
|
+
f"Capability.LIVE_RESULTS but does not override "
|
|
114
|
+
f"live_result_path(). Return the file that grows during a run "
|
|
115
|
+
f"or drop the capability flag."
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def register(name: str):
|
|
120
|
+
"""Class decorator that registers a Backend subclass.
|
|
121
|
+
|
|
122
|
+
Validates capability honesty at decoration time — see
|
|
123
|
+
:func:`_validate_capabilities`. A typo or stale flag becomes an
|
|
124
|
+
``ImportError`` (via ``TypeError``) at module-import time rather
|
|
125
|
+
than a confusing ``NotImplementedError`` mid-run.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def decorator(cls: type[Backend]) -> type[Backend]:
|
|
129
|
+
_validate_capabilities(cls, name)
|
|
130
|
+
_REGISTRY[name] = cls
|
|
131
|
+
return cls
|
|
132
|
+
|
|
133
|
+
return decorator
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_backend(config: Config) -> Backend:
|
|
137
|
+
"""Instantiate the backend specified in *config*.
|
|
138
|
+
|
|
139
|
+
Backends self-register via the ``@register`` decorator. Importing the
|
|
140
|
+
backend module triggers registration.
|
|
141
|
+
"""
|
|
142
|
+
return get_backend_class(config)(config)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_backend_class(config: Config) -> type[Backend]:
|
|
146
|
+
"""Return the backend *class* for the config's backend, without instantiating.
|
|
147
|
+
|
|
148
|
+
Useful for reading class-level attributes (e.g. ``artifact_files``) from
|
|
149
|
+
contexts that don't want to pay instantiation cost or raise the
|
|
150
|
+
unavailable-backend ``RuntimeError`` an optional dependency's absence
|
|
151
|
+
signals (FmpyBackend.__init__ imports fmpy).
|
|
152
|
+
"""
|
|
153
|
+
backend = config.backend
|
|
154
|
+
if backend not in _REGISTRY:
|
|
155
|
+
_import_builtin_backend(backend)
|
|
156
|
+
cls = _REGISTRY.get(backend)
|
|
157
|
+
if cls is None:
|
|
158
|
+
available = ", ".join(sorted(_REGISTRY)) or "(none)"
|
|
159
|
+
raise ValueError(
|
|
160
|
+
f"Unsupported backend: {backend} "
|
|
161
|
+
f"(from '{config.backend_name}'). Available: {available}"
|
|
162
|
+
)
|
|
163
|
+
return cls
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class BackendUnavailableError(RuntimeError):
|
|
167
|
+
"""The configured backend cannot be built on this machine.
|
|
168
|
+
|
|
169
|
+
Not a bug: an unregistered backend name, a missing executable, or a
|
|
170
|
+
persistent variant whose dependency probe failed. Callers decide what
|
|
171
|
+
to do about it — the CLI prints the message and exits 1
|
|
172
|
+
(:func:`cli.run_cmds._get_backend`), the authoring tool shows it on the
|
|
173
|
+
job that asked for the run, and ``Session.open`` turns it into a
|
|
174
|
+
``ConfigError`` so ``dstf author`` refuses the project with one line.
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def select_backend(config, *, persistent: bool) -> Backend:
|
|
179
|
+
"""Instantiate the backend for *config*, persistent variant when asked.
|
|
180
|
+
|
|
181
|
+
When *persistent* is True and the backend **declares**
|
|
182
|
+
:attr:`Capability.PERSISTENT_WORKERS`, pre-flight the variant's
|
|
183
|
+
dependency probe and instantiate it. When the probe raises
|
|
184
|
+
:class:`RuntimeError`, raise :class:`BackendUnavailableError` naming
|
|
185
|
+
both fixes (install the dep, or re-run with ``--batch``) — no silent
|
|
186
|
+
fallback.
|
|
187
|
+
|
|
188
|
+
The declaration is what gates, not the presence of the hook: a backend
|
|
189
|
+
that ships an experimental ``persistent_backend_cls`` it isn't ready to
|
|
190
|
+
have driven says so by leaving the flag off, and lands on the batch
|
|
191
|
+
path. Registration-time validation (:func:`_validate_capabilities`)
|
|
192
|
+
guarantees the converse — flag implies a real override — so a declaring
|
|
193
|
+
backend always has a class to return.
|
|
194
|
+
|
|
195
|
+
Backend-agnostic: no string matching on backend names. New persistent
|
|
196
|
+
backends plug in by declaring the capability and overriding
|
|
197
|
+
``persistent_backend_cls`` + ``preflight`` on their backend classes.
|
|
198
|
+
|
|
199
|
+
This function itself reads ``config.backend`` only to phrase the
|
|
200
|
+
persistent-failure message; :func:`get_backend_class`, which it calls
|
|
201
|
+
first, requires the attribute.
|
|
202
|
+
"""
|
|
203
|
+
try:
|
|
204
|
+
batch_cls = get_backend_class(config)
|
|
205
|
+
except ValueError as exc:
|
|
206
|
+
# An unregistered backend name is a configuration problem, not a
|
|
207
|
+
# bug; same class of failure as a missing executable.
|
|
208
|
+
raise BackendUnavailableError(str(exc)) from exc
|
|
209
|
+
if not persistent or Capability.PERSISTENT_WORKERS not in batch_cls.capabilities:
|
|
210
|
+
# Batch-only backend (FMPy, Python today). Persistent flag is a
|
|
211
|
+
# no-op rather than an error so a single CLI works across mixed
|
|
212
|
+
# backends in a multi-library config.
|
|
213
|
+
return _instantiate(batch_cls, config)
|
|
214
|
+
pcls = batch_cls.persistent_backend_cls()
|
|
215
|
+
if pcls is None:
|
|
216
|
+
# Unreachable for a validated backend (the flag implies the
|
|
217
|
+
# override); kept as the type narrowing for the call below.
|
|
218
|
+
return _instantiate(batch_cls, config)
|
|
219
|
+
try:
|
|
220
|
+
pcls.preflight(config)
|
|
221
|
+
except RuntimeError as exc:
|
|
222
|
+
raise BackendUnavailableError(
|
|
223
|
+
f"Persistent-worker {config.backend} unavailable.\n"
|
|
224
|
+
f"\n"
|
|
225
|
+
f"{exc}\n"
|
|
226
|
+
f"\n"
|
|
227
|
+
f"Or re-run with `--batch` to use the per-test subprocess "
|
|
228
|
+
f"backend instead."
|
|
229
|
+
) from exc
|
|
230
|
+
return _instantiate(pcls, config)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _instantiate(cls: type[Backend], config) -> Backend:
|
|
234
|
+
"""``cls(config)``, with a missing-executable ``RuntimeError`` translated.
|
|
235
|
+
|
|
236
|
+
The ``try`` wraps exactly the constructor call: backends raise
|
|
237
|
+
``RuntimeError`` when the tool they drive isn't installed
|
|
238
|
+
(``python/backend.py``, ``julia/backend.py``), which is an unavailable
|
|
239
|
+
backend, while any other exception is a genuine bug and keeps its
|
|
240
|
+
traceback.
|
|
241
|
+
"""
|
|
242
|
+
try:
|
|
243
|
+
return cls(config)
|
|
244
|
+
except RuntimeError as exc:
|
|
245
|
+
raise BackendUnavailableError(str(exc)) from exc
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _import_builtin_backend(name: str) -> None:
|
|
249
|
+
"""Import a built-in backend module so it registers itself."""
|
|
250
|
+
builtins = {
|
|
251
|
+
"Dymola": ".dymola",
|
|
252
|
+
"FMPy": ".fmpy",
|
|
253
|
+
"OpenModelica": ".openmodelica",
|
|
254
|
+
"Julia": ".julia",
|
|
255
|
+
"Python": ".python",
|
|
256
|
+
}
|
|
257
|
+
module = builtins.get(name)
|
|
258
|
+
if module:
|
|
259
|
+
import importlib
|
|
260
|
+
|
|
261
|
+
importlib.import_module(module, package=__name__)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def dataset_kind_for(backend):
|
|
265
|
+
"""The single :class:`DatasetType` *backend* declares, else the default.
|
|
266
|
+
|
|
267
|
+
``produced_datasets`` is the backend's declaration of what shape its
|
|
268
|
+
results carry; the MetricTree evaluator refuses a leaf whose metric
|
|
269
|
+
can't score that shape. When a backend declares exactly one kind we can
|
|
270
|
+
say authoritatively which it is, so we thread it. A backend declaring
|
|
271
|
+
several can't be resolved from the class alone — the kind would have to
|
|
272
|
+
come off each result — so we fall back to ``TIME_SERIES``, which is what
|
|
273
|
+
the evaluator assumes anyway. All five builtins declare exactly
|
|
274
|
+
``{TIME_SERIES}``, so this is behaviour-neutral today; it exists so the
|
|
275
|
+
first backend to declare something else is scored against the right
|
|
276
|
+
metric set without a second wiring pass.
|
|
277
|
+
"""
|
|
278
|
+
from .types import DatasetType
|
|
279
|
+
|
|
280
|
+
kinds = type(backend).produced_datasets
|
|
281
|
+
if len(kinds) == 1:
|
|
282
|
+
return next(iter(kinds))
|
|
283
|
+
return DatasetType.TIME_SERIES
|