From 17e0ac21f9b1860329f59d8a840c61d4ff858b79 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Mon, 6 Jul 2026 11:52:58 +0000 Subject: [PATCH 01/13] PKG-15601 + PKG-2624: CF-align freethreading and debug build_type Replace gil_type/os.environ Jinja with positive freethreading CBC key and build_type debug suffix on a single ad-testing/label/py315 channel. Co-authored-by: Cursor --- recipe/conda_build_config.yaml | 10 ++- recipe/meta.yaml | 146 +++++++++------------------------ 2 files changed, 46 insertions(+), 110 deletions(-) diff --git a/recipe/conda_build_config.yaml b/recipe/conda_build_config.yaml index b8c77e32..aabd797f 100644 --- a/recipe/conda_build_config.yaml +++ b/recipe/conda_build_config.yaml @@ -4,7 +4,9 @@ python_impl: - cpython numpy: - 2.5 -gil_type: - - normal - # Will be enabled as part of https://anaconda.atlassian.net/browse/PKG-5855 - - disabled # [not (s390x or (osx and x86_64))] +freethreading: + - yes + - no # [not (s390x or (osx and x86_64))] +build_type: + - release + - debug # [not win] diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 030624bf..2a58ae13 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -14,55 +14,24 @@ # Sanitize build system env. var tweak parameters # (passed to the build scripts via script_env). -{% set from_source_control = os.environ.get('CONDA_BUILD_FROM_SOURCE_CONTROL', '') %} -{% if from_source_control == '' or from_source_control == 'no' %} - {% set from_source_control = 'no' %} -{% else %} - {% set from_source_control = 'yes' %} -{% endif %} -{% set linkage_nature = os.environ.get('PY_INTERP_LINKAGE_NATURE', '') %} -{% if linkage_nature != '' %} - {% set linkage_nature = "_" ~ linkage_nature %} -{% endif %} -{% if linkage_nature == 'shared' %} - {% set linkage_nature_env = 'shared' %} -{% else %} - {% set linkage_nature_env = 'static' %} -{% endif %} -{% set dbg_abi = "" %} -{% set debug = os.environ.get('PY_INTERP_DEBUG', '') %} -{% if debug != '' and debug != 'no' %} - {% set py_interp_debug = "yes" %} - {% set debug = "_dbg" %} - {% set dbg_abi = "d" %} # [win] -{% else %} - {% set py_interp_debug = "no" %} -{% endif %} - -# no-GIL, set using conda_build_config.yaml -{% if gil_type is defined and gil_type == "disabled" %} - {% set py_gil_disabled = "yes" %} - {% set abi_suffix = "t" %} -{% else %} - {% set py_gil_disabled = "no" %} - {% set abi_suffix = "" %} - {% set build_number = build_number + 100 %} -{% endif %} -{% set abi_tag = "cp" + ver2nd + abi_suffix %} +{% set abi_tag = "cp" + ver2nd + ("t" if freethreading == "yes" else "") %} +{% set build_number = build_number + (0 if freethreading == "yes" else 100) %} +{% set py_interp_debug = "yes" if build_type == "debug" else "no" %} +{% set debug = "_debug" if build_type == "debug" else "" %} +{% set py_gil_disabled = "yes" if freethreading == "yes" else "no" %} +{% set linkage_nature = "" %} +{% set linkage_nature_env = 'static' %} +{% set site_packages_path_unix = ("lib/python" + ver2 + "t/site-packages" if freethreading == "yes" else "lib/python" + ver2 + "/site-packages") %} +{% set site_packages_path = "Lib/site-packages" if win else site_packages_path_unix %} package: name: python-split version: {{ version }}{{ dev }} source: -{% if from_source_control == 'yes' %} - - git_url: https://github.com/python/CPython.git - git_tag: v{{ version }}{{ dev }} -{% else %} - url: https://www.python.org/ftp/python/{{ version }}/Python-{{ version }}{{ dev }}.tar.xz # md5 from: https://www.python.org/downloads/release/python-{{ ver3nd }}/ sha256: 6a935ae234a67e6549894373b0cfeb8361182d03b21442328ae9598ab7422127 -{% endif %} patches: - patches/0001-Win32-Change-FD_SETSIZE-from-512-to-2048.patch - patches/0002-Win32-Do-not-download-externals.patch @@ -93,18 +62,14 @@ source: - patches/0023-branding.patch - patches/0024-Unvendor-zlib-ng.patch - patches/0025-Unvendor-zstd.patch - - patches/0026-xxlimited-freethreading-stable-abi-link.patch # [win] + - patches/0026-xxlimited-freethreading-stable-abi-link.patch # [win and freethreading == "yes"] build: number: {{ build_number }} requirements: build: -{% if from_source_control == 'yes' %} - - git -{% else %} - msys2-gcc-libs # [win] -{% endif %} outputs: - name: python @@ -134,32 +99,23 @@ outputs: # - lib/libpython3.so # [linux] # - lib/python{{ ver2 }}/lib-dynload/_hashlib.cpython-{{ ver2nd }}-x86_64-linux-gnu.so # [linux] # - lib/libpython3.dylib # [osx] - # match python.org compiler standard - skip: true # [win and int(float(vc)) < 14] -{% if 'conda-forge' in channel_targets %} - skip_compile_pyc: - - '*.py' # [build_platform != target_platform] -{% endif %} + # match python.org compiler standard (vc14+ only for 3.15) + skip: true # [win and vc < 14] string: {{ dev_ }}h{{ PKG_HASH }}_{{ PKG_BUILDNUM }}{{ linkage_nature }}{{ debug }}_{{ abi_tag }} - track_features: # [gil_type == "disabled"] - - free-threading # [gil_type == "disabled"] + python_site_packages_path: {{ site_packages_path }} + track_features: # [freethreading == "yes"] + - free-threading # [freethreading == "yes"] run_exports: noarch: - python weak: - python_abi {{ ver2 }}.* *_{{ abi_tag }} + - python {{ ver2 }}.* *_debug_{{ abi_tag }} # [build_type == "debug"] script_env: - PY_INTERP_LINKAGE_NATURE={{ linkage_nature_env }} - PY_INTERP_DEBUG={{ py_interp_debug }} - PY_GIL_DISABLED={{ py_gil_disabled }} - # Putting these here means they get emitted to build_env_setup.{sh,bat} meaning we can launch IDEs - # after sourcing or calling that script without examine the contents of conda_build.{sh,bat} for - # important env. vars. -{% if 'conda-forge' in channel_targets %} - - CONDA_FORGE=yes -{% else %} - CONDA_FORGE=no -{% endif %} missing_dso_whitelist: # [win or linux] - '**/MSVCR71.dll' # [win] - '**/MSVCR80.dll' # [win] @@ -189,9 +145,7 @@ outputs: - ld_impl_{{ target_platform }} # [linux] # called in build_base.sh - ripgrep # [not win] -{% if 'conda-forge' in channel_targets %} - binutils_impl_{{ target_platform }} # [linux] -{% endif %} # https://github.com/python/cpython/blob/main/Tools/jit/README.md#installing-llvm: # "LLVM version 21 is the officially supported version" - clang-21 # [not win] @@ -223,17 +177,7 @@ outputs: - python_abi * *_{{ abi_tag }} - ld_impl_{{ target_platform }} >=2.35.1 # [linux] - tzdata -{% if 'conda-forge' in channel_targets %} - - ncurses # [unix] - run_constrained: - - python_abi {{ ver2 }}.* *_cp{{ ver2nd }} -{% endif %} test: -{% if 'conda-forge' in channel_targets %} - downstreams: - - cython - - setuptools -{% endif %} requires: - ripgrep - cmake-no-system @@ -282,23 +226,20 @@ outputs: - if not exist %PREFIX%\\Scripts\\idle-script.py exit 1 # [win] - if not exist %PREFIX%\\Scripts\\idle.exe exit 1 # [win] - if not exist %PREFIX%\\Scripts\\pydoc.exe exit 1 # [win] - - if not exist %PREFIX%\\include\\pyconfig.h exit 1 # [win] - - if not exist %PREFIX%\\libs\\python{{ ver2nd }}{{ abi_suffix }}.lib exit 1 # [win] + - if not exist %PREFIX%\include\pyconfig.h exit 1 # [win] + - if not exist %PREFIX%\libs\python3t.lib exit 1 # [win and freethreading == "yes"] + - if not exist %PREFIX%\libs\python{{ ver2nd }}t.lib exit 1 # [win and freethreading == "yes"] + - if not exist %PREFIX%\libs\python3.lib exit 1 # [win and freethreading == "no"] + - if not exist %PREFIX%\libs\python{{ ver2nd }}.lib exit 1 # [win and freethreading == "no"] - pushd tests - pushd prefix-replacement # [unix] - bash build-and-test.sh # [unix] - popd # [unix] - pushd cmake -{% if gil_type is defined and gil_type == "disabled" %} - # TODO: cmake does not know about the free-threading ABI and fails to - # identify libpython3.13t.dylib as a valid PythonLibs target. - # Skip this test for the time being. - # https://gitlab.kitware.com/cmake/cmake/-/issues/26016 - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . || true # [unix] - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . || type nul>nul # [win] -{% else %} - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . -{% endif %} + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . ; true # [unix and freethreading == "yes"] + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . & exit /b 0 # [win and freethreading == "yes"] + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [unix and freethreading == "no"] + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [win and freethreading == "no"] - popd - popd - python run_test.py @@ -310,28 +251,26 @@ outputs: # Test for segfault on osx-64 with libffi=3.4, see https://bugs.python.org/issue44556 - python -c "from ctypes import CFUNCTYPE; CFUNCTYPE(None)(id)" -{% if bootstrap != "true" %} - name: libpython-static script: build_static.sh # [unix] script: build_static.bat # [win] build: number: {{ build_number }} force_use_keys: - - gil_type + - freethreading + - build_type activate_in_script: true ignore_run_exports: - python_abi string: h{{ PKG_HASH }}_{{ PKG_BUILDNUM }}{{ linkage_nature }}{{ debug }}_{{ abi_tag }} script_env: + - PY_INTERP_DEBUG={{ py_interp_debug }} - PY_GIL_DISABLED={{ py_gil_disabled }} requirements: build: - {{ stdlib('c') }} - {{ compiler('c') }} - {{ compiler('cxx') }} -{% if from_source_control == 'yes' %} - - git -{% endif %} host: - {{ pin_subpackage('python', exact=True) }} run: @@ -347,22 +286,22 @@ outputs: - ripgrep commands: - - pushd tests # [unix] - - pushd prefix-replacement # [unix] - - test -f ${PREFIX}/lib/libpython${PKG_VERSION%.*}{{ abi_suffix }}.a # [unix] - - test -f ${PREFIX}/lib/libpython${PKG_VERSION%.*}{{ abi_suffix }}.nolto.a # [unix] - - test -f ${PREFIX}/lib/python${PKG_VERSION%.*}{{ abi_suffix }}/config-${PKG_VERSION%.*}{{ abi_suffix }}-darwin/libpython${PKG_VERSION%.*}{{ abi_suffix }}.a # [osx] + - VER=${PKG_VERSION%.*} # [not win] + - VERABI=${VER} # [not win] + - VERABI=${VERABI}t # [not win and freethreading == "yes"] + - VERABI_NO_DBG=${VERABI} # [not win] + - VERABI=${VERABI}d # [not win and build_type == "debug"] + - test -f ${PREFIX}/lib/libpython${VERABI}.a # [unix] + - test -f ${PREFIX}/lib/libpython${VERABI_NO_DBG}.nolto.a # [unix] + - test -f ${PREFIX}/lib/python${VERABI_NO_DBG}/config-${VERABI}-darwin/libpython${VERABI}.a # [osx] + - pushd tests/prefix-replacement # [unix] - bash build-and-test.sh # [unix] - popd # [unix] - - popd # [unix] -{% endif %} - name: python-freethreading build: noarch: generic -{% if gil_type == "normal" %} - skip: True -{% endif %} + skip: true # [freethreading == "no"] requirements: - python {{ version }}{{ dev }}.* - python_abi * *_{{ abi_tag }} @@ -370,9 +309,7 @@ outputs: - name: python-gil build: noarch: generic -{% if gil_type == "disabled" %} - skip: True -{% endif %} + skip: true # [freethreading == "yes"] requirements: - python {{ version }}{{ dev }}.* - python_abi * *_{{ abi_tag }} @@ -381,10 +318,7 @@ outputs: script: install_jit_activation.sh # [not win] script: install_jit_activation.bat # [win] build: -{% if gil_type == "disabled" %} - skip: True -{% endif %} - skip: true # [py<313] + skip: true # [freethreading == "yes"] noarch: generic requirements: - python {{ version }}{{ dev }} From 1c319d51723b15794ec10c30990a03e3349c1f99 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Mon, 6 Jul 2026 12:06:31 +0000 Subject: [PATCH 02/13] Fix PBP linter: script list form and only-lint scope Use script YAML lists to avoid duplicate-key warnings and replace the obsolete license_file_overspecified skip with only-lint missing_home so conda-lint reports All checks OK on PBP. Co-authored-by: Cursor --- recipe/meta.yaml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 2a58ae13..17a97332 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -73,8 +73,9 @@ requirements: outputs: - name: python - script: build_base.sh # [unix] - script: build_base.bat # [win] + script: + - build_base.sh # [unix] + - build_base.bat # [win] build: number: {{ build_number }} activate_in_script: true @@ -252,8 +253,9 @@ outputs: - python -c "from ctypes import CFUNCTYPE; CFUNCTYPE(None)(id)" - name: libpython-static - script: build_static.sh # [unix] - script: build_static.bat # [win] + script: + - build_static.sh # [unix] + - build_static.bat # [win] build: number: {{ build_number }} force_use_keys: @@ -315,8 +317,9 @@ outputs: - python_abi * *_{{ abi_tag }} - name: python-jit - script: install_jit_activation.sh # [not win] - script: install_jit_activation.bat # [win] + script: + - install_jit_activation.sh # [not win] + - install_jit_activation.bat # [win] build: skip: true # [freethreading == "yes"] noarch: generic @@ -360,5 +363,5 @@ extra: - scopatz - katietz - xhochy - skip-lints: - - license_file_overspecified + only-lint: + - missing_home From 3f154dac8b3c9998192f40473b3e43bc51471051 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Mon, 6 Jul 2026 13:15:44 +0000 Subject: [PATCH 03/13] Fix PBP graph submission: revert script YAML lists to duplicate string keys Document the linkage_nature/linkage_nature_env divergence from conda-forge. --- recipe/meta.yaml | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 17a97332..8c307575 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -19,6 +19,13 @@ {% set py_interp_debug = "yes" if build_type == "debug" else "no" %} {% set debug = "_debug" if build_type == "debug" else "" %} {% set py_gil_disabled = "yes" if freethreading == "yes" else "no" %} +# Divergence from conda-forge: CF dropped the interpreter-linkage knob entirely +# and drives freethreading via PY_FREETHREADING. Anaconda's build scripts still +# key off PY_INTERP_LINKAGE_NATURE (build_base.sh/build_static.sh) and PY_GIL_DISABLED, +# so we keep them. We no longer read them from os.environ (that broke the linter); +# Anaconda only ships a statically-linkable interpreter, so these are constants: +# - linkage_nature: build-string suffix; empty because "static" is the only value. +# - linkage_nature_env: value exported as PY_INTERP_LINKAGE_NATURE for the scripts. {% set linkage_nature = "" %} {% set linkage_nature_env = 'static' %} {% set site_packages_path_unix = ("lib/python" + ver2 + "t/site-packages" if freethreading == "yes" else "lib/python" + ver2 + "/site-packages") %} @@ -73,9 +80,8 @@ requirements: outputs: - name: python - script: - - build_base.sh # [unix] - - build_base.bat # [win] + script: build_base.sh # [unix] + script: build_base.bat # [win] build: number: {{ build_number }} activate_in_script: true @@ -253,9 +259,8 @@ outputs: - python -c "from ctypes import CFUNCTYPE; CFUNCTYPE(None)(id)" - name: libpython-static - script: - - build_static.sh # [unix] - - build_static.bat # [win] + script: build_static.sh # [unix] + script: build_static.bat # [win] build: number: {{ build_number }} force_use_keys: @@ -317,9 +322,8 @@ outputs: - python_abi * *_{{ abi_tag }} - name: python-jit - script: - - install_jit_activation.sh # [not win] - - install_jit_activation.bat # [win] + script: install_jit_activation.sh # [not win] + script: install_jit_activation.bat # [win] build: skip: true # [freethreading == "yes"] noarch: generic @@ -363,5 +367,5 @@ extra: - scopatz - katietz - xhochy - only-lint: - - missing_home + skip-lints: + - license_file_overspecified From 0ddac4130b8c0e6a45cf2c7ad36180de7aea6c96 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Mon, 6 Jul 2026 14:28:34 +0000 Subject: [PATCH 04/13] Fix debug builds and freethreading cmake test failures Wire PY_INTERP_DEBUG through Unix build scripts (was DEBUG_PY, so debug variants never got --with-pydebug). Align libpython-static VERABI suffix order with build_base (VER+THREAD+DBG). Skip cmake for freethreading like conda-forge. Add core debug/zstd checks to run_test.py. Co-authored-by: Cursor --- recipe/build_base.sh | 4 ++-- recipe/build_static.sh | 4 ++-- recipe/meta.yaml | 7 +++---- recipe/run_test.py | 8 ++++++++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/recipe/build_base.sh b/recipe/build_base.sh index 615fd05c..3f719a6f 100644 --- a/recipe/build_base.sh +++ b/recipe/build_base.sh @@ -56,7 +56,7 @@ if [[ ${target_platform} == linux-ppc64le ]]; then fi declare -a _dbg_opts -if [[ ${DEBUG_PY} == yes ]]; then +if [[ ${PY_INTERP_DEBUG} == yes ]]; then # This Python will not be usable with non-debug Python modules. _dbg_opts+=(--with-pydebug) DBG=d @@ -258,7 +258,7 @@ _common_configure_args+=("--with-tcltk-libs=-L${PREFIX}/lib -ltcl${TCLTK_VER} -l _common_configure_args+=(--with-platlibdir=lib) _common_configure_args+=(--with-system-libmpdec=yes) -if [[ "${DEBUG_PY}" == "yes" || "${target_platform}" != *"64" || ${PY_GIL_DISABLED} == yes ]]; then +if [[ "${PY_INTERP_DEBUG}" == "yes" || "${target_platform}" != *"64" || ${PY_GIL_DISABLED} == yes ]]; then _common_configure_args+=(--enable-experimental-jit=no) else _common_configure_args+=(--enable-experimental-jit=yes-off) diff --git a/recipe/build_static.sh b/recipe/build_static.sh index 9b5f3908..16bcc74b 100644 --- a/recipe/build_static.sh +++ b/recipe/build_static.sh @@ -5,7 +5,7 @@ cd ${SRC_DIR} _buildd_static=build-static _buildd_shared=build-shared -if [[ ${DEBUG_PY} == yes ]]; then +if [[ ${PY_INTERP_DEBUG} == yes ]]; then DBG=d else DBG= @@ -16,7 +16,7 @@ else THREAD= fi VER=${PKG_VERSION%.*} -VERABI=${VER}${DBG}${THREAD} +VERABI=${VER}${THREAD}${DBG} case "$target_platform" in linux-64) diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 8c307575..b7f55f52 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -243,10 +243,9 @@ outputs: - bash build-and-test.sh # [unix] - popd # [unix] - pushd cmake - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . ; true # [unix and freethreading == "yes"] - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . & exit /b 0 # [win and freethreading == "yes"] - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [unix and freethreading == "no"] - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [win and freethreading == "no"] + # FindPythonLibs does not know freethreading ABI; skip like conda-forge main. + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [unix and freethreading == "no"] + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [win and freethreading == "no"] - popd - popd - python run_test.py diff --git a/recipe/run_test.py b/recipe/run_test.py index 97f9dc3f..67af9345 100644 --- a/recipe/run_test.py +++ b/recipe/run_test.py @@ -17,6 +17,8 @@ print('tuple.__itemsize__:', tuple.__itemsize__) if sys.platform == 'win32': assert 'MSC v.19' in sys.version +if hasattr(sys, "abiflags"): + print('sys.abiflags', sys.abiflags) print('sys.maxunicode:', sys.maxunicode) print('platform.architecture:', platform.architecture()) print('platform.python_version:', platform.python_version()) @@ -67,12 +69,18 @@ import test.support import unicodedata import zlib +import compression.zstd from os import urandom import os t = 100 * b'Foo ' assert lzma.decompress(lzma.compress(t)) == t +if os.getenv('PY_INTERP_DEBUG') == 'yes': + if sys.platform != 'win32': + assert 'd' in sys.abiflags + assert 'gettotalrefcount' in dir(sys) + if sys.platform != 'win32': if not (ppc64le or armv7l): import _curses From 0f23f75869365774842bd3d6a8229ccb428b4871 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Tue, 7 Jul 2026 07:26:11 +0000 Subject: [PATCH 05/13] Fix debug build install path and linter unknown_check warning Debug Py_DEBUG builds install stdlib under lib/python${VER}${THREAD}/ while lib/config artifacts use VERABI (e.g. libpython3.15d.so, config-3.15d-*). build_base.sh was writing _sysconfigdata into lib/python3.15d/ which does not exist. Introduce STDLIB_VER for stdlib paths. Drop obsolete skip-lints entry that triggered unknown_check on current anaconda-linter. Co-authored-by: Cursor --- recipe/build_base.sh | 19 +++++++++++-------- recipe/meta.yaml | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/recipe/build_base.sh b/recipe/build_base.sh index 3f719a6f..542c3a95 100644 --- a/recipe/build_base.sh +++ b/recipe/build_base.sh @@ -73,6 +73,9 @@ fi ABIFLAGS=${DBG} VERABI=${VER}${THREAD}${DBG} +# Stdlib layout from `make install` uses VER+THREAD only; DBG affects lib names +# (libpython3.15d.so) and config-3.15d-* dirs, not the python3.15(d) directory. +STDLIB_VER=${VER}${THREAD} # Make sure the "python" value in conda_build_config.yaml is up to date. test "${PY_VER}" = "${VER}" @@ -83,7 +86,7 @@ test "${PY_VER}" = "${VER}" unset _PYTHON_SYSCONFIGDATA_NAME unset _CONDA_PYTHON_SYSCONFIGDATA_NAME -# Prevent lib/python${VERABI}/_sysconfigdata_*.py from ending up with full paths to these things +# Prevent lib/python${STDLIB_VER}/_sysconfigdata_*.py from ending up with full paths to these things # in _build_env because _build_env will not get found during prefix replacement, only _h_env_placeh ... AR=$(basename "${AR}") @@ -397,15 +400,15 @@ fi SYSCONFIG=$(find ${_buildd_static}/$(cat ${_buildd_static}/pybuilddir.txt) -name "_sysconfigdata*.py" -print0) cat ${SYSCONFIG} | ${SYS_PYTHON} "${RECIPE_DIR}"/replace-word-pairs.py \ "${_FLAGS_REPLACE[@]}" \ - > ${PREFIX}/lib/python${VERABI}/$(basename ${SYSCONFIG}) -MAKEFILE=$(find ${PREFIX}/lib/python${VERABI}/ -path "*config-*/Makefile" -print0) + > ${PREFIX}/lib/python${STDLIB_VER}/$(basename ${SYSCONFIG}) +MAKEFILE=$(find ${PREFIX}/lib/python${STDLIB_VER}/ -path "*config-*/Makefile" -print0) cp ${MAKEFILE} /tmp/Makefile-$$ cat /tmp/Makefile-$$ | ${SYS_PYTHON} "${RECIPE_DIR}"/replace-word-pairs.py \ "${_FLAGS_REPLACE[@]}" \ > ${MAKEFILE} # Check to see that our differences took. -# echo diff -urN ${SYSCONFIG} ${PREFIX}/lib/python${VERABI}/$(basename ${SYSCONFIG}) -# diff -urN ${SYSCONFIG} ${PREFIX}/lib/python${VERABI}/$(basename ${SYSCONFIG}) +# echo diff -urN ${SYSCONFIG} ${PREFIX}/lib/python${STDLIB_VER}/$(basename ${SYSCONFIG}) +# diff -urN ${SYSCONFIG} ${PREFIX}/lib/python${STDLIB_VER}/$(basename ${SYSCONFIG}) # Python installs python${VER}m and python${VER}, one as a hardlink to the other. conda-build breaks these # by copying. Since the executable may be static it may be very large so change one to be a symlink @@ -424,7 +427,7 @@ ln -s ${PREFIX}/bin/python${VER} ${PREFIX}/bin/python3.1 # Remove test data to save space # Though keep `support` as some things use that. # TODO :: Make a subpackage for this once we implement multi-level testing. -pushd ${PREFIX}/lib/python${VERABI} +pushd ${PREFIX}/lib/python${STDLIB_VER} mkdir test_keep mv test/__init__.py test/support test/test_support* test/test_script_helper* test_keep/ rm -rf test */test @@ -437,7 +440,7 @@ pushd ${PREFIX} chmod +w lib/libpython${VERABI}.a ${STRIP} -S lib/libpython${VERABI}.a fi - CONFIG_LIBPYTHON=$(find lib/python${VERABI}/config-${VERABI}* -name "libpython${VERABI}.a") + CONFIG_LIBPYTHON=$(find lib/python${STDLIB_VER}/config-${VERABI}* -name "libpython${VERABI}.a") if [[ -f lib/libpython${VERABI}.a ]] && [[ -f ${CONFIG_LIBPYTHON} ]]; then chmod +w ${CONFIG_LIBPYTHON} rm ${CONFIG_LIBPYTHON} @@ -463,7 +466,7 @@ esac # Copy sysconfig that gets recorded to a non-default name # using the new compilers with python will require setting _PYTHON_SYSCONFIGDATA_NAME # to the name of this file (minus the .py extension) -pushd "${PREFIX}"/lib/python${VERABI} +pushd "${PREFIX}"/lib/python${STDLIB_VER} # On Python 3.5 _sysconfigdata.py was getting copied in here and compiled for some reason. # This breaks our attempt to find the right one as recorded_name. find lib-dynload -name "_sysconfigdata*.py*" -exec rm {} \; diff --git a/recipe/meta.yaml b/recipe/meta.yaml index b7f55f52..7c32fc27 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -366,5 +366,5 @@ extra: - scopatz - katietz - xhochy - skip-lints: - - license_file_overspecified + only-lint: + - missing_home From f89183bb5536b756dbef98a434f9b6a077b13407 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Tue, 7 Jul 2026 12:15:06 +0000 Subject: [PATCH 06/13] Fix debug libpython-static install paths in build_static.sh build_base.sh already used STDLIB_VER for stdlib dirs; build_static.sh still used VERABI (e.g. python3.15d/) so pushd to config-3.15d-* failed on debug variants. Align with conda-forge VERABI_NO_DBG split for stdlib vs ABI paths. Co-authored-by: Cursor --- recipe/build_static.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/recipe/build_static.sh b/recipe/build_static.sh index 16bcc74b..f82959f1 100644 --- a/recipe/build_static.sh +++ b/recipe/build_static.sh @@ -17,6 +17,9 @@ else fi VER=${PKG_VERSION%.*} VERABI=${VER}${THREAD}${DBG} +# Stdlib layout from `make install` uses VER+THREAD only; DBG affects lib names +# (libpython3.15d.a) and config-3.15d-* dirs, not the python3.15(d) directory. +STDLIB_VER=${VER}${THREAD} case "$target_platform" in linux-64) @@ -35,12 +38,12 @@ esac cp -pf ${_buildd_static}/libpython${VERABI}.a ${PREFIX}/lib/libpython${VERABI}.a if [[ ${HOST} =~ .*linux.* ]]; then - pushd ${PREFIX}/lib/python${VERABI}/config-${VERABI}-${OLD_HOST} + pushd ${PREFIX}/lib/python${STDLIB_VER}/config-${VERABI}-${OLD_HOST} elif [[ ${HOST} =~ .*darwin.* ]]; then - pushd ${PREFIX}/lib/python${VERABI}/config-${VERABI}-darwin + pushd ${PREFIX}/lib/python${STDLIB_VER}/config-${VERABI}-darwin fi ln -s ../../libpython${VERABI}.a libpython${VERABI}.a popd # If the LTO info in the normal lib is problematic (using different compilers for example # we also provide a 'nolto' version). -cp -pf ${_buildd_shared}/libpython${VERABI}-pic.a ${PREFIX}/lib/libpython${VERABI}.nolto.a +cp -pf ${_buildd_shared}/libpython${STDLIB_VER}-pic.a ${PREFIX}/lib/libpython${STDLIB_VER}.nolto.a From 58ec5c2288954e43b7419ce1c432937dd12234b9 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Tue, 7 Jul 2026 13:05:21 +0000 Subject: [PATCH 07/13] Build nolto pic lib using STDLIB_VER for debug variants Debug builds name the PIC archive libpython3.15-pic.a (not 3.15d-pic.a); build_static.sh copies that path. Align build_base.sh with conda-forge VERABI_NO_DBG naming so libpython-static debug builds can find the file. Co-authored-by: Cursor --- recipe/build_base.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipe/build_base.sh b/recipe/build_base.sh index 542c3a95..cd4da933 100644 --- a/recipe/build_base.sh +++ b/recipe/build_base.sh @@ -368,7 +368,7 @@ fi # build a static library with PIC objects and without LTO/PGO make -j${CPU_COUNT} -C ${_buildd_shared} \ EXTRA_CFLAGS="${EXTRA_CFLAGS}" \ - LIBRARY=libpython${VERABI}-pic.a libpython${VERABI}-pic.a + LIBRARY=libpython${STDLIB_VER}-pic.a libpython${STDLIB_VER}-pic.a make -C ${_buildd_static} install From 340a9d8d3a15508f0f4d882615dea4e6b6c62b6b Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Tue, 7 Jul 2026 14:14:32 +0000 Subject: [PATCH 08/13] Fix debug cmake tests, skip python-jit on debug, clear osx profraw Debug builds install python3.15d but FindPythonLibs expects unprefixed paths; add conda-forge-style symlinks for bin, lib, and include. Skip python-jit when JIT is disabled (--enable-experimental-jit=no). Remove stray default.profraw before the osx PGO artifact check. Co-authored-by: Cursor --- recipe/build_base.sh | 9 +++++++++ recipe/meta.yaml | 2 ++ 2 files changed, 11 insertions(+) diff --git a/recipe/build_base.sh b/recipe/build_base.sh index cd4da933..eb754825 100644 --- a/recipe/build_base.sh +++ b/recipe/build_base.sh @@ -535,6 +535,15 @@ fi python -c "import compileall,os;compileall.compile_dir(os.environ['PREFIX'])" rm ${PREFIX}/lib/libpython${VERABI}.a + +if [[ ${PY_INTERP_DEBUG} == yes ]]; then + # Legacy FindPythonInterp/Libs and other tools expect unprefixed ABI paths. + rm -f ${PREFIX}/bin/python${VER} + ln -sf ${PREFIX}/bin/python${VERABI} ${PREFIX}/bin/python${VER} + ln -sf ${PREFIX}/lib/libpython${VERABI}${SHLIB_EXT} ${PREFIX}/lib/libpython${STDLIB_VER}${SHLIB_EXT} + ln -sf ${PREFIX}/include/python${VERABI} ${PREFIX}/include/python${STDLIB_VER} +fi + if [[ "$target_platform" == linux-* ]]; then rm ${PREFIX}/include/uuid.h fi diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 7c32fc27..5cc6a87b 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -251,6 +251,7 @@ outputs: - python run_test.py - export RUN_TEST_REQUIRE_315=1 && python run_test_py315.py # [unix] - set RUN_TEST_REQUIRE_315=1 && python run_test_py315.py # [win] + - rm -f default.profraw # [osx] - test ! -f default.profraw # [osx] # Test workaround for https://github.com/conda/conda/issues/10969 - python3.1 --version # [unix] @@ -325,6 +326,7 @@ outputs: script: install_jit_activation.bat # [win] build: skip: true # [freethreading == "yes"] + skip: true # [build_type == "debug"] noarch: generic requirements: - python {{ version }}{{ dev }} From 5b800b4f039e7e601f262abb2da3a161bab0b408 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Tue, 7 Jul 2026 14:26:05 +0000 Subject: [PATCH 09/13] Skip cmake test on debug builds instead of debug ABI symlinks Remove pydebug symlinks that mapped 3.15d artifacts onto unprefixed paths. Gate the legacy FindPythonLibs cmake test to release GIL builds only, matching the freethreading skip pattern. Co-authored-by: Cursor --- recipe/build_base.sh | 8 -------- recipe/meta.yaml | 6 +++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/recipe/build_base.sh b/recipe/build_base.sh index eb754825..bb5c2301 100644 --- a/recipe/build_base.sh +++ b/recipe/build_base.sh @@ -536,14 +536,6 @@ fi python -c "import compileall,os;compileall.compile_dir(os.environ['PREFIX'])" rm ${PREFIX}/lib/libpython${VERABI}.a -if [[ ${PY_INTERP_DEBUG} == yes ]]; then - # Legacy FindPythonInterp/Libs and other tools expect unprefixed ABI paths. - rm -f ${PREFIX}/bin/python${VER} - ln -sf ${PREFIX}/bin/python${VERABI} ${PREFIX}/bin/python${VER} - ln -sf ${PREFIX}/lib/libpython${VERABI}${SHLIB_EXT} ${PREFIX}/lib/libpython${STDLIB_VER}${SHLIB_EXT} - ln -sf ${PREFIX}/include/python${VERABI} ${PREFIX}/include/python${STDLIB_VER} -fi - if [[ "$target_platform" == linux-* ]]; then rm ${PREFIX}/include/uuid.h fi diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 5cc6a87b..12330fe4 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -243,9 +243,9 @@ outputs: - bash build-and-test.sh # [unix] - popd # [unix] - pushd cmake - # FindPythonLibs does not know freethreading ABI; skip like conda-forge main. - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [unix and freethreading == "no"] - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [win and freethreading == "no"] + # Legacy FindPythonLibs lacks freethreading and pydebug ABI; skip those variants. + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [unix and freethreading == "no" and build_type == "release"] + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [win and freethreading == "no" and build_type == "release"] - popd - popd - python run_test.py From b944de40a715c5d54be497dc121874c9a668d42a Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Thu, 9 Jul 2026 10:47:10 +0000 Subject: [PATCH 10/13] Use VERABI_NO_DBG --- recipe/build_base.sh | 22 ++++++++++------------ recipe/build_static.sh | 10 ++++------ recipe/meta.yaml | 4 ++-- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/recipe/build_base.sh b/recipe/build_base.sh index bb5c2301..c172a236 100644 --- a/recipe/build_base.sh +++ b/recipe/build_base.sh @@ -73,9 +73,7 @@ fi ABIFLAGS=${DBG} VERABI=${VER}${THREAD}${DBG} -# Stdlib layout from `make install` uses VER+THREAD only; DBG affects lib names -# (libpython3.15d.so) and config-3.15d-* dirs, not the python3.15(d) directory. -STDLIB_VER=${VER}${THREAD} +VERABI_NO_DBG=${VER}${THREAD} # Make sure the "python" value in conda_build_config.yaml is up to date. test "${PY_VER}" = "${VER}" @@ -86,7 +84,7 @@ test "${PY_VER}" = "${VER}" unset _PYTHON_SYSCONFIGDATA_NAME unset _CONDA_PYTHON_SYSCONFIGDATA_NAME -# Prevent lib/python${STDLIB_VER}/_sysconfigdata_*.py from ending up with full paths to these things +# Prevent lib/python${VERABI_NO_DBG}/_sysconfigdata_*.py from ending up with full paths to these things # in _build_env because _build_env will not get found during prefix replacement, only _h_env_placeh ... AR=$(basename "${AR}") @@ -368,7 +366,7 @@ fi # build a static library with PIC objects and without LTO/PGO make -j${CPU_COUNT} -C ${_buildd_shared} \ EXTRA_CFLAGS="${EXTRA_CFLAGS}" \ - LIBRARY=libpython${STDLIB_VER}-pic.a libpython${STDLIB_VER}-pic.a + LIBRARY=libpython${VERABI_NO_DBG}-pic.a libpython${VERABI_NO_DBG}-pic.a make -C ${_buildd_static} install @@ -400,15 +398,15 @@ fi SYSCONFIG=$(find ${_buildd_static}/$(cat ${_buildd_static}/pybuilddir.txt) -name "_sysconfigdata*.py" -print0) cat ${SYSCONFIG} | ${SYS_PYTHON} "${RECIPE_DIR}"/replace-word-pairs.py \ "${_FLAGS_REPLACE[@]}" \ - > ${PREFIX}/lib/python${STDLIB_VER}/$(basename ${SYSCONFIG}) -MAKEFILE=$(find ${PREFIX}/lib/python${STDLIB_VER}/ -path "*config-*/Makefile" -print0) + > ${PREFIX}/lib/python${VERABI_NO_DBG}/$(basename ${SYSCONFIG}) +MAKEFILE=$(find ${PREFIX}/lib/python${VERABI_NO_DBG}/ -path "*config-*/Makefile" -print0) cp ${MAKEFILE} /tmp/Makefile-$$ cat /tmp/Makefile-$$ | ${SYS_PYTHON} "${RECIPE_DIR}"/replace-word-pairs.py \ "${_FLAGS_REPLACE[@]}" \ > ${MAKEFILE} # Check to see that our differences took. -# echo diff -urN ${SYSCONFIG} ${PREFIX}/lib/python${STDLIB_VER}/$(basename ${SYSCONFIG}) -# diff -urN ${SYSCONFIG} ${PREFIX}/lib/python${STDLIB_VER}/$(basename ${SYSCONFIG}) +# echo diff -urN ${SYSCONFIG} ${PREFIX}/lib/python${VERABI_NO_DBG}/$(basename ${SYSCONFIG}) +# diff -urN ${SYSCONFIG} ${PREFIX}/lib/python${VERABI_NO_DBG}/$(basename ${SYSCONFIG}) # Python installs python${VER}m and python${VER}, one as a hardlink to the other. conda-build breaks these # by copying. Since the executable may be static it may be very large so change one to be a symlink @@ -427,7 +425,7 @@ ln -s ${PREFIX}/bin/python${VER} ${PREFIX}/bin/python3.1 # Remove test data to save space # Though keep `support` as some things use that. # TODO :: Make a subpackage for this once we implement multi-level testing. -pushd ${PREFIX}/lib/python${STDLIB_VER} +pushd ${PREFIX}/lib/python${VERABI_NO_DBG} mkdir test_keep mv test/__init__.py test/support test/test_support* test/test_script_helper* test_keep/ rm -rf test */test @@ -440,7 +438,7 @@ pushd ${PREFIX} chmod +w lib/libpython${VERABI}.a ${STRIP} -S lib/libpython${VERABI}.a fi - CONFIG_LIBPYTHON=$(find lib/python${STDLIB_VER}/config-${VERABI}* -name "libpython${VERABI}.a") + CONFIG_LIBPYTHON=$(find lib/python${VERABI_NO_DBG}/config-${VERABI}* -name "libpython${VERABI}.a") if [[ -f lib/libpython${VERABI}.a ]] && [[ -f ${CONFIG_LIBPYTHON} ]]; then chmod +w ${CONFIG_LIBPYTHON} rm ${CONFIG_LIBPYTHON} @@ -466,7 +464,7 @@ esac # Copy sysconfig that gets recorded to a non-default name # using the new compilers with python will require setting _PYTHON_SYSCONFIGDATA_NAME # to the name of this file (minus the .py extension) -pushd "${PREFIX}"/lib/python${STDLIB_VER} +pushd "${PREFIX}"/lib/python${VERABI_NO_DBG} # On Python 3.5 _sysconfigdata.py was getting copied in here and compiled for some reason. # This breaks our attempt to find the right one as recorded_name. find lib-dynload -name "_sysconfigdata*.py*" -exec rm {} \; diff --git a/recipe/build_static.sh b/recipe/build_static.sh index f82959f1..19ad6688 100644 --- a/recipe/build_static.sh +++ b/recipe/build_static.sh @@ -17,9 +17,7 @@ else fi VER=${PKG_VERSION%.*} VERABI=${VER}${THREAD}${DBG} -# Stdlib layout from `make install` uses VER+THREAD only; DBG affects lib names -# (libpython3.15d.a) and config-3.15d-* dirs, not the python3.15(d) directory. -STDLIB_VER=${VER}${THREAD} +VERABI_NO_DBG=${VER}${THREAD} case "$target_platform" in linux-64) @@ -38,12 +36,12 @@ esac cp -pf ${_buildd_static}/libpython${VERABI}.a ${PREFIX}/lib/libpython${VERABI}.a if [[ ${HOST} =~ .*linux.* ]]; then - pushd ${PREFIX}/lib/python${STDLIB_VER}/config-${VERABI}-${OLD_HOST} + pushd ${PREFIX}/lib/python${VERABI_NO_DBG}/config-${VERABI}-${OLD_HOST} elif [[ ${HOST} =~ .*darwin.* ]]; then - pushd ${PREFIX}/lib/python${STDLIB_VER}/config-${VERABI}-darwin + pushd ${PREFIX}/lib/python${VERABI_NO_DBG}/config-${VERABI}-darwin fi ln -s ../../libpython${VERABI}.a libpython${VERABI}.a popd # If the LTO info in the normal lib is problematic (using different compilers for example # we also provide a 'nolto' version). -cp -pf ${_buildd_shared}/libpython${STDLIB_VER}-pic.a ${PREFIX}/lib/libpython${STDLIB_VER}.nolto.a +cp -pf ${_buildd_shared}/libpython${VERABI_NO_DBG}-pic.a ${PREFIX}/lib/libpython${VERABI_NO_DBG}.nolto.a diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 12330fe4..dd28c968 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -368,5 +368,5 @@ extra: - scopatz - katietz - xhochy - only-lint: - - missing_home + skip-lints: + - license_file_overspecified From 43cdcf6d680f967ebfa3f7608f197d925dac3510 Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Thu, 9 Jul 2026 11:03:04 +0000 Subject: [PATCH 11/13] Align build scripts and tests with conda-forge patterns. Use PY_FREETHREADING, PY_INTERP_DEBUG for optimization, CF debug symlinks and BUILD_DETAILS copy; drop linkage/OLD_HOST/compiler_compat divergences. Restore skip-lints, CF cmake gating, and remove run_test_py315.py. Co-authored-by: Cursor --- recipe/build_base.bat | 2 +- recipe/build_base.sh | 81 ++++----- recipe/build_static.sh | 24 +-- recipe/meta.yaml | 33 +--- recipe/run_test_py315.py | 366 --------------------------------------- 5 files changed, 46 insertions(+), 460 deletions(-) delete mode 100644 recipe/run_test_py315.py diff --git a/recipe/build_base.bat b/recipe/build_base.bat index a1eb1755..8dabada3 100644 --- a/recipe/build_base.bat +++ b/recipe/build_base.bat @@ -44,7 +44,7 @@ if "%DEBUG_C%"=="yes" ( set PGO=--pgo ) -if "%PY_GIL_DISABLED%" == "yes" ( +if "%PY_FREETHREADING%" == "yes" ( set "FREETHREADING=--disable-gil" set "THREAD=t" set "EXE_T=%VER%t" diff --git a/recipe/build_base.sh b/recipe/build_base.sh index c172a236..5018e48d 100644 --- a/recipe/build_base.sh +++ b/recipe/build_base.sh @@ -33,28 +33,14 @@ _buildd_shared=build-shared _ENABLE_SHARED=--enable-shared # We *still* build a shared lib here for non-static embedded use cases _DISABLE_SHARED=--disable-shared -# Hack to allow easily comparing static vs shared interpreter performance -# .. hack because we just build it shared in both the build-static and -# build-shared directories. -# Yes this hack is a bit confusing, sorry about that. -if [[ ${PY_INTERP_LINKAGE_NATURE} == shared ]]; then - _DISABLE_SHARED=--enable-shared - _ENABLE_SHARED=--enable-shared -fi # For debugging builds, set this to no to disable profile-guided optimization -if [[ ${DEBUG_C} == yes ]]; then +if [[ ${PY_INTERP_DEBUG} == yes ]]; then _OPTIMIZED=no else _OPTIMIZED=yes fi -if [[ ${target_platform} == linux-ppc64le ]]; then - _OPTIMIZED=no - # ppc64le cdt need to be rebuilt with files in powerpc64le-conda-linux-gnu instead of powerpc64le-conda_cos7-linux-gnu. In the mean time: - cp --force --archive --update --link $BUILD_PREFIX/powerpc64le-conda_cos7-linux-gnu/. $BUILD_PREFIX/powerpc64le-conda-linux-gnu -fi - declare -a _dbg_opts if [[ ${PY_INTERP_DEBUG} == yes ]]; then # This Python will not be usable with non-debug Python modules. @@ -64,14 +50,14 @@ else DBG= fi -if [[ ${PY_GIL_DISABLED} == yes ]]; then +if [[ ${PY_FREETHREADING} == yes ]]; then # This Python will not be usable with non-free threading Python modules. THREAD=t else THREAD= fi -ABIFLAGS=${DBG} +ABIFLAGS=${DBG}${THREAD} VERABI=${VER}${THREAD}${DBG} VERABI_NO_DBG=${VER}${THREAD} @@ -112,6 +98,12 @@ if [[ ${_OPTIMIZED} = yes ]]; then CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-O2/-O3/g") fi +if [[ ${PY_INTERP_DEBUG} == yes ]]; then + CPPFLAGS=$(echo "${CPPFLAGS}" | sed "s/-O2/-O0/g") + CFLAGS=$(echo "${CFLAGS}" | sed "s/-O2/-O0/g") + CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-O2/-O0/g") +fi + if [[ "$target_platform" == linux-* ]]; then cp ${PREFIX}/include/uuid/uuid.h ${PREFIX}/include/uuid.h fi @@ -259,7 +251,7 @@ _common_configure_args+=("--with-tcltk-libs=-L${PREFIX}/lib -ltcl${TCLTK_VER} -l _common_configure_args+=(--with-platlibdir=lib) _common_configure_args+=(--with-system-libmpdec=yes) -if [[ "${PY_INTERP_DEBUG}" == "yes" || "${target_platform}" != *"64" || ${PY_GIL_DISABLED} == yes ]]; then +if [[ "${PY_INTERP_DEBUG}" == "yes" || "${target_platform}" != *"-64" || ${PY_FREETHREADING} == yes ]]; then _common_configure_args+=(--enable-experimental-jit=no) else _common_configure_args+=(--enable-experimental-jit=yes-off) @@ -270,7 +262,7 @@ if [[ "${target_platform}" == osx-* ]]; then _common_configure_args+=(--with-tail-call-interp) fi -if [[ ${PY_GIL_DISABLED} == yes ]]; then +if [[ ${PY_FREETHREADING} == yes ]]; then _common_configure_args+=(--disable-gil) fi @@ -399,6 +391,8 @@ SYSCONFIG=$(find ${_buildd_static}/$(cat ${_buildd_static}/pybuilddir.txt) -name cat ${SYSCONFIG} | ${SYS_PYTHON} "${RECIPE_DIR}"/replace-word-pairs.py \ "${_FLAGS_REPLACE[@]}" \ > ${PREFIX}/lib/python${VERABI_NO_DBG}/$(basename ${SYSCONFIG}) +BUILD_DETAILS=${_buildd_shared}/$(cat ${_buildd_shared}/pybuilddir.txt)/build-details.json +cp ${BUILD_DETAILS} ${PREFIX}/lib/python${VERABI_NO_DBG}/ MAKEFILE=$(find ${PREFIX}/lib/python${VERABI_NO_DBG}/ -path "*config-*/Makefile" -print0) cp ${MAKEFILE} /tmp/Makefile-$$ cat /tmp/Makefile-$$ | ${SYS_PYTHON} "${RECIPE_DIR}"/replace-word-pairs.py \ @@ -445,22 +439,6 @@ pushd ${PREFIX} fi popd -# OLD_HOST is with CentOS version in them. When building this recipe -# with the compilers from conda-forge OLD_HOST != HOST, but when building -# with the compilers from defaults OLD_HOST == HOST. Both cases are handled in the -# code below -case "$target_platform" in - linux-64) - OLD_HOST=$(echo ${HOST} | sed -e 's/-conda-/-conda_cos6-/g') - ;; - linux-*) - OLD_HOST=$(echo ${HOST} | sed -e 's/-conda-/-conda_cos7-/g') - ;; - *) - OLD_HOST=$HOST - ;; -esac - # Copy sysconfig that gets recorded to a non-default name # using the new compilers with python will require setting _PYTHON_SYSCONFIGDATA_NAME # to the name of this file (minus the .py extension) @@ -479,9 +457,9 @@ pushd "${PREFIX}"/lib/python${VERABI_NO_DBG} # Append the conda-forge zoneinfo to the end sed -i.bak "s@zoneinfo'@zoneinfo:$PREFIX/share/tzinfo'@g" sysconfigfile # Remove osx sysroot as it depends on the build machine - sed -i.bak "s@-isysroot @@g" sysconfigfile - # make sure $CONDA_BUILD_SYSROOT is not empty ... - if [[ ${HOST} =~ .*darwin.* ]] && [[ -n ${CONDA_BUILD_SYSROOT} ]]; then + # be sure CONDA_BUILD_SYSROOT has value, as other we will remove here instead spaces + if [[ "${target_platform}" == osx-* ]] && [[ -n ${CONDA_BUILD_SYSROOT} ]]; then + sed -i.bak "s@-isysroot @@g" sysconfigfile sed -i.bak "s@$CONDA_BUILD_SYSROOT @@g" sysconfigfile fi # Remove unfilled config option @@ -491,16 +469,12 @@ pushd "${PREFIX}"/lib/python${VERABI_NO_DBG} sed -i.bak "s/'GNULD': 'yes'/'GNULD': 'no'/g" sysconfigfile cp sysconfigfile ${our_compilers_name} - sed -i.bak "s@${HOST}@${OLD_HOST}@g" sysconfigfile - old_compiler_name=_sysconfigdata_$(echo ${OLD_HOST} | sed -e 's/[.-]/_/g').py - cp sysconfigfile ${old_compiler_name} - # For system gcc remove the triple - sed -i.bak "s@$OLD_HOST-c++@g++@g" sysconfigfile - sed -i.bak "s@$OLD_HOST-@@g" sysconfigfile + sed -i.bak "s@$HOST-c++@g++@g" sysconfigfile + sed -i.bak "s@$HOST-@@g" sysconfigfile if [[ "$target_platform" == linux* ]]; then # For linux, make sure the system gcc uses our linker - sed -i.bak "s@-pthread@-pthread -B $PREFIX/compiler_compat@g" sysconfigfile + sed -i.bak "s@-pthread@-pthread -B $PREFIX/share/python_compiler_compat@g" sysconfigfile fi # Don't set -march and -mtune for system gcc sed -i.bak "s@-march=[^( |\\\"|\\\')]*@@g" sysconfigfile @@ -525,15 +499,22 @@ pushd "${PREFIX}"/lib/python${VERABI_NO_DBG} popd if [[ ${HOST} =~ .*linux.* ]]; then - mkdir -p ${PREFIX}/compiler_compat - ln -s ${PREFIX}/bin/${HOST}-ld ${PREFIX}/compiler_compat/ld - echo "Files in this folder are to enhance backwards compatibility of anaconda software with older compilers." > ${PREFIX}/compiler_compat/README - echo "See: https://github.com/conda/conda/issues/6030 for more information." >> ${PREFIX}/compiler_compat/README + mkdir -p ${PREFIX}/share/python_compiler_compat + ln -s ${PREFIX}/bin/${HOST}-ld ${PREFIX}/share/python_compiler_compat/ld + echo "Files in this folder are to enhance backwards compatibility of anaconda software with older compilers." > ${PREFIX}/share/python_compiler_compat/README + echo "See: https://github.com/conda/conda/issues/6030 for more information." >> ${PREFIX}/share/python_compiler_compat/README fi python -c "import compileall,os;compileall.compile_dir(os.environ['PREFIX'])" rm ${PREFIX}/lib/libpython${VERABI}.a +if [[ ${PY_INTERP_DEBUG} == yes ]]; then + rm ${PREFIX}/bin/python${VER} + ln -s ${PREFIX}/bin/python${VERABI} ${PREFIX}/bin/python${VER} + ln -s ${PREFIX}/lib/libpython${VERABI}${SHLIB_EXT} ${PREFIX}/lib/libpython${VERABI_NO_DBG}${SHLIB_EXT} + ln -s ${PREFIX}/include/python${VERABI} ${PREFIX}/include/python${VER} +fi + if [[ "$target_platform" == linux-* ]]; then rm ${PREFIX}/include/uuid.h fi @@ -551,7 +532,7 @@ fi # /lib/python3.13t/site-packages. # Note that these directories are not added to sys.path if they do not exist. SP_DIR="${PREFIX}/lib/python${PY_VER}${THREAD}/site-packages" -if [[ ${PY_GIL_DISABLED} == yes ]]; then +if [[ ${PY_FREETHREADING} == yes ]]; then echo "${PREFIX}/lib/python${PY_VER}/site-packages" >> $SP_DIR/conda-site.pth fi # Workaround for https://github.com/conda/conda/issues/10969 diff --git a/recipe/build_static.sh b/recipe/build_static.sh index 19ad6688..f7724373 100644 --- a/recipe/build_static.sh +++ b/recipe/build_static.sh @@ -1,8 +1,6 @@ #!/bin/bash set -ex -cd ${SRC_DIR} - _buildd_static=build-static _buildd_shared=build-shared if [[ ${PY_INTERP_DEBUG} == yes ]]; then @@ -10,33 +8,21 @@ if [[ ${PY_INTERP_DEBUG} == yes ]]; then else DBG= fi -if [[ ${PY_GIL_DISABLED} == yes ]]; then +if [[ ${PY_FREETHREADING} == yes ]]; then + # This Python will not be usable with non-free threading Python modules. THREAD=t else THREAD= fi + VER=${PKG_VERSION%.*} +ABIFLAGS=${DBG}${THREAD} VERABI=${VER}${THREAD}${DBG} VERABI_NO_DBG=${VER}${THREAD} -case "$target_platform" in - linux-64) - OLD_HOST=$(echo ${HOST} | sed -e 's/-conda_cos6//g') - OLD_HOST=$(echo ${OLD_HOST} | sed -e 's/-conda_cos7//g') - OLD_HOST=$(echo ${OLD_HOST} | sed -e 's/-conda//g') - ;; - linux-*) - OLD_HOST=$(echo ${HOST} | sed -e 's/-conda_cos7//g') - OLD_HOST=$(echo ${OLD_HOST} | sed -e 's/-conda//g') - ;; - *) - OLD_HOST=$HOST - ;; -esac - cp -pf ${_buildd_static}/libpython${VERABI}.a ${PREFIX}/lib/libpython${VERABI}.a if [[ ${HOST} =~ .*linux.* ]]; then - pushd ${PREFIX}/lib/python${VERABI_NO_DBG}/config-${VERABI}-${OLD_HOST} + pushd ${PREFIX}/lib/python${VERABI_NO_DBG}/config-${VERABI}-${HOST/-conda/} elif [[ ${HOST} =~ .*darwin.* ]]; then pushd ${PREFIX}/lib/python${VERABI_NO_DBG}/config-${VERABI}-darwin fi diff --git a/recipe/meta.yaml b/recipe/meta.yaml index dd28c968..4f149790 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -18,16 +18,6 @@ {% set build_number = build_number + (0 if freethreading == "yes" else 100) %} {% set py_interp_debug = "yes" if build_type == "debug" else "no" %} {% set debug = "_debug" if build_type == "debug" else "" %} -{% set py_gil_disabled = "yes" if freethreading == "yes" else "no" %} -# Divergence from conda-forge: CF dropped the interpreter-linkage knob entirely -# and drives freethreading via PY_FREETHREADING. Anaconda's build scripts still -# key off PY_INTERP_LINKAGE_NATURE (build_base.sh/build_static.sh) and PY_GIL_DISABLED, -# so we keep them. We no longer read them from os.environ (that broke the linter); -# Anaconda only ships a statically-linkable interpreter, so these are constants: -# - linkage_nature: build-string suffix; empty because "static" is the only value. -# - linkage_nature_env: value exported as PY_INTERP_LINKAGE_NATURE for the scripts. -{% set linkage_nature = "" %} -{% set linkage_nature_env = 'static' %} {% set site_packages_path_unix = ("lib/python" + ver2 + "t/site-packages" if freethreading == "yes" else "lib/python" + ver2 + "/site-packages") %} {% set site_packages_path = "Lib/site-packages" if win else site_packages_path_unix %} @@ -108,7 +98,7 @@ outputs: # - lib/libpython3.dylib # [osx] # match python.org compiler standard (vc14+ only for 3.15) skip: true # [win and vc < 14] - string: {{ dev_ }}h{{ PKG_HASH }}_{{ PKG_BUILDNUM }}{{ linkage_nature }}{{ debug }}_{{ abi_tag }} + string: {{ dev_ }}h{{ PKG_HASH }}_{{ PKG_BUILDNUM }}{{ debug }}_{{ abi_tag }} python_site_packages_path: {{ site_packages_path }} track_features: # [freethreading == "yes"] - free-threading # [freethreading == "yes"] @@ -119,10 +109,8 @@ outputs: - python_abi {{ ver2 }}.* *_{{ abi_tag }} - python {{ ver2 }}.* *_debug_{{ abi_tag }} # [build_type == "debug"] script_env: - - PY_INTERP_LINKAGE_NATURE={{ linkage_nature_env }} - PY_INTERP_DEBUG={{ py_interp_debug }} - - PY_GIL_DISABLED={{ py_gil_disabled }} - - CONDA_FORGE=no + - PY_FREETHREADING={{ freethreading }} missing_dso_whitelist: # [win or linux] - '**/MSVCR71.dll' # [win] - '**/MSVCR80.dll' # [win] @@ -187,7 +175,7 @@ outputs: test: requires: - ripgrep - - cmake-no-system + - cmake - make # [unix] - ninja-base - {{ stdlib('c') }} @@ -199,7 +187,6 @@ outputs: - tests/cython/* - tests/prefix-replacement/* - run_test.py - - run_test_py315.py commands: - echo on # [win] - set # [win] @@ -243,15 +230,13 @@ outputs: - bash build-and-test.sh # [unix] - popd # [unix] - pushd cmake - # Legacy FindPythonLibs lacks freethreading and pydebug ABI; skip those variants. - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [unix and freethreading == "no" and build_type == "release"] - - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . # [win and freethreading == "no" and build_type == "release"] +{% if freethreading == "no" %} + # TODO: use the new FindPython3 which supports freethreading + - cmake -GNinja -DPY_VER={{ version }} --debug-find --trace --debug-output --debug-trycompile . +{% endif %} - popd - popd - python run_test.py - - export RUN_TEST_REQUIRE_315=1 && python run_test_py315.py # [unix] - - set RUN_TEST_REQUIRE_315=1 && python run_test_py315.py # [win] - - rm -f default.profraw # [osx] - test ! -f default.profraw # [osx] # Test workaround for https://github.com/conda/conda/issues/10969 - python3.1 --version # [unix] @@ -269,10 +254,10 @@ outputs: activate_in_script: true ignore_run_exports: - python_abi - string: h{{ PKG_HASH }}_{{ PKG_BUILDNUM }}{{ linkage_nature }}{{ debug }}_{{ abi_tag }} + string: h{{ PKG_HASH }}_{{ PKG_BUILDNUM }}{{ debug }}_{{ abi_tag }} script_env: - PY_INTERP_DEBUG={{ py_interp_debug }} - - PY_GIL_DISABLED={{ py_gil_disabled }} + - PY_FREETHREADING={{ freethreading }} requirements: build: - {{ stdlib('c') }} diff --git a/recipe/run_test_py315.py b/recipe/run_test_py315.py deleted file mode 100644 index 63a29749..00000000 --- a/recipe/run_test_py315.py +++ /dev/null @@ -1,366 +0,0 @@ -# Python 3.15 conda-build feature test — verified against 3.15.0b3 (2026 series). -# -# Design contract: -# - HARD checks (interpreter identity, core stdlib, ABI/build sanity) -> failure -# fails the conda-build test phase (non-zero exit). -# - SOFT checks (new PEP feature probes) -> reported as PASS/SKIP, never fail -# the build UNLESS one of: -# * RUN_TEST_STRICT=1 in the environment (manual promote SOFT->HARD), or -# * we are running on rc1 or later (auto-promote — PEP feature spellings -# are frozen at the RC boundary per PEP 790, so a SKIP after that date -# means we're building against a stale probe). -# -# Sources: -# - Python 3.15 What's New: https://docs.python.org/3.15/whatsnew/3.15.html -# - PEP 790 (release schedule): https://peps.python.org/pep-0790/ — RC1 2026-08-04 -# - PEP-specific references embedded in each probe below. - -from __future__ import annotations - -import builtins -import importlib.util -import os -import platform -import site -import sys -import sysconfig -from dataclasses import dataclass - -PASS, SKIP, FAIL = "PASS", "SKIP", "FAIL" -TARGET_VERSION = (3, 15) -IS_TARGET = sys.version_info[:2] == TARGET_VERSION - -# Manual override for local development / CI hardening. -STRICT_ENV = os.environ.get("RUN_TEST_STRICT", "0") == "1" - -# Auto-promote at rc1 — after 2026-08-04 the PEP APIs are frozen and any SKIP -# from a feature probe means the probe is stale, not that the feature is absent. -# Rationale: probes are advisory during beta only. -AUTO_STRICT = IS_TARGET and sys.version_info.releaselevel != "beta" -STRICT = STRICT_ENV or AUTO_STRICT - -# For the dedicated python-3.15 feedstock, set RUN_TEST_REQUIRE_315=1 so a -# non-3.15 interpreter is a HARD failure instead of a skip (catches bad matrices). -REQUIRE_TARGET = os.environ.get("RUN_TEST_REQUIRE_315", "0") == "1" - - -@dataclass -class Result: - name: str - status: str - detail: str = "" - hard: bool = False # if True, a FAIL aborts the test phase - - -_results: list[Result] = [] - - -def record(name: str, status: str, detail: str = "", *, hard: bool = False) -> None: - _results.append(Result(name, status, detail, hard)) - - -def _spec_exists(modname: str) -> bool: - # `find_spec` is safer than `import` — it does not execute module top-level - # code, which matters for probes on packages that may hold heavy imports. - try: - return importlib.util.find_spec(modname) is not None - except (ImportError, ValueError): - return False - - -def _compiles(src: str) -> bool: - # Probe a syntax-level PEP without executing it. - try: - compile(src, "", "exec") - return True - except SyntaxError: - return False - - -# --------------------------------------------------------------------------- # -# HARD checks — must hold for any correct 3.15 build -# --------------------------------------------------------------------------- # - -def check_interpreter_identity() -> None: - ver = (f"{platform.python_version()} " - f"({sys.version_info.releaselevel} {sys.version_info.serial})") - if IS_TARGET: - record("interpreter 3.15", PASS, ver, hard=True) - elif REQUIRE_TARGET: - record("interpreter 3.15", FAIL, - f"expected 3.15, got {sys.version_info[0]}.{sys.version_info[1]}", - hard=True) - else: - record("interpreter 3.15", SKIP, f"not target ({ver}) -> probes skipped") - - -def check_core_stdlib() -> None: - # C-extension stdlib modules are the usual silent-failure surface: a build - # that lacks system libs (ssl/lzma/sqlite/...) imports the pure parts fine - # but breaks at runtime. Importing them here turns that into a build failure. - required = ["ssl", "ctypes", "sqlite3", "lzma", "bz2", "zlib", - "hashlib", "decimal", "readline", "curses"] - missing = [] - for mod in required: - # readline/curses are absent on Windows by design. - if os.name == "nt" and mod in {"readline", "curses"}: - continue - try: - __import__(mod) - except ImportError as exc: - missing.append(f"{mod} ({exc})") - if missing: - record("core C-extension stdlib", FAIL, "; ".join(missing), hard=True) - else: - record("core C-extension stdlib", PASS, "all importable", hard=True) - - -def check_build_sanity() -> None: - # Cross-check the build prefix matches the test env's interpreter. - prefix_ok = sys.prefix and os.path.isdir(sys.prefix) - abiflags = getattr(sys, "abiflags", "") - record( - "build sanity", - PASS if prefix_ok else FAIL, - f"prefix={sys.prefix} abiflags='{abiflags}'", - hard=True, - ) - - -# --------------------------------------------------------------------------- # -# SOFT probes — 3.15 new features (verified against b3; frozen at rc1) -# --------------------------------------------------------------------------- # - -def probe_pep686_utf8_default() -> None: - # PEP 686: UTF-8 is the unconditional default in 3.15 (independent of locale). - # Behavioral change is most visible on Windows; on POSIX with UTF-8 locales - # it was already effectively UTF-8. sys.flags.utf8_mode == 1 by default now. - mode = getattr(sys.flags, "utf8_mode", None) - if mode == 1: - record("PEP 686 UTF-8 default", PASS, "sys.flags.utf8_mode=1") - else: - record("PEP 686 UTF-8 default", SKIP, f"utf8_mode={mode}") - - -def probe_pep810_lazy_imports() -> None: - # PEP 810: `lazy` soft keyword — module-scope only. Syntax alone enables it; - # no __future__ import is required. Belt-and-suspenders: compile probe AND - # a runtime attribute (sys.set_lazy_imports) that also lands in 3.15. - compile_ok = _compiles("lazy import os\n") - runtime_ok = hasattr(sys, "set_lazy_imports") - if compile_ok and runtime_ok: - record("PEP 810 lazy imports", PASS, "syntax + sys.set_lazy_imports") - elif compile_ok or runtime_ok: - record("PEP 810 lazy imports", PASS, - f"partial: compile={compile_ok} runtime={runtime_ok}") - else: - record("PEP 810 lazy imports", SKIP, "neither syntax nor runtime API") - - -def probe_pep798_unpack_comprehension() -> None: - # PEP 798: unpacking in comprehensions. NOTE: unbracketed genexp-call form - # `f(*x for x in xs)` still raises SyntaxError — do not probe that here. - src = "result = [*xs for xs in [[1, 2], [3]]]" - if not _compiles(src): - record("PEP 798 unpack-in-comprehension", SKIP, "syntax not accepted") - return - ns: dict = {} - try: - exec(src, ns) - ok = ns.get("result") == [1, 2, 3] - record("PEP 798 unpack-in-comprehension", - PASS if ok else FAIL, f"result={ns.get('result')}") - except Exception as exc: # noqa: BLE001 - feature probe - record("PEP 798 unpack-in-comprehension", FAIL, repr(exc)) - - -def probe_pep814_frozendict() -> None: - # PEP 814: `frozendict` is a true BUILTIN (not types.frozendict). - # Also NOT a subclass of dict — inherits directly from object; use - # isinstance(x, (dict, frozendict)) or collections.abc.Mapping in consumers. - if not hasattr(builtins, "frozendict"): - record("PEP 814 frozendict", SKIP, "builtin absent") - return - fd = builtins.frozendict({"a": 1}) - # Sanity: constructs, hashable, not a dict subclass. - ok = hash(fd) is not None and not isinstance(fd, dict) - record("PEP 814 frozendict", - PASS if ok else FAIL, - f"hashable={ok}, not dict-subclass={not isinstance(fd, dict)}") - - -def probe_pep661_sentinel() -> None: - # PEP 661: `sentinel` is a BUILTIN lowercase callable — sentinel("NAME"). - # NOT a module import. The typing_extensions backport is capital-S Sentinel, - # but the 3.15 builtin is lowercase. - if not hasattr(builtins, "sentinel"): - record("PEP 661 sentinel", SKIP, "builtin absent") - return - try: - missing = builtins.sentinel("MISSING") - # Sanity: repr is stable and identity-comparable to itself. - ok = missing is missing and "MISSING" in repr(missing) - record("PEP 661 sentinel", PASS if ok else FAIL, repr(missing)) - except Exception as exc: # noqa: BLE001 - feature probe - record("PEP 661 sentinel", FAIL, repr(exc)) - - -def probe_pep799_profiling() -> None: - # PEP 799: dedicated `profiling` package. Tachyon (the sampling profiler) - # IS `profiling.sampling`. - # `profiling.tracing` is the relocated cProfile (cProfile remains as alias). - if not _spec_exists("profiling"): - record("PEP 799 profiling/Tachyon", SKIP, "package absent") - return - subs = [s for s in ("profiling.sampling", "profiling.tracing") - if _spec_exists(s)] - if subs: - record("PEP 799 profiling/Tachyon", PASS, ", ".join(subs)) - else: - record("PEP 799 profiling/Tachyon", FAIL, - "package present but sampling/tracing missing") - - -def probe_typing_features() -> None: - # PEP 747 TypeForm — annotation for values that are themselves type exprs. - import typing - record("PEP 747 TypeForm", - PASS if hasattr(typing, "TypeForm") else SKIP, - "typing.TypeForm") - - # PEP 728 TypedDict extra_items: functional keyword form. Introspect via - # __extra_items__ (default: typing.NoExtraItems sentinel, not None). - # Passing both closed= and extra_items= raises TypeError at runtime. - try: - td = typing.TypedDict("X", {"a": int}, extra_items=int) # type: ignore[call-arg] - no_extra = getattr(typing, "NoExtraItems", None) - extras_ok = getattr(td, "__extra_items__", no_extra) is int - default_ok = no_extra is not None - record("PEP 728 TypedDict extra_items", - PASS if (extras_ok and default_ok) else FAIL, - f"extra_items=int accepted, NoExtraItems={'ok' if default_ok else 'missing'}") - except TypeError as exc: - record("PEP 728 TypedDict extra_items", SKIP, f"rejected: {exc}") - - # PEP 800: `@typing.disjoint_base` — a CLASS DECORATOR, not a flag/attr. - # Attribute-existence is a valid capability probe; note in detail that - # semantically it's a decorator so downstream code should apply it, not read it. - has_db = hasattr(typing, "disjoint_base") - record("PEP 800 disjoint bases", - PASS if has_db else SKIP, - "typing.disjoint_base decorator" if has_db else "absent") - - -def probe_pep831_frame_pointers() -> None: - # PEP 831: -fno-omit-frame-pointer enabled by default on supporting platforms. - # No dedicated sysconfig variable — grep the compiler flags. Reflects build - # config, not runtime state. Opt-out is --without-frame-pointers at configure. - cflags = sysconfig.get_config_var("CFLAGS") or "" - if "-fno-omit-frame-pointer" in cflags: - record("PEP 831 frame pointers", PASS, "-fno-omit-frame-pointer in CFLAGS") - else: - # On Windows and some cross-compiles the flag simply doesn't apply. - record("PEP 831 frame pointers", SKIP, "not present in CFLAGS") - - -def probe_pep829_startup_config() -> None: - # PEP 829: `.start` files + site.StartupState. `.start` entries are - # `pkg.mod:callable` specs resolved by pkgutil.resolve_name and invoked - # at interpreter startup by site. Only Python-visible probe is StartupState. - record("PEP 829 startup config", - PASS if hasattr(site, "StartupState") else SKIP, - "site.StartupState") - - -def probe_build_variant_info() -> None: - # Informational: free-threading (PEP 703/793) and JIT state for this build. - # NOTE: Py_GIL_DISABLED is DEFINED AS 0 on non-FT Windows builds — using - # `is not None` would incorrectly report FT on those. Coerce to bool. - gil_disabled = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) - is_gil = getattr(sys, "_is_gil_enabled", lambda: None)() - record("free-threaded build (info)", SKIP, - f"Py_GIL_DISABLED={gil_disabled} gil_enabled={is_gil}") - - # JIT: sys._jit exposes THREE methods. is_enabled() is the most meaningful - # ("JIT actually on"); is_active() is documented as unreliable outside JIT - # self-tests. The JIT is OFF BY DEFAULT — is_available() can be True while - # is_enabled() is False. Enable at runtime with PYTHON_JIT=1 if compiled in. - jit = getattr(sys, "_jit", None) - if jit is not None: - avail = getattr(jit, "is_available", lambda: "?")() - enabled = getattr(jit, "is_enabled", lambda: "?")() - active = getattr(jit, "is_active", lambda: "?")() - record("JIT (info)", SKIP, - f"available={avail} enabled={enabled} active={active}") - else: - record("JIT (info)", SKIP, "sys._jit absent") - - -def probe_cabi_notes() -> None: - # PEP 782 PyBytesWriter and PEP 803/820/793 free-threaded stable ABI (abi3t) - # are C-API surfaces; they cannot be exercised from a pure-Python test - # without compiling an extension. Recorded as N/A for visibility. - record("PEP 782 PyBytesWriter (C-API)", SKIP, "needs C ext to test") - record("PEP 803/820/793 abi3t (C-API)", SKIP, "needs C ext to test") - - -# --------------------------------------------------------------------------- # - -def main() -> int: - # Universal sanity: runs on every Python version. - check_interpreter_identity() - check_core_stdlib() - check_build_sanity() - - # 3.15-only: PEP feature probes are skipped on any other version. - if IS_TARGET: - probe_pep686_utf8_default() - probe_pep810_lazy_imports() - probe_pep798_unpack_comprehension() - probe_pep814_frozendict() - probe_pep661_sentinel() - probe_pep799_profiling() - probe_typing_features() - probe_pep831_frame_pointers() - probe_pep829_startup_config() - probe_build_variant_info() - probe_cabi_notes() - else: - record("3.15 feature probes", SKIP, - f"interpreter {sys.version_info[0]}.{sys.version_info[1]} " - "is not 3.15") - - width = max(len(r.name) for r in _results) - print("\n=== Python 3.15 conda-build feature test ===") - print(f"interpreter: {sys.executable}") - print(f"releaselevel: {sys.version_info.releaselevel}") - print(f"strict mode: {STRICT} " - f"(env={STRICT_ENV}, auto={AUTO_STRICT})\n") - for r in _results: - tag = "[HARD]" if r.hard else "[soft]" - print(f"{tag} {r.status:<4} {r.name.ljust(width)} {r.detail}") - - hard_failed = [r for r in _results if r.hard and r.status == FAIL] - soft_failed = [r for r in _results if not r.hard and r.status == FAIL] - soft_skipped = [r for r in _results if not r.hard and r.status == SKIP] - - print() - print(f"summary: {sum(r.status == PASS for r in _results)} pass / " - f"{sum(r.status == SKIP for r in _results)} skip / " - f"{sum(r.status == FAIL for r in _results)} fail") - - if hard_failed: - return 1 - if STRICT and soft_failed: - return 2 - # In auto-strict (rc1+), a SKIP on a probe that should have landed is also - # a signal — but we deliberately don't fail on skip to keep the surface - # small. Emit a warning so it shows up in CI logs. - if AUTO_STRICT and soft_skipped: - print(f"WARNING: {len(soft_skipped)} SOFT probes SKIPPED at " - f"releaselevel={sys.version_info.releaselevel}; review probes.") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) From 24d94f5cd05ef573ddb05923329ec6d325e5198e Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Thu, 9 Jul 2026 14:22:06 +0000 Subject: [PATCH 12/13] Fix CI failures from graph 13f6ff7c: JIT skip, win nuget, CF nits. Skip python-jit where build_base.sh disables experimental JIT; set PYTHON_FOR_BUILD on Windows and patch find_python.bat after aka.ms nugetclidl broke; align track_features and ninja test dep with CF. Co-authored-by: Cursor --- recipe/build_base.bat | 3 ++ recipe/meta.yaml | 7 ++-- ...-Win32-fix-broken-nuget-download-URL.patch | 35 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 recipe/patches/0027-Win32-fix-broken-nuget-download-URL.patch diff --git a/recipe/build_base.bat b/recipe/build_base.bat index 8dabada3..41642d32 100644 --- a/recipe/build_base.bat +++ b/recipe/build_base.bat @@ -73,6 +73,9 @@ set PGO= :: lib names (tcl86t.lib/tk86t.lib) from the major.minor of these props. set TCLTK_MSBUILD_PROPS="/p:TclVersion=%tk%" "/p:TkVersion=%tk%" +:: PCbuild code-gen needs a host Python; prefer the conda build env over nuget. +if not defined PYTHON_FOR_BUILD set PYTHON_FOR_BUILD=%BUILD_PREFIX%\python.exe + cd PCbuild :: Twice because: diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 4f149790..7b163db9 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -60,6 +60,7 @@ source: - patches/0024-Unvendor-zlib-ng.patch - patches/0025-Unvendor-zstd.patch - patches/0026-xxlimited-freethreading-stable-abi-link.patch # [win and freethreading == "yes"] + - patches/0027-Win32-fix-broken-nuget-download-URL.patch # [win] build: number: {{ build_number }} @@ -101,7 +102,7 @@ outputs: string: {{ dev_ }}h{{ PKG_HASH }}_{{ PKG_BUILDNUM }}{{ debug }}_{{ abi_tag }} python_site_packages_path: {{ site_packages_path }} track_features: # [freethreading == "yes"] - - free-threading # [freethreading == "yes"] + - py_freethreading # [freethreading == "yes"] run_exports: noarch: - python @@ -177,7 +178,7 @@ outputs: - ripgrep - cmake - make # [unix] - - ninja-base + - ninja - {{ stdlib('c') }} - {{ compiler('c') }} # Tried to use enable_language(C) to avoid needing this. It does not work. @@ -312,6 +313,8 @@ outputs: build: skip: true # [freethreading == "yes"] skip: true # [build_type == "debug"] + # Mirror build_base.sh JIT gate: yes-off only when not debug, not freethreading, and target_platform matches *-64 + skip: true # [not linux64 and not win64 and not (osx and x86_64)] noarch: generic requirements: - python {{ version }}{{ dev }} diff --git a/recipe/patches/0027-Win32-fix-broken-nuget-download-URL.patch b/recipe/patches/0027-Win32-fix-broken-nuget-download-URL.patch new file mode 100644 index 00000000..303b612e --- /dev/null +++ b/recipe/patches/0027-Win32-fix-broken-nuget-download-URL.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Serhii Kupriienko +Date: Thu, 9 Jul 2026 00:00:00 +0000 +Subject: [PATCH 27/27] Win32: fix broken nuget download URL + +https://aka.ms/nugetclidl now redirects to Bing instead of nuget.exe. +Use dist.nuget.org and ensure nuget.org is configured before installing +pythonx86 (cpython gh-152919). +--- + PCbuild/find_python.bat | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/PCbuild/find_python.bat b/PCbuild/find_python.bat +index 1111111..2222222 100644 +--- a/PCbuild/find_python.bat ++++ b/PCbuild/find_python.bat +@@ -55,7 +55,7 @@ + @set _Py_HOST_PYTHON=%HOST_PYTHON% + @if "%_Py_HOST_PYTHON%"=="" set _Py_HOST_PYTHON=py + @if "%_Py_NUGET%"=="" (set _Py_NUGET=%_Py_EXTERNALS_DIR%\nuget.exe) +-@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://aka.ms/nugetclidl) ++@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe) + @if NOT exist "%_Py_NUGET%" ( + @if not "%_Py_Quiet%"=="1" @echo Downloading nuget... + @rem NB: Must use single quotes around NUGET here, NOT double! +@@ -66,6 +66,9 @@ + @%_Py_HOST_PYTHON% -E "%_Py_D%\urlretrieve.py" "%_Py_NUGET_URL%" "%_Py_NUGET%" + ) + ) ++ ++@rem ensure nuget.org source exist ++@"%_Py_NUGET%" sources add -Name nuget.org -Source https://api.nuget.org/v3/index.json >nul 2>nul + + @if not "%_Py_Quiet%"=="1" @echo Installing Python via nuget... + @if not "%_Py_Quiet%"=="1" ( From 2e1c4904e768be942049e3ccc6585433b88f8ffb Mon Sep 17 00:00:00 2001 From: Serhii Kupriienko Date: Thu, 9 Jul 2026 15:20:50 +0000 Subject: [PATCH 13/13] Win32: use CONDA_PYTHON_EXE instead of nuget for PCbuild. Set PYTHON from the conda build env (PR #238 pattern) and drop the find_python.bat patch plus PYTHON_FOR_BUILD workaround. Co-authored-by: Cursor --- recipe/build_base.bat | 6 ++-- recipe/meta.yaml | 1 - ...-Win32-fix-broken-nuget-download-URL.patch | 35 ------------------- 3 files changed, 3 insertions(+), 39 deletions(-) delete mode 100644 recipe/patches/0027-Win32-fix-broken-nuget-download-URL.patch diff --git a/recipe/build_base.bat b/recipe/build_base.bat index 41642d32..1596242d 100644 --- a/recipe/build_base.bat +++ b/recipe/build_base.bat @@ -1,6 +1,9 @@ setlocal EnableDelayedExpansion echo on +:: Avoids fetching nuget.exe from the internet. +set PYTHON=%CONDA_PYTHON_EXE% + :: Compile python, extensions and external libraries if "%ARCH%"=="64" ( set PLATFORM=x64 @@ -73,9 +76,6 @@ set PGO= :: lib names (tcl86t.lib/tk86t.lib) from the major.minor of these props. set TCLTK_MSBUILD_PROPS="/p:TclVersion=%tk%" "/p:TkVersion=%tk%" -:: PCbuild code-gen needs a host Python; prefer the conda build env over nuget. -if not defined PYTHON_FOR_BUILD set PYTHON_FOR_BUILD=%BUILD_PREFIX%\python.exe - cd PCbuild :: Twice because: diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 7b163db9..4950b032 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -60,7 +60,6 @@ source: - patches/0024-Unvendor-zlib-ng.patch - patches/0025-Unvendor-zstd.patch - patches/0026-xxlimited-freethreading-stable-abi-link.patch # [win and freethreading == "yes"] - - patches/0027-Win32-fix-broken-nuget-download-URL.patch # [win] build: number: {{ build_number }} diff --git a/recipe/patches/0027-Win32-fix-broken-nuget-download-URL.patch b/recipe/patches/0027-Win32-fix-broken-nuget-download-URL.patch deleted file mode 100644 index 303b612e..00000000 --- a/recipe/patches/0027-Win32-fix-broken-nuget-download-URL.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Serhii Kupriienko -Date: Thu, 9 Jul 2026 00:00:00 +0000 -Subject: [PATCH 27/27] Win32: fix broken nuget download URL - -https://aka.ms/nugetclidl now redirects to Bing instead of nuget.exe. -Use dist.nuget.org and ensure nuget.org is configured before installing -pythonx86 (cpython gh-152919). ---- - PCbuild/find_python.bat | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/PCbuild/find_python.bat b/PCbuild/find_python.bat -index 1111111..2222222 100644 ---- a/PCbuild/find_python.bat -+++ b/PCbuild/find_python.bat -@@ -55,7 +55,7 @@ - @set _Py_HOST_PYTHON=%HOST_PYTHON% - @if "%_Py_HOST_PYTHON%"=="" set _Py_HOST_PYTHON=py - @if "%_Py_NUGET%"=="" (set _Py_NUGET=%_Py_EXTERNALS_DIR%\nuget.exe) --@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://aka.ms/nugetclidl) -+@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe) - @if NOT exist "%_Py_NUGET%" ( - @if not "%_Py_Quiet%"=="1" @echo Downloading nuget... - @rem NB: Must use single quotes around NUGET here, NOT double! -@@ -66,6 +66,9 @@ - @%_Py_HOST_PYTHON% -E "%_Py_D%\urlretrieve.py" "%_Py_NUGET_URL%" "%_Py_NUGET%" - ) - ) -+ -+@rem ensure nuget.org source exist -+@"%_Py_NUGET%" sources add -Name nuget.org -Source https://api.nuget.org/v3/index.json >nul 2>nul - - @if not "%_Py_Quiet%"=="1" @echo Installing Python via nuget... - @if not "%_Py_Quiet%"=="1" (