8000 [dataclass_transform] support field_specifiers by wesleywright · Pull Request #14667 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

[dataclass_transform] support field_specifiers #14667

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
Prev Previous commit
Next Next commit
Merge branch 'master' of https://github.com/python/mypy into dataclas…
…s-transform-field-specifiers
  • Loading branch information
wesleywright committed Feb 13, 2023
commit 1aaa3177b07393eed3270da50d5a9c36eb5778d4
8 changes: 4 additions & 4 deletions mypy/plugins/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,11 +520,11 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
# kw_only value from the decorator parameter.
field_kw_only_param = field_args.get("kw_only")
if field_kw_only_param is not None:
value = ctx.api.parse_bool(field_kw_only_param)
value = self._api.parse_bool(field_kw_only_param)
if value is not None:
is_kw_only = value
else:
ctx.api.fail('"kw_only" argument must be True or False.', stmt.rvalue)
self._api.fail('"kw_only" argument must be True or False.', stmt.rvalue)

if sym.type is None and node.is_final and node.is_inferred:
# This is a special case, assignment like x: Final = 42 is classified
Expand All @@ -544,9 +544,9 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:

alias = None
if "alias" in field_args:
alias = self._ctx.api.parse_str_literal(field_args["alias"])
alias = self._api.parse_str_literal(field_args["alias"])
if alias is None:
self._ctx.api.fail(
self._api.fail(
message_registry.DATACLASS_FIELD_ALIAS_MUST_BE_LITERAL,
stmt.rvalue,
code=errorcodes.LITERAL_REQ,
Expand Down
124 changes: 124 additions & 0 deletions test-data/unit/check-dataclass-transform.test
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,127 @@ Foo.__dataclass_fields__ # E: "Type[Foo]" has no attribute "__dataclass_fields_

[typing fixtures/typing-full.pyi]
[builtins fixtures/dataclasses.pyi]

[case testDataclassTransformOverloadsDecoratorOnOverload]
# flags: --python-version 3.11
from typing import dataclass_transform, overload, Any, Callable, Type, Literal

@overload
def my_dataclass(*, foo: str) -> Callable[[Type], Type]: ...
@overload
@dataclass_transform(frozen_default=True)
def my_dataclass(*, foo: int) -> Callable[[Type], Type]: ...
def my_dataclass(*, foo: Any) -> Callable[[Type], Type]:
return lambda cls: cls
@my_dataclass(foo="hello")
class A:
a: int
@my_dataclass(foo=5)
class B:
b: int

reveal_type(A) # N: Revealed type is "def (a: builtins.int) -> __main__.A"
reveal_type(B) # N: Revealed type is "def (b: builtins.int) -> __main__.B"
A(1, "hello") # E: Too many arguments for "A"
a = A(1)
a.a = 2 # E: Property "a" defined in "A" is read-only

[typing fixtures/typing-full.pyi]
[builtins fixtures/dataclasses.pyi]

[case testDataclassTransformOverloadsDecoratorOnImpl]
# flags: --python-version 3.11
from typing import dataclass_transform, overload, Any, Callable, Type, Literal

@overload
def my_dataclass(*, foo: str) -> Callable[[Type], Type]: ...
@overload
def my_dataclass(*, foo: int) -> Callable[[Type], Type]: ...
@dataclass_transform(frozen_default=True)
def my_dataclass(*, foo: Any) -> Callable[[Type], Type]:
return lambda cls: cls
@my_dataclass(foo="hello")
class A:
a: int
@my_dataclass(foo=5)
class B:
b: int

reveal_type(A) # N: Revealed type is "def (a: builtins.int) -> __main__.A"
reveal_type(B) # N: Revealed type is "def (b: builtins.int) -> __main__.B"
A(1, "hello") # E: Too many arguments for "A"
a = A(1)
a.a = 2 # E: Property "a" defined in "A" is read-only

[typing fixtures/typing-full.pyi]
[builtins fixtures/dataclasses.pyi]

[case testDataclassTransformViaBaseClass]
# flags: --python-version 3.11
from typing import dataclass_transform

@dataclass_transform(frozen_default=True)
class Dataclass:
def __init_subclass__(cls, *, kw_only: bool = False): ...

class Person(Dataclass, kw_only=True):
name: str
age: int

reveal_type(Person) # N: Revealed type is "def (*, name: builtins.str, age: builtins.int) -> __main__.Person"
Person('Jonh', 21) # E: Too many positional arguments for "Person"
person = Person(name='John', age=32)
person.name = "John Smith" # E: Property "name" defined in "Person" is read-only

class Contact(Person):
email: str

reveal_type(Contact) # N: Revealed type is "def (email: builtins.str, *, name: builtins.str, age: builtins.int) -> __main__.Contact"
Contact('john@john.com', name='John', age=32)

[typing fixtures/typing-full.pyi]
[builtins fixtures/dataclasses.pyi]

[case testDataclassTransformViaMetaclass]
# flags: --python-version 3.11
from typing import dataclass_transform

@dataclass_transform(frozen_default=True)
class Dataclass(type): ...

class Person(metaclass=Dataclass, kw_only=True):
name: str
age: int

reveal_type(Person) # N: Revealed type is "def (*, name: builtins.str, age: builtins.int) -> __main__.Person"
Person('Jonh', 21) # E: Too many positional arguments for "Person"
person = Person(name='John', age=32)
person.name = "John Smith" # E: Property "name" defined in "Person" is read-only

class Contact(Person):
email: str

reveal_type(Contact) # N: Revealed type is "def (email: builtins.str, *, name: builtins.str, age: builtins.int) -> __main__.Contact"
Contact('john@john.com', name='John', age=32)

[typing fixtures/typing-full.pyi]
[builtins fixtures/dataclasses.pyi]

[case testDataclassTransformViaSubclassOfMetaclass]
# flags: --python-version 3.11
from typing import dataclass_transform

@dataclass_transform(frozen_default=True)
class BaseMeta(type): ...
class SubMeta(BaseMeta): ...

# MyPy does *not* recognize this as a dataclass because the metaclass is not directly decorated with
# dataclass_transform
class Foo(metaclass=SubMeta):
foo: int

reveal_type(Foo) # N: Revealed type is "def () -> __main__.Foo"
Foo(1) # E: Too many arguments for "Foo"

[typing fixtures/typing-full.pyi]
[builtins fixtures/dataclasses.pyi]
You are viewing a condensed version of this merge commit. You can view the full changes here.
0