diff --git a/Dockerfile b/Dockerfile index 5bcb7279..b96c0859 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,15 +35,14 @@ COPY --link scripts/build_python.sh / # ------------------------------------------------------------------------------ FROM builder-py-base AS builder-py-3_13 -RUN /build_python.sh 3.13.5 -# ------------------------------------------------------------------------------ -FROM builder-py-base AS builder-py-3_13t -# This can't be bumped to latest until https://github.com/python/cpython/issues/135734 is resolved. -RUN /build_python.sh 3.13.2t +RUN /build_python.sh 3.13.8 # ------------------------------------------------------------------------------ FROM builder-py-base AS builder-py-3_14 RUN /build_python.sh 3.14.0 # ------------------------------------------------------------------------------ +FROM builder-py-base AS builder-py-3_14t +RUN /build_python.sh 3.14.0t +# ------------------------------------------------------------------------------ FROM python:3.13-slim-bookworm AS base ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \ @@ -59,11 +58,11 @@ RUN apt-get -y update \ COPY --link --from=builder-nsjail /nsjail/nsjail /usr/sbin/ COPY --link --from=builder-py-3_13 /snekbin/ /snekbin/ -COPY --link --from=builder-py-3_13t /snekbin/ /snekbin/ COPY --link --from=builder-py-3_14 /snekbin/ /snekbin/ +COPY --link --from=builder-py-3_14t /snekbin/ /snekbin/ RUN chmod +x /usr/sbin/nsjail \ - && ln -s /snekbin/python/3.13/ /snekbin/python/default + && ln -s /snekbin/python/3.14/ /snekbin/python/default # ------------------------------------------------------------------------------ FROM base AS venv @@ -84,7 +83,7 @@ RUN if [ -n "${DEV}" ]; \ then \ pip install -U -r requirements/coverage.pip \ && export PYTHONUSERBASE=/snekbox/user_base \ - && /snekbin/python/default/bin/python -m pip install --user numpy~=2.2.5; \ + && /snekbin/python/default/bin/python -m pip install --user numpy~=2.3.3; \ fi # At the end to avoid re-installing dependencies when only a config changes. diff --git a/config/snekbox.cfg b/config/snekbox.cfg index 91ba7c97..7332d4ac 100644 --- a/config/snekbox.cfg +++ b/config/snekbox.cfg @@ -9,11 +9,11 @@ time_limit: 6 keep_env: false envar: "LANG=en_US.UTF-8" -envar: "OMP_NUM_THREADS=5" -envar: "OPENBLAS_NUM_THREADS=5" -envar: "MKL_NUM_THREADS=5" -envar: "VECLIB_MAXIMUM_THREADS=5" -envar: "NUMEXPR_NUM_THREADS=5" +envar: "OMP_NUM_THREADS=15" +envar: "OPENBLAS_NUM_THREADS=15" +envar: "MKL_NUM_THREADS=15" +envar: "VECLIB_MAXIMUM_THREADS=15" +envar: "NUMEXPR_NUM_THREADS=15" envar: "PYTHONDONTWRITEBYTECODE=true" envar: "PYTHONIOENCODING=utf-8:strict" envar: "PYTHONUNBUFFERED=true" @@ -100,7 +100,7 @@ cgroup_mem_max: 73400320 cgroup_mem_swap_max: 0 cgroup_mem_mount: "/sys/fs/cgroup/memory" -cgroup_pids_max: 6 +cgroup_pids_max: 15 cgroup_pids_mount: "/sys/fs/cgroup/pids" iface_no_lo: true diff --git a/requirements/eval-deps.pip b/requirements/eval-deps.pip index 381ce0c8..50284f06 100644 --- a/requirements/eval-deps.pip +++ b/requirements/eval-deps.pip @@ -1,27 +1,31 @@ -anyio[trio]~=4.9 +anyio[trio]~=4.11 arrow~=1.3 -attrs~=25.3 -beautifulsoup4~=4.13 +attrs~=25.4 +beautifulsoup4~=4.14 -# Doesn't support 3.13, 3.13t, nor 3.14-dev, so commented out for now. +# Doesn't support 3.13, 3.14 or 3.14t, so commented out for now. # einspect~=0.5 fishhook~=0.3; python_version == "3.13" forbiddenfruit~=0.1 fuzzywuzzy~=0.18 -kaleido~=0.2 -lark~=1.2 -matplotlib~=3.10; python_version == "3.13" -more-itertools~=10.7 + +# Subdependency orjson doesn't currently build correctly on 3.14t, +# but unfortunately there is not currently a way to provide version +# specification that filters based on free-threading capability. +kaleido~=1.1; python_version == "3.13" +lark~=1.3 +matplotlib~=3.10 +more-itertools~=10.8 networkx~=3.5 numpy~=2.3 -pandas~=2.3; python_version == "3.13" +pandas~=2.3 pendulum~=3.1 pyarrow~=21.0; python_version == "3.13" python-dateutil~=2.9 pyyaml~=6.0 scipy~=1.16 sympy~=1.14 -typing-extensions~=4.14 +typing-extensions~=4.15 tzdata~=2025.2 -yarl~=1.20 +yarl~=1.22 diff --git a/tests/test_integration.py b/tests/test_integration.py index d601a649..b3c17075 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -61,13 +61,13 @@ def test_alternate_executable_support(self): cases = [ ( get_python_version_body, - "3.13\n", + "3.14\n", "test default executable is used when executable_path not specified", ), ( get_python_version_body - | {"executable_path": "/snekbin/python/3.13/bin/python"}, - "3.13\n", + | {"executable_path": "/snekbin/python/3.14/bin/python"}, + "3.14\n", "test default executable is used when explicitly set", ), ( @@ -90,8 +90,8 @@ def test_gil_status(self): "input": "import sysconfig; print(sysconfig.get_config_var('Py_GIL_DISABLED'))" } cases = [ - ("3.13", "0\n"), - ("3.13t", "1\n"), + ("3.14", "0\n"), + ("3.14t", "1\n"), ] for version, expected in cases: with self.subTest(version=version, expected=expected): diff --git a/tests/test_nsjail.py b/tests/test_nsjail.py index 01be592a..71f797c5 100644 --- a/tests/test_nsjail.py +++ b/tests/test_nsjail.py @@ -108,17 +108,17 @@ def f(): object = "A" * 40_000_000 time.sleep(0.5) + if __name__ == "__main__": + proc_1 = Process(target=f) + proc_2 = Process(target=f) - proc_1 = Process(target=f) - proc_2 = Process(target=f) + proc_1.start() + proc_2.start() - proc_1.start() - proc_2.start() + proc_1.join() + proc_2.join() - proc_1.join() - proc_2.join() - - print(proc_1.exitcode, proc_2.exitcode) + print(proc_1.exitcode, proc_2.exitcode) """ ) @@ -137,8 +137,9 @@ def test_multiprocessing_pool(self): def f(x): return x*x - with Pool(2) as p: - print(p.map(f, [1, 2, 3])) + if __name__ == "__main__": + with Pool(2) as p: + print(p.map(f, [1, 2, 3])) """ ) @@ -212,6 +213,13 @@ def test_write_hidden_exclude(self): self.assertEqual(result.files[0].content, b"a") def test_forkbomb_resource_unavailable(self): + # Using the production max PIDs causes processes to be killed due to memory instead of + # PID allocation exhaustion. For this test case, the PID limit is reduced to ensure + # that PID exhaustion is still something that is guarded against. + + previous_pids_max = self.nsjail.config.cgroup_pids_max + self.nsjail.config.cgroup_pids_max = 5 + code = dedent( """ import os @@ -220,10 +228,13 @@ def test_forkbomb_resource_unavailable(self): """ ).strip() - result = self.eval_file(code) - self.assertEqual(result.returncode, 1) - self.assertIn("Resource temporarily unavailable", result.stdout) - self.assertEqual(result.stderr, None) + try: + result = self.eval_file(code) + self.assertEqual(result.returncode, 1) + self.assertIn("Resource temporarily unavailable", result.stdout) + self.assertEqual(result.stderr, None) + finally: + self.nsjail.config.cgroup_pids_max = previous_pids_max def test_file_parsing_timeout(self): code = dedent(