Skip to content

Commit d94dd71

Browse files
committed
fix(ci): resolve ruff lint errors blocking tests workflow
Fixes F401 unused imports and F821 undefined names in: - backend.py: remove unused UnionFindDecoder import, add cast() for mypy - license.py: remove unused ed25519 module import, narrow to Ed25519PublicKey - stripe_integration.py: define STRIPE_WEBHOOK_SECRET, fix typing This unblocks the tests / ruff-and-mypy CI job which was failing after 8s.
1 parent 81b1da1 commit d94dd71

14 files changed

Lines changed: 121 additions & 88 deletions

‎generate_license_keys.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ def create_license_token(
6363
if private_key_pem is None:
6464
private_key_pem = _load_production_private_key_pem()
6565
priv_key = serialization.load_pem_private_key(private_key_pem, password=None)
66+
if not isinstance(priv_key, ed25519.Ed25519PrivateKey):
67+
# load_pem_private_key() returns a union of every key type cryptography
68+
# supports (RSA, DSA, EC, X25519, ML-DSA, ML-KEM, ...). This project only
69+
# ever signs with Ed25519, so fail loudly if anything else is provided.
70+
raise TypeError(
71+
"QECTOR license tokens require an Ed25519 private key; "
72+
f"got {type(priv_key).__name__}."
73+
)
6674
email_clean = customer_email.strip().lower()
6775
payload = f"{receipt_id}:{email_clean}".encode("utf-8")
6876
sig = priv_key.sign(payload)

‎python/qector_decoder_v3/__init__.py‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,7 @@ def _emit_startup_notice() -> None:
138138
if any(var in _os_mod.environ for var in ci_vars):
139139
return
140140

141-
is_interactive = (
142-
hasattr(_sys_mod, "ps1")
143-
or "ipykernel" in _sys_mod.modules
144-
or "IPython" in _sys_mod.modules
145-
)
141+
is_interactive = hasattr(_sys_mod, "ps1") or "ipykernel" in _sys_mod.modules or "IPython" in _sys_mod.modules
146142

147143
if is_interactive:
148144
_sys_mod.stderr.write(

‎python/qector_decoder_v3/backend.py‎

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
qector_decoder_v3.backend — Workload-Aware AutoDecoder with Self-Auto-Debug & Multi-Tier Fallback.
33
4-
Provides automatic hardware routing, real-time performance calibration, and a
4+
Provides automatic hardware routing, real-time performance calibration, and a
55
7-tier fault-tolerant self-debugging fallback engine for quantum error correction decoding.
66
"""
77

@@ -20,7 +20,6 @@
2020
CPUBatchDecoder,
2121
CUDABatchDecoder,
2222
FastUnionFindDecoder,
23-
UnionFindDecoder,
2423
BlossomDecoder,
2524
SparseBlossomDecoder,
2625
LookupTableDecoder,
@@ -128,7 +127,7 @@ def __init__(self, check_to_qubits, n_qubits=None, config: Optional[BackendConfi
128127
self._cuda_ok = bool(self.config.allow_gpu and cuda_is_available())
129128
opencl_auto = os.environ.get("QECTOR_ENABLE_OPENCL_AUTO", "").lower() in {"1", "true", "yes", "on"}
130129
self._opencl_ok = bool(self.config.allow_gpu and opencl_auto and opencl_is_available())
131-
130+
132131
if self.config.allow_gpu and not opencl_auto and opencl_is_available():
133132
self._diag.warnings.append("OpenCL auto-routing disabled; set QECTOR_ENABLE_OPENCL_AUTO=1 to enable it")
134133

@@ -173,7 +172,11 @@ def _get_decoder(self, backend: str) -> Any:
173172
if backend in self._decoders:
174173
return self._decoders[backend]
175174

176-
dec = None
175+
# `dec` holds a heterogeneous set of decoder types (one per backend
176+
# branch below). Typing it as Any avoids mypy flagging every elif
177+
# branch as a type-incompatible assignment while remaining accurate
178+
# at runtime.
179+
dec: Any = None
177180
if backend == Backend.CPU_SINGLE:
178181
dec = FastUnionFindDecoder(self._c2q, self._nq)
179182
elif backend == Backend.CPU_RAYON:
@@ -211,16 +214,16 @@ def _get_decoder(self, backend: str) -> Any:
211214
return dec
212215

213216
def _get_cpu_single(self) -> FastUnionFindDecoder:
214-
return self._get_decoder(Backend.CPU_SINGLE)
217+
return cast(FastUnionFindDecoder, self._get_decoder(Backend.CPU_SINGLE))
215218

216219
def _get_cpu_rayon(self) -> BatchDecoder:
217-
return self._get_decoder(Backend.CPU_RAYON)
220+
return cast(BatchDecoder, self._get_decoder(Backend.CPU_RAYON))
218221

219222
def _get_cuda(self) -> Optional[CUDABatchDecoder]:
220-
return self._get_decoder(Backend.CUDA)
223+
return cast(Optional[CUDABatchDecoder], self._get_decoder(Backend.CUDA))
221224

222225
def _get_opencl(self) -> Optional[OpenCLBatchDecoder]:
223-
return self._get_decoder(Backend.OPENCL)
226+
return cast(Optional[OpenCLBatchDecoder], self._get_decoder(Backend.OPENCL))
224227

225228
# -- selection ---------------------------------------------------------
226229
def select(self, batch_size: int) -> str:
@@ -229,7 +232,11 @@ def select(self, batch_size: int) -> str:
229232
return self.config.force
230233

231234
if self.config.allow_gpu and batch_size >= self.config.gpu_threshold:
232-
if self.config.prefer == Backend.OPENCL and self._opencl_ok and self._diag.backend_health.get(Backend.OPENCL, True):
235+
if (
236+
self.config.prefer == Backend.OPENCL
237+
and self._opencl_ok
238+
and self._diag.backend_health.get(Backend.OPENCL, True)
239+
):
233240
return Backend.OPENCL
234241
if self._cuda_ok and self._diag.backend_health.get(Backend.CUDA, True):
235242
return Backend.CUDA
@@ -283,7 +290,14 @@ def batch_decode(self, syndromes) -> np.ndarray:
283290

284291
# Robust multi-tier self-debugging execution chain
285292
fallback_chain = [chosen]
286-
for t in [Backend.CUDA, Backend.OPENCL, Backend.CPU_RAYON, Backend.CPU_BATCH, Backend.CPU_SINGLE, Backend.BLOSSOM]:
293+
for t in [
294+
Backend.CUDA,
295+
Backend.OPENCL,
296+
Backend.CPU_RAYON,
297+
Backend.CPU_BATCH,
298+
Backend.CPU_SINGLE,
299+
Backend.BLOSSOM,
300+
]:
287301
if t not in fallback_chain:
288302
fallback_chain.append(t)
289303

@@ -362,12 +376,14 @@ def _record_auto_debug_failure(self, backend: str, exc: Exception, context: str)
362376
self._diag.backend_health[backend] = False
363377
msg = f"AutoDebug caught error on {backend} [{context}]: {exc}"
364378
self._diag.warnings.append(msg)
365-
self._diag.debug_log.append({
366-
"timestamp": time.time(),
367-
"backend": backend,
368-
"context": context,
369-
"error": str(exc),
370-
})
379+
self._diag.debug_log.append(
380+
{
381+
"timestamp": time.time(),
382+
"backend": backend,
383+
"context": context,
384+
"error": str(exc),
385+
}
386+
)
371387
logger.warning(msg)
372388

373389
def _python_fallback_decode(self, syndrome: np.ndarray) -> np.ndarray:

‎python/qector_decoder_v3/decoder_cache.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ def _build_decoder_pool(
9393
n_workers: Optional[int],
9494
):
9595
from .decoder_pool import DecoderPool
96+
9697
checks = [list(c) for c in checks_tuple]
9798
return DecoderPool(
9899
checks,

‎python/qector_decoder_v3/decoder_pool.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ def __init__(
133133
self._decoder_type = str(decoder_type)
134134
self._pool: Optional["_mp.pool.Pool"] = None
135135

136-
137136
def decode(self, syndromes) -> np.ndarray:
138137
"""Decode a batch of syndromes.
139138

‎python/qector_decoder_v3/license.py‎

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
11
from __future__ import annotations
22
import base64
3-
from cryptography.hazmat.primitives.asymmetric import ed25519
3+
from typing import Optional
44
from cryptography.hazmat.primitives import serialization
5+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
56
from cryptography.exceptions import InvalidSignature
67

78
# Embedded Public Key — Production Ed25519 Key (rotated 2026-07-22)
89
PUBLIC_KEY_PEM = b"""-----BEGIN PUBLIC KEY-----
910
MCowBQYDK2VwAyEAQh9t19EZ4KWZEYjY3EwHCUzUIehZBlovaMtrpLQXeGA=
1011
-----END PUBLIC KEY-----"""
1112

12-
try:
13-
_PUBLIC_KEY = serialization.load_pem_public_key(PUBLIC_KEY_PEM)
14-
except Exception:
15-
_PUBLIC_KEY = None
13+
14+
def _load_ed25519_public_key() -> Optional[Ed25519PublicKey]:
15+
"""Loads the embedded PEM and narrows it to Ed25519PublicKey.
16+
17+
load_pem_public_key() returns a union of every key type cryptography
18+
supports (RSA, DSA, EC, X25519, ML-DSA, ML-KEM, ...). This project only
19+
ever signs/verifies with Ed25519, so we assert that narrowly here rather
20+
than propagating the full union to every call site.
21+
"""
22+
try:
23+
key = serialization.load_pem_public_key(PUBLIC_KEY_PEM)
24+
except Exception:
25+
return None
26+
if not isinstance(key, Ed25519PublicKey):
27+
return None
28+
return key
29+
30+
31+
_PUBLIC_KEY: Optional[Ed25519PublicKey] = _load_ed25519_public_key()
1632

1733

1834
def verify_license_token(token: str, customer_email: str = "") -> bool:
@@ -38,11 +54,11 @@ def verify_license_token(token: str, customer_email: str = "") -> bool:
3854
if missing_pad:
3955
email_b64 += "=" * (4 - missing_pad)
4056
embedded_email = base64.urlsafe_b64decode(email_b64).decode("utf-8").lower()
41-
57+
4258
# If caller provided explicit email check, ensure match
4359
if customer_email and customer_email.strip().lower() != embedded_email:
4460
return False
45-
61+
4662
target_email = embedded_email
4763
except Exception:
4864
return False

‎python/qector_decoder_v3/predecoder.py‎

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,9 @@ def __init__(self, check_to_qubits, n_qubits=None, backend: str = "blossom"):
102102
if backend in _backend_map:
103103
backend = _backend_map[backend]
104104
if backend not in _valid_backends:
105-
raise ValueError(
106-
"backend must be one of "
107-
"['blossom', 'union_find', 'sparse_blossom', 'fast_union_find']"
108-
)
105+
raise ValueError("backend must be one of ['blossom', 'union_find', 'sparse_blossom', 'fast_union_find']")
109106
self.backend = backend
110-
residual: Union[
111-
BlossomDecoder, UnionFindDecoder, SparseBlossomDecoder, FastUnionFindDecoder
112-
]
107+
residual: Union[BlossomDecoder, UnionFindDecoder, SparseBlossomDecoder, FastUnionFindDecoder]
113108
if backend == "blossom":
114109
residual = BlossomDecoder(self._c2q, self.n_qubits)
115110
elif backend == "union_find":
@@ -118,9 +113,7 @@ def __init__(self, check_to_qubits, n_qubits=None, backend: str = "blossom"):
118113
residual = FastUnionFindDecoder(self._c2q, self.n_qubits)
119114
else:
120115
residual = SparseBlossomDecoder(self._c2q, self.n_qubits)
121-
self._residual: Union[
122-
BlossomDecoder, UnionFindDecoder, SparseBlossomDecoder, FastUnionFindDecoder
123-
] = residual
116+
self._residual: Union[BlossomDecoder, UnionFindDecoder, SparseBlossomDecoder, FastUnionFindDecoder] = residual
124117
self.last_predecoded = 0 # number of defects resolved by the predecoder
125118

126119
def _predecode(self, syndrome: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:

‎python/qector_decoder_v3/stripe_integration.py‎

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
import os
77
import json
88
import logging
9-
from typing import Dict, Any, Optional
9+
from typing import Any, Dict, Optional
1010
from pathlib import Path
1111

1212
try:
1313
from dotenv import load_dotenv
14+
1415
# Load environment variables from .env at repo root or current working dir
1516
env_path = Path(__file__).resolve().parents[2] / ".env"
1617
if env_path.exists():
@@ -26,18 +27,21 @@
2627
logger = logging.getLogger("qector_decoder_v3.stripe")
2728

2829
# Retrieve keys from environment
29-
STRIPE_SECRET_KEY: str = os.getenv("STRIPE_SECRET_KEY") or os.getenv("STRIPE_SECRET", "")
30-
STRIPE_PUBLISHABLE_KEY: str = os.getenv("STRIPE_PUBLISHABLE_KEY") or os.getenv("STRIPE_PUBLISHABLE", "")
30+
# Use the default-argument form of os.getenv so the static type is `str` rather
31+
# than `str | None`; the `or os.getenv(..., "")` pattern confuses mypy.
32+
STRIPE_SECRET_KEY: str = os.getenv("STRIPE_SECRET_KEY", "") or os.getenv("STRIPE_SECRET", "")
33+
STRIPE_PUBLISHABLE_KEY: str = os.getenv("STRIPE_PUBLISHABLE_KEY", "") or os.getenv("STRIPE_PUBLISHABLE", "")
34+
STRIPE_WEBHOOK_SECRET: str = os.getenv("STRIPE_WEBHOOK_SECRET", "")
3135
STRIPE_PURCHASE_LINK: str = os.getenv(
3236
"STRIPE_PURCHASE_LINK",
33-
"https://buy.stripe.com/7sY9AVdwlgoyfse9bYeUU00?locale=en&__embed_source=buy_btn_1TsoKxRsa9cg9l8A7ExMmc77"
37+
"https://buy.stripe.com/7sY9AVdwlgoyfse9bYeUU00?locale=en&__embed_source=buy_btn_1TsoKxRsa9cg9l8A7ExMmc77",
3438
)
3539

3640
if STRIPE_SECRET_KEY:
3741
stripe.api_key = STRIPE_SECRET_KEY
3842

3943

40-
def get_stripe_keys() -> Dict[str, str]:
44+
def get_stripe_keys() -> Dict[str, Any]:
4145
"""Returns configured Stripe keys snapshot."""
4246
return {
4347
"publishable_key": STRIPE_PUBLISHABLE_KEY,
@@ -94,9 +98,7 @@ def create_checkout_session(
9498

9599

96100
def handle_stripe_webhook_payload(
97-
payload: bytes,
98-
sig_header: str = "",
99-
webhook_secret: Optional[str] = None
101+
payload: bytes, sig_header: str = "", webhook_secret: Optional[str] = None
100102
) -> Dict[str, Any]:
101103
"""
102104
Parses and verifies Stripe webhook events, issuing a signed Ed25519 license token
@@ -114,14 +116,13 @@ def handle_stripe_webhook_payload(
114116
payload_text = payload.decode("utf-8") if isinstance(payload, bytes) else str(payload)
115117
event = json.loads(payload_text)
116118

117-
118119
event_type = event.get("type", "")
119120
response_data = {"event_type": event_type, "license_token": None, "issued": False}
120121

121122
if event_type in ("checkout.session.completed", "payment_intent.succeeded"):
122123
session_obj = event.get("data", {}).get("object", {})
123124
receipt_id = session_obj.get("id") or session_obj.get("payment_intent") or "rec_stripe_live"
124-
125+
125126
customer_details = session_obj.get("customer_details") or {}
126127
customer_email = (
127128
session_obj.get("customer_email")
@@ -130,7 +131,7 @@ def handle_stripe_webhook_payload(
130131
)
131132

132133
token = create_license_token(receipt_id=receipt_id, customer_email=customer_email)
133-
134+
134135
# Save issued license record locally
135136
_save_issued_license(receipt_id, customer_email, token)
136137

@@ -151,9 +152,11 @@ def _save_issued_license(receipt_id: str, email: str, token: str) -> None:
151152
records = json.loads(record_file.read_text(encoding="utf-8"))
152153
except Exception:
153154
records = []
154-
records.append({
155-
"receipt_id": receipt_id,
156-
"customer_email": email,
157-
"license_token": token,
158-
})
155+
records.append(
156+
{
157+
"receipt_id": receipt_id,
158+
"customer_email": email,
159+
"license_token": token,
160+
}
161+
)
159162
record_file.write_text(json.dumps(records, indent=2), encoding="utf-8")

‎python/tests/test_auto_debug_fallbacks.py‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@
1414
def test_auto_debug_fallback_recovery_on_exception():
1515
code = codes.rotated_surface_code(5)
1616
H = code.parity_check_matrix()
17-
17+
1818
cfg = BackendConfig(force=Backend.CUDA, allow_gpu=True, enable_auto_debug=True)
1919
ad = AutoDecoder(code.check_to_qubits, code.n_qubits, cfg)
20-
20+
2121
# Mock CUDA decoder to raise a simulated hardware/driver exception
2222
mock_cuda = ad._get_cuda()
2323
if mock_cuda is not None:
2424
with patch.object(mock_cuda, "batch_decode", side_effect=RuntimeError("Simulated CUDA OOM Error")):
2525
syns = (np.random.default_rng(42).random((32, code.n_checks)) < 0.08).astype(np.uint8)
26-
26+
2727
# The auto-debug engine should catch the CUDA exception and gracefully fall back to CPU_RAYON / CPU_SINGLE
2828
out = ad.batch_decode(syns)
2929
assert out.shape == (32, code.n_qubits)
3030
assert all(np.array_equal((H @ out[i]) & 1, syns[i]) for i in range(32))
31-
31+
3232
diag = ad.diagnostics()
3333
assert diag["backend_health"][Backend.CUDA] is False
3434
assert len(diag["debug_log"]) >= 1
@@ -39,6 +39,6 @@ def test_reset_backend_health():
3939
code = codes.repetition_code(7)
4040
ad = AutoDecoder(code.check_to_qubits, code.n_qubits)
4141
ad._diag.backend_health[Backend.CUDA] = False
42-
42+
4343
ad.reset_backend_health()
4444
assert ad._diag.backend_health[Backend.CUDA] is True

‎python/tests/test_license_included.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ def test_license_file_exists_and_non_empty():
4141
# Section 3 — accept any of the well-known phrasings used across revisions.
4242
assert (
4343
"commercial use requires a paid license" in text_lower
44-
or "commercial use" in text_lower and "license" in text_lower
44+
or "commercial use" in text_lower
45+
and "license" in text_lower
4546
)
4647

4748

0 commit comments

Comments
 (0)