Coverage for python / lsst / images / serialization / _dtypes.py: 58%
44 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-23 08:41 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-23 08:41 +0000
1# This file is part of lsst-images.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12from __future__ import annotations
14__all__ = (
15 "FloatType",
16 "IntegerType",
17 "NumberType",
18 "SignedIntegerType",
19 "UnsignedIntegerType",
20 "is_integer",
21)
23import enum
24from typing import Literal, TypeGuard
26import numpy as np
27import numpy.typing as npt
30class NumberType(enum.StrEnum):
31 """Enumeration of array values types supported by the library."""
33 bool = enum.auto()
34 uint8 = enum.auto()
35 uint16 = enum.auto()
36 uint32 = enum.auto()
37 uint64 = enum.auto()
38 int8 = enum.auto()
39 int16 = enum.auto()
40 int32 = enum.auto()
41 int64 = enum.auto()
42 float32 = enum.auto()
43 float64 = enum.auto()
45 def to_numpy(self) -> type:
46 """Convert an enumeration member to the corresponding numpy scalar
47 type object.
49 Returns
50 -------
51 type
52 Numpy scalar type, e.g. `numpy.int16`. Note that this inherits
53 from `type`, not `numpy.dtype` (though a `numpy.dtype` instance
54 can always be constructed from it).
55 """
56 return getattr(np, self.value)
58 @classmethod
59 def from_numpy(cls, dtype: npt.DTypeLike) -> NumberType:
60 """Construct an enumeration member from anything that can be coerced
61 to `numpy.dtype`.
63 Parameters
64 ----------
65 dtype
66 Object convertible to `numpy.dtype`.
67 """
68 datatype, shape = cls.from_numpy_with_shape(dtype)
69 if shape:
70 raise TypeError(f"{dtype} is not a scalar type.")
71 return datatype
73 @classmethod
74 def from_numpy_with_shape(cls, dtype: npt.DTypeLike) -> tuple[NumberType, tuple[int, ...]]:
75 """Construct an enumeration member and extract a shape from anything
76 that can be coerced to `numpy.dtype`.
78 Parameters
79 ----------
80 dtype
81 Object convertible to `numpy.dtype`.
83 Returns
84 -------
85 `NumberType`
86 Enumeration member.
87 `tuple` [`int`, ...]
88 Shape as a `tuple` of `int`.
89 """
90 dtype = np.dtype(dtype)
91 shape = ()
92 if dtype.shape:
93 shape = dtype.shape
94 dtype = dtype.base
95 return cls(dtype.name), shape
97 def require_integer(self) -> IntegerType:
98 """Raise `TypeError` if this enumeration does not represent an
99 integer type, and return it if it does.
100 """
101 if is_integer(self):
102 return self
103 raise TypeError(f"{self} is not an integer type.")
106type UnsignedIntegerType = (
107 Literal[NumberType.bool]
108 | Literal[NumberType.uint8]
109 | Literal[NumberType.uint16]
110 | Literal[NumberType.uint32]
111 | Literal[NumberType.uint64]
112)
114type SignedIntegerType = (
115 Literal[NumberType.int8]
116 | Literal[NumberType.int16]
117 | Literal[NumberType.int32]
118 | Literal[NumberType.int64]
119)
122type IntegerType = SignedIntegerType | UnsignedIntegerType
124type FloatType = Literal[NumberType.float32] | Literal[NumberType.float64]
127def is_integer(t: NumberType) -> TypeGuard[IntegerType]:
128 """Test whether a `NumberType` corresponds to an integer type."""
129 return np.dtype(t.to_numpy()).kind in ("i", "u")