From 471bae3e1ca8be9004156c1c881a1912259aeb16 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Thu, 18 Apr 2024 12:25:51 -0700 Subject: [PATCH 01/25] wip: various publicity modifications and refactors --- Cargo.lock | 215 ++++++++++++++++++++++------------- Cargo.toml | 8 +- crates/lib/Cargo.toml | 2 +- crates/lib/src/qpu/api.rs | 77 +++++++++---- crates/python/src/client.rs | 2 +- crates/python/src/qpu/api.rs | 2 +- 6 files changed, 200 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5869197b3..7bc675b21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,6 +227,12 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "bindgen" version = "0.53.3" @@ -248,7 +254,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "which 3.1.1", + "which", ] [[package]] @@ -1231,15 +1237,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.11.0" @@ -1284,13 +1281,14 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "8.3.0" +version = "9.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ "base64 0.21.5", + "js-sys", "pem", - "ring 0.16.20", + "ring 0.17.7", "serde", "serde_json", "simple_asn1", @@ -1838,31 +1836,31 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbjson" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048f9ac93c1eab514f9470c4bc8d97ca2a0a236b84f45cc19d69a59fc11467f6" +checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" dependencies = [ - "base64 0.13.1", + "base64 0.21.5", "serde", ] [[package]] name = "pbjson-build" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbb7b706f2afc610f3853550cdbbf6372fd324824a087806bd4480ea4996e24" +checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" dependencies = [ "heck", - "itertools 0.10.5", + "itertools 0.11.0", "prost", "prost-types", ] [[package]] name = "pbjson-types" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88c8d87f99a4ac14325e7a4c24af190fca261956e3b82dd7ed67e77e6c7043" +checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" dependencies = [ "bytes", "chrono", @@ -1881,11 +1879,12 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pem" -version = "1.1.1" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.13.1", + "base64 0.22.0", + "serde", ] [[package]] @@ -1956,12 +1955,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.1.25" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] @@ -1975,9 +1974,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.9" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", "prost-derive", @@ -1985,44 +1984,43 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.9" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", "heck", - "itertools 0.10.5", - "lazy_static", + "itertools 0.12.1", "log", "multimap", + "once_cell", "petgraph", "prettyplease", "prost", "prost-types", "regex", - "syn 1.0.109", + "syn 2.0.52", "tempfile", - "which 4.4.2", ] [[package]] name = "prost-derive" -version = "0.11.9" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] name = "prost-types" -version = "0.11.9" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ "prost", ] @@ -2176,9 +2174,8 @@ dependencies = [ [[package]] name = "qcs-api-client-common" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a168b5d1b4f6570e42c410f2476762db1e9bf0cc0816c30680b5869ddfebab8e" +version = "0.7.12" +source = "git+https://gitlab.com/rigetti/qcs/clients/qcs-api-client-rust.git?tag=grpc-internal/v0.8.13#edb5bf1ddeae40c438550930a5f7dc957057787a" dependencies = [ "async-trait", "backoff", @@ -2199,9 +2196,8 @@ dependencies = [ [[package]] name = "qcs-api-client-grpc" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1713c430354ce95c6a77f08a4e3ce0599a69b1d65be7e7cc97f5c6fcc94e8f3" +version = "0.7.14" +source = "git+https://gitlab.com/rigetti/qcs/clients/qcs-api-client-rust.git?tag=grpc-internal/v0.8.13#edb5bf1ddeae40c438550930a5f7dc957057787a" dependencies = [ "backoff", "http", @@ -2231,9 +2227,8 @@ dependencies = [ [[package]] name = "qcs-api-client-openapi" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1892f58e793ff0d68110736583c88a442a9690ceb3e59dc65cd2daf3fbec29" +version = "0.8.13" +source = "git+https://gitlab.com/rigetti/qcs/clients/qcs-api-client-rust.git?tag=grpc-internal/v0.8.13#edb5bf1ddeae40c438550930a5f7dc957057787a" dependencies = [ "anyhow", "qcs-api-client-common", @@ -2429,7 +2424,7 @@ dependencies = [ "pin-project-lite", "rustls 0.21.9", "rustls-native-certs 0.6.3", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", @@ -2481,9 +2476,9 @@ dependencies = [ [[package]] name = "rigetti-pyo3" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff977cba40f2cadf214226cf51c9729a4f5730a5413f901246eed78cb6e795c9" +checksum = "621258b9f9ace3f8dc4d44c29231647143a1f17be3e4377ec3c3231b7f38bed2" dependencies = [ "num-complex", "num-traits", @@ -2623,10 +2618,24 @@ checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", "ring 0.17.7", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct 0.7.1", ] +[[package]] +name = "rustls" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +dependencies = [ + "log", + "ring 0.17.7", + "rustls-pki-types", + "rustls-webpki 0.102.2", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.5.0" @@ -2646,7 +2655,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.2", + "rustls-pki-types", "schannel", "security-framework", ] @@ -2660,6 +2682,22 @@ dependencies = [ "base64 0.21.5", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.0", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2670,6 +2708,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring 0.17.7", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -2957,6 +3006,12 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -3234,6 +3289,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.3", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-socks" version = "0.5.1" @@ -3332,17 +3398,15 @@ dependencies = [ [[package]] name = "tonic" -version = "0.9.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" dependencies = [ "async-stream", "async-trait", "axum", "base64 0.21.5", "bytes", - "futures-core", - "futures-util", "h2", "http", "http-body", @@ -3351,10 +3415,11 @@ dependencies = [ "percent-encoding", "pin-project", "prost", - "rustls-native-certs 0.6.3", - "rustls-pemfile", + "rustls-native-certs 0.7.0", + "rustls-pemfile 2.1.2", + "rustls-pki-types", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls 0.25.0", "tokio-stream", "tower", "tower-layer", @@ -3364,15 +3429,15 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.9.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" +checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" dependencies = [ "prettyplease", "proc-macro2", "prost-build", "quote", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] @@ -3696,7 +3761,7 @@ dependencies = [ "mime_guess", "percent-encoding", "pin-project", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "scoped-tls", "serde", "serde_json", @@ -3815,18 +3880,6 @@ dependencies = [ "libc", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "winapi" version = "0.3.9" @@ -4090,6 +4143,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + [[package]] name = "zeromq-src" version = "0.2.6+4.3.4" diff --git a/Cargo.toml b/Cargo.toml index 5ca0551d0..29e38215e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ resolver = "2" [workspace.dependencies] qcs-api = "0.2.1" -qcs-api-client-common = "0.7.10" -qcs-api-client-grpc = "0.7.13" -qcs-api-client-openapi = "0.8.11" +qcs-api-client-common = { git = "https://gitlab.com/rigetti/qcs/clients/qcs-api-client-rust.git", tag="grpc-internal/v0.8.13" } +qcs-api-client-grpc = { git = "https://gitlab.com/rigetti/qcs/clients/qcs-api-client-rust.git", tag="grpc-internal/v0.8.13" } +qcs-api-client-openapi = { git = "https://gitlab.com/rigetti/qcs/clients/qcs-api-client-rust.git", tag="grpc-internal/v0.8.13" } serde_json = "1.0.86" thiserror = "1.0.57" tokio = "1.36.0" @@ -27,7 +27,7 @@ numpy = "0.20.0" pyo3 = "0.20.0" pyo3-asyncio = { version = "0.20", features = ["tokio-runtime"] } pyo3-build-config = "0.20.0" -rigetti-pyo3 = { version = "0.3.1", default-features = false, features = ["complex"] } +rigetti-pyo3 = { version = "0.3.2", default-features = false, features = ["complex"] } # The primary intent of these options is to reduce the binary size for Python wheels # since PyPi has limits on how much storage a project can use. diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml index 6ee74439b..77b081a09 100644 --- a/crates/lib/Cargo.toml +++ b/crates/lib/Cargo.toml @@ -41,7 +41,7 @@ tokio = { workspace = true, features = ["fs", "rt-multi-thread"] } toml = "0.7.3" tracing = { version = "0.1", optional = true, features = ["log"] } uuid = { version = "1.2.1", features = ["v4"] } -tonic = { version = "0.9.2", features = ["tls", "tls-roots"] } +tonic = { version = "0.11.0", features = ["tls", "tls-roots"] } zmq = { version = "0.10.0" } itertools = "0.11.0" derive_builder = "0.12.0" diff --git a/crates/lib/src/qpu/api.rs b/crates/lib/src/qpu/api.rs index ce6fd55f4..a6b9662b0 100644 --- a/crates/lib/src/qpu/api.rs +++ b/crates/lib/src/qpu/api.rs @@ -1,8 +1,9 @@ //! This module provides bindings to for submitting jobs to and retrieving them from //! Rigetti QPUs using the QCS API. -use std::{fmt, time::Duration}; +use std::{convert::TryFrom, fmt, time::Duration}; +use async_trait::async_trait; use cached::proc_macro::cached; use derive_builder::Builder; use qcs_api_client_common::configuration::RefreshError; @@ -37,7 +38,8 @@ use crate::executable::Parameters; use crate::client::{GrpcClientError, GrpcConnection, Qcs}; -const MAX_DECODING_MESSAGE_SIZE_BYTES: usize = 250 * 1024 * 1024; +/// The maximum size of a gRPC response, in bytes. +pub const MAX_DECODING_MESSAGE_SIZE_BYTES: usize = 250 * 1024 * 1024; pub(crate) fn params_into_job_execution_configuration( params: &Parameters, @@ -299,19 +301,19 @@ pub async fn retrieve_results( .result .ok_or_else(|| GrpcClientError::ResponseEmpty("Job Execution Results".into())) .map_err(QpuApiError::from) - .and_then( - |result| match controller_job_execution_result::Status::from_i32(result.status) { - Some(controller_job_execution_result::Status::Success) => Ok(result), + .and_then(|result| { + match controller_job_execution_result::Status::try_from(result.status) + .map_err(|e| QpuApiError::StatusCodeDecode(e.to_string()))? + { + controller_job_execution_result::Status::Success => Ok(result), status => Err(QpuApiError::JobExecutionFailed { - status: status - .map_or("UNDEFINED", |status| status.as_str_name()) - .to_string(), + status: status.as_str_name().to_string(), message: result .status_message .unwrap_or("No message provided.".to_string()), }), - }, - ) + } + }) } /// Options available when connecting to a QPU. @@ -431,11 +433,21 @@ pub enum ConnectionStrategy { EndpointId(String), } -/// Methods that help select and configure a controller service client given a set of -/// [`ExecutionOptions`] and QPU ID. -impl ExecutionOptions { +/// An ExecutionTarget provides methods for provided the appropriate connection to the execution +/// service. +/// +/// Implementors provide a [`ConnectionStrategy`] and timeout, the trait provides default +/// implementation for getting connections and execution targets. +#[async_trait] +pub trait ExecutionTarget<'a> { + /// The [`ConnectionStrategy`] to use to determine the connection target. + fn connection_strategy(&'a self) -> &'a ConnectionStrategy; + /// The timeout to use for requests to the target. + fn timeout(&self) -> Option; + + /// Get the [`execute_controller_job_request::Target`] for the given quantum processor ID. fn get_job_target( - &self, + &'a self, quantum_processor_id: Option<&str>, ) -> Option { match self.connection_strategy() { @@ -448,8 +460,9 @@ impl ExecutionOptions { } } + /// Get the [`get_controller_job_results_request::Target`] for the given quantum processor ID. fn get_results_target( - &self, + &'a self, quantum_processor_id: Option<&str>, ) -> Option { match self.connection_strategy() { @@ -462,8 +475,9 @@ impl ExecutionOptions { } } + /// Get the [`cancel_controller_jobs_request::Target`] for the given quantum processor ID. fn get_cancel_target( - &self, + &'a self, quantum_processor_id: Option<&str>, ) -> Option { match self.connection_strategy() { @@ -476,9 +490,9 @@ impl ExecutionOptions { } } - /// Get a controller client for the given QPU ID. - pub async fn get_controller_client( - &self, + /// Get a controller client for the given quantum processor ID. + async fn get_controller_client( + &'a self, client: &Qcs, quantum_processor_id: Option<&str>, ) -> Result, QpuApiError> { @@ -490,8 +504,8 @@ impl ExecutionOptions { } /// Get a GRPC connection to a QPU, without specifying the API to use. - pub async fn get_qpu_grpc_connection( - &self, + async fn get_qpu_grpc_connection( + &'a self, client: &Qcs, quantum_processor_id: Option<&str>, ) -> Result { @@ -521,6 +535,7 @@ impl ExecutionOptions { self.grpc_address_to_channel(&address, client) } + /// Get a channel from the given gRPC address. fn grpc_address_to_channel( &self, address: &str, @@ -535,6 +550,7 @@ impl ExecutionOptions { ))) } + /// Get the gateway address for the given quantum processor ID. async fn get_gateway_address( &self, quantum_processor_id: &str, @@ -543,6 +559,7 @@ impl ExecutionOptions { get_accessor_with_cache(quantum_processor_id, client).await } + /// Get the default endpoint address for the given quantum processor ID. async fn get_default_endpoint_address( &self, quantum_processor_id: &str, @@ -552,6 +569,19 @@ impl ExecutionOptions { } } +/// Methods that help select and configure a controller service client given a set of +/// [`ExecutionOptions`] and QPU ID. +#[async_trait] +impl<'a> ExecutionTarget<'a> for ExecutionOptions { + fn connection_strategy(&'a self) -> &'a ConnectionStrategy { + self.connection_strategy() + } + + fn timeout(&self) -> Option { + self.timeout() + } +} + #[cached( result = true, time = 60, @@ -685,4 +715,9 @@ pub enum QpuApiError { /// The message associated with the failed job. message: String, }, + + /// Error that can occur when the gRPC status code could not be decoded. + #[error("The status code could not be decoded: {0}")] + StatusCodeDecode(String), // TODO: This error is in prost. Should we really use that as a dep + // just for the error type? } diff --git a/crates/python/src/client.rs b/crates/python/src/client.rs index a02e15f18..29b550704 100644 --- a/crates/python/src/client.rs +++ b/crates/python/src/client.rs @@ -95,7 +95,7 @@ py_wrap_type! { } impl PyQcsClient { - pub(crate) async fn get_or_create_client(client: Option) -> Qcs { + pub async fn get_or_create_client(client: Option) -> Qcs { match client { Some(client) => client.into(), None => Qcs::load().await, diff --git a/crates/python/src/qpu/api.rs b/crates/python/src/qpu/api.rs index f5b4e81fe..ae0da9702 100644 --- a/crates/python/src/qpu/api.rs +++ b/crates/python/src/qpu/api.rs @@ -541,7 +541,7 @@ impl PyApiExecutionOptionsBuilder { } py_wrap_type! { - #[derive(Default)] + #[derive(Debug, Default)] PyConnectionStrategy(ConnectionStrategy) as "ConnectionStrategy" } impl_repr!(PyConnectionStrategy); From 939318f53c2aeb9239b44bcd2e53a365881783d6 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 12 Jul 2024 11:35:59 -0700 Subject: [PATCH 02/25] derive debug on PyTranslationOptions --- crates/python/src/qpu/translation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python/src/qpu/translation.rs b/crates/python/src/qpu/translation.rs index bc0467036..01d138061 100644 --- a/crates/python/src/qpu/translation.rs +++ b/crates/python/src/qpu/translation.rs @@ -73,7 +73,7 @@ py_wrap_simple_enum! { } } -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] #[pyclass(name = "TranslationOptions")] pub struct PyTranslationOptions(TranslationOptions); From 42f12b26cc4f4aea486420243ed5e9e1e3ed6db4 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Wed, 17 Jul 2024 15:28:56 -0700 Subject: [PATCH 03/25] add utility method for converting TranslationOptions into protobuf bytes --- Cargo.lock | 36 +++++++++++++++-------- Cargo.toml | 6 ++-- crates/python/Cargo.toml | 3 +- crates/python/qcs_sdk/qpu/translation.pyi | 4 +++ crates/python/src/client.rs | 10 +++++++ crates/python/src/qpu/translation.rs | 13 +++++++- 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a32d7352..59995ad63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2515,7 +2515,7 @@ dependencies = [ "num-complex", "parking_lot", "portable-atomic", - "pyo3-build-config", + "pyo3-build-config 0.20.3", "pyo3-ffi", "pyo3-macros", "unindent", @@ -2544,6 +2544,16 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "pyo3-build-config" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7879eb018ac754bba32cb0eec7526391c02c14a093121857ed09fbf1d1057d41" +dependencies = [ + "once_cell", + "target-lexicon", +] + [[package]] name = "pyo3-ffi" version = "0.20.3" @@ -2551,7 +2561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa" dependencies = [ "libc", - "pyo3-build-config", + "pyo3-build-config 0.20.3", ] [[package]] @@ -2585,7 +2595,7 @@ checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185" dependencies = [ "heck 0.4.1", "proc-macro2", - "pyo3-build-config", + "pyo3-build-config 0.20.3", "quote", "syn 2.0.66", ] @@ -2700,9 +2710,7 @@ dependencies = [ [[package]] name = "qcs-api-client-common" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "040552f233992abe94376304cf17dab2d79ff62d4a9ee986d75efe3d19337f57" +version = "0.8.12" dependencies = [ "async-trait", "backoff", @@ -2712,7 +2720,12 @@ dependencies = [ "home", "http", "jsonwebtoken", + "paste", + "pyo3", + "pyo3-asyncio", + "pyo3-build-config 0.22.1", "reqwest", + "rigetti-pyo3", "serde", "shellexpand", "thiserror", @@ -2726,9 +2739,7 @@ dependencies = [ [[package]] name = "qcs-api-client-grpc" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb755cd59b1b3ce99abe52ff6898ad74ca26463e94300c103cafaac4f345c4f3" +version = "0.8.6" dependencies = [ "backoff", "http", @@ -2759,9 +2770,7 @@ dependencies = [ [[package]] name = "qcs-api-client-openapi" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008d06c4051dd4b3e7d5b6c1f31457c68384f8f36b05b304de6059bbd81c6128" +version = "0.9.6" dependencies = [ "anyhow", "qcs-api-client-common", @@ -2787,9 +2796,10 @@ dependencies = [ "opentelemetry 0.23.0", "opentelemetry_sdk 0.23.0", "paste", + "prost", "pyo3", "pyo3-asyncio", - "pyo3-build-config", + "pyo3-build-config 0.20.3", "pyo3-log", "pyo3-opentelemetry", "pyo3-tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index 7b83e6b27..556d7e38d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ resolver = "2" [workspace.dependencies] qcs-api = "0.2.1" -qcs-api-client-common = "0.8.4" -qcs-api-client-grpc = "0.8.4" -qcs-api-client-openapi = "0.9.4" +qcs-api-client-common = { path = "../qcs-api-client-rust/qcs-api-client-common" } +qcs-api-client-grpc = { path = "../qcs-api-client-rust/qcs-api-client-grpc" } +qcs-api-client-openapi = { path = "../qcs-api-client-rust/qcs-api-client-openapi/public" } serde_json = "1.0.86" thiserror = "1.0.57" tokio = "1.36.0" diff --git a/crates/python/Cargo.toml b/crates/python/Cargo.toml index e38e17b70..a88dfe900 100644 --- a/crates/python/Cargo.toml +++ b/crates/python/Cargo.toml @@ -21,7 +21,7 @@ crate-type = ["cdylib", "rlib"] async-trait = "0.1.73" qcs = { path = "../lib", features = ["tracing-opentelemetry"] } qcs-api.workspace = true -qcs-api-client-common.workspace = true +qcs-api-client-common = { workspace = true, features = ["python"] } qcs-api-client-grpc.workspace = true qcs-api-client-openapi.workspace = true pyo3.workspace = true @@ -40,6 +40,7 @@ once_cell = "1.18.0" opentelemetry = { version = "0.23.0" } opentelemetry_sdk = { version = "0.23.0" } tracing = { version = "0.1.37" } +prost = "0.12.6" [build-dependencies] pyo3-build-config.workspace = true diff --git a/crates/python/qcs_sdk/qpu/translation.pyi b/crates/python/qcs_sdk/qpu/translation.pyi index 73edc1113..8beeb7edc 100644 --- a/crates/python/qcs_sdk/qpu/translation.pyi +++ b/crates/python/qcs_sdk/qpu/translation.pyi @@ -155,3 +155,7 @@ class TranslationOptions: :param: allow_frame_redefinition: If True, allow defined frames to differ from Rigetti defaults. Only available to certain users. Otherwise, only ``INITIAL-FREQUENCY`` and ``CHANNEL-DELAY`` may be modified. """ + def encode_as_protobuf(self) -> bytes: + """ + Serialize these translation options into the Protocol Buffer format. + """ diff --git a/crates/python/src/client.rs b/crates/python/src/client.rs index c1f420495..76c19ceca 100644 --- a/crates/python/src/client.rs +++ b/crates/python/src/client.rs @@ -201,6 +201,16 @@ impl PyQcsClient { self.as_ref().get_config().qvm_url().to_string() } + #[getter] + pub fn auth_server(&self) -> AuthServer { + self.as_ref().get_config().auth_server().clone() + } + + #[getter] + pub fn tokens(&self, py: Python<'_>) -> PyResult { + self.as_ref().get_config().get_tokens(py) + } + fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject { match op { CompareOp::Eq => (self == other).into_py(py), diff --git a/crates/python/src/qpu/translation.rs b/crates/python/src/qpu/translation.rs index 01d138061..51929ced1 100644 --- a/crates/python/src/qpu/translation.rs +++ b/crates/python/src/qpu/translation.rs @@ -1,9 +1,15 @@ //! Translating programs. use std::{collections::HashMap, time::Duration}; +use prost::Message; +use pyo3::types::PyBytes; +use pyo3::Python; use pyo3::{exceptions::PyRuntimeError, pyclass, pyfunction, pymethods, PyResult}; use qcs::qpu::translation::TranslationOptions; -use qcs_api_client_grpc::services::translation::translation_options::TranslationBackend as ApiTranslationBackend; +use qcs_api_client_grpc::services::translation::{ + translation_options::TranslationBackend as ApiTranslationBackend, + TranslationOptions as ApiTranslationOptions, +}; use rigetti_pyo3::{create_init_submodule, py_wrap_error, py_wrap_simple_enum, ToPythonError}; use crate::py_sync::py_function_sync_async; @@ -152,6 +158,11 @@ impl PyTranslationOptions { Self(builder) } + fn encode_as_protobuf<'a>(&'a self, py: Python<'a>) -> &'a PyBytes { + let options: ApiTranslationOptions = self.0.clone().into(); + PyBytes::new(py, options.encode_to_vec().as_slice()) + } + fn __repr__(&self) -> String { format!("{:?}", self.0) } From 478d68d1ecbbc51ebf379db172c8b7a29754e431 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Wed, 17 Jul 2024 16:02:54 -0700 Subject: [PATCH 04/25] add methods for inspecting a PyConnectionStrategy --- crates/python/qcs_sdk/qpu/api.pyi | 11 +++++++++++ crates/python/src/qpu/api.rs | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/crates/python/qcs_sdk/qpu/api.pyi b/crates/python/qcs_sdk/qpu/api.pyi index 524b2054e..78483a9d5 100644 --- a/crates/python/qcs_sdk/qpu/api.pyi +++ b/crates/python/qcs_sdk/qpu/api.pyi @@ -434,10 +434,21 @@ class ConnectionStrategy: @staticmethod def gateway() -> ConnectionStrategy: """Connect through the publicly accessbile gateway.""" + def is_gateway(self) -> bool: + """True if the ConnectionStrategy is to connect to the QCS gateway.""" @staticmethod def direct_access() -> ConnectionStrategy: """Connect directly to the default endpoint, bypassing the gateway. Should only be used when you have direct network access and an active reservation.""" + def is_direct_access(self) -> bool: + """True if the ConnectionStrategy is to use direct access.""" @staticmethod def endpoint_id(endpoint_id: str) -> ConnectionStrategy: """Connect directly to a specific endpoint using its ID.""" + def is_endpoint_id(self) -> bool: + """True if the ConnectionStrategy is to connect to a particular endpoint ID.""" + def get_endpoint_id(self) -> str: + """Get the endpoint ID used by the ConnectionStrategy. + + Raises an error if this ConnectionStrategy doesn't use a specific endpoint ID. + """ diff --git a/crates/python/src/qpu/api.rs b/crates/python/src/qpu/api.rs index d98fda64f..c085b515d 100644 --- a/crates/python/src/qpu/api.rs +++ b/crates/python/src/qpu/api.rs @@ -562,16 +562,37 @@ impl PyConnectionStrategy { Self(ConnectionStrategy::Gateway) } + fn is_gateway(&self) -> bool { + matches!(self.as_inner(), ConnectionStrategy::Gateway) + } + #[staticmethod] fn direct_access() -> Self { Self(ConnectionStrategy::DirectAccess) } + fn is_direct_access(&self) -> bool { + matches!(self.as_inner(), ConnectionStrategy::DirectAccess) + } + #[staticmethod] fn endpoint_id(endpoint_id: String) -> PyResult { Ok(Self(ConnectionStrategy::EndpointId(endpoint_id))) } + fn is_endpoint_id(&self) -> bool { + matches!(self.as_inner(), ConnectionStrategy::EndpointId(_)) + } + + fn get_endpoint_id(&self) -> PyResult { + match self.as_inner() { + ConnectionStrategy::EndpointId(id) => Ok(id.clone()), + _ => Err(PyValueError::new_err( + "ConnectionStrategy is not an EndpointId", + )), + } + } + fn __richcmp__(&self, py: Python<'_>, other: &Self, op: CompareOp) -> PyObject { match op { CompareOp::Eq => (self.as_inner() == other.as_inner()).into_py(py), From 2b4061bc541f152594b21d5042a4ac1cad09f36b Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Thu, 18 Jul 2024 11:42:49 -0700 Subject: [PATCH 05/25] update qcs dependencies --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 556d7e38d..ecc0397e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ resolver = "2" [workspace.dependencies] qcs-api = "0.2.1" -qcs-api-client-common = { path = "../qcs-api-client-rust/qcs-api-client-common" } -qcs-api-client-grpc = { path = "../qcs-api-client-rust/qcs-api-client-grpc" } -qcs-api-client-openapi = { path = "../qcs-api-client-rust/qcs-api-client-openapi/public" } +qcs-api-client-common = "0.8.14" +qcs-api-client-grpc = "0.8.7" +qcs-api-client-openapi = "0.9.7" serde_json = "1.0.86" thiserror = "1.0.57" tokio = "1.36.0" From cf0f95e1c5c05bf7af2f426e7b06ae14836b967c Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Thu, 18 Jul 2024 12:21:44 -0700 Subject: [PATCH 06/25] update rigetti-pyo3 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ecc0397e9..0b73696ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ pyo3-opentelemetry = { version = "=0.3.2-dev.1" } pyo3-tracing-subscriber = { version = "=0.1.2-dev.1", default-features = true } pyo3-build-config = "0.20.0" -rigetti-pyo3 = { version = "0.3.2", default-features = false, features = ["complex", "time"] } +rigetti-pyo3 = { version = "0.4.1", default-features = false, features = ["complex", "time"] } # The primary intent of these options is to reduce the binary size for Python wheels # since PyPi has limits on how much storage a project can use. From 39ef7b66957a11a4aeb67ac08cf7c765c5bb2481 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Tue, 27 Aug 2024 10:57:12 -0700 Subject: [PATCH 07/25] update Cargo.lock --- Cargo.lock | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4ae1c6d0..491cfe18b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2746,7 +2746,9 @@ dependencies = [ [[package]] name = "qcs-api-client-common" -version = "0.8.12" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43621cfb969b563276e015ca3247a6e3dc3b5cc5ba9ffd6bc58adc51a02a467f" dependencies = [ "async-trait", "backoff", @@ -2761,7 +2763,7 @@ dependencies = [ "pyo3-asyncio", "pyo3-build-config 0.22.1", "reqwest", - "rigetti-pyo3", + "rigetti-pyo3 0.3.6", "serde", "shellexpand", "thiserror", @@ -2775,7 +2777,9 @@ dependencies = [ [[package]] name = "qcs-api-client-grpc" -version = "0.8.6" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b461aabc128adcb54d644aa4517b243d366a9ffe88a75f3fd3a5bb887f6b1e75" dependencies = [ "backoff", "http", @@ -2806,7 +2810,9 @@ dependencies = [ [[package]] name = "qcs-api-client-openapi" -version = "0.9.6" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7af3429e00bd96faed7e32c8643b74ad86a072714b304475b15c8be7529d85" dependencies = [ "anyhow", "qcs-api-client-common", @@ -2845,7 +2851,7 @@ dependencies = [ "qcs-api-client-grpc", "qcs-api-client-openapi", "quil-rs", - "rigetti-pyo3", + "rigetti-pyo3 0.4.1", "serde_json", "thiserror", "tokio", @@ -3109,6 +3115,19 @@ dependencies = [ "time", ] +[[package]] +name = "rigetti-pyo3" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f924032d36104a859f936762576a9e6fc0811b37a1f4a8144c0b9b25ee89607b" +dependencies = [ + "num-complex", + "num-traits", + "paste", + "pyo3", + "time", +] + [[package]] name = "ring" version = "0.16.20" From 2ef36aad02b915bac29c8488033da6edcc037801 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Tue, 27 Aug 2024 11:05:05 -0700 Subject: [PATCH 08/25] replace py_sync module w/ rigetti-pyo3 --- crates/python/Cargo.toml | 4 + crates/python/src/compiler/quilc.rs | 5 +- crates/python/src/executable.rs | 3 +- crates/python/src/lib.rs | 5 +- crates/python/src/py_sync.rs | 133 --------------------------- crates/python/src/qpu/api.rs | 6 +- crates/python/src/qpu/isa.rs | 5 +- crates/python/src/qpu/mod.rs | 5 +- crates/python/src/qpu/translation.rs | 7 +- crates/python/src/qvm/api.rs | 5 +- crates/python/src/qvm/mod.rs | 5 +- 11 files changed, 27 insertions(+), 156 deletions(-) delete mode 100644 crates/python/src/py_sync.rs diff --git a/crates/python/Cargo.toml b/crates/python/Cargo.toml index 94abcf10d..8ac65995b 100644 --- a/crates/python/Cargo.toml +++ b/crates/python/Cargo.toml @@ -45,3 +45,7 @@ prost = "0.12.6" [build-dependencies] pyo3-build-config.workspace = true pyo3-tracing-subscriber = { version = "=0.1.2-dev.1", features = ["layer-otel-otlp-file", "layer-otel-otlp", "stubs"], default-features = false } + +[lints.clippy] +too_many_arguments = "allow" + diff --git a/crates/python/src/compiler/quilc.rs b/crates/python/src/compiler/quilc.rs index 0592d3c29..d7fbcbf4f 100644 --- a/crates/python/src/compiler/quilc.rs +++ b/crates/python/src/compiler/quilc.rs @@ -6,8 +6,8 @@ use qcs::compiler::quilc::{ use qcs_api_client_openapi::models::InstructionSetArchitecture; use quil_rs::quil::Quil; use rigetti_pyo3::{ - create_init_submodule, impl_repr, py_wrap_data_struct, py_wrap_error, py_wrap_struct, - py_wrap_type, + create_init_submodule, impl_repr, py_function_sync_async, py_wrap_data_struct, py_wrap_error, + py_wrap_struct, py_wrap_type, pyo3::{ exceptions::{PyRuntimeError, PyValueError}, pyclass, pyfunction, pymethods, @@ -17,7 +17,6 @@ use rigetti_pyo3::{ wrap_error, PyWrapper, ToPythonError, }; -use crate::py_sync::py_function_sync_async; use crate::qpu::isa::PyInstructionSetArchitecture; create_init_submodule! { diff --git a/crates/python/src/executable.rs b/crates/python/src/executable.rs index 6e56e9da8..0eaf82e64 100644 --- a/crates/python/src/executable.rs +++ b/crates/python/src/executable.rs @@ -4,7 +4,7 @@ use opentelemetry::trace::FutureExt; use pyo3::{pyclass, FromPyObject}; use qcs::{Error, Executable, ExecutionData, JobHandle, Service}; use rigetti_pyo3::{ - impl_as_mut_for_wrapper, py_wrap_error, py_wrap_simple_enum, py_wrap_type, + impl_as_mut_for_wrapper, py_async, py_sync, py_wrap_error, py_wrap_simple_enum, py_wrap_type, pyo3::{exceptions::PyRuntimeError, pymethods, types::PyDict, Py, PyAny, PyResult, Python}, wrap_error, PyWrapper, ToPython, ToPythonError, }; @@ -14,7 +14,6 @@ use tracing::instrument; use crate::{ compiler::quilc::{PyCompilerOpts, PyQuilcClient}, execution_data::PyExecutionData, - py_sync::{py_async, py_sync}, qpu::{api::PyExecutionOptions, translation::PyTranslationOptions}, }; diff --git a/crates/python/src/lib.rs b/crates/python/src/lib.rs index 18af0c172..d272dbcaf 100644 --- a/crates/python/src/lib.rs +++ b/crates/python/src/lib.rs @@ -1,7 +1,7 @@ use std::sync::Mutex; use pyo3::prelude::*; -use rigetti_pyo3::create_init_submodule; +use rigetti_pyo3::{create_init_submodule, py_sync}; use executable::ExecutionError; use execution_data::RegisterMatrixConversionError; @@ -16,7 +16,6 @@ pub mod qvm; pub mod register_data; pub(crate) mod from_py; -pub(crate) mod py_sync; create_init_submodule! { classes: [ @@ -74,5 +73,5 @@ fn reset_logging() { #[pyfunction] #[pyo3(name = "_gather_diagnostics")] fn gather_diagnostics(py: Python<'_>) -> PyResult { - py_sync::py_sync!(py, async { Ok(qcs::diagnostics::get_report().await) }) + py_sync!(py, async { Ok(qcs::diagnostics::get_report().await) }) } diff --git a/crates/python/src/py_sync.rs b/crates/python/src/py_sync.rs deleted file mode 100644 index c47fdd5f6..000000000 --- a/crates/python/src/py_sync.rs +++ /dev/null @@ -1,133 +0,0 @@ -/// Spawn and block on a future using the pyo3 tokio runtime. -/// Useful for returning a synchronous `PyResult`. -/// -/// -/// When used like the following: -/// ```rs -/// async fn say_hello(name: String) -> String { -/// format!("hello {name}") -/// } -/// -/// #[pyo3(name="say_hello")] -/// pub fn py_say_hello(name: String) -> PyResult { -/// py_sync!(say_hello(name)) -/// } -/// ``` -/// -/// Becomes the associated "synchronous" python call: -/// ```py -/// assert say_hello("Rigetti") == "hello Rigetti" -/// ``` -macro_rules! py_sync { - ($py: ident, $body: expr) => {{ - $py.allow_threads(|| { - let runtime = ::pyo3_asyncio::tokio::get_runtime(); - let handle = runtime.spawn($body); - - runtime.block_on(async { - tokio::select! { - result = handle => result.map_err(|err| ::pyo3::exceptions::PyRuntimeError::new_err(err.to_string()))?, - signal_err = async { - // A 100ms loop delay is a bit arbitrary, but seems to - // balance CPU usage and SIGINT responsiveness well enough. - let delay = ::std::time::Duration::from_millis(100); - loop { - ::pyo3::Python::with_gil(|py| { - py.check_signals() - })?; - ::tokio::time::sleep(delay).await; - } - } => signal_err, - } - }) - }) - }}; -} - -/// Convert a rust future into a Python awaitable using -/// `pyo3_asyncio::tokio::future_into_py` -macro_rules! py_async { - ($py: ident, $body: expr) => { - ::pyo3_asyncio::tokio::future_into_py($py, $body) - }; -} - -/// Given a single implementation of an async function, -/// create that function as private and two pyfunctions -/// named after it that can be used to invoke either -/// blocking or async variants of the same function. -/// -/// In order to export the function to Python using pyo3 -/// you must include the `#[pyfunction]` attribute. This -/// isn't included in the macro by default since one may -/// wish to annotate `#[pyfunction]` with additional -/// arguments. -/// -/// The given function will be spawned on a Rust event loop -/// this means functions like [`pyo3::Python::with_gil`] -/// should not be used, as acquiring Python's global -/// interpreter lock from a Rust runtime -/// isn't possible. -/// -/// This macro cannot be used when lifetime specifiers are -/// required, or the pyfunction bodies need additional -/// parameter handling besides simply calling out to -/// the underlying `py_async` or `py_sync` macros. -/// -/// ```rs -/// // ... becomes python package "things" -/// create_init_submodule! { -/// funcs: [ -/// py_do_thing, -/// py_do_thing_async, -/// ] -/// } -/// -/// py_function_sync_async! { -/// #[pyfunction] -/// #[args(timeout = "None")] -/// async fn do_thing(timeout: Option) -> PyResult { -/// // ... sleep for timeout ... -/// Ok(String::from("done")) -/// } -/// } -/// ``` -/// -/// becomes in python: -/// ```py -/// from things import do_thing, do_thing_async -/// assert do_thing() == "done" -/// assert await do_thing_async() == "done" -/// ``` -macro_rules! py_function_sync_async { - ( - $(#[$meta: meta])+ - async fn $name: ident($($(#[$arg_meta: meta])*$arg: ident : $kind: ty),* $(,)?) $(-> $ret: ty)? $body: block - ) => { - async fn $name($($arg: $kind,)*) $(-> $ret)? { - $body - } - - ::paste::paste! { - $(#[$meta])+ - #[allow(clippy::too_many_arguments)] - #[pyo3(name = $name "")] - pub fn [< py_ $name >](py: ::pyo3::Python<'_> $(, $(#[$arg_meta])*$arg: $kind)*) $(-> $ret)? { - use opentelemetry::trace::FutureExt; - $crate::py_sync::py_sync!(py, $name($($arg),*).with_current_context()) - } - - $(#[$meta])+ - #[pyo3(name = $name "_async")] - #[allow(clippy::too_many_arguments)] - pub fn [< py_ $name _async >](py: ::pyo3::Python<'_> $(, $(#[$arg_meta])*$arg: $kind)*) -> ::pyo3::PyResult<&::pyo3::PyAny> { - use opentelemetry::trace::FutureExt; - $crate::py_sync::py_async!(py, $name($($arg),*).with_current_context()) - } - } - }; -} - -pub(crate) use py_async; -pub(crate) use py_function_sync_async; -pub(crate) use py_sync; diff --git a/crates/python/src/qpu/api.rs b/crates/python/src/qpu/api.rs index b7ca61dd2..ee47f8bba 100644 --- a/crates/python/src/qpu/api.rs +++ b/crates/python/src/qpu/api.rs @@ -19,12 +19,10 @@ use qcs_api_client_grpc::models::controller::{ data_value, readout_values, ControllerJobExecutionResult, }; use rigetti_pyo3::{ - create_init_submodule, impl_as_mut_for_wrapper, impl_repr, num_complex, py_wrap_error, - py_wrap_type, py_wrap_union_enum, wrap_error, PyWrapper, ToPythonError, + create_init_submodule, impl_as_mut_for_wrapper, impl_repr, num_complex, py_function_sync_async, + py_wrap_error, py_wrap_type, py_wrap_union_enum, wrap_error, PyWrapper, ToPythonError, }; -use crate::py_sync::py_function_sync_async; - use crate::client::PyQcsClient; use super::result_data::PyMemoryValues; diff --git a/crates/python/src/qpu/isa.rs b/crates/python/src/qpu/isa.rs index 367672662..a751a22b0 100644 --- a/crates/python/src/qpu/isa.rs +++ b/crates/python/src/qpu/isa.rs @@ -3,7 +3,8 @@ use qcs_api_client_openapi::models::{ OperationSite, Parameter, }; use rigetti_pyo3::{ - create_init_submodule, py_wrap_data_struct, py_wrap_error, py_wrap_simple_enum, + create_init_submodule, py_function_sync_async, py_wrap_data_struct, py_wrap_error, + py_wrap_simple_enum, pyo3::{ exceptions::{PyRuntimeError, PyValueError}, prelude::*, @@ -15,7 +16,7 @@ use rigetti_pyo3::{ use qcs::qpu::get_isa; -use crate::{client::PyQcsClient, py_sync::py_function_sync_async}; +use crate::client::PyQcsClient; create_init_submodule! { classes: [ diff --git a/crates/python/src/qpu/mod.rs b/crates/python/src/qpu/mod.rs index 3c3307bd4..bdfdf7773 100644 --- a/crates/python/src/qpu/mod.rs +++ b/crates/python/src/qpu/mod.rs @@ -1,7 +1,9 @@ use std::time::Duration; use pyo3::{exceptions::PyRuntimeError, pyfunction, PyResult}; -use rigetti_pyo3::{create_init_submodule, py_wrap_error, wrap_error, ToPythonError}; +use rigetti_pyo3::{ + create_init_submodule, py_function_sync_async, py_wrap_error, wrap_error, ToPythonError, +}; pub use result_data::{PyQpuResultData, PyReadoutValues, RawQpuReadoutData}; @@ -11,7 +13,6 @@ mod result_data; pub mod translation; use crate::client::PyQcsClient; -use crate::py_sync::py_function_sync_async; use self::result_data::PyMemoryValues; diff --git a/crates/python/src/qpu/translation.rs b/crates/python/src/qpu/translation.rs index 82153c9e0..2cd61f37b 100644 --- a/crates/python/src/qpu/translation.rs +++ b/crates/python/src/qpu/translation.rs @@ -10,9 +10,10 @@ use qcs_api_client_grpc::services::translation::{ translation_options::TranslationBackend as ApiTranslationBackend, TranslationOptions as ApiTranslationOptions, }; -use rigetti_pyo3::{create_init_submodule, py_wrap_error, py_wrap_simple_enum, ToPythonError}; - -use crate::py_sync::py_function_sync_async; +use rigetti_pyo3::{ + create_init_submodule, py_function_sync_async, py_wrap_error, py_wrap_simple_enum, + ToPythonError, +}; use crate::client::PyQcsClient; diff --git a/crates/python/src/qvm/api.rs b/crates/python/src/qvm/api.rs index d5d3ddcd2..f90900c62 100644 --- a/crates/python/src/qvm/api.rs +++ b/crates/python/src/qvm/api.rs @@ -1,7 +1,8 @@ use std::{collections::HashMap, num::NonZeroU16}; +use crate::register_data::PyRegisterData; + use super::{PyQvmOptions, RustQvmError}; -use crate::{py_sync::py_function_sync_async, register_data::PyRegisterData}; use pyo3::{ pymethods, @@ -19,7 +20,7 @@ use qcs::{ RegisterData, }; use rigetti_pyo3::{ - create_init_submodule, impl_repr, py_wrap_data_struct, py_wrap_type, + create_init_submodule, impl_repr, py_function_sync_async, py_wrap_data_struct, py_wrap_type, pyo3::{pyfunction, PyResult}, PyTryFrom, PyWrapper, PyWrapperMut, ToPythonError, }; diff --git a/crates/python/src/qvm/mod.rs b/crates/python/src/qvm/mod.rs index 2dfb560a2..5231b084b 100644 --- a/crates/python/src/qvm/mod.rs +++ b/crates/python/src/qvm/mod.rs @@ -4,14 +4,15 @@ use qcs::{ RegisterData, }; use rigetti_pyo3::{ - create_init_submodule, impl_as_mut_for_wrapper, impl_repr, py_wrap_error, py_wrap_type, + create_init_submodule, impl_as_mut_for_wrapper, impl_repr, py_function_sync_async, + py_wrap_error, py_wrap_type, pyo3::{exceptions::PyRuntimeError, prelude::*, Python}, wrap_error, PyTryFrom, PyWrapper, PyWrapperMut, ToPython, ToPythonError, }; use std::num::NonZeroU16; use std::{collections::HashMap, time::Duration}; -use crate::{py_sync::py_function_sync_async, register_data::PyRegisterData}; +use crate::register_data::PyRegisterData; mod api; From 497d8516885b6890e7ad8dff7e4a6a5389c8cbd0 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Tue, 27 Aug 2024 11:07:39 -0700 Subject: [PATCH 09/25] Apply suggestions from code review Co-authored-by: Kyle J Strand --- crates/lib/src/qpu/api.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/lib/src/qpu/api.rs b/crates/lib/src/qpu/api.rs index a2d6b2057..d3b2b7431 100644 --- a/crates/lib/src/qpu/api.rs +++ b/crates/lib/src/qpu/api.rs @@ -437,7 +437,7 @@ pub enum ConnectionStrategy { EndpointId(String), } -/// An ExecutionTarget provides methods for provided the appropriate connection to the execution +/// An ExecutionTarget provides methods to establish the appropriate connection to the execution /// service. /// /// Implementors provide a [`ConnectionStrategy`] and timeout, the trait provides default @@ -720,7 +720,7 @@ pub enum QpuApiError { /// The message associated with the failed job. message: String, }, - /// Error that can occur when the gRPC status code could not be decoded. + /// Error that can occur when the gRPC status code cannot be decoded. #[error("The status code could not be decoded: {0}")] StatusCodeDecode(String), // TODO: This error is in prost. Should we really use that as a dep // just for the error type? From 1003b40667f2473b1fbaf7359a541bb4949fc6df Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Tue, 27 Aug 2024 11:11:14 -0700 Subject: [PATCH 10/25] update qcs-api-client-grpc --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b3494de53..bc1a4972f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,14 +5,14 @@ resolver = "2" [workspace.dependencies] qcs-api = "0.2.1" qcs-api-client-common = "0.8.14" -qcs-api-client-grpc = "0.8.7" +qcs-api-client-grpc = "0.8.8" qcs-api-client-openapi = "0.9.7" serde_json = "1.0.86" thiserror = "1.0.57" tokio = "1.36.0" # We specify quil-rs as a git and versioned dependency so that we can keep the version of # quil-rs used in both the Rust and Python packages in sync. The tag used should always -# be a `quil-py` tag and should be comaptible with the version specified in +# be a `quil-py` tag and should be compatible with the version specified in # `crates/python/pyproject.toml`. # The version must also be specified in order to publish to crates.io. Cargo enforces # that the specified version is the same as the version in the git repository. From f90eaa64bfc76ef6b1dc1e9693f4ce0278a9c50d Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Tue, 27 Aug 2024 11:22:23 -0700 Subject: [PATCH 11/25] remove stale TODO --- crates/lib/src/qpu/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lib/src/qpu/api.rs b/crates/lib/src/qpu/api.rs index d3b2b7431..67781be0b 100644 --- a/crates/lib/src/qpu/api.rs +++ b/crates/lib/src/qpu/api.rs @@ -722,7 +722,7 @@ pub enum QpuApiError { }, /// Error that can occur when the gRPC status code cannot be decoded. #[error("The status code could not be decoded: {0}")] - StatusCodeDecode(String), // TODO: This error is in prost. Should we really use that as a dep + StatusCodeDecode(String), // just for the error type? /// Error that can occur if a numeric status identifier cannot be converted /// into a known status type. From 7e0a5b7e40d293e7a1cf2b88ab41f9e8addaf4f7 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Tue, 27 Aug 2024 11:24:28 -0700 Subject: [PATCH 12/25] reduce visibility of gRPC message size constant --- crates/lib/src/qpu/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lib/src/qpu/api.rs b/crates/lib/src/qpu/api.rs index 67781be0b..fc21e216c 100644 --- a/crates/lib/src/qpu/api.rs +++ b/crates/lib/src/qpu/api.rs @@ -41,7 +41,7 @@ use crate::executable::Parameters; use crate::client::{GrpcClientError, GrpcConnection, Qcs}; /// The maximum size of a gRPC response, in bytes. -pub const MAX_DECODING_MESSAGE_SIZE_BYTES: usize = 250 * 1024 * 1024; +const MAX_DECODING_MESSAGE_SIZE_BYTES: usize = 250 * 1024 * 1024; pub(crate) fn params_into_job_execution_configuration( params: &Parameters, From b23ec8b3354c4cab23c099d661159464e345b9d6 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 12:04:54 -0700 Subject: [PATCH 13/25] update qcs-api-cilent packages, use uv instead of poetry --- Cargo.lock | 13 +- Cargo.toml | 6 +- crates/python/Makefile.toml | 91 +++++++++----- crates/python/pyproject.toml | 51 +++----- .../_tracing_subscriber/layers/__init__.py | 1 - .../_tracing_subscriber/layers/__init__.pyi | 16 +-- .../layers/file/__init__.py | 2 - .../layers/file/__init__.pyi | 1 - .../layers/otel_otlp/__init__.py | 1 - .../layers/otel_otlp/__init__.pyi | 6 +- .../layers/otel_otlp_file/__init__.py | 1 - .../layers/otel_otlp_file/__init__.pyi | 1 - .../subscriber/__init__.py | 1 - .../subscriber/__init__.pyi | 1 - crates/python/qcs_sdk/client.pyi | 84 ++++++------- crates/python/src/client.rs | 113 ++++-------------- .../compiler/__snapshots__/test_quilc.ambr | 1 + crates/python/tests/test_client.py | 22 ++-- 18 files changed, 178 insertions(+), 234 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 491cfe18b..a628cbb80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2746,12 +2746,13 @@ dependencies = [ [[package]] name = "qcs-api-client-common" -version = "0.8.14" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43621cfb969b563276e015ca3247a6e3dc3b5cc5ba9ffd6bc58adc51a02a467f" +checksum = "371aab7842d63b891a5cba071555a1e7973cd29359514e1df6d6ccd341e7e598" dependencies = [ "async-trait", "backoff", + "base64 0.22.1", "derive_builder 0.20.0", "figment", "futures", @@ -2777,9 +2778,9 @@ dependencies = [ [[package]] name = "qcs-api-client-grpc" -version = "0.8.8" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b461aabc128adcb54d644aa4517b243d366a9ffe88a75f3fd3a5bb887f6b1e75" +checksum = "6beda6c7ff777bdcd83cbb6b4201f144c3c7aa10cf632cb0fba36f7d41c4b4e8" dependencies = [ "backoff", "http", @@ -2810,9 +2811,9 @@ dependencies = [ [[package]] name = "qcs-api-client-openapi" -version = "0.9.7" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7af3429e00bd96faed7e32c8643b74ad86a072714b304475b15c8be7529d85" +checksum = "5aeaca135891229a76bc3fc03747b9b8e8622fb570594e3458679496f3cd45be" dependencies = [ "anyhow", "qcs-api-client-common", diff --git a/Cargo.toml b/Cargo.toml index bc1a4972f..db2332a11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ resolver = "2" [workspace.dependencies] qcs-api = "0.2.1" -qcs-api-client-common = "0.8.14" -qcs-api-client-grpc = "0.8.8" -qcs-api-client-openapi = "0.9.7" +qcs-api-client-common = "0.9.0" +qcs-api-client-grpc = "0.9.0" +qcs-api-client-openapi = "0.10.0" serde_json = "1.0.86" thiserror = "1.0.57" tokio = "1.36.0" diff --git a/crates/python/Makefile.toml b/crates/python/Makefile.toml index 22f9cf9dd..3248c0464 100644 --- a/crates/python/Makefile.toml +++ b/crates/python/Makefile.toml @@ -1,53 +1,85 @@ [env] +PYTHONPATH = { script = [ "python -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())'" ] } RUST_BACKTRACE = 0 -[tasks.pre-test-docker-up] -command = "docker" -args = ["compose", "up", "-d"] +[tasks.check-venv] +description = "Check if a virtual environment is activated" +script = [ + ''' + if [ -z "$VIRTUAL_ENV" ]; then + echo "No virtual environment activated. Please activate one." + exit 1 + else + echo "Virtual environment is active." + fi + ''' +] -[tasks.poetry-install] -command = "poetry" -args = ["install"] +[tasks.install-uv] +dependencies = ["check-venv"] +description = "Install dependencies using uv" +command = "pip" +args = ["install", "uv"] + +[tasks.install-deps] +dependencies = ["install-uv"] +description = "Install project dependencies using uv." +script = [ + ''' + uv pip compile pyproject.toml --all-extras > requirements-dev.txt + uv pip install -r requirements-dev.txt + rm requirements-dev.txt + ''' +] -[tasks.install-lib] -command = "poetry" -args = ["run", "maturin", "develop"] +[tasks.install-python-package] +dependencies = ["check-venv", "install-deps"] +description = "Build the python package and install to the active virtual environment." +command = "maturin" +args = ["develop"] + +[tasks.pre-test-docker-up] +script = [ + ''' + docker compose up -d || { echo 'Warning: `docker compose up` failed. Assuming QVM and quilc are running already.'; true; } + ''' +] [tasks.format-tracing-subscriber] -command = "poetry" -args = ["run", "black", "qcs_sdk/_tracing_subscriber"] +command = "black" +args = ["qcs_sdk/_tracing_subscriber"] [tasks.post-test] -command = "docker" -args = ["compose", "down"] +script = [ + ''' + docker compose down || true + ''' +] [tasks.test] -command = "poetry" -args = ["run", "pytest", "tests"] +command = "pytest" +args = ["tests"] [tasks.test-session] -command = "poetry" -args = ["run", "pytest", "tests", "--with-qcs-session"] +command = "pytest" +args = ["tests", "--with-qcs-session"] [tasks.test-execution] -command = "poetry" -args = ["run", "pytest", "tests", "--with-qcs-session", "--with-qcs-execution"] +command = "pytest" +args = ["tests", "--with-qcs-session", "--with-qcs-execution"] [tasks.pytest-flow] dependencies = [ "pre-test-docker-up", - "poetry-install", - "install-lib", + "install-python-package", "format-tracing-subscriber", "test", "post-test", ] [tasks.stubtest] -command = "poetry" +command = "stubtest" args = [ - "run", - "stubtest", "--allowlist", ".stubtest-allowlist", "--allowlist", @@ -57,9 +89,8 @@ args = [ [tasks.stubtest-flow] dependencies = [ - "poetry-install", - "install-lib", - "fomat-tracing-subscriber", + "install-python-package", + "format-tracing-subscriber", "stubtest", ] @@ -73,7 +104,7 @@ alias = "dev-flow" dependencies = ["pytest-flow", "stubtest"] [tasks.docs] -dependencies = ["poetry-install", "install-lib"] -command = "poetry" -args = ["run", "pdoc", "-o", "build/docs", "qcs_sdk", "!qcs_sdk.qcs_sdk", "--logo", "https://qcs.rigetti.com/static/img/rigetti-logo.svg"] +dependencies = ["install-python-package"] +command = "pdoc" +args = ["pdoc", "-o", "build/docs", "qcs_sdk", "!qcs_sdk.qcs_sdk", "--logo", "https://qcs.rigetti.com/static/img/rigetti-logo.svg"] diff --git a/crates/python/pyproject.toml b/crates/python/pyproject.toml index 1060446b4..5a3b10d93 100644 --- a/crates/python/pyproject.toml +++ b/crates/python/pyproject.toml @@ -20,21 +20,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Operating System :: OS Independent", ] -dependencies = ["quil>=0.11.2"] - -# PEP 621 specifies the [project] table as the source for project metadata. However, Poetry only supports [tool.poetry] -# We can remove this table once this issue is resolved: https://github.com/python-poetry/poetry/issues/3332 -[tool.poetry] -name = "qcs-sdk-python" -version = "0.19.3" -description = "Python interface for the QCS Rust SDK" -readme = "README.md" -authors = [ - "Rigetti Computing ", - "Mark Skilbeck ", - "Marquess Valdez ", - "Randall Fulton ", -] +dependencies = ["quil>=0.11.2", "qcs-api-client-common>=0.9.0"] [tool.maturin] features = ["pyo3/extension-module"] @@ -42,23 +28,24 @@ bindings = "pyo3" compatibility = "linux" sdist-include = ["README.md"] -[tool.poetry.dependencies] -python = "^3.8" -opentelemetry-api = { version = "^1.25.0", optional = true } - -[tool.poetry.group.dev.dependencies] -numpy = "^1.24.1" -maturin = "^1.2.3" -pytest = "^7.1.3" -pytest-asyncio = "^0.19.0" -black = "^22.8.0" -syrupy = "^3.0.6" -mypy = "^1.4.1" -pdoc = "^14.1.0" -opentelemetry-sdk = "^1.25.0" - -[tool.poetry.extras] -tracing-opentelemetry = ["opentelemetry-api"] +[project.optional-dependencies] +pyquil = [ + "pyquil==4.14.2", +] +dev = [ + "opentelemetry-sdk==1.25.0", + "pytest >= 8.1.1", + "pytest-asyncio >= 0.23.6", + "pytest-mock >= 3.14.0", + "pytest-sugar >= 1.0.0", + "pytest-clarity >= 1.0.1", + "syrupy >= 4.0.0", + "maturin == 1.5.1", + "numpy >= 1.24.1", + "pdoc >= 14.6.1", + "ruff >= 0.3.5", + "mypy >= 1.4.1", +] [build-system] requires = ["maturin>=1.0.0,<2.0.0"] diff --git a/crates/python/qcs_sdk/_tracing_subscriber/layers/__init__.py b/crates/python/qcs_sdk/_tracing_subscriber/layers/__init__.py index a4123cdf1..8c674f36e 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/layers/__init__.py +++ b/crates/python/qcs_sdk/_tracing_subscriber/layers/__init__.py @@ -15,4 +15,3 @@ __doc__ = layers.__doc__ __all__ = getattr(layers, "__all__", []) - diff --git a/crates/python/qcs_sdk/_tracing_subscriber/layers/__init__.pyi b/crates/python/qcs_sdk/_tracing_subscriber/layers/__init__.pyi index 741a427e2..6fdd52b85 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/layers/__init__.pyi +++ b/crates/python/qcs_sdk/_tracing_subscriber/layers/__init__.pyi @@ -13,18 +13,18 @@ from __future__ import annotations from typing import TYPE_CHECKING -from . import file as file +from . import file as file from . import otel_otlp_file as otel_otlp_file from . import otel_otlp as otel_otlp if TYPE_CHECKING: - from typing import Union + from typing import Union - Config = Union[ - file.Config, - otel_otlp_file.Config, - otel_otlp.Config, - ] - """ + Config = Union[ + file.Config, + otel_otlp_file.Config, + otel_otlp.Config, + ] + """ One of the supported layer configurations that may be set on the subscriber configuration. """ diff --git a/crates/python/qcs_sdk/_tracing_subscriber/layers/file/__init__.py b/crates/python/qcs_sdk/_tracing_subscriber/layers/file/__init__.py index f1ac331e1..80cff859b 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/layers/file/__init__.py +++ b/crates/python/qcs_sdk/_tracing_subscriber/layers/file/__init__.py @@ -15,5 +15,3 @@ __doc__ = file.__doc__ __all__ = getattr(file, "__all__", []) - - diff --git a/crates/python/qcs_sdk/_tracing_subscriber/layers/file/__init__.pyi b/crates/python/qcs_sdk/_tracing_subscriber/layers/file/__init__.pyi index 7caa5c2e0..8054827fe 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/layers/file/__init__.pyi +++ b/crates/python/qcs_sdk/_tracing_subscriber/layers/file/__init__.pyi @@ -39,4 +39,3 @@ class Config: :param json: Whether or not to format the output as JSON. Defaults to `True`. """ ... - diff --git a/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp/__init__.py b/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp/__init__.py index 7b1f6baf6..8a3bcdd6e 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp/__init__.py +++ b/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp/__init__.py @@ -15,4 +15,3 @@ __doc__ = otel_otlp.__doc__ __all__ = getattr(otel_otlp, "__all__", []) - diff --git a/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp/__init__.pyi b/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp/__init__.pyi index 5dde69007..453e2d8e8 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp/__init__.pyi +++ b/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp/__init__.pyi @@ -47,8 +47,6 @@ class Resource: schema_url: Optional[str] = None, ) -> "Resource": ... - - @final class Config: """ @@ -98,14 +96,14 @@ class Config: ... if TYPE_CHECKING: - from typing import List, Union + from typing import List, Union ResourceValueArray = Union[List[bool], List[int], List[float], List[str]] """ An array of `ResourceValue`s. This array is homogenous, so all values must be of the same type. """ - ResourceValue= Union[bool, int, float, str, ResourceValueArray] + ResourceValue = Union[bool, int, float, str, ResourceValueArray] """ A value that can be added to a `Resource`. """ diff --git a/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp_file/__init__.py b/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp_file/__init__.py index 7f928b757..4d7ea6443 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp_file/__init__.py +++ b/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp_file/__init__.py @@ -15,4 +15,3 @@ __doc__ = otel_otlp_file.__doc__ __all__ = getattr(otel_otlp_file, "__all__", []) - diff --git a/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp_file/__init__.pyi b/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp_file/__init__.pyi index 613126b30..31e2f495f 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp_file/__init__.pyi +++ b/crates/python/qcs_sdk/_tracing_subscriber/layers/otel_otlp_file/__init__.pyi @@ -32,4 +32,3 @@ class Config: and then `RUST_LOG` environment variable. If all of these values are empty, no spans will be exported. """ ... - diff --git a/crates/python/qcs_sdk/_tracing_subscriber/subscriber/__init__.py b/crates/python/qcs_sdk/_tracing_subscriber/subscriber/__init__.py index fb711d054..5f4924199 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/subscriber/__init__.py +++ b/crates/python/qcs_sdk/_tracing_subscriber/subscriber/__init__.py @@ -15,4 +15,3 @@ __doc__ = subscriber.__doc__ __all__ = getattr(subscriber, "__all__", []) - diff --git a/crates/python/qcs_sdk/_tracing_subscriber/subscriber/__init__.pyi b/crates/python/qcs_sdk/_tracing_subscriber/subscriber/__init__.pyi index 548de0cc5..e0894d0a4 100644 --- a/crates/python/qcs_sdk/_tracing_subscriber/subscriber/__init__.pyi +++ b/crates/python/qcs_sdk/_tracing_subscriber/subscriber/__init__.pyi @@ -22,4 +22,3 @@ class Config: """ def __new__(cls, *, layer: layers.Config) -> "Config": ... - diff --git a/crates/python/qcs_sdk/client.pyi b/crates/python/qcs_sdk/client.pyi index e6fb5c6ad..2efc6e836 100644 --- a/crates/python/qcs_sdk/client.pyi +++ b/crates/python/qcs_sdk/client.pyi @@ -18,9 +18,8 @@ class QCSClient: def __new__( cls, - tokens: Optional[QCSClientTokens] = None, + oauth_session: Optional[OAuthSession] = None, api_url: Optional[str] = None, - auth_server: Optional[QCSClientAuthServer] = None, grpc_api_url: Optional[str] = None, quilc_url: Optional[str] = None, qvm_url: Optional[str] = None, @@ -62,55 +61,60 @@ class QCSClient: def qvm_url(self) -> str: """URL to access the QVM.""" ... + @property + def oauth_session(self) -> OAuthSession: + """Get a copy of the OAuth session.""" @final -class QCSClientAuthServer: - """Authentication server configuration for the QCS API.""" +class OAuthSession: + def __new__( + cls, + grant_payload: RefreshToken, + auth_server: AuthServer, + access_token: str | None = None, + ) -> OAuthSession: ... + @property + def access_token(self) -> str: + """Get the current access token. - def __new__(cls, client_id: str, issuer: str) -> "QCSClientAuthServer": + This is an unvalidated copy of the access token. Meaning it can become stale, or may already be stale. See the `validate` `request_access_token` and methods. """ - Manually define authentication server parameters. - :param client_id: The OAuth application client ID. If `None`, a default value is used. - :param issuer: The OAuth token issuer url. If `None`, a default value is used. - """ - ... @property - def client_id(self) -> str: ... - @client_id.setter - def client_id(self, value: str): ... + def auth_server(self) -> AuthServer: + """The refresh token.""" + @property - def issuer(self) -> str: ... - @issuer.setter - def issuer(self, value: str): ... + def payload(self) -> RefreshToken: + """Get the payload used to request an access token.""" -@final -class QCSClientTokens: - """Authentication tokens for the QCS API.""" + def validate(self) -> str: + """Validate the current access token, returning it if it is valid. - def __new__( - cls, - bearer_access_token: str, - refresh_token: str, - auth_server: Optional[QCSClientAuthServer] = None, - ) -> "QCSClientTokens": + If the token is invalid, a `ValueError` will be raised with a description of why the token failed validation. """ - Manually define authentication session tokens. - :param bearer_access_token: The session token from an OAuth issuer. - :param refresh_token: A credential to refresh the bearer_access_token when it expires. - :param auth_server: The OAuth server configuration. If `None`, default values are loaded. - """ - ... +@final +class AuthServer: + def __new__(cls, client_id: str, issuer: str) -> AuthServer: ... + @staticmethod + def default() -> AuthServer: + """Get the default Okta auth server.""" + @property - def bearer_access_token(self) -> Optional[str]: ... - @bearer_access_token.setter - def bearer_access_token(self, value: Optional[str]): ... + def client_id(self) -> str: + """The client's Okta ID.""" + @property - def refresh_token(self) -> Optional[str]: ... - @refresh_token.setter - def refresh_token(self, value: Optional[str]): ... + def issuer(self) -> str: + """The Okta issuer URL.""" + +@final +class RefreshToken: + def __new__(cls, refresh_token: str) -> RefreshToken: ... @property - def auth_server(self) -> Optional[QCSClientAuthServer]: ... - @auth_server.setter - def auth_server(self, value: Optional[QCSClientAuthServer]): ... + def refresh_token(self) -> str: + """The refresh token.""" + @refresh_token.setter + def refresh_token(self, refresh_token: str): + """Set the refresh token.""" diff --git a/crates/python/src/client.rs b/crates/python/src/client.rs index 76c19ceca..834a5dc2e 100644 --- a/crates/python/src/client.rs +++ b/crates/python/src/client.rs @@ -1,13 +1,15 @@ +use pyo3::{exceptions::PyValueError, pyfunction}; use qcs_api_client_common::configuration::{ - AuthServer, ClientConfigurationBuilder, ClientConfigurationBuilderError, Tokens, + AuthServer, ClientConfigurationBuilder, ClientConfigurationBuilderError, OAuthSession, + RefreshToken, }; use rigetti_pyo3::{ - create_init_submodule, py_wrap_data_struct, py_wrap_error, py_wrap_type, + create_init_submodule, py_function_sync_async, py_wrap_error, py_wrap_type, pyo3::{ - conversion::IntoPy, exceptions::PyRuntimeError, pyclass::CompareOp, pymethods, - types::PyString, Py, PyObject, PyResult, Python, + conversion::IntoPy, exceptions::PyRuntimeError, pyclass::CompareOp, pymethods, PyObject, + PyResult, Python, }, - wrap_error, ToPythonError, + wrap_error, PyWrapper, ToPythonError, }; use qcs::client::{self, Qcs}; @@ -15,8 +17,9 @@ use qcs::client::{self, Qcs}; create_init_submodule! { classes: [ PyQcsClient, - PyQcsClientAuthServer, - PyQcsClientTokens + OAuthSession, + AuthServer, + RefreshToken ], errors: [ LoadClientError, @@ -36,72 +39,6 @@ py_wrap_error!( PyRuntimeError ); -// The fields on qcs_api_client_common::client::AuthServer are not public. -py_wrap_type!( - PyQcsClientAuthServer(AuthServer) as "QCSClientAuthServer" -); - -#[pymethods] -impl PyQcsClientAuthServer { - #[new] - #[pyo3(signature = (client_id = None, issuer = None))] - pub fn new(client_id: Option, issuer: Option) -> Self { - let mut auth_server = AuthServer::default(); - if let Some(client_id) = client_id { - auth_server.set_client_id(client_id); - } - if let Some(issuer) = issuer { - auth_server.set_issuer(issuer); - } - Self(auth_server) - } - - #[getter(client_id)] - fn get_client_id(&self) -> String { - self.0.client_id().to_string() - } - - #[setter(client_id)] - fn set_client_id(&mut self, value: String) { - self.0.set_client_id(value); - } - - #[getter(issuer)] - fn get_issuer(&self) -> String { - self.0.issuer().to_string() - } - - #[setter(issuer)] - fn set_issuer(&mut self, value: String) { - self.0.set_issuer(value); - } -} - -py_wrap_data_struct! { - PyQcsClientTokens(Tokens) as "QCSClientTokens" { - bearer_access_token: String => Py, - refresh_token: String => Py, - auth_server: AuthServer => PyQcsClientAuthServer - } -} - -#[pymethods] -impl PyQcsClientTokens { - #[new] - #[pyo3(signature = (bearer_access_token, refresh_token, auth_server = None))] - pub fn new( - bearer_access_token: String, - refresh_token: String, - auth_server: Option, - ) -> Self { - Self(Tokens { - bearer_access_token, - refresh_token, - auth_server: auth_server.map(Into::into).unwrap_or_default(), - }) - } -} - py_wrap_type! { PyQcsClient(Qcs) as "QCSClient"; } @@ -126,31 +63,26 @@ impl PyQcsClient { #[new] #[pyo3(signature = ( /, - tokens = None, + oauth_session = None, api_url = None, - auth_server = None, grpc_api_url = None, quilc_url = None, qvm_url = None ))] pub fn new( - tokens: Option, + oauth_session: Option, api_url: Option, - auth_server: Option, grpc_api_url: Option, quilc_url: Option, qvm_url: Option, ) -> PyResult { let mut builder = ClientConfigurationBuilder::default(); - if let Some(tokens) = tokens { - builder.tokens(Some(tokens.into())); + if let Some(session) = oauth_session { + builder.oauth_session(Some(session)); } if let Some(api_url) = api_url { builder.api_url(api_url); } - if let Some(auth_server) = auth_server { - builder.auth_server(auth_server.into()); - } if let Some(grpc_api_url) = grpc_api_url { builder.grpc_api_url(grpc_api_url); } @@ -202,13 +134,8 @@ impl PyQcsClient { } #[getter] - pub fn auth_server(&self) -> AuthServer { - self.as_ref().get_config().auth_server().clone() - } - - #[getter] - pub fn tokens(&self, py: Python<'_>) -> PyResult { - self.as_ref().get_config().get_tokens(py) + pub fn oauth_session(&self, py: Python<'_>) -> PyResult { + py_get_oauth_session(py, self.clone()) } fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject { @@ -219,3 +146,11 @@ impl PyQcsClient { } } } + +py_function_sync_async! { + #[pyfunction] + async fn get_oauth_session(client: PyQcsClient) -> PyResult { + client.as_inner().get_config().oauth_session().await.map_err(|e| PyValueError::new_err(e.to_string())) + + } +} diff --git a/crates/python/tests/compiler/__snapshots__/test_quilc.ambr b/crates/python/tests/compiler/__snapshots__/test_quilc.ambr index 7140896e7..3f3a6f11a 100644 --- a/crates/python/tests/compiler/__snapshots__/test_quilc.ambr +++ b/crates/python/tests/compiler/__snapshots__/test_quilc.ambr @@ -1,3 +1,4 @@ +# serializer version: 1 # name: test_compile_program ''' DECLARE ro BIT[2] diff --git a/crates/python/tests/test_client.py b/crates/python/tests/test_client.py index 09b5e4ecd..2c42c3b81 100644 --- a/crates/python/tests/test_client.py +++ b/crates/python/tests/test_client.py @@ -4,8 +4,9 @@ from qcs_sdk.client import ( QCSClient, LoadClientError, - QCSClientAuthServer, - QCSClientTokens, + OAuthSession, + RefreshToken, + AuthServer, ) @@ -51,18 +52,13 @@ def test_client_broken_raises(): QCSClient.load(profile_name="broken") -def test_client_auth_server_can_be_manually_defined(): +def test_client_oauth_session_can_be_manually_defined(): """Ensures that pyo3 usage is correct.""" - auth_server = QCSClientAuthServer(client_id="foo", issuer="bar") - assert auth_server.client_id == "foo" - assert auth_server.issuer == "bar" - - -def test_client_tokens_can_be_manually_defined(): - """Ensures that pyo3 usage is correct.""" - auth_server = QCSClientTokens(bearer_access_token="foo", refresh_token="bar") - assert auth_server.bearer_access_token == "foo" - assert auth_server.refresh_token == "bar" + auth_server = AuthServer("url", "issuer") + session = OAuthSession(RefreshToken("refresh"), auth_server, "access") + assert session.payload.refresh_token == "refresh" + assert session.access_token == "access" + assert session.auth_server == auth_server def test_client_constructor(): From 33ebd8f0a80cdc154f2b5c6961a9f6c1a5a318df Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 12:39:36 -0700 Subject: [PATCH 14/25] activate virtual environment --- .github/workflows/publish-docs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 07c1a5c35..d4183448a 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -19,8 +19,6 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.11' - - name: Install poetry - uses: snok/install-poetry@v1 - uses: Swatinem/rust-cache@v2 - name: Install cargo-make uses: actions-rs/cargo@v1 From b42ebcb01682b07a9f89a2953f7f1916d872bc50 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 12:50:13 -0700 Subject: [PATCH 15/25] commit the change --- .github/workflows/publish-docs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index d4183448a..ab36328f7 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -19,6 +19,9 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.11' + run: | + python -m venv .venv + source .venv/bin/activate - uses: Swatinem/rust-cache@v2 - name: Install cargo-make uses: actions-rs/cargo@v1 From 3b55aaff15b062d17beee17f4d47ccb56a1921e9 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 13:39:49 -0700 Subject: [PATCH 16/25] activate venv --- .github/workflows/checks.yml | 8 ++++---- .github/workflows/publish-docs.yml | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 351ff8b1c..b290136b1 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -20,21 +20,21 @@ jobs: with: repo-token: ${{ secrets.GITHUB_TOKEN }} version: '3.20.1' - - name: Install libquil dependencies run: sudo apt install -y libffi\* libblas\* liblapack\* - name: Install libquil run: | curl https://raw.githubusercontent.com/rigetti/libquil/main/install.sh | bash -s 0.3.0 - - uses: Swatinem/rust-cache@v2 - name: Install cargo-make uses: actions-rs/cargo@v1 with: command: install args: --debug cargo-make - - name: Install poetry - uses: snok/install-poetry@v1 + - name: Activate virtual environment + run: | + python -m venv .venv + source .venv/bin/activate # may protect against linking failure: no space left on device - name: Delete huge unnecessary tools folder run: rm -rf /opt/hostedtoolcache diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index ab36328f7..016f60912 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -19,6 +19,7 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.11' + - name: Activate virtual environment run: | python -m venv .venv source .venv/bin/activate From a04c1a3b9842e93dca4459e9c40f9c2bab583467 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 13:43:46 -0700 Subject: [PATCH 17/25] separate steps? --- .github/workflows/checks.yml | 6 +++--- .github/workflows/publish-docs.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index b290136b1..c8b520969 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -31,10 +31,10 @@ jobs: with: command: install args: --debug cargo-make + - name: Create virtual environment + run: python -m venv venv - name: Activate virtual environment - run: | - python -m venv .venv - source .venv/bin/activate + run: source venv/bin/activate # may protect against linking failure: no space left on device - name: Delete huge unnecessary tools folder run: rm -rf /opt/hostedtoolcache diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 016f60912..c7c49088f 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -19,10 +19,10 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.11' + - name: Create virtual environment + run: python -m venv venv - name: Activate virtual environment - run: | - python -m venv .venv - source .venv/bin/activate + run: source venv/bin/activate - uses: Swatinem/rust-cache@v2 - name: Install cargo-make uses: actions-rs/cargo@v1 From caaabc4b39956cd38233ad0120a6b58bc9552f53 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 14:12:02 -0700 Subject: [PATCH 18/25] write to GITHUB_ENV --- .github/workflows/checks.yml | 2 +- .github/workflows/publish-docs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c8b520969..75d3a0c8e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -34,7 +34,7 @@ jobs: - name: Create virtual environment run: python -m venv venv - name: Activate virtual environment - run: source venv/bin/activate + run: echo "source venv/bin/activate" >> $GITHUB_ENV # may protect against linking failure: no space left on device - name: Delete huge unnecessary tools folder run: rm -rf /opt/hostedtoolcache diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index c7c49088f..658cf83dd 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -22,7 +22,7 @@ jobs: - name: Create virtual environment run: python -m venv venv - name: Activate virtual environment - run: source venv/bin/activate + run: echo "source venv/bin/activate" >> $GITHUB_ENV - uses: Swatinem/rust-cache@v2 - name: Install cargo-make uses: actions-rs/cargo@v1 From 4c33b1bce99c4a24fb0bc40d7b58e0648b075e13 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 14:13:34 -0700 Subject: [PATCH 19/25] try cat --- .github/workflows/checks.yml | 2 +- .github/workflows/publish-docs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 75d3a0c8e..40a31552f 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -34,7 +34,7 @@ jobs: - name: Create virtual environment run: python -m venv venv - name: Activate virtual environment - run: echo "source venv/bin/activate" >> $GITHUB_ENV + run: cat venv/bin/activate >> $GITHUB_ENV # may protect against linking failure: no space left on device - name: Delete huge unnecessary tools folder run: rm -rf /opt/hostedtoolcache diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 658cf83dd..e9060515d 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -22,7 +22,7 @@ jobs: - name: Create virtual environment run: python -m venv venv - name: Activate virtual environment - run: echo "source venv/bin/activate" >> $GITHUB_ENV + run: cat venv/bin/activate >> $GITHUB_ENV - uses: Swatinem/rust-cache@v2 - name: Install cargo-make uses: actions-rs/cargo@v1 From d1cdc85282b4cf39d1540be634d4c46efa9cf206 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 14:16:02 -0700 Subject: [PATCH 20/25] try action --- .github/workflows/checks.yml | 5 +---- .github/workflows/publish-docs.yml | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 40a31552f..fc2ffc918 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -31,11 +31,8 @@ jobs: with: command: install args: --debug cargo-make - - name: Create virtual environment - run: python -m venv venv - - name: Activate virtual environment - run: cat venv/bin/activate >> $GITHUB_ENV # may protect against linking failure: no space left on device + - uses: syphar/restore-virtualenv@v1 - name: Delete huge unnecessary tools folder run: rm -rf /opt/hostedtoolcache - name: Run Rust CI diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index e9060515d..5b0e3e00f 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -19,16 +19,13 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.11' - - name: Create virtual environment - run: python -m venv venv - - name: Activate virtual environment - run: cat venv/bin/activate >> $GITHUB_ENV - uses: Swatinem/rust-cache@v2 - name: Install cargo-make uses: actions-rs/cargo@v1 with: command: install args: --debug cargo-make + - uses: syphar/restore-virtualenv@v1 - name: Build qcs-sdk-python documentation uses: actions-rs/cargo@v1 with: From db1960675decdaa4dc89ae54c47a00e2f2e1b2ae Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 30 Aug 2024 14:20:28 -0700 Subject: [PATCH 21/25] remove duplicated command --- crates/python/Makefile.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/python/Makefile.toml b/crates/python/Makefile.toml index 3248c0464..4874a965f 100644 --- a/crates/python/Makefile.toml +++ b/crates/python/Makefile.toml @@ -106,5 +106,5 @@ dependencies = ["pytest-flow", "stubtest"] [tasks.docs] dependencies = ["install-python-package"] command = "pdoc" -args = ["pdoc", "-o", "build/docs", "qcs_sdk", "!qcs_sdk.qcs_sdk", "--logo", "https://qcs.rigetti.com/static/img/rigetti-logo.svg"] +args = ["-o", "build/docs", "qcs_sdk", "!qcs_sdk.qcs_sdk", "--logo", "https://qcs.rigetti.com/static/img/rigetti-logo.svg"] From 846f7aa71375f30187f147444fb72ceb80516c63 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 13 Sep 2024 17:24:01 -0700 Subject: [PATCH 22/25] add missing dev dep --- crates/python/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/python/pyproject.toml b/crates/python/pyproject.toml index 5a3b10d93..6267feedb 100644 --- a/crates/python/pyproject.toml +++ b/crates/python/pyproject.toml @@ -33,6 +33,7 @@ pyquil = [ "pyquil==4.14.2", ] dev = [ + "black >= 24.8.0", "opentelemetry-sdk==1.25.0", "pytest >= 8.1.1", "pytest-asyncio >= 0.23.6", From 53c03e0511660a2f3b831a6019f1aae9f7c8adaf Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Mon, 16 Sep 2024 15:50:15 -0700 Subject: [PATCH 23/25] Update qcs-api-client-rust packages --- .github/workflows/checks.yml | 2 +- Cargo.lock | 12 ++++++------ Cargo.toml | 6 +++--- crates/python/src/qpu/isa.rs | 13 +++++++------ 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index fc2ffc918..4086fecdf 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -31,8 +31,8 @@ jobs: with: command: install args: --debug cargo-make - # may protect against linking failure: no space left on device - uses: syphar/restore-virtualenv@v1 + # may protect against linking failure: no space left on device - name: Delete huge unnecessary tools folder run: rm -rf /opt/hostedtoolcache - name: Run Rust CI diff --git a/Cargo.lock b/Cargo.lock index a628cbb80..ec2c981c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2746,9 +2746,9 @@ dependencies = [ [[package]] name = "qcs-api-client-common" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "371aab7842d63b891a5cba071555a1e7973cd29359514e1df6d6ccd341e7e598" +checksum = "a0ee577b5e5855fbd227b32e95683e863e510930710b0daefbe6214c878787c0" dependencies = [ "async-trait", "backoff", @@ -2778,9 +2778,9 @@ dependencies = [ [[package]] name = "qcs-api-client-grpc" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6beda6c7ff777bdcd83cbb6b4201f144c3c7aa10cf632cb0fba36f7d41c4b4e8" +checksum = "eae7be57e9f3e82f14e97ae287715e1d0d34a33dbdc135cb2f7800a4e1de5f04" dependencies = [ "backoff", "http", @@ -2811,9 +2811,9 @@ dependencies = [ [[package]] name = "qcs-api-client-openapi" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeaca135891229a76bc3fc03747b9b8e8622fb570594e3458679496f3cd45be" +checksum = "f3c4df0083961178536f3e277ce4aa133bd56814c351c9fcd4dd237b35ac37ec" dependencies = [ "anyhow", "qcs-api-client-common", diff --git a/Cargo.toml b/Cargo.toml index db2332a11..2d961a036 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ resolver = "2" [workspace.dependencies] qcs-api = "0.2.1" -qcs-api-client-common = "0.9.0" -qcs-api-client-grpc = "0.9.0" -qcs-api-client-openapi = "0.10.0" +qcs-api-client-common = "0.10.0" +qcs-api-client-grpc = "0.10.0" +qcs-api-client-openapi = "0.11.0" serde_json = "1.0.86" thiserror = "1.0.57" tokio = "1.36.0" diff --git a/crates/python/src/qpu/isa.rs b/crates/python/src/qpu/isa.rs index a751a22b0..ca7eb1bbc 100644 --- a/crates/python/src/qpu/isa.rs +++ b/crates/python/src/qpu/isa.rs @@ -4,7 +4,7 @@ use qcs_api_client_openapi::models::{ }; use rigetti_pyo3::{ create_init_submodule, py_function_sync_async, py_wrap_data_struct, py_wrap_error, - py_wrap_simple_enum, + py_wrap_union_enum, pyo3::{ exceptions::{PyRuntimeError, PyValueError}, prelude::*, @@ -46,12 +46,13 @@ py_wrap_error!(isa, RustSerializeIsaError, SerializeISAError, PyValueError); wrap_error!(RustGetIsaError(qcs::qpu::GetIsaError)); py_wrap_error!(isa, RustGetIsaError, GetISAError, PyRuntimeError); -py_wrap_simple_enum! { +py_wrap_union_enum! { PyFamily(Family) as "Family" { - None as NONE, - Full as Full, - Aspen as Aspen, - Ankaa as Ankaa + NONE: None, + Full: Full, + Aspen: Aspen, + Ankaa: Ankaa, + Unknown: Unknown => String } } From 5c075daad4739237dd44863258f7b747a1c90781 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Mon, 16 Sep 2024 16:38:37 -0700 Subject: [PATCH 24/25] update quil-rs --- Cargo.lock | 32 ++++++++++++++++---------------- Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 73812b197..e59f47f1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1592,18 +1592,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "lexical" -version = "6.1.1" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" +checksum = "8ecd3381ac77c22d4e2607284ac71e44b21c21bd3785ee807d21976d54ee16f9" dependencies = [ "lexical-core", ] [[package]] name = "lexical-core" -version = "0.8.5" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" +checksum = "0885f6cdfe75c96e45bbf1c4e49511f128201391ce3b56e60e29f5a1fadbc1c1" dependencies = [ "lexical-parse-float", "lexical-parse-integer", @@ -1614,9 +1614,9 @@ dependencies = [ [[package]] name = "lexical-parse-float" -version = "0.8.5" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +checksum = "924f7ec090cd4f60bd873f160b0fb69a0c80bb3a98f2e778a1893ae0e5c4b0b9" dependencies = [ "lexical-parse-integer", "lexical-util", @@ -1625,9 +1625,9 @@ dependencies = [ [[package]] name = "lexical-parse-integer" -version = "0.8.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +checksum = "8feab1da84a2ab0ddbbad2fb1830b755f71a9a8d996c7a1f2a553faf72aa3686" dependencies = [ "lexical-util", "static_assertions", @@ -1635,18 +1635,18 @@ dependencies = [ [[package]] name = "lexical-util" -version = "0.8.5" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +checksum = "591ce1a12ecd3b26d4121ab360a6a4483a67f05a5372add6acbfd0b65c9285d9" dependencies = [ "static_assertions", ] [[package]] name = "lexical-write-float" -version = "0.8.5" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +checksum = "05b0f3f9ddada5942b54e97654d535df37c9340ad66c24b50360a90619779f41" dependencies = [ "lexical-util", "lexical-write-integer", @@ -1655,9 +1655,9 @@ dependencies = [ [[package]] name = "lexical-write-integer" -version = "0.8.5" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +checksum = "48c6d47254ddb292771dce7697ae2be9619f8e369d01a9ccda15ef2ff50443fc" dependencies = [ "lexical-util", "static_assertions", @@ -2867,8 +2867,8 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quil-rs" -version = "0.27.1" -source = "git+https://github.com/rigetti/quil-rs?tag=quil-py/v0.11.1#ab5b976573b38f3ba2605f57482ccadb1f13bf30" +version = "0.28.1" +source = "git+https://github.com/rigetti/quil-rs?tag=quil-py/v0.12.1#a0f2776893d3ce33eb9f29449f3b4b0e9ed24174" dependencies = [ "approx", "indexmap 2.2.6", diff --git a/Cargo.toml b/Cargo.toml index 2d961a036..49c05bb18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ tokio = "1.36.0" # `crates/python/pyproject.toml`. # The version must also be specified in order to publish to crates.io. Cargo enforces # that the specified version is the same as the version in the git repository. -quil-rs = { version = "0.27.1", git = "https://github.com/rigetti/quil-rs", tag = "quil-py/v0.11.1" } +quil-rs = { version = "0.28.1", git = "https://github.com/rigetti/quil-rs", tag = "quil-py/v0.12.1" } # ndarray is used by the `qcs` crate, but it is also used in the `python` crate via a # re-export through the numpy crate. They should be updated as a pair to keep both From 80069b127d05bde951353ca4f21ac235fdca352b Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Mon, 16 Sep 2024 16:56:39 -0700 Subject: [PATCH 25/25] update type stubs --- crates/python/qcs_sdk/client.pyi | 43 ++++++++++++++++++++++++++++--- crates/python/qcs_sdk/qpu/isa.pyi | 25 +++++++++++++----- crates/python/src/client.rs | 8 +++--- crates/python/src/qpu/isa.rs | 10 +++---- 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/crates/python/qcs_sdk/client.pyi b/crates/python/qcs_sdk/client.pyi index 2efc6e836..9b6b96cc0 100644 --- a/crates/python/qcs_sdk/client.pyi +++ b/crates/python/qcs_sdk/client.pyi @@ -1,4 +1,4 @@ -from typing import Optional, final +from typing import Callable, Optional, final class LoadClientError(RuntimeError): """Error encountered while loading the QCS API client configuration from the environment configuration.""" @@ -69,7 +69,7 @@ class QCSClient: class OAuthSession: def __new__( cls, - grant_payload: RefreshToken, + grant_payload: RefreshToken | ClientCredentials | ExternallyManaged, auth_server: AuthServer, access_token: str | None = None, ) -> OAuthSession: ... @@ -82,12 +82,18 @@ class OAuthSession: @property def auth_server(self) -> AuthServer: - """The refresh token.""" + """The auth server.""" @property - def payload(self) -> RefreshToken: + def payload(self) -> RefreshToken | ClientCredentials: """Get the payload used to request an access token.""" + def request_access_token(self) -> str: + """Request a new access token.""" + + async def request_access_token_async(self) -> str: + """Request a new access token.""" + def validate(self) -> str: """Validate the current access token, returning it if it is valid. @@ -118,3 +124,32 @@ class RefreshToken: @refresh_token.setter def refresh_token(self, refresh_token: str): """Set the refresh token.""" + +@final +class ClientCredentials: + def __new__(cls, client_id: str, client_secret: str) -> ClientCredentials: ... + @property + def client_id(self) -> str: + """The client ID.""" + @property + def client_secret(self) -> str: + """The client secret.""" + +@final +class ExternallyManaged: + def __new__( + cls, refresh_function: Callable[[AuthServer], str] + ) -> ExternallyManaged: + """Manages access tokens by utilizing a user-provided refresh function. + + The refresh function should return a valid access token, or raise an exception if it cannot. + + .. testcode:: + from qcs_apiclient_common.configuration import AuthServer, ExternallyManaged, OAuthSession + + def refresh_function(auth_server: AuthServer) -> str: + return "my_access_token" + + externally_managed = ExternallyManaged(refresh_function) + session = OAuthSession(externally_managed, AuthServer.default()) + """ diff --git a/crates/python/qcs_sdk/qpu/isa.pyi b/crates/python/qcs_sdk/qpu/isa.pyi index 751a05b9a..0c91d5472 100644 --- a/crates/python/qcs_sdk/qpu/isa.pyi +++ b/crates/python/qcs_sdk/qpu/isa.pyi @@ -14,17 +14,30 @@ class GetISAError(RuntimeError): ... @final -class Family(Enum): +class Family: """ The architecture family identifier of an ``InstructionSetArchitecture``. Value "Full" implies that each node is connected to every other (fully-connected architecture). """ - - NONE = "NONE" - Full = "Full" - Aspen = "Aspen" - Ankaa = "Ankaa" + def is_ankaa(self) -> bool: ... + def is_aspen(self) -> bool: ... + def is_full(self) -> bool: ... + def is_unknown(self) -> bool: ... + def is_none(self) -> bool: ... + def as_unknown(self) -> Optional[str]: ... + def to_unknown(self) -> str: ... + @staticmethod + def from_unknown(inner: str) -> "Family": ... + def inner(self) -> str: ... + @staticmethod + def new_ankaa() -> "Family": ... + @staticmethod + def new_aspen() -> "Family": ... + @staticmethod + def new_full() -> "Family": ... + @staticmethod + def new_none() -> "Family": ... @final class Node: diff --git a/crates/python/src/client.rs b/crates/python/src/client.rs index 834a5dc2e..c27d63c0c 100644 --- a/crates/python/src/client.rs +++ b/crates/python/src/client.rs @@ -1,7 +1,7 @@ use pyo3::{exceptions::PyValueError, pyfunction}; use qcs_api_client_common::configuration::{ - AuthServer, ClientConfigurationBuilder, ClientConfigurationBuilderError, OAuthSession, - RefreshToken, + AuthServer, ClientConfigurationBuilder, ClientConfigurationBuilderError, ClientCredentials, + ExternallyManaged, OAuthSession, RefreshToken, }; use rigetti_pyo3::{ create_init_submodule, py_function_sync_async, py_wrap_error, py_wrap_type, @@ -19,7 +19,9 @@ create_init_submodule! { PyQcsClient, OAuthSession, AuthServer, - RefreshToken + RefreshToken, + ClientCredentials, + ExternallyManaged ], errors: [ LoadClientError, diff --git a/crates/python/src/qpu/isa.rs b/crates/python/src/qpu/isa.rs index ca7eb1bbc..c1ca99d85 100644 --- a/crates/python/src/qpu/isa.rs +++ b/crates/python/src/qpu/isa.rs @@ -48,11 +48,11 @@ py_wrap_error!(isa, RustGetIsaError, GetISAError, PyRuntimeError); py_wrap_union_enum! { PyFamily(Family) as "Family" { - NONE: None, - Full: Full, - Aspen: Aspen, - Ankaa: Ankaa, - Unknown: Unknown => String + none: None, + full: Full, + aspen: Aspen, + ankaa: Ankaa, + unknown: Unknown => String } }