Browse Source

interrogate: support enum class to limited extent in Python 2

Only the basics are supported; the __members__ or iter interface is not supported at this time.

See also #351 for discussion on pulling in enum34 module.
rdb 7 years ago
parent
commit
68e7f681f4

+ 14 - 8
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -1312,15 +1312,19 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
 
 
         CPPType *underlying_type = TypeManager::unwrap_const(object->_itype._cpptype->as_enum_type()->get_underlying_type());
         CPPType *underlying_type = TypeManager::unwrap_const(object->_itype._cpptype->as_enum_type()->get_underlying_type());
         string cast_to = underlying_type->get_local_name(&parser);
         string cast_to = underlying_type->get_local_name(&parser);
-        out << "#if PY_VERSION_HEX >= 0x03040000\n\n";
         out << "  // enum class " << object->_itype.get_scoped_name() << "\n";
         out << "  // enum class " << object->_itype.get_scoped_name() << "\n";
         out << "  {\n";
         out << "  {\n";
         out << "    PyObject *members = PyTuple_New(" << enum_count << ");\n";
         out << "    PyObject *members = PyTuple_New(" << enum_count << ");\n";
         out << "    PyObject *member;\n";
         out << "    PyObject *member;\n";
         for (int xx = 0; xx < enum_count; xx++) {
         for (int xx = 0; xx < enum_count; xx++) {
           out << "    member = PyTuple_New(2);\n"
           out << "    member = PyTuple_New(2);\n"
-                 "    PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
+                 "#if PY_MAJOR_VERSION >= 3\n"
+                 "      PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
               << object->_itype.get_enum_value_name(xx) << "\"));\n"
               << object->_itype.get_enum_value_name(xx) << "\"));\n"
+                 "#else\n"
+                 "      PyTuple_SET_ITEM(member, 0, PyString_FromString(\""
+              << object->_itype.get_enum_value_name(xx) << "\"));\n"
+                 "#endif\n"
                  "    PyTuple_SET_ITEM(member, 1, Dtool_WrapValue(("
                  "    PyTuple_SET_ITEM(member, 1, Dtool_WrapValue(("
               << cast_to << ")" << object->_itype.get_scoped_name() << "::"
               << cast_to << ")" << object->_itype.get_scoped_name() << "::"
               << object->_itype.get_enum_value_name(xx) << "));\n"
               << object->_itype.get_enum_value_name(xx) << "));\n"
@@ -1332,7 +1336,6 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
         out << "    PyModule_AddObject(module, \"" << object->_itype.get_name()
         out << "    PyModule_AddObject(module, \"" << object->_itype.get_name()
             << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n";
             << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n";
         out << "  }\n";
         out << "  }\n";
-        out << "#endif\n";
       } else {
       } else {
         out << "  // enum " << object->_itype.get_scoped_name() << "\n";
         out << "  // enum " << object->_itype.get_scoped_name() << "\n";
         for (int xx = 0; xx < enum_count; xx++) {
         for (int xx = 0; xx < enum_count; xx++) {
@@ -3129,27 +3132,30 @@ write_module_class(ostream &out, Object *obj) {
       int enum_count = nested_obj->_itype.number_of_enum_values();
       int enum_count = nested_obj->_itype.number_of_enum_values();
       CPPType *underlying_type = TypeManager::unwrap_const(nested_obj->_itype._cpptype->as_enum_type()->get_underlying_type());
       CPPType *underlying_type = TypeManager::unwrap_const(nested_obj->_itype._cpptype->as_enum_type()->get_underlying_type());
       string cast_to = underlying_type->get_local_name(&parser);
       string cast_to = underlying_type->get_local_name(&parser);
-      out << "#if PY_VERSION_HEX >= 0x03040000\n\n";
       out << "    // enum class " << nested_obj->_itype.get_scoped_name() << ";\n";
       out << "    // enum class " << nested_obj->_itype.get_scoped_name() << ";\n";
       out << "    {\n";
       out << "    {\n";
       out << "      PyObject *members = PyTuple_New(" << enum_count << ");\n";
       out << "      PyObject *members = PyTuple_New(" << enum_count << ");\n";
       out << "      PyObject *member;\n";
       out << "      PyObject *member;\n";
       for (int xx = 0; xx < enum_count; xx++) {
       for (int xx = 0; xx < enum_count; xx++) {
         out << "      member = PyTuple_New(2);\n"
         out << "      member = PyTuple_New(2);\n"
+               "#if PY_MAJOR_VERSION >= 3\n"
                "      PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
                "      PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
             << nested_obj->_itype.get_enum_value_name(xx) << "\"));\n"
             << nested_obj->_itype.get_enum_value_name(xx) << "\"));\n"
+               "#else\n"
+               "      PyTuple_SET_ITEM(member, 0, PyString_FromString(\""
+            << nested_obj->_itype.get_enum_value_name(xx) << "\"));\n"
+               "#endif\n"
                "      PyTuple_SET_ITEM(member, 1, Dtool_WrapValue(("
                "      PyTuple_SET_ITEM(member, 1, Dtool_WrapValue(("
             << cast_to << ")" << nested_obj->_itype.get_scoped_name() << "::"
             << cast_to << ")" << nested_obj->_itype.get_scoped_name() << "::"
             << nested_obj->_itype.get_enum_value_name(xx) << "));\n"
             << nested_obj->_itype.get_enum_value_name(xx) << "));\n"
                "      PyTuple_SET_ITEM(members, " << xx << ", member);\n";
                "      PyTuple_SET_ITEM(members, " << xx << ", member);\n";
       }
       }
-        out << "      Dtool_Ptr_" << safe_name << " = Dtool_EnumType_Create(\""
-            << nested_obj->_itype.get_name() << "\", members, \""
-            << _def->module_name << "\");\n";
+      out << "      Dtool_Ptr_" << safe_name << " = Dtool_EnumType_Create(\""
+          << nested_obj->_itype.get_name() << "\", members, \""
+          << _def->module_name << "\");\n";
       out << "      PyDict_SetItemString(dict, \"" << nested_obj->_itype.get_name()
       out << "      PyDict_SetItemString(dict, \"" << nested_obj->_itype.get_name()
           << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n";
           << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n";
       out << "    }\n";
       out << "    }\n";
-      out << "#endif\n";
 
 
     } else if (nested_obj->_itype.is_enum()) {
     } else if (nested_obj->_itype.is_enum()) {
       out << "    // enum " << nested_obj->_itype.get_scoped_name() << ";\n";
       out << "    // enum " << nested_obj->_itype.get_scoped_name() << ";\n";

+ 85 - 1
dtool/src/interrogatedb/py_panda.cxx

@@ -306,11 +306,39 @@ PyObject *_Dtool_Return(PyObject *value) {
   return value;
   return value;
 }
 }
 
 
+#if PY_VERSION_HEX < 0x03040000
+static PyObject *Dtool_EnumType_Str(PyObject *self) {
+  PyObject *name = PyObject_GetAttrString(self, "name");
+#if PY_MAJOR_VERSION >= 3
+  PyObject *repr = PyUnicode_FromFormat("%s.%s", Py_TYPE(self)->tp_name, PyString_AS_STRING(name));
+#else
+  PyObject *repr = PyString_FromFormat("%s.%s", Py_TYPE(self)->tp_name, PyString_AS_STRING(name));
+#endif
+  Py_DECREF(name);
+  return repr;
+}
+
+static PyObject *Dtool_EnumType_Repr(PyObject *self) {
+  PyObject *name = PyObject_GetAttrString(self, "name");
+  PyObject *value = PyObject_GetAttrString(self, "value");
+#if PY_MAJOR_VERSION >= 3
+  PyObject *repr = PyUnicode_FromFormat("<%s.%s: %ld>", Py_TYPE(self)->tp_name, PyString_AS_STRING(name), PyLongOrInt_AS_LONG(value));
+#else
+  PyObject *repr = PyString_FromFormat("<%s.%s: %ld>", Py_TYPE(self)->tp_name, PyString_AS_STRING(name), PyLongOrInt_AS_LONG(value));
+#endif
+  Py_DECREF(name);
+  Py_DECREF(value);
+  return repr;
+}
+#endif
+
 /**
 /**
- * Creates a Python 3.4-style enum type.  Steals reference to 'names'.
+ * Creates a Python 3.4-style enum type.  Steals reference to 'names', which
+ * should be a tuple of (name, value) pairs.
  */
  */
 PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *module) {
 PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *module) {
   static PyObject *enum_class = nullptr;
   static PyObject *enum_class = nullptr;
+#if PY_VERSION_HEX >= 0x03040000
   static PyObject *enum_meta = nullptr;
   static PyObject *enum_meta = nullptr;
   static PyObject *enum_create = nullptr;
   static PyObject *enum_create = nullptr;
   if (enum_meta == nullptr) {
   if (enum_meta == nullptr) {
@@ -325,6 +353,62 @@ PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const cha
 
 
   PyObject *result = PyObject_CallFunction(enum_create, (char *)"OsN", enum_class, name, names);
   PyObject *result = PyObject_CallFunction(enum_create, (char *)"OsN", enum_class, name, names);
   nassertr(result != nullptr, nullptr);
   nassertr(result != nullptr, nullptr);
+#else
+  static PyObject *name_str;
+  static PyObject *name_sunder_str;
+  static PyObject *value_str;
+  static PyObject *value_sunder_str;
+  // Emulate something vaguely like the enum module.
+  if (enum_class == nullptr) {
+#if PY_MAJOR_VERSION >= 3
+    name_str = PyUnicode_InternFromString("name");
+    value_str = PyUnicode_InternFromString("value");
+    name_sunder_str = PyUnicode_InternFromString("_name_");
+    value_sunder_str = PyUnicode_InternFromString("_value_");
+#else
+    name_str = PyString_InternFromString("name");
+    value_str = PyString_InternFromString("value");
+    name_sunder_str = PyString_InternFromString("_name_");
+    value_sunder_str = PyString_InternFromString("_value_");
+#endif
+    PyObject *name_value_tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(name_value_tuple, 0, name_str);
+    PyTuple_SET_ITEM(name_value_tuple, 1, value_str);
+    PyTuple_SET_ITEM(name_value_tuple, 2, name_sunder_str);
+    PyTuple_SET_ITEM(name_value_tuple, 3, value_sunder_str);
+    Py_INCREF(name_str);
+    Py_INCREF(value_str);
+
+    PyObject *slots_dict = PyDict_New();
+    PyDict_SetItemString(slots_dict, "__slots__", name_value_tuple);
+    Py_DECREF(name_value_tuple);
+
+    enum_class = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s()N", "Enum", slots_dict);
+    nassertr(enum_class != nullptr, nullptr);
+  }
+  PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, PyDict_New());
+  nassertr(result != nullptr, nullptr);
+
+  ((PyTypeObject *)result)->tp_str = Dtool_EnumType_Str;
+  ((PyTypeObject *)result)->tp_repr = Dtool_EnumType_Repr;
+
+  // Copy the names as instances of the above to the class dict.
+  Py_ssize_t size = PyTuple_GET_SIZE(names);
+  for (Py_ssize_t i = 0; i < size; ++i) {
+    PyObject *item = PyTuple_GET_ITEM(names, i);
+    PyObject *name = PyTuple_GET_ITEM(item, 0);
+    PyObject *value = PyTuple_GET_ITEM(item, 1);
+    PyObject *member = _PyObject_CallNoArg(result);
+    PyObject_SetAttr(member, name_str, name);
+    PyObject_SetAttr(member, name_sunder_str, name);
+    PyObject_SetAttr(member, value_str, value);
+    PyObject_SetAttr(member, value_sunder_str, value);
+    PyObject_SetAttr(result, name, member);
+    Py_DECREF(member);
+  }
+  Py_DECREF(names);
+#endif
+
   if (module != nullptr) {
   if (module != nullptr) {
     PyObject *modstr = PyUnicode_FromString(module);
     PyObject *modstr = PyUnicode_FromString(module);
     PyObject_SetAttrString(result, "__module__", modstr);
     PyObject_SetAttrString(result, "__module__", modstr);

+ 2 - 0
dtool/src/pystub/pystub.cxx

@@ -121,6 +121,7 @@ extern "C" {
   EXPCL_PYSTUB int PyObject_Repr(...);
   EXPCL_PYSTUB int PyObject_Repr(...);
   EXPCL_PYSTUB int PyObject_RichCompareBool(...);
   EXPCL_PYSTUB int PyObject_RichCompareBool(...);
   EXPCL_PYSTUB int PyObject_SelfIter(...);
   EXPCL_PYSTUB int PyObject_SelfIter(...);
+  EXPCL_PYSTUB int PyObject_SetAttr(...);
   EXPCL_PYSTUB int PyObject_SetAttrString(...);
   EXPCL_PYSTUB int PyObject_SetAttrString(...);
   EXPCL_PYSTUB int PyObject_Str(...);
   EXPCL_PYSTUB int PyObject_Str(...);
   EXPCL_PYSTUB int PyObject_Type(...);
   EXPCL_PYSTUB int PyObject_Type(...);
@@ -351,6 +352,7 @@ int PyObject_Malloc(...) { return 0; }
 int PyObject_Repr(...) { return 0; }
 int PyObject_Repr(...) { return 0; }
 int PyObject_RichCompareBool(...) { return 0; }
 int PyObject_RichCompareBool(...) { return 0; }
 int PyObject_SelfIter(...) { return 0; }
 int PyObject_SelfIter(...) { return 0; }
+int PyObject_SetAttr(...) { return 0; }
 int PyObject_SetAttrString(...) { return 0; }
 int PyObject_SetAttrString(...) { return 0; }
 int PyObject_Str(...) { return 0; }
 int PyObject_Str(...) { return 0; }
 int PyObject_Type(...) { return 0; }
 int PyObject_Type(...) { return 0; }