Skip to content

Commit 0fd2737

Browse files
bpo-44654: Refactor and clean up the union type implementation (GH-27196)
1 parent f88e138 commit 0fd2737

File tree

9 files changed

+88
-125
lines changed

9 files changed

+88
-125
lines changed

Include/genericaliasobject.h

-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@
55
extern "C" {
66
#endif
77

8-
#ifndef Py_LIMITED_API
9-
PyAPI_FUNC(PyObject *) _Py_subs_parameters(PyObject *, PyObject *, PyObject *, PyObject *);
10-
PyAPI_FUNC(PyObject *) _Py_make_parameters(PyObject *);
11-
#endif
12-
138
PyAPI_FUNC(PyObject *) Py_GenericAlias(PyObject *, PyObject *);
149
PyAPI_DATA(PyTypeObject) Py_GenericAliasType;
1510

Include/internal/pycore_unionobject.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11-
PyAPI_FUNC(PyObject *) _Py_Union(PyObject *args);
12-
PyAPI_DATA(PyTypeObject) _Py_UnionType;
13-
PyAPI_FUNC(PyObject *) _Py_union_type_or(PyObject* self, PyObject* param);
11+
PyAPI_DATA(PyTypeObject) _PyUnion_Type;
12+
#define _PyUnion_Check(op) Py_IS_TYPE(op, &_PyUnion_Type)
13+
PyAPI_FUNC(PyObject *) _Py_union_type_or(PyObject *, PyObject *);
14+
15+
#define _PyGenericAlias_Check(op) PyObject_TypeCheck(op, &Py_GenericAliasType)
16+
PyAPI_FUNC(PyObject *) _Py_subs_parameters(PyObject *, PyObject *, PyObject *, PyObject *);
17+
PyAPI_FUNC(PyObject *) _Py_make_parameters(PyObject *);
1418

1519
#ifdef __cplusplus
1620
}

Lib/test/test_types.py

+25-17
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,18 @@ def test_method_descriptor_types(self):
611611
self.assertIsInstance(int.from_bytes, types.BuiltinMethodType)
612612
self.assertIsInstance(int.__new__, types.BuiltinMethodType)
613613

614+
def test_ellipsis_type(self):
615+
self.assertIsInstance(Ellipsis, types.EllipsisType)
616+
617+
def test_notimplemented_type(self):
618+
self.assertIsInstance(NotImplemented, types.NotImplementedType)
619+
620+
def test_none_type(self):
621+
self.assertIsInstance(None, types.NoneType)
622+
623+
624+
class UnionTests(unittest.TestCase):
625+
614626
def test_or_types_operator(self):
615627
self.assertEqual(int | str, typing.Union[int, str])
616628
self.assertNotEqual(int | list, typing.Union[int, str])
@@ -657,18 +669,23 @@ def test_or_types_operator(self):
657669
with self.assertRaises(TypeError):
658670
Example() | int
659671
x = int | str
660-
self.assertNotEqual(x, {})
672+
self.assertEqual(x, int | str)
673+
self.assertEqual(x, str | int)
674+
self.assertNotEqual(x, {}) # should not raise exception
661675
with self.assertRaises(TypeError):
662-
(int | str) < typing.Union[str, int]
676+
x < x
663677
with self.assertRaises(TypeError):
664-
(int | str) < (int | bool)
678+
x <= x
679+
y = typing.Union[str, int]
665680
with self.assertRaises(TypeError):
666-
(int | str) <= (int | str)
681+
x < y
682+
y = int | bool
667683
with self.assertRaises(TypeError):
668-
# Check that we don't crash if typing.Union does not have a tuple in __args__
669-
x = typing.Union[str, int]
670-
x.__args__ = [str, int]
671-
(int | str ) == x
684+
x < y
685+
# Check that we don't crash if typing.Union does not have a tuple in __args__
686+
y = typing.Union[str, int]
687+
y.__args__ = [str, int]
688+
self.assertEqual(x, y)
672689

673690
def test_hash(self):
674691
self.assertEqual(hash(int | str), hash(str | int))
@@ -873,15 +890,6 @@ def test_or_type_operator_reference_cycle(self):
873890
self.assertLessEqual(sys.gettotalrefcount() - before, leeway,
874891
msg='Check for union reference leak.')
875892

876-
def test_ellipsis_type(self):
877-
self.assertIsInstance(Ellipsis, types.EllipsisType)
878-
879-
def test_notimplemented_type(self):
880-
self.assertIsInstance(NotImplemented, types.NotImplementedType)
881-
882-
def test_none_type(self):
883-
self.assertIsInstance(None, types.NoneType)
884-
885893

886894
class MappingProxyTests(unittest.TestCase):
887895
mappingproxy = types.MappingProxyType

Lib/typing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,7 @@ def copy_with(self, params):
11841184
return Union[params]
11851185

11861186
def __eq__(self, other):
1187-
if not isinstance(other, _UnionGenericAlias):
1187+
if not isinstance(other, (_UnionGenericAlias, types.Union)):
11881188
return NotImplemented
11891189
return set(self.__args__) == set(other.__args__)
11901190

Objects/abstract.c

+2-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include "pycore_object.h" // _Py_CheckSlotResult()
77
#include "pycore_pyerrors.h" // _PyErr_Occurred()
88
#include "pycore_pystate.h" // _PyThreadState_GET()
9-
#include "pycore_unionobject.h" // _Py_UnionType && _Py_Union()
9+
#include "pycore_unionobject.h" // _PyUnion_Check()
1010
#include <ctype.h>
1111
#include <stddef.h> // offsetof()
1212
#include "longintrepr.h"
@@ -2623,9 +2623,7 @@ recursive_issubclass(PyObject *derived, PyObject *cls)
26232623
"issubclass() arg 1 must be a class"))
26242624
return -1;
26252625

2626-
PyTypeObject *type = Py_TYPE(cls);
2627-
int is_union = (PyType_Check(type) && type == &_Py_UnionType);
2628-
if (!is_union && !check_class(cls,
2626+
if (!_PyUnion_Check(cls) && !check_class(cls,
26292627
"issubclass() arg 2 must be a class,"
26302628
" a tuple of classes, or a union.")) {
26312629
return -1;

Objects/genericaliasobject.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#include "Python.h"
44
#include "pycore_object.h"
5-
#include "pycore_unionobject.h" // _Py_union_as_number
5+
#include "pycore_unionobject.h" // _Py_union_type_or, _PyGenericAlias_Check
66
#include "structmember.h" // PyMemberDef
77

88
typedef struct {
@@ -441,8 +441,7 @@ ga_getattro(PyObject *self, PyObject *name)
441441
static PyObject *
442442
ga_richcompare(PyObject *a, PyObject *b, int op)
443443
{
444-
if (!PyObject_TypeCheck(a, &Py_GenericAliasType) ||
445-
!PyObject_TypeCheck(b, &Py_GenericAliasType) ||
444+
if (!_PyGenericAlias_Check(b) ||
446445
(op != Py_EQ && op != Py_NE))
447446
{
448447
Py_RETURN_NOTIMPLEMENTED;
@@ -622,7 +621,7 @@ ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
622621
}
623622

624623
static PyNumberMethods ga_as_number = {
625-
.nb_or = (binaryfunc)_Py_union_type_or, // Add __or__ function
624+
.nb_or = _Py_union_type_or, // Add __or__ function
626625
};
627626

628627
// TODO:

Objects/object.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
1212
#include "pycore_pystate.h" // _PyThreadState_GET()
1313
#include "pycore_symtable.h" // PySTEntry_Type
14-
#include "pycore_unionobject.h" // _Py_UnionType
14+
#include "pycore_unionobject.h" // _PyUnion_Type
1515
#include "frameobject.h"
1616
#include "interpreteridobject.h"
1717

@@ -1878,7 +1878,7 @@ _PyTypes_Init(void)
18781878
INIT_TYPE(_PyWeakref_CallableProxyType);
18791879
INIT_TYPE(_PyWeakref_ProxyType);
18801880
INIT_TYPE(_PyWeakref_RefType);
1881-
INIT_TYPE(_Py_UnionType);
1881+
INIT_TYPE(_PyUnion_Type);
18821882

18831883
return _PyStatus_OK();
18841884
#undef INIT_TYPE

Objects/typeobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include "pycore_object.h"
1010
#include "pycore_pyerrors.h"
1111
#include "pycore_pystate.h" // _PyThreadState_GET()
12-
#include "pycore_unionobject.h" // _Py_Union(), _Py_union_type_or
12+
#include "pycore_unionobject.h" // _Py_union_type_or
1313
#include "frameobject.h"
1414
#include "opcode.h" // MAKE_CELL
1515
#include "structmember.h" // PyMemberDef

0 commit comments

Comments
 (0)