From 3716a2d005571fce8476f83afe5d5cf391762511 Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Thu, 27 Aug 2026 13:57:54 -0400 Subject: [PATCH 1/9] ci(build_base_venvs): let riot install the dev package on Python 3.15 riot's install_dev_pkg runs a bare `pip install -e .`, so pip enforces the requires-python bound in pyproject.toml and refuses the dev package on 3.15: ERROR: Package 'ddtrace' requires a different Python: 3.15.0 not in '<3.15,>=3.9' That blocks the base venv before any test code runs. Setting PIP_IGNORE_REQUIRES_PYTHON only for the 3.15 matrix entry lets the venv build while leaving the published requires-python bound at <3.15, so we do not advertise support we do not have. Supported versions satisfy the bound already and never take the branch. --- .gitlab/templates/build-base-venvs.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitlab/templates/build-base-venvs.yml b/.gitlab/templates/build-base-venvs.yml index dc32b3e40d8..03e4e4e3e5c 100644 --- a/.gitlab/templates/build-base-venvs.yml +++ b/.gitlab/templates/build-base-venvs.yml @@ -37,6 +37,15 @@ build_base_venvs: python scripts/allow_prerelease_dependencies.py export PIP_PRE=true fi + # requires-python in pyproject.toml stops at the versions we actually + # support, so pip refuses riot's `pip install -e .` of the dev package on a + # newer interpreter we are only trialling here. Relax that check for the + # trial versions so their base venv can be built without advertising + # support we do not have. Drop a version from this list once requires-python + # covers it; supported versions must never appear here. + case "$PYTHON_VERSION" in + 3.15) export PIP_IGNORE_REQUIRES_PYTHON=1 ;; + esac riot -P -v generate --python=$PYTHON_VERSION .riot/venv_py3*/bin/pip freeze echo "Running smoke tests" From 19654b80a55724b20f137b5317d05ad7279050e8 Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Thu, 27 Aug 2026 18:47:57 -0400 Subject: [PATCH 2/9] ci(build_base_venvs): shorten 3.15 pip-ignore comment --- .gitlab/templates/build-base-venvs.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.gitlab/templates/build-base-venvs.yml b/.gitlab/templates/build-base-venvs.yml index 03e4e4e3e5c..a1b8d8af6f5 100644 --- a/.gitlab/templates/build-base-venvs.yml +++ b/.gitlab/templates/build-base-venvs.yml @@ -37,12 +37,10 @@ build_base_venvs: python scripts/allow_prerelease_dependencies.py export PIP_PRE=true fi - # requires-python in pyproject.toml stops at the versions we actually - # support, so pip refuses riot's `pip install -e .` of the dev package on a - # newer interpreter we are only trialling here. Relax that check for the - # trial versions so their base venv can be built without advertising - # support we do not have. Drop a version from this list once requires-python - # covers it; supported versions must never appear here. + # PIP_IGNORE_REQUIRES_PYTHON lets riot's `pip install -e .` install the + # dev package despite requires-python = ">=3.9,<3.15". 3.15 only: other + # matrix versions already satisfy the bound. + # TODO(py-315): drop this once requires-python includes 3.15 case "$PYTHON_VERSION" in 3.15) export PIP_IGNORE_REQUIRES_PYTHON=1 ;; esac From 2d0ed8f1327aba3694316f763b6bda64ef8c9781 Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Sat, 29 Aug 2026 22:43:28 -0400 Subject: [PATCH 3/9] ci(py-315): stop claiming #19861 makes cp315 wheels publishable #19861 only makes cp315 optional in the package validator. Un-prune after IMAGE_TAG can build a correct wheel, and only once 3.15 is supported. (cherry picked from commit 703428b24cddaba01001dcf5e2073004444ab54d) --- .gitlab/scripts/prune-unsupported-wheels.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab/scripts/prune-unsupported-wheels.sh b/.gitlab/scripts/prune-unsupported-wheels.sh index 5c1f2eb9ba1..f459bca0ee2 100755 --- a/.gitlab/scripts/prune-unsupported-wheels.sh +++ b/.gitlab/scripts/prune-unsupported-wheels.sh @@ -24,8 +24,9 @@ set -euo pipefail # # TODO(py-315): cp315 wheels are built by "build linux" and "build linux serverless" under # allow_failure so that 3.15 keeps producing CI signal, but they are compiled against the -# 3.15.0b1 PyThreadState layout and are ABI-broken. Drop cp315 from this list once #19861 -# makes those wheels correct and 3.15 is a supported target. +# 3.15.0b1 PyThreadState layout and are ABI-broken. #19861 does not fix that ABI; it only +# makes cp315 optional in the package validator. Drop cp315 from this list after IMAGE_TAG +# is on images that can build a correct wheel, and only once 3.15 is a supported target. UNSUPPORTED_TAGS=("cp315") if [ "$#" -eq 0 ]; then From e4ef95098bc422ed7d1894effd05fca7bf9a890b Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Sat, 29 Aug 2026 22:43:50 -0400 Subject: [PATCH 4/9] test(compat): drop inject_hook and wrap raise asserts on 3.15 Monitoring inject_hook already works. The wrap raise tests are skipif below NEXT and would demand a raise after wrap lift; they do not pin this PR error-string change. (cherry picked from commit 08c86936fd2503491737db8a8ae9155eca9cbba8) --- tests/internal/test_py315_import_degrade.py | 38 +++------------------ 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/tests/internal/test_py315_import_degrade.py b/tests/internal/test_py315_import_degrade.py index 23298bd000e..caedcae70ca 100644 --- a/tests/internal/test_py315_import_degrade.py +++ b/tests/internal/test_py315_import_degrade.py @@ -1,12 +1,10 @@ -"""Python 3.15 import-time degrade: wrapping must load, wrap() still raises. +"""Python 3.15 import-time degrade: wrapping must load. -Until #17849 lands bytecode wrapping for 3.15, products that import wrapping -(e.g. ModuleWatchdog) must not crash the process. wrap()/inject_hook still -raise NotImplementedError when actually used. +Products that import wrapping (e.g. ModuleWatchdog) must not crash the process. +Monitoring ``inject_hook`` already works on 3.15; this module does not assert +that wrap() raises. Wrap lift is a later PR. """ -import re - import pytest from ddtrace.internal.compat import NEXT_PY_UNSUPPORTED_MSG @@ -31,20 +29,6 @@ def test_wrapping_modules_import(): import ddtrace.internal.wrapping.generators # noqa: F401 -@pytest.mark.skipif(PYTHON_VERSION_INFO < NEXT_PY_VERSION_INFO, reason=f"{NEXT_PY_VERSION} wrap() degrade") -def test_wrap_raises_not_implemented_on_315(): - from ddtrace.internal.wrapping import wrap - - def f() -> None: - return None - - def wrapper(wrapped, args, kwargs): # noqa: ANN001, ANN202 - return wrapped(*args, **kwargs) - - with pytest.raises(NotImplementedError, match=re.escape(_UNSUPPORTED_MSG)): - wrap(f, wrapper) - - @pytest.mark.skipif(PYTHON_VERSION_INFO < NEXT_PY_VERSION_INFO, reason=f"{NEXT_PY_VERSION} lazy module degrade") def test_lazy_module_decorator_without_bytecode_wrap(): import tests.internal.lazy as lazy_module @@ -75,17 +59,3 @@ def test_debugging_products_load_without_failure(): "live-debugger", ): assert product_name not in product_manager._failed - - -@pytest.mark.skipif(PYTHON_VERSION_INFO < NEXT_PY_VERSION_INFO, reason=f"{NEXT_PY_VERSION} inject_hook degrade") -def test_inject_hook_raises_not_implemented_on_315(): - from ddtrace.internal.bytecode_injection import inject_hook - - def f() -> None: - return None - - def hook(_arg: object) -> None: - return None - - with pytest.raises(NotImplementedError, match=re.escape(_UNSUPPORTED_MSG)): - inject_hook(f, hook, f.__code__.co_firstlineno, None) From 64de597299911c6c82ac787abe1c61e282d97908 Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Sat, 29 Aug 2026 22:44:12 -0400 Subject: [PATCH 5/9] chore(ssi): do not skip the whole lib-injection tree in hook linters The SSI max bump does not need a bandit/mypy skip of lib-injection/. (cherry picked from commit 0cf5c73b2cb502b83dad86aa42484e757b13aecd) --- hooks/pre-commit/05-run-bandit | 2 +- hooks/scripts/run-mypy.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hooks/pre-commit/05-run-bandit b/hooks/pre-commit/05-run-bandit index 720d17fafe8..0f6315a103c 100755 --- a/hooks/pre-commit/05-run-bandit +++ b/hooks/pre-commit/05-run-bandit @@ -1,6 +1,6 @@ #!/bin/sh LINT_CMD="${LINT_CMD:-scripts/lint}" -staged_files=$(git diff --staged --name-only HEAD --diff-filter=ACMR | grep -E '\.py$' | grep -v '^tests/' | grep -v '^lib-injection/' | tr '\n' ' ') +staged_files=$(git diff --staged --name-only HEAD --diff-filter=ACMR | grep -E '\.py$' | grep -v '^tests/' | tr '\n' ' ') if [ -n "$staged_files" ]; then file_count=$(echo "$staged_files" | wc -w | tr -d ' ') echo "Running security scan on $file_count staged Python file(s)..." diff --git a/hooks/scripts/run-mypy.sh b/hooks/scripts/run-mypy.sh index 2563cfab004..47e6dc337be 100755 --- a/hooks/scripts/run-mypy.sh +++ b/hooks/scripts/run-mypy.sh @@ -1,6 +1,6 @@ #!/bin/sh LINT_CMD="${LINT_CMD:-scripts/lint}" -staged_files=$(git diff --staged --name-only HEAD --diff-filter=ACMR | grep -E '\.(py|pyi)$' | grep -v '^lib-injection/' | tr '\n' ' ') +staged_files=$(git diff --staged --name-only HEAD --diff-filter=ACMR | grep -E '\.(py|pyi)$' | tr '\n' ' ') if [ -n "$staged_files" ]; then # Drop .pyi stubs whose .py counterpart is also staged to avoid mypy # "Duplicate module named ..." errors. mypy discovers stubs automatically. From a5f64f9d6ad48599a5734ba373d6fdac93373ebd Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Fri, 28 Aug 2026 15:18:16 -0400 Subject: [PATCH 6/9] ci(py-315): mark the Cython pin as droppable after the image bump (cherry picked from commit cbe119a0e2e44cd65aa6f675b90b78674da65cbf) --- pyproject.toml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 16098ba6c53..44c3cd98f1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,7 @@ [build-system] requires = [ - # Cython 3.3.0 is the first stable release to publish compiled cp315 wheels, - # and its cp315 manylinux wheel is compiled against the 3.15.0rc1 PyThreadState - # layout, which grew a member in 3.15.0b4 (python/cpython#151614). Our - # manylinux2014 images still ship 3.15.0b1, so that wheel segfaults the moment - # setup.py imports Cython.Build. <3.3 has no cp315-tagged wheel, so on these - # images 3.15 resolves the pure-Python cython-*-py3-none-any.whl instead, which - # has no ABI to mismatch. Drop this once the image mirror ships a cp315 - # interpreter >= 3.15.0rc1; pypa manylinux 2026.08.04-1 is the first such tag. + # Cython 3.3 cp315 manylinux wheels SIGSEGV on the current 3.15 interpreter; <3.3 pins 3.2.x. + # TODO(py-315): drop this pin once IMAGE_TAG is on the 2026.08.04-1 images. "cython<3.3; python_version >= '3.15'", "cython; python_version < '3.15'", "cmake>=3.24.2,<3.28", From 0016baf72a77adda1446f0f9fd19cace41d0a14e Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Sat, 29 Aug 2026 22:44:15 -0400 Subject: [PATCH 7/9] ci(py-315): note lint bytecode pin is a different extra than runtime Lint extra bytecode==0.18.1 on 3.15 is not the runtime dep bytecode>=0.19.0. Leave both; they are different extras. (cherry picked from commit 5bd7d37375efa0293594963c393f77a8e56aa042) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 44c3cd98f1b..b65a2fc4f6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,6 +129,7 @@ lint = [ "cmake-format==0.6.13", "ruamel.yaml==0.18.6", "ast-grep-cli==0.39.4", + # Lint extra only; runtime deps above use bytecode>=0.19.0 on 3.15. "bytecode==0.18.1; python_version >= '3.15'", "bytecode==0.17.0; python_version < '3.15'", ] From d6329b6dcc703ab0f0190dbbcdf3b7bb16fd6f8f Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Fri, 28 Aug 2026 15:30:13 -0400 Subject: [PATCH 8/9] ci(testrunner): mark the Cython 3.15 cache pin with TODO(py-315) Drop it once the testrunner 3.15 interpreter is 3.15.0rc1+, when Cython 3.3 cp315 wheels no longer SIGSEGV. (cherry picked from commit fbea5a1713e979613bb3b06382e40ab92eb2bf3d) --- .gitlab/templates/cached-testrunner.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/templates/cached-testrunner.yml b/.gitlab/templates/cached-testrunner.yml index 63f735d7542..d50228971a5 100644 --- a/.gitlab/templates/cached-testrunner.yml +++ b/.gitlab/templates/cached-testrunner.yml @@ -18,8 +18,8 @@ fi source $EXT_CACHE_VENV/bin/activate - # Cython 3.3.0's cp315 wheel SIGSEGVs on pre-rc1 3.15. The pyproject.toml - # [build-system] pin (#19861) does not apply to this EXT_CACHE_VENV install. + # Cython 3.3 cp315 wheels SIGSEGV on pre-rc1 3.15; pyproject.toml pin does not apply here. + # TODO(py-315): drop this pin once testrunner 3.15 is 3.15.0rc1+. if [ "$PYTHON_VERSION" = "3.15" ]; then python -m pip install cmake setuptools_rust 'Cython<3.3' else From 617ccd750b2c310fbf2cd4aca59a649b44abe88b Mon Sep 17 00:00:00 2001 From: Vlad Scherbich Date: Sat, 29 Aug 2026 22:47:31 -0400 Subject: [PATCH 9/9] ci(py-315): pin lint bytecode to 0.19.0 on 3.15 Lint extra bytecode==0.18.1 conflicts with runtime bytecode>=0.19.0 when uv resolves extras for requires-python that includes 3.15. (cherry picked from commit cac61cabb50ea9377f5fe3a237502e6a71a9a37d) --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b65a2fc4f6e..91a052c74bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,8 +129,9 @@ lint = [ "cmake-format==0.6.13", "ruamel.yaml==0.18.6", "ast-grep-cli==0.39.4", - # Lint extra only; runtime deps above use bytecode>=0.19.0 on 3.15. - "bytecode==0.18.1; python_version >= '3.15'", + # Lint extra; 0.19.0 matches the runtime lower bound so uv can resolve + # both extras when requires-python includes 3.15. + "bytecode==0.19.0; python_version >= '3.15'", "bytecode==0.17.0; python_version < '3.15'", ] clean = [