pygame: FTBFS against python 3.15rc1

Maximiliano Curia <[email protected]>
Newsgroups gmane.linux.debian.devel.python
Message-ID <[email protected]>
Package: src:pygame
Version: 2.6.1-5 
User: [email protected]
Usertags: python3.15
Tags: patch

Hi!

While rebuilding the python related packages against the Python 3.15rc1
version we found that pygame fails to build from source [1]. 

Sadly, the upstream repository seems to be abandoned. I found that the
Fedora team had already made a patch for fixing this issue [2] and
reported it upstream [3]. 

However, that patch was causing some tests to fail (and it seemed like a
legitimate failure, due to a bug in the patch), so I've adapted it to
make it work correctly.

While doing this, I also found the reason for one of the reference
counting errors that were being hard-fixed for 3.14+ inside of
py314-refcount-tests.patch, and added a patch that fixes the test to
make it behave correctly instead.

I applied these fixes in the sandbox [4] to be able to build the
packages that depend on pygame, please consider applying the patch to
support the upcoming 3.15 version.

Happy hacking,

[1]: https://debusine.debian.net/debian/r-python-python3.15/artifact/4315864/
[2]: https://src.fedoraproject.org/rpms/pygame/c/bac081363eaae1bbec28c1af8003c37abd52c372?branch=rawhide
[3]: https://github.com/pygame/pygame/issues/4716
[4]: https://debusine.debian.net/debian/r-python-python3.15/

-- 
"Can you imagine what I would do if I could do all I can?" -- Sun Tzu
Saludos /\/\ /\ >< `/
pygame_2.6.1-6.debdiff (text/plain, 9.6 KB)
diff -Nru pygame-2.6.1/debian/changelog pygame-2.6.1/debian/changelog
--- pygame-2.6.1/debian/changelog	2026-06-03 12:36:57.000000000 +0200
+++ pygame-2.6.1/debian/changelog	2026-08-23 16:28:50.000000000 +0200
@@ -1,3 +1,17 @@
+pygame (2.6.1-6) UNRELEASED; urgency=medium
+
+  * Team upload.
+  * Add updated fedora patch for python 3.15: pygame-2.6.1-python-3.15-fix.patch
+    - Fix unlocking when consumer is NULL
+  * Add py315-refcount-test-constant-folding.patch
+    - build the bytes object in rwobject_test.test_refcount() at run time,
+      python 3.14+ constant folds b"..."[1:].
+  * Update patch py314-refcount-tests.patch
+    - drop the rwobject_test.py part, superseded by the above
+    - add the "import sys" its freetype_test.py part needs.
+
+ -- Maximiliano Curia <[email protected]>  Sun, 23 Aug 2026 16:28:50 +0200
+
 pygame (2.6.1-5) unstable; urgency=medium
 
   * Team upload.
diff -Nru pygame-2.6.1/debian/patches/py314-refcount-tests.patch pygame-2.6.1/debian/patches/py314-refcount-tests.patch
--- pygame-2.6.1/debian/patches/py314-refcount-tests.patch	2026-06-03 12:36:57.000000000 +0200
+++ pygame-2.6.1/debian/patches/py314-refcount-tests.patch	2026-08-23 16:28:50.000000000 +0200
@@ -1,27 +1,24 @@
-From: =?utf-8?q?Ren=C3=A9_Dudfield?= <[email protected]>
-Date: Mon, 8 Dec 2025 19:34:06 +0000
-Subject: freetype_test: mask_test: rwobject_test: Change ref counts for py
- 3.14
-
-Are these correct? I don't know.
-
-But apparently the garbage collector changed and reference counts
-changed.
-
+Description: freetype_test: mask_test: rwobject_test: Change ref counts for py 3.14
+ .
+ Are these correct? I don't know.
+ .
+ But apparently the garbage collector changed and reference counts changed.
+Author: =?utf-8?q?Ren=C3=A9_Dudfield?= <[email protected]>
 Origin: https://github.com/pygame/pygame/pull/4599/commits/ac69c0be7e165d967123ffaf2075f81357a5a25e
 Bug-Debian: https://bugs.debian.org/1121999
-Last-Update: 2025-12-08
----
- test/freetype_test.py | 17 +++++++++--
- test/mask_test.py     | 84 ++++++++++++++++++++++++++++++++++++++++++---------
- test/rwobject_test.py |  6 +++-
- 3 files changed, 89 insertions(+), 18 deletions(-)
-
-diff --git a/test/freetype_test.py b/test/freetype_test.py
-index 25551d8..d00db8a 100644
---- a/test/freetype_test.py
-+++ b/test/freetype_test.py
-@@ -1609,16 +1609,27 @@ class FreeTypeFontTest(unittest.TestCase):
+Forwarded: https://github.com/pygame/pygame/pull/4599
+Last-Update: 2026-08-23
+Index: pygame/test/freetype_test.py
+===================================================================
+--- pygame.orig/test/freetype_test.py
++++ pygame/test/freetype_test.py
+@@ -1,4 +1,5 @@
+ import os
++import sys
+ 
+ if os.environ.get("SDL_VIDEODRIVER") == "dummy":
+     __tags__ = ("ignore", "subprocess_ignore")
+@@ -1609,16 +1610,27 @@ class FreeTypeFontTest(unittest.TestCase
          else:
              array = arrinter.Array(rect.size, "u", 1)
              o = font.render_raw(text)
@@ -52,10 +49,10 @@
              for i in range(len(o)):
                  self.assertEqual(getrefcount(o[i]), 2, "refcount fail for item %d" % i)
  
-diff --git a/test/mask_test.py b/test/mask_test.py
-index bd7daf5..3bd7062 100644
---- a/test/mask_test.py
-+++ b/test/mask_test.py
+Index: pygame/test/mask_test.py
+===================================================================
+--- pygame.orig/test/mask_test.py
++++ pygame/test/mask_test.py
 @@ -2579,7 +2579,11 @@ class MaskTypeTest(unittest.TestCase):
      @unittest.skipIf(IS_PYPY, "Segfaults on pypy")
      def test_to_surface(self):
@@ -238,26 +235,3 @@
          expected_flag = SRCALPHA
          expected_depth = 32
          expected_color = pygame.Color("black")  # Default unsetcolor.
-diff --git a/test/rwobject_test.py b/test/rwobject_test.py
-index 31723ae..3441b04 100644
---- a/test/rwobject_test.py
-+++ b/test/rwobject_test.py
-@@ -1,5 +1,6 @@
- import pathlib
- import unittest
-+import sys
- 
- from pygame import encode_string, encode_file_path
- 
-@@ -83,7 +84,10 @@ class RWopsEncodeStringTest(unittest.TestCase):
-             bpath = encode_string(bpath)
-             self.assertEqual(getrefcount(bpath), before)
-             bpath = encode_string(upath)
--            self.assertEqual(getrefcount(bpath), before)
-+            if sys.version_info >= (3, 14):
-+                self.assertEqual(getrefcount(bpath), before - 1)
-+            else:
-+                self.assertEqual(getrefcount(bpath), before)
- 
-     def test_smp(self):
-         utf_8 = b"a\xF0\x93\x82\xA7b"
diff -Nru pygame-2.6.1/debian/patches/py315-refcount-test-constant-folding.patch pygame-2.6.1/debian/patches/py315-refcount-test-constant-folding.patch
--- pygame-2.6.1/debian/patches/py315-refcount-test-constant-folding.patch	1970-01-01 01:00:00.000000000 +0100
+++ pygame-2.6.1/debian/patches/py315-refcount-test-constant-folding.patch	2026-08-23 16:28:50.000000000 +0200
@@ -0,0 +1,18 @@
+Description: rwobject_test: build the test bytes object at run time
+ This way the object has two references no matter the python version.
+Author: Maximiliano Curia <[email protected]>
+Forwarded: no
+Last-Update: 2026-08-23
+Index: pygame/test/rwobject_test.py
+===================================================================
+--- pygame.orig/test/rwobject_test.py
++++ pygame/test/rwobject_test.py
+@@ -77,7 +77,7 @@ class RWopsEncodeStringTest(unittest.Tes
+     else:
+ 
+         def test_refcount(self):
+-            bpath = b" This is a string that is not cached."[1:]
++            bpath = bytes(bytearray(b"This is a string that is not cached."))
+             upath = bpath.decode("ascii")
+             before = getrefcount(bpath)
+             bpath = encode_string(bpath)
diff -Nru pygame-2.6.1/debian/patches/pygame-2.6.1-python-3.15-fix.patch pygame-2.6.1/debian/patches/pygame-2.6.1-python-3.15-fix.patch
--- pygame-2.6.1/debian/patches/pygame-2.6.1-python-3.15-fix.patch	1970-01-01 01:00:00.000000000 +0100
+++ pygame-2.6.1/debian/patches/pygame-2.6.1-python-3.15-fix.patch	2026-08-23 16:28:50.000000000 +0200
@@ -0,0 +1,89 @@
+Description: Replace PyWeakref_GetObject() with PyWeakref_GetRef()
+Author: Maximiliano Curia <[email protected]>
+Origin: other, https://src.fedoraproject.org/rpms/pygame/blob/rawhide/f/pygame-2.6.1-python-3.15-fix.patch
+Forwarded: no
+Last-Update: 2026-08-23
+Index: pygame/src_c/surface.c
+===================================================================
+--- pygame.orig/src_c/surface.c
++++ pygame/src_c/surface.c
+@@ -1024,8 +1024,10 @@ surf_get_locks(PyObject *self, PyObject
+         return NULL;
+ 
+     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_INCREF(Py_None);
++            tmp = Py_None;
++        }
+         PyTuple_SetItem(tuple, i, tmp);
+     }
+     return tuple;
+@@ -3449,12 +3451,11 @@ _release_buffer(Py_buffer *view_p)
+     internal = (pg_bufferinternal *)view_p->internal;
+     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)) {
+-            PyErr_Clear();
+-        }
++    PyWeakref_GetRef(consumer_ref, &consumer);
++    if (!pgSurface_UnlockBy((pgSurfaceObject *)view_p->obj, consumer)) {
++        PyErr_Clear();
+     }
++    Py_XDECREF(consumer);
+     Py_DECREF(consumer_ref);
+     PyMem_Free(internal);
+     Py_DECREF(view_p->obj);
+Index: pygame/src_c/surflock.c
+===================================================================
+--- pygame.orig/src_c/surflock.c
++++ pygame/src_c/surflock.c
+@@ -120,27 +120,32 @@ pgSurface_UnlockBy(pgSurfaceObject *surf
+     int noerror = 1;
+ 
+     if (surf->locklist != NULL) {
++        int getref_stat;
+         PyObject *item, *ref;
+         Py_ssize_t len = PyList_Size(surf->locklist);
+         while (--len >= 0 && !found) {
+             item = PyList_GetItem(surf->locklist, len);
+-            ref = PyWeakref_GetObject(item);
++            getref_stat = PyWeakref_GetRef(item, &ref);
+             if (ref == lockobj) {
+                 if (PySequence_DelItem(surf->locklist, len) == -1) {
++                    Py_XDECREF(ref);
+                     return 0;
+                 }
+                 else {
+                     found = 1;
+                 }
+             }
++            if (getref_stat > 0) {
++                Py_DECREF(ref);
++            }
+         }
+ 
+         /* Clear dead references */
+         len = PyList_Size(surf->locklist);
+         while (--len >= 0) {
+             item = PyList_GetItem(surf->locklist, len);
+-            ref = PyWeakref_GetObject(item);
+-            if (ref == Py_None) {
++            getref_stat = PyWeakref_GetRef(item, &ref);
++            if (ref == NULL) {
+                 if (PySequence_DelItem(surf->locklist, len) == -1) {
+                     noerror = 0;
+                 }
+@@ -148,6 +153,9 @@ pgSurface_UnlockBy(pgSurfaceObject *surf
+                     found++;
+                 }
+             }
++            if (getref_stat > 0) {
++                Py_DECREF(ref);
++            }
+         }
+     }
+ 
diff -Nru pygame-2.6.1/debian/patches/series pygame-2.6.1/debian/patches/series
--- pygame-2.6.1/debian/patches/series	2026-06-03 12:36:57.000000000 +0200
+++ pygame-2.6.1/debian/patches/series	2026-08-23 16:28:50.000000000 +0200
@@ -7,3 +7,5 @@
 py314-refcount-tests.patch
 Use-SDL_HasSurfaceRLE-when-available.patch
 sdl2-compat-remove-rle-assertions.patch
+pygame-2.6.1-python-3.15-fix.patch
+py315-refcount-test-constant-folding.patch
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.