From 73881257ebec2b5e592395c68a64ca008afe8534 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 2 Sep 2026 09:40:36 -0500 Subject: [PATCH 1/7] PYTHON-6070 Improve help output for just run-server and just setup-tests just run-server -h now forwards to run-mongodb.sh start -h instead of printing generic argparse help, and just setup-tests -h shows the valid sub_test_name choices for test names where they're enumerable (kms, auth_aws, auth_oidc, mod_wsgi, perf). --- .evergreen/scripts/run_server.py | 5 ++ .evergreen/scripts/utils.py | 107 ++++++++++++++++++++++++++----- 2 files changed, 97 insertions(+), 15 deletions(-) diff --git a/.evergreen/scripts/run_server.py b/.evergreen/scripts/run_server.py index 9757eb3a4f..a063444518 100644 --- a/.evergreen/scripts/run_server.py +++ b/.evergreen/scripts/run_server.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import sys from typing import Any from utils import DRIVERS_TOOLS, ROOT, get_test_options, run_command @@ -11,6 +12,10 @@ def set_env(name: str, value: Any = "1") -> None: def start_server(): + if {"-h", "--help"} & set(sys.argv[1:]): + run_command(["bash", f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh", "start", "-h"]) + return + opts, extra_opts = get_test_options( "Run a MongoDB server. All given flags will be passed to run-mongodb.sh in DRIVERS_TOOLS.", require_sub_test_name=False, diff --git a/.evergreen/scripts/utils.py b/.evergreen/scripts/utils.py index 0c718af899..90089a06d7 100644 --- a/.evergreen/scripts/utils.py +++ b/.evergreen/scripts/utils.py @@ -48,8 +48,37 @@ class Distro: "numpy": "", } -# Tests that require a sub test suite. -SUB_TEST_REQUIRED = ["auth_aws", "auth_oidc", "kms", "mod_wsgi", "perf"] +# Tests that require a sub test suite, mapped to their valid sub_test_name +# values, for test names where they're fully enumerable. A value of None +# means the sub_test_name is required but not restricted to a fixed set. +SUB_TEST_NAME_MAP: dict[str, list[str] | None] = { + "auth_aws": [ + "regular", + "assume-role", + "ec2", + "env-creds", + "session-creds", + "web-identity", + "ecs", + "ecs-remote", + ], + "kms": ["azure", "azure-remote", "azure-fail", "gcp", "gcp-remote", "gcp-fail"], + "mod_wsgi": ["standalone", "embedded"], + "perf": ["sync", "async"], + "auth_oidc": [ + "default", + "azure", + "azure-remote", + "gcp", + "gcp-remote", + "aks", + "aks-remote", + "gke", + "gke-remote", + "eks", + "eks-remote", + ], +} EXTRA_TESTS = ["mod_wsgi", "aws_lambda", "doctest"] @@ -70,27 +99,71 @@ class Distro: def get_test_options( description, require_sub_test_name=True, allow_extra_opts=False ) -> tuple[argparse.Namespace, list[str]]: + # If a test name with known sub_test_name choices was given, pin the test_name + # argument to it so the usage/help output isn't cluttered with every choice. + known_test_name = None + sub_test_choices = None + if require_sub_test_name: + for arg in sys.argv[1:]: + if arg in SUB_TEST_NAME_MAP: + known_test_name = arg + sub_test_choices = SUB_TEST_NAME_MAP[arg] + break + if known_test_name: + description = f"{description.rstrip('.')} for '{known_test_name}'." + parser = argparse.ArgumentParser( description=description, formatter_class=argparse.RawDescriptionHelpFormatter ) if require_sub_test_name: + if known_test_name: + parser.prog = f"{parser.prog} {known_test_name}" + test_name_choices = [known_test_name] + test_name_help = argparse.SUPPRESS + else: + test_name_choices = sorted(list(TEST_SUITE_MAP) + EXTRA_TESTS) + test_name_help = ( + "The optional name of the test suite to set up, typically the same name " + f"as a pytest marker. One of: {', '.join(test_name_choices)}." + ) parser.add_argument( "test_name", - choices=sorted(list(TEST_SUITE_MAP) + EXTRA_TESTS), + choices=test_name_choices, nargs="?", default="default", - help="The optional name of the test suite to set up, typically the same name as a pytest marker.", + metavar=known_test_name or "test_name", + help=test_name_help, ) + if sub_test_choices: + example_sub_test_name = sub_test_choices[0] + help_text = ( + f"The optional sub test name, for example {example_sub_test_name!r}. " + f"One of: {', '.join(sub_test_choices)}." + ) + else: + example_sub_test_name = "azure" + help_text = f"The optional sub test name, for example {example_sub_test_name!r}." parser.add_argument( - "sub_test_name", nargs="?", help="The optional sub test name, for example 'azure'." + "sub_test_name", + nargs="?", + choices=sub_test_choices, + metavar="sub_test_name", + help=help_text, ) else: + run_server_choices = sorted( + set(list(TEST_SUITE_MAP) + EXTRA_TESTS) - set(NO_RUN_ORCHESTRATION) + ) parser.add_argument( "test_name", - choices=set(list(TEST_SUITE_MAP) + EXTRA_TESTS) - set(NO_RUN_ORCHESTRATION), + choices=run_server_choices, nargs="?", default="default", - help="The optional name of the test suite to be run, which informs the server configuration.", + metavar="test_name", + help=( + "The optional name of the test suite to be run, which informs the server " + f"configuration. One of: {', '.join(run_server_choices)}." + ), ) parser.add_argument( "--verbose", "-v", action="store_true", help="Whether to log at the DEBUG level." @@ -100,7 +173,9 @@ def get_test_options( ) parser.add_argument("--auth", action="store_true", help="Whether to add authentication.") parser.add_argument("--ssl", action="store_true", help="Whether to add TLS configuration.") - parser.add_argument( + + other_group = parser.add_argument_group("other options") + other_group.add_argument( "--test-min-deps", action="store_true", help="Test against minimum dependency versions" ) @@ -110,24 +185,26 @@ def get_test_options( "--debug-log", action="store_true", help="Enable pymongo standard logging." ) parser.add_argument("--cov", action="store_true", help="Add test coverage.") - parser.add_argument( + other_group.add_argument( "--green-framework", nargs=1, choices=["gevent"], help="Optional green framework to test against.", ) - parser.add_argument( + other_group.add_argument( "--compressor", nargs=1, choices=["zlib", "zstd", "snappy"], help="Optional compression algorithm.", ) - parser.add_argument("--crypt-shared", action="store_true", help="Test with crypt_shared.") - parser.add_argument("--no-ext", action="store_true", help="Run without c extensions.") - parser.add_argument( + other_group.add_argument( + "--crypt-shared", action="store_true", help="Test with crypt_shared." + ) + other_group.add_argument("--no-ext", action="store_true", help="Run without c extensions.") + other_group.add_argument( "--mongodb-api-version", choices=["1"], help="MongoDB stable API version to use." ) - parser.add_argument( + other_group.add_argument( "--disable-test-commands", action="store_true", help="Disable test commands." ) @@ -146,7 +223,7 @@ def get_test_options( # Handle validation and environment variable overrides. test_name = opts.test_name sub_test_name = opts.sub_test_name if require_sub_test_name else "" - if require_sub_test_name and test_name in SUB_TEST_REQUIRED and not sub_test_name: + if require_sub_test_name and test_name in SUB_TEST_NAME_MAP and not sub_test_name: raise ValueError(f"Test '{test_name}' requires a sub_test_name") handle_env_overrides(parser, opts) if "auth" in test_name: From c3770bd46696f7a3f941544ecdea49bd3a833682 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 2 Sep 2026 10:15:46 -0500 Subject: [PATCH 2/7] PYTHON-6070 Address review feedback on help output Pass cwd=DRIVERS_TOOLS when forwarding -h to run-mongodb.sh, matching the normal start path. Clarify that sub_test_name is required, not optional, for test names in SUB_TEST_NAME_MAP. --- .evergreen/scripts/run_server.py | 5 ++++- .evergreen/scripts/utils.py | 14 ++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.evergreen/scripts/run_server.py b/.evergreen/scripts/run_server.py index a063444518..987323a840 100644 --- a/.evergreen/scripts/run_server.py +++ b/.evergreen/scripts/run_server.py @@ -13,7 +13,10 @@ def set_env(name: str, value: Any = "1") -> None: def start_server(): if {"-h", "--help"} & set(sys.argv[1:]): - run_command(["bash", f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh", "start", "-h"]) + run_command( + ["bash", f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh", "start", "-h"], + cwd=DRIVERS_TOOLS, + ) return opts, extra_opts = get_test_options( diff --git a/.evergreen/scripts/utils.py b/.evergreen/scripts/utils.py index 90089a06d7..e30053d108 100644 --- a/.evergreen/scripts/utils.py +++ b/.evergreen/scripts/utils.py @@ -134,15 +134,13 @@ def get_test_options( metavar=known_test_name or "test_name", help=test_name_help, ) - if sub_test_choices: - example_sub_test_name = sub_test_choices[0] - help_text = ( - f"The optional sub test name, for example {example_sub_test_name!r}. " - f"One of: {', '.join(sub_test_choices)}." - ) + if known_test_name: + example_sub_test_name = sub_test_choices[0] if sub_test_choices else "azure" + help_text = f"The sub test name, for example {example_sub_test_name!r}. Required for {known_test_name!r}." + if sub_test_choices: + help_text += f" One of: {', '.join(sub_test_choices)}." else: - example_sub_test_name = "azure" - help_text = f"The optional sub test name, for example {example_sub_test_name!r}." + help_text = "The optional sub test name, for example 'azure'." parser.add_argument( "sub_test_name", nargs="?", From 521ed9a1d916b7cfc555c346e8a72cc5996d91a1 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 2 Sep 2026 10:56:36 -0500 Subject: [PATCH 3/7] PYTHON-6070 Address further review feedback on help output Only pin the test_name argument's choices from the first positional argument, not any token in argv, so a sub_test_name that happens to match a SUB_TEST_NAME_MAP key no longer gets misidentified as the test suite. Fail fast with a clear error when DRIVERS_TOOLS is unset instead of a confusing FileNotFoundError. --- .evergreen/scripts/run_server.py | 6 ++++++ .evergreen/scripts/utils.py | 10 ++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.evergreen/scripts/run_server.py b/.evergreen/scripts/run_server.py index 987323a840..4b828836e4 100644 --- a/.evergreen/scripts/run_server.py +++ b/.evergreen/scripts/run_server.py @@ -12,6 +12,12 @@ def set_env(name: str, value: Any = "1") -> None: def start_server(): + if not DRIVERS_TOOLS: + raise ValueError( + "DRIVERS_TOOLS is not set; run `just run-server` from an Evergreen task " + "or set DRIVERS_TOOLS to a drivers-evergreen-tools checkout." + ) + if {"-h", "--help"} & set(sys.argv[1:]): run_command( ["bash", f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh", "start", "-h"], diff --git a/.evergreen/scripts/utils.py b/.evergreen/scripts/utils.py index e30053d108..871afd3fd3 100644 --- a/.evergreen/scripts/utils.py +++ b/.evergreen/scripts/utils.py @@ -104,12 +104,10 @@ def get_test_options( known_test_name = None sub_test_choices = None if require_sub_test_name: - for arg in sys.argv[1:]: - if arg in SUB_TEST_NAME_MAP: - known_test_name = arg - sub_test_choices = SUB_TEST_NAME_MAP[arg] - break - if known_test_name: + positional_args = [arg for arg in sys.argv[1:] if not arg.startswith("-")] + if positional_args and positional_args[0] in SUB_TEST_NAME_MAP: + known_test_name = positional_args[0] + sub_test_choices = SUB_TEST_NAME_MAP[known_test_name] description = f"{description.rstrip('.')} for '{known_test_name}'." parser = argparse.ArgumentParser( From d0c8a71efaf8b93c5d26cdd82933c9f078bb2848 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 2 Sep 2026 11:01:58 -0500 Subject: [PATCH 4/7] PYTHON-6070 Forward run-server -h without run_command's logging noise run_command logs "Running command..." before and after every call, which wrapped the forwarded run-mongodb.sh start -h output and defeated the point of showing it verbatim. Call subprocess.run directly for the -h path instead. --- .evergreen/scripts/run_server.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.evergreen/scripts/run_server.py b/.evergreen/scripts/run_server.py index 4b828836e4..adfbbe311e 100644 --- a/.evergreen/scripts/run_server.py +++ b/.evergreen/scripts/run_server.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import subprocess import sys from typing import Any @@ -19,9 +20,12 @@ def start_server(): ) if {"-h", "--help"} & set(sys.argv[1:]): - run_command( - ["bash", f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh", "start", "-h"], + # Forward straight to run-mongodb.sh's own help, without run_command's + # "Running command..." logging noise. + subprocess.run( # noqa: S603 + ["bash", f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh", "start", "-h"], # noqa: S607 cwd=DRIVERS_TOOLS, + check=True, ) return From 4c4c45037b5d11c0908e6bfdb2b14e93f49c6a36 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 2 Sep 2026 11:16:50 -0500 Subject: [PATCH 5/7] PYTHON-6070 Only require DRIVERS_TOOLS for actually starting a server just run-server -h/--help was raising before reaching the help path when DRIVERS_TOOLS was unset. Only raise for the real start path; -h without DRIVERS_TOOLS now falls back to get_test_options' own argparse help instead of erroring. --- .evergreen/scripts/run_server.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.evergreen/scripts/run_server.py b/.evergreen/scripts/run_server.py index adfbbe311e..b2500d9453 100644 --- a/.evergreen/scripts/run_server.py +++ b/.evergreen/scripts/run_server.py @@ -13,13 +13,8 @@ def set_env(name: str, value: Any = "1") -> None: def start_server(): - if not DRIVERS_TOOLS: - raise ValueError( - "DRIVERS_TOOLS is not set; run `just run-server` from an Evergreen task " - "or set DRIVERS_TOOLS to a drivers-evergreen-tools checkout." - ) - - if {"-h", "--help"} & set(sys.argv[1:]): + want_help = bool({"-h", "--help"} & set(sys.argv[1:])) + if want_help and DRIVERS_TOOLS: # Forward straight to run-mongodb.sh's own help, without run_command's # "Running command..." logging noise. subprocess.run( # noqa: S603 @@ -29,6 +24,14 @@ def start_server(): ) return + # DRIVERS_TOOLS is only needed to actually start a server. When it's unset and + # -h/--help was requested, fall through to get_test_options' own argparse help below. + if not want_help and not DRIVERS_TOOLS: + raise ValueError( + "DRIVERS_TOOLS is not set; run `just run-server` from an Evergreen task " + "or set DRIVERS_TOOLS to a drivers-evergreen-tools checkout." + ) + opts, extra_opts = get_test_options( "Run a MongoDB server. All given flags will be passed to run-mongodb.sh in DRIVERS_TOOLS.", require_sub_test_name=False, From ba5a8a36092c406366b8bb67716002c4c8368c4e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 2 Sep 2026 12:57:23 -0500 Subject: [PATCH 6/7] PYTHON-6070 Skip option values when inferring known_test_name An option that takes a value (e.g. --green-framework gevent) placed before the test_name positional was mistaken for it, silently skipping the help specialization. Skip known value-taking options' values when scanning for the first positional argument. --- .evergreen/scripts/utils.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/.evergreen/scripts/utils.py b/.evergreen/scripts/utils.py index 871afd3fd3..aeff490962 100644 --- a/.evergreen/scripts/utils.py +++ b/.evergreen/scripts/utils.py @@ -95,6 +95,20 @@ class Distro: # Mapping of env variables to options OPTION_TO_ENV_VAR = {"cov": "COVERAGE", "crypt_shared": "TEST_CRYPT_SHARED"} +# Options that consume a following value, so it isn't mistaken for the test_name +# positional when inferring known_test_name below. +OPTIONS_WITH_VALUES = ["--green-framework", "--compressor", "--mongodb-api-version"] + + +def _first_positional_arg(argv: list[str]) -> str | None: + args = iter(argv) + for arg in args: + if arg in OPTIONS_WITH_VALUES: + next(args, None) + elif not arg.startswith("-"): + return arg + return None + def get_test_options( description, require_sub_test_name=True, allow_extra_opts=False @@ -104,9 +118,9 @@ def get_test_options( known_test_name = None sub_test_choices = None if require_sub_test_name: - positional_args = [arg for arg in sys.argv[1:] if not arg.startswith("-")] - if positional_args and positional_args[0] in SUB_TEST_NAME_MAP: - known_test_name = positional_args[0] + first_positional = _first_positional_arg(sys.argv[1:]) + if first_positional in SUB_TEST_NAME_MAP: + known_test_name = first_positional sub_test_choices = SUB_TEST_NAME_MAP[known_test_name] description = f"{description.rstrip('.')} for '{known_test_name}'." From 4a516cda3a2839d285694a95d28be2a256567b1c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 2 Sep 2026 13:46:22 -0500 Subject: [PATCH 7/7] PYTHON-6070 Verify run-mongodb.sh exists before forwarding -h to it A stale or invalid DRIVERS_TOOLS (pointing at a missing checkout or missing run-mongodb.sh) crashed with a confusing FileNotFoundError instead of falling back to the local argparse help. Only forward -h when run-mongodb.sh actually exists. --- .evergreen/scripts/run_server.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.evergreen/scripts/run_server.py b/.evergreen/scripts/run_server.py index b2500d9453..f81964f367 100644 --- a/.evergreen/scripts/run_server.py +++ b/.evergreen/scripts/run_server.py @@ -3,6 +3,7 @@ import os import subprocess import sys +from pathlib import Path from typing import Any from utils import DRIVERS_TOOLS, ROOT, get_test_options, run_command @@ -13,12 +14,15 @@ def set_env(name: str, value: Any = "1") -> None: def start_server(): + run_mongodb_script = ( + Path(DRIVERS_TOOLS) / ".evergreen" / "run-mongodb.sh" if DRIVERS_TOOLS else None + ) want_help = bool({"-h", "--help"} & set(sys.argv[1:])) - if want_help and DRIVERS_TOOLS: + if want_help and run_mongodb_script and run_mongodb_script.is_file(): # Forward straight to run-mongodb.sh's own help, without run_command's # "Running command..." logging noise. subprocess.run( # noqa: S603 - ["bash", f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh", "start", "-h"], # noqa: S607 + ["bash", str(run_mongodb_script), "start", "-h"], # noqa: S607 cwd=DRIVERS_TOOLS, check=True, )