diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index d47e3a950..1f54717a1 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -258,6 +258,10 @@ if [ -z "$GREEN_FRAMEWORK" ]; then # Use --capture=tee-sys so pytest prints test output inline: # https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 $TEST_ARGS + if [ -z "$TEST_ARGS" ]; then # TODO: remove this in PYTHON-4528 + python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 test/synchronous/ $TEST_ARGS + fi + python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 test/asynchronous/ $TEST_ARGS else python green_framework_test.py $GREEN_FRAMEWORK -v $TEST_ARGS fi diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index b93c93c02..cbac42f54 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -71,6 +71,9 @@ jobs: - name: Run tests run: | tox -m test + - name: Run async tests + run: | + tox -m test-async doctest: runs-on: ubuntu-latest @@ -203,3 +206,5 @@ jobs: which python pip install -e ".[test]" pytest -v + pytest -v test/synchronous/ + pytest -v test/asynchronous/ diff --git a/test/asynchronous/test_collection.py b/test/asynchronous/test_collection.py index 45f1c34c9..9e40f53a4 100644 --- a/test/asynchronous/test_collection.py +++ b/test/asynchronous/test_collection.py @@ -28,7 +28,10 @@ from pymongo.asynchronous.database import AsyncDatabase sys.path[0:0] = [""] from test import unittest -from test.asynchronous import AsyncIntegrationTest, async_client_context +from test.asynchronous import ( # TODO: fix sync imports in PYTHON-4528 + AsyncIntegrationTest, + async_client_context, +) from test.utils import ( IMPOSSIBLE_WRITE_CONCERN, EventListener, @@ -472,8 +475,8 @@ class AsyncTestCollection(AsyncIntegrationTest): async def test_index_haystack(self): db = self.db await db.test.drop() - _id = await db.test.insert_one( - {"pos": {"long": 34.2, "lat": 33.3}, "type": "restaurant"} + _id = ( + await db.test.insert_one({"pos": {"long": 34.2, "lat": 33.3}, "type": "restaurant"}) ).inserted_id await db.test.insert_one({"pos": {"long": 34.2, "lat": 37.3}, "type": "restaurant"}) await db.test.insert_one({"pos": {"long": 59.1, "lat": 87.2}, "type": "office"}) diff --git a/test/synchronous/conftest.py b/test/synchronous/conftest.py index 5befb96e1..58f04ea7c 100644 --- a/test/synchronous/conftest.py +++ b/test/synchronous/conftest.py @@ -1,6 +1,6 @@ from __future__ import annotations -from test.synchronous import setup, teardown +from test import setup, teardown import pytest diff --git a/test/synchronous/test_collection.py b/test/synchronous/test_collection.py index d46b43989..7d105acb6 100644 --- a/test/synchronous/test_collection.py +++ b/test/synchronous/test_collection.py @@ -27,8 +27,11 @@ from pymongo.synchronous.database import Database sys.path[0:0] = [""] -from test import unittest -from test.synchronous import IntegrationTest, client_context +from test import ( # TODO: fix sync imports in PYTHON-4528 + IntegrationTest, + client_context, + unittest, +) from test.utils import ( IMPOSSIBLE_WRITE_CONCERN, EventListener, @@ -461,8 +464,8 @@ class TestCollection(IntegrationTest): def test_index_haystack(self): db = self.db db.test.drop() - _id = db.test.insert_one( - {"pos": {"long": 34.2, "lat": 33.3}, "type": "restaurant"} + _id = ( + db.test.insert_one({"pos": {"long": 34.2, "lat": 33.3}, "type": "restaurant"}) ).inserted_id db.test.insert_one({"pos": {"long": 34.2, "lat": 37.3}, "type": "restaurant"}) db.test.insert_one({"pos": {"long": 59.1, "lat": 87.2}, "type": "office"}) diff --git a/tools/synchro.py b/tools/synchro.py index b13a0a351..1c555748f 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -92,6 +92,8 @@ docstring_replacements: dict[tuple[str, str], str] = { type_replacements = {"_Condition": "threading.Condition"} +import_replacements = {"test.synchronous": "test"} + _pymongo_base = "./pymongo/asynchronous/" _gridfs_base = "./gridfs/asynchronous/" _test_base = "./test/asynchronous/" @@ -136,23 +138,31 @@ def process_files(files: list[str]) -> None: if "__init__" not in file or "__init__" and "test" in file: with open(file, "r+") as f: lines = f.readlines() - lines = apply_is_sync(lines) + lines = apply_is_sync(lines, file) lines = translate_coroutine_types(lines) lines = translate_async_sleeps(lines) if file in docstring_translate_files: lines = translate_docstrings(lines) translate_locks(lines) translate_types(lines) + if file in sync_test_files: + translate_imports(lines) f.seek(0) f.writelines(lines) f.truncate() -def apply_is_sync(lines: list[str]) -> list[str]: - is_sync = next(iter([line for line in lines if line.startswith("_IS_SYNC = ")])) - index = lines.index(is_sync) - is_sync = is_sync.replace("False", "True") - lines[index] = is_sync +def apply_is_sync(lines: list[str], file: str) -> list[str]: + try: + is_sync = next(iter([line for line in lines if line.startswith("_IS_SYNC = ")])) + index = lines.index(is_sync) + is_sync = is_sync.replace("False", "True") + lines[index] = is_sync + except StopIteration as e: + print( + f"Missing _IS_SYNC at top of async file {file.replace('synchronous', 'asynchronous')}" + ) + raise e return lines @@ -196,6 +206,15 @@ def translate_types(lines: list[str]) -> list[str]: return lines +def translate_imports(lines: list[str]) -> list[str]: + for k, v in import_replacements.items(): + matches = [line for line in lines if k in line and "import" in line] + for line in matches: + index = lines.index(line) + lines[index] = line.replace(k, v) + return lines + + def translate_async_sleeps(lines: list[str]) -> list[str]: blocking_sleeps = [line for line in lines if "asyncio.sleep(0)" in line] lines = [line for line in lines if line not in blocking_sleeps] diff --git a/tools/synchro.sh b/tools/synchro.sh index fe48b663b..f5e7ab68c 100644 --- a/tools/synchro.sh +++ b/tools/synchro.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -eu python ./tools/synchro.py python -m ruff check pymongo/synchronous/ gridfs/synchronous/ test/synchronous --fix --silent diff --git a/tox.ini b/tox.ini index 331c73ce1..a154bf424 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,8 @@ requires = envlist = # Test using the system Python. test, + # Test async tests using the system Python. + test-async, # Test using the run-tests Evergreen script. test-eg, # Set up encryption files and services. @@ -34,6 +36,7 @@ envlist = labels = # Use labels and -m instead of -e so that tox -m