diff --git a/conformance/results/mypy/constructors_call_init.toml b/conformance/results/mypy/constructors_call_init.toml index f2d7df715..d64e6b248 100644 --- a/conformance/results/mypy/constructors_call_init.toml +++ b/conformance/results/mypy/constructors_call_init.toml @@ -2,16 +2,26 @@ conformant = "Partial" notes = """ Does not report errors during binding to self parameter of `__init__` method. Does not reject use of class-scoped type variables in annotation of self parameter in `__init__` method. +Does not solve a subclass's type parameters from the self annotation of an inherited `__init__` method. """ conformance_automated = "Fail" errors_diff = """ -Line 56: Expected 1 errors -Line 107: Expected 1 errors -Line 73: Unexpected errors ['constructors_call_init.py:73: error: Expression is of type "Class5[list[int]]", not "Class5[int]" [assert-type]'] +Line 57: Expected 1 errors +Line 108: Expected 1 errors +Line 147: Expected 1 errors +Line 159: Expected 1 errors +Line 167: Expected 1 errors +Line 74: Unexpected errors ['constructors_call_init.py:74: error: Expression is of type "Class5[list[int]]", not "Class5[int]" [assert-type]'] +Line 129: Unexpected errors ['constructors_call_init.py:129: error: Expression is of type "Class13[Never, Never]", not "Class13[int, str]" [assert-type]'] +Line 136: Unexpected errors ['constructors_call_init.py:136: error: Expression is of type "Class14[Never, Never, Never]", not "Class14[Any, str, int]" [assert-type]'] +Line 189: Unexpected errors ['constructors_call_init.py:189: error: Expression is of type "Class20[int]", not "Class20[int | None]" [assert-type]'] """ output = """ -constructors_call_init.py:21: error: Argument 1 to "Class1" has incompatible type "float"; expected "int" [arg-type] -constructors_call_init.py:42: error: Argument 1 to "Class3" has incompatible type "Class2[Never]"; expected "Class3 | None" [arg-type] -constructors_call_init.py:73: error: Expression is of type "Class5[list[int]]", not "Class5[int]" [assert-type] -constructors_call_init.py:130: error: Too many arguments for "Class11" [call-arg] +constructors_call_init.py:22: error: Argument 1 to "Class1" has incompatible type "float"; expected "int" [arg-type] +constructors_call_init.py:43: error: Argument 1 to "Class3" has incompatible type "Class2[Never]"; expected "Class3 | None" [arg-type] +constructors_call_init.py:74: error: Expression is of type "Class5[list[int]]", not "Class5[int]" [assert-type] +constructors_call_init.py:129: error: Expression is of type "Class13[Never, Never]", not "Class13[int, str]" [assert-type] +constructors_call_init.py:136: error: Expression is of type "Class14[Never, Never, Never]", not "Class14[Any, str, int]" [assert-type] +constructors_call_init.py:189: error: Expression is of type "Class20[int]", not "Class20[int | None]" [assert-type] +constructors_call_init.py:211: error: Too many arguments for "Class11" [call-arg] """ diff --git a/conformance/results/mypy/constructors_callable.toml b/conformance/results/mypy/constructors_callable.toml index c477fd6b8..f38c520f2 100644 --- a/conformance/results/mypy/constructors_callable.toml +++ b/conformance/results/mypy/constructors_callable.toml @@ -3,6 +3,7 @@ notes = """ Does not generate a union type for `__new__` and `__init__` when converting class to callable. Does not ignore `__init__` based on `__new__` return type when converting class to callable. Does not support `__new__` return type that is different from class being constructed. +Does not solve a subclass's type parameters from the self annotation of an inherited `__init__` method when converting class to callable. """ conformance_automated = "Fail" errors_diff = """ @@ -14,6 +15,8 @@ Line 107: Unexpected errors ['constructors_callable.py:107: error: Expression is Line 118: Unexpected errors ['constructors_callable.py:118: error: Incompatible return type for "__new__" (returns "Class6Proxy", but must return a subtype of "Class6") [misc]'] Line 128: Unexpected errors ['constructors_callable.py:128: error: Expression is of type "Class6", not "Class6Proxy" [assert-type]', 'constructors_callable.py:128: error: Too few arguments [call-arg]'] Line 145: Unexpected errors ['constructors_callable.py:145: error: Expression is of type "Class6Any", not "Any" [assert-type]', 'constructors_callable.py:145: error: Too few arguments [call-arg]'] +Line 219: Unexpected errors ['constructors_callable.py:219: error: Need type annotation for "r11" [var-annotated]'] +Line 221: Unexpected errors ['constructors_callable.py:221: error: Expression is of type "Class11[Any, Any]", not "Class11[int, str]" [assert-type]'] """ output = """ constructors_callable.py:36: note: Revealed type is "def (x: int) -> constructors_callable.Class1" @@ -46,4 +49,7 @@ constructors_callable.py:187: error: Cannot infer function type argument [misc] constructors_callable.py:196: note: Revealed type is "def [T] (x: list[T], y: list[T]) -> constructors_callable.Class9" constructors_callable.py:197: error: Argument 2 has incompatible type "str"; expected "list[str]" [arg-type] constructors_callable.py:199: error: Cannot infer function type argument [misc] +constructors_callable.py:219: error: Need type annotation for "r11" [var-annotated] +constructors_callable.py:220: note: Revealed type is "def () -> constructors_callable.Class11[Any, Any]" +constructors_callable.py:221: error: Expression is of type "Class11[Any, Any]", not "Class11[int, str]" [assert-type] """ diff --git a/conformance/results/pycroscope/constructors_call_init.toml b/conformance/results/pycroscope/constructors_call_init.toml index da605980b..77cbfee47 100644 --- a/conformance/results/pycroscope/constructors_call_init.toml +++ b/conformance/results/pycroscope/constructors_call_init.toml @@ -1,10 +1,28 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not solve a subclass's type parameters from the self annotation of an inherited `__init__` method. +Does not report errors during binding to the self parameter of an inherited `__init__` method. +""" +conformance_automated = "Fail" errors_diff = """ +Line 147: Expected 1 errors +Line 159: Expected 1 errors +Line 167: Expected 1 errors +Line 129: Unexpected errors ['./constructors_call_init.py:129:12: ./constructors_call_init.py.Class13~[Any[generic_argument], Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class13[int, str]'] +Line 136: Unexpected errors ['./constructors_call_init.py:136:12: ./constructors_call_init.py.Class14~[Any[generic_argument], Any[generic_argument], Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class14[Any[explicit], str, int]'] +Line 187: Unexpected errors ['./constructors_call_init.py:187:12: ./constructors_call_init.py.Class20~[Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class20[int]'] +Line 188: Unexpected errors ['./constructors_call_init.py:188:12: ./constructors_call_init.py.Class20~[Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class20[int]'] +Line 189: Unexpected errors ['./constructors_call_init.py:189:12: ./constructors_call_init.py.Class20~[Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class20[int | None]'] """ output = """ -./constructors_call_init.py:21:12: Incompatible argument type for x: expected int but got Literal[1.0] [incompatible_argument] -./constructors_call_init.py:42:7: Incompatible argument type for x: expected ./constructors_call_init.py.Class3 | None but got ./constructors_call_init.py.Class2 [incompatible_argument] -./constructors_call_init.py:56:0: Missing required positional argument '%self' [incompatible_call] -./constructors_call_init.py:107:17: Class-scoped type variables are not allowed in __init__ self annotation [invalid_self_usage] -./constructors_call_init.py:130:0: Takes 0 positional arguments but 1 were given [incompatible_call] +./constructors_call_init.py:22:12: Incompatible argument type for x: expected int but got Literal[1.0] [incompatible_argument] +./constructors_call_init.py:43:7: Incompatible argument type for x: expected ./constructors_call_init.py.Class3 | None but got ./constructors_call_init.py.Class2 [incompatible_argument] +./constructors_call_init.py:57:0: Missing required positional argument '%self' [incompatible_call] +./constructors_call_init.py:108:17: Class-scoped type variables are not allowed in __init__ self annotation [invalid_self_usage] +./constructors_call_init.py:129:12: ./constructors_call_init.py.Class13~[Any[generic_argument], Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class13[int, str] +./constructors_call_init.py:136:12: ./constructors_call_init.py.Class14~[Any[generic_argument], Any[generic_argument], Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class14[Any[explicit], str, int] +./constructors_call_init.py:187:12: ./constructors_call_init.py.Class20~[Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class20[int] +./constructors_call_init.py:188:12: ./constructors_call_init.py.Class20~[Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class20[int] +./constructors_call_init.py:189:12: ./constructors_call_init.py.Class20~[Any[generic_argument]] is not equivalent to ./constructors_call_init.py.Class20[int | None] +./constructors_call_init.py:211:0: Takes 0 positional arguments but 1 were given [incompatible_call] """ diff --git a/conformance/results/pycroscope/constructors_callable.toml b/conformance/results/pycroscope/constructors_callable.toml index c9dbd4a39..58c72ec7d 100644 --- a/conformance/results/pycroscope/constructors_callable.toml +++ b/conformance/results/pycroscope/constructors_callable.toml @@ -1,5 +1,10 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not solve a subclass's type parameters from the self annotation of an inherited `__init__` method when converting class to callable. +""" +conformance_automated = "Fail" errors_diff = """ +Line 221: Unexpected errors ['./constructors_callable.py:221:12: ./constructors_callable.py.Class11[Any[generic_argument], Any[generic_argument]] is not equivalent to ./constructors_callable.py.Class11[int, str]'] """ output = """ ./constructors_callable.py:36:12: Revealed type is '(x: int) -> ./constructors_callable.py.Class1' [reveal_type] @@ -26,4 +31,6 @@ output = """ ./constructors_callable.py:196:12: Revealed type is '(x: list[~T@./constructors_callable.py.Class9.__init__..__init__], y: list[~T@./constructors_callable.py.Class9.__init__..__init__]) -> ./constructors_callable.py.Class9' [reveal_type] ./constructors_callable.py:197:9: Incompatible argument type for y: expected list[~T@./constructors_callable.py.Class9.__init__..__init__] but got Literal['not a list'] [incompatible_argument] ./constructors_callable.py:199:0: Cannot resolve type variables [incompatible_call] +./constructors_callable.py:220:12: Revealed type is '() -> ./constructors_callable.py.Class11[~T1@./constructors_callable.py.Class11, ~T2@./constructors_callable.py.Class11]' [reveal_type] +./constructors_callable.py:221:12: ./constructors_callable.py.Class11[Any[generic_argument], Any[generic_argument]] is not equivalent to ./constructors_callable.py.Class11[int, str] """ diff --git a/conformance/results/pyrefly/constructors_call_init.toml b/conformance/results/pyrefly/constructors_call_init.toml index 05593e0a0..fca07cf10 100644 --- a/conformance/results/pyrefly/constructors_call_init.toml +++ b/conformance/results/pyrefly/constructors_call_init.toml @@ -1,11 +1,17 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not report errors during binding to the self parameter of an inherited `__init__` method when the class is specialized via the `__new__` method return type. +""" +conformance_automated = "Fail" errors_diff = """ +Line 167: Expected 1 errors """ output = """ -ERROR constructors_call_init.py:21:13-16: Argument `float` is not assignable to parameter `x` with type `int` in function `Class1.__init__` [bad-argument-type] -ERROR constructors_call_init.py:42:8-20: Argument `Class2[@_]` is not assignable to parameter `x` with type `Class3 | None` in function `Class2.__init__` [bad-argument-type] -ERROR constructors_call_init.py:56:12-14: Argument `Class4[str]` is not assignable to parameter `self` with type `Class4[int]` in function `Class4.__init__` [bad-argument-type] -ERROR constructors_call_init.py:107:9-17: `__init__` method self type cannot reference class type parameters `T2`, `T1` [invalid-annotation] -ERROR constructors_call_init.py:130:9-10: Expected 0 positional arguments, got 1 in function `object.__init__` [bad-argument-count] +ERROR constructors_call_init.py:22:13-16: Argument `float` is not assignable to parameter `x` with type `int` in function `Class1.__init__` [bad-argument-type] +ERROR constructors_call_init.py:43:8-20: Argument `Class2[@_]` is not assignable to parameter `x` with type `Class3 | None` in function `Class2.__init__` [bad-argument-type] +ERROR constructors_call_init.py:57:12-14: Argument `Class4[str]` is not assignable to parameter `self` with type `Class4[int]` in function `Class4.__init__` [bad-argument-type] +ERROR constructors_call_init.py:108:9-17: `__init__` method self type cannot reference class type parameters `T2`, `T1` [invalid-annotation] +ERROR constructors_call_init.py:147:8-10: Argument `Class15[@_]` is not assignable to parameter `self` with type `Class12[int, str]` in function `Class12.__init__` [bad-argument-type] +ERROR constructors_call_init.py:159:9-10: Argument `Literal[1]` is not assignable to parameter `value` with type `str` in function `Class16.__init__` [bad-argument-type] +ERROR constructors_call_init.py:211:9-10: Expected 0 positional arguments, got 1 in function `object.__init__` [bad-argument-count] """ diff --git a/conformance/results/pyrefly/constructors_callable.toml b/conformance/results/pyrefly/constructors_callable.toml index e89288ee3..86615c2d4 100644 --- a/conformance/results/pyrefly/constructors_callable.toml +++ b/conformance/results/pyrefly/constructors_callable.toml @@ -1,6 +1,10 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Returns the base class from the self annotation of an inherited `__init__` method when converting class to callable. +""" +conformance_automated = "Fail" errors_diff = """ +Line 221: Unexpected errors ['assert_type(Class10[int, str], Class11[int, str]) failed [assert-type]'] """ output = """ ERROR constructors_callable.py:38:3-5: Missing argument `x` [missing-argument] @@ -20,4 +24,5 @@ ERROR constructors_callable.py:185:10-22: Argument `Literal['not a list']` is no ERROR constructors_callable.py:187:9-13: Argument `list[str]` is not assignable to parameter `y` with type `list[int]` [bad-argument-type] ERROR constructors_callable.py:197:10-22: Argument `Literal['not a list']` is not assignable to parameter `y` with type `list[str]` [bad-argument-type] ERROR constructors_callable.py:199:9-13: Argument `list[str]` is not assignable to parameter `y` with type `list[int]` [bad-argument-type] +ERROR constructors_callable.py:221:12-38: assert_type(Class10[int, str], Class11[int, str]) failed [assert-type] """ diff --git a/conformance/results/pyright/constructors_call_init.toml b/conformance/results/pyright/constructors_call_init.toml index 4d6672588..7f9fc261d 100644 --- a/conformance/results/pyright/constructors_call_init.toml +++ b/conformance/results/pyright/constructors_call_init.toml @@ -1,18 +1,35 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not solve a subclass's type parameters from the self annotation of an inherited `__init__` method. +Does not report an error when a subclass's specialization of a base class is not assignable to the self annotation of an inherited `__init__` method. +""" +conformance_automated = "Fail" errors_diff = """ +Line 147: Expected 1 errors +Line 129: Unexpected errors ['constructors_call_init.py:129:13 - error: "assert_type" mismatch: expected "Class13[int, str]" but received "Class13[Unknown, Unknown]" (reportAssertTypeFailure)'] +Line 136: Unexpected errors ['constructors_call_init.py:136:13 - error: "assert_type" mismatch: expected "Class14[Any, str, int]" but received "Class14[Unknown, Unknown, Unknown]" (reportAssertTypeFailure)'] +Line 189: Unexpected errors ['constructors_call_init.py:189:13 - error: "assert_type" mismatch: expected "Class20[int | None]" but received "Class20[int]" (reportAssertTypeFailure)'] """ output = """ -constructors_call_init.py:21:13 - error: Argument of type "float" cannot be assigned to parameter "x" of type "int" in function "__init__" +constructors_call_init.py:22:13 - error: Argument of type "float" cannot be assigned to parameter "x" of type "int" in function "__init__"   "float" is not assignable to "int" (reportArgumentType) -constructors_call_init.py:42:8 - error: Argument of type "Class2[Unknown]" cannot be assigned to parameter "x" of type "Class3 | None" in function "__init__" +constructors_call_init.py:43:8 - error: Argument of type "Class2[Unknown]" cannot be assigned to parameter "x" of type "Class3 | None" in function "__init__"   Type "Class2[Unknown]" is not assignable to type "Class3 | None"     "Class2[Unknown]" is not assignable to "Class3"     "Class2[Unknown]" is not assignable to "None" (reportArgumentType) -constructors_call_init.py:56:1 - error: +constructors_call_init.py:57:1 - error:   Could not bind method "__init__" because "Class4[str]" is not assignable to parameter "self"     "Class4[str]" is not assignable to "Class4[int]"       Type parameter "T@Class4" is invariant, but "str" is not the same as "int" (reportGeneralTypeIssues) -constructors_call_init.py:107:24 - warning: Type annotation for "self" parameter of "__init__" method cannot contain class-scoped type variables (reportInvalidTypeVarUse) -constructors_call_init.py:130:9 - error: Expected 0 positional arguments (reportCallIssue) +constructors_call_init.py:108:24 - warning: Type annotation for "self" parameter of "__init__" method cannot contain class-scoped type variables (reportInvalidTypeVarUse) +constructors_call_init.py:129:13 - error: "assert_type" mismatch: expected "Class13[int, str]" but received "Class13[Unknown, Unknown]" (reportAssertTypeFailure) +constructors_call_init.py:136:13 - error: "assert_type" mismatch: expected "Class14[Any, str, int]" but received "Class14[Unknown, Unknown, Unknown]" (reportAssertTypeFailure) +constructors_call_init.py:159:9 - error: Argument of type "Literal[1]" cannot be assigned to parameter "value" of type "str" in function "__init__" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +constructors_call_init.py:167:1 - error: +  Could not bind method "__init__" because "Class18[str, str]" is not assignable to parameter "self" +    "Class18[str, str]" is not assignable to "Class12[int, str]" +      Type parameter "T1@Class12" is invariant, but "str" is not the same as "int" (reportGeneralTypeIssues) +constructors_call_init.py:189:13 - error: "assert_type" mismatch: expected "Class20[int | None]" but received "Class20[int]" (reportAssertTypeFailure) +constructors_call_init.py:211:9 - error: Expected 0 positional arguments (reportCallIssue) """ diff --git a/conformance/results/pyright/constructors_callable.toml b/conformance/results/pyright/constructors_callable.toml index 901fe77b0..422234efa 100644 --- a/conformance/results/pyright/constructors_callable.toml +++ b/conformance/results/pyright/constructors_callable.toml @@ -1,5 +1,10 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Returns the base class from the self annotation of an inherited `__init__` method when converting class to callable. +""" +conformance_automated = "Fail" errors_diff = """ +Line 221: Unexpected errors ['constructors_callable.py:221:13 - error: "assert_type" mismatch: expected "Class11[int, str]" but received "Class10[int, str]" (reportAssertTypeFailure)'] """ output = """ constructors_callable.py:36:13 - information: Type of "r1" is "(x: int) -> Class1" @@ -33,4 +38,6 @@ constructors_callable.py:197:10 - error: Argument of type "Literal['not a list']   "Literal['not a list']" is not assignable to "list[str]" (reportArgumentType) constructors_callable.py:199:10 - error: Argument of type "list[str]" cannot be assigned to parameter "y" of type "list[T@__init__]"   "Literal['']" is not assignable to "int" (reportArgumentType) +constructors_callable.py:220:13 - information: Type of "r11" is "() -> Class10[int, str]" +constructors_callable.py:221:13 - error: "assert_type" mismatch: expected "Class11[int, str]" but received "Class10[int, str]" (reportAssertTypeFailure) """ diff --git a/conformance/results/results.html b/conformance/results/results.html index 2525a3b81..c1902a67f 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -1503,18 +1503,42 @@

Python Type System Conformance Test Results

  • Does not report errors during binding to self parameter of __init__ method.
  • Does not reject use of class-scoped type variables in annotation of self parameter in __init__ method.
  • +
  • Does not solve a subclass's type parameters from the self annotation of an inherited __init__ method.
  • +
+ + + Partial +
    +
  • Does not solve a subclass's type parameters from the self annotation of an inherited __init__ method.
  • +
  • Does not report errors during binding to the self parameter of an inherited __init__ method.
  • +
+ + + Partial +
    +
  • Does not report errors during binding to the self parameter of an inherited __init__ method when the class is specialized via the __new__ method return type.
  • +
+ + + Partial +
    +
  • Does not solve a subclass's type parameters from the self annotation of an inherited __init__ method.
  • +
  • Does not report an error when a subclass's specialization of a base class is not assignable to the self annotation of an inherited __init__ method.
- Pass - Pass - Pass Partial
  • Does not reject invalid argument types to an inherited constructor in a specialized subclass of a generic superclass.
  • +
  • Does not report errors during binding to the self parameter of an inherited __init__ method when the class is specialized via the __new__ method return type.
  • +
+ + + Partial +
    +
  • Does not report errors during binding to the self parameter of an inherited __init__ method when the class is specialized via the __new__ method return type.
- Pass constructors_call_metaclass @@ -1569,20 +1593,42 @@

Python Type System Conformance Test Results

  • Does not generate a union type for __new__ and __init__ when converting class to callable.
  • Does not ignore __init__ based on __new__ return type when converting class to callable.
  • Does not support __new__ return type that is different from class being constructed.
  • +
  • Does not solve a subclass's type parameters from the self annotation of an inherited __init__ method when converting class to callable.
  • + + + + Partial +
      +
    • Does not solve a subclass's type parameters from the self annotation of an inherited __init__ method when converting class to callable.
    • +
    + + + Partial +
      +
    • Returns the base class from the self annotation of an inherited __init__ method when converting class to callable.
    • +
    + + + Partial +
      +
    • Returns the base class from the self annotation of an inherited __init__ method when converting class to callable.
    - Pass - Pass - Pass Partial
    • Does not include __init__ when __new__ returns Self.
    • Does not ignore __init__ when __new__ returns Any.
    • Unions overload return types.
    • +
    • Returns the base class from the self annotation of an inherited __init__ method when converting class to callable.
    • +
    + + + Partial +
      +
    • Returns the base class from the self annotation of an inherited __init__ method when converting class to callable.
    - Pass constructors_consistency @@ -1601,11 +1647,11 @@

    Python Type System Conformance Test Results

    3 / 6 • 50.0% - 6 / 6 • 100.0% - 6 / 6 • 100.0% - 6 / 6 • 100.0% 5 / 6 • 83.3% - 6 / 6 • 100.0% + 5 / 6 • 83.3% + 5 / 6 • 83.3% + 5 / 6 • 83.3% + 5 / 6 • 83.3% @@ -2511,11 +2557,11 @@

    Python Type System Conformance Test Results

    110.5 / 146 • 75.7% - 139 / 146 • 95.2% - 142.5 / 146 • 97.6% - 140.5 / 146 • 96.2% + 138 / 146 • 94.5% + 141.5 / 146 • 96.9% + 139.5 / 146 • 95.5% 141.5 / 146 • 96.9% - 146 / 146 • 100.0% + 145 / 146 • 99.3% diff --git a/conformance/results/ty/constructors_call_init.toml b/conformance/results/ty/constructors_call_init.toml index c6b0eb40a..1fc1dd20d 100644 --- a/conformance/results/ty/constructors_call_init.toml +++ b/conformance/results/ty/constructors_call_init.toml @@ -2,14 +2,18 @@ conformance_automated = "Fail" conformant = "Partial" notes = """ Does not reject invalid argument types to an inherited constructor in a specialized subclass of a generic superclass. +Does not report errors during binding to the self parameter of an inherited `__init__` method when the class is specialized via the `__new__` method return type. """ errors_diff = """ -Line 42: Expected 1 errors +Line 43: Expected 1 errors +Line 167: Expected 1 errors """ output = """ -constructors_call_init.py:21:13: error[invalid-argument-type] Argument to `Class1.__init__` is incorrect: Expected `int`, found `float*` -constructors_call_init.py:56:1: error[invalid-argument-type] Argument to `Class4.__init__` is incorrect: Expected `Class4[int]`, found `Class4[str]` -constructors_call_init.py:107:32: error[invalid-init-type-variable] First parameter of `__init__` cannot use type variable `T2` from an outer scope -constructors_call_init.py:107:36: error[invalid-init-type-variable] First parameter of `__init__` cannot use type variable `T1` from an outer scope -constructors_call_init.py:130:9: error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2 +constructors_call_init.py:22:13: error[invalid-argument-type] Argument to `Class1.__init__` is incorrect: Expected `int`, found `float*` +constructors_call_init.py:57:1: error[invalid-argument-type] Argument to `Class4.__init__` is incorrect: Expected `Class4[int]`, found `Class4[str]` +constructors_call_init.py:108:32: error[invalid-init-type-variable] First parameter of `__init__` cannot use type variable `T2` from an outer scope +constructors_call_init.py:108:36: error[invalid-init-type-variable] First parameter of `__init__` cannot use type variable `T1` from an outer scope +constructors_call_init.py:147:1: error[invalid-argument-type] Argument to `Class12.__init__` is incorrect: Expected `Class12[int, str]`, found `Class15[str]` +constructors_call_init.py:159:9: error[invalid-argument-type] Argument to `Class16.__init__` is incorrect: Expected `str`, found `Literal[1]` +constructors_call_init.py:211:9: error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2 """ diff --git a/conformance/results/ty/constructors_callable.toml b/conformance/results/ty/constructors_callable.toml index 7f22bcaae..c3e7d204a 100644 --- a/conformance/results/ty/constructors_callable.toml +++ b/conformance/results/ty/constructors_callable.toml @@ -4,6 +4,7 @@ notes = """ Does not include `__init__` when `__new__` returns `Self`. Does not ignore `__init__` when `__new__` returns `Any`. Unions overload return types. +Returns the base class from the self annotation of an inherited `__init__` method when converting class to callable. """ errors_diff = """ Line 66: Expected 1 errors @@ -13,6 +14,7 @@ Line 143: Unexpected errors ["constructors_callable.py:143:27: error[invalid-arg Line 145: Unexpected errors ['constructors_callable.py:145:1: error[type-assertion-failure] Type `Any | Class6Any` does not match asserted type `Any`'] Line 166: Unexpected errors ['constructors_callable.py:166:1: error[type-assertion-failure] Type `Class7[int] | Class7[str]` does not match asserted type `Class7[int]`'] Line 167: Unexpected errors ['constructors_callable.py:167:1: error[type-assertion-failure] Type `Class7[int] | Class7[str]` does not match asserted type `Class7[str]`'] +Line 221: Unexpected errors ['constructors_callable.py:221:1: error[type-assertion-failure] Type `Class10[int, str]` does not match asserted type `Class11[int, str]`'] """ output = """ constructors_callable.py:36:13: info[revealed-type] Revealed type: `(x: int) -> Class1` @@ -40,4 +42,6 @@ constructors_callable.py:184:13: info[revealed-type] Revealed type: `[T](x: list constructors_callable.py:185:10: error[invalid-argument-type] Argument is incorrect: Expected `list[str]`, found `Literal["not a list"]` constructors_callable.py:196:13: info[revealed-type] Revealed type: `[T](x: list[T], y: list[T]) -> Class9` constructors_callable.py:197:10: error[invalid-argument-type] Argument is incorrect: Expected `list[str]`, found `Literal["not a list"]` +constructors_callable.py:220:13: info[revealed-type] Revealed type: `[T1, T2]() -> Class10[int, str]` +constructors_callable.py:221:1: error[type-assertion-failure] Type `Class10[int, str]` does not match asserted type `Class11[int, str]` """ diff --git a/conformance/results/zuban/constructors_call_init.toml b/conformance/results/zuban/constructors_call_init.toml index 6192d7a0d..90d0f26fe 100644 --- a/conformance/results/zuban/constructors_call_init.toml +++ b/conformance/results/zuban/constructors_call_init.toml @@ -1,11 +1,18 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not report errors during binding to the self parameter of an inherited `__init__` method when the class is specialized via the `__new__` method return type. +""" +conformance_automated = "Fail" errors_diff = """ +Line 167: Expected 1 errors """ output = """ -constructors_call_init.py:21: error: Argument 1 to "Class1" has incompatible type "float"; expected "int" [arg-type] -constructors_call_init.py:42: error: Argument 1 to "Class3" has incompatible type "Class2[int]"; expected "Self | None" [arg-type] -constructors_call_init.py:56: error: Invalid self type in __init__ [call-arg] -constructors_call_init.py:107: error: The type of self "T1" has type vars in non standard positions for class "Class8" [misc] -constructors_call_init.py:107: error: The type of self "T2" has type vars in non standard positions for class "Class8" [misc] -constructors_call_init.py:130: error: Too many arguments for "Class11" [call-arg] +constructors_call_init.py:22: error: Argument 1 to "Class1" has incompatible type "float"; expected "int" [arg-type] +constructors_call_init.py:43: error: Argument 1 to "Class3" has incompatible type "Class2[int]"; expected "Self | None" [arg-type] +constructors_call_init.py:57: error: Invalid self type in __init__ [call-arg] +constructors_call_init.py:108: error: The type of self "T1" has type vars in non standard positions for class "Class8" [misc] +constructors_call_init.py:108: error: The type of self "T2" has type vars in non standard positions for class "Class8" [misc] +constructors_call_init.py:147: error: Invalid self type in __init__ [call-arg] +constructors_call_init.py:159: error: Argument 1 to "Class17" has incompatible type "int"; expected "str" [arg-type] +constructors_call_init.py:211: error: Too many arguments for "Class11" [call-arg] """ diff --git a/conformance/results/zuban/constructors_callable.toml b/conformance/results/zuban/constructors_callable.toml index 569f40b9a..79afc2297 100644 --- a/conformance/results/zuban/constructors_callable.toml +++ b/conformance/results/zuban/constructors_callable.toml @@ -1,5 +1,10 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Returns the base class from the self annotation of an inherited `__init__` method when converting class to callable. +""" +conformance_automated = "Fail" errors_diff = """ +Line 221: Unexpected errors ['constructors_callable.py:221: error: Expression is of type "Class10[int, str]", not "Class11[int, str]" [misc]'] """ output = """ constructors_callable.py:36: note: Revealed type is "def (x: builtins.int) -> tests.constructors_callable.Class1" @@ -26,4 +31,6 @@ constructors_callable.py:187: error: List item 0 has incompatible type "str"; ex constructors_callable.py:196: note: Revealed type is "def [T] (x: builtins.list[T], y: builtins.list[T]) -> tests.constructors_callable.Class9" constructors_callable.py:197: error: Argument 2 has incompatible type "str"; expected "list[str]" [arg-type] constructors_callable.py:199: error: List item 0 has incompatible type "str"; expected "int" [list-item] +constructors_callable.py:220: note: Revealed type is "def () -> tests.constructors_callable.Class10[builtins.int, builtins.str]" +constructors_callable.py:221: error: Expression is of type "Class10[int, str]", not "Class11[int, str]" [misc] """ diff --git a/conformance/tests/constructors_call_init.py b/conformance/tests/constructors_call_init.py index c3749634a..dcbe2bf69 100644 --- a/conformance/tests/constructors_call_init.py +++ b/conformance/tests/constructors_call_init.py @@ -5,7 +5,8 @@ # Specification: https://typing.readthedocs.io/en/latest/spec/constructors.html#init-method -from typing import Any, Generic, Self, TypeVar, assert_type, overload +from typing import Any, Generic, Literal, Self, assert_type, overload +from typing_extensions import TypeVar T = TypeVar("T") @@ -108,6 +109,86 @@ def __init__(self: "Class8[T2, T1]") -> None: # E pass +# > When the class being constructed inherits an __init__ method whose self +# > annotation names a specialization of a base class, a type checker should +# > bind the class's specialization of that base class to the annotation. +# > Type parameters of the class that are not specialized explicitly or via +# > the __new__ method return type should be solved from the annotation. + +T3 = TypeVar("T3") + + +class Class12(Generic[T1, T2]): + def __init__(self: "Class12[int, str]") -> None: ... + + +class Class13(Class12[T1, T2]): + pass + + +assert_type(Class13(), Class13[int, str]) + + +class Class14(Class12[T3, T2], Generic[T1, T2, T3]): + pass + + +assert_type(Class14(), Class14[Any, str, int]) + + +# > If the resulting specialization is not assignable to the annotation, +# > binding fails. + + +class Class15(Class12[list[T], T]): + pass + + +Class15() # E + + +class Class16(Generic[T]): + def __init__(self: "Class16[V1]", value: V1) -> None: ... + + +class Class17(Class16[str]): + pass + + +Class17("") # OK +Class17(1) # E + + +class Class18(Class12[T1, T2]): + def __new__(cls) -> "Class18[str, str]": + raise NotImplementedError + + +Class18() # E + + +# Only the selected __init__ overload solves the subclass's type parameters. + +T4 = TypeVar("T4", default=int) + + +class Class19(Generic[T]): + @overload + def __init__(self, *, null: Literal[False] = ...) -> None: ... + @overload + def __init__(self: "Class19[int | None]", *, null: Literal[True]) -> None: ... + def __init__(self, *, null: bool = False) -> None: ... + + +class Class20(Class19[T4]): + pass + + +assert_type(Class20(), Class20[int]) +assert_type(Class20(null=False), Class20[int]) +assert_type(Class20(null=True), Class20[int | None]) + + # > If a class does not define a __new__ method or __init__ method and does # > not inherit either of these methods from a base class other than object, # > a type checker should evaluate the argument list using the __new__ and diff --git a/conformance/tests/constructors_callable.py b/conformance/tests/constructors_callable.py index 0ec091e2d..99f32f331 100644 --- a/conformance/tests/constructors_callable.py +++ b/conformance/tests/constructors_callable.py @@ -197,3 +197,25 @@ def __init__(self, x: list[T], y: list[T]) -> None: r9([""], "not a list") # E: no assignment of T makes the second argument valid assert_type(r9([""], [""]), Class9) r9([1], [""]) # E?: T = int | str is a potential solution + + +# > If the __init__ method is inherited and its self annotation names a +# > specialization of a base class, the return type is the class being +# > constructed, specialized with the type arguments solved from that +# > annotation, not the base class. + +T1 = TypeVar("T1") +T2 = TypeVar("T2") + + +class Class10(Generic[T1, T2]): + def __init__(self: "Class10[int, str]") -> None: ... + + +class Class11(Class10[T1, T2]): + pass + + +r11 = accepts_callable(Class11) +reveal_type(r11) # `def () -> Class11[int, str]` +assert_type(r11(), Class11[int, str]) diff --git a/docs/spec/constructors.rst b/docs/spec/constructors.rst index c95ecc488..980382405 100644 --- a/docs/spec/constructors.rst +++ b/docs/spec/constructors.rst @@ -273,6 +273,35 @@ within a type annotation for the ``self`` parameter in an ``__init__`` method. # The ``self`` annotation should result in a type error def __init__(self: "MyClass4[T2, T1]") -> None: ... +When the class being constructed inherits an ``__init__`` method whose +``self`` annotation names a specialization of a base class, a type checker +should bind the class's specialization of that base class to the annotation. +Type parameters of the class that are not specialized explicitly or via the +``__new__`` method return type should be solved from the annotation. If the +resulting specialization is not assignable to the annotation, binding fails. + + :: + + class MyClass5[T1, T2]: + def __init__(self: "MyClass5[int, str]") -> None: ... + + class MyClass6[T1, T2](MyClass5[T1, T2]): ... + + assert_type(MyClass6(), MyClass6[int, str]) + + class MyClass7[T1, T2, T3](MyClass5[T3, T2]): ... + + assert_type(MyClass7(), MyClass7[Any, str, int]) + + class MyClass8[T](MyClass5[list[T], T]): ... + + MyClass8() # Type error + + class MyClass9[T1, T2](MyClass5[T1, T2]): + def __new__(cls) -> "MyClass9[str, str]": ... + + MyClass9() # Type error + Classes Without ``__new__`` and ``__init__`` Methods ---------------------------------------------------- @@ -284,11 +313,11 @@ does not inherit either of these methods from a base class other than :: - class MyClass5: + class MyClass10: pass - MyClass5() # OK - MyClass5(1) # Type error + MyClass10() # OK + MyClass10(1) # Type error Constructor Calls for type[T] @@ -400,7 +429,10 @@ constructor calls: from a base class other than ``object``, a callable type should be synthesized from the parameters of the ``__init__`` method after it is bound to the class instance resulting from step 2. The return type of this synthesized callable - should be the concrete value of ``Self``. + should be the concrete value of ``Self``. If the ``__init__`` method is + inherited and its ``self`` annotation names a specialization of a base + class, the return type is the class being constructed, specialized with the + type arguments solved from that annotation, not the base class. 5. If step 2 and 4 both produce no result because the class does not define or inherit a ``__new__`` or ``__init__`` method from a class other than ``object``,