Browse Source

Fix tp_compare, better __repr__/__str__ handling

rdb 10 years ago
parent
commit
3371df8403
55 changed files with 328 additions and 183 deletions
  1. 4 4
      direct/src/distributed/ConnectionRepository.py
  2. 1 1
      direct/src/distributed/DistributedCartesianGrid.py
  3. 0 9
      direct/src/extensions/ConfigVariableFilename-extensions.py
  4. 1 1
      direct/src/extensions/HTTPChannel-extensions.py
  5. 1 1
      direct/src/extensions_native/HTTPChannel_extensions.py
  6. 1 1
      direct/src/leveleditor/LevelEditorBase.py
  7. 2 2
      direct/src/p3d/AppRunner.py
  8. 1 1
      direct/src/p3d/FileSpec.py
  9. 1 1
      direct/src/p3d/HostInfo.py
  10. 2 2
      direct/src/p3d/PackageInfo.py
  11. 1 1
      direct/src/p3d/PatchMaker.py
  12. 1 1
      direct/src/p3d/ScanDirectoryNode.py
  13. 1 1
      direct/src/showbase/VFSImporter.py
  14. 1 1
      direct/src/showutil/FreezeTool.py
  15. 2 0
      dtool/src/dtoolutil/filename.h
  16. 153 63
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  17. 6 2
      dtool/src/interrogate/interfaceMakerPythonNative.h
  18. 1 1
      dtool/src/interrogate/typeManager.cxx
  19. 25 21
      dtool/src/interrogatedb/py_panda.cxx
  20. 1 0
      dtool/src/interrogatedb/py_panda.h
  21. 32 0
      panda/src/express/filename_ext.cxx
  22. 1 0
      panda/src/express/filename_ext.h
  23. 7 6
      panda/src/linmath/lmatrix3_ext_src.I
  24. 1 1
      panda/src/linmath/lmatrix3_ext_src.h
  25. 1 1
      panda/src/linmath/lmatrix3_src.h
  26. 6 5
      panda/src/linmath/lmatrix4_ext_src.I
  27. 1 1
      panda/src/linmath/lmatrix4_ext_src.h
  28. 1 1
      panda/src/linmath/lmatrix4_src.h
  29. 6 4
      panda/src/linmath/lpoint2_ext_src.I
  30. 1 1
      panda/src/linmath/lpoint2_ext_src.h
  31. 1 1
      panda/src/linmath/lpoint2_src.h
  32. 6 4
      panda/src/linmath/lpoint3_ext_src.I
  33. 1 1
      panda/src/linmath/lpoint3_ext_src.h
  34. 1 1
      panda/src/linmath/lpoint3_src.h
  35. 6 4
      panda/src/linmath/lpoint4_ext_src.I
  36. 1 1
      panda/src/linmath/lpoint4_ext_src.h
  37. 1 1
      panda/src/linmath/lpoint4_src.h
  38. 6 4
      panda/src/linmath/lvecBase2_ext_src.I
  39. 1 1
      panda/src/linmath/lvecBase2_ext_src.h
  40. 1 1
      panda/src/linmath/lvecBase2_src.h
  41. 6 4
      panda/src/linmath/lvecBase3_ext_src.I
  42. 1 1
      panda/src/linmath/lvecBase3_ext_src.h
  43. 1 1
      panda/src/linmath/lvecBase3_src.h
  44. 6 4
      panda/src/linmath/lvecBase4_ext_src.I
  45. 1 1
      panda/src/linmath/lvecBase4_ext_src.h
  46. 1 1
      panda/src/linmath/lvecBase4_src.h
  47. 6 4
      panda/src/linmath/lvector2_ext_src.I
  48. 1 1
      panda/src/linmath/lvector2_ext_src.h
  49. 1 1
      panda/src/linmath/lvector2_src.h
  50. 6 4
      panda/src/linmath/lvector3_ext_src.I
  51. 1 1
      panda/src/linmath/lvector3_ext_src.h
  52. 1 1
      panda/src/linmath/lvector3_src.h
  53. 6 4
      panda/src/linmath/lvector4_ext_src.I
  54. 1 1
      panda/src/linmath/lvector4_ext_src.h
  55. 1 1
      panda/src/linmath/lvector4_src.h

+ 4 - 4
direct/src/distributed/ConnectionRepository.py

@@ -491,7 +491,7 @@ class ConnectionRepository(
         elif self.connectMethod == self.CM_NET or (not hasattr(self,"connectNative")):
             # Try each of the servers in turn.
             for url in serverList:
-                self.notify.info("Connecting to %s via NET interface." % (url.cStr()))
+                self.notify.info("Connecting to %s via NET interface." % (url))
                 if self.tryConnectNet(url):
                     self.startReaderPollTask()
                     if successCallback:
@@ -503,7 +503,7 @@ class ConnectionRepository(
                 failureCallback(0, '', *failureArgs)
         elif self.connectMethod == self.CM_NATIVE:
             for url in serverList:
-                self.notify.info("Connecting to %s via Native interface." % (url.cStr()))
+                self.notify.info("Connecting to %s via Native interface." % (url))
                 if self.connectNative(url):
                     self.startReaderPollTask()
                     if successCallback:
@@ -536,7 +536,7 @@ class ConnectionRepository(
         if ch.isConnectionReady():
             self.setConnectionHttp(ch)
             self._serverAddress = serverList[serverIndex-1]
-            self.notify.info("Successfully connected to %s." % (self._serverAddress.cStr()))
+            self.notify.info("Successfully connected to %s." % (self._serverAddress))
 
             ## if self.recorder:
             ##     # If we have a recorder, we wrap the connect inside a
@@ -562,7 +562,7 @@ class ConnectionRepository(
             # No connection yet, but keep trying.
 
             url = serverList[serverIndex]
-            self.notify.info("Connecting to %s via HTTP interface." % (url.cStr()))
+            self.notify.info("Connecting to %s via HTTP interface." % (url))
             ch.preserveStatus()
 
             ch.beginConnectTo(DocumentSpec(url))

+ 1 - 1
direct/src/distributed/DistributedCartesianGrid.py

@@ -250,7 +250,7 @@ class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
         assert self.notify.debug("removeObjectFromGrid %s" % av)
         # TODO: WHAT LOCATION SHOULD WE SET THIS TO?
         #av.reparentTo(hidden)
-        if (av.getParent().compareTo(self) == 0):
+        if av.getParent() == self:
             # only detach if object is directly parented
             av.detachNode()
         #av.b_setLocation(0, 0)

+ 0 - 9
direct/src/extensions/ConfigVariableFilename-extensions.py

@@ -1,9 +0,0 @@
-
-    def __str__(self):
-        return self.cStr()
-
-    def __len__(self):
-        return self.length()
-    
-    def __getitem__(self, n):
-        return self.cStr().__getitem__(n)

+ 1 - 1
direct/src/extensions/HTTPChannel-extensions.py

@@ -14,7 +14,7 @@
         Returns the newly-spawned task.
         """
         if not name:
-            name = self.getUrl().cStr()
+            name = str(self.getUrl())
         from direct.task import Task
         task = Task.Task(self.doTask)
         task.callback = callback

+ 1 - 1
direct/src/extensions_native/HTTPChannel_extensions.py

@@ -18,7 +18,7 @@ def spawnTask(self, name = None, callback = None, extraArgs = []):
         Returns the newly-spawned task.
         """
         if not name:
-            name = self.getUrl().cStr()
+            name = str(self.getUrl())
         from direct.task import Task
         task = Task.Task(self.doTask)
         task.callback = callback

+ 1 - 1
direct/src/leveleditor/LevelEditorBase.py

@@ -111,7 +111,7 @@ class LevelEditorBase(DirectObject):
         base.direct.deselect(nodePath)
         self.objectMgr.removeObjectByNodePath(nodePath)
 
-        if (base.direct.selected.last != None and nodePath.compareTo(base.direct.selected.last)==0):
+        if base.direct.selected.last is not None and nodePath == base.direct.selected.last:
             # if base.direct.selected.last is refering to this
             # removed obj, clear the reference
             if (hasattr(__builtins__,'last')):

+ 2 - 2
direct/src/p3d/AppRunner.py

@@ -175,7 +175,7 @@ class AppRunner(DirectObject):
         # the current working directory, for convenience; but when we
         # move to multiple-instance sessions, it may have to be
         # different for each instance.
-        self.multifileRoot = ExecutionEnvironment.getCwd().cStr()
+        self.multifileRoot = str(ExecutionEnvironment.getCwd())
 
         # The "main" object will be exposed to the DOM as a property
         # of the plugin object; that is, document.pluginobject.main in
@@ -554,7 +554,7 @@ class AppRunner(DirectObject):
         # Write the file to a temporary filename, then atomically move
         # it to its actual filename, to avoid race conditions when
         # updating this file.
-        tfile = Filename.temporary(self.rootDir.cStr(), '.xml')
+        tfile = Filename.temporary(str(self.rootDir), '.xml')
         if doc.SaveFile(tfile.toOsSpecific()):
             tfile.renameTo(filename)
 

+ 1 - 1
direct/src/p3d/FileSpec.py

@@ -25,7 +25,7 @@ class FileSpec:
         if pathname is None:
             pathname = Filename(packageDir, filename)
 
-        self.filename = filename.cStr()
+        self.filename = str(filename)
         self.basename = filename.getBasename()
 
         if st is None:

+ 1 - 1
direct/src/p3d/HostInfo.py

@@ -646,7 +646,7 @@ class HostInfo:
         if hostDirBasename:
             # If the contents.xml specified a host_dir parameter, use
             # it.
-            hostDir = self.rootDir.cStr() + '/hosts'
+            hostDir = str(self.rootDir) + '/hosts'
             for component in hostDirBasename.split('/'):
                 if component:
                     if component[0] == '.':

+ 2 - 2
direct/src/p3d/PackageInfo.py

@@ -1066,7 +1066,7 @@ class PackageInfo:
             return False
 
         # We mount it under its actual location on disk.
-        root = self.getPackageDir().cStr()
+        root = self.getPackageDir()
 
         vfs = VirtualFileSystem.getGlobalPtr()
         vfs.mount(mf, root, vfs.MFReadOnly)
@@ -1210,7 +1210,7 @@ class PackageInfo:
         # Write the file to a temporary filename, then atomically move
         # it to its actual filename, to avoid race conditions when
         # updating this file.
-        tfile = Filename.temporary(self.getPackageDir().cStr(), '.xml')
+        tfile = Filename.temporary(str(self.getPackageDir()), '.xml')
         if doc.SaveFile(tfile.toOsSpecific()):
             tfile.renameTo(filename)
 

+ 1 - 1
direct/src/p3d/PatchMaker.py

@@ -526,7 +526,7 @@ class PatchMaker:
             # Also copy the seq to the import desc file, for
             # documentation purposes.
 
-            importDescFilename = self.packageDesc.cStr()[:-3] + 'import.xml'
+            importDescFilename = str(self.packageDesc)[:-3] + 'import.xml'
             importDescFullpath = Filename(self.patchMaker.installDir, importDescFilename)
             doc = TiXmlDocument(importDescFullpath.toOsSpecific())
             if doc.LoadFile():

+ 1 - 1
direct/src/p3d/ScanDirectoryNode.py

@@ -58,7 +58,7 @@ class ScanDirectoryNode:
             # Now update the usage.xml file with the newly-determined
             # disk space.
             xusage.SetAttribute('disk_space', str(self.getTotalSize()))
-            tfile = Filename.temporary(pathname.cStr(), '.xml')
+            tfile = Filename.temporary(str(pathname), '.xml')
             if doc.SaveFile(tfile.toOsSpecific()):
                 tfile.renameTo(usageFilename)
 

+ 1 - 1
direct/src/showbase/VFSImporter.py

@@ -408,7 +408,7 @@ class VFSSharedImporter:
             path = sys.path
 
         for dir in path:
-            pdir = Filename.fromOsSpecific(dir).cStr()
+            pdir = str(Filename.fromOsSpecific(dir))
             if pdir + '/' + basename == dirname:
                 # We found it!
                 return dir

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

@@ -1174,7 +1174,7 @@ class Freezer:
                 source = open(sourceFilename.toOsSpecific(), 'r').read()
                 if source and source[-1] != '\n':
                     source = source + '\n'
-                code = compile(source, sourceFilename.cStr(), 'exec')
+                code = compile(source, str(sourceFilename), 'exec')
 
         self.__addPyc(multifile, filename, code, compressionLevel)
 

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

@@ -122,6 +122,8 @@ PUBLISHED:
   INLINE size_t length() const;
   INLINE char operator [] (int n) const;
 
+  EXTENSION(PyObject *__repr__() const);
+
   INLINE string substr(size_t begin, size_t end = string::npos) const;
   INLINE void operator += (const string &other);
   INLINE Filename operator + (const string &other) const;

+ 153 - 63
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -569,6 +569,24 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap,
     return true;
   }
 
+  if (method_name == "__repr__") {
+    def._answer_location = "tp_repr";
+    def._wrapper_type = WT_no_params;
+    return true;
+  }
+
+  if (method_name == "__str__") {
+    def._answer_location = "tp_str";
+    def._wrapper_type = WT_no_params;
+    return true;
+  }
+
+  if (method_name == "__cmp__" || (remap->_flags & FunctionRemap::F_compare_to) != 0) {
+    def._answer_location = "tp_compare";
+    def._wrapper_type = WT_compare;
+    return true;
+  }
+
   if (remap->_type == FunctionRemap::T_typecast_method) {
     // A typecast operator.  Check for a supported low-level typecast type.
     if (TypeManager::is_bool(remap->_return_type->get_orig_type())) {
@@ -589,6 +607,12 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap,
       def._answer_location = "nb_float";
       def._wrapper_type = WT_no_params;
       return true;
+
+    } else if (remap->_return_type->new_type_is_atomic_string()) {
+      // A string type.
+      def._answer_location = "tp_str";
+      def._wrapper_type = WT_no_params;
+      return true;
     }
   }
 
@@ -2214,6 +2238,33 @@ write_module_class(ostream &out, Object *obj) {
         }
         break;
 
+      case WT_compare:
+        // int func(PyObject *self, Py_ssize_t index)
+        {
+          out << "//////////////////\n";
+          out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
+          out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
+          out << "//////////////////\n";
+          out << "static int " << def._wrapper_name << "(PyObject *self, PyObject *arg) {\n";
+          out << "  " << cClassName  << " *local_this = NULL;\n";
+          out << "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
+          out << "    return -1;\n";
+          out << "  }\n\n";
+
+          string expected_params;
+          write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true,
+                                AT_single_arg, RF_compare, false, true);
+
+          out << "  if (!_PyErr_OCCURRED()) {\n";
+          out << "    Dtool_Raise_BadArgumentsError(\n";
+          output_quoted(out, 6, expected_params);
+          out << ");\n";
+          out << "  }\n";
+          out << "  return -1;\n";
+          out << "}\n\n";
+        }
+        break;
+
       case WT_none:
         // Nothing special about the wrapper function: just write it normally.
         string fname = "static PyObject *" + def._wrapper_name + "(PyObject *self, PyObject *args, PyObject *kwds)\n";
@@ -2261,7 +2312,10 @@ write_module_class(ostream &out, Object *obj) {
       }
     }
 
-    int need_repr = NeedsAReprFunction(obj->_itype);
+    int need_repr = 0;
+    if (slots.count("tp_repr") == 0) {
+      need_repr = NeedsAReprFunction(obj->_itype);
+    }
     if (need_repr > 0) {
       out << "//////////////////\n";
       out << "//  A __repr__ function\n";
@@ -2292,7 +2346,10 @@ write_module_class(ostream &out, Object *obj) {
       has_local_repr = true;
     }
 
-    int need_str = NeedsAStrFunction(obj->_itype);
+    int need_str = 0;
+    if (slots.count("tp_str") == 0) {
+      need_str = NeedsAStrFunction(obj->_itype);
+    }
     if (need_str > 0) {
       out << "//////////////////\n";
       out << "//  A __str__ function\n";
@@ -2332,7 +2389,6 @@ write_module_class(ostream &out, Object *obj) {
     out << "  }\n\n";
 
     out << "  switch (op) {\n";
-    Function *compare_to_func = NULL;
     for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) {
       std::set<FunctionRemap*> remaps;
       Function *func = (*fi);
@@ -2349,77 +2405,66 @@ write_module_class(ostream &out, Object *obj) {
       }
       const string &fname = func->_ifunc.get_name();
       if (fname == "operator <") {
-        out << "  case Py_LT: {\n";
+        out << "  case Py_LT:\n";
       } else if (fname == "operator <=") {
-        out << "  case Py_LE: {\n";
+        out << "  case Py_LE:\n";
       } else if (fname == "operator ==") {
-        out << "  case Py_EQ: {\n";
+        out << "  case Py_EQ:\n";
       } else if (fname == "operator !=") {
-        out << "  case Py_NE: {\n";
+        out << "  case Py_NE:\n";
       } else if (fname == "operator >") {
-        out << "  case Py_GT: {\n";
+        out << "  case Py_GT:\n";
       } else if (fname == "operator >=") {
-        out << "  case Py_GE: {\n";
-      } else if (fname == "compare_to") {
-        compare_to_func = func;
-        continue;
+        out << "  case Py_GE:\n";
       } else {
         continue;
       }
+      out << "    {\n";
 
       string expected_params;
-      write_function_forset(out, remaps, 1, 1, expected_params, 4, true, false,
+      write_function_forset(out, remaps, 1, 1, expected_params, 6, true, false,
                             AT_single_arg, RF_pyobject | RF_err_null, false);
 
-      out << "    break;\n";
-      out << "  }\n";
+      out << "      break;\n";
+      out << "    }\n";
       has_local_richcompare = true;
     }
 
     out << "  }\n\n";
 
     out << "  if (_PyErr_OCCURRED()) {\n";
-    out << "    return (PyObject *)NULL;\n";
+    out << "    PyErr_Clear();\n";
     out << "  }\n\n";
 
-    if (compare_to_func != NULL) {
+    if (slots.count("tp_compare")) {
+      // A lot of Panda code depends on comparisons being done via the
+      // compare_to function, which is mapped to the tp_compare slot, which
+      // Python 3 no longer has.  So, we'll write code to fall back to that if
+      // no matching comparison operator was found.
       out << "#if PY_MAJOR_VERSION >= 3\n";
       out << "  // All is not lost; we still have the compare_to function to fall back onto.\n";
-      if (compare_to_func->_args_type == AT_single_arg) {
-        out << "  PyObject *result = " << compare_to_func->_name << "(self, arg);\n";
-      } else {
-        out << "  PyObject *args = PyTuple_Pack(1, arg);\n";
-        out << "  PyObject *result = " << compare_to_func->_name << "(self, args);\n";
-        out << "  Py_DECREF(args);\n";
-      }
-      out << "  if (result != NULL) {\n";
-      out << "    if (PyLong_Check(result)) {;\n";
-      out << "      long cmpval = PyLong_AS_LONG(result);\n";
-      out << "      switch (op) {\n";
-      out << "      case Py_LT:\n";
-      out << "        return PyBool_FromLong(cmpval < 0);\n";
-      out << "      case Py_LE:\n";
-      out << "        return PyBool_FromLong(cmpval <= 0);\n";
-      out << "      case Py_EQ:\n";
-      out << "        return PyBool_FromLong(cmpval == 0);\n";
-      out << "      case Py_NE:\n";
-      out << "        return PyBool_FromLong(cmpval != 0);\n";
-      out << "      case Py_GT:\n";
-      out << "        return PyBool_FromLong(cmpval > 0);\n";
-      out << "      case Py_GE:\n";
-      out << "        return PyBool_FromLong(cmpval >= 0);\n";
-      out << "      }\n";
-      out << "    }\n";
-      out << "    Py_DECREF(result);\n";
-      out << "  }\n\n";
-
-      out << "  if (_PyErr_OCCURRED()) {\n";
+      out << "  int cmpval = " << slots["tp_compare"]._wrapper_name << "(self, arg);\n";
+      out << "  if (cmpval == -1 && _PyErr_OCCURRED()) {\n";
       out << "    if (PyErr_ExceptionMatches(PyExc_TypeError)) {\n";
       out << "      PyErr_Clear();\n";
       out << "    } else {\n";
       out << "      return (PyObject *)NULL;\n";
       out << "    }\n";
       out << "  }\n";
+      out << "  switch (op) {\n";
+      out << "  case Py_LT:\n";
+      out << "    return PyBool_FromLong(cmpval < 0);\n";
+      out << "  case Py_LE:\n";
+      out << "    return PyBool_FromLong(cmpval <= 0);\n";
+      out << "  case Py_EQ:\n";
+      out << "    return PyBool_FromLong(cmpval == 0);\n";
+      out << "  case Py_NE:\n";
+      out << "    return PyBool_FromLong(cmpval != 0);\n";
+      out << "  case Py_GT:\n";
+      out << "    return PyBool_FromLong(cmpval > 0);\n";
+      out << "  case Py_GE:\n";
+      out << "    return PyBool_FromLong(cmpval >= 0);\n";
+      out << "  }\n";
       out << "#endif\n\n";
     }
 
@@ -2606,15 +2651,17 @@ write_module_class(ostream &out, Object *obj) {
   write_function_slot(out, 4, slots, "tp_setattr");
 
   // cmpfunc tp_compare;  (reserved in Python 3)
-  if (has_local_hash) {
-    out << "#if PY_MAJOR_VERSION >= 3\n";
-    out << "    0,\n";
-    out << "#else\n";
-    out << "    &DTOOL_PyObject_Compare,\n";
-    out << "#endif\n";
+  out << "#if PY_MAJOR_VERSION >= 3\n";
+  out << "    0, // tp_reserved\n";
+  out << "#else\n";
+  if (slots.count("tp_compare") != 0) {
+    write_function_slot(out, 4, slots, "tp_compare");
   } else {
-    out << "    0, // tp_compare\n";
+    // We can/should still define a comparison in Python 2 that is more
+    // meaningful that comparing id(x) with id(y).
+    out << "    &DTOOL_PyObject_ComparePointers,\n";
   }
+  out << "#endif\n";
 
   // reprfunc tp_repr;
   if (has_local_repr) {
@@ -2806,11 +2853,40 @@ write_module_class(ostream &out, Object *obj) {
     out << "    Dtool_" << ClassName << ".As_PyTypeObject().tp_bases = PyTuple_Pack(" << bases.size() << baseargs << ");\n";
   }
 
-  // get dictionary
-  out << "    Dtool_" << ClassName << ".As_PyTypeObject().tp_dict = PyDict_New();\n";
-  out << "    PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"DtoolClassDict\", Dtool_" << ClassName << ".As_PyTypeObject().tp_dict);\n";
-
   int num_nested = obj->_itype.number_of_nested_types();
+  int num_dict_items = 1;
+
+  // Go through once to estimate the number of elements the dict will hold.
+  for (int ni = 0; ni < num_nested; ni++) {
+    TypeIndex nested_index = obj->_itype.get_nested_type(ni);
+    if (_objects.count(nested_index) == 0) {
+      continue;
+    }
+    Object *nested_obj = _objects[nested_index];
+    assert(nested_obj != (Object *)NULL);
+
+    if (nested_obj->_itype.is_class() || nested_obj->_itype.is_struct()) {
+      num_dict_items += 2;
+
+    } else if (nested_obj->_itype.is_typedef()) {
+      ++num_dict_items;
+
+    } else if (nested_obj->_itype.is_enum()) {
+      CPPEnumType *enum_type = nested_obj->_itype._cpptype->as_enum_type();
+      num_dict_items += 2 * enum_type->_elements.size();
+    }
+  }
+
+  // Build type dictionary.  The size is just an estimation.
+  if (num_dict_items > 5) {
+    out << "    PyObject *dict = _PyDict_NewPresized(" << num_dict_items << ");\n";
+  } else {
+    out << "    PyObject *dict = PyDict_New();\n";
+  }
+  out << "    Dtool_" << ClassName << ".As_PyTypeObject().tp_dict = dict;\n";
+  out << "    PyDict_SetItemString(dict, \"DtoolClassDict\", dict);\n";
+
+  // Now go through the nested types again to actually add the dict items.
   for (int ni = 0; ni < num_nested; ni++) {
     TypeIndex nested_index = obj->_itype.get_nested_type(ni);
     if (_objects.count(nested_index) == 0) {
@@ -2828,9 +2904,9 @@ write_module_class(ostream &out, Object *obj) {
       out << "    Dtool_PyModuleClassInit_" << ClassName1 << "(NULL);\n";
       string name1 = classNameFromCppName(ClassName2, false);
       string name2 = classNameFromCppName(ClassName2, true);
-      out << "    PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n";
+      out << "    PyDict_SetItemString(dict, \"" << name1 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n";
       if (name1 != name2) {
-        out << "    PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name2 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n";
+        out << "    PyDict_SetItemString(dict, \"" << name2 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n";
       }
 
     } else if (nested_obj->_itype.is_typedef()) {
@@ -2849,7 +2925,7 @@ write_module_class(ostream &out, Object *obj) {
       string ClassName2 = make_safe_name(interrogate_type_name(wrapped));
 
       string name1 = classNameFromCppName(ClassName2, false);
-      out << "    PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n";
+      out << "    PyDict_SetItemString(dict, \"" << name1 << "\", (PyObject *)&Dtool_" << ClassName1 << ".As_PyTypeObject());\n";
       // No need to support mangled names for nested typedefs; we only added support recently.
 
     } else if (nested_obj->_itype.is_enum()) {
@@ -2867,9 +2943,9 @@ write_module_class(ostream &out, Object *obj) {
           name2 = name1;
         }
         string enum_value = obj->_itype.get_scoped_name() + "::" + (*ei)->get_simple_name();
-        out << "    PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", PyLongOrInt_FromLong(" << enum_value << "));\n";
+        out << "    PyDict_SetItemString(dict, \"" << name1 << "\", PyLongOrInt_FromLong(" << enum_value << "));\n";
         if (name1 != name2) {
-          out << "    PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name2 << "\", PyLongOrInt_FromLong(" << enum_value << "));\n";
+          out << "    PyDict_SetItemString(dict, \"" << name2 << "\", PyLongOrInt_FromLong(" << enum_value << "));\n";
         }
       }
     }
@@ -5566,7 +5642,13 @@ write_function_instance(ostream &out, FunctionRemap *remap,
         << "return DTool_PyInit_Finalize(self, " << return_expr << ", &" << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) << ", true, false);\n";
 
     } else if (TypeManager::is_integer(orig_type)) {
-      indent(out, indent_level) << "return " << return_expr << ";\n";
+      if (return_flags & RF_compare) {
+        // Make sure it returns -1, 0, or 1, or Python complains with:
+        // RuntimeWarning: tp_compare didn't return -1, 0 or 1
+        indent(out, indent_level) << "return (int)(" << return_expr << " > 0) - (int)(" << return_expr << " < 0);\n";
+      } else {
+        indent(out, indent_level) << "return " << return_expr << ";\n";
+      }
 
     } else if (TypeManager::is_void(orig_type)) {
       indent(out, indent_level) << "return 0;\n";
@@ -6613,6 +6695,10 @@ HasAGetClassTypeFunction(const InterrogateType &itype_class) {
 //               Returns 1 if the class defines write(ostream).
 //
 //               Returns 2 if the class defines write(ostream, int).
+//
+//               Note that if you want specific behavior for Python
+//               str(), you should just define a __str__ function,
+//               which maps directly to the appropriate type slot.
 ////////////////////////////////////////////////////////////////////
 int InterfaceMakerPythonNative::
 NeedsAStrFunction(const InterrogateType &itype_class) {
@@ -6682,6 +6768,10 @@ NeedsAStrFunction(const InterrogateType &itype_class) {
 //
 //               Returns 3 if the class defines an extension
 //               function for python_repr(ostream, string).
+//
+//               Note that defining python_repr is deprecated in
+//               favor of defining a __repr__ that returns a string,
+//               which maps directly to the appropriate type slot.
 ////////////////////////////////////////////////////////////////////
 int InterfaceMakerPythonNative::
 NeedsAReprFunction(const InterrogateType &itype_class) {

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

@@ -81,13 +81,17 @@ private:
     WT_inplace_binary_operator,
     WT_inplace_ternary_operator,
     WT_traverse,
+    WT_compare,
   };
 
   // This enum is passed to the wrapper generation functions to indicate
   // what sort of values the wrapper function is expected to return.
   enum ReturnFlags {
     // -1 on failure, 0 on success.
-    RF_int  = 0x100,
+    RF_int = 0x100,
+
+    // Like RF_int, but special case that it returns -1, 0, or 1.
+    RF_compare = RF_int | 0x200,
 
     // Returns the actual return value as PyObject*.
     RF_pyobject = 0x010,
@@ -104,7 +108,7 @@ private:
     RF_err_false = 0x008,
 
     // Decref temporary args object before returning.
-    RF_decref_args = 0x200,
+    RF_decref_args = 0x1000,
   };
 
   class SlottedFunctionDef {

+ 1 - 1
dtool/src/interrogate/typeManager.cxx

@@ -834,7 +834,7 @@ is_string(CPPType *type) {
     break;
   }
 
-  return is_basic_string_wchar(type);
+  return is_basic_string_char(type);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 25 - 21
dtool/src/interrogatedb/py_panda.cxx

@@ -637,6 +637,30 @@ inline long  DTool_HashKey(PyObject * inst)
    XXX of error.
 */
 
+int DTOOL_PyObject_ComparePointers(PyObject *v1, PyObject *v2) {
+  // try this compare
+  void *v1_this = DTOOL_Call_GetPointerThis(v1);
+  void *v2_this = DTOOL_Call_GetPointerThis(v2);
+  if (v1_this != NULL && v2_this != NULL) { // both are our types...
+    if (v1_this < v2_this) {
+      return -1;
+    }
+    if (v1_this > v2_this) {
+      return 1;
+    }
+    return 0;
+  }
+
+  // ok self compare...
+  if (v1 < v2) {
+    return -1;
+  }
+  if (v1 > v2) {
+    return 1;
+  }
+  return 0;
+}
+
 int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2) {
   // First try compareTo function..
   PyObject * func = PyObject_GetAttrString(v1, "compare_to");
@@ -685,27 +709,7 @@ int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2) {
     }
   }
 
-  // try this compare
-  void *v1_this = DTOOL_Call_GetPointerThis(v1);
-  void *v2_this = DTOOL_Call_GetPointerThis(v2);
-  if (v1_this != NULL && v2_this != NULL) { // both are our types...
-    if (v1_this < v2_this) {
-      return -1;
-    }
-    if (v1_this > v2_this) {
-      return 1;
-    }
-    return 0;
-  }
-
-  // ok self compare...
-  if (v1 < v2) {
-    return -1;
-  }
-  if (v1 > v2) {
-    return 1;
-  }
-  return 0;
+  return DTOOL_PyObject_ComparePointers(v1, v2);
 }
 
 PyObject *DTOOL_PyObject_RichCompare(PyObject *v1, PyObject *v2, int op) {

+ 1 - 0
dtool/src/interrogatedb/py_panda.h

@@ -532,6 +532,7 @@ EXPCL_DTOOLCONFIG long  DTool_HashKey(PyObject * inst)
    XXX of error.
 */
 
+EXPCL_DTOOLCONFIG int DTOOL_PyObject_ComparePointers(PyObject *v1, PyObject *v2);
 EXPCL_DTOOLCONFIG int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2);
 
 EXPCL_DTOOLCONFIG PyObject *DTOOL_PyObject_RichCompare(PyObject *v1, PyObject *v2, int op);

+ 32 - 0
panda/src/express/filename_ext.cxx

@@ -36,6 +36,38 @@ __reduce__(PyObject *self) const {
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<Filename>::__repr__
+//       Access: Published
+//  Description: Returns a string representation of the filename that
+//               communicates both its type and value.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<Filename>::
+__repr__() const {
+#if PY_MAJOR_VERSION >= 3
+  // Python 3 case: return a unicode object.
+  wstring filename = _this->get_fullpath_w();
+  PyObject *str = PyUnicode_FromWideChar(filename.data(), (Py_ssize_t)filename.size());
+
+#if PY_VERSION_HEX >= 0x03040000
+  PyObject *result = PyUnicode_FromFormat("Filename(%R)", str);
+#else
+  static PyObject *format = PyUnicode_FromString("Filename(%r)");
+  PyObject *result = PyUnicode_Format(format, str);
+#endif
+
+#else
+  // Python 2 case: return a regular string.
+  string filename = _this->get_fullpath();
+  PyObject *str = PyString_FromStringAndSize(filename.data(), (Py_ssize_t)filename.size());
+  static PyObject *format = PyString_FromString("Filename(%r)");
+  PyObject *result = PyString_Format(format, str);
+#endif
+
+  Py_DECREF(str);
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Extension<Filename>::scan_directory
 //       Access: Published

+ 1 - 0
panda/src/express/filename_ext.h

@@ -33,6 +33,7 @@ template<>
 class Extension<Filename> : public ExtensionBase<Filename> {
 public:
   PyObject *__reduce__(PyObject *self) const;
+  PyObject *__repr__() const;
   PyObject *scan_directory() const;
 };
 

+ 7 - 6
panda/src/linmath/lmatrix3_ext_src.I

@@ -30,7 +30,7 @@ __reduce__(PyObject *self) const {
     return NULL;
   }
 
-  PyObject *result = Py_BuildValue("(O(fffffffff))", this_class, 
+  PyObject *result = Py_BuildValue("(O(fffffffff))", this_class,
     _this->_m(0, 0), _this->_m(0, 1), _this->_m(0, 2),
     _this->_m(1, 0), _this->_m(1, 1), _this->_m(1, 2),
     _this->_m(2, 0), _this->_m(2, 1), _this->_m(2, 2));
@@ -40,13 +40,14 @@ __reduce__(PyObject *self) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LMatrix3::python_repr
+//     Function: LMatrix3::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LMatrix3)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "(" 
+INLINE_LINMATH string Extension<FLOATNAME(LMatrix3)>::
+__repr__() const {
+  ostringstream out;
+  out << "LMatrix3" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_m(0, 0)) << ", "
       << MAYBE_ZERO(_this->_m(0, 1)) << ", "
       << MAYBE_ZERO(_this->_m(0, 2)) << ", "
@@ -58,5 +59,5 @@ python_repr(ostream &out, const string &class_name) const {
       << MAYBE_ZERO(_this->_m(2, 0)) << ", "
       << MAYBE_ZERO(_this->_m(2, 1)) << ", "
       << MAYBE_ZERO(_this->_m(2, 2)) << ")";
+  return out.str();
 }
-

+ 1 - 1
panda/src/linmath/lmatrix3_ext_src.h

@@ -23,7 +23,7 @@ template<>
 class Extension<FLOATNAME(LMatrix3)> : public ExtensionBase<FLOATNAME(LMatrix3)> {
 public:
   INLINE_LINMATH PyObject *__reduce__(PyObject *self) const;
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 };
 
 #include "lmatrix3_ext_src.I"

+ 1 - 1
panda/src/linmath/lmatrix3_src.h

@@ -291,7 +291,7 @@ PUBLISHED:
 
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
   INLINE_LINMATH void generate_hash(ChecksumHashGenerator &hashgen) const;
   void generate_hash(

+ 6 - 5
panda/src/linmath/lmatrix4_ext_src.I

@@ -41,13 +41,14 @@ __reduce__(PyObject *self) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LMatrix4::python_repr
+//     Function: LMatrix4::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LMatrix4)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "(" 
+INLINE_LINMATH string Extension<FLOATNAME(LMatrix4)>::
+__repr__() const {
+  ostringstream out;
+  out << "LMatrix4" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_m(0, 0)) << ", "
       << MAYBE_ZERO(_this->_m(0, 1)) << ", "
       << MAYBE_ZERO(_this->_m(0, 2)) << ", "
@@ -67,5 +68,5 @@ python_repr(ostream &out, const string &class_name) const {
       << MAYBE_ZERO(_this->_m(3, 1)) << ", "
       << MAYBE_ZERO(_this->_m(3, 2)) << ", "
       << MAYBE_ZERO(_this->_m(3, 3)) << ")";
+  return out.str();
 }
-

+ 1 - 1
panda/src/linmath/lmatrix4_ext_src.h

@@ -23,7 +23,7 @@ template<>
 class Extension<FLOATNAME(LMatrix4)> : public ExtensionBase<FLOATNAME(LMatrix4)> {
 public:
   INLINE_LINMATH PyObject *__reduce__(PyObject *self) const;
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 };
 
 #include "lmatrix4_ext_src.I"

+ 1 - 1
panda/src/linmath/lmatrix4_src.h

@@ -265,7 +265,7 @@ PUBLISHED:
 
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
   INLINE_LINMATH void generate_hash(ChecksumHashGenerator &hashgen) const;
   void generate_hash(ChecksumHashGenerator &hashgen, FLOATTYPE scale) const;

+ 6 - 4
panda/src/linmath/lpoint2_ext_src.I

@@ -23,15 +23,17 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LPoint2::python_repr
+//     Function: LPoint2::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LPoint2)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LPoint2)>::
+__repr__() const {
+  ostringstream out;
+  out << "LPoint2" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lpoint2_ext_src.h

@@ -24,7 +24,7 @@ class Extension<FLOATNAME(LPoint2)> : public ExtensionBase<FLOATNAME(LPoint2)> {
 public:
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 };
 
 #include "lpoint2_ext_src.I"

+ 1 - 1
panda/src/linmath/lpoint2_src.h

@@ -54,7 +54,7 @@ PUBLISHED:
   INLINE_LINMATH FLOATNAME(LPoint2) project(const FLOATNAME(LVecBase2) &onto) const;
 #endif
 
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
 public:
   static TypeHandle get_class_type() {

+ 6 - 4
panda/src/linmath/lpoint3_ext_src.I

@@ -23,16 +23,18 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LPoint3::python_repr
+//     Function: LPoint3::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LPoint3)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LPoint3)>::
+__repr__() const {
+  ostringstream out;
+  out << "LPoint3" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ", "
       << MAYBE_ZERO(_this->_v(2)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lpoint3_ext_src.h

@@ -24,7 +24,7 @@ class Extension<FLOATNAME(LPoint3)> : public ExtensionBase<FLOATNAME(LPoint3)> {
 public:
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 };
 
 #include "lpoint3_ext_src.I"

+ 1 - 1
panda/src/linmath/lpoint3_src.h

@@ -75,7 +75,7 @@ PUBLISHED:
                                        FLOATTYPE up,
                                        CoordinateSystem cs = CS_default);
 
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
 public:
   static TypeHandle get_class_type() {

+ 6 - 4
panda/src/linmath/lpoint4_ext_src.I

@@ -23,17 +23,19 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LPoint4::python_repr
+//     Function: LPoint4::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LPoint4)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LPoint4)>::
+__repr__() const {
+  ostringstream out;
+  out << "LPoint4" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ", "
       << MAYBE_ZERO(_this->_v(2)) << ", "
       << MAYBE_ZERO(_this->_v(3)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lpoint4_ext_src.h

@@ -24,7 +24,7 @@ class Extension<FLOATNAME(LPoint4)> : public ExtensionBase<FLOATNAME(LPoint4)> {
 public:
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 };
 
 #include "lpoint4_ext_src.I"

+ 1 - 1
panda/src/linmath/lpoint4_src.h

@@ -56,7 +56,7 @@ PUBLISHED:
   INLINE_LINMATH FLOATNAME(LPoint4) project(const FLOATNAME(LVecBase4) &onto) const;
 #endif
 
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
 public:
   static TypeHandle get_class_type() {

+ 6 - 4
panda/src/linmath/lvecBase2_ext_src.I

@@ -29,15 +29,17 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LVecBase2::python_repr
+//     Function: LVecBase2::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LVecBase2)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LVecBase2)>::
+__repr__() const {
+  ostringstream out;
+  out << "LVecBase2" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lvecBase2_ext_src.h

@@ -25,7 +25,7 @@ public:
   INLINE_LINMATH PyObject *__reduce__(PyObject *self) const;
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 
   INLINE_LINMATH FLOATNAME(LVecBase2) __pow__(FLOATTYPE exponent) const;
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);

+ 1 - 1
panda/src/linmath/lvecBase2_src.h

@@ -144,7 +144,7 @@ PUBLISHED:
   INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase2) &other) const;
 
   INLINE_LINMATH void output(ostream &out) const;
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
   INLINE_LINMATH void write_datagram_fixed(Datagram &destination) const;
   INLINE_LINMATH void read_datagram_fixed(DatagramIterator &source);

+ 6 - 4
panda/src/linmath/lvecBase3_ext_src.I

@@ -29,16 +29,18 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LVecBase3::python_repr
+//     Function: LVecBase3::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LVecBase3)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LVecBase3)>::
+__repr__() const {
+  ostringstream out;
+  out << "LVecBase3" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ", "
       << MAYBE_ZERO(_this->_v(2)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lvecBase3_ext_src.h

@@ -25,7 +25,7 @@ public:
   INLINE_LINMATH PyObject *__reduce__(PyObject *self) const;
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 
   INLINE_LINMATH FLOATNAME(LVecBase3) __pow__(FLOATTYPE exponent) const;
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);

+ 1 - 1
panda/src/linmath/lvecBase3_src.h

@@ -159,7 +159,7 @@ PUBLISHED:
   INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase3) &other) const;
 
   INLINE_LINMATH void output(ostream &out) const;
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
   INLINE_LINMATH void write_datagram_fixed(Datagram &destination) const;
   INLINE_LINMATH void read_datagram_fixed(DatagramIterator &source);

+ 6 - 4
panda/src/linmath/lvecBase4_ext_src.I

@@ -29,17 +29,19 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LVecBase4::python_repr
+//     Function: LVecBase4::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LVecBase4)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LVecBase4)>::
+__repr__() const {
+  ostringstream out;
+  out << "LVecBase4" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ", "
       << MAYBE_ZERO(_this->_v(2)) << ", "
       << MAYBE_ZERO(_this->_v(3)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lvecBase4_ext_src.h

@@ -25,7 +25,7 @@ public:
   INLINE_LINMATH PyObject *__reduce__(PyObject *self) const;
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 
   INLINE_LINMATH FLOATNAME(LVecBase4) __pow__(FLOATTYPE exponent) const;
   INLINE_LINMATH PyObject *__ipow__(PyObject *self, FLOATTYPE exponent);

+ 1 - 1
panda/src/linmath/lvecBase4_src.h

@@ -163,7 +163,7 @@ PUBLISHED:
   INLINE_LINMATH bool almost_equal(const FLOATNAME(LVecBase4) &other) const;
 
   INLINE_LINMATH void output(ostream &out) const;
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
   INLINE_LINMATH void write_datagram_fixed(Datagram &destination) const;
   INLINE_LINMATH void read_datagram_fixed(DatagramIterator &source);

+ 6 - 4
panda/src/linmath/lvector2_ext_src.I

@@ -23,15 +23,17 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LVector2::python_repr
+//     Function: LVector2::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LVector2)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LVector2)>::
+__repr__() const {
+  ostringstream out;
+  out << "LVector2" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lvector2_ext_src.h

@@ -24,7 +24,7 @@ class Extension<FLOATNAME(LVector2)> : public ExtensionBase<FLOATNAME(LVector2)>
 public:
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 };
 
 #include "lvector2_ext_src.I"

+ 1 - 1
panda/src/linmath/lvector2_src.h

@@ -49,7 +49,7 @@ PUBLISHED:
   INLINE_LINMATH FLOATTYPE signed_angle_deg(const FLOATNAME(LVector2) &other) const;
 #endif
 
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
 public:
   static TypeHandle get_class_type() {

+ 6 - 4
panda/src/linmath/lvector3_ext_src.I

@@ -23,16 +23,18 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LVector3::python_repr
+//     Function: LVector3::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LVector3)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LVector3)>::
+__repr__() const {
+  ostringstream out;
+  out << "LVector3" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ", "
       << MAYBE_ZERO(_this->_v(2)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lvector3_ext_src.h

@@ -24,7 +24,7 @@ class Extension<FLOATNAME(LVector3)> : public ExtensionBase<FLOATNAME(LVector3)>
 public:
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 };
 
 #include "lvector3_ext_src.I"

+ 1 - 1
panda/src/linmath/lvector3_src.h

@@ -85,7 +85,7 @@ PUBLISHED:
   INLINE_LINMATH static FLOATNAME(LVector3) rfu(FLOATTYPE right,
                                         FLOATTYPE fwd,FLOATTYPE up,     CoordinateSystem cs = CS_default);
 
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
 public:
   static TypeHandle get_class_type() {

+ 6 - 4
panda/src/linmath/lvector4_ext_src.I

@@ -23,17 +23,19 @@
 #endif
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LVector4::python_repr
+//     Function: LVector4::__repr__
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE_LINMATH void Extension<FLOATNAME(LVector4)>::
-python_repr(ostream &out, const string &class_name) const {
-  out << class_name << "("
+INLINE_LINMATH string Extension<FLOATNAME(LVector4)>::
+__repr__() const {
+  ostringstream out;
+  out << "LVector4" << FLOATTOKEN << "("
       << MAYBE_ZERO(_this->_v(0)) << ", "
       << MAYBE_ZERO(_this->_v(1)) << ", "
       << MAYBE_ZERO(_this->_v(2)) << ", "
       << MAYBE_ZERO(_this->_v(3)) << ")";
+  return out.str();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/linmath/lvector4_ext_src.h

@@ -24,7 +24,7 @@ class Extension<FLOATNAME(LVector4)> : public ExtensionBase<FLOATNAME(LVector4)>
 public:
   INLINE_LINMATH PyObject *__getattr__(const string &attr_name) const;
   INLINE_LINMATH int __setattr__(PyObject *self, const string &attr_name, PyObject *assign);
-  INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const;
+  INLINE_LINMATH string __repr__() const;
 };
 
 #include "lvector4_ext_src.I"

+ 1 - 1
panda/src/linmath/lvector4_src.h

@@ -50,7 +50,7 @@ PUBLISHED:
   INLINE_LINMATH FLOATNAME(LVector4) project(const FLOATNAME(LVecBase4) &onto) const;
 #endif
 
-  EXTENSION(INLINE_LINMATH void python_repr(ostream &out, const string &class_name) const);
+  EXTENSION(INLINE_LINMATH string __repr__() const);
 
 public:
   static TypeHandle get_class_type() {