Skip to content
Open
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
98 changes: 80 additions & 18 deletions peps/pep-0827.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,16 @@ the database) like::
id: Property[int]

name: Property[str]
age: Property[int | None]
email: Property[str]
posts: Link[Post]


(In the example, ``Property`` indicates a scalar type, ``Link``
indicates a reference to another table, and ``MultiLink`` indicates a
potentially many-to-many reference to another table; all would be
defined by the ORM library.)

So, in Python code, a call like::

db.select(
Expand Down Expand Up @@ -509,7 +516,7 @@ Operators <pep827-boolean-ops>`, defined below, potentially combined with
``any``, the argument is a comprehension of type booleans, evaluated
in the same way as the :ref:`unpacked comprehensions <pep827-unpacked>`.

When evaluated in type annotation context, they will evaluate to
When evaluated in type annotation context, they will be equivalent to
``Literal[True]`` or ``Literal[False]``.

We restrict what operators may be used in a conditional
Expand Down Expand Up @@ -556,6 +563,18 @@ and we want typechecking to match.
Type operators
--------------

Type operators are the core engine of type manipulation, and provide
the primitives that are used to deconstruct types and construct new
ones.

This section defines the operators being introduced, and explains how
they are to be evaluated in a typechecking or type evaluation context.
The actual runtime classes being introduced, though, are just regular classes,
and subscripting them produces normal ``typing`` generic alias objects
(with the partial exception of the boolean operators and ``Iter``,
which produce aliases that have some dunder methods overloaded for
:ref:`runtime hooks <pep827-rt-support>`).

Many of the operators specified have type bounds listed for some of
their operands. These should be interpreted more as documentation than
as exact type bounds. Trying to evaluate operators with invalid
Expand Down Expand Up @@ -600,22 +619,20 @@ Basic operators

Negative indexes work in the usual way.

Note that runtime evaluation will only be able to support proper classes
as ``Base``, *not* protocols. So, for example, ``GetArg[Ty,
Iterable, Literal[0]]`` to get the type of something iterable will
fail in the runtime evaluator.
(Note that runtime evaluators of type annotations are likely
to struggle with using protocols as ``Base``. So, for example, ``GetArg[Ty,
Iterable, Literal[0]]`` to get the type of something iterable may
fail in a runtime evaluator of types.)

Special forms require special handling: the arguments list of a ``Callable``
will be packed in a tuple, and a ``...`` will become
``SpecialFormEllipsis``.
Special forms require special handling: the arguments list of a
``Callable`` will be packed in a tuple and a ``...`` will be treated
as ``*args: Any`` and ``**kwargs: Any``, represented with the new
``Param`` types.

* ``GetArgs[T, Base]``: returns a tuple containing all of the type
arguments of ``T`` when interpreted as ``Base``, or ``Never`` if it
cannot be.

* ``GetMemberType[T, S: Literal[str]]``: Extract the type of the
member named ``S`` from the class ``T``.

* ``Length[T: tuple]`` - Gets the length of a tuple as an int literal
(or ``Literal[None]`` if it is unbounded)

Expand All @@ -627,6 +644,10 @@ Basic operators
attributes are ``__name__``, ``__module__``, and ``__qualname__``.
Returns the value as a ``Literal[str]``.

For non-class types, the principal should be that if ``x`` has type
``T``, we want the value of ``type(x).<attr>``. So
``GetSpecialAttr[Literal[1], "__name__"]`` should produce ``Literal["int"]``.

All of the operators in this section are :ref:`lifted over union types
<pep827-lifting>`.

Expand All @@ -643,17 +664,24 @@ Union processing
Object inspection
'''''''''''''''''

.. _pep827-members:

* ``Members[T]``: produces a ``tuple`` of ``Member`` types describing
the members (attributes and methods) of class or typed dict ``T``.

In order to allow typechecking time and runtime evaluation to coincide
more closely, **only members with explicit type annotations are included**.
In order to allow typechecking time and runtime evaluation to
coincide more closely, **only members with explicit type annotations
are included**. (This is intended to also exclude unannotated
methods, though see Open Issues.)

* ``Attrs[T]``: like ``Members[T]`` but only returns attributes (not
methods).

* ``GetMember[T, S: Literal[str]]``: Produces a ``Member`` type for the
member named ``S`` from the class ``T``.
member named ``S`` from the class ``T``, or ``Never`` if it does not exist.

* ``GetMemberType[T, S: Literal[str]]``: Extract the type of the
member named ``S`` from the class ``T``, or ``Never`` if it does not exist.

* ``Member[N: Literal[str], T, Q: MemberQuals, Init, D]``: ``Member``,
is a simple type, not an operator, that is used to describe members
Expand All @@ -662,9 +690,11 @@ Object inspection

* ``N`` is the name, as a literal string type. Accessible with ``.name``.
* ``T`` is the type. Accessible with ``.type``.
* ``Q`` is a union of qualifiers (see ``MemberQuals`` below). Accessible with ``.quals``.
* ``Q`` is a union of qualifiers (see ``MemberQuals``
below). Accessible with ``.quals``. ``Never`` if no qualifiers.
* ``Init`` is the literal type of the attribute initializer in the
class (see :ref:`InitField <pep827-init-field>`). Accessible with ``.init``.
class (see :ref:`InitField <pep827-init-field>`). Accessible with
``.init``. ``Never`` if no initializer.
* ``D`` is the defining class of the member. (That is, which class
the member is inherited from. Always ``Never``, for a ``TypedDict``).
Accessible with ``.definer``.
Expand All @@ -674,8 +704,10 @@ Object inspection
member; currently ``ClassVar`` and ``Final`` apply to classes, and
``NotRequired`` and ``ReadOnly`` apply to typed dicts.

Methods are functions, staticmethods, and classmethods that are
defined class body. Properties should be treated as attributes.

Methods are returned as callables using the new ``Param`` based
Methods are returned as callables that are introspectable as ``Param``-based
extended callables, and carrying the ``ClassVar``
qualifier. ``staticmethod`` and ``classmethod`` will return
``staticmethod`` and ``classmethod`` types, which are subscriptable as
Expand Down Expand Up @@ -787,7 +819,7 @@ Update class

When a class is declared, if one or more of its ancestors have an
``__init_subclass__`` with an ``UpdateClass`` return type, they are
applied in reverse MRO order. N.B: If the ``cls`` param is
applied in reverse MRO order. If the ``cls`` param is
parameterized by ``type[T]``, then the class type should be
substituted in for ``T``.

Expand Down Expand Up @@ -1216,6 +1248,7 @@ We present implementations of a selection of them::

# Omit<T, Keys>
# Constructs a type by picking all properties from T and then removing Keys.
# Note that unlike in TS, our Omit does not depend on Exclude.
type Omit[T, Keys] = typing.NewProtocol[
*[
p
Expand All @@ -1224,6 +1257,27 @@ We present implementations of a selection of them::
]
]

# Exclude<T, U>
# Constructs a type by excluding from T all union members assignable to U.
type Exclude[T, U] = Union[
*[
x
for x in typing.Iter[typing.FromUnion[T]]
if not typing.IsAssignable[x, U]
]
]

# Extract<T, U>
# Constructs a type by extracting from T all union members assignable to U.
type Extract[T, U] = Union[
*[
x
for x in typing.Iter[typing.FromUnion[T]]
# Just the inverse of Exclude, really
if typing.IsAssignable[x, U]
]
]

# Partial<T>
# Constructs a type with all properties of T set to optional (T | None).
type Partial[T] = typing.NewProtocol[
Expand Down Expand Up @@ -1843,6 +1897,12 @@ Open Issues
there is a default, and have whether there is a default represented in
an ``init`` field, like we do for class member initializers with ``Member``.

* :ref:`Members <pep827-members>`: Should ``Members`` return all
methods, even those without annotations? We excluded them out of the
desire for some consistency with attributes, but it would not be
technically difficult to include them in either static or runtime
evaluators.

* :ref:`Generic Callable <pep827-generic-callable>`: Should we have any mechanisms
to inspect/destruct ``GenericCallable``? Maybe can fetch the variable
information and maybe can apply it to concrete types?
Expand All @@ -1855,6 +1915,8 @@ Open Issues
rejected. This does actually exactly mirror a potential **runtime**
evaluation-order dependence, though.

* Should ``RaiseError`` support string templating when outputing the types?

* Because of generic functions, there will be plenty of cases where we
can't evaluate a type operator (because it's applied to an unresolved
type variable), and exactly what the type evaluation rules should be
Expand Down
Loading