Skip to content

Commit

Permalink
Support stable Python ABI.
Browse files Browse the repository at this point in the history
  • Loading branch information
vadimcn committed Nov 18, 2019
1 parent cb5d739 commit fd43207
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 21 deletions.
10 changes: 10 additions & 0 deletions .travis.yml
Expand Up @@ -245,6 +245,16 @@ matrix:
env: SWIGLANG=python SWIG_FEATURES=-builtin PY3=3 VER=3.7 SWIGOPTPY3=
sudo: required
dist: xenial
- compiler: gcc
os: linux
env: SWIGLANG=python SWIG_FEATURES=-py3-stable-abi PY3=3
sudo: required
dist: xenial
- compiler: gcc
os: linux
env: SWIGLANG=python SWIG_FEATURES="-py3-stable-abi -O" PY3=3
sudo: required
dist: xenial
- compiler: gcc
os: linux
env: SWIGLANG=python SWIG_FEATURES=-O
Expand Down
60 changes: 60 additions & 0 deletions Doc/Manual/Python.html
Expand Up @@ -138,6 +138,7 @@ <H1><a name="Python">32 SWIG and Python</a></H1>
<li><a href="#Python_nn76">Abstract base classes</a>
<li><a href="#Python_nn77">Byte string output conversion</a>
<li><a href="#Python_2_unicode">Python 2 Unicode</a>
<li><a href="#Python_3_stable_abi">Stable ABI</a>
</ul>
<li><a href="#Python_multithreaded">Support for Multithreaded Applications</a>
<ul>
Expand Down Expand Up @@ -964,6 +965,7 @@ <H3><a name="Python_commandline">32.2.9 Additional Python commandline options</a
<tr><td>-nothreads </td><td>Disable thread support for the entire interface</td></tr>
<tr><td>-olddefs </td><td>Keep the old method definitions when using -fastproxy</td></tr>
<tr><td>-py3 </td><td>Generate code with Python 3 specific features and syntax</td></tr>
<tr><td>-py3-stable-abi </td><td>Generate code compatible with Python 3 stable ABI (PEP 384)</td></tr>
<tr><td>-relativeimport </td><td>Use relative Python imports</td></tr>
<tr><td>-threads </td><td>Add thread support for all the interface</td></tr>
<tr><td>-O </td><td>Enable the following optimization options: -fastdispatch -fastproxy -fvirtual</td></tr>
Expand Down Expand Up @@ -7228,6 +7230,64 @@ <H3><a name="Python_2_unicode">32.12.5 Python 2 Unicode</a></H3>
prohibiting it.
</p>

<H3><a name="Python_3_stable_abi">32.12.5 Python 3 Stable ABI</a></H3>

<p>
Quoting from <a href="https://docs.python.org/3/c-api/stable.html">Python 3 documentation</a>:
<blockquote>

<p>
Traditionally, the C API of Python will change with every release.
Most changes will be source-compatible, typically by only adding API, rather
than changing existing API or removing API (although some interfaces do get
removed after being deprecated first).
</p>

<p>
Unfortunately, the API compatibility does not extend to binary compatibility (the ABI).
The reason is primarily the evolution of struct definitions, where addition of a new field,
or changing the type of a field, might not break the API, but can break the ABI.
As a consequence, extension modules need to be recompiled for every Python release
(although an exception is possible on Unix when none of the affected interfaces are used).
In addition, on Windows, extension modules link with a specific pythonXY.dll and need to be
recompiled to link with a newer one.
</p>

<p>
Since Python 3.2, a subset of the API has been declared to guarantee a stable ABI.
Extension modules wishing to use this API (called “limited API”) need to define Py_LIMITED_API.
A number of interpreter details then become hidden from the extension module; in return,
a module is built that works on any 3.x version (x>=2) without recompilation.
</p>

<p>
In some cases, the stable ABI needs to be extended with new functions. Extension modules
wishing to use these new APIs need to set Py_LIMITED_API to the PY_VERSION_HEX value
(see API and ABI Versioning) of the minimum Python version they want to support
(e.g. 0x03030000 for Python 3.3). Such modules will work on all subsequent Python
releases, but fail to load (because of missing symbols) on the older releases.
</p>

<p>
The set of functions available to the limited API is documented in <a href="https://www.python.org/dev/peps/pep-0384/">PEP 384</a>.
</p>

</blockquote>
</p>

<p>
The <tt>-py3-stable-abi</tt> option enables compatibility of code generated by SWIG with stable ABI. SWIG modules that use stable ABI will be able to work
with Python versions 3.4 and above without recompilation.

<p> However, this compatibility comes at a performance cost: some Python API macros are not available in this mode,
so SWIG must use function versions of the same. For example, instead of <tt>PyTuple_GET_ITEM</tt> SWIG has to use <tt>PyTuple_GetItem</tt>.
Unlike the macro, <tt>PyTuple_GetItem</tt> cannot be inlined, so it will execute slower.
</p>

<p>
Also, please note that <tt>-py3-stable-abi</tt> is not compatible with some of the other SWIG Python options such as <tt>-builtin</tt> or <tt>-fastproxy</tt>.
</p>

<H2><a name="Python_multithreaded">32.13 Support for Multithreaded Applications</a></H2>


Expand Down
6 changes: 5 additions & 1 deletion Examples/test-suite/python/Makefile.in
Expand Up @@ -67,7 +67,6 @@ CPP_TEST_CASES += \
python_moduleimport \
python_overload_simple_cast \
python_pickle \
python_pybuffer \
python_pythoncode \
python_richcompare \
python_strict_unicode \
Expand All @@ -77,6 +76,11 @@ CPP_TEST_CASES += \
swigobject \
template_matrix \

# buffers are not supported with -py3-stable-abi
ifeq (,$(findstring -py3-stable-abi,$(SWIG_FEATURES)))
CPP_TEST_CASES += python_pybuffer
endif

# li_std_carray
# director_profile

Expand Down
11 changes: 11 additions & 0 deletions Lib/python/pyhead.swg
Expand Up @@ -85,3 +85,14 @@ SWIG_Python_str_FromChar(const char *c)
#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name)
#define Py_hash_t long
#endif

#ifdef Py_LIMITED_API
# define PyTuple_GET_ITEM PyTuple_GetItem
# 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
25 changes: 24 additions & 1 deletion Lib/python/pyinit.swg
Expand Up @@ -15,9 +15,11 @@ extern "C" {

/* Method creation and docstring support functions */

#ifndef Py_LIMITED_API
SWIGINTERN PyMethodDef *SWIG_PythonGetProxyDoc(const char *name);
SWIGINTERN PyObject *SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func);
SWIGINTERN PyObject *SWIG_PyStaticMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func);
#endif // Py_LIMITED_API

#ifdef __cplusplus
}
Expand Down Expand Up @@ -146,6 +148,7 @@ swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) {
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) {
Expand Down Expand Up @@ -200,12 +203,29 @@ swig_varlink_type(void) {
return NULL;
}
return &varlink_type;
#else
PyType_Slot slots[] = {
{ Py_tp_dealloc, (void*)swig_varlink_dealloc },
{ Py_tp_getattr, (void*)swig_varlink_getattr },
{ Py_tp_setattr, (void*)swig_varlink_setattr },
{ Py_tp_repr, (void*)swig_varlink_repr },
{ 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.flags = Py_TPFLAGS_DEFAULT;
spec.slots = slots;
return (PyTypeObject*)PyType_FromSpec(&spec);
#endif
}

/* 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;
}
Expand Down Expand Up @@ -318,6 +338,7 @@ SWIG_Python_FixMethods(PyMethodDef *methods,
* Method creation and docstring support functions
* ----------------------------------------------------------------------------- */

#ifndef Py_LIMITED_API
/* -----------------------------------------------------------------------------
* Function to find the method definition with the correct docstring for the
* proxy module as opposed to the low-level API
Expand Down Expand Up @@ -372,6 +393,8 @@ SWIGINTERN PyObject *SWIG_PyStaticMethod_New(PyObject *SWIGUNUSEDPARM(self), PyO
return PyStaticMethod_New(func);
}

#endif // Py_LIMITED_API

#ifdef __cplusplus
}
#endif
Expand Down

0 comments on commit fd43207

Please sign in to comment.