Skip to content

Support keyword-only parameters in structs - #242

Merged
jcrist merged 6 commits into
mainfrom
kw-only
Dec 17, 2022
Merged

Support keyword-only parameters in structs#242
jcrist merged 6 commits into
mainfrom
kw-only

Conversation

@jcrist

@jcrist jcrist commented Dec 17, 2022

Copy link
Copy Markdown
Member

This adds support for keyword-only parameters in Structs, via a kw_only configuration option. This option has the same semantics and behavior as the kw_only parameter for dataclasses. If true, all parameters specified on the respective class (not base classes) are keyword-only.

In [1]: import msgspec

In [2]: class Point(msgspec.Struct, kw_only=True):
   ...:     x: int
   ...:     y: int
   ...:     z: int = 0
   ...: 

In [3]: Point.__signature__
Out[3]: <Signature (*, x: int, y: int, z: int = 0)>

This commit also changes the field-ordering algorithm used by msgspec to match the behavior of dataclasses. This is a Breaking Change, but helps maintain compatibility with the larger Python ecosystem (it also makes pyright type check fields with the proper order in the presence of subclasses).

The main way this breaking change may affect users is by causing struct definitions with required parameters defined after optional ones to fail at import time.

For example, the following definition is now invalid and will error at import time.

class MyStruct(Struct):
    x: int = 1  # an optional parameter
    y: int  # a required parameter

To be clear, dataclasses & attrs have this same restriction - only msgspec lacked this restriction before, but now has it for compatibility with dev tools like pyright

To resolve this issue, you can either:

  • reorder the fields so x is after y
  • add a default value to y
  • Specify kw_only=True in the struct definition. Keyword-only fields may mix required and optional fields in any order, but impose limitations on the caller.
class MyStruct(Struct, kw_only=True):
    x: int = 1
    y: int

x = MyStruct(x=10, y=20)  # this works

It is unfortunate that a breaking change was needed here, but the compatibility with dataclasses (and pyright) feels worth it. Better break things now than later.

This adds support for keyword-only parameters in Structs, via a
`kw_only` configuration option. This option has the same semantics and
behavior as the `kw_only` parameter for dataclasses. If true, all
parameters specified on the respective class (*not* base classes) are
keyword-only.

This commit also changes the field-ordering algorithm used by msgspec to
match the behavior of dataclasses. This is a *breaking change*, but
helps maintain compatibility with the larger Python ecosystem (it also
makes `pyright` type check fields with the proper order).

The main way this breaking change may affect users is by causing struct
definitions with required parameters defined after optional ones to fail
at import time.

For example, the following definition is now invalid and will error at
import time.

```python
class MyStruct(Struct):
    x: int = 1  # an optional parameter
    y: int  # a required parameter
```

To resolve this issue, you can either:

- reorder the fields so `x` is after `y`
- add a default value to `y`
- Specify `kw_only=True` in the struct definition. Keyword-only fields
  may mix required and optional fields in any order, but impose
  limitations on the caller.

It is unfortunate that a breaking change was needed here, but the
compatibility with `dataclasses` (and `pyright`) feels worth it. Better
break things now than later.
@jcrist
jcrist merged commit c2553e9 into main Dec 17, 2022
@jcrist
jcrist deleted the kw-only branch December 17, 2022 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant