blob: b3a65068626189df623fefa0379aaee2e88d5c0f [file] [log] [blame]
Maggie Moss1b4552c2020-09-09 13:23:24 -07001// types.Union -- used to represent e.g. Union[int, str], int | str
2#include "Python.h"
Miss Islington (bot)08561342021-07-03 06:33:16 -07003#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
Maggie Moss1b4552c2020-09-09 13:23:24 -07004#include "pycore_unionobject.h"
5#include "structmember.h"
6
7
8typedef struct {
9 PyObject_HEAD
10 PyObject *args;
Serhiy Storchaka2d055ce2021-07-17 22:14:57 +030011 PyObject *parameters;
Maggie Moss1b4552c2020-09-09 13:23:24 -070012} unionobject;
13
14static void
15unionobject_dealloc(PyObject *self)
16{
17 unionobject *alias = (unionobject *)self;
18
Miss Islington (bot)08561342021-07-03 06:33:16 -070019 _PyObject_GC_UNTRACK(self);
20
Maggie Moss1b4552c2020-09-09 13:23:24 -070021 Py_XDECREF(alias->args);
Serhiy Storchaka2d055ce2021-07-17 22:14:57 +030022 Py_XDECREF(alias->parameters);
Neil Schemenauer0564aaf2020-10-27 11:55:52 -070023 Py_TYPE(self)->tp_free(self);
Maggie Moss1b4552c2020-09-09 13:23:24 -070024}
25
Miss Islington (bot)08561342021-07-03 06:33:16 -070026static int
27union_traverse(PyObject *self, visitproc visit, void *arg)
28{
29 unionobject *alias = (unionobject *)self;
30 Py_VISIT(alias->args);
Serhiy Storchaka2d055ce2021-07-17 22:14:57 +030031 Py_VISIT(alias->parameters);
Miss Islington (bot)08561342021-07-03 06:33:16 -070032 return 0;
33}
34
Maggie Moss1b4552c2020-09-09 13:23:24 -070035static Py_hash_t
36union_hash(PyObject *self)
37{
38 unionobject *alias = (unionobject *)self;
Miss Islington (bot)70598802021-07-16 02:02:59 -070039 PyObject *args = PyFrozenSet_New(alias->args);
40 if (args == NULL) {
41 return (Py_hash_t)-1;
Maggie Moss1b4552c2020-09-09 13:23:24 -070042 }
Miss Islington (bot)70598802021-07-16 02:02:59 -070043 Py_hash_t hash = PyObject_Hash(args);
44 Py_DECREF(args);
45 return hash;
Maggie Moss1b4552c2020-09-09 13:23:24 -070046}
47
48static int
49is_generic_alias_in_args(PyObject *args) {
50 Py_ssize_t nargs = PyTuple_GET_SIZE(args);
51 for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
52 PyObject *arg = PyTuple_GET_ITEM(args, iarg);
Ken Jin49cd68f2021-01-03 00:19:15 +080053 if (PyObject_TypeCheck(arg, &Py_GenericAliasType)) {
Maggie Moss1b4552c2020-09-09 13:23:24 -070054 return 0;
55 }
56 }
57 return 1;
58}
59
60static PyObject *
61union_instancecheck(PyObject *self, PyObject *instance)
62{
63 unionobject *alias = (unionobject *) self;
64 Py_ssize_t nargs = PyTuple_GET_SIZE(alias->args);
65 if (!is_generic_alias_in_args(alias->args)) {
66 PyErr_SetString(PyExc_TypeError,
67 "isinstance() argument 2 cannot contain a parameterized generic");
68 return NULL;
69 }
70 for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
71 PyObject *arg = PyTuple_GET_ITEM(alias->args, iarg);
Miss Islington (bot)b42eee72021-07-13 21:55:45 -070072 if (PyType_Check(arg)) {
73 int res = PyObject_IsInstance(instance, arg);
74 if (res < 0) {
75 return NULL;
76 }
77 if (res) {
78 Py_RETURN_TRUE;
79 }
Maggie Moss1b4552c2020-09-09 13:23:24 -070080 }
81 }
82 Py_RETURN_FALSE;
83}
84
85static PyObject *
86union_subclasscheck(PyObject *self, PyObject *instance)
87{
88 if (!PyType_Check(instance)) {
89 PyErr_SetString(PyExc_TypeError, "issubclass() arg 1 must be a class");
90 return NULL;
91 }
92 unionobject *alias = (unionobject *)self;
93 if (!is_generic_alias_in_args(alias->args)) {
94 PyErr_SetString(PyExc_TypeError,
95 "issubclass() argument 2 cannot contain a parameterized generic");
96 return NULL;
97 }
98 Py_ssize_t nargs = PyTuple_GET_SIZE(alias->args);
99 for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
100 PyObject *arg = PyTuple_GET_ITEM(alias->args, iarg);
Miss Islington (bot)b42eee72021-07-13 21:55:45 -0700101 if (PyType_Check(arg)) {
102 int res = PyObject_IsSubclass(instance, arg);
103 if (res < 0) {
104 return NULL;
105 }
106 if (res) {
107 Py_RETURN_TRUE;
108 }
Maggie Moss1b4552c2020-09-09 13:23:24 -0700109 }
110 }
111 Py_RETURN_FALSE;
112}
113
114static int
115is_typing_module(PyObject *obj) {
116 PyObject *module = PyObject_GetAttrString(obj, "__module__");
117 if (module == NULL) {
118 return -1;
119 }
120 int is_typing = PyUnicode_Check(module) && _PyUnicode_EqualToASCIIString(module, "typing");
121 Py_DECREF(module);
122 return is_typing;
123}
124
125static int
126is_typing_name(PyObject *obj, char *name)
127{
128 PyTypeObject *type = Py_TYPE(obj);
129 if (strcmp(type->tp_name, name) != 0) {
130 return 0;
131 }
Miss Islington (bot)cc1a47c2021-07-15 00:25:22 -0700132 return is_typing_module((PyObject *)type);
Maggie Moss1b4552c2020-09-09 13:23:24 -0700133}
134
135static PyObject *
136union_richcompare(PyObject *a, PyObject *b, int op)
137{
138 PyObject *result = NULL;
139 if (op != Py_EQ && op != Py_NE) {
140 result = Py_NotImplemented;
141 Py_INCREF(result);
142 return result;
143 }
144
145 PyTypeObject *type = Py_TYPE(b);
146
147 PyObject* a_set = PySet_New(((unionobject*)a)->args);
148 if (a_set == NULL) {
149 return NULL;
150 }
151 PyObject* b_set = PySet_New(NULL);
152 if (b_set == NULL) {
153 goto exit;
154 }
155
156 // Populate b_set with the data from the right object
157 int is_typing_union = is_typing_name(b, "_UnionGenericAlias");
158 if (is_typing_union < 0) {
159 goto exit;
160 }
161 if (is_typing_union) {
162 PyObject *b_args = PyObject_GetAttrString(b, "__args__");
163 if (b_args == NULL) {
164 goto exit;
165 }
166 if (!PyTuple_CheckExact(b_args)) {
167 Py_DECREF(b_args);
168 PyErr_SetString(PyExc_TypeError, "__args__ argument of typing.Union object is not a tuple");
169 goto exit;
170 }
171 Py_ssize_t b_arg_length = PyTuple_GET_SIZE(b_args);
172 for (Py_ssize_t i = 0; i < b_arg_length; i++) {
173 PyObject* arg = PyTuple_GET_ITEM(b_args, i);
Maggie Moss1b4552c2020-09-09 13:23:24 -0700174 if (PySet_Add(b_set, arg) == -1) {
175 Py_DECREF(b_args);
176 goto exit;
177 }
178 }
179 Py_DECREF(b_args);
180 } else if (type == &_Py_UnionType) {
181 PyObject* args = ((unionobject*) b)->args;
182 Py_ssize_t arg_length = PyTuple_GET_SIZE(args);
183 for (Py_ssize_t i = 0; i < arg_length; i++) {
184 PyObject* arg = PyTuple_GET_ITEM(args, i);
185 if (PySet_Add(b_set, arg) == -1) {
186 goto exit;
187 }
188 }
189 } else {
Serhiy Storchakac3007ab2021-07-16 14:48:20 +0300190 Py_DECREF(a_set);
191 Py_DECREF(b_set);
192 Py_RETURN_NOTIMPLEMENTED;
Maggie Moss1b4552c2020-09-09 13:23:24 -0700193 }
194 result = PyObject_RichCompare(a_set, b_set, op);
195exit:
196 Py_XDECREF(a_set);
197 Py_XDECREF(b_set);
198 return result;
199}
200
201static PyObject*
202flatten_args(PyObject* args)
203{
Victor Stinnerd67de0a2020-09-23 23:25:54 +0200204 Py_ssize_t arg_length = PyTuple_GET_SIZE(args);
205 Py_ssize_t total_args = 0;
Maggie Moss1b4552c2020-09-09 13:23:24 -0700206 // Get number of total args once it's flattened.
207 for (Py_ssize_t i = 0; i < arg_length; i++) {
208 PyObject *arg = PyTuple_GET_ITEM(args, i);
209 PyTypeObject* arg_type = Py_TYPE(arg);
210 if (arg_type == &_Py_UnionType) {
211 total_args += PyTuple_GET_SIZE(((unionobject*) arg)->args);
212 } else {
213 total_args++;
214 }
215 }
216 // Create new tuple of flattened args.
217 PyObject *flattened_args = PyTuple_New(total_args);
218 if (flattened_args == NULL) {
219 return NULL;
220 }
221 Py_ssize_t pos = 0;
222 for (Py_ssize_t i = 0; i < arg_length; i++) {
223 PyObject *arg = PyTuple_GET_ITEM(args, i);
224 PyTypeObject* arg_type = Py_TYPE(arg);
225 if (arg_type == &_Py_UnionType) {
226 PyObject* nested_args = ((unionobject*)arg)->args;
Victor Stinnerd73cf7c2020-09-26 12:48:41 +0200227 Py_ssize_t nested_arg_length = PyTuple_GET_SIZE(nested_args);
228 for (Py_ssize_t j = 0; j < nested_arg_length; j++) {
Maggie Moss1b4552c2020-09-09 13:23:24 -0700229 PyObject* nested_arg = PyTuple_GET_ITEM(nested_args, j);
230 Py_INCREF(nested_arg);
231 PyTuple_SET_ITEM(flattened_args, pos, nested_arg);
232 pos++;
233 }
234 } else {
Serhiy Storchaka6dec5252021-07-15 10:15:14 +0300235 if (arg == Py_None) {
236 arg = (PyObject *)&_PyNone_Type;
237 }
Maggie Moss1b4552c2020-09-09 13:23:24 -0700238 Py_INCREF(arg);
239 PyTuple_SET_ITEM(flattened_args, pos, arg);
240 pos++;
241 }
242 }
243 return flattened_args;
244}
245
246static PyObject*
247dedup_and_flatten_args(PyObject* args)
248{
249 args = flatten_args(args);
250 if (args == NULL) {
251 return NULL;
252 }
253 Py_ssize_t arg_length = PyTuple_GET_SIZE(args);
254 PyObject *new_args = PyTuple_New(arg_length);
255 if (new_args == NULL) {
256 return NULL;
257 }
258 // Add unique elements to an array.
Victor Stinnerd73cf7c2020-09-26 12:48:41 +0200259 Py_ssize_t added_items = 0;
Maggie Moss1b4552c2020-09-09 13:23:24 -0700260 for (Py_ssize_t i = 0; i < arg_length; i++) {
261 int is_duplicate = 0;
262 PyObject* i_element = PyTuple_GET_ITEM(args, i);
Serhiy Storchaka80844d12021-07-16 16:42:04 +0300263 for (Py_ssize_t j = 0; j < added_items; j++) {
264 PyObject* j_element = PyTuple_GET_ITEM(new_args, j);
kj463c7d32020-12-14 02:38:24 +0800265 int is_ga = PyObject_TypeCheck(i_element, &Py_GenericAliasType) &&
266 PyObject_TypeCheck(j_element, &Py_GenericAliasType);
kj4eb41d02020-11-09 12:00:13 +0800267 // RichCompare to also deduplicate GenericAlias types (slower)
268 is_duplicate = is_ga ? PyObject_RichCompareBool(i_element, j_element, Py_EQ)
269 : i_element == j_element;
270 // Should only happen if RichCompare fails
271 if (is_duplicate < 0) {
272 Py_DECREF(args);
273 Py_DECREF(new_args);
274 return NULL;
Maggie Moss1b4552c2020-09-09 13:23:24 -0700275 }
kj4eb41d02020-11-09 12:00:13 +0800276 if (is_duplicate)
277 break;
Maggie Moss1b4552c2020-09-09 13:23:24 -0700278 }
279 if (!is_duplicate) {
280 Py_INCREF(i_element);
281 PyTuple_SET_ITEM(new_args, added_items, i_element);
282 added_items++;
283 }
284 }
285 Py_DECREF(args);
286 _PyTuple_Resize(&new_args, added_items);
287 return new_args;
288}
289
290static int
291is_typevar(PyObject *obj)
292{
293 return is_typing_name(obj, "TypeVar");
294}
295
296static int
297is_special_form(PyObject *obj)
298{
299 return is_typing_name(obj, "_SpecialForm");
300}
301
302static int
303is_new_type(PyObject *obj)
304{
305 PyTypeObject *type = Py_TYPE(obj);
306 if (type != &PyFunction_Type) {
307 return 0;
308 }
309 return is_typing_module(obj);
310}
311
Miss Islington (bot)7e6cad72021-06-23 02:38:49 -0700312// Emulates short-circuiting behavior of the ``||`` operator
313// while also checking negative values.
314#define CHECK_RES(res) { \
315 int result = res; \
316 if (result) { \
317 return result; \
318 } \
319}
320
321// Returns 1 on true, 0 on false, and -1 on error.
Maggie Moss1b4552c2020-09-09 13:23:24 -0700322static int
323is_unionable(PyObject *obj)
324{
325 if (obj == Py_None) {
326 return 1;
327 }
328 PyTypeObject *type = Py_TYPE(obj);
Miss Islington (bot)7e6cad72021-06-23 02:38:49 -0700329 CHECK_RES(is_typevar(obj));
330 CHECK_RES(is_new_type(obj));
331 CHECK_RES(is_special_form(obj));
Maggie Moss1b4552c2020-09-09 13:23:24 -0700332 return (
Miss Islington (bot)7e6cad72021-06-23 02:38:49 -0700333 // The following checks never fail.
Maggie Moss1b4552c2020-09-09 13:23:24 -0700334 PyType_Check(obj) ||
kj463c7d32020-12-14 02:38:24 +0800335 PyObject_TypeCheck(obj, &Py_GenericAliasType) ||
Maggie Moss1b4552c2020-09-09 13:23:24 -0700336 type == &_Py_UnionType);
337}
338
kj4eb41d02020-11-09 12:00:13 +0800339PyObject *
340_Py_union_type_or(PyObject* self, PyObject* param)
Maggie Moss1b4552c2020-09-09 13:23:24 -0700341{
342 PyObject *tuple = PyTuple_Pack(2, self, param);
343 if (tuple == NULL) {
344 return NULL;
345 }
346 PyObject *new_union = _Py_Union(tuple);
347 Py_DECREF(tuple);
348 return new_union;
349}
350
351static int
352union_repr_item(_PyUnicodeWriter *writer, PyObject *p)
353{
354 _Py_IDENTIFIER(__module__);
355 _Py_IDENTIFIER(__qualname__);
356 _Py_IDENTIFIER(__origin__);
357 _Py_IDENTIFIER(__args__);
358 PyObject *qualname = NULL;
359 PyObject *module = NULL;
Serhiy Storchaka98c44332020-10-10 22:23:42 +0300360 PyObject *tmp;
Maggie Moss1b4552c2020-09-09 13:23:24 -0700361 PyObject *r = NULL;
362 int err;
363
Serhiy Storchaka6dec5252021-07-15 10:15:14 +0300364 if (p == (PyObject *)&_PyNone_Type) {
365 return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4);
366 }
367
Serhiy Storchaka98c44332020-10-10 22:23:42 +0300368 if (_PyObject_LookupAttrId(p, &PyId___origin__, &tmp) < 0) {
Maggie Moss1b4552c2020-09-09 13:23:24 -0700369 goto exit;
370 }
371
Serhiy Storchaka98c44332020-10-10 22:23:42 +0300372 if (tmp) {
373 Py_DECREF(tmp);
374 if (_PyObject_LookupAttrId(p, &PyId___args__, &tmp) < 0) {
Maggie Moss1b4552c2020-09-09 13:23:24 -0700375 goto exit;
376 }
Serhiy Storchaka98c44332020-10-10 22:23:42 +0300377 if (tmp) {
Maggie Moss1b4552c2020-09-09 13:23:24 -0700378 // It looks like a GenericAlias
Serhiy Storchaka98c44332020-10-10 22:23:42 +0300379 Py_DECREF(tmp);
Maggie Moss1b4552c2020-09-09 13:23:24 -0700380 goto use_repr;
381 }
382 }
383
384 if (_PyObject_LookupAttrId(p, &PyId___qualname__, &qualname) < 0) {
385 goto exit;
386 }
387 if (qualname == NULL) {
388 goto use_repr;
389 }
390 if (_PyObject_LookupAttrId(p, &PyId___module__, &module) < 0) {
391 goto exit;
392 }
393 if (module == NULL || module == Py_None) {
394 goto use_repr;
395 }
396
397 // Looks like a class
398 if (PyUnicode_Check(module) &&
399 _PyUnicode_EqualToASCIIString(module, "builtins"))
400 {
401 // builtins don't need a module name
402 r = PyObject_Str(qualname);
403 goto exit;
404 }
405 else {
406 r = PyUnicode_FromFormat("%S.%S", module, qualname);
407 goto exit;
408 }
409
410use_repr:
411 r = PyObject_Repr(p);
412exit:
413 Py_XDECREF(qualname);
414 Py_XDECREF(module);
415 if (r == NULL) {
416 return -1;
417 }
418 err = _PyUnicodeWriter_WriteStr(writer, r);
419 Py_DECREF(r);
420 return err;
421}
422
423static PyObject *
424union_repr(PyObject *self)
425{
426 unionobject *alias = (unionobject *)self;
427 Py_ssize_t len = PyTuple_GET_SIZE(alias->args);
428
429 _PyUnicodeWriter writer;
430 _PyUnicodeWriter_Init(&writer);
431 for (Py_ssize_t i = 0; i < len; i++) {
432 if (i > 0 && _PyUnicodeWriter_WriteASCIIString(&writer, " | ", 3) < 0) {
433 goto error;
434 }
435 PyObject *p = PyTuple_GET_ITEM(alias->args, i);
436 if (union_repr_item(&writer, p) < 0) {
437 goto error;
438 }
439 }
440 return _PyUnicodeWriter_Finish(&writer);
441error:
442 _PyUnicodeWriter_Dealloc(&writer);
443 return NULL;
444}
445
446static PyMemberDef union_members[] = {
447 {"__args__", T_OBJECT, offsetof(unionobject, args), READONLY},
448 {0}
449};
450
451static PyMethodDef union_methods[] = {
452 {"__instancecheck__", union_instancecheck, METH_O},
453 {"__subclasscheck__", union_subclasscheck, METH_O},
454 {0}};
455
Serhiy Storchaka2d055ce2021-07-17 22:14:57 +0300456
457static PyObject *
458union_getitem(PyObject *self, PyObject *item)
459{
460 unionobject *alias = (unionobject *)self;
461 // Populate __parameters__ if needed.
462 if (alias->parameters == NULL) {
463 alias->parameters = _Py_make_parameters(alias->args);
464 if (alias->parameters == NULL) {
465 return NULL;
466 }
467 }
468
469 PyObject *newargs = _Py_subs_parameters(self, alias->args, alias->parameters, item);
470 if (newargs == NULL) {
471 return NULL;
472 }
473
474 PyObject *res = _Py_Union(newargs);
475
476 Py_DECREF(newargs);
477 return res;
478}
479
480static PyMappingMethods union_as_mapping = {
481 .mp_subscript = union_getitem,
482};
483
484static PyObject *
485union_parameters(PyObject *self, void *Py_UNUSED(unused))
486{
487 unionobject *alias = (unionobject *)self;
488 if (alias->parameters == NULL) {
489 alias->parameters = _Py_make_parameters(alias->args);
490 if (alias->parameters == NULL) {
491 return NULL;
492 }
493 }
494 Py_INCREF(alias->parameters);
495 return alias->parameters;
496}
497
498static PyGetSetDef union_properties[] = {
499 {"__parameters__", union_parameters, (setter)NULL, "Type variables in the types.Union.", NULL},
500 {0}
501};
502
Maggie Moss1b4552c2020-09-09 13:23:24 -0700503static PyNumberMethods union_as_number = {
kj4eb41d02020-11-09 12:00:13 +0800504 .nb_or = _Py_union_type_or, // Add __or__ function
Maggie Moss1b4552c2020-09-09 13:23:24 -0700505};
506
507PyTypeObject _Py_UnionType = {
508 PyVarObject_HEAD_INIT(&PyType_Type, 0)
509 .tp_name = "types.Union",
510 .tp_doc = "Represent a PEP 604 union type\n"
511 "\n"
512 "E.g. for int | str",
513 .tp_basicsize = sizeof(unionobject),
514 .tp_dealloc = unionobject_dealloc,
515 .tp_alloc = PyType_GenericAlloc,
Miss Islington (bot)08561342021-07-03 06:33:16 -0700516 .tp_free = PyObject_GC_Del,
517 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
518 .tp_traverse = union_traverse,
Maggie Moss1b4552c2020-09-09 13:23:24 -0700519 .tp_hash = union_hash,
520 .tp_getattro = PyObject_GenericGetAttr,
521 .tp_members = union_members,
522 .tp_methods = union_methods,
523 .tp_richcompare = union_richcompare,
Serhiy Storchaka2d055ce2021-07-17 22:14:57 +0300524 .tp_as_mapping = &union_as_mapping,
Maggie Moss1b4552c2020-09-09 13:23:24 -0700525 .tp_as_number = &union_as_number,
526 .tp_repr = union_repr,
Serhiy Storchaka2d055ce2021-07-17 22:14:57 +0300527 .tp_getset = union_properties,
Maggie Moss1b4552c2020-09-09 13:23:24 -0700528};
529
530PyObject *
531_Py_Union(PyObject *args)
532{
533 assert(PyTuple_CheckExact(args));
534
535 unionobject* result = NULL;
536
537 // Check arguments are unionable.
Victor Stinnerd67de0a2020-09-23 23:25:54 +0200538 Py_ssize_t nargs = PyTuple_GET_SIZE(args);
Maggie Moss1b4552c2020-09-09 13:23:24 -0700539 for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
540 PyObject *arg = PyTuple_GET_ITEM(args, iarg);
541 if (arg == NULL) {
542 return NULL;
543 }
544 int is_arg_unionable = is_unionable(arg);
545 if (is_arg_unionable < 0) {
546 return NULL;
547 }
548 if (!is_arg_unionable) {
549 Py_INCREF(Py_NotImplemented);
550 return Py_NotImplemented;
551 }
552 }
553
Serhiy Storchakac3007ab2021-07-16 14:48:20 +0300554 args = dedup_and_flatten_args(args);
555 if (args == NULL) {
556 return NULL;
557 }
558 if (PyTuple_GET_SIZE(args) == 1) {
559 PyObject *result1 = PyTuple_GET_ITEM(args, 0);
560 Py_INCREF(result1);
561 Py_DECREF(args);
562 return result1;
563 }
564
Miss Islington (bot)08561342021-07-03 06:33:16 -0700565 result = PyObject_GC_New(unionobject, &_Py_UnionType);
Maggie Moss1b4552c2020-09-09 13:23:24 -0700566 if (result == NULL) {
Serhiy Storchakac3007ab2021-07-16 14:48:20 +0300567 Py_DECREF(args);
Maggie Moss1b4552c2020-09-09 13:23:24 -0700568 return NULL;
569 }
570
Serhiy Storchaka2d055ce2021-07-17 22:14:57 +0300571 result->parameters = NULL;
Serhiy Storchakac3007ab2021-07-16 14:48:20 +0300572 result->args = args;
Miss Islington (bot)000b9e82021-07-03 13:51:10 -0700573 _PyObject_GC_TRACK(result);
Maggie Moss1b4552c2020-09-09 13:23:24 -0700574 return (PyObject*)result;
575}