From c65094f22804e6fa46e5958506cb3a8dc353ed1f Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Thu, 26 Mar 2026 13:17:30 -0500 Subject: [PATCH 1/9] Add Python 3.14 support --- .github/workflows/tests.yml | 38 ++++++++++++++++++++-------------- recipe/conda_build_config.yaml | 1 + 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 23e4853217..57cd7b4585 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -92,10 +92,10 @@ jobs: conda-version: 25.11.0 test-type: parallel # maximum Python/conda combo - - python-version: '3.13' + - python-version: '3.14' conda-version: canary test-type: serial - - python-version: '3.13' + - python-version: '3.14' conda-version: canary test-type: parallel env: @@ -217,7 +217,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.13'] + python-version: ['3.14'] steps: - name: Checkout Source @@ -281,15 +281,19 @@ jobs: strategy: fail-fast: false matrix: - # test lower version (w/ stable conda) and upper version (w/ canary conda) - python-version: ['3.10'] - conda-version: [release] - test-type: [serial, parallel] include: - - python-version: '3.13' + # test lower version (w/ stable conda) + - python-version: '3.10' + conda-version: release + test-type: serial + - python-version: '3.10' + conda-version: release + test-type: parallel + # upper version (w/ canary conda) + - python-version: '3.14' conda-version: canary test-type: serial - - python-version: '3.13' + - python-version: '3.14' conda-version: canary test-type: parallel env: @@ -390,15 +394,19 @@ jobs: strategy: fail-fast: false matrix: - # test lower version (w/ stable conda) and upper version (w/ canary conda) - python-version: ['3.10'] - conda-version: [release] - test-type: [serial, parallel] include: - - python-version: '3.13' + # test lower version (w/ stable conda) + - python-version: '3.10' + conda-version: release + test-type: serial + - python-version: '3.10' + conda-version: release + test-type: parallel + # upper version (w/ canary conda) + - python-version: '3.14' conda-version: canary test-type: serial - - python-version: '3.13' + - python-version: '3.14' conda-version: canary test-type: parallel env: diff --git a/recipe/conda_build_config.yaml b/recipe/conda_build_config.yaml index 2aac1238b2..a7ee12f40c 100644 --- a/recipe/conda_build_config.yaml +++ b/recipe/conda_build_config.yaml @@ -3,3 +3,4 @@ python: - '3.11' - '3.12' - '3.13' + - '3.14' From 1f367835badeaf95325d5975b0b7b1b37be46fcb Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Thu, 26 Mar 2026 13:33:03 -0500 Subject: [PATCH 2/9] Try using macos-latest --- .github/workflows/tests.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 57cd7b4585..7f44a78e5b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -383,10 +383,7 @@ jobs: needs: changes if: github.event_name == 'schedule' || needs.changes.outputs.code == 'true' - # we still need intel macs so we are stuck on intel runners - # the issue is that there are recipes that depend on packages - # that do not exist for osx-arm64 - see #5388 - runs-on: macos-15-intel + runs-on: macos-latest defaults: run: # https://github.com/conda-incubator/setup-miniconda#use-a-default-shell From 614a47f358d90ebcc8b48735d77f094780d971ff Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Fri, 27 Mar 2026 01:03:57 -0500 Subject: [PATCH 3/9] Replace Python 3.6 with 3.13 --- tests/cli/test_main_build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cli/test_main_build.py b/tests/cli/test_main_build.py index 3b14222fc1..8977126662 100644 --- a/tests/cli/test_main_build.py +++ b/tests/cli/test_main_build.py @@ -322,11 +322,11 @@ def test_no_force_upload( @pytest.mark.slow def test_conda_py_no_period(testing_workdir, testing_metadata, monkeypatch): - monkeypatch.setenv("CONDA_PY", "36") + monkeypatch.setenv("CONDA_PY", "313") testing_metadata.meta["requirements"] = {"host": ["python"], "run": ["python"]} api.output_yaml(testing_metadata, "meta.yaml") outputs = api.build(testing_workdir, notest=True) - assert any("py36" in output for output in outputs) + assert any("py313" in output for output in outputs) def test_build_skip_existing( From 87c41371f4b2cb67e370443da406e2bddf6ab582 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Mon, 30 Mar 2026 10:32:51 -0500 Subject: [PATCH 4/9] Skip tests that fail on osx-arm64 --- tests/cli/test_main_build.py | 9 +++++++++ tests/cli/test_main_render.py | 5 +++++ tests/test_api_build.py | 17 +++++++++++++++++ tests/test_api_build_go_package.py | 4 ++++ tests/test_api_render.py | 4 ++++ tests/test_api_skeleton.py | 9 +++++++++ 6 files changed, 48 insertions(+) diff --git a/tests/cli/test_main_build.py b/tests/cli/test_main_build.py index 8977126662..fd59448374 100644 --- a/tests/cli/test_main_build.py +++ b/tests/cli/test_main_build.py @@ -8,6 +8,7 @@ from typing import TYPE_CHECKING import pytest +from conda.base.context import context from conda.exceptions import PackagesNotFoundError from conda_build import api @@ -42,6 +43,10 @@ def test_build_empty_sections(conda_build_test_recipe_envvar: str): @pytest.mark.serial +@pytest.mark.skipif( + context.subdir == "osx-arm64", + reason="conda_build_test channel does not support osx-arm64", +) def test_build_add_channel(): """This recipe requires the conda_build_test_requirement package, which is only on the conda_build_test channel. This verifies that the -c argument @@ -459,6 +464,10 @@ def test_relative_path_test_artifact( main_build.execute(args) +@pytest.mark.skipif( + context.subdir == "osx-arm64", + reason="imagesize<1.0 package not available on osx-arm64", +) def test_test_extra_dep(testing_metadata): testing_metadata.meta["test"]["imports"] = ["imagesize"] api.output_yaml(testing_metadata, "meta.yaml") diff --git a/tests/cli/test_main_render.py b/tests/cli/test_main_render.py index 7c5bd1f9ed..1c53283da3 100644 --- a/tests/cli/test_main_render.py +++ b/tests/cli/test_main_render.py @@ -7,6 +7,7 @@ import pytest import yaml +from conda.base.context import context from conda.exceptions import PackagesNotFoundError from conda_build import api @@ -22,6 +23,10 @@ from conda_build.metadata import MetaData +@pytest.mark.skipif( + context.subdir == "osx-arm64", + reason="conda_build_test channel does not support osx-arm64", +) def test_render_add_channel(tmp_path: Path) -> None: """This recipe requires the conda_build_test_requirement package, which is only on the conda_build_test channel. This verifies that the -c argument diff --git a/tests/test_api_build.py b/tests/test_api_build.py index 662cbfb175..4eaa815b9f 100644 --- a/tests/test_api_build.py +++ b/tests/test_api_build.py @@ -143,6 +143,17 @@ def test_recipe_builds( pytest.xfail("Numpy build doesn't run on Python 3.13 yet.") elif recipe.name == "dll_linking_fail": pytest.xfail("Recipe being built needs to fail linking tests.") + elif ( + recipe.name + in ( + "r_test_import", + "transitive_subpackage", + "always_include_files_glob", + "osx_rpath", + ) + and context.subdir == "osx-arm64" + ): + pytest.skip("Recipe uses packages that are not available on osx-arm64") # These variables are defined solely for testing purposes, # so they can be checked within build scripts @@ -1478,12 +1489,18 @@ def test_failed_recipe_leaves_folders(testing_config): @pytest.mark.sanity +@pytest.mark.skipif( + context.subdir == "osx-arm64", reason="r-base package not available on osx-arm64" +) def test_only_r_env_vars_defined(testing_config): recipe = os.path.join(metadata_dir, "_r_env_defined") api.build(recipe, config=testing_config) @pytest.mark.sanity +@pytest.mark.skipif( + context.subdir == "osx-arm64", reason="perl package not available on osx-arm64" +) def test_only_perl_env_vars_defined(testing_config): recipe = os.path.join(metadata_dir, "_perl_env_defined") api.build(recipe, config=testing_config) diff --git a/tests/test_api_build_go_package.py b/tests/test_api_build_go_package.py index 35cc12a965..c56394f764 100644 --- a/tests/test_api_build_go_package.py +++ b/tests/test_api_build_go_package.py @@ -1,6 +1,7 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause import pytest +from conda.base.context import context from conda_build.api import build @@ -9,6 +10,9 @@ @pytest.mark.sanity @pytest.mark.serial +@pytest.mark.skipif( + context.subdir == "osx-arm64", reason="go package not compiled for osx-arm64" +) def test_recipe_build(testing_config, monkeypatch): # These variables are defined solely for testing purposes, # so they can be checked within build scripts diff --git a/tests/test_api_render.py b/tests/test_api_render.py index d1ab7cc307..bcc31a1a5d 100644 --- a/tests/test_api_render.py +++ b/tests/test_api_render.py @@ -122,6 +122,10 @@ def test_pin_compatible_semver(testing_config): assert "zlib >=1.2.11,<2.0a0" in metadata.get_value("requirements/run") +@pytest.mark.skipif( + context.subdir == "osx-arm64", + reason="python=3.6 package not available on osx-arm64", +) def test_transitive_subpackage_dependency(testing_config): recipe_dir = os.path.join(metadata_dir, "transitive_subpackage") metadata = api.render(recipe_dir, config=testing_config)[1][0] diff --git a/tests/test_api_skeleton.py b/tests/test_api_skeleton.py index b30ed3b41a..1e605a75a0 100644 --- a/tests/test_api_skeleton.py +++ b/tests/test_api_skeleton.py @@ -10,6 +10,7 @@ import pytest import ruamel.yaml +from conda.base.context import context from conda_build import api from conda_build.skeletons.pypi import ( @@ -406,6 +407,10 @@ def test_pypi_with_version_arg(tmp_path: Path): @pytest.mark.slow +@pytest.mark.skipif( + context.subdir == "osx-arm64", + reason="python=3.6 package not available on osx-arm64", +) def test_pypi_with_extra_specs(tmp_path: Path, testing_config): # regression test for https://github.com/conda/conda-build/issues/1697 # For mpi4py: @@ -429,6 +434,10 @@ def test_pypi_with_extra_specs(tmp_path: Path, testing_config): @pytest.mark.slow +@pytest.mark.skipif( + context.subdir == "osx-arm64", + reason="python=3.6 package not available on osx-arm64", +) def test_pypi_with_version_inconsistency(tmp_path: Path, testing_config): # regression test for https://github.com/conda/conda-build/issues/189 # For mpi4py: From 2eb769355e607d4578aa5abf47dff1e44c523fd2 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Mon, 30 Mar 2026 11:53:04 -0500 Subject: [PATCH 5/9] Replace codecs.open with open --- conda_build/skeletons/cpan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conda_build/skeletons/cpan.py b/conda_build/skeletons/cpan.py index 4727f4fcf5..58c806493b 100644 --- a/conda_build/skeletons/cpan.py +++ b/conda_build/skeletons/cpan.py @@ -6,7 +6,6 @@ from __future__ import annotations -import codecs import gzip import hashlib import json @@ -264,7 +263,8 @@ def get_cpan_api_url(url, colons): output = output.decode("utf-8-sig") rel_dict = json.loads(output) except OSError: - rel_dict = json.loads(codecs.open(json_path, encoding="utf-8").read()) + with open(json_path, encoding="utf-8") as fp: + rel_dict = json.load(fp) except CondaHTTPError: rel_dict = None return rel_dict From 0f5738ff45776ffcdd9ec5881848de31a7373501 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Mon, 30 Mar 2026 12:21:23 -0500 Subject: [PATCH 6/9] Test osx-64 on lower bound, osx-arm64 on upper --- .github/workflows/tests.yml | 42 ++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7f44a78e5b..d474a18b94 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -383,7 +383,7 @@ jobs: needs: changes if: github.event_name == 'schedule' || needs.changes.outputs.code == 'true' - runs-on: macos-latest + runs-on: ${{ matrix.runs-on }} defaults: run: # https://github.com/conda-incubator/setup-miniconda#use-a-default-shell @@ -392,20 +392,24 @@ jobs: fail-fast: false matrix: include: - # test lower version (w/ stable conda) + # test lower version (w/ osx-64 & stable conda) - python-version: '3.10' conda-version: release test-type: serial + runs-on: macos-15-intel # osx-64 - python-version: '3.10' conda-version: release test-type: parallel - # upper version (w/ canary conda) + runs-on: macos-15-intel # osx-64 + # upper version (w/ osx-arm64 & canary conda) - python-version: '3.14' conda-version: canary test-type: serial + runs-on: macos-latest # osx-arm64 - python-version: '3.14' conda-version: canary test-type: parallel + runs-on: macos-latest # osx-arm64 env: # Use conda-forge for release tests since conda 25.11+ is not in defaults for osx-64 CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev' || 'conda-forge' }} @@ -436,21 +440,25 @@ jobs: - name: SDK Download run: | - echo "MACOSX_SDK_DIR=${HOME}/macosx_sdks" >> "$GITHUB_ENV" - export MACOSX_SDK_DIR=${HOME}/macosx_sdks - echo "MACOSX_SDK_VERSION=10.15" >> "$GITHUB_ENV" - export MACOSX_SDK_VERSION=10.15 - echo "MACOSX_SDK_ROOT=${MACOSX_SDK_DIR}/MacOSX${MACOSX_SDK_VERSION}.sdk" >> "$GITHUB_ENV" - export MACOSX_SDK_ROOT=${MACOSX_SDK_DIR}/MacOSX${MACOSX_SDK_VERSION}.sdk - - if [ ! -d ${MACOSX_SDK_DIR} ]; then mkdir ${MACOSX_SDK_DIR}; fi - if [ ! -d ${MACOSX_SDK_ROOT} ]; then - url="https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX${MACOSX_SDK_VERSION}.sdk.tar.xz" - curl -L --output MacOSX${MACOSX_SDK_VERSION}.sdk.tar.xz "${url}" - sdk_sha256=ac75d9e0eb619881f5aa6240689fce862dcb8e123f710032b7409ff5f4c3d18b - echo "${sdk_sha256} *MacOSX${MACOSX_SDK_VERSION}.sdk.tar.xz" | shasum -a 256 -c - tar -xf MacOSX${MACOSX_SDK_VERSION}.sdk.tar.xz -C "${MACOSX_SDK_DIR}" + if [ "${{ matrix.runs-on }}" = "macos-latest" ]; then + MACOSX_SDK_ROOT="$(xcrun --sdk macosx --show-sdk-path)" + else + MACOSX_SDK_VERSION=10.15 + MACOSX_SDK_SHA256=ac75d9e0eb619881f5aa6240689fce862dcb8e123f710032b7409ff5f4c3d18b + + MACOSX_SDK_DIR="${HOME}/macosx_sdks" + if [ ! -d "${MACOSX_SDK_DIR}" ]; then mkdir "${MACOSX_SDK_DIR}"; fi + + MACOSX_SDK_ROOT="${MACOSX_SDK_DIR}/MacOSX${MACOSX_SDK_VERSION}.sdk" + if [ ! -d "${MACOSX_SDK_ROOT}" ]; then + url="https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX${MACOSX_SDK_VERSION}.sdk.tar.xz" + curl -L --output "MacOSX${MACOSX_SDK_VERSION}.sdk.tar.xz" "${url}" + + echo "${MACOSX_SDK_SHA256} *MacOSX${MACOSX_SDK_VERSION}.sdk.tar.xz" | shasum -a 256 -c + tar -xf "MacOSX${MACOSX_SDK_VERSION}.sdk.tar.xz" -C "${MACOSX_SDK_DIR}" + fi fi + echo "MACOSX_SDK_ROOT=${MACOSX_SDK_ROOT}" >> "$GITHUB_ENV" - name: Conda Install run: > From a28d4c500a8cdd60aff77910304f0d59a0ebc866 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Mon, 30 Mar 2026 13:21:53 -0500 Subject: [PATCH 7/9] Fix test_noarch_with_no_platform_deps --- tests/test_api_render.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/test_api_render.py b/tests/test_api_render.py index bcc31a1a5d..b9dd4398a9 100644 --- a/tests/test_api_render.py +++ b/tests/test_api_render.py @@ -222,10 +222,20 @@ def test_noarch_with_platform_deps(testing_workdir, testing_config): def test_noarch_with_no_platform_deps(testing_workdir, testing_config): recipe_path = os.path.join(metadata_dir, "_noarch_with_no_platform_deps") build_ids = set() - for platform in ["osx", "linux", "win"]: - metadata = api.render(recipe_path, config=testing_config, platform=platform)[0][ - 0 - ] + for platform, arch in [ + ("osx", "64"), + ("osx", "arm64"), + ("linux", "64"), + ("linux", "aarch64"), + ("win", "64"), + ("win", "arm64"), + ]: + metadata = api.render( + recipe_path, + config=testing_config, + platform=platform, + arch=arch, + )[0][0] build_ids.add(metadata.build_id()) assert len(build_ids) == 1 From 5ce969aea11d4039eb44e4a2db65ade32d360f75 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Mon, 30 Mar 2026 14:00:55 -0500 Subject: [PATCH 8/9] Fix test_api_build_pytorch_cpu_issue5644 --- tests/test_api_build.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/tests/test_api_build.py b/tests/test_api_build.py index 4eaa815b9f..7ef25a1751 100644 --- a/tests/test_api_build.py +++ b/tests/test_api_build.py @@ -2181,27 +2181,20 @@ def test_api_build_grpc_issue5645(monkeypatch, tmp_path, testing_config): @pytest.mark.skipif( - not on_mac, reason="needs to cross-compile from osx-64 to osx-arm64" + not on_mac, + reason="needs to cross-compile from osx-64 to osx-arm64", ) def test_api_build_pytorch_cpu_issue5644(monkeypatch, tmp_path, testing_config): # this test has to cross-compile from osx-64 to osx-arm64 - try: - if "CONDA_SUBDIR" in os.environ: - old_subdir = os.environ["CONDA_SUBDIR"] - has_old_subdir = True - else: - has_old_subdir = False - old_subdir = None - os.environ["CONDA_SUBDIR"] = "osx-64" - - testing_config.channel_urls = ["conda-forge"] - monkeypatch.chdir(tmp_path) - api.build(str(metadata_path / "_pytorch_cpu"), config=testing_config) - finally: - if has_old_subdir: - os.environ["CONDA_SUBDIR"] = old_subdir - else: - del os.environ["CONDA_SUBDIR"] + # monkeypatch.setenv("CONDA_SUBDIR", "osx-64") + monkeypatch.chdir(tmp_path) + api.build( + str(metadata_path / "_pytorch_cpu"), + config=testing_config, + channel_urls=["conda-forge"], + platform="osx", + arch="64", + ) @pytest.mark.skipif(on_win, reason="file permissions not relevant on Windows") From bdf6e31bb4ca65e08a4b9d5bd213a637e3c0be96 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Mon, 30 Mar 2026 19:22:47 -0500 Subject: [PATCH 9/9] Add news --- news/5941-python-314.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 news/5941-python-314.md diff --git a/news/5941-python-314.md b/news/5941-python-314.md new file mode 100644 index 0000000000..b80cc15a98 --- /dev/null +++ b/news/5941-python-314.md @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* Add Python 3.13 to test matrix. (#5941)