Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 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
23 changes: 22 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
checks/lint_.py

mypy:
name: Type check
name: Type check (mypy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
Expand All @@ -93,6 +93,27 @@ jobs:
run: |
checks/mypy_.py

ty:
name: Type check (ty)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python 3.14
uses: actions/setup-python@v6
with:
python-version: 3.14
- uses: actions/cache@v5
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('**/pyproject.toml', '**/*requirements.txt', '.github/workflows/ci.yml') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ./checks-superstaq -e ./general-superstaq[dev] -e ./qiskit-superstaq[dev] -e ./cirq-superstaq[dev] -e ./supermarq-benchmarks[dev]
- name: Type check
run: |
checks/ty_.py general-superstaq

coverage:
name: Pytest and Coverage check
strategy:
Expand Down
2 changes: 2 additions & 0 deletions checks-superstaq/checks_superstaq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
mypy_,
pytest_,
requirements,
ty_,
)

__all__ = [
Expand All @@ -41,4 +42,5 @@
"mypy_",
"pytest_",
"requirements",
"ty_",
]
10 changes: 1 addition & 9 deletions checks-superstaq/checks_superstaq/check_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,15 +297,7 @@ def get_test_files(
####################################################################################################
# file parsing, incremental checks, and decorator to exit instead of returning a failing exit code

CHECK_LIST = [
"configs",
"format",
"mypy",
"pytest",
"coverage",
"requirements",
"build_docs",
]
CHECK_LIST = ["configs", "format", "mypy", "pytest", "coverage", "requirements", "build_docs", "ty"]


def get_check_parser(no_files: bool = False) -> argparse.ArgumentParser:
Expand Down
64 changes: 64 additions & 0 deletions checks-superstaq/checks_superstaq/ty_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
# Copyright 2026 Infleqtion
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

import subprocess
import sys
import textwrap
from collections.abc import Iterable

from checks_superstaq import check_utils


@check_utils.enable_exit_on_failure
def run(
*args: str,
include: str | Iterable[str] = "*.py",
exclude: str | Iterable[str] = (),
silent: bool = False,
) -> int:
"""Runs ty on the repository (typing check).

Args:
*args: Command line arguments.
include: Glob(s) indicating which tracked files to consider (e.g. "*.py").
exclude: Glob(s) indicating which tracked files to skip (e.g. "*integration_test.py").
silent: If True, restrict printing to warning and error messages.

Returns:
Terminal exit code. 0 indicates success, while any other integer indicates a test failure.
"""
parser = check_utils.get_check_parser()
parser.description = textwrap.dedent(
"""
Runs ty on the repository (typing check).
Ignores files in the [repo_root]/examples directory.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this handled somewhere? Or are we ignoring it for now since gss has no examples?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, that was because I copied it from somewhere else - thanks for catching!

"""
)

parsed_args, args_to_pass = parser.parse_known_intermixed_args(args)
if "ty" in parsed_args.skip:
return 0

files = check_utils.extract_files(parsed_args, include, exclude, silent)

return subprocess.call(
[sys.executable, "-m", "ty", "check", *files, *args_to_pass], cwd=check_utils.root_dir
)


if __name__ == "__main__":
sys.exit(run(*sys.argv[1:]))
4 changes: 4 additions & 0 deletions checks-superstaq/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ ruff>=0.13.0
setuptools>=67.0.0
sphinx-autoapi>=3.2.1
sphinx-rtd-theme>=1.0.0
ty>=0.0.29
types-decorator>=5.2.0
types-PyYAML>=6.0.12
types-requests>=2.32.0
35 changes: 9 additions & 26 deletions ...rstaq/general_superstaq/check/__init__.py → checks/ty_.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
# Copyright 2026 Infleqtion
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -11,30 +12,12 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from checks_superstaq import ( # To be removed in version 0.5.0
__version__,
all_,
build_docs,
check_utils,
configs,
coverage_,
format_,
lint_,
mypy_,
pytest_,
requirements,
)

__all__ = [
"__version__",
"all_",
"build_docs",
"check_utils",
"configs",
"coverage_",
"format_",
"lint_",
"mypy_",
"pytest_",
"requirements",
]
from __future__ import annotations

import sys

import checks_superstaq as checks

if __name__ == "__main__":
sys.exit(checks.ty_.run(*sys.argv[1:]))
4 changes: 2 additions & 2 deletions general-superstaq/general_superstaq/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def _refresh_job(self) -> None:
if self._job_data is not None:
if all(s in gss.models.TERMINAL_CIRCUIT_STATES for s in self._job_data.statuses):
return
self._job_data = gss.models.JobData(
**self._client.fetch_jobs([self._job_id])[str(self._job_id)]
self._job_data = gss.models.JobData.model_validate(
self._client.fetch_jobs([self._job_id])[str(self._job_id)]
)
self._update_status_queue_info()

Expand Down
10 changes: 5 additions & 5 deletions general-superstaq/general_superstaq/job_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def test_to_dict(
mock_get.return_value = _mocked_response({str(uuid.UUID(int=123)): job_dict})

job = gss.job.Job(mock_client, uuid.UUID(int=123))
assert job.to_dict() == gss.models.JobData(**job_dict).model_dump()
assert job.to_dict() == gss.models.JobData.model_validate(job_dict).model_dump()


def test_equality(mock_client: gss.superstaq_client._SuperstaqClientV3) -> None:
Expand Down Expand Up @@ -286,23 +286,23 @@ def test_update_status_queue_info(mock_client: gss.superstaq_client._SuperstaqCl
job_dict = _job_dict()
job_dict["num_circuits"] = 3
job_dict["statuses"] = ["completed"] * 3
job._job_data = gss.models.JobData(**job_dict)
job._job_data = gss.models.JobData.model_validate(job_dict)

job._update_status_queue_info()
assert job._overall_status == "completed"

job_dict["statuses"] = ["awaiting_submission", "cancelled", "cancelled"]
job._job_data = gss.models.JobData(**job_dict)
job._job_data = gss.models.JobData.model_validate(job_dict)
job._update_status_queue_info()
assert job._overall_status == "awaiting_submission"

job_dict["statuses"] = ["cancelled", "cancelled", "awaiting_submission"]
job._job_data = gss.models.JobData(**job_dict)
job._job_data = gss.models.JobData.model_validate(job_dict)
job._update_status_queue_info()
assert job._overall_status == "awaiting_submission"

job_dict["statuses"] = ["completed", "completed", "failed"]
job._job_data = gss.models.JobData(**job_dict)
job._job_data = gss.models.JobData.model_validate(job_dict)
job._update_status_queue_info()
assert job._overall_status == "failed"

Expand Down
4 changes: 3 additions & 1 deletion general-superstaq/general_superstaq/machine_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ def post_result(
status=status,
status_message=status_message,
successful_shots=len(bitstrings) if bitstrings is not None else None,
measurements=compressed_bitstrings,
measurements={k: set(v) for k, v in compressed_bitstrings.items()}
Comment thread
cdbf1 marked this conversation as resolved.
Outdated
if compressed_bitstrings is not None
else compressed_bitstrings,
)
self.post_request("/cq_worker/circuit_results", results.model_dump(mode="json"))

Expand Down
5 changes: 3 additions & 2 deletions general-superstaq/general_superstaq/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import uuid
from collections.abc import Mapping, Sequence
from enum import Enum
from typing import Annotated, Any
from typing import Annotated, Any, cast

import pydantic.functional_validators

Expand Down Expand Up @@ -153,7 +153,8 @@ def _validate_cq_token(cls, cq_token: object) -> object:
Previously CQ tokens were specified via a dict, e.g. `cq_token={"access_token": "token"}`.
"""
if isinstance(cq_token, Mapping):
return cq_token.get("access_token")
cq_token_mapping = cast("Mapping[str, object]", cq_token)
Comment thread
cdbf1 marked this conversation as resolved.
Outdated
return cq_token_mapping.get("access_token")
return cq_token


Expand Down
10 changes: 5 additions & 5 deletions general-superstaq/general_superstaq/models_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_external_provider_credentials() -> None:

# Old-style CQ credentials
options_old = {"cq_token": {"access_token": "token"}, "project_id": "123", "org_id": "456"}
credentials = gss.models.ExternalProviderCredentials(**options_old)
credentials = gss.models.ExternalProviderCredentials.model_validate(options_old)
assert credentials.cq_token == "token"
assert credentials.cq_project_id == "123"
assert credentials.cq_org_id == "456"
Expand Down Expand Up @@ -121,7 +121,7 @@ def test_worker_task_results_validation() -> None:
circuit_ref="f76e84f7-0c65-4f0b-b2d7-14135db3900c",
status=gss.models.CircuitStatus.COMPLETED,
successful_shots=10,
measurements={"a": [0], "b": [1, 2]},
measurements={"a": {0}, "b": {1, 2}},
)

with pytest.raises(
Expand All @@ -132,7 +132,7 @@ def test_worker_task_results_validation() -> None:
circuit_ref="f76e84f7-0c65-4f0b-b2d7-14135db3900c",
status=gss.models.CircuitStatus.COMPLETED,
successful_shots=10,
measurements={"101": [0], "01": [1, 2]},
measurements={"101": {0}, "01": {1, 2}},
)

with pytest.raises(
Expand All @@ -143,14 +143,14 @@ def test_worker_task_results_validation() -> None:
circuit_ref="f76e84f7-0c65-4f0b-b2d7-14135db3900c",
status=gss.models.CircuitStatus.COMPLETED,
successful_shots=2,
measurements={"101": [0], "001": [1, 3]},
measurements={"101": {0}, "001": {1, 3}},
)

_ = gss.models.WorkerTaskResults(
circuit_ref="f76e84f7-0c65-4f0b-b2d7-14135db3900c",
status=gss.models.CircuitStatus.COMPLETED,
successful_shots=4,
measurements={"000": [0, 1, 3], "101": [2]},
measurements={"000": {0, 1, 3}, "101": {2}},
)

_ = gss.models.WorkerTaskResults(
Expand Down
Loading