From bc2b83d5954c1b1d78112baac0cb2ce0a1ec56b2 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Tue, 16 May 2023 22:23:46 +0900 Subject: [PATCH 1/3] ParamSpecs should have an upper bound that is just their parameters. There is some part of my PR that fixes the joining, but I will have to check again. I also need to find some things that are fixed by this. --- mypy/semanal.py | 8 ++++++-- mypy/types.py | 16 ++++++++++++++-- .../unit/check-parameter-specification.test | 6 +++++- test-data/unit/semanal-types.test | 3 ++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 70bd876af46e..872ff77a5f1c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4183,9 +4183,13 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: # on the lines of process_typevar_parameters if not call.analyzed: - paramspec_var = ParamSpecExpr( - name, self.qualified_name(name), self.object_type(), INVARIANT + parameters = Parameters( + arg_types=[self.object_type(), self.object_type()], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=[None, None], + is_ellipsis_args=True, ) + paramspec_var = ParamSpecExpr(name, self.qualified_name(name), parameters, INVARIANT) paramspec_var.line = call.line call.analyzed = paramspec_var else: diff --git a/mypy/types.py b/mypy/types.py index f23800234600..a25f89db2f87 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -704,13 +704,14 @@ def copy_modified( id: Bogus[TypeVarId | int] = _dummy, flavor: int = _dummy_int, prefix: Bogus[Parameters] = _dummy, + upper_bound: Bogus[Type] = _dummy, ) -> ParamSpecType: return ParamSpecType( self.name, self.fullname, id if id is not _dummy else self.id, flavor if flavor != _dummy_int else self.flavor, - self.upper_bound, + upper_bound if upper_bound is not _dummy else self.upper_bound, line=self.line, column=self.column, prefix=prefix if prefix is not _dummy else self.prefix, @@ -1991,7 +1992,18 @@ def param_spec(self) -> ParamSpecType | None: # TODO: confirm that all arg kinds are positional prefix = Parameters(self.arg_types[:-2], self.arg_kinds[:-2], self.arg_names[:-2]) - return arg_type.copy_modified(flavor=ParamSpecFlavor.BARE, prefix=prefix) + # TODO: should this take in `object`s? Or use prefix + *Any **Any as upper bound? + any_type = AnyType(TypeOfAny.special_form) + return arg_type.copy_modified( + flavor=ParamSpecFlavor.BARE, + prefix=prefix, + upper_bound=Parameters( + arg_types=[any_type, any_type], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=[None, None], + is_ellipsis_args=True, + ), + ) def expand_param_spec( self, c: CallableType | Parameters, no_prefix: bool = False diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index fe66b18fbfea..7ea268c7a1a6 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -310,7 +310,11 @@ class C(Generic[P, P2]): def m3(self, c: C[P, P3]) -> None: reveal_type(join(c, c)) # N: Revealed type is "__main__.C[P`1, P3`-1]" - reveal_type(join(self, c)) # N: Revealed type is "builtins.object" + # TODO: undo this join change + reveal_type(join(self, c)) # E: Value of type variable "T" of "join" cannot be "[VarArg(object), KwArg(object)]" \ + # N: Revealed type is "..." \ + # E: Argument 1 to "join" has incompatible type "C[P, P2]"; expected "[VarArg(object), KwArg(object)]" \ + # E: Argument 2 to "join" has incompatible type "C[P, P3]"; expected "[VarArg(object), KwArg(object)]" [builtins fixtures/dict.pyi] [case testParamSpecClassWithAny] diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 71a5c6dd87b5..d9ef030f034a 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -1549,7 +1549,8 @@ MypyFile:1( ImportFrom:1(typing, [ParamSpec]) AssignmentStmt:2( NameExpr(P* [__main__.P]) - ParamSpecExpr:2())) + ParamSpecExpr:2( + UpperBound(...)))) [case testTypeVarTuple] from typing_extensions import TypeVarTuple From fac047b0f2fb2d7f4759380a96fe33cd5838fe2d Mon Sep 17 00:00:00 2001 From: A5rocks Date: Sat, 20 May 2023 07:17:59 +0900 Subject: [PATCH 2/3] Joins between instances using ParamSpecs should return an object --- mypy/join.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/join.py b/mypy/join.py index 62d256f4440f..ea91916867b9 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -97,7 +97,7 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: else: # ParamSpec type variables behave the same, independent of variance if not is_equivalent(ta, sa): - return get_proper_type(type_var.upper_bound) + return object_from_instance(t) new_type = join_types(ta, sa, self) assert new_type is not None args.append(new_type) From e3bb3ed34d886a5473a13ef3ea3dffe096f810cf Mon Sep 17 00:00:00 2001 From: A5rocks Date: Sat, 20 May 2023 07:22:25 +0900 Subject: [PATCH 3/3] Fix the tests --- test-data/unit/check-parameter-specification.test | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 7ea268c7a1a6..fe66b18fbfea 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -310,11 +310,7 @@ class C(Generic[P, P2]): def m3(self, c: C[P, P3]) -> None: reveal_type(join(c, c)) # N: Revealed type is "__main__.C[P`1, P3`-1]" - # TODO: undo this join change - reveal_type(join(self, c)) # E: Value of type variable "T" of "join" cannot be "[VarArg(object), KwArg(object)]" \ - # N: Revealed type is "..." \ - # E: Argument 1 to "join" has incompatible type "C[P, P2]"; expected "[VarArg(object), KwArg(object)]" \ - # E: Argument 2 to "join" has incompatible type "C[P, P3]"; expected "[VarArg(object), KwArg(object)]" + reveal_type(join(self, c)) # N: Revealed type is "builtins.object" [builtins fixtures/dict.pyi] [case testParamSpecClassWithAny]