From a480e7d13f73c596105eaf2775d16162496aab32 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Jul 2025 09:55:09 -0500 Subject: [PATCH 01/11] PYTHON-5027 Test Windows with Python 3.14t --- .evergreen/generated_configs/variants.yml | 9 +++++++++ .evergreen/scripts/generate_config.py | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.evergreen/generated_configs/variants.yml b/.evergreen/generated_configs/variants.yml index e98b0d342c..f02464a86a 100644 --- a/.evergreen/generated_configs/variants.yml +++ b/.evergreen/generated_configs/variants.yml @@ -319,6 +319,15 @@ buildvariants: expansions: PYTHON_BINARY: /Library/Frameworks/PythonT.Framework/Versions/3.13/bin/python3t tags: [] + - name: free-threaded-win64-python3.14t + tasks: + - name: .free-threading + display_name: Free-threaded Win64 Python3.14t + run_on: + - windows-64-vsMulti-small + expansions: + PYTHON_BINARY: C:/python/Python314t/python.exe + tags: [] # Green framework tests - name: green-eventlet-rhel8 diff --git a/.evergreen/scripts/generate_config.py b/.evergreen/scripts/generate_config.py index a30beb1d39..c27cfe130f 100644 --- a/.evergreen/scripts/generate_config.py +++ b/.evergreen/scripts/generate_config.py @@ -108,14 +108,14 @@ def create_free_threaded_variants() -> list[BuildVariant]: variants = [] for host_name in ("rhel8", "macos", "macos-arm64", "win64"): if host_name == "win64": - # TODO: PYTHON-5027 - continue + python = "3.14t" + else: + python = "3.13t" tasks = [".free-threading"] tags = [] if host_name == "rhel8": tags.append("pr") host = HOSTS[host_name] - python = "3.13t" display_name = get_variant_name("Free-threaded", host, python=python) variant = create_variant(tasks, display_name, tags=tags, python=python, host=host) variants.append(variant) From abdb71f2ebc988210a43f16ff622cffcbdef6ddb Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Jul 2025 10:13:51 -0500 Subject: [PATCH 02/11] fix python bin handling --- .evergreen/generated_configs/variants.yml | 6 +++--- .evergreen/scripts/generate_config_utils.py | 11 ++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.evergreen/generated_configs/variants.yml b/.evergreen/generated_configs/variants.yml index f02464a86a..362f828452 100644 --- a/.evergreen/generated_configs/variants.yml +++ b/.evergreen/generated_configs/variants.yml @@ -308,7 +308,7 @@ buildvariants: run_on: - macos-14 expansions: - PYTHON_BINARY: /Library/Frameworks/PythonT.Framework/Versions/3.13/bin/python3t + PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13t/bin/python3t tags: [] - name: free-threaded-macos-arm64-python3.13t tasks: @@ -317,7 +317,7 @@ buildvariants: run_on: - macos-14-arm64 expansions: - PYTHON_BINARY: /Library/Frameworks/PythonT.Framework/Versions/3.13/bin/python3t + PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13t/bin/python3t tags: [] - name: free-threaded-win64-python3.14t tasks: @@ -326,7 +326,7 @@ buildvariants: run_on: - windows-64-vsMulti-small expansions: - PYTHON_BINARY: C:/python/Python314t/python.exe + PYTHON_BINARY: C:/python/Python314t/python3.14t.exe tags: [] # Green framework tests diff --git a/.evergreen/scripts/generate_config_utils.py b/.evergreen/scripts/generate_config_utils.py index 62ea982cd8..c1bf83d422 100644 --- a/.evergreen/scripts/generate_config_utils.py +++ b/.evergreen/scripts/generate_config_utils.py @@ -153,18 +153,15 @@ def get_python_binary(python: str, host: Host) -> str: base = "C:/python/32" else: base = "C:/python" - python = python.replace(".", "") - if python == "313t": - return f"{base}/Python313/python3.13t.exe" - return f"{base}/Python{python}/python.exe" + python_dir = python.replace(".", "") + return f"{base}/Python{python_dir}/python{python}.exe" if name in ["rhel8", "ubuntu22", "ubuntu20", "rhel7"]: return f"/opt/python/{python}/bin/python3" if name in ["macos", "macos-arm64"]: - if python == "3.13t": - return "/Library/Frameworks/PythonT.Framework/Versions/3.13/bin/python3t" - return f"/Library/Frameworks/Python.Framework/Versions/{python}/bin/python3" + bin_name = "python3t" if "t" in python else "python3" + return f"/Library/Frameworks/Python.Framework/Versions/{python}/bin/{bin_name}" raise ValueError(f"no match found for python {python} on {name}") From 5ea46f12209bf6a8815132496f665bc0022a4b21 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Jul 2025 14:35:12 -0500 Subject: [PATCH 03/11] fix dir handling --- .evergreen/generated_configs/variants.yml | 6 +++--- .evergreen/scripts/generate_config_utils.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.evergreen/generated_configs/variants.yml b/.evergreen/generated_configs/variants.yml index 362f828452..f390111255 100644 --- a/.evergreen/generated_configs/variants.yml +++ b/.evergreen/generated_configs/variants.yml @@ -308,7 +308,7 @@ buildvariants: run_on: - macos-14 expansions: - PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13t/bin/python3t + PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3t tags: [] - name: free-threaded-macos-arm64-python3.13t tasks: @@ -317,7 +317,7 @@ buildvariants: run_on: - macos-14-arm64 expansions: - PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13t/bin/python3t + PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3t tags: [] - name: free-threaded-win64-python3.14t tasks: @@ -326,7 +326,7 @@ buildvariants: run_on: - windows-64-vsMulti-small expansions: - PYTHON_BINARY: C:/python/Python314t/python3.14t.exe + PYTHON_BINARY: C:/python/Python314/python3.14t.exe tags: [] # Green framework tests diff --git a/.evergreen/scripts/generate_config_utils.py b/.evergreen/scripts/generate_config_utils.py index c1bf83d422..d178bcb084 100644 --- a/.evergreen/scripts/generate_config_utils.py +++ b/.evergreen/scripts/generate_config_utils.py @@ -153,7 +153,7 @@ def get_python_binary(python: str, host: Host) -> str: base = "C:/python/32" else: base = "C:/python" - python_dir = python.replace(".", "") + python_dir = python.replace(".", "").replace("t", "") return f"{base}/Python{python_dir}/python{python}.exe" if name in ["rhel8", "ubuntu22", "ubuntu20", "rhel7"]: @@ -161,7 +161,8 @@ def get_python_binary(python: str, host: Host) -> str: if name in ["macos", "macos-arm64"]: bin_name = "python3t" if "t" in python else "python3" - return f"/Library/Frameworks/Python.Framework/Versions/{python}/bin/{bin_name}" + python_dir = python.replace("t", "") + return f"/Library/Frameworks/Python.Framework/Versions/{python_dir}/bin/{bin_name}" raise ValueError(f"no match found for python {python} on {name}") From 75b078068eee9443bf04796430bd2a1f775bb65b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Jul 2025 14:42:31 -0500 Subject: [PATCH 04/11] fix dir handling --- .evergreen/generated_configs/variants.yml | 4 ++-- .evergreen/scripts/generate_config_utils.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.evergreen/generated_configs/variants.yml b/.evergreen/generated_configs/variants.yml index f390111255..d4726a22a9 100644 --- a/.evergreen/generated_configs/variants.yml +++ b/.evergreen/generated_configs/variants.yml @@ -308,7 +308,7 @@ buildvariants: run_on: - macos-14 expansions: - PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3t + PYTHON_BINARY: /Library/Frameworks/PythonT.Framework/Versions/3.13/bin/python3t tags: [] - name: free-threaded-macos-arm64-python3.13t tasks: @@ -317,7 +317,7 @@ buildvariants: run_on: - macos-14-arm64 expansions: - PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3t + PYTHON_BINARY: /Library/Frameworks/PythonT.Framework/Versions/3.13/bin/python3t tags: [] - name: free-threaded-win64-python3.14t tasks: diff --git a/.evergreen/scripts/generate_config_utils.py b/.evergreen/scripts/generate_config_utils.py index d178bcb084..e676200d38 100644 --- a/.evergreen/scripts/generate_config_utils.py +++ b/.evergreen/scripts/generate_config_utils.py @@ -162,7 +162,8 @@ def get_python_binary(python: str, host: Host) -> str: if name in ["macos", "macos-arm64"]: bin_name = "python3t" if "t" in python else "python3" python_dir = python.replace("t", "") - return f"/Library/Frameworks/Python.Framework/Versions/{python_dir}/bin/{bin_name}" + framework_dir = "PythonT" if "t" in python else "Python" + return f"/Library/Frameworks/{framework_dir}.Framework/Versions/{python_dir}/bin/{bin_name}" raise ValueError(f"no match found for python {python} on {name}") From 3d711b921f9fe47627ddd7c38b590872d3092ace Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Jul 2025 14:45:57 -0500 Subject: [PATCH 05/11] address warning --- test/__init__.py | 2 +- test/asynchronous/__init__.py | 2 +- test/asynchronous/helpers.py | 2 +- test/asynchronous/test_comment.py | 2 +- test/asynchronous/test_session.py | 2 +- test/asynchronous/unified_format.py | 2 +- test/asynchronous/utils.py | 2 +- test/asynchronous/utils_spec_runner.py | 2 +- test/helpers.py | 2 +- test/test_comment.py | 2 +- test/test_session.py | 2 +- test/unified_format.py | 2 +- test/utils.py | 2 +- test/utils_spec_runner.py | 2 +- tools/convert_test_to_async.py | 4 ++-- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index 40d58cff89..95c2d7ee9d 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -30,7 +30,7 @@ import traceback import unittest import warnings -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from pymongo.errors import AutoReconnect from pymongo.synchronous.uri_parser import parse_uri diff --git a/test/asynchronous/__init__.py b/test/asynchronous/__init__.py index 5d52c348df..96769dc9c5 100644 --- a/test/asynchronous/__init__.py +++ b/test/asynchronous/__init__.py @@ -30,7 +30,7 @@ import traceback import unittest import warnings -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from pymongo.asynchronous.uri_parser import parse_uri from pymongo.errors import AutoReconnect diff --git a/test/asynchronous/helpers.py b/test/asynchronous/helpers.py index 49b9af9bf7..bcb004af51 100644 --- a/test/asynchronous/helpers.py +++ b/test/asynchronous/helpers.py @@ -29,7 +29,7 @@ import traceback import unittest import warnings -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from pymongo._asyncio_task import create_task diff --git a/test/asynchronous/test_comment.py b/test/asynchronous/test_comment.py index d3ddaf2b65..2d6d0f5f1e 100644 --- a/test/asynchronous/test_comment.py +++ b/test/asynchronous/test_comment.py @@ -20,7 +20,7 @@ import sys sys.path[0:0] = [""] -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from test.asynchronous import AsyncIntegrationTest, async_client_context, unittest from test.utils_shared import OvertCommandListener diff --git a/test/asynchronous/test_session.py b/test/asynchronous/test_session.py index d357948ed0..5ed3597751 100644 --- a/test/asynchronous/test_session.py +++ b/test/asynchronous/test_session.py @@ -19,7 +19,7 @@ import copy import sys import time -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from io import BytesIO from test.asynchronous.helpers import ExceptionCatchingTask from typing import Any, Callable, List, Set, Tuple diff --git a/test/asynchronous/unified_format.py b/test/asynchronous/unified_format.py index 5b2e3563f9..b9cebd0fab 100644 --- a/test/asynchronous/unified_format.py +++ b/test/asynchronous/unified_format.py @@ -27,8 +27,8 @@ import sys import time import traceback -from asyncio import iscoroutinefunction from collections import defaultdict +from inspect import iscoroutinefunction from test.asynchronous import ( AsyncIntegrationTest, async_client_context, diff --git a/test/asynchronous/utils.py b/test/asynchronous/utils.py index 2b82355fc1..02ba46c71a 100644 --- a/test/asynchronous/utils.py +++ b/test/asynchronous/utils.py @@ -23,8 +23,8 @@ import threading # Used in the synchronized version of this file import time import traceback -from asyncio import iscoroutinefunction from functools import wraps +from inspect import iscoroutinefunction from bson.son import SON from pymongo import AsyncMongoClient diff --git a/test/asynchronous/utils_spec_runner.py b/test/asynchronous/utils_spec_runner.py index 3ce2984c62..496c28a045 100644 --- a/test/asynchronous/utils_spec_runner.py +++ b/test/asynchronous/utils_spec_runner.py @@ -20,8 +20,8 @@ import os import time import unittest -from asyncio import iscoroutinefunction from collections import abc +from inspect import iscoroutinefunction from test.asynchronous import AsyncIntegrationTest, async_client_context, client_knobs from test.asynchronous.helpers import ConcurrentRunner from test.utils_shared import ( diff --git a/test/helpers.py b/test/helpers.py index ccf19a9228..22bdc0d25d 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -29,7 +29,7 @@ import traceback import unittest import warnings -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from pymongo._asyncio_task import create_task diff --git a/test/test_comment.py b/test/test_comment.py index b6c17c14fe..bcab0061fa 100644 --- a/test/test_comment.py +++ b/test/test_comment.py @@ -20,7 +20,7 @@ import sys sys.path[0:0] = [""] -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from test import IntegrationTest, client_context, unittest from test.utils_shared import OvertCommandListener diff --git a/test/test_session.py b/test/test_session.py index d8add9f3b6..16a219ae52 100644 --- a/test/test_session.py +++ b/test/test_session.py @@ -19,7 +19,7 @@ import copy import sys import time -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from io import BytesIO from test.helpers import ExceptionCatchingTask from typing import Any, Callable, List, Set, Tuple diff --git a/test/unified_format.py b/test/unified_format.py index 859cb29977..2c10506fc0 100644 --- a/test/unified_format.py +++ b/test/unified_format.py @@ -27,8 +27,8 @@ import sys import time import traceback -from asyncio import iscoroutinefunction from collections import defaultdict +from inspect import iscoroutinefunction from test import ( IntegrationTest, client_context, diff --git a/test/utils.py b/test/utils.py index 3447440927..bfc606fe83 100644 --- a/test/utils.py +++ b/test/utils.py @@ -23,8 +23,8 @@ import threading # Used in the synchronized version of this file import time import traceback -from asyncio import iscoroutinefunction from functools import wraps +from inspect import iscoroutinefunction from bson.son import SON from pymongo import MongoClient diff --git a/test/utils_spec_runner.py b/test/utils_spec_runner.py index c0a8c81e30..46adeaefb5 100644 --- a/test/utils_spec_runner.py +++ b/test/utils_spec_runner.py @@ -20,8 +20,8 @@ import os import time import unittest -from asyncio import iscoroutinefunction from collections import abc +from inspect import iscoroutinefunction from test import IntegrationTest, client_context, client_knobs from test.helpers import ConcurrentRunner from test.utils_shared import ( diff --git a/tools/convert_test_to_async.py b/tools/convert_test_to_async.py index dbdb217c84..6c68c34bf3 100644 --- a/tools/convert_test_to_async.py +++ b/tools/convert_test_to_async.py @@ -1,6 +1,6 @@ from __future__ import annotations -import asyncio +import inspect import sys from pymongo import AsyncMongoClient @@ -83,7 +83,7 @@ def get_async_methods() -> set[str]: for k, v in vars(x).items() if callable(v) and not isinstance(v, classmethod) - and asyncio.iscoroutinefunction(v) + and inspect.iscoroutinefunction(v) and v.__name__[0] != "_" } result = result | methods From 24e40d07a48c6a4f9fa7e2e847d84b5dfb37cf6c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Jul 2025 14:56:01 -0500 Subject: [PATCH 06/11] address warning --- test/utils_shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils_shared.py b/test/utils_shared.py index e0789b6632..f2e8852f0c 100644 --- a/test/utils_shared.py +++ b/test/utils_shared.py @@ -26,9 +26,9 @@ import threading import unittest import warnings -from asyncio import iscoroutinefunction from collections import abc, defaultdict from functools import partial +from inspect import iscoroutinefunction from test import client_context from test.asynchronous.utils import async_wait_until from test.utils import wait_until From 03692f9818a3f744f35fc41b00e848f6fbd22a10 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Jul 2025 15:07:49 -0500 Subject: [PATCH 07/11] handle warnings --- test/test_bson_binary_vector.py | 3 +-- test/test_bson_corpus.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/test_bson_binary_vector.py b/test/test_bson_binary_vector.py index ba3eff8bb2..2783338793 100644 --- a/test/test_bson_binary_vector.py +++ b/test/test_bson_binary_vector.py @@ -15,7 +15,6 @@ from __future__ import annotations import binascii -import codecs import struct from pathlib import Path from test import unittest @@ -111,7 +110,7 @@ def run_test(self): def create_tests(): for filename in _TEST_PATH.glob("*.json"): - with codecs.open(str(filename), encoding="utf-8") as test_file: + with open(str(filename), encoding="utf-8") as test_file: test_method = create_test(json_util.loads(test_file.read())) setattr(TestBSONBinaryVector, "test_" + filename.stem, test_method) diff --git a/test/test_bson_corpus.py b/test/test_bson_corpus.py index 96ef458ec5..504025e766 100644 --- a/test/test_bson_corpus.py +++ b/test/test_bson_corpus.py @@ -16,7 +16,6 @@ from __future__ import annotations import binascii -import codecs import functools import glob import json @@ -227,7 +226,7 @@ def run_test(self): def create_tests(): for filename in glob.glob(os.path.join(_TEST_PATH, "*.json")): test_suffix, _ = os.path.splitext(os.path.basename(filename)) - with codecs.open(filename, encoding="utf-8") as bson_test_file: + with open(filename, encoding="utf-8") as bson_test_file: test_method = create_test(json.load(bson_test_file)) setattr(TestBSONCorpus, "test_" + test_suffix, test_method) From d920500fd51667125b9204d5958d0e1f2a1c6e17 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Jul 2025 15:25:40 -0500 Subject: [PATCH 08/11] ignore warning --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index fb2dd58131..ef01594651 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,6 +111,8 @@ filterwarnings = [ "module:Wire protocol compression with:UserWarning", "module:GridIn property:DeprecationWarning", "module:GridOut property:DeprecationWarning", + # TODO: this needs a ticket + "module:WindowsSelectorEventLoopPolicy:DeprecationWarning", # TODO: Remove as part of PYTHON-3923. "module:unclosed Date: Wed, 23 Jul 2025 15:56:49 -0500 Subject: [PATCH 09/11] ignore warning --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ef01594651..9b4aaee119 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,7 +112,7 @@ filterwarnings = [ "module:GridIn property:DeprecationWarning", "module:GridOut property:DeprecationWarning", # TODO: this needs a ticket - "module:WindowsSelectorEventLoopPolicy:DeprecationWarning", + "module:.*WindowsSelectorEventLoopPolicy:DeprecationWarning", # TODO: Remove as part of PYTHON-3923. "module:unclosed Date: Wed, 23 Jul 2025 16:32:12 -0500 Subject: [PATCH 10/11] handle warnings --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9b4aaee119..28693591f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,8 +111,9 @@ filterwarnings = [ "module:Wire protocol compression with:UserWarning", "module:GridIn property:DeprecationWarning", "module:GridOut property:DeprecationWarning", - # TODO: this needs a ticket + # pytest-asyncio known issue: https://github.com/pytest-dev/pytest-asyncio/issues/1032 "module:.*WindowsSelectorEventLoopPolicy:DeprecationWarning", + "module:.*get_event_loop_policy:DeprecationWarning", # TODO: Remove as part of PYTHON-3923. "module:unclosed Date: Wed, 23 Jul 2025 19:41:40 -0500 Subject: [PATCH 11/11] handle warnings --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 28693591f1..e7e3161906 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,7 +113,7 @@ filterwarnings = [ "module:GridOut property:DeprecationWarning", # pytest-asyncio known issue: https://github.com/pytest-dev/pytest-asyncio/issues/1032 "module:.*WindowsSelectorEventLoopPolicy:DeprecationWarning", - "module:.*get_event_loop_policy:DeprecationWarning", + "module:.*et_event_loop_policy:DeprecationWarning", # TODO: Remove as part of PYTHON-3923. "module:unclosed