-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Preliminary implementation of stdlib/3.7/dataclasses.pyi. #1944
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
Conversation
stdlib/3.7/dataclasses.py
Outdated
@@ -0,0 +1,47 @@ | |||
from typing import * |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding these stubs!
A couple of style comments:
- don't use
import *
- names that don't actually exist at runtime (like
T
andDictType
) should be private, so their names should start with an underscore. - indent with four spaces
- add spaces after
:
in annotations and around=
for annotated functions with arguments.
@JelleZijlstra thanks for the feedback; working on an update now. Does the private rule include public names imported from typing? |
Names imported with |
I made the requested style changes, and one important fix: |
There are still a few Travis failures that should be fixable. |
2648516
to
c031abd
Compare
@JelleZijlstra latest push passed all tests - please give it another look! |
stdlib/3.7/dataclasses.pyi
Outdated
|
||
@overload | ||
def dataclass(*, init: bool = ..., repr: bool = ..., eq: bool = ..., | ||
order: bool = ..., hash: Optional[bool] = ..., frozen: bool = ...) -> Callable[[Type[_T]], Type[_T]]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsafe_hash
, see https://www.python.org/dev/peps/pep-0557/#id7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. The hash
tristate parameter changed to unsafe_hash
boolean in cpython commit dbf9cff48a4ad0fd58e1c623ce1f36c3dd3d5f38.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed the code against the implementation of dataclasses.py (not the documentation since it doesn't exist yet: https://bugs.python.org/issue32216) and noticed a couple of places where it can be improved.
Also cc @ericvsmith who implemented dataclasses and @ilevkivskyi who is interested in writing dataclass support in mypy.
stdlib/3.7/dataclasses.pyi
Outdated
_DictType = TypeVar('_DictType', bound=dict) | ||
_TupleType = TypeVar('_TupleType', bound=tuple) | ||
|
||
class _MISSING_TYPE: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're missing MISSING
.
stdlib/3.7/dataclasses.pyi
Outdated
|
||
def field(*, default: Union[_T, _MISSING_TYPE] = ..., default_factory: Union[Callable[[], _T], _MISSING_TYPE] = ..., | ||
init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., | ||
metadata: Optional[Mapping[str, Any]] = ...) -> Field: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments in the code say
# This function is used instead of exposing Field creation directly,
# so that a type checker can be told (via overloads) that this is a
# function whose type depends on its parameters.
I do think we should use overloads. An overload with default
given should return a Field[_T]
and allow default_factory
as an argument, and one with default
not given or _MISSING_TYPE should not allow default_factory
and return Field[Any]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An overload with default given should return a Field[_T] and allow default_factory as an argument
I think you mean "disallow default_factory
as an argument", correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops yes, they are exclusive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I read it, there are three cases: neither provided (which returns Field[Any]
), just default
provided, and just default_factory
provided. The last two both return Field[_T]
. Is that correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds right.
stdlib/3.7/dataclasses.pyi
Outdated
init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., | ||
metadata: Optional[Mapping[str, Any]] = ...) -> Field: ... | ||
|
||
def fields(class_or_instance: Type) -> Tuple[Field, ...]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also accepts dataclass instances. We should probably just type it as Any; the mypy plugin can do something smarter internally if we want to.
Also, we should be explicit here and say Field[Any]
.
stdlib/3.7/dataclasses.pyi
Outdated
|
||
class _InitVarMeta(type): ... | ||
|
||
def asdict(obj: Any, *, dict_factory: _DictType = ...) -> _DictType: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not quite correct; the argument type should be something like Type[_DictType]
.
Also, the range of acceptable type is a little wider than what's given here: in practice, dict_factory can be any callable accepting a list of (key, value) tuples. We could perhaps better type this as: def asdict(obj: Any, *, dict_factory: Callable[[List[Tuple[str, Any]], _T]) -> _T: ...
, where _T = TypeVar('_T')
.
All this also goes for astuple
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This took me a couple of readings but I now understand and agree that using Callable
is the most accurate approach.
stdlib/3.7/dataclasses.pyi
Outdated
class InitVar(metaclass=_InitVarMeta): ... | ||
|
||
def make_dataclass(cls_name: str, fields: Iterable[Union[str, Tuple[str, type], Tuple[str, type, Field]]], *, | ||
bases: Tuple[type, ...] = ..., namespace: Dict[str, Any] = ..., |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace is Optional
Is there a good way to pull this definition into a python 3.6 project? Will the file need to be duplicated this repo into the stdlib and thirdparty sections? |
We can use symbolic links.
|
Having now played around with this definition a bit, am I right in thinking that The problem I'm currently running into is that although the dataclass' properties are correctly specified, there is nothing in the decorator's type signature to indicate that an |
thanks to everyone for the reviews; I'll continue working whenever I find a free moment, hopefully next week. @glenjamin what do you mean about 3.6? Here is my attempt to backport dataclasses for use in both 3.6 and 3.7: https://github.com/gwk/pithy/tree/master/pithy/dataclasses. Regarding |
I was only asking about the typeshed mechanims required to use the same |
It's actually not that obvious: see our woes in #1175. |
@gwk Do you have time to finish this within next week (or preferably next few days)? If you don't, then I will need to continue your work myself (Python 3.7 release is coming very soon). |
@ilevkivskyi if you like, I can take over the implementation here if you do the review. |
@JelleZijlstra @ilevkivskyi I'll try to update this now to address your comments. |
Great! Just please don't rebase so that it will be easier to review, and don't forget to push your local changes so that we see the new commits :-) |
…llables; add MISSING; field typed with overloads; namespace is optional.
@JelleZijlstra I have pushed the changes. @ilevkivskyi apologies in advance, I had previously rebased this branch against master and was not in a position to undo that operation. I hope it doesn't cause too much trouble for review. I did make the changes as a second commit so you will at least be able to see them separately! |
Thanks! Could you fix the CI errors? |
stdlib/3.7/dataclasses.pyi
Outdated
|
||
|
||
@overload # `default` and `default_factory` are optional and mutually exclusive. | ||
def field(*, default: _T = ..., default_factory: _MISSING_TYPE = ..., |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All three overloads are actually the same unless I'm missing something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate? The return type of the last one is Field[Any]
, while the first two are constrained.
stdlib/3.7/dataclasses.pyi
Outdated
|
||
class _InitVarMeta(type): ... | ||
|
||
def asdict(obj: Any, *, dict_factory: _DictFactory = ...) -> _T: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to be _DictFactory[_T]
. Maybe just skip the type alias since it's only used once and write Callable...
here directly.
stdlib/3.7/dataclasses.pyi
Outdated
|
||
_T = TypeVar('_T') | ||
|
||
_DictFactory = Callable[List[Tuple[str, Any]], _T] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not quite right; you need one more set of [] around the first argument to Callable, because that's how you indicate the argument list.
Is there any avenue in the future to make @dataclass
class C:
field: int
c = C(field='field')
dataclasses.replace(c, doesnotexist=10) # Should not type check.
dataclasses.replace(c, field='notint') # Should not type check. Asking as a heavy user of this function (or rather |
This is work for mypy plugin, the stub is only part of the (unfinished) picture. |
@JelleZijlstra @ilevkivskyi I can't figure out how to fix the error regarding
What is strange is that when I remove the additional optional parameters as shown below, it passes:
This suggests to me that mypy has a bug, possibly around non-optional parameters following a |
That does look like a bug. Some of the work @Michael0x2a is currently doing on mypy may end up fixing this. Sometimes mypy reports errors on the overload definition, but then behaves correctly when you use it. Can you try adding |
@JelleZijlstra Yes, Michael's PR fixed it. I restarted the build after mypy merge and it is all green now. Is anything left to do with this PR? |
stdlib/3.7/dataclasses.pyi
Outdated
|
||
class FrozenInstanceError(AttributeError): ... | ||
|
||
class InitVar(metaclass=_InitVarMeta): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I think this one should be Generic[_T]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the usage of InitVar; I simply translated this from the implementation. Nor do I know quite what you are asking me to change the definition to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilevkivskyi if you tell me what you want me to change it to I'll do so and we can close this out. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would write
class InitVar(Generic[_T]): ...
and remove _InitVarMeta
completely because it is private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! @JelleZijlstra do you have other comments?
This is a first effort at adding typing for the 3.7 dataclasses module.