Coverage for install/scipp/core/structured.py: 60%
43 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-11-17 01:51 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-11-17 01:51 +0000
1# SPDX-License-Identifier: BSD-3-Clause
2# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
3# @author Simon Heybrock
4from collections.abc import Iterator
5from typing import Any
7from .._scipp.core import _element_keys, _get_elements, _set_elements
8from .cpp_classes import DataArray, Dataset, DType, DTypeError, Variable
11def _prop(key: str) -> property:
12 def getter(self: Any) -> Variable:
13 return _get_elements(self._var, key) # type: ignore[no-any-return]
15 def setter(self: Any, x: Variable) -> None:
16 _set_elements(self._var, key, x)
18 return property(getter, setter)
21def is_structured(obj: Variable | DataArray) -> bool:
22 """Check whether a variable has a structured dtype."""
23 if obj.bins is not None:
24 data = obj.bins.constituents['data']
25 if isinstance(data, Dataset):
26 raise DTypeError(
27 "Datasets cannot have a structured dtype, "
28 "got a variable with Datasets in bins."
29 )
30 return is_structured(data)
31 return obj.dtype in [DType.vector3, DType.linear_transform3]
34class Fields:
35 """Accessor for fields of a structured variable."""
37 def __init__(self, keys: list[str], var: Variable) -> None:
38 self._var = var
39 self._keys = keys
41 def __contains__(self, key: str) -> bool:
42 return key in self._keys
44 def __iter__(self) -> Iterator[str]:
45 return self._keys.__iter__()
47 def __getitem__(self, key: str) -> Variable:
48 return _get_elements(self._var, key) # type: ignore[no-any-return]
50 def __setitem__(self, key: str, x: Variable) -> None:
51 _set_elements(self._var, key, x)
53 def keys(self) -> Iterator[str]:
54 yield from self._keys
56 def values(self) -> Iterator[Variable]:
57 yield from (self[key] for key in self)
59 def items(self) -> Iterator[tuple[str, Variable]]:
60 yield from ((key, self[key]) for key in self)
63# Make a new 'Fields' type to dynamically insert properties.
64# This means that the return annotation has to be `Any` and not `Fields`,
65# because otherwise, using the properties would result in type errors.
66def _fields(obj: Variable) -> Any:
67 fields = type('Fields', Fields.__bases__, dict(Fields.__dict__))
68 if is_structured(obj):
69 keys = _element_keys(obj)
70 for key in keys:
71 setattr(fields, key, _prop(key))
72 return fields(keys=keys, var=obj)
73 return None