Skip to content

Commit cfe7784

Browse files
authored
PYTHON-4976 Replace hatch with uv as our python environment and workfow tool (mongodb#2068)
1 parent f1af917 commit cfe7784

17 files changed

+2234
-175
lines changed

.evergreen/run-tests.sh

+39-35
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ export PIP_QUIET=1 # Quiet by default
3737
export PIP_PREFER_BINARY=1 # Prefer binary dists by default
3838

3939
set +x
40-
python -c "import sys; sys.exit(sys.prefix == sys.base_prefix)" || (echo "Not inside a virtual env!"; exit 1)
41-
PYTHON_IMPL=$(python -c "import platform; print(platform.python_implementation())")
40+
PYTHON_IMPL=$(uv run python -c "import platform; print(platform.python_implementation())")
4241

4342
# Try to source local Drivers Secrets
4443
if [ -f ./secrets-export.sh ]; then
@@ -48,9 +47,13 @@ else
4847
echo "Not sourcing secrets"
4948
fi
5049

51-
# Ensure C extensions have compiled.
50+
# Start compiling the args we'll pass to uv.
51+
# Run in an isolated environment so as not to pollute the base venv.
52+
UV_ARGS=("--isolated --extra test")
53+
54+
# Ensure C extensions if applicable.
5255
if [ -z "${NO_EXT:-}" ] && [ "$PYTHON_IMPL" = "CPython" ]; then
53-
python tools/fail_if_no_c.py
56+
uv run tools/fail_if_no_c.py
5457
fi
5558

5659
if [ "$AUTH" != "noauth" ]; then
@@ -77,7 +80,7 @@ if [ "$AUTH" != "noauth" ]; then
7780
fi
7881

7982
if [ -n "$TEST_ENTERPRISE_AUTH" ]; then
80-
python -m pip install '.[gssapi]'
83+
UV_ARGS+=("--extra gssapi")
8184
if [ "Windows_NT" = "$OS" ]; then
8285
echo "Setting GSSAPI_PASS"
8386
export GSSAPI_PASS=${SASL_PASS}
@@ -118,24 +121,26 @@ if [ "$SSL" != "nossl" ]; then
118121
fi
119122

120123
if [ "$COMPRESSORS" = "snappy" ]; then
121-
python -m pip install '.[snappy]'
124+
UV_ARGS+=("--extra snappy")
122125
elif [ "$COMPRESSORS" = "zstd" ]; then
123-
python -m pip install zstandard
126+
UV_ARGS+=("--extra zstandard")
124127
fi
125128

126129
# PyOpenSSL test setup.
127130
if [ -n "$TEST_PYOPENSSL" ]; then
128-
python -m pip install '.[ocsp]'
131+
UV_ARGS+=("--extra ocsp")
129132
fi
130133

131134
if [ -n "$TEST_ENCRYPTION" ] || [ -n "$TEST_FLE_AZURE_AUTO" ] || [ -n "$TEST_FLE_GCP_AUTO" ]; then
132-
# Check for libmongocrypt checkout.
135+
# Check for libmongocrypt download.
133136
if [ ! -d "libmongocrypt" ]; then
134137
echo "Run encryption setup first!"
135138
exit 1
136139
fi
137140

138-
python -m pip install '.[encryption]'
141+
UV_ARGS+=("--extra encryption")
142+
# TODO: Test with 'pip install pymongocrypt'
143+
UV_ARGS+=("--group pymongocrypt_source")
139144

140145
# Use the nocrypto build to avoid dependency issues with older windows/python versions.
141146
BASE=$(pwd)/libmongocrypt/nocrypto
@@ -155,21 +160,17 @@ if [ -n "$TEST_ENCRYPTION" ] || [ -n "$TEST_FLE_AZURE_AUTO" ] || [ -n "$TEST_FLE
155160
exit 1
156161
fi
157162
export PYMONGOCRYPT_LIB
158-
159-
# TODO: Test with 'pip install pymongocrypt'
160-
if [ ! -d "libmongocrypt_git" ]; then
161-
git clone https://github.com/mongodb/libmongocrypt.git libmongocrypt_git
162-
fi
163-
python -m pip install -U setuptools
164-
python -m pip install ./libmongocrypt_git/bindings/python
165-
python -c "import pymongocrypt; print('pymongocrypt version: '+pymongocrypt.__version__)"
166-
python -c "import pymongocrypt; print('libmongocrypt version: '+pymongocrypt.libmongocrypt_version())"
167-
# PATH is updated by PREPARE_SHELL for access to mongocryptd.
163+
# Ensure pymongocrypt is working properly.
164+
# shellcheck disable=SC2048
165+
uv run ${UV_ARGS[*]} python -c "import pymongocrypt; print('pymongocrypt version: '+pymongocrypt.__version__)"
166+
# shellcheck disable=SC2048
167+
uv run ${UV_ARGS[*]} python -c "import pymongocrypt; print('libmongocrypt version: '+pymongocrypt.libmongocrypt_version())"
168+
# PATH is updated by configure-env.sh for access to mongocryptd.
168169
fi
169170

170171
if [ -n "$TEST_ENCRYPTION" ]; then
171172
if [ -n "$TEST_ENCRYPTION_PYOPENSSL" ]; then
172-
python -m pip install '.[ocsp]'
173+
UV_ARGS+=("--extra ocsp")
173174
fi
174175

175176
if [ -n "$TEST_CRYPT_SHARED" ]; then
@@ -214,31 +215,31 @@ if [ -n "$TEST_ATLAS" ]; then
214215
fi
215216

216217
if [ -n "$TEST_OCSP" ]; then
217-
python -m pip install ".[ocsp]"
218+
UV_ARGS+=("--extra ocsp")
218219
TEST_SUITES="ocsp"
219220
fi
220221

221222
if [ -n "$TEST_AUTH_AWS" ]; then
222-
python -m pip install ".[aws]"
223+
UV_ARGS+=("--extra aws")
223224
TEST_SUITES="auth_aws"
224225
fi
225226

226227
if [ -n "$TEST_AUTH_OIDC" ]; then
227-
python -m pip install ".[aws]"
228+
UV_ARGS+=("--extra aws")
228229
TEST_SUITES="auth_oidc"
229230
fi
230231

231232
if [ -n "$PERF_TEST" ]; then
232-
python -m pip install simplejson
233+
UV_ARGS+=("--group perf")
233234
start_time=$(date +%s)
234235
TEST_SUITES="perf"
235236
# PYTHON-4769 Run perf_test.py directly otherwise pytest's test collection negatively
236237
# affects the benchmark results.
237238
TEST_ARGS="test/performance/perf_test.py $TEST_ARGS"
238239
fi
239240

240-
echo "Running $AUTH tests over $SSL with python $(which python)"
241-
python -c 'import sys; print(sys.version)'
241+
echo "Running $AUTH tests over $SSL with python $(uv python find)"
242+
uv run python -c 'import sys; print(sys.version)'
242243

243244

244245
# Run the tests, and store the results in Evergreen compatible XUnit XML
@@ -249,27 +250,30 @@ python -c 'import sys; print(sys.version)'
249250
if [ -n "$COVERAGE" ] && [ "$PYTHON_IMPL" = "CPython" ]; then
250251
# Keep in sync with combine-coverage.sh.
251252
# coverage >=5 is needed for relative_files=true.
252-
python -m pip install pytest-cov "coverage>=5,<=7.5"
253+
UV_ARGS+=("--group coverage")
253254
TEST_ARGS="$TEST_ARGS --cov"
254255
fi
255256

256257
if [ -n "$GREEN_FRAMEWORK" ]; then
257-
python -m pip install $GREEN_FRAMEWORK
258+
UV_ARGS+=("--group $GREEN_FRAMEWORK")
258259
fi
259260

260261
# Show the installed packages
261-
PIP_QUIET=0 python -m pip list
262+
# shellcheck disable=SC2048
263+
PIP_QUIET=0 uv run ${UV_ARGS[*]} --with pip pip list
262264

263265
if [ -z "$GREEN_FRAMEWORK" ]; then
264266
# Use --capture=tee-sys so pytest prints test output inline:
265267
# https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html
266-
if [ -z "$TEST_SUITES" ]; then
267-
python -m pytest -v --capture=tee-sys --durations=5 $TEST_ARGS
268-
else
269-
python -m pytest -v --capture=tee-sys --durations=5 -m $TEST_SUITES $TEST_ARGS
268+
PYTEST_ARGS="-v --capture=tee-sys --durations=5 $TEST_ARGS"
269+
if [ -n "$TEST_SUITES" ]; then
270+
PYTEST_ARGS="-m $TEST_SUITES $PYTEST_ARGS"
270271
fi
272+
# shellcheck disable=SC2048
273+
uv run ${UV_ARGS[*]} pytest $PYTEST_ARGS
271274
else
272-
python green_framework_test.py $GREEN_FRAMEWORK -v $TEST_ARGS
275+
# shellcheck disable=SC2048
276+
uv run ${UV_ARGS[*]} green_framework_test.py $GREEN_FRAMEWORK -v $TEST_ARGS
273277
fi
274278

275279
# Handle perf test post actions.

.evergreen/scripts/configure-env.sh

+7-3
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ fi
1414
PROJECT_DIRECTORY="$(pwd)"
1515
DRIVERS_TOOLS="$(dirname $PROJECT_DIRECTORY)/drivers-tools"
1616
CARGO_HOME=${CARGO_HOME:-${DRIVERS_TOOLS}/.cargo}
17-
HATCH_CONFIG=$PROJECT_DIRECTORY/hatch_config.toml
17+
UV_TOOL_DIR=$PROJECT_DIRECTORY/.local/uv/tools
18+
UV_CACHE_DIR=$PROJECT_DIRECTORY/.local/uv/cache
1819

1920
# Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory
2021
if [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin
2122
DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS)
2223
PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY)
2324
CARGO_HOME=$(cygpath -m $CARGO_HOME)
24-
HATCH_CONFIG=$(cygpath -m "$HATCH_CONFIG")
25+
UV_TOOL_DIR=$(cygpath -m "$UV_TOOL_DIR")
26+
UV_CACHE_DIR=$(cygpath -m "$UV_CACHE_DIR")
2527
fi
2628

2729
SCRIPT_DIR="$PROJECT_DIRECTORY/.evergreen/scripts"
@@ -62,7 +64,9 @@ export skip_ECS_auth_test="${skip_ECS_auth_test:-}"
6264
6365
export CARGO_HOME="$CARGO_HOME"
6466
export TMPDIR="$MONGO_ORCHESTRATION_HOME/db"
65-
export HATCH_CONFIG="$HATCH_CONFIG"
67+
export UV_TOOL_DIR="$UV_TOOL_DIR"
68+
export UV_CACHE_DIR="$UV_CACHE_DIR"
69+
export UV_TOOL_BIN_DIR="$DRIVERS_TOOLS_BINARIES"
6670
export PATH="$MONGODB_BINARIES:$DRIVERS_TOOLS_BINARIES:$PATH"
6771
# shellcheck disable=SC2154
6872
export PROJECT="${project:-mongo-python-driver}"

.evergreen/scripts/generate_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# ]
77
# ///
88

9-
# Note: Run this file with `hatch run`, `pipx run`, or `uv run`.
9+
# Note: Run this file with `pipx run`, or `uv run`.
1010
from __future__ import annotations
1111

1212
import sys

.evergreen/scripts/install-dependencies.sh

+13
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,16 @@ if ! command -v just 2>/dev/null; then
4040
fi
4141
echo "Installing just... done."
4242
fi
43+
44+
# Install uv.
45+
if ! command -v uv 2>/dev/null; then
46+
echo "Installing uv..."
47+
# On most systems we can install directly.
48+
curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR="$_BIN_DIR" INSTALLER_NO_MODIFY_PATH=1 sh || {
49+
_pip_install uv uv
50+
}
51+
if ! command -v uv 2>/dev/null; then
52+
export PATH="$PATH:$_BIN_DIR"
53+
fi
54+
echo "Installing uv... done."
55+
fi

.evergreen/scripts/setup-dev-env.sh

+16-48
Original file line numberDiff line numberDiff line change
@@ -6,69 +6,37 @@ HERE=$(dirname ${BASH_SOURCE:-$0})
66
pushd "$(dirname "$(dirname $HERE)")" > /dev/null
77

88
# Source the env file to pick up common variables.
9-
if [ -f $HERE/scripts/env.sh ]; then
10-
source $HERE/scripts/env.sh
9+
if [ -f $HERE/env.sh ]; then
10+
source $HERE/env.sh
1111
fi
1212

13+
# Ensure dependencies are installed.
14+
. $HERE/install-dependencies.sh
15+
16+
1317
# Set the location of the python bin dir.
1418
if [ "Windows_NT" = "${OS:-}" ]; then
1519
BIN_DIR=.venv/Scripts
1620
else
1721
BIN_DIR=.venv/bin
1822
fi
1923

20-
. $HERE/install-dependencies.sh
21-
2224
# Ensure there is a python venv.
2325
if [ ! -d $BIN_DIR ]; then
2426
. .evergreen/utils.sh
2527

2628
if [ -z "${PYTHON_BINARY:-}" ]; then
2729
PYTHON_BINARY=$(find_python3)
2830
fi
29-
30-
echo "Creating virtual environment..."
31-
createvirtualenv "$PYTHON_BINARY" .venv
32-
echo "Creating virtual environment... done."
33-
fi
34-
35-
# Activate the virtual env.
36-
. $BIN_DIR/activate
37-
38-
# Ensure there is a local hatch.
39-
if [ ! -f $BIN_DIR/hatch ]; then
40-
echo "Installing hatch..."
41-
python -m pip install hatch || {
42-
# CARGO_HOME is defined in configure-env.sh
43-
export CARGO_HOME=${CARGO_HOME:-$HOME/.cargo/}
44-
export RUSTUP_HOME="${CARGO_HOME}/.rustup"
45-
${DRIVERS_TOOLS}/.evergreen/install-rust.sh
46-
source "${CARGO_HOME}/env"
47-
python -m pip install hatch
48-
}
49-
echo "Installing hatch... done."
50-
fi
51-
52-
# Ensure hatch does not write to user or global locations.
53-
HATCH_CONFIG=${HATCH_CONFIG:-hatch_config.toml}
54-
if [ ! -f ${HATCH_CONFIG} ]; then
55-
touch hatch_config.toml
56-
hatch config restore
57-
hatch config set dirs.data "$(pwd)/.hatch/data"
58-
hatch config set dirs.cache "$(pwd)/.hatch/cache"
31+
export UV_PYTHON=${PYTHON_BINARY}
32+
echo "export UV_PYTHON=$UV_PYTHON" >> $HERE/env.sh
5933
fi
60-
61-
# Ensure there is a local pre-commit if there is a git checkout.
62-
if [ -d .git ]; then
63-
if [ ! -f $BIN_DIR/pre-commit ]; then
64-
python -m pip install pre-commit
65-
fi
66-
67-
# Ensure the pre-commit hook is installed.
68-
if [ ! -f .git/hooks/pre-commit ]; then
69-
pre-commit install
70-
fi
34+
echo "Using python $UV_PYTHON"
35+
uv sync
36+
uv run --with pip pip install -e .
37+
echo "Setting up python environment... done."
38+
39+
# Ensure there is a pre-commit hook if there is a git checkout.
40+
if [ -d .git ] && [ ! -f .git/hooks/pre-commit ]; then
41+
uv run pre-commit install
7142
fi
72-
73-
# Install pymongo and its test deps.
74-
python -m pip install ".[test]"

.evergreen/teardown-encryption.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ if [ -z "${DRIVERS_TOOLS}" ]; then
77
fi
88

99
bash ${DRIVERS_TOOLS}/.evergreen/csfle/stop-servers.sh
10-
rm -rf libmongocrypt/ libmongocrypt_git/ libmongocrypt.tar.gz mongocryptd.pid
10+
rm -rf libmongocrypt/ libmongocrypt.tar.gz mongocryptd.pid

.gitignore

+1-2
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ venv/
2222
secrets-export.sh
2323
libmongocrypt.tar.gz
2424
libmongocrypt/
25-
libmongocrypt_git/
26-
hatch_config.toml
2725
.venv
2826
expansion.yml
27+
*expansions.yml
2928
.evergreen/scripts/env.sh
3029

3130
# Lambda temp files

CONTRIBUTING.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ be of interest or that has already been addressed.
1616

1717
## Supported Interpreters
1818

19-
PyMongo supports CPython 3.9+ and PyPy3.9+. Language features not
19+
PyMongo supports CPython 3.9+ and PyPy3.10+. Language features not
2020
supported by all interpreters can not be used.
2121

2222
## Style Guide
@@ -28,7 +28,7 @@ including 4 space indents and 79 character line limits.
2828

2929
- Avoid backward breaking changes if at all possible.
3030
- Write inline documentation for new classes and methods.
31-
- We use [hatch](https://hatch.pypa.io/dev/) for python environment management and packaging.
31+
- We use [uv](https://docs.astral.sh/uv/) for python environment management and packaging.
3232
- We use [just](https://just.systems/man/en/) as our task runner.
3333
- Write tests and make sure they pass (make sure you have a mongod
3434
running on the default port, then execute `just test` from the cmd
@@ -194,7 +194,7 @@ the pages will re-render and the browser will automatically refresh.
194194
## Running Tests Locally
195195

196196
- Ensure you have started the appropriate Mongo Server(s).
197-
- Run `just install` to set up `hatch` in a local virtual environment, or you can manually
197+
- Run `just install` to set a local virtual environment, or you can manually
198198
create a virtual environment and run `pytest` directly. If you want to use a specific
199199
version of Python, remove the `.venv` folder and set `PYTHON_BINARY` before running `just install`.
200200
- Run `just test` or `pytest` to run all of the tests.

README.md

+6-11
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,6 @@ command:
152152
python -m pip install "pymongo[gssapi,aws,ocsp,snappy,zstd,encryption]"
153153
```
154154

155-
Additional dependencies are:
156-
157-
- (to generate documentation or run tests)
158-
[hatch](https://hatch.pypa.io/dev/)
159-
160155
## Examples
161156

162157
Here's a basic example (for more see the *examples* section of the
@@ -201,8 +196,7 @@ ObjectId('4aba160ee23f6b543e000002')
201196
Documentation is available at
202197
[pymongo.readthedocs.io](https://pymongo.readthedocs.io/en/stable/).
203198

204-
Documentation can be generated by running **pip install hatch; hatch run doc:build**. Generated
205-
documentation can be found in the `doc/build/html/` directory.
199+
See the [contributing guide](./CONTRIBUTING.md#documentation) for how to build the documentation.
206200

207201
## Learning Resources
208202

@@ -213,10 +207,11 @@ Center](https://www.mongodb.com/developer/languages/python/).
213207

214208
## Testing
215209

216-
The easiest way to run the tests is to run *hatch run test:test** in the root
217-
of the distribution. For example,
210+
The easiest way to run the tests is to run the following from the repository root.
218211

219212
```bash
220-
pip install hatch
221-
hatch run test:test
213+
pip install -e ".[test]"
214+
pytest
222215
```
216+
217+
For more advanced testing scenarios, see the [contributing guide](./CONTRIBUTING.md#running-tests-locally).

0 commit comments

Comments
 (0)