Skip to content

Commit cb5aebe

Browse files
committed
Work around missing features in Python < 3.9
1 parent 74c5a38 commit cb5aebe

File tree

4 files changed

+62
-9
lines changed

4 files changed

+62
-9
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ jobs:
147147
env:
148148
WRAPT_INSTALL_EXTENSIONS: true # Require extensions.
149149
CIBW_SKIP: pp* # Skip wheels for PyPy.
150-
CIBW_BUILD: cp36-* # only build on 3.6 for stable cp36-abi3
150+
# build on 3.6 to 3.9. cp39 provides stable abi cp39-abi3
151+
CIBW_BUILD: cp36-* cp37-* cp38-* cp39-*
151152
- uses: actions/upload-artifact@v2
152153
with:
153154
name: dist

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ where=src
4444

4545
[bdist_wheel]
4646
universal = false
47-
py_limited_api=cp36
4847

4948
# --- Test and coverage configuration ------------------------------------------
5049

setup.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import os
22
import platform
3+
4+
import sys
35
import setuptools
46

7+
try:
8+
from wheel.bdist_wheel import bdist_wheel
9+
except ImportError:
10+
bdist_wheel = None
11+
512

613
# # --- Detect if extensions should be disabled ------------------------------
714

@@ -17,20 +24,43 @@
1724
if platform.python_implementation() != "CPython":
1825
disable_extensions = True
1926

27+
# # --- stable ABI / limited API hacks ---------------------------------------
28+
# Python < 3.9 is missing some features to build wheels with stable ABI
29+
30+
define_macros = []
31+
cmdclass = {}
32+
33+
if sys.version_info >= (3, 9, 0) and platform.python_implementation() == "CPython":
34+
py_limited_api = True
35+
define_macros.append(("Py_LIMITED_API", "0x03090000"))
36+
37+
if bdist_wheel is not None:
38+
class LimitedAPIWheel(bdist_wheel):
39+
def finalize_options(self):
40+
self.py_limited_api = "cp39"
41+
bdist_wheel.finalize_options(self)
42+
43+
cmdclass["bdist_wheel"] = LimitedAPIWheel
44+
else:
45+
py_limited_api = False
46+
47+
2048
# --- C extension ------------------------------------------------------------
2149

2250
extensions = [
2351
setuptools.Extension(
2452
"wrapt._wrappers",
2553
sources=["src/wrapt/_wrappers.c"],
2654
optional=not force_extensions,
27-
py_limited_api=True,
55+
py_limited_api=py_limited_api,
56+
define_macros=define_macros,
2857
)
2958
]
3059

3160

3261
# --- Setup ------------------------------------------------------------------
3362

3463
setuptools.setup(
35-
ext_modules=[] if disable_extensions else extensions
64+
ext_modules=[] if disable_extensions else extensions,
65+
cmdclass=cmdclass
3666
)

src/wrapt/_wrappers.c

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
/* ------------------------------------------------------------------------- */
22

3-
#ifndef Py_LIMITED_API
4-
/* stable ABI Python >= 3.6, keep in sync with setup.cfg py_limited_api */
5-
#define Py_LIMITED_API 0x03060000
6-
#endif
7-
83
#include "Python.h"
94

105
#include "structmember.h"
@@ -13,6 +8,20 @@
138
#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
149
#endif
1510

11+
/* Heap types in Python < 3.9 don't support __weaklistoffset__ and
12+
* __dictoffset__ in PyMemberDef.
13+
*/
14+
#if PY_VERSION_HEX >= 0x03090000
15+
#define WRAPT_HEAPTYPE_SUPPORT_OFFSET 1
16+
#endif
17+
18+
/* Instanes of heap types in Python < 3.8 don't hold strong ref to their
19+
* type object.
20+
*/
21+
#if PY_VERSION_HEX >= 0x03080000
22+
#define WRAPT_HEAPTYPE_STRONG_TYPEREF 1
23+
#endif
24+
1625
/* ------------------------------------------------------------------------- */
1726

1827
typedef struct {
@@ -147,6 +156,7 @@ static int WraptObjectProxy_init(WraptObjectProxyObject *self,
147156
static int WraptObjectProxy_traverse(WraptObjectProxyObject *self,
148157
visitproc visit, void *arg)
149158
{
159+
Py_VISIT(Py_TYPE(self));
150160
Py_VISIT(self->dict);
151161
Py_VISIT(self->wrapped);
152162

@@ -167,7 +177,10 @@ static int WraptObjectProxy_clear(WraptObjectProxyObject *self)
167177

168178
static void WraptObjectProxy_dealloc(WraptObjectProxyObject *self)
169179
{
180+
#ifdef WRAPT_HEAPTYPE_STRONG_TYPEREF
181+
/* Type decref causes segfaults in <= 3.7 */
170182
PyTypeObject *tp = Py_TYPE(self);
183+
#endif
171184

172185
PyObject_GC_UnTrack(self);
173186

@@ -179,7 +192,9 @@ static void WraptObjectProxy_dealloc(WraptObjectProxyObject *self)
179192
freefunc free_func = PyType_GetSlot(Py_TYPE(self), Py_tp_free);
180193
free_func(self);
181194

195+
#ifdef WRAPT_HEAPTYPE_STRONG_TYPEREF
182196
Py_DECREF(tp);
197+
#endif
183198
}
184199

185200
/* ------------------------------------------------------------------------- */
@@ -1589,8 +1604,10 @@ static PyGetSetDef WraptObjectProxy_getset[] = {
15891604
};
15901605

15911606
static struct PyMemberDef WraptObjectProxy_Type_members[] = {
1607+
#ifdef WRAPT_HEAPTYPE_SUPPORT_OFFSET
15921608
{"__weaklistoffset__", T_PYSSIZET, offsetof(WraptObjectProxyObject, weakreflist), READONLY},
15931609
{"__dictoffset__", T_PYSSIZET, offsetof(WraptObjectProxyObject, dict), READONLY},
1610+
#endif
15941611
{NULL},
15951612
};
15961613

@@ -1697,6 +1714,7 @@ static PyType_Spec WraptCallableObjectProxy_Type_spec = {
16971714
"CallableObjectProxy",
16981715
sizeof(WraptObjectProxyObject),
16991716
0,
1717+
/* Only define HAVE_GC if the object defines custom traverse and clear */
17001718
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
17011719
WraptCallableObjectProxy_Type_slots
17021720
};
@@ -2778,6 +2796,11 @@ init_type(PyObject *module, PyObject **newtype, PyType_Spec *spec,
27782796
return -1;
27792797
}
27802798
assert(((PyTypeObject *)*newtype)->tp_traverse != NULL);
2799+
#ifndef WRAPT_HEAPTYPE_SUPPORT_OFFSET
2800+
/* hack for Python <= 3.8 */
2801+
((PyTypeObject *)*newtype)->tp_weaklistoffset = offsetof(WraptObjectProxyObject, weakreflist);
2802+
((PyTypeObject *)*newtype)->tp_dictoffset = offsetof(WraptObjectProxyObject, dict);
2803+
#endif
27812804
}
27822805

27832806
if (PyModule_AddObject(module, attrname, *newtype) < 0) {

0 commit comments

Comments
 (0)