diff --git a/mypy/plugin.py b/mypy/plugin.py index 2c39a4548a32..4cd769427ce1 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -254,6 +254,18 @@ def named_type(self, qualified_name: str, args: Optional[List[Type]] = None) -> """Construct an instance of a builtin type with given type arguments.""" raise NotImplementedError + @abstractmethod + def named_type_or_none(self, + qualified_name: str, + args: Optional[List[Type]] = None) -> Optional[Instance]: + """Construct an instance of a type with given type arguments. + + Return None if a type could not be constructed for the qualified + type name. This is possible when the qualified name includes a + module name and the module has not been imported. + """ + raise NotImplementedError + @abstractmethod def parse_bool(self, expr: Expression) -> Optional[bool]: """Parse True/False literals.""" diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 1494ca913545..9822b441df58 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -21,8 +21,8 @@ deserialize_and_fixup_type ) from mypy.types import ( - Type, AnyType, TypeOfAny, CallableType, NoneType, TypeVarType, - Overloaded, UnionType, FunctionLike, get_proper_type + TupleType, Type, AnyType, TypeOfAny, CallableType, NoneType, TypeVarType, + Overloaded, UnionType, FunctionLike, get_proper_type, ) from mypy.typeops import make_simplified_union, map_type_from_supertype from mypy.typevars import fill_typevars @@ -300,6 +300,8 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', ctx.api.defer() return + _add_attrs_magic_attribute(ctx, raw_attr_types=[info[attr.name].type for attr in attributes]) + # Save the attributes so that subclasses can reuse them. ctx.cls.info.metadata['attrs'] = { 'attributes': [attr.serialize() for attr in attributes], @@ -703,6 +705,27 @@ def _add_init(ctx: 'mypy.plugin.ClassDefContext', attributes: List[Attribute], adder.add_method('__init__', args, NoneType()) +def _add_attrs_magic_attribute(ctx: 'mypy.plugin.ClassDefContext', + raw_attr_types: 'List[Optional[Type]]') -> None: + attr_name = '__attrs_attrs__' + any_type = AnyType(TypeOfAny.explicit) + attributes_types: 'List[Type]' = [ + ctx.api.named_type_or_none('attr.Attribute', [attr_type or any_type]) or any_type + for attr_type in raw_attr_types + ] + fallback_type = ctx.api.named_type('__builtins__.tuple', [ + ctx.api.named_type_or_none('attr.Attribute', [any_type]) or any_type, + ]) + var = Var(name=attr_name, type=TupleType(attributes_types, fallback=fallback_type)) + var.info = ctx.cls.info + var._fullname = ctx.cls.info.fullname + '.' + attr_name + ctx.cls.info.names[attr_name] = SymbolTableNode( + kind=MDEF, + node=var, + plugin_generated=True, + ) + + class MethodAdder: """Helper to add methods to a TypeInfo. diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index af0153b2f56c..96b58b3f43a7 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -13,7 +13,10 @@ add_method, _get_decorator_bool_argument, deserialize_and_fixup_type, ) from mypy.typeops import map_type_from_supertype -from mypy.types import Type, Instance, NoneType, TypeVarType, CallableType, get_proper_type +from mypy.types import ( + Type, Instance, NoneType, TypeVarType, CallableType, get_proper_type, + AnyType, TypeOfAny, +) from mypy.server.trigger import make_wildcard_trigger # The set of decorators that generate dataclasses. @@ -187,6 +190,8 @@ def transform(self) -> None: self.reset_init_only_vars(info, attributes) + self._add_dataclass_fields_magic_attribute() + info.metadata['dataclass'] = { 'attributes': [attr.serialize() for attr in attributes], 'frozen': decorator_arguments['frozen'], @@ -417,6 +422,23 @@ def _is_kw_only_type(self, node: Optional[Type]) -> bool: return False return node_type.type.fullname == 'dataclasses.KW_ONLY' + def _add_dataclass_fields_magic_attribute(self) -> None: + attr_name = '__dataclass_fields__' + any_type = AnyType(TypeOfAny.explicit) + field_type = self._ctx.api.named_type_or_none('dataclasses.Field', [any_type]) or any_type + attr_type = self._ctx.api.named_type('__builtins__.dict', [ + self._ctx.api.named_type('__builtins__.str'), + field_type, + ]) + var = Var(name=attr_name, type=attr_type) + var.info = self._ctx.cls.info + var._fullname = self._ctx.cls.info.fullname + '.' + attr_name + self._ctx.cls.info.names[attr_name] = SymbolTableNode( + kind=MDEF, + node=var, + plugin_generated=True, + ) + def dataclass_class_maker_callback(ctx: ClassDefContext) -> None: """Hooks into the class typechecking process to add support for dataclasses. diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index 97e026fea38b..f8dcfd811e01 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -1390,3 +1390,15 @@ class B(A): reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> __main__.B" [builtins fixtures/bool.pyi] + +[case testAttrsClassHasAttributeWithAttributes] +import attr + +@attr.s +class A: + b: int = attr.ib() + c: str = attr.ib() + +reveal_type(A.__attrs_attrs__) # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str]]" + +[builtins fixtures/attr.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 75b0b3681de7..62959488aa27 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -14,7 +14,7 @@ reveal_type(Person) # N: Revealed type is "def (name: builtins.str, age: builti Person('John', 32) Person('Jonh', 21, None) # E: Too many arguments for "Person" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [typing fixtures/typing-medium.pyi] [case testDataclassesCustomInit] @@ -30,7 +30,7 @@ class A: A('1') -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesBasicInheritance] # flags: --python-version 3.7 @@ -52,7 +52,7 @@ Mammal(10) Person(32, 'John') Person(21, 'Jonh', None) # E: Too many arguments for "Person" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [typing fixtures/typing-medium.pyi] [case testDataclassesDeepInheritance] @@ -80,7 +80,7 @@ reveal_type(B) # N: Revealed type is "def (a: builtins.int, b: builtins.int) -> reveal_type(C) # N: Revealed type is "def (a: builtins.int, b: builtins.int, c: builtins.int) -> __main__.C" reveal_type(D) # N: Revealed type is "def (a: builtins.int, b: builtins.int, c: builtins.int, d: builtins.int) -> __main__.D" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesMultipleInheritance] # flags: --python-version 3.7 @@ -103,7 +103,7 @@ class C(A, B): reveal_type(C) # N: Revealed type is "def (b: builtins.bool, a: builtins.bool) -> __main__.C" -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesDeepInitVarInheritance] # flags: --python-version 3.7 @@ -132,7 +132,7 @@ class D(C): reveal_type(C) # N: Revealed type is "def () -> __main__.C" reveal_type(D) # N: Revealed type is "def (b: builtins.bool) -> __main__.D" -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOverriding] # flags: --python-version 3.7 @@ -165,7 +165,7 @@ Person(21, 'John', None) # E: Too many arguments for "Person" SpecialPerson(21, 'John', 0.5) ExtraSpecialPerson(21, 'John', 0.5) -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOverridingWithDefaults] # Issue #5681 https://github.com/python/mypy/issues/5681 @@ -185,7 +185,7 @@ class C(Base): reveal_type(C) # N: Revealed type is "def (some_int: builtins.int, some_str: builtins.str =) -> __main__.C" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesFreezing] # flags: --python-version 3.7 @@ -198,7 +198,7 @@ class Person: john = Person('John') john.name = 'Ben' # E: Property "name" defined in "Person" is read-only -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesFields] # flags: --python-version 3.7 @@ -214,7 +214,7 @@ john = Person('John') john.age = 'invalid' # E: Incompatible types in assignment (expression has type "str", variable has type "int") john.age = 24 -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesBadInit] # flags: --python-version 3.7 @@ -228,7 +228,7 @@ class Person: # N: def field(*, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> Any \ # N: <2 more non-matching overloads not shown> -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesMultiInit] # flags: --python-version 3.7 @@ -244,7 +244,7 @@ class Person: reveal_type(Person) # N: Revealed type is "def (name: builtins.str, friend_names: builtins.list[builtins.str], enemy_names: builtins.list[builtins.str]) -> __main__.Person" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesMultiInitDefaults] # flags: --python-version 3.7 @@ -261,7 +261,7 @@ class Person: reveal_type(Person) # N: Revealed type is "def (name: builtins.str, friend_names: builtins.list[builtins.str], enemy_names: builtins.list[builtins.str], nickname: Union[builtins.str, None] =) -> __main__.Person" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesDefaults] # flags: --python-version 3.7 @@ -275,7 +275,7 @@ class Application: reveal_type(Application) # N: Revealed type is "def (name: builtins.str =, rating: builtins.int =) -> __main__.Application" app = Application() -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultFactories] # flags: --python-version 3.7 @@ -287,7 +287,7 @@ class Application: rating: int = field(default_factory=int) rating_count: int = field() # E: Attributes without a default cannot follow attributes with one -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultFactoryTypeChecking] # flags: --python-version 3.7 @@ -298,7 +298,7 @@ class Application: name: str = 'Unnamed' rating: int = field(default_factory=str) # E: Incompatible types in assignment (expression has type "str", variable has type "int") -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultOrdering] # flags: --python-version 3.7 @@ -309,7 +309,7 @@ class Application: name: str = 'Unnamed' rating: int # E: Attributes without a default cannot follow attributes with one -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOrderingKwOnly] # flags: --python-version 3.10 @@ -327,9 +327,9 @@ Application('name') # E: Too many positional arguments for "Application" # E: M Application('name', 123) # E: Too many positional arguments for "Application" Application('name', rating=123) # E: Too many positional arguments for "Application" Application(name=123, rating='name') # E: Argument "name" to "Application" has incompatible type "int"; expected "str" # E: Argument "rating" to "Application" has incompatible type "str"; expected "int" -Application(rating='name', name=123) # E: Argument "rating" to "Application" has incompatible type "str"; expected "int" # E: Argument "name" to "Application" has incompatible type "int"; expected "str" +Application(rating='name', name=123) # E: Argument "rating" to "Application" has incompatible type "str"; expected "int" # E: Argument "name" to "Application" has incompatible type "int"; expected "str" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOrderingKwOnlyOnField] # flags: --python-version 3.10 @@ -348,7 +348,7 @@ Application('name') # E: Missing named argument "rating" for "Application" Application('name', 123) # E: Too many positional arguments for "Application" Application(123, rating='name') # E: Argument 1 to "Application" has incompatible type "int"; expected "str" # E: Argument "rating" to "Application" has incompatible type "str"; expected "int" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOrderingKwOnlyOnFieldFalse] # flags: --python-version 3.10 @@ -357,7 +357,7 @@ from dataclasses import dataclass, field @dataclass class Application: name: str = 'Unnamed' - rating: int = field(kw_only=False) # E: Attributes without a default cannot follow attributes with one + rating: int = field(kw_only=False) # E: Attributes without a default cannot follow attributes with one Application(name='name', rating=5) Application('name', 123) @@ -365,7 +365,7 @@ Application('name', rating=123) Application() # E: Missing positional argument "name" in call to "Application" Application('name') # E: Too few arguments for "Application" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOrderingKwOnlyWithSentinel] # flags: --python-version 3.10 @@ -384,7 +384,7 @@ Application('name') # E: Too many positional arguments for "Application" # E: M Application('name', 123) # E: Too many positional arguments for "Application" Application('name', rating=123) # E: Too many positional arguments for "Application" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOrderingKwOnlyWithSentinelAndFieldOverride] # flags: --python-version 3.10 @@ -394,7 +394,7 @@ from dataclasses import dataclass, field, KW_ONLY class Application: _: KW_ONLY name: str = 'Unnamed' - rating: int = field(kw_only=False) # E: Attributes without a default cannot follow attributes with one + rating: int = field(kw_only=False) # E: Attributes without a default cannot follow attributes with one Application(name='name', rating=5) Application() # E: Missing positional argument "name" in call to "Application" @@ -402,7 +402,7 @@ Application('name') # E: Too many positional arguments for "Application" # E: T Application('name', 123) # E: Too many positional arguments for "Application" Application('name', rating=123) # E: Too many positional arguments for "Application" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOrderingKwOnlyWithSentinelAndSubclass] # flags: --python-version 3.10 @@ -429,6 +429,8 @@ D(123, "World") # E: Argument 1 to "D" has incompatible type "int"; expected "s D("Hello", False) # E: Argument 2 to "D" has incompatible type "bool"; expected "str" D(123, False) # E: Argument 1 to "D" has incompatible type "int"; expected "str" # E: Argument 2 to "D" has incompatible type "bool"; expected "str" +[builtins fixtures/dataclasses.pyi] + [case testDataclassesOrderingKwOnlyWithMultipleSentinel] # flags: --python-version 3.10 from dataclasses import dataclass, field, KW_ONLY @@ -441,7 +443,7 @@ class Base: __: KW_ONLY # E: There may not be more than one field with the KW_ONLY type w: int = 1 -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesClassmethods] # flags: --python-version 3.7 @@ -457,8 +459,7 @@ class Application: app = Application.parse('') -[builtins fixtures/list.pyi] -[builtins fixtures/classmethod.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesOverloadsAndClassmethods] # flags: --python-version 3.7 @@ -491,7 +492,7 @@ class A: reveal_type(A.foo(3)) # N: Revealed type is "builtins.int" reveal_type(A.foo("foo")) # N: Revealed type is "builtins.str" -[builtins fixtures/classmethod.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesClassVars] # flags: --python-version 3.7 @@ -509,7 +510,7 @@ application = Application("example") application.COUNTER = 1 # E: Cannot assign to class variable "COUNTER" via instance Application.COUNTER = 1 -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassOrdering] # flags: --python-version 3.7 @@ -540,7 +541,7 @@ app1 > app3 app1 <= app3 app1 >= app3 -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassOrderingWithoutEquality] # flags: --python-version 3.7 @@ -550,7 +551,7 @@ from dataclasses import dataclass class Application: # E: eq must be True if order is True ... -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassOrderingWithCustomMethods] # flags: --python-version 3.7 @@ -561,7 +562,7 @@ class Application: def __lt__(self, other: 'Application') -> bool: # E: You may not have a custom __lt__ method when order=True ... -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassDefaultsInheritance] # flags: --python-version 3.7 @@ -579,7 +580,7 @@ class SpecializedApplication(Application): reveal_type(SpecializedApplication) # N: Revealed type is "def (id: Union[builtins.int, None], name: builtins.str, rating: builtins.int =) -> __main__.SpecializedApplication" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassGenerics] # flags: --python-version 3.7 @@ -612,7 +613,7 @@ reveal_type(a.y) # N: Revealed type is "builtins.int*" reveal_type(a.z) # N: Revealed type is "builtins.list[builtins.int*]" s: str = a.bar() # E: Incompatible types in assignment (expression has type "int", variable has type "str") -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassUntypedGenericInheritance] @@ -634,6 +635,7 @@ sub = Sub(attr=1) reveal_type(sub) # N: Revealed type is "__main__.Sub" reveal_type(sub.attr) # N: Revealed type is "Any" +[builtins fixtures/dataclasses.pyi] [case testDataclassGenericSubtype] # flags: --python-version 3.7 @@ -660,6 +662,7 @@ sub_str = Sub[str](attr='ok') reveal_type(sub_str) # N: Revealed type is "__main__.Sub[builtins.str*]" reveal_type(sub_str.attr) # N: Revealed type is "builtins.str*" +[builtins fixtures/dataclasses.pyi] [case testDataclassGenericInheritance] # flags: --python-version 3.7 @@ -686,6 +689,7 @@ reveal_type(sub.one) # N: Revealed type is "builtins.int*" reveal_type(sub.two) # N: Revealed type is "builtins.str*" reveal_type(sub.three) # N: Revealed type is "builtins.float*" +[builtins fixtures/dataclasses.pyi] [case testDataclassMultiGenericInheritance] # flags: --python-version 3.7 @@ -713,6 +717,7 @@ reveal_type(sub) # N: Revealed type is "__main__.Sub" reveal_type(sub.base_attr) # N: Revealed type is "builtins.int*" reveal_type(sub.middle_attr) # N: Revealed type is "builtins.str*" +[builtins fixtures/dataclasses.pyi] [case testDataclassGenericsClassmethod] # flags: --python-version 3.7 @@ -734,7 +739,7 @@ class A(Generic[T]): def other(cls, x: T) -> A[T]: ... reveal_type(A(0).other) # N: Revealed type is "def (x: builtins.int*) -> __main__.A[builtins.int*]" -[builtins fixtures/classmethod.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesForwardRefs] # flags: --python-version 3.7 @@ -752,7 +757,7 @@ reveal_type(A) # N: Revealed type is "def (b: __main__.B) -> __main__.A" A(b=B(42)) A(b=42) # E: Argument "b" to "A" has incompatible type "int"; expected "B" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesInitVars] @@ -782,7 +787,7 @@ app.name app.rating app.database_name # E: "SpecializedApplication" has no attribute "database_name" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesInitVarsAndDefer] # flags: --python-version 3.7 @@ -802,7 +807,7 @@ app.name app.database_name # E: "Application" has no attribute "database_name" class Yes: ... -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesNoInitInitVarInheritance] # flags: --python-version 3.7 @@ -819,7 +824,7 @@ class Sub(Super): sub = Sub(5) sub.foo # E: "Sub" has no attribute "foo" sub.bar -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassFactory] # flags: --python-version 3.7 @@ -835,7 +840,7 @@ class A: reveal_type(cls) # N: Revealed type is "Type[T`-1]" reveal_type(cls()) # N: Revealed type is "T`-1" return cls() -[builtins fixtures/classmethod.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesInitVarOverride] # flags: --python-version 3.7 @@ -858,7 +863,7 @@ class B(A): super().__init__(b+1) self._b = b -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesInitVarNoOverride] # flags: --python-version 3.7 @@ -884,7 +889,7 @@ class B(A): B(1, 2) B(1, 'a') # E: Argument 2 to "B" has incompatible type "str"; expected "int" -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesInitVarPostInitOverride] # flags: --python-version 3.7 @@ -977,7 +982,7 @@ from typing import Optional @dataclass class Foo: bar: Optional[int] = field(default=None) -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] [case testNoComplainFieldNoneStrict] @@ -989,7 +994,7 @@ from typing import Optional @dataclass class Foo: bar: Optional[int] = field(default=None) -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] [case testDisallowUntypedWorksForward] @@ -1005,7 +1010,7 @@ class C(List[C]): pass reveal_type(B) # N: Revealed type is "def (x: __main__.C) -> __main__.B" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDisallowUntypedWorksForwardBad] # flags: --disallow-untyped-defs --python-version 3.7 @@ -1017,7 +1022,7 @@ class B: y = undefined() # E: Name "undefined" is not defined reveal_type(B) # N: Revealed type is "def (x: Any) -> __main__.B" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testMemberExprWorksAsField] # flags: --python-version 3.7 @@ -1055,7 +1060,7 @@ b = Application('', 0) a < b class Yes: ... -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassFieldDeferred] # flags: --python-version 3.7 @@ -1067,7 +1072,7 @@ class C: def func() -> int: ... C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int" -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassFieldDeferredFrozen] # flags: --python-version 3.7 @@ -1080,7 +1085,7 @@ class C: def func() -> int: ... c: C c.x = 1 # E: Property "x" defined in "C" is read-only -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testTypeInDataclassDeferredStar] # flags: --python-version 3.7 @@ -1099,7 +1104,7 @@ C() # E: Missing positional argument "total" in call to "C" C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int" [file other.py] import lib -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testDeferredDataclassInitSignature] # flags: --python-version 3.7 --no-strict-optional @@ -1116,7 +1121,7 @@ class C: return cls(x=None, y=None) class Deferred: pass -[builtins fixtures/classmethod.pyi] +[builtins fixtures/dataclasses.pyi] [case testDeferredDataclassInitSignatureSubclass] # flags: --strict-optional --python-version 3.7 @@ -1132,7 +1137,7 @@ class C(B): y: str a = C(None, 'abc') -[builtins fixtures/bool.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultsIncremental] # flags: --python-version 3.7 @@ -1164,7 +1169,7 @@ class Person: b: int a: str = 'test' -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultsMroOtherFile] # flags: --python-version 3.7 @@ -1193,7 +1198,7 @@ class A1: class A2: b: str = 'test' -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassesInheritingDuplicateField] # flags: --python-version 3.7 @@ -1209,6 +1214,8 @@ class A: # E: Name "x" already defined (possibly by an import) class B(A): pass +[builtins fixtures/dataclasses.pyi] + [case testDataclassInheritanceNoAnnotation] # flags: --python-version 3.7 from dataclasses import dataclass @@ -1224,6 +1231,8 @@ class B(A): reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> __main__.B" +[builtins fixtures/dataclasses.pyi] + [case testDataclassInheritanceNoAnnotation2] # flags: --python-version 3.7 from dataclasses import dataclass @@ -1239,7 +1248,19 @@ class B(A): reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> __main__.B" -[builtins fixtures/property.pyi] +[builtins fixtures/dataclasses.pyi] + +[case testDataclassHasAttributeWithFields] +# flags: --python-version 3.7 +from dataclasses import dataclass + +@dataclass +class A: + pass + +reveal_type(A.__dataclass_fields__) # N: Revealed type is "builtins.dict[builtins.str, dataclasses.Field[Any]]" + +[builtins fixtures/dict.pyi] [case testDataclassCallableFieldAccess] # flags: --python-version 3.7 @@ -1258,6 +1279,8 @@ reveal_type(a.x) # N: Revealed type is "def (builtins.int) -> builtins.int" reveal_type(a.y) # N: Revealed type is "def (builtins.int) -> builtins.int" reveal_type(A.y) # N: Revealed type is "def (builtins.int) -> builtins.int" +[builtins fixtures/dataclasses.pyi] + [case testDataclassCallableFieldAssignment] # flags: --python-version 3.7 from dataclasses import dataclass @@ -1275,3 +1298,5 @@ def x2(s: str) -> str: a = A(lambda i:i) a.x = x a.x = x2 # E: Incompatible types in assignment (expression has type "Callable[[str], str]", variable has type "Callable[[int], int]") + +[builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 3e321abd46d6..e0825715db15 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1193,6 +1193,8 @@ import attr class Unannotated: foo = attr.ib() +[builtins fixtures/attr.pyi] + [case testDisallowIncompleteDefsAttrsWithAnnotations] # flags: --disallow-incomplete-defs import attr @@ -1201,6 +1203,8 @@ import attr class Annotated: bar: int = attr.ib() +[builtins fixtures/attr.pyi] + [case testDisallowIncompleteDefsAttrsPartialAnnotations] # flags: --disallow-incomplete-defs import attr @@ -1210,6 +1214,8 @@ class PartiallyAnnotated: # E: Function is missing a type annotation for one or bar: int = attr.ib() baz = attr.ib() +[builtins fixtures/attr.pyi] + [case testAlwaysTrueAlwaysFalseFlags] # flags: --always-true=YOLO --always-true=YOLO1 --always-false=BLAH1 --always-false BLAH --ignore-missing-imports from somewhere import YOLO, BLAH diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 0baea1c978b1..0d7d2f125338 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -3830,7 +3830,7 @@ class A: E = 7 F: ClassVar[int] = 22 -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out1] [out2] @@ -3863,7 +3863,7 @@ from dataclasses import dataclass class A: x: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out1] [out2] tmp/b.py:8: note: Revealed type is "def (x: builtins.int) -> b.B" @@ -3908,7 +3908,7 @@ class NoInit: class NoCmp: x: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out1] [out2] tmp/b.py:4: error: Property "x" defined in "Frozen" is read-only @@ -3963,7 +3963,7 @@ from dataclasses import dataclass class A: a: int -[builtins fixtures/attr.pyi] +[builtins fixtures/dataclasses.pyi] [out1] [out2] tmp/b.py:3: note: Revealed type is "def (a: builtins.int) -> a.A" @@ -4010,7 +4010,7 @@ from dataclasses import dataclass class B(A): y: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out1] [out2] main:3: error: Argument 2 to "B" has incompatible type "str"; expected "int" @@ -4044,7 +4044,7 @@ from dataclasses import dataclass class B(A): y: str -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out1] main:3: error: Argument 2 to "B" has incompatible type "str"; expected "int" @@ -4087,7 +4087,7 @@ from dataclasses import dataclass class C(A, B): c: bool -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out1] [out2] tmp/c.py:7: error: Incompatible types in assignment (expression has type "bool", base class "B" defined the type as "str") @@ -4119,7 +4119,7 @@ from dataclasses import dataclass class A: a: int = 6 -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out1] [out2] main:3: error: Argument 1 to "A" has incompatible type "int"; expected "str" diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 952192995642..7c8415b75fe1 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -170,7 +170,7 @@ if x5["key"] is Key.A: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal[__main__.Key.A], 'foo': builtins.int})" else: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal[__main__.Key.B], 'foo': builtins.str})" -[builtins fixtures/tuple.pyi] +[builtins fixtures/narrowing.pyi] [case testNarrowingParentWithIsInstanceBasic] # flags: --python-version 3.7 @@ -236,7 +236,7 @@ if isinstance(x5["key"], int): reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': builtins.int})" else: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': builtins.str})" -[builtins fixtures/isinstance.pyi] +[builtins fixtures/narrowing.pyi] [case testNarrowingParentMultipleKeys] # flags: --warn-unreachable diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 8c074abc83a2..d833a05e201a 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1430,11 +1430,11 @@ class A: @dataclass class B(A): y: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] -> , m - -> + -> -> , m.B.__init__ -> -> diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 4963c459e8ff..28f0260ccce3 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -688,7 +688,7 @@ class A: == b.py:8: error: Argument 1 to "B" has incompatible type "int"; expected "str" == -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassUpdate2] # flags: --python-version 3.7 @@ -719,7 +719,7 @@ B(1, 2) [out] == b.py:8: error: Argument 1 to "B" has incompatible type "int"; expected "str" -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [case testDataclassUpdate3] # flags: --python-version 3.7 @@ -743,7 +743,7 @@ from dataclasses import dataclass class A: a: int other: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] == main:3: error: Missing positional argument "b" in call to "B" @@ -770,7 +770,7 @@ from dataclasses import dataclass class A: a: int other: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] == main:3: error: Missing positional argument "b" in call to "B" @@ -804,7 +804,7 @@ from dataclasses import dataclass class A: a: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] == main:3: error: Missing positional argument "b" in call to "B" @@ -831,7 +831,7 @@ from dataclasses import dataclass @dataclass class A: a: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] == main:3: error: Unsupported left operand type for < ("B") @@ -864,7 +864,7 @@ from dataclasses import dataclass class A: a: int other: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] == main:3: error: Missing positional argument "c" in call to "C" @@ -904,7 +904,7 @@ from dataclasses import dataclass class A: a: int -[builtins fixtures/list.pyi] +[builtins fixtures/dataclasses.pyi] [out] == main:3: error: Missing positional argument "c" in call to "C" diff --git a/test-data/unit/fixtures/attr.pyi b/test-data/unit/fixtures/attr.pyi index deb1906d931e..c209abfef0d9 100644 --- a/test-data/unit/fixtures/attr.pyi +++ b/test-data/unit/fixtures/attr.pyi @@ -25,3 +25,4 @@ class complex: class str: pass class unicode: pass class ellipsis: pass +class tuple: pass diff --git a/test-data/unit/fixtures/dataclasses.pyi b/test-data/unit/fixtures/dataclasses.pyi new file mode 100644 index 000000000000..fb0053c80b25 --- /dev/null +++ b/test-data/unit/fixtures/dataclasses.pyi @@ -0,0 +1,22 @@ +from typing import Generic, Sequence, TypeVar + +_T = TypeVar('_T') +_U = TypeVar('_U') + +class object: + def __init__(self) -> None: pass + def __eq__(self, o: object) -> bool: pass + def __ne__(self, o: object) -> bool: pass + +class type: pass +class ellipsis: pass +class tuple(Generic[_T]): pass +class int: pass +class float: pass +class str: pass +class bool(int): pass +class dict(Generic[_T, _U]): pass +class list(Generic[_T], Sequence[_T]): pass +class function: pass +class classmethod: pass +property = object() diff --git a/test-data/unit/fixtures/narrowing.pyi b/test-data/unit/fixtures/narrowing.pyi new file mode 100644 index 000000000000..89ee011c1c80 --- /dev/null +++ b/test-data/unit/fixtures/narrowing.pyi @@ -0,0 +1,20 @@ +# Builtins stub used in check-narrowing test cases. +from typing import Generic, Sequence, Tuple, Type, TypeVar, Union + + +Tco = TypeVar('Tco', covariant=True) +KT = TypeVar("KT") +VT = TypeVar("VT") + +class object: + def __init__(self) -> None: pass + +class type: pass +class tuple(Sequence[Tco], Generic[Tco]): pass +class function: pass +class ellipsis: pass +class int: pass +class str: pass +class dict(Generic[KT, VT]): pass + +def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...]]) -> bool: pass diff --git a/test-data/unit/lib-stub/attr/__init__.pyi b/test-data/unit/lib-stub/attr/__init__.pyi index 475cfb7571a5..6ce4b3a64ed5 100644 --- a/test-data/unit/lib-stub/attr/__init__.pyi +++ b/test-data/unit/lib-stub/attr/__init__.pyi @@ -1,4 +1,4 @@ -from typing import TypeVar, overload, Callable, Any, Type, Optional, Union, Sequence, Mapping +from typing import TypeVar, overload, Callable, Any, Type, Optional, Union, Sequence, Mapping, Generic _T = TypeVar('_T') _C = TypeVar('_C', bound=type) @@ -115,6 +115,8 @@ def attrs(maybe_cls: None = ..., ) -> Callable[[_C], _C]: ... +class Attribute(Generic[_T]): pass + # aliases s = attributes = attrs ib = attr = attrib diff --git a/test-data/unit/lib-stub/dataclasses.pyi b/test-data/unit/lib-stub/dataclasses.pyi index 9c283e31c50c..e0491e14876b 100644 --- a/test-data/unit/lib-stub/dataclasses.pyi +++ b/test-data/unit/lib-stub/dataclasses.pyi @@ -30,3 +30,6 @@ def field(*, default_factory: Callable[[], _T], def field(*, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...,) -> Any: ... + + +class Field(Generic[_T]): pass