Skip to content

Commit ac8be1a

Browse files
committed
py_panda: fix int-to-enum conversion in Python 2
Fixes panda3d#498
1 parent 4bf1092 commit ac8be1a

File tree

1 file changed

+55
-3
lines changed

1 file changed

+55
-3
lines changed

dtool/src/interrogatedb/py_panda.cxx

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,43 @@ PyObject *_Dtool_Return(PyObject *value) {
286286
}
287287

288288
#if PY_VERSION_HEX < 0x03040000
289+
/**
290+
* This function converts an int value to the appropriate enum instance.
291+
*/
292+
PyObject *Dtool_EnumType_New(PyTypeObject *subtype, PyObject *args, PyObject *kwds) {
293+
PyObject *arg;
294+
if (!Dtool_ExtractArg(&arg, args, kwds, "value")) {
295+
return PyErr_Format(PyExc_TypeError,
296+
"%s() missing 1 required argument: 'value'",
297+
subtype->tp_name);
298+
}
299+
300+
if (Py_TYPE(arg) == subtype) {
301+
Py_INCREF(arg);
302+
return arg;
303+
}
304+
305+
PyObject *value2member = PyDict_GetItemString(subtype->tp_dict, "_value2member_map_");
306+
nassertr_always(value2member != nullptr, nullptr);
307+
308+
PyObject *member = PyDict_GetItem(value2member, arg);
309+
if (member != nullptr) {
310+
Py_INCREF(member);
311+
return member;
312+
}
313+
314+
PyObject *repr = PyObject_Repr(arg);
315+
PyErr_Format(PyExc_ValueError, "%s is not a valid %s",
316+
#if PY_MAJOR_VERSION >= 3
317+
PyUnicode_AS_STRING(repr),
318+
#else
319+
PyString_AS_STRING(repr),
320+
#endif
321+
subtype->tp_name);
322+
Py_DECREF(repr);
323+
return nullptr;
324+
}
325+
289326
static PyObject *Dtool_EnumType_Str(PyObject *self) {
290327
PyObject *name = PyObject_GetAttrString(self, "name");
291328
#if PY_MAJOR_VERSION >= 3
@@ -337,18 +374,21 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha
337374
static PyObject *name_sunder_str;
338375
static PyObject *value_str;
339376
static PyObject *value_sunder_str;
377+
static PyObject *value2member_map_sunder_str;
340378
// Emulate something vaguely like the enum module.
341379
if (enum_class == nullptr) {
342380
#if PY_MAJOR_VERSION >= 3
343381
name_str = PyUnicode_InternFromString("name");
344382
value_str = PyUnicode_InternFromString("value");
345383
name_sunder_str = PyUnicode_InternFromString("_name_");
346384
value_sunder_str = PyUnicode_InternFromString("_value_");
385+
value2member_map_sunder_str = PyUnicode_InternFromString("_value2member_map_");
347386
#else
348387
name_str = PyString_InternFromString("name");
349388
value_str = PyString_InternFromString("value");
350389
name_sunder_str = PyString_InternFromString("_name_");
351390
value_sunder_str = PyString_InternFromString("_value_");
391+
value2member_map_sunder_str = PyString_InternFromString("_value2member_map_");
352392
#endif
353393
PyObject *name_value_tuple = PyTuple_New(4);
354394
PyTuple_SET_ITEM(name_value_tuple, 0, name_str);
@@ -365,27 +405,39 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha
365405
enum_class = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s()N", "Enum", slots_dict);
366406
nassertr(enum_class != nullptr, nullptr);
367407
}
368-
PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, PyDict_New());
408+
409+
// Create a subclass of this generic Enum class we just created.
410+
PyObject *value2member = PyDict_New();
411+
PyObject *dict = PyDict_New();
412+
PyDict_SetItem(dict, value2member_map_sunder_str, value2member);
413+
PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, dict);
369414
nassertr(result != nullptr, nullptr);
370415

416+
((PyTypeObject *)result)->tp_new = Dtool_EnumType_New;
371417
((PyTypeObject *)result)->tp_str = Dtool_EnumType_Str;
372418
((PyTypeObject *)result)->tp_repr = Dtool_EnumType_Repr;
373419

374-
// Copy the names as instances of the above to the class dict.
420+
PyObject *empty_tuple = PyTuple_New(0);
421+
422+
// Copy the names as instances of the above to the class dict, and create a
423+
// reverse mapping in the _value2member_map_ dict.
375424
Py_ssize_t size = PyTuple_GET_SIZE(names);
376425
for (Py_ssize_t i = 0; i < size; ++i) {
377426
PyObject *item = PyTuple_GET_ITEM(names, i);
378427
PyObject *name = PyTuple_GET_ITEM(item, 0);
379428
PyObject *value = PyTuple_GET_ITEM(item, 1);
380-
PyObject *member = _PyObject_CallNoArg(result);
429+
PyObject *member = PyType_GenericNew((PyTypeObject *)result, empty_tuple, nullptr);
381430
PyObject_SetAttr(member, name_str, name);
382431
PyObject_SetAttr(member, name_sunder_str, name);
383432
PyObject_SetAttr(member, value_str, value);
384433
PyObject_SetAttr(member, value_sunder_str, value);
385434
PyObject_SetAttr(result, name, member);
435+
PyDict_SetItem(value2member, value, member);
386436
Py_DECREF(member);
387437
}
388438
Py_DECREF(names);
439+
Py_DECREF(value2member);
440+
Py_DECREF(empty_tuple);
389441
#endif
390442

391443
if (module != nullptr) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy