Browse Source

Merge branch 'release/1.10.x'

rdb 1 year ago
parent
commit
7560a1edd1

+ 17 - 0
.github/workflows/ci.yml

@@ -387,6 +387,23 @@ jobs:
         rmdir panda3d-1.10.14
         (cd thirdparty/darwin-libs-a && rm -rf rocket)
 
+    - name: Set up Python 3.13
+      if: runner.os != 'Windows'
+      uses: actions/setup-python@v5
+      with:
+        python-version: '3.13'
+    - name: Build Python 3.13
+      if: runner.os != 'Windows'
+      shell: bash
+      run: |
+        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10
+    - name: Test Python 3.13
+      if: runner.os != 'Windows'
+      shell: bash
+      run: |
+        python -m pip install pytest setuptools
+        PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
+
     - name: Set up Python 3.12
       uses: actions/setup-python@v5
       with:

+ 3 - 2
direct/src/dcparser/dcField_ext.cxx

@@ -278,8 +278,9 @@ get_pystr(PyObject *value) {
     return result;
   }
 
-  if (value->ob_type != nullptr) {
-    PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
+  PyTypeObject *type = Py_TYPE(value);
+  if (type != nullptr) {
+    PyObject *typestr = PyObject_Str((PyObject *)type);
     if (typestr != nullptr) {
       std::string result = PyUnicode_AsUTF8(typestr);
       Py_DECREF(typestr);

+ 2 - 1
direct/src/showbase/DistancePhasedNode.py

@@ -87,7 +87,8 @@ class DistancePhasedNode(PhasedObject, DirectObject, NodePath):
         """
         Reuse abandoned ids.
         """
-        DistancePhasedNode.__InstanceDeque.append(id)
+        if DistancePhasedNode is not None:
+            DistancePhasedNode.__InstanceDeque.append(id)
 
     def __init__(self, name, phaseParamMap = {},
                  autoCleanup = True,

+ 1 - 1
dtool/src/dtoolutil/filename_ext.cxx

@@ -47,7 +47,7 @@ __init__(PyObject *path) {
     return;
   }
 
-  if (Py_TYPE(path) == &Dtool_Filename._PyType) {
+  if (Py_IS_TYPE(path, Dtool_GetPyTypeObject(&Dtool_Filename))) {
     // Copy constructor.
     *_this = *(Filename *)DtoolInstance_VOID_PTR(path);
     return;

+ 28 - 2
dtool/src/interrogatedb/py_compat.h

@@ -65,7 +65,7 @@ typedef int Py_ssize_t;
 
 /* Python 2.6 */
 
-#ifndef Py_TYPE
+#if PY_VERSION_HEX < 0x02060000
 #  define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
 #endif
 
@@ -221,8 +221,12 @@ INLINE PyObject *_PyLong_Lshift(PyObject *a, size_t shiftby) {
 
 /* Python 3.9 */
 
+#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE)
+#  define Py_IS_TYPE(ob, type) (Py_TYPE((PyObject *)ob) == type)
+#endif
+
 #ifndef PyCFunction_CheckExact
-#  define PyCFunction_CheckExact(op) (Py_TYPE(op) == &PyCFunction_Type)
+#  define PyCFunction_CheckExact(op) (Py_IS_TYPE(op, &PyCFunction_Type))
 #endif
 
 #if PY_VERSION_HEX < 0x03090000
@@ -281,6 +285,28 @@ ALWAYS_INLINE PyObject *Py_XNewRef(PyObject *obj) {
 }
 #endif
 
+/* Python 3.10 */
+
+#if PY_VERSION_HEX < 0x030A0000
+INLINE int PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) {
+  int ret = PyModule_AddObject(module, name, value);
+  if (ret == 0) {
+    Py_INCREF(value);
+  }
+  return ret;
+}
+
+ALWAYS_INLINE PyObject *Py_NewRef(PyObject *obj) {
+  Py_INCREF(obj);
+  return obj;
+}
+
+ALWAYS_INLINE PyObject *Py_XNewRef(PyObject *obj) {
+  Py_XINCREF(obj);
+  return obj;
+}
+#endif
+
 /* Python 3.12 */
 
 #if PY_VERSION_HEX < 0x030C0000

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

@@ -321,7 +321,7 @@ static PyObject *Dtool_EnumType_New(PyTypeObject *subtype, PyObject *args, PyObj
                         subtype->tp_name);
   }
 
-  if (Py_TYPE(arg) == subtype) {
+  if (Py_IS_TYPE(arg, subtype)) {
     return Py_NewRef(arg);
   }
 

+ 1 - 1
makepanda/makepanda.py

@@ -1723,7 +1723,7 @@ def CompileLink(dll, obj, opts):
                 if "PYTHON" not in opts:
                     pythonv = SDK["PYTHONVERSION"].replace('.', '')
                     if optlevel <= 2:
-                        cmd += ' /NOD:{}d.lib'.format(pythonv)
+                        cmd += ' /NOD:{}_d.lib'.format(pythonv)
                     else:
                         cmd += ' /NOD:{}.lib'.format(pythonv)
 

+ 17 - 6
makepanda/makepandacore.py

@@ -2129,6 +2129,10 @@ def SdkLocatePython(prefer_thirdparty_python=False):
         gil_disabled = locations.get_config_var("Py_GIL_DISABLED")
         if gil_disabled and int(gil_disabled):
             SDK["PYTHONEXEC"] += "3.13t"
+            abiflags = "t"
+            DefSymbol("PYTHON", "Py_GIL_DISABLED", "1")
+        else:
+            abiflags = ""
 
         if (GetOptimize() <= 2):
             SDK["PYTHONEXEC"] += "_d.exe"
@@ -2140,11 +2144,11 @@ def SdkLocatePython(prefer_thirdparty_python=False):
 
         # Determine which version it is by checking which dll is in the directory.
         if (GetOptimize() <= 2):
-            py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]_d.dll") + \
-                      glob.glob(SDK["PYTHON"] + "/python[0-9][0-9][0-9]_d.dll")
+            py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]" + abiflags + "_d.dll") + \
+                      glob.glob(SDK["PYTHON"] + "/python[0-9][0-9][0-9]" + abiflags + "_d.dll")
         else:
-            py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9].dll") + \
-                      glob.glob(SDK["PYTHON"] + "/python[0-9][0-9][0-9].dll")
+            py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]" + abiflags + ".dll") + \
+                      glob.glob(SDK["PYTHON"] + "/python[0-9][0-9][0-9]" + abiflags + ".dll")
 
         if len(py_dlls) == 0:
             exit("Could not find the Python dll in %s." % (SDK["PYTHON"]))
@@ -2155,7 +2159,7 @@ def SdkLocatePython(prefer_thirdparty_python=False):
         py_dllver = py_dll.strip(".DHLNOPTY_dhlnopty")
         ver = py_dllver[0] + '.' + py_dllver[1:]
 
-        SDK["PYTHONVERSION"] = "python" + ver
+        SDK["PYTHONVERSION"] = "python" + ver + abiflags
         os.environ["PYTHONHOME"] = SDK["PYTHON"]
 
         running_ver = '%d.%d' % sys.version_info[:2]
@@ -3389,7 +3393,14 @@ def GetPythonABI():
         if soabi:
             return soabi
 
-    return 'cpython-%d%d' % (sys.version_info[:2])
+    soabi = 'cpython-%d%d' % (sys.version_info[:2])
+
+    if sys.version_info >= (3, 13):
+        gil_disabled = locations.get_config_var("Py_GIL_DISABLED")
+        if gil_disabled and int(gil_disabled):
+            return soabi + 't'
+
+    return soabi
 
 def CalcLocation(fn, ipath):
     if fn.startswith("panda3d/") and fn.endswith(".py"):

+ 1 - 1
panda/src/event/asyncFuture_ext.cxx

@@ -175,7 +175,7 @@ set_result(PyObject *result) {
   else if (DtoolInstance_Check(result)) {
     // If this is a Python subclass of a C++ type, fall through to below, since
     // we don't want to lose that extra information.
-    if (Py_TYPE(result) == (PyTypeObject *)DtoolInstance_TYPE(result)) {
+    if (Py_IS_TYPE(result, Dtool_GetPyTypeObject(DtoolInstance_TYPE(result)))) {
       void *ptr;
       if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedWritableReferenceCount))) {
         _this->set_result((TypedWritableReferenceCount *)ptr);

+ 5 - 5
panda/src/event/pythonTask.cxx

@@ -300,13 +300,13 @@ __setattr__(PyObject *self, PyObject *attr, PyObject *v) {
   if (!PyUnicode_Check(attr)) {
     PyErr_Format(PyExc_TypeError,
                  "attribute name must be string, not '%.200s'",
-                 attr->ob_type->tp_name);
+                 Py_TYPE(attr)->tp_name);
     return -1;
   }
 
   PyObject *descr = _PyType_Lookup(Py_TYPE(self), attr);
   if (descr != nullptr) {
-    descrsetfunc f = descr->ob_type->tp_descr_set;
+    descrsetfunc f = Py_TYPE(descr)->tp_descr_set;
     if (f != nullptr) {
       return f(descr, self, v);
     }
@@ -469,7 +469,7 @@ cancel() {
 #endif
 
       // Shortcut for unextended AsyncFuture.
-      if (Py_TYPE(_fut_waiter) == (PyTypeObject *)&Dtool_AsyncFuture) {
+      if (Py_IS_TYPE(_fut_waiter, Dtool_GetPyTypeObject(&Dtool_AsyncFuture))) {
         AsyncFuture *fut = (AsyncFuture *)DtoolInstance_VOID_PTR(_fut_waiter);
         if (!fut->done()) {
           fut->cancel();
@@ -717,7 +717,7 @@ do_python_task() {
         _retrieved_exception = false;
 
         if (task_cat.is_debug()) {
-          if (_exception != nullptr && Py_TYPE(_exception) == &PyType_Type) {
+          if (_exception != nullptr && Py_IS_TYPE(_exception, &PyType_Type)) {
             task_cat.debug()
               << *this << " received " << ((PyTypeObject *)_exception)->tp_name << " from coroutine.\n";
           } else {
@@ -862,7 +862,7 @@ do_python_task() {
   PyMethodDef *meth = nullptr;
   if (PyCFunction_Check(result)) {
     meth = ((PyCFunctionObject *)result)->m_ml;
-  } else if (Py_TYPE(result) == &PyMethodDescr_Type) {
+  } else if (Py_IS_TYPE(result, &PyMethodDescr_Type)) {
     meth = ((PyMethodDescrObject *)result)->d_method;
   }
 

+ 1 - 1
panda/src/express/datagram_ext.I

@@ -47,7 +47,7 @@ __reduce__() const {
   }
 
   extern struct Dtool_PyTypedObject Dtool_Datagram;
-  PyObject *tp = (PyObject *)&Dtool_Datagram._PyType;
+  PyObject *tp = (PyObject *)Dtool_GetPyTypeObject(&Dtool_Datagram);
 
   PyObject *result = PyTuple_New(2);
   PyTuple_SET_ITEM(result, 0, Py_NewRef(tp));

+ 1 - 1
panda/src/express/pointerToArray_ext.I

@@ -96,7 +96,7 @@ __init__(PyObject *self, PyObject *source) {
 
   // Now construct the internal list by copying the elements one-at-a-time
   // from Python.
-  PyObject *dict = DtoolInstance_TYPE(self)->_PyType.tp_dict;
+  PyObject *dict = Dtool_GetPyTypeObject(DtoolInstance_TYPE(self))->tp_dict;
   PyObject *push_back = PyDict_GetItemString(dict, "push_back");
   if (push_back == nullptr) {
     PyErr_BadArgument();

+ 1 - 1
panda/src/gobj/internalName_ext.cxx

@@ -74,7 +74,7 @@ PyObject *Extension<InternalName>::
 __reduce__() const {
   std::string name = _this->get_name();
   return Py_BuildValue("(N(s#))",
-    PyObject_GetAttrString((PyObject *)&Dtool_InternalName._PyType, "make"), name.c_str(), name.size());
+    PyObject_GetAttrString((PyObject *)Dtool_GetPyTypeObject(&Dtool_InternalName), "make"), name.c_str(), name.size());
 }
 
 #endif  // HAVE_PYTHON

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

@@ -183,7 +183,7 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) {
 #endif
       } else {
         PyErr_Format(PyExc_ValueError, "'%.200s' object is not iterable",
-          assign->ob_type->tp_name);
+          Py_TYPE(assign)->tp_name);
       }
       return -1;
     }

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

@@ -187,7 +187,7 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) {
 #endif
       } else {
         PyErr_Format(PyExc_ValueError, "'%.200s' object is not iterable",
-          assign->ob_type->tp_name);
+          Py_TYPE(assign)->tp_name);
       }
       return -1;
     }

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

@@ -196,7 +196,7 @@ __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign) {
 #endif
       } else {
         PyErr_Format(PyExc_ValueError, "'%.200s' object is not iterable",
-          assign->ob_type->tp_name);
+          Py_TYPE(assign)->tp_name);
       }
       return -1;
     }

+ 1 - 1
panda/src/ode/odeSpace_ext.cxx

@@ -72,7 +72,7 @@ collide(PyObject* arg, PyObject* callback) {
   nassertr(callback != nullptr, -1);
 
   if (!PyCallable_Check(callback)) {
-    PyErr_Format(PyExc_TypeError, "'%s' object is not callable", callback->ob_type->tp_name);
+    PyErr_Format(PyExc_TypeError, "'%s' object is not callable", Py_TYPE(callback)->tp_name);
     return -1;
 
   } else if (_this->get_id() == nullptr) {

+ 1 - 1
panda/src/ode/odeUtil_ext.cxx

@@ -28,7 +28,7 @@ int Extension<OdeUtil>::
 collide2(const OdeGeom &geom1, const OdeGeom &geom2, PyObject* arg, PyObject* callback) {
   nassertr(callback != nullptr, -1);
   if (!PyCallable_Check(callback)) {
-    PyErr_Format(PyExc_TypeError, "'%s' object is not callable", callback->ob_type->tp_name);
+    PyErr_Format(PyExc_TypeError, "'%s' object is not callable", Py_TYPE(callback)->tp_name);
     return -1;
   } else {
     _python_callback = Py_XNewRef(callback);

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

@@ -75,7 +75,7 @@ __reduce__(PyObject *self) const {
   // object whose constructor we should call (e.g.  this), and the arguments
   // necessary to reconstruct this object.
 
-  PyObject *this_class = (PyObject *)self->ob_type;
+  PyObject *this_class = (PyObject *)Py_TYPE(self);
   if (this_class == nullptr) {
     return nullptr;
   }

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

@@ -85,7 +85,7 @@ static TypedWritable *factory_callback(const FactoryParams &params){
       Py_DECREF(result);
     }
     else if (DtoolInstance_TYPE(result) == &Dtool_TypedWritable &&
-             Py_TYPE(result) != &Dtool_TypedWritable._PyType) {
+             !Py_IS_TYPE(result, Dtool_GetPyTypeObject(&Dtool_TypedWritable))) {
       // It is a custom subclass of TypedWritable, so we have to keep it
       // alive, and decrement it in finalize(), see typedWritable_ext.cxx.
       manager->register_finalize(ptr);

+ 1 - 1
panda/src/putil/pythonCallbackObject.cxx

@@ -87,7 +87,7 @@ get_function() {
  */
 PyObject *PythonCallbackObject::
 __reduce__() const {
-  return Py_BuildValue("O(O)", (PyObject *)&Dtool_PythonCallbackObject._PyType, _function);
+  return Py_BuildValue("O(O)", (PyObject *)Dtool_GetPyTypeObject(&Dtool_PythonCallbackObject), _function);
 }
 
 /**

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

@@ -134,10 +134,10 @@ wrap_typed_writable(void *from_this, PyTypeObject *from_type) {
   nassertr(from_type != nullptr, nullptr);
 
   TypedWritableProxy *to_this;
-  if (from_type == &Dtool_TypedWritable._PyType) {
+  if (from_type == Dtool_GetPyTypeObject(&Dtool_TypedWritable)) {
     to_this = (TypedWritableProxy *)(TypedWritable *)from_this;
   }
-  else if (from_type == (PyTypeObject *)&Dtool_TypedObject._PyType) {
+  else if (from_type == Dtool_GetPyTypeObject(&Dtool_TypedObject)) {
     to_this = (TypedWritableProxy *)(TypedObject *)from_this;
   }
   else {