essreduce 24.11.3__tar.gz → 24.12.0__tar.gz
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.
- {essreduce-24.11.3/src/essreduce.egg-info → essreduce-24.12.0}/PKG-INFO +1 -1
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/api-reference/index.md +1 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/pyproject.toml +1 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/base.txt +1 -1
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/live/raw.py +3 -1
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/nexus/types.py +25 -2
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/nexus/workflow.py +49 -22
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/parameter.py +6 -1
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/ui.py +87 -6
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/uncertainty.py +11 -11
- essreduce-24.12.0/src/ess/reduce/widgets/_base.py +63 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_binedges_widget.py +3 -1
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_bounds_widget.py +2 -1
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_linspace_widget.py +3 -1
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_string_widget.py +9 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_vector_widget.py +3 -1
- {essreduce-24.11.3 → essreduce-24.12.0/src/essreduce.egg-info}/PKG-INFO +1 -1
- {essreduce-24.11.3 → essreduce-24.12.0}/src/essreduce.egg-info/SOURCES.txt +1 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/workflow_test.py +74 -8
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/widget_test.py +78 -3
- {essreduce-24.11.3 → essreduce-24.12.0}/.copier-answers.ess.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.copier-answers.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/ISSUE_TEMPLATE/blank.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/ISSUE_TEMPLATE/high-level-requirement.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/dependabot.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/workflows/ci.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/workflows/docs.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/workflows/nightly_at_main.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/workflows/nightly_at_release.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/workflows/python-version-ci +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/workflows/release.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/workflows/test.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.github/workflows/unpinned.yml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.gitignore +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.pre-commit-config.yaml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/.python-version +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/CODE_OF_CONDUCT.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/CONTRIBUTING.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/LICENSE +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/MANIFEST.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/README.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/conda/meta.yaml +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/_static/anaconda-icon.js +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/_static/favicon.svg +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/_static/logo-dark.svg +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/_static/logo.svg +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/_templates/class-template.rst +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/_templates/doc_version.html +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/_templates/module-template.rst +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/about/index.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/conf.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/developer/coding-conventions.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/developer/dependency-management.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/developer/getting-started.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/developer/gui.ipynb +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/developer/index.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/index.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/user-guide/index.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/user-guide/reduction-workflow-guidelines.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/docs/user-guide/widget.md +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/base.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/basetest.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/basetest.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/ci.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/ci.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/dev.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/dev.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/docs.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/docs.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/make_base.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/mypy.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/mypy.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/nightly.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/nightly.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/static.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/static.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/test.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/test.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/wheels.in +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/requirements/wheels.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/resources/logo.svg +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/setup.cfg +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/__init__.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/data.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/live/__init__.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/live/workflow.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/logging.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/nexus/__init__.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/nexus/_nexus_loader.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/nexus/json_generator.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/nexus/json_nexus.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/py.typed +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/scripts/grow_nexus.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/streaming.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/__init__.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_config.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_filename_widget.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_optional_widget.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/widgets/_switchable_widget.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/ess/reduce/workflow.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/essreduce.egg-info/dependency_links.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/essreduce.egg-info/entry_points.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/essreduce.egg-info/requires.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/src/essreduce.egg-info/top_level.txt +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/live/raw_test.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_generator_test.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_nexus_examples/array_dataset.json +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_nexus_examples/dataset.json +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_nexus_examples/detector.json +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_nexus_examples/entry.json +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_nexus_examples/event_data.json +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_nexus_examples/instrument.json +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_nexus_examples/log.json +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/json_nexus_test.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/nexus/nexus_loader_test.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/package_test.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/scripts/test_grow_nexus.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/streaming_test.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tests/uncertainty_test.py +0 -0
- {essreduce-24.11.3 → essreduce-24.12.0}/tox.ini +0 -0
|
@@ -285,7 +285,9 @@ class RollingDetectorView(Detector):
|
|
|
285
285
|
wf[RollingDetectorViewWindow] = window
|
|
286
286
|
if isinstance(projection, LogicalView):
|
|
287
287
|
wf[LogicalView] = projection
|
|
288
|
-
wf[NeXusTransformation[snx.NXdetector, SampleRun]] =
|
|
288
|
+
wf[NeXusTransformation[snx.NXdetector, SampleRun]] = NeXusTransformation[
|
|
289
|
+
snx.NXdetector, SampleRun
|
|
290
|
+
](sc.scalar(1))
|
|
289
291
|
wf.insert(RollingDetectorView.from_detector_and_logical_view)
|
|
290
292
|
elif projection == 'cylinder_mantle_z':
|
|
291
293
|
wf.insert(make_cylinder_mantle_coords)
|
|
@@ -78,7 +78,17 @@ RunType = TypeVar(
|
|
|
78
78
|
TransmissionRun[BackgroundRun],
|
|
79
79
|
VanadiumRun,
|
|
80
80
|
)
|
|
81
|
-
"""TypeVar
|
|
81
|
+
"""TypeVar for specifying what run some data belongs to.
|
|
82
|
+
|
|
83
|
+
Possible values:
|
|
84
|
+
|
|
85
|
+
- :class:`BackgroundRun`
|
|
86
|
+
- :class:`EmptyBeamRun`
|
|
87
|
+
- :class:`SampleRun`
|
|
88
|
+
- :class:`TransmissionRun`
|
|
89
|
+
- :class:`VanadiumRun`
|
|
90
|
+
"""
|
|
91
|
+
|
|
82
92
|
|
|
83
93
|
# 1.2 Monitor types
|
|
84
94
|
Monitor1 = NewType('Monitor1', int)
|
|
@@ -108,7 +118,20 @@ MonitorType = TypeVar(
|
|
|
108
118
|
IncidentMonitor,
|
|
109
119
|
TransmissionMonitor,
|
|
110
120
|
)
|
|
111
|
-
"""TypeVar
|
|
121
|
+
"""TypeVar for specifying what monitor some data belongs to.
|
|
122
|
+
|
|
123
|
+
Possible values:
|
|
124
|
+
|
|
125
|
+
- :class:`Monitor1`
|
|
126
|
+
- :class:`Monitor2`
|
|
127
|
+
- :class:`Monitor3`
|
|
128
|
+
- :class:`Monitor4`
|
|
129
|
+
- :class:`Monitor5`
|
|
130
|
+
- :class:`Monitor6`
|
|
131
|
+
- :class:`IncidentMonitor`
|
|
132
|
+
- :class:`TransmissionMonitor`
|
|
133
|
+
"""
|
|
134
|
+
|
|
112
135
|
|
|
113
136
|
Component = TypeVar(
|
|
114
137
|
'Component',
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
"""Workflow and workflow components for interacting with NeXus files."""
|
|
5
5
|
|
|
6
|
-
from collections.abc import
|
|
6
|
+
from collections.abc import Iterable
|
|
7
7
|
from copy import deepcopy
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
-
import networkx as nx
|
|
11
10
|
import sciline
|
|
11
|
+
import sciline.typing
|
|
12
12
|
import scipp as sc
|
|
13
13
|
import scippnexus as snx
|
|
14
14
|
from scipp.constants import g
|
|
@@ -649,30 +649,39 @@ def LoadDetectorWorkflow() -> sciline.Pipeline:
|
|
|
649
649
|
|
|
650
650
|
def GenericNeXusWorkflow(
|
|
651
651
|
*,
|
|
652
|
-
run_types:
|
|
653
|
-
monitor_types:
|
|
652
|
+
run_types: Iterable[sciline.typing.Key] | None = None,
|
|
653
|
+
monitor_types: Iterable[sciline.typing.Key] | None = None,
|
|
654
654
|
) -> sciline.Pipeline:
|
|
655
655
|
"""
|
|
656
656
|
Generic workflow for loading detector and monitor data from a NeXus file.
|
|
657
657
|
|
|
658
|
+
It is possible to limit which run types and monitor types
|
|
659
|
+
are supported by the returned workflow.
|
|
660
|
+
This is useful to reduce the size of the workflow and make it easier to inspect.
|
|
661
|
+
Make sure to add *all* required run types and monitor types when using this feature.
|
|
662
|
+
|
|
663
|
+
Attention
|
|
664
|
+
---------
|
|
665
|
+
Filtering by run type and monitor type does not work with nested type vars.
|
|
666
|
+
E.g., if you have a type like ``Outer[Inner[RunType]]``, this type and its
|
|
667
|
+
provider will be removed.
|
|
668
|
+
|
|
658
669
|
Parameters
|
|
659
670
|
----------
|
|
660
671
|
run_types:
|
|
661
672
|
List of run types to include in the workflow. If not provided, all run types
|
|
662
|
-
are included.
|
|
663
|
-
|
|
673
|
+
are included.
|
|
674
|
+
Must be a possible value of :class:`ess.reduce.nexus.types.RunType`.
|
|
664
675
|
monitor_types:
|
|
665
676
|
List of monitor types to include in the workflow. If not provided, all monitor
|
|
666
|
-
types are included.
|
|
667
|
-
|
|
677
|
+
types are included.
|
|
678
|
+
Must be a possible value of :class:`ess.reduce.nexus.types.MonitorType`.
|
|
668
679
|
|
|
669
680
|
Returns
|
|
670
681
|
-------
|
|
671
682
|
:
|
|
672
683
|
The workflow.
|
|
673
684
|
"""
|
|
674
|
-
if monitor_types is not None and run_types is None:
|
|
675
|
-
raise ValueError("run_types must be specified if monitor_types is specified")
|
|
676
685
|
wf = sciline.Pipeline(
|
|
677
686
|
(
|
|
678
687
|
*_common_providers,
|
|
@@ -685,16 +694,34 @@ def GenericNeXusWorkflow(
|
|
|
685
694
|
wf[DetectorBankSizes] = DetectorBankSizes({})
|
|
686
695
|
wf[PreopenNeXusFile] = PreopenNeXusFile(False)
|
|
687
696
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
# their ancestors is what we need to strip unused run and monitor types.
|
|
692
|
-
for rt in run_types or ():
|
|
693
|
-
ancestors |= nx.ancestors(g, DetectorData[rt])
|
|
694
|
-
ancestors.add(DetectorData[rt])
|
|
695
|
-
for mt in monitor_types or ():
|
|
696
|
-
ancestors |= nx.ancestors(g, MonitorData[rt, mt])
|
|
697
|
-
ancestors.add(MonitorData[rt, mt])
|
|
698
|
-
if run_types is not None:
|
|
699
|
-
g.remove_nodes_from(set(g.nodes) - ancestors)
|
|
697
|
+
if run_types is not None or monitor_types is not None:
|
|
698
|
+
_prune_type_vars(wf, run_types=run_types, monitor_types=monitor_types)
|
|
699
|
+
|
|
700
700
|
return wf
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def _prune_type_vars(
|
|
704
|
+
workflow: sciline.Pipeline,
|
|
705
|
+
*,
|
|
706
|
+
run_types: Iterable[sciline.typing.Key] | None,
|
|
707
|
+
monitor_types: Iterable[sciline.typing.Key] | None,
|
|
708
|
+
) -> None:
|
|
709
|
+
# Remove all nodes that use a run type or monitor types that is
|
|
710
|
+
# not listed in the function arguments.
|
|
711
|
+
excluded_run_types = _excluded_type_args(RunType, run_types)
|
|
712
|
+
excluded_monitor_types = _excluded_type_args(MonitorType, monitor_types)
|
|
713
|
+
excluded_types = excluded_run_types | excluded_monitor_types
|
|
714
|
+
|
|
715
|
+
graph = workflow.underlying_graph
|
|
716
|
+
to_remove = [
|
|
717
|
+
node for node in graph if excluded_types & set(getattr(node, "__args__", set()))
|
|
718
|
+
]
|
|
719
|
+
graph.remove_nodes_from(to_remove)
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def _excluded_type_args(
|
|
723
|
+
type_var: Any, keep: Iterable[sciline.typing.Key] | None
|
|
724
|
+
) -> set[sciline.typing.Key]:
|
|
725
|
+
if keep is None:
|
|
726
|
+
return set()
|
|
727
|
+
return set(type_var.__constraints__) - set(keep)
|
|
@@ -71,7 +71,12 @@ class ParamWithOptions(Parameter[T]):
|
|
|
71
71
|
|
|
72
72
|
@classmethod
|
|
73
73
|
def from_enum(cls: type[C], t: type[T], default: T) -> C:
|
|
74
|
-
return cls(
|
|
74
|
+
return cls(
|
|
75
|
+
name=t.__name__,
|
|
76
|
+
description=t.__doc__,
|
|
77
|
+
options=t.__members__,
|
|
78
|
+
default=default,
|
|
79
|
+
)
|
|
75
80
|
|
|
76
81
|
|
|
77
82
|
@dataclass
|
|
@@ -10,6 +10,7 @@ from ipywidgets import Layout
|
|
|
10
10
|
|
|
11
11
|
from .parameter import Parameter
|
|
12
12
|
from .widgets import SwitchWidget, create_parameter_widget, default_layout
|
|
13
|
+
from .widgets._base import get_fields, set_fields
|
|
13
14
|
from .workflow import (
|
|
14
15
|
Key,
|
|
15
16
|
assign_parameter_values,
|
|
@@ -81,11 +82,13 @@ class ParameterBox(widgets.VBox):
|
|
|
81
82
|
self._input_widgets.clear()
|
|
82
83
|
self._input_widgets.update(
|
|
83
84
|
{
|
|
84
|
-
node:
|
|
85
|
-
for node, parameter in
|
|
85
|
+
node: create_parameter_widget(parameter)
|
|
86
|
+
for node, parameter in new_input_parameters.items()
|
|
86
87
|
}
|
|
87
88
|
)
|
|
88
|
-
self._input_box.children =
|
|
89
|
+
self._input_box.children = [
|
|
90
|
+
widgets.HBox([widget]) for widget in self._input_widgets.values()
|
|
91
|
+
]
|
|
89
92
|
|
|
90
93
|
self.parameter_refresh_button.on_click(_refresh_input_box)
|
|
91
94
|
|
|
@@ -97,8 +100,7 @@ class ParameterBox(widgets.VBox):
|
|
|
97
100
|
return {
|
|
98
101
|
node: widget.value
|
|
99
102
|
for node, widget_box in self._input_widgets.items()
|
|
100
|
-
if (not isinstance((widget := widget_box
|
|
101
|
-
or widget.enabled
|
|
103
|
+
if (not isinstance((widget := widget_box), SwitchWidget)) or widget.enabled
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
|
|
@@ -130,7 +132,7 @@ class ResultBox(widgets.VBox):
|
|
|
130
132
|
result_registry.clear()
|
|
131
133
|
result_registry.update(compute_result)
|
|
132
134
|
for i in compute_result.values():
|
|
133
|
-
display.display(
|
|
135
|
+
display.display(i)
|
|
134
136
|
|
|
135
137
|
def clear_output(_: widgets.Button) -> None:
|
|
136
138
|
self.output.clear_output()
|
|
@@ -232,3 +234,82 @@ def workflow_widget(result_registry: dict | None = None) -> widgets.Widget:
|
|
|
232
234
|
workflow_selection_box = widgets.HBox([workflow_select], layout=default_layout)
|
|
233
235
|
workflow_box = widgets.Box(layout=default_layout)
|
|
234
236
|
return widgets.VBox([workflow_selection_box, workflow_box])
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _get_parameter_box(widget: WorkflowWidget | ParameterBox) -> ParameterBox:
|
|
240
|
+
if isinstance(widget, WorkflowWidget):
|
|
241
|
+
return widget.parameter_box
|
|
242
|
+
elif isinstance(widget, ParameterBox):
|
|
243
|
+
return widget
|
|
244
|
+
else:
|
|
245
|
+
raise TypeError(
|
|
246
|
+
f"Expected target_widget to be a WorkflowWidget or ParameterBox, "
|
|
247
|
+
f"got {type(widget)}."
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def set_parameter_widget_values(
|
|
252
|
+
widget: WorkflowWidget | ParameterBox, new_parameter_values: dict[type, Any]
|
|
253
|
+
) -> None:
|
|
254
|
+
"""Set the values of the input widgets in the target widget.
|
|
255
|
+
|
|
256
|
+
Nodes that don't exist in the input widgets will be ignored.
|
|
257
|
+
|
|
258
|
+
Example
|
|
259
|
+
-------
|
|
260
|
+
.. code-block::
|
|
261
|
+
|
|
262
|
+
set_parameter_widget_values(widget, {
|
|
263
|
+
'WavelengthBins': {'start': 1.0, 'stop': 14.0, 'nbins': 500}
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
Parameters
|
|
267
|
+
----------
|
|
268
|
+
widget:
|
|
269
|
+
The widget containing the input widgets.
|
|
270
|
+
new_parameter_values:
|
|
271
|
+
A dictionary of values/state to set each fields/state or value of input widgets.
|
|
272
|
+
|
|
273
|
+
Raises
|
|
274
|
+
------
|
|
275
|
+
TypeError:
|
|
276
|
+
If the widget is not a WorkflowWidget or a ParameterBox.
|
|
277
|
+
|
|
278
|
+
"""
|
|
279
|
+
parameter_box = _get_parameter_box(widget)
|
|
280
|
+
# Walk through the existing input widgets and set the values
|
|
281
|
+
# ``node`s that don't exist in the input widgets will be ignored.
|
|
282
|
+
for node, widget in parameter_box._input_widgets.items():
|
|
283
|
+
if node in new_parameter_values:
|
|
284
|
+
# We shouldn't use `get` here because ``None`` is a valid value.
|
|
285
|
+
set_fields(widget, new_parameter_values[node])
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def get_parameter_widget_values(
|
|
289
|
+
widget: WorkflowWidget | ParameterBox,
|
|
290
|
+
) -> dict[type, Any]:
|
|
291
|
+
"""Return the current values of the input widgets in the target widget.
|
|
292
|
+
|
|
293
|
+
The result of this function can be used to set the values of the input widgets
|
|
294
|
+
using the :py:func:`~set_parameter_widget_values` function.
|
|
295
|
+
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
widget:
|
|
299
|
+
The widget containing the input widgets.
|
|
300
|
+
|
|
301
|
+
Returns
|
|
302
|
+
-------
|
|
303
|
+
:
|
|
304
|
+
A dictionary of the current values/state of each input widget.
|
|
305
|
+
|
|
306
|
+
Raises
|
|
307
|
+
------
|
|
308
|
+
TypeError:
|
|
309
|
+
If the widget is not a WorkflowWidget or a ParameterBox.
|
|
310
|
+
|
|
311
|
+
"""
|
|
312
|
+
return {
|
|
313
|
+
node: get_fields(widget)
|
|
314
|
+
for node, widget in _get_parameter_box(widget)._input_widgets.items()
|
|
315
|
+
}
|
|
@@ -13,7 +13,7 @@ The recommended use of this module is via the :py:func:`broadcast_uncertainties`
|
|
|
13
13
|
helper function.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
from enum import Enum
|
|
16
|
+
from enum import Enum, auto
|
|
17
17
|
from typing import TypeVar, overload
|
|
18
18
|
|
|
19
19
|
import numpy as np
|
|
@@ -23,18 +23,18 @@ from scipp.core.concepts import irreducible_mask
|
|
|
23
23
|
T = TypeVar("T", bound=sc.Variable | sc.DataArray)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
UncertaintyBroadcastMode
|
|
27
|
-
|
|
28
|
-
)
|
|
29
|
-
"""
|
|
30
|
-
Mode for broadcasting uncertainties.
|
|
26
|
+
class UncertaintyBroadcastMode(Enum):
|
|
27
|
+
"""Mode for broadcasting uncertainties.
|
|
31
28
|
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
- `fail`: Do not broadcast, simply return the input data.
|
|
29
|
+
See https://doi.org/10.3233/JNR-220049 for context.
|
|
30
|
+
"""
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
"""
|
|
32
|
+
drop = auto()
|
|
33
|
+
"""Drop variances if the data is broadcast."""
|
|
34
|
+
upper_bound = auto()
|
|
35
|
+
"""Compute an upper bound for the variances."""
|
|
36
|
+
fail = auto()
|
|
37
|
+
"""Do not broadcast, simply return the input data."""
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
@overload
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
+
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
|
|
3
|
+
import warnings
|
|
4
|
+
from typing import Any, Protocol, runtime_checkable
|
|
5
|
+
|
|
6
|
+
from ipywidgets import Widget
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@runtime_checkable
|
|
10
|
+
class WidgetWithFieldsProtocol(Protocol):
|
|
11
|
+
def set_fields(self, new_values: dict[str, Any]) -> None: ...
|
|
12
|
+
|
|
13
|
+
def get_fields(self) -> dict[str, Any]: ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WidgetWithFieldsMixin:
|
|
17
|
+
def set_fields(self, new_values: dict[str, Any]) -> None:
|
|
18
|
+
# Extract valid fields
|
|
19
|
+
new_field_names = set(new_values.keys())
|
|
20
|
+
valid_field_names = new_field_names & set(self.fields.keys())
|
|
21
|
+
# Warn for invalid fields
|
|
22
|
+
invalid_field_names = new_field_names - valid_field_names
|
|
23
|
+
for field_name in invalid_field_names:
|
|
24
|
+
warning_msg = f"Cannot set field '{field_name}'."
|
|
25
|
+
" The field does not exist in the widget."
|
|
26
|
+
"The field value will be ignored."
|
|
27
|
+
warnings.warn(warning_msg, UserWarning, stacklevel=1)
|
|
28
|
+
# Set valid fields
|
|
29
|
+
for field_name in valid_field_names:
|
|
30
|
+
self.fields[field_name].value = new_values[field_name]
|
|
31
|
+
|
|
32
|
+
def get_fields(self) -> dict[str, Any]:
|
|
33
|
+
return {
|
|
34
|
+
field_name: field_sub_widget.value
|
|
35
|
+
for field_name, field_sub_widget in self.fields.items()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _has_widget_value_setter(widget: Widget) -> bool:
|
|
40
|
+
widget_type = type(widget)
|
|
41
|
+
return (
|
|
42
|
+
widget_property := getattr(widget_type, 'value', None)
|
|
43
|
+
) is not None and getattr(widget_property, 'fset', None) is not None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def set_fields(widget: Widget, new_values: Any) -> None:
|
|
47
|
+
if isinstance(widget, WidgetWithFieldsProtocol) and isinstance(new_values, dict):
|
|
48
|
+
widget.set_fields(new_values)
|
|
49
|
+
elif _has_widget_value_setter(widget):
|
|
50
|
+
widget.value = new_values
|
|
51
|
+
else:
|
|
52
|
+
warnings.warn(
|
|
53
|
+
f"Cannot set value or fields for widget of type {type(widget)}."
|
|
54
|
+
" The new_value(s) will be ignored.",
|
|
55
|
+
UserWarning,
|
|
56
|
+
stacklevel=1,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_fields(widget: Widget) -> Any:
|
|
61
|
+
if isinstance(widget, WidgetWithFieldsProtocol):
|
|
62
|
+
return widget.get_fields()
|
|
63
|
+
return widget.value
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import ipywidgets as ipw
|
|
4
4
|
import scipp as sc
|
|
5
5
|
|
|
6
|
+
from ._base import WidgetWithFieldsMixin
|
|
7
|
+
|
|
6
8
|
UNITS_LIBRARY = {
|
|
7
9
|
"wavelength": {"options": ("angstrom", "nm")},
|
|
8
10
|
"Q": {"options": ("1/angstrom", "1/nm")},
|
|
@@ -19,7 +21,7 @@ UNITS_LIBRARY = {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
class BinEdgesWidget(ipw.HBox, ipw.ValueWidget):
|
|
24
|
+
class BinEdgesWidget(ipw.HBox, ipw.ValueWidget, WidgetWithFieldsMixin):
|
|
23
25
|
def __init__(
|
|
24
26
|
self,
|
|
25
27
|
name: str,
|
|
@@ -4,9 +4,10 @@ import scipp as sc
|
|
|
4
4
|
from ipywidgets import FloatText, GridBox, Label, Text, ValueWidget
|
|
5
5
|
|
|
6
6
|
from ..parameter import ParamWithBounds
|
|
7
|
+
from ._base import WidgetWithFieldsMixin
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class BoundsWidget(GridBox, ValueWidget):
|
|
10
|
+
class BoundsWidget(GridBox, ValueWidget, WidgetWithFieldsMixin):
|
|
10
11
|
def __init__(self):
|
|
11
12
|
super().__init__()
|
|
12
13
|
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
import scipp as sc
|
|
4
4
|
from ipywidgets import FloatText, GridBox, IntText, Label, ValueWidget
|
|
5
5
|
|
|
6
|
+
from ._base import WidgetWithFieldsMixin
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
class LinspaceWidget(GridBox, ValueWidget, WidgetWithFieldsMixin):
|
|
8
10
|
def __init__(self, dim: str, unit: str):
|
|
9
11
|
super().__init__()
|
|
10
12
|
|
|
@@ -27,6 +27,15 @@ class StringWidget(HBox, ValueWidget):
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class MultiStringWidget(StringWidget):
|
|
30
|
+
def __init__(
|
|
31
|
+
self, description: str, value: tuple[str, ...] | None = None, **kwargs
|
|
32
|
+
):
|
|
33
|
+
# Special case handling to allow initialising with a single string
|
|
34
|
+
if not isinstance(value, str) and value is not None:
|
|
35
|
+
value = ', '.join(value)
|
|
36
|
+
|
|
37
|
+
super().__init__(description, value, **kwargs)
|
|
38
|
+
|
|
30
39
|
@property
|
|
31
40
|
def value(self) -> tuple[str, ...]:
|
|
32
41
|
v = super().value
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
import scipp as sc
|
|
5
5
|
from ipywidgets import FloatText, HBox, Label, Text, ValueWidget
|
|
6
6
|
|
|
7
|
+
from ._base import WidgetWithFieldsMixin
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
class VectorWidget(HBox, ValueWidget, WidgetWithFieldsMixin):
|
|
9
11
|
def __init__(self, name: str, variable: sc.Variable, components: str):
|
|
10
12
|
super().__init__()
|
|
11
13
|
|
|
@@ -83,6 +83,7 @@ src/ess/reduce/nexus/types.py
|
|
|
83
83
|
src/ess/reduce/nexus/workflow.py
|
|
84
84
|
src/ess/reduce/scripts/grow_nexus.py
|
|
85
85
|
src/ess/reduce/widgets/__init__.py
|
|
86
|
+
src/ess/reduce/widgets/_base.py
|
|
86
87
|
src/ess/reduce/widgets/_binedges_widget.py
|
|
87
88
|
src/ess/reduce/widgets/_bounds_widget.py
|
|
88
89
|
src/ess/reduce/widgets/_config.py
|
|
@@ -12,16 +12,20 @@ from ess.reduce.nexus.types import (
|
|
|
12
12
|
BackgroundRun,
|
|
13
13
|
Choppers,
|
|
14
14
|
DetectorData,
|
|
15
|
+
EmptyBeamRun,
|
|
15
16
|
Filename,
|
|
16
17
|
Monitor1,
|
|
17
18
|
Monitor2,
|
|
18
19
|
Monitor3,
|
|
19
20
|
MonitorData,
|
|
21
|
+
MonitorType,
|
|
20
22
|
NeXusComponentLocationSpec,
|
|
21
23
|
NeXusName,
|
|
22
24
|
NeXusTransformation,
|
|
25
|
+
RunType,
|
|
23
26
|
SampleRun,
|
|
24
27
|
TimeInterval,
|
|
28
|
+
TransmissionMonitor,
|
|
25
29
|
)
|
|
26
30
|
from ess.reduce.nexus.workflow import (
|
|
27
31
|
GenericNeXusWorkflow,
|
|
@@ -574,16 +578,11 @@ def test_generic_nexus_workflow_load_analyzers() -> None:
|
|
|
574
578
|
assert analyzer['usage'] == 'Bragg'
|
|
575
579
|
|
|
576
580
|
|
|
577
|
-
def test_generic_nexus_workflow_raises_if_monitor_types_but_not_run_types_given() -> (
|
|
578
|
-
None
|
|
579
|
-
):
|
|
580
|
-
with pytest.raises(ValueError, match='run_types'):
|
|
581
|
-
GenericNeXusWorkflow(monitor_types=[Monitor1])
|
|
582
|
-
|
|
583
|
-
|
|
584
581
|
def test_generic_nexus_workflow_includes_only_given_run_and_monitor_types() -> None:
|
|
585
582
|
wf = GenericNeXusWorkflow(run_types=[SampleRun], monitor_types=[Monitor1, Monitor3])
|
|
586
583
|
graph = wf.underlying_graph
|
|
584
|
+
|
|
585
|
+
# Check some examples to avoid relying entirely on complicated loops below.
|
|
587
586
|
assert DetectorData[SampleRun] in graph
|
|
588
587
|
assert DetectorData[BackgroundRun] not in graph
|
|
589
588
|
assert MonitorData[SampleRun, Monitor1] in graph
|
|
@@ -592,7 +591,11 @@ def test_generic_nexus_workflow_includes_only_given_run_and_monitor_types() -> N
|
|
|
592
591
|
assert MonitorData[BackgroundRun, Monitor1] not in graph
|
|
593
592
|
assert MonitorData[BackgroundRun, Monitor2] not in graph
|
|
594
593
|
assert MonitorData[BackgroundRun, Monitor3] not in graph
|
|
595
|
-
|
|
594
|
+
assert Choppers[SampleRun] in graph
|
|
595
|
+
assert Choppers[BackgroundRun] not in graph
|
|
596
|
+
assert Analyzers[SampleRun] in graph
|
|
597
|
+
assert Analyzers[BackgroundRun] not in graph
|
|
598
|
+
|
|
596
599
|
assert NeXusComponentLocationSpec[Monitor1, SampleRun] in graph
|
|
597
600
|
assert NeXusComponentLocationSpec[Monitor2, SampleRun] not in graph
|
|
598
601
|
assert NeXusComponentLocationSpec[Monitor3, SampleRun] in graph
|
|
@@ -605,3 +608,66 @@ def test_generic_nexus_workflow_includes_only_given_run_and_monitor_types() -> N
|
|
|
605
608
|
assert NeXusComponentLocationSpec[snx.NXdetector, BackgroundRun] not in graph
|
|
606
609
|
assert NeXusComponentLocationSpec[snx.NXsample, BackgroundRun] not in graph
|
|
607
610
|
assert NeXusComponentLocationSpec[snx.NXsource, BackgroundRun] not in graph
|
|
611
|
+
|
|
612
|
+
excluded_run_types = set(RunType.__constraints__) - {SampleRun}
|
|
613
|
+
excluded_monitor_types = set(MonitorType.__constraints__) - {Monitor1, Monitor3}
|
|
614
|
+
for node in graph:
|
|
615
|
+
assert_not_contains_type_arg(node, excluded_run_types)
|
|
616
|
+
assert_not_contains_type_arg(node, excluded_monitor_types)
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def test_generic_nexus_workflow_includes_only_given_run_types() -> None:
|
|
620
|
+
wf = GenericNeXusWorkflow(run_types=[EmptyBeamRun])
|
|
621
|
+
graph = wf.underlying_graph
|
|
622
|
+
|
|
623
|
+
# Check some examples to avoid relying entirely on complicated loops below.
|
|
624
|
+
assert DetectorData[EmptyBeamRun] in graph
|
|
625
|
+
assert DetectorData[SampleRun] not in graph
|
|
626
|
+
assert MonitorData[EmptyBeamRun, Monitor1] in graph
|
|
627
|
+
assert MonitorData[EmptyBeamRun, Monitor2] in graph
|
|
628
|
+
assert MonitorData[EmptyBeamRun, Monitor3] in graph
|
|
629
|
+
assert MonitorData[SampleRun, Monitor1] not in graph
|
|
630
|
+
assert MonitorData[SampleRun, Monitor2] not in graph
|
|
631
|
+
assert MonitorData[SampleRun, Monitor3] not in graph
|
|
632
|
+
assert Choppers[EmptyBeamRun] in graph
|
|
633
|
+
assert Choppers[SampleRun] not in graph
|
|
634
|
+
assert Analyzers[EmptyBeamRun] in graph
|
|
635
|
+
assert Analyzers[SampleRun] not in graph
|
|
636
|
+
|
|
637
|
+
excluded_run_types = set(RunType.__constraints__) - {EmptyBeamRun}
|
|
638
|
+
for node in graph:
|
|
639
|
+
assert_not_contains_type_arg(node, excluded_run_types)
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def test_generic_nexus_workflow_includes_only_given_monitor_types() -> None:
|
|
643
|
+
wf = GenericNeXusWorkflow(monitor_types=[TransmissionMonitor, Monitor1])
|
|
644
|
+
graph = wf.underlying_graph
|
|
645
|
+
|
|
646
|
+
# Check some examples to avoid relying entirely on complicated loops below.
|
|
647
|
+
assert DetectorData[SampleRun] in graph
|
|
648
|
+
assert DetectorData[BackgroundRun] in graph
|
|
649
|
+
assert MonitorData[SampleRun, TransmissionMonitor] in graph
|
|
650
|
+
assert MonitorData[SampleRun, Monitor1] in graph
|
|
651
|
+
assert MonitorData[SampleRun, Monitor2] not in graph
|
|
652
|
+
assert MonitorData[SampleRun, Monitor3] not in graph
|
|
653
|
+
assert MonitorData[BackgroundRun, TransmissionMonitor] in graph
|
|
654
|
+
assert MonitorData[BackgroundRun, Monitor1] in graph
|
|
655
|
+
assert MonitorData[BackgroundRun, Monitor2] not in graph
|
|
656
|
+
assert MonitorData[BackgroundRun, Monitor3] not in graph
|
|
657
|
+
assert Choppers[SampleRun] in graph
|
|
658
|
+
assert Choppers[BackgroundRun] in graph
|
|
659
|
+
assert Analyzers[SampleRun] in graph
|
|
660
|
+
assert Analyzers[BackgroundRun] in graph
|
|
661
|
+
|
|
662
|
+
excluded_monitor_types = set(MonitorType.__constraints__) - {
|
|
663
|
+
Monitor1,
|
|
664
|
+
TransmissionMonitor,
|
|
665
|
+
}
|
|
666
|
+
for node in graph:
|
|
667
|
+
assert_not_contains_type_arg(node, excluded_monitor_types)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def assert_not_contains_type_arg(node: object, excluded: set[type]) -> None:
|
|
671
|
+
assert not any(
|
|
672
|
+
arg in excluded for arg in getattr(node, "__args__", ())
|
|
673
|
+
), f"Node {node} contains one of {excluded!r}"
|
|
@@ -4,12 +4,19 @@ from collections.abc import Callable, Generator
|
|
|
4
4
|
from contextlib import contextmanager
|
|
5
5
|
from typing import Any, NewType
|
|
6
6
|
|
|
7
|
+
import pytest
|
|
7
8
|
import sciline as sl
|
|
8
9
|
import scipp as sc
|
|
9
10
|
from ipywidgets import FloatText, IntText
|
|
10
11
|
|
|
11
|
-
from ess.reduce.parameter import
|
|
12
|
-
|
|
12
|
+
from ess.reduce.parameter import (
|
|
13
|
+
BinEdgesParameter,
|
|
14
|
+
FilenameParameter,
|
|
15
|
+
MultiFilenameParameter,
|
|
16
|
+
Parameter,
|
|
17
|
+
parameter_registry,
|
|
18
|
+
)
|
|
19
|
+
from ess.reduce.ui import ResultBox, WorkflowWidget, workflow_widget
|
|
13
20
|
from ess.reduce.widgets import OptionalWidget, SwitchWidget, create_parameter_widget
|
|
14
21
|
from ess.reduce.workflow import register_workflow, workflow_registry
|
|
15
22
|
|
|
@@ -79,7 +86,7 @@ def provider_with_optional(a: OptionalInt, b: OptionalFloat) -> str:
|
|
|
79
86
|
|
|
80
87
|
|
|
81
88
|
def _get_param_widget(widget: WorkflowWidget, param_type: type) -> Any:
|
|
82
|
-
return widget.parameter_box._input_widgets[param_type]
|
|
89
|
+
return widget.parameter_box._input_widgets[param_type]
|
|
83
90
|
|
|
84
91
|
|
|
85
92
|
def test_parameter_default_value_test() -> None:
|
|
@@ -402,3 +409,71 @@ def test_bin_edges_widget_with_default_values() -> None:
|
|
|
402
409
|
assert param_widget.fields['stop'].value == 0.6
|
|
403
410
|
assert param_widget.fields['nbins'].value == 150
|
|
404
411
|
assert param_widget.fields['spacing'].value == 'linear'
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
@pytest.mark.parametrize(
|
|
415
|
+
'output',
|
|
416
|
+
[
|
|
417
|
+
(sc.scalar(1), sc.scalar(2)),
|
|
418
|
+
'Test with a string',
|
|
419
|
+
sc.data.binned_xy(100, 10, 10),
|
|
420
|
+
],
|
|
421
|
+
)
|
|
422
|
+
def test_result_box_can_handle_different_outputs(output):
|
|
423
|
+
was_called = False
|
|
424
|
+
|
|
425
|
+
def run_workflow():
|
|
426
|
+
nonlocal was_called
|
|
427
|
+
was_called = True
|
|
428
|
+
return dict(enumerate(output))
|
|
429
|
+
|
|
430
|
+
ResultBox(run_workflow).run_button.click()
|
|
431
|
+
assert was_called
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
FilenameSampleRun = NewType('FilenameSampleRun', str)
|
|
435
|
+
parameter_registry[FilenameSampleRun] = FilenameParameter.from_type(
|
|
436
|
+
FilenameSampleRun, default="SampleRun.hdf"
|
|
437
|
+
)
|
|
438
|
+
MultiFilenameSampleRun = NewType('MultiFilenameSampleRun', list[str])
|
|
439
|
+
parameter_registry[MultiFilenameSampleRun] = MultiFilenameParameter.from_type(
|
|
440
|
+
MultiFilenameSampleRun, default=["file1.hdf", "file2.hdf"]
|
|
441
|
+
)
|
|
442
|
+
MultiFilenameBackgroundRun = NewType('MultiFilenameBackgroundRun', list[str])
|
|
443
|
+
parameter_registry[MultiFilenameBackgroundRun] = MultiFilenameParameter.from_type(
|
|
444
|
+
MultiFilenameBackgroundRun, default="background.hdf"
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def filename_print_provider(fname: FilenameSampleRun) -> str:
|
|
449
|
+
return str(fname)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def multi_filename_print_provider(fnames: MultiFilenameSampleRun) -> list[str]:
|
|
453
|
+
return [str(fname) for fname in fnames]
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def multi_filename_print_provider_bis(fnames: MultiFilenameBackgroundRun) -> list[str]:
|
|
457
|
+
return [str(fname) for fname in fnames]
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def test_filename_widget_single() -> None:
|
|
461
|
+
widget = _ready_widget(providers=[filename_print_provider], output_selections=[str])
|
|
462
|
+
param_widget = _get_param_widget(widget, FilenameSampleRun)
|
|
463
|
+
assert param_widget.value == "SampleRun.hdf"
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def test_filename_widget_multiple() -> None:
|
|
467
|
+
widget = _ready_widget(
|
|
468
|
+
providers=[multi_filename_print_provider], output_selections=[list[str]]
|
|
469
|
+
)
|
|
470
|
+
param_widget = _get_param_widget(widget, MultiFilenameSampleRun)
|
|
471
|
+
assert param_widget.value == ("file1.hdf", "file2.hdf")
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def test_filename_widget_multiple_initialized_with_single_string() -> None:
|
|
475
|
+
widget = _ready_widget(
|
|
476
|
+
providers=[multi_filename_print_provider_bis], output_selections=[list[str]]
|
|
477
|
+
)
|
|
478
|
+
param_widget = _get_param_widget(widget, MultiFilenameBackgroundRun)
|
|
479
|
+
assert param_widget.value == ("background.hdf",)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|