-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
PEP 712: Adding a "converter" parameter to dataclasses.field #3095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
835d0a1
Add dataclass converter PEP
thejcannon d84dd26
Add Eric as CODEOWNER
thejcannon 1431bbb
fix lint errors
thejcannon 4797b36
Fixup Reference Implementation
thejcannon eff261c
Rename to PEP 712
thejcannon 5f6ff86
add missing underscore
thejcannon de0dfcc
Merge branch 'main' into converter
thejcannon a73569b
review feedback
thejcannon 18ae4d3
Add Python version
thejcannon 1ae265c
fix line length
thejcannon 4af75d0
Merge branch 'converter' of https://github.com/thejcannon/peps into c…
thejcannon a1615b9
Fix title underline
thejcannon 60b26c4
Add another cross-link
thejcannon 7a7c47d
Fix broken cross-links
thejcannon 8ea8463
Add missing sections
thejcannon c6ec599
feedback
thejcannon 9a8a8f5
Fix indentation
thejcannon df21ecc
Try with newlines
thejcannon 74b8dce
review comments
thejcannon a0c1d77
Jelle's comments
thejcannon 3677a9b
Apply suggestions from code review
thejcannon 373dd07
Encorproate feedback from my personal celebrities
thejcannon e92ff55
fix lint
thejcannon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,13 +7,14 @@ Type: Standards Track | |
| Content-Type: text/x-rst | ||
| Created: 01-Jan-2023 | ||
| Python-Version: 3.13 | ||
| Post-History: `27-Dec-2022 <https://mail.python.org/archives/list/[email protected]/thread/NWZQIINJQZDOCZGO6TGCUP2PNW4PEKNY/>`__, `19-Jan-2023 <https://discuss.python.org/t/add-converter-to-dataclass-field/22956>`__ | ||
| Post-History: `27-Dec-2022 <https://mail.python.org/archives/list/[email protected]/thread/NWZQIINJQZDOCZGO6TGCUP2PNW4PEKNY/>`__, | ||
| `19-Jan-2023 <https://discuss.python.org/t/add-converter-to-dataclass-field/22956>`__, | ||
|
|
||
| Abstract | ||
| ======== | ||
|
|
||
| :pep:`557` added :mod:`dataclasses` to the Python stdlib. :pep:`681` added | ||
| :func:`~py3.11:~typing.dataclass_transform` to help type checkers understand | ||
| :func:`~py3.11:typing.dataclass_transform` to help type checkers understand | ||
| several common dataclass-like libraries, such as attrs, Pydantic, and object | ||
| relational mapper (ORM) packages such as SQLAlchemy and Django. | ||
|
|
||
|
|
@@ -24,7 +25,7 @@ user-provided conversion function. | |
|
|
||
| Therefore, this PEP adds a ``converter`` parameter to :func:`dataclasses.field` | ||
| (along with the requisite changes to :class:`dataclasses.Field` and | ||
| :func:`~py3.11:~typing.dataclass_transform` to specify the function to use to | ||
| :func:`~py3.11:typing.dataclass_transform`) to specify the function to use to | ||
| convert the input value for each field to the representation to be stored in | ||
| the dataclass. | ||
|
|
||
|
|
@@ -57,7 +58,7 @@ dataclass-like libraries provide support. Adding this feature to the standard | |
| library means more users are able to opt-in to these benefits without requiring | ||
| third-party libraries. Additionally third-party libraries are able to clue | ||
| type-checkers into their own conversion semantics through added support in | ||
| :func:`~py3.11:~typing.dataclass_transform`, meaning users of those libraries | ||
| :func:`~py3.11:typing.dataclass_transform`, meaning users of those libraries | ||
| benefit as well. | ||
|
|
||
| Specification | ||
|
|
@@ -78,7 +79,7 @@ Adding this parameter also implies the following changes: | |
|
|
||
| * A ``converter`` attribute will be added to :class:`dataclasses.Field`. | ||
| * ``converter`` will be added to the field specifier parameters of arguments | ||
| provided to :func:`~py3.11:~typing.dataclass_transform`'s ``field`` parameter. | ||
| provided to :func:`~py3.11:typing.dataclass_transform`'s ``field`` parameter. | ||
|
|
||
| Example | ||
| ''''''' | ||
|
|
@@ -95,7 +96,7 @@ Example | |
| converter=lambda names: tuple(map(str.lower, names)) | ||
| ) | ||
|
|
||
| # The default value is also converted, therefore the following is not a | ||
| # The default value is also converted; therefore the following is not a | ||
| # type error. | ||
| stock_image_path: pathlib.PurePosixPath = dataclasses.field( | ||
| converter=pathlib.PurePosixPath, default="assets/unknown.png" | ||
|
|
@@ -143,7 +144,7 @@ Type-checking the default value | |
| Because the ``default`` value is unconditionally converted using ``converter``, | ||
| if arguments for both ``converter`` and ``default`` are provided to | ||
| :func:`dataclasses.field`, the ``default`` argument's type should be checked | ||
| using the ``converter``'s unary argument's type. | ||
| using the type of the unary argument to the ``converter`` callable. | ||
|
|
||
| Type-checking the return type | ||
| ''''''''''''''''''''''''''''' | ||
|
|
@@ -225,11 +226,23 @@ Example | |
| def converter(x: int, y: str) -> list: ... | ||
| def converter(x=..., y = ...): ... | ||
|
|
||
| # Type checkers should not error on the following, since the default value | ||
| # is type-checked against the converter's unary argument type if a converter | ||
| # is provided. | ||
| @dataclasses.dataclass | ||
| class Example: | ||
| # Although the default value is of type `str` and the field is declared to | ||
| # be of type `pathlib.Path`, this is not a type error because the default | ||
| # value will be converted. | ||
| tmpdir: pathlib.Path = dataclasses.field(default="/tmp", converter=pathlib.Path) | ||
thejcannon marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
|
|
||
| Backward Compatibility | ||
| ====================== | ||
|
|
||
| These changes don't introduce any compatibility problems since they | ||
| only introduce new features. | ||
| only introduce opt-in new features. | ||
|
|
||
| Security Implications | ||
| ====================== | ||
|
|
@@ -243,6 +256,10 @@ Documentation and examples explaining the new parameter and behavior will be | |
| added to the relevant sections of the docs site (primarily on | ||
| :mod:`dataclasses`) and linked from the *What's New* document. | ||
|
|
||
| The added documentation/examples will also cover the "common pitfalls" that | ||
| users of converters are likely to encounter. Such pitfalls include: | ||
| * Needing to handle ``None``/sentinel values. | ||
| * Needing to handle values that are already of the correct type. | ||
thejcannon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Reference Implementation | ||
| ======================== | ||
|
|
@@ -255,8 +272,8 @@ CPython support is implemented on `a branch in the author's fork <cpython-branch | |
| Rejected Ideas | ||
| ============== | ||
|
|
||
| Just adding "converter" to :func:`~py3.11:~typing.dataclass_transform`'s ``field_specifiers`` | ||
| --------------------------------------------------------------------------------------------- | ||
| Just adding "converter" to ``typing.dataclass_transform``'s ``field_specifiers`` | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| The idea of isolating this addition to | ||
| :func:`~py3.11:~typing.dataclass_transform` was briefly | ||
thejcannon marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
@@ -266,6 +283,21 @@ to broaden this to :mod:`dataclasses` more generally. | |
| Additionally, adding this to :mod:`dataclasses` ensures anyone can reap the | ||
| benefits without requiring additional libraries. | ||
|
|
||
| Not converting default values | ||
| ----------------------------- | ||
|
|
||
| There are pros and cons with both converting and not converting default values. | ||
| Leaving default values as-is allows type-checkers and dataclass authors to | ||
| expect that the type of the default matches the type of the field. However, | ||
| converting default values has two large advantages: | ||
|
|
||
| 1. Compatibility with ``attrs``. Attrs unconditionally uses the converter to | ||
thejcannon marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| convert the default value. | ||
|
|
||
| 2. Simpler defaults. Allowing the default value to have the same type as | ||
| user-provided values means dataclass authors get the same conveniences as | ||
| their callers. | ||
|
|
||
| Automatic conversion using the field's type | ||
| ------------------------------------------- | ||
|
|
||
|
|
@@ -275,7 +307,7 @@ One idea could be to allow the type of the field specified (e.g. ``str`` or | |
| appear to be similar to this approach. | ||
|
|
||
| This works well for fairly simple types, but leads to ambiguity in expected | ||
| behavior for complex types such as generics. E.g. For ``tuple[int]`` it is | ||
| behavior for complex types such as generics. E.g. For ``tuple[int, ...]`` it is | ||
| ambiguous if the converter is supposed to simply convert an iterable to a tuple, | ||
| or if it is additionally supposed to convert each element type to ``int``. | ||
|
|
||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.