-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
stubgen: generate valid dataclass stubs #15625
Changes from all commits
faae3c0
691b8e9
00db7c1
be3028e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3512,3 +3512,185 @@ def gen2() -> _Generator[_Incomplete, _Incomplete, _Incomplete]: ... | |
|
||
class X(_Incomplete): ... | ||
class Y(_Incomplete): ... | ||
|
||
[case testDataclass] | ||
import dataclasses | ||
import dataclasses as dcs | ||
from dataclasses import dataclass, InitVar, KW_ONLY | ||
from dataclasses import dataclass as dc | ||
from typing import ClassVar | ||
|
||
@dataclasses.dataclass | ||
class X: | ||
a: int | ||
b: str = "hello" | ||
c: ClassVar | ||
d: ClassVar = 200 | ||
f: list[int] = field(init=False, default_factory=list) | ||
g: int = field(default=2, kw_only=True) | ||
_: KW_ONLY | ||
h: int = 1 | ||
i: InitVar[str] | ||
j: InitVar = 100 | ||
non_field = None | ||
|
||
@dcs.dataclass | ||
class Y: ... | ||
|
||
@dataclass | ||
class Z: ... | ||
|
||
@dc | ||
class W: ... | ||
|
||
@dataclass(init=False, repr=False) | ||
class V: ... | ||
|
||
[out] | ||
import dataclasses | ||
import dataclasses as dcs | ||
from dataclasses import InitVar, KW_ONLY, dataclass, dataclass as dc | ||
from typing import ClassVar | ||
|
||
@dataclasses.dataclass | ||
class X: | ||
a: int | ||
b: str = ... | ||
c: ClassVar | ||
d: ClassVar = ... | ||
f: list[int] = ... | ||
g: int = ... | ||
_: KW_ONLY | ||
h: int = ... | ||
i: InitVar[str] | ||
j: InitVar = ... | ||
non_field = ... | ||
|
||
@dcs.dataclass | ||
class Y: ... | ||
@dataclass | ||
class Z: ... | ||
@dc | ||
class W: ... | ||
@dataclass(init=False, repr=False) | ||
class V: ... | ||
|
||
[case testDataclass_semanal] | ||
from dataclasses import dataclass, InitVar | ||
from typing import ClassVar | ||
|
||
@dataclass | ||
class X: | ||
a: int | ||
b: str = "hello" | ||
c: ClassVar | ||
d: ClassVar = 200 | ||
f: list[int] = field(init=False, default_factory=list) | ||
g: int = field(default=2, kw_only=True) | ||
h: int = 1 | ||
i: InitVar[str] | ||
j: InitVar = 100 | ||
non_field = None | ||
|
||
@dataclass(init=False, repr=False, frozen=True) | ||
class Y: ... | ||
|
||
[out] | ||
from dataclasses import InitVar, dataclass | ||
from typing import ClassVar | ||
|
||
@dataclass | ||
class X: | ||
a: int | ||
b: str = ... | ||
c: ClassVar | ||
d: ClassVar = ... | ||
f: list[int] = ... | ||
g: int = ... | ||
h: int = ... | ||
i: InitVar[str] | ||
j: InitVar = ... | ||
non_field = ... | ||
def __init__(self, a, b, f, g, h, i, j) -> None: ... | ||
|
||
@dataclass(init=False, repr=False, frozen=True) | ||
class Y: ... | ||
|
||
[case testDataclassWithKwOnlyField_semanal] | ||
# flags: --python-version=3.10 | ||
from dataclasses import dataclass, InitVar, KW_ONLY | ||
from typing import ClassVar | ||
|
||
@dataclass | ||
class X: | ||
a: int | ||
b: str = "hello" | ||
c: ClassVar | ||
d: ClassVar = 200 | ||
f: list[int] = field(init=False, default_factory=list) | ||
g: int = field(default=2, kw_only=True) | ||
_: KW_ONLY | ||
h: int = 1 | ||
i: InitVar[str] | ||
j: InitVar = 100 | ||
non_field = None | ||
|
||
@dataclass(init=False, repr=False, frozen=True) | ||
class Y: ... | ||
|
||
[out] | ||
from dataclasses import InitVar, KW_ONLY, dataclass | ||
from typing import ClassVar | ||
|
||
@dataclass | ||
class X: | ||
a: int | ||
b: str = ... | ||
c: ClassVar | ||
d: ClassVar = ... | ||
f: list[int] = ... | ||
g: int = ... | ||
_: KW_ONLY | ||
h: int = ... | ||
i: InitVar[str] | ||
j: InitVar = ... | ||
non_field = ... | ||
def __init__(self, a, b, f, g, *, h, i, j) -> None: ... | ||
|
||
@dataclass(init=False, repr=False, frozen=True) | ||
class Y: ... | ||
|
||
[case testDataclassWithExplicitGeneratedMethodsOverrides_semanal] | ||
from dataclasses import dataclass | ||
|
||
@dataclass | ||
class X: | ||
a: int | ||
def __init__(self, a: int, b: str = ...) -> None: ... | ||
def __post_init__(self) -> None: ... | ||
|
||
[out] | ||
from dataclasses import dataclass | ||
|
||
@dataclass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to the dataclass documentation,
So it is ignored in this case. This was not the goal of the test anyway, it was to test that user defined methods are always included in the stub. |
||
class X: | ||
a: int | ||
def __init__(self, a: int, b: str = ...) -> None: ... | ||
def __post_init__(self) -> None: ... | ||
|
||
[case testDataclassInheritsFromAny_semanal] | ||
from dataclasses import dataclass | ||
import missing | ||
|
||
@dataclass | ||
class X(missing.Base): | ||
a: int | ||
|
||
[out] | ||
import missing | ||
from dataclasses import dataclass | ||
|
||
@dataclass | ||
class X(missing.Base): | ||
a: int | ||
def __init__(self, *selfa_, a, **selfa__) -> None: ... |
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.
Sorry to comment on an old, merged PR, but I wanted to ask if it's expected that the arguments to
__init__()
are missing type annotations in the generated stub.If I install this stub and then try to typecheck
I would expect mypy to error with
but instead it passes (I think the type annotation for
a
is implicitlyAny
when it's not annotated in the signature, which is incorrect I think - I think the type ofa
should beint
, notAny
).Let me know if this is actually better positioned as a new issue (with reproducible example etc.), but I just wanted to see if I was missing something obvious before opening a new one.
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.
Stubgen doesn't use inferred types of parameters in generated stubs unless it sees a parameter with a primitive default value. You'll always need to tweak the generated stubs by hand if you want better experience with the type checker.
Also note that the
__init__
method can be safely deleted from the stub most of the time. It is included because a field declaration likesome_field: int = field(init=False)
that affects the signature of__init__
cannot be currently represented in the stub.With that being said, I agree that perhaps we can do better but I am not a mypy maintainer. So yes please open a new issue asking for this feature.