8000 bpo-44676: Serialize the union type using only public API (GH-27323) · python/cpython@435a033 · GitHub
[go: up one dir, main page]

Skip to content

Commit 435a033

Browse files
bpo-44676: Serialize the union type using only public API (GH-27323)
Remove also the _from_args() constructor.
1 parent 4f5980a commit 435a033

File tree

4 files changed

+23
-80
lines changed

4 files changed

+23
-80
lines changed

Lib/copyreg.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ def pickle_complex(c):
3636

3737
pickle(complex, pickle_complex, complex)
3838

39+
def pickle_union(obj):
40+
import functools, operator
41+
return functools.reduce, (operator.or_, obj.__args__)
42+
43+
pickle(type(int | str), pickle_union)
44+
3945
# Support for pickling new-style objects
4046

4147
def _reconstructor(cls, base, state):

Lib/test/test_types.py

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from test.support import run_with_locale, cpython_only
44
import collections.abc
55
from collections import namedtuple
6+
import copy
67
import gc
78
import inspect
89
import pickle
@@ -807,36 +808,20 @@ def eq(actual, expected, typed=True):
807808
eq(x[S], int | S | bytes)
808809

809810
def test_union_pickle(self):
810-
alias = list[T] | int
811-
s = pickle.dumps(alias)
812-
loaded = pickle.loads(s)
813-
self.assertEqual(alias, loaded)
814-
self.assertEqual(alias.__args__, loaded.__args__)
815-
self.assertEqual(alias.__parameters__, loaded.__parameters__)
816-
817-
def test_union_from_args(self):
818-
with self.assertRaisesRegex(
819-
TypeError,
820-
r"^Each union argument must be a type, got 1$",
821-
):
822-
types.Union._from_args((1,))
823-
824-
with self.assertRaisesRegex(
825-
TypeError,
826-
r"Union._from_args\(\) argument 'args' must be tuple, not int$",
827-
):
828-
types.Union._from_args(1)
829-
830-
with self.assertRaisesRegex(ValueError, r"args must be not empty"):
831-
types.Union._from_args(())
832-
833-
alias = types.Union._from_args((int, list[T], None))
834-
835-
self.assertEqual(alias.__args__, (int, list[T], type(None)))
836-
self.assertEqual(alias.__parameters__, (T,))
837-
838-
result = types.Union._from_args((int,))
839-
self.assertIs(int, result)
811+
orig = list[T] | int
812+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
813+
s = pickle.dumps(orig, proto)
814+
loaded = pickle.loads(s)
815+
self.assertEqual(loaded, orig)
816+
self.assertEqual(loaded.__args__, orig.__args__)
817+
self.assertEqual(loaded.__parameters__, orig.__parameters__)
818+
819+
def test_union_copy(self):
820+
orig = list[T] | int
821+
for copied in (copy.copy(orig), copy.deepcopy(orig)):
822+
self.assertEqual(copied, orig)
823+
self.assertEqual(copied.__args__, orig.__args__)
824+
self.assertEqual(copied.__parameters__, orig.__parameters__)
840825

841826
def test_union_parameter_substitution_errors(self):
842827
T = typing.TypeVar("T")

Lib/typing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
329329
if isinstance(t, GenericAlias):
330330
return GenericAlias(t.__origin__, ev_args)
331331
if isinstance(t, types.Union):
332-
return types.Union._from_args(ev_args)
332+
return functools.reduce(operator.or_, ev_args)
333333
else:
334334
return t.copy_with(ev_args)
335335
return t
@@ -1814,7 +1814,7 @@ def _strip_annotations(t):
18141814
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
18151815
if stripped_args == t.__args__:
18161816
return t< 57AE /span>
1817-
return types.Union._from_args(stripped_args)
1817+
return functools.reduce(operator.or_, stripped_args)
18181818

18191819
return t
18201820

Objects/unionobject.c

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -235,21 +235,6 @@ is_unionable(PyObject *obj)
235235
_PyUnion_Check(obj));
236236
}
237237

238-
static int
239-
is_args_unionable(PyObject *args)
240-
{
241-
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
242-
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
243-
PyObject *arg = PyTuple_GET_ITEM(args, iarg);
244-
if (!is_unionable(arg)) {
245-
PyErr_Format(PyExc_TypeError,
246-
"Each union argument must be a type, got %.100R", arg);
247-
return 0;
248-
}
249-
}
250-
return 1;
251-
}
252-
253238
PyObject *
254239
_Py_union_type_or(PyObject* self, PyObject* other)
255240
{
@@ -362,47 +347,14 @@ union_repr(PyObject *self)
362347
return NULL;
363348
}
364349

365-
static PyObject *
366-
union_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
367-
{
368-
unionobject *alias = (unionobject *)self;
369-
PyObject* from_args = PyObject_GetAttrString(self, "_from_args");
370-
if (from_args == NULL) {
371-
return NULL;
372-
}
373-
374-
return Py_BuildValue("N(O)", from_args, alias->args);
375-
}
376-
377350
static PyMemberDef union_members[] = {
378351
{"__args__", T_OBJECT, offsetof(unionobject, args), READONLY},
379352
{0}
380353
};
381354

382-
static PyObject *
383-
union_from_args(PyObject *cls, PyObject *args)
384-
{
385-
if (!PyTuple_CheckExact(args)) {
386-
_PyArg_BadArgument("Union._from_args", "argument 'args'", "tuple", args);
387-
return NULL;
388-
}
389-
if (!PyTuple_GET_SIZE(args)) {
390-
PyErr_SetString(PyExc_ValueError, "args must be not empty");
391-
return NULL;
392-
}
393-
394-
if (!is_args_unionable(args)) {
395-
return NULL;
396-
}
397-
398-
return make_union(args);
399-
}
400-
401355
static PyMethodDef union_methods[] = {
402-
{"_from_args", union_from_args, METH_O | METH_CLASS},
403356
{"__instancecheck__", union_instancecheck, METH_O},
404357
{"__subclasscheck__", union_subclasscheck, METH_O},
405-
{"__reduce__", union_reduce, METH_NOARGS},
406358
{0}};
407359

408360

0 commit comments

Comments
 (0)
0