Browse Source

low-level support for python copy and pickle modules

David Rose 16 years ago
parent
commit
816ddc0387

+ 24 - 0
dtool/src/dtoolutil/filename.cxx

@@ -302,6 +302,30 @@ Filename(const Filename &dirname, const Filename &basename) {
   }
 }
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *Filename::
+__reduce__(PyObject *self) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *result = Py_BuildValue("(O(s))", this_class, c_str());
+  Py_DECREF(this_class);
+  return result;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Filename::from_os_specific
 //       Access: Published, Static

+ 4 - 0
dtool/src/dtoolutil/filename.h

@@ -66,6 +66,10 @@ PUBLISHED:
   Filename(const Filename &dirname, const Filename &basename);
   INLINE ~Filename();
 
+#ifdef HAVE_PYTHON
+  PyObject *__reduce__(PyObject *self) const;
+#endif
+
   // Static constructors to explicitly create a filename that refers
   // to a text or binary file.  This is in lieu of calling set_text()
   // or set_binary() or set_type().

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

@@ -297,6 +297,16 @@ make_wrapper_entry(FunctionIndex function_index) {
     assert(!iwrapper._parameters.empty());
     iwrapper._parameters.front()._parameter_flags |=
       InterrogateFunctionWrapper::PF_is_this;
+
+    if (_parameters.size() >= 2 && _parameters[1]._name == "self" &&
+        TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type())) {
+      // Here's a special case.  If the first parameter of a nonstatic
+      // method is a PyObject * called "self", then we will
+      // automatically fill it in from the this pointer, and remove it
+      // from the generated parameter list.
+      _parameters.erase(_parameters.begin() + 1);
+      _flags |= F_explicit_self;
+    }
   }
 
   if (!_void_return) {
@@ -382,6 +392,14 @@ get_call_str(const string &container, const vector_string &pexprs) const {
     }
 
     call << "(";
+    if (_flags & F_explicit_self) {
+      // Pass on the PyObject * that we stripped off above.
+      call << "self";
+      if (_first_true_parameter < (int)_parameters.size()) {
+        call << ", ";
+      }
+    }
+
     int pn = _first_true_parameter;
     if (pn < (int)_parameters.size()) {
       _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs));
@@ -630,6 +648,23 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
         // It receives no parameters, and returns an integer.
         _flags |= F_size;
       }
+
+    } else if (fname == "make_copy" ) {
+      if (_has_this && _parameters.size() == 1 &&
+          TypeManager::is_pointer(_return_type->get_new_type())) {
+        // It receives no parameters, and returns a pointer.
+        _flags |= F_make_copy;
+      }
+    }
+
+  } else if (_type == T_constructor) {
+    if (!_has_this && _parameters.size() == 1) {
+      if (TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) == 
+          TypeManager::unwrap(_return_type->get_orig_type())) {
+        // If this is the only parameter, and it's the same as the
+        // "this" type, this is a copy constructor.
+        _flags |= F_copy_constructor;
+      }
     }
   }
 

+ 8 - 5
dtool/src/interrogate/functionRemap.h

@@ -79,11 +79,14 @@ public:
   };
 
   enum Flags {
-    F_getitem      = 0x0001,
-    F_getitem_int  = 0x0002,
-    F_size         = 0x0004,
-    F_setitem      = 0x0008,
-    F_setitem_int  = 0x0010,
+    F_getitem          = 0x0001,
+    F_getitem_int      = 0x0002,
+    F_size             = 0x0004,
+    F_setitem          = 0x0008,
+    F_setitem_int      = 0x0010,
+    F_make_copy        = 0x0020,
+    F_copy_constructor = 0x0040,
+    F_explicit_self    = 0x0080,
   };
 
   typedef vector<Parameter> Parameters;

+ 14 - 0
dtool/src/interrogate/interfaceMaker.cxx

@@ -123,6 +123,10 @@ check_protocols() {
   int flags = 0;
 
   Functions::const_iterator fi;
+  for (fi = _constructors.begin(); fi != _constructors.end(); ++fi) {
+    Function *func = (*fi);
+    flags |= func->_flags;
+  }
   for (fi = _methods.begin(); fi != _methods.end(); ++fi) {
     Function *func = (*fi);
     flags |= func->_flags;
@@ -140,6 +144,16 @@ check_protocols() {
     _protocol_types |= PT_mapping;
   }
 
+  if (flags & FunctionRemap::F_make_copy) {
+    // It's not exactly a protocol, but if we have a make_copy()
+    // method, we can use it to synthesize a __copy__ and __deepcopy__
+    // Python method to support the copy module.
+    _protocol_types |= PT_make_copy;
+  } else if (flags & FunctionRemap::F_copy_constructor) {
+    // Ditto for the copy constructor.
+    _protocol_types |= PT_copy_constructor;
+  }
+
   // Now are there any make_seq requests within this class?
   CPPStructType *stype = _itype._cpptype->as_struct_type();
   if (stype != (CPPStructType *)NULL) {

+ 4 - 2
dtool/src/interrogate/interfaceMaker.h

@@ -110,8 +110,10 @@ public:
     MakeSeqs _make_seqs;
 
     enum ProtocolTypes {
-      PT_sequence =   0x0001,
-      PT_mapping =    0x0002,
+      PT_sequence         = 0x0001,
+      PT_mapping          = 0x0002,
+      PT_make_copy        = 0x0004,
+      PT_copy_constructor = 0x0008,
     };
     int _protocol_types;
   };

+ 185 - 155
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -97,7 +97,10 @@ RenameSet methodRenameDictionary[] = {
     { "operator <<="  , "__ilshift__",          1 },
     { "operator >>="  , "__irshift__",          1 },
     { "operator typecast bool", "__nonzero__",  0 },
-    { "__nonzero__"   , "__nonzero__",          0 },
+    { "__nonzero__"   , "__nonzero__",          0 }, 
+    { "__reduce__"    , "__reduce__",           0 },
+    { "__copy__"      , "__copy__",             0 },
+    { "__deepcopy__"  , "__deepcopy__",         0 },
     { "print"         , "Cprint",               0 },
     { "CInterval.set_t", "_priv__cSetT",        0 },
     { NULL, NULL, -1 }
@@ -159,34 +162,36 @@ RenameSet classRenameDictionary[] = {
 ///////////////////////////////////////////////////////////////////////////////////////
 const char * pythonKeywords[] = {
     "and",
-        "del",
-        "for",
-        "is",
-        "raise",
-        "assert",
-        "elif",
-        "from",
-        "lambda",
-        "return",
-        "break",
-        "else",
-        "global",
-        "not",
-        "try",
-        "class",
-        "except",
-        "if",
-        "or",
-        "while",
-        "continue",
-        "exec",
-        "import",
-        "pass",
-        "def",
-        "finally",
-        "in",
-        "print",
-        NULL};
+    "del",
+    "for",
+    "is",
+    "raise",
+    "assert",
+    "elif",
+    "from",
+    "lambda",
+    "return",
+    "break",
+    "else",
+    "global",
+    "not",
+    "try",
+    "class",
+    "except",
+    "if",
+    "or",
+    "while",
+    "continue",
+    "exec",
+    "import",
+    "pass",
+    "def",
+    "finally",
+    "in",
+    "print",
+    NULL
+};
+
 ///////////////////////////////////////////////////////////////////////////////////////
 std::string  checkKeyword(std::string & cppName)
 {
@@ -813,69 +818,69 @@ void InterfaceMakerPythonNative::write_prototypes_class(ostream &out_code,ostrea
 void InterfaceMakerPythonNative::write_functions(ostream &out) 
 {
   inside_python_native = true;
-
-    out << "//********************************************************************\n";
-    out << "//*** Functions for .. Global \n" ;
-    out << "//********************************************************************\n";
-        Functions::iterator fi;
-        for (fi = _functions.begin(); fi != _functions.end(); ++fi) 
-        {
-            Function *func = (*fi);
-            if(!func->_itype.is_global() && isFunctionLegal(func))
-                write_function_for_top(out, NULL, func,"");
-        }
-
-        Objects::iterator oi;
-        for (oi = _objects.begin(); oi != _objects.end(); ++oi) 
+  
+  out << "//********************************************************************\n";
+  out << "//*** Functions for .. Global \n" ;
+  out << "//********************************************************************\n";
+  Functions::iterator fi;
+  for (fi = _functions.begin(); fi != _functions.end(); ++fi) 
+    {
+      Function *func = (*fi);
+      if(!func->_itype.is_global() && isFunctionLegal(func))
+        write_function_for_top(out, NULL, func,"");
+    }
+  
+  Objects::iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) 
+    {
+      Object *object = (*oi).second;
+      if(object->_itype.is_class() ||object->_itype.is_struct())
         {
-            Object *object = (*oi).second;
-            if(object->_itype.is_class() ||object->_itype.is_struct())
-            {
-                if(isCppTypeLegal(object->_itype._cpptype))
-                  if(isExportThisRun(object->_itype._cpptype))
-                        write_ClasseDetails(out,object);
-            }
+          if(isCppTypeLegal(object->_itype._cpptype))
+            if(isExportThisRun(object->_itype._cpptype))
+              write_ClasseDetails(out,object);
         }
-
-//    Objects::iterator oi;
-    for (oi = _objects.begin(); oi != _objects.end(); ++oi) 
+    }
+  
+  //    Objects::iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) 
     {
-        Object *object = (*oi).second;
-        if(!object->_itype.get_outer_class())
+      Object *object = (*oi).second;
+      if(!object->_itype.get_outer_class())
         {
-            if(object->_itype.is_class() ||object->_itype.is_struct())
-              if(isCppTypeLegal(object->_itype._cpptype))
-                  if(isExportThisRun(object->_itype._cpptype))
-                        write_module_class(out,object);
+          if(object->_itype.is_class() ||object->_itype.is_struct())
+            if(isCppTypeLegal(object->_itype._cpptype))
+              if(isExportThisRun(object->_itype._cpptype))
+                write_module_class(out,object);
         }
     }
   inside_python_native = true;
-
 }
+
 ////////////////////////////////////////////////////////////
 //  Function : write_ClasseDetails
 ////////////////////////////////////////////////////////////
 void InterfaceMakerPythonNative::write_ClasseDetails(ostream &out, Object * obj) 
 {
-    Functions::iterator fi;
-
-    //std::string cClassName = 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 export_class_name = classNameFromCppName(obj->_itype.get_name());
-
-    out << "//********************************************************************\n";
-    out << "//*** Functions for .. "<< cClassName <<" \n" ;
-    out << "//********************************************************************\n";
-
-    for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) 
+  Functions::iterator fi;
+  
+  //std::string cClassName = 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 export_class_name = classNameFromCppName(obj->_itype.get_name());
+  
+  out << "//********************************************************************\n";
+  out << "//*** Functions for .. "<< cClassName <<" \n" ;
+  out << "//********************************************************************\n";
+  
+  for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) 
     {
-        Function *func = (*fi);
-        if( (func))
+      Function *func = (*fi);
+      if( (func))
         {
           SlottedFunctionDef def;
           get_slotted_function_def(obj, func, def);
-
+          
           ostringstream GetThis;
           GetThis << "    "<<cClassName  << " * local_this = NULL;\n";
           GetThis << "    DTOOL_Call_ExtractThisPointerForType(self,&Dtool_"<<  ClassName<<",(void **)&local_this);\n";
@@ -887,7 +892,7 @@ void InterfaceMakerPythonNative::write_ClasseDetails(ostream &out, Object * obj)
             // likes to call on the wrong-type objects.
             GetThis << "       Py_INCREF(Py_NotImplemented);\n";
             GetThis << "       return Py_NotImplemented;\n";
-
+            
           } else {
             // Other functions should raise an exception if the this
             // pointer isn't set or is the wrong type.
@@ -898,100 +903,100 @@ void InterfaceMakerPythonNative::write_ClasseDetails(ostream &out, Object * obj)
           write_function_for_top(out, obj, func,GetThis.str());
         }
     }
-
-//    bool AnyLeganConstructors;
-
-    if(obj->_constructors.size() == 0)
+  
+  //    bool AnyLeganConstructors;
+  
+  if(obj->_constructors.size() == 0)
     {
-        std::string fname =     "int  Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds)";
-        out << fname << "\n";
-        out << "{\n";
-        out << "       PyErr_SetString(PyExc_TypeError, \"Error Can Not Init Constant Class (" << cClassName << ")\");\n";
-        out << "       return -1;\n" ;
-        out << "}\n";
-
+      std::string fname =     "int  Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds)";
+      out << fname << "\n";
+      out << "{\n";
+      out << "       PyErr_SetString(PyExc_TypeError, \"Error Can Not Init Constant Class (" << cClassName << ")\");\n";
+      out << "       return -1;\n" ;
+      out << "}\n";
+      
     }
-    else
+  else
     {
-        for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) 
+      for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) 
         {
-            Function *func = (*fi);
-            std::string fname =     "int  Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds) ";
-
-            write_function_for_name(out, obj, func,fname,"",ClassName);
+          Function *func = (*fi);
+          std::string fname =     "int  Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds) ";
+          
+          write_function_for_name(out, obj, func,fname,"",ClassName);
         }
     }
-
-    MakeSeqs::iterator msi;
-    for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) {
-      write_make_seq(out, obj, ClassName, *msi);
+  
+  MakeSeqs::iterator msi;
+  for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) {
+    write_make_seq(out, obj, ClassName, *msi);
+  }
+  
+  CPPType *cpptype = TypeManager::resolve_type(obj->_itype._cpptype);
+  std::map< string ,CastDetails > details;
+  std::map< string ,CastDetails >::iterator di;
+  builder.get_type(TypeManager::unwrap(cpptype),false);
+  GetValideChildClasses(details, cpptype->as_struct_type());
+  for(di = details.begin(); di != details.end(); di++)
+    {
+      //InterrogateType ptype =idb->get_type(di->first);
+      if(di->second._is_legal_py_class && !isExportThisRun(di->second._structType))
+        _external_imports.insert(make_safe_name(di->second._to_class_name));
+      //out << "IMPORT_THIS struct   Dtool_PyTypedObject Dtool_" << make_safe_name(di->second._to_class_name) <<";\n";
     }
+  
+  
+  
+  { // the Cast Converter
+    
+    out << "inline void  * Dtool_UpcastInterface_"<< ClassName << "(PyObject *self, Dtool_PyTypedObject *requested_type)\n";
+    out << "{\n";
+    out << "    Dtool_PyTypedObject *SelfType = ((Dtool_PyInstDef *)self)->_My_Type;\n";
+    out << "    if(SelfType != &Dtool_" << ClassName <<")\n";
+    out << "    {\n";
+    out << "        printf(\""<<ClassName<<" ** Bad Source Type-- Requesting Conversion from %s to %s\\n\",((Dtool_PyInstDef *)self)->_My_Type->_name,requested_type->_name);fflush(NULL);\n";;
+    out << "        return NULL;\n";
+    out << "    }\n";
+    out << " \n";
+    out << "    "<<cClassName<<" * local_this = (" << cClassName<<" *)((Dtool_PyInstDef *)self)->_ptr_to_object;\n"; 
+    out << "    if(requested_type == &Dtool_"<<ClassName<<")\n";
+    out << "        return local_this;\n";
 
-    CPPType *cpptype = TypeManager::resolve_type(obj->_itype._cpptype);
-    std::map< string ,CastDetails > details;
-    std::map< string ,CastDetails >::iterator di;
-    builder.get_type(TypeManager::unwrap(cpptype),false);
-    GetValideChildClasses(details, cpptype->as_struct_type());
     for(di = details.begin(); di != details.end(); di++)
       {
-        //InterrogateType ptype =idb->get_type(di->first);
-        if(di->second._is_legal_py_class && !isExportThisRun(di->second._structType))
-          _external_imports.insert(make_safe_name(di->second._to_class_name));
-        //out << "IMPORT_THIS struct   Dtool_PyTypedObject Dtool_" << make_safe_name(di->second._to_class_name) <<";\n";
+        if(di->second._is_legal_py_class)
+          {
+            out << "    if(requested_type == &Dtool_"<<make_safe_name(di->second._to_class_name)<<")\n";
+            out << "        return "<< di->second._up_cast_string << " local_this;\n";
+          }
       }
-    
-
-
-    { // the Cast Converter
-
-        out << "inline void  * Dtool_UpcastInterface_"<< ClassName << "(PyObject *self, Dtool_PyTypedObject *requested_type)\n";
-        out << "{\n";
-        out << "    Dtool_PyTypedObject *SelfType = ((Dtool_PyInstDef *)self)->_My_Type;\n";
-        out << "    if(SelfType != &Dtool_" << ClassName <<")\n";
-        out << "    {\n";
-        out << "        printf(\""<<ClassName<<" ** Bad Source Type-- Requesting Conversion from %s to %s\\n\",((Dtool_PyInstDef *)self)->_My_Type->_name,requested_type->_name);fflush(NULL);\n";;
-        out << "        return NULL;\n";
-        out << "    }\n";
-        out << " \n";
-        out << "    "<<cClassName<<" * local_this = (" << cClassName<<" *)((Dtool_PyInstDef *)self)->_ptr_to_object;\n"; 
-        out << "    if(requested_type == &Dtool_"<<ClassName<<")\n";
-        out << "        return local_this;\n";
-
-        for(di = details.begin(); di != details.end(); di++)
-        {
-            if(di->second._is_legal_py_class)
-            {
-                    out << "    if(requested_type == &Dtool_"<<make_safe_name(di->second._to_class_name)<<")\n";
-                    out << "        return "<< di->second._up_cast_string << " local_this;\n";
-            }
-        }
-
-        out << "    return NULL;\n";
-        out << "}\n";
 
-        out << "inline void  * Dtool_DowncastInterface_"<< ClassName << "(void *from_this, Dtool_PyTypedObject *from_type)\n";
-        out << "{\n";
-        out << "    if(from_this == NULL || from_type == NULL)\n";
-        out << "        return NULL;\n";
-        out << "    if(from_type == &Dtool_" << ClassName<<")\n";
-        out << "        return from_this;\n";
-        for(di = details.begin(); di != details.end(); di++)
-        {
-            if(di->second._can_downcast && di->second._is_legal_py_class)
-            {
-                out << "    if(from_type == &Dtool_"<<make_safe_name(di->second._to_class_name)<<")\n";
-                out << "    {\n";
-                out << "          "<< di->second._to_class_name << "* other_this = ("<< di->second._to_class_name <<  "*)from_this;\n" ;
-                out << "          return ("<< cClassName << "*)other_this;\n";
-                out << "    }\n";
-            }
-        }
-        out << "    return (void *) NULL;\n";
-        out << "}\n";
+    out << "    return NULL;\n";
+    out << "}\n";
 
-    }
+    out << "inline void  * Dtool_DowncastInterface_"<< ClassName << "(void *from_this, Dtool_PyTypedObject *from_type)\n";
+    out << "{\n";
+    out << "    if(from_this == NULL || from_type == NULL)\n";
+    out << "        return NULL;\n";
+    out << "    if(from_type == &Dtool_" << ClassName<<")\n";
+    out << "        return from_this;\n";
+    for(di = details.begin(); di != details.end(); di++)
+      {
+        if(di->second._can_downcast && di->second._is_legal_py_class)
+          {
+            out << "    if(from_type == &Dtool_"<<make_safe_name(di->second._to_class_name)<<")\n";
+            out << "    {\n";
+            out << "          "<< di->second._to_class_name << "* other_this = ("<< di->second._to_class_name <<  "*)from_this;\n" ;
+            out << "          return ("<< cClassName << "*)other_this;\n";
+            out << "    }\n";
+          }
+      }
+    out << "    return (void *) NULL;\n";
+    out << "}\n";
 
+  }
 }
+
 ////////////////////////////////////////////////////////////
 /// Function : write_ClasseDeclarations
 //
@@ -1215,9 +1220,18 @@ write_module_class(ostream &out,  Object *obj) {
   std::map<Function *, std::string >       normal_Operator_functions;
   std::map<Function *, SlottedFunctionDef> wraped_Operator_functions;
   // function Table
+  bool got_copy = false;
+  bool got_deepcopy = false;
+
   int x;
   for (x = 0, fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi,x++) {
     Function *func = (*fi);
+    if (func->_name == "__copy__") {
+      got_copy = true;
+    } else if (func->_name == "__deepcopy__") {
+      got_deepcopy = true;
+    }
+
     SlottedFunctionDef slotted_def;
     if (!get_slotted_function_def(obj, func, slotted_def)) {
       out << "  { \"" << methodNameFromCppName(func,export_calss_name) << "\",(PyCFunction ) &" 
@@ -1248,6 +1262,22 @@ write_module_class(ostream &out,  Object *obj) {
     }
   }
 
+  if (obj->_protocol_types & Object::PT_make_copy) {
+    if (!got_copy) {
+      out << "  { \"__copy__\", (PyCFunction)&copy_from_make_copy, METH_NOARGS, NULL},\n";
+      got_copy = true;
+    }
+  } else if (obj->_protocol_types & Object::PT_copy_constructor) {
+    if (!got_copy) {
+      out << "  { \"__copy__\", (PyCFunction)&copy_from_copy_constructor, METH_NOARGS, NULL},\n";
+      got_copy = true;
+    }
+  }
+
+  if (got_copy && !got_deepcopy) {
+    out << "  { \"__deepcopy__\", (PyCFunction)&map_deepcopy_to_copy, METH_VARARGS, NULL},\n";
+  }
+  
   MakeSeqs::iterator msi;
   for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) {
     out << "  { \""

+ 36 - 0
dtool/src/interrogatedb/py_panda.cxx

@@ -708,6 +708,42 @@ PyObject *make_list_for_item(PyObject *self, const char *num_name,
   return list;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: copy_from_make_copy
+//  Description: This is a support function for a synthesized
+//               __copy__() method from a C++ make_copy() method.
+////////////////////////////////////////////////////////////////////
+PyObject *copy_from_make_copy(PyObject *self) {
+  return PyObject_CallMethod(self, (char *)"makeCopy", (char *)"()");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: copy_from_make_copy
+//  Description: This is a support function for a synthesized
+//               __copy__() method from a C++ copy constructor.
+////////////////////////////////////////////////////////////////////
+PyObject *copy_from_copy_constructor(PyObject *self) {
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *result = PyObject_CallFunction(this_class, (char *)"(O)", self);
+  Py_DECREF(this_class);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: copy_from_make_copy
+//  Description: This is a support function for a synthesized
+//               __deepcopy__() method for any class that has a
+//               __copy__() method.  The sythethic method simply
+//               invokes __copy__().
+////////////////////////////////////////////////////////////////////
+PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) {
+  return PyObject_CallMethod(self, (char *)"__copy__", (char *)"()");
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PyLongOrInt_FromUnsignedLong
 //  Description: Similar to PyLong_FromUnsignedLong(), but returns

+ 121 - 107
dtool/src/interrogatedb/py_panda.h

@@ -174,114 +174,118 @@ struct Dtool_PyTypedObject {
 
 
 ////////////////////////////////////////////////////////////////////////
-// Macro's from Hell..  May want to Just Add this to the Code generator..
+// Macros from Hell..  May want to just add this to the code generator..
 ////////////////////////////////////////////////////////////////////////
-#define   Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \
-static  PyNumberMethods     Dtool_PyNumberMethods_##CLASS_NAME ={\
-        0,/*binaryfunc nb_add*/\
-        0,/*binaryfunc nb_subtract*/\
-        0,/*binaryfunc nb_multiply*/\
-        0,/*binaryfunc nb_divide*/\
-        0,/*binaryfunc nb_remainder*/\
-        0,/*binaryfunc nb_divmod*/\
-        0,/*ternaryfunc nb_power*/\
-        0,/*unaryfunc nb_negative*/\
-        0,/*unaryfunc nb_positive*/\
-        0,/*unaryfunc nb_absolute*/\
-        0,/*inquiry nb_nonzero*/\
-        0,/*unaryfunc nb_invert*/\
-        0,/*binaryfunc nb_lshift*/\
-        0,/*binaryfunc nb_rshift*/\
-        0,/*binaryfunc nb_and*/\
-        0,/*binaryfunc nb_xor*/\
-        0,/*binaryfunc nb_or*/\
-        0,/*coercion nb_coerce*/\
-        0,/*unaryfunc nb_int*/\
-        0,/*unaryfunc nb_long*/\
-        0,/*unaryfunc nb_float*/\
-        0,/*unaryfunc nb_oct*/\
-        0,/*unaryfunc nb_hex*/\
-        0,/*binaryfunc nb_inplace_add*/\
-        0,/*binaryfunc nb_inplace_subtract*/\
-        0,/*binaryfunc nb_inplace_multiply*/\
-        0,/*binaryfunc nb_inplace_divide*/\
-        0,/*binaryfunc nb_inplace_remainder*/\
-        0,/*ternaryfunc nb_inplace_power*/\
-        0,/*binaryfunc nb_inplace_lshift*/\
-        0,/*binaryfunc nb_inplace_rshift*/\
-        0,/*binaryfunc nb_inplace_and*/\
-        0,/*binaryfunc nb_inplace_xor*/\
-        0,/*binaryfunc nb_inplace_or*/\
-        0,/*binaryfunc nb_floor_divide*/\
-        0,/*binaryfunc nb_true_divide*/\
-        0,/*binaryfunc nb_inplace_floor_divide*/\
-        0,/*binaryfunc nb_inplace_true_divide*/\
-    };\
-static  PySequenceMethods    Dtool_PySequenceMethods_##CLASS_NAME ={\
-        0,/*lenfunc sq_length */\
-        0,/*binaryfunc sq_concat */\
-        0,/*ssizeargfunc sq_repeat */\
-        0,/*ssizeargfunc sq_item */\
-        0,/*ssizeargfunc sq_ass_item */\
-        0,/*objobjproc sq_contains */\
-        0,/*binaryfunc sq_inplace_concat */\
-        0,/*ssizeargfunc sq_inplace_repeat */\
-};\
-static  PyMappingMethods    Dtool_PyMappingMethods_##CLASS_NAME ={\
-        0,/*inquiry mp_length */\
-        0,/*binaryfunc mp_subscript */\
-        0,/*objobjargproc mp_ass_subscript */\
-};\
-EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME =  {\
-{\
-    PyObject_HEAD_INIT(NULL)\
-    0,\
-    "lib" #MODULE_NAME "." #PUBLIC_NAME, /*type name with module */ \
-    sizeof(Dtool_PyInstDef),     /* tp_basicsize*/ \
-    0,     /*tp_itemsize*/ \
-    &Dtool_Deallocate_General, /*Dtool_Deallocate_##CLASS_NAME,*/ /*tp_dealloc*/\
-    0,              /*tp_print*/\
-    0,              /*tp_getattr*/\
-    0,              /*tp_setattr*/\
-    0,              /*tp_compare*/\
-    0,              /*tp_repr*/\
-    &Dtool_PyNumberMethods_##CLASS_NAME,               /*tp_as_number*/\
-    &Dtool_PySequenceMethods_##CLASS_NAME,             /*tp_as_sequence*/\
-    &Dtool_PyMappingMethods_##CLASS_NAME,              /*tp_as_mapping*/\
-    0,     /*tp_hash */\
-        0,                                      /* tp_call */\
-        0,                                      /* tp_str */\
-        PyObject_GenericGetAttr,                /* tp_getattro */\
-        PyObject_GenericSetAttr,                /* tp_setattro */\
-        0,                                      /* tp_as_buffer */\
-        (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES), /* tp_flags */\
-        0,                                      /* tp_doc */\
-        0,                                      /* tp_traverse */\
-        0,                                      /* tp_clear */\
-        0,                                      /* tp_richcompare */\
-        0,                                      /* tp_weaklistoffset */\
-        0,                                      /* tp_iter */\
-        0,                                      /* tp_iternext */\
-    Dtool_Methods_##CLASS_NAME,                 /* tp_methods */\
-        standard_type_members,                                  /* tp_members */\
-        0,                          /* tp_getset */\
-        0,                          /* tp_base */\
-        0,                                      /* tp_dict */\
-        0,                                      /* tp_descr_get */\
-        0,                                      /* tp_descr_set */\
-        0,                                      /* tp_dictoffset */\
-    Dtool_Init_##CLASS_NAME,            /* tp_init */\
-        PyType_GenericAlloc, /* tp_alloc */\
-        Dtool_new_##CLASS_NAME,                 /* tp_new */\
-        _PyObject_Del,                                  /* tp_free */\
-},\
-    #CLASS_NAME,   \
-    false,\
-    Dtool_UpcastInterface_##CLASS_NAME, \
-    Dtool_DowncastInterface_##CLASS_NAME, \
-    Dtool_FreeInstance_##CLASS_NAME, \
-    Dtool_PyModuleClassInit_##CLASS_NAME\
-}; 
+#define Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME)        \
+  static PyNumberMethods Dtool_PyNumberMethods_##CLASS_NAME =           \
+    {                                                                   \
+      0,/*binaryfunc nb_add*/                                           \
+      0,/*binaryfunc nb_subtract*/                                      \
+      0,/*binaryfunc nb_multiply*/                                      \
+      0,/*binaryfunc nb_divide*/                                        \
+      0,/*binaryfunc nb_remainder*/                                     \
+      0,/*binaryfunc nb_divmod*/                                        \
+      0,/*ternaryfunc nb_power*/                                        \
+      0,/*unaryfunc nb_negative*/                                       \
+      0,/*unaryfunc nb_positive*/                                       \
+      0,/*unaryfunc nb_absolute*/                                       \
+      0,/*inquiry nb_nonzero*/                                          \
+      0,/*unaryfunc nb_invert*/                                         \
+      0,/*binaryfunc nb_lshift*/                                        \
+      0,/*binaryfunc nb_rshift*/                                        \
+      0,/*binaryfunc nb_and*/                                           \
+      0,/*binaryfunc nb_xor*/                                           \
+      0,/*binaryfunc nb_or*/                                            \
+      0,/*coercion nb_coerce*/                                          \
+      0,/*unaryfunc nb_int*/                                            \
+      0,/*unaryfunc nb_long*/                                           \
+      0,/*unaryfunc nb_float*/                                          \
+      0,/*unaryfunc nb_oct*/                                            \
+      0,/*unaryfunc nb_hex*/                                            \
+      0,/*binaryfunc nb_inplace_add*/                                   \
+      0,/*binaryfunc nb_inplace_subtract*/                              \
+      0,/*binaryfunc nb_inplace_multiply*/                              \
+      0,/*binaryfunc nb_inplace_divide*/                                \
+      0,/*binaryfunc nb_inplace_remainder*/                             \
+      0,/*ternaryfunc nb_inplace_power*/                                \
+      0,/*binaryfunc nb_inplace_lshift*/                                \
+      0,/*binaryfunc nb_inplace_rshift*/                                \
+      0,/*binaryfunc nb_inplace_and*/                                   \
+      0,/*binaryfunc nb_inplace_xor*/                                   \
+      0,/*binaryfunc nb_inplace_or*/                                    \
+      0,/*binaryfunc nb_floor_divide*/                                  \
+      0,/*binaryfunc nb_true_divide*/                                   \
+      0,/*binaryfunc nb_inplace_floor_divide*/                          \
+      0,/*binaryfunc nb_inplace_true_divide*/                           \
+    };                                                                  \
+  static PySequenceMethods Dtool_PySequenceMethods_##CLASS_NAME =       \
+    {                                                                   \
+      0,/*lenfunc sq_length */                                          \
+      0,/*binaryfunc sq_concat */                                       \
+      0,/*ssizeargfunc sq_repeat */                                     \
+      0,/*ssizeargfunc sq_item */                                       \
+      0,/*ssizeargfunc sq_ass_item */                                   \
+      0,/*objobjproc sq_contains */                                     \
+      0,/*binaryfunc sq_inplace_concat */                               \
+      0,/*ssizeargfunc sq_inplace_repeat */                             \
+    };                                                                  \
+  static PyMappingMethods Dtool_PyMappingMethods_##CLASS_NAME =         \
+    {                                                                   \
+      0,/*inquiry mp_length */                                          \
+      0,/*binaryfunc mp_subscript */                                    \
+      0,/*objobjargproc mp_ass_subscript */                             \
+    };                                                                  \
+  EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME =                  \
+    {                                                                   \
+      {                                                                 \
+        PyObject_HEAD_INIT(NULL)                                        \
+        0,                                                              \
+        "lib" #MODULE_NAME "." #PUBLIC_NAME, /*type name with module */ \
+        sizeof(Dtool_PyInstDef),                /* tp_basicsize */      \
+        0,                                      /* tp_itemsize */       \
+        &Dtool_Deallocate_General,              /* tp_dealloc */        \
+        0,                                      /* tp_print */          \
+        0,                                      /* tp_getattr */        \
+        0,                                      /* tp_setattr */        \
+        0,                                      /* tp_compare */        \
+        0,                                      /* tp_repr */           \
+        &Dtool_PyNumberMethods_##CLASS_NAME,    /* tp_as_number */      \
+        &Dtool_PySequenceMethods_##CLASS_NAME,  /* tp_as_sequence */    \
+        &Dtool_PyMappingMethods_##CLASS_NAME,   /* tp_as_mapping */     \
+        0,                                      /* tp_hash */           \
+        0,                                      /* tp_call */           \
+        0,                                      /* tp_str */            \
+        PyObject_GenericGetAttr,                /* tp_getattro */       \
+        PyObject_GenericSetAttr,                /* tp_setattro */       \
+        0,                                      /* tp_as_buffer */      \
+        (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES), /* tp_flags */ \
+        0,                                      /* tp_doc */            \
+        0,                                      /* tp_traverse */       \
+        0,                                      /* tp_clear */          \
+        0,                                      /* tp_richcompare */    \
+        0,                                      /* tp_weaklistoffset */ \
+        0,                                      /* tp_iter */           \
+        0,                                      /* tp_iternext */       \
+        Dtool_Methods_##CLASS_NAME,             /* tp_methods */        \
+        standard_type_members,                  /* tp_members */        \
+        0,                                      /* tp_getset */         \
+        0,                                      /* tp_base */           \
+        0,                                      /* tp_dict */           \
+        0,                                      /* tp_descr_get */      \
+        0,                                      /* tp_descr_set */      \
+        0,                                      /* tp_dictoffset */     \
+        Dtool_Init_##CLASS_NAME,                /* tp_init */           \
+        PyType_GenericAlloc,                    /* tp_alloc */          \
+        Dtool_new_##CLASS_NAME,                 /* tp_new */            \
+        _PyObject_Del,                          /* tp_free */           \
+      },                                                                \
+      #CLASS_NAME,                                                      \
+      false,                                                            \
+      Dtool_UpcastInterface_##CLASS_NAME,                               \
+      Dtool_DowncastInterface_##CLASS_NAME,                             \
+      Dtool_FreeInstance_##CLASS_NAME,                                  \
+      Dtool_PyModuleClassInit_##CLASS_NAME                              \
+    }; 
 
 
 ////////////////////////////////////////////////////////////////////////
@@ -538,6 +542,16 @@ EXPCL_DTOOLCONFIG int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2);
 EXPCL_DTOOLCONFIG PyObject *
 make_list_for_item(PyObject *self, const char *num_name,
                    const char *element_name);
+
+EXPCL_DTOOLCONFIG PyObject *
+copy_from_make_copy(PyObject *self);
+
+EXPCL_DTOOLCONFIG PyObject *
+copy_from_copy_constructor(PyObject *self);
+
+EXPCL_DTOOLCONFIG PyObject *
+map_deepcopy_to_copy(PyObject *self, PyObject *args);
+
 EXPCL_DTOOLCONFIG PyObject *
 PyLongOrInt_FromUnsignedLong(unsigned long value);
 

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

@@ -51,6 +51,7 @@ extern "C" {
   EXPCL_DTOOLCONFIG int PyGen_Type(...);
   EXPCL_DTOOLCONFIG int PyGILState_Ensure(...);
   EXPCL_DTOOLCONFIG int PyGILState_Release(...);
+  EXPCL_DTOOLCONFIG int PyImport_GetModuleDict(...);
   EXPCL_DTOOLCONFIG int PyInt_AsLong(...);
   EXPCL_DTOOLCONFIG int PyInt_AsSsize_t(...);
   EXPCL_DTOOLCONFIG int PyInt_FromLong(...);
@@ -74,6 +75,7 @@ extern "C" {
   EXPCL_DTOOLCONFIG int PyModule_AddObject(...);
   EXPCL_DTOOLCONFIG int PyNumber_Long(...);
   EXPCL_DTOOLCONFIG int PyObject_Call(...);
+  EXPCL_DTOOLCONFIG int PyObject_CallFunction(...);
   EXPCL_DTOOLCONFIG int PyObject_CallMethod(...);
   EXPCL_DTOOLCONFIG int PyObject_CallMethodObjArgs(...);
   EXPCL_DTOOLCONFIG int PyObject_CallObject(...);
@@ -88,6 +90,7 @@ extern "C" {
   EXPCL_DTOOLCONFIG int PyObject_Repr(...);
   EXPCL_DTOOLCONFIG int PyObject_SetAttrString(...);
   EXPCL_DTOOLCONFIG int PyObject_Str(...);
+  EXPCL_DTOOLCONFIG int PyObject_Type(...);
   EXPCL_DTOOLCONFIG int PySequence_Check(...);
   EXPCL_DTOOLCONFIG int PySequence_GetItem(...);
   EXPCL_DTOOLCONFIG int PySequence_Size(...);
@@ -180,6 +183,7 @@ int PyGen_Check(...) { return 0; }
 int PyGen_Type(...) { return 0; }
 int PyGILState_Ensure(...) { return 0; }
 int PyGILState_Release(...) { return 0; }
+int PyImport_GetModuleDict(...) { return 0; }
 int PyInt_AsLong(...) { return 0; }
 int PyInt_AsSsize_t(...) { return 0; }
 int PyInt_FromLong(...) { return 0; }
@@ -203,6 +207,7 @@ int PyModule_AddIntConstant(...) { return 0; };
 int PyModule_AddObject(...) { return 0; };
 int PyNumber_Long(...) { return 0; }
 int PyObject_Call(...) { return 0; }
+int PyObject_CallFunction(...) { return 0; }
 int PyObject_CallMethod(...) { return 0; }
 int PyObject_CallMethodObjArgs(...) { return 0; }
 int PyObject_CallObject(...) { return 0; }
@@ -217,6 +222,7 @@ int PyObject_IsTrue(...) { return 0; }
 int PyObject_Repr(...) { return 0; }
 int PyObject_SetAttrString(...) { return 0; }
 int PyObject_Str(...) { return 0; }
+int PyObject_Type(...) { return 0; }
 int PySequence_Check(...) { return 0; }
 int PySequence_GetItem(...) { return 0; }
 int PySequence_Size(...) { return 0; }

+ 27 - 0
panda/src/linmath/lmatrix3_src.cxx

@@ -46,6 +46,33 @@ const FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::_ly_to_rz_mat =
   FLOATNAME(LMatrix3)::_flip_z_mat * FLOATNAME(LMatrix3)::_y_to_z_up_mat;
 
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix3::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *FLOATNAME(LMatrix3)::
+__reduce__(PyObject *self) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *result = Py_BuildValue("(O(fffffffff))", this_class, 
+                                   _m.m._00, _m.m._01, _m.m._02,
+                                   _m.m._10, _m.m._11, _m.m._12,
+                                   _m.m._20, _m.m._21, _m.m._22);
+  Py_DECREF(this_class);
+  return result;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix::set_scale_shear_mat
 //       Access: Public

+ 4 - 0
panda/src/linmath/lmatrix3_src.h

@@ -65,6 +65,10 @@ PUBLISHED:
     FLOATTYPE e20, FLOATTYPE e21, FLOATTYPE e22);
   ALLOC_DELETED_CHAIN(FLOATNAME(LMatrix3));
 
+#ifdef HAVE_PYTHON
+  PyObject *__reduce__(PyObject *self) const;
+#endif
+
   void fill(FLOATTYPE fill_value);
   INLINE_LINMATH void set(
     FLOATTYPE e00, FLOATTYPE e01, FLOATTYPE e02,

+ 29 - 0
panda/src/linmath/lmatrix4_src.cxx

@@ -62,6 +62,35 @@ const FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::_lz_to_ry_mat =
 const FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::_ly_to_rz_mat =
   FLOATNAME(LMatrix4)::_flip_z_mat * FLOATNAME(LMatrix4)::_y_to_z_up_mat;
 
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix4::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *FLOATNAME(LMatrix4)::
+__reduce__(PyObject *self) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *result = Py_BuildValue("(O(ffffffffffffffff))", this_class, 
+                                   _m.m._00, _m.m._01, _m.m._02, _m.m._03,
+                                   _m.m._10, _m.m._11, _m.m._12, _m.m._13,
+                                   _m.m._20, _m.m._21, _m.m._22, _m.m._23,
+                                   _m.m._30, _m.m._31, _m.m._32, _m.m._33);
+  Py_DECREF(this_class);
+  return result;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix::convert_mat
 //       Access: Public, Static

+ 4 - 0
panda/src/linmath/lmatrix4_src.h

@@ -60,6 +60,10 @@ PUBLISHED:
                                      FLOATTYPE e30, FLOATTYPE e31, FLOATTYPE e32, FLOATTYPE e33);
   ALLOC_DELETED_CHAIN(FLOATNAME(LMatrix4));
 
+#ifdef HAVE_PYTHON
+  PyObject *__reduce__(PyObject *self) const;
+#endif
+
   // Construct a 4x4 matrix given a 3x3 rotation matrix and an optional
   // translation component.
   INLINE_LINMATH FLOATNAME(LMatrix4)(const FLOATNAME(LMatrix3) &upper3);

+ 25 - 0
panda/src/linmath/lvecBase2_src.cxx

@@ -21,6 +21,31 @@ const FLOATNAME(LVecBase2) FLOATNAME(LVecBase2)::_unit_x =
 const FLOATNAME(LVecBase2) FLOATNAME(LVecBase2)::_unit_y =
   FLOATNAME(LVecBase2)(0.0f, 1.0f);
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: LVecBase2::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *FLOATNAME(LVecBase2)::
+__reduce__(PyObject *self) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *result = Py_BuildValue("(O(ff))", this_class, 
+                                   (*this)[0], (*this)[1]);
+  Py_DECREF(this_class);
+  return result;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LVecBase2::init_type
 //       Access: Public, Static

+ 4 - 0
panda/src/linmath/lvecBase2_src.h

@@ -39,6 +39,10 @@ PUBLISHED:
 
   INLINE_LINMATH ~FLOATNAME(LVecBase2)();
 
+#ifdef HAVE_PYTHON
+  PyObject *__reduce__(PyObject *self) const;
+#endif
+
   INLINE_LINMATH FLOATTYPE operator [](int i) const;
   INLINE_LINMATH FLOATTYPE &operator [](int i);
 #ifdef HAVE_PYTHON

+ 27 - 0
panda/src/linmath/lvecBase3_src.cxx

@@ -12,6 +12,7 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
 TypeHandle FLOATNAME(LVecBase3)::_type_handle;
 
 const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_zero =
@@ -23,6 +24,32 @@ const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_unit_y =
 const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_unit_z =
   FLOATNAME(LVecBase3)(0.0f, 0.0f, 1.0f);
 
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: LVecBase3::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *FLOATNAME(LVecBase3)::
+__reduce__(PyObject *self) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *result = Py_BuildValue("(O(fff))", this_class, 
+                                   (*this)[0], (*this)[1], (*this)[2]);
+  Py_DECREF(this_class);
+  return result;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LVecBase3::init_type
 //       Access: Public, Static

+ 4 - 0
panda/src/linmath/lvecBase3_src.h

@@ -37,6 +37,10 @@ PUBLISHED:
 
   INLINE_LINMATH ~FLOATNAME(LVecBase3)();
 
+#ifdef HAVE_PYTHON
+  PyObject *__reduce__(PyObject *self) const;
+#endif
+
   INLINE_LINMATH FLOATTYPE operator [](int i) const;
   INLINE_LINMATH FLOATTYPE &operator [](int i);
 #ifdef HAVE_PYTHON

+ 25 - 0
panda/src/linmath/lvecBase4_src.cxx

@@ -25,6 +25,31 @@ const FLOATNAME(LVecBase4) FLOATNAME(LVecBase4)::_unit_z =
 const FLOATNAME(LVecBase4) FLOATNAME(LVecBase4)::_unit_w =
   FLOATNAME(LVecBase4)(0.0f, 0.0f, 0.0f, 1.0f);
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: LVecBase4::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *FLOATNAME(LVecBase4)::
+__reduce__(PyObject *self) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *result = Py_BuildValue("(O(ffff))", this_class, 
+                                   (*this)[0], (*this)[1], (*this)[2], (*this)[3]);
+  Py_DECREF(this_class);
+  return result;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LVecBase4::init_type
 //       Access: Public, Static

+ 4 - 0
panda/src/linmath/lvecBase4_src.h

@@ -38,6 +38,10 @@ PUBLISHED:
 
   INLINE_LINMATH ~FLOATNAME(LVecBase4)();
 
+#ifdef HAVE_PYTHON
+  PyObject *__reduce__(PyObject *self) const;
+#endif
+
   INLINE_LINMATH FLOATTYPE operator [](int i) const;
   INLINE_LINMATH FLOATTYPE &operator [](int i);
 #ifdef HAVE_PYTHON

+ 143 - 0
panda/src/pgraph/nodePath.cxx

@@ -66,6 +66,7 @@
 #include "pStatCollector.h"
 #include "pStatTimer.h"
 #include "modelNode.h"
+#include "py_panda.h"
 
 // stack seems to overflow on Intel C++ at 7000.  If we need more than 
 // 7000, need to increase stack size.
@@ -127,6 +128,128 @@ static ConfigVariableEnum<EmptyNodePathType> empty_node_path
 // ***End temporary transition code for operator bool
 
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::__copy__
+//       Access: Published
+//  Description: A special Python method that is invoked by
+//               copy.copy(node).  Unlike the NodePath copy
+//               constructor, this makes a duplicate copy of the
+//               underlying PandaNode (but shares children, instead of
+//               copying them or omitting them).
+////////////////////////////////////////////////////////////////////
+NodePath NodePath::
+__copy__() const {
+  if (is_empty()) {
+    // Invoke the copy constructor if we have no node.
+    return *this;
+  }
+
+  // If we do have a node, duplicate it, and wrap it in a new
+  // NodePath.
+  return NodePath(node()->__copy__());
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::__deepcopy__
+//       Access: Published
+//  Description: A special Python method that is invoked by
+//               copy.deepcopy(np).  This calls copy_to() unless the
+//               NodePath is already present in the provided
+//               dictionary.
+////////////////////////////////////////////////////////////////////
+PyObject *NodePath::
+__deepcopy__(PyObject *self, PyObject *memo) const {
+  IMPORT_THIS struct Dtool_PyTypedObject Dtool_NodePath;
+
+  // Borrowed reference.
+  PyObject *dupe = PyDict_GetItem(memo, self);
+  if (dupe != NULL) {
+    // Already in the memo dictionary.
+    Py_INCREF(dupe);
+    return dupe;
+  }
+
+  NodePath *np_dupe;
+  if (is_empty()) {
+    np_dupe = new NodePath(*this);
+  } else {
+    np_dupe = new NodePath(copy_to(NodePath()));
+  }
+
+  dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath,
+                                true, false);
+  if (PyDict_SetItem(memo, self, dupe) != 0) {
+    Py_DECREF(dupe);
+    return NULL;
+  }
+
+  return dupe;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *NodePath::
+__reduce__(PyObject *self) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+
+  if (is_empty()) {
+    // Reconstruct an empty NodePath.  Not a 100% reconstruction,
+    // because we lose the specific error status, but I don't think
+    // that matters much.
+    PyObject *this_class = PyObject_Type(self);
+    if (this_class == NULL) {
+      return NULL;
+    }
+    
+    PyObject *result = Py_BuildValue("(O())", this_class);
+    Py_DECREF(this_class);
+    return result;
+  }
+
+  // We have a non-empty NodePath.  We need to streamify the
+  // underlying node.
+
+  string bam_stream;
+  if (!node()->encode_to_bam_stream(bam_stream)) {
+    ostringstream stream;
+    stream << "Could not bamify object of type " << node()->get_type() << "\n";
+    string message = stream.str();
+    PyErr_SetString(PyExc_TypeError, message.c_str());
+    return NULL;
+  }
+
+  // Start by getting this class object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *func = TypedWritable::find_global_decode(this_class, "pyDecodeNodePathFromBamStream");
+  if (func == NULL) {
+    PyErr_SetString(PyExc_TypeError, "Couldn't find pyDecodeNodePathFromBamStream()");
+    Py_DECREF(this_class);
+    return NULL;
+  }
+  Py_DECREF(this_class);
+
+  PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size());
+  Py_DECREF(func);
+  return result;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::operator bool
 //       Access: Published
@@ -7292,3 +7415,23 @@ r_find_all_materials(PandaNode *node, const RenderState *state,
     r_find_all_materials(child, next_state, materials);
   }
 }
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: py_decode_NodePath_from_bam_stream
+//       Access: Published
+//  Description: This wrapper is defined as a global function to suit
+//               pickle's needs.
+////////////////////////////////////////////////////////////////////
+NodePath
+py_decode_NodePath_from_bam_stream(const string &data) {
+  PT(PandaNode) node = PandaNode::decode_from_bam_stream(data);
+  if (node == (PandaNode *)NULL) {
+    PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream");
+    return NodePath();
+  }    
+
+  return NodePath(node);
+}
+#endif  // HAVE_PYTHON
+

+ 12 - 0
panda/src/pgraph/nodePath.h

@@ -168,6 +168,12 @@ PUBLISHED:
   INLINE NodePath(const NodePath &copy);
   INLINE void operator = (const NodePath &copy);
 
+#ifdef HAVE_PYTHON
+  NodePath __copy__() const;
+  PyObject *__deepcopy__(PyObject *self, PyObject *memo) const;
+  PyObject *__reduce__(PyObject *self) const;
+#endif
+
   INLINE static NodePath not_found();
   INLINE static NodePath removed();
   INLINE static NodePath fail();
@@ -949,6 +955,12 @@ private:
 
 INLINE ostream &operator << (ostream &out, const NodePath &node_path);
 
+#ifdef HAVE_PYTHON
+BEGIN_PUBLISH
+NodePath py_decode_NodePath_from_bam_stream(const string &data);
+END_PUBLISH
+#endif
+
 #include "nodePath.I"
 
 #endif

+ 110 - 16
panda/src/pgraph/pandaNode.cxx

@@ -28,6 +28,7 @@
 #include "config_mathutil.h"
 #include "lightReMutexHolder.h"
 #include "graphicsStateGuardianBase.h"
+#include "py_panda.h"
 
 // This category is just temporary for debugging convenience.
 NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
@@ -211,20 +212,6 @@ as_reference_count() {
   return this;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::make_copy
-//       Access: Public, Virtual
-//  Description: Returns a newly-allocated PandaNode that is a shallow
-//               copy of this one.  It will be a different pointer,
-//               but its internal data may or may not be shared with
-//               that of the original PandaNode.  No children will be
-//               copied.
-////////////////////////////////////////////////////////////////////
-PandaNode *PandaNode::
-make_copy() const {
-  return new PandaNode(*this);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::dupe_for_flatten
 //       Access: Public, Virtual
@@ -608,6 +595,20 @@ void PandaNode::
 add_for_draw(CullTraverser *, CullTraverserData &) {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::make_copy
+//       Access: Published, Virtual
+//  Description: Returns a newly-allocated PandaNode that is a shallow
+//               copy of this one.  It will be a different pointer,
+//               but its internal data may or may not be shared with
+//               that of the original PandaNode.  No children will be
+//               copied.
+////////////////////////////////////////////////////////////////////
+PandaNode *PandaNode::
+make_copy() const {
+  return new PandaNode(*this);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::copy_subgraph
 //       Access: Published
@@ -618,12 +619,79 @@ add_for_draw(CullTraverser *, CullTraverserData &) {
 //               will impede normal use of the PandaNode.
 ////////////////////////////////////////////////////////////////////
 PT(PandaNode) PandaNode::
-copy_subgraph(Thread *current_thread) const 
-{
+copy_subgraph(Thread *current_thread) const {
   InstanceMap inst_map;
   return r_copy_subgraph(inst_map, current_thread);
 }
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::__copy__
+//       Access: Published
+//  Description: A special Python method that is invoked by
+//               copy.copy(node).  Unlike the PandaNode copy
+//               constructor, which creates a new node without
+//               children, this shares child pointers (essentially
+//               making every child an instance).  This is intended to
+//               simulate the behavior of copy.copy() for other
+//               objects.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) PandaNode::
+__copy__() const {
+  Thread *current_thread = Thread::get_current_thread();
+
+  PT(PandaNode) node_dupe = make_copy();
+
+  Children children = get_children(current_thread);
+  int num_children = children.get_num_children();
+
+  for (int i = 0; i < num_children; ++i) {
+    PandaNode *child = children.get_child(i);
+    node_dupe->add_child(children.get_child(i), children.get_child_sort(i));
+  }
+
+  return node_dupe;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::__deepcopy__
+//       Access: Published
+//  Description: A special Python method that is invoked by
+//               copy.deepcopy(node).  This calls copy_subgraph()
+//               unless the node is already present in the provided
+//               dictionary.
+////////////////////////////////////////////////////////////////////
+PyObject *PandaNode::
+__deepcopy__(PyObject *self, PyObject *memo) const {
+  IMPORT_THIS struct Dtool_PyTypedObject Dtool_PandaNode;
+
+  // Borrowed reference.
+  PyObject *dupe = PyDict_GetItem(memo, self);
+  if (dupe != NULL) {
+    // Already in the memo dictionary.
+    Py_INCREF(dupe);
+    return dupe;
+  }
+
+  PT(PandaNode) node_dupe = copy_subgraph();
+
+  // DTool_CreatePyInstanceTyped() steals a C++ reference.
+  node_dupe->ref();
+  dupe = DTool_CreatePyInstanceTyped
+    ((void *)node_dupe.p(), Dtool_PandaNode, true, false, 
+     node_dupe->get_type_index());
+
+  if (PyDict_SetItem(memo, self, dupe) != 0) {
+    Py_DECREF(dupe);
+    return NULL;
+  }
+
+  return dupe;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::count_num_descendants
 //       Access: Published
@@ -2597,6 +2665,32 @@ is_ambient_light() const {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::decode_from_bam_stream
+//       Access: Published, Static
+//  Description: Reads the string created by a previous call to
+//               encode_to_bam_stream(), and extracts and returns the
+//               single object on that string.  Returns NULL on error.
+//
+//               This method is intended to replace
+//               decode_raw_from_bam_stream() when you know the stream
+//               in question returns an object of type PandaNode,
+//               allowing for easier reference count management.  Note
+//               that the caller is still responsible for maintaining
+//               the reference count on the return value.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) PandaNode::
+decode_from_bam_stream(const string &data) {
+  TypedWritable *object;
+  ReferenceCount *ref_ptr;
+
+  if (!TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, data)) {
+    return NULL;
+  }
+
+  return DCAST(PandaNode, object);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_internal_bounds
 //       Access: Protected

+ 10 - 1
panda/src/pgraph/pandaNode.h

@@ -84,7 +84,6 @@ private:
 
 public:
   virtual ReferenceCount *as_reference_count();
-  virtual PandaNode *make_copy() const;
   virtual PandaNode *dupe_for_flatten() const;
 
   virtual bool safe_to_flatten() const;
@@ -116,8 +115,14 @@ public:
   virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
 
 PUBLISHED:
+  virtual PandaNode *make_copy() const;
   PT(PandaNode) copy_subgraph(Thread *current_thread = Thread::get_current_thread()) const;
 
+#ifdef HAVE_PYTHON
+  PT(PandaNode) __copy__() const;
+  PyObject *__deepcopy__(PyObject *self, PyObject *memo) const;
+#endif
+
   INLINE int get_num_parents(Thread *current_thread = Thread::get_current_thread()) const;
   INLINE PandaNode *get_parent(int n, Thread *current_thread = Thread::get_current_thread()) const;
   INLINE int find_parent(PandaNode *node, Thread *current_thread = Thread::get_current_thread()) const;
@@ -300,6 +305,10 @@ PUBLISHED:
   };
   INLINE int get_fancy_bits(Thread *current_thread = Thread::get_current_thread()) const;
 
+
+PUBLISHED:
+  static PT(PandaNode) decode_from_bam_stream(const string &data);
+
 protected:
   class BoundsData;
 

+ 25 - 0
panda/src/putil/typedWritable.I

@@ -66,3 +66,28 @@ INLINE UpdateSeq TypedWritable::
 get_bam_modified() const {
   return _bam_modified;
 }
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::encode_to_bam_stream
+//       Access: Published
+//  Description: Converts the TypedWritable object into a single
+//               stream of data using a BamWriter, and returns that
+//               data as a string string.  Returns empty string on
+//               failure.
+//
+//               This is a convenience method particularly useful for
+//               cases when you are only serializing a single object.
+//               If you have many objects to process, it is more
+//               efficient to use the same BamWriter to serialize all
+//               of them together.
+////////////////////////////////////////////////////////////////////
+string TypedWritable::
+encode_to_bam_stream() const {
+  string data;
+  if (!encode_to_bam_stream(data)) {
+    return string();
+  }
+  return data;
+}
+

+ 277 - 0
panda/src/putil/typedWritable.cxx

@@ -14,7 +14,11 @@
 
 #include "typedWritable.h"
 #include "bamWriter.h"
+#include "bamReader.h"
+#include "datagramOutputFile.h"
+#include "datagramInputFile.h"
 #include "lightMutexHolder.h"
+#include "bam.h"
 
 LightMutex TypedWritable::_bam_writers_lock;
 
@@ -144,3 +148,276 @@ ReferenceCount *TypedWritable::
 as_reference_count() {
   return NULL;
 }
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *TypedWritable::
+__reduce__(PyObject *self) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+
+  // Check that we have a decodeFromBamStream python method.  If not,
+  // we can't use this interface.
+  PyObject *method = PyObject_GetAttrString(self, "decodeFromBamStream");
+  if (method == NULL) {
+    ostringstream stream;
+    stream << "Cannot pickle objects of type " << get_type() << "\n";
+    string message = stream.str();
+    PyErr_SetString(PyExc_TypeError, message.c_str());
+    return NULL;
+  }
+  Py_DECREF(method);
+
+  // First, streamify the object, if possible.
+  string bam_stream;
+  if (!encode_to_bam_stream(bam_stream)) {
+    ostringstream stream;
+    stream << "Could not bamify object of type " << get_type() << "\n";
+    string message = stream.str();
+    PyErr_SetString(PyExc_TypeError, message.c_str());
+    return NULL;
+  }
+
+  // Start by getting this class object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *func = find_global_decode(this_class, "pyDecodeTypedWritableFromBamStream");
+  if (func == NULL) {
+    PyErr_SetString(PyExc_TypeError, "Couldn't find pyDecodeTypedWritableFromBamStream()");
+    Py_DECREF(this_class);
+    return NULL;
+  }
+
+  PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size());
+  Py_DECREF(func);
+  Py_DECREF(this_class);
+  return result;
+}
+#endif  // HAVE_PYTHON
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::encode_to_bam_stream
+//       Access: Published
+//  Description: Converts the TypedWritable object into a single
+//               stream of data using a BamWriter, and stores that
+//               data in the indicated string.  Returns true on
+//               success, false on failure.
+//
+//               This is a convenience method particularly useful for
+//               cases when you are only serializing a single object.
+//               If you have many objects to process, it is more
+//               efficient to use the same BamWriter to serialize all
+//               of them together.
+////////////////////////////////////////////////////////////////////
+bool TypedWritable::
+encode_to_bam_stream(string &data) const {
+  data.clear();
+  ostringstream stream;
+
+  // We use nested scoping to ensure the destructors get called in the
+  // right order.
+  {
+    DatagramOutputFile dout;
+    if (!dout.open(stream)) {
+      return false;
+    }
+    
+    if (!dout.write_header(_bam_header)) {
+      return false;
+    }
+    
+    {
+      BamWriter writer(&dout, "bam_stream");
+      if (!writer.init()) {
+        return false;
+      }
+      
+      if (!writer.write_object(this)) {
+        return false;
+      }
+    }
+  }
+
+  data = stream.str();
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::decode_raw_from_bam_stream
+//       Access: Published, Static
+//  Description: Reads the string created by a previous call to
+//               encode_to_bam_stream(), and extracts the single
+//               object on that string.  Returns true on success,
+//               false on on error.
+//
+//               This variant sets the TypedWritable and
+//               ReferenceCount pointers separately; both are pointers
+//               to the same object.  The reference count is not
+//               incremented; it is the caller's responsibility to
+//               manage the reference count.
+//
+//               Note that this method cannot be used to retrieve
+//               objects that do not inherit from ReferenceCount,
+//               because these objects cannot persist beyond the
+//               lifetime of the BamReader that reads them.  To
+//               retrieve these objects from a bam stream, you must
+//               construct a BamReader directly.
+//
+//               If you happen to know that the particular object in
+//               question inherits from TypedWritableReferenceCount or
+//               PandaNode, consider calling the variant of
+//               decode_from_bam_stream() defined for those methods,
+//               which presents a simpler interface.
+////////////////////////////////////////////////////////////////////
+bool TypedWritable::
+decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr,
+                           const string &data) {
+  istringstream stream(data);
+
+  DatagramInputFile din;
+  if (!din.open(stream)) {
+    return false;
+  }
+  
+  string head;
+  if (!din.read_header(head, _bam_header.size())) {
+    return false;
+  }
+  
+  if (head != _bam_header) {
+    return false;
+  }
+
+  // We scope this so we can control when the BamReader destructs.
+  {
+    BamReader reader(&din, "bam_stream");
+    if (!reader.init()) {
+      return false;
+    }
+    
+    if (!reader.read_object(ptr, ref_ptr)) {
+      return false;
+    }
+    
+    if (!reader.resolve()) {
+      return false;
+    }
+    
+    if (ref_ptr == NULL) {
+      // Can't support non-reference-counted objects.
+      return false;
+    }
+
+    // Protect the pointer from accidental deletion when the BamReader
+    // goes away.
+    ref_ptr->ref();
+  }
+
+  // Now decrement the ref count, without deleting the object.  This
+  // may reduce the reference count to zero, but that's OK--we trust
+  // the caller to manage the reference count from this point on.
+  ref_ptr->unref();
+  return true;
+}
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::find_global_decode
+//       Access: Public, Static
+//  Description: This is a support function for __reduce__().  It
+//               searches for the global function
+//               pyDecodeTypedWritableFromBamStream() in this class's
+//               module, or in the module for any base class.  (It's
+//               really looking for the libpanda module, but we can't
+//               be sure what name that module was loaded under, so we
+//               search upwards this way.)
+//
+//               Returns: new reference on success, or NULL on failure.
+////////////////////////////////////////////////////////////////////
+PyObject *TypedWritable::
+find_global_decode(PyObject *this_class, const char *func_name) {
+  PyObject *module_name = PyObject_GetAttrString(this_class, "__module__");
+  if (module_name != NULL) {
+    // borrowed reference
+    PyObject *sys_modules = PyImport_GetModuleDict();
+    if (sys_modules != NULL) {
+      // borrowed reference
+      PyObject *module = PyDict_GetItem(sys_modules, module_name);
+      if (module != NULL){ 
+        PyObject *func = PyObject_GetAttrString(module, func_name);
+        if (func != NULL) {
+          Py_DECREF(module_name);
+          return func;
+        }
+      }
+    }
+  }
+  Py_DECREF(module_name);
+
+  PyObject *bases = PyObject_GetAttrString(this_class, "__bases__");
+  if (bases != NULL) {
+    if (PySequence_Check(bases)) {
+      Py_ssize_t size = PySequence_Size(bases);
+      for (Py_ssize_t i = 0; i < size; ++i) {
+        PyObject *base = PySequence_GetItem(bases, i);
+        if (base != NULL) {
+          PyObject *func = find_global_decode(base, func_name);
+          Py_DECREF(base);
+          if (func != NULL) {
+            Py_DECREF(bases);
+            return func;
+          }
+        }
+      }
+    }
+    Py_DECREF(bases);
+  }
+
+  return NULL;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: py_decode_TypedWritable_from_bam_stream
+//       Access: Published
+//  Description: This wrapper is defined as a global function to suit
+//               pickle's needs.
+////////////////////////////////////////////////////////////////////
+PyObject *
+py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) {
+  // We need the function TypedWritable::decode_from_bam_stream, which
+  // invokes the BamReader to reconstruct this object.  Since we use
+  // the specific object's class as the pointer, we get the particular
+  // instance of decode_from_bam_stream appropriate to this class.
+
+  PyObject *func = PyObject_GetAttrString(this_class, "decodeFromBamStream");
+  if (func == NULL) {
+    return NULL;
+  }
+
+  PyObject *result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size());
+  if (result == NULL) {
+    return NULL;
+  }
+
+  if (result == Py_None) {
+    Py_DECREF(result);
+    PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream");
+    return NULL;
+  }    
+
+  return result;
+}
+#endif  // HAVE_PYTHON
+

+ 22 - 3
panda/src/putil/typedWritable.h

@@ -60,6 +60,21 @@ PUBLISHED:
   INLINE void mark_bam_modified();
   INLINE UpdateSeq get_bam_modified() const;
 
+#ifdef HAVE_PYTHON
+  PyObject *__reduce__(PyObject *self) const;
+#endif
+
+  INLINE string encode_to_bam_stream() const;
+  bool encode_to_bam_stream(string &data) const;
+  static bool decode_raw_from_bam_stream(TypedWritable *&ptr, 
+                                         ReferenceCount *&ref_ptr,
+                                         const string &data);
+
+public:
+#ifdef HAVE_PYTHON
+  static PyObject *find_global_decode(PyObject *this_class, const char *func_name);
+#endif
+
 private:
   // We may need to store a list of the BamWriter(s) that have a
   // reference to this object, so that we can remove the object from
@@ -70,12 +85,10 @@ private:
 
   UpdateSeq _bam_modified;
 
-PUBLISHED:
+public:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
-
-public:
   static void init_type() {
     TypedObject::init_type();
     register_type(_type_handle, "TypedWritable",
@@ -96,6 +109,12 @@ private:
   friend class BamWriter;
 };
 
+#ifdef HAVE_PYTHON
+BEGIN_PUBLISH
+PyObject *py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data);
+END_PUBLISH
+#endif
+
 #include "typedWritable.I"
 
 #endif

+ 27 - 0
panda/src/putil/typedWritableReferenceCount.cxx

@@ -26,3 +26,30 @@ ReferenceCount *TypedWritableReferenceCount::
 as_reference_count() {
   return this;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritableReferenceCount::decode_from_bam_stream
+//       Access: Published, Static
+//  Description: Reads the string created by a previous call to
+//               encode_to_bam_stream(), and extracts and returns the
+//               single object on that string.  Returns NULL on error.
+//
+//               This method is intended to replace
+//               decode_raw_from_bam_stream() when you know the stream
+//               in question returns an object of type
+//               TypedWritableReferenceCount, allowing for easier
+//               reference count management.  Note that the caller is
+//               still responsible for maintaining the reference count
+//               on the return value.
+////////////////////////////////////////////////////////////////////
+PT(TypedWritableReferenceCount) TypedWritableReferenceCount::
+decode_from_bam_stream(const string &data) {
+  TypedWritable *object;
+  ReferenceCount *ref_ptr;
+
+  if (!TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, data)) {
+    return NULL;
+  }
+
+  return DCAST(TypedWritableReferenceCount, object);
+}

+ 3 - 4
panda/src/putil/typedWritableReferenceCount.h

@@ -40,18 +40,17 @@ public:
 
   virtual ReferenceCount *as_reference_count();
 
+PUBLISHED:
+  static PT(TypedWritableReferenceCount) decode_from_bam_stream(const string &data);
+
 public:
   virtual TypeHandle get_type() const {
     return get_class_type();
   }
   virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-PUBLISHED:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
-
-public:
   static void init_type() {
     TypedWritable::init_type();
     ReferenceCount::init_type();