Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,9 @@ jobs:
# itself" (#1633, measured with conda 26.7.2). So 3.15 leaves base on its
# own Python: `conda build --python` below builds against 3.15 regardless,
# measured to build and import pypcapkit-*-py315_0. Drop the exception
# once conda publishes a py315 build.
# once conda publishes a py315 build. #2123: nor on macos-26-intel, where
# changing base's Python rebuilds conda-forge's libmambapy at a version
# conda-libmamba-solver cannot drive, failing "Install conda build tools".
- name: Conda environment creation and activation
uses: conda-incubator/setup-miniconda@be893c923ea9cf1cf7cd510fbdde27c7e18cbdcb # v4.1.0
env:
Expand All @@ -826,7 +828,7 @@ jobs:
# that leg installs Miniforge. An empty string names no installer, so
# the other legs keep the bundled or downloaded Miniconda.
miniforge-version: ${{ matrix.os == 'macos-26-intel' && 'latest' || '' }}
python-version: ${{ matrix.python-version != '3.15' && matrix.python-version || '' }}
python-version: ${{ matrix.os != 'macos-26-intel' && matrix.python-version != '3.15' && matrix.python-version || '' }}
activate-environment: base
auto-update-conda: false
auto-activate: true
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/cron-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,8 @@ jobs:
mkdir -p conda/wheels
python -m pip download -r conda/requirements.txt -d conda/wheels

# See create-release.yml's step of the same name for the 3.15 exception.
# See create-release.yml's step of the same name for the 3.15 and Intel
# exceptions (#2123).
- name: Conda environment creation and activation
id: miniconda
continue-on-error: ${{ matrix.os == 'windows-latest' }}
Expand All @@ -554,7 +555,7 @@ jobs:
# #2044: Miniforge on the Intel leg; see create-release.yml's step of
# the same name.
miniforge-version: ${{ matrix.os == 'macos-26-intel' && 'latest' || '' }}
python-version: ${{ matrix.python-version != '3.15' && matrix.python-version || '' }}
python-version: ${{ matrix.os != 'macos-26-intel' && matrix.python-version != '3.15' && matrix.python-version || '' }}
activate-environment: base
auto-update-conda: false
auto-activate: true
Expand Down
4 changes: 4 additions & 0 deletions conda/pypcapkit/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ build:
entry_points:
- pcapkit-cli = pcapkit.__main__:main
- pcapkit-vendor = pcapkit.vendor.__main__:main
# #2123: every binary comes from a wheel and links only system libraries, so
# none needs an rpath into the prefix. On osx-64, adding one fails on the
# vendored chardet extensions, which have no header room for it.
binary_relocation: false # [osx and x86_64]
#script: "{{ PYTHON }} -m pip install . -vv"

requirements:
Expand Down
100 changes: 100 additions & 0 deletions tests/project/test_conda_intel_miniforge_2044.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,29 @@
``installer-url`` first, then Miniforge when ``miniforge-version`` or
``miniforge-variant`` is non-empty, else Miniconda.

#2123: with Miniforge, the Intel legs broke twice more. Installing the matrix
Python into base rebuilt conda-forge's libmambapy at 2.10.0, which
conda-libmamba-solver 26.10.0 cannot drive, so the Intel legs now leave base's
Python alone, as 3.15 does everywhere; :class:`TestTheIntelLegsKeepBasesPython`
pins that. And conda-build 26.9's rpath relocation failed on the vendored chardet
extensions, so :file:`conda/pypcapkit/meta.yaml` turns it off on osx-64 alone;
:class:`TestOsx64SkipsBinaryRelocation` evaluates that line's selector as
conda-build does, for each leg's platform.

"""

from __future__ import annotations

import ast
import pathlib
import re
import unittest
from typing import TYPE_CHECKING

from tests import _workflows
from tests.project.test_python_versions_1660 import script as versions_script
from tests.project.test_release_gates import _as_string, evaluate_expression
from util import release_conda

if TYPE_CHECKING:
from typing import Any
Expand Down Expand Up @@ -110,5 +124,91 @@ def test_no_leg_names_two_installers(self) -> None:
self.assertEqual(received['installer-url'], '')


def python_input(entry: 'dict[str, Any]', os: 'str', python: 'str') -> 'str':
"""The ``python-version`` setup-miniconda receives on leg ``os`` with ``python``."""
context = {'matrix': {'os': os, 'python-version': python}} # type: dict[str, object]
return _as_string(evaluate_expression(str(entry['with']['python-version']), context)).strip()


class TestTheIntelLegsKeepBasesPython(unittest.TestCase):
"""#2123: no Python install into base on ``macos-26-intel``; the other legs as before."""

supported = () # type: tuple[str, ...]

@classmethod
def setUpClass(cls) -> None:
script = versions_script()
cls.supported = tuple(script.tier(script.load(), 'cpython', 'supported'))

def test_the_intel_leg_installs_no_python(self) -> None:
self.assertTrue(self.supported, 'no supported CPython to check')
for name, job_id in JOBS:
for python in self.supported:
with self.subTest(workflow=name, job=job_id, python=python):
self.assertEqual(python_input(step(name, job_id), INTEL, python), '')

def test_the_other_legs_install_the_matrix_python_but_315(self) -> None:
for name, job_id in JOBS:
for os in legs(name, job_id):
if os == INTEL:
continue
for python in self.supported:
with self.subTest(workflow=name, job=job_id, os=os, python=python):
expected = '' if python == '3.15' else python
self.assertEqual(python_input(step(name, job_id), os, python), expected)


#: The recipe whose ``build:`` section carries the relocation switch.
META = pathlib.Path(__file__).resolve().parents[2] / 'conda' / 'pypcapkit' / 'meta.yaml'
#: A ``key: value # [selector]`` line, as conda-build reads one.
SELECTED = re.compile(r'^\s*(?P<key>[\w-]+):\s*(?P<value>\S+)\s*#\s*\[(?P<selector>[^\]]+)\]\s*$')
#: conda-build's ``ARCH_MAP``: the subdir arch names that are not selector names.
ARCH_MAP = {'32': 'x86', '64': 'x86_64'}


def selectors(subdir: 'str') -> 'dict[str, bool]':
"""The OS and arch selectors conda-build 26.9's ``get_selectors`` sets for ``subdir``."""
subdirs = set(release_conda.PLATFORMS.values()) | {subdir, 'linux-aarch64', 'linux-32'}
names = {'unix': subdir.startswith(('linux-', 'osx-', 'emscripten-'))}
for os in {entry.split('-')[0] for entry in subdirs}:
names[os] = subdir.startswith(f'{os}-')
for arch in {entry.split('-')[1] for entry in subdirs}:
names[ARCH_MAP.get(arch, arch)] = subdir.endswith(f'-{arch}')
return names


def holds(selector: 'str', names: 'dict[str, bool]') -> 'bool':
"""Whether ``selector``, of names, ``and``, ``or`` and ``not``, holds; an unknown name raises."""
def walk(node: 'ast.AST') -> 'bool':
if isinstance(node, ast.BoolOp):
values = [walk(value) for value in node.values]
return all(values) if isinstance(node.op, ast.And) else any(values)
if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not):
return not walk(node.operand)
if isinstance(node, ast.Name):
return names[node.id]
raise AssertionError(f'unsupported selector {selector!r}')
return walk(ast.parse(selector, mode='eval').body)


class TestOsx64SkipsBinaryRelocation(unittest.TestCase):
"""#2123: ``binary_relocation: false`` on osx-64 alone, so the other platforms still relocate."""

def line(self) -> 're.Match[str]':
found = [match for match in map(SELECTED.match, META.read_text(encoding='utf-8').splitlines())
if match and match['key'] == 'binary_relocation']
self.assertEqual(len(found), 1, f'{META.name} has {len(found)} selected binary_relocation lines')
return found[0]

def test_the_line_turns_relocation_off(self) -> None:
self.assertEqual(self.line()['value'], 'false')

def test_it_applies_to_the_intel_legs_platform_alone(self) -> None:
selector = self.line()['selector']
for os, subdir in sorted(release_conda.PLATFORMS.items()):
with self.subTest(os=os, subdir=subdir):
self.assertEqual(holds(selector, selectors(subdir)), os == INTEL)


if __name__ == '__main__':
unittest.main()
3 changes: 2 additions & 1 deletion tests/project/test_python_matrix_tiers_1633.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,8 @@ def test_no_conda_leg_is_ktlo_and_315_leaves_base_python_alone(self) -> None:
setup = next(step for step in spec['steps']
if str(step.get('uses', '')).startswith('conda-incubator/setup-miniconda@'))
self.assertEqual(setup['with']['python-version'],
"${{ matrix.python-version != '3.15' && matrix.python-version || '' }}")
"${{ matrix.os != 'macos-26-intel' && matrix.python-version != '3.15' && "
"matrix.python-version || '' }}")


#: ``pip`` for an install step, which runs util/pip_install.py on the real
Expand Down
Loading