Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .evergreen/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/test-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -203,3 +206,5 @@ jobs:
which python
pip install -e ".[test]"
pytest -v
pytest -v test/synchronous/
pytest -v test/asynchronous/
9 changes: 6 additions & 3 deletions test/asynchronous/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
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,
Expand Down Expand Up @@ -472,8 +475,8 @@ async def test_index_geo2d(self):
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"})
Expand Down
2 changes: 1 addition & 1 deletion test/synchronous/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from test.synchronous import setup, teardown
from test import setup, teardown

import pytest

Expand Down
11 changes: 7 additions & 4 deletions test/synchronous/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@

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,
Expand Down Expand Up @@ -461,8 +464,8 @@ def test_index_geo2d(self):
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"})
Expand Down
31 changes: 25 additions & 6 deletions tools/synchro.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@

type_replacements = {"_Condition": "threading.Condition"}

import_replacements = {"test.synchronous": "test"}
Copy link
Contributor Author

@NoahStapp NoahStapp Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to reduce the code duplication for the conversion process, due to the bulk of our tests still using the original test/__init__.py and associated helpers. Once we've fully migrated all tests into the test/asynchronous and test/synchronous directories, we will switch to using test.synchronous.<module> again.


_pymongo_base = "./pymongo/asynchronous/"
_gridfs_base = "./gridfs/asynchronous/"
_test_base = "./test/asynchronous/"
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion tools/synchro.sh
Original file line number Diff line number Diff line change
@@ -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
Expand Down
11 changes: 11 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -34,6 +36,7 @@ envlist =

labels = # Use labels and -m instead of -e so that tox -m <label> fails instantly if the label does not exist
test = test
test-async = test-async
test-eg = test-eg
setup-encryption = setup-encryption
teardown-encryption = teardown-encryption
Expand Down Expand Up @@ -63,6 +66,14 @@ extras =
test
commands =
pytest -v --durations=5 --maxfail=10 {posargs}
pytest -v --durations=5 --maxfail=10 test/synchronous/ {posargs}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does running pytest twice generate two XML reports? Which one gets uploaded?

There should be a way to run both of these in one pytest command, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'm not sure how the XML reports interact. I'll open a follow-up PR with a better solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you opened a ticket for this yet? The second XML file overwrites the first and there are actually other problems too. Like any test that relies on posargs gets run twice, for example the test_auth_oidc.py file gets run twice: https://evergreen.mongodb.com/task/mongo_python_driver_oidc_auth_test__platform~macos_oidc_auth_test_patch_636603f89371e626c41e48504c986e53f5122aaf_6696cbe31505d30007b20aae_24_07_16_19_39_11

Copy link
Contributor Author

@NoahStapp NoahStapp Jul 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't, sorry--this slipped past me. I don't know if there is a way to run this all in one command while keeping the existing behavior: we want to run all tests in test/ and test/asynchronous by default, but override both those directories with whatever tests are specified. We'd need a command that runs pytest ... test/ test/asynchronous/ when posargs is empty, but runs pytest ... posargs when posargs is present. The best approach I can think of is to add a check on posargs that runs one of two separate commands.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


[testenv:test-async]
description = run base set of async unit tests with no extra functionality
extras =
test
commands =
pytest -v --durations=5 --maxfail=10 test/asynchronous/ {posargs}

[testenv:test-eg]
description = run tests using run-tests.sh Evergreen script
Expand Down