diff --git a/Dockerfile b/Dockerfile index cb990269..051b2757 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,11 +35,18 @@ COPY --link scripts/build_python.sh / # ------------------------------------------------------------------------------ FROM builder-py-base as builder-py-3_12 -RUN /build_python.sh 3.12.5 +RUN /build_python.sh 3.12.7 # ------------------------------------------------------------------------------ FROM builder-py-base as builder-py-3_13 RUN /build_python.sh 3.13.0rc3 # ------------------------------------------------------------------------------ +FROM builder-py-base as builder-py-3_13t +# Building with all 3 of the options below causes tests to fail. +# Removing just the first means the image is a bit bigger, but we keep optimisations +# --disable-test-modules --enable-optimizations --with-lto +ENV PYTHON_CONFIGURE_OPTS='--enable-optimizations --with-lto --with-system-expat --without-ensurepip' +RUN /build_python.sh 3.13.0rc3t +# ------------------------------------------------------------------------------ FROM python:3.12-slim-bookworm as base ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \ @@ -56,6 +63,7 @@ RUN apt-get -y update \ COPY --link --from=builder-nsjail /nsjail/nsjail /usr/sbin/ COPY --link --from=builder-py-3_12 /snekbin/ /snekbin/ COPY --link --from=builder-py-3_13 /snekbin/ /snekbin/ +COPY --link --from=builder-py-3_13t /snekbin/ /snekbin/ RUN chmod +x /usr/sbin/nsjail \ && ln -s /snekbin/python/3.12/ /snekbin/python/default diff --git a/requirements/eval-deps.pip b/requirements/eval-deps.pip index 45496d70..d18948c7 100644 --- a/requirements/eval-deps.pip +++ b/requirements/eval-deps.pip @@ -7,7 +7,7 @@ fishhook~=0.3; python_version == '3.12' forbiddenfruit~=0.1 fuzzywuzzy~=0.18 lark~=1.2 -matplotlib~=3.9 +matplotlib~=3.9 ; python_version != '3.13' more-itertools~=10.5 networkx~=3.3 numpy~=2.1 @@ -16,7 +16,7 @@ pendulum~=3.0 ; python_version == '3.12' pyarrow~=17.0; python_version == '3.12' python-dateutil~=2.9 pyyaml~=6.0 -scipy~=1.14 +scipy~=1.14 ; python_version != '3.13' sympy~=1.13 typing-extensions~=4.12 tzdata~=2024.2 diff --git a/scripts/build_python.sh b/scripts/build_python.sh index 77f50ab6..d026eb89 100755 --- a/scripts/build_python.sh +++ b/scripts/build_python.sh @@ -4,11 +4,19 @@ shopt -s inherit_errexit py_version="${1}" -# Install Python interpreter under e.g. /snekbin/python/3.11/ (no patch version). +# Install Python interpreter under e.g. /snekbin/python/3.13/ (no patch version) +# By dropping everything after, and including, the last period or hyphen. +install_path="${py_version%[-.]*}" + +# If python version ends with a t, then ensure Python is installed to a dir ending with a t. +if [[ $py_version == *t ]]; then + install_path+="t" +fi + "${PYENV_ROOT}/plugins/python-build/bin/python-build" \ "${py_version}" \ - "/snekbin/python/${py_version%[-.]*}" -"/snekbin/python/${py_version%[-.]*}/bin/python" -m pip install -U pip + "/snekbin/python/${install_path}" +"/snekbin/python/${install_path}/bin/python" -m pip install -U pip # Clean up some unnecessary files to reduce image size bloat. find /snekbin/python/ -depth \ diff --git a/tests/test_integration.py b/tests/test_integration.py index 0d8f700d..ed44d86a 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -83,6 +83,26 @@ def test_alternate_executable_support(self): self.assertEqual(status, 200) self.assertEqual(json.loads(response)["stdout"], expected) + def test_gil_status(self): + """Test no-gil builds actually don't have a gil.""" + with run_gunicorn(): + get_gil_status = { + "input": "import sysconfig; print(sysconfig.get_config_var('Py_GIL_DISABLED'))" + } + cases = [ + ("3.13", "0\n"), + ("3.13t", "1\n"), + ] + for version, expected in cases: + with self.subTest(version=version, expected=expected): + payload = { + "executable_path": f"/snekbin/python/{version}/bin/python", + **get_gil_status, + } + response, status = snekbox_request(payload) + self.assertEqual(status, 200) + self.assertEqual(json.loads(response)["stdout"], expected) + def invalid_executable_paths(self): """Test that passing invalid executable paths result in no code execution.""" with run_gunicorn():