Skip to content

Commit e2968de

Browse files
authored
Sync OpenAPI to May 2026 upstream and polish hand-written layer (#44)
* Polish hand-written layer: narrower except, design notes, drop pass-through Internal cleanups; no behavior change visible from the public API. - _transport.py: narrow JSON-decode `except Exception` to `(ValueError, UnicodeDecodeError)` and parenthesise the `message`/`error` truthy-fallback so operator precedence is obvious. - ionq_client.py: document why `IonQClient` is a PascalCase factory (so call sites read like construction) and why the async path re-injects `Authorization` and reads private `_verify_ssl` / `_follow_redirects` from the generated `AuthenticatedClient`. - polling.py: drop the `job_id` pass-through to `_check_terminal`; use `job.id` (identical here \u2014 we just fetched the job with that uuid). - tests/test_polling.py: note that `_real_sleep = asyncio.sleep` is captured at import so async tests can call the real sleep even after monkeypatch shadows it. - tests/integration/conftest.py: replace module-level `_job_ids` global with a session-scoped `_tracked_jobs` fixture consumed by `track_job` and `cleanup_jobs` via normal pytest DI. * Regenerate client against May 15 2026 upstream spec Refreshes openapi.json from https://api.ionq.co/v0.4/api-docs and regenerates ionq_core/api/ + ionq_core/models/. Tracks the upstream changes surfaced by the spec-drift workflow (issue #43); the bot's diff in #43 was truncated at 60 KB and missed cost_model + the qubits tightening on NativeCircuitInput/JsonMultiCircuitInput. User-visible changes: - New `QctrlQaoaJobCreationPayload` + `QctrlQaoaJobInput` models, plus the inline schemas the generator synthesises (`*_external_settings`, `*_settings`, `*_settings_error_mitigation`, `*_problem`, and the literal-enum modules for `type` / `problem_type`). The `create_job` body union now also accepts `QctrlQaoaJobCreationPayload`. - New optional `cost_model: CostModel | Unset` field on `BaseJob`, `GetCircuitJobResponse`, and `GetJobResponse`. `CostModel` is a string literal enum: "quantum_compute_time" or "execution_time". - `NativeCircuitInput.qubits` and `JsonMultiCircuitInput.qubits` go from `float | Unset` to `int | Unset` to match upstream's tightening to `format: int32, minimum: 1`. Callers that pass integer literals (which is everyone) are unaffected. Overlay simplification: - Dropped the `QisCircuitInput.properties.qubits` type/format action from openapi-overlay.yaml. Upstream adopted that fix in May 2026 (and extended it to NativeCircuitInput and JsonMultiCircuitInput), so the local patch is now a no-op. The two `required:` actions remain load-bearing \u2014 upstream still treats `qubits` as optional on QisCircuitInput, which fails the simulator preflight. All gates pass: ruff check, ruff format --check, ty check, pytest (242 passed, 100% branch coverage on hand-written code). * Stop pre-commit from rewriting generated files The previous regen commit failed `generated.yml` because pre-commit's `trailing-whitespace` and `end-of-file-fixer` hooks rewrote the raw output of `openapi-python-client` locally (stripping docstring trailing spaces and collapsing double trailing newlines), but CI regenerates the files fresh and gets back those same artifacts. Two parts: - Re-commit the raw regen output for the 11 affected files in `ionq_core/api/` + `ionq_core/models/` so `git diff ionq_core/` is empty on a fresh regen. - Exclude generated paths (per `.gitattributes`) from the two whitespace hooks in `.pre-commit-config.yaml` so a future regen PR doesn't trip on the same thing. * Drop the pre-commit-config exclude The exclude was added in the previous commit to defend against a trap that turns out not to exist in practice: the project's recommended `pre-commit install` workflow has never been load-bearing here (no historical regen PR has failed `staleness`, and the on-commit hook strip would only fire for contributors who actually installed it), so the structural guard is purely speculative. The regen-output restoration in that commit was the load-bearing part; keep that, drop this.
1 parent 11edf32 commit e2968de

24 files changed

Lines changed: 842 additions & 34 deletions

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- `QctrlQaoaJobCreationPayload` and `QctrlQaoaJobInput` for submitting Q-CTRL QAOA maxcut combinatorial-optimization jobs via `create_job`. The `create_job` body union now also accepts `QctrlQaoaJobCreationPayload`.
12+
- `cost_model` optional field on `BaseJob`, `GetCircuitJobResponse`, and `GetJobResponse`, typed as `CostModel` (`"quantum_compute_time"` or `"execution_time"`).
13+
14+
### Changed
15+
16+
- `NativeCircuitInput.qubits` and `JsonMultiCircuitInput.qubits` are now `int | Unset` (previously `float | Unset`), matching upstream's tightening to `format: int32, minimum: 1`. `QisCircuitInput.qubits` already had this type locally via the OpenAPI overlay; that overlay action has been removed now that upstream is correct natively.
17+
918
## [0.1.1] - 2026-04-30
1019

1120
### Changed

ionq_core/_transport.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
def _raise_for_response(response: httpx.Response) -> None:
2929
try:
3030
body: dict | str | None = response.json()
31-
except Exception:
31+
except (ValueError, UnicodeDecodeError):
32+
# json.JSONDecodeError subclasses ValueError; UnicodeDecodeError covers
33+
# bodies that aren't decodable in the declared (or guessed) encoding.
3234
body = (response.text or "")[:500] or None
33-
message = body.get("message") or body.get("error") if isinstance(body, dict) else None
35+
message = (body.get("message") or body.get("error")) if isinstance(body, dict) else None
3436
try:
3537
retry_after = max(0.0, float(response.headers["retry-after"]))
3638
except (KeyError, ValueError):

ionq_core/api/default/create_job.py

Lines changed: 12 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ionq_core/ionq_client.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
_AUTH_HEADER = "Authorization"
3535

3636

37+
# Factory named in PascalCase (deliberately, not a class) so call sites read
38+
# like construction. Returns the generated `AuthenticatedClient`.
3739
def IonQClient(
3840
*,
3941
api_key: str | None = None,
@@ -169,6 +171,12 @@ def IonQClient(
169171
httpx_args={"transport": sync_transport},
170172
**kwargs,
171173
)
174+
# `set_async_httpx_client` bypasses `AuthenticatedClient`'s lazy auth-header
175+
# injection (see generated `client.py::get_async_httpx_client`), so we merge
176+
# `Authorization` in manually here. The `_verify_ssl` / `_follow_redirects`
177+
# fields are private on the generated `AuthenticatedClient` but are the only
178+
# way to mirror the caller's choices onto the async transport; do not add a
179+
# public accessor in the hand-written layer — they belong to generated code.
172180
client.set_async_httpx_client(
173181
httpx.AsyncClient(
174182
base_url=base_url,

ionq_core/models/__init__.py

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ionq_core/models/base_job.py

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ionq_core/models/cost_model.py

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ionq_core/models/get_circuit_job_response.py

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)