Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 35 additions & 146 deletions packages/essreduce/src/ess/reduce/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
from __future__ import annotations

from collections.abc import MutableMapping
from dataclasses import dataclass
from enum import Enum
from typing import Generic, Self, TypeVar
from collections.abc import Callable, MutableMapping
from dataclasses import dataclass, replace
from typing import Any, Generic, TypeVar

import scipp as sc
from sciline import Pipeline
from sciline._utils import key_name
from sciline.typing import Key

Expand All @@ -21,158 +20,51 @@ class KeepDefaultType:
keep_default = KeepDefaultType()


@dataclass
class Parameter(Generic[T]):
"""Interface between workflow parameters and widgets.
@dataclass(frozen=True)
class ParameterSpec(Generic[T]):
"""Specification for configuring a Sciline workflow key."""

.. versionadded:: RELEASE_PLACEHOLDER
"""
model: Any
category: str
title: str | None = None
description: str | None = None
default: T | KeepDefaultType = keep_default
transform: Callable[[T], Any] | None = None
apply: Callable[[Pipeline, T], Pipeline] | None = None
use_workflow_default: bool = True
key: Key | None = None

name: str
description: str
default: T
optional: bool = False
"""If True, widget has radio buttons switch between "None" and param widget."""
switchable: bool = False
"""If True, widget has checkbox to enable/disable parameter."""
def bind(self, key: Key) -> ParameterSpec[T]:
return replace(self, key=key)

def with_default(self: Self, default: T | KeepDefaultType = keep_default) -> Self:
if default == keep_default:
def with_default(
self, default: T | KeepDefaultType = keep_default
) -> ParameterSpec[T]:
if default is keep_default or not self.use_workflow_default:
return self
# TODO I think some subclasses currently cannot be instantiated with this method
return type(self)(
self.name, self.description, default, self.optional, self.switchable
)

@classmethod
def from_type(
cls: type[Self],
t: type[T],
default: T | None = None,
optional: bool = False,
switchable: bool = False,
) -> Self:
# TODO __doc__ not correct when using NewType
# TODO __doc__ not correct when using Generic
# use sciline type->string helper
return cls(
name=key_name(t),
description=t.__doc__,
default=default,
optional=optional,
switchable=switchable,
)
return replace(self, default=default)

def with_apply(
self, apply: Callable[[Pipeline, T], Pipeline] | None
) -> ParameterSpec[T]:
return replace(self, apply=apply)

@dataclass(kw_only=True)
class ParamWithOptions(Parameter[T]):
options: Enum

@classmethod
def from_enum(cls: type[Self], t: type[T], default: T) -> Self:
return cls(
name=t.__name__,
description=t.__doc__,
options=t.__members__,
default=default,
@property
def name(self) -> str:
return self.title or (
key_name(self.key) if self.key is not None else 'Parameter'
)


@dataclass
class FilenameParameter(Parameter[str]):
"""Widget for entering a filename or selecting one in a file dialog."""

# TODO need specifics for different file types, nexus, ...


@dataclass
class MultiFilenameParameter(Parameter[tuple[str, ...]]):
"""Widget for entering multiple filenames or selecting multiple in a file dialog."""


@dataclass(kw_only=True)
class BinEdgesParameter(Parameter[sc.Variable]):
"""Widget for entering bin edges."""

dim: str
start: float | None = None
stop: float | None = None
nbins: int = 1
unit: str | None = "undefined" # If "undefined", the unit is deduced from the dim
log: bool = False

def __init__(
self,
t: type[T],
dim: str,
start: float | None = None,
stop: float | None = None,
nbins: int = 1,
unit: str | None = "undefined",
log: bool = False,
):
self.dim = dim
self.start = start
self.stop = stop
self.nbins = nbins
self.unit = unit
self.log = log
super().__init__(name=key_name(t), description=t.__doc__, default=None)


@dataclass
class BooleanParameter(Parameter[bool]):
pass


@dataclass
class StringParameter(Parameter[str]):
pass


@dataclass
class MultiStringParameter(Parameter[tuple[str, ...]]):
"""Widget for entering multiple strings."""


@dataclass(kw_only=True)
class ParamWithBounds(Parameter[T]):
bounds: tuple[T, T]


@dataclass(kw_only=True)
class ScalarParameter(Parameter[T]):
"""Fixed unit displayed in widget"""

unit: str


@dataclass(kw_only=True)
class ScalarParamWithUnitOptions(Parameter[T]):
"""User can select between compatible units"""

unit_options: list[str]


@dataclass(kw_only=True)
class Vector2dParameter(Parameter[sc.Variable]):
"""Widget for entering a 2d vector."""


@dataclass(kw_only=True)
class Vector3dParameter(Parameter[sc.Variable]):
"""Widget for entering a 3d vector."""


class ParameterRegistry(MutableMapping):
def __init__(self):
self._parameters = {}
self._parameters: dict[Key, ParameterSpec] = {}

def __getitem__(self, key: Key) -> Parameter:
def __getitem__(self, key: Key) -> ParameterSpec:
return self._parameters[key]

def __setitem__(self, key: Key, value: Parameter):
self._parameters[key] = value
def __setitem__(self, key: Key, value: ParameterSpec):
self._parameters[key] = value.bind(key)

def __delitem__(self, key: Key):
del self._parameters[key]
Expand All @@ -185,6 +77,3 @@ def __len__(self) -> int:


parameter_registry = ParameterRegistry()


parameter_mappers = {}
Loading
Loading