Defining new trait types#

To define a new trait type, subclass from TraitType. You can define the following things:

class traitlets.MyTrait#
info_text#

A short string describing what this trait should hold.

default_value#

A default value, if one makes sense for this trait type. If there is no obvious default, don’t provide this.

validate(obj, value)#

Check whether a given value is valid. If it is, it should return the value (coerced to the desired type, if necessary). If not, it should raise TraitError. TraitType.error() is a convenient way to raise an descriptive error saying that the given value is not of the required type.

obj is the object to which the trait belongs.

For instance, here’s the definition of the TCPAddress trait:

class TCPAddress(TraitType[G, S]):
    """A trait for an (ip, port) tuple.

    This allows for both IPv4 IP addresses as well as hostnames.
    """

    default_value = ("127.0.0.1", 0)
    info_text = "an (ip, port) tuple"

    if t.TYPE_CHECKING:

        @t.overload
        def __init__(
            self: TCPAddress[tuple[str, int], tuple[str, int]],
            default_value: bool | Sentinel = ...,
            allow_none: Literal[False] = ...,
            read_only: bool | None = ...,
            help: str | None = ...,
            config: t.Any = ...,
            **kwargs: t.Any,
        ) -> None:
            ...

        @t.overload
        def __init__(
            self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None],
            default_value: bool | None | Sentinel = ...,
            allow_none: Literal[True] = ...,
            read_only: bool | None = ...,
            help: str | None = ...,
            config: t.Any = ...,
            **kwargs: t.Any,
        ) -> None:
            ...

        def __init__(
            self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None]
            | TCPAddress[tuple[str, int], tuple[str, int]],
            default_value: bool | None | Sentinel = Undefined,
            allow_none: Literal[True, False] = False,
            read_only: bool | None = None,
            help: str | None = None,
            config: t.Any = None,
            **kwargs: t.Any,
        ) -> None:
            ...

    def validate(self, obj: t.Any, value: t.Any) -> G:
        if isinstance(value, tuple):
            if len(value) == 2:
                if isinstance(value[0], str) and isinstance(value[1], int):
                    port = value[1]
                    if port >= 0 and port <= 65535:
                        return t.cast(G, value)
        self.error(obj, value)

    def from_string(self, s: str) -> G:
        if self.allow_none and s == "None":
            return t.cast(G, None)
        if ":" not in s:
            raise ValueError("Require `ip:port`, got %r" % s)
        ip, port_str = s.split(":", 1)
        port = int(port_str)
        return t.cast(G, (ip, port))