@@ -345,7 +345,7 @@ Extended Callables, take 2
345345We introduce a new extended callable proposal for expressing arbitrary
346346complex callable types. The goal here is not really to produce a new
347347syntax to write in annotations (it seems less pleasant to write than
348- callback protocols are), but to provide a way of contructing the types
348+ callback protocols are), but to provide a way of constructing the types
349349that is amenable to creating and introspecting callable types using
350350the other features of this PEP.
351351
@@ -512,7 +512,7 @@ The type ``true_typ if bool_typ else false_typ`` is a conditional
512512type, which resolves to ``true_typ `` if ``bool_typ `` is equivalent to
513513``Literal[True] `` and to ``false_typ `` otherwise.
514514
515- ``bool_typ `` is a type, but it needs syntactically be a type boolean,
515+ ``bool_typ `` is a type, but it needs to syntactically be a type boolean,
516516defined above.
517517
518518.. _unpacked :
@@ -628,7 +628,7 @@ Object inspection
628628* ``Members[T] ``: produces a ``tuple `` of ``Member `` types describing
629629 the members (attributes and methods) of class or typed dict ``T ``.
630630
631- In order to allow typechecking time and runtime evaluation coincide
631+ In order to allow typechecking time and runtime evaluation to coincide
632632 more closely, **only members with explicit type annotations are included **.
633633
634634* ``Attrs[T] ``: like ``Members[T] `` but only returns attributes (not
@@ -642,14 +642,14 @@ Object inspection
642642 of classes. Its type parameters encode the information about each
643643 member.
644644
645- * ``N `` is the name, as a literal string type. Accessable with ``.name ``.
646- * ``T `` is the type. Accessable with ``.type ``.
647- * ``Q `` is a union of qualifiers (see ``MemberQuals `` below). Accessable with ``.quals ``.
645+ * ``N `` is the name, as a literal string type. Accessible with ``.name ``.
646+ * ``T `` is the type. Accessible with ``.type ``.
647+ * ``Q `` is a union of qualifiers (see ``MemberQuals `` below). Accessible with ``.quals ``.
648648 * ``Init `` is the literal type of the attribute initializer in the
649- class (see :ref: `InitField <init-field >`). Accessable with ``.init ``.
649+ class (see :ref: `InitField <init-field >`). Accessible with ``.init ``.
650650 * ``D `` is the defining class of the member. (That is, which class
651651 the member is inherited from. Always ``Never ``, for a ``TypedDict ``).
652- Accessable with ``.definer ``.
652+ Accessible with ``.definer ``.
653653
654654* ``MemberQuals = Literal['ClassVar', 'Final', 'NotRequired', 'ReadOnly'] `` -
655655 ``MemberQuals `` is the type of "qualifiers" that can apply to a
@@ -673,7 +673,12 @@ Object creation
673673 specified by ``Member `` arguments
674674
675675* ``NewProtocolWithBases[Bases: tuple[type], *Ms: Member] `` - A variant that
676- allows specifying bases too. TODO: Is this something we actually want?
676+ allows specifying bases too. The idea is that a type would satisfy
677+ this protocol if it extends all of the given bases and has the
678+ specified members. (TODO: Is this something we actually
679+ want? It would would be a potentially powerful feature for dealing
680+ with things like Pydantic models, but protocol-with-bases would be
681+ something of a new concept.)
677682
678683* ``NewTypedDict[*Ps: Member] `` - Creates a new ``TypedDict `` with
679684 items specified by the ``Member `` arguments. TODO: Do we want a way
@@ -837,7 +842,7 @@ base classes and type decorators that do ``dataclass`` like things.
837842 When a class is declared, if one or more of its ancestors have an
838843 ``__init_subclass__ `` with an ``UpdateClass `` return type, they are
839844 applied in reverse MRO order. N.B: If the ``cls `` param is
840- parameterized by ``type[T]] ``, then the class type should
845+ parameterized by ``type[T] ``, then the class type should be
841846 substituted in for ``T ``.
842847
843848One snag here: it introduces type-evaluation-order dependence; if the
@@ -886,13 +891,14 @@ Runtime evaluation support
886891--------------------------
887892
888893An important goal is supporting runtime evaluation of these computed
889- types. We do not propose to add an official evaluator to the standard
894+ types. We ** do not ** propose to add an official evaluator to the standard
890895library, but intend to release a third-party evaluator library.
891896
892897While most of the extensions to the type system are "inert" type
893- operator applications, the syntax also includes list iteration and
894- conditionals, which will be automatically evaluated when the
895- ``__annotate__ `` method of a class, alias, or function is called.
898+ operator applications, the syntax also includes list iteration,
899+ conditionals, and attribute access, which will be automatically
900+ evaluated when the ``__annotate__ `` method of a class, alias, or
901+ function is called.
896902
897903In order to allow an evaluator library to trigger type evaluation in
898904those cases, we add a new hook to ``typing ``:
@@ -1073,7 +1079,7 @@ The ``Create`` type alias creates a new type (via ``NewProtocol``) by
10731079iterating over the attributes of the original type. It has access to
10741080names, types, qualifiers, and the literal types of initializers (in
10751081part through new facilities to handle the extremely common
1076- ``= Field(...) `` like pattern used here.
1082+ ``= Field(...) ``- like pattern used here) .
10771083
10781084Here, we filter out attributes that have ``primary_key=True `` in their
10791085``Field `` as well as extracting default arguments (which may be either
@@ -1149,6 +1155,10 @@ I am proposing a fully new extended callable syntax because:
11491155 closely mimic the ``mypy_extensions `` version though, if something new
11501156 is a non starter)
11511157
1158+ TODO: Currently I made the qualifiers be short strings, for code brevity
1159+ when using them, but an alternate approach would be to mirror
1160+ ``inspect.Signature `` more directly, and have an enum with names like
1161+ ``ParamKind.POSITIONAL_OR_KEYWORD ``.
11521162
11531163.. _generic-callable-rationale :
11541164
@@ -1162,7 +1172,7 @@ Consider a method with the following signature::
11621172
11631173The type of the method is generic, and the generic is bound at the
11641174**method **, not the class. We need a way to represent such a generic
1165- function both as a programmer might write it for a ``NewProtocol ``.
1175+ function as a programmer might write it for a ``NewProtocol ``.
11661176
11671177One option that is somewhat appealing but doesn't work would be to use
11681178unbound type variables and let them be generalized::
@@ -1226,6 +1236,9 @@ like mapped types are unmentioned in current documentation
12261236Reference Implementation
12271237========================
12281238
1239+ There is a demo of a runtime evaluator [#runtime ]_, which is
1240+ also where this PEP draft currently lives.
1241+
12291242There is an in-progress proof-of-concept implementation in mypy [#ref-impl ]_.
12301243
12311244It can type check the ORM and FastAPI-style model derivation
@@ -1234,8 +1247,6 @@ examples.
12341247It is missing support for callables, ``UpdateClass ``, annotation
12351248processing, and various smaller things.
12361249
1237- There is a demo of a runtime evaluator as well [#runtime ]_.
1238-
12391250Alternate syntax ideas
12401251======================
12411252
@@ -1342,7 +1353,7 @@ The main proposal is currently silent about exactly *how* ``Member``
13421353and ``Param `` will have associated types for ``.name `` and ``.type ``.
13431354
13441355We could just make it work for those particular types, or we could
1345- introduce a general mechansim that might look something like::
1356+ introduce a general mechanism that might look something like::
13461357
13471358 @typing.has_associated_types
13481359 class Member[
@@ -1374,15 +1385,48 @@ Rejected Ideas
13741385Renounce all cares of runtime evaluation
13751386----------------------------------------
13761387
1377- This would have a lot of simplifying features.
1388+ This would give us more flexibility to experiment with syntactic
1389+ forms, and would allow us to dispense with some ugliness such as
1390+ requiring ``typing.Iter `` in unpacked comprehension types and having a
1391+ limited set of ``<type-bool> `` expressions that can appear in
1392+ conditional types.
13781393
1379- TODO: Expand
1394+ For better or worse, though, runtime use of type annotations is
1395+ widespread, and one of our motivating examples (automatically deriving
1396+ FastAPI CRUD models) depends on it.
13801397
13811398Support TypeScript style pattern matching in subtype checking
13821399-------------------------------------------------------------
13831400
1384- This would almost certainly only be possible if we also decide not to
1385- care about runtime evaluation, as above.
1401+ In TypeScript, conditional types are formed like::
1402+
1403+ SomeType extends OtherType ? TrueType : FalseType
1404+
1405+ What's more, the right hand side of the check allows binding type
1406+ variables based on pattern matching, using the ``infer `` keyword, like
1407+ this example that extracts the element type of an array::
1408+
1409+ type ArrayArg<T> = T extends [infer El] ? El : never;
1410+
1411+ This is a very elegant mechanism, especially in the way that it
1412+ eliminates the need for ``typing.GetArg `` and its subtle ``Base ``
1413+ parameter.
1414+
1415+ Unfortunately it seems very difficult to shoehorn into Python's
1416+ existing syntax in any sort of satisfactory way, especially because of
1417+ the subtle binding structure.
1418+
1419+ Perhaps the most plausible variant would be something like::
1420+
1421+ type ArrayArg[T] = El if IsAssignable[T, list[Infer[El]]] else Never
1422+
1423+ Then, if we wanted to evaluate it at runtime, we'd need to do
1424+ something gnarly involving a custom ``globals `` environment that
1425+ catches the unbound ``Infer `` arguments.
1426+
1427+ Additionally, without major syntactic changes (using type operators
1428+ instead of ternary), we wouldn't be able to match TypeScript's
1429+ behavior of lifting the conditional over unions.
13861430
13871431
13881432Replace ``IsAssignable `` with something weaker than "assignable to" checking
@@ -1410,6 +1454,34 @@ that is similar to but not the same as subtyping, and that would need
14101454to either have a long and weird name like ``IsAssignableSimilar `` or a
14111455misleading short one like ``IsAssignable ``.
14121456
1457+
1458+ Don't use dot notation to access ``Member `` components
1459+ ------------------------------------------------------
1460+
1461+ Earlier versions of this PEP draft omitted the ability to write
1462+ ``m.name `` and similar on ``Member `` and ``Param `` components, and
1463+ instead relied on helper operators such as ``typing.GetName `` (that
1464+ could be implemented under the hood using ``typing.GetArg `` or
1465+ ``typing.GetMemberType ``).
1466+
1467+ The potential advantage here is reducing the number of new constructs
1468+ being added to the type language, and avoiding needing to either
1469+ introduce a new general mechanism for associated types or having a
1470+ special-case for ``Member ``.
1471+
1472+ ``PropsOnly `` (from :ref: `the query builder example <qb-impl >`) would
1473+ look like::
1474+
1475+ type PropsOnly[T] = typing.NewProtocol[
1476+ *[
1477+ typing.Member[typing.GetName[p], PointerArg[typing.GetType[p]]]
1478+ for p in typing.Iter[typing.Attrs[T]]
1479+ if typing.IsAssignable[typing.GetType[p], Property]
1480+ ]
1481+ ]
1482+
1483+ Everyone hated how this looked a lot.
1484+
14131485.. _less_syntax :
14141486
14151487
@@ -1430,8 +1502,9 @@ Boolean operations would likewise become operators (``Not``, ``And``,
14301502etc).
14311503
14321504The advantage of this is that constructing a type annotation never
1433- needs to do non-trivial computation, and thus we don't need
1434- :ref: `runtime hooks <rt-support >` to support evaluating them.
1505+ needs to do non-trivial computation (assuming we also get rid of dot
1506+ notation), and thus we don't need :ref: `runtime hooks <rt-support >` to
1507+ support evaluating them.
14351508
14361509It would also mean that it would be much easier to extract the raw
14371510type annotation. (The lambda form would still be somewhat fiddly.
@@ -1448,35 +1521,6 @@ worse. Supporting filtering while mapping would make it even more bad
14481521
14491522We can explore other options too if needed.
14501523
1451-
1452- Don't use dot notation to access ``Member `` components
1453- ------------------------------------------------------
1454-
1455- Earlier versions of this PEP draft omitted the ability to write
1456- ``m.name `` and similar on ``Member `` and ``Param `` components, and
1457- instead relied on helper operators such as ``typing.GetName `` (that
1458- could be implemented under the hood using ``typing.GetArg `` or
1459- ``typing.GetMemberType ``).
1460-
1461- The potential advantage here is reducing the number of new constructs
1462- being added to the type language, and avoiding needing to either
1463- introduce a new general mechanism for associated types or having a
1464- special-case for ``Member ``.
1465-
1466- ``PropsOnly `` (from :ref: `the query builder example <qb-impl >`) would
1467- look like::
1468-
1469- type PropsOnly[T] = typing.NewProtocol[
1470- *[
1471- typing.Member[typing.GetName[p], PointerArg[typing.GetType[p]]]
1472- for p in typing.Iter[typing.Attrs[T]]
1473- if typing.IsAssignable[typing.GetType[p], Property]
1474- ]
1475- ]
1476-
1477- Everyone hated how this looked a lot.
1478-
1479-
14801524Perform type manipulations with normal Python functions
14811525-------------------------------------------------------
14821526
@@ -1494,7 +1538,7 @@ types would be quite a bit *more* complicated.
14941538
14951539It would require a well-defined and safe-to-run subset of the language
14961540(and standard library) to be defined that could be run from within
1497- typecheckers. Subsets like this have been defined in other system
1541+ typecheckers. Subsets like this have been defined in other systems
14981542(see `Starlark <#starlark _>`_, the configuration language for Bazel),
14991543but it's still a lot of surface area, and programmers would need to
15001544keep in mind the boundaries of it.
@@ -1559,7 +1603,7 @@ arguments invariantly.
15591603Acknowledgements
15601604================
15611605
1562- Jukka Lehtosalo
1606+ Jukka Lehtosalo, etc
15631607
15641608[Thank anyone who has helped with the PEP.]
15651609
0 commit comments