From d202d1e9ae7a0b77ba054ac98e9305a0e473a5cf Mon Sep 17 00:00:00 2001 From: A5rocks Date: Sun, 26 Dec 2021 13:18:34 +0900 Subject: [PATCH] Tests for literals --- mypy/typeanal.py | 34 +++++-------- .../unit/check-parameter-specification.test | 51 +++++++++++++++++++ 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 2953e6a83822..23498e6957a3 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -423,18 +423,15 @@ def analyze_type_with_type_info( if len(args) > 0 and info.fullname == 'builtins.tuple': fallback = Instance(info, [AnyType(TypeOfAny.special_form)], ctx.line) return TupleType(self.anal_array(args), fallback, ctx.line) - # Only allow ParamSpec literals if there's a ParamSpec arg type: - # This might not be necessary. - allow_param_spec_literal = any(isinstance(tvar, ParamSpecType) for tvar in info.defn.type_vars) # Analyze arguments and (usually) construct Instance type. The # number of type arguments and their values are # checked only later, since we do not always know the # valid count at this point. Thus we may construct an # Instance with an invalid number of type arguments. - instance = Instance(info, self.anal_array(args, allow_param_spec=True, - allow_param_spec_literal=allow_param_spec_literal), + instance = Instance(info, self.anal_array(args, allow_param_spec=True), ctx.line, ctx.column) + # Check type argument count. if len(instance.args) != len(info.type_vars) and not self.defining_alias: fix_instance(instance, self.fail, self.note, @@ -566,9 +563,15 @@ def visit_deleted_type(self, t: DeletedType) -> Type: return t def visit_type_list(self, t: TypeList) -> Type: - self.fail('Bracketed expression "[...]" is not valid as a type', t) - self.note('Did you mean "List[...]"?', t) - return AnyType(TypeOfAny.from_error) + # paramspec literal (Z[[int, str, Whatever]]) + # TODO: invalid usage restrictions + params = self.analyze_callable_args(t) + if params: + ts, kinds, names = params + # bind these types + return Parameters(self.anal_array(ts), kinds, names) + else: + return AnyType(TypeOfAny.from_error) def visit_callable_argument(self, t: CallableArgument) -> Type: self.fail('Invalid type', t) @@ -1040,21 +1043,10 @@ def is_defined_type_var(self, tvar: str, context: Context) -> bool: def anal_array(self, a: Iterable[Type], nested: bool = True, *, - allow_param_spec: bool = False, - allow_param_spec_literal: bool = False) -> List[Type]: + allow_param_spec: bool = False) -> List[Type]: res: List[Type] = [] for t in a: - if allow_param_spec_literal and isinstance(t, TypeList): - # paramspec literal (Z[[int, str, Whatever]]) - params = self.analyze_callable_args(t) - if params: - ts, kinds, names = params - # bind these types - res.append(Parameters(self.anal_array(ts), kinds, names)) - else: - res.append(AnyType(TypeOfAny.from_error)) - else: - res.append(self.anal_type(t, nested, allow_param_spec=allow_param_spec)) + res.append(self.anal_type(t, nested, allow_param_spec=allow_param_spec)) return res def anal_type(self, t: Type, nested: bool = True, *, allow_param_spec: bool = False) -> Type: diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index f6123915aada..fb6dd4e7728f 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -406,3 +406,54 @@ with f() as x: pass [builtins fixtures/dict.pyi] [typing fixtures/typing-full.pyi] + +[case testParamSpecLiterals] +from typing_extensions import ParamSpec, TypeAlias +from typing import Generic, TypeVar + +P = ParamSpec("P") +T = TypeVar("T") + +class Z(Generic[P]): ... + +# literals can be applied +n: Z[[int]] + +# type aliases too +nt1 = Z[[int]] +nt2: TypeAlias = Z[[int]] + +unt1: nt1 +unt2: nt2 + +# literals actually keep types +reveal_type(n) # N: Revealed type is "__main__.Z[[builtins.int]]" +reveal_type(unt1) # N: Revealed type is "__main__.Z[[builtins.int]]" +reveal_type(unt2) # N: Revealed type is "__main__.Z[[builtins.int]]" + +# passing into a function keeps the type +def fT(a: T) -> T: ... +def fP(a: Z[P]) -> Z[P]: ... + +reveal_type(fT(n)) # N: Revealed type is "__main__.Z*[[builtins.int]]" +reveal_type(fP(n)) # N: Revealed type is "__main__.Z[[builtins.int]]" + +# literals can be in function args and return type +def k(a: Z[[int]]) -> Z[[str]]: ... + +# functions work +reveal_type(k(n)) # N: Revealed type is "__main__.Z[[builtins.str]]" + +# literals can be matched in arguments +def kb(a: Z[[bytes]]) -> Z[[str]]: ... + +# TODO: return type is a bit weird, return Any +reveal_type(kb(n)) # N: Revealed type is "__main__.Z[[builtins.str]]" \ + # E: Argument 1 to "kb" has incompatible type "Z[[int]]"; expected "Z[[bytes]]" + + +# TODO(PEP612): fancy "aesthetic" syntax defined in PEP +# n2: Z[bytes] +# +# reveal_type(kb(n2)) # N: Revealed type is "__main__.Z[[builtins.str]]" +[builtins fixtures/tuple.pyi]