From 93b864557b5c8eb03249e85a47b1cee253257708 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 06:45:41 -0500 Subject: [PATCH 01/25] PYTHON-6091 Pin a consistent uv binary version in CI and locally --- .evergreen/scripts/configure-env.sh | 34 +++-------- .evergreen/scripts/install-dependencies.sh | 66 +++++++++++++++++++--- .evergreen/scripts/setup-dev-env.sh | 13 +++-- .github/workflows/test-python.yml | 16 +++--- CONTRIBUTING.md | 8 +++ justfile | 5 ++ pyproject.toml | 4 ++ 7 files changed, 99 insertions(+), 47 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 8dc328aab3..572960e304 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -19,15 +19,17 @@ UV_CACHE_DIR=$PROJECT_DIRECTORY/.local/uv/cache DRIVERS_TOOLS_BINARIES="$DRIVERS_TOOLS/.bin" MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" -# On Evergreen jobs, "CI" will be set, and we don't want to write to $HOME. +# On Evergreen jobs, "CI" will be set, and we don't want to write to $HOME. Put +# our binaries (uv, just, ...) in a task-local dir we control, independent of the +# drivers-tools tree. if [ "${CI:-}" == "true" ]; then - PYMONGO_BIN_DIR=${DRIVERS_TOOLS_BINARIES:-} + PYMONGO_BIN_DIR="${TMPDIR:-/tmp}/pymongo-bin" # We want to use a path that's already on PATH on spawn hosts. else PYMONGO_BIN_DIR=$HOME/cli_bin fi -PATH_EXT="$MONGODB_BINARIES:$DRIVERS_TOOLS_BINARIES:$PYMONGO_BIN_DIR:\$PATH" +PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory if [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin @@ -38,7 +40,9 @@ if [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin UV_CACHE_DIR=$(cygpath -m "$UV_CACHE_DIR") DRIVERS_TOOLS_BINARIES=$(cygpath -m "$DRIVERS_TOOLS_BINARIES") MONGODB_BINARIES=$(cygpath -m "$MONGODB_BINARIES") - PYMONGO_BIN_DIR=$(cygpath -m "$PYMONGO_BIN_DIR") + # Keep PYMONGO_BIN_DIR in cygwin form so bash can search it on PATH; native + # uv gets the Windows form (via cygpath -m) inside install-dependencies.sh. + PYMONGO_BIN_DIR=$(cygpath -u "$PYMONGO_BIN_DIR") fi SCRIPT_DIR="$PROJECT_DIRECTORY/.evergreen/scripts" @@ -90,25 +94,3 @@ cat < expansion.yml DRIVERS_TOOLS: "$DRIVERS_TOOLS" PROJECT_DIRECTORY: "$PROJECT_DIRECTORY" EOT - -# If the toolchain is available, symlink binaries to the bin dir. This has to be done -# after drivers-tools is cloned, since we might be using its binary dir. -_bin_path="" -if [ "Windows_NT" == "${OS:-}" ]; then - _bin_path="/cygdrive/c/Python/Current/Scripts" -elif [ "$(uname -s)" == "Darwin" ]; then - _bin_path="/Library/Frameworks/Python.Framework/Versions/Current/bin" -else - _bin_path="/opt/python/Current/bin" -fi -if [ -d "${_bin_path}" ]; then - _suffix="" - if [ "Windows_NT" == "${OS:-}" ]; then - _suffix=".exe" - fi - echo "Symlinking binaries from toolchain" - mkdir -p $PYMONGO_BIN_DIR - ln -s ${_bin_path}/just${_suffix} $PYMONGO_BIN_DIR/just${_suffix} - ln -s ${_bin_path}/uv${_suffix} $PYMONGO_BIN_DIR/uv${_suffix} - ln -s ${_bin_path}/uvx${_suffix} $PYMONGO_BIN_DIR/uvx${_suffix} -fi diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 7f943d7d00..e7b9e5f23b 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -1,6 +1,6 @@ #!/bin/bash # Install the necessary dependencies. -set -eu +set -euo pipefail HERE=$(dirname ${BASH_SOURCE:-$0}) HERE="$( cd -- "$HERE" > /dev/null 2>&1 && pwd )" @@ -15,23 +15,75 @@ fi if [ -z "${PYMONGO_BIN_DIR:-}" ]; then PYMONGO_BIN_DIR="$HOME/.local/bin" fi +# uv.exe on Windows needs Windows-style paths, while bash uses the cygwin form. +# Keep PYMONGO_BIN_DIR in the form PATH uses and give uv the Windows form. +if [ "Windows_NT" = "${OS:-}" ]; then + export UV_TOOL_BIN_DIR="$(cygpath -m "$PYMONGO_BIN_DIR")" + export UV_TOOL_DIR="$(cygpath -m "${UV_TOOL_DIR:-$(dirname "$PYMONGO_BIN_DIR")/uv-tools}")" +else + export UV_TOOL_BIN_DIR="$PYMONGO_BIN_DIR" +fi + +# Locate the Python toolchain's binary dir, so we can prefer its uv and just. +_toolchain_bin="" +if [ "Windows_NT" = "${OS:-}" ]; then + _toolchain_bin="/cygdrive/c/Python/Current/Scripts" +elif [ "$(uname -s)" = "Darwin" ]; then + _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" +else + _toolchain_bin="/opt/python/Current/bin" +fi -# Ensure uv is installed. +# Prefer the toolchain's uv as a bootstrap when uv is not already on PATH, so we +# do not fall back to installing from astral. +if ! command -v uv &>/dev/null; then + if [ -x "$_toolchain_bin/uv" ] || [ -x "$_toolchain_bin/uv.exe" ]; then + echo "Found uv in the toolchain at $_toolchain_bin" + export PATH="$_toolchain_bin:$PATH" + fi +fi + +# Ensure uv is available (bootstrap if absent). if ! command -v uv &>/dev/null; then _BIN_DIR=$PYMONGO_BIN_DIR mkdir -p ${_BIN_DIR} - echo "Installing uv..." + echo "uv not found on PATH; installing the latest uv from astral..." curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR="$_BIN_DIR" INSTALLER_NO_MODIFY_PATH=1 sh if [ "Windows_NT" = "${OS:-}" ]; then chmod +x "$(cygpath -u $_BIN_DIR)/uv.exe" fi export PATH="$PYMONGO_BIN_DIR:$PATH" - echo "Installing uv... done." fi -# Ensure just is installed. -if ! command -v just &>/dev/null; then - uv tool install rust-just +# Pin the uv binary to the version in pyproject.toml's [tool.uv] required-version. +# Run the current uv directly: it writes into PYMONGO_BIN_DIR (a different +# location), so nothing running is overwritten and no temp copy is needed. +_uv_bin="$(command -v uv 2>/dev/null || true)" +if [ -n "$_uv_bin" ]; then + _uv_pin="$(awk -F'"' '/^[[:space:]]*required-version[[:space:]]*=/{print $2}' pyproject.toml)" + _uv_vers="$(uv --version 2>/dev/null | head -1 | awk '{print $2}' | sed 's/^v//')" + if [ "uv${_uv_pin}" != "uv==${_uv_vers}" ]; then + rm -f "$PYMONGO_BIN_DIR/uv" "$PYMONGO_BIN_DIR/uv.exe" \ + "$PYMONGO_BIN_DIR/uvx" "$PYMONGO_BIN_DIR/uvx.exe" + uv tool install -q --force --from "uv${_uv_pin}" uv + echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" + fi +fi + +# Use just from the toolchain if available, otherwise install it. It must live in +# our bin dir to be on PATH for callers; copying it keeps the toolchain's just. +if [ ! -x "$PYMONGO_BIN_DIR/just" ] && [ ! -x "$PYMONGO_BIN_DIR/just.exe" ]; then + if [ -x "$_toolchain_bin/just" ]; then + echo "Using just from the toolchain" + cp "$_toolchain_bin/just" "$PYMONGO_BIN_DIR/just" + chmod +x "$PYMONGO_BIN_DIR/just" + elif [ -x "$_toolchain_bin/just.exe" ]; then + echo "Using just from the toolchain" + cp "$_toolchain_bin/just.exe" "$PYMONGO_BIN_DIR/just.exe" + chmod +x "$PYMONGO_BIN_DIR/just.exe" + else + uv tool install rust-just + fi fi popd > /dev/null diff --git a/.evergreen/scripts/setup-dev-env.sh b/.evergreen/scripts/setup-dev-env.sh index e58d6210fe..5a1794ee71 100755 --- a/.evergreen/scripts/setup-dev-env.sh +++ b/.evergreen/scripts/setup-dev-env.sh @@ -16,23 +16,24 @@ if [ -f $HERE/test-env.sh ]; then . $HERE/test-env.sh fi -# Handle the value for UV_PYTHON. -. $HERE/setup-uv-python.sh - # Ensure dependencies are installed. bash $HERE/install-dependencies.sh -# Re-source env.sh: install-dependencies.sh may have appended to it, e.g. when it -# had to install Python on an image that lacks a toolchain. +# Re-source env.sh in case a dependency install updated it, e.g. on a host +# without a toolchain where uv was installed into a shared bin dir. if [ -f $HERE/env.sh ]; then . $HERE/env.sh fi -# Add the default install path to the path if needed. +# Add the default install path to the path before configuring uv: setup-uv-python.sh +# calls uv, and on a machine without env.sh the tool dir must already be on PATH. if [ -z "${PYMONGO_BIN_DIR:-}" ]; then export PATH="$PATH:$HOME/.local/bin" fi +# Handle the value for UV_PYTHON. +. $HERE/setup-uv-python.sh + # Only run the next part if not running on CI. if [ -z "${CI:-}" ]; then # Set up venv, making sure c extensions build unless disabled. diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index 73d5513a16..f19a783d6f 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -26,7 +26,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 + uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 with: python-version: "3.10" # Runs before the install so a stale lock fails in seconds rather than @@ -80,7 +80,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 + uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 with: # The beta Python here relies on the action's prerelease default. python-version: ${{ matrix.python-version }} @@ -102,7 +102,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 + uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 with: python-version: "3.10" - id: setup-mongodb @@ -127,7 +127,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 + uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 with: python-version: "3.10" - name: Install dependencies @@ -149,7 +149,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 + uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 with: python-version: "3.10" - name: Install dependencies @@ -168,7 +168,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 + uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 with: python-version: "${{matrix.python}}" - name: Install dependencies @@ -185,7 +185,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 + uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 with: python-version: "3.10" - name: Install dependencies @@ -271,7 +271,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 + uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 with: python-version: "3.9" - id: setup-mongodb diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ef90224c2..980a18a76d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -199,6 +199,10 @@ the pages will re-render and the browser will automatically refresh. - Run `just install` to set a local virtual environment, or you can manually create a virtual environment and run `pytest` directly. If you want to use a specific version of Python, set `UV_PYTHON` before running `just install`. + + `just install` installs the pinned version of `uv` (from `[tool.uv] required-version`) into `$HOME/.local/bin`, + so make sure that directory is on your `PATH` (it usually is). If your `uv` is a different version, `uv` fails + fast and tells you how to update. - Ensure you have started the appropriate Mongo Server(s). You can run `just run-server` with optional args to set up the server. All given options will be passed to [`run-mongodb.sh`](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/run-mongodb.sh). Run `$DRIVERS_TOOLS/.evergreen/run-mongodb.sh start -h` @@ -419,6 +423,10 @@ tasks are host-agnostic. supported version of Python and use that. This ensures a consistent behavior across host types that do not have the Python toolchain (e.g. Azure VMs), by having a known version of Python with the build headers (`Python.h`) needed to build the C extensions. + - The uv binary version is pinned once in `[tool.uv] required-version` in `pyproject.toml`. + `.evergreen/scripts/install-dependencies.sh` installs it with `uv tool install`, uv enforces it locally, and + `astral-sh/setup-uv` reads it on GitHub. Bump it manually when a newer uv is needed. If uv cannot find the + requested Python, it installs it; if that fails, the task fails. - Regenerate the test variants and tasks using `pre-commit run --all-files generate-config`. - Make sure to add instructions for running the test suite to `CONTRIBUTING.md`. diff --git a/justfile b/justfile index df678cdbac..09f7fd6f33 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,11 @@ # See https://just.systems/man/en/ for instructions set shell := ["bash", "-c"] +# Put the dir that holds the pinned uv first on PATH for every recipe. On CI that +# is PYMONGO_BIN_DIR (so `just install`'s uv wins over a brew/system one); locally +# it is ~/.local/bin. This matters only when a different uv is earlier on PATH. +export PATH := env_var_or_default('PYMONGO_BIN_DIR', home_directory() + '/.local/bin') + ':' + env_var('PATH') + # Commonly used command segments. typing_run := "uv run --group typing --extra aws --extra encryption --with numpy --extra ocsp --extra snappy --extra test --extra zstd" docs_run := "uv run --extra docs" diff --git a/pyproject.toml b/pyproject.toml index 346df2f427..a41e230f0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,10 @@ Source = "https://github.com/mongodb/mongo-python-driver" Tracker = "https://jira.mongodb.org/projects/PYTHON/issues" [tool.uv] +# Pin the uv binary version across local dev, GitHub Actions, and Evergreen. +# uv enforces this locally and astral-sh/setup-uv reads it on GitHub; Evergreen +# pins it in setup-uv-python.sh. Bump manually when a newer uv is needed. +required-version = "==0.12.12" # boto3 dropped Python 3.9 support in 1.43, so the universal lock forks at # 3.10. Without a floor the pre-3.10 fork back-solves to boto3 1.7.84 (2018), # whose vendored six and invalid escape sequences break test collection. No From 10fc7a27c4e5941f4288e3f0654ff57d8edfb97b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 06:56:45 -0500 Subject: [PATCH 02/25] PYTHON-6091 Drop unneeded test-python action bump (uv auto-reads required-version) --- .github/workflows/test-python.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index f19a783d6f..73d5513a16 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -26,7 +26,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 + uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 with: python-version: "3.10" # Runs before the install so a stale lock fails in seconds rather than @@ -80,7 +80,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 + uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 with: # The beta Python here relies on the action's prerelease default. python-version: ${{ matrix.python-version }} @@ -102,7 +102,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 + uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 with: python-version: "3.10" - id: setup-mongodb @@ -127,7 +127,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 + uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 with: python-version: "3.10" - name: Install dependencies @@ -149,7 +149,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 + uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 with: python-version: "3.10" - name: Install dependencies @@ -168,7 +168,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 + uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 with: python-version: "${{matrix.python}}" - name: Install dependencies @@ -185,7 +185,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 + uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 with: python-version: "3.10" - name: Install dependencies @@ -271,7 +271,7 @@ jobs: with: persist-credentials: false - name: Install Python tooling - uses: mongodb-labs/drivers-github-tools/python/setup@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3.0.5 + uses: mongodb-labs/drivers-github-tools/python/setup@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3.0.2 with: python-version: "3.9" - id: setup-mongodb From 239c968c5005321e636ee7a5f784af840e1abfae Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 07:33:48 -0500 Subject: [PATCH 03/25] PYTHON-6091 Address review: always install pinned uv and fix bin dir/comment --- .evergreen/scripts/configure-env.sh | 3 ++- .evergreen/scripts/install-dependencies.sh | 15 +++++++-------- CONTRIBUTING.md | 4 ++-- pyproject.toml | 5 +++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 572960e304..adde681285 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -68,7 +68,8 @@ export PROJECT_DIRECTORY="$PROJECT_DIRECTORY" export CARGO_HOME="$CARGO_HOME" export UV_TOOL_DIR="$UV_TOOL_DIR" export UV_CACHE_DIR="$UV_CACHE_DIR" -export UV_TOOL_BIN_DIR="$DRIVERS_TOOLS_BINARIES" +# Send uv tool installs into our own bin dir, alongside the pinned uv/just. +export UV_TOOL_BIN_DIR="$PYMONGO_BIN_DIR" export PYMONGO_BIN_DIR="$PYMONGO_BIN_DIR" export PATH="$PATH_EXT" # shellcheck disable=SC2154 diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index e7b9e5f23b..ce6f812492 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -23,6 +23,7 @@ if [ "Windows_NT" = "${OS:-}" ]; then else export UV_TOOL_BIN_DIR="$PYMONGO_BIN_DIR" fi +mkdir -p "$PYMONGO_BIN_DIR" # Locate the Python toolchain's binary dir, so we can prefer its uv and just. _toolchain_bin="" @@ -57,17 +58,15 @@ fi # Pin the uv binary to the version in pyproject.toml's [tool.uv] required-version. # Run the current uv directly: it writes into PYMONGO_BIN_DIR (a different -# location), so nothing running is overwritten and no temp copy is needed. +# location), so nothing running is overwritten and no temp copy is needed. Always +# run it (idempotent) so uv lands in our bin dir even when it already matches. _uv_bin="$(command -v uv 2>/dev/null || true)" if [ -n "$_uv_bin" ]; then _uv_pin="$(awk -F'"' '/^[[:space:]]*required-version[[:space:]]*=/{print $2}' pyproject.toml)" - _uv_vers="$(uv --version 2>/dev/null | head -1 | awk '{print $2}' | sed 's/^v//')" - if [ "uv${_uv_pin}" != "uv==${_uv_vers}" ]; then - rm -f "$PYMONGO_BIN_DIR/uv" "$PYMONGO_BIN_DIR/uv.exe" \ - "$PYMONGO_BIN_DIR/uvx" "$PYMONGO_BIN_DIR/uvx.exe" - uv tool install -q --force --from "uv${_uv_pin}" uv - echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" - fi + rm -f "$PYMONGO_BIN_DIR/uv" "$PYMONGO_BIN_DIR/uv.exe" \ + "$PYMONGO_BIN_DIR/uvx" "$PYMONGO_BIN_DIR/uvx.exe" + uv tool install -q --force --from "uv${_uv_pin}" uv + echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" fi # Use just from the toolchain if available, otherwise install it. It must live in diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 980a18a76d..e9608aaeb4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -201,8 +201,8 @@ the pages will re-render and the browser will automatically refresh. version of Python, set `UV_PYTHON` before running `just install`. `just install` installs the pinned version of `uv` (from `[tool.uv] required-version`) into `$HOME/.local/bin`, - so make sure that directory is on your `PATH` (it usually is). If your `uv` is a different version, `uv` fails - fast and tells you how to update. + so make sure that directory is on your `PATH` (it usually is). If a project `uv` command (e.g. `just test`) runs + with a different `uv` version, `uv` fails fast and tells you how to update. - Ensure you have started the appropriate Mongo Server(s). You can run `just run-server` with optional args to set up the server. All given options will be passed to [`run-mongodb.sh`](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/run-mongodb.sh). Run `$DRIVERS_TOOLS/.evergreen/run-mongodb.sh start -h` diff --git a/pyproject.toml b/pyproject.toml index a41e230f0c..0323e044c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,8 +48,9 @@ Tracker = "https://jira.mongodb.org/projects/PYTHON/issues" [tool.uv] # Pin the uv binary version across local dev, GitHub Actions, and Evergreen. -# uv enforces this locally and astral-sh/setup-uv reads it on GitHub; Evergreen -# pins it in setup-uv-python.sh. Bump manually when a newer uv is needed. +# uv enforces this locally, astral-sh/setup-uv reads it on GitHub, and +# install-dependencies.sh installs it in Evergreen. Bump manually when a newer uv +# is needed. required-version = "==0.12.12" # boto3 dropped Python 3.9 support in 1.43, so the universal lock forks at # 3.10. Without a floor the pre-3.10 fork back-solves to boto3 1.7.84 (2018), From 097964a38de8a2521f768b04d229088bf99817c5 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 07:58:18 -0500 Subject: [PATCH 04/25] PYTHON-6091 Fix shellcheck SC2155 in Windows uv path handling --- .evergreen/scripts/install-dependencies.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index ce6f812492..24a2578d49 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -18,8 +18,10 @@ fi # uv.exe on Windows needs Windows-style paths, while bash uses the cygwin form. # Keep PYMONGO_BIN_DIR in the form PATH uses and give uv the Windows form. if [ "Windows_NT" = "${OS:-}" ]; then - export UV_TOOL_BIN_DIR="$(cygpath -m "$PYMONGO_BIN_DIR")" - export UV_TOOL_DIR="$(cygpath -m "${UV_TOOL_DIR:-$(dirname "$PYMONGO_BIN_DIR")/uv-tools}")" + _uv_tool_bin="$(cygpath -m "$PYMONGO_BIN_DIR")" + _uv_tool_dir="$(cygpath -m "${UV_TOOL_DIR:-$(dirname "$PYMONGO_BIN_DIR")/uv-tools}")" + export UV_TOOL_BIN_DIR="$_uv_tool_bin" + export UV_TOOL_DIR="$_uv_tool_dir" else export UV_TOOL_BIN_DIR="$PYMONGO_BIN_DIR" fi From e484fd15777e6e175228c49cd6bfb8f8a22b9945 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 12:41:31 -0500 Subject: [PATCH 05/25] PYTHON-6091 Source PATH from env.sh; only prepend local bin dir when it is absent --- justfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/justfile b/justfile index 09f7fd6f33..004ae38995 100644 --- a/justfile +++ b/justfile @@ -1,10 +1,10 @@ # See https://just.systems/man/en/ for instructions set shell := ["bash", "-c"] -# Put the dir that holds the pinned uv first on PATH for every recipe. On CI that -# is PYMONGO_BIN_DIR (so `just install`'s uv wins over a brew/system one); locally -# it is ~/.local/bin. This matters only when a different uv is earlier on PATH. -export PATH := env_var_or_default('PYMONGO_BIN_DIR', home_directory() + '/.local/bin') + ':' + env_var('PATH') +# On CI, env.sh (sourced by just.sh) already puts the bin dir that holds the +# pinned uv first on PATH, so only prepend ~/.local/bin locally, where there is +# no env.sh. This lets `just` find the pinned uv after `just install`. +export PATH := if env_var_or_default('PYMONGO_BIN_DIR', '') != '' { env_var('PATH') } else { home_directory() + '/.local/bin:' + env_var('PATH') } # Commonly used command segments. typing_run := "uv run --group typing --extra aws --extra encryption --with numpy --extra ocsp --extra snappy --extra test --extra zstd" From 17d616b032c9976b9ed43bdf00373bffe057769d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 12:58:56 -0500 Subject: [PATCH 06/25] PYTHON-6091 Use env.sh on CI and ~/.local/bin on spawn/local hosts --- .evergreen/scripts/configure-env.sh | 5 +++-- .evergreen/scripts/setup-system.sh | 10 ++++++++++ justfile | 5 ----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index adde681285..398447207a 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -24,9 +24,10 @@ MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" # drivers-tools tree. if [ "${CI:-}" == "true" ]; then PYMONGO_BIN_DIR="${TMPDIR:-/tmp}/pymongo-bin" -# We want to use a path that's already on PATH on spawn hosts. +# On spawn hosts and local dev, use the conventional ~/.local/bin which tools on +# the PATH (or the shell rc) can find. else - PYMONGO_BIN_DIR=$HOME/cli_bin + PYMONGO_BIN_DIR=$HOME/.local/bin fi PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" diff --git a/.evergreen/scripts/setup-system.sh b/.evergreen/scripts/setup-system.sh index bd7e2dd4bc..2ba8bba706 100755 --- a/.evergreen/scripts/setup-system.sh +++ b/.evergreen/scripts/setup-system.sh @@ -15,6 +15,16 @@ if [ -z "${CI:-}" ]; then bash $HERE/setup-dev-env.sh fi +# On spawn hosts / local dev (no Evergreen CI or GitHub Actions) the pinned uv and +# just live in ~/.local/bin, so make sure that is on PATH, adding it to .bashrc if +# it is not already. +if [ "${CI:-}" != "true" ] && [ "${GITHUB_ACTIONS:-}" != "true" ]; then + case ":$PATH:" in + *":$HOME/.local/bin:"*) ;; + *) echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc" ;; + esac +fi + # Enable core dumps if enabled on the machine # Copied from https://github.com/mongodb/mongo/blob/master/etc/evergreen.yml if [ -f /proc/self/coredump_filter ]; then diff --git a/justfile b/justfile index 004ae38995..df678cdbac 100644 --- a/justfile +++ b/justfile @@ -1,11 +1,6 @@ # See https://just.systems/man/en/ for instructions set shell := ["bash", "-c"] -# On CI, env.sh (sourced by just.sh) already puts the bin dir that holds the -# pinned uv first on PATH, so only prepend ~/.local/bin locally, where there is -# no env.sh. This lets `just` find the pinned uv after `just install`. -export PATH := if env_var_or_default('PYMONGO_BIN_DIR', '') != '' { env_var('PATH') } else { home_directory() + '/.local/bin:' + env_var('PATH') } - # Commonly used command segments. typing_run := "uv run --group typing --extra aws --extra encryption --with numpy --extra ocsp --extra snappy --extra test --extra zstd" docs_run := "uv run --extra docs" From 81abd31a3f5a61e5a1dad8aa14f82d80a840c9fb Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 13:08:49 -0500 Subject: [PATCH 07/25] PYTHON-6091 Note non-CI hosts include VMs in bin dir comment --- .evergreen/scripts/configure-env.sh | 4 ++-- .evergreen/scripts/setup-system.sh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 398447207a..e68907175d 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -24,8 +24,8 @@ MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" # drivers-tools tree. if [ "${CI:-}" == "true" ]; then PYMONGO_BIN_DIR="${TMPDIR:-/tmp}/pymongo-bin" -# On spawn hosts and local dev, use the conventional ~/.local/bin which tools on -# the PATH (or the shell rc) can find. +# On non-CI hosts (spawn hosts, VMs such as GCP/Azure, and local dev), use the +# conventional ~/.local/bin which tools on the PATH (or the shell rc) can find. else PYMONGO_BIN_DIR=$HOME/.local/bin fi diff --git a/.evergreen/scripts/setup-system.sh b/.evergreen/scripts/setup-system.sh index 2ba8bba706..eca8375a53 100755 --- a/.evergreen/scripts/setup-system.sh +++ b/.evergreen/scripts/setup-system.sh @@ -15,9 +15,9 @@ if [ -z "${CI:-}" ]; then bash $HERE/setup-dev-env.sh fi -# On spawn hosts / local dev (no Evergreen CI or GitHub Actions) the pinned uv and -# just live in ~/.local/bin, so make sure that is on PATH, adding it to .bashrc if -# it is not already. +# On non-CI hosts (spawn hosts, VMs such as GCP/Azure, and local dev) the pinned +# uv and just live in ~/.local/bin, so make sure that is on PATH, adding it to +# .bashrc if it is not already. if [ "${CI:-}" != "true" ] && [ "${GITHUB_ACTIONS:-}" != "true" ]; then case ":$PATH:" in *":$HOME/.local/bin:"*) ;; From 04c1ef2c6e88e5bcb5070b803b82e565f79bd22c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 13:44:19 -0500 Subject: [PATCH 08/25] PYTHON-6091 Fix uv pin on no-toolchain hosts; avoid overwriting running uv --- .evergreen/scripts/install-dependencies.sh | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 24a2578d49..ac6b54dc7c 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -60,15 +60,26 @@ fi # Pin the uv binary to the version in pyproject.toml's [tool.uv] required-version. # Run the current uv directly: it writes into PYMONGO_BIN_DIR (a different -# location), so nothing running is overwritten and no temp copy is needed. Always -# run it (idempotent) so uv lands in our bin dir even when it already matches. +# location), so nothing running is overwritten. If the running uv is already our +# pinned bin-dir uv, skip the install to avoid overwriting it (Windows refuses to +# overwrite a running executable); otherwise install so the pin lands in the bin +# dir even when the discovered uv already matches. _uv_bin="$(command -v uv 2>/dev/null || true)" if [ -n "$_uv_bin" ]; then _uv_pin="$(awk -F'"' '/^[[:space:]]*required-version[[:space:]]*=/{print $2}' pyproject.toml)" - rm -f "$PYMONGO_BIN_DIR/uv" "$PYMONGO_BIN_DIR/uv.exe" \ - "$PYMONGO_BIN_DIR/uvx" "$PYMONGO_BIN_DIR/uvx.exe" - uv tool install -q --force --from "uv${_uv_pin}" uv - echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" + case "$_uv_bin" in + "$PYMONGO_BIN_DIR"/*) + _uv_vers="$(uv --version 2>/dev/null | head -1 | awk '{print $2}' | sed 's/^v//')" + if [ "uv${_uv_pin}" != "uv==${_uv_vers}" ]; then + uv tool install -q --force --from "uv${_uv_pin}" uv + echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" + fi + ;; + *) + uv tool install -q --force --from "uv${_uv_pin}" uv + echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" + ;; + esac fi # Use just from the toolchain if available, otherwise install it. It must live in From 15760ac7f756fdbe89d2f93ee67d3fd5de68e825 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 16:54:32 -0500 Subject: [PATCH 09/25] PYTHON-6091 Address Copilot: prepend PATH, persist .bashrc entry, avoid Windows uv self-overwrite --- .evergreen/scripts/install-dependencies.sh | 7 ++++++- .evergreen/scripts/setup-dev-env.sh | 3 ++- .evergreen/scripts/setup-system.sh | 11 +++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index ac6b54dc7c..6fcad678dc 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -71,7 +71,12 @@ if [ -n "$_uv_bin" ]; then "$PYMONGO_BIN_DIR"/*) _uv_vers="$(uv --version 2>/dev/null | head -1 | awk '{print $2}' | sed 's/^v//')" if [ "uv${_uv_pin}" != "uv==${_uv_vers}" ]; then - uv tool install -q --force --from "uv${_uv_pin}" uv + # The running uv lives in our bin dir and is not the pin, so run the + # install from a copy: Windows will not overwrite a running executable. + _uv_tmp="$(mktemp -d)/$(basename "$_uv_bin")" + cp "$_uv_bin" "$_uv_tmp" && chmod +x "$_uv_tmp" + "$_uv_tmp" tool install -q --force --from "uv${_uv_pin}" uv + rm -rf "$(dirname "$_uv_tmp")" echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" fi ;; diff --git a/.evergreen/scripts/setup-dev-env.sh b/.evergreen/scripts/setup-dev-env.sh index 5a1794ee71..fecee90890 100755 --- a/.evergreen/scripts/setup-dev-env.sh +++ b/.evergreen/scripts/setup-dev-env.sh @@ -27,8 +27,9 @@ fi # Add the default install path to the path before configuring uv: setup-uv-python.sh # calls uv, and on a machine without env.sh the tool dir must already be on PATH. +# Prepend it so the pinned uv/just win over a different install earlier on PATH. if [ -z "${PYMONGO_BIN_DIR:-}" ]; then - export PATH="$PATH:$HOME/.local/bin" + export PATH="$HOME/.local/bin:$PATH" fi # Handle the value for UV_PYTHON. diff --git a/.evergreen/scripts/setup-system.sh b/.evergreen/scripts/setup-system.sh index eca8375a53..d3271b495a 100755 --- a/.evergreen/scripts/setup-system.sh +++ b/.evergreen/scripts/setup-system.sh @@ -16,13 +16,12 @@ if [ -z "${CI:-}" ]; then fi # On non-CI hosts (spawn hosts, VMs such as GCP/Azure, and local dev) the pinned -# uv and just live in ~/.local/bin, so make sure that is on PATH, adding it to -# .bashrc if it is not already. +# uv and just live in ~/.local/bin, so make sure a login shell finds them by +# adding the entry to .bashrc if it is not already there. env.sh (sourced earlier) +# already puts it on PATH, but that PATH does not persist past this SSH session. if [ "${CI:-}" != "true" ] && [ "${GITHUB_ACTIONS:-}" != "true" ]; then - case ":$PATH:" in - *":$HOME/.local/bin:"*) ;; - *) echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc" ;; - esac + grep -q 'HOME/.local/bin' "$HOME/.bashrc" 2>/dev/null || \ + echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc" fi # Enable core dumps if enabled on the machine From c39418071a1f841570746019a156b5de605da77d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 18:10:28 -0500 Subject: [PATCH 10/25] PYTHON-6091 Address Copilot: no-config bootstrap installs and always prepend bin dir --- .evergreen/scripts/install-dependencies.sh | 6 +++--- .evergreen/scripts/setup-dev-env.sh | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 6fcad678dc..7c225a6297 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -75,13 +75,13 @@ if [ -n "$_uv_bin" ]; then # install from a copy: Windows will not overwrite a running executable. _uv_tmp="$(mktemp -d)/$(basename "$_uv_bin")" cp "$_uv_bin" "$_uv_tmp" && chmod +x "$_uv_tmp" - "$_uv_tmp" tool install -q --force --from "uv${_uv_pin}" uv + "$_uv_tmp" tool install --no-config -q --force --from "uv${_uv_pin}" uv rm -rf "$(dirname "$_uv_tmp")" echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" fi ;; *) - uv tool install -q --force --from "uv${_uv_pin}" uv + uv tool install --no-config -q --force --from "uv${_uv_pin}" uv echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" ;; esac @@ -99,7 +99,7 @@ if [ ! -x "$PYMONGO_BIN_DIR/just" ] && [ ! -x "$PYMONGO_BIN_DIR/just.exe" ]; the cp "$_toolchain_bin/just.exe" "$PYMONGO_BIN_DIR/just.exe" chmod +x "$PYMONGO_BIN_DIR/just.exe" else - uv tool install rust-just + uv tool install --no-config rust-just fi fi diff --git a/.evergreen/scripts/setup-dev-env.sh b/.evergreen/scripts/setup-dev-env.sh index fecee90890..60c1295fb4 100755 --- a/.evergreen/scripts/setup-dev-env.sh +++ b/.evergreen/scripts/setup-dev-env.sh @@ -25,12 +25,11 @@ if [ -f $HERE/env.sh ]; then . $HERE/env.sh fi -# Add the default install path to the path before configuring uv: setup-uv-python.sh -# calls uv, and on a machine without env.sh the tool dir must already be on PATH. -# Prepend it so the pinned uv/just win over a different install earlier on PATH. -if [ -z "${PYMONGO_BIN_DIR:-}" ]; then - export PATH="$HOME/.local/bin:$PATH" -fi +# Add the install dir to the path before configuring uv, so the pinned uv/just +# win over a different install earlier on PATH (in this parent shell, since the +# dependency installer runs as a child process and its PATH change does not +# propagate here). +export PATH="${PYMONGO_BIN_DIR:-$HOME/.local/bin}:$PATH" # Handle the value for UV_PYTHON. . $HERE/setup-uv-python.sh From 50b804f15926a2de05c3813d0906651f75c5e37c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 11 Sep 2026 18:24:03 -0500 Subject: [PATCH 11/25] PYTHON-6091 Use checkout-local bin dir and persist actual install dir --- .evergreen/scripts/configure-env.sh | 2 +- .evergreen/scripts/setup-system.sh | 11 ++++++----- .gitignore | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index e68907175d..3143a76c4d 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -23,7 +23,7 @@ MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" # our binaries (uv, just, ...) in a task-local dir we control, independent of the # drivers-tools tree. if [ "${CI:-}" == "true" ]; then - PYMONGO_BIN_DIR="${TMPDIR:-/tmp}/pymongo-bin" + PYMONGO_BIN_DIR="$PROJECT_DIRECTORY/.local/bin" # On non-CI hosts (spawn hosts, VMs such as GCP/Azure, and local dev), use the # conventional ~/.local/bin which tools on the PATH (or the shell rc) can find. else diff --git a/.evergreen/scripts/setup-system.sh b/.evergreen/scripts/setup-system.sh index d3271b495a..6aafcd31c0 100755 --- a/.evergreen/scripts/setup-system.sh +++ b/.evergreen/scripts/setup-system.sh @@ -16,12 +16,13 @@ if [ -z "${CI:-}" ]; then fi # On non-CI hosts (spawn hosts, VMs such as GCP/Azure, and local dev) the pinned -# uv and just live in ~/.local/bin, so make sure a login shell finds them by -# adding the entry to .bashrc if it is not already there. env.sh (sourced earlier) -# already puts it on PATH, but that PATH does not persist past this SSH session. +# uv and just live in the sourced install dir (env.sh's PYMONGO_BIN_DIR), so make +# sure a login shell finds them by adding it to .bashrc if it is not already +# there. env.sh's PATH does not persist past this SSH session. if [ "${CI:-}" != "true" ] && [ "${GITHUB_ACTIONS:-}" != "true" ]; then - grep -q 'HOME/.local/bin' "$HOME/.bashrc" 2>/dev/null || \ - echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc" + _bin="${PYMONGO_BIN_DIR:-$HOME/.local/bin}" + grep -qF 'export PATH="'"$_bin"':$PATH"' "$HOME/.bashrc" 2>/dev/null || \ + printf 'export PATH="%s:$PATH"\n' "$_bin" >> "$HOME/.bashrc" fi # Enable core dumps if enabled on the machine diff --git a/.gitignore b/.gitignore index 6c4a512018..8546aeabb7 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ mongocryptd.pid .nova/ .temp/ venv/ +.local/ secrets-export.sh libmongocrypt.tar.gz libmongocrypt/ From 10ff9d6087fd7e59c5a34dfbb904627e6f5ac6ca Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 05:22:37 -0500 Subject: [PATCH 12/25] more cleanup --- .evergreen/scripts/configure-env.sh | 21 +--- .evergreen/scripts/install-dependencies.sh | 114 ++++++------------- .evergreen/scripts/setup-dev-env.sh | 25 ++++- .evergreen/scripts/setup-system.sh | 10 -- .evergreen/scripts/setup-uv.py | 125 +++++++++++++++++++++ 5 files changed, 185 insertions(+), 110 deletions(-) create mode 100755 .evergreen/scripts/setup-uv.py diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 3143a76c4d..839c43d31d 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -14,18 +14,15 @@ fi PROJECT_DIRECTORY="$(pwd)" DRIVERS_TOOLS="$(dirname $PROJECT_DIRECTORY)/drivers-tools" CARGO_HOME=${CARGO_HOME:-${DRIVERS_TOOLS}/.cargo} -UV_TOOL_DIR=$PROJECT_DIRECTORY/.local/uv/tools -UV_CACHE_DIR=$PROJECT_DIRECTORY/.local/uv/cache DRIVERS_TOOLS_BINARIES="$DRIVERS_TOOLS/.bin" MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" -# On Evergreen jobs, "CI" will be set, and we don't want to write to $HOME. Put -# our binaries (uv, just, ...) in a task-local dir we control, independent of the -# drivers-tools tree. +# On Evergreen jobs, "CI" will be set, and we don't want to write to $HOME or +# have binaries shared across tasks, so use a TMPDIR. On non-CI hosts +# (spawn hosts, VMs such as GCP/Azure, and local dev), use the conventional +# ~/.local/bin which tools on the PATH (or the shell rc) can find. if [ "${CI:-}" == "true" ]; then - PYMONGO_BIN_DIR="$PROJECT_DIRECTORY/.local/bin" -# On non-CI hosts (spawn hosts, VMs such as GCP/Azure, and local dev), use the -# conventional ~/.local/bin which tools on the PATH (or the shell rc) can find. + PYMONGO_BIN_DIR="${TMPDIR:-/tmp}"/pymongo_bin else PYMONGO_BIN_DIR=$HOME/.local/bin fi @@ -37,12 +34,8 @@ if [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS) PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY) CARGO_HOME=$(cygpath -m $CARGO_HOME) - UV_TOOL_DIR=$(cygpath -m "$UV_TOOL_DIR") - UV_CACHE_DIR=$(cygpath -m "$UV_CACHE_DIR") DRIVERS_TOOLS_BINARIES=$(cygpath -m "$DRIVERS_TOOLS_BINARIES") MONGODB_BINARIES=$(cygpath -m "$MONGODB_BINARIES") - # Keep PYMONGO_BIN_DIR in cygwin form so bash can search it on PATH; native - # uv gets the Windows form (via cygpath -m) inside install-dependencies.sh. PYMONGO_BIN_DIR=$(cygpath -u "$PYMONGO_BIN_DIR") fi @@ -67,10 +60,6 @@ export DRIVERS_TOOLS_BINARIES="$DRIVERS_TOOLS_BINARIES" export PROJECT_DIRECTORY="$PROJECT_DIRECTORY" export CARGO_HOME="$CARGO_HOME" -export UV_TOOL_DIR="$UV_TOOL_DIR" -export UV_CACHE_DIR="$UV_CACHE_DIR" -# Send uv tool installs into our own bin dir, alongside the pinned uv/just. -export UV_TOOL_BIN_DIR="$PYMONGO_BIN_DIR" export PYMONGO_BIN_DIR="$PYMONGO_BIN_DIR" export PATH="$PATH_EXT" # shellcheck disable=SC2154 diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 7c225a6297..47d6eca045 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -11,96 +11,54 @@ if [ -f $HERE/env.sh ]; then . $HERE/env.sh fi -# Set up the default bin directory. -if [ -z "${PYMONGO_BIN_DIR:-}" ]; then - PYMONGO_BIN_DIR="$HOME/.local/bin" -fi -# uv.exe on Windows needs Windows-style paths, while bash uses the cygwin form. -# Keep PYMONGO_BIN_DIR in the form PATH uses and give uv the Windows form. +# PYMONGO_BIN_DIR is set by setup-system.sh/env.sh (or setup-dev-env.sh); default +# it for robustness. UV_TOOL_BIN_DIR is uv's name for the same dir (setup-uv.py +# reads both). UV_TOOL_DIR is left to ensure_uv.sh. +export PYMONGO_BIN_DIR="${PYMONGO_BIN_DIR:-$HOME/.local/bin}" +export UV_TOOL_BIN_DIR="${UV_TOOL_BIN_DIR:-$PYMONGO_BIN_DIR}" +# uv is a native Windows binary: give it a Windows path on cygwin. if [ "Windows_NT" = "${OS:-}" ]; then _uv_tool_bin="$(cygpath -m "$PYMONGO_BIN_DIR")" - _uv_tool_dir="$(cygpath -m "${UV_TOOL_DIR:-$(dirname "$PYMONGO_BIN_DIR")/uv-tools}")" export UV_TOOL_BIN_DIR="$_uv_tool_bin" - export UV_TOOL_DIR="$_uv_tool_dir" -else - export UV_TOOL_BIN_DIR="$PYMONGO_BIN_DIR" -fi -mkdir -p "$PYMONGO_BIN_DIR" - -# Locate the Python toolchain's binary dir, so we can prefer its uv and just. -_toolchain_bin="" -if [ "Windows_NT" = "${OS:-}" ]; then - _toolchain_bin="/cygdrive/c/Python/Current/Scripts" -elif [ "$(uname -s)" = "Darwin" ]; then - _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" -else - _toolchain_bin="/opt/python/Current/bin" fi -# Prefer the toolchain's uv as a bootstrap when uv is not already on PATH, so we -# do not fall back to installing from astral. -if ! command -v uv &>/dev/null; then - if [ -x "$_toolchain_bin/uv" ] || [ -x "$_toolchain_bin/uv.exe" ]; then - echo "Found uv in the toolchain at $_toolchain_bin" - export PATH="$_toolchain_bin:$PATH" +# If uv is on PATH, check it via `uv sync`, which fails fast if it is not the +# pinned version (from pyproject.toml's [tool.uv] required-version). If that +# succeeds, the environment is already correct and there is nothing to set up; +# otherwise fall through to the setup below. +# +# On CI we also require UV_CACHE_DIR to be set: ensure_uv.sh scopes uv's cache +# to a task-local dir, so an unset UV_CACHE_DIR means the uv setup has not run +# yet in this task and we must do the setup phase. +_need_setup=1 +if command -v uv >/dev/null 2>&1 && uv sync >/dev/null 2>&1; then + if [ "${CI:-}" != "true" ] || [ -n "${UV_CACHE_DIR:-}" ]; then + echo "uv is already set up; skipping uv setup." + _need_setup=0 fi fi -# Ensure uv is available (bootstrap if absent). -if ! command -v uv &>/dev/null; then - _BIN_DIR=$PYMONGO_BIN_DIR - mkdir -p ${_BIN_DIR} - echo "uv not found on PATH; installing the latest uv from astral..." - curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR="$_BIN_DIR" INSTALLER_NO_MODIFY_PATH=1 sh - if [ "Windows_NT" = "${OS:-}" ]; then - chmod +x "$(cygpath -u $_BIN_DIR)/uv.exe" +# Set up uv if needed. +if [ "$_need_setup" = "1" ]; then + # ensure-uv.sh (drivers-evergreen-tools) finds or installs uv and scopes its env. + if [ -n "${DRIVERS_TOOLS:-}" ] && [ -f "$DRIVERS_TOOLS/.evergreen/ensure-uv.sh" ]; then + . "$DRIVERS_TOOLS/.evergreen/ensure-uv.sh" + ensure_uv || exit 1 fi - export PATH="$PYMONGO_BIN_DIR:$PATH" -fi -# Pin the uv binary to the version in pyproject.toml's [tool.uv] required-version. -# Run the current uv directly: it writes into PYMONGO_BIN_DIR (a different -# location), so nothing running is overwritten. If the running uv is already our -# pinned bin-dir uv, skip the install to avoid overwriting it (Windows refuses to -# overwrite a running executable); otherwise install so the pin lands in the bin -# dir even when the discovered uv already matches. -_uv_bin="$(command -v uv 2>/dev/null || true)" -if [ -n "$_uv_bin" ]; then - _uv_pin="$(awk -F'"' '/^[[:space:]]*required-version[[:space:]]*=/{print $2}' pyproject.toml)" - case "$_uv_bin" in - "$PYMONGO_BIN_DIR"/*) - _uv_vers="$(uv --version 2>/dev/null | head -1 | awk '{print $2}' | sed 's/^v//')" - if [ "uv${_uv_pin}" != "uv==${_uv_vers}" ]; then - # The running uv lives in our bin dir and is not the pin, so run the - # install from a copy: Windows will not overwrite a running executable. - _uv_tmp="$(mktemp -d)/$(basename "$_uv_bin")" - cp "$_uv_bin" "$_uv_tmp" && chmod +x "$_uv_tmp" - "$_uv_tmp" tool install --no-config -q --force --from "uv${_uv_pin}" uv - rm -rf "$(dirname "$_uv_tmp")" - echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" - fi - ;; - *) - uv tool install --no-config -q --force --from "uv${_uv_pin}" uv - echo "Using uv at $PYMONGO_BIN_DIR/uv ($("$PYMONGO_BIN_DIR/uv" --version 2>/dev/null | head -1 | awk '{print $2}'))" - ;; - esac -fi + # Do the uv setup (bin dir, pinning, env.sh) under uv's own interpreter. + uv run "$HERE/setup-uv.py" -# Use just from the toolchain if available, otherwise install it. It must live in -# our bin dir to be on PATH for callers; copying it keeps the toolchain's just. -if [ ! -x "$PYMONGO_BIN_DIR/just" ] && [ ! -x "$PYMONGO_BIN_DIR/just.exe" ]; then - if [ -x "$_toolchain_bin/just" ]; then - echo "Using just from the toolchain" - cp "$_toolchain_bin/just" "$PYMONGO_BIN_DIR/just" - chmod +x "$PYMONGO_BIN_DIR/just" - elif [ -x "$_toolchain_bin/just.exe" ]; then - echo "Using just from the toolchain" - cp "$_toolchain_bin/just.exe" "$PYMONGO_BIN_DIR/just.exe" - chmod +x "$PYMONGO_BIN_DIR/just.exe" - else - uv tool install --no-config rust-just + # Re-source env.sh so the values setup-uv.py wrote are available. + if [ -f $HERE/env.sh ]; then + . $HERE/env.sh fi fi +# Make just available. It has no version constraint, so if it is already on PATH +# there is nothing to do; otherwise install it into the bin dir via uv. +if ! command -v just >/dev/null 2>&1; then + uv tool install --no-config rust-just +fi + popd > /dev/null diff --git a/.evergreen/scripts/setup-dev-env.sh b/.evergreen/scripts/setup-dev-env.sh index 60c1295fb4..18b6cd5b5a 100755 --- a/.evergreen/scripts/setup-dev-env.sh +++ b/.evergreen/scripts/setup-dev-env.sh @@ -16,6 +16,25 @@ if [ -f $HERE/test-env.sh ]; then . $HERE/test-env.sh fi +# The bin dir for the pinned uv/just. setup-system.sh sets it on evergreen hosts; +# default it here so local dev (without setup-system.sh) also has a usable value. +export PYMONGO_BIN_DIR="${PYMONGO_BIN_DIR:-$HOME/.local/bin}" + +# Make sure a login shell can find the bin dir by adding it to the rc file, so +# local dev (which may never run setup-system.sh) still has it on PATH. env.sh's +# PATH does not persist past this session. Prefer .zshrc when the shell is zsh. +if [ "${CI:-}" != "true" ] && [ "${GITHUB_ACTIONS:-}" != "true" ]; then + if [ -f "$HOME/.zshrc" ]; then + _rc="$HOME/.zshrc" + else + _rc="$HOME/.bashrc" + fi + if [ -f "$_rc" ]; then + grep -qF 'export PATH="'"$PYMONGO_BIN_DIR"':$PATH"' "$_rc" 2>/dev/null || \ + printf 'export PATH="%s:$PATH"\n' "$PYMONGO_BIN_DIR" >> "$_rc" + fi +fi + # Ensure dependencies are installed. bash $HERE/install-dependencies.sh @@ -25,12 +44,6 @@ if [ -f $HERE/env.sh ]; then . $HERE/env.sh fi -# Add the install dir to the path before configuring uv, so the pinned uv/just -# win over a different install earlier on PATH (in this parent shell, since the -# dependency installer runs as a child process and its PATH change does not -# propagate here). -export PATH="${PYMONGO_BIN_DIR:-$HOME/.local/bin}:$PATH" - # Handle the value for UV_PYTHON. . $HERE/setup-uv-python.sh diff --git a/.evergreen/scripts/setup-system.sh b/.evergreen/scripts/setup-system.sh index 6aafcd31c0..bd7e2dd4bc 100755 --- a/.evergreen/scripts/setup-system.sh +++ b/.evergreen/scripts/setup-system.sh @@ -15,16 +15,6 @@ if [ -z "${CI:-}" ]; then bash $HERE/setup-dev-env.sh fi -# On non-CI hosts (spawn hosts, VMs such as GCP/Azure, and local dev) the pinned -# uv and just live in the sourced install dir (env.sh's PYMONGO_BIN_DIR), so make -# sure a login shell finds them by adding it to .bashrc if it is not already -# there. env.sh's PATH does not persist past this SSH session. -if [ "${CI:-}" != "true" ] && [ "${GITHUB_ACTIONS:-}" != "true" ]; then - _bin="${PYMONGO_BIN_DIR:-$HOME/.local/bin}" - grep -qF 'export PATH="'"$_bin"':$PATH"' "$HOME/.bashrc" 2>/dev/null || \ - printf 'export PATH="%s:$PATH"\n' "$_bin" >> "$HOME/.bashrc" -fi - # Enable core dumps if enabled on the machine # Copied from https://github.com/mongodb/mongo/blob/master/etc/evergreen.yml if [ -f /proc/self/coredump_filter ]; then diff --git a/.evergreen/scripts/setup-uv.py b/.evergreen/scripts/setup-uv.py new file mode 100755 index 0000000000..3ec9a7eeba --- /dev/null +++ b/.evergreen/scripts/setup-uv.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""Bootstrap the pinned uv/just for the test environment. + +install-dependencies.sh bails out if the pinned uv is already on PATH, sources +ensure-uv.sh (which finds or installs uv), then runs this script for the rest. +Only the standard library is used. +""" + +from __future__ import annotations + +import os +import re +import shutil +import subprocess +import sys +from pathlib import Path + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parent.parent +ENV_SH = HERE / "env.sh" +ASTURL_INSTALL_URL = "https://astral.sh/uv/install.sh" + + +def required_uv_pin() -> str: + """Return [tool.uv] required-version, e.g. '==0.12.12' ('' if absent).""" + pattern = re.compile(r"required-version\s*=\s*['\"]?([^'\"\s]+)['\"]?") + in_uv = False + for line in (ROOT / "pyproject.toml").read_text().splitlines(): + stripped = line.strip() + if stripped.startswith("["): + in_uv = stripped == "[tool.uv]" + continue + if in_uv: + match = pattern.search(stripped) + if match: + return match.group(1) + return "" + + +def _add_path(dir_: str) -> None: + os.environ["PATH"] = dir_ + os.pathsep + os.environ.get("PATH", "") + + +def _install_uv_astral() -> None: + """Install uv from astral when ensure-uv.sh was not available. + + UV_TOOL_BIN_DIR is set (in native form on Windows) by install-dependencies.sh. + """ + print("uv not found; installing the latest uv from astral...") + env = { + **os.environ, + "UV_INSTALL_DIR": os.environ["UV_TOOL_BIN_DIR"], + "INSTALLER_NO_MODIFY_PATH": "1", + } + curl = shutil.which("curl") + sh = shutil.which("sh") + proc = subprocess.run( # noqa: S603 + [curl, "-LsSf", ASTURL_INSTALL_URL], capture_output=True, env=env, check=True + ) + subprocess.run([sh], input=proc.stdout, env=env, check=True) # noqa: S603 + _add_path(os.environ["UV_TOOL_BIN_DIR"]) + + +def _pin_uv(uv_pin: str) -> None: + """Install the pinned uv via uv tool install --force. + + UV_TOOL_BIN_DIR and UV_TOOL_DIR are inherited from the environment (set by + install-dependencies.sh / ensure-uv.sh, in native form on Windows). uv tool + install writes the binary into UV_TOOL_BIN_DIR and the tool venv into + UV_TOOL_DIR; --force lets it overwrite an existing install. + """ + subprocess.run( # noqa: S603 + [ + shutil.which("uv"), + "tool", + "install", + "--no-config", + "-q", + "--force", + "--from", + f"uv{uv_pin}", + "uv", + ], + check=True, + ) + + +def _write_env() -> None: + """Write every UV_* env var into env.sh, replacing existing UV_* entries.""" + values = {k: v for k, v in os.environ.items() if k.startswith("UV_")} + if not values: + return + existing = ENV_SH.read_text() if ENV_SH.exists() else "" + keep = [] + for line in existing.splitlines(): + stripped = line.strip() + if stripped.startswith("export "): + var, _, _ = stripped[len("export ") :].partition("=") + if var.startswith("UV_"): + continue + keep.append(line) + keep.append("") + keep.extend(f'export {name}="{value}"' for name, value in sorted(values.items())) + ENV_SH.write_text("\n".join(keep) + "\n") + + +def main() -> int: + bin_dir = os.environ["UV_TOOL_BIN_DIR"] + Path(bin_dir).mkdir(parents=True, exist_ok=True) + _add_path(bin_dir) + + # Bootstrap a uv from astral if ensure-uv.sh didn't run and uv isn't on PATH. + if shutil.which("uv") is None: + _install_uv_astral() + + uv_pin = required_uv_pin() + if uv_pin: + _pin_uv(uv_pin) + + _write_env() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 2a2212dd1fa9741b6e36a629c19db7ad37f9129d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 05:47:34 -0500 Subject: [PATCH 13/25] PYTHON-6091 Use toolchain python3 for setup-uv.py --- .evergreen/scripts/configure-env.sh | 17 ++++++++++++++++- .evergreen/scripts/install-dependencies.sh | 5 +++-- .evergreen/scripts/setup-uv.py | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 839c43d31d..64252d2002 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -27,7 +27,22 @@ else PYMONGO_BIN_DIR=$HOME/.local/bin fi -PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" +# Add the latest MongoDB toolchain bin dir to PATH if it exists, so that hosts +# with old system Python3 still get a modern interpreter for setup scripts. +# It goes after PYMONGO_BIN_DIR so the pinned uv (installed there by setup-uv.py) +# takes precedence over the toolchain's uv. +if [ "Windows_NT" = "${OS:-}" ]; then + _toolchain_bin="/cygdrive/c/Python/Current/Scripts" +elif [ "$(uname -s)" == "Darwin" ]; then + _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" +else + _toolchain_bin="/opt/python/Current/bin" +fi +if [ -d "$_toolchain_bin" ]; then + PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$_toolchain_bin:$DRIVERS_TOOLS_BINARIES:\$PATH" +else + PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" +fi # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory if [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 47d6eca045..9e7ef8499e 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -46,8 +46,9 @@ if [ "$_need_setup" = "1" ]; then ensure_uv || exit 1 fi - # Do the uv setup (bin dir, pinning, env.sh) under uv's own interpreter. - uv run "$HERE/setup-uv.py" + # Do the uv setup (bin dir, pinning, env.sh). Uses the toolchain python3 + # (added to PATH by configure-env.sh) to avoid uv's required-version check. + python3 "$HERE/setup-uv.py" # Re-source env.sh so the values setup-uv.py wrote are available. if [ -f $HERE/env.sh ]; then diff --git a/.evergreen/scripts/setup-uv.py b/.evergreen/scripts/setup-uv.py index 3ec9a7eeba..057eb858f0 100755 --- a/.evergreen/scripts/setup-uv.py +++ b/.evergreen/scripts/setup-uv.py @@ -18,7 +18,7 @@ HERE = Path(__file__).resolve().parent ROOT = HERE.parent.parent ENV_SH = HERE / "env.sh" -ASTURL_INSTALL_URL = "https://astral.sh/uv/install.sh" +ASTRAL_INSTALL_URL = "https://astral.sh/uv/install.sh" def required_uv_pin() -> str: @@ -55,7 +55,7 @@ def _install_uv_astral() -> None: curl = shutil.which("curl") sh = shutil.which("sh") proc = subprocess.run( # noqa: S603 - [curl, "-LsSf", ASTURL_INSTALL_URL], capture_output=True, env=env, check=True + [curl, "-LsSf", ASTRAL_INSTALL_URL], capture_output=True, env=env, check=True ) subprocess.run([sh], input=proc.stdout, env=env, check=True) # noqa: S603 _add_path(os.environ["UV_TOOL_BIN_DIR"]) From 22a9b9adfa920bf862a6771c1258888b31bd82a7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 05:59:09 -0500 Subject: [PATCH 14/25] PYTHON-6091 Convert setup-uv.py path for Windows python3 --- .evergreen/scripts/install-dependencies.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 9e7ef8499e..99cfde8568 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -48,7 +48,12 @@ if [ "$_need_setup" = "1" ]; then # Do the uv setup (bin dir, pinning, env.sh). Uses the toolchain python3 # (added to PATH by configure-env.sh) to avoid uv's required-version check. - python3 "$HERE/setup-uv.py" + # On Windows the script path must be a native Windows path for python3. + _uv_setup_script="$HERE/setup-uv.py" + if [ "Windows_NT" = "${OS:-}" ]; then + _uv_setup_script="$(cygpath -m "$_uv_setup_script")" + fi + python3 "$_uv_setup_script" # Re-source env.sh so the values setup-uv.py wrote are available. if [ -f $HERE/env.sh ]; then From c3daaf9e6f1e1022a0ccbebb09cd73866fc903b7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 06:04:09 -0500 Subject: [PATCH 15/25] PYTHON-6091 Bypass required-version via uv run --no-config --- .evergreen/scripts/configure-env.sh | 17 +---------------- .evergreen/scripts/install-dependencies.sh | 12 ++++-------- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 64252d2002..839c43d31d 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -27,22 +27,7 @@ else PYMONGO_BIN_DIR=$HOME/.local/bin fi -# Add the latest MongoDB toolchain bin dir to PATH if it exists, so that hosts -# with old system Python3 still get a modern interpreter for setup scripts. -# It goes after PYMONGO_BIN_DIR so the pinned uv (installed there by setup-uv.py) -# takes precedence over the toolchain's uv. -if [ "Windows_NT" = "${OS:-}" ]; then - _toolchain_bin="/cygdrive/c/Python/Current/Scripts" -elif [ "$(uname -s)" == "Darwin" ]; then - _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" -else - _toolchain_bin="/opt/python/Current/bin" -fi -if [ -d "$_toolchain_bin" ]; then - PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$_toolchain_bin:$DRIVERS_TOOLS_BINARIES:\$PATH" -else - PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" -fi +PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory if [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 99cfde8568..d0a10275a8 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -46,14 +46,10 @@ if [ "$_need_setup" = "1" ]; then ensure_uv || exit 1 fi - # Do the uv setup (bin dir, pinning, env.sh). Uses the toolchain python3 - # (added to PATH by configure-env.sh) to avoid uv's required-version check. - # On Windows the script path must be a native Windows path for python3. - _uv_setup_script="$HERE/setup-uv.py" - if [ "Windows_NT" = "${OS:-}" ]; then - _uv_setup_script="$(cygpath -m "$_uv_setup_script")" - fi - python3 "$_uv_setup_script" + # Do the uv setup (bin dir, pinning, env.sh). `--no-config` skips the + # required-version check from pyproject.toml, letting any bootstrapped uv + # install the pinned version in setup-uv.py. + uv run --no-config "$HERE/setup-uv.py" # Re-source env.sh so the values setup-uv.py wrote are available. if [ -f $HERE/env.sh ]; then From 905d3bf93edd17534a04448b2d29a45e560414af Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 06:41:40 -0500 Subject: [PATCH 16/25] PYTHON-6091 Add toolchain python to PATH and fix Windows script path --- .evergreen/scripts/configure-env.sh | 18 +++++++++++++++++- .evergreen/scripts/install-dependencies.sh | 9 +++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index 839c43d31d..a92c0b023a 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -27,7 +27,23 @@ else PYMONGO_BIN_DIR=$HOME/.local/bin fi -PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" +# Add the latest MongoDB toolchain bin dir to PATH if it exists, so that hosts +# with an old system Python (e.g. RHEL8's 3.6) still get a modern interpreter +# for tool installs like `uv tool install rust-just`. It goes after +# PYMONGO_BIN_DIR so the pinned uv (installed there by setup-uv.py) takes +# precedence over the toolchain's uv. +if [ "Windows_NT" = "${OS:-}" ]; then + _toolchain_bin="/cygdrive/c/Python/Current/Scripts" +elif [ "$(uname -s)" == "Darwin" ]; then + _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" +else + _toolchain_bin="/opt/python/Current/bin" +fi +if [ -d "$_toolchain_bin" ]; then + PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$_toolchain_bin:$DRIVERS_TOOLS_BINARIES:\$PATH" +else + PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" +fi # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory if [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index d0a10275a8..860c44e7a1 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -48,8 +48,13 @@ if [ "$_need_setup" = "1" ]; then # Do the uv setup (bin dir, pinning, env.sh). `--no-config` skips the # required-version check from pyproject.toml, letting any bootstrapped uv - # install the pinned version in setup-uv.py. - uv run --no-config "$HERE/setup-uv.py" + # install the pinned version in setup-uv.py. On Windows the script path must + # be a native Windows path for uv. + _uv_setup_script="$HERE/setup-uv.py" + if [ "Windows_NT" = "${OS:-}" ]; then + _uv_setup_script="$(cygpath -m "$_uv_setup_script")" + fi + uv run --no-config "$_uv_setup_script" # Re-source env.sh so the values setup-uv.py wrote are available. if [ -f $HERE/env.sh ]; then From cc4fea506990bf63b1724745848b58a63f0ace16 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 09:30:14 -0500 Subject: [PATCH 17/25] PYTHON-6091 Use toolchain python3 for setup-uv and write LF env.sh --- .evergreen/scripts/install-dependencies.sh | 10 +++++----- .evergreen/scripts/setup-uv.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 860c44e7a1..42f5893079 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -46,15 +46,15 @@ if [ "$_need_setup" = "1" ]; then ensure_uv || exit 1 fi - # Do the uv setup (bin dir, pinning, env.sh). `--no-config` skips the - # required-version check from pyproject.toml, letting any bootstrapped uv - # install the pinned version in setup-uv.py. On Windows the script path must - # be a native Windows path for uv. + # Do the uv setup (bin dir, pinning, env.sh). Uses the toolchain python3 + # (added to PATH by configure-env.sh) so no project .venv is created here, + # and no required-version check is triggered. On Windows the script path must + # be a native Windows path for python3. _uv_setup_script="$HERE/setup-uv.py" if [ "Windows_NT" = "${OS:-}" ]; then _uv_setup_script="$(cygpath -m "$_uv_setup_script")" fi - uv run --no-config "$_uv_setup_script" + python3 "$_uv_setup_script" # Re-source env.sh so the values setup-uv.py wrote are available. if [ -f $HERE/env.sh ]; then diff --git a/.evergreen/scripts/setup-uv.py b/.evergreen/scripts/setup-uv.py index 057eb858f0..dedc67b5be 100755 --- a/.evergreen/scripts/setup-uv.py +++ b/.evergreen/scripts/setup-uv.py @@ -101,7 +101,7 @@ def _write_env() -> None: keep.append(line) keep.append("") keep.extend(f'export {name}="{value}"' for name, value in sorted(values.items())) - ENV_SH.write_text("\n".join(keep) + "\n") + ENV_SH.write_text("\n".join(keep) + "\n", newline="\n") def main() -> int: From 79bd6a6b4fa3717e0792337ee48e8444d4db849f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 12:19:12 -0500 Subject: [PATCH 18/25] PYTHON-6091 Fix setup-uv on Windows: LF env.sh and avoid uv self-overwrite --- .evergreen/scripts/setup-uv.py | 47 +++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/.evergreen/scripts/setup-uv.py b/.evergreen/scripts/setup-uv.py index dedc67b5be..7089275be3 100755 --- a/.evergreen/scripts/setup-uv.py +++ b/.evergreen/scripts/setup-uv.py @@ -13,6 +13,7 @@ import shutil import subprocess import sys +import tempfile from pathlib import Path HERE = Path(__file__).resolve().parent @@ -68,21 +69,35 @@ def _pin_uv(uv_pin: str) -> None: install-dependencies.sh / ensure-uv.sh, in native form on Windows). uv tool install writes the binary into UV_TOOL_BIN_DIR and the tool venv into UV_TOOL_DIR; --force lets it overwrite an existing install. + + Windows will not overwrite a running executable, so when the current uv is + already the target bin dir, run the install from a copy of the binary. """ - subprocess.run( # noqa: S603 - [ - shutil.which("uv"), - "tool", - "install", - "--no-config", - "-q", - "--force", - "--from", - f"uv{uv_pin}", - "uv", - ], - check=True, - ) + uv_path = shutil.which("uv") + tool = uv_path + tmp_uv = None + if os.name == "nt": + tmp_uv = Path(tempfile.mkdtemp()) / Path(uv_path).name + shutil.copy2(uv_path, tmp_uv) + tool = str(tmp_uv) + try: + subprocess.run( # noqa: S603 + [ + tool, + "tool", + "install", + "--no-config", + "-q", + "--force", + "--from", + f"uv{uv_pin}", + "uv", + ], + check=True, + ) + finally: + if tmp_uv: + shutil.rmtree(tmp_uv.parent, ignore_errors=True) def _write_env() -> None: @@ -101,7 +116,9 @@ def _write_env() -> None: keep.append(line) keep.append("") keep.extend(f'export {name}="{value}"' for name, value in sorted(values.items())) - ENV_SH.write_text("\n".join(keep) + "\n", newline="\n") + # Write LF bytes directly: on Windows text mode translates \n to \r\n, which + # breaks bash sourcing env.sh, and `newline=` isn't available on all Pythons. + ENV_SH.write_bytes(("\n".join(keep) + "\n").encode()) def main() -> int: From a1cc070901e4cc5788997b9b3fc3c2dd13ce12b7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 22 Sep 2026 06:42:41 -0500 Subject: [PATCH 19/25] PYTHON-6091 Address review: export bin dir PATH, py3.6 compat, docs --- .evergreen/scripts/install-dependencies.sh | 8 ++++++++ .evergreen/scripts/setup-uv.py | 11 +++++++---- CONTRIBUTING.md | 4 ++-- pyproject.toml | 4 ++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 42f5893079..ef9f60d162 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -22,6 +22,14 @@ if [ "Windows_NT" = "${OS:-}" ]; then export UV_TOOL_BIN_DIR="$_uv_tool_bin" fi +# Ensure the bin dir is on PATH: hosts without env.sh (e.g. auth-aws-ecs) never +# export it, so a fresh pinned install there would be invisible to the probe +# below and to later steps like `uv tool install` and `uv sync`. +case ":$PATH:" in + *":$PYMONGO_BIN_DIR:"*) ;; + *) export PATH="$PYMONGO_BIN_DIR:$PATH" ;; +esac + # If uv is on PATH, check it via `uv sync`, which fails fast if it is not the # pinned version (from pyproject.toml's [tool.uv] required-version). If that # succeeds, the environment is already correct and there is nothing to set up; diff --git a/.evergreen/scripts/setup-uv.py b/.evergreen/scripts/setup-uv.py index 7089275be3..30d20949e9 100755 --- a/.evergreen/scripts/setup-uv.py +++ b/.evergreen/scripts/setup-uv.py @@ -3,11 +3,10 @@ install-dependencies.sh bails out if the pinned uv is already on PATH, sources ensure-uv.sh (which finds or installs uv), then runs this script for the rest. -Only the standard library is used. +Only the standard library is used, and it must run on the old system +Pythons (3.6) found on some no-toolchain hosts. """ -from __future__ import annotations - import os import re import shutil @@ -56,7 +55,11 @@ def _install_uv_astral() -> None: curl = shutil.which("curl") sh = shutil.which("sh") proc = subprocess.run( # noqa: S603 - [curl, "-LsSf", ASTRAL_INSTALL_URL], capture_output=True, env=env, check=True + [curl, "-LsSf", ASTRAL_INSTALL_URL], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + check=True, ) subprocess.run([sh], input=proc.stdout, env=env, check=True) # noqa: S603 _add_path(os.environ["UV_TOOL_BIN_DIR"]) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9608aaeb4..95d93e1aab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -201,8 +201,8 @@ the pages will re-render and the browser will automatically refresh. version of Python, set `UV_PYTHON` before running `just install`. `just install` installs the pinned version of `uv` (from `[tool.uv] required-version`) into `$HOME/.local/bin`, - so make sure that directory is on your `PATH` (it usually is). If a project `uv` command (e.g. `just test`) runs - with a different `uv` version, `uv` fails fast and tells you how to update. + and adds that directory to your shell rc file when missing, so the pinned `uv` takes effect in new shells. If a + project `uv` command (e.g. `just test`) runs with a different `uv` version, `uv` fails fast and tells you how to update. - Ensure you have started the appropriate Mongo Server(s). You can run `just run-server` with optional args to set up the server. All given options will be passed to [`run-mongodb.sh`](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/run-mongodb.sh). Run `$DRIVERS_TOOLS/.evergreen/run-mongodb.sh start -h` diff --git a/pyproject.toml b/pyproject.toml index 0323e044c1..427759a491 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -254,6 +254,10 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?)|dummy.*)$" [tool.ruff.lint.per-file-ignores] "pymongo/__init__.py" = ["E402"] ".evergreen/scripts/*.py" = ["T201"] +# setup-uv.py must run on the bare system python3 (3.6) found on some +# no-toolchain hosts: `from __future__ import annotations` (I002) is a +# SyntaxError there, and `capture_output=` (UP022) was added in 3.7. +".evergreen/scripts/setup-uv.py" = ["I002", "UP022"] "test/*.py" = ["PT", "E402", "PLW", "SIM", "E741", "PTH", "S", "B904", "E722", "T201", "RET", "ARG", "F405", "B028", "PGH001", "B018", "F403", "RUF015", "E731", "B007", "UP031", "F401", "B023", "F811"] From 784b545d286d4cfa27dbcfe30d84211dee255f8b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 22 Sep 2026 10:14:43 -0500 Subject: [PATCH 20/25] PYTHON-6091 Prepend toolchain python dir to PATH before setup-uv.py --- .evergreen/scripts/install-dependencies.sh | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index ef9f60d162..206d6be67b 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -54,10 +54,28 @@ if [ "$_need_setup" = "1" ]; then ensure_uv || exit 1 fi + # Prefer a known-good toolchain python3 over the bare system one (e.g. + # RHEL8's 3.6 is too old for setup-uv.py): prepend the mongodbtoolchain's + # "Current" python dir, the same location configure-env.sh puts on PATH via + # env.sh, for hosts that skip or delete env.sh (e.g. auth-aws-ecs). + if [ "Windows_NT" = "${OS:-}" ]; then + _toolchain_bin="/cygdrive/c/Python/Current/Scripts" + elif [ "$(uname -s)" = "Darwin" ]; then + _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" + else + _toolchain_bin="/opt/python/Current/bin" + fi + if [ -d "$_toolchain_bin" ]; then + case ":$PATH:" in + *":$_toolchain_bin:"*) ;; + *) export PATH="$_toolchain_bin:$PATH" ;; + esac + fi + # Do the uv setup (bin dir, pinning, env.sh). Uses the toolchain python3 - # (added to PATH by configure-env.sh) so no project .venv is created here, - # and no required-version check is triggered. On Windows the script path must - # be a native Windows path for python3. + # (added to PATH by configure-env.sh and above) so no project .venv is + # created here, and no required-version check is triggered. On Windows the + # script path must be a native Windows path for python3. _uv_setup_script="$HERE/setup-uv.py" if [ "Windows_NT" = "${OS:-}" ]; then _uv_setup_script="$(cygpath -m "$_uv_setup_script")" From 95d8c071a54696d3dda42346fc398fc52aad3eed Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 22 Sep 2026 10:20:41 -0500 Subject: [PATCH 21/25] PYTHON-6091 Address copilot review: credential-safe env.sh, rc from $SHELL, shared toolchain helper --- .evergreen/scripts/configure-env.sh | 9 ++----- .evergreen/scripts/install-dependencies.sh | 9 ++----- .evergreen/scripts/setup-dev-env.sh | 29 ++++++++++++++-------- .evergreen/scripts/setup-uv.py | 15 ++++++++--- .evergreen/scripts/toolchain-bin.sh | 12 +++++++++ 5 files changed, 47 insertions(+), 27 deletions(-) create mode 100755 .evergreen/scripts/toolchain-bin.sh diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index a92c0b023a..d90067d202 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -32,13 +32,8 @@ fi # for tool installs like `uv tool install rust-just`. It goes after # PYMONGO_BIN_DIR so the pinned uv (installed there by setup-uv.py) takes # precedence over the toolchain's uv. -if [ "Windows_NT" = "${OS:-}" ]; then - _toolchain_bin="/cygdrive/c/Python/Current/Scripts" -elif [ "$(uname -s)" == "Darwin" ]; then - _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" -else - _toolchain_bin="/opt/python/Current/bin" -fi +. "$(dirname "${BASH_SOURCE:-$0}")/toolchain-bin.sh" +_toolchain_bin="$(mongodb_toolchain_bin)" if [ -d "$_toolchain_bin" ]; then PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$_toolchain_bin:$DRIVERS_TOOLS_BINARIES:\$PATH" else diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 206d6be67b..5081b3230f 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -58,13 +58,8 @@ if [ "$_need_setup" = "1" ]; then # RHEL8's 3.6 is too old for setup-uv.py): prepend the mongodbtoolchain's # "Current" python dir, the same location configure-env.sh puts on PATH via # env.sh, for hosts that skip or delete env.sh (e.g. auth-aws-ecs). - if [ "Windows_NT" = "${OS:-}" ]; then - _toolchain_bin="/cygdrive/c/Python/Current/Scripts" - elif [ "$(uname -s)" = "Darwin" ]; then - _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" - else - _toolchain_bin="/opt/python/Current/bin" - fi + . "$HERE/toolchain-bin.sh" + _toolchain_bin="$(mongodb_toolchain_bin)" if [ -d "$_toolchain_bin" ]; then case ":$PATH:" in *":$_toolchain_bin:"*) ;; diff --git a/.evergreen/scripts/setup-dev-env.sh b/.evergreen/scripts/setup-dev-env.sh index 18b6cd5b5a..5166c4b523 100755 --- a/.evergreen/scripts/setup-dev-env.sh +++ b/.evergreen/scripts/setup-dev-env.sh @@ -20,19 +20,28 @@ fi # default it here so local dev (without setup-system.sh) also has a usable value. export PYMONGO_BIN_DIR="${PYMONGO_BIN_DIR:-$HOME/.local/bin}" +# install-dependencies.sh runs as a child process, so its PATH changes do not +# propagate back here: ensure the bin dir is on this process's PATH too, so a +# fresh install (first run, bin dir not on PATH yet) is visible to the +# `uv sync` and pre-commit setup below. +case ":$PATH:" in + *":$PYMONGO_BIN_DIR:"*) ;; + *) export PATH="$PYMONGO_BIN_DIR:$PATH" ;; +esac + # Make sure a login shell can find the bin dir by adding it to the rc file, so # local dev (which may never run setup-system.sh) still has it on PATH. env.sh's -# PATH does not persist past this session. Prefer .zshrc when the shell is zsh. +# PATH does not persist past this session. Select the rc file from $SHELL (not +# by which rc file happens to exist) so the user's actual shell is updated, and +# create it if it does not exist yet. if [ "${CI:-}" != "true" ] && [ "${GITHUB_ACTIONS:-}" != "true" ]; then - if [ -f "$HOME/.zshrc" ]; then - _rc="$HOME/.zshrc" - else - _rc="$HOME/.bashrc" - fi - if [ -f "$_rc" ]; then - grep -qF 'export PATH="'"$PYMONGO_BIN_DIR"':$PATH"' "$_rc" 2>/dev/null || \ - printf 'export PATH="%s:$PATH"\n' "$PYMONGO_BIN_DIR" >> "$_rc" - fi + case "${SHELL:-}" in + */zsh) _rc="$HOME/.zshrc" ;; + *) _rc="$HOME/.bashrc" ;; + esac + touch "$_rc" + grep -qF 'export PATH="'"$PYMONGO_BIN_DIR"':$PATH"' "$_rc" 2>/dev/null || \ + printf 'export PATH="%s:$PATH"\n' "$PYMONGO_BIN_DIR" >> "$_rc" fi # Ensure dependencies are installed. diff --git a/.evergreen/scripts/setup-uv.py b/.evergreen/scripts/setup-uv.py index 30d20949e9..6e2570bba3 100755 --- a/.evergreen/scripts/setup-uv.py +++ b/.evergreen/scripts/setup-uv.py @@ -9,6 +9,7 @@ import os import re +import shlex import shutil import subprocess import sys @@ -103,9 +104,15 @@ def _pin_uv(uv_pin: str) -> None: shutil.rmtree(tmp_uv.parent, ignore_errors=True) +# The UV_* env vars this bootstrap manages and may persist into env.sh. +# Deliberately narrow: other UV_* vars (e.g. UV_PUBLISH_TOKEN, +# UV_INDEX_..._PASSWORD) may hold credentials and must never be written out. +PERSISTED_UV_VARS = ("UV_CACHE_DIR", "UV_TOOL_BIN_DIR", "UV_TOOL_DIR") + + def _write_env() -> None: - """Write every UV_* env var into env.sh, replacing existing UV_* entries.""" - values = {k: v for k, v in os.environ.items() if k.startswith("UV_")} + """Write the bootstrap's UV_* env vars into env.sh, replacing existing UV_* entries.""" + values = {k: v for k, v in os.environ.items() if k in PERSISTED_UV_VARS} if not values: return existing = ENV_SH.read_text() if ENV_SH.exists() else "" @@ -118,7 +125,9 @@ def _write_env() -> None: continue keep.append(line) keep.append("") - keep.extend(f'export {name}="{value}"' for name, value in sorted(values.items())) + # shlex.quote emits shell-safe values (single-quoted, escaping embedded + # quotes) so a value cannot break out of or inject into the assignment. + keep.extend(f"export {name}={shlex.quote(value)}" for name, value in sorted(values.items())) # Write LF bytes directly: on Windows text mode translates \n to \r\n, which # breaks bash sourcing env.sh, and `newline=` isn't available on all Pythons. ENV_SH.write_bytes(("\n".join(keep) + "\n").encode()) diff --git a/.evergreen/scripts/toolchain-bin.sh b/.evergreen/scripts/toolchain-bin.sh new file mode 100755 index 0000000000..8d7e0ba640 --- /dev/null +++ b/.evergreen/scripts/toolchain-bin.sh @@ -0,0 +1,12 @@ +# Echo the mongodbtoolchain's "Current" python bin dir for this platform. +# Sourced by configure-env.sh and install-dependencies.sh. The dir may not +# exist on a given host (e.g. no-toolchain machines); callers must check. +mongodb_toolchain_bin() { + if [ "Windows_NT" = "${OS:-}" ]; then + echo "/cygdrive/c/Python/Current/Scripts" + elif [ "$(uname -s)" = "Darwin" ]; then + echo "/Library/Frameworks/Python.Framework/Versions/Current/bin" + else + echo "/opt/python/Current/bin" + fi +} From 26da029dee613d87945d0d576671060b9a303d23 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 22 Sep 2026 10:25:07 -0500 Subject: [PATCH 22/25] PYTHON-6091 Revert toolchain PATH re-derivation: rely on configure-env.sh --- .evergreen/scripts/configure-env.sh | 9 +++++++-- .evergreen/scripts/install-dependencies.sh | 19 +++---------------- .evergreen/scripts/toolchain-bin.sh | 12 ------------ 3 files changed, 10 insertions(+), 30 deletions(-) delete mode 100755 .evergreen/scripts/toolchain-bin.sh diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index d90067d202..a92c0b023a 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -32,8 +32,13 @@ fi # for tool installs like `uv tool install rust-just`. It goes after # PYMONGO_BIN_DIR so the pinned uv (installed there by setup-uv.py) takes # precedence over the toolchain's uv. -. "$(dirname "${BASH_SOURCE:-$0}")/toolchain-bin.sh" -_toolchain_bin="$(mongodb_toolchain_bin)" +if [ "Windows_NT" = "${OS:-}" ]; then + _toolchain_bin="/cygdrive/c/Python/Current/Scripts" +elif [ "$(uname -s)" == "Darwin" ]; then + _toolchain_bin="/Library/Frameworks/Python.Framework/Versions/Current/bin" +else + _toolchain_bin="/opt/python/Current/bin" +fi if [ -d "$_toolchain_bin" ]; then PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$_toolchain_bin:$DRIVERS_TOOLS_BINARIES:\$PATH" else diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index 5081b3230f..ef9f60d162 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -54,23 +54,10 @@ if [ "$_need_setup" = "1" ]; then ensure_uv || exit 1 fi - # Prefer a known-good toolchain python3 over the bare system one (e.g. - # RHEL8's 3.6 is too old for setup-uv.py): prepend the mongodbtoolchain's - # "Current" python dir, the same location configure-env.sh puts on PATH via - # env.sh, for hosts that skip or delete env.sh (e.g. auth-aws-ecs). - . "$HERE/toolchain-bin.sh" - _toolchain_bin="$(mongodb_toolchain_bin)" - if [ -d "$_toolchain_bin" ]; then - case ":$PATH:" in - *":$_toolchain_bin:"*) ;; - *) export PATH="$_toolchain_bin:$PATH" ;; - esac - fi - # Do the uv setup (bin dir, pinning, env.sh). Uses the toolchain python3 - # (added to PATH by configure-env.sh and above) so no project .venv is - # created here, and no required-version check is triggered. On Windows the - # script path must be a native Windows path for python3. + # (added to PATH by configure-env.sh) so no project .venv is created here, + # and no required-version check is triggered. On Windows the script path must + # be a native Windows path for python3. _uv_setup_script="$HERE/setup-uv.py" if [ "Windows_NT" = "${OS:-}" ]; then _uv_setup_script="$(cygpath -m "$_uv_setup_script")" diff --git a/.evergreen/scripts/toolchain-bin.sh b/.evergreen/scripts/toolchain-bin.sh deleted file mode 100755 index 8d7e0ba640..0000000000 --- a/.evergreen/scripts/toolchain-bin.sh +++ /dev/null @@ -1,12 +0,0 @@ -# Echo the mongodbtoolchain's "Current" python bin dir for this platform. -# Sourced by configure-env.sh and install-dependencies.sh. The dir may not -# exist on a given host (e.g. no-toolchain machines); callers must check. -mongodb_toolchain_bin() { - if [ "Windows_NT" = "${OS:-}" ]; then - echo "/cygdrive/c/Python/Current/Scripts" - elif [ "$(uname -s)" = "Darwin" ]; then - echo "/Library/Frameworks/Python.Framework/Versions/Current/bin" - else - echo "/opt/python/Current/bin" - fi -} From 9d0d31c7c648a57a8bf665c479261b74d7aae6e9 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 22 Sep 2026 10:29:07 -0500 Subject: [PATCH 23/25] PYTHON-6091 Drop py3.6 compat from setup-uv.py: hosts have known-good python3 --- .evergreen/scripts/setup-uv.py | 11 ++++------- pyproject.toml | 4 ---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.evergreen/scripts/setup-uv.py b/.evergreen/scripts/setup-uv.py index 6e2570bba3..7c0209c835 100755 --- a/.evergreen/scripts/setup-uv.py +++ b/.evergreen/scripts/setup-uv.py @@ -3,10 +3,11 @@ install-dependencies.sh bails out if the pinned uv is already on PATH, sources ensure-uv.sh (which finds or installs uv), then runs this script for the rest. -Only the standard library is used, and it must run on the old system -Pythons (3.6) found on some no-toolchain hosts. +Only the standard library is used. """ +from __future__ import annotations + import os import re import shlex @@ -56,11 +57,7 @@ def _install_uv_astral() -> None: curl = shutil.which("curl") sh = shutil.which("sh") proc = subprocess.run( # noqa: S603 - [curl, "-LsSf", ASTRAL_INSTALL_URL], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env, - check=True, + [curl, "-LsSf", ASTRAL_INSTALL_URL], capture_output=True, env=env, check=True ) subprocess.run([sh], input=proc.stdout, env=env, check=True) # noqa: S603 _add_path(os.environ["UV_TOOL_BIN_DIR"]) diff --git a/pyproject.toml b/pyproject.toml index 52fb0137d0..d821fa8673 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -255,10 +255,6 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?)|dummy.*)$" [tool.ruff.lint.per-file-ignores] "pymongo/__init__.py" = ["E402"] ".evergreen/scripts/*.py" = ["T201"] -# setup-uv.py must run on the bare system python3 (3.6) found on some -# no-toolchain hosts: `from __future__ import annotations` (I002) is a -# SyntaxError there, and `capture_output=` (UP022) was added in 3.7. -".evergreen/scripts/setup-uv.py" = ["I002", "UP022"] "test/*.py" = ["PT", "E402", "PLW", "SIM", "E741", "PTH", "S", "B904", "E722", "T201", "RET", "ARG", "F405", "B028", "PGH001", "B018", "F403", "RUF015", "E731", "B007", "UP031", "F401", "B023", "F811"] From d9602782354fb9a150e9376fd4e9614b9dc855b0 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 22 Sep 2026 10:37:00 -0500 Subject: [PATCH 24/25] PYTHON-6091 Probe pinned uv via setup-uv.py --check; persist UV_PYTHON_INSTALL_DIR --- .evergreen/scripts/install-dependencies.sh | 26 ++++++++------- .evergreen/scripts/setup-uv.py | 37 ++++++++++++++++++---- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index ef9f60d162..b701b914a2 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -30,17 +30,24 @@ case ":$PATH:" in *) export PATH="$PYMONGO_BIN_DIR:$PATH" ;; esac -# If uv is on PATH, check it via `uv sync`, which fails fast if it is not the -# pinned version (from pyproject.toml's [tool.uv] required-version). If that -# succeeds, the environment is already correct and there is nothing to set up; -# otherwise fall through to the setup below. +# Compute the setup script path once (native Windows path on cygwin). +_uv_setup_script="$HERE/setup-uv.py" +if [ "Windows_NT" = "${OS:-}" ]; then + _uv_setup_script="$(cygpath -m "$_uv_setup_script")" +fi + +# Skip setup when the pinned uv is already installed: `setup-uv.py --check` +# compares the uv on PATH with the required-version pin, without side effects. +# A full `uv sync` here would download Python and install/build dependencies +# just to make this decision, and would run before setup-uv-python.sh sets +# UV_PYTHON, creating the environment twice on local dev. # # On CI we also require UV_CACHE_DIR to be set: ensure_uv.sh scopes uv's cache # to a task-local dir, so an unset UV_CACHE_DIR means the uv setup has not run # yet in this task and we must do the setup phase. _need_setup=1 -if command -v uv >/dev/null 2>&1 && uv sync >/dev/null 2>&1; then - if [ "${CI:-}" != "true" ] || [ -n "${UV_CACHE_DIR:-}" ]; then +if [ "${CI:-}" != "true" ] || [ -n "${UV_CACHE_DIR:-}" ]; then + if python3 "$_uv_setup_script" --check >/dev/null 2>&1; then echo "uv is already set up; skipping uv setup." _need_setup=0 fi @@ -56,12 +63,7 @@ if [ "$_need_setup" = "1" ]; then # Do the uv setup (bin dir, pinning, env.sh). Uses the toolchain python3 # (added to PATH by configure-env.sh) so no project .venv is created here, - # and no required-version check is triggered. On Windows the script path must - # be a native Windows path for python3. - _uv_setup_script="$HERE/setup-uv.py" - if [ "Windows_NT" = "${OS:-}" ]; then - _uv_setup_script="$(cygpath -m "$_uv_setup_script")" - fi + # and no required-version check is triggered. python3 "$_uv_setup_script" # Re-source env.sh so the values setup-uv.py wrote are available. diff --git a/.evergreen/scripts/setup-uv.py b/.evergreen/scripts/setup-uv.py index 7c0209c835..dc7b0964f4 100755 --- a/.evergreen/scripts/setup-uv.py +++ b/.evergreen/scripts/setup-uv.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """Bootstrap the pinned uv/just for the test environment. -install-dependencies.sh bails out if the pinned uv is already on PATH, sources -ensure-uv.sh (which finds or installs uv), then runs this script for the rest. -Only the standard library is used. +install-dependencies.sh probes with `setup-uv.py --check`, sources ensure-uv.sh +(which finds or installs uv), then runs this script for the rest. Only the +standard library is used. """ from __future__ import annotations @@ -101,10 +101,11 @@ def _pin_uv(uv_pin: str) -> None: shutil.rmtree(tmp_uv.parent, ignore_errors=True) -# The UV_* env vars this bootstrap manages and may persist into env.sh. -# Deliberately narrow: other UV_* vars (e.g. UV_PUBLISH_TOKEN, -# UV_INDEX_..._PASSWORD) may hold credentials and must never be written out. -PERSISTED_UV_VARS = ("UV_CACHE_DIR", "UV_TOOL_BIN_DIR", "UV_TOOL_DIR") +# The UV_* env vars this bootstrap (or its caller, ensure-uv.sh) manages and +# may persist into env.sh. Deliberately narrow: other UV_* vars (e.g. +# UV_PUBLISH_TOKEN, UV_INDEX_..._PASSWORD) may hold credentials and must +# never be written out. +PERSISTED_UV_VARS = ("UV_CACHE_DIR", "UV_PYTHON_INSTALL_DIR", "UV_TOOL_BIN_DIR", "UV_TOOL_DIR") def _write_env() -> None: @@ -130,7 +131,29 @@ def _write_env() -> None: ENV_SH.write_bytes(("\n".join(keep) + "\n").encode()) +def _pinned_uv_installed() -> bool: + """Return True when the uv on PATH already matches the required-version pin.""" + pin = required_uv_pin() + if not pin.startswith("=="): + # Only exact pins are checked here; anything else falls back to setup. + return False + uv_path = shutil.which("uv") + if uv_path is None: + return False + proc = subprocess.run( # noqa: S603 + [uv_path, "--version"], capture_output=True, check=True + ) + # Output looks like "uv 0.12.12 (aarch64-unknown-linux-gnu)". + return proc.stdout.decode().split()[1] == pin[len("==") :] + + def main() -> int: + # Side-effect-free probe for install-dependencies.sh: a full `uv sync` + # there would download Python and install dependencies just to decide + # whether setup is needed. + if "--check" in sys.argv[1:]: + return 0 if _pinned_uv_installed() else 1 + bin_dir = os.environ["UV_TOOL_BIN_DIR"] Path(bin_dir).mkdir(parents=True, exist_ok=True) _add_path(bin_dir) From 37553ac8d070101791d210246c54fbf74a40dac6 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 23 Sep 2026 20:48:27 -0500 Subject: [PATCH 25/25] PYTHON-6091 Keep PYMONGO_BIN_DIR native on cygwin; export POSIX form The variable stays in native (Windows) form on cygwin, uniform with the other paths in configure-env.sh, for consumers like uv; the derived PYMONGO_BIN_DIR_POSIX is used for bash PATH contexts (PATH_EXT, the dedupe/prepend steps, and the shell rc writer). --- .evergreen/scripts/configure-env.sh | 16 +++++++++++++--- .evergreen/scripts/install-dependencies.sh | 21 +++++++++++++-------- .evergreen/scripts/setup-dev-env.sh | 17 +++++++++++++---- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/.evergreen/scripts/configure-env.sh b/.evergreen/scripts/configure-env.sh index a92c0b023a..fec3cdadae 100755 --- a/.evergreen/scripts/configure-env.sh +++ b/.evergreen/scripts/configure-env.sh @@ -27,6 +27,14 @@ else PYMONGO_BIN_DIR=$HOME/.local/bin fi +# Cygwin spelling for bash PATH entries; PYMONGO_BIN_DIR itself is converted +# to native form below for consumers like uv. +if [ "Windows_NT" = "${OS:-}" ]; then + PYMONGO_BIN_DIR_POSIX="$(cygpath -u "$PYMONGO_BIN_DIR")" +else + PYMONGO_BIN_DIR_POSIX="$PYMONGO_BIN_DIR" +fi + # Add the latest MongoDB toolchain bin dir to PATH if it exists, so that hosts # with an old system Python (e.g. RHEL8's 3.6) still get a modern interpreter # for tool installs like `uv tool install rust-just`. It goes after @@ -40,9 +48,9 @@ else _toolchain_bin="/opt/python/Current/bin" fi if [ -d "$_toolchain_bin" ]; then - PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$_toolchain_bin:$DRIVERS_TOOLS_BINARIES:\$PATH" + PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR_POSIX:$_toolchain_bin:$DRIVERS_TOOLS_BINARIES:\$PATH" else - PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR:$DRIVERS_TOOLS_BINARIES:\$PATH" + PATH_EXT="$MONGODB_BINARIES:$PYMONGO_BIN_DIR_POSIX:$DRIVERS_TOOLS_BINARIES:\$PATH" fi # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory @@ -52,7 +60,8 @@ if [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin CARGO_HOME=$(cygpath -m $CARGO_HOME) DRIVERS_TOOLS_BINARIES=$(cygpath -m "$DRIVERS_TOOLS_BINARIES") MONGODB_BINARIES=$(cygpath -m "$MONGODB_BINARIES") - PYMONGO_BIN_DIR=$(cygpath -u "$PYMONGO_BIN_DIR") + # Native form, uniform with the paths above, for consumers like uv. + PYMONGO_BIN_DIR=$(cygpath -m "$PYMONGO_BIN_DIR") fi SCRIPT_DIR="$PROJECT_DIRECTORY/.evergreen/scripts" @@ -77,6 +86,7 @@ export PROJECT_DIRECTORY="$PROJECT_DIRECTORY" export CARGO_HOME="$CARGO_HOME" export PYMONGO_BIN_DIR="$PYMONGO_BIN_DIR" +export PYMONGO_BIN_DIR_POSIX="$PYMONGO_BIN_DIR_POSIX" export PATH="$PATH_EXT" # shellcheck disable=SC2154 export PROJECT="${project:-mongo-python-driver}" diff --git a/.evergreen/scripts/install-dependencies.sh b/.evergreen/scripts/install-dependencies.sh index b701b914a2..36e7866735 100755 --- a/.evergreen/scripts/install-dependencies.sh +++ b/.evergreen/scripts/install-dependencies.sh @@ -12,22 +12,27 @@ if [ -f $HERE/env.sh ]; then fi # PYMONGO_BIN_DIR is set by setup-system.sh/env.sh (or setup-dev-env.sh); default -# it for robustness. UV_TOOL_BIN_DIR is uv's name for the same dir (setup-uv.py -# reads both). UV_TOOL_DIR is left to ensure_uv.sh. +# it for robustness. Native (Windows) form on cygwin, for consumers like uv; +# PYMONGO_BIN_DIR_POSIX is the cygwin form for bash PATH contexts. export PYMONGO_BIN_DIR="${PYMONGO_BIN_DIR:-$HOME/.local/bin}" -export UV_TOOL_BIN_DIR="${UV_TOOL_BIN_DIR:-$PYMONGO_BIN_DIR}" -# uv is a native Windows binary: give it a Windows path on cygwin. if [ "Windows_NT" = "${OS:-}" ]; then - _uv_tool_bin="$(cygpath -m "$PYMONGO_BIN_DIR")" - export UV_TOOL_BIN_DIR="$_uv_tool_bin" + _bin_dir="$(cygpath -m "$PYMONGO_BIN_DIR")" + export PYMONGO_BIN_DIR="$_bin_dir" + _posix_bin_dir="$(cygpath -u "$_bin_dir")" + export PYMONGO_BIN_DIR_POSIX="$_posix_bin_dir" +else + export PYMONGO_BIN_DIR_POSIX="$PYMONGO_BIN_DIR" fi +# UV_TOOL_BIN_DIR is uv's name for the same dir (setup-uv.py reads both). +# UV_TOOL_DIR is left to ensure_uv.sh. +export UV_TOOL_BIN_DIR="${UV_TOOL_BIN_DIR:-$PYMONGO_BIN_DIR}" # Ensure the bin dir is on PATH: hosts without env.sh (e.g. auth-aws-ecs) never # export it, so a fresh pinned install there would be invisible to the probe # below and to later steps like `uv tool install` and `uv sync`. case ":$PATH:" in - *":$PYMONGO_BIN_DIR:"*) ;; - *) export PATH="$PYMONGO_BIN_DIR:$PATH" ;; + *":$PYMONGO_BIN_DIR_POSIX:"*) ;; + *) export PATH="$PYMONGO_BIN_DIR_POSIX:$PATH" ;; esac # Compute the setup script path once (native Windows path on cygwin). diff --git a/.evergreen/scripts/setup-dev-env.sh b/.evergreen/scripts/setup-dev-env.sh index 5166c4b523..508eb099bc 100755 --- a/.evergreen/scripts/setup-dev-env.sh +++ b/.evergreen/scripts/setup-dev-env.sh @@ -18,15 +18,24 @@ fi # The bin dir for the pinned uv/just. setup-system.sh sets it on evergreen hosts; # default it here so local dev (without setup-system.sh) also has a usable value. +# Native (Windows) form on cygwin, like install-dependencies.sh. export PYMONGO_BIN_DIR="${PYMONGO_BIN_DIR:-$HOME/.local/bin}" +if [ "Windows_NT" = "${OS:-}" ]; then + _bin_dir="$(cygpath -m "$PYMONGO_BIN_DIR")" + export PYMONGO_BIN_DIR="$_bin_dir" + _posix_bin_dir="$(cygpath -u "$_bin_dir")" + export PYMONGO_BIN_DIR_POSIX="$_posix_bin_dir" +else + export PYMONGO_BIN_DIR_POSIX="$PYMONGO_BIN_DIR" +fi # install-dependencies.sh runs as a child process, so its PATH changes do not # propagate back here: ensure the bin dir is on this process's PATH too, so a # fresh install (first run, bin dir not on PATH yet) is visible to the # `uv sync` and pre-commit setup below. case ":$PATH:" in - *":$PYMONGO_BIN_DIR:"*) ;; - *) export PATH="$PYMONGO_BIN_DIR:$PATH" ;; + *":$PYMONGO_BIN_DIR_POSIX:"*) ;; + *) export PATH="$PYMONGO_BIN_DIR_POSIX:$PATH" ;; esac # Make sure a login shell can find the bin dir by adding it to the rc file, so @@ -40,8 +49,8 @@ if [ "${CI:-}" != "true" ] && [ "${GITHUB_ACTIONS:-}" != "true" ]; then *) _rc="$HOME/.bashrc" ;; esac touch "$_rc" - grep -qF 'export PATH="'"$PYMONGO_BIN_DIR"':$PATH"' "$_rc" 2>/dev/null || \ - printf 'export PATH="%s:$PATH"\n' "$PYMONGO_BIN_DIR" >> "$_rc" + grep -qF 'export PATH="'"$PYMONGO_BIN_DIR_POSIX"':$PATH"' "$_rc" 2>/dev/null || \ + printf 'export PATH="%s:$PATH"\n' "$PYMONGO_BIN_DIR_POSIX" >> "$_rc" fi # Ensure dependencies are installed.