Skip to content
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 49 additions & 42 deletions pep-0695.rst
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,20 @@ the existing rules enforced by type checkers for a ``TypeVar`` constructor call.
class ClassG[T: (list[S], str)]: ... # Type checker error: generic type


Runtime Representation of Bounds and Constraints
------------------------------------------------

The upper bounds and constraints of ``TypeVar`` objects are accessible at
runtime through the ``__bound__`` and ``__constraints__`` attributes.
For ``TypeVar`` objects defined through the new syntax, these attributes
become lazily evaluated: when constructed, the object stores a closure that
allows the evaluation of the value. When the ``__bound__`` or ``__constraints__``
attribute is accessed, the value is evaluated and cached. This is consistent
with the behavior of :pep:`649` and generally eliminates the need for quoted
forward references. If :pep:`649` is accepted, the ``TypeVar`` class should
gain a way to evaluate the bound and constraints using each of the formats
proposed by that PEP.


Generic Type Alias
------------------
Expand Down Expand Up @@ -365,13 +379,18 @@ At runtime, a ``type`` statement will generate an instance of
include:

* ``__name__`` is a str representing the name of the type alias
* ``__parameters__`` is a tuple of ``TypeVar``, ``TypeVarTuple``, or
* ``__type_params__`` is a tuple of ``TypeVar``, ``TypeVarTuple``, or
``ParamSpec`` objects that parameterize the type alias if it is generic
* ``__value__`` is the evaluated value of the type alias

The ``__value__`` attribute initially has a value of ``None`` while the type
alias expression is evaluated. It is then updated after a successful evaluation.
This allows for self-referential type aliases.
All of these attributes are read-only.

The value of the type alias is evaluated lazily. Access to the ``__value__`` attribute
triggers evaluation of the value and caches the resulting value.
This allows for self-referential and mutually recursive type aliases, and it
should largely eliminate the need for quoted forward references in type aliases.
If :pep:`649` is accepted, additional functionality should be added to allow evaluating
the value using each of the formats proposed by that PEP.


Type Parameter Scopes
Expand All @@ -382,9 +401,9 @@ includes the type parameters. Type parameters can be accessed by name
within inner scopes. As with other symbols in Python, an inner scope can
define its own symbol that overrides an outer-scope symbol of the same name.

Type parameters declared earlier in a type parameter list are visible to
type parameters declared later in the list. This allows later type parameters
to use earlier type parameters within their definition. While there is currently
Type parameters are visible to other
type parameters declared elsewhere in the list. This allows type parameters
to use other type parameters within their definition. While there is currently
no use for this capability, it preserves the ability in the future to support
upper bound expressions or type argument defaults that depend on earlier
type parameters.
Expand All @@ -401,11 +420,9 @@ defined in an outer scope.
# eliminate this limitation.
class ClassA[S, T: Sequence[S]]: ...

# The following generates a compiler error or runtime exception because T
# is referenced before it is defined. This occurs even though T is defined
# in the outer scope.
T = 0
class ClassB[S: Sequence[T], T]: ... # Compiler error: T is not defined
# The following generates no compiler error, because the bound for ``S``
# is lazily evaluated. However, type checkers should generate an error.
class ClassB[S: Sequence[T], T]: ...


A type parameter declared as part of a generic class is valid within the
Expand Down Expand Up @@ -475,7 +492,7 @@ Type parameter symbols defined in outer scopes cannot be bound with
The lexical scope introduced by the new type parameter syntax is unlike
traditional scopes introduced by a ``def`` or ``class`` statement. A type
parameter scope acts more like a temporary "overlay" to the containing scope.
It does not capture variables from outer scopes, and the only symbols contained
The only new symbols contained
within its symbol table are the type parameters defined using the new syntax.
References to all other symbols are treated as though they were found within
the containing scope. This allows base class lists (in class definitions) and
Expand Down Expand Up @@ -570,11 +587,14 @@ When the new type parameter syntax is used for a generic class, assignment
expressions are not allowed within the argument list for the class definition.
Likewise, with functions that use the new type parameter syntax, assignment
expressions are not allowed within parameter or return type annotations, nor
are they allowed within the expression that defines a type alias.
are they allowed within the expression that defines a type alias, or within
the bounds and constraints of a ``TypeVar``. Similarly, ``yield``, ``yield from``,
and ``await`` expressions are disallowed in these contexts.

This restriction is necessary because expressions evaluated within the
new lexical scope should not introduce symbols within that scope other than
the defined type parameters.
the defined type parameters, and should not affect whether the enclosing function
is a generator or coroutine.

::

Expand All @@ -590,15 +610,10 @@ the defined type parameters.
Accessing Type Parameters at Runtime
------------------------------------

A new read-only attribute called ``__type_variables__`` is available on class,
function, and type alias objects. This attribute is a tuple of the active
type variables that are visible within the scope of that class, function,
or type alias. This attribute is needed for runtime evaluation of stringified
(forward referenced) type annotations that include references to type
parameters. Functions like ``typing.get_type_hints`` can use this attribute
to populate the ``locals`` dictionary with values for type parameters that
are in scope when calling ``eval`` to evaluate the stringified expression.
The tuple contains ``TypeVar`` instances.
A new read-only attribute called ``__type_params__`` is available on generic class,
function, and type alias objects. This attribute is a tuple of the
type parameters that parameterize the class, function, or alias.
The tuple contains ``TypeVar``, ``ParamSpec``, and ``TypeVarTuple`` instances.

Type parameters declared using the new syntax will not appear within the
dictionary returned by ``globals()`` or ``locals()``.
Expand Down Expand Up @@ -809,24 +824,27 @@ It also adds an AST node type that represents a type parameter.
| ParamSpec(identifier name)
| TypeVarTuple(identifier name)

Bounds and constraints are represented identically in the AST. In the implementation,
any expression that is syntactically a tuple is treated as a constraint, and any other
expression is treated as a bound.

It also modifies existing AST node types ``FunctionDef``, ``AsyncFunctionDef``
and ``ClassDef`` to include an additional optional attribute called
``typeparam*`` that includes a list of type parameters associated with the
``typeparams`` that includes a list of type parameters associated with the
function or class.


Library Changes
---------------

Several classes in the ``typing`` module that are currently implemented in
Python must be reimplemented in C. This includes: ``TypeVar``,
``TypeVarTuple``, ``ParamSpec``, ``Generic``, and ``Union``. The new class
``TypeAliasType`` (described above) also must be implemented in C. The
Python must be partially implemented in C. This includes ``TypeVar``,
``TypeVarTuple``, ``ParamSpec``, and ``Generic``, and the new class
``TypeAliasType`` (described above). The implementation may delegate to the
Python version of ``typing.py`` for some behaviors that interact heavily with
the rest of the module. The
documented behaviors of these classes should not change.

The ``typing.get_type_hints`` must be updated to use the new
``__type_variables__`` attribute.


Reference Implementation
========================
Expand Down Expand Up @@ -917,17 +935,6 @@ Furthermore, this approach is not compatible with techniques used for
evaluating quoted (forward referenced) type annotations.


Lambda Lifting
--------------
When considering implementation options, we considered introducing a new
scope and executing the ``class``, ``def``, or ``type`` statement within
a lambda -- a technique that is sometimes referred to as "lambda lifting".
We ultimately rejected this idea because it did not work well for statements
within a class body (because class-scoped symbols cannot be accessed by
inner scopes). It also introduced many odd behaviors for scopes that were
further nested within the lambda.


Appendix A: Survey of Type Parameter Syntax
===========================================

Expand Down