Skip to content

Commit 248bac1

Browse files
committed
WIP: add intersection types
1 parent c7c3a60 commit 248bac1

13 files changed

+936
-32
lines changed

Doc/c-api/typehints.rst

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ Objects for Type Hinting
66
------------------------
77

88
Various built-in types for type hinting are provided. Currently,
9-
two types exist -- :ref:`GenericAlias <types-genericalias>` and
10-
:ref:`Union <types-union>`. Only ``GenericAlias`` is exposed to C.
9+
three types exist -- :ref:`GenericAlias <types-genericalias>`,
10+
:ref:`Union <types-union>` and :ref:`Intersection <types-intersection>`.
11+
Only ``GenericAlias`` is exposed to C.
1112

1213
.. c:function:: PyObject* Py_GenericAlias(PyObject *origin, PyObject *args)
1314

Doc/library/stdtypes.rst

+93
Original file line numberDiff line numberDiff line change
@@ -5206,6 +5206,99 @@ instantiated from the type::
52065206
.. versionadded:: 3.10
52075207

52085208

5209+
.. _types-intersection:
5210+
5211+
Intersection Type
5212+
-----------------
5213+
5214+
.. index::
5215+
object: Intersection
5216+
pair: intersection; type
5217+
5218+
An intersection object holds the value of the ``&`` (bitwise and) operation on
5219+
multiple :ref:`type objects <bltin-type-objects>`. These types are intended
5220+
primarily for :term:`type annotations <annotation>`.
5221+
5222+
.. describe:: X & Y & ...
5223+
5224+
Defines an intersection object which holds types *X*, *Y*, and so forth. ``X & Y``
5225+
means both X and Y.
5226+
For example, the following function expects an argument of type
5227+
``Resettable`` and ``Growable[str]``::
5228+
5229+
class Resettable(Protocol):
5230+
def reset() -> None: ...
5231+
5232+
class Growable(Protocol[T]):
5233+
def grow(t: T) -> None: ...
5234+
5235+
def f(x: Resettable & Growable[str]) -> None:
5236+
x.reset()
5237+
x.grow("first")
5238+
5239+
.. describe:: intersection_object == other
5240+
5241+
Intersection objects can be tested for equality with other intersection objects. Details:
5242+
5243+
* Intersections of intersections are flattened::
5244+
5245+
(A & B) & C == A & B & C
5246+
5247+
* Redundant types are removed::
5248+
5249+
A & B & A == A & B
5250+
5251+
* When comparing intersections, the order is ignored::
5252+
5253+
A & B == B & A
5254+
5255+
.. describe:: isinstance(obj, intersection_object)
5256+
.. describe:: issubclass(obj, intersection_object)
5257+
5258+
Calls to :func:`isinstance` and :func:`issubclass` are also supported with an
5259+
intersection object::
5260+
5261+
>>> isinstance("", object & str)
5262+
True
5263+
5264+
However, intersection objects containing :ref:`parameterized generics
5265+
<types-genericalias>` cannot be used::
5266+
5267+
>>> isinstance(1, int & list[int])
5268+
Traceback (most recent call last):
5269+
File "<stdin>", line 1, in <module>
5270+
TypeError: isinstance() argument 2 cannot contain a parameterized generic
5271+
5272+
The user-exposed type for the intersection object can be accessed from
5273+
:data:`types.IntersectionType` and used for :func:`isinstance` checks. An object can be
5274+
instantiated from the type::
5275+
5276+
>>> import types
5277+
>>> isinstance(int & str, types.IntersectionType)
5278+
True
5279+
>>> types.IntersectionType(int, str)
5280+
int & str
5281+
5282+
.. note::
5283+
The :meth:`__and__` method for type objects was added to support the syntax
5284+
``X & Y``. If a metaclass implements :meth:`__and__`, the Intersection may
5285+
override it::
5286+
5287+
>>> class M(type):
5288+
... def __and__(self, other):
5289+
... return "Hello"
5290+
...
5291+
>>> class C(metaclass=M):
5292+
... pass
5293+
...
5294+
>>> C & int
5295+
'Hello'
5296+
>>> int & C
5297+
int & __main__.C
5298+
5299+
.. versionadded:: 3.13
5300+
5301+
52095302
.. _typesother:
52105303

52115304
Other Built-in Types

Doc/library/types.rst

+6
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,12 @@ Standard names are defined for the following types:
365365

366366
.. versionadded:: 3.10
367367

368+
.. class:: IntersectionType
369+
370+
The type of :ref:`intersection type expressions<types-intersection>`.
371+
372+
.. versionadded:: 3.13
373+
368374
.. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno)
369375

370376
The type of traceback objects such as found in ``sys.exception().__traceback__``.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef Py_INTERNAL_INTERSECTIONOBJECT_H
2+
#define Py_INTERNAL_INTERSECTIONOBJECT_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
extern PyTypeObject _PyIntersection_Type;
12+
#define _PyIntersection_Check(op) Py_IS_TYPE((op), &_PyIntersection_Type)
13+
extern PyObject *_Py_intersection_type_and(PyObject *, PyObject *);
14+
15+
#define _PyGenericAlias_Check(op) PyObject_TypeCheck((op), &Py_GenericAliasType)
16+
extern PyObject *_Py_subs_parameters(PyObject *, PyObject *, PyObject *, PyObject *);
17+
extern PyObject *_Py_make_parameters(PyObject *);
18+
extern PyObject *_Py_intersection_args(PyObject *self);
19+
20+
#ifdef __cplusplus
21+
}
22+
#endif
23+
#endif /* !Py_INTERNAL_INTERSECTIONOBJECT_H */

0 commit comments

Comments
 (0)