Skip to content

Commit d9d82c9

Browse files
committed
don't state that a parameter specification is always invariant
1 parent 7f8f6bb commit d9d82c9

File tree

7 files changed

+120
-14
lines changed

7 files changed

+120
-14
lines changed

conformance/results/mypy/generics_paramspec_semantics.toml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
conformant = "Pass"
1+
conformant = "Partial"
22
output = """
33
generics_paramspec_semantics.py:26: error: Unexpected keyword argument "a" [call-arg]
44
generics_paramspec_semantics.py:26: error: Unexpected keyword argument "b" [call-arg]
@@ -11,7 +11,23 @@ generics_paramspec_semantics.py:127: error: Argument 1 to "expects_int_first" ha
1111
generics_paramspec_semantics.py:127: note: This is likely because "one" has named arguments: "x". Consider marking them positional-only
1212
generics_paramspec_semantics.py:132: error: Argument 1 to "expects_int_first" has incompatible type "def two(*, x: int) -> int"; expected "def (int, /, *, x: int) -> int" [arg-type]
1313
generics_paramspec_semantics.py:137: error: Argument 1 to "expects_int_first" has incompatible type "def three(**kwargs: int) -> int"; expected "def (int, /, **kwargs: int) -> int" [arg-type]
14+
generics_paramspec_semantics.py:151: error: Incompatible types in assignment (expression has type "ContravariantParamSpec[[int]]", variable has type "ContravariantParamSpec[[object]]") [assignment]
15+
generics_paramspec_semantics.py:155: error: Missing return statement [empty-body]
16+
generics_paramspec_semantics.py:158: error: Incompatible types in assignment (expression has type "CovariantParamSpec[[int]]", variable has type "CovariantParamSpec[[object]]") [assignment]
17+
generics_paramspec_semantics.py:161: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]
18+
generics_paramspec_semantics.py:164: error: Missing return statement [empty-body]
19+
generics_paramspec_semantics.py:168: error: Incompatible types in assignment (expression has type "ContravariantParamSpecOld[[int]]", variable has type "ContravariantParamSpecOld[[object]]") [assignment]
20+
generics_paramspec_semantics.py:170: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]
21+
generics_paramspec_semantics.py:176: error: Incompatible types in assignment (expression has type "ContravariantParamSpecOld[[int]]", variable has type "ContravariantParamSpecOld[[object]]") [assignment]
1422
"""
15-
conformance_automated = "Pass"
23+
conformance_automated = "Fail"
1624
errors_diff = """
25+
Line 159: Expected 1 errors
26+
Line 173: Expected 1 errors
27+
Line 177: Expected 1 errors
28+
Line 155: Unexpected errors ['generics_paramspec_semantics.py:155: error: Missing return statement [empty-body]']
29+
Line 158: Unexpected errors ['generics_paramspec_semantics.py:158: error: Incompatible types in assignment (expression has type "CovariantParamSpec[[int]]", variable has type "CovariantParamSpec[[object]]") [assignment]']
30+
Line 161: Unexpected errors ['generics_paramspec_semantics.py:161: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]']
31+
Line 170: Unexpected errors ['generics_paramspec_semantics.py:170: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]']
32+
Line 176: Unexpected errors ['generics_paramspec_semantics.py:176: error: Incompatible types in assignment (expression has type "ContravariantParamSpecOld[[int]]", variable has type "ContravariantParamSpecOld[[object]]") [assignment]']
1733
"""

conformance/results/pyrefly/generics_paramspec_semantics.toml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
1-
conformant = "Pass"
2-
conformance_automated = "Pass"
1+
conformant = "Partial"
2+
conformance_automated = "Fail"
33
errors_diff = """
4+
Line 151: Expected 1 errors
5+
Line 159: Expected 1 errors
6+
Line 164: Expected 1 errors
7+
Line 168: Expected 1 errors
8+
Line 173: Expected 1 errors
9+
Line 177: Expected 1 errors
10+
Line 150: Unexpected errors ['`ContravariantParamSpec[[object]]` is not assignable to `ContravariantParamSpec[[int]]` [bad-assignment]']
11+
Line 161: Unexpected errors ['Unexpected keyword argument `contravariant` to ParamSpec [invalid-param-spec]']
12+
Line 167: Unexpected errors ['`ContravariantParamSpecOld[[object]]` is not assignable to `ContravariantParamSpecOld[[int]]` [bad-assignment]']
13+
Line 170: Unexpected errors ['Unexpected keyword argument `covariant` to ParamSpec [invalid-param-spec]']
14+
Line 176: Unexpected errors ['`ContravariantParamSpecOld[[int]]` is not assignable to `ContravariantParamSpecOld[[object]]` [bad-assignment]']
415
"""
516
output = """
617
ERROR generics_paramspec_semantics.py:26:4-5: Expected argument `a` to be positional [unexpected-keyword]
@@ -14,4 +25,9 @@ ERROR generics_paramspec_semantics.py:120:4-5: Argument `Literal[1]` is not assi
1425
ERROR generics_paramspec_semantics.py:127:1-19: Argument `(x: str) -> int` is not assignable to parameter `x` with type `(int, ParamSpec(@_)) -> int` in function `expects_int_first` [bad-argument-type]
1526
ERROR generics_paramspec_semantics.py:132:1-19: Argument `(*, x: int) -> int` is not assignable to parameter `x` with type `(int, ParamSpec(@_)) -> int` in function `expects_int_first` [bad-argument-type]
1627
ERROR generics_paramspec_semantics.py:137:1-19: Argument `(**kwargs: int) -> int` is not assignable to parameter `x` with type `(int, ParamSpec(@_)) -> int` in function `expects_int_first` [bad-argument-type]
28+
ERROR generics_paramspec_semantics.py:150:39-45: `ContravariantParamSpec[[object]]` is not assignable to `ContravariantParamSpec[[int]]` [bad-assignment]
29+
ERROR generics_paramspec_semantics.py:161:24-42: Unexpected keyword argument `contravariant` to ParamSpec [invalid-param-spec]
30+
ERROR generics_paramspec_semantics.py:167:46-56: `ContravariantParamSpecOld[[object]]` is not assignable to `ContravariantParamSpecOld[[int]]` [bad-assignment]
31+
ERROR generics_paramspec_semantics.py:170:26-40: Unexpected keyword argument `covariant` to ParamSpec [invalid-param-spec]
32+
ERROR generics_paramspec_semantics.py:176:50-61: `ContravariantParamSpecOld[[int]]` is not assignable to `ContravariantParamSpecOld[[object]]` [bad-assignment]
1733
"""

conformance/results/pyright/generics_paramspec_semantics.toml

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
conformant = "Pass"
1+
conformant = "Partial"
22
notes = """
33
Constraint solver doesn't find common type for two signatures captured by a single ParamSpec (allowed).
44
"""
@@ -30,7 +30,33 @@ generics_paramspec_semantics.py:132:2 - error: Argument of type "(*, x: int) ->
3030
generics_paramspec_semantics.py:137:2 - error: Argument of type "(**kwargs: int) -> int" cannot be assigned to parameter "x" of type "(int, **P@expects_int_first) -> int" in function "expects_int_first"
3131
  Type "(**kwargs: int) -> int" is not assignable to type "(int, **P@expects_int_first) -> int"
3232
    Function accepts too many positional parameters; expected 0 but received 1 (reportArgumentType)
33+
generics_paramspec_semantics.py:150:39 - error: Type "ContravariantParamSpec[(object)]" is not assignable to declared type "ContravariantParamSpec[(int)]"
34+
  "ContravariantParamSpec[(object)]" is not assignable to "ContravariantParamSpec[(int)]"
35+
    Type parameter "InP@ContravariantParamSpec" is invariant, but "(object)" is not the same as "(int)" (reportAssignmentType)
36+
generics_paramspec_semantics.py:158:39 - error: Type "CovariantParamSpec[(int)]" is not assignable to declared type "CovariantParamSpec[(object)]"
37+
  "CovariantParamSpec[(int)]" is not assignable to "CovariantParamSpec[(object)]"
38+
    Type parameter "OutP@CovariantParamSpec" is invariant, but "(int)" is not the same as "(object)" (reportAssignmentType)
39+
generics_paramspec_semantics.py:161:24 - error: "contravariant" is unknown parameter to ParamSpec (reportGeneralTypeIssues)
40+
generics_paramspec_semantics.py:167:46 - error: Type "ContravariantParamSpecOld[(object)]" is not assignable to declared type "ContravariantParamSpecOld[(int)]"
41+
  "ContravariantParamSpecOld[(object)]" is not assignable to "ContravariantParamSpecOld[(int)]"
42+
    Type parameter "InP@ContravariantParamSpecOld" is invariant, but "(object)" is not the same as "(int)" (reportAssignmentType)
43+
generics_paramspec_semantics.py:170:26 - error: "covariant" is unknown parameter to ParamSpec (reportGeneralTypeIssues)
44+
generics_paramspec_semantics.py:176:50 - error: Type "ContravariantParamSpecOld[(int)]" is not assignable to declared type "ContravariantParamSpecOld[(object)]"
45+
  "ContravariantParamSpecOld[(int)]" is not assignable to "ContravariantParamSpecOld[(object)]"
46+
    Type parameter "InP@ContravariantParamSpecOld" is invariant, but "(int)" is not the same as "(object)" (reportAssignmentType)
3347
"""
34-
conformance_automated = "Pass"
48+
conformance_automated = "Fail"
3549
errors_diff = """
50+
Line 151: Expected 1 errors
51+
Line 159: Expected 1 errors
52+
Line 164: Expected 1 errors
53+
Line 168: Expected 1 errors
54+
Line 173: Expected 1 errors
55+
Line 177: Expected 1 errors
56+
Line 150: Unexpected errors ['generics_paramspec_semantics.py:150:39 - error: Type "ContravariantParamSpec[(object)]" is not assignable to declared type "ContravariantParamSpec[(int)]"']
57+
Line 158: Unexpected errors ['generics_paramspec_semantics.py:158:39 - error: Type "CovariantParamSpec[(int)]" is not assignable to declared type "CovariantParamSpec[(object)]"']
58+
Line 161: Unexpected errors ['generics_paramspec_semantics.py:161:24 - error: "contravariant" is unknown parameter to ParamSpec (reportGeneralTypeIssues)']
59+
Line 167: Unexpected errors ['generics_paramspec_semantics.py:167:46 - error: Type "ContravariantParamSpecOld[(object)]" is not assignable to declared type "ContravariantParamSpecOld[(int)]"']
60+
Line 170: Unexpected errors ['generics_paramspec_semantics.py:170:26 - error: "covariant" is unknown parameter to ParamSpec (reportGeneralTypeIssues)']
61+
Line 176: Unexpected errors ['generics_paramspec_semantics.py:176:50 - error: Type "ContravariantParamSpecOld[(int)]" is not assignable to declared type "ContravariantParamSpecOld[(object)]"']
3662
"""

conformance/results/results.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,10 @@ <h3>Python Type System Conformance Test Results</h3>
293293
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Does not reject usage of args/kwargs for out-of-scope ParamSpec</p></span></div></th>
294294
</tr>
295295
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;generics_paramspec_semantics</th>
296-
<th class="column col2 conformant">Pass</th>
297-
<th class="column col2 conformant"><div class="hover-text">Pass*<span class="tooltip-text" id="bottom"><p>Constraint solver doesn't find common type for two signatures captured by a single ParamSpec (allowed).</p></span></div></th>
298-
<th class="column col2 conformant">Pass</th>
299-
<th class="column col2 conformant">Pass</th>
296+
<th class="column col2 partially-conformant">Partial</th>
297+
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Constraint solver doesn't find common type for two signatures captured by a single ParamSpec (allowed).</p></span></div></th>
298+
<th class="column col2 not-conformant">Unknown</th>
299+
<th class="column col2 partially-conformant">Partial</th>
300300
</tr>
301301
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;generics_paramspec_specialization</th>
302302
<th class="column col2 conformant">Pass</th>

conformance/results/zuban/generics_paramspec_semantics.toml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
conformance_automated = "Pass"
1+
conformance_automated = "Fail"
22
errors_diff = """
3+
Line 151: Expected 1 errors
4+
Line 159: Expected 1 errors
5+
Line 168: Expected 1 errors
6+
Line 173: Expected 1 errors
7+
Line 177: Expected 1 errors
8+
Line 155: Unexpected errors ['generics_paramspec_semantics.py:155: error: Missing return statement [empty-body]']
9+
Line 158: Unexpected errors ['generics_paramspec_semantics.py:158: error: Incompatible types in assignment (expression has type "CovariantParamSpec[[int]]", variable has type "CovariantParamSpec[[object]]") [assignment]']
10+
Line 161: Unexpected errors ['generics_paramspec_semantics.py:161: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]']
11+
Line 170: Unexpected errors ['generics_paramspec_semantics.py:170: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]']
12+
Line 176: Unexpected errors ['generics_paramspec_semantics.py:176: error: Incompatible types in assignment (expression has type "ContravariantParamSpecOld[[int]]", variable has type "ContravariantParamSpecOld[[object]]") [assignment]']
313
"""
414
output = """
515
generics_paramspec_semantics.py:26: error: Unexpected keyword argument "a" [call-arg]
@@ -12,4 +22,10 @@ generics_paramspec_semantics.py:120: error: Argument 1 has incompatible type "in
1222
generics_paramspec_semantics.py:127: error: Argument 1 to "expects_int_first" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]" [arg-type]
1323
generics_paramspec_semantics.py:132: error: Argument 1 to "expects_int_first" has incompatible type "Callable[[NamedArg(int, 'x')], int]"; expected "Callable[[int, Never], int]" [arg-type]
1424
generics_paramspec_semantics.py:137: error: Argument 1 to "expects_int_first" has incompatible type "Callable[[KwArg(int)], int]"; expected "Callable[[int, Never], int]" [arg-type]
25+
generics_paramspec_semantics.py:155: error: Missing return statement [empty-body]
26+
generics_paramspec_semantics.py:158: error: Incompatible types in assignment (expression has type "CovariantParamSpec[[int]]", variable has type "CovariantParamSpec[[object]]") [assignment]
27+
generics_paramspec_semantics.py:161: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]
28+
generics_paramspec_semantics.py:164: error: Missing return statement [empty-body]
29+
generics_paramspec_semantics.py:170: error: The variance and bound arguments to ParamSpec do not have defined semantics yet [misc]
30+
generics_paramspec_semantics.py:176: error: Incompatible types in assignment (expression has type "ContravariantParamSpecOld[[int]]", variable has type "ContravariantParamSpecOld[[object]]") [assignment]
1531
"""

conformance/tests/generics_paramspec_semantics.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,36 @@ def three(**kwargs: int) -> int:
142142
@expects_int_first # OK
143143
def four(*args: int) -> int:
144144
raise NotImplementedError
145+
146+
class ContravariantParamSpec[**InP]:
147+
def f(self, *args: InP.args, **kwargs: InP.kwargs): ...
148+
149+
in_obj = ContravariantParamSpec[object]()
150+
in_int: ContravariantParamSpec[int] = in_obj # OK
151+
in_obj = in_int # E
152+
153+
154+
class CovariantParamSpec[**OutP]:
155+
def f(self) -> Callable[OutP, None]: ...
156+
157+
out_int = CovariantParamSpec[int]()
158+
out_obj: CovariantParamSpec[object] = out_int # OK
159+
out_int = out_obj # E
160+
161+
InP = ParamSpec("InP", contravariant=True)
162+
163+
class ContravariantParamSpecOld(Generic[InP]):
164+
def f(self) -> Callable[InP, None]: ... # E
165+
166+
in_obj_old = ContravariantParamSpecOld[object]()
167+
in_int_old: ContravariantParamSpecOld[int] = in_obj_old # OK
168+
in_obj_old = in_int_old # E
169+
170+
OutP = ParamSpec("OutP", covariant=True)
171+
172+
class CovariantParamSpecOld(Generic[OutP]):
173+
def f(self, *args: OutP.args, **kwargs: OutP.kwargs): ... # E
174+
175+
out_int_old = ContravariantParamSpecOld[int]()
176+
out_obj_old: ContravariantParamSpecOld[object] = out_int_old # OK
177+
out_int_old = out_obj_old # E

docs/spec/generics.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2729,9 +2729,8 @@ The algorithm for computing the variance of a type parameter is as follows.
27292729

27302730
For each type parameter in a generic class:
27312731

2732-
1. If the type parameter is variadic (``TypeVarTuple``) or a parameter
2733-
specification (``ParamSpec``), it is always considered invariant. No further
2734-
inference is needed.
2732+
1. If the type parameter is variadic (``TypeVarTuple``) it is always
2733+
considered invariant. No further inference is needed.
27352734

27362735
2. If the type parameter comes from a traditional ``TypeVar`` declaration and
27372736
is not specified as ``infer_variance`` (see below), its variance is specified

0 commit comments

Comments
 (0)