Skip to content

Commit 63c2676

Browse files
authored
PEP 827: Minor tweaks to the examples (#4847)
* PEP 827: Minor tweaks to the examples * Add Exclude and Extract to the TS-style utility types * Add an `age` field to User in the prisma style example so that we aren't selecting *all* non-id fields * Add some discussion about Property/Link/MultiLink, since there was some confusion where they came from * a bunch of clarifications based on questions from Jelle
1 parent 4d64741 commit 63c2676

File tree

1 file changed

+80
-18
lines changed

1 file changed

+80
-18
lines changed

peps/pep-0827.rst

Lines changed: 80 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,16 @@ the database) like::
133133
id: Property[int]
134134

135135
name: Property[str]
136+
age: Property[int | None]
136137
email: Property[str]
137138
posts: Link[Post]
138139

140+
141+
(In the example, ``Property`` indicates a scalar type, ``Link``
142+
indicates a reference to another table, and ``MultiLink`` indicates a
143+
potentially many-to-many reference to another table; all would be
144+
defined by the ORM library.)
145+
139146
So, in Python code, a call like::
140147

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

512-
When evaluated in type annotation context, they will evaluate to
519+
When evaluated in type annotation context, they will be equivalent to
513520
``Literal[True]`` or ``Literal[False]``.
514521

515522
We restrict what operators may be used in a conditional
@@ -556,6 +563,18 @@ and we want typechecking to match.
556563
Type operators
557564
--------------
558565

566+
Type operators are the core engine of type manipulation, and provide
567+
the primitives that are used to deconstruct types and construct new
568+
ones.
569+
570+
This section defines the operators being introduced, and explains how
571+
they are to be evaluated in a typechecking or type evaluation context.
572+
The actual runtime classes being introduced, though, are just regular classes,
573+
and subscripting them produces normal ``typing`` generic alias objects
574+
(with the partial exception of the boolean operators and ``Iter``,
575+
which produce aliases that have some dunder methods overloaded for
576+
:ref:`runtime hooks <pep827-rt-support>`).
577+
559578
Many of the operators specified have type bounds listed for some of
560579
their operands. These should be interpreted more as documentation than
561580
as exact type bounds. Trying to evaluate operators with invalid
@@ -600,22 +619,20 @@ Basic operators
600619

601620
Negative indexes work in the usual way.
602621

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

608-
Special forms require special handling: the arguments list of a ``Callable``
609-
will be packed in a tuple, and a ``...`` will become
610-
``SpecialFormEllipsis``.
627+
Special forms require special handling: the arguments list of a
628+
``Callable`` will be packed in a tuple and a ``...`` will be treated
629+
as ``*args: Any`` and ``**kwargs: Any``, represented with the new
630+
``Param`` types.
611631

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

616-
* ``GetMemberType[T, S: Literal[str]]``: Extract the type of the
617-
member named ``S`` from the class ``T``.
618-
619636
* ``Length[T: tuple]`` - Gets the length of a tuple as an int literal
620637
(or ``Literal[None]`` if it is unbounded)
621638

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

647+
For non-class types, the principal should be that if ``x`` has type
648+
``T``, we want the value of ``type(x).<attr>``. So
649+
``GetSpecialAttr[Literal[1], "__name__"]`` should produce ``Literal["int"]``.
650+
630651
All of the operators in this section are :ref:`lifted over union types
631652
<pep827-lifting>`.
632653

@@ -643,17 +664,24 @@ Union processing
643664
Object inspection
644665
'''''''''''''''''
645666

667+
.. _pep827-members:
668+
646669
* ``Members[T]``: produces a ``tuple`` of ``Member`` types describing
647670
the members (attributes and methods) of class or typed dict ``T``.
648671

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

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

655680
* ``GetMember[T, S: Literal[str]]``: Produces a ``Member`` type for the
656-
member named ``S`` from the class ``T``.
681+
member named ``S`` from the class ``T``, or ``Never`` if it does not exist.
682+
683+
* ``GetMemberType[T, S: Literal[str]]``: Extract the type of the
684+
member named ``S`` from the class ``T``, or ``Never`` if it does not exist.
657685

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

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

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

678-
Methods are returned as callables using the new ``Param`` based
710+
Methods are returned as callables that are introspectable as ``Param``-based
679711
extended callables, and carrying the ``ClassVar``
680712
qualifier. ``staticmethod`` and ``classmethod`` will return
681713
``staticmethod`` and ``classmethod`` types, which are subscriptable as
@@ -787,7 +819,7 @@ Update class
787819

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

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

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

1260+
# Exclude<T, U>
1261+
# Constructs a type by excluding from T all union members assignable to U.
1262+
type Exclude[T, U] = Union[
1263+
*[
1264+
x
1265+
for x in typing.Iter[typing.FromUnion[T]]
1266+
if not typing.IsAssignable[x, U]
1267+
]
1268+
]
1269+
1270+
# Extract<T, U>
1271+
# Constructs a type by extracting from T all union members assignable to U.
1272+
type Extract[T, U] = Union[
1273+
*[
1274+
x
1275+
for x in typing.Iter[typing.FromUnion[T]]
1276+
# Just the inverse of Exclude, really
1277+
if typing.IsAssignable[x, U]
1278+
]
1279+
]
1280+
12271281
# Partial<T>
12281282
# Constructs a type with all properties of T set to optional (T | None).
12291283
type Partial[T] = typing.NewProtocol[
@@ -1843,6 +1897,12 @@ Open Issues
18431897
there is a default, and have whether there is a default represented in
18441898
an ``init`` field, like we do for class member initializers with ``Member``.
18451899

1900+
* :ref:`Members <pep827-members>`: Should ``Members`` return all
1901+
methods, even those without annotations? We excluded them out of the
1902+
desire for some consistency with attributes, but it would not be
1903+
technically difficult to include them in either static or runtime
1904+
evaluators.
1905+
18461906
* :ref:`Generic Callable <pep827-generic-callable>`: Should we have any mechanisms
18471907
to inspect/destruct ``GenericCallable``? Maybe can fetch the variable
18481908
information and maybe can apply it to concrete types?
@@ -1855,6 +1915,8 @@ Open Issues
18551915
rejected. This does actually exactly mirror a potential **runtime**
18561916
evaluation-order dependence, though.
18571917

1918+
* Should ``RaiseError`` support string templating when outputing the types?
1919+
18581920
* Because of generic functions, there will be plenty of cases where we
18591921
can't evaluate a type operator (because it's applied to an unresolved
18601922
type variable), and exactly what the type evaluation rules should be

0 commit comments

Comments
 (0)