Browse Source

Separate out CPython calls from core libs; eliminate need for pystub

rdb 9 years ago
parent
commit
47388b0dbc
100 changed files with 807 additions and 1093 deletions
  1. 1 1
      direct/src/dcparser/dcArrayParameter.h
  2. 1 1
      direct/src/dcparser/dcAtomicField.h
  3. 1 1
      direct/src/dcparser/dcClass.h
  4. 1 1
      direct/src/dcparser/dcClassParameter.h
  5. 1 1
      direct/src/dcparser/dcDeclaration.h
  6. 1 1
      direct/src/dcparser/dcField.h
  7. 1 1
      direct/src/dcparser/dcFile.h
  8. 1 1
      direct/src/dcparser/dcKeyword.h
  9. 1 1
      direct/src/dcparser/dcKeywordList.h
  10. 1 1
      direct/src/dcparser/dcMolecularField.h
  11. 1 1
      direct/src/dcparser/dcPackData.h
  12. 2 2
      direct/src/dcparser/dcPacker.h
  13. 1 1
      direct/src/dcparser/dcPackerCatalog.h
  14. 1 1
      direct/src/dcparser/dcPackerInterface.h
  15. 1 1
      direct/src/dcparser/dcParameter.h
  16. 1 1
      direct/src/dcparser/dcSimpleParameter.h
  17. 1 1
      direct/src/dcparser/dcSwitch.h
  18. 1 1
      direct/src/dcparser/dcSwitchParameter.h
  19. 1 1
      direct/src/dcparser/dcTypedef.h
  20. 22 2
      direct/src/distributed/cConnectionRepository.cxx
  21. 0 2
      direct/src/plugin/p3dCInstance.h
  22. 49 39
      direct/src/showbase/Loader.py
  23. 4 1
      direct/src/showutil/FreezeTool.py
  24. 10 10
      direct/src/stdpy/thread.py
  25. 48 4
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  26. 2 1
      dtool/src/interrogate/interfaceMakerPythonNative.h
  27. 0 4
      dtool/src/interrogate/interrogate.cxx
  28. 0 4
      dtool/src/interrogate/interrogate_module.cxx
  29. 1 1
      dtool/src/parser-inc/Python.h
  30. 0 1
      dtool/src/test_interrogate/test_interrogate.cxx
  31. 136 126
      makepanda/makepanda.py
  32. 0 4
      panda/src/android/pview.cxx
  33. 17 0
      panda/src/chan/animChannelFixed.cxx
  34. 2 0
      panda/src/chan/animChannelFixed.h
  35. 1 0
      panda/src/chan/p3chan_composite1.cxx
  36. 0 4
      panda/src/downloadertools/apply_patch.cxx
  37. 0 4
      panda/src/downloadertools/build_patch.cxx
  38. 0 4
      panda/src/downloadertools/check_adler.cxx
  39. 0 4
      panda/src/downloadertools/check_crc.cxx
  40. 0 4
      panda/src/downloadertools/check_md5.cxx
  41. 0 4
      panda/src/downloadertools/multify.cxx
  42. 0 4
      panda/src/downloadertools/pdecrypt.cxx
  43. 0 1
      panda/src/downloadertools/pencrypt.cxx
  44. 0 4
      panda/src/downloadertools/punzip.cxx
  45. 0 4
      panda/src/downloadertools/pzip.cxx
  46. 0 4
      panda/src/downloadertools/show_ddb.cxx
  47. 0 39
      panda/src/event/asyncTask.cxx
  48. 0 10
      panda/src/event/asyncTask.h
  49. 0 34
      panda/src/event/pythonTask.I
  50. 3 7
      panda/src/event/pythonTask.cxx
  51. 2 5
      panda/src/event/pythonTask.h
  52. 0 4
      panda/src/express/pointerToArray.h
  53. 12 6
      panda/src/express/pointerToArray_ext.I
  54. 0 4
      panda/src/express/pointerToArray_ext.h
  55. 1 1
      panda/src/gobj/geomVertexArrayData.h
  56. 10 6
      panda/src/gobj/geomVertexArrayData_ext.cxx
  57. 0 2
      panda/src/gobj/geomVertexArrayData_ext.h
  58. 0 20
      panda/src/nativenet/buffered_datagramconnection.cxx
  59. 4 0
      panda/src/pgraph/modelFlattenRequest.h
  60. 6 0
      panda/src/pgraph/modelLoadRequest.h
  61. 7 0
      panda/src/pgraph/modelSaveRequest.h
  62. 12 8
      panda/src/pgraph/nodePath.h
  63. 29 14
      panda/src/pgraph/nodePath_ext.I
  64. 1 1
      panda/src/pgraph/nodePath_ext.cxx
  65. 12 8
      panda/src/pgraph/nodePath_ext.h
  66. 4 1
      panda/src/pgraph/pandaNode.I
  67. 18 180
      panda/src/pgraph/pandaNode.cxx
  68. 18 11
      panda/src/pgraph/pandaNode.h
  69. 98 89
      panda/src/pgraph/pandaNode_ext.cxx
  70. 19 5
      panda/src/pgraph/pandaNode_ext.h
  71. 0 4
      panda/src/pipeline/config_pipeline.cxx
  72. 0 1
      panda/src/pipeline/p3pipeline_composite2.cxx
  73. 187 13
      panda/src/pipeline/pythonThread.cxx
  74. 10 1
      panda/src/pipeline/pythonThread.h
  75. 19 0
      panda/src/pipeline/thread.I
  76. 1 261
      panda/src/pipeline/thread.cxx
  77. 13 14
      panda/src/pipeline/thread.h
  78. 1 1
      panda/src/pnmimage/pfmFile.h
  79. 4 4
      panda/src/pnmimage/pfmFile_ext.cxx
  80. 0 2
      panda/src/pnmimage/pfmFile_ext.h
  81. 2 2
      panda/src/putil/bamReader_ext.cxx
  82. 2 3
      panda/src/putil/pythonCallbackObject.cxx
  83. 0 4
      panda/src/testbed/pgrid.cxx
  84. 0 4
      panda/src/testbed/pview.cxx
  85. 0 4
      pandatool/src/bam/bamInfo.cxx
  86. 0 4
      pandatool/src/bam/bamToEgg.cxx
  87. 0 4
      pandatool/src/bam/eggToBam.cxx
  88. 0 4
      pandatool/src/bam/ptsToBam.cxx
  89. 0 4
      pandatool/src/cvscopy/testCopy.cxx
  90. 0 4
      pandatool/src/daeprogs/daeToEgg.cxx
  91. 0 4
      pandatool/src/daeprogs/eggToDAE.cxx
  92. 0 4
      pandatool/src/dxfprogs/dxfPoints.cxx
  93. 0 4
      pandatool/src/dxfprogs/dxfToEgg.cxx
  94. 0 4
      pandatool/src/dxfprogs/eggToDXF.cxx
  95. 0 4
      pandatool/src/egg-mkfont/eggMakeFont.cxx
  96. 0 4
      pandatool/src/egg-optchar/eggOptchar.cxx
  97. 0 4
      pandatool/src/egg-palettize/eggPalettize.cxx
  98. 0 4
      pandatool/src/egg-qtess/eggQtess.cxx
  99. 0 4
      pandatool/src/eggprogs/eggCrop.cxx
  100. 0 4
      pandatool/src/eggprogs/eggListTextures.cxx

+ 1 - 1
direct/src/dcparser/dcArrayParameter.h

@@ -23,7 +23,7 @@
  * parameter type accepts an arbitrary (or possibly fixed) number of nested
  * parameter type accepts an arbitrary (or possibly fixed) number of nested
  * fields, all of which are of the same type.
  * fields, all of which are of the same type.
  */
  */
-class EXPCL_DIRECT DCArrayParameter : public DCParameter {
+class DCArrayParameter : public DCParameter {
 public:
 public:
   DCArrayParameter(DCParameter *element_type,
   DCArrayParameter(DCParameter *element_type,
                    const DCUnsignedIntRange &size = DCUnsignedIntRange());
                    const DCUnsignedIntRange &size = DCUnsignedIntRange());

+ 1 - 1
direct/src/dcparser/dcAtomicField.h

@@ -27,7 +27,7 @@
  * This defines an interface to the Distributed Class, and is always
  * This defines an interface to the Distributed Class, and is always
  * implemented as a remote procedure method.
  * implemented as a remote procedure method.
  */
  */
-class EXPCL_DIRECT DCAtomicField : public DCField {
+class DCAtomicField : public DCField {
 public:
 public:
   DCAtomicField(const string &name, DCClass *dclass, bool bogus_field);
   DCAtomicField(const string &name, DCClass *dclass, bool bogus_field);
   virtual ~DCAtomicField();
   virtual ~DCAtomicField();

+ 1 - 1
direct/src/dcparser/dcClass.h

@@ -41,7 +41,7 @@ class DCParameter;
 /**
 /**
  * Defines a particular DistributedClass as read from an input .dc file.
  * Defines a particular DistributedClass as read from an input .dc file.
  */
  */
-class EXPCL_DIRECT DCClass : public DCDeclaration {
+class DCClass : public DCDeclaration {
 public:
 public:
   DCClass(DCFile *dc_file, const string &name,
   DCClass(DCFile *dc_file, const string &name,
           bool is_struct, bool bogus_class);
           bool is_struct, bool bogus_class);

+ 1 - 1
direct/src/dcparser/dcClassParameter.h

@@ -23,7 +23,7 @@ class DCClass;
  * This represents a class (or struct) object used as a parameter itself.
  * This represents a class (or struct) object used as a parameter itself.
  * This means that all the fields of the class get packed into the message.
  * This means that all the fields of the class get packed into the message.
  */
  */
-class EXPCL_DIRECT DCClassParameter : public DCParameter {
+class DCClassParameter : public DCParameter {
 public:
 public:
   DCClassParameter(const DCClass *dclass);
   DCClassParameter(const DCClass *dclass);
   DCClassParameter(const DCClassParameter &copy);
   DCClassParameter(const DCClassParameter &copy);

+ 1 - 1
direct/src/dcparser/dcDeclaration.h

@@ -26,7 +26,7 @@ class DCSwitch;
  * only purpose is so that classes and typedefs can be stored in one list
  * only purpose is so that classes and typedefs can be stored in one list
  * together so they can be ordered correctly on output.
  * together so they can be ordered correctly on output.
  */
  */
-class EXPCL_DIRECT DCDeclaration {
+class DCDeclaration {
 public:
 public:
   virtual ~DCDeclaration();
   virtual ~DCDeclaration();
 
 

+ 1 - 1
direct/src/dcparser/dcField.h

@@ -34,7 +34,7 @@ class HashGenerator;
 /**
 /**
  * A single field of a Distributed Class, either atomic or molecular.
  * A single field of a Distributed Class, either atomic or molecular.
  */
  */
-class EXPCL_DIRECT DCField : public DCPackerInterface, public DCKeywordList {
+class DCField : public DCPackerInterface, public DCKeywordList {
 public:
 public:
   DCField();
   DCField();
   DCField(const string &name, DCClass *dclass);
   DCField(const string &name, DCClass *dclass);

+ 1 - 1
direct/src/dcparser/dcFile.h

@@ -29,7 +29,7 @@ class DCDeclaration;
  * Represents the complete list of Distributed Class descriptions as read from
  * Represents the complete list of Distributed Class descriptions as read from
  * a .dc file.
  * a .dc file.
  */
  */
-class EXPCL_DIRECT DCFile {
+class DCFile {
 PUBLISHED:
 PUBLISHED:
   DCFile();
   DCFile();
   ~DCFile();
   ~DCFile();

+ 1 - 1
direct/src/dcparser/dcKeyword.h

@@ -25,7 +25,7 @@ class HashGenerator;
  * define a communication property associated with a field, for instance
  * define a communication property associated with a field, for instance
  * "broadcast" or "airecv".
  * "broadcast" or "airecv".
  */
  */
-class EXPCL_DIRECT DCKeyword : public DCDeclaration {
+class DCKeyword : public DCDeclaration {
 public:
 public:
   DCKeyword(const string &name, int historical_flag = ~0);
   DCKeyword(const string &name, int historical_flag = ~0);
   virtual ~DCKeyword();
   virtual ~DCKeyword();

+ 1 - 1
direct/src/dcparser/dcKeywordList.h

@@ -23,7 +23,7 @@ class HashGenerator;
  * This is a list of keywords (see DCKeyword) that may be set on a particular
  * This is a list of keywords (see DCKeyword) that may be set on a particular
  * field.
  * field.
  */
  */
-class EXPCL_DIRECT DCKeywordList {
+class DCKeywordList {
 public:
 public:
   DCKeywordList();
   DCKeywordList();
   DCKeywordList(const DCKeywordList &copy);
   DCKeywordList(const DCKeywordList &copy);

+ 1 - 1
direct/src/dcparser/dcMolecularField.h

@@ -25,7 +25,7 @@ class DCParameter;
  * This represents a combination of two or more related atomic fields, that
  * This represents a combination of two or more related atomic fields, that
  * will often be treated as a unit.
  * will often be treated as a unit.
  */
  */
-class EXPCL_DIRECT DCMolecularField : public DCField {
+class DCMolecularField : public DCField {
 public:
 public:
   DCMolecularField(const string &name, DCClass *dclass);
   DCMolecularField(const string &name, DCClass *dclass);
 
 

+ 1 - 1
direct/src/dcparser/dcPackData.h

@@ -19,7 +19,7 @@
 /**
 /**
  * This is a block of data that receives the results of DCPacker.
  * This is a block of data that receives the results of DCPacker.
  */
  */
-class EXPCL_DIRECT DCPackData {
+class DCPackData {
 PUBLISHED:
 PUBLISHED:
   INLINE DCPackData();
   INLINE DCPackData();
   INLINE ~DCPackData();
   INLINE ~DCPackData();

+ 2 - 2
direct/src/dcparser/dcPacker.h

@@ -31,7 +31,7 @@ class DCSwitchParameter;
  * See also direct/src/doc/dcPacker.txt for a more complete description and
  * See also direct/src/doc/dcPacker.txt for a more complete description and
  * examples of using this class.
  * examples of using this class.
  */
  */
-class EXPCL_DIRECT DCPacker {
+class DCPacker {
 PUBLISHED:
 PUBLISHED:
   DCPacker();
   DCPacker();
   ~DCPacker();
   ~DCPacker();
@@ -216,7 +216,7 @@ private:
   const DCPackerCatalog *_catalog;
   const DCPackerCatalog *_catalog;
   const DCPackerCatalog::LiveCatalog *_live_catalog;
   const DCPackerCatalog::LiveCatalog *_live_catalog;
 
 
-  class EXPCL_DIRECT StackElement {
+  class StackElement {
   public:
   public:
     // As an optimization, we implement operator new and delete here to
     // As an optimization, we implement operator new and delete here to
     // minimize allocation overhead during push() and pop().
     // minimize allocation overhead during push() and pop().

+ 1 - 1
direct/src/dcparser/dcPackerCatalog.h

@@ -26,7 +26,7 @@ class DCSwitchParameter;
  * requested from a particular field; its ownership is retained by the field
  * requested from a particular field; its ownership is retained by the field
  * so it must not be deleted.
  * so it must not be deleted.
  */
  */
-class EXPCL_DIRECT DCPackerCatalog {
+class DCPackerCatalog {
 private:
 private:
   DCPackerCatalog(const DCPackerInterface *root);
   DCPackerCatalog(const DCPackerInterface *root);
   DCPackerCatalog(const DCPackerCatalog &copy);
   DCPackerCatalog(const DCPackerCatalog &copy);

+ 1 - 1
direct/src/dcparser/dcPackerInterface.h

@@ -64,7 +64,7 @@ END_PUBLISH
  * Normally these methods are called only by the DCPacker object; the user
  * Normally these methods are called only by the DCPacker object; the user
  * wouldn't normally call these directly.
  * wouldn't normally call these directly.
  */
  */
-class EXPCL_DIRECT DCPackerInterface {
+class DCPackerInterface {
 public:
 public:
   DCPackerInterface(const string &name = string());
   DCPackerInterface(const string &name = string());
   DCPackerInterface(const DCPackerInterface &copy);
   DCPackerInterface(const DCPackerInterface &copy);

+ 1 - 1
direct/src/dcparser/dcParameter.h

@@ -32,7 +32,7 @@ class HashGenerator;
  * This may also be a typedef reference to another type, which has the same
  * This may also be a typedef reference to another type, which has the same
  * properties as the referenced type, but a different name.
  * properties as the referenced type, but a different name.
  */
  */
-class EXPCL_DIRECT DCParameter : public DCField {
+class DCParameter : public DCField {
 protected:
 protected:
   DCParameter();
   DCParameter();
   DCParameter(const DCParameter &copy);
   DCParameter(const DCParameter &copy);

+ 1 - 1
direct/src/dcparser/dcSimpleParameter.h

@@ -25,7 +25,7 @@
  * divisor, which is meaningful only for the numeric type elements (and
  * divisor, which is meaningful only for the numeric type elements (and
  * represents a fixed-point numeric convention).
  * represents a fixed-point numeric convention).
  */
  */
-class EXPCL_DIRECT DCSimpleParameter : public DCParameter {
+class DCSimpleParameter : public DCParameter {
 public:
 public:
   DCSimpleParameter(DCSubatomicType type, unsigned int divisor = 1);
   DCSimpleParameter(DCSubatomicType type, unsigned int divisor = 1);
   DCSimpleParameter(const DCSimpleParameter &copy);
   DCSimpleParameter(const DCSimpleParameter &copy);

+ 1 - 1
direct/src/dcparser/dcSwitch.h

@@ -27,7 +27,7 @@ class DCField;
  * and represents two or more alternative unpacking schemes based on the first
  * and represents two or more alternative unpacking schemes based on the first
  * field read.
  * field read.
  */
  */
-class EXPCL_DIRECT DCSwitch : public DCDeclaration {
+class DCSwitch : public DCDeclaration {
 public:
 public:
   DCSwitch(const string &name, DCField *key_parameter);
   DCSwitch(const string &name, DCField *key_parameter);
   virtual ~DCSwitch();
   virtual ~DCSwitch();

+ 1 - 1
direct/src/dcparser/dcSwitchParameter.h

@@ -23,7 +23,7 @@ class DCSwitch;
  * This represents a switch object used as a parameter itself, which packs the
  * This represents a switch object used as a parameter itself, which packs the
  * appropriate fields of the switch into the message.
  * appropriate fields of the switch into the message.
  */
  */
-class EXPCL_DIRECT DCSwitchParameter : public DCParameter {
+class DCSwitchParameter : public DCParameter {
 public:
 public:
   DCSwitchParameter(const DCSwitch *dswitch);
   DCSwitchParameter(const DCSwitch *dswitch);
   DCSwitchParameter(const DCSwitchParameter &copy);
   DCSwitchParameter(const DCSwitchParameter &copy);

+ 1 - 1
direct/src/dcparser/dcTypedef.h

@@ -23,7 +23,7 @@ class DCParameter;
  * This represents a single typedef declaration in the dc file.  It assigns a
  * This represents a single typedef declaration in the dc file.  It assigns a
  * particular type to a new name, just like a C typedef.
  * particular type to a new name, just like a C typedef.
  */
  */
-class EXPCL_DIRECT DCTypedef : public DCDeclaration {
+class DCTypedef : public DCDeclaration {
 public:
 public:
   DCTypedef(DCParameter *parameter, bool implicit = false);
   DCTypedef(DCParameter *parameter, bool implicit = false);
   DCTypedef(const string &name);
   DCTypedef(const string &name);

+ 22 - 2
direct/src/distributed/cConnectionRepository.cxx

@@ -402,8 +402,28 @@ send_datagram(const Datagram &dg) {
   }
   }
 
 
 #ifdef WANT_NATIVE_NET
 #ifdef WANT_NATIVE_NET
-  if(_native)
-    return _bdc.SendMessage(dg);
+  if (_native) {
+    bool result = _bdc.SendMessage();
+    if (!result && _bdc.IsConnected()) {
+#ifdef HAVE_PYTHON
+      ostringstream s;
+
+#if PY_VERSION_HEX >= 0x03030000
+      PyObject *exc_type = PyExc_ConnectionError;
+#else
+      PyObject *exc_type = PyExc_OSError;
+#endif
+
+      s << endl << "Error sending message: " << endl;
+      msg.dump_hex(s);
+      s << "Message data: " << msg.get_data() << endl;
+
+      string message = s.str();
+      PyErr_SetString(exc_type, message.c_str());
+#endif
+    }
+    return result;
+  }
 #endif
 #endif
 
 
 #ifdef HAVE_NET
 #ifdef HAVE_NET

+ 0 - 2
direct/src/plugin/p3dCInstance.h

@@ -21,8 +21,6 @@
 #include "get_tinyxml.h"
 #include "get_tinyxml.h"
 #include "windowHandle.h"
 #include "windowHandle.h"
 
 
-#include <Python.h>
-
 class P3DSession;
 class P3DSession;
 
 
 /**
 /**

+ 49 - 39
direct/src/showbase/Loader.py

@@ -28,7 +28,7 @@ class Loader(DirectObject):
             self.extraArgs = extraArgs
             self.extraArgs = extraArgs
             self.numRemaining = numObjects
             self.numRemaining = numObjects
             self.cancelled = False
             self.cancelled = False
-            self.requests = {}
+            self.requests = set()
 
 
         def gotObject(self, index, object):
         def gotObject(self, index, object):
             self.objects[index] = object
             self.objects[index] = object
@@ -45,6 +45,8 @@ class Loader(DirectObject):
         self.base = base
         self.base = base
         self.loader = PandaLoader.getGlobalPtr()
         self.loader = PandaLoader.getGlobalPtr()
 
 
+        self.__requests = {}
+
         self.hook = "async_loader_%s" % (Loader.loaderIndex)
         self.hook = "async_loader_%s" % (Loader.loaderIndex)
         Loader.loaderIndex += 1
         Loader.loaderIndex += 1
         self.accept(self.hook, self.__gotAsyncObject)
         self.accept(self.hook, self.__gotAsyncObject)
@@ -116,7 +118,7 @@ class Loader(DirectObject):
         """
         """
 
 
         assert Loader.notify.debug("Loading model: %s" % (modelPath))
         assert Loader.notify.debug("Loading model: %s" % (modelPath))
-        if loaderOptions == None:
+        if loaderOptions is None:
             loaderOptions = LoaderOptions()
             loaderOptions = LoaderOptions()
         else:
         else:
             loaderOptions = LoaderOptions(loaderOptions)
             loaderOptions = LoaderOptions(loaderOptions)
@@ -156,7 +158,7 @@ class Loader(DirectObject):
             result = []
             result = []
             for modelPath in modelList:
             for modelPath in modelList:
                 node = self.loader.loadSync(Filename(modelPath), loaderOptions)
                 node = self.loader.loadSync(Filename(modelPath), loaderOptions)
-                if (node != None):
+                if node is not None:
                     nodePath = NodePath(node)
                     nodePath = NodePath(node)
                 else:
                 else:
                     nodePath = None
                     nodePath = None
@@ -179,16 +181,16 @@ class Loader(DirectObject):
             # callback (passing it the models on the parameter list).
             # callback (passing it the models on the parameter list).
 
 
             cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
             cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
-            i=0
+            i = 0
             for modelPath in modelList:
             for modelPath in modelList:
                 request = self.loader.makeAsyncRequest(Filename(modelPath), loaderOptions)
                 request = self.loader.makeAsyncRequest(Filename(modelPath), loaderOptions)
                 if priority is not None:
                 if priority is not None:
                     request.setPriority(priority)
                     request.setPriority(priority)
                 request.setDoneEvent(self.hook)
                 request.setDoneEvent(self.hook)
-                request.setPythonObject((cb, i))
-                i+=1
                 self.loader.loadAsync(request)
                 self.loader.loadAsync(request)
-                cb.requests[request] = True
+                cb.requests.add(request)
+                self.__requests[request] = (cb, i)
+                i += 1
             return cb
             return cb
 
 
     def cancelRequest(self, cb):
     def cancelRequest(self, cb):
@@ -200,6 +202,7 @@ class Loader(DirectObject):
             cb.cancelled = True
             cb.cancelled = True
             for request in cb.requests:
             for request in cb.requests:
                 self.loader.remove(request)
                 self.loader.remove(request)
+                del self.__requests[request]
             cb.requests = None
             cb.requests = None
 
 
     def isRequestPending(self, cb):
     def isRequestPending(self, cb):
@@ -273,7 +276,7 @@ class Loader(DirectObject):
             # to resolve it for us.
             # to resolve it for us.
             options = LoaderOptions(LoaderOptions.LFSearch | LoaderOptions.LFNoDiskCache | LoaderOptions.LFCacheOnly)
             options = LoaderOptions(LoaderOptions.LFSearch | LoaderOptions.LFNoDiskCache | LoaderOptions.LFCacheOnly)
             modelNode = self.loader.loadSync(Filename(model), options)
             modelNode = self.loader.loadSync(Filename(model), options)
-            if modelNode == None:
+            if modelNode is None:
                 # Model not found.
                 # Model not found.
                 assert Loader.notify.debug("Unloading model not loaded: %s" % (model))
                 assert Loader.notify.debug("Unloading model not loaded: %s" % (model))
                 return
                 return
@@ -293,7 +296,7 @@ class Loader(DirectObject):
         a callback is used, the model is saved asynchronously, and the
         a callback is used, the model is saved asynchronously, and the
         true/false status is passed to the callback function. """
         true/false status is passed to the callback function. """
 
 
-        if loaderOptions == None:
+        if loaderOptions is None:
             loaderOptions = LoaderOptions()
             loaderOptions = LoaderOptions()
         else:
         else:
             loaderOptions = LoaderOptions(loaderOptions)
             loaderOptions = LoaderOptions(loaderOptions)
@@ -342,16 +345,16 @@ class Loader(DirectObject):
             # callback (passing it the models on the parameter list).
             # callback (passing it the models on the parameter list).
 
 
             cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
             cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
-            i=0
+            i = 0
             for modelPath, node in modelList:
             for modelPath, node in modelList:
                 request = self.loader.makeAsyncSaveRequest(Filename(modelPath), loaderOptions, node)
                 request = self.loader.makeAsyncSaveRequest(Filename(modelPath), loaderOptions, node)
                 if priority is not None:
                 if priority is not None:
                     request.setPriority(priority)
                     request.setPriority(priority)
                 request.setDoneEvent(self.hook)
                 request.setDoneEvent(self.hook)
-                request.setPythonObject((cb, i))
-                i+=1
                 self.loader.saveAsync(request)
                 self.loader.saveAsync(request)
-                cb.requests[request] = True
+                cb.requests.add(request)
+                self.__requests[request] = (cb, i)
+                i += 1
             return cb
             return cb
 
 
 
 
@@ -496,7 +499,7 @@ class Loader(DirectObject):
             phaseChecker(modelPath, loaderOptions)
             phaseChecker(modelPath, loaderOptions)
 
 
         font = FontPool.loadFont(modelPath)
         font = FontPool.loadFont(modelPath)
-        if font == None:
+        if font is None:
             if not okMissing:
             if not okMissing:
                 message = 'Could not load font file: %s' % (modelPath)
                 message = 'Could not load font file: %s' % (modelPath)
                 raise IOError(message)
                 raise IOError(message)
@@ -506,21 +509,21 @@ class Loader(DirectObject):
 
 
         # The following properties may only be set for dynamic fonts.
         # The following properties may only be set for dynamic fonts.
         if hasattr(font, "setPointSize"):
         if hasattr(font, "setPointSize"):
-            if pointSize != None:
+            if pointSize is not None:
                 font.setPointSize(pointSize)
                 font.setPointSize(pointSize)
-            if pixelsPerUnit != None:
+            if pixelsPerUnit is not None:
                 font.setPixelsPerUnit(pixelsPerUnit)
                 font.setPixelsPerUnit(pixelsPerUnit)
-            if scaleFactor != None:
+            if scaleFactor is not None:
                 font.setScaleFactor(scaleFactor)
                 font.setScaleFactor(scaleFactor)
-            if textureMargin != None:
+            if textureMargin is not None:
                 font.setTextureMargin(textureMargin)
                 font.setTextureMargin(textureMargin)
-            if polyMargin != None:
+            if polyMargin is not None:
                 font.setPolyMargin(polyMargin)
                 font.setPolyMargin(polyMargin)
-            if minFilter != None:
+            if minFilter is not None:
                 font.setMinfilter(minFilter)
                 font.setMinfilter(minFilter)
-            if magFilter != None:
+            if magFilter is not None:
                 font.setMagfilter(magFilter)
                 font.setMagfilter(magFilter)
-            if anisotropicDegree != None:
+            if anisotropicDegree is not None:
                 font.setAnisotropicDegree(anisotropicDegree)
                 font.setAnisotropicDegree(anisotropicDegree)
             if color:
             if color:
                 font.setFg(color)
                 font.setFg(color)
@@ -577,10 +580,10 @@ class Loader(DirectObject):
         the texture and the number of expected mipmap images.
         the texture and the number of expected mipmap images.
 
 
         If minfilter or magfilter is not None, they should be a symbol
         If minfilter or magfilter is not None, they should be a symbol
-        like Texture.FTLinear or Texture.FTNearest.  (minfilter may be
-        further one of the Mipmap filter type symbols.)  These specify
-        the filter mode that will automatically be applied to the
-        texture when it is loaded.  Note that this setting may
+        like SamplerState.FTLinear or SamplerState.FTNearest.  (minfilter
+        may be further one of the Mipmap filter type symbols.)  These
+        specify the filter mode that will automatically be applied to
+        the texture when it is loaded.  Note that this setting may
         override the texture's existing settings, even if it has
         override the texture's existing settings, even if it has
         already been loaded.  See egg-texture-cards for a more robust
         already been loaded.  See egg-texture-cards for a more robust
         way to apply per-texture filter types and settings.
         way to apply per-texture filter types and settings.
@@ -596,7 +599,7 @@ class Loader(DirectObject):
         left image and '1' for the right image.  Larger numbers are
         left image and '1' for the right image.  Larger numbers are
         also allowed if you need more than two views.
         also allowed if you need more than two views.
         """
         """
-        if loaderOptions == None:
+        if loaderOptions is None:
             loaderOptions = LoaderOptions()
             loaderOptions = LoaderOptions()
         else:
         else:
             loaderOptions = LoaderOptions(loaderOptions)
             loaderOptions = LoaderOptions(loaderOptions)
@@ -657,7 +660,7 @@ class Loader(DirectObject):
         numbered 8 - 15 will be part of the right eye view.
         numbered 8 - 15 will be part of the right eye view.
         """
         """
         assert Loader.notify.debug("Loading 3-D texture: %s" % (texturePattern))
         assert Loader.notify.debug("Loading 3-D texture: %s" % (texturePattern))
-        if loaderOptions == None:
+        if loaderOptions is None:
             loaderOptions = LoaderOptions()
             loaderOptions = LoaderOptions()
         else:
         else:
             loaderOptions = LoaderOptions(loaderOptions)
             loaderOptions = LoaderOptions(loaderOptions)
@@ -711,7 +714,7 @@ class Loader(DirectObject):
         and each six images will define a new view.
         and each six images will define a new view.
         """
         """
         assert Loader.notify.debug("Loading cube map: %s" % (texturePattern))
         assert Loader.notify.debug("Loading cube map: %s" % (texturePattern))
-        if loaderOptions == None:
+        if loaderOptions is None:
             loaderOptions = LoaderOptions()
             loaderOptions = LoaderOptions()
         else:
         else:
             loaderOptions = LoaderOptions(loaderOptions)
             loaderOptions = LoaderOptions(loaderOptions)
@@ -821,13 +824,12 @@ class Loader(DirectObject):
             # callback (passing it the sounds on the parameter list).
             # callback (passing it the sounds on the parameter list).
 
 
             cb = Loader.Callback(len(soundList), gotList, callback, extraArgs)
             cb = Loader.Callback(len(soundList), gotList, callback, extraArgs)
-            for i in range(len(soundList)):
-                soundPath = soundList[i]
+            for i, soundPath in enumerate(soundList):
                 request = AudioLoadRequest(manager, soundPath, positional)
                 request = AudioLoadRequest(manager, soundPath, positional)
                 request.setDoneEvent(self.hook)
                 request.setDoneEvent(self.hook)
-                request.setPythonObject((cb, i))
                 self.loader.loadAsync(request)
                 self.loader.loadAsync(request)
-                cb.requests[request] = True
+                cb.requests.add(request)
+                self.__requests[request] = (cb, i)
             return cb
             return cb
 
 
     def unloadSfx(self, sfx):
     def unloadSfx(self, sfx):
@@ -852,7 +854,7 @@ class Loader(DirectObject):
         return shader
         return shader
 
 
     def unloadShader(self, shaderPath):
     def unloadShader(self, shaderPath):
-        if (shaderPath != None):
+        if shaderPath is not None:
             ShaderPool.releaseShader(shaderPath)
             ShaderPool.releaseShader(shaderPath)
 
 
     def asyncFlattenStrong(self, model, inPlace = True,
     def asyncFlattenStrong(self, model, inPlace = True,
@@ -886,14 +888,15 @@ class Loader(DirectObject):
             gotList = True
             gotList = True
 
 
         cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
         cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
-        i=0
+        i = 0
         for model in modelList:
         for model in modelList:
             request = ModelFlattenRequest(model.node())
             request = ModelFlattenRequest(model.node())
             request.setDoneEvent(self.hook)
             request.setDoneEvent(self.hook)
             request.setPythonObject((cb, i))
             request.setPythonObject((cb, i))
-            i+=1
             self.loader.loadAsync(request)
             self.loader.loadAsync(request)
             cb.requests[request] = True
             cb.requests[request] = True
+            self.__requests[request] = (cb, i)
+            i += 1
         return cb
         return cb
 
 
     def __asyncFlattenDone(self, models,
     def __asyncFlattenDone(self, models,
@@ -921,16 +924,23 @@ class Loader(DirectObject):
         of loaded objects, and call the appropriate callback when it's
         of loaded objects, and call the appropriate callback when it's
         time."""
         time."""
 
 
-        cb, i = request.getPythonObject()
+        if request not in self.__requests:
+            return
+
+        cb, i = self.__requests[request]
         if cb.cancelled:
         if cb.cancelled:
+            # Shouldn't be here.
+            del self.__requests[request]
             return
             return
 
 
-        del cb.requests[request]
+        cb.requests.discard(request)
+        if not cb.requests:
+            del self.__requests[request]
 
 
         object = None
         object = None
         if hasattr(request, "getModel"):
         if hasattr(request, "getModel"):
             node = request.getModel()
             node = request.getModel()
-            if (node != None):
+            if node is not None:
                 object = NodePath(node)
                 object = NodePath(node)
 
 
         elif hasattr(request, "getSound"):
         elif hasattr(request, "getSound"):

+ 4 - 1
direct/src/showutil/FreezeTool.py

@@ -984,7 +984,10 @@ class Freezer:
             try:
             try:
                 self.__loadModule(mdef)
                 self.__loadModule(mdef)
             except ImportError as ex:
             except ImportError as ex:
-                print("Unknown module: %s (%s)" % (mdef.moduleName, str(ex)))
+                message = "Unknown module: %s" % (mdef.moduleName)
+                if str(ex) != "No module named " + str(mdef.moduleName):
+                    message += " (%s)" % (ex)
+                print(message)
 
 
         # Also attempt to import any implicit modules.  If any of
         # Also attempt to import any implicit modules.  If any of
         # these fail to import, we don't really care.
         # these fail to import, we don't really care.

+ 10 - 10
direct/src/stdpy/thread.py

@@ -102,7 +102,7 @@ def start_new_thread(function, args, kwargs = {}, name = None):
             name = 'PythonThread-%s' % (threadId)
             name = 'PythonThread-%s' % (threadId)
 
 
         thread = core.PythonThread(threadFunc, [threadId], name, name)
         thread = core.PythonThread(threadFunc, [threadId], name, name)
-        thread.setPythonData(threadId)
+        thread.setPythonIndex(threadId)
         _threads[threadId] = (thread, {}, None)
         _threads[threadId] = (thread, {}, None)
 
 
         thread.start(core.TPNormal, False)
         thread.start(core.TPNormal, False)
@@ -121,7 +121,7 @@ def _add_thread(thread, wrapper):
         threadId = _nextThreadId
         threadId = _nextThreadId
         _nextThreadId += 1
         _nextThreadId += 1
 
 
-        thread.setPythonData(threadId)
+        thread.setPythonIndex(threadId)
         _threads[threadId] = (thread, {}, wrapper)
         _threads[threadId] = (thread, {}, wrapper)
         return threadId
         return threadId
 
 
@@ -133,8 +133,8 @@ def _get_thread_wrapper(thread, wrapperClass):
     is not one, creates an instance of the indicated wrapperClass
     is not one, creates an instance of the indicated wrapperClass
     instead. """
     instead. """
 
 
-    threadId = thread.getPythonData()
-    if threadId is None:
+    threadId = thread.getPythonIndex()
+    if threadId == -1:
         # The thread has never been assigned a threadId.  Go assign one.
         # The thread has never been assigned a threadId.  Go assign one.
 
 
         global _nextThreadId
         global _nextThreadId
@@ -143,7 +143,7 @@ def _get_thread_wrapper(thread, wrapperClass):
             threadId = _nextThreadId
             threadId = _nextThreadId
             _nextThreadId += 1
             _nextThreadId += 1
 
 
-            thread.setPythonData(threadId)
+            thread.setPythonIndex(threadId)
             wrapper = wrapperClass(thread, threadId)
             wrapper = wrapperClass(thread, threadId)
             _threads[threadId] = (thread, {}, wrapper)
             _threads[threadId] = (thread, {}, wrapper)
             return wrapper
             return wrapper
@@ -169,8 +169,8 @@ def _get_thread_locals(thread, i):
     """ Returns the locals dictionary for the indicated thread.  If
     """ Returns the locals dictionary for the indicated thread.  If
     there is not one, creates an empty dictionary. """
     there is not one, creates an empty dictionary. """
 
 
-    threadId = thread.getPythonData()
-    if threadId is None:
+    threadId = thread.getPythonIndex()
+    if threadId == -1:
         # The thread has never been assigned a threadId.  Go assign one.
         # The thread has never been assigned a threadId.  Go assign one.
 
 
         global _nextThreadId
         global _nextThreadId
@@ -179,7 +179,7 @@ def _get_thread_locals(thread, i):
             threadId = _nextThreadId
             threadId = _nextThreadId
             _nextThreadId += 1
             _nextThreadId += 1
 
 
-            thread.setPythonData(threadId)
+            thread.setPythonIndex(threadId)
             locals = {}
             locals = {}
             _threads[threadId] = (thread, locals, None)
             _threads[threadId] = (thread, locals, None)
             return locals.setdefault(i, {})
             return locals.setdefault(i, {})
@@ -205,9 +205,9 @@ def _remove_thread_id(threadId):
     _threadsLock.acquire()
     _threadsLock.acquire()
     try:
     try:
         thread, locals, wrapper = _threads[threadId]
         thread, locals, wrapper = _threads[threadId]
-        assert thread.getPythonData() == threadId
+        assert thread.getPythonIndex() == threadId
         del _threads[threadId]
         del _threads[threadId]
-        thread.setPythonData(None)
+        thread.setPythonIndex(-1)
 
 
     finally:
     finally:
         _threadsLock.release()
         _threadsLock.release()

+ 48 - 4
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -1259,9 +1259,16 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
       if (is_cpp_type_legal(object->_itype._cpptype) &&
       if (is_cpp_type_legal(object->_itype._cpptype) &&
           isExportThisRun(object->_itype._cpptype)) {
           isExportThisRun(object->_itype._cpptype)) {
         string class_name = make_safe_name(object->_itype.get_scoped_name());
         string class_name = make_safe_name(object->_itype.get_scoped_name());
-        bool is_typed = HasAGetClassTypeFunction(object->_itype._cpptype);
+        bool is_typed = has_get_class_type_function(object->_itype._cpptype);
 
 
         if (is_typed) {
         if (is_typed) {
+          if (has_init_type_function(object->_itype._cpptype)) {
+            // Call the init_type function.  This isn't necessary for all
+            // types as many of them are automatically initialized at static
+            // init type, but for some extension classes it's useful.
+            out << "  " << object->_itype._cpptype->get_local_name(&parser)
+                << "::init_type();\n";
+          }
           out << "  Dtool_" << class_name << "._type = "
           out << "  Dtool_" << class_name << "._type = "
               << object->_itype._cpptype->get_local_name(&parser)
               << object->_itype._cpptype->get_local_name(&parser)
               << "::get_class_type();\n"
               << "::get_class_type();\n"
@@ -1291,7 +1298,7 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
     string class_name = (*ii)->get_local_name(&parser);
     string class_name = (*ii)->get_local_name(&parser);
     string safe_name = make_safe_name(class_name);
     string safe_name = make_safe_name(class_name);
 
 
-    if (HasAGetClassTypeFunction(*ii)) {
+    if (has_get_class_type_function(*ii)) {
       out << "  Dtool_Ptr_" << safe_name << " = LookupRuntimeTypedClass(" << class_name << "::get_class_type());\n";
       out << "  Dtool_Ptr_" << safe_name << " = LookupRuntimeTypedClass(" << class_name << "::get_class_type());\n";
     } else {
     } else {
       out << "  Dtool_Ptr_" << safe_name << " = LookupNamedClass(\"" << class_name << "\");\n";
       out << "  Dtool_Ptr_" << safe_name << " = LookupNamedClass(\"" << class_name << "\");\n";
@@ -1541,7 +1548,7 @@ write_module_class(ostream &out, Object *obj) {
   std::string export_class_name = classNameFromCppName(obj->_itype.get_name(), false);
   std::string export_class_name = classNameFromCppName(obj->_itype.get_name(), false);
 
 
   bool is_runtime_typed = IsPandaTypedObject(obj->_itype._cpptype->as_struct_type());
   bool is_runtime_typed = IsPandaTypedObject(obj->_itype._cpptype->as_struct_type());
-  if (!is_runtime_typed && HasAGetClassTypeFunction(obj->_itype._cpptype)) {
+  if (!is_runtime_typed && has_get_class_type_function(obj->_itype._cpptype)) {
     is_runtime_typed = true;
     is_runtime_typed = true;
   }
   }
 
 
@@ -7010,7 +7017,7 @@ DoesInheritFromIsClass(const CPPStructType *inclass, const std::string &name) {
 
 
  */
  */
 bool InterfaceMakerPythonNative::
 bool InterfaceMakerPythonNative::
-HasAGetClassTypeFunction(CPPType *type) {
+has_get_class_type_function(CPPType *type) {
   while (type->get_subtype() == CPPDeclaration::ST_typedef) {
   while (type->get_subtype() == CPPDeclaration::ST_typedef) {
     type = type->as_typedef_type()->_type;
     type = type->as_typedef_type()->_type;
   }
   }
@@ -7025,6 +7032,43 @@ HasAGetClassTypeFunction(CPPType *type) {
   return scope->_functions.find("get_class_type") != scope->_functions.end();
   return scope->_functions.find("get_class_type") != scope->_functions.end();
 }
 }
 
 
+/**
+ *
+ */
+bool InterfaceMakerPythonNative::
+has_init_type_function(CPPType *type) {
+  while (type->get_subtype() == CPPDeclaration::ST_typedef) {
+    type = type->as_typedef_type()->_type;
+  }
+
+  CPPStructType *struct_type = type->as_struct_type();
+  if (struct_type == NULL) {
+    return false;
+  }
+
+  CPPScope *scope = struct_type->get_scope();
+  CPPScope::Functions::const_iterator it = scope->_functions.find("init_type");
+  if (it == scope->_functions.end()) {
+    return false;
+  }
+  const CPPFunctionGroup *group = it->second;
+
+  CPPFunctionGroup::Instances::const_iterator ii;
+  for (ii = group->_instances.begin(); ii != group->_instances.end(); ++ii) {
+    const CPPInstance *cppinst = *ii;
+    const CPPFunctionType *cppfunc = cppinst->_type->as_function_type();
+
+    if (cppfunc != NULL &&
+        cppfunc->_parameters != NULL &&
+        cppfunc->_parameters->_parameters.size() == 0 &&
+        (cppinst->_storage_class & CPPInstance::SC_static) != 0) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 /**
 /**
  * Returns -1 if the class does not define write() (and therefore cannot
  * Returns -1 if the class does not define write() (and therefore cannot
  * support a __str__ function).
  * support a __str__ function).

+ 2 - 1
dtool/src/interrogate/interfaceMakerPythonNative.h

@@ -201,7 +201,8 @@ public:
   bool DoesInheritFromIsClass(const CPPStructType * inclass, const std::string &name);
   bool DoesInheritFromIsClass(const CPPStructType * inclass, const std::string &name);
   bool IsPandaTypedObject(CPPStructType * inclass) { return DoesInheritFromIsClass(inclass,"TypedObject"); };
   bool IsPandaTypedObject(CPPStructType * inclass) { return DoesInheritFromIsClass(inclass,"TypedObject"); };
   void write_python_instance(ostream &out, int indent_level, const std::string &return_expr, bool owns_memory, const InterrogateType &itype, bool is_const);
   void write_python_instance(ostream &out, int indent_level, const std::string &return_expr, bool owns_memory, const InterrogateType &itype, bool is_const);
-  bool HasAGetClassTypeFunction(CPPType *type);
+  bool has_get_class_type_function(CPPType *type);
+  bool has_init_type_function(CPPType *type);
   int NeedsAStrFunction(const InterrogateType &itype_class);
   int NeedsAStrFunction(const InterrogateType &itype_class);
   int NeedsAReprFunction(const InterrogateType &itype_class);
   int NeedsAReprFunction(const InterrogateType &itype_class);
   bool NeedsARichCompareFunction(const InterrogateType &itype_class);
   bool NeedsARichCompareFunction(const InterrogateType &itype_class);

+ 0 - 4
dtool/src/interrogate/interrogate.cxx

@@ -19,7 +19,6 @@
 #include "pnotify.h"
 #include "pnotify.h"
 #include "panda_getopt_long.h"
 #include "panda_getopt_long.h"
 #include "preprocess_argv.h"
 #include "preprocess_argv.h"
-#include "pystub.h"
 #include <time.h>
 #include <time.h>
 
 
 CPPParser parser;
 CPPParser parser;
@@ -306,9 +305,6 @@ predefine_macro(CPPParser& parser, const string& inoption) {
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   preprocess_argv(argc, argv);
   preprocess_argv(argc, argv);
   string command_line;
   string command_line;
   int i;
   int i;

+ 0 - 4
dtool/src/interrogate/interrogate_module.cxx

@@ -19,7 +19,6 @@
 #include "interrogate_interface.h"
 #include "interrogate_interface.h"
 #include "interrogate_request.h"
 #include "interrogate_request.h"
 #include "load_dso.h"
 #include "load_dso.h"
-#include "pystub.h"
 #include "pnotify.h"
 #include "pnotify.h"
 #include "panda_getopt_long.h"
 #include "panda_getopt_long.h"
 #include "preprocess_argv.h"
 #include "preprocess_argv.h"
@@ -380,9 +379,6 @@ int main(int argc, char *argv[]) {
   extern int optind;
   extern int optind;
   int flag;
   int flag;
 
 
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   preprocess_argv(argc, argv);
   preprocess_argv(argc, argv);
   flag = getopt_long_only(argc, argv, short_options, long_options, NULL);
   flag = getopt_long_only(argc, argv, short_options, long_options, NULL);
   while (flag != EOF) {
   while (flag != EOF) {

+ 1 - 1
dtool/src/parser-inc/Python.h

@@ -30,7 +30,7 @@ typedef struct {} PyUnicodeObject;
 
 
 class PyThreadState;
 class PyThreadState;
 typedef int Py_ssize_t;
 typedef int Py_ssize_t;
-struct Py_buffer;
+typedef struct bufferinfo Py_buffer;
 
 
 // We need to define these accurately since interrogate may want to
 // We need to define these accurately since interrogate may want to
 // write these out to default value assignments.
 // write these out to default value assignments.

+ 0 - 1
dtool/src/test_interrogate/test_interrogate.cxx

@@ -17,7 +17,6 @@
 #include "interrogate_request.h"
 #include "interrogate_request.h"
 #include "load_dso.h"
 #include "load_dso.h"
 #include "filename.h"
 #include "filename.h"
-#include "pystub.h"
 #include "panda_getopt.h"
 #include "panda_getopt.h"
 #include "preprocess_argv.h"
 #include "preprocess_argv.h"
 
 

File diff suppressed because it is too large
+ 136 - 126
makepanda/makepanda.py


+ 0 - 4
panda/src/android/pview.cxx

@@ -13,7 +13,6 @@
 
 
 #include "pandaFramework.h"
 #include "pandaFramework.h"
 #include "pandaSystem.h"
 #include "pandaSystem.h"
-#include "pystub.h"
 #include "texturePool.h"
 #include "texturePool.h"
 #include "multitexReducer.h"
 #include "multitexReducer.h"
 #include "sceneGraphReducer.h"
 #include "sceneGraphReducer.h"
@@ -29,9 +28,6 @@
 #include "checkPandaVersion.h"
 #include "checkPandaVersion.h"
 
 
 int main(int argc, char **argv) {
 int main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   PandaFramework framework;
   PandaFramework framework;
   framework.open_framework(argc, argv);
   framework.open_framework(argc, argv);
   framework.set_window_title("Panda Viewer");
   framework.set_window_title("Panda Viewer");

+ 17 - 0
panda/src/chan/animChannelFixed.cxx

@@ -0,0 +1,17 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file animChannelFixed.cxx
+ * @author rdb
+ * @date 2016-05-29
+ */
+
+#include "animChannelFixed.h"
+
+template class AnimChannelFixed<ACMatrixSwitchType>;
+template class AnimChannelFixed<ACScalarSwitchType>;

+ 2 - 0
panda/src/chan/animChannelFixed.h

@@ -62,6 +62,8 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 };
 };
 
 
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDA_CHAN, EXPTP_PANDA_CHAN, AnimChannelFixed<ACMatrixSwitchType>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDA_CHAN, EXPTP_PANDA_CHAN, AnimChannelFixed<ACScalarSwitchType>);
 
 
 #include "animChannelFixed.I"
 #include "animChannelFixed.I"
 
 

+ 1 - 0
panda/src/chan/p3chan_composite1.cxx

@@ -3,6 +3,7 @@
 #include "animBundleNode.cxx"
 #include "animBundleNode.cxx"
 #include "animChannel.cxx"
 #include "animChannel.cxx"
 #include "animChannelBase.cxx"
 #include "animChannelBase.cxx"
+#include "animChannelFixed.cxx"
 #include "animChannelMatrixDynamic.cxx"
 #include "animChannelMatrixDynamic.cxx"
 #include "animChannelMatrixFixed.cxx"
 #include "animChannelMatrixFixed.cxx"
 #include "animChannelMatrixXfmTable.cxx"
 #include "animChannelMatrixXfmTable.cxx"

+ 0 - 4
panda/src/downloadertools/apply_patch.cxx

@@ -10,7 +10,6 @@
  */
  */
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "pystub.h"
 #include "panda_getopt.h"
 #include "panda_getopt.h"
 #include "preprocess_argv.h"
 #include "preprocess_argv.h"
 #include "patchfile.h"
 #include "patchfile.h"
@@ -18,9 +17,6 @@
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   preprocess_argv(argc, argv);
   preprocess_argv(argc, argv);
 
 
   if (argc < 3) {
   if (argc < 3) {

+ 0 - 4
panda/src/downloadertools/build_patch.cxx

@@ -10,7 +10,6 @@
  */
  */
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "pystub.h"
 #include "panda_getopt.h"
 #include "panda_getopt.h"
 #include "preprocess_argv.h"
 #include "preprocess_argv.h"
 #include "patchfile.h"
 #include "patchfile.h"
@@ -51,9 +50,6 @@ help() {
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   Filename patch_file;
   Filename patch_file;
   bool complete_file = false;
   bool complete_file = false;
   int footprint_length = 0;
   int footprint_length = 0;

+ 0 - 4
panda/src/downloadertools/check_adler.cxx

@@ -10,13 +10,9 @@
  */
  */
 
 
 #include "download_utils.h"
 #include "download_utils.h"
-#include "pystub.h"
 
 
 int
 int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   if (argc < 2) {
   if (argc < 2) {
     cerr << "Usage: check_adler <file>" << endl;
     cerr << "Usage: check_adler <file>" << endl;
     return 1;
     return 1;

+ 0 - 4
panda/src/downloadertools/check_crc.cxx

@@ -10,13 +10,9 @@
  */
  */
 
 
 #include "download_utils.h"
 #include "download_utils.h"
-#include "pystub.h"
 
 
 int
 int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   if (argc < 2) {
   if (argc < 2) {
     cerr << "Usage: check_crc <file>" << endl;
     cerr << "Usage: check_crc <file>" << endl;
     return 1;
     return 1;

+ 0 - 4
panda/src/downloadertools/check_md5.cxx

@@ -10,7 +10,6 @@
  */
  */
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "pystub.h"
 #include "hashVal.h"
 #include "hashVal.h"
 #include "filename.h"
 #include "filename.h"
 #include "panda_getopt.h"
 #include "panda_getopt.h"
@@ -65,9 +64,6 @@ output_hash(const string &filename, const HashVal &hash) {
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   extern char *optarg;
   extern char *optarg;
   extern int optind;
   extern int optind;
   const char *optstr = "i:db:qh";
   const char *optstr = "i:db:qh";

+ 0 - 4
panda/src/downloadertools/multify.cxx

@@ -10,7 +10,6 @@
  */
  */
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "pystub.h"
 #include "panda_getopt.h"
 #include "panda_getopt.h"
 #include "preprocess_argv.h"
 #include "preprocess_argv.h"
 #include "multifile.h"
 #include "multifile.h"
@@ -758,9 +757,6 @@ tokenize_extensions(const string &str, pset<string> &extensions) {
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   preprocess_argv(argc, argv);
   preprocess_argv(argc, argv);
   if (argc < 2) {
   if (argc < 2) {
     usage();
     usage();

+ 0 - 4
panda/src/downloadertools/pdecrypt.cxx

@@ -11,7 +11,6 @@
  * @date 2004-09-01
  * @date 2004-09-01
  */
  */
 
 
-#include "pystub.h"
 #include "filename.h"
 #include "filename.h"
 #include "encrypt_string.h"
 #include "encrypt_string.h"
 #include "pnotify.h"
 #include "pnotify.h"
@@ -46,9 +45,6 @@ usage() {
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   extern char *optarg;
   extern char *optarg;
   extern int optind;
   extern int optind;
   const char *optstr = "o:p:h";
   const char *optstr = "o:p:h";

+ 0 - 1
panda/src/downloadertools/pencrypt.cxx

@@ -11,7 +11,6 @@
  * @date 2004-09-01
  * @date 2004-09-01
  */
  */
 
 
-#include "pystub.h"
 #include "filename.h"
 #include "filename.h"
 #include "encrypt_string.h"
 #include "encrypt_string.h"
 #include "pnotify.h"
 #include "pnotify.h"

+ 0 - 4
panda/src/downloadertools/punzip.cxx

@@ -9,7 +9,6 @@
  * @file punzip.cxx
  * @file punzip.cxx
  */
  */
 
 
-#include "pystub.h"
 #include "filename.h"
 #include "filename.h"
 #include "compress_string.h"
 #include "compress_string.h"
 #include "pnotify.h"
 #include "pnotify.h"
@@ -31,9 +30,6 @@ usage() {
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   extern char *optarg;
   extern char *optarg;
   extern int optind;
   extern int optind;
   const char *optstr = "o:ch";
   const char *optstr = "o:ch";

+ 0 - 4
panda/src/downloadertools/pzip.cxx

@@ -9,7 +9,6 @@
  * @file pzip.cxx
  * @file pzip.cxx
  */
  */
 
 
-#include "pystub.h"
 #include "filename.h"
 #include "filename.h"
 #include "compress_string.h"
 #include "compress_string.h"
 #include "pnotify.h"
 #include "pnotify.h"
@@ -55,9 +54,6 @@ usage() {
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   extern char *optarg;
   extern char *optarg;
   extern int optind;
   extern int optind;
   const char *optstr = "o:c123456789h";
   const char *optstr = "o:c123456789h";

+ 0 - 4
panda/src/downloadertools/show_ddb.cxx

@@ -12,15 +12,11 @@
  */
  */
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "pystub.h"
 #include "downloadDb.h"
 #include "downloadDb.h"
 #include "filename.h"
 #include "filename.h"
 
 
 int
 int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   if (argc != 3) {
   if (argc != 3) {
     cerr << "Usage: show_ddb server.ddb client.ddb\n";
     cerr << "Usage: show_ddb server.ddb client.ddb\n";
     return 1;
     return 1;

+ 0 - 39
panda/src/event/asyncTask.cxx

@@ -18,10 +18,6 @@
 #include "throw_event.h"
 #include "throw_event.h"
 #include "eventParameter.h"
 #include "eventParameter.h"
 
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
 AtomicAdjust::Integer AsyncTask::_next_task_id;
 AtomicAdjust::Integer AsyncTask::_next_task_id;
 PStatCollector AsyncTask::_show_code_pcollector("App:Show code");
 PStatCollector AsyncTask::_show_code_pcollector("App:Show code");
 TypeHandle AsyncTask::_type_handle;
 TypeHandle AsyncTask::_type_handle;
@@ -48,9 +44,6 @@ AsyncTask(const string &name) :
   _total_dt(0.0),
   _total_dt(0.0),
   _num_frames(0)
   _num_frames(0)
 {
 {
-#ifdef HAVE_PYTHON
-  _python_object = NULL;
-#endif  // HAVE_PYTHON
   set_name(name);
   set_name(name);
 
 
   // Carefully copy _next_task_id and increment it so that we get a unique ID.
   // Carefully copy _next_task_id and increment it so that we get a unique ID.
@@ -68,9 +61,6 @@ AsyncTask(const string &name) :
 AsyncTask::
 AsyncTask::
 ~AsyncTask() {
 ~AsyncTask() {
   nassertv(_state == S_inactive && _manager == NULL && _chain == NULL);
   nassertv(_state == S_inactive && _manager == NULL && _chain == NULL);
-#ifdef HAVE_PYTHON
-  set_python_object(NULL);
-#endif
 }
 }
 
 
 /**
 /**
@@ -355,35 +345,6 @@ set_priority(int priority) {
   }
   }
 }
 }
 
 
-#ifdef HAVE_PYTHON
-/**
- * Specifies an arbitrary Python object that will be piggybacked on the task
- * object.
- */
-void AsyncTask::
-set_python_object(PyObject *python_object) {
-  Py_XINCREF(python_object);
-  Py_XDECREF(_python_object);
-  _python_object = python_object;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns the Python object that was specified to set_python_object(), if
- * any, or None if no object was specified.
- */
-PyObject *AsyncTask::
-get_python_object() const {
-  if (_python_object != (PyObject *)NULL) {
-    Py_XINCREF(_python_object);
-    return _python_object;
-  }
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-#endif  // HAVE_PYTHON
-
 /**
 /**
  *
  *
  */
  */

+ 0 - 10
panda/src/event/asyncTask.h

@@ -92,11 +92,6 @@ PUBLISHED:
   INLINE void set_done_event(const string &done_event);
   INLINE void set_done_event(const string &done_event);
   INLINE const string &get_done_event() const;
   INLINE const string &get_done_event() const;
 
 
-#ifdef HAVE_PYTHON
-  void set_python_object(PyObject *python_object);
-  PyObject *get_python_object() const;
-#endif  // HAVE_PYTHON
-
   INLINE double get_dt() const;
   INLINE double get_dt() const;
   INLINE double get_max_dt() const;
   INLINE double get_max_dt() const;
   INLINE double get_average_dt() const;
   INLINE double get_average_dt() const;
@@ -140,11 +135,6 @@ protected:
   static PStatCollector _show_code_pcollector;
   static PStatCollector _show_code_pcollector;
   PStatCollector _task_pcollector;
   PStatCollector _task_pcollector;
 
 
-private:
-#ifdef HAVE_PYTHON
-  PyObject *_python_object;
-#endif  // HAVE_PYTHON
-
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;

+ 0 - 34
panda/src/event/pythonTask.I

@@ -10,37 +10,3 @@
  * @author drose
  * @author drose
  * @date 2008-09-16
  * @date 2008-09-16
  */
  */
-
-/**
- * If None is passed, calls clear_delay, otherwise sets the delay time.  See
- * AsyncTask::set_delay() and AsyncTask::clear_delay().
- */
-INLINE void PythonTask::
-set_delay(PyObject *delay) {
-  if (delay == Py_None) {
-    AsyncTask::clear_delay();
-    return;
-  }
-
-  PyObject *value = PyNumber_Float(delay);
-  if (value == NULL) {
-    return;
-  }
-
-  AsyncTask::set_delay(PyFloat_AS_DOUBLE(value));
-  Py_DECREF(value);
-}
-
-/**
- * Returns the delay time if set, None otherwise.  See AsyncTask::has_delay()
- * and AsyncTask::get_delay().
- */
-INLINE PyObject *PythonTask::
-get_delay() const {
-  if (AsyncTask::has_delay()) {
-    return PyFloat_FromDouble(AsyncTask::get_delay());
-  } else {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-}

+ 3 - 7
panda/src/event/pythonTask.cxx

@@ -18,12 +18,9 @@
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
 #include "py_panda.h"
 #include "py_panda.h"
 
 
-TypeHandle PythonTask::_type_handle;
+#include "pythonThread.h"
 
 
-Configure(config_pythonTask);
-ConfigureFn(config_pythonTask) {
-  PythonTask::init_type();
-}
+TypeHandle PythonTask::_type_handle;
 
 
 #ifndef CPPPARSER
 #ifndef CPPPARSER
 extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount;
 extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount;
@@ -404,8 +401,7 @@ do_python_task() {
   if (_generator == (PyObject *)NULL) {
   if (_generator == (PyObject *)NULL) {
     // We are calling the function directly.
     // We are calling the function directly.
     PyObject *args = get_args();
     PyObject *args = get_args();
-    result =
-      Thread::get_current_thread()->call_python_func(_function, args);
+    result = PythonThread::call_python_func(_function, args);
     Py_DECREF(args);
     Py_DECREF(args);
 
 
 #ifdef PyGen_Check
 #ifdef PyGen_Check

+ 2 - 5
panda/src/event/pythonTask.h

@@ -50,9 +50,6 @@ PUBLISHED:
   int __traverse__(visitproc visit, void *arg);
   int __traverse__(visitproc visit, void *arg);
   int __clear__();
   int __clear__();
 
 
-  INLINE void set_delay(PyObject *delay);
-  INLINE PyObject *get_delay() const;
-
 PUBLISHED:
 PUBLISHED:
   // The name of this task.
   // The name of this task.
   MAKE_PROPERTY(name, get_name, set_name);
   MAKE_PROPERTY(name, get_name, set_name);
@@ -72,10 +69,10 @@ PUBLISHED:
   MAKE_PROPERTY(wakeTime, get_wake_time);
   MAKE_PROPERTY(wakeTime, get_wake_time);
 
 
   // The delay value that has been set on this task, if any, or None.
   // The delay value that has been set on this task, if any, or None.
-  MAKE_PROPERTY(delay_time, get_delay, set_delay);
+  MAKE_PROPERTY2(delay_time, has_delay, get_delay, set_delay, clear_delay);
 
 
   // Alias of delay_time.
   // Alias of delay_time.
-  MAKE_PROPERTY(delayTime, get_delay, set_delay);
+  MAKE_PROPERTY2(delayTime, has_delay, get_delay, set_delay, clear_delay);
 
 
   // The number of frames that have elapsed since the task was started,
   // The number of frames that have elapsed since the task was started,
   // according to the task manager's clock.
   // according to the task manager's clock.

+ 0 - 4
panda/src/express/pointerToArray.h

@@ -102,11 +102,9 @@ PUBLISHED:
   INLINE int get_node_ref_count() const;
   INLINE int get_node_ref_count() const;
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
-#if PY_VERSION_HEX >= 0x02060000
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags));
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags));
   EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
   EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
 #endif
 #endif
-#endif
 
 
 #else  // CPPPARSER
 #else  // CPPPARSER
   // This is the actual, complete interface.
   // This is the actual, complete interface.
@@ -257,11 +255,9 @@ PUBLISHED:
   INLINE int get_node_ref_count() const;
   INLINE int get_node_ref_count() const;
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
-#if PY_VERSION_HEX >= 0x02060000
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
   EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
   EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
 #endif
 #endif
-#endif
 
 
 #else  // CPPPARSER
 #else  // CPPPARSER
   // This is the actual, complete interface.
   // This is the actual, complete interface.

+ 12 - 6
panda/src/express/pointerToArray_ext.I

@@ -175,7 +175,6 @@ __getitem__(size_t n) const {
   return (*this->_this)[n];
   return (*this->_this)[n];
 }
 }
 
 
-#if PY_VERSION_HEX >= 0x02060000
 /**
 /**
  * This is used to implement the buffer protocol, in order to allow efficient
  * This is used to implement the buffer protocol, in order to allow efficient
  * access to the array data through a Python multiview object.
  * access to the array data through a Python multiview object.
@@ -183,7 +182,7 @@ __getitem__(size_t n) const {
 template<class Element>
 template<class Element>
 INLINE int Extension<PointerToArray<Element> >::
 INLINE int Extension<PointerToArray<Element> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
-
+#if PY_VERSION_HEX >= 0x02060000
   const char *format = get_format_code(Element);
   const char *format = get_format_code(Element);
   if (format == NULL) {
   if (format == NULL) {
     // Not supported.
     // Not supported.
@@ -222,6 +221,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
   view->internal = (void*) this->_this;
   view->internal = (void*) this->_this;
 
 
   return 0;
   return 0;
+#else
+  return -1;
+#endif
 }
 }
 
 
 /**
 /**
@@ -230,13 +232,14 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
 template<class Element>
 template<class Element>
 INLINE void Extension<PointerToArray<Element> >::
 INLINE void Extension<PointerToArray<Element> >::
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
+#if PY_VERSION_HEX >= 0x02060000
   // Note: PyBuffer_Release automatically decrements view->obj.
   // Note: PyBuffer_Release automatically decrements view->obj.
-
   if (view->internal != NULL) {
   if (view->internal != NULL) {
     // Oh, right, let's not forget to unref this.
     // Oh, right, let's not forget to unref this.
     unref_delete((const PointerToArray<Element> *)view->internal);
     unref_delete((const PointerToArray<Element> *)view->internal);
     view->internal = NULL;
     view->internal = NULL;
   }
   }
+#endif
 }
 }
 
 
 /**
 /**
@@ -246,7 +249,7 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
 template<class Element>
 template<class Element>
 INLINE int Extension<ConstPointerToArray<Element> >::
 INLINE int Extension<ConstPointerToArray<Element> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-
+#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
     PyErr_SetString(PyExc_BufferError,
     PyErr_SetString(PyExc_BufferError,
                     "Object is not writable.");
                     "Object is not writable.");
@@ -291,6 +294,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->internal = (void*) this->_this;
   view->internal = (void*) this->_this;
 
 
   return 0;
   return 0;
+#else
+  return -1;
+#endif
 }
 }
 
 
 /**
 /**
@@ -299,12 +305,12 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 template<class Element>
 template<class Element>
 INLINE void Extension<ConstPointerToArray<Element> >::
 INLINE void Extension<ConstPointerToArray<Element> >::
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
+#if PY_VERSION_HEX >= 0x02060000
   // Note: PyBuffer_Release automatically decrements obj->view.
   // Note: PyBuffer_Release automatically decrements obj->view.
-
   if (view->internal != NULL) {
   if (view->internal != NULL) {
     // Oh, right, let's not forget to unref this.
     // Oh, right, let's not forget to unref this.
     unref_delete((const PointerToArray<Element> *)view->internal);
     unref_delete((const PointerToArray<Element> *)view->internal);
     view->internal = NULL;
     view->internal = NULL;
   }
   }
+#endif
 }
 }
-#endif  // PY_VERSION_HEX

+ 0 - 4
panda/src/express/pointerToArray_ext.h

@@ -35,10 +35,8 @@ public:
   INLINE const Element &__getitem__(size_t n) const;
   INLINE const Element &__getitem__(size_t n) const;
   INLINE void __setitem__(size_t n, const Element &value);
   INLINE void __setitem__(size_t n, const Element &value);
 
 
-#if PY_VERSION_HEX >= 0x02060000
   INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags);
   INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags);
   INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
   INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
-#endif
 };
 };
 
 
 /**
 /**
@@ -55,10 +53,8 @@ public:
 
 
   INLINE const Element &__getitem__(size_t n) const;
   INLINE const Element &__getitem__(size_t n) const;
 
 
-#if PY_VERSION_HEX >= 0x02060000
   INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
   INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
   INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
   INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
-#endif
 };
 };
 
 
 #ifdef _MSC_VER
 #ifdef _MSC_VER

+ 1 - 1
panda/src/gobj/geomVertexArrayData.h

@@ -113,7 +113,7 @@ PUBLISHED:
   static void lru_epoch();
   static void lru_epoch();
   INLINE static VertexDataBook &get_book();
   INLINE static VertexDataBook &get_book();
 
 
-#if PY_VERSION_HEX >= 0x02060000
+#ifdef HAVE_PYTHON
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags));
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags));
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
   EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
   EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);

+ 10 - 6
panda/src/gobj/geomVertexArrayData_ext.cxx

@@ -22,14 +22,13 @@ struct InternalBufferData {
   string _format;
   string _format;
 };
 };
 
 
-#if PY_VERSION_HEX >= 0x02060000
 /**
 /**
  * This is used to implement the buffer protocol, in order to allow efficient
  * This is used to implement the buffer protocol, in order to allow efficient
  * access to the array data through a Python multiview object.
  * access to the array data through a Python multiview object.
  */
  */
 int Extension<GeomVertexArrayData>::
 int Extension<GeomVertexArrayData>::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
-
+#if PY_VERSION_HEX >= 0x02060000
   PT(GeomVertexArrayDataHandle) handle = _this->modify_handle();
   PT(GeomVertexArrayDataHandle) handle = _this->modify_handle();
   CPT(GeomVertexArrayFormat) format = handle->get_array_format();
   CPT(GeomVertexArrayFormat) format = handle->get_array_format();
 
 
@@ -79,6 +78,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
   view->suboffsets = NULL;
   view->suboffsets = NULL;
 
 
   return 0;
   return 0;
+#else
+  return -1;
+#endif
 }
 }
 
 
 /**
 /**
@@ -86,7 +88,7 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
  */
  */
 int Extension<GeomVertexArrayData>::
 int Extension<GeomVertexArrayData>::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-
+#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
       PyErr_SetString(PyExc_BufferError,
       PyErr_SetString(PyExc_BufferError,
                       "Object is not writable.");
                       "Object is not writable.");
@@ -142,6 +144,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->suboffsets = NULL;
   view->suboffsets = NULL;
 
 
   return 0;
   return 0;
+#else
+  return -1;
+#endif
 }
 }
 
 
 /**
 /**
@@ -149,8 +154,8 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
  */
  */
 void Extension<GeomVertexArrayData>::
 void Extension<GeomVertexArrayData>::
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
+#if PY_VERSION_HEX >= 0x02060000
   // Note: PyBuffer_Release automatically decrements view->obj.
   // Note: PyBuffer_Release automatically decrements view->obj.
-
   InternalBufferData *data;
   InternalBufferData *data;
   data = (InternalBufferData *) view->internal;
   data = (InternalBufferData *) view->internal;
   if (data == NULL) {
   if (data == NULL) {
@@ -158,10 +163,9 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
   }
   }
   delete data;
   delete data;
   view->internal = NULL;
   view->internal = NULL;
+#endif
 }
 }
 
 
-#endif  // PY_VERSION_HEX >= 0x02060000
-
 /**
 /**
  * Copies all data from the given buffer object.  The array is rescaled as
  * Copies all data from the given buffer object.  The array is rescaled as
  * necessary.
  * necessary.

+ 0 - 2
panda/src/gobj/geomVertexArrayData_ext.h

@@ -29,11 +29,9 @@
 template<>
 template<>
 class Extension<GeomVertexArrayData> : public ExtensionBase<GeomVertexArrayData> {
 class Extension<GeomVertexArrayData> : public ExtensionBase<GeomVertexArrayData> {
 public:
 public:
-#if PY_VERSION_HEX >= 0x02060000
   int __getbuffer__(PyObject *self, Py_buffer *view, int flags);
   int __getbuffer__(PyObject *self, Py_buffer *view, int flags);
   int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
   int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
   void __releasebuffer__(PyObject *self, Py_buffer *view) const;
   void __releasebuffer__(PyObject *self, Py_buffer *view) const;
-#endif
 };
 };
 
 
 /**
 /**

+ 0 - 20
panda/src/nativenet/buffered_datagramconnection.cxx

@@ -13,10 +13,6 @@
 
 
 #include "buffered_datagramconnection.h"
 #include "buffered_datagramconnection.h"
 
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
 TypeHandle Buffered_DatagramConnection::_type_handle;
 TypeHandle Buffered_DatagramConnection::_type_handle;
 
 
 /**
 /**
@@ -34,22 +30,6 @@ SendMessage(const Datagram &msg) {
 
 
     // Raise an exception to give us more information at the python level
     // Raise an exception to give us more information at the python level
     nativenet_cat.warning() << "Buffered_DatagramConnection::SendMessage->Error On Write--Out Buffer = " << _Writer.AmountBuffered() << "\n";
     nativenet_cat.warning() << "Buffered_DatagramConnection::SendMessage->Error On Write--Out Buffer = " << _Writer.AmountBuffered() << "\n";
-#ifdef HAVE_PYTHON
-    ostringstream s;
-
-#if PY_VERSION_HEX >= 0x03030000
-    PyObject *exc_type = PyExc_ConnectionError;
-#else
-    PyObject *exc_type = PyExc_OSError;
-#endif
-
-    s << endl << "Error sending message: " << endl;
-    msg.dump_hex(s);
-    s << "Message data: " << msg.get_data() << endl;
-
-    string message = s.str();
-    PyErr_SetString(exc_type, message.c_str());
-#endif
 
 
     ClearAll();
     ClearAll();
   }
   }

+ 4 - 0
panda/src/pgraph/modelFlattenRequest.h

@@ -38,6 +38,10 @@ PUBLISHED:
   INLINE bool is_ready() const;
   INLINE bool is_ready() const;
   INLINE PandaNode *get_model() const;
   INLINE PandaNode *get_model() const;
 
 
+  MAKE_PROPERTY(orig, get_orig);
+  MAKE_PROPERTY(ready, is_ready);
+  MAKE_PROPERTY(model, get_model);
+
 protected:
 protected:
   virtual DoneStatus do_task();
   virtual DoneStatus do_task();
 
 

+ 6 - 0
panda/src/pgraph/modelLoadRequest.h

@@ -45,6 +45,12 @@ PUBLISHED:
   INLINE bool is_ready() const;
   INLINE bool is_ready() const;
   INLINE PandaNode *get_model() const;
   INLINE PandaNode *get_model() const;
 
 
+  MAKE_PROPERTY(filename, get_filename);
+  MAKE_PROPERTY(options, get_options);
+  MAKE_PROPERTY(loader, get_loader);
+  MAKE_PROPERTY(ready, is_ready);
+  MAKE_PROPERTY(model, get_model);
+
 protected:
 protected:
   virtual DoneStatus do_task();
   virtual DoneStatus do_task();
 
 

+ 7 - 0
panda/src/pgraph/modelSaveRequest.h

@@ -46,6 +46,13 @@ PUBLISHED:
   INLINE bool is_ready() const;
   INLINE bool is_ready() const;
   INLINE bool get_success() const;
   INLINE bool get_success() const;
 
 
+  MAKE_PROPERTY(filename, get_filename);
+  MAKE_PROPERTY(options, get_options);
+  MAKE_PROPERTY(node, get_node);
+  MAKE_PROPERTY(loader, get_loader);
+  MAKE_PROPERTY(ready, is_ready);
+  MAKE_PROPERTY(success, get_success);
+
 protected:
 protected:
   virtual DoneStatus do_task();
   virtual DoneStatus do_task();
 
 

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

@@ -905,15 +905,19 @@ PUBLISHED:
   NodePath find_net_tag(const string &key) const;
   NodePath find_net_tag(const string &key) const;
 
 
   EXTENSION(INLINE PyObject *get_tag_keys() const);
   EXTENSION(INLINE PyObject *get_tag_keys() const);
-  EXTENSION(INLINE void set_python_tag(const string &key, PyObject *value));
-  EXTENSION(INLINE PyObject *get_python_tag(const string &key) const);
-  EXTENSION(INLINE void get_python_tag_keys(vector_string &keys) const);
+
+  EXTENSION(PyObject *get_python_tags());
+  EXTENSION(INLINE void set_python_tag(PyObject *keys, PyObject *value));
+  EXTENSION(INLINE PyObject *get_python_tag(PyObject *keys) const);
   EXTENSION(INLINE PyObject *get_python_tag_keys() const);
   EXTENSION(INLINE PyObject *get_python_tag_keys() const);
-  EXTENSION(INLINE bool has_python_tag(const string &key) const);
-  EXTENSION(INLINE void clear_python_tag(const string &key));
-  EXTENSION(INLINE PyObject *get_net_python_tag(const string &key) const);
-  EXTENSION(INLINE bool has_net_python_tag(const string &key) const);
-  EXTENSION(NodePath find_net_python_tag(const string &key) const);
+  EXTENSION(INLINE bool has_python_tag(PyObject *keys) const);
+  EXTENSION(INLINE void clear_python_tag(PyObject *keys));
+  EXTENSION(INLINE PyObject *get_net_python_tag(PyObject *keys) const);
+  EXTENSION(INLINE bool has_net_python_tag(PyObject *keys) const);
+  EXTENSION(NodePath find_net_python_tag(PyObject *keys) const);
+  MAKE_PROPERTY(python_tags, get_python_tags);
+
+  EXTENSION(int __traverse__(visitproc visit, void *arg));
 
 
   INLINE void list_tags() const;
   INLINE void list_tags() const;
 
 

+ 29 - 14
panda/src/pgraph/nodePath_ext.I

@@ -14,15 +14,19 @@
 #include "pandaNode_ext.h"
 #include "pandaNode_ext.h"
 
 
 /**
 /**
- * Fills the given vector up with the list of Python tags on this PandaNode.
- *
- * It is the user's responsibility to ensure that the keys vector is empty
- * before making this call; otherwise, the new files will be appended to it.
+ * Returns a dictionary of Python tags that is associated with the current
+ * node.  This is similar to the regular tags, except this can store any
+ * Python object instead of just a string.  However, the Python object is not
+ * recorded to a bam file.
  */
  */
-INLINE void Extension<NodePath>::
-get_python_tag_keys(vector_string &keys) const {
-  nassertv_always(!_this->is_empty());
-  invoke_extension(_this->node()).get_python_tag_keys(keys);
+INLINE PyObject *Extension<NodePath>::
+get_python_tags() {
+  // An empty NodePath returns None
+  if (_this->is_empty()) {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+  return invoke_extension(_this->node()).get_python_tags();
 }
 }
 
 
 /**
 /**
@@ -63,7 +67,7 @@ get_python_tag_keys() const {
  * of any one key's object.
  * of any one key's object.
  */
  */
 INLINE void Extension<NodePath>::
 INLINE void Extension<NodePath>::
-set_python_tag(const string &key, PyObject *value) {
+set_python_tag(PyObject *key, PyObject *value) {
   nassertv_always(!_this->is_empty());
   nassertv_always(!_this->is_empty());
   invoke_extension(_this->node()).set_python_tag(key, value);
   invoke_extension(_this->node()).set_python_tag(key, value);
 }
 }
@@ -74,7 +78,7 @@ set_python_tag(const string &key, PyObject *value) {
  * None.  See also get_net_python_tag().
  * None.  See also get_net_python_tag().
  */
  */
 INLINE PyObject *Extension<NodePath>::
 INLINE PyObject *Extension<NodePath>::
-get_python_tag(const string &key) const {
+get_python_tag(PyObject *key) const {
   // An empty NodePath quietly returns no tags.  This makes
   // An empty NodePath quietly returns no tags.  This makes
   // get_net_python_tag() easier to implement.
   // get_net_python_tag() easier to implement.
   if (_this->is_empty()) {
   if (_this->is_empty()) {
@@ -90,7 +94,7 @@ get_python_tag(const string &key) const {
  * value has been set.  See also has_net_python_tag().
  * value has been set.  See also has_net_python_tag().
  */
  */
 INLINE bool Extension<NodePath>::
 INLINE bool Extension<NodePath>::
-has_python_tag(const string &key) const {
+has_python_tag(PyObject *key) const {
   // An empty NodePath quietly has no tags.  This makes has_net_python_tag()
   // An empty NodePath quietly has no tags.  This makes has_net_python_tag()
   // easier to implement.
   // easier to implement.
   if (_this->is_empty()) {
   if (_this->is_empty()) {
@@ -105,7 +109,7 @@ has_python_tag(const string &key) const {
  * the indicated key.
  * the indicated key.
  */
  */
 INLINE void Extension<NodePath>::
 INLINE void Extension<NodePath>::
-clear_python_tag(const string &key) {
+clear_python_tag(PyObject *key) {
   nassertv_always(!_this->is_empty());
   nassertv_always(!_this->is_empty());
   invoke_extension(_this->node()).clear_python_tag(key);
   invoke_extension(_this->node()).clear_python_tag(key);
 }
 }
@@ -117,7 +121,7 @@ clear_python_tag(const string &key) {
  * get_python_tag().
  * get_python_tag().
  */
  */
 INLINE PyObject *Extension<NodePath>::
 INLINE PyObject *Extension<NodePath>::
-get_net_python_tag(const string &key) const {
+get_net_python_tag(PyObject *key) const {
   NodePath tag_np = find_net_python_tag(key);
   NodePath tag_np = find_net_python_tag(key);
   return invoke_extension(&tag_np).get_python_tag(key);
   return invoke_extension(&tag_np).get_python_tag(key);
 }
 }
@@ -127,6 +131,17 @@ get_net_python_tag(const string &key) const {
  * or on any ancestor node, or false otherwise.  See also has_python_tag().
  * or on any ancestor node, or false otherwise.  See also has_python_tag().
  */
  */
 INLINE bool Extension<NodePath>::
 INLINE bool Extension<NodePath>::
-has_net_python_tag(const string &key) const {
+has_net_python_tag(PyObject *key) const {
   return !find_net_python_tag(key).is_empty();
   return !find_net_python_tag(key).is_empty();
 }
 }
+
+/**
+ * Called by Python to implement cycle detection.
+ */
+INLINE int Extension<NodePath>::
+__traverse__(visitproc visit, void *arg) {
+  if (_this->is_empty()) {
+    return 0;
+  }
+  return invoke_extension(_this->node()).__traverse__(visit, arg);
+}

+ 1 - 1
panda/src/pgraph/nodePath_ext.cxx

@@ -172,7 +172,7 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const {
  * node contains this tag definition.  See set_python_tag().
  * node contains this tag definition.  See set_python_tag().
  */
  */
 NodePath Extension<NodePath>::
 NodePath Extension<NodePath>::
-find_net_python_tag(const string &key) const {
+find_net_python_tag(PyObject *key) const {
   if (_this->is_empty()) {
   if (_this->is_empty()) {
     return NodePath::not_found();
     return NodePath::not_found();
   }
   }

+ 12 - 8
panda/src/pgraph/nodePath_ext.h

@@ -35,15 +35,19 @@ public:
   PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const;
   PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const;
 
 
   INLINE PyObject *get_tag_keys() const;
   INLINE PyObject *get_tag_keys() const;
-  INLINE void set_python_tag(const string &key, PyObject *value);
-  INLINE PyObject *get_python_tag(const string &key) const;
-  INLINE void get_python_tag_keys(vector_string &keys) const;
+
+  INLINE PyObject *get_python_tags();
+  INLINE void set_python_tag(PyObject *key, PyObject *value);
+  INLINE PyObject *get_python_tag(PyObject *key) const;
   INLINE PyObject *get_python_tag_keys() const;
   INLINE PyObject *get_python_tag_keys() const;
-  INLINE bool has_python_tag(const string &key) const;
-  INLINE void clear_python_tag(const string &key);
-  INLINE PyObject *get_net_python_tag(const string &key) const;
-  INLINE bool has_net_python_tag(const string &key) const;
-  NodePath find_net_python_tag(const string &key) const;
+  INLINE bool has_python_tag(PyObject *key) const;
+  INLINE void clear_python_tag(PyObject *key);
+  INLINE PyObject *get_net_python_tag(PyObject *key) const;
+  INLINE bool has_net_python_tag(PyObject *key) const;
+  NodePath find_net_python_tag(PyObject *key) const;
+
+  // This is defined to implement cycle detection in Python tags.
+  INLINE int __traverse__(visitproc visit, void *arg);
 
 
   PyObject *get_tight_bounds(const NodePath &other = NodePath()) const;
   PyObject *get_tight_bounds(const NodePath &other = NodePath()) const;
 };
 };

+ 4 - 1
panda/src/pgraph/pandaNode.I

@@ -380,7 +380,10 @@ has_tags() const {
     return true;
     return true;
   }
   }
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
-  if (!cdata->_python_tag_data.empty()) {
+  // This is a bit awkward, since it doesn't catch cases where the Python tags
+  // are cleared.  Maybe we should make this behavior deprecated, so that
+  // has_tags will not consider the Python tags.
+  if (!_python_tag_data.is_null()) {
     return true;
     return true;
   }
   }
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON

+ 18 - 180
panda/src/pgraph/pandaNode.cxx

@@ -27,7 +27,6 @@
 #include "config_mathutil.h"
 #include "config_mathutil.h"
 #include "lightReMutexHolder.h"
 #include "lightReMutexHolder.h"
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
-#include "py_panda.h"
 
 
 // This category is just temporary for debugging convenience.
 // This category is just temporary for debugging convenience.
 NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
 NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
@@ -131,7 +130,8 @@ PandaNode(const PandaNode &copy) :
   TypedWritableReferenceCount(copy),
   TypedWritableReferenceCount(copy),
   Namable(copy),
   Namable(copy),
   _paths_lock("PandaNode::_paths_lock"),
   _paths_lock("PandaNode::_paths_lock"),
-  _dirty_prev_transform(false)
+  _dirty_prev_transform(false),
+  _python_tag_data(copy._python_tag_data)
 {
 {
   if (pgraph_cat.is_debug()) {
   if (pgraph_cat.is_debug()) {
     pgraph_cat.debug()
     pgraph_cat.debug()
@@ -172,12 +172,6 @@ PandaNode(const PandaNode &copy) :
     ++cdata->_internal_bounds_mark;
     ++cdata->_internal_bounds_mark;
     cdata->_final_bounds = copy_cdata->_final_bounds;
     cdata->_final_bounds = copy_cdata->_final_bounds;
     cdata->_fancy_bits = copy_cdata->_fancy_bits;
     cdata->_fancy_bits = copy_cdata->_fancy_bits;
-
-#ifdef HAVE_PYTHON
-    // Copy and increment all of the Python objects held by the other node.
-    cdata->_python_tag_data = copy_cdata->_python_tag_data;
-    cdata->inc_py_refs();
-#endif  // HAVE_PYTHON
   }
   }
 }
 }
 
 
@@ -1270,32 +1264,13 @@ copy_tags(PandaNode *other) {
       cdataw->_tag_data[(*ti).first] = (*ti).second;
       cdataw->_tag_data[(*ti).first] = (*ti).second;
     }
     }
     cdataw->set_fancy_bit(FB_tag, !cdataw->_tag_data.empty());
     cdataw->set_fancy_bit(FB_tag, !cdataw->_tag_data.empty());
-
-#ifdef HAVE_PYTHON
-    PythonTagData::const_iterator pti;
-    for (pti = cdatar->_python_tag_data.begin();
-         pti != cdatar->_python_tag_data.end();
-         ++pti) {
-      const string &key = (*pti).first;
-      PyObject *value = (*pti).second;
-      Py_XINCREF(value);
-
-      pair<PythonTagData::iterator, bool> result;
-      result = cdataw->_python_tag_data.insert(PythonTagData::value_type(key, value));
-
-      if (!result.second) {
-        // The insert was unsuccessful; that means the key was already present
-        // in the map.  In this case, we should decrement the original value's
-        // reference count and replace it with the new object.
-        PythonTagData::iterator wpti = result.first;
-        PyObject *old_value = (*wpti).second;
-        Py_XDECREF(old_value);
-        (*wpti).second = value;
-      }
-    }
-#endif // HAVE_PYTHON
   }
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
+
+  // It's okay to copy the tags by pointer, because get_python_tags does a
+  // copy-on-write.
+  _python_tag_data = other->_python_tag_data;
+
   mark_bam_modified();
   mark_bam_modified();
 }
 }
 
 
@@ -1321,20 +1296,9 @@ list_tags(ostream &out, const string &separator) const {
     }
     }
   }
   }
 
 
-#ifdef HAVE_PYTHON
-  if (!cdata->_python_tag_data.empty()) {
-    if (!cdata->_tag_data.empty()) {
-      out << separator;
-    }
-    PythonTagData::const_iterator ti = cdata->_python_tag_data.begin();
-    out << (*ti).first;
-    ++ti;
-    while (ti != cdata->_python_tag_data.end()) {
-      out << separator << (*ti).first;
-      ++ti;
-    }
-  }
-#endif  // HAVE_PYTHON
+  // We used to list the Python tags here.  That's a bit awkward, though,
+  // since that means calling up into Python code to print the keys.  If
+  // someone finds it useful, we can implement it in an extension method.
 }
 }
 
 
 /**
 /**
@@ -1393,50 +1357,11 @@ compare_tags(const PandaNode *other) const {
     return -1;
     return -1;
   }
   }
 
 
-#ifdef HAVE_PYTHON
-  PythonTagData::const_iterator api = cdata->_python_tag_data.begin();
-  PythonTagData::const_iterator bpi = cdata_other->_python_tag_data.begin();
-  while (api != cdata->_python_tag_data.end() &&
-         bpi != cdata_other->_python_tag_data.end()) {
-    int cmp = strcmp((*api).first.c_str(), (*bpi).first.c_str());
-    if (cmp != 0) {
-      return cmp;
-    }
-
-#if PY_MAJOR_VERSION >= 3
-    if (PyObject_RichCompareBool((*api).second, (*bpi).second, Py_LT) == 1) {
-      return -1;
-    } else if (PyObject_RichCompareBool((*api).second, (*bpi).second, Py_GT) == 1) {
-      return 1;
-    } else if (PyObject_RichCompareBool((*api).second, (*bpi).second, Py_EQ) == 1) {
-      cmp = 0;
-    } else {
-#else
-    if (PyObject_Cmp((*api).second, (*bpi).second, &cmp) == -1) {
-#endif
-      // Unable to compare objects; just compare pointers.
-      if ((*api).second != (*bpi).second) {
-        cmp = (*api).second < (*bpi).second ? -1 : 1;
-      } else {
-        cmp = 0;
-      }
-    }
-    if (cmp != 0) {
-      return cmp;
-    }
-
-    ++api;
-    ++bpi;
-  }
-  if (api != cdata->_python_tag_data.end()) {
-    // list A is longer.
-    return 1;
+  // We compare these by pointer, since it's problematic to call up into
+  // Python from arbitrary C++ code.
+  if (_python_tag_data != other->_python_tag_data) {
+    return (_python_tag_data < other->_python_tag_data) ? -1 : 1;
   }
   }
-  if (bpi != cdata_other->_python_tag_data.end()) {
-    // list B is longer.
-    return -1;
-  }
-#endif  // HAVE_PYTHON
 
 
   return 0;
   return 0;
 }
 }
@@ -1494,30 +1419,6 @@ copy_all_properties(PandaNode *other) {
       cdataw->_tag_data[(*ti).first] = (*ti).second;
       cdataw->_tag_data[(*ti).first] = (*ti).second;
     }
     }
 
 
-#ifdef HAVE_PYTHON
-    PythonTagData::const_iterator pti;
-    for (pti = cdatar->_python_tag_data.begin();
-         pti != cdatar->_python_tag_data.end();
-         ++pti) {
-      const string &key = (*pti).first;
-      PyObject *value = (*pti).second;
-      Py_XINCREF(value);
-
-      pair<PythonTagData::iterator, bool> result;
-      result = cdataw->_python_tag_data.insert(PythonTagData::value_type(key, value));
-
-      if (!result.second) {
-        // The insert was unsuccessful; that means the key was already present
-        // in the map.  In this case, we should decrement the original value's
-        // reference count and replace it with the new object.
-        PythonTagData::iterator wpti = result.first;
-        PyObject *old_value = (*wpti).second;
-        Py_XDECREF(old_value);
-        (*wpti).second = value;
-      }
-    }
-#endif // HAVE_PYTHON
-
     static const int change_bits = (FB_transform | FB_state | FB_effects |
     static const int change_bits = (FB_transform | FB_state | FB_effects |
                                     FB_tag | FB_draw_mask);
                                     FB_tag | FB_draw_mask);
     cdataw->_fancy_bits =
     cdataw->_fancy_bits =
@@ -1532,6 +1433,10 @@ copy_all_properties(PandaNode *other) {
   }
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 
 
+  // It's okay to copy the tags by pointer, because get_python_tags does a
+  // copy-on-write.
+  _python_tag_data = other->_python_tag_data;
+
   if (any_transform_changed || any_state_changed || any_draw_mask_changed) {
   if (any_transform_changed || any_state_changed || any_draw_mask_changed) {
     mark_bounds_stale(current_thread);
     mark_bounds_stale(current_thread);
 
 
@@ -3810,7 +3715,6 @@ CData(const PandaNode::CData &copy) :
 
 
   _effects(copy._effects),
   _effects(copy._effects),
   _tag_data(copy._tag_data),
   _tag_data(copy._tag_data),
-  // _python_tag_data appears below.
   _draw_control_mask(copy._draw_control_mask),
   _draw_control_mask(copy._draw_control_mask),
   _draw_show_mask(copy._draw_show_mask),
   _draw_show_mask(copy._draw_show_mask),
   _into_collide_mask(copy._into_collide_mask),
   _into_collide_mask(copy._into_collide_mask),
@@ -3836,12 +3740,6 @@ CData(const PandaNode::CData &copy) :
   // Note that this copy constructor is not used by the PandaNode copy
   // Note that this copy constructor is not used by the PandaNode copy
   // constructor!  Any elements that must be copied between nodes should also
   // constructor!  Any elements that must be copied between nodes should also
   // be explicitly copied there.
   // be explicitly copied there.
-
-#ifdef HAVE_PYTHON
-  // Copy and increment all of the Python objects held by the other node.
-  _python_tag_data = copy._python_tag_data;
-  inc_py_refs();
-#endif  // HAVE_PYTHON
 }
 }
 
 
 /**
 /**
@@ -3849,10 +3747,6 @@ CData(const PandaNode::CData &copy) :
  */
  */
 PandaNode::CData::
 PandaNode::CData::
 ~CData() {
 ~CData() {
-#ifdef HAVE_PYTHON
-  // Free all of the Python objects held by this node.
-  dec_py_refs();
-#endif  // HAVE_PYTHON
 }
 }
 
 
 /**
 /**
@@ -4029,62 +3923,6 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   fillin_down_list(*modify_stashed(), "stashed", scan, manager);
   fillin_down_list(*modify_stashed(), "stashed", scan, manager);
 }
 }
 
 
-#ifdef HAVE_PYTHON
-/**
- * Increments the reference counts on all held Python objects.
- */
-void PandaNode::CData::
-inc_py_refs() {
-  if (!_python_tag_data.empty()) {
-#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
-    // This might happen at any time, so be sure the Python state is ready for
-    // it.
-    PyGILState_STATE gstate;
-    gstate = PyGILState_Ensure();
-#endif
-    PythonTagData::const_iterator ti;
-    for (ti = _python_tag_data.begin();
-         ti != _python_tag_data.end();
-         ++ti) {
-      PyObject *value = (*ti).second;
-      Py_XINCREF(value);
-    }
-#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
-    PyGILState_Release(gstate);
-#endif
-  }
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Decrements the reference counts on all held Python objects.
- */
-void PandaNode::CData::
-dec_py_refs() {
-  if (!_python_tag_data.empty()) {
-#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
-    // This might happen at any time, so be sure the Python state is ready for
-    // it.
-    PyGILState_STATE gstate;
-    gstate = PyGILState_Ensure();
-#endif
-
-    PythonTagData::const_iterator ti;
-    for (ti = _python_tag_data.begin();
-         ti != _python_tag_data.end();
-         ++ti) {
-      PyObject *value = (*ti).second;
-      Py_XDECREF(value);
-    }
-
-#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
-    PyGILState_Release(gstate);
-#endif
-  }
-}
-#endif  // HAVE_PYTHON
-
 /**
 /**
  * Writes the indicated list of parent node pointers to the datagram.
  * Writes the indicated list of parent node pointers to the datagram.
  */
  */

+ 18 - 11
panda/src/pgraph/pandaNode.h

@@ -200,12 +200,15 @@ PUBLISHED:
 
 
   EXTENSION(PyObject *get_tag_keys() const);
   EXTENSION(PyObject *get_tag_keys() const);
 
 
-  EXTENSION(void set_python_tag(const string &key, PyObject *value));
-  EXTENSION(PyObject *get_python_tag(const string &key) const);
-  EXTENSION(bool has_python_tag(const string &key) const);
-  EXTENSION(void clear_python_tag(const string &key));
-  EXTENSION(void get_python_tag_keys(vector_string &keys) const);
+  EXTENSION(PyObject *get_python_tags());
+  EXTENSION(void set_python_tag(PyObject *key, PyObject *value));
+  EXTENSION(PyObject *get_python_tag(PyObject *key) const);
+  EXTENSION(bool has_python_tag(PyObject *key) const);
+  EXTENSION(void clear_python_tag(PyObject *key));
   EXTENSION(PyObject *get_python_tag_keys() const);
   EXTENSION(PyObject *get_python_tag_keys() const);
+  MAKE_PROPERTY(python_tags, get_python_tags);
+
+  EXTENSION(int __traverse__(visitproc visit, void *arg));
 
 
   INLINE bool has_tags() const;
   INLINE bool has_tags() const;
   void copy_tags(PandaNode *other);
   void copy_tags(PandaNode *other);
@@ -507,9 +510,16 @@ private:
   // This is used to maintain a table of keyed data on each node, for the
   // This is used to maintain a table of keyed data on each node, for the
   // user's purposes.
   // user's purposes.
   typedef phash_map<string, string, string_hash> TagData;
   typedef phash_map<string, string, string_hash> TagData;
-#ifdef HAVE_PYTHON
-  typedef phash_map<string, PyObject *, string_hash> PythonTagData;
-#endif  // HAVE_PYTHON
+
+  // This is actually implemented in pandaNode_ext.h, but defined here so
+  // that we can destruct it from the C++ side.  Note that it isn't cycled,
+  // because I imagine it's rare to see a Python thread on a stage other than
+  // stage 0, and let's not make things unnecessarily complicated.
+  class PythonTagData : public ReferenceCount {
+  public:
+    virtual ~PythonTagData() {};
+  };
+  PT(PythonTagData) _python_tag_data;
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
   unsigned int _unexpected_change_flags;
   unsigned int _unexpected_change_flags;
@@ -555,9 +565,6 @@ private:
     CPT(RenderEffects) _effects;
     CPT(RenderEffects) _effects;
 
 
     TagData _tag_data;
     TagData _tag_data;
-#ifdef HAVE_PYTHON
-    PythonTagData _python_tag_data;
-#endif  // HAVE_PYTHON
 
 
     // These two together determine the per-camera visibility of this node.
     // These two together determine the per-camera visibility of this node.
     // See adjust_draw_mask() for details.
     // See adjust_draw_mask() for details.

+ 98 - 89
panda/src/pgraph/pandaNode_ext.cxx

@@ -70,6 +70,40 @@ __deepcopy__(PyObject *self, PyObject *memo) const {
   return dupe;
   return dupe;
 }
 }
 
 
+/**
+ * This variant on get_tag_keys returns a Python tuple of strings keys.
+ */
+PyObject *Extension<PandaNode>::
+get_tag_keys() const {
+  vector_string keys;
+  _this->get_tag_keys(keys);
+
+  PyObject *result = PyTuple_New(keys.size());
+  for (size_t i = 0; i < keys.size(); ++i) {
+    PyTuple_SET_ITEM(result, i, Dtool_WrapValue(keys[i]));
+  }
+
+  return result;
+}
+
+/**
+ * Returns a dictionary of Python tags that is associated with the current
+ * node.  This is similar to the regular tags, except this can store any
+ * Python object instead of just a string.  However, the Python object is not
+ * recorded to a bam file.
+ */
+PyObject *Extension<PandaNode>::
+get_python_tags() {
+  if (_this->_python_tag_data == NULL) {
+    _this->_python_tag_data = new PythonTagDataImpl;
+
+  } else if (_this->_python_tag_data->get_ref_count() > 1) {
+    // Copy-on-write.
+    _this->_python_tag_data = new PythonTagDataImpl(*(PythonTagDataImpl *)_this->_python_tag_data.p());
+  }
+  return ((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict;
+}
+
 /**
 /**
  * Associates an arbitrary Python object with a user-defined key which is
  * Associates an arbitrary Python object with a user-defined key which is
  * stored on the node.  This is similar to set_tag(), except it can store any
  * stored on the node.  This is similar to set_tag(), except it can store any
@@ -80,30 +114,9 @@ __deepcopy__(PyObject *self, PyObject *memo) const {
  * limit on the number of different keys that may be stored or on the length
  * limit on the number of different keys that may be stored or on the length
  * of any one key's value.
  * of any one key's value.
  */
  */
-void Extension<PandaNode>::
-set_python_tag(const string &key, PyObject *value) {
-  Thread *current_thread = Thread::get_current_thread();
-  int pipeline_stage = current_thread->get_pipeline_stage();
-  nassertv(pipeline_stage == 0);
-
-  PandaNode::CDWriter cdata(_this->_cycler);
-  Py_XINCREF(value);
-
-  pair<PandaNode::PythonTagData::iterator, bool> result;
-  result = cdata->_python_tag_data.insert(PandaNode::PythonTagData::value_type(key, value));
-
-  if (!result.second) {
-    // The insert was unsuccessful; that means the key was already present in
-    // the map.  In this case, we should decrement the original value's
-    // reference count and replace it with the new object.
-    PandaNode::PythonTagData::iterator ti = result.first;
-    PyObject *old_value = (*ti).second;
-    Py_XDECREF(old_value);
-    (*ti).second = value;
-  }
-
-  // Even though the python tag isn't recorded in the bam stream?
-  _this->mark_bam_modified();
+int Extension<PandaNode>::
+set_python_tag(PyObject *key, PyObject *value) {
+  return PyDict_SetItem(get_python_tags(), key, value);
 }
 }
 
 
 /**
 /**
@@ -111,17 +124,20 @@ set_python_tag(const string &key, PyObject *value) {
  * particular key, if any.  If no value has been previously set, returns None.
  * particular key, if any.  If no value has been previously set, returns None.
  */
  */
 PyObject *Extension<PandaNode>::
 PyObject *Extension<PandaNode>::
-get_python_tag(const string &key) const {
-  PandaNode::CDReader cdata(_this->_cycler);
-  PandaNode::PythonTagData::const_iterator ti;
-  ti = cdata->_python_tag_data.find(key);
-  if (ti != cdata->_python_tag_data.end()) {
-    PyObject *result = (*ti).second;
-    Py_XINCREF(result);
-    return result;
+get_python_tag(PyObject *key) const {
+  if (_this->_python_tag_data == NULL) {
+    Py_INCREF(Py_None);
+    return Py_None;
   }
   }
-  Py_INCREF(Py_None);
-  return Py_None;
+
+  PyObject *dict = ((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict;
+  PyObject *value = PyDict_GetItem(dict, key);
+  if (value == NULL) {
+    value = Py_None;
+  }
+  // PyDict_GetItem returns a borrowed reference.
+  Py_INCREF(value);
+  return value;
 }
 }
 
 
 /**
 /**
@@ -130,11 +146,13 @@ get_python_tag(const string &key) const {
  * been set.
  * been set.
  */
  */
 bool Extension<PandaNode>::
 bool Extension<PandaNode>::
-has_python_tag(const string &key) const {
-  PandaNode::CDReader cdata(_this->_cycler);
-  PandaNode::PythonTagData::const_iterator ti;
-  ti = cdata->_python_tag_data.find(key);
-  return (ti != cdata->_python_tag_data.end());
+has_python_tag(PyObject *key) const {
+  if (_this->_python_tag_data == NULL) {
+    return false;
+  }
+
+  PyObject *dict = ((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict;
+  return (PyDict_GetItem(dict, key) != NULL);
 }
 }
 
 
 /**
 /**
@@ -143,72 +161,63 @@ has_python_tag(const string &key) const {
  * the indicated key.
  * the indicated key.
  */
  */
 void Extension<PandaNode>::
 void Extension<PandaNode>::
-clear_python_tag(const string &key) {
-  Thread *current_thread = Thread::get_current_thread();
-  int pipeline_stage = current_thread->get_pipeline_stage();
-  nassertv(pipeline_stage == 0);
-
-  PandaNode::CDWriter cdata(_this->_cycler, current_thread);
-  PandaNode::PythonTagData::iterator ti;
-  ti = cdata->_python_tag_data.find(key);
-  if (ti != cdata->_python_tag_data.end()) {
-    PyObject *value = (*ti).second;
-    Py_XDECREF(value);
-    cdata->_python_tag_data.erase(ti);
+clear_python_tag(PyObject *key) {
+  if (_this->_python_tag_data == NULL) {
+    return;
   }
   }
 
 
-  // Even though the python tag isn't recorded in the bam stream?
-  _this->mark_bam_modified();
-}
-
-/**
- * Fills the given vector up with the list of Python tags on this PandaNode.
- *
- * It is the user's responsibility to ensure that the keys vector is empty
- * before making this call; otherwise, the new files will be appended to it.
- */
-void Extension<PandaNode>::
-get_python_tag_keys(vector_string &keys) const {
-  PandaNode::CDReader cdata(_this->_cycler);
-  if (!cdata->_python_tag_data.empty()) {
-    PandaNode::PythonTagData::const_iterator ti = cdata->_python_tag_data.begin();
-    while (ti != cdata->_python_tag_data.end()) {
-      keys.push_back((*ti).first);
-      ++ti;
-    }
+  PyObject *dict = get_python_tags();
+  if (PyDict_GetItem(dict, key) != NULL) {
+    PyDict_DelItem(dict, key);
   }
   }
 }
 }
 
 
 /**
 /**
- * This variant on get_tag_keys returns a Python list of strings.
+ * This variant on get_python_tag_keys returns a Python list of strings.
  */
  */
 PyObject *Extension<PandaNode>::
 PyObject *Extension<PandaNode>::
-get_tag_keys() const {
-  vector_string keys;
-  _this->get_tag_keys(keys);
-
-  PyObject *result = PyTuple_New(keys.size());
-  for (size_t i = 0; i < keys.size(); ++i) {
-    PyTuple_SET_ITEM(result, i, Dtool_WrapValue(keys[i]));
+get_python_tag_keys() const {
+  if (_this->_python_tag_data == NULL) {
+    return PyTuple_New(0);
   }
   }
 
 
-  return result;
+  PyObject *dict = ((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict;
+  return PyDict_Keys(dict);
 }
 }
 
 
 /**
 /**
- * This variant on get_python_tag_keys returns a Python list of strings.
+ * Called by Python to implement cycle detection.
  */
  */
-PyObject *Extension<PandaNode>::
-get_python_tag_keys() const {
-  vector_string keys;
-  get_python_tag_keys(keys);
-
-  PyObject *result = PyTuple_New(keys.size());
-  for (size_t i = 0; i < keys.size(); ++i) {
-    PyTuple_SET_ITEM(result, i, Dtool_WrapValue(keys[i]));
+int Extension<PandaNode>::
+__traverse__(visitproc visit, void *arg) {
+  // To fully implement cycle breaking, we also have to recurse into all of
+  // the node's children.  However, this seems like it would be potentially
+  // quite expensive, so I'd rather not do it unless we had some optimization
+  // that would allow us to quickly find out whether there are children with
+  // Python tags.
+  if (_this->_python_tag_data != NULL) {
+    Py_VISIT(((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict);
   }
   }
+  return 0;
+}
 
 
-  return result;
+/**
+ * Destroys the tags associated with the node.
+ */
+Extension<PandaNode>::PythonTagDataImpl::
+~PythonTagDataImpl() {
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  // This might happen at any time, so be sure the Python state is ready for
+  // it.
+  PyGILState_STATE gstate;
+  gstate = PyGILState_Ensure();
+#endif
+
+  Py_CLEAR(_dict);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_Release(gstate);
+#endif
 }
 }
 
 
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON

+ 19 - 5
panda/src/pgraph/pandaNode_ext.h

@@ -34,12 +34,26 @@ public:
 
 
   PyObject *get_tag_keys() const;
   PyObject *get_tag_keys() const;
 
 
-  void set_python_tag(const string &key, PyObject *value);
-  PyObject *get_python_tag(const string &key) const;
-  bool has_python_tag(const string &key) const;
-  void clear_python_tag(const string &key);
-  void get_python_tag_keys(vector_string &keys) const;
+  PyObject *get_python_tags();
+  int set_python_tag(PyObject *keys, PyObject *value);
+  PyObject *get_python_tag(PyObject *keys) const;
+  bool has_python_tag(PyObject *keys) const;
+  void clear_python_tag(PyObject *keys);
   PyObject *get_python_tag_keys() const;
   PyObject *get_python_tag_keys() const;
+
+  // This is defined to implement cycle detection in Python tags.
+  int __traverse__(visitproc visit, void *arg);
+
+private:
+  // This is what actually stores the Python tags.
+  class PythonTagDataImpl : public PandaNode::PythonTagData {
+  public:
+    PythonTagDataImpl() : _dict(PyDict_New()) {};
+    PythonTagDataImpl(const PythonTagDataImpl &copy) : _dict(PyDict_Copy(copy._dict)) {};
+    virtual ~PythonTagDataImpl();
+
+    PyObject *_dict;
+  };
 };
 };
 
 
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON

+ 0 - 4
panda/src/pipeline/config_pipeline.cxx

@@ -17,7 +17,6 @@
 #include "externalThread.h"
 #include "externalThread.h"
 #include "genericThread.h"
 #include "genericThread.h"
 #include "thread.h"
 #include "thread.h"
-#include "pythonThread.h"
 #include "pandaSystem.h"
 #include "pandaSystem.h"
 
 
 #include "dconfig.h"
 #include "dconfig.h"
@@ -73,9 +72,6 @@ init_libpipeline() {
   ExternalThread::init_type();
   ExternalThread::init_type();
   GenericThread::init_type();
   GenericThread::init_type();
   Thread::init_type();
   Thread::init_type();
-#ifdef HAVE_PYTHON
-  PythonThread::init_type();
-#endif  // HAVE_PYTHON
 
 
 #ifdef HAVE_THREADS
 #ifdef HAVE_THREADS
  {
  {

+ 0 - 1
panda/src/pipeline/p3pipeline_composite2.cxx

@@ -10,7 +10,6 @@
 #include "pipelineCyclerTrueImpl.cxx"
 #include "pipelineCyclerTrueImpl.cxx"
 #include "pmutex.cxx"
 #include "pmutex.cxx"
 #include "psemaphore.cxx"
 #include "psemaphore.cxx"
-#include "pythonThread.cxx"
 #include "reMutex.cxx"
 #include "reMutex.cxx"
 #include "reMutexDirect.cxx"
 #include "reMutexDirect.cxx"
 #include "reMutexHolder.cxx"
 #include "reMutexHolder.cxx"

+ 187 - 13
panda/src/pipeline/pythonThread.cxx

@@ -36,18 +36,7 @@ PythonThread(PyObject *function, PyObject *args,
     nassert_raise("Invalid function passed to PythonThread constructor");
     nassert_raise("Invalid function passed to PythonThread constructor");
   }
   }
 
 
-  if (args == Py_None) {
-    // None means no arguments; create an empty tuple.
-    _args = PyTuple_New(0);
-  } else {
-    _args = NULL;
-    if (PySequence_Check(args)) {
-      _args = PySequence_Tuple(args);
-    }
-    if (_args == NULL) {
-      nassert_raise("Invalid args passed to PythonThread constructor");
-    }
-  }
+  set_args(args);
 }
 }
 
 
 /**
 /**
@@ -73,13 +62,198 @@ join() {
 
 
   if (_result == NULL) {
   if (_result == NULL) {
     // No result; return None.
     // No result; return None.
-    return Py_BuildValue("");
+    Py_INCREF(Py_None);
+    return Py_None;
   }
   }
 
 
   Py_INCREF(_result);
   Py_INCREF(_result);
   return _result;
   return _result;
 }
 }
 
 
+/**
+ *
+ */
+PyObject *PythonThread::
+get_args() const {
+  return _args;
+}
+
+/**
+ *
+ */
+void PythonThread::
+set_args(PyObject *args) {
+  Py_XDECREF(_args);
+
+  if (args == Py_None) {
+    // None means no arguments; create an empty tuple.
+    _args = PyTuple_New(0);
+  } else {
+    _args = NULL;
+    if (PySequence_Check(args)) {
+      _args = PySequence_Tuple(args);
+    }
+    if (_args == NULL) {
+      Dtool_Raise_TypeError("PythonThread args must be a tuple");
+    }
+  }
+}
+
+#ifdef HAVE_PYTHON
+/**
+ * Internal function to safely call a Python function within a sub-thread,
+ * that might execute in parallel with existing Python code.  The return value
+ * is the return value of the Python function, or NULL if there was an
+ * exception.
+ */
+PyObject *PythonThread::
+call_python_func(PyObject *function, PyObject *args) {
+  Thread *current_thread = get_current_thread();
+
+  // Create a new Python thread state data structure, so Python can properly
+  // lock itself.
+  PyObject *result = NULL;
+
+  if (current_thread == get_main_thread()) {
+    // In the main thread, just call the function.
+    result = PyObject_Call(function, args, NULL);
+
+    if (result == (PyObject *)NULL) {
+      if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
+        // If we caught SystemExit, let it pass by without bothering to print
+        // a callback.
+
+      } else {
+        // Temporarily save and restore the exception state so we can print a
+        // callback on-the-spot.
+        PyObject *exc, *val, *tb;
+        PyErr_Fetch(&exc, &val, &tb);
+
+        Py_XINCREF(exc);
+        Py_XINCREF(val);
+        Py_XINCREF(tb);
+        PyErr_Restore(exc, val, tb);
+        PyErr_Print();
+
+        PyErr_Restore(exc, val, tb);
+      }
+    }
+
+  } else {
+#ifndef HAVE_THREADS
+    // Shouldn't be possible to come here without having some kind of
+    // threading support enabled.
+    nassertr(false, NULL);
+#else
+
+#ifdef SIMPLE_THREADS
+    // We can't use the PyGILState interface, which assumes we are using true
+    // OS-level threading.  PyGILState enforces policies like only one thread
+    // state per OS-level thread, which is not true in the case of
+    // SIMPLE_THREADS.
+
+    // For some reason I don't fully understand, I'm getting a crash when I
+    // clean up old PyThreadState objects with PyThreadState_Delete().  It
+    // appears that the thread state is still referenced somewhere at the time
+    // I call delete, and the crash occurs because I've deleted an active
+    // pointer.
+
+    // Storing these pointers in a vector for permanent recycling seems to
+    // avoid this problem.  I wish I understood better what's going wrong, but
+    // I guess this workaround will do.
+    static pvector<PyThreadState *> thread_states;
+
+    PyThreadState *orig_thread_state = PyThreadState_Get();
+    PyInterpreterState *istate = orig_thread_state->interp;
+    PyThreadState *new_thread_state;
+    if (thread_states.empty()) {
+      new_thread_state = PyThreadState_New(istate);
+    } else {
+      new_thread_state = thread_states.back();
+      thread_states.pop_back();
+    }
+    PyThreadState_Swap(new_thread_state);
+
+    // Call the user's function.
+    result = PyObject_Call(function, args, NULL);
+    if (result == (PyObject *)NULL && PyErr_Occurred()) {
+      // We got an exception.  Move the exception from the current thread into
+      // the main thread, so it can be handled there.
+      PyObject *exc, *val, *tb;
+      PyErr_Fetch(&exc, &val, &tb);
+
+      thread_cat.error()
+        << "Exception occurred within " << *current_thread << "\n";
+
+      // Temporarily restore the exception state so we can print a callback
+      // on-the-spot.
+      Py_XINCREF(exc);
+      Py_XINCREF(val);
+      Py_XINCREF(tb);
+      PyErr_Restore(exc, val, tb);
+      PyErr_Print();
+
+      PyThreadState_Swap(orig_thread_state);
+      thread_states.push_back(new_thread_state);
+      // PyThreadState_Clear(new_thread_state);
+      // PyThreadState_Delete(new_thread_state);
+
+      PyErr_Restore(exc, val, tb);
+
+      // Now attempt to force the main thread to the head of the ready queue,
+      // so it can respond to the exception immediately.  This only works if
+      // the main thread is not blocked, of course.
+      Thread::get_main_thread()->preempt();
+
+    } else {
+      // No exception.  Restore the thread state normally.
+      PyThreadState *state = PyThreadState_Swap(orig_thread_state);
+      thread_states.push_back(new_thread_state);
+      // PyThreadState_Clear(new_thread_state);
+      // PyThreadState_Delete(new_thread_state);
+    }
+
+#else  // SIMPLE_THREADS
+    // With true threading enabled, we're better off using PyGILState.
+    PyGILState_STATE gstate;
+    gstate = PyGILState_Ensure();
+
+    // Call the user's function.
+    result = PyObject_Call(function, args, NULL);
+    if (result == (PyObject *)NULL && PyErr_Occurred()) {
+      // We got an exception.  Move the exception from the current thread into
+      // the main thread, so it can be handled there.
+      PyObject *exc, *val, *tb;
+      PyErr_Fetch(&exc, &val, &tb);
+
+      thread_cat.error()
+        << "Exception occurred within " << *current_thread << "\n";
+
+      // Temporarily restore the exception state so we can print a callback
+      // on-the-spot.
+      Py_XINCREF(exc);
+      Py_XINCREF(val);
+      Py_XINCREF(tb);
+      PyErr_Restore(exc, val, tb);
+      PyErr_Print();
+
+      PyGILState_Release(gstate);
+
+      PyErr_Restore(exc, val, tb);
+    } else {
+      // No exception.  Restore the thread state normally.
+      PyGILState_Release(gstate);
+    }
+
+
+#endif  // SIMPLE_THREADS
+#endif  // HAVE_THREADS
+  }
+
+  return result;
+}
+#endif  // HAVE_PYTHON
+
 /**
 /**
  *
  *
  */
  */

+ 10 - 1
panda/src/pipeline/pythonThread.h

@@ -24,7 +24,7 @@
  * the Python level.  It will spawn a thread that executes an arbitrary Python
  * the Python level.  It will spawn a thread that executes an arbitrary Python
  * functor.
  * functor.
  */
  */
-class EXPCL_PANDA_PIPELINE PythonThread : public Thread {
+class PythonThread : public Thread {
 PUBLISHED:
 PUBLISHED:
   PythonThread(PyObject *function, PyObject *args,
   PythonThread(PyObject *function, PyObject *args,
                const string &name, const string &sync_name);
                const string &name, const string &sync_name);
@@ -32,6 +32,15 @@ PUBLISHED:
 
 
   BLOCKING PyObject *join();
   BLOCKING PyObject *join();
 
 
+public:
+  PyObject *get_args() const;
+  void set_args(PyObject *);
+
+  static PyObject *call_python_func(PyObject *function, PyObject *args);
+
+PUBLISHED:
+  MAKE_PROPERTY(args, get_args, set_args);
+
 protected:
 protected:
   virtual void thread_main();
   virtual void thread_main();
 
 

+ 19 - 0
panda/src/pipeline/thread.I

@@ -48,6 +48,16 @@ get_pstats_index() const {
   return _pstats_index;
   return _pstats_index;
 }
 }
 
 
+/**
+ * Returns the Python index associated with this thread, or -1 if no index has
+ * yet been associated with this thread.  This is used internally by the
+ * direct.stdpy.thread module; you should not need to call this directly.
+ */
+INLINE int Thread::
+get_python_index() const {
+  return _python_index;
+}
+
 /**
 /**
  * Returns a string that is guaranteed to be unique to this thread, across all
  * Returns a string that is guaranteed to be unique to this thread, across all
  * processes on the machine, during at least the lifetime of this process.
  * processes on the machine, during at least the lifetime of this process.
@@ -263,6 +273,15 @@ get_current_task() const {
   return _current_task;
   return _current_task;
 }
 }
 
 
+/**
+ * Stores a Python index to be associated with this thread.  This is used
+ * internally by the thread module; you should not need to call this directly.
+ */
+INLINE void Thread::
+set_python_index(int python_index) {
+  _python_index = python_index;
+}
+
 /**
 /**
  * Should be called by the main thread just before exiting the program, this
  * Should be called by the main thread just before exiting the program, this
  * blocks until any remaining thread cleanup has finished.
  * blocks until any remaining thread cleanup has finished.

+ 1 - 261
panda/src/pipeline/thread.cxx

@@ -19,10 +19,6 @@
 #include "conditionVarDebug.h"
 #include "conditionVarDebug.h"
 #include "conditionVarFullDebug.h"
 #include "conditionVarFullDebug.h"
 
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
 Thread *Thread::_main_thread;
 Thread *Thread::_main_thread;
 Thread *Thread::_external_thread;
 Thread *Thread::_external_thread;
 TypeHandle Thread::_type_handle;
 TypeHandle Thread::_type_handle;
@@ -47,33 +43,17 @@ Thread(const string &name, const string &sync_name) :
 {
 {
   _started = false;
   _started = false;
   _pstats_index = -1;
   _pstats_index = -1;
+  _python_index = -1;
   _pstats_callback = NULL;
   _pstats_callback = NULL;
   _pipeline_stage = 0;
   _pipeline_stage = 0;
   _joinable = false;
   _joinable = false;
   _current_task = NULL;
   _current_task = NULL;
 
 
-#ifdef HAVE_PYTHON
-  _python_data = Py_None;
-  Py_INCREF(_python_data);
-#endif
-
 #ifdef DEBUG_THREADS
 #ifdef DEBUG_THREADS
   _blocked_on_mutex = NULL;
   _blocked_on_mutex = NULL;
   _waiting_on_cvar = NULL;
   _waiting_on_cvar = NULL;
   _waiting_on_cvar_full = NULL;
   _waiting_on_cvar_full = NULL;
 #endif
 #endif
-
-#if defined(HAVE_PYTHON) && !defined(SIMPLE_THREADS)
-  // Ensure that the Python threading system is initialized and ready to go.
-#ifdef WITH_THREAD  // This symbol defined within Python.h
-
-#if PY_VERSION_HEX >= 0x03020000
-  Py_Initialize();
-#endif
-
-  PyEval_InitThreads();
-#endif
-#endif
 }
 }
 
 
 /**
 /**
@@ -81,10 +61,6 @@ Thread(const string &name, const string &sync_name) :
  */
  */
 Thread::
 Thread::
 ~Thread() {
 ~Thread() {
-#ifdef HAVE_PYTHON
-  Py_DECREF(_python_data);
-#endif
-
 #ifdef DEBUG_THREADS
 #ifdef DEBUG_THREADS
   nassertv(_blocked_on_mutex == NULL &&
   nassertv(_blocked_on_mutex == NULL &&
            _waiting_on_cvar == NULL &&
            _waiting_on_cvar == NULL &&
@@ -225,242 +201,6 @@ start(ThreadPriority priority, bool joinable) {
   return _started;
   return _started;
 }
 }
 
 
-#ifdef HAVE_PYTHON
-/**
- * Sets an arbitrary Python object that may be associated with this thread
- * object.  This is just for the purposes of associated arbitrary Python data
- * with the C++ object; other than managing the reference count, the C++ code
- * does nothing with this object.
- */
-void Thread::
-set_python_data(PyObject *python_data) {
-  Py_DECREF(_python_data);
-  _python_data = python_data;
-  Py_INCREF(_python_data);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns the Python object that was set with set_python_data().
- */
-PyObject *Thread::
-get_python_data() const {
-  Py_INCREF(_python_data);
-  return _python_data;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Internal function to safely call a Python function within a sub-thread,
- * that might execute in parallel with existing Python code.  The return value
- * is the return value of the Python function, or NULL if there was an
- * exception.
- */
-PyObject *Thread::
-call_python_func(PyObject *function, PyObject *args) {
-  nassertr(this == get_current_thread(), NULL);
-
-  // Create a new Python thread state data structure, so Python can properly
-  // lock itself.
-  PyObject *result = NULL;
-
-  if (this == get_main_thread()) {
-    // In the main thread, just call the function.
-    result = PyObject_Call(function, args, NULL);
-
-    if (result == (PyObject *)NULL) {
-      if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
-        // If we caught SystemExit, let it pass by without bothering to print
-        // a callback.
-
-      } else {
-        // Temporarily save and restore the exception state so we can print a
-        // callback on-the-spot.
-        PyObject *exc, *val, *tb;
-        PyErr_Fetch(&exc, &val, &tb);
-
-        Py_XINCREF(exc);
-        Py_XINCREF(val);
-        Py_XINCREF(tb);
-        PyErr_Restore(exc, val, tb);
-        PyErr_Print();
-
-        PyErr_Restore(exc, val, tb);
-      }
-    }
-
-  } else {
-#ifndef HAVE_THREADS
-    // Shouldn't be possible to come here without having some kind of
-    // threading support enabled.
-    nassertr(false, NULL);
-#else
-
-#ifdef SIMPLE_THREADS
-    // We can't use the PyGILState interface, which assumes we are using true
-    // OS-level threading.  PyGILState enforces policies like only one thread
-    // state per OS-level thread, which is not true in the case of
-    // SIMPLE_THREADS.
-
-    // For some reason I don't fully understand, I'm getting a crash when I
-    // clean up old PyThreadState objects with PyThreadState_Delete().  It
-    // appears that the thread state is still referenced somewhere at the time
-    // I call delete, and the crash occurs because I've deleted an active
-    // pointer.
-
-    // Storing these pointers in a vector for permanent recycling seems to
-    // avoid this problem.  I wish I understood better what's going wrong, but
-    // I guess this workaround will do.
-    static pvector<PyThreadState *> thread_states;
-
-    PyThreadState *orig_thread_state = PyThreadState_Get();
-    PyInterpreterState *istate = orig_thread_state->interp;
-    PyThreadState *new_thread_state;
-    if (thread_states.empty()) {
-      new_thread_state = PyThreadState_New(istate);
-    } else {
-      new_thread_state = thread_states.back();
-      thread_states.pop_back();
-    }
-    PyThreadState_Swap(new_thread_state);
-
-    // Call the user's function.
-    result = PyObject_Call(function, args, NULL);
-    if (result == (PyObject *)NULL && PyErr_Occurred()) {
-      // We got an exception.  Move the exception from the current thread into
-      // the main thread, so it can be handled there.
-      PyObject *exc, *val, *tb;
-      PyErr_Fetch(&exc, &val, &tb);
-
-      thread_cat.error()
-        << "Exception occurred within " << *this << "\n";
-
-      // Temporarily restore the exception state so we can print a callback
-      // on-the-spot.
-      Py_XINCREF(exc);
-      Py_XINCREF(val);
-      Py_XINCREF(tb);
-      PyErr_Restore(exc, val, tb);
-      PyErr_Print();
-
-      PyThreadState_Swap(orig_thread_state);
-      thread_states.push_back(new_thread_state);
-      // PyThreadState_Clear(new_thread_state);
-      // PyThreadState_Delete(new_thread_state);
-
-      PyErr_Restore(exc, val, tb);
-
-      // Now attempt to force the main thread to the head of the ready queue,
-      // so it can respond to the exception immediately.  This only works if
-      // the main thread is not blocked, of course.
-      Thread::get_main_thread()->preempt();
-
-    } else {
-      // No exception.  Restore the thread state normally.
-      PyThreadState *state = PyThreadState_Swap(orig_thread_state);
-      thread_states.push_back(new_thread_state);
-      // PyThreadState_Clear(new_thread_state);
-      // PyThreadState_Delete(new_thread_state);
-    }
-
-#else  // SIMPLE_THREADS
-    // With true threading enabled, we're better off using PyGILState.
-    PyGILState_STATE gstate;
-    gstate = PyGILState_Ensure();
-
-    // Call the user's function.
-    result = PyObject_Call(function, args, NULL);
-    if (result == (PyObject *)NULL && PyErr_Occurred()) {
-      // We got an exception.  Move the exception from the current thread into
-      // the main thread, so it can be handled there.
-      PyObject *exc, *val, *tb;
-      PyErr_Fetch(&exc, &val, &tb);
-
-      thread_cat.error()
-        << "Exception occurred within " << *this << "\n";
-
-      // Temporarily restore the exception state so we can print a callback
-      // on-the-spot.
-      Py_XINCREF(exc);
-      Py_XINCREF(val);
-      Py_XINCREF(tb);
-      PyErr_Restore(exc, val, tb);
-      PyErr_Print();
-
-      PyGILState_Release(gstate);
-
-      PyErr_Restore(exc, val, tb);
-    } else {
-      // No exception.  Restore the thread state normally.
-      PyGILState_Release(gstate);
-    }
-
-
-#endif  // SIMPLE_THREADS
-#endif  // HAVE_THREADS
-  }
-
-  return result;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Called when a Python exception is raised during processing of a thread.
- * Gets the error string and passes it back to the calling Python process in a
- * sensible way.
- */
-void Thread::
-handle_python_exception() {
-  /*
-  PyObject *exc, *val, *tb;
-  PyErr_Fetch(&exc, &val, &tb);
-
-  ostringstream strm;
-  strm << "\n";
-
-  if (PyObject_HasAttrString(exc, "__name__")) {
-    PyObject *exc_name = PyObject_GetAttrString(exc, "__name__");
-    PyObject *exc_str = PyObject_Str(exc_name);
-    strm << PyString_AsString(exc_str);
-    Py_DECREF(exc_str);
-    Py_DECREF(exc_name);
-  } else {
-    PyObject *exc_str = PyObject_Str(exc);
-    strm << PyString_AsString(exc_str);
-    Py_DECREF(exc_str);
-  }
-  Py_DECREF(exc);
-
-  if (val != (PyObject *)NULL) {
-    PyObject *val_str = PyObject_Str(val);
-    strm << ": " << PyString_AsString(val_str);
-    Py_DECREF(val_str);
-    Py_DECREF(val);
-  }
-  if (tb != (PyObject *)NULL) {
-    Py_DECREF(tb);
-  }
-
-  strm << "\nException occurred within thread " << get_name();
-  string message = strm.str();
-  nout << message << "\n";
-
-  nassert_raise(message);
-  */
-
-  thread_cat.error()
-    << "Exception occurred within " << *this << "\n";
-
-  // Now attempt to force the main thread to the head of the ready queue, so
-  // it will be the one to receive the above assertion.  This mainly only has
-  // an effect if SIMPLE_THREADS is in use.
-  Thread::get_main_thread()->preempt();
-}
-#endif  // HAVE_PYTHON
-
 /**
 /**
  * Creates the Thread object that represents the main thread.
  * Creates the Thread object that represents the main thread.
  */
  */

+ 13 - 14
panda/src/pipeline/thread.h

@@ -59,6 +59,7 @@ PUBLISHED:
   INLINE const string &get_sync_name() const;
   INLINE const string &get_sync_name() const;
 
 
   INLINE int get_pstats_index() const;
   INLINE int get_pstats_index() const;
+  INLINE int get_python_index() const;
   INLINE string get_unique_id() const;
   INLINE string get_unique_id() const;
 
 
   INLINE int get_pipeline_stage() const;
   INLINE int get_pipeline_stage() const;
@@ -88,15 +89,21 @@ PUBLISHED:
   BLOCKING INLINE void join();
   BLOCKING INLINE void join();
   INLINE void preempt();
   INLINE void preempt();
 
 
-#ifdef HAVE_PYTHON
-  void set_python_data(PyObject *python_data);
-  PyObject *get_python_data() const;
-#endif
-
   INLINE AsyncTaskBase *get_current_task() const;
   INLINE AsyncTaskBase *get_current_task() const;
 
 
+  INLINE void set_python_index(int index);
+
   INLINE static void prepare_for_exit();
   INLINE static void prepare_for_exit();
 
 
+  MAKE_PROPERTY(sync_name, get_sync_name);
+  MAKE_PROPERTY(pstats_index, get_pstats_index);
+  MAKE_PROPERTY(python_index, get_python_index);
+  MAKE_PROPERTY(unique_id, get_unique_id);
+  MAKE_PROPERTY(pipeline_stage, get_pipeline_stage, set_pipeline_stage);
+  MAKE_PROPERTY(started, is_started);
+  MAKE_PROPERTY(joinable, is_joinable);
+  MAKE_PROPERTY(current_task, get_current_task);
+
 public:
 public:
   // This class allows integration with PStats, particularly in the
   // This class allows integration with PStats, particularly in the
   // SIMPLE_THREADS case.
   // SIMPLE_THREADS case.
@@ -111,12 +118,6 @@ public:
   INLINE void set_pstats_callback(PStatsCallback *pstats_callback);
   INLINE void set_pstats_callback(PStatsCallback *pstats_callback);
   INLINE PStatsCallback *get_pstats_callback() const;
   INLINE PStatsCallback *get_pstats_callback() const;
 
 
-#ifdef HAVE_PYTHON
-  // Integration with Python.
-  PyObject *call_python_func(PyObject *function, PyObject *args);
-  void handle_python_exception();
-#endif  // HAVE_PYTHON
-
 private:
 private:
   static void init_main_thread();
   static void init_main_thread();
   static void init_external_thread();
   static void init_external_thread();
@@ -133,9 +134,7 @@ private:
   bool _joinable;
   bool _joinable;
   AsyncTaskBase *_current_task;
   AsyncTaskBase *_current_task;
 
 
-#ifdef HAVE_PYTHON
-  PyObject *_python_data;
-#endif
+  int _python_index;
 
 
 #ifdef DEBUG_THREADS
 #ifdef DEBUG_THREADS
   MutexDebug *_blocked_on_mutex;
   MutexDebug *_blocked_on_mutex;

+ 1 - 1
panda/src/pnmimage/pfmFile.h

@@ -170,9 +170,9 @@ PUBLISHED:
 
 
   void output(ostream &out) const;
   void output(ostream &out) const;
 
 
+#ifdef HAVE_PYTHON
   EXTENSION(PyObject *get_points() const);
   EXTENSION(PyObject *get_points() const);
 
 
-#if PY_VERSION_HEX >= 0x02060000
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
   EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
 #endif
 #endif
 
 

+ 4 - 4
panda/src/pnmimage/pfmFile_ext.cxx

@@ -70,7 +70,6 @@ get_points() const {
   return list;
   return list;
 }
 }
 
 
-#if PY_VERSION_HEX >= 0x02060000
 /**
 /**
  * This is a very low-level function that returns a read-only multiview into
  * This is a very low-level function that returns a read-only multiview into
  * the internal table of floating-point numbers.  Use this method at your own
  * the internal table of floating-point numbers.  Use this method at your own
@@ -78,7 +77,7 @@ get_points() const {
  */
  */
 int Extension<PfmFile>::
 int Extension<PfmFile>::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-
+#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
       PyErr_SetString(PyExc_BufferError,
       PyErr_SetString(PyExc_BufferError,
                       "Object is not writable.");
                       "Object is not writable.");
@@ -118,8 +117,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->suboffsets = NULL;
   view->suboffsets = NULL;
 
 
   return 0;
   return 0;
+#else
+  return -1;
+#endif
 }
 }
 
 
-#endif  // PY_VERSION_HEX >= 0x02060000
-
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON

+ 0 - 2
panda/src/pnmimage/pfmFile_ext.h

@@ -31,9 +31,7 @@ class Extension<PfmFile> : public ExtensionBase<PfmFile> {
 public:
 public:
   PyObject *get_points() const;
   PyObject *get_points() const;
 
 
-#if PY_VERSION_HEX >= 0x02060000
   int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
   int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
-#endif
 };
 };
 
 
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON

+ 2 - 2
panda/src/putil/bamReader_ext.cxx

@@ -13,6 +13,7 @@
 
 
 #include "bamReader_ext.h"
 #include "bamReader_ext.h"
 #include "config_util.h"
 #include "config_util.h"
+#include "pythonThread.h"
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
 
 
@@ -45,8 +46,7 @@ static TypedWritable *factory_callback(const FactoryParams &params){
   PyObject *args = PyTuple_Pack(2, py_scan, py_manager);
   PyObject *args = PyTuple_Pack(2, py_scan, py_manager);
 
 
   // Now call the Python function.
   // Now call the Python function.
-  Thread *current_thread = Thread::get_current_thread();
-  PyObject *result = current_thread->call_python_func(func, args);
+  PyObject *result = PythonThread::call_python_func(func, args);
   Py_DECREF(args);
   Py_DECREF(args);
   Py_DECREF(py_scan);
   Py_DECREF(py_scan);
   Py_DECREF(py_manager);
   Py_DECREF(py_manager);

+ 2 - 3
panda/src/putil/pythonCallbackObject.cxx

@@ -16,7 +16,7 @@
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
 
 
 #include "py_panda.h"
 #include "py_panda.h"
-#include "thread.h"
+#include "pythonThread.h"
 #include "callbackData.h"
 #include "callbackData.h"
 #include "config_util.h"
 #include "config_util.h"
 
 
@@ -121,8 +121,7 @@ do_python_callback(CallbackData *cbdata) {
   PyObject *args = Py_BuildValue("(O)", pycbdata);
   PyObject *args = Py_BuildValue("(O)", pycbdata);
   Py_DECREF(pycbdata);
   Py_DECREF(pycbdata);
 
 
-  PyObject *result =
-    Thread::get_current_thread()->call_python_func(_function, args);
+  PyObject *result = PythonThread::call_python_func(_function, args);
   Py_DECREF(args);
   Py_DECREF(args);
 
 
   if (result == (PyObject *)NULL) {
   if (result == (PyObject *)NULL) {

+ 0 - 4
panda/src/testbed/pgrid.cxx

@@ -12,7 +12,6 @@
  */
  */
 
 
 #include "pandaFramework.h"
 #include "pandaFramework.h"
-#include "pystub.h"
 #include "pandaNode.h"
 #include "pandaNode.h"
 #include "transformState.h"
 #include "transformState.h"
 #include "clockObject.h"
 #include "clockObject.h"
@@ -385,9 +384,6 @@ load_gridded_models(WindowFramework *window,
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   preprocess_argv(argc, argv);
   preprocess_argv(argc, argv);
   PandaFramework framework;
   PandaFramework framework;
   framework.open_framework(argc, argv);
   framework.open_framework(argc, argv);

+ 0 - 4
panda/src/testbed/pview.cxx

@@ -13,7 +13,6 @@
 
 
 #include "pandaFramework.h"
 #include "pandaFramework.h"
 #include "pandaSystem.h"
 #include "pandaSystem.h"
-#include "pystub.h"
 #include "textNode.h"
 #include "textNode.h"
 #include "configVariableBool.h"
 #include "configVariableBool.h"
 #include "texturePool.h"
 #include "texturePool.h"
@@ -234,9 +233,6 @@ report_version() {
 
 
 int
 int
 main(int argc, char **argv) {
 main(int argc, char **argv) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   preprocess_argv(argc, argv);
   preprocess_argv(argc, argv);
   framework.open_framework(argc, argv);
   framework.open_framework(argc, argv);
   framework.set_window_title("Panda Viewer");
   framework.set_window_title("Panda Viewer");

+ 0 - 4
pandatool/src/bam/bamInfo.cxx

@@ -24,7 +24,6 @@
 #include "pvector.h"
 #include "pvector.h"
 #include "bamCacheRecord.h"
 #include "bamCacheRecord.h"
 #include "bamCacheIndex.h"
 #include "bamCacheIndex.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -322,9 +321,6 @@ list_hierarchy(PandaNode *node, int indent_level) {
 }
 }
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   BamInfo prog;
   BamInfo prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/bam/bamToEgg.cxx

@@ -13,7 +13,6 @@
 
 
 #include "bamToEgg.h"
 #include "bamToEgg.h"
 #include "save_egg_file.h"
 #include "save_egg_file.h"
-#include "pystub.h"
 #include "string_utils.h"
 #include "string_utils.h"
 #include "bamFile.h"
 #include "bamFile.h"
 #include "bamCacheRecord.h"
 #include "bamCacheRecord.h"
@@ -98,9 +97,6 @@ run() {
 }
 }
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   BamToEgg prog;
   BamToEgg prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/bam/eggToBam.cxx

@@ -31,7 +31,6 @@
 #include "load_prc_file.h"
 #include "load_prc_file.h"
 #include "windowProperties.h"
 #include "windowProperties.h"
 #include "frameBufferProperties.h"
 #include "frameBufferProperties.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -485,9 +484,6 @@ make_buffer() {
 
 
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggToBam prog;
   EggToBam prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/bam/ptsToBam.cxx

@@ -19,7 +19,6 @@
 #include "pandaNode.h"
 #include "pandaNode.h"
 #include "geomNode.h"
 #include "geomNode.h"
 #include "dcast.h"
 #include "dcast.h"
-#include "pystub.h"
 #include "string_utils.h"
 #include "string_utils.h"
 #include "config_egg2pg.h"
 #include "config_egg2pg.h"
 
 
@@ -230,9 +229,6 @@ close_vertex_data() {
 }
 }
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   PtsToBam prog;
   PtsToBam prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/cvscopy/testCopy.cxx

@@ -13,7 +13,6 @@
 
 
 #include "testCopy.h"
 #include "testCopy.h"
 #include "cvsSourceDirectory.h"
 #include "cvsSourceDirectory.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -59,9 +58,6 @@ copy_file(const Filename &source, const Filename &dest,
 
 
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   TestCopy prog;
   TestCopy prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/daeprogs/daeToEgg.cxx

@@ -14,7 +14,6 @@
 #include "daeToEgg.h"
 #include "daeToEgg.h"
 
 
 #include "daeToEggConverter.h"
 #include "daeToEggConverter.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -76,9 +75,6 @@ run() {
 
 
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   DAEToEgg prog;
   DAEToEgg prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/daeprogs/eggToDAE.cxx

@@ -13,7 +13,6 @@
 
 
 #include "eggToDAE.h"
 #include "eggToDAE.h"
 #include "dcast.h"
 #include "dcast.h"
-#include "pystub.h"
 #include "pandaVersion.h"
 #include "pandaVersion.h"
 
 
 #include "FCDocument/FCDocument.h"
 #include "FCDocument/FCDocument.h"
@@ -165,9 +164,6 @@ void EggToDAE::apply_transform(FCDSceneNode* to, const PT(EggGroup) from) {
 }
 }
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggToDAE prog;
   EggToDAE prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/dxfprogs/dxfPoints.cxx

@@ -12,7 +12,6 @@
  */
  */
 
 
 #include "dxfPoints.h"
 #include "dxfPoints.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -83,9 +82,6 @@ handle_args(ProgramBase::Args &args) {
 
 
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   DXFPoints prog;
   DXFPoints prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/dxfprogs/dxfToEgg.cxx

@@ -14,7 +14,6 @@
 #include "dxfToEgg.h"
 #include "dxfToEgg.h"
 
 
 #include "dxfToEggConverter.h"
 #include "dxfToEggConverter.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -68,9 +67,6 @@ run() {
 
 
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   DXFToEgg prog;
   DXFToEgg prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/dxfprogs/eggToDXF.cxx

@@ -14,7 +14,6 @@
 #include "eggToDXF.h"
 #include "eggToDXF.h"
 #include "eggPolygon.h"
 #include "eggPolygon.h"
 #include "dcast.h"
 #include "dcast.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -143,9 +142,6 @@ write_entities(ostream &out) {
 
 
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggToDXF prog;
   EggToDXF prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/egg-mkfont/eggMakeFont.cxx

@@ -29,7 +29,6 @@
 #include "eggVertex.h"
 #include "eggVertex.h"
 #include "string_utils.h"
 #include "string_utils.h"
 #include "dcast.h"
 #include "dcast.h"
-#include "pystub.h"
 
 
 #include <ctype.h>
 #include <ctype.h>
 
 
@@ -737,9 +736,6 @@ is_numeric(const string &str) {
 }
 }
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggMakeFont prog;
   EggMakeFont prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/egg-optchar/eggOptchar.cxx

@@ -31,7 +31,6 @@
 #include "pset.h"
 #include "pset.h"
 #include "compose_matrix.h"
 #include "compose_matrix.h"
 #include "fftCompressor.h"
 #include "fftCompressor.h"
-#include "pystub.h"
 
 
 #include <algorithm>
 #include <algorithm>
 
 
@@ -1512,9 +1511,6 @@ do_defpose() {
 }
 }
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggOptchar prog;
   EggOptchar prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/egg-palettize/eggPalettize.cxx

@@ -23,7 +23,6 @@
 #include "pnotify.h"
 #include "pnotify.h"
 #include "notifyCategory.h"
 #include "notifyCategory.h"
 #include "notifySeverity.h"
 #include "notifySeverity.h"
-#include "pystub.h"
 
 
 #include <stdio.h>
 #include <stdio.h>
 
 
@@ -866,9 +865,6 @@ run() {
 
 
 int
 int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggPalettize prog;
   EggPalettize prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/egg-qtess/eggQtess.cxx

@@ -14,7 +14,6 @@
 #include "eggQtess.h"
 #include "eggQtess.h"
 #include "qtessGlobals.h"
 #include "qtessGlobals.h"
 #include "dcast.h"
 #include "dcast.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -331,9 +330,6 @@ find_surfaces(EggNode *egg_node) {
 }
 }
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggQtess prog;
   EggQtess prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/eggprogs/eggCrop.cxx

@@ -17,7 +17,6 @@
 #include "eggPrimitive.h"
 #include "eggPrimitive.h"
 #include "eggVertex.h"
 #include "eggVertex.h"
 #include "dcast.h"
 #include "dcast.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -118,9 +117,6 @@ strip_prims(EggGroupNode *group) {
 
 
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggCrop prog;
   EggCrop prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

+ 0 - 4
pandatool/src/eggprogs/eggListTextures.cxx

@@ -14,7 +14,6 @@
 #include "eggListTextures.h"
 #include "eggListTextures.h"
 #include "eggTextureCollection.h"
 #include "eggTextureCollection.h"
 #include "pnmImageHeader.h"
 #include "pnmImageHeader.h"
-#include "pystub.h"
 
 
 /**
 /**
  *
  *
@@ -59,9 +58,6 @@ run() {
 
 
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
-  // A call to pystub() to force libpystub.so to be linked in.
-  pystub();
-
   EggListTextures prog;
   EggListTextures prog;
   prog.parse_command_line(argc, argv);
   prog.parse_command_line(argc, argv);
   prog.run();
   prog.run();

Some files were not shown because too many files changed in this diff