diff --git a/peps/pep-0827.rst b/peps/pep-0827.rst index ea1f36a3744..92f4834dd90 100644 --- a/peps/pep-0827.rst +++ b/peps/pep-0827.rst @@ -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( @@ -509,7 +516,7 @@ Operators `, defined below, potentially combined with ``any``, the argument is a comprehension of type booleans, evaluated in the same way as the :ref:`unpacked comprehensions `. -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 @@ -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 `). + 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 @@ -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) @@ -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).``. So + ``GetSpecialAttr[Literal[1], "__name__"]`` should produce ``Literal["int"]``. + All of the operators in this section are :ref:`lifted over union types `. @@ -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 @@ -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 `). Accessible with ``.init``. + class (see :ref:`InitField `). 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``. @@ -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 @@ -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``. @@ -1216,6 +1248,7 @@ We present implementations of a selection of them:: # Omit # 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 @@ -1224,6 +1257,27 @@ We present implementations of a selection of them:: ] ] + # Exclude + # 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 + # 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 # Constructs a type with all properties of T set to optional (T | None). type Partial[T] = typing.NewProtocol[ @@ -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 `: 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 `: Should we have any mechanisms to inspect/destruct ``GenericCallable``? Maybe can fetch the variable information and maybe can apply it to concrete types? @@ -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