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
11 changes: 3 additions & 8 deletions .ado/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ resources:

parameters:
- name: buildAndTest
displayName: 'Build and test binar/paulimer/pauliverse (uncheck to publish only)'
displayName: 'Build and test native packages (required for qodec Python publication)'
type: boolean
default: true
- name: buildAndTestDeqRuntime
Expand Down Expand Up @@ -131,7 +131,7 @@ extends:
- template: stages/build.yaml@self
parameters:
platforms: ${{ parameters.platforms }}
buildAndTest: ${{ parameters.buildAndTest }}
buildAndTest: ${{ or(parameters.buildAndTest, parameters.publishQodecPython) }}
buildDeqRuntime: ${{ or(parameters.publishDeqRuntimePython, parameters.buildAndTestDeqRuntime) }}

- template: stages/publish_crate.yaml@self
Expand Down Expand Up @@ -176,12 +176,6 @@ extends:
platforms: ${{ parameters.platforms }}
publishPackage: ${{ parameters.publishDeqRuntimePython }}

- template: stages/publish_python_package.yaml@self
parameters:
packageName: qodec
packagePath: qodec/bindings/python
publishPackage: ${{ parameters.publishQodecPython }}

- template: stages/publish_crate.yaml@self
parameters:
platforms: ${{ parameters.platforms }}
Expand All @@ -208,6 +202,7 @@ extends:
publishBinarPython: ${{ parameters.publishBinarPython }}
publishPaulimerPython: ${{ parameters.publishPaulimerPython }}
publishDeqagramPython: ${{ parameters.publishDeqagramPython }}
publishQodecPython: ${{ parameters.publishQodecPython }}

- template: stages/publish_wasm_wheels.yaml@self
parameters:
Expand Down
51 changes: 40 additions & 11 deletions .ado/stages/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ stages:
variables:
arch: ${{ platform.arch }}
# Portable RUSTFLAGS shared by test builds AND published wheels so the
# binaries we test match the ones we ship. x86_64 picks the x86-64-v3
# CPU requirements match. x86_64 picks the x86-64-v3
# microarchitecture level (Westmere AES, AVX2); aarch64 enables the
# AES extension that `gxhash` (via `ptr_hash 2`) requires.
${{ if eq(platform.arch, 'aarch64') }}:
Expand All @@ -67,6 +67,11 @@ stages:
condition: succeeded()
targetPath: $(System.DefaultWorkingDirectory)/target/wheels
artifactName: ${{ platform.name }}-wheels
- output: pipelineArtifact
displayName: "Upload Rust build timings"
condition: succeededOrFailed()
targetPath: $(System.DefaultWorkingDirectory)/target/cargo-timings
artifactName: ${{ platform.name }}-rust-timings
steps:
- template: _platform_setup_steps.yaml
parameters:
Expand All @@ -76,12 +81,17 @@ stages:
displayName: Install cbindgen

# the cargo test below requires the shared library to be built first
- script: cargo build -p deq-decoder-reference-plugin --release
- script: cargo build -p deq-decoder-reference-plugin --profile ci-test
displayName: Build deq-decoder-reference-plugin cdylib
env:
RUSTFLAGS: $(rustflagsPortable)

- script: cargo test --workspace --exclude deq-runtime --exclude binar-python --exclude paulimer-bindings --release --all-features
- script: cargo build -p qodec-c --profile ci-test
displayName: Build qodec C libraries
env:
RUSTFLAGS: $(rustflagsPortable)

- script: cargo test --workspace --exclude deq-runtime --exclude binar-python --exclude paulimer-bindings --profile ci-test --all-features --timings
displayName: Build and test all Rust crates (except deq-runtime + Python shims)
env:
RUSTFLAGS: $(rustflagsPortable)
Expand Down Expand Up @@ -116,11 +126,12 @@ stages:
# agents ship a much newer glibc (Ubuntu 22.04 = 2.35, Azure
# Linux 3 = 2.38) and a native link would raise the wheel's
# glibc floor above the tag we claim.
pip install "maturin[zig]" pytest hypothesis more-itertools numpy
python -m pip install -r requirements-build.txt pytest hypothesis more-itertools numpy
export CARGO_ZIGBUILD_PYTHON_PATH="$PWD/tools/zig.py"
maturin_args+=(--zig --target "$(uname -m)-unknown-linux-gnu" --compatibility $(manylinuxTag))
;;
Darwin)
pip install maturin pytest hypothesis more-itertools numpy
python -m pip install -r requirements-build.txt pytest hypothesis more-itertools numpy
;;
*)
echo "Unsupported Unix platform: $(uname -s)" >&2
Expand All @@ -145,11 +156,15 @@ stages:
build_and_test paulimer
build_and_test deq/deqagram --import-mode=importlib

maturin build "${maturin_args[@]}" --manifest-path qodec/bindings/python/Cargo.toml --out target/qodec-wheels
python qodec/tools/check_wheel.py target/qodec-wheels
cp target/qodec-wheels/qodec-*.whl "$wheelhouse/"

if [[ "$(uname -s)" == "Linux" ]]; then
ls target/wheels/*$(manylinuxTag)*.whl
fi
ls -la target/wheels/
displayName: Build + test binar/paulimer/deqagram wheels (Unix)
displayName: Build + test binar/paulimer/deqagram/qodec wheels (Unix)
condition: ne(variables['Agent.OS'], 'Windows_NT')
env:
RUSTFLAGS: $(rustflagsPortable)
Expand All @@ -161,7 +176,7 @@ stages:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install --upgrade pip
pip install maturin pytest hypothesis more-itertools numpy
python -m pip install -r requirements-build.txt pytest hypothesis more-itertools numpy

$wheelhouse = Join-Path $PWD 'target\wheels'
function Build-AndTest([string]$cratePath, [string[]]$pytestArgs = @()) {
Expand All @@ -181,12 +196,25 @@ stages:
Build-AndTest 'paulimer'
Build-AndTest 'deq/deqagram' @('--import-mode=importlib')

maturin build --release --strip --manifest-path qodec/bindings/python/Cargo.toml --out target/qodec-wheels
if ($LASTEXITCODE -ne 0) { throw "maturin build failed for qodec" }
python qodec/tools/check_wheel.py target/qodec-wheels
if ($LASTEXITCODE -ne 0) { throw "qodec wheel import check failed" }
Copy-Item -Path 'target/qodec-wheels/qodec-*.whl' -Destination $wheelhouse

dir target\wheels
displayName: Build + test binar/paulimer/deqagram wheels (Windows)
displayName: Build + test binar/paulimer/deqagram/qodec wheels (Windows)
condition: eq(variables['Agent.OS'], 'Windows_NT')
env:
RUSTFLAGS: $(rustflagsPortable)

- bash: |
set -euo pipefail
source .venv/bin/activate
maturin sdist --manifest-path qodec/bindings/python/Cargo.toml --out target/wheels
displayName: Build qodec source distribution
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['arch'], 'x86_64'))

- bash: |
source .venv/bin/activate
pip install build
Expand Down Expand Up @@ -251,14 +279,15 @@ stages:
pip install --upgrade pip
maturin_args=(--release --strip)
if [[ "$(uname -s)" == "Linux" ]]; then
pip install "maturin[zig]"
python -m pip install -r requirements-build.txt
export CARGO_ZIGBUILD_PYTHON_PATH="$PWD/tools/zig.py"
maturin_args+=(--zig --target "$(uname -m)-unknown-linux-gnu" --compatibility $(manylinuxTag))
# stim-cxx links a prebuilt libstim.a built against libstdc++,
# but zig links libc++; mixing the two leaves std::__cxx11
# symbols undefined and the wheel fails at import.
export STIM_RS_BUILD_FROM_SOURCE=1
else
pip install maturin
python -m pip install -r requirements-build.txt
fi

cd deq/deq_runtime
Expand Down Expand Up @@ -294,7 +323,7 @@ stages:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install --upgrade pip
pip install maturin
python -m pip install -r requirements-build.txt

cd deq\deq_runtime
maturin build --release --strip --out ..\..\target\wheels
Expand Down
29 changes: 28 additions & 1 deletion .ado/stages/publish_python.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ parameters:
type: boolean
- name: publishDeqagramPython
type: boolean
- name: publishQodecPython
type: boolean
default: false

stages:
- stage: publish_python
displayName: Publish Python Packages
dependsOn: build
condition: and(succeeded(), eq(variables['Build.Reason'], 'Manual'), or(eq(${{ parameters.publishBinarPython }}, true), eq(${{ parameters.publishPaulimerPython }}, true), eq(${{ parameters.publishDeqagramPython }}, true)))
condition: and(succeeded(), eq(variables['Build.Reason'], 'Manual'), or(eq(${{ parameters.publishBinarPython }}, true), eq(${{ parameters.publishPaulimerPython }}, true), eq(${{ parameters.publishDeqagramPython }}, true), eq(${{ parameters.publishQodecPython }}, true)))
jobs:
- job: "Publish_Python_Packages"
pool:
Expand Down Expand Up @@ -48,6 +51,30 @@ stages:
displayName: Collect deqagram wheels
condition: eq(${{ parameters.publishDeqagramPython }}, true)

- script: |
set -euo pipefail
for platform in "$(System.DefaultWorkingDirectory)/artifacts"/*; do
wheels=("$platform"/qodec-*.whl)
if [[ "${#wheels[@]}" -ne 1 || ! -f "${wheels[0]}" ]]; then
echo "Expected one qodec wheel in $platform" >&2
exit 1
fi
destination="$(System.DefaultWorkingDirectory)/target/wheels/$(basename "${wheels[0]}")"
if [[ -e "$destination" ]]; then
echo "Duplicate qodec wheel: $destination" >&2
exit 1
fi
cp "${wheels[0]}" "$destination"
done
sdists=("$(System.DefaultWorkingDirectory)/artifacts"/*/qodec-*.tar.gz)
if [[ "${#sdists[@]}" -ne 1 || ! -f "${sdists[0]}" ]]; then
echo "Expected one qodec source distribution" >&2
exit 1
fi
cp "${sdists[0]}" "$(System.DefaultWorkingDirectory)/target/wheels/"
displayName: Collect qodec wheels
condition: eq(${{ parameters.publishQodecPython }}, true)

- script: |
ls -la $(System.DefaultWorkingDirectory)/target/wheels
displayName: List collected wheels
Expand Down
2 changes: 1 addition & 1 deletion .ado/stages/publish_python_package.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Builds a committed pure-Python package and publishes it to PyPI via ESRP.
# Equivalent to publish_python.yaml but for a single source-built package
# (used by deq and qodec) rather than collected maturin wheels.
# (used by deq) rather than collected maturin wheels.
parameters:
- name: packageName
type: string
Expand Down
4 changes: 2 additions & 2 deletions .ado/templates/build-python-bindings-steps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ steps:
- bash: |
python -m venv qdk_env
source qdk_env/bin/activate
python -m pip install --upgrade maturin pytest hypothesis more-itertools
python -m pip install -r requirements-build.txt pytest hypothesis more-itertools
cd binar/bindings/python
maturin develop --release
cd ../../..
Expand All @@ -19,7 +19,7 @@ steps:
- script: |
python -m venv qdk_env
call qdk_env\Scripts\activate.bat
python -m pip install --upgrade maturin pytest hypothesis more-itertools
python -m pip install -r requirements-build.txt pytest hypothesis more-itertools
cd binar\bindings\python
maturin develop --release
cd ..\..\..
Expand Down
7 changes: 4 additions & 3 deletions .ado/templates/build-wheels-steps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ steps:
# Link against Zig's glibc sysroot: the agents ship a newer glibc
# (Ubuntu 22.04 = 2.35, Azure Linux 3 = 2.38) and a native link would
# raise the wheel's glibc floor above the tag we claim.
python -m pip install --upgrade "maturin[zig]"
python -m pip install -r requirements-build.txt
export CARGO_ZIGBUILD_PYTHON_PATH="$PWD/tools/zig.py"
maturin_args+=(--zig --target "$(uname -m)-unknown-linux-gnu" --compatibility manylinux_2_28)
;;
Darwin)
python -m pip install --upgrade maturin
python -m pip install -r requirements-build.txt
;;
*)
echo "Unsupported Unix platform: $(uname -s)" >&2
Expand All @@ -42,7 +43,7 @@ steps:
condition: ne( variables['Agent.OS'], 'Windows_NT')

- script: |
python -m pip install --upgrade maturin
python -m pip install -r requirements-build.txt
if not exist target\wheels mkdir target\wheels
cd binar\bindings\python
maturin build --release --out ..\..\..\target\wheels
Expand Down
43 changes: 43 additions & 0 deletions .github/agents/qodec-bindings.agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
description: 'Reviews and evolves the qodec Python bindings (PyO3) API surface — investigates, proposes options, implements across Rust + .pyi stubs + tests, and verifies the full toolchain.'
tools: ['edit', 'search', 'runCommands', 'runTasks', 'usages', 'problems', 'testFailure', 'todos']
---

# qodec Bindings API Reviewer

You evolve the **qodec Python bindings** (`qodec/bindings/python/`) — the PyO3 0.29 wrapper
over the Rust core — with an emphasis on a clean, Pythonic, internally-consistent API
surface. You work in deliberate, reviewable increments.

Read the [shared instructions](../instructions/qodec.instructions.md) and
[Python guidance](../instructions/qodec-python.instructions.md) before work.
For model changes, also read the [model guidance](../instructions/qodec-model.instructions.md).
These files own compatibility and API conventions; do not duplicate them here.

## Operating principles

- **The Rust core (`qodec/src/`) is the source of truth.** Prefer extending/fixing the core
over duplicating logic in the bindings. Only reshape `qodec/src/` when the user explicitly
asks for a behavior change, not merely a binding ergonomics tweak.
- **Keep the affected surfaces in sync** using the shared instructions, including
the C ABI when model changes affect it.
- **Avoid over-engineering.** Do not add speculative accessors, typed introspection, or
abstractions that have no consumer. If introspection is test-only, question whether it
should exist at all. Prefer the smallest honest surface.
- **Compatibility.** Follow the changelog's compatibility contract and release
rules from the shared instructions; pre-1.0 does not waive them.

## Workflow for each review item

1. **Investigate first.** Read the relevant Rust wrapper, the `.pyi` stub, the Rust core
type it wraps, and any consumers (`qodec/tests/`, `qodec/examples/`, and downstream packages)
before proposing anything. Confirm claims against the code — do not assume.
2. **Present options, then let the user decide.** For non-trivial changes, lay out 2–4
concrete options with honest tradeoffs and a recommendation. Wait for the decision.
3. **Implement across all layers** in one pass: Rust wrapper + `.pyi` stub + tests +
docstrings (+ schema/docs if the on-disk shape moved).
4. **Verify** using [qodec-checks.instructions.md](../instructions/qodec-checks.instructions.md).
Run focused checks first and the full gates before merge. Use its check runner
with the existing selected interpreter; do not maintain separate commands here.
5. **Report concisely.** Summarize what changed and why, note the verification result,
and offer the next item.
8 changes: 7 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@
- Benchmarks used to compare packages should typically use deterministic inputs for reproducibility
- When adding new functionality, include relevant unit tests and documentation.
- Avoid inline commments. They are usually redundant and often wrong. Prefer clear code and good variable names instead.
- Avoid abbreviations and single-letter variable names, unless they very clearly improve clarity.
- Avoid abbreviations and single-letter variable names, unless they very clearly improve clarity.

## qodec

For work under [qodec/](../qodec/), follow the scoped
[qodec instructions](instructions/qodec.instructions.md). They link to the model,
Python binding, and verification guidance; do not apply those rules to sibling crates.
Loading
Loading