Repository: astropenguin/pandas-dataclasses

Issue:
Support for union type in type hints
Update typing module to allow users to use union type in `Data` and `Index`:
```python
@dataclass
class Weather(AsFrame):
    """Weather information."""

    year: Index[int]
    month: Index[int]
    temp: Data[float | None]
    wind: Data[float | None]
```
See also #115 for related discussions.

Discussion:

Patch:
diff --git a/pandas_dataclasses/core/typing.py b/pandas_dataclasses/core/typing.py
index a97a6a0..161ce48 100644
--- a/pandas_dataclasses/core/typing.py
+++ b/pandas_dataclasses/core/typing.py
@@ -2,6 +2,7 @@ __all__ = ["Attr", "Column", "Data", "Index", "Other"]
 
 
 # standard library
+import types
 from dataclasses import Field
 from enum import Enum, auto
 from itertools import chain
@@ -149,6 +150,9 @@ def get_dtype(tp: Any) -> Optional[str]:
     if dtype is Any or dtype is type(None):
         return None
 
+    if is_union_type(dtype):
+        dtype = get_args(dtype)[0]
+
     if get_origin(dtype) is Literal:
         dtype = get_args(dtype)[0]
 
@@ -179,3 +183,12 @@ def get_role(tp: Any, default: Role = Role.OTHER) -> Role:
         return get_annotations(tp)[0]  # type: ignore
     except TypeError:
         return default
+
+
+def is_union_type(tp: Any) -> bool:
+    """Check if a type hint is a union type."""
+    if get_origin(tp) is Union:
+        return True
+
+    UnionType = getattr(types, "UnionType", None)
+    return UnionType is not None and isinstance(tp, UnionType)


Test patch:
diff --git a/tests/test_typing.py b/tests/test_typing.py
index de6d646..4ef0e43 100644
--- a/tests/test_typing.py
+++ b/tests/test_typing.py
@@ -16,12 +16,14 @@ testdata_dtype = [
     (Data[Any], None),
     (Data[None], None),
     (Data[int], np.dtype("i8")),
+    (Data[Union[int, None]], np.dtype("i8")),
     (Data[L["i8"]], np.dtype("i8")),
     (Data[L["boolean"]], pd.BooleanDtype()),
     (Data[L["category"]], pd.CategoricalDtype()),
     (Index[Any], None),
     (Index[None], None),
     (Index[int], np.dtype("i8")),
+    (Index[Union[int, None]], np.dtype("i8")),
     (Index[L["i8"]], np.dtype("i8")),
     (Index[L["boolean"]], pd.BooleanDtype()),
     (Index[L["category"]], pd.CategoricalDtype()),