No proper upstream fix yet, patch taken from [1]. Sounded questionable
but upon inspection I believe it's Ok. Originally tried Fedora's patch
but it caused test failures (or at least did with python3.14).

In addition to [1], this also extend on py314-tests.patch to adjust
refcount values for py315. [1] mentions & explain these failures a bit
but did not address them, I "believe" that they are nothing to worry
about. If this test causes more problems, may make sense to patch it
out downstream until upstream handles it.

[1] https://github.com/pygame/pygame/issues/4716#issuecomment-6104711463
--- a/src_c/pgcompat.h
+++ b/src_c/pgcompat.h
@@ -25,3 +25,25 @@
     PySlice_GetIndicesEx(slice, length, start, stop, step, slicelength)
 
+/* PyWeakref_GetObject() was removed in Python 3.15. PyWeakref_GetRef()
+ * (Python 3.13+) replaces it: returns 1 and a new reference if the referent
+ * is alive, 0 and NULL if it is dead, -1 and NULL on error. */
+#if PY_VERSION_HEX < 0x030D0000
+static inline int
+PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
+{
+    PyObject *obj = PyWeakref_GetObject(ref);
+    if (obj == NULL) {
+        *pobj = NULL;
+        return -1;
+    }
+    if (obj == Py_None) {
+        *pobj = NULL;
+        return 0;
+    }
+    Py_INCREF(obj);
+    *pobj = obj;
+    return 1;
+}
+#endif
+
 #endif /* ~PGCOMPAT_INTERNAL_H */
--- a/src_c/surface.c
+++ b/src_c/surface.c
@@ -1021,6 +1021,12 @@
 
     for (i = 0; i < len; i++) {
-        tmp = PyWeakref_GetObject(PyList_GetItem(surf->locklist, i));
-        Py_INCREF(tmp);
+        if (PyWeakref_GetRef(PyList_GetItem(surf->locklist, i), &tmp) < 0) {
+            Py_DECREF(tuple);
+            return NULL;
+        }
+        if (tmp == NULL) { /* dead lock owner: report None, as before */
+            tmp = Py_None;
+            Py_INCREF(tmp);
+        }
         PyTuple_SetItem(tuple, i, tmp);
     }
@@ -3444,9 +3450,15 @@
     consumer_ref = internal->consumer_ref;
     assert(consumer_ref && PyWeakref_CheckRef(consumer_ref));
-    consumer = PyWeakref_GetObject(consumer_ref);
-    if (consumer) {
-        if (!pgSurface_UnlockBy((pgSurfaceObject *)view_p->obj, consumer)) {
+    if (PyWeakref_GetRef(consumer_ref, &consumer) < 0) {
+        PyErr_Clear();
+    }
+    else {
+        /* A dead consumer is passed as None, as PyWeakref_GetObject() did,
+         * so UnlockBy still purges its dead lock entry. */
+        if (!pgSurface_UnlockBy((pgSurfaceObject *)view_p->obj,
+                                consumer ? consumer : Py_None)) {
             PyErr_Clear();
         }
+        Py_XDECREF(consumer);
     }
     Py_DECREF(consumer_ref);
--- a/src_c/surflock.c
+++ b/src_c/surflock.c
@@ -122,9 +122,14 @@
     if (surf->locklist != NULL) {
         PyObject *item, *ref;
+        int alive, match;
         Py_ssize_t len = PyList_Size(surf->locklist);
         while (--len >= 0 && !found) {
             item = PyList_GetItem(surf->locklist, len);
-            ref = PyWeakref_GetObject(item);
-            if (ref == lockobj) {
+            if (PyWeakref_GetRef(item, &ref) < 0) {
+                return 0;
+            }
+            match = (ref == lockobj);
+            Py_XDECREF(ref);
+            if (match) {
                 if (PySequence_DelItem(surf->locklist, len) == -1) {
                     return 0;
@@ -140,6 +145,11 @@
         while (--len >= 0) {
             item = PyList_GetItem(surf->locklist, len);
-            ref = PyWeakref_GetObject(item);
-            if (ref == Py_None) {
+            alive = PyWeakref_GetRef(item, &ref);
+            if (alive < 0) {
+                noerror = 0;
+                continue;
+            }
+            Py_XDECREF(ref);
+            if (!alive) {
                 if (PySequence_DelItem(surf->locklist, len) == -1) {
                     noerror = 0;
--- a/test/rwobject_test.py
+++ b/test/rwobject_test.py
@@ -83,7 +83,10 @@
             before = getrefcount(bpath)
             bpath = encode_string(bpath)
-            self.assertEqual(getrefcount(bpath), before)
+            if sys.version_info >= (3, 15):
+                self.assertEqual(getrefcount(bpath), before + 1)
+            else:
+                self.assertEqual(getrefcount(bpath), before)
             bpath = encode_string(upath)
-            if sys.version_info >= (3, 14):
+            if sys.version_info[:2] == (3, 14):
                 self.assertEqual(getrefcount(bpath), before - 1)
             else:
