Merge branch 'master' into fix_1792

This commit is contained in:
Jim Easterbrook 2025-07-19 09:53:34 +01:00
commit 3a37288e06
17 changed files with 431 additions and 113 deletions

View File

@ -129,8 +129,13 @@ jobs:
VER: '3.13-dbg'
CSTD: gnu99
- SWIGLANG: python
VER: '3.13t' # no-gil testing
VER: '3.13'
CSTD: gnu99
SWIG_FEATURES: -nogil # Test that -nogil has no effect on non-free-threading Python
- SWIGLANG: python
VER: '3.13t'
CSTD: gnu99
SWIG_FEATURES: -nogil
- SWIGLANG: python
VER: '3.14'
PY_ABI_VER: '3.14'

View File

@ -7,6 +7,20 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.4.0 (in progress)
===========================
2025-07-18: crusaderky
[Python] #3215 Added -nogil flag to mark modules as free-threading compatible.
2025-07-18: jim-easterbrook
[Python] #3219 Python builtin heap types buffer support via the
"python:bf_getbuffer" and "python:bf_releasebuffer" features improvement.
Add workarounds/fixes for python-3.9 and earlier and ignore when using
the limited API for versions prior to python-3.11 instead of generating
code that won't compile.
2025-07-09: timfel
[Python] #3217 Fix undefined behaviour in SwigPyObject_richcompare and
SwigPyObject_compare.
2025-06-27: wsfulton
[Python] Raise an AttributeError instead of TypeError when there are internal
and unexpected coding errors around member variable wrappers.

View File

@ -213,6 +213,7 @@ SWIGGO_CGO Defined when using Go for cgo
SWIGGO_GCCGO Defined when using Go for gccgo
SWIGGO_INTGO_SIZE Size of the Go type int when using Go (32 or 64)
SWIGPYTHON_BUILTIN Defined when using Python with -builtin
SWIGPYTHON_NOGIL Defined when using Python with -nogil
SWIG_RUBY_AUTORENAME Defined when using Ruby with -autorename
</pre></div>

View File

@ -963,6 +963,7 @@ swig -python -help
<tr><td>-interface &lt;mod&gt;</td><td>Set low-level C/C++ module name to &lt;mod&gt; (default: module name prefixed by '_')</td></tr>
<tr><td>-keyword </td><td>Use keyword arguments</td></tr>
<tr><td>-nofastunpack </td><td>Use traditional UnpackTuple method to parse the argument functions</td></tr>
<tr><td>-nogil </td><td>Enable free-threading if supported by the Python interpreter</td></tr>
<tr><td>-noh </td><td>Don't generate the output header file</td></tr>
<tr><td>-noproxy </td><td>Don't generate proxy classes</td></tr>
<tr><td>-nortti </td><td>Disable the use of the native C++ RTTI with directors</td></tr>
@ -7641,10 +7642,12 @@ will not be able to run any other threads, even if the wrapped C/C++ code is wai
<a href="https://docs.python.org/3/howto/free-threading-python.html">Free threading Python</a>
disables the Global Interpreter Lock (GIL) for improved parallel execution or multi-threaded
execution.
Python free threading was first released with experimental support for Python 3.13 and requires a special build of Python where the GIL can be disabled.
SWIG supports free threading Python; just use a free threading build of Python.
In particular, the Python header files from the free threading build of Python must be used
when compiling the SWIG generated code.
Python free threading was first released with experimental support for Python 3.13 and requires a
special build of Python where the GIL can be disabled.
SWIG support for free threading Python is opt-in; you must pass the <tt>-nogil</tt> flag to swig to
indicate that the wrapped code is thread-safe. The flag has no effect on Python builds that don't
support free threading. In particular, the Python header files from the free threading build of
Python must be used when compiling the SWIG generated code.
This is needed for the C/C++ compiler to be aware of the <tt>Py_GIL_DISABLED</tt> Python macro,
which identifies the free threading build, and is used by the SWIG generated code.
Please see the <a href="https://py-free-threading.github.io/">Python free-threading guide</a>
@ -7659,6 +7662,14 @@ Users must also write thread-safe code and prevent unsafe shared mutation using
Python code or using C/C++ native locking.
</p>
<p>
Note that free-threading support is separate to the standard multithreading support described
in the previous section. For example, one can still declare that functions should or shouldn't
release the GIL, but that will only have an effect when and if the GIL is available in the
Python interpreter to begin with. If your wrapped code is thread-unsafe, you should use
an explicit locking system for it and should not rely on the GIL to protect it.
</p>
<p>
<b>Compatibility Note:</b> Support for free threading Python was added in SWIG-4.4.0.
</p>

View File

@ -1303,8 +1303,7 @@ else
PYTHON_SO = @PYTHON_SO@
endif
PYNOGIL = @PYNOGIL@
PYFLAGS = -Walways
PYFLAGS = -Werror
PYCODESTYLE = @PYCODESTYLE@
PYCODESTYLE_FLAGS = --ignore=E252,E30,E402,E501,E731,W291,W391
PYABI3AUDIT = @PYABI3AUDIT@
@ -1368,7 +1367,7 @@ python_run: $(PYSCRIPT)
ifneq (,$(PYCODESTYLE))
$(COMPILETOOL) $(PYCODESTYLE) $(PYCODESTYLE_FLAGS) $(PYSCRIPT)
endif
env PYTHONPATH=$$PWD $(RUNTOOL) $(PYTHON) $(PYNOGIL) $(PYFLAGS) $(PYSCRIPT) $(RUNPIPE)
env PYTHONPATH=$$PWD $(RUNTOOL) $(PYTHON) $(PYFLAGS) $(PYSCRIPT) $(RUNPIPE)
ifneq (,$(SRCDIR))
$(RUNME).py: $(SRCDIR)$(RUNME).py

View File

@ -0,0 +1,124 @@
%module pyabi311_bufferinterface
%begin %{
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030b0000
// Examples/tests-suite/python/Makefile should only run this test with Py_LIMITED_API >= 3.11
#error testcase misconfiguration - Py_buffer is not defined in Py_LIMITED_API < 3.11
#endif
%}
#ifdef SWIGPYTHON_BUILTIN
%feature("python:bf_getbuffer", functype="getbufferproc") ReadOnlyData "ReadOnlyData::getbuffer";
%feature("python:bf_releasebuffer", functype="releasebufferproc") ReadOnlyData "ReadOnlyData::releasebuffer";
%feature("python:bf_getbuffer", functype="getbufferproc") ReadWriteData "ReadWriteData::getbuffer";
%feature("python:bf_releasebuffer", functype="releasebufferproc") ReadWriteData "ReadWriteData::releasebuffer";
#endif
/* swig Python objects can only "expose" a Python buffer if the following
* conditions are met:
* 1/ SWIGPYTHON_BUILTIN is set
* and
* 2a/ Py_LIMITED_API is not set
* or
* 2b/ Py_LIMITED_API is 3.11 or greater
*
* or
*
* 1/ SWIGPYTHON_BUILTIN is not set
* and
* 2/ Python version is 3.12 or higher
*/
%inline %{
#ifdef SWIGPYTHON_BUILTIN
#ifdef Py_LIMITED_API
#if Py_LIMITED_API+0 >= 0x030b0000
bool buffers_supported() { return true; }
#else
bool buffers_supported() { return false; }
#endif
#else // Py_LIMITED_API
bool buffers_supported() { return true; }
#endif // Py_LIMITED_API
#else // SWIGPYTHON_BUILTIN
#if PY_VERSION_HEX >= 0x030c0000
bool buffers_supported() { return true; }
#else
bool buffers_supported() { return false; }
#endif
#endif // SWIGPYTHON_BUILTIN
%}
%inline %{
class BaseClassData {
private:
char data[1024];
public:
bool released; // public so runme.py can use it as a diagnostic
BaseClassData() {
released = true;
strcpy(data, "This string represents a large block of memory.");
};
#ifdef SWIGPYTHON_BUILTIN
static int getbuffer(PyObject *exporter, Py_buffer *view, int flags, bool readonly) {
#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030b0000
goto fail;
#endif
BaseClassData *self = 0;
if (!SWIG_IsOK(SWIG_ConvertPtr(exporter, (void**)&self, SWIGTYPE_p_BaseClassData, 0)))
goto fail;
self->released = false;
return PyBuffer_FillInfo(view, exporter, &self->data, sizeof(self->data), readonly ? 1 : 0, flags);
fail:
PyErr_SetNone(PyExc_BufferError);
view->obj = NULL;
return -1;
};
static void releasebuffer(PyObject *exporter, Py_buffer *view) {
BaseClassData *self = 0;
if (!SWIG_IsOK(SWIG_ConvertPtr(exporter, (void**)&self, SWIGTYPE_p_BaseClassData, 0)))
return;
self->released = true;
};
#else
PyObject* __buffer__(int flags, bool readonly) {
Py_buffer view;
if (PyBuffer_FillInfo(&view, NULL, data, sizeof(data), readonly ? 1 : 0, flags)) {
PyErr_SetNone(PyExc_BufferError);
return NULL;
}
released = false;
return PyMemoryView_FromBuffer(&view);
};
void __release_buffer__(PyObject* buffer) {
released = true;
};
#endif
};
class ReadOnlyData: public BaseClassData {
public:
#ifdef SWIGPYTHON_BUILTIN
static int getbuffer(PyObject *exporter, Py_buffer *view, int flags) {
return BaseClassData::getbuffer(exporter, view, flags, true);
};
#else
PyObject* __buffer__(int flags) {
return BaseClassData::__buffer__(flags, true);
};
#endif
};
class ReadWriteData: public BaseClassData {
public:
#ifdef SWIGPYTHON_BUILTIN
static int getbuffer(PyObject *exporter, Py_buffer *view, int flags) {
return BaseClassData::getbuffer(exporter, view, flags, false);
};
#else
PyObject* __buffer__(int flags) {
return BaseClassData::__buffer__(flags, false);
};
#endif
};
%}

View File

@ -9,8 +9,7 @@ else
endif
LANGUAGE = python
PYNOGIL = @PYNOGIL@
PYFLAGS = -Walways
PYFLAGS = -Werror
SCRIPTSUFFIX = _runme.py
PYCODESTYLE = @PYCODESTYLE@
PYCODESTYLE_FLAGS = --ignore=E252,E30,E402,E501,E731,E741,W291,W391
@ -81,6 +80,7 @@ CPP_TEST_CASES += \
python_strict_unicode \
python_threads \
python_typemap_macro \
python_various \
simutry \
std_containers \
swigobject \
@ -88,6 +88,19 @@ CPP_TEST_CASES += \
# director_profile
ifeq (,$(PY_ABI_VER))
PY_ABI_311:=1
else
PY_ABI_VER_INTEGER:=$(shell printf "%d%02d" $(subst ., ,$(PY_ABI_VER)))
PY_ABI_311:=$(shell test $(PY_ABI_VER_INTEGER) -ge 311 && echo "1")
endif
# Test requiring python-3.11 minimum if using Py_LIMITED_API
ifeq (1,$(PY_ABI_311))
CPP_TEST_CASES += pyabi311_bufferinterface
endif
CPP11_TEST_CASES = \
cpp11_hash_tables \
cpp11_shared_ptr_const \
@ -171,7 +184,7 @@ endif
# a file is found which has _runme.py appended after the testcase name.
run_testcase = \
if [ -f $(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) ]; then \
env LD_LIBRARY_PATH=.:$$LD_LIBRARY_PATH PYTHONPATH=.:$(srcdir):$$PYTHONPATH $(RUNTOOL) $(PYTHON) $(PYNOGIL) $(PYFLAGS) $(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX); \
env LD_LIBRARY_PATH=.:$$LD_LIBRARY_PATH PYTHONPATH=.:$(srcdir):$$PYTHONPATH $(RUNTOOL) $(PYTHON) $(PYFLAGS) $(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX); \
fi
# Clean: remove the generated .py file
@ -194,5 +207,5 @@ hugemod:
perl hugemod.pl $(hugemod_runme)
$(MAKE) hugemod_a.cpptest
$(MAKE) hugemod_b.cpptest
time $(PYTHON) $(PYNOGIL) $(PYFLAGS) $(hugemod_runme)
time $(PYTHON) $(PYNOGIL) $(PYFLAGS) $(hugemod_runme)
time $(PYTHON) $(PYFLAGS) $(hugemod_runme)
time $(PYTHON) $(PYFLAGS) $(hugemod_runme)

View File

@ -0,0 +1,40 @@
import sys
# don't bother with Python 2
if sys.version_info < (3,0):
exit(0)
import pyabi311_bufferinterface
def check(what, expected, actual):
if expected != actual:
raise RuntimeError(
"Failed: ", what, " Expected: ", expected, " Actual: ", actual)
# test not relevant unless certain conditions are met (builtin, limited API)
if not pyabi311_bufferinterface.buffers_supported():
exit(0)
data = pyabi311_bufferinterface.ReadOnlyData()
view = memoryview(data)
check('readonly', view.readonly, True)
check('read data', view[:10], b"This string represents"[:10])
check('not released', data.released, False)
view.release()
check('released', data.released, True)
data = pyabi311_bufferinterface.ReadWriteData()
view = memoryview(data)
check('readonly', view.readonly, False)
text = b'Lorem ipsum dolor sit amet'
view[:len(text)] = b'Lorem ipsum dolor sit amet'
check('read data', view[:len(text)], text)
check('not released', data.released, False)
view.release()
check('released', data.released, True)
view = memoryview(data)
check('written data', view[:len(text)], text)
check('not released', data.released, False)
del view
check('released', data.released, True)

View File

@ -83,6 +83,10 @@ if (a1 == 42) :
if not (a1 != 42) :
raise RuntimeError("Comparing class (with overloaded operator ==) to incompatible type, != returned False")
if sys.version_info[0] >= 3:
if a1.__eq__(None) is not NotImplemented:
raise RuntimeError("Comparing to incompatible type should return NotImplemented")
# Check inequalities
#-------------------------------------------------------------------------

View File

@ -0,0 +1,26 @@
from python_various import *
import datetime
import sys
class MyClass:
pass
# Only implemented for python 3
if sys.version_info[0:2] < (3, 0):
exit(0)
type = GetFullyQualifiedName(1234) # Test builtins type
if type != "int":
raise RuntimeError("wrong type {}".format(type))
type = GetFullyQualifiedName(MyClass()) # Test __main__ type
if type != "MyClass":
raise RuntimeError("wrong type {}".format(type))
type = GetFullyQualifiedName(WrappedClass()) # Test a SWIG wrapped class
if type != "python_various.WrappedClass":
raise RuntimeError("wrong type {}".format(type))
type = GetFullyQualifiedName(datetime.date(2020, 1, 20)) # Test a Python standard library class
if type != "datetime.date":
raise RuntimeError("wrong type {}".format(type))

View File

@ -0,0 +1,14 @@
%module python_various
%inline %{
struct WrappedClass {};
// A unit test for SWIG_PyType_GetFullyQualifiedName
PyObject *GetFullyQualifiedName(PyObject *type) {
#if PY_VERSION_HEX >= 0x03000000
return SWIG_PyType_GetFullyQualifiedName(Py_TYPE(type));
#else
return type;
#endif
}
%}

View File

@ -32,7 +32,13 @@ SWIG_PyNumber_AsPyHash(PyObject *obj) {
SWIGINTERN int
SwigPyBuiltin_BadInit(PyObject *self, PyObject *SWIGUNUSEDPARM(args), PyObject *SWIGUNUSEDPARM(kwds)) {
#if PY_VERSION_HEX >= 0x03000000
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(Py_TYPE(self));
PyErr_Format(PyExc_TypeError, "Cannot create new instances of type '%S'", tpname);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_TypeError, "Cannot create new instances of type '%s'", Py_TYPE(self)->tp_name);
#endif
return -1;
}
@ -40,7 +46,13 @@ SWIGINTERN void
SwigPyBuiltin_BadDealloc(PyObject *obj) {
SwigPyObject *sobj = (SwigPyObject *)obj;
if (sobj->own) {
#if PY_VERSION_HEX >= 0x03000000
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(Py_TYPE(obj));
PyErr_Format(PyExc_TypeError, "Swig detected a memory leak in type '%S': no callable destructor found.", tpname);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_TypeError, "Swig detected a memory leak in type '%s': no callable destructor found.", Py_TYPE(obj)->tp_name);
#endif
}
}
@ -87,12 +99,24 @@ SwigPyBuiltin_SetterClosure (PyObject *obj, PyObject *val, void *closure) {
return -1;
}
if (!val) {
#if PY_VERSION_HEX >= 0x03000000
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(Py_TYPE(obj));
PyErr_Format(PyExc_AttributeError, "Illegal member variable deletion in type '%S'", tpname);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_AttributeError, "Illegal member variable deletion in type '%s'", Py_TYPE(obj)->tp_name);
#endif
return -1;
}
getset = (SwigPyGetSet *)closure;
if (!getset->set) {
#if PY_VERSION_HEX >= 0x03000000
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(Py_TYPE(obj));
PyErr_Format(PyExc_AttributeError, "Illegal member variable assignment in type '%S'", tpname);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_AttributeError, "Illegal member variable assignment in type '%s'", Py_TYPE(obj)->tp_name);
#endif
return -1;
}
tuple = PyTuple_New(1);
@ -114,12 +138,24 @@ SwigPyBuiltin_FunpackSetterClosure (PyObject *obj, PyObject *val, void *closure)
return -1;
}
if (!val) {
#if PY_VERSION_HEX >= 0x03000000
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(Py_TYPE(obj));
PyErr_Format(PyExc_AttributeError, "Illegal member variable deletion in type '%S'", tpname);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_AttributeError, "Illegal member variable deletion in type '%s'", Py_TYPE(obj)->tp_name);
#endif
return -1;
}
getset = (SwigPyGetSet *)closure;
if (!getset->set) {
#if PY_VERSION_HEX >= 0x03000000
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(Py_TYPE(obj));
PyErr_Format(PyExc_AttributeError, "Illegal member variable assignment in type '%S'", tpname);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_AttributeError, "Illegal member variable assignment in type '%s'", Py_TYPE(obj)->tp_name);
#endif
return -1;
}
result = (*getset->set)(obj, val);
@ -138,7 +174,6 @@ SwigPyStaticVar_dealloc(PyDescrObject *descr) {
SWIGINTERN PyObject *
SwigPyStaticVar_repr(PyGetSetDescrObject *descr) {
#if PY_VERSION_HEX >= 0x03000000
return PyUnicode_FromFormat("<class attribute '%S' of type '%s'>", PyDescr_NAME(descr), PyDescr_TYPE(descr)->tp_name);
#else
return PyString_FromFormat("<class attribute '%s' of type '%s'>", PyString_AsString(PyDescr_NAME(descr)), PyDescr_TYPE(descr)->tp_name);
@ -181,24 +216,29 @@ SWIGINTERN int
SwigPyObjectType_setattro(PyObject *typeobject, PyObject *name, PyObject *value) {
PyObject *attribute;
PyTypeObject *type;
descrsetfunc local_set;
assert(PyType_Check(typeobject));
type = (PyTypeObject *)typeobject;
attribute = _PyType_Lookup(type, name);
if (attribute != NULL) {
/* Implement descriptor functionality, if any */
local_set = Py_TYPE(attribute)->tp_descr_set;
if (local_set != NULL)
return local_set(attribute, (PyObject *)type, value);
descrsetfunc local_set = Py_TYPE(attribute)->tp_descr_set;
if (local_set == NULL) {
#if PY_VERSION_HEX >= 0x03000000
PyErr_Format(PyExc_AttributeError, "cannot modify read-only attribute '%s.%S'", type->tp_name, name);
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(type);
PyErr_Format(PyExc_AttributeError, "cannot modify read-only attribute '%S.%S'", tpname, name);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_AttributeError, "cannot modify read-only attribute '%s.%s'", type->tp_name, PyString_AS_STRING(name));
PyErr_Format(PyExc_AttributeError, "cannot modify read-only attribute '%s.%s'", type->tp_name, PyString_AS_STRING(name));
#endif
} else {
return local_set(attribute, (PyObject *)type, value);
}
} else {
#if PY_VERSION_HEX >= 0x03000000
PyErr_Format(PyExc_AttributeError, "type '%s' has no attribute '%S'", type->tp_name, name);
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(type);
PyErr_Format(PyExc_AttributeError, "type '%S' has no attribute '%S'", tpname, name);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_AttributeError, "type '%s' has no attribute '%s'", type->tp_name, PyString_AS_STRING(name));
#endif

View File

@ -66,7 +66,7 @@ SWIG_PyUnicode_AsUTF8AndSize(PyObject *str, Py_ssize_t *psize, PyObject **pbytes
#endif
}
SWIGINTERN PyObject*
SWIGINTERN PyObject *
SWIG_Python_str_FromChar(const char *c)
{
#if PY_VERSION_HEX >= 0x03000000
@ -132,3 +132,31 @@ SWIG_Python_str_FromChar(const char *c)
# define SWIG_Py_DECREF Py_DECREF
# define SWIG_Py_XDECREF Py_XDECREF
#endif
#if PY_VERSION_HEX >= 0x03000000
SWIGINTERN PyObject *
SWIG_PyType_GetFullyQualifiedName(PyTypeObject *type) {
#if PY_VERSION_HEX >= 0x030d0000
return PyType_GetFullyQualifiedName(type);
#else
PyObject *result = NULL;
PyObject *qualname = PyObject_GetAttrString((PyObject *)type, "__qualname__");
if (qualname) {
PyObject *mod = PyObject_GetAttrString((PyObject *)type, "__module__");
if (mod) {
if (PyUnicode_Check(mod) && PyUnicode_CompareWithASCIIString(mod, "builtins") && PyUnicode_CompareWithASCIIString(mod, "__main__")) {
result = PyUnicode_FromFormat("%U%c%U", mod, '.', qualname);
SWIG_Py_DECREF(qualname);
} else {
result = qualname;
}
SWIG_Py_DECREF(mod);
} else {
result = qualname;
}
}
return result;
#endif
}
#endif

View File

@ -206,6 +206,11 @@ SWIG_init(void) {
#if PY_VERSION_HEX >= 0x03000000
static PyModuleDef_Slot SwigSlots[] = {
{ Py_mod_exec, (void *)SWIG_mod_exec },
#ifdef SWIGPYTHON_NOGIL
#ifdef Py_GIL_DISABLED
{ Py_mod_gil, Py_MOD_GIL_NOT_USED },
#endif
#endif
{ 0, NULL }
};

View File

@ -723,26 +723,33 @@ SwigPyObject_repr2(PyObject *v, PyObject *SWIGUNUSEDPARM(args))
}
SWIGRUNTIME int
SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w)
SwigPyObject_compare(PyObject *v, PyObject *w)
{
void *i = v->ptr;
void *j = w->ptr;
/* tp_compare is only called when both objects have the same type, so
* the casts are guaranteed to be ok. */
void *i = ((SwigPyObject *)v)->ptr;
void *j = ((SwigPyObject *)w)->ptr;
return (i < j) ? -1 : ((i > j) ? 1 : 0);
}
SWIGRUNTIMEINLINE int SwigPyObject_Check(PyObject *);
/* Added for Python 3.x, would it also be useful for Python 2.x? */
SWIGRUNTIME PyObject*
SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op)
SwigPyObject_richcompare(PyObject *v, PyObject *w, int op)
{
PyObject* res = NULL;
if (!PyErr_Occurred()) {
if (op != Py_EQ && op != Py_NE) {
/* Per https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_richcompare
* the first argument is guaranteed to be an instance of SwigPyObject, but the
* second is not, so we typecheck that one. */
if ((op != Py_EQ && op != Py_NE) || !SwigPyObject_Check(w)) {
SWIG_Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0);
}
return res;
return res;
}
@ -776,23 +783,22 @@ SwigPyObject_Check(PyObject *op) {
return PyType_IsSubtype(op_type, target_tp);
#else
/* Check for an exact match to SwigPyObject */
# ifdef Py_LIMITED_API
int cmp;
PyObject *tp_name;
#endif
if (op_type == target_tp)
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;
cmp = PyUnicode_CompareWithASCIIString(tp_name, "SwigPyObject");
SWIG_Py_DECREF(tp_name);
return cmp == 0;
# else
return (strcmp(op_type->tp_name, SWIG_RUNTIME_MODULE ".SwigPyObject") == 0);
# endif
} else {
/* Fallback for multiple modules */
#if PY_VERSION_HEX >= 0x03000000
int cmp;
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(op_type);
if (!tpname)
return 0;
cmp = PyUnicode_CompareWithASCIIString(tpname, SWIG_RUNTIME_MODULE ".SwigPyObject");
SWIG_Py_DECREF(tpname);
return cmp == 0;
#else
return strcmp(op_type->tp_name, SWIG_RUNTIME_MODULE ".SwigPyObject") == 0;
#endif
}
#endif
}
@ -1100,7 +1106,7 @@ SwigPyObject_TypeOnce(void) {
};
PyObject *pytype = PyType_FromSpec(&spec);
PyObject *runtime_data_module = SWIG_runtime_data_module();
# if !defined(Py_LIMITED_API)
#if !defined(Py_LIMITED_API)
/* While this __dictoffset__ is only used with the builtin wrappers, SwigPyObject ought to be
identical when created for use by proxy class wrappers in case it is shared across multiple modules. */
#if PY_VERSION_HEX < 0x03090000
@ -1193,23 +1199,22 @@ 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
if (op_type == target_tp)
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;
cmp = PyUnicode_CompareWithASCIIString(tp_name, "SwigPyPacked");
SWIG_Py_DECREF(tp_name);
return cmp == 0;
} else {
/* Fallback for multiple modules */
#if PY_VERSION_HEX >= 0x03000000
int cmp;
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(op_type);
if (!tpname)
return 0;
cmp = PyUnicode_CompareWithASCIIString(tpname, SWIG_RUNTIME_MODULE ".SwigPyPacked");
SWIG_Py_DECREF(tpname);
return cmp == 0;
#else
return (strcmp(op_type->tp_name, SWIG_RUNTIME_MODULE ".SwigPyPacked") == 0);
return strcmp(op_type->tp_name, SWIG_RUNTIME_MODULE ".SwigPyPacked") == 0;
#endif
}
}
SWIGRUNTIME void
@ -2009,49 +2014,6 @@ SwigPyObject_GetDesc(PyObject *self)
return ty ? ty->str : "";
}
SWIGRUNTIME void
SWIG_Python_TypeError(const char *type, PyObject *obj)
{
(void) obj;
if (type) {
#if defined(SWIG_COBJECT_TYPES)
if (obj && SwigPyObject_Check(obj)) {
const char *otype = (const char *) SwigPyObject_GetDesc(obj);
if (otype) {
PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received",
type, otype);
return;
}
} else
#endif
{
#ifndef Py_LIMITED_API
/* tp_name is not accessible */
const char *otype = (obj ? Py_TYPE(obj)->tp_name : 0);
if (otype) {
PyObject *str = PyObject_Str(obj);
PyObject *bytes = NULL;
const char *cstr = str ? SWIG_PyUnicode_AsUTF8AndSize(str, NULL, &bytes) : 0;
if (cstr) {
PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received",
type, otype, cstr);
} else {
PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received",
type, otype);
}
SWIG_Py_XDECREF(bytes);
SWIG_Py_XDECREF(str);
return;
}
#endif
}
PyErr_Format(PyExc_TypeError, "a '%s' is expected", type);
} else {
PyErr_Format(PyExc_TypeError, "unexpected type is received");
}
}
/* Convert a pointer value, signal an exception on a type mismatch */
SWIGRUNTIME void *
SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) {
@ -2081,7 +2043,13 @@ SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) {
if (!PyString_Check(name))
# endif
{
#if PY_VERSION_HEX >= 0x03000000
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(Py_TYPE(name));
PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%S'", tpname);
SWIG_Py_DECREF(tpname);
#else
PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%s'", Py_TYPE(name)->tp_name);
#endif
return -1;
} else {
SWIG_Py_INCREF(name);
@ -2105,7 +2073,15 @@ SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) {
if (!encoded_name)
goto done;
}
#if PY_VERSION_HEX >= 0x03000000
{
PyObject *tpname = SWIG_PyType_GetFullyQualifiedName(tp);
PyErr_Format(PyExc_AttributeError, "'%S' object has no attribute '%s'", tpname, PyString_AsString(encoded_name));
SWIG_Py_DECREF(tpname);
}
#else
PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", tp->tp_name, PyString_AsString(encoded_name));
#endif
SWIG_Py_DECREF(encoded_name);
} else {
res = f(descr, obj, value);

View File

@ -92,6 +92,7 @@ static int extranative = 0;
static int nortti = 0;
static int relativeimport = 0;
static int flat_static_method = 0;
static int nogil = 0;
/* flags for the make_autodoc function */
namespace {
@ -123,6 +124,7 @@ Python Options (available with -python)\n\
-keyword - Use keyword arguments\n";
static const char *usage2 = "\
-nofastunpack - Use traditional UnpackTuple method to parse the argument functions\n\
-nogil - Enable free-threading if supported by the Python interpreter\n\
-noh - Don't generate the output header file\n";
static const char *usage3 = "\
-noproxy - Don't generate proxy classes\n\
@ -414,6 +416,10 @@ public:
builtin = 1;
Preprocessor_define("SWIGPYTHON_BUILTIN", 0);
Swig_mark_arg(i);
} else if (strcmp(argv[i], "-nogil") == 0) {
nogil = 1;
Preprocessor_define("SWIGPYTHON_NOGIL", 0);
Swig_mark_arg(i);
} else if (strcmp(argv[i], "-relativeimport") == 0) {
relativeimport = 1;
Swig_mark_arg(i);
@ -644,6 +650,10 @@ public:
Printf(f_runtime, "#define SWIGPYTHON_FASTPROXY\n");
}
if (nogil) {
Printf(f_runtime, "#define SWIGPYTHON_NOGIL\n");
}
Printf(f_runtime, "\n");
Printf(f_header, "#ifdef SWIG_TypeQuery\n");
@ -4096,7 +4106,7 @@ public:
Delete(richcompare_list);
Printv(f, " if (!result && !PyErr_Occurred()) {\n", NIL);
Printv(f, " if (SwigPyObject_Check(self) && SwigPyObject_Check(other)) {\n", NIL);
Printv(f, " result = SwigPyObject_richcompare((SwigPyObject *)self, (SwigPyObject *)other, op);\n", NIL);
Printv(f, " result = SwigPyObject_richcompare(self, other, op);\n", NIL);
Printv(f, " } else {\n", NIL);
Printv(f, " result = Py_NotImplemented;\n", NIL);
Printv(f, " SWIG_Py_INCREF(result);\n", NIL);
@ -4522,8 +4532,14 @@ public:
printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_inplace_repeat"), "sq_inplace_repeat", "ssizeargfunc");
// buffer slots
printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:bf_getbuffer"), "bf_getbuffer", "getbufferproc");
printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:bf_releasebuffer"), "bf_releasebuffer", "releasebufferproc");
String *bf_getbuffer = Getattr(n, "feature:python:bf_getbuffer");
String *bf_releasebuffer = Getattr(n, "feature:python:bf_releasebuffer");
if (bf_getbuffer || bf_releasebuffer) {
Printv(f, "#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x03090000 || Py_LIMITED_API+0 >= 0x030b0000\n", NIL);
printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:bf_getbuffer"), "bf_getbuffer", "getbufferproc");
printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:bf_releasebuffer"), "bf_releasebuffer", "releasebufferproc");
Printv(f, "#endif\n", NIL);
}
Printf(f, " { 0, NULL }\n");
Printf(f, " };\n");
@ -4536,6 +4552,16 @@ public:
Printf(f, " };\n");
Printv(f, " PyObject *tuple_bases = SwigPyBuiltin_InitBases(bases);\n", NIL);
Printf(f, " PyTypeObject *pytype = (PyTypeObject *)PyType_FromSpecWithBases(&spec, tuple_bases);\n");
if (bf_getbuffer || bf_releasebuffer) {
Printv(f, "#if !defined(Py_LIMITED_API) && PY_VERSION_HEX < 0x03090000\n", NIL);
Printf(f, " if (pytype) {\n");
if (bf_getbuffer)
Printf(f, " pytype->tp_as_buffer->bf_getbuffer = %s;\n", getSlot(n, "feature:python:bf_getbuffer"));
if (bf_releasebuffer)
Printf(f, " pytype->tp_as_buffer->bf_releasebuffer = %s;\n", getSlot(n, "feature:python:bf_releasebuffer"));
Printf(f, " }\n");
Printv(f, "#endif\n", NIL);
}
Printf(f, " if (pytype) {\n");
Printf(f, " if (PyDict_Merge(pytype->tp_dict, dict, 1) == 0) {\n");
Printv(f, " SwigPyBuiltin_SetMetaType(pytype, type);\n", NIL);

View File

@ -2050,13 +2050,6 @@ else
if test -z "$PYVER"; then
PYVER=0
fi
AC_MSG_CHECKING([if $PYTHON3 GIL can be disabled])
PYGIL=`($PYTHON3 -c "import sysconfig; print('yes' if bool(sysconfig.get_config_var('Py_GIL_DISABLED')) else 'no')") 2>/dev/null`
AC_MSG_RESULT($PYGIL)
if test x"$PYGIL" = x"yes"; then
PYNOGIL="-X gil=0"
fi
fi
if test $PYVER -ge 3; then
@ -2185,7 +2178,6 @@ else
*)PYTHON3DYNAMICLINKING="";;
esac
AC_SUBST(PYNOGIL)
AC_SUBST(PY3INCLUDE)
AC_SUBST(PY3LIB)
AC_SUBST(PY3LINK)