From 1880845926b393a985a1754e878c979f1a2a5ee2 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 19 May 2026 15:01:13 -0500 Subject: [PATCH 1/6] Drop 3.9 support and stop testing PyPy. Use newer manylinux images to build wheels. --- .github/workflows/tests.yml | 37 ++++++++++------------------- CHANGES.rst | 7 ++++-- scripts/releases/make-manylinux | 3 ++- setup.py | 7 ++---- src/relstorage/tests/reltestbase.py | 2 +- 5 files changed, 23 insertions(+), 33 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f5a3171..153257d8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -88,22 +88,13 @@ jobs: # 3.12.1 breaks zope.testrunner. # See https://github.com/zopefoundation/zope.testrunner/issues/157 python-version: - - '3.9' - '3.10' - '3.11' - "3.12" - "3.13" - - "pypy-3.11" - - "3.14.0-rc.3" + - "3.14" os: [ubuntu-latest, macos-latest] - exclude: - # Can't build persistent on 3.12 with -UNDEBUG because it - # accesses fields of objects that aren't there in assert statements; - # we build linux with assertions enabled, and unless there is a 3.12 - # persistent wheel, we have to exclude that one. - # The same thing goes for PyPy. - - os: ubuntu-latest - python-version: "pypy-3.11" + # exclude: steps: - name: checkout @@ -173,13 +164,12 @@ jobs: ls -l dist twine check dist/* - name: Upload RelStorage wheel - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: RelStorage-${{ runner.os }}-${{ matrix.python-version }}.whl path: dist/*whl - name: lint - if: matrix.python-version == '3.11' && startsWith(runner.os, 'Linux') - # At this writing, PyLint 2.17/astroid 2.15 won't work on 3.12 + if: matrix.python-version == '3.14' && startsWith(runner.os, 'Linux') run: | pip install -U pylint python -m pylint --limit-inference-results=1 --rcfile=.pylintrc relstorage -f parseable -r n @@ -219,7 +209,7 @@ jobs: with: flag-name: run-${{ join(matrix.*, '-') }} parallel: true - - name: Publish package to PyPI (mac) + - name: Publish package to PyPI (Mac) # We cannot 'uses: pypa/gh-action-pypi-publish@v1.4.1' because # that's apparently a container action, and those don't run on # the Mac. @@ -245,15 +235,14 @@ jobs: # We use a regular Python matrix entry to share as much code as possible. strategy: matrix: - python-version: [3.9] + python-version: [3.14] image: - manylinux_2_28_x86_64 - - manylinux2014_aarch64 - - manylinux2014_ppc64le - - manylinux2014_s390x - - manylinux2014_x86_64 - - musllinux_1_1_x86_64 - - musllinux_1_1_aarch64 + - manylinux_2_28_aarch64 + # - manylinux_2_28_ppc64le + # - manylinux_2_28_s390x + - musllinux_1_2_x86_64 + - musllinux_1_2_aarch64 name: ${{ matrix.image }} steps: - name: checkout @@ -263,7 +252,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 with: platforms: all - name: Build RelStorage @@ -271,7 +260,7 @@ jobs: DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }} run: bash ./scripts/releases/make-manylinux - name: Store RelStorage wheels - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: path: wheelhouse/*whl name: ${{ matrix.image }}_wheels.zip diff --git a/CHANGES.rst b/CHANGES.rst index 2641ee4a..0913f4ec 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,10 +2,13 @@ Changes ========= -4.2.1 (unreleased) +4.3.0 (unreleased) ================== -- Nothing changed yet. +- Use newer ``manylinux`` images to build binary wheels. +- Drop support for Python 3.9. This will be the last major RelStorage + release to support 3.10. +- Stop testing on PyPy 3.11. 4.2.0 (2025-09-29) diff --git a/scripts/releases/make-manylinux b/scripts/releases/make-manylinux index 37bd065a..f14def40 100755 --- a/scripts/releases/make-manylinux +++ b/scripts/releases/make-manylinux @@ -16,6 +16,7 @@ export GEVENT_MANYLINUX=1 # know. The env var works for pip 20. export PIP_NO_PYTHON_VERSION_WARNING=1 export PIP_NO_WARN_SCRIPT_LOCATION=1 +export PIP_ROOT_USER_ACTION=ignore # Build configuration. export CFLAGS="-pipe -O3" @@ -58,7 +59,7 @@ if [ -d /RelStorage ] && [ -d /opt/python ]; then rm -rf wheelhouse mkdir wheelhouse ls -l /opt/python - for variant in `ls -d /opt/python/cp{314,312,39,310,311,313}*`; do + for variant in `ls -d /opt/python/cp{314,312,310,311,313}*`; do echo "Building $variant" mkdir /tmp/build cd /tmp/build diff --git a/setup.py b/setup.py index 4c0dec57..1ff9630a 100644 --- a/setup.py +++ b/setup.py @@ -168,15 +168,12 @@ def read_file(*path): license="ZPL-2.1", platforms=["any"], description="A backend for ZODB that stores pickles in a relational database.", - # Bump to 3.10 or even 3.11 at next release. We'll have to make changes - # in this file and our CI configuration to make sure we still have the - # same test coverage, as we were using just 3.9 for some things. - python_requires=">=3.9", + # Bump to 3.11 at next release. + python_requires=">=3.10", classifiers=[ "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/src/relstorage/tests/reltestbase.py b/src/relstorage/tests/reltestbase.py index c3392f73..5d8b1ab8 100644 --- a/src/relstorage/tests/reltestbase.py +++ b/src/relstorage/tests/reltestbase.py @@ -136,7 +136,7 @@ def __init__(self, storage, *args, **kwargs): def runtest(self): try: - super().runtest() + super().runtest() # pylint:disable=no-member finally: self.storage.release() self.storage = None From f0fa784c203bcc0c20c1dcb35e5569f7a740f92c Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 19 May 2026 15:07:07 -0500 Subject: [PATCH 2/6] linting --- src/relstorage/adapters/mysql/drivers/_mysqldb_gevent.py | 2 +- src/relstorage/zodbconvert.py | 1 + src/relstorage/zodbpack.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/relstorage/adapters/mysql/drivers/_mysqldb_gevent.py b/src/relstorage/adapters/mysql/drivers/_mysqldb_gevent.py index d37d3ff4..acf75d75 100644 --- a/src/relstorage/adapters/mysql/drivers/_mysqldb_gevent.py +++ b/src/relstorage/adapters/mysql/drivers/_mysqldb_gevent.py @@ -146,7 +146,7 @@ def enter_critical_phase_until_transaction_end(self): self.commit = self._critical_commit self.rollback = self._critical_rollback self.query = self._critical_query - self.gevent_sleep = _noop + self.gevent_sleep = _noop # pylint:disable=attribute-defined-outside-init def is_in_critical_phase(self): return 'gevent_sleep' in self.__dict__ diff --git a/src/relstorage/zodbconvert.py b/src/relstorage/zodbconvert.py index 48649536..be10bd26 100644 --- a/src/relstorage/zodbconvert.py +++ b/src/relstorage/zodbconvert.py @@ -133,6 +133,7 @@ def main(argv=None): default=logging.INFO, help="Set the logging level to TRACE instead of the default of INFO." ) + # pylint:disable-next=deprecated-class parser.add_argument("config_file", type=argparse.FileType('r')) options = parser.parse_args(argv[1:]) diff --git a/src/relstorage/zodbpack.py b/src/relstorage/zodbpack.py index 25a6649c..4ed66d6c 100644 --- a/src/relstorage/zodbpack.py +++ b/src/relstorage/zodbpack.py @@ -68,6 +68,7 @@ def main(argv=None): "to objects that have already been removed. The --days and --prepack " "arguments are ignored." ) + # pylint:disable-next=deprecated-class parser.add_argument("config_file", type=argparse.FileType('r')) options = parser.parse_args(argv[1:]) From d0fab7f18045d7ec588aaafa8816f11f0bd0e94b Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 19 May 2026 15:23:40 -0500 Subject: [PATCH 3/6] Try building and testing free-threaded. --- .github/workflows/tests.yml | 5 ++++- CHANGES.rst | 5 ++++- docs/install.rst | 8 ++++++-- setup.py | 3 +-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 153257d8..2af620b3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -93,7 +93,10 @@ jobs: - "3.12" - "3.13" - "3.14" - os: [ubuntu-latest, macos-latest] + - "3.14t" + os: + - ubuntu-latest + - macos-latest # exclude: steps: diff --git a/CHANGES.rst b/CHANGES.rst index 0913f4ec..86624ce0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,7 +8,10 @@ - Use newer ``manylinux`` images to build binary wheels. - Drop support for Python 3.9. This will be the last major RelStorage release to support 3.10. -- Stop testing on PyPy 3.11. +- Stop testing on PyPy 3.11 and with psycopg2cffi. +- Build, test, and provide free-threaded support. However, it is + likely that RelStorage dependencies will cause the GIL to be + automatically enabled. 4.2.0 (2025-09-29) diff --git a/docs/install.rst b/docs/install.rst index 0e488948..6ff29841 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -2,8 +2,9 @@ Installation ============== -RelStorage 3.0 is supported on Python 3.8, through 3.12, as well as -PyPy3 3.9 or later. +RelStorage 4.3 is supported on Python 3.10 through 3.14. PyPy 3.11 +was tested and supported in the past and may continue to work as long +as RelStorage supports Python 3.11. .. seealso:: :doc:`supported-databases` @@ -25,6 +26,9 @@ need to use even older versions of ZODB/ZEO, install RelStorage 1.6. Likewise, if you need Python 2.6 support, install RelStorage 1.6 (note that 1.6 *does not* run on Python 3 or PyPy). +RelStorage 4.2.0 was the last version to support Python 3.9. + +RelStorage 4.0.0 was the last version to support Python 3.8. Database Adapter ================ diff --git a/setup.py b/setup.py index 1ff9630a..34a8b13f 100644 --- a/setup.py +++ b/setup.py @@ -260,6 +260,7 @@ def read_file(*path): 'always_allow_keywords': False, 'infer_types': True, 'nonecheck': False, + 'freethreading_compatible': True, }, ), tests_require=tests_require, @@ -353,8 +354,6 @@ def read_file(*path): # pg8000 # This requirement is repeated in the driver class. 'pg8000 >= 1.29.0; python_version == "3.11"', - # CFFI, runs on all implementations. - 'psycopg2cffi >= 2.7.4; python_version == "3.11" or platform_python_implementation == "PyPy"', # Psycopg2 on all CPython, it's the default 'psycopg2 >= 2.8.3; platform_python_implementation == "CPython"', ], From 97248fff41e8c0b9598d44e6461192af79208b78 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 19 May 2026 15:32:55 -0500 Subject: [PATCH 4/6] Stop trying to test 3.14t; too many modules still require the GIL -- and gevent crashed. Try enabling 3.15 tests. --- .github/workflows/tests.yml | 8 +++++++- CHANGES.rst | 6 ++++-- scripts/releases/make-manylinux | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2af620b3..45029e26 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -93,7 +93,13 @@ jobs: - "3.12" - "3.13" - "3.14" - - "3.14t" + - "3.15-dev" + # Too many modules we depend on aren't free-threaded to make + # it worthwhile to try to test this. We'd have to forcibly + # disable the GIL, which is almost certainly unsafe given + # the modules that aren't free-threaded (e.g., gevent, persistent), + # and the fact that our test suite heavily uses threads. + # - "3.14t" os: - ubuntu-latest - macos-latest diff --git a/CHANGES.rst b/CHANGES.rst index 86624ce0..28362e26 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,9 +9,11 @@ - Drop support for Python 3.9. This will be the last major RelStorage release to support 3.10. - Stop testing on PyPy 3.11 and with psycopg2cffi. -- Build, test, and provide free-threaded support. However, it is +- Build and provide binary wheels with free-threaded support. However, it is likely that RelStorage dependencies will cause the GIL to be - automatically enabled. + automatically enabled, and these wheels are thus untested. Crashes + have been seen in combination with gevent on 3.14t. +- Add support for Python 3.15. 4.2.0 (2025-09-29) diff --git a/scripts/releases/make-manylinux b/scripts/releases/make-manylinux index f14def40..6c1bf5ac 100755 --- a/scripts/releases/make-manylinux +++ b/scripts/releases/make-manylinux @@ -59,7 +59,7 @@ if [ -d /RelStorage ] && [ -d /opt/python ]; then rm -rf wheelhouse mkdir wheelhouse ls -l /opt/python - for variant in `ls -d /opt/python/cp{314,312,310,311,313}*`; do + for variant in `ls -d /opt/python/cp{315,314,312,310,311,313}*`; do echo "Building $variant" mkdir /tmp/build cd /tmp/build From 71a2dbf234b342034387d130aa99c47986d22cfe Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 19 May 2026 15:40:43 -0500 Subject: [PATCH 5/6] Disable coverage for 3.15 for now. --- .github/workflows/tests.yml | 6 ++++-- .readthedocs.yml | 2 +- setup.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 45029e26..b52829e9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -211,10 +211,12 @@ jobs: - name: Run tests and report coverage uses: ./.github/actions/run-tests with: - useCoverage: ${{ !startsWith(matrix.python-version, 'pypy') }} + # On 3.15b1 with coverage 7.14.0, the binary tracer isn't available + # and the pure-python tracer can't do greenlet concurrency + useCoverage: ${{ !startsWith(matrix.python-version, 'pypy') && matrix.python-version != '3.15-dev' }} - name: Coveralls Parallel uses: coverallsapp/github-action@v2 - if: ${{ !startsWith(matrix.python-version, 'pypy') }} + if: ${{ !startsWith(matrix.python-version, 'pypy') && matrix.python-version != '3.15-dev' }} with: flag-name: run-${{ join(matrix.*, '-') }} parallel: true diff --git a/.readthedocs.yml b/.readthedocs.yml index 9d720e91..e73731d0 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -21,7 +21,7 @@ build: # os is required for some reason os: ubuntu-22.04 tools: - python: "3.11" + python: "3.14" python: install: diff --git a/setup.py b/setup.py index 34a8b13f..cedee837 100644 --- a/setup.py +++ b/setup.py @@ -342,7 +342,7 @@ def read_file(*path): # First, mysql # pymysql on 3.9 on all platforms. - 'PyMySQL >= 0.6.6; python_version == "3.9"', + 'PyMySQL >= 0.6.6; python_version == "3.14"', # mysqlclient (binary) on all CPythons. It's the default. 'mysqlclient >= 2.0.0', # mysql-connector-python; one of two pure-python versions From 6435373d24245a008b1fdf7c43bb3c5dbc7c7040 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 19 May 2026 16:42:47 -0500 Subject: [PATCH 6/6] Take 3.9 out of the Appveyor matrix. --- appveyor.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 438f92dd..c467e02a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -39,7 +39,7 @@ environment: # all the current (non EOL) versions. - PYTHON: "C:\\Python314-x64" - PYTHON_VERSION: "3.14.0rc3" + PYTHON_VERSION: "3.14.0" PYTHON_ARCH: "64" PYTHON_EXE: python @@ -63,11 +63,6 @@ environment: PYTHON_ARCH: "64" PYTHON_EXE: python - - PYTHON: "C:\\Python39-x64" - PYTHON_VERSION: "3.9.x" - PYTHON_ARCH: "64" - PYTHON_EXE: python - # The images have moved to MySQL8 only, and that cannot