| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,6 +13,9 @@ | |||
| 13 | 13 | import weakref | |
| 14 | 14 | import typing | |
| 15 | 15 | ||
| 16 | + | ||
| 17 | + T = typing.TypeVar("T") | ||
| 18 | + | ||
| 16 | 19 | class Example: | |
| 17 | 20 | pass | |
| 18 | 21 | ||
@@ -802,6 +805,38 @@ def eq(actual, expected): | |||
| 802 | 805 | eq(x[NT], int | NT | bytes) | |
| 803 | 806 | eq(x[S], int | S | bytes) | |
| 804 | 807 | ||
| 808 | + def test_union_pickle(self): | ||
| 809 | + alias = list[T] | int | ||
| 810 | + s = pickle.dumps(alias) | ||
| 811 | + loaded = pickle.loads(s) | ||
| 812 | + self.assertEqual(alias, loaded) | ||
| 813 | + self.assertEqual(alias.__args__, loaded.__args__) | ||
| 814 | + self.assertEqual(alias.__parameters__, loaded.__parameters__) | ||
| 815 | + | ||
| 816 | + def test_union_from_args(self): | ||
| 817 | + with self.assertRaisesRegex( | ||
| 818 | + TypeError, | ||
| 819 | + r"^Each union argument must be a type, got 1$", | ||
| 820 | + ): | ||
| 821 | + types.Union._from_args((1,)) | ||
| 822 | + | ||
| 823 | + with self.assertRaisesRegex( | ||
| 824 | + TypeError, | ||
| 825 | + r"Union._from_args\(\) argument 'args' must be tuple, not int$", | ||
| 826 | + ): | ||
| 827 | + types.Union._from_args(1) | ||
| 828 | + | ||
| 829 | + with self.assertRaisesRegex(ValueError, r"args must be not empty"): | ||
| 830 | + types.Union._from_args(()) | ||
| 831 | + | ||
| 832 | + alias = types.Union._from_args((int, str, T)) | ||
| 833 | + | ||
| 834 | + self.assertEqual(alias.__args__, (int, str, T)) | ||
| 835 | + self.assertEqual(alias.__parameters__, (T,)) | ||
| 836 | + | ||
| 837 | + result = types.Union._from_args((int,)) | ||
| 838 | + self.assertIs(int, result) | ||
| 839 | + | ||
| 805 | 840 | def test_union_parameter_substitution_errors(self): | |
| 806 | 841 | T = typing.TypeVar("T") | |
| 807 | 842 | x = int | T | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -329,7 +329,7 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()): | |||
| 329 | 329 | if isinstance(t, GenericAlias): | |
| 330 | 330 | return GenericAlias(t.__origin__, ev_args) | |
| 331 | 331 | if isinstance(t, types.Union): | |
| 332 | - return functools.reduce(operator.or_, ev_args) | ||
| 332 | + return types.Union._from_args(ev_args) | ||
| 333 | 333 | else: | |
| 334 | 334 | return t.copy_with(ev_args) | |
| 335 | 335 | return t | |
@@ -1808,7 +1808,7 @@ def _strip_annotations(t): | |||
| 1808 | 1808 | stripped_args = tuple(_strip_annotations(a) for a in t.__args__) | |
| 1809 | 1809 | if stripped_args == t.__args__: | |
| 1810 | 1810 | return t | |
| 1811 | - return functools.reduce(operator.or_, stripped_args) | ||
| 1811 | + return types.Union._from_args(stripped_args) | ||
| 1812 | 1812 | ||
| 1813 | 1813 | return t | |
| 1814 | 1814 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Add ability to serialise ``types.Union`` objects. Patch provided by Yurii | ||
| 2 | + Karabas. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -299,6 +299,24 @@ is_unionable(PyObject *obj) | |||
| 299 | 299 | return 0; | |
| 300 | 300 | } | |
| 301 | 301 | ||
| 302 | + static int | ||
| 303 | + is_args_unionable(PyObject *args) | ||
| 304 | + { | ||
| 305 | + Py_ssize_t nargs = PyTuple_GET_SIZE(args); | ||
| 306 | + for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { | ||
| 307 | + PyObject *arg = PyTuple_GET_ITEM(args, iarg); | ||
| 308 | + int is_arg_unionable = is_unionable(arg); | ||
| 309 | + if (is_arg_unionable <= 0) { | ||
| 310 | + if (is_arg_unionable == 0) { | ||
| 311 | + PyErr_Format(PyExc_TypeError, | ||
| 312 | + "Each union argument must be a type, got %.100R", arg); | ||
| 313 | + } | ||
| 314 | + return 0; | ||
| 315 | + } | ||
| 316 | + } | ||
| 317 | + return 1; | ||
| 318 | + } | ||
| 319 | + | ||
| 302 | 320 | PyObject * | |
| 303 | 321 | _Py_union_type_or(PyObject* self, PyObject* other) | |
| 304 | 322 | { | |
@@ -418,14 +436,47 @@ union_repr(PyObject *self) | |||
| 418 | 436 | return NULL; | |
| 419 | 437 | } | |
| 420 | 438 | ||
| 439 | + static PyObject * | ||
| 440 | + union_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) | ||
| 441 | + { | ||
| 442 | + unionobject *alias = (unionobject *)self; | ||
| 443 | + PyObject* from_args = PyObject_GetAttrString(self, "_from_args"); | ||
| 444 | + if (from_args == NULL) { | ||
| 445 | + return NULL; | ||
| 446 | + } | ||
| 447 | + | ||
| 448 | + return Py_BuildValue("O(O)", from_args, alias->args); | ||
| 449 | + } | ||
| 450 | + | ||
| 421 | 451 | static PyMemberDef union_members[] = { | |
| 422 | 452 | {"__args__", T_OBJECT, offsetof(unionobject, args), READONLY}, | |
| 423 | 453 | {0} | |
| 424 | 454 | }; | |
| 425 | 455 | ||
| 456 | + static PyObject * | ||
| 457 | + union_from_args(PyObject *cls, PyObject *args) | ||
| 458 | + { | ||
| 459 | + if (!PyTuple_CheckExact(args)) { | ||
| 460 | + _PyArg_BadArgument("Union._from_args", "argument 'args'", "tuple", args); | ||
| 461 | + return NULL; | ||
| 462 | + } | ||
| 463 | + if (!PyTuple_GET_SIZE(args)) { | ||
| 464 | + PyErr_SetString(PyExc_ValueError, "args must be not empty"); | ||
| 465 | + return NULL; | ||
| 466 | + } | ||
| 467 | + | ||
| 468 | + if (is_args_unionable(args) <= 0) { | ||
| 469 | + return NULL; | ||
| 470 | + } | ||
| 471 | + | ||
| 472 | + return make_union(args); | ||
| 473 | + } | ||
| 474 | + | ||
| 426 | 475 | static PyMethodDef union_methods[] = { | |
| 476 | + {"_from_args", union_from_args, METH_O | METH_CLASS}, | ||
| 427 | 477 | {"__instancecheck__", union_instancecheck, METH_O}, | |
| 428 | 478 | {"__subclasscheck__", union_subclasscheck, METH_O}, | |
| 479 | + {"__reduce__", union_reduce, METH_NOARGS}, | ||
| 429 | 480 | {0}}; | |
| 430 | 481 | ||
| 431 | 482 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments