diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f5a3171..b52829e9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -88,22 +88,22 @@ 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" - 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" + - "3.14" + - "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 + # exclude: steps: - name: checkout @@ -173,13 +173,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 @@ -212,14 +211,16 @@ 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 - - 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 +246,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 +263,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 +271,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/.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/CHANGES.rst b/CHANGES.rst index 2641ee4a..28362e26 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,10 +2,18 @@ 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 and with psycopg2cffi. +- Build and provide binary wheels with free-threaded support. However, it is + likely that RelStorage dependencies will cause the GIL to be + 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/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 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/scripts/releases/make-manylinux b/scripts/releases/make-manylinux index 37bd065a..6c1bf5ac 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{315,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..cedee837 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", @@ -263,6 +260,7 @@ def read_file(*path): 'always_allow_keywords': False, 'infer_types': True, 'nonecheck': False, + 'freethreading_compatible': True, }, ), tests_require=tests_require, @@ -344,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 @@ -356,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"', ], 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/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 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:])