Merge branch 'master' of github.com:mongodb/mongo-python-driver
This commit is contained in:
commit
7ad62152ba
@ -2826,42 +2826,150 @@ buildvariants:
|
||||
- "test-6.0-standalone"
|
||||
- "test-5.0-standalone"
|
||||
|
||||
- matrix_name: "ocsp-test"
|
||||
matrix_spec:
|
||||
platform: rhel8
|
||||
python-version: ["3.9", "3.10", "pypy3.9", "pypy3.10"]
|
||||
mongodb-version: ["4.4", "5.0", "6.0", "7.0", "8.0", "latest"]
|
||||
auth: "noauth"
|
||||
ssl: "ssl"
|
||||
display_name: "OCSP test ${platform} ${python-version} ${mongodb-version}"
|
||||
batchtime: 20160 # 14 days
|
||||
# OCSP test matrix.
|
||||
- name: ocsp-test-rhel8-v4.4-py3.9
|
||||
tasks:
|
||||
- name: ".ocsp"
|
||||
|
||||
- matrix_name: "ocsp-test-windows"
|
||||
matrix_spec:
|
||||
platform: windows
|
||||
python-version-windows: ["3.9", "3.10"]
|
||||
mongodb-version: ["4.4", "5.0", "6.0", "7.0", "8.0", "latest"]
|
||||
auth: "noauth"
|
||||
ssl: "ssl"
|
||||
display_name: "OCSP test ${platform} ${python-version-windows} ${mongodb-version}"
|
||||
batchtime: 20160 # 14 days
|
||||
- name: .ocsp
|
||||
display_name: OCSP test RHEL8 v4.4 py3.9
|
||||
run_on:
|
||||
- rhel87-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "4.4"
|
||||
PYTHON_BINARY: /opt/python/3.9/bin/python3
|
||||
- name: ocsp-test-rhel8-v5.0-py3.10
|
||||
tasks:
|
||||
# Windows MongoDB servers do not staple OCSP responses and only support RSA.
|
||||
- name: ".ocsp-rsa !.ocsp-staple"
|
||||
|
||||
- matrix_name: "ocsp-test-macos"
|
||||
matrix_spec:
|
||||
platform: macos
|
||||
mongodb-version: ["4.4", "5.0", "6.0", "7.0", "8.0", "latest"]
|
||||
auth: "noauth"
|
||||
ssl: "ssl"
|
||||
display_name: "OCSP test ${platform} ${mongodb-version}"
|
||||
batchtime: 20160 # 14 days
|
||||
- name: .ocsp
|
||||
display_name: OCSP test RHEL8 v5.0 py3.10
|
||||
run_on:
|
||||
- rhel87-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "5.0"
|
||||
PYTHON_BINARY: /opt/python/3.10/bin/python3
|
||||
- name: ocsp-test-rhel8-v6.0-py3.11
|
||||
tasks:
|
||||
# macOS MongoDB servers do not staple OCSP responses and only support RSA.
|
||||
- name: ".ocsp-rsa !.ocsp-staple"
|
||||
- name: .ocsp
|
||||
display_name: OCSP test RHEL8 v6.0 py3.11
|
||||
run_on:
|
||||
- rhel87-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "6.0"
|
||||
PYTHON_BINARY: /opt/python/3.11/bin/python3
|
||||
- name: ocsp-test-rhel8-v7.0-py3.12
|
||||
tasks:
|
||||
- name: .ocsp
|
||||
display_name: OCSP test RHEL8 v7.0 py3.12
|
||||
run_on:
|
||||
- rhel87-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "7.0"
|
||||
PYTHON_BINARY: /opt/python/3.12/bin/python3
|
||||
- name: ocsp-test-rhel8-v8.0-py3.13
|
||||
tasks:
|
||||
- name: .ocsp
|
||||
display_name: OCSP test RHEL8 v8.0 py3.13
|
||||
run_on:
|
||||
- rhel87-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "8.0"
|
||||
PYTHON_BINARY: /opt/python/3.13/bin/python3
|
||||
- name: ocsp-test-rhel8-rapid-pypy3.9
|
||||
tasks:
|
||||
- name: .ocsp
|
||||
display_name: OCSP test RHEL8 rapid pypy3.9
|
||||
run_on:
|
||||
- rhel87-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: rapid
|
||||
PYTHON_BINARY: /opt/python/pypy3.9/bin/python3
|
||||
- name: ocsp-test-rhel8-latest-pypy3.10
|
||||
tasks:
|
||||
- name: .ocsp
|
||||
display_name: OCSP test RHEL8 latest pypy3.10
|
||||
run_on:
|
||||
- rhel87-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: latest
|
||||
PYTHON_BINARY: /opt/python/pypy3.10/bin/python3
|
||||
- name: ocsp-test-win64-v4.4-py3.9
|
||||
tasks:
|
||||
- name: .ocsp-rsa !.ocsp-staple
|
||||
display_name: OCSP test Win64 v4.4 py3.9
|
||||
run_on:
|
||||
- windows-64-vsMulti-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "4.4"
|
||||
PYTHON_BINARY: C:/python/Python39/python.exe
|
||||
- name: ocsp-test-win64-v8.0-py3.13
|
||||
tasks:
|
||||
- name: .ocsp-rsa !.ocsp-staple
|
||||
display_name: OCSP test Win64 v8.0 py3.13
|
||||
run_on:
|
||||
- windows-64-vsMulti-small
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "8.0"
|
||||
PYTHON_BINARY: C:/python/Python313/python.exe
|
||||
- name: ocsp-test-macos-v4.4-py3.9
|
||||
tasks:
|
||||
- name: .ocsp-rsa !.ocsp-staple
|
||||
display_name: OCSP test macOS v4.4 py3.9
|
||||
run_on:
|
||||
- macos-14
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "4.4"
|
||||
PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.9/bin/python3
|
||||
- name: ocsp-test-macos-v8.0-py3.13
|
||||
tasks:
|
||||
- name: .ocsp-rsa !.ocsp-staple
|
||||
display_name: OCSP test macOS v8.0 py3.13
|
||||
run_on:
|
||||
- macos-14
|
||||
batchtime: 20160
|
||||
expansions:
|
||||
AUTH: noauth
|
||||
SSL: ssl
|
||||
TOPOLOGY: server
|
||||
VERSION: "8.0"
|
||||
PYTHON_BINARY: /Library/Frameworks/Python.Framework/Versions/3.13/bin/python3
|
||||
|
||||
- matrix_name: "oidc-auth-test"
|
||||
matrix_spec:
|
||||
|
||||
@ -18,17 +18,25 @@ if [ -n "$SKIP_HATCH" ]; then
|
||||
run_hatch() {
|
||||
bash ./.evergreen/run-tests.sh
|
||||
}
|
||||
elif $PYTHON_BINARY -m hatch --version; then
|
||||
run_hatch() {
|
||||
$PYTHON_BINARY -m hatch run "$@"
|
||||
}
|
||||
else # No toolchain hatch present, set up virtualenv before installing hatch
|
||||
else # Set up virtualenv before installing hatch
|
||||
# Use a random venv name because the encryption tasks run this script multiple times in the same run.
|
||||
ENV_NAME=hatchenv-$RANDOM
|
||||
createvirtualenv "$PYTHON_BINARY" $ENV_NAME
|
||||
# shellcheck disable=SC2064
|
||||
trap "deactivate; rm -rf $ENV_NAME" EXIT HUP
|
||||
python -m pip install -q hatch
|
||||
|
||||
# Ensure hatch does not write to user or global locations.
|
||||
touch hatch_config.toml
|
||||
HATCH_CONFIG=$(pwd)/hatch_config.toml
|
||||
if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
|
||||
HATCH_CONFIG=$(cygpath -m "$HATCH_CONFIG")
|
||||
fi
|
||||
export HATCH_CONFIG
|
||||
hatch config restore
|
||||
hatch config set dirs.data ".hatch/data"
|
||||
hatch config set dirs.cache ".hatch/cache"
|
||||
|
||||
run_hatch() {
|
||||
python -m hatch run "$@"
|
||||
}
|
||||
|
||||
167
.evergreen/scripts/generate_config.py
Normal file
167
.evergreen/scripts/generate_config.py
Normal file
@ -0,0 +1,167 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.9"
|
||||
# dependencies = [
|
||||
# "shrub.py>=3.2.0",
|
||||
# "pyyaml>=6.0.2"
|
||||
# ]
|
||||
# ///
|
||||
|
||||
# Note: Run this file with `hatch run`, `pipx run`, or `uv run`.
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from itertools import cycle, product, zip_longest
|
||||
from typing import Any
|
||||
|
||||
from shrub.v3.evg_build_variant import BuildVariant
|
||||
from shrub.v3.evg_project import EvgProject
|
||||
from shrub.v3.evg_task import EvgTaskRef
|
||||
from shrub.v3.shrub_service import ShrubService
|
||||
|
||||
##############
|
||||
# Globals
|
||||
##############
|
||||
|
||||
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"]
|
||||
ALL_PYTHONS = CPYTHONS + PYPYS
|
||||
BATCHTIME_WEEK = 10080
|
||||
HOSTS = dict()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Host:
|
||||
name: str
|
||||
run_on: str
|
||||
display_name: str
|
||||
|
||||
|
||||
HOSTS["rhel8"] = Host("rhel8", "rhel87-small", "RHEL8")
|
||||
HOSTS["win64"] = Host("win64", "windows-64-vsMulti-small", "Win64")
|
||||
HOSTS["macos"] = Host("macos", "macos-14", "macOS")
|
||||
|
||||
|
||||
##############
|
||||
# Helpers
|
||||
##############
|
||||
|
||||
|
||||
def create_variant(
|
||||
task_names: list[str],
|
||||
display_name: str,
|
||||
*,
|
||||
python: str | None = None,
|
||||
version: str | None = None,
|
||||
host: str | None = None,
|
||||
**kwargs: Any,
|
||||
) -> BuildVariant:
|
||||
"""Create a build variant for the given inputs."""
|
||||
task_refs = [EvgTaskRef(name=n) for n in task_names]
|
||||
kwargs.setdefault("expansions", dict())
|
||||
expansions = kwargs.pop("expansions", dict()).copy()
|
||||
host = host or "rhel8"
|
||||
run_on = [HOSTS[host].run_on]
|
||||
name = display_name.replace(" ", "-").lower()
|
||||
if python:
|
||||
expansions["PYTHON_BINARY"] = get_python_binary(python, host)
|
||||
if version:
|
||||
expansions["VERSION"] = version
|
||||
expansions = expansions or None
|
||||
return BuildVariant(
|
||||
name=name,
|
||||
display_name=display_name,
|
||||
tasks=task_refs,
|
||||
expansions=expansions,
|
||||
run_on=run_on,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def get_python_binary(python: str, host: str) -> str:
|
||||
"""Get the appropriate python binary given a python version and host."""
|
||||
if host == "win64":
|
||||
is_32 = python.startswith("32-bit")
|
||||
if is_32:
|
||||
_, python = python.split()
|
||||
base = "C:/python/32"
|
||||
else:
|
||||
base = "C:/python"
|
||||
python = python.replace(".", "")
|
||||
return f"{base}/Python{python}/python.exe"
|
||||
|
||||
if host == "rhel8":
|
||||
return f"/opt/python/{python}/bin/python3"
|
||||
|
||||
if host == "macos":
|
||||
return f"/Library/Frameworks/Python.Framework/Versions/{python}/bin/python3"
|
||||
|
||||
raise ValueError(f"no match found for python {python} on {host}")
|
||||
|
||||
|
||||
def get_display_name(base: str, host: str, version: str, python: str) -> str:
|
||||
"""Get the display name of a variant."""
|
||||
if version not in ["rapid", "latest"]:
|
||||
version = f"v{version}"
|
||||
if not python.startswith("pypy"):
|
||||
python = f"py{python}"
|
||||
return f"{base} {HOSTS[host].display_name} {version} {python}"
|
||||
|
||||
|
||||
def zip_cycle(*iterables, empty_default=None):
|
||||
"""Get all combinations of the inputs, cycling over the shorter list(s)."""
|
||||
cycles = [cycle(i) for i in iterables]
|
||||
for _ in zip_longest(*iterables):
|
||||
yield tuple(next(i, empty_default) for i in cycles)
|
||||
|
||||
|
||||
##############
|
||||
# Variants
|
||||
##############
|
||||
|
||||
|
||||
def create_ocsp_variants() -> list[BuildVariant]:
|
||||
variants = []
|
||||
batchtime = BATCHTIME_WEEK * 2
|
||||
expansions = dict(AUTH="noauth", SSL="ssl", TOPOLOGY="server")
|
||||
base_display = "OCSP test"
|
||||
|
||||
# OCSP tests on rhel8 with all servers v4.4+ and all python versions.
|
||||
versions = [v for v in ALL_VERSIONS if v != "4.0"]
|
||||
for version, python in zip_cycle(versions, ALL_PYTHONS):
|
||||
host = "rhel8"
|
||||
variant = create_variant(
|
||||
[".ocsp"],
|
||||
get_display_name(base_display, host, version, python),
|
||||
python=python,
|
||||
version=version,
|
||||
host=host,
|
||||
expansions=expansions,
|
||||
batchtime=batchtime,
|
||||
)
|
||||
variants.append(variant)
|
||||
|
||||
# OCSP tests on Windows and MacOS.
|
||||
# MongoDB servers on these hosts do not staple OCSP responses and only support RSA.
|
||||
for host, version in product(["win64", "macos"], ["4.4", "8.0"]):
|
||||
python = CPYTHONS[0] if version == "4.4" else CPYTHONS[-1]
|
||||
variant = create_variant(
|
||||
[".ocsp-rsa !.ocsp-staple"],
|
||||
get_display_name(base_display, host, version, python),
|
||||
python=python,
|
||||
version=version,
|
||||
host=host,
|
||||
expansions=expansions,
|
||||
batchtime=batchtime,
|
||||
)
|
||||
variants.append(variant)
|
||||
|
||||
return variants
|
||||
|
||||
|
||||
##################
|
||||
# Generate Config
|
||||
##################
|
||||
|
||||
project = EvgProject(tasks=None, buildvariants=create_ocsp_variants())
|
||||
print(ShrubService.generate_yaml(project)) # noqa: T201
|
||||
@ -1412,12 +1412,11 @@ class TestCursor(AsyncIntegrationTest):
|
||||
self.assertEqual(len(docs), 2)
|
||||
|
||||
async def test_to_list_csot_applied(self):
|
||||
client = await self.async_single_client(timeoutMS=500)
|
||||
client = await self.async_single_client(timeoutMS=500, w=1)
|
||||
coll = client.pymongo.test
|
||||
# Initialize the client with a larger timeout to help make test less flakey
|
||||
with pymongo.timeout(10):
|
||||
await client.admin.command("ping")
|
||||
coll = client.pymongo.test
|
||||
await coll.insert_many([{} for _ in range(5)])
|
||||
await coll.insert_many([{} for _ in range(5)])
|
||||
cursor = coll.find({"$where": delay(1)})
|
||||
with self.assertRaises(PyMongoError) as ctx:
|
||||
await cursor.to_list()
|
||||
@ -1454,12 +1453,11 @@ class TestCursor(AsyncIntegrationTest):
|
||||
|
||||
@async_client_context.require_failCommand_blockConnection
|
||||
async def test_command_cursor_to_list_csot_applied(self):
|
||||
client = await self.async_single_client(timeoutMS=500)
|
||||
client = await self.async_single_client(timeoutMS=500, w=1)
|
||||
coll = client.pymongo.test
|
||||
# Initialize the client with a larger timeout to help make test less flakey
|
||||
with pymongo.timeout(10):
|
||||
await client.admin.command("ping")
|
||||
coll = client.pymongo.test
|
||||
await coll.insert_many([{} for _ in range(5)])
|
||||
await coll.insert_many([{} for _ in range(5)])
|
||||
fail_command = {
|
||||
"configureFailPoint": "failCommand",
|
||||
"mode": {"times": 5},
|
||||
|
||||
@ -36,7 +36,6 @@ from test.asynchronous import (
|
||||
unittest,
|
||||
)
|
||||
from test.unified_format_shared import (
|
||||
IS_INTERRUPTED,
|
||||
KMS_TLS_OPTS,
|
||||
PLACEHOLDER_MAP,
|
||||
SKIP_CSOT_TESTS,
|
||||
@ -104,6 +103,13 @@ from pymongo.write_concern import WriteConcern
|
||||
|
||||
_IS_SYNC = False
|
||||
|
||||
IS_INTERRUPTED = False
|
||||
|
||||
|
||||
def interrupt_loop():
|
||||
global IS_INTERRUPTED
|
||||
IS_INTERRUPTED = True
|
||||
|
||||
|
||||
async def is_run_on_requirement_satisfied(requirement):
|
||||
topology_satisfied = True
|
||||
|
||||
@ -1403,12 +1403,11 @@ class TestCursor(IntegrationTest):
|
||||
self.assertEqual(len(docs), 2)
|
||||
|
||||
def test_to_list_csot_applied(self):
|
||||
client = self.single_client(timeoutMS=500)
|
||||
client = self.single_client(timeoutMS=500, w=1)
|
||||
coll = client.pymongo.test
|
||||
# Initialize the client with a larger timeout to help make test less flakey
|
||||
with pymongo.timeout(10):
|
||||
client.admin.command("ping")
|
||||
coll = client.pymongo.test
|
||||
coll.insert_many([{} for _ in range(5)])
|
||||
coll.insert_many([{} for _ in range(5)])
|
||||
cursor = coll.find({"$where": delay(1)})
|
||||
with self.assertRaises(PyMongoError) as ctx:
|
||||
cursor.to_list()
|
||||
@ -1445,12 +1444,11 @@ class TestCursor(IntegrationTest):
|
||||
|
||||
@client_context.require_failCommand_blockConnection
|
||||
def test_command_cursor_to_list_csot_applied(self):
|
||||
client = self.single_client(timeoutMS=500)
|
||||
client = self.single_client(timeoutMS=500, w=1)
|
||||
coll = client.pymongo.test
|
||||
# Initialize the client with a larger timeout to help make test less flakey
|
||||
with pymongo.timeout(10):
|
||||
client.admin.command("ping")
|
||||
coll = client.pymongo.test
|
||||
coll.insert_many([{} for _ in range(5)])
|
||||
coll.insert_many([{} for _ in range(5)])
|
||||
fail_command = {
|
||||
"configureFailPoint": "failCommand",
|
||||
"mode": {"times": 5},
|
||||
|
||||
@ -36,7 +36,6 @@ from test import (
|
||||
unittest,
|
||||
)
|
||||
from test.unified_format_shared import (
|
||||
IS_INTERRUPTED,
|
||||
KMS_TLS_OPTS,
|
||||
PLACEHOLDER_MAP,
|
||||
SKIP_CSOT_TESTS,
|
||||
@ -104,6 +103,13 @@ from pymongo.write_concern import WriteConcern
|
||||
|
||||
_IS_SYNC = True
|
||||
|
||||
IS_INTERRUPTED = False
|
||||
|
||||
|
||||
def interrupt_loop():
|
||||
global IS_INTERRUPTED
|
||||
IS_INTERRUPTED = True
|
||||
|
||||
|
||||
def is_run_on_requirement_satisfied(requirement):
|
||||
topology_satisfied = True
|
||||
|
||||
@ -139,11 +139,6 @@ elif OIDC_ENV == "gcp":
|
||||
}
|
||||
|
||||
|
||||
def interrupt_loop():
|
||||
global IS_INTERRUPTED
|
||||
IS_INTERRUPTED = True
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass.
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user