Skip to content

Commit 91d2352

Browse files
DRIVERS-3136 Improve efficiency of find-python3 (#635)
Co-authored-by: Ezra Chung <[email protected]>
1 parent 4e18803 commit 91d2352

File tree

3 files changed

+117
-122
lines changed

3 files changed

+117
-122
lines changed

.evergreen/find-python3.sh

Lines changed: 62 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fi
2626
# Parameters:
2727
# "$1": The name or path of the python binary to test.
2828
#
29-
# Return 0 (true) if the given argument "$1" is a Python 3 binary.
29+
# Return 0 (true) if the given argument "$1" is a viable Python 3 binary.
3030
# Return a non-zero value (false) otherwise.
3131
#
3232
# Diagnostic messages may be printed to stderr (pipe 2). Redirect to /dev/null
@@ -35,41 +35,17 @@ is_python3() (
3535
set -o errexit
3636
set -o pipefail
3737

38+
HERE="$(dirname "${BASH_SOURCE[0]}")"
39+
3840
# Binary to use, e.g. "python".
3941
local -r bin="${1:?'is_python3 requires a name or path of a python binary to test'}"
4042

4143
# Binary must be executable.
4244
command -V "$bin" &>/dev/null || return
4345

44-
# Reject binaries that do not support argument "-V".
45-
"$bin" -V &>/dev/null || return
46-
47-
# Expect an output of the form: "Python x.y.z".
48-
# Note: Python 2 binaries output to stderr rather than stdout.
49-
local -r version_output="$("$bin" -V 2>&1 | tr -d '\n')"
50-
51-
# For diagnostic purposes.
52-
echo " - $bin: $version_output"
53-
54-
# shellcheck disable=SC2091
55-
if ! $("$bin" -c "import sys; exit('free-threading' in sys.version)"); then
56-
echo "Skipping free-threaded version of Python ${version}"
57-
return 1
58-
fi
59-
60-
# The minimum version we support is Python 3.9. All other versions are EOL.
61-
# shellcheck disable=SC2091
62-
if ! $("$bin" -c "import sys; exit(sys.version_info[0] == 3 and sys.version_info[1] < 9)"); then
63-
version=$($bin --version)
64-
echo "Detected EOL Python ${version}, skipping."
65-
return 1
66-
fi
46+
echo "- ${bin}"
6747

68-
# Evaluate result of this function.
69-
# Note: Python True (1) and False (0) is treated as fail (1) and success (0)
70-
# by Bash; therefore `is_python3` returns "true" when `v < 3` is false.
71-
# Skip prerelease versions of python.
72-
"$bin" -c "import sys; exit(sys.version_info[0] < 3 or sys.version_info.releaselevel != 'final')"
48+
"$bin" "${HERE:?}/is_python3.py" || return
7349
) 1>&2
7450

7551
# is_venv_capable
@@ -209,15 +185,26 @@ find_python3() (
209185
set -o errexit
210186
set -o pipefail
211187

212-
local -a bins=()
213-
local bin=""
214-
215-
# The list of Python binaries to test for venv or virtualenv support.
216-
# The binaries are tested in the order of their position in the array.
217-
{
218-
echo "Finding python3 binaries to test..."
188+
# Prefer Python Toolchain Current version.
189+
declare python_binary=""
190+
case "${OSTYPE:?}" in
191+
cygwin)
192+
python_binary="/cygdrive/c/Python/Current/python.exe"
193+
;;
194+
darwin*)
195+
python_binary="/Library/Frameworks/Python.Framework/Versions/Current/bin/python3"
196+
;;
197+
*)
198+
python_binary="/opt/python/Current/bin/python3"
199+
;;
200+
esac
201+
if is_python3 "${python_binary:?}"; then
202+
echo "Using Python binary ${python_binary:?}" >&2
203+
echo "${python_binary:?}"
204+
return
205+
fi
219206

220-
append_bins() {
207+
test_bins() {
221208
local -r bin_path="${1:?'missing path'}"
222209
shift
223210
local -r pattern="${1:?'missing pattern'}"
@@ -227,78 +214,50 @@ find_python3() (
227214
for dir in $(find "$bin_path" -maxdepth 1 -name "$pattern" -type d 2>/dev/null | sort -rV); do
228215
for bin in "${suffixes[@]}"; do
229216
if is_python3 "$dir/$bin"; then
230-
bins+=("$dir/$bin")
217+
echo "$dir/$bin"
218+
return
231219
fi
232220
done
233221
done
234222
}
235223

236-
# C:/python/Python3X/bin/python
237-
append_bins "C:/python" "Python3[0-9]*" "python3.exe" "python.exe"
238-
239-
# /opt/python/3.X/bin/python
240-
append_bins "/opt/python" "3.[0-9]*" "bin/python3" "bin/python"
241-
242-
# /opt/mongodbtoolchain/vX/bin/python
243-
append_bins "/opt/mongodbtoolchain" "v[0-9]*" "bin/python3" "bin/python"
244-
245-
# /Library/Frameworks/Python.Framework/Versions/XXX/bin/python3 (used by AWS Mac Fleet)
246-
append_bins "/Library/Frameworks/Python.Framework/Versions/" "[0-9]*" "bin/python3"
247-
248-
bin="python3"
249-
if is_python3 "$bin"; then bins+=("$bin"); fi
250-
251-
bin="python"
252-
if is_python3 "$bin"; then bins+=("$bin"); fi
253-
} 1>&2
254-
255-
{
256-
# Some environments trigger an unbound variable error if "${bins[@]}" is empty when used below.
257-
if (("${#bins[@]}" == 0)); then
258-
echo "Could not find any python3 binaries!"
259-
return 1
224+
# Look in other standard locations.
225+
case "${OSTYPE:?}" in
226+
cygwin)
227+
# Python toolchain: C:/python/Python3X/bin/python
228+
python_binary="$(test_bins "C:/python" "Python3[0-9]*" "python3.exe" "python.exe")"
229+
;;
230+
darwin*)
231+
# Standard location: /Library/Frameworks/Python.Framework/Versions/XXX/bin/python3
232+
python_binary="$(test_bins "/Library/Frameworks/Python.Framework/Versions/" "[0-9]*" "bin/python3")"
233+
;;
234+
*)
235+
# MongoDB toolchain: /opt/mongodbtoolchain/vX/bin/python
236+
python_binary="$(test_bins "/opt/mongodbtoolchain" "v[0-9]*" "bin/python3" "bin/python")"
237+
if [ -z "$python_binary" ]; then
238+
# Python toolchain: /opt/python/3.X/bin/python
239+
python_binary=$(test_bins "/opt/python" "3.[0-9]*" "bin/python3" "bin/python")
260240
fi
241+
;;
242+
esac
261243

262-
# For diagnostic purposes.
263-
echo "List of python3 binaries to test:"
264-
for bin in "${bins[@]}"; do
265-
echo " - $bin"
266-
done
267-
} 1>&2
268-
269-
# Find a binary that is capable of venv or virtualenv and set it as `res`.
270-
local res=""
271-
{
272-
echo "Testing python3 binaries..."
273-
for bin in "${bins[@]}"; do
274-
{
275-
if ! is_venv_capable "$bin"; then
276-
echo " - $bin is not capable of venv"
277-
278-
if ! is_virtualenv_capable "$bin"; then
279-
echo " - $bin is not capable of virtualenv"
280-
continue
281-
else
282-
echo " - $bin is capable of virtualenv"
283-
fi
284-
else
285-
echo " - $bin is capable of venv"
286-
fi
287-
} 1>&2
288-
289-
res="$bin"
290-
break
291-
done
292-
293-
if [[ -z "$res" ]]; then
294-
echo "Could not find a python3 binary capable of creating a virtual environment!"
295-
return 1
244+
# Fall back to looking for a system python.
245+
if [ -z "${python_binary}" ]; then
246+
if is_python3 "python3"; then
247+
python_binary="python3"
248+
elif is_python3 "python"; then
249+
python_binary="python"
296250
fi
297-
} 1>&2
298-
299-
echo "$res"
251+
fi
300252

301-
return 0
253+
# Handle success.
254+
if [ -n "${python_binary}" ]; then
255+
echo "Using Python binary ${python_binary:?}" >&2
256+
echo "${python_binary:?}"
257+
return
258+
else
259+
echo "No valid pythons found!" >&2
260+
fi
302261
)
303262

304263
#
@@ -314,7 +273,6 @@ find_python3() (
314273
# with `2>/dev/null` to silence these messages.
315274
#
316275
# If DRIVERS_TOOLS_PYTHON is set, it will return that value. Otherwise
317-
# it will look for the "Current" python in the python toolchain. Finally,
318276
# it will fall back to using find_python3 to return a suitable value.
319277
#
320278
ensure_python3() {
@@ -325,30 +283,12 @@ ensure_python3() {
325283
return
326284
fi
327285

328-
# Use Python Toolchain.
329-
declare python_binary=""
330-
case "${OSTYPE:?}" in
331-
cygwin)
332-
python_binary="/cygdrive/c/Python/Current/python.exe"
333-
;;
334-
darwin*)
335-
python_binary="/Library/Frameworks/Python.Framework/Versions/Current/bin/python3"
336-
;;
337-
*)
338-
python_binary="/opt/python/Current/bin/python3"
339-
;;
340-
esac
341-
if command -v "${python_binary:?}" >/dev/null; then
342-
echo "Using Python binary ${python_binary:?}" >&2
343-
echo "${python_binary:?}"
344-
return
345-
fi
346-
347286
# Use find_python3.
287+
declare python_binary=""
348288
{
349289
echo "Finding Python3 binary..."
350-
python_binary="$(find_python3 2>/dev/null)" || return
290+
python_binary="$(find_python3 2>/dev/null)"
351291
echo "Finding Python3 binary... done."
352292
} 1>&2
353-
echo "${python_binary:?}"
293+
echo "${python_binary}"
354294
}

.evergreen/install-cli.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ else
5555

5656
echo "Ensuring python binary..."
5757
PYTHON="$(ensure_python3 2>/dev/null)"
58+
if [ -z "${PYTHON}" ]; then
59+
# For debug purposes
60+
find_python3
61+
exit 1
62+
fi
5863
echo "Ensuring python binary... done."
5964

6065
echo "Creating virtual environment 'venv'..."

.evergreen/is_python3.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Determine whether the current Python executable is usable for Drivers Evergreen Tools.
2+
# Exit status will determine compatibility.
3+
4+
import sys
5+
6+
REQUIRED_PYTHON_VERSION_MAJOR = 3
7+
REQUIRED_PYTHON_VERSION_MINOR = 9
8+
9+
# For diagnostic purposes.
10+
print(f" Version: {sys.version.split()[0]}")
11+
12+
13+
def error_out(message):
14+
print(f" INVALID: {message}")
15+
sys.exit(1)
16+
17+
18+
try:
19+
import pip # noqa: F401
20+
except ImportError:
21+
try:
22+
import ensurepip # noqa: F401
23+
except ImportError:
24+
error_out("Does not support pip")
25+
26+
try:
27+
import venv # noqa: F401
28+
except ImportError:
29+
try:
30+
import virtualenv # noqa: F401
31+
except ImportError:
32+
error_out("Does not support venv or virtualenv")
33+
34+
35+
if "free-threading" in sys.version:
36+
error_out("Free threaded python not supported")
37+
38+
39+
if not (
40+
sys.version_info[0] == REQUIRED_PYTHON_VERSION_MAJOR
41+
and sys.version_info[1] >= REQUIRED_PYTHON_VERSION_MINOR
42+
):
43+
error_out(
44+
f"Unsupported version of python, requires {REQUIRED_PYTHON_VERSION_MAJOR}.{REQUIRED_PYTHON_VERSION_MINOR}+"
45+
)
46+
47+
if sys.version_info.releaselevel != "final":
48+
error_out("Prerelease version of python")
49+
50+
print(" VALID!")

0 commit comments

Comments
 (0)