Merge branch 'py3-stable-abi'

* py3-stable-abi:
  Add CI build using -py3-stable-abi option
  Make Python buffer typemaps compatible with limited API
  Don't use PyUnicode_AsUTF8() when python limited API is used
  Make directors implementation for Python work with limited API
  Support using stable Python ABI

Conflicts:
	.github/workflows/ci.yml
	Lib/python/pyhead.swg
	Lib/python/pyrun.swg
	Source/Modules/python.cxx
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index edd8c4d..deebbad 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -145,6 +145,8 @@
           VER: '3.12'
           CSTD: gnu99
         - SWIGLANG: python
+          SWIG_FEATURES: -py3-stable-abi
+        - SWIGLANG: python
           PY2: 2
           SWIG_FEATURES: -builtin
         - SWIGLANG: python
diff --git a/Lib/python/director.swg b/Lib/python/director.swg
index f6beafa..a1dd00f 100644
--- a/Lib/python/director.swg
+++ b/Lib/python/director.swg
@@ -14,6 +14,24 @@
 #include <vector>
 #include <map>
 
+#if defined(SWIG_PYTHON_THREADS)
+/*  __THREAD__ is the old macro to activate some thread support */
+# if !defined(__THREAD__)
+#   define __THREAD__ 1
+# endif
+#endif
+
+#ifdef __THREAD__
+#ifndef Py_LIMITED_API
+# include "pythread.h"
+#else
+# if defined(_WIN32)
+#   include <windows.h>
+# else
+#   include <pthread.h>
+# endif
+#endif
+#endif
 
 /*
   Use -DSWIG_PYTHON_DIRECTOR_NO_VTABLE if you don't want to generate a 'virtual
@@ -244,25 +262,89 @@
   };
 
 
-#if defined(SWIG_PYTHON_THREADS)
-/*  __THREAD__ is the old macro to activate some thread support */
-# if !defined(__THREAD__)
-#   define __THREAD__ 1
-# endif
-#endif
-
 #ifdef __THREAD__
-# include "pythread.h"
+#ifndef Py_LIMITED_API
+   class Mutex
+   {
+   public:
+       Mutex() {
+           mutex_ = PyThread_allocate_lock();
+       }
+
+       ~Mutex() {
+           PyThread_release_lock(mutex_);
+       }
+
+   private:
+       void Lock() {
+           PyThread_acquire_lock(mutex_, WAIT_LOCK);
+       }
+
+       void Unlock() {
+           PyThread_free_lock(mutex_);
+       }
+
+       PyThread_type_lock mutex_;
+
+       friend class Guard;
+   };
+#elif defined(_WIN32)
+    class Mutex : private CRITICAL_SECTION {
+    public:
+        Mutex() {
+            InitializeCriticalSection(this);
+        }
+
+        ~Mutex() {
+            DeleteCriticalSection(this);
+        }
+
+    private:
+        void Lock() {
+            EnterCriticalSection(this);
+        }
+
+        void Unlock() {
+            LeaveCriticalSection(this);
+        }
+
+        friend class Guard;
+    };
+#else
+    class Mutex {
+    public:
+        Mutex() {
+            pthread_mutex_init(&mutex_, NULL);
+        }
+
+        ~Mutex() {
+            pthread_mutex_destroy(&mutex_);
+        }
+
+    private:
+        void Lock() {
+            pthread_mutex_lock(&mutex_);
+        }
+
+        void Unlock() {
+            pthread_mutex_unlock(&mutex_);
+        }
+
+        friend class Guard;
+
+        pthread_mutex_t mutex_;
+    };
+#endif
   class Guard {
-    PyThread_type_lock &mutex_;
+    Mutex &mutex_;
 
   public:
-    Guard(PyThread_type_lock & mutex) : mutex_(mutex) {
-      PyThread_acquire_lock(mutex_, WAIT_LOCK);
+    Guard(Mutex & mutex) : mutex_(mutex) {
+      mutex_.Lock();
     }
 
     ~Guard() {
-      PyThread_release_lock(mutex_);
+      mutex_.Unlock();
     }
   };
 # define SWIG_GUARD(mutex) Guard _guard(mutex)
@@ -330,7 +412,7 @@
     typedef std::map<void *, GCItem_var> swig_ownership_map;
     mutable swig_ownership_map swig_owner;
 #ifdef __THREAD__
-    static PyThread_type_lock swig_mutex_own;
+    static Mutex swig_mutex_own;
 #endif
 
   public:
@@ -382,7 +464,7 @@
   };
 
 #ifdef __THREAD__
-  PyThread_type_lock Director::swig_mutex_own = PyThread_allocate_lock();
+  Mutex Director::swig_mutex_own;
 #endif
 }
 
diff --git a/Lib/python/pybuffer.i b/Lib/python/pybuffer.i
index 2fdaa6d..9ebc36a 100644
--- a/Lib/python/pybuffer.i
+++ b/Lib/python/pybuffer.i
@@ -12,18 +12,43 @@
  *      }
  */
 
+/* Note that in Py_LIMITED_API case we have no choice, but to use deprecated
+ * functions, as they provides the only way to access buffer data with limited
+ * API, which doesn't include Py_buffer definition. We also disable the
+ * warnings about doing this because they're not useful in our case.
+ */
+
 %define %pybuffer_mutable_binary(TYPEMAP, SIZE)
 %typemap(in) (TYPEMAP, SIZE) {
   int res; Py_ssize_t size = 0; void *buf = 0;
+%#ifndef Py_LIMITED_API
   Py_buffer view;
   res = PyObject_GetBuffer($input, &view, PyBUF_WRITABLE);
+%#else
+  %#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+    %#pragma GCC diagnostic push
+    %#pragma GCC diagnostic ignored "-Wdeprecated"
+    %#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+  %#elif defined(_MSC_VER)
+    %#pragma warning(push)
+    %#pragma warning(disable: 4996)
+  %#endif
+  res = PyObject_AsWriteBuffer($input, &buf, &size);
+  %#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+    %#pragma GCC diagnostic pop
+  %#elif defined(_MSC_VER)
+    %#pragma warning(pop)
+  %#endif
+%#endif
   if (res < 0) {
     PyErr_Clear();
     %argument_fail(res, "(TYPEMAP, SIZE)", $symname, $argnum);
   }
+%#ifndef Py_LIMITED_API
   size = view.len;
   buf = view.buf;
   PyBuffer_Release(&view);
+%#endif
   $1 = ($1_ltype) buf;
   $2 = ($2_ltype) (size/sizeof($*1_type));
 }
@@ -45,14 +70,34 @@
 %define %pybuffer_mutable_string(TYPEMAP)
 %typemap(in) (TYPEMAP) {
   int res; void *buf = 0;
+%#ifndef Py_LIMITED_API
   Py_buffer view;
   res = PyObject_GetBuffer($input, &view, PyBUF_WRITABLE);
+%#else
+  %#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+    %#pragma GCC diagnostic push
+    %#pragma GCC diagnostic ignored "-Wdeprecated"
+    %#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+  %#elif defined(_MSC_VER)
+    %#pragma warning(push)
+    %#pragma warning(disable: 4996)
+  %#endif
+  Py_ssize_t size;
+  res = PyObject_AsWriteBuffer($input, &buf, &size);
+  %#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+    %#pragma GCC diagnostic pop
+  %#elif defined(_MSC_VER)
+    %#pragma warning(pop)
+  %#endif
+%#endif
   if (res < 0) {
     PyErr_Clear();
     %argument_fail(res, "(TYPEMAP)", $symname, $argnum);
   }
+%#ifndef Py_LIMITED_API
   buf = view.buf;
   PyBuffer_Release(&view);
+%#endif
   $1 = ($1_ltype) buf;
 }
 %enddef
@@ -74,15 +119,34 @@
 %define %pybuffer_binary(TYPEMAP, SIZE)
 %typemap(in) (TYPEMAP, SIZE) {
   int res; Py_ssize_t size = 0; const void *buf = 0;
+%#ifndef Py_LIMITED_API
   Py_buffer view;
   res = PyObject_GetBuffer($input, &view, PyBUF_CONTIG_RO);
+%#else
+  %#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+    %#pragma GCC diagnostic push
+    %#pragma GCC diagnostic ignored "-Wdeprecated"
+    %#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+  %#elif defined(_MSC_VER)
+    %#pragma warning(push)
+    %#pragma warning(disable: 4996)
+  %#endif
+  res = PyObject_AsReadBuffer($input, &buf, &size);
+  %#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+    %#pragma GCC diagnostic pop
+  %#elif defined(_MSC_VER)
+    %#pragma warning(pop)
+  %#endif
+%#endif
   if (res < 0) {
     PyErr_Clear();
     %argument_fail(res, "(TYPEMAP, SIZE)", $symname, $argnum);
   }
+%#ifndef Py_LIMITED_API
   size = view.len;
   buf = view.buf;
   PyBuffer_Release(&view);
+%#endif
   $1 = ($1_ltype) buf;
   $2 = ($2_ltype) (size / sizeof($*1_type));
 }
@@ -106,14 +170,34 @@
 %define %pybuffer_string(TYPEMAP)
 %typemap(in) (TYPEMAP) {
   int res; const void *buf = 0;
+%#ifndef Py_LIMITED_API
   Py_buffer view;
   res = PyObject_GetBuffer($input, &view, PyBUF_CONTIG_RO);
+%#else
+  %#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+    %#pragma GCC diagnostic push
+    %#pragma GCC diagnostic ignored "-Wdeprecated"
+    %#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+  %#elif defined(_MSC_VER)
+    %#pragma warning(push)
+    %#pragma warning(disable: 4996)
+  %#endif
+  Py_ssize_t size;
+  res = PyObject_AsReadBuffer($input, &buf, &size);
+  %#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+    %#pragma GCC diagnostic pop
+  %#elif defined(_MSC_VER)
+    %#pragma warning(pop)
+  %#endif
+%#endif
   if (res < 0) {
     PyErr_Clear();
     %argument_fail(res, "(TYPEMAP)", $symname, $argnum);
   }
+%#ifndef Py_LIMITED_API
   buf = view.buf;
   PyBuffer_Release(&view);
+%#endif
   $1 = ($1_ltype) buf;
 }
 %enddef
diff --git a/Lib/python/pyhead.swg b/Lib/python/pyhead.swg
index a5f804a..790da08 100644
--- a/Lib/python/pyhead.swg
+++ b/Lib/python/pyhead.swg
@@ -80,3 +80,17 @@
 #define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name)
 #define Py_hash_t long
 #endif
+
+#ifdef Py_LIMITED_API
+# define PyTuple_GET_ITEM PyTuple_GetItem
+/* Note that PyTuple_SetItem() has different semantics from PyTuple_SET_ITEM as it decref's the original tuple item, so in general they cannot be used
+  interchangeably. However in SWIG-generated code PyTuple_SET_ITEM is only used with newly initialized tuples without any items and for them this does work. */
+# define PyTuple_SET_ITEM PyTuple_SetItem
+# define PyTuple_GET_SIZE PyTuple_Size
+# define PyCFunction_GET_FLAGS PyCFunction_GetFlags
+# define PyCFunction_GET_FUNCTION PyCFunction_GetFunction
+# define PyCFunction_GET_SELF PyCFunction_GetSelf
+# define PyList_GET_ITEM PyList_GetItem
+# define PyList_SET_ITEM PyList_SetItem
+# define PySliceObject PyObject
+#endif
diff --git a/Lib/python/pyrun.swg b/Lib/python/pyrun.swg
index 96d4ab8..3bb617e 100644
--- a/Lib/python/pyrun.swg
+++ b/Lib/python/pyrun.swg
@@ -338,6 +338,7 @@
 SWIGINTERN PyTypeObject*
 swig_varlink_type(void) {
   static char varlink__doc__[] = "Swig var link object";
+#ifndef Py_LIMITED_API
   static PyTypeObject varlink_type;
   static int type_init = 0;
   if (!type_init) {
@@ -405,12 +406,28 @@
       return NULL;
   }
   return &varlink_type;
+#else // Py_LIMITED_API
+  PyType_Slot slots[] = {
+    { Py_tp_dealloc, (void*)swig_varlink_dealloc },
+    { Py_tp_repr, (void*)swig_varlink_repr },
+    { Py_tp_getattr, (void*)swig_varlink_getattr },
+    { Py_tp_setattr, (void*)swig_varlink_setattr },
+    { Py_tp_str, (void*)swig_varlink_str },
+    { Py_tp_doc, (void*)varlink__doc__ },
+    { 0, NULL }
+  };
+  PyType_Spec spec = {};
+  spec.name = "swigvarlink";
+  spec.basicsize = sizeof(swig_varlinkobject);
+  spec.slots = slots;
+  return (PyTypeObject*)PyType_FromSpec(&spec);
+#endif // Py_LIMITED_API
 }
 
 /* Create a variable linking object for use later */
 SWIGINTERN PyObject *
 SWIG_Python_newvarlink(void) {
-  swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type());
+  swig_varlinkobject *result = PyObject_New(swig_varlinkobject, swig_varlink_type());
   if (result) {
     result->vars = 0;
   }
@@ -723,6 +740,14 @@
   if (PyType_IsSubtype(op->ob_type, target_tp))
     return 1;
   return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0);
+#elif defined(Py_LIMITED_API)
+  int cmp;
+  PyObject *tp_name = PyObject_GetAttrString((PyObject*)Py_TYPE(op), "__name__");
+  if (!tp_name)
+    return 0;
+  cmp = PyUnicode_CompareWithASCIIString(tp_name, "SwigPyObject");
+  Py_DECREF(tp_name);
+  return cmp == 0;
 #else
   return (Py_TYPE(op) == SwigPyObject_type())
     || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0);
@@ -869,7 +894,7 @@
 SWIGRUNTIME PyTypeObject*
 SwigPyObject_TypeOnce(void) {
   static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer";
-
+#ifndef Py_LIMITED_API
   static PyNumberMethods SwigPyObject_as_number = {
     (binaryfunc)0, /*nb_add*/
     (binaryfunc)0, /*nb_subtract*/
@@ -1004,12 +1029,30 @@
       return NULL;
   }
   return &swigpyobject_type;
+#else // Py_LIMITED_API
+  PyType_Slot slots[] = {
+    { Py_tp_dealloc, (void*)SwigPyObject_dealloc },
+    { Py_tp_repr, (void*)SwigPyObject_repr },
+    { Py_tp_getattro, (void*)PyObject_GenericGetAttr },
+    { Py_tp_doc, (void*)swigobject_doc },
+    { Py_tp_richcompare, (void*)SwigPyObject_richcompare },
+    { Py_tp_methods, (void*)swigobject_methods },
+    { Py_nb_int, (void*)SwigPyObject_long },
+    { 0, NULL }
+  };
+  PyType_Spec spec = {};
+  spec.name = "SwigPyObject";
+  spec.basicsize = sizeof(SwigPyObject);
+  spec.flags = Py_TPFLAGS_DEFAULT;
+  spec.slots = slots;
+  return (PyTypeObject*)PyType_FromSpec(&spec);
+#endif // Py_LIMITED_API
 }
 
 SWIGRUNTIME PyObject *
 SwigPyObject_New(void *ptr, swig_type_info *ty, int own)
 {
-  SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type());
+  SwigPyObject *sobj = PyObject_New(SwigPyObject, SwigPyObject_type());
   if (sobj) {
     sobj->ptr  = ptr;
     sobj->ty   = ty;
@@ -1080,8 +1123,18 @@
 
 SWIGRUNTIMEINLINE int
 SwigPyPacked_Check(PyObject *op) {
+#ifndef Py_LIMITED_API
   return ((op)->ob_type == SwigPyPacked_TypeOnce()) 
     || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0);
+#else
+  int cmp;
+  PyObject *tp_name = PyObject_GetAttrString((PyObject*)Py_TYPE(op), "__name__");
+  if (!tp_name)
+    return 0;
+  cmp = PyUnicode_CompareWithASCIIString(tp_name, "SwigPyPacked");
+  Py_DECREF(tp_name);
+  return cmp == 0;
+#endif
 }
 
 SWIGRUNTIME void
@@ -1097,6 +1150,7 @@
 SWIGRUNTIME PyTypeObject*
 SwigPyPacked_TypeOnce(void) {
   static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer";
+#ifndef Py_LIMITED_API
   static PyTypeObject swigpypacked_type;
   static int type_init = 0;
   if (!type_init) {
@@ -1187,12 +1241,28 @@
       return NULL;
   }
   return &swigpypacked_type;
+#else
+  PyType_Slot slots[] = {
+    { Py_tp_dealloc, (void*)SwigPyPacked_dealloc },
+    { Py_tp_repr, (void*)SwigPyPacked_repr },
+    { Py_tp_str, (void*)SwigPyPacked_str },
+    { Py_tp_getattro, (void*)PyObject_GenericGetAttr },
+    { Py_tp_doc, swigpacked_doc },
+    { 0, NULL }
+  };
+  PyType_Spec spec = {};
+  spec.name = "SwigPyPacked";
+  spec.basicsize = sizeof(SwigPyPacked);
+  spec.flags = Py_TPFLAGS_DEFAULT;
+  spec.slots = slots;
+  return (PyTypeObject*)PyType_FromSpec(&spec);
+#endif
 }
 
 SWIGRUNTIME PyObject *
 SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty)
 {
-  SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type());
+  SwigPyPacked *sobj = PyObject_New(SwigPyPacked, SwigPyPacked_type());
   if (sobj) {
     void *pack = malloc(size);
     if (pack) {
@@ -1444,10 +1514,18 @@
     swig_cast_info *tc;
 
     /* here we get the method pointer for callbacks */
+#ifndef Py_LIMITED_API
     const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc);
+#else
+    PyObject* pystr_doc = PyObject_GetAttrString(obj, "__doc__");
+    const char *doc = pystr_doc ? SWIG_Python_str_AsChar(pystr_doc) : 0;
+#endif
     const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0;
     if (desc)
       desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0;
+#ifdef Py_LIMITED_API
+    SWIG_Python_str_DelForPy3(doc);
+#endif
     if (!desc)
       return SWIG_ERROR;
     tc = SWIG_TypeCheck(desc,ty);
@@ -1523,7 +1601,12 @@
     if (empty_args) {
       PyObject *empty_kwargs = PyDict_New();
       if (empty_kwargs) {
-        inst = ((PyTypeObject *)data->newargs)->tp_new((PyTypeObject *)data->newargs, empty_args, empty_kwargs);
+#ifndef Py_LIMITED_API
+        newfunc newfn = ((PyTypeObject *)data->newargs)->tp_new;
+#else
+        newfunc newfn = (newfunc)PyType_GetSlot((PyTypeObject *)data->newargs, Py_tp_new);
+#endif
+        inst = newfn((PyTypeObject *)data->newargs, empty_args, empty_kwargs);
         Py_DECREF(empty_kwargs);
         if (inst) {
           if (PyObject_SetAttr(inst, SWIG_This(), swig_this) == -1) {
@@ -1605,7 +1688,12 @@
     if (flags & SWIG_BUILTIN_TP_INIT) {
       newobj = (SwigPyObject*) self;
       if (newobj->ptr) {
-        PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0);
+#ifndef Py_LIMITED_API
+        allocfunc alloc = clientdata->pytype->tp_alloc;
+#else
+        allocfunc alloc = (allocfunc)PyType_GetSlot(clientdata->pytype, Py_tp_alloc);
+#endif
+        PyObject *next_self = alloc(clientdata->pytype, 0);
         while (newobj->next)
 	  newobj = (SwigPyObject *) newobj->next;
         newobj->next = next_self;
@@ -1831,6 +1919,7 @@
     } else 
 #endif      
     {
+#ifndef Py_LIMITED_API // tp_name is not accessible
       const char *otype = (obj ? obj->ob_type->tp_name : 0); 
       if (otype) {
 	PyObject *str = PyObject_Str(obj);
@@ -1845,6 +1934,7 @@
 	Py_XDECREF(str);
 	return;
       }
+#endif
     }   
     PyErr_Format(PyExc_TypeError, "a '%s' is expected", type);
   } else {
diff --git a/Source/Modules/python.cxx b/Source/Modules/python.cxx
index 69e47d6..2326739 100644
--- a/Source/Modules/python.cxx
+++ b/Source/Modules/python.cxx
@@ -67,6 +67,7 @@
 static int no_header_file = 0;
 static int max_bases = 0;
 static int builtin_bases_needed = 0;
+static int py3_stable_abi = 0;
 
 /* C++ Support + Shadow Classes */
 
@@ -128,6 +129,7 @@
      -nortti         - Disable the use of the native C++ RTTI with directors\n\
      -nothreads      - Disable thread support for the entire interface\n\
      -olddefs        - Keep the old method definitions when using -fastproxy\n\
+     -py3-stable-abi - Generate code compatible with Python 3 stable ABI (PEP 384)\n\
      -relativeimport - Use relative Python imports\n\
      -threads        - Add thread support for all the interface\n\
      -O              - Enable the following optimization options:\n\
@@ -392,6 +394,10 @@
 	  fputs(usage1, stdout);
 	  fputs(usage2, stdout);
 	  fputs(usage3, stdout);
+	} else if (strcmp(argv[i], "-py3-stable-abi") == 0) {
+	  py3_stable_abi = 1;
+	  Preprocessor_define("SWIGPYTHON_PY3", 0);
+	  Swig_mark_arg(i);
 	} else if (strcmp(argv[i], "-builtin") == 0) {
 	  builtin = 1;
 	  Preprocessor_define("SWIGPYTHON_BUILTIN", 0);
@@ -458,6 +464,16 @@
     if (doxygen)
       doxygenTranslator = new PyDocConverter(doxygen_translator_flags);
 
+    if (py3_stable_abi && builtin) {
+      Printf(stderr, "-py3-stable-abi and -builtin options are not compatible.\n");
+      SWIG_exit(EXIT_FAILURE);
+    }
+
+    if (py3_stable_abi && fastproxy) {
+      Printf(stderr, "-py3-stable-abi and -fastproxy options are not compatible.  Disabling -fastproxy.\n");
+      fastproxy = 0;
+    }
+
     if (!global_name)
       global_name = NewString("cvar");
     Preprocessor_define("SWIGPYTHON 1", 0);
@@ -626,6 +642,10 @@
       Printf(f_runtime, "#define SWIGPYTHON_FASTPROXY\n");
     }
 
+    if (py3_stable_abi) {
+      Printf(f_runtime, "#define Py_LIMITED_API 0x03040000\n");
+    }
+
     Printf(f_runtime, "\n");
 
     Printf(f_header, "#ifdef SWIG_TypeQuery\n");