[med-svn] [Git][med-team/cyvcf2][master] 11 commits: Packaging update (routine-update)

Andreas Tille (@tille) gitlab at salsa.debian.org
Mon Sep 21 09:45:28 BST 2026



Andreas Tille pushed to branch master at Debian Med / cyvcf2


Commits:
bc95e727 by Andreas Tille at 2026-09-21T10:19:30+02:00
Packaging update (routine-update)

- - - - -
0538ce97 by Andreas Tille at 2026-09-21T10:19:35+02:00
New upstream version

- - - - -
7960b528 by Andreas Tille at 2026-09-21T10:19:36+02:00
New upstream version 0.34.0
- - - - -
6d2c9ae5 by Andreas Tille at 2026-09-21T10:19:37+02:00
Update upstream source from tag 'upstream/0.34.0'

Update to upstream version '0.34.0'
with Debian dir 3b68a2cb1f5e717fb1d0a99e2b6016adb7c92e9e
- - - - -
11e9dfc0 by Andreas Tille at 2026-09-21T10:19:37+02:00
Standards-Version: 4.7.4 (routine-update)

- - - - -
53de818e by Andreas Tille at 2026-09-21T10:19:46+02:00
Changes made by cme

- - - - -
20b5b748 by Andreas Tille at 2026-09-21T10:20:11+02:00
Build-Depends: pybuild-plugin-pyproject (routine-update)

- - - - -
ea3f6e1a by Andreas Tille at 2026-09-21T09:20:18+01:00
Set upstream metadata fields: Documentation.

Changes-By: lintian-brush

- - - - -
671a8454 by Andreas Tille at 2026-09-21T10:35:02+02:00
Build-Depends: python3-scikit-build-core

- - - - -
e0815324 by Andreas Tille at 2026-09-21T10:35:39+02:00
Build-Depends: dh-sequence-single-binary

- - - - -
c76d3e28 by Andreas Tille at 2026-09-21T10:44:26+02:00
Build-Depends: cmake

- - - - -


25 changed files:

- .github/workflows/build.yml
- .github/workflows/wheels.yml
- CHANGES.md
- + CMakeLists.txt
- − MANIFEST.in
- README.md
- ci/Dockerfile.alpine.test
- ci/Dockerfile.slim.test
- ci/linux-deps
- ci/osx-deps
- cyvcf2/__init__.py
- + cyvcf2/_version.py
- cyvcf2/cyvcf2.pxd
- cyvcf2/cyvcf2.pyx
- cyvcf2/helpers.h
- + cyvcf2/tests/test.warnings.vcf.gz
- + cyvcf2/tests/test.warnings.vcf.gz.tbi
- cyvcf2/tests/test_cli.py
- debian/changelog
- debian/control
- debian/upstream/metadata
- pyproject.toml
- requirements.txt
- − setup.cfg
- − setup.py


Changes:

=====================================
.github/workflows/build.yml
=====================================
@@ -1,6 +1,14 @@
 name: Build
 
-on: [push, pull_request]
+on:
+  push:
+    branches:
+      - main
+  pull_request:
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
 
 jobs:
   docker-build:
@@ -28,6 +36,7 @@ jobs:
         with:
           submodules: recursive
       - name: Set up QEMU
+        if: matrix.platform == 'linux/arm64'
         uses: docker/setup-qemu-action at v3
       - name: Set up Docker Buildx
         uses: docker/setup-buildx-action at v3
@@ -41,37 +50,33 @@ jobs:
           tags: cyvcf2:${{ matrix.python_tag_type }}-test
           push: false
           load: true
+          cache-from: type=gha,scope=docker-test-${{ matrix.python_tag_type }}
+          cache-to: type=gha,mode=max,scope=docker-test-${{ matrix.python_tag_type }}
           build-args: |
             PYTHON_VERSION=${{ matrix.python_tag_type }}
 
       - name: Docker Run Tests
         run: |
-          docker run --rm --platform ${{ matrix.platform }} cyvcf2:${{ matrix.python_tag_type }}-test pytest --cov cyvcf2 --cov-report term-missing
+          docker run --rm --platform ${{ matrix.platform }} cyvcf2:${{ matrix.python_tag_type }}-test pytest -q
 
   build:
     name: Run tests on Python ${{ matrix.python-version }} ${{ matrix.os }}
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        os: [ubuntu-22.04, macos-13]
+        os: [ubuntu-22.04, macos-14]
         python-version:
-          ["pypy3.10", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
+          ["pypy3.10", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
         exclude:
           # Run only the latest versions on macOS and windows
-          - os: macos-13
+          - os: macos-14
             python-version: "pypy3.10"
-          - os: macos-13
-            python-version: "3.7"
-          - os: macos-13
-            python-version: "3.8"
-          - os: macos-13
+          - os: macos-14
             python-version: "3.9"
-          - os: macos-13
+          - os: macos-14
             python-version: "3.10"
-          - os: macos-13
+          - os: macos-14
             python-version: "3.11"
-          - os: macos-12
-            python-version: "3.12"
 
     steps:
       - uses: actions/checkout at v4
@@ -85,41 +90,54 @@ jobs:
       - name: Set macOS env
         if: runner.os == 'macOS'
         run: |
-          # building options
-          echo "MACOSX_DEPLOYMENT_TARGET=10.9" >> "$GITHUB_ENV"
-          echo "ARCHFLAGS=-arch x86_64" >> "$GITHUB_ENV"
+          BREW_PREFIX="$(brew --prefix)"
+          LIBDEFLATE_PREFIX="${BREW_PREFIX}/opt/libdeflate"
+
+          # Homebrew on arm64 installs under /opt/homebrew, which is not in default compiler search paths.
+          echo "CPPFLAGS=-I${BREW_PREFIX}/include -I${LIBDEFLATE_PREFIX}/include ${CPPFLAGS:-}" >> "$GITHUB_ENV"
+          echo "LDFLAGS=-L${BREW_PREFIX}/lib -L${LIBDEFLATE_PREFIX}/lib ${LDFLAGS:-}" >> "$GITHUB_ENV"
+          echo "PKG_CONFIG_PATH=${LIBDEFLATE_PREFIX}/lib/pkgconfig:${BREW_PREFIX}/lib/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}" >> "$GITHUB_ENV"
+
+          # arm64 requires a newer deployment target than x86_64.
+          if [ "${{ runner.arch }}" = "X64" ]; then
+            echo "MACOSX_DEPLOYMENT_TARGET=10.9" >> "$GITHUB_ENV"
+            echo "ARCHFLAGS=-arch x86_64" >> "$GITHUB_ENV"
+          else
+            echo "MACOSX_DEPLOYMENT_TARGET=11.0" >> "$GITHUB_ENV"
+          fi
 
       - name: Install Linux build prerequisites
         if: runner.os == 'Linux'
         run: |
           sudo apt-get update
-          sudo apt-get install -y --no-install-recommends libcurl4-openssl-dev zlib1g-dev libssl-dev liblzma-dev \
-            libbz2-dev libdeflate-dev
+          sudo apt-get install -y --no-install-recommends cmake ninja-build libcurl4-openssl-dev zlib1g-dev \
+            libssl-dev liblzma-dev libbz2-dev libdeflate-dev
 
       - name: Install macOS build prerequisites
         if: runner.os == 'macOS'
         run: |
-          brew install automake libdeflate
+          brew install automake cmake libdeflate ninja pkg-config
 
       - name: Install
         run: |
-          pip install -r requirements.txt
-          pip install pytest pytest-cov
+          python -m pip install --upgrade pip
+          python -m pip install pytest pytest-cov
           CYVCF2_HTSLIB_CONFIGURE_OPTIONS="--enable-libcurl --enable-s3 --enable-lzma --enable-bz2 --with-libdeflate" \
-            CYTHONIZE=1 python setup.py build_ext -i
+            python -m pip install -v -e .
 
       - name: Test
         run: |
           pytest --cov cyvcf2 --cov-report term-missing
 
-      # make sure to keep this numpy version in sync with setup.py
+      # make sure to keep this numpy version in sync with pyproject.toml
       - name: Test with oldest numpy that we support
-        if: contains(fromJson('["3.7"]'), matrix.python-version)
+        if: contains(fromJson('["3.9"]'), matrix.python-version)
         run: |
-          pip install --force-reinstall --no-cache-dir 'numpy==1.16.0'
+          pip install --force-reinstall --no-cache-dir 'numpy==2.0.0'
           pytest --cov cyvcf2 --cov-report term-missing
 
       - name: Test with newest available numpy
+        if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.14'
         run: |
           pip install -U numpy
           pytest --cov cyvcf2 --cov-report term-missing
@@ -147,6 +165,7 @@ jobs:
             mingw-w64-ucrt-x86_64-libdeflate
             mingw-w64-ucrt-x86_64-xz
             mingw-w64-ucrt-x86_64-curl
+            mingw-w64-ucrt-x86_64-cmake
             mingw-w64-ucrt-x86_64-zlib
             mingw-w64-ucrt-x86_64-bzip2
             mingw-w64-ucrt-x86_64-tools-git
@@ -170,19 +189,84 @@ jobs:
 
       - name: Install
         run: |
-          pip install -r requirements.txt
-          pip install pytest pytest-cov
-          CYTHONIZE=1 python setup.py build_ext -i
+          python -m venv .venv
+          . .venv/bin/activate
+          python -m pip install --upgrade pip
+          python -m pip install pytest pytest-cov
+          python -m pip install -v -e .
 
       - name: Test
         run: |
+          . .venv/bin/activate
           pytest --cov cyvcf2 --cov-report term-missing
 
       - name: Test with newest available numpy
         run: |
+          . .venv/bin/activate
           pip install -U numpy
           pytest --cov cyvcf2 --cov-report term-missing
 
+  conda_external_htslib:
+    name: Conda external htslib ${{ matrix.htslib-version }}
+    runs-on: ubuntu-22.04
+    strategy:
+      fail-fast: false
+      matrix:
+        htslib-version: ["1.21", "1.22.1", "1.23.1"]
+
+    steps:
+      - uses: actions/checkout at v4
+        with:
+          submodules: recursive
+
+      - name: Set up micromamba
+        uses: mamba-org/setup-micromamba at v2
+        with:
+          environment-name: cyvcf2-conda-external
+          condarc: |
+            channels:
+              - conda-forge
+              - bioconda
+            channel_priority: strict
+          create-args: >-
+            python=3.12
+            pip
+            scikit-build-core>=0.11
+            cmake>=3.20
+            ninja
+            cython>=3
+            numpy>=2.0.0
+            htslib=${{ matrix.htslib-version }}
+            pkg-config
+            setuptools
+            wheel
+
+      - name: Install against conda htslib
+        shell: micromamba-shell {0}
+        run: |
+          export CYVCF2_HTSLIB_MODE=EXTERNAL
+          export CMAKE_PREFIX_PATH="${CONDA_PREFIX}${CMAKE_PREFIX_PATH:+:${CMAKE_PREFIX_PATH}}"
+          export PKG_CONFIG_PATH="${CONDA_PREFIX}/lib/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}"
+
+          python -m pip install . --no-deps --no-build-isolation -v > conda-external-build.log 2>&1
+          cat conda-external-build.log
+          if grep -E "building bundled( shared)? htslib" conda-external-build.log; then
+            echo "Expected external htslib, but build used bundled htslib"
+            exit 1
+          fi
+
+      - name: Verify external htslib linkage
+        shell: micromamba-shell {0}
+        run: |
+          extension="$(cd "${RUNNER_TEMP}" && python -c 'import cyvcf2.cyvcf2 as module; print(module.__file__)')"
+          echo "Extension path: ${extension}"
+
+          cd "${RUNNER_TEMP}"
+          python -c 'from cyvcf2 import VCF; print(VCF)'
+
+          ldd "${extension}" | tee "${GITHUB_WORKSPACE}/conda-external-linkage.txt"
+          grep -E "libhts\.so" "${GITHUB_WORKSPACE}/conda-external-linkage.txt"
+
   sdist:
     runs-on: ubuntu-22.04
     strategy:
@@ -200,10 +284,10 @@ jobs:
 
       - name: Install sdist prerequisites
         run: |
-          pip install -r requirements.txt
+          python -m pip install --upgrade pip build
 
       - name: Create source distribution
-        run: CYTHONIZE=1 python setup.py sdist
+        run: python -m build --sdist
 
       - name: Install Linux build prerequisites
         if: runner.os == 'Linux'
@@ -213,7 +297,7 @@ jobs:
             libbz2-dev libdeflate-dev
 
       - name: Build (via sdist tarball)
-        run: pip install --verbose --no-deps --no-binary='cyvcf2' cyvcf2-*.tar.gz
+        run: python -m pip install --verbose --no-binary='cyvcf2' cyvcf2-*.tar.gz
         working-directory: dist
 
       - name: Install test prerequisites


=====================================
.github/workflows/wheels.yml
=====================================
@@ -14,12 +14,35 @@ on:
         required: false
         default: false
   schedule:
-    # run weekly on a Monday
-    - cron: "0 0 * * 1"
+    # run monthly on the 1st
+    - cron: "0 0 1 * *"
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+env:
+  CIBW_ENABLE: pypy
+  CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
+  # manylinux2014 can't build on aarch64
+  CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
+  CIBW_MUSLLINUX_X86_64_IMAGE: musllinux_1_2
+  CIBW_MUSLLINUX_AARCH64_IMAGE: musllinux_1_2
+  CIBW_ARCHS_LINUX: auto64 aarch64
+  CIBW_BEFORE_BUILD_LINUX: "{project}/ci/linux-deps"
+  CIBW_REPAIR_WHEEL_COMMAND_LINUX: 'LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib64" && auditwheel repair -w {dest_dir} {wheel}'
+  CIBW_ARCHS_MACOS: auto64
+  CIBW_BEFORE_BUILD_MACOS: "{project}/ci/osx-deps"
+  CIBW_REPAIR_WHEEL_COMMAND_MACOS: "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
+  CIBW_ENVIRONMENT: >-
+    CYVCF2_HTSLIB_CONFIGURE_OPTIONS="--enable-libcurl --enable-s3 --enable-lzma --enable-bz2 --with-libdeflate"
+  CIBW_TEST_COMMAND: "{project}/ci/test"
 
 jobs:
   build_wheels:
-    name: Build wheels for  ${{ matrix.python-version }}-${{ matrix.buildplat[1] }}
+    # Full wheel matrix for releases, schedule, and manual runs.
+    if: github.event_name != 'push' || github.ref != 'refs/heads/main'
+    name: Build wheels (full matrix)
     runs-on: ${{ matrix.buildplat[0] }}
     strategy:
       matrix:
@@ -28,67 +51,52 @@ jobs:
           - [ubuntu-22.04, musllinux_x86_64]
           - [ubuntu-22.04, manylinux_aarch64]
           - [ubuntu-22.04, musllinux_aarch64]
-          - [macos-13, macosx_x86_64]
+          - [macos-15-intel, macosx_x86_64]
           - [macos-14, macosx_arm64]
-        python-version: [pp310, cp38, cp39, cp310, cp311, cp312, cp313, cp314]
+        python-version: [pp311, cp39, cp310, cp311, cp312, cp313, cp314]
         exclude:
-          # pp310, cp38 on musllinux is not support
+          # pp311 not built on musllinux or manylinux_aarch64
           # cp39, cp310 on musllinux_aarch64, wheel building may hangup, ignore it
           # drop 37 so we can use newer cibuildwheel
+          - buildplat: [ubuntu-22.04, manylinux_aarch64]
+            python-version: pp311
           - buildplat: [ubuntu-22.04, musllinux_x86_64]
-            python-version: cp38
-          - buildplat: [ubuntu-22.04, musllinux_x86_64]
-            python-version: pp310
-          - buildplat: [ubuntu-22.04, musllinux_aarch64]
-            python-version: cp38
+            python-version: pp311
           - buildplat: [ubuntu-22.04, musllinux_aarch64]
             python-version: cp39
           - buildplat: [ubuntu-22.04, musllinux_aarch64]
             python-version: cp310
           - buildplat: [ubuntu-22.04, musllinux_aarch64]
-            python-version: pp310
+            python-version: pp311
 
     steps:
       - uses: actions/checkout at v4
         with:
           submodules: recursive
 
+      - name: Cache wheel build downloads
+        uses: actions/cache at v4
+        with:
+          path: |
+            ~/.cache/pip
+            ~/.cache/cibuildwheel
+          key: ${{ runner.os }}-${{ matrix.buildplat[1] }}-cibw-${{ hashFiles('pyproject.toml', 'requirements.txt', 'ci/linux-deps', 'ci/osx-deps') }}
+          restore-keys: |
+            ${{ runner.os }}-${{ matrix.buildplat[1] }}-cibw-
+            ${{ runner.os }}-cibw-
+
       - name: Set up QEMU
-        if: runner.os == 'Linux'
+        if: runner.os == 'Linux' && contains(matrix.buildplat[1], 'aarch64')
         uses: docker/setup-qemu-action at v3
 
       - name: Build wheels
-        uses: pypa/cibuildwheel at v3.1.4
+        uses: pypa/cibuildwheel at v4.1.0
         with:
           package-dir: .
           output-dir: wheelhouse
           config-file: "{package}/pyproject.toml"
         env:
-          # select
           CIBW_BUILD: ${{ matrix.python-version }}-${{ matrix.buildplat[1] }}
-          CIBW_ENABLE: pypy
-          CIBW_PRERELEASE_PYTHONS: 1
-
-          # linux
-          CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
-          # manylinux2014 can't build on aarch64
-          CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
-          CIBW_MUSLLINUX_X86_64_IMAGE: musllinux_1_2
-          CIBW_MUSLLINUX_AARCH64_IMAGE: musllinux_1_2
-          CIBW_ARCHS_LINUX: auto64 aarch64
-          CIBW_BEFORE_BUILD_LINUX: "{project}/ci/linux-deps"
-          CIBW_REPAIR_WHEEL_COMMAND_LINUX: 'LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib64" && auditwheel repair -w {dest_dir} {wheel}'
-
-          # macos
-          CIBW_ARCHS_MACOS: auto64
-          CIBW_BEFORE_BUILD_MACOS: "{project}/ci/osx-deps"
-          CIBW_REPAIR_WHEEL_COMMAND_MACOS: "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
- 
-          # build
-          CIBW_ENVIRONMENT: >-
-            CYVCF2_HTSLIB_CONFIGURE_OPTIONS="--enable-libcurl --enable-s3 --enable-lzma --enable-bz2 --with-libdeflate"
-            CYTHONIZE=1
-          CIBW_TEST_COMMAND: "{project}/ci/test"
 
       # Enable tmate debugging of manually-triggered workflows if the input option was provided
       - name: Setup tmate session
@@ -100,6 +108,65 @@ jobs:
           name: artifact-${{ matrix.python-version }}-${{ matrix.buildplat[1] }}
           path: ./wheelhouse/*.whl
 
+  build_wheels_smoke:
+    # Smaller matrix on main to keep CI cost controlled.
+    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+    name: Build wheels (smoke matrix)
+    runs-on: ${{ matrix.buildplat[0] }}
+    strategy:
+      matrix:
+        include:
+          - buildplat: [ubuntu-22.04, manylinux_x86_64]
+            python-version: cp39
+          - buildplat: [ubuntu-22.04, manylinux_x86_64]
+            python-version: cp314
+          - buildplat: [ubuntu-22.04, manylinux_x86_64]
+            python-version: pp311
+          - buildplat: [ubuntu-22.04, musllinux_x86_64]
+            python-version: cp314
+          - buildplat: [ubuntu-22.04, manylinux_aarch64]
+            python-version: cp314
+          - buildplat: [ubuntu-22.04, musllinux_aarch64]
+            python-version: cp314
+          - buildplat: [macos-15-intel, macosx_x86_64]
+            python-version: cp314
+          - buildplat: [macos-14, macosx_arm64]
+            python-version: cp314
+
+    steps:
+      - uses: actions/checkout at v4
+        with:
+          submodules: recursive
+
+      - name: Cache wheel build downloads
+        uses: actions/cache at v4
+        with:
+          path: |
+            ~/.cache/pip
+            ~/.cache/cibuildwheel
+          key: ${{ runner.os }}-${{ matrix.buildplat[1] }}-cibw-${{ hashFiles('pyproject.toml', 'requirements.txt', 'ci/linux-deps', 'ci/osx-deps') }}
+          restore-keys: |
+            ${{ runner.os }}-${{ matrix.buildplat[1] }}-cibw-
+            ${{ runner.os }}-cibw-
+
+      - name: Set up QEMU
+        if: runner.os == 'Linux' && contains(matrix.buildplat[1], 'aarch64')
+        uses: docker/setup-qemu-action at v3
+
+      - name: Build wheels
+        uses: pypa/cibuildwheel at v4.1.0
+        with:
+          package-dir: .
+          output-dir: wheelhouse
+          config-file: "{package}/pyproject.toml"
+        env:
+          CIBW_BUILD: ${{ matrix.python-version }}-${{ matrix.buildplat[1] }}
+
+      - uses: actions/upload-artifact at v4
+        with:
+          name: artifact-smoke-${{ matrix.python-version }}-${{ matrix.buildplat[1] }}
+          path: ./wheelhouse/*.whl
+
   build_sdist:
     name: Build source distribution
     runs-on: ubuntu-22.04
@@ -111,13 +178,15 @@ jobs:
         name: Install Python
         with:
           python-version: "3.14"
+          cache: pip
+          cache-dependency-path: requirements.txt
 
       - name: Install dependencies
         run: |
-          pip install -r requirements.txt
+          python -m pip install --upgrade pip build
 
       - name: Build sdist
-        run: CYTHONIZE=1 python setup.py sdist
+        run: python -m build --sdist
 
       - uses: actions/upload-artifact at v4
         with:


=====================================
CHANGES.md
=====================================
@@ -1,3 +1,11 @@
+# v0.34.0
++ drop support for end-of-life Python 3.8 (minimum is now 3.9)
++ add PyPI trove classifiers (Python 3.9–3.14)
++ bump cibuildwheel to v4 and drop the stale prerelease-pythons flag
+
+# v0.33.0
++ modernize build system to use scikit-build-core (thanks @jhl-oai #327)
+
 # v0.32.0
 + make repeated `vcf()` calls restart from the beginning and isolate region
   queries from the main iterator state. *NOTE* this can be backwards incompatible for those using vcf() to iterate without an index.


=====================================
CMakeLists.txt
=====================================
@@ -0,0 +1,273 @@
+cmake_minimum_required(VERSION 3.20...3.30)
+
+project(${SKBUILD_PROJECT_NAME} LANGUAGES C)
+
+if(SKBUILD_STATE STREQUAL "sdist")
+  find_program(MAKE_EXECUTABLE NAMES make gmake mingw32-make REQUIRED)
+  execute_process(
+    COMMAND "${MAKE_EXECUTABLE}" htscodecs/htscodecs/version.h
+    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/htslib"
+    COMMAND_ERROR_IS_FATAL ANY
+  )
+  return()
+endif()
+
+find_package(Python COMPONENTS Interpreter Development.Module NumPy REQUIRED)
+
+set(CYVCF2_HTSLIB_CONFIGURE_OPTIONS "$ENV{CYVCF2_HTSLIB_CONFIGURE_OPTIONS}" CACHE STRING "Extra options passed to htslib configure")
+set(CYVCF2_HTSLIB_MODE "$ENV{CYVCF2_HTSLIB_MODE}" CACHE STRING "How to link htslib: BUILTIN or EXTERNAL")
+set(CYVCF2_CYTHONIZE_DEFAULT "$ENV{CYVCF2_CYTHONIZE}")
+if(CYVCF2_CYTHONIZE_DEFAULT STREQUAL "")
+  set(CYVCF2_CYTHONIZE_DEFAULT "$ENV{CYTHONIZE}")
+endif()
+set(CYVCF2_CYTHONIZE "${CYVCF2_CYTHONIZE_DEFAULT}" CACHE STRING "Whether to regenerate cyvcf2.c from Cython: AUTO, ON, or OFF")
+
+if(CYVCF2_HTSLIB_MODE STREQUAL "")
+  if(WIN32)
+    set(CYVCF2_HTSLIB_MODE "EXTERNAL")
+  else()
+    set(CYVCF2_HTSLIB_MODE "BUILTIN")
+  endif()
+endif()
+
+string(TOUPPER "${CYVCF2_HTSLIB_MODE}" CYVCF2_HTSLIB_MODE)
+set_property(CACHE CYVCF2_HTSLIB_MODE PROPERTY STRINGS BUILTIN EXTERNAL)
+set_property(CACHE CYVCF2_CYTHONIZE PROPERTY STRINGS AUTO ON OFF)
+
+if(NOT CYVCF2_HTSLIB_MODE STREQUAL "BUILTIN" AND NOT CYVCF2_HTSLIB_MODE STREQUAL "EXTERNAL")
+  message(FATAL_ERROR "CYVCF2_HTSLIB_MODE must be BUILTIN or EXTERNAL, got '${CYVCF2_HTSLIB_MODE}'")
+endif()
+
+set(CYVCF2_SOURCE_C "${CMAKE_CURRENT_SOURCE_DIR}/cyvcf2/cyvcf2.c")
+set(CYVCF2_GENERATED_C "${CMAKE_CURRENT_BINARY_DIR}/generated/cyvcf2.c")
+
+function(cyvcf2_boolish INPUT OUTPUT)
+  string(TOUPPER "${INPUT}" NORMALIZED_INPUT)
+  if(NORMALIZED_INPUT STREQUAL "" OR NORMALIZED_INPUT STREQUAL "AUTO")
+    set(${OUTPUT} "AUTO" PARENT_SCOPE)
+  elseif(NORMALIZED_INPUT MATCHES "^(1|ON|TRUE|YES)$")
+    set(${OUTPUT} "ON" PARENT_SCOPE)
+  elseif(NORMALIZED_INPUT MATCHES "^(0|OFF|FALSE|NO)$")
+    set(${OUTPUT} "OFF" PARENT_SCOPE)
+  else()
+    message(FATAL_ERROR "Expected a boolean-like value or AUTO, got '${INPUT}'")
+  endif()
+endfunction()
+
+cyvcf2_boolish("${CYVCF2_CYTHONIZE}" CYVCF2_CYTHONIZE_NORMALIZED)
+
+if(CYVCF2_CYTHONIZE_NORMALIZED STREQUAL "AUTO")
+  if(EXISTS "${CYVCF2_SOURCE_C}")
+    set(CYVCF2_CYTHONIZE_NORMALIZED "OFF")
+  else()
+    set(CYVCF2_CYTHONIZE_NORMALIZED "ON")
+  endif()
+endif()
+
+if(CYVCF2_CYTHONIZE_NORMALIZED)
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
+  add_custom_command(
+    OUTPUT "${CYVCF2_GENERATED_C}"
+    COMMAND Python::Interpreter -m cython
+            "${CMAKE_CURRENT_SOURCE_DIR}/cyvcf2/cyvcf2.pyx"
+            --output-file "${CYVCF2_GENERATED_C}"
+    DEPENDS
+      "${CMAKE_CURRENT_SOURCE_DIR}/cyvcf2/cyvcf2.pyx"
+      "${CMAKE_CURRENT_SOURCE_DIR}/cyvcf2/cyvcf2.pxd"
+    COMMENT "Generating cyvcf2.c from Cython"
+    VERBATIM
+  )
+  set(CYVCF2_EXTENSION_SOURCE "${CYVCF2_GENERATED_C}")
+else()
+  if(NOT EXISTS "${CYVCF2_SOURCE_C}")
+    message(FATAL_ERROR "CYVCF2_CYTHONIZE is OFF, but ${CYVCF2_SOURCE_C} does not exist")
+  endif()
+  set(CYVCF2_EXTENSION_SOURCE "${CYVCF2_SOURCE_C}")
+endif()
+
+function(cyvcf2_split_configure_options OUTPUT)
+  if(CYVCF2_HTSLIB_CONFIGURE_OPTIONS STREQUAL "")
+    set(${OUTPUT} "" PARENT_SCOPE)
+  else()
+    separate_arguments(_cyvcf2_configure_options UNIX_COMMAND "${CYVCF2_HTSLIB_CONFIGURE_OPTIONS}")
+    set(${OUTPUT} "${_cyvcf2_configure_options}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+function(cyvcf2_build_htslib STATIC_MODE)
+  find_program(MAKE_EXECUTABLE NAMES make gmake mingw32-make REQUIRED)
+
+  set(HTSLIB_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/htslib")
+  cyvcf2_split_configure_options(HTSLIB_CONFIGURE_OPTION_LIST)
+
+  if(NOT EXISTS "${HTSLIB_SOURCE_DIR}/config.status")
+    execute_process(
+      COMMAND autoreconf -i
+      WORKING_DIRECTORY "${HTSLIB_SOURCE_DIR}"
+      COMMAND_ERROR_IS_FATAL ANY
+    )
+
+    set(HTSLIB_CONFIGURE_COMMAND ./configure)
+    if(STATIC_MODE)
+      list(APPEND HTSLIB_CONFIGURE_COMMAND "CFLAGS=-fPIC")
+    endif()
+    list(APPEND HTSLIB_CONFIGURE_COMMAND ${HTSLIB_CONFIGURE_OPTION_LIST})
+
+    execute_process(
+      COMMAND ${HTSLIB_CONFIGURE_COMMAND}
+      WORKING_DIRECTORY "${HTSLIB_SOURCE_DIR}"
+      COMMAND_ERROR_IS_FATAL ANY
+    )
+  endif()
+
+  execute_process(
+    COMMAND "${MAKE_EXECUTABLE}" htscodecs/htscodecs/version.h
+    WORKING_DIRECTORY "${HTSLIB_SOURCE_DIR}"
+    COMMAND_ERROR_IS_FATAL ANY
+  )
+
+  if(STATIC_MODE)
+    set(HTSLIB_BUILD_TARGET libhts.a)
+  elseif(APPLE)
+    set(HTSLIB_BUILD_TARGET libhts.dylib)
+  elseif(WIN32)
+    set(HTSLIB_BUILD_TARGET hts-3.dll)
+  else()
+    set(HTSLIB_BUILD_TARGET libhts.so)
+  endif()
+
+  execute_process(
+    COMMAND "${MAKE_EXECUTABLE}" "${HTSLIB_BUILD_TARGET}"
+    WORKING_DIRECTORY "${HTSLIB_SOURCE_DIR}"
+    COMMAND_ERROR_IS_FATAL ANY
+  )
+endfunction()
+
+function(cyvcf2_read_htslib_static_libs OUTPUT)
+  set(HTSLIB_CONFIG_STATUS "${CMAKE_CURRENT_SOURCE_DIR}/htslib/config.status")
+  if(NOT EXISTS "${HTSLIB_CONFIG_STATUS}")
+    set(${OUTPUT} "" PARENT_SCOPE)
+    return()
+  endif()
+
+  file(READ "${HTSLIB_CONFIG_STATUS}" HTSLIB_CONFIG_STATUS_CONTENTS)
+  string(REGEX MATCH "S\\[[\"']static_LIBS[\"']\\][ ]*=[ ]*[\"']([^\"']*)[\"']" _static_libs_match "${HTSLIB_CONFIG_STATUS_CONTENTS}")
+  if(_static_libs_match)
+    separate_arguments(HTSLIB_STATIC_LIBS UNIX_COMMAND "${CMAKE_MATCH_1}")
+    set(${OUTPUT} "${HTSLIB_STATIC_LIBS}" PARENT_SCOPE)
+  else()
+    set(${OUTPUT} "" PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(CYVCF2_HTSLIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/htslib" "${CMAKE_CURRENT_SOURCE_DIR}/htslib/htslib")
+set(CYVCF2_HTSLIB_LIBRARIES "")
+set(CYVCF2_HTSLIB_TARGET "")
+
+if(CYVCF2_HTSLIB_MODE STREQUAL "BUILTIN")
+  if(WIN32)
+    message(FATAL_ERROR "CYVCF2_HTSLIB_MODE=BUILTIN is not supported on Windows")
+  endif()
+
+  message(STATUS "cyvcf2: building bundled htslib")
+  cyvcf2_build_htslib(TRUE)
+  cyvcf2_read_htslib_static_libs(CYVCF2_HTSLIB_STATIC_LIBS)
+
+  set(CYVCF2_HTSLIB_STATIC_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/htslib/libhts.a")
+  if(NOT EXISTS "${CYVCF2_HTSLIB_STATIC_LIBRARY}")
+    message(FATAL_ERROR "Expected ${CYVCF2_HTSLIB_STATIC_LIBRARY} after building htslib")
+  endif()
+
+  add_library(cyvcf2_htslib STATIC IMPORTED GLOBAL)
+  set_target_properties(cyvcf2_htslib PROPERTIES
+    IMPORTED_LOCATION "${CYVCF2_HTSLIB_STATIC_LIBRARY}"
+    INTERFACE_INCLUDE_DIRECTORIES "${CYVCF2_HTSLIB_INCLUDE_DIRS}"
+  )
+  set(CYVCF2_HTSLIB_TARGET cyvcf2_htslib)
+  list(APPEND CYVCF2_HTSLIB_LIBRARIES ${CYVCF2_HTSLIB_STATIC_LIBS})
+else()
+  find_package(PkgConfig QUIET)
+  if(PkgConfig_FOUND)
+    if(WIN32)
+      pkg_check_modules(HTSLIB_PC QUIET htslib)
+    else()
+      pkg_check_modules(HTSLIB_PC QUIET IMPORTED_TARGET htslib)
+    endif()
+  endif()
+
+  if(HTSLIB_PC_FOUND)
+    if(WIN32)
+      find_path(HTSLIB_INCLUDE_DIR htslib/hts.h)
+      if(NOT HTSLIB_INCLUDE_DIR)
+        message(FATAL_ERROR "Could not find external htslib headers")
+      endif()
+      list(APPEND CYVCF2_HTSLIB_INCLUDE_DIRS "${HTSLIB_INCLUDE_DIR}")
+      list(APPEND CYVCF2_HTSLIB_LIBRARIES ${HTSLIB_PC_LIBRARIES})
+    else()
+      list(APPEND CYVCF2_HTSLIB_LIBRARIES PkgConfig::HTSLIB_PC)
+    endif()
+  else()
+    find_path(HTSLIB_INCLUDE_DIR htslib/hts.h)
+    find_library(HTSLIB_LIBRARY NAMES hts hts-3 libhts)
+  endif()
+
+  if(NOT HTSLIB_PC_FOUND AND (NOT HTSLIB_INCLUDE_DIR OR NOT HTSLIB_LIBRARY))
+    if(WIN32)
+      message(FATAL_ERROR "Could not find external htslib; install htslib first")
+    endif()
+
+    message(STATUS "cyvcf2: external htslib not found; building bundled shared htslib")
+    cyvcf2_build_htslib(FALSE)
+    set(HTSLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/htslib")
+    find_library(HTSLIB_LIBRARY NAMES hts PATHS "${CMAKE_CURRENT_SOURCE_DIR}/htslib" NO_DEFAULT_PATH)
+  endif()
+
+  if(NOT HTSLIB_PC_FOUND AND NOT HTSLIB_LIBRARY)
+    message(FATAL_ERROR "Could not locate htslib library")
+  endif()
+
+  if(NOT HTSLIB_PC_FOUND)
+    list(APPEND CYVCF2_HTSLIB_INCLUDE_DIRS "${HTSLIB_INCLUDE_DIR}")
+    list(APPEND CYVCF2_HTSLIB_LIBRARIES "${HTSLIB_LIBRARY}")
+  endif()
+endif()
+
+Python_add_library(cyvcf2_extension MODULE WITH_SOABI
+  "${CYVCF2_EXTENSION_SOURCE}"
+  "${CMAKE_CURRENT_SOURCE_DIR}/cyvcf2/helpers.c"
+)
+
+set_target_properties(cyvcf2_extension PROPERTIES
+  OUTPUT_NAME "cyvcf2"
+)
+
+target_include_directories(cyvcf2_extension PRIVATE
+  "${CMAKE_CURRENT_SOURCE_DIR}/cyvcf2"
+  ${CYVCF2_HTSLIB_INCLUDE_DIRS}
+  ${Python_NumPy_INCLUDE_DIRS}
+)
+
+if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
+  target_compile_options(cyvcf2_extension PRIVATE
+    -Wno-sign-compare
+    -Wno-unused-function
+    -Wno-strict-prototypes
+    -Wno-unused-result
+  )
+endif()
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+  target_compile_options(cyvcf2_extension PRIVATE -Wno-discarded-qualifiers)
+endif()
+if(CMAKE_C_COMPILER_ID MATCHES "^(AppleClang|Clang)$")
+  target_compile_options(cyvcf2_extension PRIVATE -Wno-incompatible-pointer-types-discards-qualifiers)
+endif()
+
+if(CYVCF2_HTSLIB_TARGET)
+  target_link_libraries(cyvcf2_extension PRIVATE ${CYVCF2_HTSLIB_TARGET})
+endif()
+target_link_libraries(cyvcf2_extension PRIVATE ${CYVCF2_HTSLIB_LIBRARIES})
+
+install(TARGETS cyvcf2_extension
+  LIBRARY DESTINATION cyvcf2
+  RUNTIME DESTINATION cyvcf2
+)


=====================================
MANIFEST.in deleted
=====================================
@@ -1,18 +0,0 @@
-include LICENSE
-include *.md
-include cyvcf2/*.pyx
-include cyvcf2/*.pxd
-include cyvcf2/*.[ch]
-
-# exclude tests from pypi because no data file
-prune cyvcf2/tests
-
-# htslib
-include htslib/LICENSE htslib/README
-recursive-include htslib *.[ch]
-exclude htslib/*config*.h
-
-include htslib/configure.ac htslib/m4/*.m4 htslib/*.in
-include htslib/configure htslib/version.sh
-include htslib/Makefile htslib/*.mk
-exclude htslib/config.mk htslib/htscodecs.mk


=====================================
README.md
=====================================
@@ -11,7 +11,7 @@ The latest documentation for cyvcf2 can be found here:
 If you use cyvcf2, please cite the [paper](https://academic.oup.com/bioinformatics/article/2971439/cyvcf2)
 
 
-Fast python **(2 and 3)** parsing of VCF and BCF including region-queries.
+Fast Python **(3.9+)** parsing of VCF and BCF including region-queries.
 
 
 [![Build](https://github.com/brentp/cyvcf2/actions/workflows/build.yml/badge.svg)](https://github.com/brentp/cyvcf2/actions/workflows/build.yml)
@@ -75,35 +75,72 @@ all_vars = list(vcf())
 Installation
 ============
 
-## pip with bundled htslib
+## pip or uv with bundled htslib
+
+If a binary wheel is available for your platform, pip and uv will install it
+without building from source.
+
 ```
 pip install cyvcf2
+uv pip install cyvcf2
 ```
 
-## pip with system htslib
+## pip or uv with system htslib
 
 Assuming you have already built and installed htslib version 1.12 or higher.
 ```
 CYVCF2_HTSLIB_MODE=EXTERNAL pip install --no-binary cyvcf2 cyvcf2
+CYVCF2_HTSLIB_MODE=EXTERNAL uv pip install --no-binary cyvcf2 cyvcf2
 ```
 
-## windows (experimental, only test on MSYS2)
+## windows (experimental, only tested on MSYS2)
 
 Assuming you have already built and installed htslib.
 ```
-SETUPTOOLS_USE_DISTUTILS=stdlib pip install cyvcf2
+CYVCF2_HTSLIB_MODE=EXTERNAL pip install cyvcf2
 ```
 
 ## github (building htslib and cyvcf2 from source)
 
 ```
 git clone --recursive https://github.com/brentp/cyvcf2
-pip install -r requirements.txt
-# sometimes it can be required to remove old files:
-# python setup.py clean_ext
-CYVCF2_HTSLIB_MODE=BUILTIN CYTHONIZE=1 python setup.py install
+cd cyvcf2
+CYVCF2_HTSLIB_MODE=BUILTIN python -m pip install .
+CYVCF2_HTSLIB_MODE=BUILTIN uv pip install .
 # or to use a system htslib.so
-CYVCF2_HTSLIB_MODE=EXTERNAL python setup.py install
+CYVCF2_HTSLIB_MODE=EXTERNAL python -m pip install .
+CYVCF2_HTSLIB_MODE=EXTERNAL uv pip install .
+```
+
+Source builds use scikit-build-core and CMake. Python build dependencies such
+as Cython, NumPy, CMake, and Ninja are installed by PEP 517 build isolation, but
+you still need a C compiler plus the htslib native dependencies for your
+platform.
+
+Most users can leave `CYVCF2_CYTHONIZE` unset. It is mainly for developers
+building from a Git checkout who need to control when CMake regenerates
+`cyvcf2.c` from `cyvcf2/cyvcf2.pyx`:
+
+- `AUTO` (the default) uses an existing `cyvcf2/cyvcf2.c` when present and runs
+  Cython only when the file is missing.
+- `ON` regenerates the C file after edits to `cyvcf2.pyx` or `cyvcf2.pxd`, or
+  when testing Cython-generated output.
+- `OFF` requires an existing C file.
+
+For example:
+
+```
+CYVCF2_CYTHONIZE=ON python -m pip install .
+python -m pip install . --config-settings=cmake.define.CYVCF2_CYTHONIZE=ON
+```
+
+The legacy `CYTHONIZE=1` environment variable is still accepted.
+
+To build source distributions and wheels locally:
+
+```
+python -m build
+uv build
 ```
 
 On **OSX**, using brew, you may have to set the following as indicated by the brew install:
@@ -124,6 +161,7 @@ Install `pytest`, then tests can be run with:
 
 ```
 pytest
+uv run --with pytest pytest
 ```
 
 CLI


=====================================
ci/Dockerfile.alpine.test
=====================================
@@ -4,12 +4,12 @@ FROM python:${PYTHON_VERSION}
 
 WORKDIR /workspace
 
-RUN apk add --no-cache build-base autoconf automake git xz-dev curl-dev libdeflate-dev bzip2-dev
+RUN apk add --no-cache build-base autoconf automake cmake ninja git xz-dev curl-dev libdeflate-dev bzip2-dev
 
-COPY . .
+RUN pip install pytest pytest-cov
 
-RUN pip install -r requirements.txt && pip install pytest pytest-cov
+COPY . .
 
 # build cyvcf2
 RUN CYVCF2_HTSLIB_CONFIGURE_OPTIONS="--enable-libcurl --enable-s3 --enable-lzma --enable-bz2 --with-libdeflate" \
-    CYTHONIZE=1 python setup.py build_ext -i
+    python -m pip install -v -e .


=====================================
ci/Dockerfile.slim.test
=====================================
@@ -4,14 +4,14 @@ FROM python:${PYTHON_VERSION}
 
 WORKDIR /workspace
 
-RUN apt-get update && apt-get install --no-install-recommends -y autoconf automake gcc libcurl4-openssl-dev \
-    make git libc6-dev zlib1g-dev libssl-dev liblzma-dev libbz2-dev libdeflate-dev && \
+RUN apt-get update && apt-get install --no-install-recommends -y autoconf automake cmake gcc libcurl4-openssl-dev \
+    make ninja-build git libc6-dev zlib1g-dev libssl-dev liblzma-dev libbz2-dev libdeflate-dev && \
     rm -rf /var/lib/apt/lists/*
 
-COPY . .
+RUN pip install pytest pytest-cov
 
-RUN pip install -r requirements.txt && pip install pytest pytest-cov
+COPY . .
 
 # build cyvcf2
 RUN CYVCF2_HTSLIB_CONFIGURE_OPTIONS="--enable-libcurl --enable-s3 --enable-lzma --enable-bz2 --with-libdeflate" \
-    CYTHONIZE=1 python setup.py build_ext -i
+    python -m pip install -v -e .


=====================================
ci/linux-deps
=====================================
@@ -14,14 +14,14 @@ echo "manylinux image woking on $ID"
 # Install cyvcf2 development files.
 case "$ID" in
 almalinux)
-    dnf install -y bzip2-devel xz-devel libcurl-devel openssl-devel openblas-devel epel-release
+    dnf install -y bzip2-devel cmake ninja-build xz-devel libcurl-devel openssl-devel openblas-devel epel-release
 
     # packages at epel
     dnf install -y libdeflate-devel
     ;;
 
 alpine)
-    apk add --no-cache xz-dev curl-dev libdeflate-dev
+    apk add --no-cache cmake ninja xz-dev curl-dev libdeflate-dev
     ;;
 
 centos)


=====================================
ci/osx-deps
=====================================
@@ -10,7 +10,7 @@ COMMON_CMAKE_ARGS=(
   -DCMAKE_BUILD_TYPE=Release
 )
 
-brew install automake libdeflate
+brew install automake cmake libdeflate ninja
 brew unlink xz
 
 # build liblzma and libdeflate for deployment target


=====================================
cyvcf2/__init__.py
=====================================
@@ -1,5 +1,4 @@
+from ._version import __version__
 from .cyvcf2 import (VCF, Variant, Writer, r_ as r_unphased, par_relatedness,
                      par_het)
 Reader = VCFReader = VCF
-
-__version__ = "0.32.0"


=====================================
cyvcf2/_version.py
=====================================
@@ -0,0 +1 @@
+__version__ = "0.34.0"


=====================================
cyvcf2/cyvcf2.pxd
=====================================
@@ -3,6 +3,9 @@ import numpy as np
 cimport numpy as np
 np.import_array()
 
+cdef extern from "htslib/hts_log.h":
+    void hts_set_log_level(int val)
+
 cdef extern from "string.h":
     void* memcpy(void* dest, const void* src, size_t n)
 


=====================================
cyvcf2/cyvcf2.pyx
=====================================
@@ -30,6 +30,47 @@ from cython cimport view
 
 from cpython.version cimport PY_MAJOR_VERSION
 
+def set_htslib_log_level(int level):
+    """
+    Set htslib log level. 
+
+    Parameters:
+    level (int): The desired log level. Per htslib's documentation (https://www.htslib.org/doc/samtools.html), the following values are supported:
+        - 0 or 1: Suppress some error messages.
+        - 2: Reduce warning messages.
+        - 3: Default level (HTS_LOG_WARNING).
+        - >3: Increase verbosity with additional warnings and logging messages.
+    """
+    hts_set_log_level(level)
+
+
+def _init_logging_from_env():
+    """
+    If a valid CYVCF2_HTSLIB_LOG_LEVEL environment variable exists, use it to set htslib log level. 
+    Otherwise use the htslib default: 3 (HTS_LOG_WARNING).
+    """
+    HTSLIB_DEFAULT = 3
+
+    s = stdlib.getenv("CYVCF2_HTSLIB_LOG_LEVEL")
+    if s is NULL:
+        set_htslib_log_level(HTSLIB_DEFAULT)
+        return
+
+    py_s = s.decode()
+    try:
+        level = int(py_s)
+        if level < 0:
+            warnings.warn(f"CYVCF2_HTSLIB_LOG_LEVEL={py_s!r} is not a non-negative integer; using htslib default")
+            level = HTSLIB_DEFAULT
+    except (ValueError, TypeError):
+        warnings.warn(f"CYVCF2_HTSLIB_LOG_LEVEL={py_s!r} is not a valid integer; using htslib default")
+        level = HTSLIB_DEFAULT
+
+    set_htslib_log_level(level)
+
+_init_logging_from_env()
+
+
 # overcome lack of __file__ in cython
 import inspect
 if not hasattr(sys.modules[__name__], '__file__'):


=====================================
cyvcf2/helpers.h
=====================================
@@ -1,4 +1,5 @@
 #include <htslib/vcf.h>
+#include <htslib/hts_log.h>
 #include <htslib/khash.h>
 
 int as_gts(int *gts, int num_samples, int ploidy, int strict_gt, int HOM_ALT, int UNKNOWN);


=====================================
cyvcf2/tests/test.warnings.vcf.gz
=====================================
Binary files /dev/null and b/cyvcf2/tests/test.warnings.vcf.gz differ


=====================================
cyvcf2/tests/test.warnings.vcf.gz.tbi
=====================================
Binary files /dev/null and b/cyvcf2/tests/test.warnings.vcf.gz.tbi differ


=====================================
cyvcf2/tests/test_cli.py
=====================================
@@ -1,16 +1,72 @@
+import os
 from cyvcf2 import VCF
-import os.path
-
 from click.testing import CliRunner
-
 from cyvcf2.cli import cyvcf2
+from cyvcf2.cyvcf2 import set_htslib_log_level, _init_logging_from_env
 
 HERE = os.path.dirname(__file__)
 VCF_PATH = os.path.join(HERE, "test.vcf.gz")
+VCF_WITH_WARNINGS_PATH = os.path.join(HERE, "test.warnings.vcf.gz")
 
 
-#Sanity check for cli
+# Sanity check for cli
 def test_version():
     runner = CliRunner()
     result = runner.invoke(cyvcf2, [VCF_PATH])
     assert result.exit_code == 0
+
+
+def test_htslib_log_level(capfd):
+    runner = CliRunner()
+
+    for log_level in [4, 5]:
+        set_htslib_log_level(log_level)
+        _ = runner.invoke(cyvcf2, [VCF_WITH_WARNINGS_PATH])
+        captured = capfd.readouterr()
+        assert "[W::vcf_parse_format" in captured.err
+        assert "[I::hts_idx_check_local" in captured.err
+
+    set_htslib_log_level(3)
+    _ = runner.invoke(cyvcf2, [VCF_WITH_WARNINGS_PATH])
+    captured = capfd.readouterr()
+    assert "[W::vcf_parse_format" in captured.err
+    assert "[I::hts_idx_check_local" not in captured.err
+
+    for log_level in [0, 1, 2]:
+        set_htslib_log_level(log_level)
+        _ = runner.invoke(cyvcf2, [VCF_WITH_WARNINGS_PATH])
+        captured = capfd.readouterr()
+        assert "[W::vcf_parse_format" not in captured.err
+        assert "[I::hts_idx_check_local" not in captured.err
+
+
+def test_htslib_log_level_env(monkeypatch, capfd):
+    runner = CliRunner()
+
+    monkeypatch.setenv("CYVCF2_HTSLIB_LOG_LEVEL", "5")
+    _init_logging_from_env()
+    _ = runner.invoke(cyvcf2, [VCF_WITH_WARNINGS_PATH])
+    captured = capfd.readouterr()
+    assert "[W::vcf_parse_format" in captured.err
+    assert "[I::hts_idx_check_local" in captured.err
+
+    monkeypatch.setenv("CYVCF2_HTSLIB_LOG_LEVEL", "3")
+    _init_logging_from_env()
+    _ = runner.invoke(cyvcf2, [VCF_WITH_WARNINGS_PATH])
+    captured = capfd.readouterr()
+    assert "[W::vcf_parse_format" in captured.err
+    assert "[I::hts_idx_check_local" not in captured.err
+
+    monkeypatch.setenv("CYVCF2_HTSLIB_LOG_LEVEL", "0")
+    _init_logging_from_env()
+    _ = runner.invoke(cyvcf2, [VCF_WITH_WARNINGS_PATH])
+    captured = capfd.readouterr()
+    assert "[W::vcf_parse_format" not in captured.err
+    assert "[I::hts_idx_check_local" not in captured.err
+
+    monkeypatch.delenv("CYVCF2_HTSLIB_LOG_LEVEL", raising=False)
+    _init_logging_from_env()
+    _ = runner.invoke(cyvcf2, [VCF_WITH_WARNINGS_PATH])
+    captured = capfd.readouterr()
+    assert "[W::vcf_parse_format" in captured.err
+    assert "[I::hts_idx_check_local" not in captured.err


=====================================
debian/changelog
=====================================
@@ -1,3 +1,16 @@
+cyvcf2 (0.34.0-1) UNRELEASED; urgency=medium
+
+  * Packaging update (routine-update)
+  * New upstream version
+  * Standards-Version: 4.7.4 (routine-update)
+  * Reflow Uploaders field (cme)
+  * Build-Depends: pybuild-plugin-pyproject (routine-update)
+  * Set upstream metadata fields: Documentation.
+  * Build-Depends: python3-scikit-build-core, cmake
+  * Build-Depends: dh-sequence-single-binary
+
+ -- Andreas Tille <tille at debian.org>  Mon, 21 Sep 2026 10:19:35 +0200
+
 cyvcf2 (0.32.0-1) unstable; urgency=medium
 
   * New upstream version 0.32.0


=====================================
debian/control
=====================================
@@ -1,27 +1,33 @@
 Source: cyvcf2
+Standards-Version: 4.7.4
 Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Steffen Moeller <moeller at debian.org>,
-           Liubov Chuprikova <chuprikovalv at gmail.com>,
-           Étienne Mollier <emollier at debian.org>
+Uploaders:
+ Steffen Moeller <moeller at debian.org>,
+ Liubov Chuprikova <chuprikovalv at gmail.com>,
+ Étienne Mollier <emollier at debian.org>,
 Section: science
 Testsuite: autopkgtest-pkg-python
-Build-Depends: debhelper-compat (= 13),
-               dh-sequence-python3,
-               zlib1g-dev,
-               cython3,
-               python3-setuptools,
-               python3-all-dev,
-               python3-numpy,
-               python3-numpy-dev,
-               python3-coloredlogs,
-               python3-click,
-               python3-pytest,
-               libssl-dev,
-               libcurl4-gnutls-dev|libcurl4-openssl-dev,
-               libhts-dev,
-               libbz2-dev,
-               help2man <!nodoc>
-Standards-Version: 4.7.3
+Build-Depends:
+ debhelper-compat (= 14),
+ dh-sequence-python3,
+ dh-sequence-single-binary,
+ zlib1g-dev,
+ cmake,
+ cython3,
+ pybuild-plugin-pyproject,
+ python3-setuptools,
+ python3-all-dev,
+ python3-numpy,
+ python3-numpy-dev,
+ python3-coloredlogs,
+ python3-click,
+ python3-scikit-build-core,
+ python3-pytest,
+ libssl-dev,
+ libcurl4-gnutls-dev|libcurl4-openssl-dev,
+ libhts-dev,
+ libbz2-dev,
+ help2man <!nodoc>,
 Vcs-Browser: https://salsa.debian.org/med-team/cyvcf2
 Vcs-Git: https://salsa.debian.org/med-team/cyvcf2.git
 Homepage: https://github.com/brentp/cyvcf2
@@ -29,11 +35,12 @@ Homepage: https://github.com/brentp/cyvcf2
 Package: python3-cyvcf2
 Architecture: any
 Section: python
-Depends: ${python3:Depends},
-         ${misc:Depends},
-         ${shlibs:Depends},
-         python3-coloredlogs,
-         python3-click
+Depends:
+ ${python3:Depends},
+ ${misc:Depends},
+ ${shlibs:Depends},
+ python3-coloredlogs,
+ python3-click,
 Description: VCF parser based on htslib (Python 3)
  This modules allows fast parsing of VCF and BCF including region-queries
  with Python. This is essential for efficient analyses of nucleotide


=====================================
debian/upstream/metadata
=====================================
@@ -1,5 +1,6 @@
 Bug-Database: https://github.com/brentp/cyvcf2/issues
 Bug-Submit: https://github.com/brentp/cyvcf2/issues/new
+Documentation: https://brentp.github.io/cyvcf2/
 Reference:
  - Author: Brent S. Pedersen and Aaron R. Quinlan
    Title: "cyvcf2: fast, flexible variant analysis with Python"


=====================================
pyproject.toml
=====================================
@@ -1,10 +1,85 @@
 [build-system]
 requires = [
-  "setuptools",
-  "wheel",
-  'cython>=0.23.3,<3; python_version < "3.9"',
-  'cython>=3; python_version >= "3.9"',
-  'oldest-supported-numpy; os_name != "nt" and python_version < "3.9"',
-  'numpy; os_name == "nt" and python_version < "3.9"',
-  'numpy>=2.0.0; python_version >= "3.9"',
+  "scikit-build-core>=0.11",
+  "cython>=3",
+  "numpy>=2.0.0",
 ]
+build-backend = "scikit_build_core.build"
+
+[project]
+name = "cyvcf2"
+dynamic = ["version"]
+description = "fast vcf parsing with cython + htslib"
+readme = "README.md"
+requires-python = ">=3.9"
+license = { text = "MIT" }
+authors = [
+  { name = "Brent Pedersen", email = "bpederse at gmail.com" },
+]
+classifiers = [
+  "Development Status :: 5 - Production/Stable",
+  "Intended Audience :: Science/Research",
+  "Operating System :: POSIX :: Linux",
+  "Operating System :: MacOS",
+  "Operating System :: Microsoft :: Windows",
+  "Programming Language :: Python :: 3",
+  "Programming Language :: Python :: 3.9",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
+  "Programming Language :: Python :: 3.13",
+  "Programming Language :: Python :: 3.14",
+  "Programming Language :: Python :: Implementation :: CPython",
+  "Programming Language :: Python :: Implementation :: PyPy",
+  "Programming Language :: Cython",
+  "Topic :: Scientific/Engineering :: Bio-Informatics",
+]
+dependencies = [
+  "numpy>=2.0.0",
+  "coloredlogs",
+  "click",
+]
+
+[project.urls]
+Homepage = "https://github.com/brentp/cyvcf2/"
+Documentation = "https://brentp.github.io/cyvcf2/"
+
+[project.scripts]
+cyvcf2 = "cyvcf2.__main__:cli"
+
+[tool.scikit-build]
+wheel.packages = ["cyvcf2"]
+wheel.exclude = [
+  "cyvcf2/tests/**",
+  "cyvcf2/*.c",
+  "cyvcf2/*.h",
+  "cyvcf2/*.pxd",
+  "cyvcf2/*.pyx",
+]
+sdist.cmake = true
+sdist.include = [
+  "CMakeLists.txt",
+  "htslib/htscodecs/htscodecs/version.h",
+]
+sdist.exclude = [
+  "cyvcf2/tests/**",
+  "htslib/test/**",
+  "htslib/htscodecs/tests/**",
+  "htslib/htscodecs/javascript/**",
+  "htslib/config.status",
+  "htslib/config.mk",
+  "htslib/htscodecs.mk",
+  "htslib/*.o",
+  "htslib/**/*.o",
+  "htslib/*.a",
+  "htslib/*.so",
+  "htslib/*.dylib",
+  "htslib/*.dll",
+]
+
+[tool.scikit-build.metadata.version]
+provider = "scikit_build_core.metadata.regex"
+input = "cyvcf2/_version.py"
+
+[tool.pytest.ini_options]
+testpaths = ["cyvcf2/tests"]


=====================================
requirements.txt
=====================================
@@ -1,8 +1,5 @@
-cython>=0.23.3,<3; python_version < "3.9"
-cython>=3; python_version >= "3.9"
+cython>=3
 coloredlogs
 click
 setuptools
-oldest-supported-numpy; os_name != "nt" and python_version < "3.9"
-numpy; os_name == "nt" and python_version < "3.9"
-numpy>=2.0.0; python_version >= "3.9"
+numpy>=2.0.0


=====================================
setup.cfg deleted
=====================================
@@ -1,2 +0,0 @@
-[tool:pytest]
-testpaths = cyvcf2/tests


=====================================
setup.py deleted
=====================================
@@ -1,302 +0,0 @@
-import ctypes
-import glob
-import os
-import platform
-import shutil
-import sys
-import subprocess
-
-from setuptools import setup, Extension, Command
-from setuptools.command.build_ext import build_ext
-from setuptools.command.sdist import sdist
-
-if sys.version_info.major == 2 and sys.version_info.minor != 7:
-    sys.stderr.write(
-        "ERROR: cyvcf2 is only for python 2.7 or greater you are running %d.%d\n",
-        (sys.version_info.major, sys.version_info.minor),
-    )
-    sys.exit(1)
-
-import numpy as np
-
-
-def get_version():
-    """Get the version info from the mpld3 package without importing it"""
-    import ast
-
-    with open(os.path.join("cyvcf2", "__init__.py"), "r") as init_file:
-        module = ast.parse(init_file.read())
-
-    version = (
-        ast.literal_eval(node.value)
-        for node in ast.walk(module)
-        if isinstance(node, ast.Assign) and node.targets[0].id == "__version__"
-    )
-    try:
-        return next(version)
-    except StopIteration:
-        raise ValueError("version could not be located")
-
-
-def no_cythonize(extensions, **_ignore):
-    for extension in extensions:
-        sources = []
-        for sfile in extension.sources:
-            path, ext = os.path.splitext(sfile)
-            if ext in (".pyx", ".py"):
-                sfile = path + ".c"
-            sources.append(sfile)
-        extension.sources[:] = sources
-    return extensions
-
-
-def check_libhts():
-    os_type = platform.system()
-    if os_type == "Linux":
-        lib_name = "libhts.so"
-    elif os_type == "Darwin":  # macOS
-        lib_name = "libhts.dylib"
-    elif os_type == "Windows":
-        lib_name = "hts-3.dll"
-    else:
-        return False  # Unsupported OS
-
-    try:
-        ctypes.CDLL(lib_name)
-        return True
-    except Exception:
-        return False
-
-
-def build_htslib(htslib_configure_options, static_mode):
-    current_directory = os.getcwd()
-    os.chdir(os.path.join(current_directory, "htslib"))
-
-    if os.path.exists("config.status"):
-        print("# cyvcf2: config.status exists, skip configure htslib")
-    else:
-        subprocess.run(["autoreconf", "-i"], check=True)
-
-        configure_args = ["./configure"]
-        if static_mode:
-            configure_args.append("CFLAGS=-fPIC")
-        if htslib_configure_options:
-            configure_args.extend(htslib_configure_options.split())
-
-        subprocess.run(configure_args, check=True)
-    subprocess.run(["make"], check=True)
-
-    os.chdir(current_directory)
-
-
-def pre_sdist():
-    current_directory = os.getcwd()
-    os.chdir(os.path.join(current_directory, "htslib"))
-
-    # generate version.h
-    subprocess.run(["make", "htscodecs/htscodecs/version.h"], check=True)
-
-    # remove redundant file
-    redudant_files = ["htscodecs.mk"]
-    for redudant_file in redudant_files:
-        if os.path.exists(redudant_file):
-            os.remove(redudant_file)
-
-    os.chdir(current_directory)
-
-
-class cyvcf2_build_ext(build_ext):
-    def run(self):
-        print("# cyvcf2: htslib mode is {}".format(CYVCF2_HTSLIB_MODE))
-        if CYVCF2_HTSLIB_MODE == "BUILTIN" or not check_libhts():
-            if platform.system() == "Windows":
-                # Windows htslib can't be built internall
-                raise RuntimeError(
-                    "Required library 'hts-3.dll' not found. Please install htslib first."
-                )
-
-            print(
-                "# cyvcf2: htslib configure options is {}".format(
-                    CYVCF2_HTSLIB_CONFIGURE_OPTIONS
-                )
-            )
-            build_htslib(
-                CYVCF2_HTSLIB_CONFIGURE_OPTIONS, CYVCF2_HTSLIB_MODE == "BUILTIN"
-            )
-
-        if CYVCF2_HTSLIB_MODE == "BUILTIN":
-            # add htslib linked libraries
-            # libcrypto is bundled with libssl, capabale to replace libssl
-            all_dynamic_libs = {
-                "z",
-                "bz2",
-                "lzma",
-                "curl",
-                "deflate",
-                "crypto",
-            }
-
-            extra_libs = []
-            # read the htslib config.status file to get the linked libraries
-            with open(os.path.join("htslib", "config.status"), "r") as f:
-                for line in f:
-                    if 'S["static_LIBS"]' in line:
-                        linked_libs_str = line.split("=")[1].strip()[1:-1]
-                        print("# cyvcf2: htslib librarys is {}".format(linked_libs_str))
-                        linked_libs = linked_libs_str.split()
-                        for lib in linked_libs:
-                            if lib[2:] in all_dynamic_libs:  # remove -l prefix
-                                extra_libs.append(lib[2:])
-
-            self.extensions[0].libraries = self.extensions[0].libraries + extra_libs
-
-        super().run()
-
-
-class cyvcf2_sdist(sdist):
-    def run(self):
-        if platform.system() == "Windows":
-            raise RuntimeError("cyvcf2 Can't build sdist on Windows")
-
-        pre_sdist()
-
-        super().run()
-
-
-class clean_ext(Command):
-    description = "clean up Cython temporary files and htslib build files"
-    user_options = []
-
-    def initialize_options(self):
-        pass
-
-    def finalize_options(self):
-        pass
-
-    def run(self):
-        print("cleaning build files")
-        if os.path.exists("build"):
-            shutil.rmtree("build")
-
-        if os.path.exists("cyvcf2.egg-info"):
-            shutil.rmtree("cyvcf2.egg-info")
-
-        print("cleaning Cython temporary files")
-        cyvcf2_c_path = os.path.join("cyvcf2", "cyvcf2.c")
-        if os.path.exists(cyvcf2_c_path):
-            os.remove(cyvcf2_c_path)
-
-        lib_files = glob.glob("cyvcf2/cyvcf2.cpython-*")
-        for file in lib_files:
-            os.remove(file)
-
-        if platform.system() != "Windows":
-            print("cleaning htslib build files")
-            current_directory = os.getcwd()
-            os.chdir(os.path.join(current_directory, "htslib"))
-            subprocess.run(["make", "distclean"], check=True)
-            os.chdir(current_directory)
-
-
-# How to link against HTSLIB
-# BUILTIN:  build and static link against htslib from
-#           builtin htslib code (default)
-# EXTERNAL: use shared libhts.so compiled outside of
-#           cyvcf2
-if platform.system() == "Windows":
-    # can't static link to htslib on Windows
-    htslib_mode_default = "EXTERNAL"
-else:
-    htslib_mode_default = "BUILTIN"
-
-CYVCF2_HTSLIB_MODE = os.environ.get("CYVCF2_HTSLIB_MODE", htslib_mode_default)
-
-if platform.system() == "Windows" and CYVCF2_HTSLIB_MODE == "BUILTIN":
-    print(
-        "# cyvcf2 WARNING: The use of cyvcf2 on Windows is experimental. It will not work when statically linked to htslib. Fallback to htslib EXTERNAL mode"
-    )
-    CYVCF2_HTSLIB_MODE = "EXTERNAL"
-
-CYVCF2_HTSLIB_CONFIGURE_OPTIONS = os.environ.get(
-    "CYVCF2_HTSLIB_CONFIGURE_OPTIONS", None
-)
-
-htslib_objects = []
-htslib_librarys = []
-htslib_library_dirs = []
-
-if CYVCF2_HTSLIB_MODE == "BUILTIN":
-    htslib_objects = ["htslib/libhts.a"]
-else:
-    htslib_librarys = ["hts"]
-    if not check_libhts():
-        htslib_library_dirs = ["htslib"]
-
-htslib_include_dirs = ["htslib", "htslib/htslib"]
-
-# Build the Cython extension by statically linking to the bundled htslib
-sources = ["cyvcf2/cyvcf2.pyx", "cyvcf2/helpers.c"]
-
-extensions = [
-    Extension(
-        "cyvcf2.cyvcf2",
-        sources,
-        extra_objects=htslib_objects,
-        libraries=htslib_librarys,
-        extra_compile_args=[
-            "-Wno-sign-compare",
-            "-Wno-unused-function",
-            "-Wno-strict-prototypes",
-            "-Wno-unused-result",
-            "-Wno-discarded-qualifiers",
-        ],
-        include_dirs=["cyvcf2", np.get_include()] + htslib_include_dirs,
-        library_dirs=htslib_library_dirs,
-    )
-]
-
-
-CYTHONIZE = bool(int(os.getenv("CYTHONIZE", 0)))
-if CYTHONIZE:
-    try:
-        from Cython.Build import cythonize
-    except ImportError:
-        sys.stderr.write(
-            "Cannot find Cython. Have you installed all the requirements?\n"
-            "Try pip install -r requirements.txt\n"
-        )
-        sys.exit(1)
-    compiler_directives = {"language_level": 2, "embedsignature": True}
-    extensions = cythonize(extensions, compiler_directives=compiler_directives)
-else:
-    extensions = no_cythonize(extensions)
-
-
-setup(
-    name="cyvcf2",
-    description="fast vcf parsing with cython + htslib",
-    long_description_content_type="text/markdown",
-    url="https://github.com/brentp/cyvcf2/",
-    long_description=open("README.md").read(),
-    license="MIT",
-    author="Brent Pedersen",
-    author_email="bpederse at gmail.com",
-    version=get_version(),
-    ext_modules=extensions,
-    packages=["cyvcf2"],
-    entry_points=dict(
-        console_scripts=[
-            "cyvcf2 = cyvcf2.__main__:cli",
-        ],
-    ),
-    python_requires=">=3.7",
-    # make sure to keep this numpy version in sync with build.yml
-    install_requires=["numpy>=1.16.0", "coloredlogs", "click"],
-    include_package_data=True,
-    zip_safe=False,
-    cmdclass={
-        "clean_ext": clean_ext,
-        "build_ext": cyvcf2_build_ext,
-        "sdist": cyvcf2_sdist,
-    },
-)



View it on GitLab: https://salsa.debian.org/med-team/cyvcf2/-/compare/9b5f9390893ec117f9047d694f49f3560b784631...c76d3e28fea9f9d80e4c8b1385a3a9ea179a7c87

-- 
View it on GitLab: https://salsa.debian.org/med-team/cyvcf2/-/compare/9b5f9390893ec117f9047d694f49f3560b784631...c76d3e28fea9f9d80e4c8b1385a3a9ea179a7c87
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20260921/2b06b7c3/attachment-0001.htm>


More information about the debian-med-commit mailing list