Browse Source

interrogate: improvements to __setstate__ handling:

* Force single arg variant, easing argument parsing
* Allow defining __setstate__ taking multiple args, leading to tuple unpack
* Allow __setstate__ to be called on already initialized object (useful with __reduce__)
rdb 5 years ago
parent
commit
99f9352e76

+ 3 - 0
dtool/src/interrogate/functionRemap.cxx

@@ -920,6 +920,9 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
             || fname == "__delattr__") {
             || fname == "__delattr__") {
       // Just to prevent these from getting keyword arguments.
       // Just to prevent these from getting keyword arguments.
 
 
+    } else if (fname == "__setstate__") {
+      _args_type = InterfaceMaker::AT_single_arg;
+
     } else {
     } else {
       if (_args_type == InterfaceMaker::AT_varargs) {
       if (_args_type == InterfaceMaker::AT_varargs) {
         // Every other method can take keyword arguments, if they take more
         // Every other method can take keyword arguments, if they take more

+ 23 - 10
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -3616,18 +3616,23 @@ write_function_for_name(ostream &out, Object *obj,
     std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
     std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
     std::string cClassName = obj->_itype.get_true_name();
     std::string cClassName = obj->_itype.get_true_name();
     // string class_name = remap->_cpptype->get_simple_name();
     // string class_name = remap->_cpptype->get_simple_name();
+    CPPStructType *struct_type = obj->_itype._cpptype->as_struct_type();
 
 
     // If this is a non-static __setstate__, we run the default constructor.
     // If this is a non-static __setstate__, we run the default constructor.
-    if (remap->_cppfunc->get_local_name() == "__setstate__") {
-      out << "  if (DtoolInstance_VOID_PTR(self) != nullptr) {\n"
-          << "    Dtool_Raise_TypeError(\"C++ object is already constructed.\");\n";
-      error_return(out, 4, return_flags);
-      out << "  }\n"
-          << "  " << cClassName << " *local_this = new " << cClassName << ";\n"
-          << "  DTool_PyInit_Finalize(self, local_this, "
-          << "((Dtool_PyInstDef *)self)->_My_Type, true, false);\n"
-          << "  if (local_this == nullptr) {\n"
-          << "    PyErr_NoMemory();\n";
+    if (remap->_cppfunc->get_local_name() == "__setstate__" &&
+        !struct_type->is_abstract()) {
+      out << "  " << cClassName << " *local_this = nullptr;\n"
+          << "  if (DtoolInstance_VOID_PTR(self) == nullptr) {\n"
+          << "    local_this = new " << cClassName << ";\n"
+          << "    DTool_PyInit_Finalize(self, local_this, &Dtool_" << ClassName
+          << ", true, false);\n"
+          << "    if (local_this == nullptr) {\n"
+          << "      PyErr_NoMemory();\n";
+      error_return(out, 6, return_flags);
+      out << "    }\n"
+          << "  } else if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", "
+          << "(void **)&local_this, \"" << classNameFromCppName(cClassName, false)
+          << "." << methodNameFromCppName(remap, cClassName, false) << "\")) {\n";
     }
     }
     else if (all_nonconst) {
     else if (all_nonconst) {
       // All remaps are non-const.  Also check that this object isn't const.
       // All remaps are non-const.  Also check that this object isn't const.
@@ -3664,6 +3669,14 @@ write_function_for_name(ostream &out, Object *obj,
     args_type = AT_varargs;
     args_type = AT_varargs;
   }
   }
 
 
+  // If this is a __setstate__ taking multiple arguments, and we're given a
+  // tuple as argument, unpack it.
+  if (args_type == AT_single_arg && max_required_args > 1 &&
+      remap->_cppfunc->get_local_name() == "__setstate__") {
+    out << "  PyObject *args = arg;\n";
+    args_type = AT_varargs;
+  }
+
   if (args_type == AT_keyword_args || args_type == AT_varargs) {
   if (args_type == AT_keyword_args || args_type == AT_varargs) {
     max_required_args = collapse_default_remaps(map_sets, max_required_args);
     max_required_args = collapse_default_remaps(map_sets, max_required_args);
   }
   }