"""Utils for traitlets"""
import traitlets
from traitlets import TraitType
import typing
from typing import Union, Type, Any, Dict
from collections.abc import Callable
import enum
import re
import collections.abc
ObserveHandlerPyType = Callable[[dict[str, Any]], Any]
# Mapping from traitlets types to Python types (as defined previously)
py_type_for_traitlet_type = {
# C* types before their base types
traitlets.CBool: bool,
traitlets.Bool: bool,
traitlets.CBytes: bytes,
traitlets.Bytes: bytes,
traitlets.CComplex: complex,
traitlets.Complex: complex,
traitlets.CFloat: float,
traitlets.Float: float,
traitlets.CInt: int,
traitlets.Int: int,
traitlets.CUnicode: str,
traitlets.Unicode: str,
traitlets.CRegExp: re.Pattern,
traitlets.DottedObjectName: str,
traitlets.ObjectName: str,
traitlets.CaselessStrEnum: str,
traitlets.FuzzyEnum: str,
traitlets.Enum: enum.Enum,
traitlets.TCPAddress: tuple,
# Container types and their subclasses
traitlets.List: list,
traitlets.Set: set,
traitlets.Tuple: tuple,
traitlets.Container: collections.abc.Container,
traitlets.Dict: dict,
# Class and instance types
traitlets.ForwardDeclaredInstance: object,
traitlets.Instance: object,
traitlets.ForwardDeclaredType: type,
traitlets.Type: type,
traitlets.This: type,
traitlets.ClassBasedTraitType: type,
# Other types
traitlets.Callable: collections.abc.Callable,
traitlets.ObserveHandler: ObserveHandlerPyType,
traitlets.UseEnum: enum.Enum,
traitlets.Union: typing.Union,
traitlets.Any: object,
traitlets.TraitType: object,
}
[docs]
def trait_to_py(trait: TraitType | type[TraitType]) -> object | type:
"""
Convert a traitlets trait (instance or type) to a Python object (instance or type)
>>> trait_to_py(traitlets.Bool())
<class 'bool'>
>>> trait_to_py(traitlets.Union([traitlets.Unicode(), traitlets.Float()]))
typing.Union[str, float]
"""
if isinstance(trait, type):
# Trait is a type (class), map it directly
if trait in py_type_for_traitlet_type:
py_type = py_type_for_traitlet_type[trait]
return py_type
else:
raise ValueError(f"Unknown traitlet type: {trait}")
else:
# Trait is an instance
trait_type = type(trait)
if trait_type is traitlets.Instance:
# For Instance traits, return the class if available
if isinstance(trait.klass, type):
return trait.klass
else:
return typing.Any
elif trait_type is traitlets.UseEnum:
# For UseEnum traits, return the enum class
if hasattr(trait, "enum_class"):
return trait.enum_class
else:
return enum.Enum
elif trait_type is traitlets.Type:
# For Type traits, return Type[class]
if isinstance(trait.klass, type):
return type[trait.klass]
else:
return type[typing.Any]
else:
# Get the base Python type
py_type = trait_to_py(trait_type)
# Extract type parameters if any
args = extract_type_params(trait_type, trait)
if args:
# For typing generics, apply the type parameters
return py_type[args]
else:
return py_type