Bug#1145040: casadi: FTBFS against python 3.15rc2
Maximiliano Curia
maxy at debian.org
Wed Sep 9 21:30:37 BST 2026
user debian-python at lists.debian.org
usertags 1145040 python3.15
thanks
Hi!
While rebuilding the python related packages against the Python 3.15rc2
version I ran into this FTBFS [1].
I had to make a few changes to get the package back to a healthy state.
- I applied upstream's commit 28f0910 [2]: Python: stop relying on
SWIG's removed Py2 compat macros [ci:run_slow]. This is a different
patch than the one proposed by Rafael, more invasive but as it's
already applied upstream it makes sense to use that one.
- The package repository was missing the `debian/*.install` files, so I
added those
- And I had to also add a missing python3-numpy dependency.
I've applied these fixes in the sandbox [3] to verify that it builds
successfully, these should be applied in Debian to get the package back
to a healthy state.
Happy hacking,
[1]: https://debusine.debian.net/debian/r-python-python3.15/artifact/TODO/
[2]: https://github.com/casadi/casadi/commit/28f0910eedc41861e8544d4c7590b67f934dd57c
[3]: 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 /\/\ /\ >< `/
-------------- next part --------------
diff -Nru casadi-3.7.2+ds2/debian/casadi.install casadi-3.7.2+ds2/debian/casadi.install
--- casadi-3.7.2+ds2/debian/casadi.install 2025-07-30 13:35:32.000000000 +0200
+++ casadi-3.7.2+ds2/debian/casadi.install 2026-09-08 14:27:12.000000000 +0200
@@ -1 +1 @@
-usr/bin
+usr/bin/casadi-cli
diff -Nru casadi-3.7.2+ds2/debian/changelog casadi-3.7.2+ds2/debian/changelog
--- casadi-3.7.2+ds2/debian/changelog 2026-06-30 20:49:54.000000000 +0200
+++ casadi-3.7.2+ds2/debian/changelog 2026-09-08 14:27:12.000000000 +0200
@@ -1,3 +1,16 @@
+casadi (3.7.2+ds2-1.1) UNRELEASED; urgency=medium
+
+ [ Bastian Germann ]
+ * Non-maintainer upload.
+ * d/copyright: delete empty line
+
+ [ Maximiliano Curia ]
+ * Add upstream patch 0002-Python-stop-relying-on-SWIG-s-removed-Py2-compat-mac.patch
+ * commit missing install files
+ * Add numpy runtime dependency
+
+ -- Maximiliano Curia <maxy at debian.org> Tue, 08 Sep 2026 14:27:12 +0200
+
casadi (3.7.2+ds2-1) unstable; urgency=medium
* New upstream release.
diff -Nru casadi-3.7.2+ds2/debian/control casadi-3.7.2+ds2/debian/control
--- casadi-3.7.2+ds2/debian/control 2026-06-30 19:42:29.000000000 +0200
+++ casadi-3.7.2+ds2/debian/control 2026-09-08 14:27:12.000000000 +0200
@@ -74,6 +74,7 @@
Architecture: any
Depends:
python3,
+ python3-numpy,
${misc:Depends},
${python3:Depends},
${shlibs:Depends},
diff -Nru casadi-3.7.2+ds2/debian/copyright casadi-3.7.2+ds2/debian/copyright
--- casadi-3.7.2+ds2/debian/copyright 2026-06-30 19:42:26.000000000 +0200
+++ casadi-3.7.2+ds2/debian/copyright 2026-09-08 14:27:12.000000000 +0200
@@ -173,7 +173,6 @@
modification, are permitted provided that the following conditions
are met:
.
-
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
.
diff -Nru casadi-3.7.2+ds2/debian/libcasadi3.7.install casadi-3.7.2+ds2/debian/libcasadi3.7.install
--- casadi-3.7.2+ds2/debian/libcasadi3.7.install 2025-07-30 14:26:40.000000000 +0200
+++ casadi-3.7.2+ds2/debian/libcasadi3.7.install 2026-09-08 14:27:12.000000000 +0200
@@ -1 +1 @@
-usr/lib/${DEB_HOST_MULTIARCH}/*.so.*
+usr/lib/*/libcasadi*.so.*
diff -Nru casadi-3.7.2+ds2/debian/libcasadi-dev.install casadi-3.7.2+ds2/debian/libcasadi-dev.install
--- casadi-3.7.2+ds2/debian/libcasadi-dev.install 2025-06-24 16:25:41.000000000 +0200
+++ casadi-3.7.2+ds2/debian/libcasadi-dev.install 2026-09-08 14:27:12.000000000 +0200
@@ -1,4 +1,4 @@
-usr/include
-usr/lib/${DEB_HOST_MULTIARCH}/cmake
-usr/lib/${DEB_HOST_MULTIARCH}/pkgconfig
-usr/lib/*/lib*.so
+usr/include/casadi
+usr/lib/*/cmake/casadi
+usr/lib/*/libcasadi*.so
+usr/lib/*/pkgconfig/casadi.pc
diff -Nru casadi-3.7.2+ds2/debian/patches/0002-Python-stop-relying-on-SWIG-s-removed-Py2-compat-mac.patch casadi-3.7.2+ds2/debian/patches/0002-Python-stop-relying-on-SWIG-s-removed-Py2-compat-mac.patch
--- casadi-3.7.2+ds2/debian/patches/0002-Python-stop-relying-on-SWIG-s-removed-Py2-compat-mac.patch 1970-01-01 01:00:00.000000000 +0100
+++ casadi-3.7.2+ds2/debian/patches/0002-Python-stop-relying-on-SWIG-s-removed-Py2-compat-mac.patch 2026-09-08 14:27:12.000000000 +0200
@@ -0,0 +1,239 @@
+From: Joris Gillis <joris.gillis42 at gmail.com>
+Date: Thu, 20 Aug 2026 21:01:08 +0200
+Subject: Python: stop relying on SWIG's removed Py2 compat macros [ci:run_slow]
+
+SWIG 4.5.0 removed the Python 2 compatibility macros that pyhead.swg used
+to inject into every generated wrapper, so casadi.i no longer compiles
+with a current SWIG. Substitute the macro bodies as SWIG documents them:
+PyString_FromString -> PyUnicode_FromString, PyInt_Check -> PyLong_Check,
+PyInt_AsLong -> PyLong_AsLong.
+
+PyString_Check expanded to PyBytes_Check, not PyUnicode_Check, so its
+three call sites need care rather than a rename: keep excluding bytes
+from the "iterable of matrices" path (iterating b"ab" would silently
+yield [97, 98]), and drop bytes from to_ptr(std::string) and the
+std::map key check, where python_string_to_std_string fed a NULL from
+PyUnicode_AsUTF8* straight into a std::string -- casadi.SX.sym(b"x")
+segfaulted the interpreter.
+
+Harden python_string_to_std_string itself rather than leaving that a
+caller-side convention: it now decodes to "" instead of dereferencing
+NULL, for both a missing object and a failed decode. That closes a
+second reachable crash -- handle_director_exception calls it on
+PyObject_Str(pvalue), which returns NULL when the exception's __str__
+raises, and then Py_DECREF'd that NULL as well.
+
+Auditing the rest of the file for the same shape turned up two more:
+
+- the 1-D probe in to_ptr(std::vector) released its shape tuple only on
+ the reject path, so every 1-D numeric-like argument leaked a reference
+ per probe (measured: 3000 for 1000 conversions). It also fed a
+ possibly-NULL shape to PyTuple_Check.
+- PyObjectHasClassName had the same unchecked-getattr pattern twice over
+ plus two bare Py_DECREFs. It has no callers anywhere in the tree or in
+ the generated wrappers, so drop it rather than harden dead code.
+
+Covered by typemaps.py test_bytes_typemaps and test_shape_attr_no_refleak,
+and function.py test_Callback_unstringable_exception.
+
+Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
+Origin: upstream, https://github.com/casadi/casadi/commit/28f0910eedc41861e8544d4c7590b67f934dd57c
+Applied-Upstream: 3.8.0
+---
+ swig/casadi.i | 68 +++++++++++++++++++++++++++++---------------
+ swig/python/casadi_numpy.hpp | 4 ---
+ 2 files changed, 45 insertions(+), 27 deletions(-)
+
+diff --git a/swig/casadi.i b/swig/casadi.i
+index 7be64320c..d5b64090a 100644
+--- a/swig/casadi.i
++++ b/swig/casadi.i
+@@ -109,16 +109,20 @@
+ // SWIG_PYTHON_THREAD_END_BLOCK is not needed, destructor will release GIL
+ }
+
++ // Decodes to "" rather than dereferencing NULL when str_py is absent or not a str
+ std::string python_string_to_std_string(PyObject *str_py) {
++ if (!str_py) return "";
+ #if SWIG_VERSION < 0x040200
+- const char *str_char = SWIG_Python_str_AsChar(str_py);
+- std::string str(str_char);
+- SWIG_Python_str_DelForPy3(str_char);
++ const char *chars = SWIG_Python_str_AsChar(str_py);
++ std::string str(chars ? chars : "");
++ SWIG_Python_str_DelForPy3(chars);
+ #else
+ PyObject *bytes = NULL;
+- std::string str(SWIG_PyUnicode_AsUTF8AndSize(str_py, NULL, &bytes));
++ const char *chars = SWIG_PyUnicode_AsUTF8AndSize(str_py, NULL, &bytes);
++ std::string str(chars ? chars : "");
+ Py_XDECREF(bytes);
+ #endif
++ if (!chars) PyErr_Clear();
+ return str;
+ }
+
+@@ -139,7 +143,7 @@
+ PyErr_Fetch(&ptype, &pvalue, &ptraceback);
+ PyObject* msg_py = PyObject_Str(pvalue);
+ msg = python_string_to_std_string(msg_py);
+- Py_DECREF(msg_py);
++ Py_XDECREF(msg_py);
+ PyErr_Restore(ptype, pvalue, ptraceback);
+ PyErr_Print();
+ #ifndef CASADI_WITH_PYTHON_GIL_RELEASE
+@@ -684,7 +688,7 @@ namespace std {
+ GUESTOBJECT* full(const DM& m, bool simplify=false) {
+ #ifdef SWIGPYTHON
+ PyObject *p = from_ptr(&m);
+- PyObject *method_name = PyString_FromString("toarray");
++ PyObject *method_name = PyUnicode_FromString("toarray");
+ PyObject *cr = PyObject_CallMethodObjArgs(p, method_name, (simplify? Py_True: Py_False), 0);
+ Py_DECREF(method_name);
+ Py_DECREF(p);
+@@ -737,7 +741,7 @@ namespace std {
+ %#if PY_VERSION_HEX < 0x03070000
+ PyObject* module = PyImport_AddModule("casadi");
+ %#else
+- PyObject* c_name = PyString_FromString("casadi");
++ PyObject* c_name = PyUnicode_FromString("casadi");
+ PyObject* module = PyImport_GetModule(c_name);
+ Py_DECREF(c_name);
+ %#endif
+@@ -1273,15 +1277,18 @@ namespace std {
+ #ifdef SWIGPYTHON
+
+ // Some built-in types are iterable
+- if (PyDict_Check(p) || PyString_Check(p) || PySet_Check(p) || PyUnicode_Check(p)) return false;
++ // bytes is iterable but yields ints; walking it as a vector of numbers
++ // would silently turn b"ab" into [97, 98] instead of raising.
++ if (PyDict_Check(p) || PyBytes_Check(p) || PySet_Check(p) || PyUnicode_Check(p)) return false;
+
+ // Make sure shape is 1D, if defined.
++ if (PyErr_Occurred()) PyErr_Clear(); // Clear pending exception before type check
+ if (PyObject_HasAttrString(p, "shape")) {
+ PyObject * shape = PyObject_GetAttrString(p, "shape");
+- if(!PyTuple_Check(shape) || PyTuple_Size(shape)!=1) {
+- Py_DECREF(shape);
+- return false;
+- }
++ if (!shape) { PyErr_Clear(); return false; }
++ bool is_1d = PyTuple_Check(shape) && PyTuple_Size(shape)==1;
++ Py_DECREF(shape);
++ if (!is_1d) return false;
+ }
+
+ // Iterator to the sequence
+@@ -1554,7 +1561,9 @@ namespace std {
+ }
+
+ #ifdef SWIGPYTHON
+- if (PyString_Check(p) || PyUnicode_Check(p)) {
++ // str only: python_string_to_std_string goes through
++ // PyUnicode_AsUTF8*, which returns NULL for a bytes object.
++ if (PyUnicode_Check(p)) {
+ if (m) {
+ (*m)->clear();
+ (*m)->append(python_string_to_std_string(p));
+@@ -1589,7 +1598,7 @@ namespace std {
+
+ GUESTOBJECT* from_ptr(const std::string *a) {
+ #ifdef SWIGPYTHON
+- return PyString_FromString(a->c_str());
++ return PyUnicode_FromString(a->c_str());
+ #elif defined(SWIGMATLAB)
+ return mxCreateString(a->c_str());
+ #else
+@@ -1614,23 +1623,40 @@ namespace std {
+ #ifdef SWIGPYTHON
+
+ // Python casadi_int
+- if (PyInt_Check(p)) {
++ if (PyLong_Check(p)) {
+ if (m) {
+- (**m).start = PyInt_AsLong(p);
++ (**m).start = PyLong_AsLong(p);
+ (**m).stop = (**m).start+1;
+ if ((**m).stop==0) (**m).stop = std::numeric_limits<casadi_int>::max();
+ }
+ return true;
+ }
+- // Python slice
++ // Python slice - use Limited API compatible approach
+ if (PySlice_Check(p)) {
+- PySliceObject *r = (PySliceObject*)(p);
++ Py_ssize_t start, stop, step;
++%#if PY_VERSION_HEX >= 0x03060100
++ int res = PySlice_Unpack(p, &start, &stop, &step);
++%#else
++ // Python 2.7 and early Python 3.x use _PySlice_Unpack (private API)
++ int res = _PySlice_Unpack(p, &start, &stop, &step);
++%#endif
++ if (res < 0) {
++ return false; // TypeError already set by PySlice_Unpack
++ }
++
+ if (m) {
+- (**m).start = (r->start == Py_None || PyNumber_AsSsize_t(r->start, NULL) <= std::numeric_limits<int>::min())
+- ? std::numeric_limits<casadi_int>::min() : PyInt_AsLong(r->start);
+- (**m).stop = (r->stop ==Py_None || PyNumber_AsSsize_t(r->stop, NULL)>= std::numeric_limits<int>::max())
+- ? std::numeric_limits<casadi_int>::max() : PyInt_AsLong(r->stop);
+- if(r->step !=Py_None) (**m).step = PyInt_AsLong(r->step);
++ // Map sentinel values from PySlice_Unpack to CasADi's limits
++ // PySlice_Unpack returns PY_SSIZE_T_MIN or PY_SSIZE_T_MAX as sentinels
++ // depending on step direction, so check both extremes
++ (**m).start = (start == PY_SSIZE_T_MIN || start == PY_SSIZE_T_MAX)
++ ? std::numeric_limits<casadi_int>::min()
++ : static_cast<casadi_int>(start);
++ (**m).stop = (stop == PY_SSIZE_T_MAX || stop == PY_SSIZE_T_MIN)
++ ? std::numeric_limits<casadi_int>::max()
++ : static_cast<casadi_int>(stop);
++ if (step != 1) {
++ (**m).step = static_cast<casadi_int>(step);
++ }
+ }
+ return true;
+ }
+@@ -1655,7 +1681,7 @@ namespace std {
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next(p, &pos, &key, &value)) {
+- if (!(PyString_Check(key) || PyUnicode_Check(key))) return false;
++ if (!PyUnicode_Check(key)) return false;
+ if (m) {
+ M *v=&(**m)[python_string_to_std_string(key)], *v2=v;
+ if (!casadi::to_ptr(value, &v)) return false;
+@@ -1912,18 +1938,6 @@ namespace std {
+
+ %fragment("casadi_dmatrix", "header", fragment="casadi_aux") {
+ namespace casadi {
+-#ifdef SWIGPYTHON
+- /** Check PyObjects by class name */
+- bool PyObjectHasClassName(PyObject* p, const char * name) {
+- PyObject * classo = PyObject_GetAttrString( p, "__class__");
+- PyObject * classname = PyObject_GetAttrString( classo, "__name__");
+-
+- bool ret = python_string_to_std_string(classname) == name;
+- Py_DECREF(classo);Py_DECREF(classname);
+- return ret;
+- }
+-#endif // SWIGPYTHON
+-
+ bool to_ptr(GUESTOBJECT *p, DM** m) {
+ // Treat Null
+ if (is_null(p)) return false;
+diff --git a/swig/python/casadi_numpy.hpp b/swig/python/casadi_numpy.hpp
+index 2cdbd6e85..2f5ee9329 100644
+--- a/swig/python/casadi_numpy.hpp
++++ b/swig/python/casadi_numpy.hpp
+@@ -21,10 +21,6 @@
+ #define array_size(a,i) (PyArray_DIM(((PyArrayObject *)a),i))
+ #define array_data(a) (PyArray_DATA(((PyArrayObject *)a)))
+
+-#if PY_MAJOR_VERSION >= 3
+-#define PyInt_Type PyLong_Type
+-#endif
+-
+ /* Convert the given PyObject to a NumPy array with the given
+ * typecode. On success, return a valid PyArrayObject* with the
+ * correct type. On failure, the python error string will be set and
diff -Nru casadi-3.7.2+ds2/debian/patches/series casadi-3.7.2+ds2/debian/patches/series
--- casadi-3.7.2+ds2/debian/patches/series 2026-06-30 20:22:49.000000000 +0200
+++ casadi-3.7.2+ds2/debian/patches/series 2026-09-08 14:27:12.000000000 +0200
@@ -1 +1,2 @@
0001-Testsuite-force-python3.patch
+0002-Python-stop-relying-on-SWIG-s-removed-Py2-compat-mac.patch
diff -Nru casadi-3.7.2+ds2/debian/python3-casadi.install casadi-3.7.2+ds2/debian/python3-casadi.install
--- casadi-3.7.2+ds2/debian/python3-casadi.install 2025-06-22 17:00:23.000000000 +0200
+++ casadi-3.7.2+ds2/debian/python3-casadi.install 2026-09-08 14:27:12.000000000 +0200
@@ -1 +1 @@
-usr/lib/python3
+usr/lib/python3/dist-packages/casadi
More information about the debian-science-maintainers
mailing list