Skip to content

Commit

Permalink
Support integer Struct tags
Browse files Browse the repository at this point in the history
Previously tagged unions only supported `str` tag values. We now also
support `int` tag values, with a few caveats:

- A single `Union` of `Struct` types cannot mix both `int` and `str` tag
types.
- Integer `tag` values currently only support values that fit in an
int64 (-2**63 <= tag <= 2**63 - 1). This restriction has also been added
to `IntEnum` and integer `Literal` types (previously these supported up
to a `uint64`). This restriction makes the implementation easier, but
can be removed if needed in the future. For now a `NotImplementedError`
is raised if the user attempts to use out-of-range integer values,
pointing them to raise an issue on GitHub if they need the feature.
  • Loading branch information
jcrist committed Jul 1, 2022
1 parent b5d0401 commit 0e83653
Show file tree
Hide file tree
Showing 9 changed files with 1,023 additions and 496 deletions.
13 changes: 7 additions & 6 deletions docs/source/structs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -334,11 +334,12 @@ A struct's tagging configuration is determined as follows.
a union.

- If a struct is tagged, ``tag`` defaults to the class name (e.g. ``"Get"``) if
not provided or inherited. This can be overridden by passing a tag value
explicitly (e.g. ``tag="get"``). or a callable from class name to ``str``
(e.g. ``tag=lambda name: name.lower()`` to lowercase the class name
automatically). Note that the tag value must be unique for all struct types
in a union.
not provided or inherited. This can be overridden by passing a string (or
less commonly an integer) value explicitly (e.g. ``tag="get"``). ``tag`` can
also be passed a callable that takes the class name and returns a valid tag
value (e.g. ``tag=str.lower``). Note that tag values must be unique for all
struct types in a union, and ``str`` and ``int`` tag types cannot both be
used within the same union.

If you like subclassing, both ``tag_field`` and ``tag`` are inheritable by
subclasses, allowing configuration to be set once on a base class and reused
Expand All @@ -353,7 +354,7 @@ for all struct types you wish to tag.
>>> # Create a base class for tagged structs, where:
... # - the tag field is "op"
... # - the tag is the class name lowercased
... class TaggedBase(msgspec.Struct, tag_field="op", tag=lambda name: name.lower()):
... class TaggedBase(msgspec.Struct, tag_field="op", tag=str.lower):
... pass
>>> # Use the base class to pass on the configuration
Expand Down
4 changes: 2 additions & 2 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ or doesn't match any valid `enum.IntEnum` member.
>>> msgspec.json.decode(b'4', type=JobState)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
msgspec.DecodeError: Invalid enum value `4`
msgspec.DecodeError: Invalid enum value 4
``Enum``
~~~~~~~~
Expand Down Expand Up @@ -565,7 +565,7 @@ values, or doesn't match any of their component types.
>>> msgspec.json.decode(b'4', type=Literal[1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
msgspec.DecodeError: Invalid enum value `4`
msgspec.DecodeError: Invalid enum value 4
>>> msgspec.json.decode(b'"bad"', type=Literal[1, 2, 3])
Traceback (most recent call last):
Expand Down
4 changes: 2 additions & 2 deletions msgspec/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Struct(metaclass=__StructMeta):
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def __init_subclass__(
cls,
tag: Union[None, bool, str, Callable[[str], str]] = None,
tag: Union[None, bool, str, int, Callable[[str], Union[str, int]]] = None,
tag_field: Union[None, str] = None,
rename: Union[
None, Literal["lower", "upper", "camel", "pascal"], Callable[[str], str]
Expand All @@ -59,7 +59,7 @@ def defstruct(
bases: Tuple[Type[Struct], ...] = (),
module: Optional[str] = None,
namespace: Optional[Dict[str, Any]] = None,
tag: Union[None, bool, str, Callable[[str], str]] = None,
tag: Union[None, bool, str, int, Callable[[str], Union[str, int]]] = None,
tag_field: Union[None, str] = None,
rename: Union[
None, Literal["lower", "upper", "camel", "pascal"], Callable[[str], str]
Expand Down
Loading

0 comments on commit 0e83653

Please sign in to comment.