diff --git a/pep-0696.rst b/pep-0696.rst index 938aa06ec..564449904 100644 --- a/pep-0696.rst +++ b/pep-0696.rst @@ -119,10 +119,14 @@ should flag this an error. class OneDefault(Generic[T, DefaultBoolT]): ... OneDefault[float] == OneDefault[float, bool] # Valid + reveal_type(OneDefault) # type is type[OneDefault[T, DefaultBoolT = bool]] + reveal_type(OneDefault[float]()) # type is OneDefault[float, bool] class AllTheDefaults(Generic[T1, T2, DefaultStrT, DefaultIntT, DefaultBoolT]): ... + reveal_type(AllTheDefaults) # type is type[AllTheDefaults[T1, T2, DefaultStrT = str, DefaultIntT = int, DefaultBoolT = bool]] + reveal_type(AllTheDefaults[int, complex]()) # type is AllTheDefaults[int, complex, str, int, bool] AllTheDefaults[int] # Invalid: expected 2 arguments to AllTheDefaults ( AllTheDefaults[int, complex] == @@ -140,7 +144,7 @@ future, this might be possible (see `Interaction with PEP ``ParamSpec`` defaults are defined using the same syntax as ``TypeVar`` \ s but use a ``list`` or ``tuple`` of types or an ellipsis -literal "``...``" or another in-scope ``ParamSpec`` (see :ref:`scoping-rules`). +literal "``...``" or another in-scope ``ParamSpec`` (see :ref:`696-scoping-rules`). .. code-block:: py @@ -148,6 +152,7 @@ literal "``...``" or another in-scope ``ParamSpec`` (see :ref:`scoping-rules`). class Foo(Generic[DefaultP]): ... + reveal_type(Foo) # type is type[Foo[DefaultP = (str, int)]] reveal_type(Foo()) # type is Foo[(str, int)] reveal_type(Foo[(bool, bool)]()) # type is Foo[(bool, bool)] @@ -156,7 +161,7 @@ literal "``...``" or another in-scope ``ParamSpec`` (see :ref:`scoping-rules`). ``TypeVarTuple`` defaults are defined using the same syntax as ``TypeVar`` \ s but use an unpacked tuple of types instead of a single type -or another in-scope ``TypeVarTuple`` (see :ref:`scoping-rules`). +or another in-scope ``TypeVarTuple`` (see :ref:`696-scoping-rules`). .. code-block:: py @@ -164,6 +169,7 @@ or another in-scope ``TypeVarTuple`` (see :ref:`scoping-rules`). class Foo(Generic[*DefaultTs]): ... + reveal_type(Foo) # type is type[Foo[DefaultTs = *tuple[str, int]]] reveal_type(Foo()) # type is Foo[str, int] reveal_type(Foo[int, bool]()) # type is Foo[int, bool] @@ -190,38 +196,56 @@ default to the type of ``start`` and step default to ``int | None``. class slice(Generic[StartT, StopT, StepT]): ... + reveal_type(slice) # type is type[slice[StartT = int, StopT = StartT, StepT = int | None]] reveal_type(slice()) # type is slice[int, int, int | None] reveal_type(slice[str]()) # type is slice[str, str, int | None] reveal_type(slice[str, bool, timedelta]()) # type is slice[str, bool, timedelta] + T2 = TypeVar("T2", default=DefaultStrT) + + class Foo(Generic[DefaultStrT, T2]): + def __init__(self, a: DefaultStrT, b: T2) -> None: ... + + reveal_type(Foo(1, "")) # type is Foo[int, str] + Foo[int](1, "") # Invalid: Foo[int, str] cannot be assigned to self: Foo[int, int] in Foo.__init__ + Foo[int]("", 1) # Invalid: Foo[str, int] cannot be assigned to self: Foo[int, int] in Foo.__init__ + When using a ``TypeVarLike`` as the default to another ``TypeVarLike``. Where ``T1`` is the default for ``T2`` the following rules apply. -.. _scoping-rules: +``TypeVarTuple``\s are not supported because: + +- :ref:`696-scoping-rules` does not allow usage of ``TypeVarLikes`` + from outer scopes. +- Multiple ``TypeVarTuple``\s cannot appear in the type + parameter list for a single class, as specified in + :pep:`646#multiple-type-variable-tuples-not-allowed`. +- ``TypeVarLike`` defaults in functions are not supported. + +These reasons leave no current valid location where a +``TypeVarTuple`` could have a default. + +.. _696-scoping-rules: Scoping Rules ~~~~~~~~~~~~~ -``T1`` must be used before ``T2`` in the parameter list of the generic, -or be bound in an outer class or function scope. +``T1`` must be used before ``T2`` in the parameter list of the generic. .. code-block:: py DefaultT = TypeVar("DefaultT", default=T) class Foo(Generic[T, DefaultT]): ... # Valid - def bar(x: T, y: DefaultT): ... # Valid class Foo(Generic[T]): class Bar(Generic[DefaultT]): ... # Valid - def outer(x: T): - def inner(y: DefaultT): ... # Valid StartT = TypeVar("StartT", default="StopT") # Swapped defaults around from previous example StopT = TypeVar("StopT", default=int) class slice(Generic[StartT, StopT, StepT]): ... # ^^^^^^ Invalid: ordering does not allow StopT to be bound - def baz(x: DefaultT, y: T): ... - # ^^^^^^^^ Invalid: ordering does not allow DefaultT to be bound + +Using a ``TypeVarLike`` from an outer scope as a default is not supported. Bound Rules ~~~~~~~~~~~ @@ -255,7 +279,7 @@ The constraints of ``T2`` must be a superset of the constraints of ``T1``. ``TypeVarLike``\ s are valid as parameters to generics inside of a ``default`` when the first parameter is in scope as determined by the -:ref:`previous section `. +:ref:`previous section <696-scoping-rules>`. .. code-block:: py @@ -265,9 +289,11 @@ The constraints of ``T2`` must be a superset of the constraints of ``T1``. class Bar(Generic[T, ListDefaultT]): def __init__(self, x: T, y: ListDefaultT): ... - reveal_type(Bar[int]) # type is Bar[int, list[int]] - reveal_type(Bar[int, list[str]]) # type is Bar[int, list[str]] - reveal_type(Bar[int, str]) # type is Bar[int, str] + reveal_type(Bar) # type is type[Bar[T, ListDefaultT = list[T]]] + reveal_type(Bar[int]) # type is type[Bar[int, list[int]]] + reveal_type(Bar[int]()) # type is Bar[int, list[int]] + reveal_type(Bar[int, list[str]]()) # type is Bar[int, list[str]] + reveal_type(Bar[int, str]()) # type is Bar[int, str] Specialisation Rules ~~~~~~~~~~~~~~~~~~~~ @@ -291,7 +317,7 @@ further down the line. class SomethingWithNoDefaults(Generic[T, T2]): ... MyAlias: TypeAlias = SomethingWithNoDefaults[int, DefaultStrT] # Valid - reveal_type(MyAlias()) # type is SomethingWithNoDefaults[int, str] + reveal_type(MyAlias) # type is type[SomethingWithNoDefaults[int, DefaultStrT]] reveal_type(MyAlias[bool]()) # type is SomethingWithNoDefaults[int, bool] MyAlias[bool, int] # Invalid: too many arguments passed to MyAlias @@ -308,6 +334,7 @@ behave similarly to ``Generic`` ``TypeAlias``\ es. x: DefaultStrT class Bar(SubclassMe[int, DefaultStrT]): ... + reveal_type(Bar) # type is type[Bar[DefaultStrT = str]] reveal_type(Bar()) # type is Bar[str] reveal_type(Bar[bool]()) # type is Bar[bool] @@ -346,6 +373,8 @@ subtype of one of the constraints. TypeVar("Ok", float, str, default=float) # Valid TypeVar("Invalid", float, str, default=int) # Invalid: expected one of float or str got int +.. _696-function-defaults: + Function Defaults ''''''''''''''''' @@ -373,6 +402,8 @@ The following changes would be required to both ``GenericAlias``\ es: A reference implementation of the type checker can be found at https://github.com/Gobot1234/mypy/tree/TypeVar-defaults +Pyright currently supports this functionality. + Interaction with PEP 695 ------------------------ @@ -385,15 +416,12 @@ using the "=" operator inside of the square brackets like so: # TypeVars class Foo[T = str]: ... - def bar[U = int](): ... # ParamSpecs class Baz[**P = (int, str)]: ... - def spam[**Q = (bool,)](): ... # TypeVarTuples class Qux[*Ts = *tuple[int, bool]]: ... - def ham[*Us = *tuple[str]](): ... # TypeAliases type Foo[T, U = str] = Bar[T, U] @@ -507,6 +535,19 @@ convenient, could have a ``TypeVarLike`` with no default follow a This would have also been a breaking change for a small number of cases where the code relied on ``Any`` being the implicit default. +Allowing ``TypeVarLike``\s with defaults to be used in function signatures +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +A previous version of this PEP allowed ``TypeVarLike``\s with defaults to be used in +function signatures. This was removed for the reasons described in +:ref:`696-function-defaults`. Hopefully, this can be added in the future if +a way to get the runtime value of a type parameter is added. + +Allowing ``TypeVarLikes`` from outer scopes in ``default`` +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +This was deemed too niche a feature to be worth the added complexity. +If any cases arise where this is needed, it can be added in a future PEP. Acknowledgements ----------------