Skip to content
Merged
Changes from all 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
51 changes: 39 additions & 12 deletions peps/pep-0747.rst
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,16 @@ be assignable to ``TypeForm``::

v5: TypeForm[set[str]] = "set[str]" # OK

The typing spec defines syntactic rules for type expressions in the form of a
:ref:`formal grammar <typing:expression-grammar>`. Semantic rules are specified
as comments along with the grammar definition. Contextual requirements are detailed
throughout the typing spec in sections that discuss concepts that appear within
type expressions. For example, the special form ``Self`` can be used in a
type expression only within a class, and a type variable can be used within
a type expression only when it is associated with a valid scope.

Expressions that violate one or more of the syntactic, semantic, or contextual
rules for type expressions should not evaluate to a ``TypeForm`` type. The rules
for type expression validity are explained in detail within the typing spec, so
they are not repeated here::
rules for type expressions should not evaluate to a ``TypeForm`` type.::

bad1: TypeForm = tuple() # Error: Call expression not allowed in type expression
bad2: TypeForm = (1, 2) # Error: Tuple expression not allowed in type expression
Expand Down Expand Up @@ -300,14 +306,6 @@ Assignability
t1: TypeForm[int | str] = get_type_form() # OK
t2: TypeForm[str] = get_type_form() # Error

``type[T]`` is a subtype of ``TypeForm[T]``, which means that ``type[B]`` is
assignable to ``TypeForm[A]`` if ``B`` is assignable to ``A``::

def get_type() -> type[int]: ...

t3: TypeForm[int | str] = get_type() # OK
t4: TypeForm[str] = get_type() # Error

``TypeForm`` is a subtype of ``object`` and is assumed to have all of the
attributes and methods of ``object``.

Expand Down Expand Up @@ -532,6 +530,35 @@ of ``type``.
.. _designed: https://mail.python.org/archives/list/typing-sig@python.org/message/D5FHORQVPHX3BHUDGF3A3TBZURBXLPHD/


Treat ``type[T]`` as a subtype of ``TypeForm[T]``
-------------------------------------------------

It was suggested that type ``type[T]`` should be considered a subtype
of ``TypeForm[T]``. This was ultimately rejected because there are ways to
create an object of type ``type[T]`` that does not encode a valid type expression.
For example, the expression ``type[1]`` is not a valid type expression. Likewise,
the expression ``type[S]`` is not a valid type expression if ``S`` is an
out-of-scope type variable. This same argument applies to other special forms
Comment on lines +537 to +541
Copy link
Member

@carljm carljm Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I agree with the conclusion that type[T] should not be a subtype of TypeForm[T], I don't think this argument makes sense. type[1] and type[S] do not "create an object of type type[T]" in the first place, so they are irrelevant here. They do create an object of type types.GenericAlias, but we are not discussing here whether types.GenericAlias should be a subtype of TypeForm (it should not).

The type type[C] is inhabited by the Python type/class object C and its subclasses. Example expressions that evaluate to an object of type type[C] are C or C().__class__. Note that none of these expressions use the type[...] syntactic form, because we are also not talking here about whether type[C] should be a subtype of TypeForm[type[C]] (it clearly should not.)

The intuitive reason for thinking that type[C] might be a subtype of TypeForm[C] is that the expression C both evaluates at runtime to the class object C (which inhabits the type type[C]) and is a valid spelling of the type C. The reason type[C] is not a subtype of TypeForm[C] is that the expression C().__class__ also evaluates at runtime to the class object C (which inhabits the type type[C]) but is not a valid spelling of the type C.

Since this PR has already landed, I can put up a PR with my suggested changes to this argument.

like objects of type ``UnionType``, which may or may not encode a valid type
expression. Rather than carving out a special case for ``type[T]`` that would
allow for potential unsoundness, it was decided to treat all type forms
Copy link
Member

@carljm carljm Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't think there is any possible unsoundness that could result from making type[T] a subtype of TypeForm[T], because the runtime objects that inhabit type[T] (the Python type/class object T and its subclasses) must already be handled by any function taking TypeForm[T], since a type/class object named by a valid type expression and one named by an invalid type expression are indistinguishable at runtime. The argument for making type[T] not a subtype of TypeForm[T] is a purely theoretical one, deriving from the requirement that only a valid type expression can evaluate to a TypeForm[T] type.

consistently. Therefore, ``type[T]`` is not considered a subtype of ``TypeForm[T]``.

It was also pointed out that the expression ``C | C`` (where ``C`` is a class
object) is a valid type expression - and therefore a valid ``TypeForm``,
but its runtime type form encoding is an instance of ``UnionType`` and
therefore is not compatible with ``type[C]``.

If a function wishes to indicate that it accepts values of type ``TypeForm[T]``
_and_ ``type[T]``, the parameter can simply be annotated with a union of these
two types.

::

def func[T](t: TypeForm[T] | type[T]) -> None: ...



Accept arbitrary annotation expressions
---------------------------------------

Expand All @@ -547,7 +574,7 @@ not as a parameter type or a return type:

def foo(not_reassignable: Final[object]): ... # Error: Final not allowed here

def nonsense() -> Final[object]: ... # Error: Final not alowed here
def nonsense() -> Final[object]: ... # Error: Final not allowed here

With the exception of ``Annotated``, type qualifiers are not allowed in type
expressions. ``TypeForm`` is limited to type expressions because its
Expand Down
Loading