Py_LIMITED_API support in SwigPyObject_Check for -builtin

Simplify implementation for -builtin, which does not need a fallback to
use strcmp as PyType_IsSubtype() 'just works' even when using multiple
modules (I think perhaps because SwigPyObject_stype->clientdata->pytype is
common across modules due to the implementation in SwigPyObject_Type()).

In the non-builtin case SwigPyObject is not a base type and usage is
different and when multiple modules are being used, SwigPyObject_Type()
returns two implementations of SwigPyObject which is solved in a hacky
way by comparing the types as strings when the pointer comparison fails.

Add import_callback test - tests %import and %callback to exercise
all of SwigPyPacked_Check(). Python only - the main callback example is
not widely tested and needs work in most of the other languages.
This commit is contained in:
William S Fulton 2025-06-08 10:33:10 +01:00
parent 9ec4c6f1aa
commit f3672e2d6f
8 changed files with 72 additions and 5 deletions

View File

@ -0,0 +1,2 @@
import_callback_x
import_callback_y

View File

@ -0,0 +1,16 @@
class X {
int val;
public:
X(int val = 0) : val(val) {}
int xmethod(int a) {
return -a;
}
int call_xmethod(int a) {
return xmethod(a);
}
};
extern "C" int go_for_it_x(int a, X x, int (X::*pf)(int a)) {
return (x.*pf)(a) + 11;
}

View File

@ -0,0 +1,11 @@
%module import_callback_x
%{
#include "import_callback_x.h"
%}
%callback("%s_cb_ptr") X::xmethod;
%import "import_callback_y.i"
%include "import_callback_x.h"

View File

@ -0,0 +1,5 @@
#include "import_callback_x.h"
extern "C" int go_for_it_y(int a, X x, int (X::*pf)(int a)) {
return (x.*pf)(a) + 111;
}

View File

@ -0,0 +1,7 @@
%module import_callback_y
%{
#include "import_callback_y.h"
%}
%include "import_callback_y.h"

View File

@ -113,6 +113,7 @@ C_TEST_CASES += \
python_varargs_typemap \
MULTI_CPP_TEST_CASES += \
import_callback \
python_runtime_data \
include $(srcdir)/../common.mk
@ -183,6 +184,7 @@ clean:
rm -f imports_a.py imports_b.py mod_a.py mod_b.py multi_import_a.py
rm -f multi_import_b.py multi_import_d.py packageoption_a.py packageoption_b.py packageoption_c.py
rm -f template_typedef_cplx2.py python_runtime_data_builtin.py python_runtime_data_nobuiltin.py
rm -f import_callback_x.py import_callback_y.py
hugemod_runme = $(SCRIPTPREFIX)hugemod$(SCRIPTSUFFIX)

View File

@ -0,0 +1,20 @@
import import_callback_x
import import_callback_y
x = import_callback_x.X()
# Sanity check
aa = x.call_xmethod(5)
if aa != -5:
raise RuntimeError("Bad aa {}".format(aa))
# Callback within import_callback_x module
res = import_callback_x.go_for_it_x(3, x, import_callback_x.X.xmethod_cb_ptr) - 11
if res != x.call_xmethod(3):
raise RuntimeError("Bad res {}".format(res))
# Callback across modules
# This uses the fallback string comparison in SwigPyPacked_Check()
res = import_callback_y.go_for_it_y(3, x, import_callback_x.X.xmethod_cb_ptr) - 111
if res != x.call_xmethod(3):
raise RuntimeError("Bad res {}".format(res))

View File

@ -780,10 +780,10 @@ SwigPyObject_Check(PyObject *op) {
PyTypeObject *target_tp = SwigPyObject_Type();
PyTypeObject *op_type = Py_TYPE(op);
#ifdef SWIGPYTHON_BUILTIN
if (PyType_IsSubtype(op_type, target_tp))
return 1;
return (strcmp(op_type->tp_name, SWIG_RUNTIME_MODULE ".SwigPyObject") == 0);
/* Only builtin types have SwigPyObject as a base type */
return PyType_IsSubtype(op_type, target_tp);
#else
/* Check for an exact match to SwigPyObject */
# ifdef Py_LIMITED_API
int cmp;
PyObject *tp_name;
@ -791,6 +791,7 @@ SwigPyObject_Check(PyObject *op) {
if (op_type == target_tp)
return 1;
# ifdef Py_LIMITED_API
/* Fallback for multiple modules */
tp_name = PyObject_GetAttrString((PyObject *)op_type, "__name__");
if (!tp_name)
return 0;
@ -1184,14 +1185,17 @@ SwigPyPacked_Type(void) {
SWIGRUNTIMEINLINE int
SwigPyPacked_Check(PyObject *op) {
PyTypeObject *target_tp = SwigPyPacked_Type();
PyTypeObject *op_type = Py_TYPE(op);
/* Check for an exact match to SwigPyPacked */
#ifdef Py_LIMITED_API
int cmp;
PyObject *tp_name;
#endif
PyTypeObject* op_type = Py_TYPE(op);
if (op_type == SwigPyPacked_Type())
if (op_type == target_tp)
return 1;
#ifdef Py_LIMITED_API
/* Fallback for multiple modules */
tp_name = PyObject_GetAttrString((PyObject *)op_type, "__name__");
if (!tp_name)
return 0;