Skip to content

pane.types

pane.types

Helper types for use with pane.convert and dataclasses.

Num = t.TypeVar('Num', bound=t.Union[int, float]) module-attribute

PositiveInt = t.Annotated[int, Positive] module-attribute

NonNegativeInt = t.Annotated[int, NonNegative] module-attribute

NegativeInt = t.Annotated[int, Negative] module-attribute

NonPositiveInt = t.Annotated[int, NonPositive] module-attribute

PositiveFloat = t.Annotated[float, Positive] module-attribute

NonNegativeFloat = t.Annotated[float, NonNegative] module-attribute

NegativeFloat = t.Annotated[float, Negative] module-attribute

NonPositiveFloat = t.Annotated[float, NonPositive] module-attribute

FiniteFloat = t.Annotated[float, Finite] module-attribute

ListNotEmpty = t.Annotated[t.List[T], len_range(min=1)] module-attribute

Range

Bases: PaneBase, Generic[Num]

Source code in pane/types.py
class Range(PaneBase, t.Generic[Num],
            in_format=('tuple', 'struct'),
            out_format='struct'):
    start: Num
    end: Num

    n: t.Optional[NonNegativeInt] = field(default=None)
    step: t.Optional[Num] = field(default=None, kw_only=True)

    def __post_init__(self):
        s = sum((self.step is None, self.n is None))
        if s == 0:
            raise TypeError("Either 'n' or 'step' may be specified, but not both")
        if s == 2:
            raise TypeError("Either 'n' or 'step' must be specified")
        span = self.end - self.start
        if self.step is not None:
            if math.isclose(self.step, 0.):
                raise ValueError("'step' should be nonzero")
            n = 1 + math.ceil(span / self.step - 1e-6) if span > 0 else 0
            object.__setattr__(self, 'n', n)
        else:
            assert self.n is not None
            if not isinstance(self.start, float) and span % (self.n - 1):
                raise ValueError("Range must be evenly divisible by 'n'")
            step = type(self.start)(span / (self.n - 1)) if self.n > 1 else None
            object.__setattr__(self, 'step', step)

    def __len__(self) -> int:
        return t.cast(int, self.n)

    def __iter__(self) -> t.Iterator[Num]:
        assert self.n is not None
        if self.n == 0:
            return
        val: Num = self.start
        for _ in range(self.n - 1):
            yield val
            val = t.cast(Num, val + self.step)
        yield self.end

start instance-attribute

end instance-attribute

n = field(default=None) class-attribute instance-attribute

step = field(default=None, kw_only=True) class-attribute instance-attribute

_converter(*args, handlers) classmethod

Source code in pane/classes.py
@classmethod
def _converter(cls: t.Type[PaneBaseT], *args: t.Type[Convertible],
               handlers: ConverterHandlers) -> Converter[PaneBaseT]:
    if len(args) > 0:
        cls = t.cast(t.Type[PaneBaseT], cls[tuple(args)])  # type: ignore
    return PaneConverter(cls, handlers=handlers)

make_unchecked(*args, **kwargs) classmethod

Source code in pane/classes.py
@classmethod
def make_unchecked(cls, *args: t.Any, **kwargs: t.Any) -> Self:
    ...

from_dict_unchecked(d, *, set_fields=None) classmethod

Source code in pane/classes.py
@classmethod
def from_dict_unchecked(cls, d: t.Dict[str, t.Any], *, set_fields: t.Optional[t.Set[str]] = None) -> Self:
    ...

from_obj(obj, *, custom=None) classmethod

Convert obj into cls. Equivalent to convert(obj, cls)

Parameters:

Name Type Description Default
obj Convertible

Object to convert. Must be convertible.

required
Source code in pane/classes.py
@classmethod
def from_obj(cls, obj: Convertible, *,
             custom: t.Optional[IntoConverterHandlers] = None) -> Self:
    """
    Convert `obj` into `cls`. Equivalent to `convert(obj, cls)`

    Parameters:
      obj: Object to convert. Must be convertible.
    """
    return convert(obj, cls, custom=custom)

from_data(data, *, custom=None) classmethod

Convert data into cls. Equivalent to from_data(data, cls)

Parameters:

Name Type Description Default
data DataType

Data to convert. Must be a data interchange type.

required
Source code in pane/classes.py
@classmethod
def from_data(cls, data: DataType, *,
              custom: t.Optional[IntoConverterHandlers] = None) -> Self:
    """
    Convert `data` into `cls`. Equivalent to `from_data(data, cls)`

    Parameters:
      data: Data to convert. Must be a data interchange type.
    """
    return from_data(data, cls, custom=custom)

into_data(*, custom=None)

Convert self into interchange data

Source code in pane/classes.py
def into_data(self, *, custom: t.Optional[IntoConverterHandlers] = None) -> DataType:
    """Convert `self` into interchange data"""
    return into_data(self, self.__class__, custom=custom)

dict(*, set_only=False, rename=None)

Return a dict of the fields in self

Parameters:

Name Type Description Default
set_only bool

If True, return only the fields which have been set

False
rename Optional[RenameStyle]

Rename fields to match the given style

None
Source code in pane/classes.py
def dict(self, *, set_only: bool = False, rename: t.Optional[RenameStyle] = None) -> t.Dict[str, t.Any]:
    """
    Return a dict of the fields in `self`

    Parameters:
      set_only: If `True`, return only the fields which have been set
      rename: Rename fields to match the given style
    """
    if set_only:
        return {
            rename_field(k, rename): getattr(self, k) for k in getattr(self, PANE_SET_FIELDS)
        }
    return {
        rename_field(field.name, rename): getattr(self, field.name) for field in self.__pane_info__.fields
        if not field.exclude
    }

from_json(f, *, custom=None) classmethod

Load cls from a JSON file f

Parameters:

Name Type Description Default
f FileOrPath

File-like or path-like to load from

required
custom Optional[IntoConverterHandlers]

Custom converters to use

None
Source code in pane/classes.py
@classmethod
def from_json(cls, f: io.FileOrPath, *,
              custom: t.Optional[IntoConverterHandlers] = None) -> Self:
    """
    Load `cls` from a JSON file `f`

    Parameters:
      f: File-like or path-like to load from
      custom: Custom converters to use
    """
    return io.from_json(f, cls, custom=custom)

from_yaml(f, *, custom=None) classmethod

Load cls from a YAML file f

Parameters:

Name Type Description Default
f FileOrPath

File-like or path-like to load from

required
custom Optional[IntoConverterHandlers]

Custom converters to use

None
Source code in pane/classes.py
@classmethod
def from_yaml(cls, f: io.FileOrPath, *,
              custom: t.Optional[IntoConverterHandlers] = None) -> Self:
    """
    Load `cls` from a YAML file `f`

    Parameters:
      f: File-like or path-like to load from
      custom: Custom converters to use
    """
    return io.from_yaml(f, cls, custom=custom)

from_yaml_all(f, *, custom=None) classmethod

Load a list of cls from a YAML file f

Parameters:

Name Type Description Default
f FileOrPath

File-like or path-like to load from

required
custom Optional[IntoConverterHandlers]

Custom converters to use

None
Source code in pane/classes.py
@classmethod
def from_yaml_all(cls, f: io.FileOrPath, *,
              custom: t.Optional[IntoConverterHandlers] = None) -> t.List[Self]:
    """
    Load a list of `cls` from a YAML file `f`

    Parameters:
      f: File-like or path-like to load from
      custom: Custom converters to use
    """
    return io.from_yaml_all(f, cls, custom=custom)

from_yamls(s, *, custom=None) classmethod

Load cls from a YAML string s

Parameters:

Name Type Description Default
s str

YAML string to load from

required
custom Optional[IntoConverterHandlers]

Custom converters to use

None
Source code in pane/classes.py
@classmethod
def from_yamls(cls, s: str, *,
               custom: t.Optional[IntoConverterHandlers] = None) -> Self:
    """
    Load `cls` from a YAML string `s`

    Parameters:
      s: YAML string to load from
      custom: Custom converters to use
    """
    from io import StringIO
    return io.from_yaml(StringIO(s), cls, custom=custom)

from_jsons(s, *, custom=None) classmethod

Load cls from a JSON string s

Parameters:

Name Type Description Default
s str

JSON string to load from

required
custom Optional[IntoConverterHandlers]

Custom converters to use

None
Source code in pane/classes.py
@classmethod
def from_jsons(cls, s: str, *,
               custom: t.Optional[IntoConverterHandlers] = None) -> Self:
    """
    Load `cls` from a JSON string `s`

    Parameters:
      s: JSON string to load from
      custom: Custom converters to use
    """
    from io import StringIO
    return io.from_json(StringIO(s), cls, custom=custom)

write_json(f=None, *, indent=None, sort_keys=False, custom=None)

write_json(f: None = None, *, indent: t.Union[str, int, None] = None, sort_keys: bool = False, custom: t.Optional[IntoConverterHandlers] = None) -> str
write_json(f: io.FileOrPath, *, indent: t.Union[str, int, None] = None, sort_keys: bool = False, custom: t.Optional[IntoConverterHandlers] = None) -> None

Write data to a JSON file or string. If given a file f, write to that. Otherwise, write to a string and return.

Parameters:

Name Type Description Default
indent Union[str, int, None]

Indent to format JSON with. Defaults to None (no indentation)

None
sort_keys bool

Whether to sort keys prior to serialization.

False
custom Optional[IntoConverterHandlers]

Custom converters to use

None
Source code in pane/classes.py
def write_json(self, f: t.Optional[io.FileOrPath] = None, *,
              indent: t.Union[str, int, None] = None,
              sort_keys: bool = False,
              custom: t.Optional[IntoConverterHandlers] = None) -> t.Optional[str]:
    """
    Write data to a JSON file or string. If given a file `f`, write to that.
    Otherwise, write to a string and return.

    Parameters:
      indent: Indent to format JSON with. Defaults to None (no indentation)
      sort_keys: Whether to sort keys prior to serialization.
      custom: Custom converters to use
    """
    from io import StringIO

    buf = StringIO() if f is None else f
    io.write_json(
        self, buf, ty=self.__class__,
        indent=indent, sort_keys=sort_keys, custom=custom
    )
    return buf.getvalue() if f is None else None  # type: ignore

write_yaml(f=None, *, indent=None, width=None, allow_unicode=True, explicit_start=True, explicit_end=False, default_style=None, default_flow_style=None, sort_keys=False, custom=None)

write_yaml(f: io.FileOrPath, *, indent: t.Optional[int] = None, width: t.Optional[int] = None, allow_unicode: bool = True, explicit_start: bool = True, explicit_end: bool = False, default_style: t.Optional[t.Literal['"', '|', '>']] = None, default_flow_style: t.Optional[bool] = None, sort_keys: bool = False, custom: t.Optional[IntoConverterHandlers] = None) -> None
write_yaml(f: None = None, *, indent: t.Optional[int] = None, width: t.Optional[int] = None, allow_unicode: bool = True, explicit_start: bool = True, explicit_end: bool = False, default_style: t.Optional[t.Literal['"', '|', '>']] = None, default_flow_style: t.Optional[bool] = None, sort_keys: bool = False, custom: t.Optional[IntoConverterHandlers] = None) -> str

Write data to a YAML file or string. If given a file f, write to that. Otherwise, write to a string and return.

Parameters:

Name Type Description Default
indent Optional[int]

Number of spaces to indent blocks with

None
width Optional[int]

Maximum width of file created

None
allow_unicode bool

Whether to output unicode characters or escape them

True
explicit_start bool

Whether to include a YAML document start "---"

True
explicit_end bool

Whether to include a YAML document end "..."

False
default_style Optional[Literal['"', '|', '>']]

Default style to use for scalar nodes. See YAML documentation for more information.

None
default_flow_style Optional[bool]

Whether to default to flow style or block style for collections. See YAML documentation for more information.

None
sort_keys bool

Whether to sort keys prior to serialization.

False
custom Optional[IntoConverterHandlers]

Custom converters to use

None
Source code in pane/classes.py
def write_yaml(self, f: t.Optional[io.FileOrPath] = None, *,
              indent: t.Optional[int] = None, width: t.Optional[int] = None,
              allow_unicode: bool = True,
              explicit_start: bool = True, explicit_end: bool = False,
              default_style: t.Optional[t.Literal['"', '|', '>']] = None,
              default_flow_style: t.Optional[bool] = None,
              sort_keys: bool = False,
              custom: t.Optional[IntoConverterHandlers] = None) -> t.Optional[str]:
    """
    Write data to a YAML file or string. If given a file `f`, write to that.
    Otherwise, write to a string and return.

    Parameters:
      indent: Number of spaces to indent blocks with
      width: Maximum width of file created
      allow_unicode: Whether to output unicode characters or escape them
      explicit_start: Whether to include a YAML document start "---"
      explicit_end: Whether to include a YAML document end "..."
      default_style: Default style to use for scalar nodes.
          See YAML documentation for more information.
      default_flow_style: Whether to default to flow style or block style for collections.
          See YAML documentation for more information.
      sort_keys: Whether to sort keys prior to serialization.
      custom: Custom converters to use
    """
    from io import StringIO

    buf = StringIO() if f is None else f
    io.write_yaml(
        self, buf, ty=self.__class__,
        indent=indent, width=width, allow_unicode=allow_unicode,
        explicit_start=explicit_start, explicit_end=explicit_end,
        default_style=default_style, default_flow_style=default_flow_style,
        sort_keys=sort_keys, custom=custom
    )
    return buf.getvalue() if f is None else None  # type: ignore

ValueOrList

Bases: Generic[T]

Source code in pane/types.py
class ValueOrList(t.Generic[T]):
    _inner: t.Union[T, t.List[T]]
    _is_val: bool

    def __init__(self, val: t.Union[T, t.List[T]], _is_val: bool):
        self._inner = val
        self._is_val = _is_val

    @classmethod
    def from_val(cls, val: T) -> ValueOrList[T]:
        return cls(val, True)

    @classmethod
    def from_list(cls, list_val: t.List[T]) -> ValueOrList[T]:
        return cls(list_val, False)

    def __repr__(self) -> str:
        return f"ValueOrList({self._inner!r})"

    def __str__(self) -> str:
        return str(self._inner)

    def __eq__(self, other: t.Any) -> bool:
        if not self.__class__ == other.__class__:
            return False
        return self._is_val == other._is_val and self._inner == other._inner

    @classmethod
    def _converter(cls: t.Type[T], *args: t.Type[Convertible],
                   handlers: ConverterHandlers) -> ValueOrListConverter:
        arg = t.cast(t.Type[Convertible], args[0] if len(args) > 0 else t.Any)
        return ValueOrListConverter(arg, handlers=handlers)

    def __len__(self) -> int:
        return 1 if self._is_val else len(t.cast(t.List[T], self._inner))

    def map(self, f: t.Callable[[T], U]) -> ValueOrList[U]:
        if self._is_val:
            return ValueOrList(f(t.cast(T, self._inner)), True)
        return ValueOrList(list(map(f, t.cast(t.List[T], self._inner))), False)

    def __iter__(self) -> t.Iterator[T]:
        if self._is_val:
            yield t.cast(T, self._inner)
        else:
            yield from t.cast(t.List[T], self._inner)

from_val(val) classmethod

Source code in pane/types.py
@classmethod
def from_val(cls, val: T) -> ValueOrList[T]:
    return cls(val, True)

from_list(list_val) classmethod

Source code in pane/types.py
@classmethod
def from_list(cls, list_val: t.List[T]) -> ValueOrList[T]:
    return cls(list_val, False)

_converter(*args, handlers) classmethod

Source code in pane/types.py
@classmethod
def _converter(cls: t.Type[T], *args: t.Type[Convertible],
               handlers: ConverterHandlers) -> ValueOrListConverter:
    arg = t.cast(t.Type[Convertible], args[0] if len(args) > 0 else t.Any)
    return ValueOrListConverter(arg, handlers=handlers)

map(f)

Source code in pane/types.py
def map(self, f: t.Callable[[T], U]) -> ValueOrList[U]:
    if self._is_val:
        return ValueOrList(f(t.cast(T, self._inner)), True)
    return ValueOrList(list(map(f, t.cast(t.List[T], self._inner))), False)

ValueOrListConverter

Bases: UnionConverter

Source code in pane/types.py
class ValueOrListConverter(UnionConverter):
    def __init__(self, ty: t.Type[Convertible], handlers: ConverterHandlers):
        types = t.cast(t.Sequence[t.Type[Convertible]], (ty, t.List[ty]))
        super().__init__(types, constructor=lambda v, i: ValueOrList(v, i == 0), handlers=handlers)
        self.ty = ty

    def expected(self, plural: bool = False) -> str:
        inner = self.converters[0].expected(plural)
        return f"{inner} or sequence of {inner}"

    def into_data(self, val: t.Any) -> DataType:
        if not isinstance(val, ValueOrList):
            return into_data(val)
        return t.cast(ValueOrList[t.Any], val).map(
            lambda v: into_data(v, self.ty)
        )._inner

types = tuple(flatten_union_args(types)) instance-attribute

List of potential types

converters = tuple(make_converter(ty, handlers) for ty in types) instance-attribute

List of type converters

constructor = constructor instance-attribute

Constructor to call with parsed value. Called with (val, index of type in union)

ty = ty instance-attribute

convert(val)

Convert val to T_co. Raises a ConvertError on failure.

Source code in pane/converters.py
def convert(self, val: t.Any) -> T_co:
    """Convert ``val`` to ``T_co``. Raises a ``ConvertError`` on failure."""
    try:
        return self.try_convert(val)
    except ParseInterrupt:
        pass
    node = self.collect_errors(val)
    if node is None:
        raise RuntimeError("convert() raised but ``collect_errors`` returned ``None``."
                           " This is a bug of the ``Converter`` implementation.")
    raise ConvertError(node)

try_convert(val)

See Converter.try_convert

Source code in pane/converters.py
def try_convert(self, val: t.Any) -> t.Any:
    """See [`Converter.try_convert`][pane.converters.Converter.try_convert]"""
    for (i, conv) in enumerate(self.converters):
        try:
            val = conv.try_convert(val)
            try:
                return self.construct(val, i)
            except Exception:
                pass
        except ParseInterrupt:
            pass
    raise ParseInterrupt

collect_errors(val)

See Converter.collect_errors

Source code in pane/converters.py
def collect_errors(self, val: t.Any) -> t.Optional[ErrorNode]:
    """See [`Converter.collect_errors`][pane.converters.Converter.collect_errors]"""
    failed_children: t.List[ErrorNode] = []
    for (i, conv) in enumerate(self.converters):
        # if one branch is successful, the whole type is successful
        try:
            conv_val = conv.try_convert(val)
        except ParseInterrupt:
            failed_children.append(t.cast(t.Union[ProductErrorNode, WrongTypeError], conv.collect_errors(val)))
            continue
        try:
            self.construct(conv_val, i)
            return None
        except Exception as e:
            tb = e.__traceback__.tb_next  # type: ignore
            tb = traceback.TracebackException(type(e), e, tb)
            failed_children.append(WrongTypeError(self.expected(), val, tb))
    return SumErrorNode(failed_children)

construct(val, i)

Source code in pane/converters.py
def construct(self, val: t.Any, i: int) -> t.Any:
    if self.constructor is None:
        return val
    return self.constructor(val, i)

expected(plural=False)

Source code in pane/types.py
def expected(self, plural: bool = False) -> str:
    inner = self.converters[0].expected(plural)
    return f"{inner} or sequence of {inner}"

into_data(val)

Source code in pane/types.py
def into_data(self, val: t.Any) -> DataType:
    if not isinstance(val, ValueOrList):
        return into_data(val)
    return t.cast(ValueOrList[t.Any], val).map(
        lambda v: into_data(v, self.ty)
    )._inner

YAMLDocList

Bases: list

list subclass representing a list of objects from YAML documents.

Source code in pane/types.py
class YAMLDocList(list):  # type: ignore
    """
    `list` subclass representing a list of objects from YAML documents.
    """
    ...