From ad7645f027d27705f798f22766b90eb9a817c70a Mon Sep 17 00:00:00 2001 From: zydo Date: Mon, 12 Oct 2026 02:55:13 -0700 Subject: [PATCH 1/4] ci: stop running setup.py inside conda-build load_setup_py_data() executes setup.py in the conda-build process. setup.py prepends "." to sys.path, so the source tree's unbuilt zstandard package later shadows the zstandard bindings that conda-package-handling imports for .conda support. conda-build then fails with "Didn't recognize extension for file '...conda'". Read the version from c-ext/python-zstandard.h with load_file_regex() instead. --- ci/conda/meta.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ci/conda/meta.yaml b/ci/conda/meta.yaml index cbea594a..2311ac3b 100644 --- a/ci/conda/meta.yaml +++ b/ci/conda/meta.yaml @@ -1,8 +1,12 @@ -{% set data = load_setup_py_data() %} +# Don't use load_setup_py_data(): it runs setup.py inside the conda-build +# process, and setup.py's sys.path.insert(0, ".") then makes the unbuilt +# zstandard package in the source tree shadow the zstandard bindings that +# conda-package-handling needs for .conda support. +{% set version_match = load_file_regex(load_file="../../c-ext/python-zstandard.h", regex_pattern='PYTHON_ZSTANDARD_VERSION "([^"]+)"', from_recipe_dir=True) %} package: name: zstandard - version: "{{ data.get('version') }}" + version: "{{ version_match.group(1) }}" source: path: ../../ From 914af5fde730bd1c9cc138d96901be09d7b43935 Mon Sep 17 00:00:00 2001 From: zydo Date: Mon, 12 Oct 2026 02:55:26 -0700 Subject: [PATCH 2/4] ci: install only test dependencies when testing wheels cibuildwheel installs the test-groups packages unlocked, so it picked up the latest mypy, which now depends on ast-serialize. ast-serialize has no wheels for musllinux_1_1 or musllinux s390x, and building it from source needs a Rust toolchain the containers don't have, failing those wheel jobs before any test ran. Move the test suite's dependencies into a new "test" dependency group, have "dev" include it plus mypy, and point cibuildwheel at "test". --- pyproject.toml | 12 +++++++++--- uv.lock | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b358a7f5..cbd2d07b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,14 +42,20 @@ requires = [ build-backend = "setuptools.build_meta" [dependency-groups] -dev = [ +# Dependencies needed to run the test suite. cibuildwheel installs this group +# unlocked inside its build containers, so keep it free of packages (like mypy) +# without wheels for every platform we build. +test = [ "cffi~=1.17; platform_python_implementation != 'PyPy' and python_version < '3.14'", "cffi>=2.0.0b; platform_python_implementation != 'PyPy' and python_version >= '3.14'", "hypothesis==6.111.0", - "mypy>=1.17.1", "pytest>=8.4.1", "pytest-xdist>=3.8.0", ] +dev = [ + { include-group = "test" }, + "mypy>=1.17.1", +] [tool.pytest.ini_options] testpaths = ["tests"] @@ -71,7 +77,7 @@ test-command = [ "pytest -v --numprocesses=auto", "PYTHON_ZSTANDARD_IMPORT_POLICY=cffi pytest -v --numprocesses=auto", ] -test-groups = ["dev"] +test-groups = ["test"] test-sources = ["tests"] [tool.cibuildwheel.linux] diff --git a/uv.lock b/uv.lock index 89fdd450..717aa37f 100644 --- a/uv.lock +++ b/uv.lock @@ -420,6 +420,13 @@ dev = [ { name = "pytest" }, { name = "pytest-xdist" }, ] +test = [ + { name = "cffi", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14' and platform_python_implementation != 'PyPy'" }, + { name = "cffi", version = "2.0.0b1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14' and platform_python_implementation != 'PyPy'" }, + { name = "hypothesis" }, + { name = "pytest" }, + { name = "pytest-xdist" }, +] [package.metadata] requires-dist = [ @@ -437,3 +444,10 @@ dev = [ { name = "pytest", specifier = ">=8.4.1" }, { name = "pytest-xdist", specifier = ">=3.8.0" }, ] +test = [ + { name = "cffi", marker = "python_full_version < '3.14' and platform_python_implementation != 'PyPy'", specifier = "~=1.17" }, + { name = "cffi", marker = "python_full_version >= '3.14' and platform_python_implementation != 'PyPy'", specifier = ">=2.0.0b0" }, + { name = "hypothesis", specifier = "==6.111.0" }, + { name = "pytest", specifier = ">=8.4.1" }, + { name = "pytest-xdist", specifier = ">=3.8.0" }, +] From a5adf085271435205c0a717c6b1995aab6ea9576 Mon Sep 17 00:00:00 2001 From: zydo Date: Mon, 12 Oct 2026 02:55:49 -0700 Subject: [PATCH 3/4] ci: find Homebrew zstd in the macOS external zstd job On Apple Silicon runners Homebrew installs into /opt/homebrew, which isn't on the compiler's default search paths, so the --system-zstd build failed with "'zdict.h' file not found". Pass Homebrew's include and lib directories explicitly, and build for arm64 only since Homebrew's libzstd is not universal2. --- .github/workflows/external-zstd.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/external-zstd.yml b/.github/workflows/external-zstd.yml index 2e0f3b69..42bf25e6 100644 --- a/.github/workflows/external-zstd.yml +++ b/.github/workflows/external-zstd.yml @@ -61,4 +61,11 @@ jobs: - name: Build run: | + # Homebrew's prefix (/opt/homebrew on Apple Silicon) isn't on the + # default compiler search paths, and its libzstd is arm64 only, so + # don't build the universal2 binary the Python framework asks for. + ZSTD_PREFIX="$(brew --prefix zstd)" + export CFLAGS="-I${ZSTD_PREFIX}/include" + export LDFLAGS="-L${ZSTD_PREFIX}/lib" + export ARCHFLAGS="-arch arm64" python -m pip install --config-settings=--global-option=--system-zstd . From 1f2111ca62a123e31040febf334ba86bb06cf668 Mon Sep 17 00:00:00 2001 From: zydo Date: Mon, 12 Oct 2026 02:55:53 -0700 Subject: [PATCH 4/4] tests: disable hypothesis deadlines in the ci profile test_stream_source_readinto intermittently fails on macos-26-intel runners with DeadlineExceeded: a single example took 31s against the 20s deadline, then 18s on hypothesis's rerun, which hypothesis reports as a FlakyFailure. The timing depends on runner load, not on the code under test, so don't enforce a deadline in the ci profile, as the expensive profile already does. --- tests/conftest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 84e71215..dcba0693 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,7 +25,9 @@ def pytest_configure(config): default_settings = hypothesis.settings(deadline=10000) hypothesis.settings.register_profile("default", default_settings) - ci_settings = hypothesis.settings(deadline=20000, max_examples=1000) + # Shared CI runners (notably the Intel macOS ones) are slow and noisy + # enough that per-example deadlines only produce flaky failures. + ci_settings = hypothesis.settings(deadline=None, max_examples=1000) hypothesis.settings.register_profile("ci", ci_settings) expensive_settings = hypothesis.settings(deadline=None, max_examples=10000)