typedspark 0.0.0__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.
typedspark/__init__.py ADDED
@@ -0,0 +1,54 @@
1
+ """Typedspark: column-wise type annotations for pyspark DataFrames."""
2
+
3
+ from typedspark._core.column import Column
4
+ from typedspark._core.column_meta import ColumnMeta
5
+ from typedspark._core.dataset import DataSet, DataSetImplements
6
+ from typedspark._core.datatypes import (
7
+ ArrayType,
8
+ DayTimeIntervalType,
9
+ DecimalType,
10
+ MapType,
11
+ StructType,
12
+ )
13
+ from typedspark._core.literaltype import IntervalType
14
+ from typedspark._schema.schema import MetaSchema, Schema
15
+ from typedspark._transforms.structtype_column import structtype_column
16
+ from typedspark._transforms.transform_to_schema import transform_to_schema
17
+ from typedspark._utils.create_dataset import (
18
+ create_empty_dataset,
19
+ create_partially_filled_dataset,
20
+ create_structtype_row,
21
+ )
22
+ from typedspark._utils.databases import Catalogs, Database, Databases
23
+ from typedspark._utils.load_table import create_schema, load_table
24
+ from typedspark._utils.register_schema_to_dataset import (
25
+ register_schema_to_dataset,
26
+ register_schema_to_dataset_with_alias,
27
+ )
28
+
29
+ __all__ = [
30
+ "ArrayType",
31
+ "Catalogs",
32
+ "Column",
33
+ "ColumnMeta",
34
+ "Database",
35
+ "Databases",
36
+ "DataSet",
37
+ "DayTimeIntervalType",
38
+ "DecimalType",
39
+ "IntervalType",
40
+ "MapType",
41
+ "MetaSchema",
42
+ "DataSetImplements",
43
+ "Schema",
44
+ "StructType",
45
+ "create_empty_dataset",
46
+ "create_partially_filled_dataset",
47
+ "create_structtype_row",
48
+ "create_schema",
49
+ "load_table",
50
+ "register_schema_to_dataset",
51
+ "register_schema_to_dataset_with_alias",
52
+ "structtype_column",
53
+ "transform_to_schema",
54
+ ]
File without changes
@@ -0,0 +1,97 @@
1
+ """Module containing classes and functions related to TypedSpark Columns."""
2
+
3
+ from logging import warn
4
+ from typing import Generic, Optional, TypeVar, Union, get_args, get_origin
5
+
6
+ from pyspark.sql import Column as SparkColumn
7
+ from pyspark.sql import DataFrame, SparkSession
8
+ from pyspark.sql.functions import col
9
+ from pyspark.sql.types import DataType
10
+
11
+ from typedspark._core.datatypes import StructType
12
+
13
+ T = TypeVar("T", bound=DataType)
14
+
15
+
16
+ class EmptyColumn(SparkColumn):
17
+ """Column object to be instantiated when there is no active Spark session."""
18
+
19
+ def __init__(self, *args, **kwargs) -> None: # pragma: no cover
20
+ pass
21
+
22
+
23
+ class Column(SparkColumn, Generic[T]):
24
+ """Represents a ``Column`` in a ``Schema``. Can be used as:
25
+
26
+ .. code-block:: python
27
+
28
+ class A(Schema):
29
+ a: Column[IntegerType]
30
+ b: Column[StringType]
31
+ """
32
+
33
+ def __new__(
34
+ cls,
35
+ name: str,
36
+ dataframe: Optional[DataFrame] = None,
37
+ curid: Optional[int] = None,
38
+ dtype: Optional[T] = None,
39
+ parent: Union[DataFrame, "Column", None] = None,
40
+ alias: Optional[str] = None,
41
+ ):
42
+ """``__new__()`` instantiates the object (prior to ``__init__()``).
43
+
44
+ Here, we simply take the provided ``name``, create a pyspark
45
+ ``Column`` object and cast it to a typedspark ``Column`` object.
46
+ This allows us to bypass the pypsark ``Column`` constuctor in
47
+ ``__init__()``, which requires parameters that may be difficult
48
+ to access.
49
+ """
50
+ # pylint: disable=unused-argument
51
+
52
+ if dataframe is not None and parent is None:
53
+ parent = dataframe
54
+ warn("The use of Column(dataframe=...) is deprecated, use Column(parent=...) instead.")
55
+
56
+ column: SparkColumn
57
+ if SparkSession.getActiveSession() is None:
58
+ column = EmptyColumn() # pragma: no cover
59
+ elif alias is not None:
60
+ column = col(f"{alias}.{name}")
61
+ elif parent is not None:
62
+ column = parent[name]
63
+ else:
64
+ column = col(name)
65
+
66
+ column.__class__ = Column
67
+ return column
68
+
69
+ def __init__(
70
+ self,
71
+ name: str,
72
+ dataframe: Optional[DataFrame] = None,
73
+ curid: Optional[int] = None,
74
+ dtype: Optional[T] = None,
75
+ parent: Union[DataFrame, "Column", None] = None,
76
+ alias: Optional[str] = None,
77
+ ):
78
+ # pylint: disable=unused-argument
79
+ self.str = name
80
+ self._dtype = dtype if dtype is not None else DataType
81
+ self._curid = curid
82
+
83
+ def __hash__(self) -> int:
84
+ return hash((self.str, self._curid))
85
+
86
+ @property
87
+ def dtype(self) -> T:
88
+ """Get the datatype of the column, e.g. Column[IntegerType] -> IntegerType."""
89
+ dtype = self._dtype
90
+
91
+ if get_origin(dtype) == StructType:
92
+ return StructType(
93
+ schema=get_args(dtype)[0],
94
+ parent=self,
95
+ ) # type: ignore
96
+
97
+ return dtype() # type: ignore
@@ -0,0 +1,26 @@
1
+ """Metadata for ``Column`` objects that can be accessed during runtime."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Dict, Optional
5
+
6
+
7
+ @dataclass
8
+ class ColumnMeta:
9
+ """Contains the metadata for a ``Column``. Used as:
10
+
11
+ .. code-block:: python
12
+
13
+ class A(Schema):
14
+ a: Annotated[
15
+ Column[IntegerType],
16
+ ColumnMeta(
17
+ comment="This is a comment",
18
+ )
19
+ ]
20
+ """
21
+
22
+ comment: Optional[str] = None
23
+
24
+ def get_metadata(self) -> Optional[Dict[str, str]]:
25
+ """Returns the metadata of this column."""
26
+ return {"comment": self.comment} if self.comment else None
@@ -0,0 +1,302 @@
1
+ """Module containing classes and functions related to TypedSpark DataSets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from copy import deepcopy
6
+ from typing import Callable, Generic, List, Literal, Optional, Type, TypeVar, Union, cast, overload
7
+
8
+ from pyspark.sql import Column as SparkColumn
9
+ from pyspark.sql import DataFrame
10
+ from typing_extensions import Concatenate, ParamSpec
11
+
12
+ from typedspark._core.validate_schema import validate_schema
13
+ from typedspark._schema.schema import Schema
14
+
15
+ _Schema = TypeVar("_Schema", bound=Schema)
16
+ _Protocol = TypeVar("_Protocol", bound=Schema, covariant=True)
17
+ _Implementation = TypeVar("_Implementation", bound=Schema, covariant=True)
18
+
19
+ P = ParamSpec("P")
20
+ _ReturnType = TypeVar("_ReturnType", bound=DataFrame) # pylint: disable=C0103
21
+
22
+
23
+ class DataSetImplements(DataFrame, Generic[_Protocol, _Implementation]):
24
+ """DataSetImplements allows us to define functions such as:
25
+
26
+ .. code-block:: python
27
+ class Age(Schema, Protocol):
28
+ age: Column[LongType]
29
+
30
+ def birthday(df: DataSetImplements[Age, T]) -> DataSet[T]:
31
+ return transform_to_schema(
32
+ df,
33
+ df.typedspark_schema,
34
+ {Age.age: Age.age + 1},
35
+ )
36
+
37
+ Such a function:
38
+ 1. Takes as an input ``DataSetImplements[Age, T]``: a ``DataSet`` that implements the protocol
39
+ ``Age`` as ``T``.
40
+ 2. Returns a ``DataSet[T]``: a ``DataSet`` of the same type as the one that was provided.
41
+
42
+ ``DataSetImplements`` should solely be used as a type annotation, it is never initialized."""
43
+
44
+ _schema_annotations: Type[_Implementation]
45
+
46
+ def __init__(self):
47
+ raise NotImplementedError(
48
+ "DataSetImplements should solely be used as a type annotation, it is never initialized."
49
+ )
50
+
51
+ @property
52
+ def typedspark_schema(self) -> Type[_Implementation]:
53
+ """Returns the ``Schema`` of the ``DataSet``."""
54
+ return self._schema_annotations
55
+
56
+ """The following functions are equivalent to their parents in ``DataFrame``, but since they
57
+ don't affect the ``Schema``, we can add type annotations here. We're omitting docstrings,
58
+ such that the docstring from the parent will appear."""
59
+
60
+ def alias(self, alias: str) -> DataSet[_Implementation]:
61
+ return DataSet[self._schema_annotations](super().alias(alias)) # type: ignore
62
+
63
+ def distinct(self) -> DataSet[_Implementation]: # pylint: disable=C0116
64
+ return DataSet[self._schema_annotations](super().distinct()) # type: ignore
65
+
66
+ def filter(self, condition) -> DataSet[_Implementation]: # pylint: disable=C0116
67
+ return DataSet[self._schema_annotations](super().filter(condition)) # type: ignore
68
+
69
+ @overload
70
+ def join( # type: ignore
71
+ self,
72
+ other: DataFrame,
73
+ on: Optional[ # pylint: disable=C0103
74
+ Union[str, List[str], SparkColumn, List[SparkColumn]]
75
+ ] = ...,
76
+ how: None = ...,
77
+ ) -> DataFrame: ... # pragma: no cover
78
+
79
+ @overload
80
+ def join(
81
+ self,
82
+ other: DataFrame,
83
+ on: Optional[ # pylint: disable=C0103
84
+ Union[str, List[str], SparkColumn, List[SparkColumn]]
85
+ ] = ...,
86
+ how: Literal["semi"] = ...,
87
+ ) -> DataSet[_Implementation]: ... # pragma: no cover
88
+
89
+ @overload
90
+ def join(
91
+ self,
92
+ other: DataFrame,
93
+ on: Optional[ # pylint: disable=C0103
94
+ Union[str, List[str], SparkColumn, List[SparkColumn]]
95
+ ] = ...,
96
+ how: Optional[str] = ...,
97
+ ) -> DataFrame: ... # pragma: no cover
98
+
99
+ def join( # pylint: disable=C0116
100
+ self,
101
+ other: DataFrame,
102
+ on: Optional[ # pylint: disable=C0103
103
+ Union[str, List[str], SparkColumn, List[SparkColumn]]
104
+ ] = None,
105
+ how: Optional[str] = None,
106
+ ) -> DataFrame:
107
+ return super().join(other, on, how) # type: ignore
108
+
109
+ def orderBy(self, *args, **kwargs) -> DataSet[_Implementation]: # type: ignore # noqa: N802, E501 # pylint: disable=C0116, C0103
110
+ return DataSet[self._schema_annotations](super().orderBy(*args, **kwargs)) # type: ignore
111
+
112
+ def transform(
113
+ self,
114
+ func: Callable[Concatenate[DataSet[_Implementation], P], _ReturnType],
115
+ *args: P.args,
116
+ **kwargs: P.kwargs,
117
+ ) -> _ReturnType:
118
+ return super().transform(func, *args, **kwargs) # type: ignore
119
+
120
+ @overload
121
+ def unionByName( # noqa: N802 # pylint: disable=C0116, C0103
122
+ self,
123
+ other: DataSet[_Implementation],
124
+ allowMissingColumns: Literal[False] = ..., # noqa: N803
125
+ ) -> DataSet[_Implementation]: ... # pragma: no cover
126
+
127
+ @overload
128
+ def unionByName( # noqa: N802 # pylint: disable=C0116, C0103
129
+ self,
130
+ other: DataFrame,
131
+ allowMissingColumns: bool = ..., # noqa: N803
132
+ ) -> DataFrame: ... # pragma: no cover
133
+
134
+ def unionByName( # noqa: N802 # pylint: disable=C0116, C0103
135
+ self,
136
+ other: DataFrame,
137
+ allowMissingColumns: bool = False, # noqa: N803
138
+ ) -> DataFrame:
139
+ res = super().unionByName(other, allowMissingColumns)
140
+ if isinstance(other, DataSet) and other._schema_annotations == self._schema_annotations:
141
+ return DataSet[self._schema_annotations](res) # type: ignore
142
+ return res # pragma: no cover
143
+
144
+
145
+ class DataSet(DataSetImplements[_Schema, _Schema]):
146
+ """``DataSet`` subclasses pyspark ``DataFrame`` and hence has all the same
147
+ functionality, with in addition the possibility to define a schema.
148
+
149
+ .. code-block:: python
150
+
151
+ class Person(Schema):
152
+ name: Column[StringType]
153
+ age: Column[LongType]
154
+
155
+ def foo(df: DataSet[Person]) -> DataSet[Person]:
156
+ # do stuff
157
+ return df
158
+ """
159
+
160
+ def __new__(cls, dataframe: DataFrame) -> DataSet[_Schema]:
161
+ """``__new__()`` instantiates the object (prior to ``__init__()``).
162
+
163
+ Here, we simply take the provided ``df`` and cast it to a
164
+ ``DataSet``. This allows us to bypass the ``DataFrame``
165
+ constuctor in ``__init__()``, which requires parameters that may
166
+ be difficult to access. Subsequently, we perform schema validation, if
167
+ the schema annotations are provided.
168
+ """
169
+ dataframe = cast(DataSet, dataframe)
170
+ dataframe.__class__ = DataSet
171
+
172
+ # first we reset the schema annotations to None, in case they are inherrited through the
173
+ # passed DataFrame
174
+ dataframe._schema_annotations = None # type: ignore
175
+
176
+ # then we use the class' schema annotations to validate the schema and add metadata
177
+ if hasattr(cls, "_schema_annotations"):
178
+ dataframe._schema_annotations = cls._schema_annotations # type: ignore
179
+ dataframe._validate_schema()
180
+ dataframe._add_schema_metadata()
181
+
182
+ return dataframe # type: ignore
183
+
184
+ def __init__(self, dataframe: DataFrame):
185
+ pass
186
+
187
+ def __class_getitem__(cls, item):
188
+ """Allows us to define a schema for the ``DataSet``.
189
+
190
+ To make sure that the DataSet._schema_annotations variable isn't reused globally, we
191
+ generate a subclass of the ``DataSet`` with the schema annotations as a class variable.
192
+ """
193
+ subclass_name = f"{cls.__name__}[{item.__name__}]"
194
+ subclass = type(subclass_name, (cls,), {"_schema_annotations": item})
195
+ return subclass
196
+
197
+ def _validate_schema(self) -> None:
198
+ """Validates the schema of the ``DataSet`` against the schema annotations."""
199
+ validate_schema(
200
+ self._schema_annotations.get_structtype(),
201
+ deepcopy(self.schema),
202
+ self._schema_annotations.get_schema_name(),
203
+ )
204
+
205
+ def _add_schema_metadata(self) -> None:
206
+ """Adds the ``ColumnMeta`` comments as metadata to the ``DataSet``.
207
+
208
+ Previously set metadata is deleted. Hence, if ``foo(dataframe: DataSet[A]) -> DataSet[B]``,
209
+ then ``DataSet[B]`` will not inherrit any metadata from ``DataSet[A]``.
210
+
211
+ Assumes validate_schema() in __setattr__() has been run.
212
+ """
213
+ for field in self._schema_annotations.get_structtype().fields:
214
+ self.schema[field.name].metadata = field.metadata
215
+
216
+ """The following functions are equivalent to their parents in ``DataSetImplements``. However,
217
+ to support functions like ``functools.reduce(DataSet.unionByName, datasets)``, we also add them
218
+ here. Unfortunately, this leads to some code redundancy, but we'll take that for granted."""
219
+
220
+ def alias(self, alias: str) -> DataSet[_Schema]:
221
+ return DataSet[self._schema_annotations](super().alias(alias)) # type: ignore
222
+
223
+ def distinct(self) -> DataSet[_Schema]: # pylint: disable=C0116
224
+ return DataSet[self._schema_annotations](super().distinct()) # type: ignore
225
+
226
+ def filter(self, condition) -> DataSet[_Schema]: # pylint: disable=C0116
227
+ return DataSet[self._schema_annotations](super().filter(condition)) # type: ignore
228
+
229
+ @overload
230
+ def join( # type: ignore
231
+ self,
232
+ other: DataFrame,
233
+ on: Optional[ # pylint: disable=C0103
234
+ Union[str, List[str], SparkColumn, List[SparkColumn]]
235
+ ] = ...,
236
+ how: None = ...,
237
+ ) -> DataFrame: ... # pragma: no cover
238
+
239
+ @overload
240
+ def join(
241
+ self,
242
+ other: DataFrame,
243
+ on: Optional[ # pylint: disable=C0103
244
+ Union[str, List[str], SparkColumn, List[SparkColumn]]
245
+ ] = ...,
246
+ how: Literal["semi"] = ...,
247
+ ) -> DataSet[_Schema]: ... # pragma: no cover
248
+
249
+ @overload
250
+ def join(
251
+ self,
252
+ other: DataFrame,
253
+ on: Optional[ # pylint: disable=C0103
254
+ Union[str, List[str], SparkColumn, List[SparkColumn]]
255
+ ] = ...,
256
+ how: Optional[str] = ...,
257
+ ) -> DataFrame: ... # pragma: no cover
258
+
259
+ def join( # pylint: disable=C0116
260
+ self,
261
+ other: DataFrame,
262
+ on: Optional[ # pylint: disable=C0103
263
+ Union[str, List[str], SparkColumn, List[SparkColumn]]
264
+ ] = None,
265
+ how: Optional[str] = None,
266
+ ) -> DataFrame:
267
+ return super().join(other, on, how) # type: ignore
268
+
269
+ def orderBy(self, *args, **kwargs) -> DataSet[_Schema]: # type: ignore # noqa: N802, E501 # pylint: disable=C0116, C0103
270
+ return DataSet[self._schema_annotations](super().orderBy(*args, **kwargs)) # type: ignore
271
+
272
+ def transform(
273
+ self,
274
+ func: Callable[Concatenate[DataSet[_Schema], P], _ReturnType],
275
+ *args: P.args,
276
+ **kwargs: P.kwargs,
277
+ ) -> _ReturnType:
278
+ return super().transform(func, *args, **kwargs) # type: ignore
279
+
280
+ @overload
281
+ def unionByName( # noqa: N802 # pylint: disable=C0116, C0103
282
+ self,
283
+ other: DataSet[_Schema],
284
+ allowMissingColumns: Literal[False] = ..., # noqa: N803
285
+ ) -> DataSet[_Schema]: ... # pragma: no cover
286
+
287
+ @overload
288
+ def unionByName( # noqa: N802 # pylint: disable=C0116, C0103
289
+ self,
290
+ other: DataFrame,
291
+ allowMissingColumns: bool = ..., # noqa: N803
292
+ ) -> DataFrame: ... # pragma: no cover
293
+
294
+ def unionByName( # noqa: N802 # pylint: disable=C0116, C0103
295
+ self,
296
+ other: DataFrame,
297
+ allowMissingColumns: bool = False, # noqa: N803
298
+ ) -> DataFrame:
299
+ res = super().unionByName(other, allowMissingColumns)
300
+ if isinstance(other, DataSet) and other._schema_annotations == self._schema_annotations:
301
+ return DataSet[self._schema_annotations](res) # type: ignore
302
+ return res # pragma: no cover
@@ -0,0 +1,106 @@
1
+ """Here, we make our own definitions of ``MapType``, ``ArrayType`` and ``StructType`` in
2
+ order to allow e.g. for ``ArrayType[StringType]``."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import TYPE_CHECKING, Any, Dict, Generic, Type, TypeVar
7
+
8
+ from pyspark.sql.types import DataType
9
+
10
+ if TYPE_CHECKING: # pragma: no cover
11
+ from typedspark._core.column import Column
12
+ from typedspark._schema.schema import Schema
13
+
14
+ _Schema = TypeVar("_Schema", bound=Schema)
15
+ else:
16
+ _Schema = TypeVar("_Schema")
17
+
18
+ _KeyType = TypeVar("_KeyType", bound=DataType) # pylint: disable=invalid-name
19
+ _ValueType = TypeVar("_ValueType", bound=DataType) # pylint: disable=invalid-name
20
+ _Precision = TypeVar("_Precision", bound=int) # pylint: disable=invalid-name
21
+ _Scale = TypeVar("_Scale", bound=int) # pylint: disable=invalid-name
22
+ _StartField = TypeVar("_StartField", bound=int) # pylint: disable=invalid-name
23
+ _EndField = TypeVar("_EndField", bound=int) # pylint: disable=invalid-name
24
+
25
+
26
+ class TypedSparkDataType(DataType):
27
+ """Base class for typedspark specific ``DataTypes``."""
28
+
29
+ @classmethod
30
+ def get_name(cls) -> str:
31
+ """Return the name of the type."""
32
+ return cls.__name__
33
+
34
+
35
+ class StructTypeMeta(type):
36
+ """Initializes the schema attribute as None.
37
+
38
+ This allows for auto-complete in Databricks notebooks (uninitialized variables don't
39
+ show up in auto-complete there).
40
+ """
41
+
42
+ def __new__(cls, name: str, bases: Any, dct: Dict[str, Any]):
43
+ dct["schema"] = None
44
+ return super().__new__(cls, name, bases, dct)
45
+
46
+
47
+ class StructType(Generic[_Schema], TypedSparkDataType, metaclass=StructTypeMeta):
48
+ """Allows for type annotations such as:
49
+
50
+ .. code-block:: python
51
+
52
+ class Job(Schema):
53
+ position: Column[StringType]
54
+ salary: Column[LongType]
55
+
56
+ class Person(Schema):
57
+ job: Column[StructType[Job]]
58
+ """
59
+
60
+ def __init__(
61
+ self,
62
+ schema: Type[_Schema],
63
+ parent: Column,
64
+ ) -> None:
65
+ self.schema = schema
66
+ self.schema._parent = parent
67
+
68
+
69
+ class MapType(Generic[_KeyType, _ValueType], TypedSparkDataType):
70
+ """Allows for type annotations such as.
71
+
72
+ .. code-block:: python
73
+
74
+ class Basket(Schema):
75
+ items: Column[MapType[StringType, StringType]]
76
+ """
77
+
78
+
79
+ class ArrayType(Generic[_ValueType], TypedSparkDataType):
80
+ """Allows for type annotations such as.
81
+
82
+ .. code-block:: python
83
+
84
+ class Basket(Schema):
85
+ items: Column[ArrayType[StringType]]
86
+ """
87
+
88
+
89
+ class DecimalType(Generic[_Precision, _Scale], TypedSparkDataType):
90
+ """Allows for type annotations such as.
91
+
92
+ .. code-block:: python
93
+
94
+ class Numbers(Schema):
95
+ number: Column[DecimalType[Literal[10], Literal[0]]]
96
+ """
97
+
98
+
99
+ class DayTimeIntervalType(Generic[_StartField, _EndField], TypedSparkDataType):
100
+ """Allows for type annotations such as.
101
+
102
+ .. code-block:: python
103
+
104
+ class TimeInterval(Schema):
105
+ interval: Column[DayTimeIntervalType[IntervalType.HOUR, IntervalType.SECOND]
106
+ """
@@ -0,0 +1,41 @@
1
+ """Defines ``LiteralTypes``, e.g. ``IntervalType.DAY``, that map their class attribute
2
+ to a ``Literal`` integer.
3
+
4
+ Can be used for example in ``DayTimeIntervalType``.
5
+ """
6
+
7
+ from typing import Dict, Literal
8
+
9
+
10
+ class LiteralType:
11
+ """Base class for literal types, that map their class attribute to a Literal
12
+ integer."""
13
+
14
+ @classmethod
15
+ def get_dict(cls) -> Dict[str, str]:
16
+ """Returns a dictionary mapping e.g. "IntervalType.DAY" -> "Literal[0]"."""
17
+ dictionary = {}
18
+ for key, value in cls.__dict__.items():
19
+ if key.startswith("_"):
20
+ continue
21
+
22
+ key = f"{cls.__name__}.{key}"
23
+ value = str(value).replace("typing.", "")
24
+
25
+ dictionary[key] = value
26
+
27
+ return dictionary
28
+
29
+ @classmethod
30
+ def get_inverse_dict(cls) -> Dict[str, str]:
31
+ """Returns a dictionary mapping e.g. "Literal[0]" -> "IntervalType.DAY"."""
32
+ return {v: k for k, v in cls.get_dict().items()}
33
+
34
+
35
+ class IntervalType(LiteralType):
36
+ """Interval types for ``DayTimeIntervalType``."""
37
+
38
+ DAY = Literal[0]
39
+ HOUR = Literal[1]
40
+ MINUTE = Literal[2]
41
+ SECOND = Literal[3]