From 6b2490d0f078f3a1b0d1e05eb0a2e9fed6317d56 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 21 Jan 2025 09:35:11 -0600 Subject: [PATCH 1/2] PYTHON-5049 Drop support for PyPy 3.9 --- .evergreen/generated_configs/variants.yml | 64 +++++------------------ .evergreen/scripts/generate_config.py | 2 +- .github/workflows/test-python.yml | 2 +- README.md | 2 +- doc/changelog.rst | 4 +- doc/faq.rst | 2 +- doc/installation.rst | 2 +- doc/python3.rst | 2 +- test/asynchronous/test_client.py | 5 +- test/asynchronous/test_collection.py | 9 ++-- test/asynchronous/test_database.py | 5 +- test/test_client.py | 5 +- test/test_collection.py | 9 ++-- test/test_database.py | 5 +- test/test_errors.py | 12 ++--- 15 files changed, 33 insertions(+), 97 deletions(-) diff --git a/.evergreen/generated_configs/variants.yml b/.evergreen/generated_configs/variants.yml index b1db61d492..79c9b22c93 100644 --- a/.evergreen/generated_configs/variants.yml +++ b/.evergreen/generated_configs/variants.yml @@ -256,15 +256,15 @@ buildvariants: expansions: COMPRESSORS: zstd PYTHON_BINARY: /opt/python/3.9/bin/python3 - - name: compression-snappy-rhel8-pypy3.9 + - name: compression-snappy-rhel8-pypy3.10 tasks: - name: .standalone .noauth .nossl .sync_async - display_name: Compression snappy RHEL8 PyPy3.9 + display_name: Compression snappy RHEL8 PyPy3.10 run_on: - rhel87-small expansions: COMPRESSORS: snappy - PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 + PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 - name: compression-zlib-rhel8-pypy3.10 tasks: - name: .standalone .noauth .nossl .sync_async @@ -274,15 +274,15 @@ buildvariants: expansions: COMPRESSORS: zlib PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 - - name: compression-zstd-rhel8-pypy3.9 + - name: compression-zstd-rhel8-pypy3.10 tasks: - name: .standalone .noauth .nossl .sync_async !.4.0 - display_name: Compression zstd RHEL8 PyPy3.9 + display_name: Compression zstd RHEL8 PyPy3.10 run_on: - rhel87-small expansions: COMPRESSORS: zstd - PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 + PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 # Disable test commands tests - name: disable-test-commands-rhel8-python3.9 @@ -460,15 +460,6 @@ buildvariants: test_encryption: "true" test_encryption_pyopenssl: "true" PYTHON_BINARY: /opt/python/3.12/bin/python3 - - name: encryption-rhel8-pypy3.9 - tasks: - - name: .sharded_cluster .auth .ssl .sync_async - display_name: Encryption RHEL8 PyPy3.9 - run_on: - - rhel87-small - expansions: - test_encryption: "true" - PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 - name: encryption-macos-python3.9 tasks: - name: .latest .replica_set .sync_async @@ -608,15 +599,6 @@ buildvariants: expansions: AUTH: auth PYTHON_BINARY: C:/python/Python313/python.exe - - name: auth-enterprise-rhel8-pypy3.9-auth - tasks: - - name: test-enterprise-auth - display_name: Auth Enterprise RHEL8 PyPy3.9 Auth - run_on: - - rhel87-small - expansions: - AUTH: auth - PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 - name: auth-enterprise-rhel8-pypy3.10-auth tasks: - name: test-enterprise-auth @@ -900,10 +882,10 @@ buildvariants: TOPOLOGY: server VERSION: "8.0" PYTHON_BINARY: /opt/python/3.13/bin/python3 - - name: ocsp-rhel8-rapid-pypy3.9 + - name: ocsp-rhel8-rapid-pypy3.10 tasks: - name: .ocsp - display_name: OCSP RHEL8 rapid PyPy3.9 + display_name: OCSP RHEL8 rapid PyPy3.10 run_on: - rhel87-small batchtime: 20160 @@ -912,11 +894,11 @@ buildvariants: SSL: ssl TOPOLOGY: server VERSION: rapid - PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 - - name: ocsp-rhel8-latest-pypy3.10 + PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 + - name: ocsp-rhel8-latest-python3.9 tasks: - name: .ocsp - display_name: OCSP RHEL8 latest PyPy3.10 + display_name: OCSP RHEL8 latest Python3.9 run_on: - rhel87-small batchtime: 20160 @@ -925,7 +907,7 @@ buildvariants: SSL: ssl TOPOLOGY: server VERSION: latest - PYTHON_BINARY: /opt/python/pypy3.10/bin/python3 + PYTHON_BINARY: /opt/python/3.9/bin/python3 - name: ocsp-win64-v4.4-python3.9 tasks: - name: .ocsp-rsa !.ocsp-staple @@ -1061,17 +1043,6 @@ buildvariants: expansions: test_pyopenssl: "true" PYTHON_BINARY: C:/python/Python313/python.exe - - name: pyopenssl-rhel8-pypy3.9 - tasks: - - name: .replica_set .auth .ssl .sync_async - - name: .7.0 .auth .ssl .sync_async - display_name: PyOpenSSL RHEL8 PyPy3.9 - run_on: - - rhel87-small - batchtime: 10080 - expansions: - test_pyopenssl: "true" - PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 - name: pyopenssl-rhel8-pypy3.10 tasks: - name: .replica_set .auth .ssl .sync_async @@ -1164,17 +1135,6 @@ buildvariants: expansions: COVERAGE: coverage PYTHON_BINARY: /opt/python/3.12/bin/python3 - - name: test-rhel8-pypy3.9 - tasks: - - name: .sharded_cluster .auth .ssl .sync_async - - name: .replica_set .noauth .ssl .sync_async - - name: .standalone .noauth .nossl .sync_async - display_name: "* Test RHEL8 PyPy3.9" - run_on: - - rhel87-small - expansions: - COVERAGE: coverage - PYTHON_BINARY: /opt/python/pypy3.9/bin/python3 - name: test-macos-python3.9 tasks: - name: .sharded_cluster .auth .ssl !.sync_async diff --git a/.evergreen/scripts/generate_config.py b/.evergreen/scripts/generate_config.py index e8d0b171bd..2917e882d8 100644 --- a/.evergreen/scripts/generate_config.py +++ b/.evergreen/scripts/generate_config.py @@ -28,7 +28,7 @@ ALL_VERSIONS = ["4.0", "4.4", "5.0", "6.0", "7.0", "8.0", "rapid", "latest"] CPYTHONS = ["3.9", "3.10", "3.11", "3.12", "3.13"] -PYPYS = ["pypy3.9", "pypy3.10"] +PYPYS = ["pypy3.10"] ALL_PYTHONS = CPYTHONS + PYPYS MIN_MAX_PYTHON = [CPYTHONS[0], CPYTHONS[-1]] BATCHTIME_WEEK = 10080 diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index a41daaabb1..3760e308a5 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -55,7 +55,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04] - python-version: ["3.9", "pypy-3.9", "3.13", "3.13t"] + python-version: ["3.9", "pypy-3.10", "3.13", "3.13t"] name: CPython ${{ matrix.python-version }}-${{ matrix.os }} steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index bd0755620e..b8e0078101 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ package that is incompatible with PyMongo. ## Dependencies -PyMongo supports CPython 3.9+ and PyPy3.9+. +PyMongo supports CPython 3.9+ and PyPy3.10+. Required dependencies: diff --git a/doc/changelog.rst b/doc/changelog.rst index fba6713bd9..4942d85de8 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -4,7 +4,7 @@ Changelog Changes in Version 4.11.0 (YYYY/MM/DD) -------------------------------------- -.. warning:: PyMongo 4.11 drops support for Python 3.8: Python 3.9+ or PyPy 3.9+ is now required. +.. warning:: PyMongo 4.11 drops support for Python 3.8 and PyPy 3.9: Python 3.9+ or PyPy 3.10+ is now required. .. warning:: PyMongo 4.11 drops support for MongoDB 3.6. PyMongo now supports MongoDB 4.0+. Driver support for MongoDB 3.6 reached end of life in April 2024. .. warning:: Driver support for MongoDB 4.0 reaches end of life in April 2025. @@ -14,7 +14,7 @@ Changes in Version 4.11.0 (YYYY/MM/DD) PyMongo 4.11 brings a number of changes including: -- Dropped support for Python 3.8. +- Dropped support for Python 3.8 and PyPy 3.9. - Dropped support for MongoDB 3.6. - Dropped support for the MONGODB-CR authenticate mechanism, which is no longer supported by MongoDB 4.0+. - pymongocrypt>=1.12 is now required for :ref:`In-Use Encryption` support. diff --git a/doc/faq.rst b/doc/faq.rst index 15950e7716..73d0ec8966 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -166,7 +166,7 @@ they are returned to the pool. Does PyMongo support Python 3? ------------------------------ -PyMongo supports CPython 3.9+ and PyPy3.9+. See the :doc:`python3` for details. +PyMongo supports CPython 3.9+ and PyPy3.10+. See the :doc:`python3` for details. Does PyMongo support asynchronous frameworks like Gevent, asyncio, Tornado, or Twisted? --------------------------------------------------------------------------------------- diff --git a/doc/installation.rst b/doc/installation.rst index f21a3792ad..abda06db16 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -28,7 +28,7 @@ To upgrade using pip:: Dependencies ------------ -PyMongo supports CPython 3.9+ and PyPy3.9+. +PyMongo supports CPython 3.9+ and PyPy3.10+. Required dependencies ..................... diff --git a/doc/python3.rst b/doc/python3.rst index 1ea43b3ccb..0a63f968a5 100644 --- a/doc/python3.rst +++ b/doc/python3.rst @@ -4,7 +4,7 @@ Python 3 FAQ What Python 3 versions are supported? ------------------------------------- -PyMongo supports CPython 3.9+ and PyPy3.9+. +PyMongo supports CPython 3.9+ and PyPy3.10+. Are there any PyMongo behavior changes with Python 3? ----------------------------------------------------- diff --git a/test/asynchronous/test_client.py b/test/asynchronous/test_client.py index db232386ee..744a170be2 100644 --- a/test/asynchronous/test_client.py +++ b/test/asynchronous/test_client.py @@ -237,10 +237,7 @@ def test_getattr(self): def test_iteration(self): client = self.client - if "PyPy" in sys.version and sys.version_info < (3, 8, 15): - msg = "'NoneType' object is not callable" - else: - msg = "'AsyncMongoClient' object is not iterable" + msg = "'AsyncMongoClient' object is not iterable" # Iteration fails with self.assertRaisesRegex(TypeError, msg): for _ in client: # type: ignore[misc] # error: "None" not callable [misc] diff --git a/test/asynchronous/test_collection.py b/test/asynchronous/test_collection.py index 528919f63c..75e26f6cd1 100644 --- a/test/asynchronous/test_collection.py +++ b/test/asynchronous/test_collection.py @@ -133,13 +133,10 @@ def test_getattr(self): def test_iteration(self): coll = self.db.coll - if "PyPy" in sys.version and sys.version_info < (3, 8, 15): - msg = "'NoneType' object is not callable" + if _IS_SYNC: + msg = "'Collection' object is not iterable" else: - if _IS_SYNC: - msg = "'Collection' object is not iterable" - else: - msg = "'AsyncCollection' object is not iterable" + msg = "'AsyncCollection' object is not iterable" # Iteration fails with self.assertRaisesRegex(TypeError, msg): for _ in coll: # type: ignore[misc] # error: "None" not callable [misc] diff --git a/test/asynchronous/test_database.py b/test/asynchronous/test_database.py index b5a5960420..55a8cc3ab2 100644 --- a/test/asynchronous/test_database.py +++ b/test/asynchronous/test_database.py @@ -103,10 +103,7 @@ def test_getattr(self): def test_iteration(self): db = self.client.pymongo_test - if "PyPy" in sys.version and sys.version_info < (3, 8, 15): - msg = "'NoneType' object is not callable" - else: - msg = "'AsyncDatabase' object is not iterable" + msg = "'AsyncDatabase' object is not iterable" # Iteration fails with self.assertRaisesRegex(TypeError, msg): for _ in db: # type: ignore[misc] # error: "None" not callable [misc] diff --git a/test/test_client.py b/test/test_client.py index 5ec425f312..2a33077f5f 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -234,10 +234,7 @@ def test_getattr(self): def test_iteration(self): client = self.client - if "PyPy" in sys.version and sys.version_info < (3, 8, 15): - msg = "'NoneType' object is not callable" - else: - msg = "'MongoClient' object is not iterable" + msg = "'MongoClient' object is not iterable" # Iteration fails with self.assertRaisesRegex(TypeError, msg): for _ in client: # type: ignore[misc] # error: "None" not callable [misc] diff --git a/test/test_collection.py b/test/test_collection.py index af524bba47..9a6fcf87e4 100644 --- a/test/test_collection.py +++ b/test/test_collection.py @@ -133,13 +133,10 @@ def test_getattr(self): def test_iteration(self): coll = self.db.coll - if "PyPy" in sys.version and sys.version_info < (3, 8, 15): - msg = "'NoneType' object is not callable" + if _IS_SYNC: + msg = "'Collection' object is not iterable" else: - if _IS_SYNC: - msg = "'Collection' object is not iterable" - else: - msg = "'Collection' object is not iterable" + msg = "'Collection' object is not iterable" # Iteration fails with self.assertRaisesRegex(TypeError, msg): for _ in coll: # type: ignore[misc] # error: "None" not callable [misc] diff --git a/test/test_database.py b/test/test_database.py index 5e854c941d..aad9089bd8 100644 --- a/test/test_database.py +++ b/test/test_database.py @@ -102,10 +102,7 @@ def test_getattr(self): def test_iteration(self): db = self.client.pymongo_test - if "PyPy" in sys.version and sys.version_info < (3, 8, 15): - msg = "'NoneType' object is not callable" - else: - msg = "'Database' object is not iterable" + msg = "'Database' object is not iterable" # Iteration fails with self.assertRaisesRegex(TypeError, msg): for _ in db: # type: ignore[misc] # error: "None" not callable [misc] diff --git a/test/test_errors.py b/test/test_errors.py index 2cee7c15d8..d6db6a4ec1 100644 --- a/test/test_errors.py +++ b/test/test_errors.py @@ -47,15 +47,9 @@ def test_operation_failure(self): self.assertIn("full error", traceback.format_exc()) def _test_unicode_strs(self, exc): - if sys.implementation.name == "pypy" and sys.implementation.version < (7, 3, 7): - # PyPy used to display unicode in repr differently. - self.assertEqual( - "unicode \U0001f40d, full error: {'errmsg': 'unicode \\U0001f40d'}", str(exc) - ) - else: - self.assertEqual( - "unicode \U0001f40d, full error: {'errmsg': 'unicode \U0001f40d'}", str(exc) - ) + self.assertEqual( + "unicode \U0001f40d, full error: {'errmsg': 'unicode \U0001f40d'}", str(exc) + ) try: raise exc except Exception: From d06a6c4812445dae57e7e4ddba18e755ce0e440c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 21 Jan 2025 10:23:19 -0600 Subject: [PATCH 2/2] Address review --- test/asynchronous/test_collection.py | 5 +---- test/test_collection.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/test/asynchronous/test_collection.py b/test/asynchronous/test_collection.py index 75e26f6cd1..beb58012a8 100644 --- a/test/asynchronous/test_collection.py +++ b/test/asynchronous/test_collection.py @@ -133,10 +133,7 @@ def test_getattr(self): def test_iteration(self): coll = self.db.coll - if _IS_SYNC: - msg = "'Collection' object is not iterable" - else: - msg = "'AsyncCollection' object is not iterable" + msg = "'AsyncCollection' object is not iterable" # Iteration fails with self.assertRaisesRegex(TypeError, msg): for _ in coll: # type: ignore[misc] # error: "None" not callable [misc] diff --git a/test/test_collection.py b/test/test_collection.py index 9a6fcf87e4..8a862646eb 100644 --- a/test/test_collection.py +++ b/test/test_collection.py @@ -133,10 +133,7 @@ def test_getattr(self): def test_iteration(self): coll = self.db.coll - if _IS_SYNC: - msg = "'Collection' object is not iterable" - else: - msg = "'Collection' object is not iterable" + msg = "'Collection' object is not iterable" # Iteration fails with self.assertRaisesRegex(TypeError, msg): for _ in coll: # type: ignore[misc] # error: "None" not callable [misc]