From 5bb4283428ab736fd894bcd81f619e5a4b7a85ce Mon Sep 17 00:00:00 2001 From: Martin Ahindura Date: Fri, 7 Aug 2026 11:47:12 +0200 Subject: [PATCH 1/2] fix(qpi-driver): create a quantify data directory's parents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit quantify-core's `set_datadir` does a non-recursive `os.mkdir`, so pointing it at a directory whose *parent* does not exist yet dies with a FileNotFoundError naming the child rather than the missing parent. A driver defaulting to `./bin/data` in a checkout where nothing has run `make build` never starts. It hid locally because `bin/` is left behind by any earlier build, and `mkdir` only needs the parent: removing `bin/data` alone still passes. CI has no `bin/` at all, which is where `make test-py-loop` failed — 4 failed, 34 errors, every one of them the quantify half of the parametrised loop. The qblox half passes because that tuner sets no data directory. Fixed in `compat/quantify`, which is the seam both the tuner and the executor already import `set_datadir` through, and whose job is smoothing over exactly this kind of edge. The not-installed stub keeps its no-op behaviour. --- CHANGELOG.md | 7 +++++++ qpi-driver/py/qpi_driver/compat/quantify.py | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f96002bf..45edfc16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project follows versions of format `{year}.{month}.{patch_number}`. ## [Unreleased] +### Fixed + +- `qpi-driver/py`: a `quantify` tuner or executor creates its data directory's parents. + quantify-core's own `mkdir` is not recursive, so a directory whose parent did not + exist yet — `bin/data` in a checkout where nothing has run `make build` — failed at + construction with a `FileNotFoundError` naming the child. + ## [0.4.0] - 2026-08-06 ### Added diff --git a/qpi-driver/py/qpi_driver/compat/quantify.py b/qpi-driver/py/qpi_driver/compat/quantify.py index a8fb071f..a3039646 100644 --- a/qpi-driver/py/qpi_driver/compat/quantify.py +++ b/qpi-driver/py/qpi_driver/compat/quantify.py @@ -2,6 +2,7 @@ import logging from enum import Enum +from pathlib import Path from typing import Any, TypeAlias from qpi_driver.compat.shared import BasicCompatClass @@ -14,7 +15,7 @@ from qcodes.instrument.channel import InstrumentChannel from qcodes.instrument.parameter import ManualParameter from qcodes.parameters import ParameterBase - from quantify_core.data.handling import set_datadir + from quantify_core.data.handling import set_datadir as _set_datadir from quantify_scheduler import Operation, Schedule from quantify_scheduler.backends.graph_compilation import SerialCompiler from quantify_scheduler.backends.qblox_backend import ( @@ -69,7 +70,7 @@ logging.debug(f"failed importing from quantify.compat {exp}") IS_QUANTIFY_INSTALLED: bool = False - def set_datadir(*args, **kwargs): + def _set_datadir(*args, **kwargs): pass def field_validator(*args, **kwargs): @@ -168,3 +169,17 @@ class Instrument(BasicCompatClass): @classmethod def close_all(cls): pass + + +def set_datadir(datadir: Any = None) -> None: + """quantify-core's ``set_datadir``, with the directory created first. + + Its own ``mkdir`` is not recursive, so pointing it anywhere whose *parent* does + not exist yet dies with a ``FileNotFoundError`` naming the child — `bin/data` in + a checkout where nothing has run `make build`, or a service's data directory + before its first start. The path is already vetted as safe by + :func:`~qpi_driver.paths.as_safe_dir` wherever it came from an ``-o`` option. + """ + if datadir is not None: + Path(datadir).mkdir(parents=True, exist_ok=True) + _set_datadir(datadir) From d1f4f3732590acbc44a66455d14e31a367ede317 Mon Sep 17 00:00:00 2001 From: Martin Ahindura Date: Fri, 7 Aug 2026 11:52:21 +0200 Subject: [PATCH 2/2] chore: bump to 0.4.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A patch: the release carries one fix, to the quantify data directory's parents. Every component that declares a version: - qpi-ui (main.go, the dashboard's package.json, the WiX installer manifest) - qpi-driver py, js and the Go CLI's Version var - qpi-client py and js The Python packages read their version from installed metadata and fall back to a literal, so the literal moves too — and with it the two tests that assert the fallback path by patching metadata to raise. Five lockfiles regenerated. The README's release URLs move as well; they 404 until the tag exists. Makefile's VERSION is bumped for consistency but referenced nowhere else in it. Not verified by a test run, at the author's request. --- CHANGELOG.md | 7 ++++--- Makefile | 2 +- README.md | 12 ++++++------ qpi-client/js/package-lock.json | 4 ++-- qpi-client/js/package.json | 2 +- qpi-client/py/pyproject.toml | 2 +- qpi-client/py/qpi_client/__init__.py | 2 +- qpi-client/py/uv.lock | 2 +- qpi-driver/go/cli/cli.go | 2 +- qpi-driver/js/package-lock.json | 4 ++-- qpi-driver/js/package.json | 2 +- qpi-driver/py/pyproject.toml | 2 +- qpi-driver/py/qpi_driver/__init__.py | 2 +- qpi-driver/py/qpi_driver/cli.py | 2 +- qpi-driver/py/tests/test_cli.py | 4 ++-- qpi-driver/py/uv.lock | 2 +- qpi-ui/internal/dashboard/package-lock.json | 4 ++-- qpi-ui/internal/dashboard/package.json | 2 +- qpi-ui/main.go | 2 +- qpi-ui/pkg/qpi.wxs | 2 +- 20 files changed, 32 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45edfc16..e7e08db3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,13 @@ and this project follows versions of format `{year}.{month}.{patch_number}`. ## [Unreleased] +## [0.4.1] - 2026-08-07 + ### Fixed - `qpi-driver/py`: a `quantify` tuner or executor creates its data directory's parents. - quantify-core's own `mkdir` is not recursive, so a directory whose parent did not - exist yet — `bin/data` in a checkout where nothing has run `make build` — failed at - construction with a `FileNotFoundError` naming the child. + quantify-core's own `mkdir` is not recursive, so `bin/data` in a fresh checkout + failed at construction. ## [0.4.0] - 2026-08-06 diff --git a/Makefile b/Makefile index f554d4b7..c8b3b6ed 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: test-dashboard test-docs test-docs-static test-docs-snippets test-docs-example test-docs-site all build build-dashboard test test-js-driver test-go-driver lint lint-go lint-py lint-js lint-dashboard lint-go-client lint-py-client lint-js-driver lint-go-driver format format-go format-py format-js format-dashboard format-go-client format-py-client format-js-driver format-go-driver package package-driver package-driver-js package-driver-go package-js package-py package-go publish-js publish-driver-js publish-py clean venv-check test-e2e-dashboard -VERSION ?= 0.4.0 +VERSION ?= 0.4.1 UV := $(shell command -v uv 2> /dev/null || echo "$$HOME/.local/bin/uv") # Scratch locations for the documentation checks. Under bin/, which is already diff --git a/README.md b/README.md index 2546c8cf..1b9cb510 100644 --- a/README.md +++ b/README.md @@ -77,12 +77,12 @@ The server is available as a single executable binary, as well as native OS pack #### Standalone Binary (macOS & Linux) Download the binary for your platform, make it executable, and run: ```bash -# macOS (replace 0.4.0 with the version you wish to install) -curl -LO https://github.com/sopherapps/qpi/releases/download/v0.4.0/qpi-0.4.0-darwin-amd64 -chmod +x qpi-0.4.0-darwin-amd64 && mv qpi-0.4.0-darwin-amd64 qpi +# macOS (replace 0.4.1 with the version you wish to install) +curl -LO https://github.com/sopherapps/qpi/releases/download/v0.4.1/qpi-0.4.1-darwin-amd64 +chmod +x qpi-0.4.1-darwin-amd64 && mv qpi-0.4.1-darwin-amd64 qpi # Linux (standalone binary) -curl -L https://github.com/sopherapps/qpi/releases/download/v0.4.0/qpi-0.4.0-linux-amd64.tar.gz | tar -xz +curl -L https://github.com/sopherapps/qpi/releases/download/v0.4.1/qpi-0.4.1-linux-amd64.tar.gz | tar -xz # Start the server ./qpi serve @@ -91,8 +91,8 @@ curl -L https://github.com/sopherapps/qpi/releases/download/v0.4.0/qpi-0.4.0-lin #### Native Linux Packages (Ubuntu, Debian, Fedora, Alpine) For Debian/Ubuntu, download and install the `.deb` package: ```bash -wget https://github.com/sopherapps/qpi/releases/download/v0.4.0/qpi_0.4.0_amd64.deb -sudo apt install ./qpi_0.4.0_amd64.deb +wget https://github.com/sopherapps/qpi/releases/download/v0.4.1/qpi_0.4.1_amd64.deb +sudo apt install ./qpi_0.4.1_amd64.deb ``` *(Note: Installing the package automatically registers and starts `qpi.service` under systemd (or OpenRC on Alpine) to run the server in the background on port `8090`)* diff --git a/qpi-client/js/package-lock.json b/qpi-client/js/package-lock.json index 26a0cca9..63e8feee 100644 --- a/qpi-client/js/package-lock.json +++ b/qpi-client/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "qpi-client", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "qpi-client", - "version": "0.4.0", + "version": "0.4.1", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.0", diff --git a/qpi-client/js/package.json b/qpi-client/js/package.json index 3c2b8acb..f34bb988 100644 --- a/qpi-client/js/package.json +++ b/qpi-client/js/package.json @@ -1,6 +1,6 @@ { "name": "qpi-client", - "version": "0.4.0", + "version": "0.4.1", "description": "JavaScript/TypeScript client SDK for the QPI quantum computing platform", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/qpi-client/py/pyproject.toml b/qpi-client/py/pyproject.toml index 25589043..899190e0 100644 --- a/qpi-client/py/pyproject.toml +++ b/qpi-client/py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "qpi-client" -version = "0.4.0" +version = "0.4.1" description = "Python client SDK for the QPI quantum computing platform" readme = "README.md" license = {text = "MIT"} diff --git a/qpi-client/py/qpi_client/__init__.py b/qpi-client/py/qpi_client/__init__.py index 4c0ea605..b036fef0 100644 --- a/qpi-client/py/qpi_client/__init__.py +++ b/qpi-client/py/qpi_client/__init__.py @@ -30,7 +30,7 @@ try: __version__ = importlib.metadata.version("qpi-client") except importlib.metadata.PackageNotFoundError: - __version__ = "0.4.0" + __version__ = "0.4.1" from qpi_client.client import QPIClient from qpi_client.provider import QPIBackend, QPIJob diff --git a/qpi-client/py/uv.lock b/qpi-client/py/uv.lock index d842a7ba..1b5a145f 100644 --- a/qpi-client/py/uv.lock +++ b/qpi-client/py/uv.lock @@ -387,7 +387,7 @@ wheels = [ [[package]] name = "qpi-client" -version = "0.4.0" +version = "0.4.1" source = { editable = "." } dependencies = [ { name = "qiskit" }, diff --git a/qpi-driver/go/cli/cli.go b/qpi-driver/go/cli/cli.go index faaef462..8f7eb0c4 100644 --- a/qpi-driver/go/cli/cli.go +++ b/qpi-driver/go/cli/cli.go @@ -27,7 +27,7 @@ import ( // Version is the CLI version; overridable at build time with // -ldflags "-X github.com/sopherapps/qpi/qpi-driver/go/cli.Version=…". -var Version = "0.4.0" +var Version = "0.4.1" // commonFlags are the universal options `start` shares across every operation, // mirroring the Python CLI. A device's own settings go through -o instead. diff --git a/qpi-driver/js/package-lock.json b/qpi-driver/js/package-lock.json index 9e6d012c..3ce8f498 100644 --- a/qpi-driver/js/package-lock.json +++ b/qpi-driver/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "qpi-driver", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "qpi-driver", - "version": "0.4.0", + "version": "0.4.1", "license": "MIT", "dependencies": { "commander": "^12.1.0" diff --git a/qpi-driver/js/package.json b/qpi-driver/js/package.json index fbdfeb7e..177cf59c 100644 --- a/qpi-driver/js/package.json +++ b/qpi-driver/js/package.json @@ -1,6 +1,6 @@ { "name": "qpi-driver", - "version": "0.4.0", + "version": "0.4.1", "description": "TypeScript/JavaScript SDK for building QPI quantum platform drivers", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/qpi-driver/py/pyproject.toml b/qpi-driver/py/pyproject.toml index b19cb0b7..2db1eef4 100644 --- a/qpi-driver/py/pyproject.toml +++ b/qpi-driver/py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "qpi-driver" -version = "0.4.0" +version = "0.4.1" description = "Quantum Processing Interface (QPI) QPU Driver" readme = "README.md" requires-python = ">=3.12,<3.13" diff --git a/qpi-driver/py/qpi_driver/__init__.py b/qpi-driver/py/qpi_driver/__init__.py index bd0a45b0..4260d7c4 100644 --- a/qpi-driver/py/qpi_driver/__init__.py +++ b/qpi-driver/py/qpi_driver/__init__.py @@ -3,7 +3,7 @@ try: __version__ = importlib.metadata.version("qpi-driver") except importlib.metadata.PackageNotFoundError: - __version__ = "0.4.0" + __version__ = "0.4.1" from qpi_driver.builtins import ( DeviceBuilder, diff --git a/qpi-driver/py/qpi_driver/cli.py b/qpi-driver/py/qpi_driver/cli.py index b4b55135..7c2812b9 100644 --- a/qpi-driver/py/qpi_driver/cli.py +++ b/qpi-driver/py/qpi_driver/cli.py @@ -253,7 +253,7 @@ def _get_version() -> str: try: return importlib.metadata.version("qpi-driver") except importlib.metadata.PackageNotFoundError: - return "0.4.0" + return "0.4.1" def _banner(): """Renders the banner at the top of the CLI""" diff --git a/qpi-driver/py/tests/test_cli.py b/qpi-driver/py/tests/test_cli.py index 78bd522a..bdca514b 100644 --- a/qpi-driver/py/tests/test_cli.py +++ b/qpi-driver/py/tests/test_cli.py @@ -40,7 +40,7 @@ def test_cli_version(): try: expected_version = importlib.metadata.version("qpi-driver") except importlib.metadata.PackageNotFoundError: - expected_version = "0.4.0" + expected_version = "0.4.1" assert expected_version in result.stdout @@ -418,7 +418,7 @@ def test_version_falls_back_when_the_package_is_not_installed(): "version", side_effect=importlib.metadata.PackageNotFoundError("qpi-driver"), ): - assert _get_version() == "0.4.0" + assert _get_version() == "0.4.1" def test_cli_process_requires_token(): diff --git a/qpi-driver/py/uv.lock b/qpi-driver/py/uv.lock index 9eddae1d..bb373881 100644 --- a/qpi-driver/py/uv.lock +++ b/qpi-driver/py/uv.lock @@ -1694,7 +1694,7 @@ wheels = [ [[package]] name = "qpi-driver" -version = "0.4.0" +version = "0.4.1" source = { editable = "." } dependencies = [ { name = "numpy" }, diff --git a/qpi-ui/internal/dashboard/package-lock.json b/qpi-ui/internal/dashboard/package-lock.json index 999f29b8..7823c628 100644 --- a/qpi-ui/internal/dashboard/package-lock.json +++ b/qpi-ui/internal/dashboard/package-lock.json @@ -1,12 +1,12 @@ { "name": "dashboard-src", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dashboard-src", - "version": "0.4.0", + "version": "0.4.1", "dependencies": { "@visx/axis": "^4.0.0", "@visx/scale": "^4.0.0", diff --git a/qpi-ui/internal/dashboard/package.json b/qpi-ui/internal/dashboard/package.json index f7522b22..bff4aea1 100644 --- a/qpi-ui/internal/dashboard/package.json +++ b/qpi-ui/internal/dashboard/package.json @@ -1,7 +1,7 @@ { "name": "dashboard-src", "private": true, - "version": "0.4.0", + "version": "0.4.1", "type": "module", "scripts": { "dev": "vite", diff --git a/qpi-ui/main.go b/qpi-ui/main.go index 37ac668e..29d47643 100644 --- a/qpi-ui/main.go +++ b/qpi-ui/main.go @@ -20,7 +20,7 @@ import ( //go:embed all:internal/dashboard/dist var dashboardFS embed.FS -var Version = "v0.4.0" +var Version = "v0.4.1" func main() { app := pocketbase.New() diff --git a/qpi-ui/pkg/qpi.wxs b/qpi-ui/pkg/qpi.wxs index 929e231d..22805341 100644 --- a/qpi-ui/pkg/qpi.wxs +++ b/qpi-ui/pkg/qpi.wxs @@ -1,6 +1,6 @@ - +