mirror of
https://github.com/python/cpython.git
synced 2025-09-16 13:47:31 +00:00
bpo-44676: Serialize the union type using only public API (GH-27323) (GH-27340)
Remove also the _from_args() constructor.
(cherry picked from commit 435a0334d3
)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
ca5a4cf826
commit
0aea99e444
4 changed files with 22 additions and 79 deletions
|
@ -36,6 +36,12 @@ else:
|
||||||
|
|
||||||
pickle(complex, pickle_complex, complex)
|
pickle(complex, pickle_complex, complex)
|
||||||
|
|
||||||
|
def pickle_union(obj):
|
||||||
|
import functools, operator
|
||||||
|
return functools.reduce, (operator.or_, obj.__args__)
|
||||||
|
|
||||||
|
pickle(type(int | str), pickle_union)
|
||||||
|
|
||||||
# Support for pickling new-style objects
|
# Support for pickling new-style objects
|
||||||
|
|
||||||
def _reconstructor(cls, base, state):
|
def _reconstructor(cls, base, state):
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
from test.support import run_with_locale, cpython_only
|
from test.support import run_with_locale, cpython_only
|
||||||
import collections.abc
|
import collections.abc
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
import copy
|
||||||
import gc
|
import gc
|
||||||
import inspect
|
import inspect
|
||||||
import pickle
|
import pickle
|
||||||
|
@ -807,36 +808,20 @@ class UnionTests(unittest.TestCase):
|
||||||
eq(x[S], int | S | bytes)
|
eq(x[S], int | S | bytes)
|
||||||
|
|
||||||
def test_union_pickle(self):
|
def test_union_pickle(self):
|
||||||
alias = list[T] | int
|
orig = list[T] | int
|
||||||
s = pickle.dumps(alias)
|
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||||
loaded = pickle.loads(s)
|
s = pickle.dumps(orig, proto)
|
||||||
self.assertEqual(alias, loaded)
|
loaded = pickle.loads(s)
|
||||||
self.assertEqual(alias.__args__, loaded.__args__)
|
self.assertEqual(loaded, orig)
|
||||||
self.assertEqual(alias.__parameters__, loaded.__parameters__)
|
self.assertEqual(loaded.__args__, orig.__args__)
|
||||||
|
self.assertEqual(loaded.__parameters__, orig.__parameters__)
|
||||||
|
|
||||||
def test_union_from_args(self):
|
def test_union_copy(self):
|
||||||
with self.assertRaisesRegex(
|
orig = list[T] | int
|
||||||
TypeError,
|
for copied in (copy.copy(orig), copy.deepcopy(orig)):
|
||||||
r"^Each union argument must be a type, got 1$",
|
self.assertEqual(copied, orig)
|
||||||
):
|
self.assertEqual(copied.__args__, orig.__args__)
|
||||||
types.Union._from_args((1,))
|
self.assertEqual(copied.__parameters__, orig.__parameters__)
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
|
||||||
TypeError,
|
|
||||||
r"Union._from_args\(\) argument 'args' must be tuple, not int$",
|
|
||||||
):
|
|
||||||
types.Union._from_args(1)
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(ValueError, r"args must be not empty"):
|
|
||||||
types.Union._from_args(())
|
|
||||||
|
|
||||||
alias = types.Union._from_args((int, list[T], None))
|
|
||||||
|
|
||||||
self.assertEqual(alias.__args__, (int, list[T], type(None)))
|
|
||||||
self.assertEqual(alias.__parameters__, (T,))
|
|
||||||
|
|
||||||
result = types.Union._from_args((int,))
|
|
||||||
self.assertIs(int, result)
|
|
||||||
|
|
||||||
def test_union_parameter_substitution_errors(self):
|
def test_union_parameter_substitution_errors(self):
|
||||||
T = typing.TypeVar("T")
|
T = typing.TypeVar("T")
|
||||||
|
|
|
@ -321,7 +321,7 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
|
||||||
if isinstance(t, GenericAlias):
|
if isinstance(t, GenericAlias):
|
||||||
return GenericAlias(t.__origin__, ev_args)
|
return GenericAlias(t.__origin__, ev_args)
|
||||||
if isinstance(t, types.Union):
|
if isinstance(t, types.Union):
|
||||||
return types.Union._from_args(ev_args)
|
return functools.reduce(operator.or_, ev_args)
|
||||||
else:
|
else:
|
||||||
return t.copy_with(ev_args)
|
return t.copy_with(ev_args)
|
||||||
return t
|
return t
|
||||||
|
@ -1806,7 +1806,7 @@ def _strip_annotations(t):
|
||||||
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
|
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
|
||||||
if stripped_args == t.__args__:
|
if stripped_args == t.__args__:
|
||||||
return t
|
return t
|
||||||
return types.Union._from_args(stripped_args)
|
return functools.reduce(operator.or_, stripped_args)
|
||||||
|
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
|
@ -235,21 +235,6 @@ is_unionable(PyObject *obj)
|
||||||
_PyUnion_Check(obj));
|
_PyUnion_Check(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
is_args_unionable(PyObject *args)
|
|
||||||
{
|
|
||||||
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
|
|
||||||
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
|
|
||||||
PyObject *arg = PyTuple_GET_ITEM(args, iarg);
|
|
||||||
if (!is_unionable(arg)) {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"Each union argument must be a type, got %.100R", arg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
_Py_union_type_or(PyObject* self, PyObject* other)
|
_Py_union_type_or(PyObject* self, PyObject* other)
|
||||||
{
|
{
|
||||||
|
@ -362,47 +347,14 @@ error:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
union_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
||||||
{
|
|
||||||
unionobject *alias = (unionobject *)self;
|
|
||||||
PyObject* from_args = PyObject_GetAttrString(self, "_from_args");
|
|
||||||
if (from_args == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Py_BuildValue("N(O)", from_args, alias->args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyMemberDef union_members[] = {
|
static PyMemberDef union_members[] = {
|
||||||
{"__args__", T_OBJECT, offsetof(unionobject, args), READONLY},
|
{"__args__", T_OBJECT, offsetof(unionobject, args), READONLY},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
union_from_args(PyObject *cls, PyObject *args)
|
|
||||||
{
|
|
||||||
if (!PyTuple_CheckExact(args)) {
|
|
||||||
_PyArg_BadArgument("Union._from_args", "argument 'args'", "tuple", args);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!PyTuple_GET_SIZE(args)) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "args must be not empty");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_args_unionable(args)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return make_union(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyMethodDef union_methods[] = {
|
static PyMethodDef union_methods[] = {
|
||||||
{"_from_args", union_from_args, METH_O | METH_CLASS},
|
|
||||||
{"__instancecheck__", union_instancecheck, METH_O},
|
{"__instancecheck__", union_instancecheck, METH_O},
|
||||||
{"__subclasscheck__", union_subclasscheck, METH_O},
|
{"__subclasscheck__", union_subclasscheck, METH_O},
|
||||||
{"__reduce__", union_reduce, METH_NOARGS},
|
|
||||||
{0}};
|
{0}};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue