diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index affac53b..7cc7f3c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10.0-beta.1] os: [ubuntu-latest, macos-latest] steps: - uses: actions/checkout@v2 @@ -77,7 +77,7 @@ jobs: strategy: matrix: python-version: [3.9] - image: [manylinux2010_x86_64, manylinux2014_aarch64, manylinux2014_ppc64le] + image: [manylinux2010_x86_64, manylinux2014_aarch64, manylinux2014_ppc64le, manylinux2014_x86_64] steps: - name: checkout @@ -95,10 +95,17 @@ jobs: #run: | # docker run --rm --privileged multiarch/qemu-user-static:register --reset - name: Build and test greenlet + if: matrix.image == 'manylinux2010_x86_64' # An alternate way to do this is to run the container directly with a uses: # and then the script runs inside it. That may work better with caching. # See https://github.com/pyca/bcrypt/blob/f6b5ee2eda76d077c531362ac65e16f045cf1f29/.github/workflows/wheel-builder.yml - # The 2010 image is the last one that comes with Python 2.7. + # The 2010 image is the last one that comes with Python 2.7, + # and only up through the tag 2021-02-06-3d322a5 + env: + DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }}:2021-02-06-3d322a5 + run: bash ./make-manylinux + - name: Build and test greenlet (other) + if: matrix.image != 'manylinux2010_x86_64' env: DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }} run: bash ./make-manylinux diff --git a/CHANGES.rst b/CHANGES.rst index 400d1703..e6d5936b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,10 +2,11 @@ Changes ========= -1.0.1 (unreleased) +1.1.0 (unreleased) ================== -- Nothing changed yet. +- Add support for Python 3.10. Pre-built binary wheels are not + currently available for 3.10. 1.0.0 (2021-01-13) diff --git a/make-manylinux b/make-manylinux index cc7a4ac5..0d41bd82 100755 --- a/make-manylinux +++ b/make-manylinux @@ -27,7 +27,7 @@ if [ -d /greenlet -a -d /opt/python ]; then mkdir -p /greenlet/wheelhouse OPATH="$PATH" which auditwheel - for variant in `ls -d /opt/python/cp{27,35,36,37,38,39}*`; do + for variant in `ls -d /opt/python/cp{27,35,36,37,38,39,310}*`; do export PATH="$variant/bin:$OPATH" echo "Building $variant $(python --version)" diff --git a/setup.py b/setup.py index 92132f13..8e7533c1 100755 --- a/setup.py +++ b/setup.py @@ -138,6 +138,7 @@ def get_greenlet_version(): 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules' ], diff --git a/src/greenlet/greenlet.c b/src/greenlet/greenlet.c index eff39a10..e299ed09 100644 --- a/src/greenlet/greenlet.c +++ b/src/greenlet/greenlet.c @@ -96,6 +96,19 @@ extern PyTypeObject PyGreenlet_Type; # define GREENLET_PY37 0 #endif +#if PY_VERSION_HEX >= 0x30A00B1 +/* +Python 3.10 beta 1 changed tstate->use_tracing to a nested cframe member. +See https://github.com/python/cpython/pull/25276 +We have to save and restore this as well. +*/ +#define TSTATE_USE_TRACING(tstate) (tstate->cframe->use_tracing) +#define GREENLET_USE_CFRAME 1 +#else +#define TSTATE_USE_TRACING(tstate) (tstate->use_tracing) +#define GREENLET_USE_CFRAME 0 +#endif + #ifndef Py_SET_REFCNT /* Py_REFCNT and Py_SIZE macros are converted to functions https://bugs.python.org/issue39573 */ @@ -504,6 +517,9 @@ g_switchstack(void) current->exc_type = tstate->exc_type; current->exc_value = tstate->exc_value; current->exc_traceback = tstate->exc_traceback; +#endif +#if GREENLET_USE_CFRAME + current->cframe = tstate->cframe; #endif } err = slp_switch(); @@ -548,6 +564,10 @@ g_switchstack(void) #endif green_clear_exc(target); +#if GREENLET_USE_CFRAME + tstate->cframe = target->cframe; +#endif + assert(ts_origin == NULL); Py_INCREF(target); ts_current = target; @@ -567,10 +587,10 @@ g_calltrace(PyObject* tracefunc, PyObject* event, PyGreenlet* origin, PyErr_Fetch(&exc_type, &exc_val, &exc_tb); tstate = PyThreadState_GET(); tstate->tracing++; - tstate->use_tracing = 0; + TSTATE_USE_TRACING(tstate) = 0; retval = PyObject_CallFunction(tracefunc, "O(OO)", event, origin, target); tstate->tracing--; - tstate->use_tracing = + TSTATE_USE_TRACING(tstate) = (tstate->tracing <= 0 && ((tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL))); if (retval == NULL) { @@ -897,6 +917,9 @@ green_new(PyTypeObject* type, PyObject* args, PyObject* kwds) } Py_INCREF(ts_current); ((PyGreenlet*)o)->parent = ts_current; +#if GREENLET_USE_CFRAME + ((PyGreenlet*)o)->cframe = &PyThreadState_GET()->root_cframe; +#endif } return o; } @@ -1601,8 +1624,9 @@ PyGreenlet_SetParent(PyGreenlet* g, PyGreenlet* nparent) static PyGreenlet* PyGreenlet_New(PyObject* run, PyGreenlet* parent) { + /* XXX: Why doesn't this call green_new()? There's some duplicate + code. */ PyGreenlet* g = NULL; - g = (PyGreenlet*)PyType_GenericAlloc(&PyGreenlet_Type, 0); if (g == NULL) { return NULL; @@ -1625,7 +1649,9 @@ PyGreenlet_New(PyObject* run, PyGreenlet* parent) return NULL; } } - +#if GREENLET_USE_CFRAME + g->cframe = &PyThreadState_GET()->root_cframe; +#endif return g; } diff --git a/src/greenlet/greenlet.h b/src/greenlet/greenlet.h index 13977821..830bef8d 100644 --- a/src/greenlet/greenlet.h +++ b/src/greenlet/greenlet.h @@ -38,6 +38,9 @@ typedef struct _greenlet { #if PY_VERSION_HEX >= 0x030700A3 PyObject* context; #endif +#if PY_VERSION_HEX >= 0x30A00B1 + CFrame* cframe; +#endif } PyGreenlet; #define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) diff --git a/src/greenlet/tests/test_extension_interface.py b/src/greenlet/tests/test_extension_interface.py index bc874521..a92ea1f5 100644 --- a/src/greenlet/tests/test_extension_interface.py +++ b/src/greenlet/tests/test_extension_interface.py @@ -72,3 +72,6 @@ def foo(): str(seen[0]), 'take that sucka!', "message doesn't match") + +if __name__ == '__main__': + unittest.main() diff --git a/tox.ini b/tox.ini index e4bde4c6..df06fc33 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py27,py35,py36,py37,py38,py39,docs + py27,py35,py36,py37,py38,py39,py310,docs [testenv] commands =