From 575f772950ecffffc49144f80727bf5b1b796bfc Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 2 May 2023 16:53:06 +0200 Subject: [PATCH] Require ListExpr for ParamSpec defaults --- mypy/exprtotype.py | 6 +--- mypy/semanal.py | 40 +++++++++++++--------- mypy/typeanal.py | 7 ++-- mypy/types.py | 28 ++------------- test-data/unit/check-typevar-defaults.test | 19 +++++----- test-data/unit/semanal-errors.test | 2 +- 6 files changed, 40 insertions(+), 62 deletions(-) diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index ca5adb6d186ae..bbc284a5188a6 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -33,7 +33,6 @@ Type, TypeList, TypeOfAny, - TypeOfTypeList, UnboundType, UnionType, ) @@ -162,12 +161,9 @@ def expr_to_unanalyzed_type( else: raise TypeTranslationError() return CallableArgument(typ, name, arg_const, expr.line, expr.column) - elif isinstance(expr, (ListExpr, TupleExpr)): + elif isinstance(expr, ListExpr): return TypeList( [expr_to_unanalyzed_type(t, options, allow_new_syntax, expr) for t in expr.items], - TypeOfTypeList.callable_args - if isinstance(expr, ListExpr) - else TypeOfTypeList.param_spec_defaults, line=expr.line, column=expr.column, ) diff --git a/mypy/semanal.py b/mypy/semanal.py index 6ff22bf28f1ea..9e43f0c0c7c5b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4114,9 +4114,7 @@ def process_typevar_parameters( tv_arg = self.get_typevarlike_argument( "TypeVar", param_name, param_value, context, allow_unbound_tvars=True ) - if tv_arg is None: - return None - default = tv_arg + default = tv_arg or AnyType(TypeOfAny.from_error) elif param_name == "values": # Probably using obsolete syntax with values=(...). Explain the current syntax. self.fail('TypeVar "values" argument not supported', context) @@ -4153,6 +4151,7 @@ def get_typevarlike_argument( *, allow_unbound_tvars: bool = False, allow_param_spec_literals: bool = False, + report_invalid_typevar_arg: bool = True, ) -> ProperType | None: try: # We want to use our custom error message below, so we suppress @@ -4173,7 +4172,7 @@ def get_typevarlike_argument( # ... analyzed = PlaceholderType(None, [], context.line) typ = get_proper_type(analyzed) - if isinstance(typ, AnyType) and typ.is_from_error: + if report_invalid_typevar_arg and isinstance(typ, AnyType) and typ.is_from_error: self.fail( message_registry.TYPEVAR_ARG_MUST_BE_TYPE.format(typevarlike_name, param_name), param_value, @@ -4182,10 +4181,11 @@ def get_typevarlike_argument( # using the AnyType as the upper bound. return typ except TypeTranslationError: - self.fail( - message_registry.TYPEVAR_ARG_MUST_BE_TYPE.format(typevarlike_name, param_name), - param_value, - ) + if report_invalid_typevar_arg: + self.fail( + message_registry.TYPEVAR_ARG_MUST_BE_TYPE.format(typevarlike_name, param_name), + param_value, + ) return None def extract_typevarlike_name(self, s: AssignmentStmt, call: CallExpr) -> str | None: @@ -4236,10 +4236,9 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: s, allow_unbound_tvars=True, allow_param_spec_literals=True, + report_invalid_typevar_arg=False, ) - if tv_arg is None: - return False - default = tv_arg + default = tv_arg or AnyType(TypeOfAny.from_error) if isinstance(tv_arg, Parameters): for i, arg_type in enumerate(tv_arg.arg_types): typ = get_proper_type(arg_type) @@ -4247,9 +4246,13 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: self.fail( f"Argument {i} of ParamSpec default must be a type", param_value ) - elif not isinstance(default, (AnyType, UnboundType)): + elif ( + isinstance(default, AnyType) + and default.is_from_error + or not isinstance(default, (AnyType, UnboundType)) + ): self.fail( - "The default argument to ParamSpec must be a tuple expression, ellipsis, or a ParamSpec", + "The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec", param_value, ) default = AnyType(TypeOfAny.from_error) @@ -4306,11 +4309,14 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool: ): if param_name == "default": tv_arg = self.get_typevarlike_argument( - "TypeVarTuple", param_name, param_value, s, allow_unbound_tvars=True + "TypeVarTuple", + param_name, + param_value, + s, + allow_unbound_tvars=True, + report_invalid_typevar_arg=False, ) - if tv_arg is None: - return False - default = tv_arg + default = tv_arg or AnyType(TypeOfAny.from_error) if not isinstance(default, UnpackType): self.fail( "The default argument to TypeVarTuple must be an Unpacked tuple", diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 1813a1b09d45c..40c0d8719cf2a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -72,7 +72,6 @@ TypedDictType, TypeList, TypeOfAny, - TypeOfTypeList, TypeQuery, TypeType, TypeVarLikeType, @@ -897,12 +896,10 @@ def visit_type_list(self, t: TypeList) -> Type: else: return AnyType(TypeOfAny.from_error) else: - s = "[...]" if t.list_type == TypeOfTypeList.callable_args else "(...)" self.fail( - f'Bracketed expression "{s}" is not valid as a type', t, code=codes.VALID_TYPE + 'Bracketed expression "[...]" is not valid as a type', t, code=codes.VALID_TYPE ) - if t.list_type == TypeOfTypeList.callable_args: - self.note('Did you mean "List[...]"?', t) + self.note('Did you mean "List[...]"?', t) return AnyType(TypeOfAny.from_error) def visit_callable_argument(self, t: CallableArgument) -> Type: diff --git a/mypy/types.py b/mypy/types.py index f2884ece70fc7..b4b9240483ee6 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -197,17 +197,6 @@ class TypeOfAny: suggestion_engine: Final = 9 -class TypeOfTypeList: - """This class describes the different types of TypeList.""" - - __slots__ = () - - # List expressions for callable args - callable_args: Final = 1 - # Tuple expressions for ParamSpec defaults - param_spec_defaults: Final = 2 - - def deserialize_type(data: JsonDict | str) -> Type: if isinstance(data, str): return Instance.deserialize(data) @@ -1005,20 +994,13 @@ class TypeList(ProperType): types before they are processed into Callable types. """ - __slots__ = ("items", "list_type") + __slots__ = ("items",) items: list[Type] - def __init__( - self, - items: list[Type], - list_type: int = TypeOfTypeList.callable_args, - line: int = -1, - column: int = -1, - ) -> None: + def __init__(self, items: list[Type], line: int = -1, column: int = -1) -> None: super().__init__(line, column) self.items = items - self.list_type = list_type def accept(self, visitor: TypeVisitor[T]) -> T: assert isinstance(visitor, SyntheticTypeVisitor) @@ -1032,11 +1014,7 @@ def __hash__(self) -> int: return hash(tuple(self.items)) def __eq__(self, other: object) -> bool: - return ( - isinstance(other, TypeList) - and self.items == other.items - and self.list_type == other.list_type - ) + return isinstance(other, TypeList) and self.items == other.items class UnpackType(ProperType): diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 3a7e55920de8c..7bc2d4089ecd9 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -4,7 +4,7 @@ from typing import Generic, TypeVar, ParamSpec, Callable, Tuple, List from typing_extensions import TypeVarTuple, Unpack T1 = TypeVar("T1", default=int) -P1 = ParamSpec("P1", default=(int, str)) +P1 = ParamSpec("P1", default=[int, str]) Ts1 = TypeVarTuple("Ts1", default=Unpack[Tuple[int, str]]) def f1(a: T1) -> List[T1]: ... @@ -44,9 +44,9 @@ T5 = TypeVar("T5", default=S0) T6 = TypeVar("T6", bound=float, default=S1) # T7 = TypeVar("T7", bound=List[Any], default=List[S0]) # TODO -P1 = ParamSpec("P1", default=()) +P1 = ParamSpec("P1", default=[]) P2 = ParamSpec("P2", default=...) -P3 = ParamSpec("P3", default=(int, str)) +P3 = ParamSpec("P3", default=[int, str]) P4 = ParamSpec("P4", default=P0) Ts1 = TypeVarTuple("Ts1", default=Unpack[Tuple[int]]) @@ -59,15 +59,16 @@ from typing import TypeVar, ParamSpec, Tuple from typing_extensions import TypeVarTuple, Unpack T1 = TypeVar("T1", default=2) # E: TypeVar "default" must be a type -T2 = TypeVar("T2", default=(int, str)) # E: Bracketed expression "(...)" is not valid as a type \ +T2 = TypeVar("T2", default=[int, str]) # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ # E: TypeVar "default" must be a type -P1 = ParamSpec("P1", default=int) # E: The default argument to ParamSpec must be a tuple expression, ellipsis, or a ParamSpec -P2 = ParamSpec("P2", default=2) # E: ParamSpec "default" must be a type -P3 = ParamSpec("P3", default=(2, int)) # E: Argument 0 of ParamSpec default must be a type +P1 = ParamSpec("P1", default=int) # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +P2 = ParamSpec("P2", default=2) # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +P3 = ParamSpec("P3", default=(2, int)) # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +P4 = ParamSpec("P4", default=[2, int]) # E: Argument 0 of ParamSpec default must be a type -Ts1 = TypeVarTuple("Ts1", default=2) # E: TypeVarTuple "default" must be a type \ - # E: The default argument to TypeVarTuple must be an Unpacked tuple +Ts1 = TypeVarTuple("Ts1", default=2) # E: The default argument to TypeVarTuple must be an Unpacked tuple Ts2 = TypeVarTuple("Ts2", default=int) # E: The default argument to TypeVarTuple must be an Unpacked tuple Ts3 = TypeVarTuple("Ts3", default=Tuple[int]) # E: The default argument to TypeVarTuple must be an Unpacked tuple [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index f4ac9ae4cd149..66a990cce2fda 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1045,7 +1045,7 @@ c = TypeVar(1) # E: TypeVar() expects a string literal as first argument T = TypeVar(b'T') # E: TypeVar() expects a string literal as first argument d = TypeVar('D') # E: String argument 1 "D" to TypeVar(...) does not match variable name "d" e = TypeVar('e', int, str, x=1) # E: Unexpected argument to "TypeVar()": "x" -f = TypeVar('f', (int, str), int) # E: Bracketed expression "(...)" is not valid as a type +f = TypeVar('f', (int, str), int) # E: Type expected g = TypeVar('g', int) # E: TypeVar cannot have only a single constraint h = TypeVar('h', x=(int, str)) # E: Unexpected argument to "TypeVar()": "x" i = TypeVar('i', bound=1) # E: TypeVar "bound" must be a type