diff --git a/pytype/overlays/typing_overlay.py b/pytype/overlays/typing_overlay.py index e6c43b7a6..4b88dfb34 100644 --- a/pytype/overlays/typing_overlay.py +++ b/pytype/overlays/typing_overlay.py @@ -301,7 +301,16 @@ def _get_typeparam_args(self, node, args): contravariant = self._get_namedarg(node, args, "contravariant", False) if constraints and bound: raise TypeVarError("constraints and a bound are mutually exclusive") - extra_kwargs = set(args.namedargs) - {"bound", "covariant", "contravariant"} + # `default` is unsupported for now - access it just to generate a warning. + # TODO: b/382028836 - actually support the `default` arg to `TypeVar` + self._get_namedarg(node, args, "default", None) + + extra_kwargs = set(args.namedargs) - { + "bound", + "covariant", + "contravariant", + "default", + } if extra_kwargs: raise TypeVarError("extra keyword arguments: " + ", ".join(extra_kwargs)) if args.starargs: @@ -345,7 +354,10 @@ def _get_namedarg(self, node, args, name, default_value): if name == "bound": return self._get_annotation(node, args.namedargs[name], name) else: - ret = self._get_constant(args.namedargs[name], name, bool) + if name == "default": + ret = self._get_annotation(node, args.namedargs[name], name) + else: + ret = self._get_constant(args.namedargs[name], name, bool) # This error is logged only if _get_constant succeeds. self.ctx.errorlog.not_supported_yet( self.ctx.vm.frames, f'argument "{name}" to TypeVar' diff --git a/pytype/stubs/builtins/typing.pytd b/pytype/stubs/builtins/typing.pytd index d0ca51d71..bf6165c24 100644 --- a/pytype/stubs/builtins/typing.pytd +++ b/pytype/stubs/builtins/typing.pytd @@ -35,8 +35,14 @@ Text = str # purposes and to make it easier for pytype to type-check arguments to TypeVar # and ParamSpec. def _typevar_new( - name: str, *constraints: type | str | None, bound: type | str | None = None, - covariant: bool = False, contravariant: bool = False): ... + name: str, + *constraints: type | str | None, + bound: type | str | None = None, + covariant: bool = False, + contravariant: bool = False, + infer_variance: bool = False, + default: Any = None, +): ... def _paramspec_new( name: str, *constraints: type | str | None, bound: type | str | None = None, covariant: bool = False, contravariant: bool = False): ... diff --git a/pytype/tests/test_typevar1.py b/pytype/tests/test_typevar1.py index 8bb778966..a9342c6ba 100644 --- a/pytype/tests/test_typevar1.py +++ b/pytype/tests/test_typevar1.py @@ -208,6 +208,41 @@ def test_contravariant(self): }, ) + def test_default(self): + ty = self.Infer(""" + from typing import Generic, TypeVar + + T = TypeVar("T", default=int) # pytype: disable=not-supported-yet + + class Foo(Generic[T]): + pass + + f = Foo() + """) + self.assertTypesMatchPytd( + ty, + """ + from typing import Generic, TypeVar + + T = TypeVar('T') + + class Foo(Generic[T]): ... + + f: Foo[nothing] + """, + ) + + self.CheckWithErrors(""" + from typing import Generic, TypeVar + + T = TypeVar("T", default=int) # not-supported-yet + + class Foo(Generic[T]): + pass + + f = Foo() + """) + def test_dont_propagate_pyval(self): # in functions like f(x: T) -> T, if T has constraints we should not copy # the value of constant types between instances of the typevar.