[3.10] bpo-44676: Add ability to serialize types.Union (GH-27244) (GH-27333)

(cherry picked from commit fe13f0b0f696464dd6f283576668dbf57cb11399)

Co-authored-by: Yurii Karabas <1998uriyyo@gmail.com>
diff --git a/Objects/unionobject.c b/Objects/unionobject.c
index 659346a..54cdeda 100644
--- a/Objects/unionobject.c
+++ b/Objects/unionobject.c
@@ -299,6 +299,24 @@ is_unionable(PyObject *obj)
     return 0;
 }
 
+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);
+        int is_arg_unionable = is_unionable(arg);
+        if (is_arg_unionable <= 0) {
+            if (is_arg_unionable == 0) {
+                PyErr_Format(PyExc_TypeError,
+                             "Each union argument must be a type, got %.100R", arg);
+            }
+            return 0;
+        }
+    }
+    return 1;
+}
+
 PyObject *
 _Py_union_type_or(PyObject* self, PyObject* other)
 {
@@ -418,14 +436,47 @@ union_repr(PyObject *self)
     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[] = {
         {"__args__", T_OBJECT, offsetof(unionobject, args), READONLY},
         {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) <= 0) {
+        return NULL;
+    }
+
+    return make_union(args);
+}
+
 static PyMethodDef union_methods[] = {
+        {"_from_args", union_from_args, METH_O | METH_CLASS},
         {"__instancecheck__", union_instancecheck, METH_O},
         {"__subclasscheck__", union_subclasscheck, METH_O},
+        {"__reduce__", union_reduce, METH_NOARGS},
         {0}};