Replies: 2 comments 7 replies
-
A few thoughts:
|
Beta Was this translation helpful? Give feedback.
-
... so this might work, although not quite "no changes to third-party libraries". Also, if we go down this route, I think it'll be prudent to document it in somewhere, either in cpython's typing.rst or in python/typing's docs, or in each type checker's docs, but certainly not solely in this discussion 🙏 . IMO the first is preferable, since it's the main and only documentation on the Also, given that libs would have to make some updates anyway, this would be clearly the more complex syntax (vs. bool param), and I wonder to what advantage (fewer params? some future flexibility? some merit in avoiding special-casing dataclass more than it already is?). Footnotes |
Beta Was this translation helpful? Give feedback.
-
Exposé
In PEP-681, Data Class Transforms were conceived as "a way for third-party libraries to indicate that certain decorator functions, classes, and metaclasses provide behaviors similar to dataclasses". Let's call this 📖 the intended use. The PEP was successful in that, e.g. in pydantic, but over time another use emerged — let's call it 🎁 the bonus use - where
@dataclass_transform
is used for decorators which don't merely return something with similar to, but actual dataclasses. The motivation for the latter is to make custom decorators seen as equivalent to@dataclasses.dataclass
by type-checkers and IDEs:Real-world examples1:
So,
@dataclass_transform
is useful in more than one way, and that's great. Meanwhile, type checkers are also improving their understanding ofdataclasses
module's utility functions:dataclasses.is_dataclass
dataclasses.fields
obj
argtuple[Field[int], Field[str], ...]
)dataclasses.as_dict
obj
argTypedDict
?)dataclasses.as_tuple
obj
argtuple(42, 'spam')
)dataclasses.replace
obj
argchanges
argThe problem we're solving
For the purposes of the improved checks above, we should not treat any Data Class Transform'd class as a "true" dataclass, e.g. pydantic models do not function as dataclasses (i.e.
is_dataclass(some_pydantic_model) is False
). Straight reading of PEP-681 also does not suggest that. Meanwhile, some type checkers (mypy, pyright) are currently doing just that, and that's where we run into a conundrum:TypeGuard
fordataclasses.is_dataclass()
; refineasdict()
,astuple()
,fields()
,replace()
typeshed#9362, then we (a) lose type safety forobj
, and (b) we keep inconsistent behavior in type-checkers whenever we have special treatment of dataclasses utility functions.Proposal
PEP-681 foresaw the need to add more parameters:
I'm suggesting adding a
returns_dataclass
parameter todataclass_transform
:def dataclass_transform( *, eq_default: bool = True, order_default: bool = False, kw_only_default: bool = False, + returns_dataclass: bool = <...>, field_specifiers: tuple[type | Callable[..., Any], ...] = (), **kwargs: Any, ) -> Callable[[_T], _T]: ...
I'm not sure whether it should default to
True
orFalse
. My unscientific exploration of uses in the wild suggests more 🎁 bonus uses than 📖 intended uses, which would tip the scale in favor ofTrue
. Meanwhile, adherence to PEP-681 contract would tip the scale in favor ofFalse
. But most importantly we'll gain a way to express this property of the returned class.Footnotes
can probably find more if it makes the case stronger ↩
Beta Was this translation helpful? Give feedback.
All reactions