diff --git a/.github/workflows/unit-test-sgx.yml b/.github/workflows/unit-test-sgx.yml index 684b5c7926..ac16dbc77c 100644 --- a/.github/workflows/unit-test-sgx.yml +++ b/.github/workflows/unit-test-sgx.yml @@ -46,4 +46,4 @@ jobs: working-directory: exe-unit run: | echo "TODO: fix sgx tests" - # cargo test --features sgx \ No newline at end of file + # cargo test --features sgx diff --git a/.gitignore b/.gitignore index e93842e282..11d3715b9c 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,9 @@ db.sqlite-wal # bak files (generated by notepad++) **/*.bak + +# Suggested debug data dirs +yagnadir +providerdir + +*.bat \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 1dd675c289..e13972c68c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1464,6 +1464,17 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" +[[package]] +name = "cron" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8c3e73077b4b4a6ab1ea5047c37c57aee77657bc8ecd6f29b0af082d0b0c07" +dependencies = [ + "chrono", + "nom 7.1.3", + "once_cell", +] + [[package]] name = "crossbeam" version = "0.7.3" @@ -8901,7 +8912,7 @@ dependencies = [ [[package]] name = "ya-client" version = "0.9.0" -source = "git+https://github.com/golemfactory/ya-client.git?rev=653e7ed3ff8836837b660a76e604055e167b1f2e#653e7ed3ff8836837b660a76e604055e167b1f2e" +source = "git+https://github.com/golemfactory/ya-client.git?rev=b4bf2aab46181d594dcba5b6956c03172ee1f2ea#b4bf2aab46181d594dcba5b6956c03172ee1f2ea" dependencies = [ "actix-codec", "awc", @@ -8925,7 +8936,7 @@ dependencies = [ [[package]] name = "ya-client-model" version = "0.6.0" -source = "git+https://github.com/golemfactory/ya-client.git?rev=653e7ed3ff8836837b660a76e604055e167b1f2e#653e7ed3ff8836837b660a76e604055e167b1f2e" +source = "git+https://github.com/golemfactory/ya-client.git?rev=b4bf2aab46181d594dcba5b6956c03172ee1f2ea#b4bf2aab46181d594dcba5b6956c03172ee1f2ea" dependencies = [ "bigdecimal 0.2.2", "bytesize", @@ -9669,6 +9680,7 @@ dependencies = [ "base64 0.12.3", "bigdecimal 0.2.2", "chrono", + "cron", "derive_more", "diesel", "diesel_migrations", @@ -9764,9 +9776,11 @@ dependencies = [ "chrono", "diesel", "dotenv", + "humantime 2.1.0", "libsqlite3-sys", "log", "r2d2", + "serde", "serde_json", "structopt", "tempdir", diff --git a/Cargo.toml b/Cargo.toml index 982e81c085..3e1044e000 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -192,6 +192,7 @@ arc_with_non_send_sync = "allow" blocks_in_conditions = "allow" get_first = "allow" doc_lazy_continuation = "allow" +len_zero = "allow" [workspace] members = [ @@ -287,8 +288,8 @@ ya-sb-proto = { version = "0.6.2" } ya-sb-util = { version = "0.5.1" } parking_lot = "0.12.3" mime = "0.3.17" -ya-client = { git = "https://github.com/golemfactory/ya-client.git", rev = "653e7ed3ff8836837b660a76e604055e167b1f2e" } -ya-client-model = { git = "https://github.com/golemfactory/ya-client.git", rev = "653e7ed3ff8836837b660a76e604055e167b1f2e" } +ya-client = { git = "https://github.com/golemfactory/ya-client.git", rev = "b4bf2aab46181d594dcba5b6956c03172ee1f2ea" } +ya-client-model = { git = "https://github.com/golemfactory/ya-client.git", rev = "b4bf2aab46181d594dcba5b6956c03172ee1f2ea" } ya-compile-time-utils.path = "utils/compile-time-utils" ya-manifest-utils = { path = "utils/manifest-utils" } @@ -325,8 +326,8 @@ ya-framework-basic.path = "test-utils/test-framework/framework-basic" ya-framework-mocks.path = "test-utils/test-framework/framework-mocks" [patch.crates-io] -ya-client = { git = "https://github.com/golemfactory/ya-client.git", rev = "653e7ed3ff8836837b660a76e604055e167b1f2e" } -ya-client-model = { git = "https://github.com/golemfactory/ya-client.git", rev = "653e7ed3ff8836837b660a76e604055e167b1f2e" } +ya-client = { git = "https://github.com/golemfactory/ya-client.git", rev = "b4bf2aab46181d594dcba5b6956c03172ee1f2ea" } +ya-client-model = { git = "https://github.com/golemfactory/ya-client.git", rev = "b4bf2aab46181d594dcba5b6956c03172ee1f2ea" } golem-certificate = { git = "https://github.com/golemfactory/golem-certificate.git", rev = "f2d7514c18fc066e9cfb796090b90f5b27cfe1c6" } ethereum-tx-sign = { git = "https://github.com/golemfactory/ethereum-tx-sign.git", rev = "1164c74187a9e2947faeaea7dde104c3cdec4195" } graphene-sgx = { git = " https://github.com/golemfactory/graphene-rust.git", rev = "dbd993ebad7f9190410ea390a589348479af6407" } diff --git a/core/activity/src/error.rs b/core/activity/src/error.rs index 85d6ef654e..f74ca51bb6 100644 --- a/core/activity/src/error.rs +++ b/core/activity/src/error.rs @@ -117,7 +117,7 @@ impl ResponseError for Error { } _ => { let e = self.to_string(); - log::error!("Activity API server error: {}", e); + //log::error!("Activity API server error: {}", e); HttpResponse::InternalServerError().json(ErrorMessage::new(e)) } } diff --git a/core/model/src/driver.rs b/core/model/src/driver.rs index d24ff2c23f..79f5172d10 100644 --- a/core/model/src/driver.rs +++ b/core/model/src/driver.rs @@ -5,7 +5,10 @@ use derive_more::From; use serde::{Deserialize, Serialize}; use std::fmt::Display; use std::time::Duration; -use ya_client_model::payment::{allocation::Deposit, Allocation, DriverStatusProperty, Payment}; +use ya_client_model::payment::{ + allocation::Allocation, allocation::Deposit, DriverStatusProperty, Payment, +}; +use ya_client_model::NodeId; use ya_service_bus::RpcMessage; pub fn driver_bus_id(driver_name: T) -> String { @@ -296,6 +299,43 @@ impl RpcMessage for Init { type Error = GenericError; } +// ************************** FLUSH PAYMENTS ************************** + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct FlushPayments { + node_id: Option, + flush_date: DateTime, +} + +impl FlushPayments { + pub fn new(node_id: Option, flush_date: DateTime) -> FlushPayments { + FlushPayments { + node_id, + flush_date, + } + } + + pub fn flush_date(&self) -> DateTime { + self.flush_date + } + + pub fn node_id(&self) -> Option { + self.node_id + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum FlushPaymentResult { + FlushScheduled, + FlushNotNeeded, +} + +impl RpcMessage for FlushPayments { + const ID: &'static str = "FlushPayments"; + type Item = FlushPaymentResult; + type Error = GenericError; +} + // ************************** SCHEDULE PAYMENT ************************** #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/core/model/src/payment.rs b/core/model/src/payment.rs index be4f2a21b8..ea3ff0d080 100644 --- a/core/model/src/payment.rs +++ b/core/model/src/payment.rs @@ -21,7 +21,8 @@ pub mod local { use super::{public::Ack, *}; use crate::driver::{AccountMode, GasDetails, PaymentConfirmation, ValidateAllocationResult}; use bigdecimal::{BigDecimal, Zero}; - use chrono::{DateTime, Utc}; + use chrono::{DateTime, NaiveDateTime, Utc}; + use serde_json::json; use std::fmt::Display; use std::time::Duration; use structopt::*; @@ -52,84 +53,6 @@ pub mod local { Invoice(InvoicePayment), } - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct SchedulePayment { - pub title: PaymentTitle, - pub payer_id: NodeId, - pub payee_id: NodeId, - pub payer_addr: String, - pub payee_addr: String, - pub payment_platform: String, - pub allocation_id: String, - pub amount: BigDecimal, - pub due_date: DateTime, - } - - impl SchedulePayment { - pub fn from_invoice( - invoice: Invoice, - allocation_id: String, - amount: BigDecimal, - ) -> Option { - if amount <= BigDecimal::zero() { - return None; - } - Some(Self { - title: PaymentTitle::Invoice(InvoicePayment { - invoice_id: invoice.invoice_id, - agreement_id: invoice.agreement_id, - }), - payer_id: invoice.recipient_id, - payee_id: invoice.issuer_id, - payer_addr: invoice.payer_addr, - payee_addr: invoice.payee_addr, - payment_platform: invoice.payment_platform, - allocation_id, - amount, - due_date: invoice.payment_due_date, - }) - } - - pub fn from_debit_note( - debit_note: DebitNote, - allocation_id: String, - amount: BigDecimal, - ) -> Option { - if amount <= BigDecimal::zero() { - return None; - } - debit_note.payment_due_date.map(|due_date| Self { - title: PaymentTitle::DebitNote(DebitNotePayment { - debit_note_id: debit_note.debit_note_id, - activity_id: debit_note.activity_id, - }), - payer_id: debit_note.recipient_id, - payee_id: debit_note.issuer_id, - payer_addr: debit_note.payer_addr, - payee_addr: debit_note.payee_addr, - payment_platform: debit_note.payment_platform, - allocation_id, - amount, - due_date, - }) - } - - pub fn document_id(&self) -> String { - match &self.title { - PaymentTitle::Invoice(invoice_payment) => invoice_payment.invoice_id.clone(), - PaymentTitle::DebitNote(debit_note_payment) => { - debit_note_payment.debit_note_id.clone() - } - } - } - } - - impl RpcMessage for SchedulePayment { - const ID: &'static str = "SchedulePayment"; - type Item = (); - type Error = GenericError; - } - #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)] #[error("{inner}")] pub struct GenericError { @@ -161,6 +84,8 @@ pub mod local { InvalidDefaultNetwork(String), #[error("Internal timeout")] InternalTimeout, + #[error("Other")] + Other(String), } impl RpcMessage for RegisterDriver { @@ -240,7 +165,7 @@ pub mod local { pub amount: BigDecimal, pub sender: String, pub recipient: String, - pub order_ids: Vec, + pub payment_id: String, pub confirmation: PaymentConfirmation, } @@ -446,24 +371,19 @@ pub mod local { } #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct ReleaseAllocations {} + pub struct BuildPayments {} - impl RpcMessage for ReleaseAllocations { - const ID: &'static str = "ReleaseAllocations"; - type Item = (); - type Error = GenericError; + impl RpcMessage for BuildPayments { + const ID: &'static str = "BuildPayments"; + type Item = String; + type Error = NoError; } #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct ReleaseDeposit { - pub platform: String, - pub from: String, - pub deposit_contract: String, - pub deposit_id: String, - } + pub struct ReleaseAllocations {} - impl RpcMessage for ReleaseDeposit { - const ID: &'static str = "ReleaseDeposit"; + impl RpcMessage for ReleaseAllocations { + const ID: &'static str = "ReleaseAllocations"; type Item = (); type Error = GenericError; } @@ -519,6 +439,115 @@ pub mod local { type Error = PaymentDriverStatusError; } + // ********************* GET PROCESS PAYMENTS INFO ******************************** + #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)] + pub enum ProcessBatchCycleError { + #[error("ProcessBatchCycleError: {0}")] + ProcessBatchCycleError(String), + } + + impl From for GenericError { + fn from(e: ProcessBatchCycleError) -> Self { + GenericError::new(e) + } + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct ProcessBatchCycleResponse { + pub node_id: NodeId, + pub platform: String, + pub interval: Option, + pub cron: Option, + pub max_interval: Duration, + pub extra_payment_time: Duration, + pub next_process: NaiveDateTime, + pub last_process: Option, + } + + fn round_duration_to_sec(d: Duration) -> Duration { + //0.500 gives 1.0 + //0.499 gives 0.0 + let secs = ((d.as_millis() + 500) / 1000) as u64; + Duration::from_secs(secs) + } + + pub fn batch_cycle_response_to_json(resp: &ProcessBatchCycleResponse) -> serde_json::Value { + json!({ + "nodeId": resp.node_id, + "platform": resp.platform, + "intervalSec": resp.interval.map(|d| d.as_secs()), + "cron": resp.cron, + "extraPayTimeSec": round_duration_to_sec(resp.extra_payment_time).as_secs(), + "maxIntervalSec": round_duration_to_sec(resp.max_interval).as_secs(), + "nextProcess": resp.next_process.and_utc().to_rfc3339(), + "lastProcess": resp.last_process.map(|l| l.and_utc().to_rfc3339()), + }) + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct ProcessBatchCycleInfo { + pub node_id: NodeId, + pub platform: String, + } + + impl RpcMessage for ProcessBatchCycleInfo { + const ID: &'static str = "ProcessBatchCycleInfo"; + type Item = ProcessBatchCycleResponse; + type Error = ProcessBatchCycleError; + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct ProcessBatchCycleSet { + pub node_id: NodeId, + pub platform: String, + pub interval: Option, + pub cron: Option, + pub next_update: Option>, + pub safe_payout: Option, + } + + impl RpcMessage for ProcessBatchCycleSet { + const ID: &'static str = "ProcessBatchCycleSet"; + type Item = ProcessBatchCycleResponse; + type Error = ProcessBatchCycleError; + } + + // ********************* PROCESS PAYMENTS ******************************** + #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)] + pub enum ProcessPaymentsError { + #[error("ProcessPaymentsError: {0}")] + ProcessPaymentsError(String), + } + + impl From for GenericError { + fn from(e: ProcessPaymentsError) -> Self { + GenericError::new(e) + } + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct ProcessPaymentsNowResponse { + pub resolve_time_ms: f64, + pub send_time_ms: f64, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct ProcessPaymentsNow { + pub node_id: NodeId, + pub platform: String, + pub skip_resolve: bool, + pub skip_send: bool, + } + + impl RpcMessage for ProcessPaymentsNow { + const ID: &'static str = "ProcessPaymentsNow"; + type Item = ProcessPaymentsNowResponse; + type Error = ProcessPaymentsError; + } + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ShutDown { pub timeout: Duration, diff --git a/core/payment-driver/base/src/bus.rs b/core/payment-driver/base/src/bus.rs index 4df5e9d80e..b0214e9cc9 100644 --- a/core/payment-driver/base/src/bus.rs +++ b/core/payment-driver/base/src/bus.rs @@ -65,6 +65,9 @@ pub async fn bind_service( .bind_with_processor( move |_, dr, c, m| async move { dr.schedule_payment( c, m).await } ) + .bind_with_processor( + move |_, dr, c, m| async move { dr.flush_payments( c, m).await } + ) .bind_with_processor( move |_, dr, c, m| async move { dr.verify_payment( c, m).await } ) @@ -176,7 +179,7 @@ pub async fn get_pubkey(node_id: NodeId) -> Result, GenericError> { pub async fn notify_payment( driver_name: &str, platform: &str, - order_ids: Vec, + payment_id: String, details: &PaymentDetails, confirmation: Vec, ) -> Result<(), GenericError> { @@ -186,7 +189,7 @@ pub async fn notify_payment( amount: details.amount.clone(), sender: details.sender.clone(), recipient: details.recipient.clone(), - order_ids, + payment_id, confirmation: PaymentConfirmation { confirmation }, }; service(payment_srv::BUS_ID) diff --git a/core/payment-driver/base/src/driver.rs b/core/payment-driver/base/src/driver.rs index 40d5ff168e..f44747ea63 100644 --- a/core/payment-driver/base/src/driver.rs +++ b/core/payment-driver/base/src/driver.rs @@ -68,6 +68,12 @@ pub trait PaymentDriver { msg: SchedulePayment, ) -> Result; + async fn flush_payments( + &self, + caller: String, + msg: FlushPayments, + ) -> Result; + async fn verify_payment( &self, caller: String, diff --git a/core/payment-driver/dummy/src/service.rs b/core/payment-driver/dummy/src/service.rs index 694b50b3c2..8bdfe51b4d 100644 --- a/core/payment-driver/dummy/src/service.rs +++ b/core/payment-driver/dummy/src/service.rs @@ -97,14 +97,14 @@ async fn schedule_payment( let confirmation = serde_json::to_string(&details) .map_err(GenericError::new)? .into_bytes(); - let order_id = Uuid::new_v4().to_string(); + let payment_id = Uuid::new_v4().to_string(); let msg = payment_srv::NotifyPayment { driver: DRIVER_NAME.to_string(), platform: PLATFORM_NAME.to_string(), amount: details.amount, sender: details.sender, recipient: details.recipient, - order_ids: vec![order_id.clone()], + payment_id: payment_id.clone(), confirmation: PaymentConfirmation { confirmation }, }; @@ -118,7 +118,7 @@ async fn schedule_payment( .map_err(|e| log::error!("{}", e)); }); - Ok(order_id) + Ok(payment_id) } async fn verify_payment( diff --git a/core/payment-driver/erc20/src/driver.rs b/core/payment-driver/erc20/src/driver.rs index 86232afe31..9ccfa1a5a3 100644 --- a/core/payment-driver/erc20/src/driver.rs +++ b/core/payment-driver/erc20/src/driver.rs @@ -133,21 +133,7 @@ impl Erc20Driver { let payment_id = Uuid::new_v4().to_simple().to_string(); - let deposit_id = if let Some(deposit) = deposit_id { - Some(DepositId { - deposit_id: U256::from_str(&deposit.id).map_err(|err| { - GenericError::new(format!("Error when parsing deposit id {err:?}")) - })?, - lock_address: Address::from_str(&deposit.contract).map_err(|err| { - GenericError::new(format!( - "Error when parsing deposit contract address {err:?}" - )) - })?, - }) - } else { - None - }; - + let deposit_id = extract_deposit_id(deposit_id)?; self.payment_runtime .transfer_guess_account(TransferArgs { network: network.to_string(), @@ -355,19 +341,13 @@ impl Erc20Driver { GenericError::new(format!("Malformed tx.tx_hash: {:?} {err}", tx_hash)) })?; - log::info!("name: {}", &self.get_name()); - log::info!("platform: {}", platform); - log::info!("order_id: {}", token_transfer.payment_id.as_ref().unwrap()); - log::info!("payment_details: {}", payment_details); - log::info!("confirmation: 0x{}", hex::encode(&transaction_hash)); - let Some(payment_id) = &token_transfer.payment_id else { return Err(GenericError::new("token_transfer.payment_id is null")); }; bus::notify_payment( &self.get_name(), platform, - vec![payment_id.clone()], + payment_id.clone(), &payment_details, transaction_hash, ) @@ -856,6 +836,15 @@ impl PaymentDriver for Erc20Driver { false } + async fn flush_payments( + &self, + _caller: String, + _msg: FlushPayments, + ) -> Result { + //self.payment_runtime.trigger_payments(Utc::now(), None); + Ok(FlushPaymentResult::FlushScheduled) + } + async fn init(&self, _caller: String, msg: Init) -> Result { cli::init(self, msg).await?; Ok(Ack {}) @@ -1225,3 +1214,20 @@ impl PaymentDriver for Erc20Driver { Ok(()) } } +fn extract_deposit_id(deposit_id: Option) -> Result, GenericError> { + let deposit_id = if let Some(deposit) = deposit_id { + Some(DepositId { + deposit_id: U256::from_str(&deposit.id).map_err(|err| { + GenericError::new(format!("Error when parsing deposit id {err:?}")) + })?, + lock_address: Address::from_str(&deposit.contract).map_err(|err| { + GenericError::new(format!( + "Error when parsing deposit contract address {err:?}" + )) + })?, + }) + } else { + None + }; + Ok(deposit_id) +} diff --git a/core/payment/Cargo.toml b/core/payment/Cargo.toml index 19a47772a1..60f1ea877e 100644 --- a/core/payment/Cargo.toml +++ b/core/payment/Cargo.toml @@ -32,6 +32,7 @@ anyhow = "1.0" base64 = "0.12" bigdecimal = "0.2" chrono = { version = "0.4", features = ["serde"] } +cron = "0.12" derive_more = "0.99.18" diesel = { version = "1.4", features = [ "sqlite", @@ -85,4 +86,4 @@ test-context = "0.1.4" url = "2.5" [lints] -workspace = true +workspace = true \ No newline at end of file diff --git a/core/payment/examples/resolve_invoices.rs b/core/payment/examples/resolve_invoices.rs new file mode 100644 index 0000000000..f440642be6 --- /dev/null +++ b/core/payment/examples/resolve_invoices.rs @@ -0,0 +1,112 @@ +use std::sync::Arc; +use structopt::StructOpt; +use ya_core_model::NodeId; +use ya_payment::dao::BatchDao; +use ya_payment::send_batch_payments; +use ya_persistence::executor::DbExecutor; + +#[derive(StructOpt)] +struct Args { + #[structopt(subcommand)] + command: Command, +} + +#[derive(StructOpt)] +enum Command { + Generate { + #[structopt(long, default_value = "0x206bfe4f439a83b65a5b9c2c3b1cc6cb49054cc4")] + owner: NodeId, + #[structopt(long, default_value = "erc20-mumbai-tglm")] + payment_platform: String, + }, + SendPayments { + #[structopt(long, default_value = "0x206bfe4f439a83b65a5b9c2c3b1cc6cb49054cc4")] + owner: NodeId, + #[structopt(long)] + order_id: String, + }, + Run { + #[structopt(long)] + owner: NodeId, + #[structopt(long)] + payment_platform: String, + #[structopt(long)] + interval: Option, + }, +} + +#[actix_rt::main] +async fn main() -> anyhow::Result<()> { + std::env::set_var("RUST_LOG", "debug"); + env_logger::init(); + + let args = Args::from_args_safe()?; + + log::info!("test1"); + + let db = { + let database_url = "yagnadir/payment.db"; + let db = DbExecutor::new(database_url)?; + db.apply_migration(ya_payment::migrations::run_with_output)?; + db + }; + + match args.command { + Command::Generate { + payment_platform, + owner, + } => generate(db, owner, payment_platform).await?, + Command::SendPayments { order_id, owner } => { + send_batch_payments(Arc::new(tokio::sync::Mutex::new(db)), owner, &order_id).await? + } + Command::Run { + owner, + payment_platform, + interval, + } => { + if let Some(duration) = interval { + loop { + tokio::time::sleep(duration.into()).await; + log::info!("sending payments for {} {}", owner, payment_platform); + if let Err(e) = run(db.clone(), owner, payment_platform.clone()).await { + log::error!("failed to process order: {:?}", e); + } + } + } else { + run(db, owner, payment_platform).await?; + } + } + } + Ok(()) +} + +async fn generate( + db: DbExecutor, + owner_id: NodeId, + payment_platform: String, +) -> anyhow::Result<()> { + let ts = chrono::Utc::now() + chrono::Duration::days(-7); + + let order_id = db + .as_dao::() + .resolve(owner_id, owner_id.to_string(), payment_platform.clone(), ts) + .await?; + + eprintln!("order={:?}", order_id); + Ok(()) +} + +async fn run(db: DbExecutor, owner_id: NodeId, payment_platform: String) -> anyhow::Result<()> { + let ts = chrono::Utc::now() + chrono::Duration::days(-15); + + if let Some(order_id) = db + .as_dao::() + .resolve(owner_id, owner_id.to_string(), payment_platform, ts) + .await? + { + log::info!("resolved order: {}", order_id); + //nothing + //send_payments(db, order_id).await?; + } + Ok(()) +} diff --git a/core/payment/migrations/2024-08-12-152257_timestamps/down.sql b/core/payment/migrations/2024-08-12-152257_timestamps/down.sql new file mode 100644 index 0000000000..cb601295e5 --- /dev/null +++ b/core/payment/migrations/2024-08-12-152257_timestamps/down.sql @@ -0,0 +1 @@ +-- It is not possible to revert this migration. diff --git a/core/payment/migrations/2024-08-12-152257_timestamps/up.sql b/core/payment/migrations/2024-08-12-152257_timestamps/up.sql new file mode 100644 index 0000000000..1bd4d8da2b --- /dev/null +++ b/core/payment/migrations/2024-08-12-152257_timestamps/up.sql @@ -0,0 +1,18 @@ +-- Your SQL goes here +ALTER TABLE pay_agreement ADD created_ts DATETIME; + +ALTER TABLE pay_agreement ADD updated_ts DATETIME; + +ALTER TABLE pay_activity ADD created_ts DATETIME; + +ALTER TABLE pay_activity ADD updated_ts DATETIME; + +CREATE INDEX idx_created_ts_pay_agreement ON pay_agreement(created_ts); +CREATE INDEX idx_updated_ts_pay_agreement ON pay_agreement(updated_ts); +CREATE INDEX idx_created_ts_pay_activity ON pay_activity(created_ts); +CREATE INDEX idx_updated_ts_pay_activity ON pay_activity(updated_ts); + +DELETE FROM pay_debit_note; + +ALTER TABLE pay_debit_note ADD debit_nonce INTEGER NOT NULL; +CREATE UNIQUE INDEX idx_debit_nonce_pay_debit_note ON pay_debit_note(activity_id, debit_nonce); diff --git a/core/payment/migrations/2024-08-12-171012_extended_order/down.sql b/core/payment/migrations/2024-08-12-171012_extended_order/down.sql new file mode 100644 index 0000000000..177119066d --- /dev/null +++ b/core/payment/migrations/2024-08-12-171012_extended_order/down.sql @@ -0,0 +1,2 @@ +-- It is not possible to revert this migration. + diff --git a/core/payment/migrations/2024-08-12-171012_extended_order/up.sql b/core/payment/migrations/2024-08-12-171012_extended_order/up.sql new file mode 100644 index 0000000000..b7596b9121 --- /dev/null +++ b/core/payment/migrations/2024-08-12-171012_extended_order/up.sql @@ -0,0 +1,227 @@ + + +-- Your SQL goes here +CREATE TABLE pay_batch_order( + id VARCHAR (50) NOT NULL, + created_ts DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), + updated_ts DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), + owner_id VARCHAR(50) NOT NULL, + payer_addr VARCHAR(50) NOT NULL, + platform VARCHAR(50) NOT NULL, + total_amount VARCHAR(32) NOT NULL, + paid_amount VARCHAR(32) NOT NULL, + + CONSTRAINT pay_batch_order_pk PRIMARY KEY(owner_id, id) +); + +CREATE INDEX pay_batch_order_created_ts ON pay_batch_order (created_ts); + +CREATE TABLE pay_batch_order_item( + order_id VARCHAR(50) NOT NULL, + owner_id VARCHAR(50) NOT NULL, + payee_addr VARCHAR(50) NOT NULL, + allocation_id VARCHAR(50) NOT NULL, + amount VARCHAR(32) NOT NULL, + payment_id VARCHAR(50), + paid BOOLEAN NOT NULL DEFAULT FALSE, + + CONSTRAINT pay_batch_order_item_pk PRIMARY KEY (owner_id, order_id, payee_addr, allocation_id), + CONSTRAINT pay_batch_order_item_fk1 FOREIGN KEY (owner_id, order_id) REFERENCES pay_batch_order(owner_id, id), + CONSTRAINT pay_batch_order_item_fk2 FOREIGN KEY (owner_id, allocation_id) REFERENCES pay_allocation(owner_id, id) +); + +-- We are selecting by payment_id when notifying the pay_batch_order_item +CREATE INDEX pay_batch_order_item_payment_id ON pay_batch_order_item (payment_id); + +CREATE TABLE pay_batch_order_item_document( + order_id VARCHAR(50) NOT NULL, + owner_id VARCHAR(50) NOT NULL, + payee_addr VARCHAR(50) NOT NULL, + allocation_id VARCHAR(50) NOT NULL, + agreement_id VARCHAR(50) NOT NULL, + invoice_id VARCHAR(50) NULL, + activity_id VARCHAR(50) NULL, + debit_note_id VARCHAR(50) NULL, + amount VARCHAR(32) NOT NULL, + + CONSTRAINT pay_batch_order_item_agreement_pk PRIMARY KEY (owner_id, order_id, payee_addr, allocation_id, agreement_id, activity_id), + CONSTRAINT pay_batch_order_item_agreement_fk1 FOREIGN KEY (owner_id, order_id, payee_addr, allocation_id) REFERENCES pay_batch_order_item(owner_id, order_id, payee_addr, allocation_id), + CONSTRAINT pay_batch_order_item_agreement_fk2 FOREIGN KEY (owner_id, agreement_id) REFERENCES pay_agreement(owner_id, id), + CONSTRAINT pay_batch_order_item_agreement_fk3 FOREIGN KEY (owner_id, activity_id) REFERENCES pay_activity(owner_id, id), + CONSTRAINT pay_batch_order_item_agreement_fk4 FOREIGN KEY (owner_id, allocation_id) REFERENCES pay_allocation(owner_id, id), + CONSTRAINT pay_batch_order_item_agreement_fk5 FOREIGN KEY (owner_id, invoice_id) + REFERENCES pay_invoice(owner_id, id) + ON DELETE SET NULL, + CONSTRAINT pay_batch_order_item_agreement_fk6 FOREIGN KEY (owner_id, debit_note_id) + REFERENCES pay_debit_note(owner_id, id) + ON DELETE SET NULL, + CHECK ((invoice_id IS NULL) <> (debit_note_id IS NULL)) +); + +CREATE TABLE pay_batch_cycle +( + owner_id VARCHAR(50) NOT NULL, + platform VARCHAR(50) NOT NULL, + created_ts DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), + updated_ts DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), + cycle_interval VARCHAR(50), + cycle_cron VARCHAR(50), + cycle_last_process DATETIME, + cycle_next_process DATETIME NOT NULL, + cycle_max_interval VARCHAR(50) NOT NULL, + cycle_extra_pay_time VARCHAR(50) NOT NULL, + + CONSTRAINT pay_batch_cycle_pk PRIMARY KEY(owner_id, platform), + CONSTRAINT pay_batch_cycle_check_1 CHECK((cycle_interval IS NULL) <> (cycle_cron IS NULL)) +); + +DROP TABLE pay_allocation; + +CREATE TABLE pay_allocation +( + id VARCHAR(50) NOT NULL, + owner_id VARCHAR(50) NOT NULL, + payment_platform VARCHAR(50) NOT NULL, + address VARCHAR(50) NOT NULL, + avail_amount VARCHAR(32) NOT NULL, + spent_amount VARCHAR(32) NOT NULL, + created_ts DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), + updated_ts DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), + timeout DATETIME NOT NULL, + released BOOLEAN NOT NULL, + deposit TEXT, + deposit_status TEXT, + + CONSTRAINT pay_allocation_deposit_status_check CHECK (deposit_status IN ('open', 'closing', 'closed')), + CONSTRAINT pay_allocation_pk PRIMARY KEY(owner_id, id) +); + +CREATE INDEX pay_allocation_ppa_idx ON pay_allocation (payment_platform, address); +CREATE INDEX pay_allocation_created_ts ON pay_allocation (created_ts); +CREATE INDEX pay_allocation_updated_ts ON pay_allocation (updated_ts); + +CREATE TABLE pay_allocation_expenditure +( + owner_id VARCHAR(50) NOT NULL, + allocation_id VARCHAR(50) NOT NULL, + agreement_id VARCHAR(50) NOT NULL, + activity_id VARCHAR(50), + accepted_amount VARCHAR(32) NOT NULL, + scheduled_amount VARCHAR(32) NOT NULL, + + CONSTRAINT pay_allocation_expenditure_pk PRIMARY KEY (owner_id, allocation_id, agreement_id, activity_id), + CONSTRAINT pay_allocation_expenditure_fk1 FOREIGN KEY (owner_id, allocation_id) REFERENCES pay_allocation(owner_id, id), + CONSTRAINT pay_allocation_expenditure_fk2 FOREIGN KEY (owner_id, activity_id) REFERENCES pay_activity(owner_id, id), + CONSTRAINT pay_allocation_expenditure_fk3 FOREIGN KEY (owner_id, agreement_id) REFERENCES pay_agreement(owner_id, id) +); + +ALTER TABLE pay_payment RENAME TO pay_payment_old; + +CREATE TABLE pay_payment( + id VARCHAR(50) NOT NULL, + owner_id VARCHAR(50) NOT NULL, + peer_id VARCHAR(50) NOT NULL, + payee_addr VARCHAR(50) NOT NULL, + payer_addr VARCHAR(50) NOT NULL, + payment_platform VARCHAR(50) NOT NULL, + role CHAR(1) NOT NULL CHECK (role in ('R', 'P')), + amount VARCHAR(32) NOT NULL, + timestamp DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), + details BLOB NOT NULL, + send_payment BOOLEAN NOT NULL DEFAULT FALSE, + signature BLOB, + signed_bytes TEXT, + + CONSTRAINT pay_payment_pk PRIMARY KEY (id, owner_id, peer_id) +); + +INSERT INTO pay_payment (id, owner_id, peer_id, payee_addr, payer_addr, payment_platform, role, amount, timestamp, details, send_payment, signature, signed_bytes) +SELECT id, owner_id, peer_id, payee_addr, payer_addr, payment_platform, role, amount, timestamp, details, send_payment, signature, signed_bytes +FROM pay_payment_old; + +CREATE TABLE pay_payment_document( + payment_id VARCHAR(50) NOT NULL, + owner_id VARCHAR(50) NOT NULL, + peer_id VARCHAR(50) NOT NULL, + agreement_id VARCHAR(50) NOT NULL, + invoice_id VARCHAR(50), + activity_id VARCHAR(50), + debit_note_id VARCHAR(50), + amount VARCHAR(32) NOT NULL, + + CONSTRAINT pay_payment_document_pk PRIMARY KEY (payment_id, owner_id, peer_id, agreement_id, activity_id), + CONSTRAINT pay_payment_document_fk1 FOREIGN KEY (payment_id, owner_id, peer_id) REFERENCES pay_payment(id, owner_id, peer_id), + CONSTRAINT pay_payment_document_fk2 FOREIGN KEY (owner_id, activity_id) REFERENCES pay_activity(owner_id, id), + CONSTRAINT pay_payment_document_fk3 FOREIGN KEY (owner_id, agreement_id) REFERENCES pay_agreement(owner_id, id), + CONSTRAINT pay_payment_document_fk4 FOREIGN KEY (owner_id, invoice_id) + REFERENCES pay_invoice(owner_id, id) + ON DELETE SET NULL, + CONSTRAINT pay_payment_document_fk5 FOREIGN KEY (owner_id, debit_note_id) + REFERENCES pay_debit_note(owner_id, id) + ON DELETE SET NULL +); + + +-- copy data from pay_activity_payment to pay_payment_document + +INSERT INTO pay_payment_document ( + owner_id, + payment_id, + peer_id, + agreement_id, + invoice_id, + activity_id, + debit_note_id, + amount) +SELECT pap.owner_id, + pap.payment_id, + pp.peer_id, + pa.agreement_id, + null, + pap.activity_id, + null, + pap.amount +FROM pay_activity_payment pap + LEFT JOIN pay_activity pa + on pap.activity_id = pa.id + LEFT JOIN pay_payment_old pp + on pap.payment_id = pp.id and pap.owner_id = pp.owner_id; + + +INSERT INTO pay_payment_document ( + owner_id, + payment_id, + peer_id, + agreement_id, + invoice_id, + activity_id, + debit_note_id, + amount) +SELECT pap.owner_id, + pap.payment_id, + pp.peer_id, + pap.agreement_id, + null, + null, + null, + pap.amount +FROM pay_agreement_payment as pap + LEFT JOIN pay_payment_old pp + on pap.payment_id = pp.id and pap.owner_id = pp.owner_id; + +DROP TABLE pay_activity_payment; +DROP TABLE pay_agreement_payment; +DROP TABLE pay_payment_old; +DROP TABLE pay_order; + +CREATE TABLE pay_post_migration( + job VARCHAR(50) NOT NULL, + done DATETIME, + result TEXT, + + CONSTRAINT pay_post_migration_pk PRIMARY KEY (job) +); + +-- update total_paid in pay_agreement after migrations +INSERT INTO pay_post_migration (job) VALUES ('sum_activities_into_agreement'); + diff --git a/core/payment/sql/list_payment_documents.sql b/core/payment/sql/list_payment_documents.sql new file mode 100644 index 0000000000..63a20c9341 --- /dev/null +++ b/core/payment/sql/list_payment_documents.sql @@ -0,0 +1,13 @@ +SELECT pp.timestamp, + pp.payee_addr, + ppd.payment_id, + ppd.owner_id, + ppd.peer_id, + ppd.agreement_id, + ppd.invoice_id, + ppd.activity_id, + ppd.debit_note_id, + ppd.amount + FROM pay_payment_document ppd + JOIN pay_payment pp ON ppd.owner_id = pp.owner_id AND ppd.peer_id = pp.peer_id AND ppd.payment_id = pp.id + ORDER BY pp.timestamp desc diff --git a/core/payment/src/api.rs b/core/payment/src/api.rs index 0416489549..d4de5e13da 100644 --- a/core/payment/src/api.rs +++ b/core/payment/src/api.rs @@ -10,7 +10,11 @@ mod debit_notes; mod invoices; mod payments; +mod batch; +mod cycle; mod guard; +mod pay_activities; +mod pay_agreements; pub fn api_scope(scope: Scope) -> Scope { scope @@ -20,6 +24,10 @@ pub fn api_scope(scope: Scope) -> Scope { .extend(debit_notes::register_endpoints) .extend(invoices::register_endpoints) .extend(payments::register_endpoints) + .extend(pay_agreements::register_endpoints) + .extend(pay_activities::register_endpoints) + .extend(batch::register_endpoints) + .extend(cycle::register_endpoints) } pub fn web_scope(db: &DbExecutor) -> Scope { diff --git a/core/payment/src/api/allocations/mod.rs b/core/payment/src/api/allocations/mod.rs index a6bddbba05..b8b609093a 100644 --- a/core/payment/src/api/allocations/mod.rs +++ b/core/payment/src/api/allocations/mod.rs @@ -12,8 +12,7 @@ use ya_agreement_utils::{ClauseOperator, ConstraintKey, Constraints}; use ya_client_model::payment::allocation::PaymentPlatformEnum; use ya_client_model::payment::*; use ya_core_model::payment::local::{ - DriverName, NetworkName, ReleaseDeposit, ValidateAllocation, ValidateAllocationError, - BUS_ID as LOCAL_SERVICE, + DriverName, NetworkName, ValidateAllocation, ValidateAllocationError, BUS_ID as LOCAL_SERVICE, }; use ya_core_model::payment::RpcMessageError; use ya_persistence::executor::DbExecutor; @@ -47,6 +46,14 @@ pub fn register_endpoints(scope: Scope) -> Scope { delete().to(release_allocation), ) .route("/demandDecorations", get().to(get_demand_decorations)) + .route( + "/allocations/{allocation_id}/orders", + get().to(get_pay_allocation_orders), + ) + .route( + "/allocations/{allocation_id}/expenditures", + get().to(get_allocation_expenditures), + ) } async fn create_allocation( @@ -168,13 +175,8 @@ async fn create_allocation( Ok(AllocationStatus::Active(allocation)) => { let allocation_id = allocation.allocation_id.clone(); - release_allocation_after( - db.clone(), - allocation_id, - allocation.timeout, - Some(node_id), - ) - .await; + release_allocation_after(db.clone(), allocation_id, allocation.timeout, node_id) + .await; response::created(allocation) } @@ -226,6 +228,21 @@ async fn get_allocation( } } +async fn get_allocation_expenditures( + db: Data, + path: Path, + id: Identity, +) -> HttpResponse { + let allocation_id = path.allocation_id.clone(); + let node_id = id.identity; + let dao: AllocationDao = db.as_dao(); + + match dao.get_expenditures(node_id, allocation_id.clone()).await { + Ok(expenditures) => response::ok(expenditures), + Err(e) => response::server_error(&e), + } +} + fn amend_allocation_fields( old_allocation: Allocation, update: AllocationUpdate, @@ -361,29 +378,11 @@ async fn release_allocation( id: Identity, ) -> HttpResponse { let allocation_id = path.allocation_id.clone(); - let node_id = Some(id.identity); + let node_id = id.identity; let dao = db.as_dao::(); match dao.release(allocation_id.clone(), node_id).await { - Ok(AllocationReleaseStatus::Released { deposit, platform }) => { - if let Some(deposit) = deposit { - let release_result = bus::service(LOCAL_SERVICE) - .send(ReleaseDeposit { - from: id.identity.to_string(), - deposit_id: deposit.id, - deposit_contract: deposit.contract, - platform, - }) - .await; - match release_result { - Ok(Ok(_)) => response::ok(Null), - Err(e) => response::server_error(&e), - Ok(Err(e)) => response::server_error(&e), - } - } else { - response::ok(Null) - } - } + Ok(AllocationReleaseStatus::Released { deposit, platform }) => response::ok(Null), Ok(AllocationReleaseStatus::NotFound) => response::not_found(), Ok(AllocationReleaseStatus::Gone) => response::gone(&format!( "Allocation {} has been already released", @@ -465,7 +464,7 @@ pub async fn release_allocation_after( db: Data, allocation_id: String, allocation_timeout: Option>, - node_id: Option, + node_id: NodeId, ) { tokio::task::spawn(async move { if let Some(timeout) = allocation_timeout { @@ -491,7 +490,7 @@ pub async fn release_allocation_after( pub async fn forced_release_allocation( db: Data, allocation_id: String, - node_id: Option, + node_id: NodeId, ) { match db .as_dao::() @@ -511,3 +510,26 @@ pub async fn forced_release_allocation( _ => (), } } + +async fn get_pay_allocation_orders( + db: Data, + path: Path, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let allocation_id = path.into_inner(); + let dao: BatchDao = db.as_dao(); + match dao + .get_batch_items( + node_id, + BatchItemFilter { + allocation_id: Some(allocation_id), + ..Default::default() + }, + ) + .await + { + Ok(items) => response::ok(items), + Err(e) => response::server_error(&e), + } +} diff --git a/core/payment/src/api/batch.rs b/core/payment/src/api/batch.rs new file mode 100644 index 0000000000..7da3e5a972 --- /dev/null +++ b/core/payment/src/api/batch.rs @@ -0,0 +1,87 @@ +use crate::dao::*; +use crate::utils::*; +use actix_web::web::{get, Data, Path, Query}; +use actix_web::{HttpResponse, Scope}; +use ya_client_model::payment::params; +use ya_persistence::executor::DbExecutor; +use ya_service_api_web::middleware::Identity; + +pub fn register_endpoints(scope: Scope) -> Scope { + scope + .route("/batchOrders", get().to(get_batch_orders)) + .route("/batchOrders/{order_id}", get().to(get_batch_order)) + .route( + "/batchOrders/{order_id}/items", + get().to(get_batch_order_items), + ) + .route( + "/batchOrders/{order_id}/items/{payee_addr}/details", + get().to(get_batch_order_item_details), + ) +} + +async fn get_batch_orders( + db: Data, + query: Query, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let dao: BatchDao = db.as_dao(); + let after_timestamp = query.after_timestamp.map(|d| d.naive_utc()); + let max_items = query.max_items; + match dao + .get_for_node_id(node_id, after_timestamp, max_items) + .await + { + Ok(batch_orders) => response::ok(batch_orders), + Err(e) => response::server_error(&e), + } +} + +async fn get_batch_order(db: Data, path: Path, id: Identity) -> HttpResponse { + let node_id = id.identity; + let batch_order_id = path.into_inner(); + let dao: BatchDao = db.as_dao(); + match dao.get_batch_order(batch_order_id, node_id).await { + Ok(batch_order) => response::ok(batch_order), + Err(e) => response::server_error(&e), + } +} + +async fn get_batch_order_items( + db: Data, + path: Path, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let batch_order_id = path.into_inner(); + let dao: BatchDao = db.as_dao(); + match dao.get_batch_order_items(batch_order_id, node_id).await { + Ok(batch_order_items) => response::ok(batch_order_items), + Err(e) => response::server_error(&e), + } +} + +async fn get_batch_order_item_details( + db: Data, + path: Path<(String, String)>, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let (batch_order_id, payee_addr) = path.into_inner(); + let dao: BatchDao = db.as_dao(); + match dao + .get_batch_items( + node_id, + BatchItemFilter { + order_id: Some(batch_order_id), + payee_addr: Some(payee_addr), + ..Default::default() + }, + ) + .await + { + Ok(items) => response::ok(items), + Err(e) => response::server_error(&e), + } +} diff --git a/core/payment/src/api/cycle.rs b/core/payment/src/api/cycle.rs new file mode 100644 index 0000000000..df0736d0e2 --- /dev/null +++ b/core/payment/src/api/cycle.rs @@ -0,0 +1,134 @@ +use crate::dao::*; +use crate::utils::*; +use actix_web::web::{get, post}; +use actix_web::{web, HttpResponse, Scope}; +use chrono::{DateTime, Utc}; +use serde::Deserialize; +use ya_core_model::payment::local as pay_local; +use ya_core_model::payment::local::{ + batch_cycle_response_to_json, ProcessBatchCycleInfo, ProcessBatchCycleSet, ProcessPaymentsNow, +}; +use ya_service_api_web::middleware::Identity; +use ya_service_bus::typed as bus; + +pub fn register_endpoints(scope: Scope) -> Scope { + scope + .route("/batchCycles", get().to(get_batch_cycles)) + .route("/batchCycle/{platform}", get().to(get_batch_cycle)) + .route("/batchCycle", post().to(set_batch_cycle)) + .route("/batchCycle/{platform}/now", post().to(set_batch_cycle_now)) +} + +async fn get_batch_cycles(id: Identity) -> HttpResponse { + let node_id = id.identity; + + let drivers = match bus::service(pay_local::BUS_ID) + .call(pay_local::GetDrivers { + ignore_legacy_networks: true, + }) + .await + { + Ok(Ok(drivers)) => drivers, + Err(e) => { + return response::server_error(&e); + } + Ok(Err(e)) => { + return response::server_error(&e); + } + }; + + let mut res: Vec = Vec::new(); + + for driver in drivers { + for network in driver.1.networks { + let platform = + format!("{}-{}-{}", driver.0, network.0, network.1.default_token).to_lowercase(); + match bus::service(pay_local::BUS_ID) + .call(ProcessBatchCycleInfo { + node_id, + platform: platform.to_string(), + }) + .await + { + Ok(Ok(result)) => { + res.push(batch_cycle_response_to_json(&result)); + } + Ok(Err(e)) => { + return response::server_error(&e); + } + Err(e) => return response::server_error(&e), + } + } + } + response::ok(res) +} + +async fn get_batch_cycle(id: Identity, platform: web::Path) -> HttpResponse { + let node_id = id.identity; + + match bus::service(pay_local::BUS_ID) + .call(ProcessBatchCycleInfo { + node_id, + platform: platform.into_inner(), + }) + .await + { + Ok(Ok(batch_cycle)) => response::ok(batch_cycle_response_to_json(&batch_cycle)), + Ok(Err(e)) => response::server_error(&e), + Err(e) => response::server_error(&e), + } +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct ProcessBatchCycleSetPost { + platform: String, + interval_sec: Option, + cron: Option, + extra_pay_time_sec: Option, + next_update: Option>, +} +async fn set_batch_cycle_now(platform: web::Path, id: Identity) -> HttpResponse { + let node_id = id.identity; + + match bus::service(pay_local::BUS_ID) + .call(ProcessPaymentsNow { + node_id, + platform: platform.into_inner(), + skip_resolve: false, + skip_send: false, + }) + .await + { + Ok(Ok(batch_cycle)) => response::ok(batch_cycle), + Ok(Err(e)) => response::server_error(&e), + Err(e) => response::server_error(&e), + } +} +async fn set_batch_cycle(body: web::Json, id: Identity) -> HttpResponse { + let node_id = id.identity; + let cycle_set = body.into_inner(); + let interval: Option = + cycle_set.interval_sec.map(core::time::Duration::from_secs); + let cron = cycle_set.cron; + let extra_pay_time = cycle_set + .extra_pay_time_sec + .map(core::time::Duration::from_secs) + .unwrap_or(PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME.to_std().unwrap()); + let next_update = cycle_set.next_update; + + match bus::service(pay_local::BUS_ID) + .call(ProcessBatchCycleSet { + node_id, + platform: cycle_set.platform, + interval, + cron, + next_update, + safe_payout: Some(extra_pay_time), + }) + .await + { + Ok(batch_cycle) => response::ok(batch_cycle), + Err(e) => response::server_error(&e), + } +} diff --git a/core/payment/src/api/debit_notes.rs b/core/payment/src/api/debit_notes.rs index bf60f12da1..8b2b50a746 100644 --- a/core/payment/src/api/debit_notes.rs +++ b/core/payment/src/api/debit_notes.rs @@ -3,13 +3,12 @@ use std::sync::Arc; // Extrnal crates use actix_web::web::{get, post, Data, Json, Path, Query}; use actix_web::{HttpResponse, Scope}; +use bigdecimal::BigDecimal; use serde_json::value::Value::Null; use std::time::Instant; - // Workspace uses use metrics::{counter, timing}; use ya_client_model::payment::*; -use ya_core_model::payment::local::{SchedulePayment, BUS_ID as LOCAL_SERVICE}; use ya_core_model::payment::public::{ AcceptDebitNote, AcceptRejectError, SendDebitNote, SendError, BUS_ID as PUBLIC_SERVICE, }; @@ -19,7 +18,6 @@ use ya_persistence::executor::DbExecutor; use ya_persistence::types::Role; use ya_service_api_web::middleware::Identity; use ya_service_bus::timeout::IntoTimeoutFuture; -use ya_service_bus::{typed as bus, RpcEndpoint}; // Local uses use super::guard::AgreementLock; @@ -86,7 +84,7 @@ async fn get_debit_note( let debit_note_id = path.debit_note_id.clone(); let node_id = id.identity; let dao: DebitNoteDao = db.as_dao(); - match dao.get(debit_note_id, node_id).await { + match dao.get(debit_note_id, Some(node_id)).await { Ok(Some(debit_note)) => response::ok(debit_note), Ok(None) => response::not_found(), Err(e) => response::server_error(&e), @@ -190,7 +188,7 @@ async fn issue_debit_note( let dao: DebitNoteDao = db.as_dao(); let debit_note_id = dao.create_new(debit_note, node_id).await?; - let debit_note = dao.get(debit_note_id.clone(), node_id).await?; + let debit_note = dao.get(debit_note_id.clone(), Some(node_id)).await?; log::info!("DebitNote [{debit_note_id}] for Activity [{activity_id}] issued."); counter!("payment.debit_notes.provider.issued", 1); @@ -220,7 +218,7 @@ async fn send_debit_note( log::debug!("Requested send DebitNote [{}]", debit_note_id); counter!("payment.debit_notes.provider.sent.call", 1); - let debit_note = match dao.get(debit_note_id.clone(), node_id).await { + let debit_note = match dao.get(debit_note_id.clone(), Some(node_id)).await { Ok(Some(debit_note)) => debit_note, Ok(None) => return response::not_found(), Err(e) => return response::server_error(&e), @@ -311,7 +309,7 @@ async fn accept_debit_note( let sync_dao: SyncNotifsDao = db.as_dao(); log::trace!("Querying DB for Debit Note [{}]", debit_note_id); - let debit_note: DebitNote = match dao.get(debit_note_id.clone(), node_id).await { + let debit_note: DebitNote = match dao.get(debit_note_id.clone(), Some(node_id)).await { Ok(Some(debit_note)) => debit_note, Ok(None) => return response::not_found(), Err(e) => return response::server_error(&e), @@ -394,7 +392,7 @@ async fn accept_debit_note( } Err(e) => return response::server_error(&e), }; - let amount_to_pay = &debit_note.total_amount_due - &activity.total_amount_scheduled.0; + let amount_to_pay = &debit_note.total_amount_due - &activity.total_amount_accepted.0; log::trace!( "Querying DB for Allocation [{}] for Debit Note [{}]", @@ -426,19 +424,28 @@ async fn accept_debit_note( return response::bad_request(&msg); } + if amount_to_pay > BigDecimal::from(0) { + match db + .as_dao::() + .spend_from_allocation_transaction(SpendFromAllocationArgs { + owner_id: node_id, + allocation_id: allocation_id.clone(), + agreement_id: activity.agreement_id.clone(), + activity_id: Some(activity.id.clone()), + amount: amount_to_pay.clone(), + }) + .await + { + Ok(_) => (), + Err(e) => return response::server_error(&e), + } + } + let timeout = query.timeout.unwrap_or(params::DEFAULT_ACK_TIMEOUT); let result = async move { let issuer_id = debit_note.issuer_id; let accept_msg = AcceptDebitNote::new(debit_note_id.clone(), acceptance, issuer_id); - let schedule_msg = - SchedulePayment::from_debit_note(debit_note, allocation_id, amount_to_pay); match async move { - // Schedule payment (will be none for amount=0, which is OK) - if let Some(msg) = schedule_msg { - log::trace!("Calling SchedulePayment [{}] locally", debit_note_id); - bus::service(LOCAL_SERVICE).send(msg).await??; - } - // Mark the debit note as accepted in DB log::trace!("Accepting DebitNote [{}] in DB", debit_note_id); dao.accept(debit_note_id.clone(), node_id).await?; diff --git a/core/payment/src/api/invoices.rs b/core/payment/src/api/invoices.rs index 81f8df64f8..e92d28aec7 100644 --- a/core/payment/src/api/invoices.rs +++ b/core/payment/src/api/invoices.rs @@ -1,15 +1,14 @@ // External crates use actix_web::web::{get, post, Data, Json, Path, Query}; use actix_web::{HttpResponse, Scope}; +use bigdecimal::BigDecimal; use serde_json::value::Value::Null; use std::borrow::Cow; use std::sync::Arc; use std::time::Instant; - // Workspace uses use metrics::{counter, timing}; use ya_client_model::payment::*; -use ya_core_model::payment::local::{SchedulePayment, BUS_ID as LOCAL_SERVICE}; use ya_core_model::payment::public::{ AcceptInvoice, AcceptRejectError, CancelError, CancelInvoice, RejectInvoiceV2, SendError, SendInvoice, BUS_ID as PUBLIC_SERVICE, @@ -20,7 +19,6 @@ use ya_persistence::executor::DbExecutor; use ya_persistence::types::Role; use ya_service_api_web::middleware::Identity; use ya_service_bus::timeout::IntoTimeoutFuture; -use ya_service_bus::{typed as bus, RpcEndpoint}; // Local uses use super::guard::AgreementLock; @@ -469,7 +467,7 @@ async fn accept_invoice( log::warn!("{}", msg); return response::bad_request(&msg); } - let amount_to_pay = &invoice.amount - &agreement.total_amount_scheduled.0; + let amount_to_pay = &invoice.amount - &agreement.total_amount_accepted.0; log::trace!( "Querying DB for Allocation [{}] for Invoice [{}]", @@ -503,18 +501,28 @@ async fn accept_invoice( return response::bad_request(&msg); } + if amount_to_pay > BigDecimal::from(0) { + match db + .as_dao::() + .spend_from_allocation_transaction(SpendFromAllocationArgs { + owner_id: node_id, + allocation_id: allocation_id.clone(), + agreement_id: agreement_id.clone(), + activity_id: None, + amount: amount_to_pay.clone(), + }) + .await + { + Ok(_) => (), + Err(e) => return response::server_error(&e), + } + } + let timeout = query.timeout.unwrap_or(params::DEFAULT_ACK_TIMEOUT); let result = async move { let issuer_id = invoice.issuer_id; let accept_msg = AcceptInvoice::new(invoice_id.clone(), acceptance, issuer_id); - let schedule_msg = SchedulePayment::from_invoice(invoice, allocation_id, amount_to_pay); match async move { - // Schedule payment (will be none for amount=0, which is OK) - if let Some(msg) = schedule_msg { - log::trace!("Calling SchedulePayment [{}] locally", invoice_id); - bus::service(LOCAL_SERVICE).send(msg).await??; - } - // Mark the invoice as accepted in DB log::trace!("Accepting Invoice [{}] in DB", invoice_id); dao.accept(invoice_id.clone(), node_id).await?; diff --git a/core/payment/src/api/pay_activities.rs b/core/payment/src/api/pay_activities.rs new file mode 100644 index 0000000000..4316750466 --- /dev/null +++ b/core/payment/src/api/pay_activities.rs @@ -0,0 +1,199 @@ +use std::collections::HashMap; +// External crates +use crate::dao::*; +use crate::models::debit_note::DebitNoteForApi; +use crate::utils::*; +use actix_web::web::{get, Data, Path, Query}; +use actix_web::{HttpResponse, Scope}; +use anyhow::anyhow; +use ya_client_model::payment::params; +use ya_persistence::executor::DbExecutor; +use ya_service_api_web::middleware::Identity; + +pub fn register_endpoints(scope: Scope) -> Scope { + scope + .route("/payActivities", get().to(get_pay_activities)) + .route("/payActivities/{activity_id}", get().to(get_pay_activity)) + .route( + "/payActivities/{activity_id}/debitNotes", + get().to(get_activity_debit_notes), + ) + .route( + "/payActivities/{activity_id}/invoice", + get().to(get_activity_invoice), + ) + .route( + "/payActivities/{activity_id}/orders", + get().to(get_pay_activity_orders), + ) +} + +async fn get_pay_activities( + db: Data, + query: Query, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let dao: ActivityDao = db.as_dao(); + let after_timestamp = query.after_timestamp.map(|d| d.naive_utc()); + let max_items = query.max_items; + match dao + .get_for_node_id(node_id, after_timestamp, max_items) + .await + { + Ok(activities) => response::ok(activities), + Err(e) => response::server_error(&e), + } +} + +async fn get_pay_activity(db: Data, path: Path, id: Identity) -> HttpResponse { + let node_id = id.identity; + let activity_id = path.into_inner(); + let dao: ActivityDao = db.as_dao(); + match dao.get(activity_id, node_id).await { + Ok(activity) => response::ok(activity), + Err(e) => response::server_error(&e), + } +} + +pub async fn get_debit_note_chain( + debit_list: Vec, +) -> Result, anyhow::Error> { + if debit_list.is_empty() { + return Ok(Vec::new()); + } + let mut debit_note_chain = Vec::::new(); + let mut debit_by_id = HashMap::new(); + let mut debit_by_prev_id = HashMap::new(); + + for debit in debit_list.iter() { + if debit_by_id + .insert(debit.debit_note_id.clone(), debit.clone()) + .is_some() + { + return Err(anyhow!( + "Duplicate debit note with id {}", + debit.debit_note_id + )); + } + if let Some(prev_id) = &debit.previous_debit_note_id { + if debit_by_prev_id + .insert(prev_id.clone(), debit.clone()) + .is_some() + { + return Err(anyhow!("Duplicate debit note with previous id {}", prev_id)); + } + } + } + //find debit note that is not a previous debit note + let mut not_previous_list: Vec = Vec::new(); + for debit in debit_list.iter() { + if !debit_by_prev_id.contains_key(&debit.debit_note_id) { + not_previous_list.push(debit.clone()); + break; + } + } + if not_previous_list.len() > 1 { + return Err(anyhow!( + "Expected exactly one debit note with no previous debit note, found {}", + not_previous_list.len() + )); + } + + let start_debit_note = not_previous_list + .into_iter() + .next() + .ok_or(anyhow!("Debit note with no previous debit note not found"))?; + + debit_note_chain.push(start_debit_note.clone()); + let mut prev_debit_note_id = start_debit_note.previous_debit_note_id.clone(); + while let Some(next_debit_note_id) = &prev_debit_note_id { + let next_debit_note = match debit_by_id.get(next_debit_note_id) { + Some(debit_note) => debit_note.clone(), + None => { + return Err(anyhow!( + "Debit note {} not found when building debit note chain", + next_debit_note_id + )) + } + }; + + debit_note_chain.push(next_debit_note.clone()); + prev_debit_note_id = next_debit_note.previous_debit_note_id.clone(); + } + Ok(debit_note_chain) +} + +async fn get_activity_invoice( + db: Data, + path: Path, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let activity_id = path.into_inner(); + let dao: ActivityDao = db.as_dao(); + let Some(activity) = (match dao.get(activity_id, node_id).await { + Ok(activity) => activity, + Err(e) => return response::server_error(&e), + }) else { + return response::server_error(&"Activity not found"); + }; + + let dao: InvoiceDao = db.as_dao(); + dao.get_by_agreement(activity.agreement_id, node_id) + .await + .map(response::ok) + .unwrap_or_else(|e| response::server_error(&e)) +} + +async fn get_activity_debit_notes( + db: Data, + path: Path, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let activity_id = path.into_inner(); + let dao: ActivityDao = db.as_dao(); + let Some(activity) = (match dao.get(activity_id, node_id).await { + Ok(activity) => activity, + Err(e) => return response::server_error(&e), + }) else { + return response::server_error(&"Activity not found"); + }; + + let dao: DebitNoteDao = db.as_dao(); + let chain = match dao + .list(Some(activity.role), None, None, Some(activity.id)) + .await + { + Ok(chain) => chain, + Err(e) => return response::server_error(&e), + }; + get_debit_note_chain(chain) + .await + .map(response::ok) + .unwrap_or_else(|e| response::server_error(&e)) +} + +async fn get_pay_activity_orders( + db: Data, + path: Path, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let activity_id = path.into_inner(); + let dao: BatchDao = db.as_dao(); + match dao + .get_batch_items( + node_id, + BatchItemFilter { + activity_id: Some(activity_id), + ..Default::default() + }, + ) + .await + { + Ok(items) => response::ok(items), + Err(e) => response::server_error(&e), + } +} diff --git a/core/payment/src/api/pay_agreements.rs b/core/payment/src/api/pay_agreements.rs new file mode 100644 index 0000000000..7f585b47a3 --- /dev/null +++ b/core/payment/src/api/pay_agreements.rs @@ -0,0 +1,87 @@ +// External crates +use crate::dao::*; +use crate::utils::*; +use actix_web::web::{get, Data, Path, Query}; +use actix_web::{HttpResponse, Scope}; +use ya_client_model::payment::params; +use ya_persistence::executor::DbExecutor; +use ya_service_api_web::middleware::Identity; + +pub fn register_endpoints(scope: Scope) -> Scope { + scope + .route("/payAgreements", get().to(get_pay_agreements)) + .route("/payAgreements/{agreement_id}", get().to(get_pay_agreement)) + .route( + "/payAgreements/{agreement_id}/activities", + get().to(get_pay_agreement_activities), + ) + .route( + "/payAgreements/{agreement_id}/orders", + get().to(get_pay_agreement_orders), + ) +} + +async fn get_pay_agreements( + db: Data, + query: Query, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let dao: AgreementDao = db.as_dao(); + let after_timestamp = query.after_timestamp.map(|d| d.naive_utc()); + let max_items = query.max_items; + match dao + .get_for_node_id(node_id, after_timestamp, max_items) + .await + { + Ok(agreements) => response::ok(agreements), + Err(e) => response::server_error(&e), + } +} + +async fn get_pay_agreement(db: Data, path: Path, id: Identity) -> HttpResponse { + let node_id = id.identity; + let agreement_id = path.into_inner(); + let dao: AgreementDao = db.as_dao(); + match dao.get(agreement_id, node_id).await { + Ok(agreement) => response::ok(agreement), + Err(e) => response::server_error(&e), + } +} + +async fn get_pay_agreement_activities( + db: Data, + path: Path, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let agreement_id = path.into_inner(); + let dao: ActivityDao = db.as_dao(); + match dao.list(None, Some(agreement_id)).await { + Ok(activities) => response::ok(activities), + Err(e) => response::server_error(&e), + } +} + +async fn get_pay_agreement_orders( + db: Data, + path: Path, + id: Identity, +) -> HttpResponse { + let node_id = id.identity; + let agreement_id = path.into_inner(); + let dao: BatchDao = db.as_dao(); + match dao + .get_batch_items( + node_id, + BatchItemFilter { + agreement_id: Some(agreement_id), + ..Default::default() + }, + ) + .await + { + Ok(items) => response::ok(items), + Err(e) => response::server_error(&e), + } +} diff --git a/core/payment/src/batch.rs b/core/payment/src/batch.rs new file mode 100644 index 0000000000..da0a1529a4 --- /dev/null +++ b/core/payment/src/batch.rs @@ -0,0 +1,196 @@ +use crate::dao::BatchDao; +use crate::models::batch::DbBatchOrderItemFullInfo; +use crate::timeout_lock::MutexTimeoutExt; +use bigdecimal::{BigDecimal, Zero}; +use lazy_static::lazy_static; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use ya_client_model::payment::allocation::Deposit; +use ya_client_model::NodeId; +use ya_core_model::driver::{driver_bus_id, SchedulePayment}; +use ya_core_model::payment::local::ProcessPaymentsError; +use ya_persistence::executor::DbExecutor; +use ya_service_bus::timeout::IntoTimeoutFuture; +use ya_service_bus::typed as bus; + +fn get_order_lock(order_id: &str) -> Arc> { + lazy_static! { + static ref ORDER_LOCKS: Arc>>>> = + Arc::new(Mutex::new(HashMap::new())); + } + + ORDER_LOCKS + .lock() + .unwrap() + .entry(order_id.to_string()) + .or_default() + .clone() +} + +async fn send_deposit_payments( + db: Arc>, + owner: NodeId, + order_id: &str, + deposit_items: Vec, +) -> anyhow::Result<()> { + //Send payments from deposits + let bus_id = driver_bus_id("erc20"); + + for item in deposit_items { + let deposit = item.deposit.ok_or(anyhow::anyhow!( + "deposit is missing in send_deposit_payments, probably logic error in application code" + ))?; + + let deposit = serde_json::from_str::(&deposit) + .map_err(|err| anyhow::anyhow!("failed to parse deposit: {:?}", err))?; + + let payment_order_id = bus::service(&bus_id) + .call(SchedulePayment::new( + item.amount.0, + item.payer_addr.clone(), + item.payee_addr.clone(), + item.platform.clone(), + Some(deposit), + chrono::Utc::now(), + )) + .await??; + + { + let db_executor = db + .timeout_lock(crate::processor::DB_LOCK_TIMEOUT) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!( + "Db timeout lock when sending payments {err}" + )) + })?; + db_executor + .as_dao::() + .batch_order_item_send( + order_id.to_string(), + owner, + item.payee_addr, + item.allocation_id, + payment_order_id, + ) + .await?; + } + } + Ok(()) +} + +pub async fn send_batch_payments( + db: Arc>, + owner: NodeId, + order_id: &str, +) -> anyhow::Result<()> { + // Whole operation send payments should be in transaction, but it's not possible because we have to send messages via GSB. + // So we have to lock order_id to prevent sending payments for the same order in parallel. + // It's not perfect but at least it gives some hopes that data stays consistent. + + { + //_lock has to stay in scope to keep the lock + let order_lock = get_order_lock(order_id); + let _lock = order_lock + .lock() + .timeout(Some(Duration::from_secs(60))) + .await + .map_err(|err| { + anyhow::anyhow!( + "failed to acquire lock for order_id: {:?}, err: {:?}", + order_id, + err + ) + })?; + + let items = { + let db_executor = db + .timeout_lock(crate::processor::DB_LOCK_TIMEOUT) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!( + "Db timeout lock when sending payments {err}" + )) + })?; + db_executor + .as_dao::() + .get_unsent_batch_items(owner, order_id.to_string()) + .await? + }; + + //group items without deposit + let mut map: HashMap> = HashMap::new(); + //the rest of items go to deposit_items + let mut deposit_items = Vec::new(); + + for item in items { + if item.deposit.is_none() { + map.entry(BatchOrderItemKey { + order_id: item.order_id.clone(), + platform: item.platform.clone(), + owner_id: item.owner_id.clone(), + payer_addr: item.payer_addr.clone(), + payee_addr: item.payee_addr.clone(), + }) + .or_default() + .push(item); + } else { + deposit_items.push(item); + } + } + send_deposit_payments(db.clone(), owner, order_id, deposit_items).await?; + let bus_id = driver_bus_id("erc20"); + + for (key, items) in map { + //sum item amount + let mut payment_group_amount = BigDecimal::zero(); + for item in &items { + payment_group_amount += item.amount.0.clone(); + } + + let payment_order_id = bus::service(&bus_id) + .call(SchedulePayment::new( + payment_group_amount, + key.payer_addr.clone(), + key.payee_addr.clone(), + key.platform.clone(), + None, + chrono::Utc::now(), + )) + .await??; + for item in items { + let db_executor = db + .timeout_lock(crate::processor::DB_LOCK_TIMEOUT) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!( + "Db timeout lock when sending payments {err}" + )) + })?; + db_executor + .as_dao::() + .batch_order_item_send( + order_id.to_string(), + owner, + key.payee_addr.clone(), + item.allocation_id, + payment_order_id.clone(), + ) + .await?; + } + } + + //End of _lock + } + Ok(()) +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct BatchOrderItemKey { + pub order_id: String, + pub platform: String, + pub owner_id: String, + pub payer_addr: String, + pub payee_addr: String, +} diff --git a/core/payment/src/cli.rs b/core/payment/src/cli.rs index 70a0984bd5..cd9a3f36e4 100644 --- a/core/payment/src/cli.rs +++ b/core/payment/src/cli.rs @@ -1,21 +1,21 @@ mod rpc; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; // External crates use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; -use serde_json::{to_value, Value}; +use serde_json::{json, to_value, Value}; use std::str::FromStr; -use std::time::UNIX_EPOCH; +use std::time::{Duration, UNIX_EPOCH}; use structopt::*; use ya_client_model::payment::{DriverDetails, DriverStatusProperty}; -use ya_core_model::payment::local::NetworkName; +use ya_client_model::NodeId; +use ya_core_model::payment::local::{NetworkName, ProcessBatchCycleResponse}; // Workspace uses use ya_core_model::{identity as id_api, payment::local as pay}; use ya_service_api::{CliCtx, CommandOutput, ResponseTable}; use ya_service_bus::{typed as bus, RpcEndpoint}; - // Local uses use crate::accounts::{init_account, Account}; use crate::cli::rpc::{run_command_rpc, RpcCommandParams}; @@ -138,6 +138,10 @@ pub enum PaymentCli { #[structopt(subcommand)] command: InvoiceCommand, }, + Process { + #[structopt(subcommand)] + command: ProcessCommand, + }, /// Clear all existing allocations ReleaseAllocations, @@ -151,6 +155,52 @@ pub enum InvoiceCommand { }, } +#[derive(StructOpt, Debug)] +pub enum ProcessCommand { + Now { + #[structopt(flatten)] + account: pay::AccountCli, + }, + Info { + /// Wallet address [default: ] + #[structopt(long, env = "YA_ACCOUNT")] + account: Option, + }, + Set { + #[structopt(flatten)] + account: pay::AccountCli, + #[structopt(long, help = "Set interval")] + interval: Option, + #[structopt(long, help = "Set safe payout")] + payout: Option, + #[structopt(long, help = "Set cron")] + cron: Option, + #[structopt( + long, + help = "Optionally set the next process time (if lower than interval)" + )] + next: Option>, + }, +} + +fn round_duration_to_sec(d: Duration) -> Duration { + //0.500 gives 1.0 + //0.499 gives 0.0 + let secs = ((d.as_millis() + 500) / 1000) as u64; + Duration::from_secs(secs) +} + +fn round_duration_to_sec_chrono(d: chrono::Duration) -> Duration { + //0.500 gives 1.0 + //0.499 gives 0.0 + let secs = ((d.num_milliseconds() + 500) / 1000) as u64; + Duration::from_secs(secs) +} + +fn round_duration_humantime(d: chrono::Duration) -> String { + humantime::format_duration(round_duration_to_sec_chrono(d)).to_string() +} + impl PaymentCli { pub async fn run_command(self, ctx: &CliCtx) -> anyhow::Result { match self { @@ -414,6 +464,269 @@ Typically operation should take less than 1 minute. .await??, ) } + PaymentCli::Process { command } => { + match command { + ProcessCommand::Now { account } => { + //return Ok(CommandOutput::Object(json!({"res":"Process command now"}))); + let node_id = if let Some(node_id) = account.account { + node_id + } else { + match bus::service(id_api::BUS_ID) + .send(id_api::Get::ByDefault) + .await?? + { + None => { + log::error!("Default identity not found"); + panic!("Default identity not found"); + } + Some(node_id) => node_id.node_id, + } + }; + + let driver_status_props = bus::service(pay::BUS_ID) + .call(pay::ProcessPaymentsNow { + node_id, + platform: format!( + "{}-{}-{}", + account.driver(), + account.network(), + account.token() + ) + .to_lowercase(), + skip_resolve: false, + skip_send: false, + }) + .await??; + Ok(CommandOutput::object(driver_status_props) + .expect("Failed to create object")) + } + ProcessCommand::Set { + account, + interval, + cron, + next, + payout, + } => { + let node_id = if let Some(node_id) = account.account { + node_id + } else { + match bus::service(id_api::BUS_ID) + .send(id_api::Get::ByDefault) + .await?? + { + None => { + log::error!("Default identity not found"); + panic!("Default identity not found"); + } + Some(node_id) => node_id.node_id, + } + }; + + let platform = format!( + "{}-{}-{}", + account.driver(), + account.network(), + account.token() + ) + .to_lowercase(); + let driver_status_props = bus::service(pay::BUS_ID) + .call(pay::ProcessBatchCycleSet { + node_id, + platform: platform.clone(), + interval: interval.map(|d| d.into()), + cron, + next_update: next, + safe_payout: payout.map(|d| d.into()), + }) + .await??; + + let mut values = Vec::new(); + + let result = driver_status_props; + let next_process_in = + Utc::now().signed_duration_since(result.next_process.and_utc()); + + let next_process_descr = format!( + "{}\n(in {})", + result.next_process.format("%F %T"), + round_duration_humantime(next_process_in.abs()) + ); + let last_process_descr = result + .last_process + .map(|l| { + format!( + "{}\n({} ago)", + l.format("%F %T"), + round_duration_humantime( + Utc::now().signed_duration_since(l.and_utc()) + ) + ) + }) + .unwrap_or("NULL".to_string()); + values.push(json!([ + platform, + result + .interval + .map(|d| humantime::format_duration(d).to_string()) + .unwrap_or("NULL".to_string()), + result.cron, + humantime::format_duration(round_duration_to_sec(result.max_interval)) + .to_string(), + humantime::format_duration(round_duration_to_sec( + result.extra_payment_time + )) + .to_string(), + next_process_descr, + last_process_descr, + ])); + Ok(CommandOutput::Table { + columns: [ + "Platform", + "Interval", + "Cron", + "Max interval", + "Extra time", + "Next process", + "Last processed", + ] + .iter() + .map(ToString::to_string) + .collect(), + values, + summary: vec![json!(["", "", "", "", ""])], + header: Some(format!( + "Batch cycle after change: {}", + account + .account + .map(|a| a.to_string()) + .unwrap_or("default".to_string()) + )), + }) + } + ProcessCommand::Info { account } => { + let drivers = bus::service(pay::BUS_ID) + .call(pay::GetDrivers { + ignore_legacy_networks: true, + }) + .await??; + + let node_id = if let Some(node_id) = account { + node_id + } else { + match bus::service(id_api::BUS_ID) + .send(id_api::Get::ByDefault) + .await?? + { + None => { + log::error!("Default identity not found"); + panic!("Default identity not found"); + } + Some(node_id) => node_id.node_id, + } + }; + + let mut results = BTreeMap::::new(); + + for driver in drivers { + for network in driver.1.networks { + let platform = format!( + "{}-{}-{}", + driver.0, network.0, network.1.default_token + ) + .to_lowercase(); + + let driver_status_props = bus::service(pay::BUS_ID) + .call(pay::ProcessBatchCycleInfo { + node_id, + platform: platform.clone(), + }) + .await??; + + results.insert(platform, driver_status_props); + } + } + + if ctx.json_output { + CommandOutput::object( results.iter().map( |(platform, result)| + json!({ + "platform": platform, + "intervalSec": result.interval.map(|d| d.as_secs()), + "cron": result.cron, + "maxIntervalSec": round_duration_to_sec(result.max_interval).as_secs(), + "extraPaymentTimeSec": round_duration_to_sec(result.extra_payment_time).as_secs(), + "nextProcess": result.next_process.and_utc().to_rfc3339(), + "lastProcess": result.last_process.map(|l| l.and_utc().to_rfc3339()), + } + )).collect::>()) + } else { + let mut values = Vec::new(); + + for (platform, result) in results { + let next_process_in = + Utc::now().signed_duration_since(result.next_process.and_utc()); + + let next_process_descr = format!( + "{}\n(in {})", + result.next_process.format("%F %T"), + round_duration_humantime(next_process_in.abs()) + ); + let last_process_descr = result + .last_process + .map(|l| { + format!( + "{}\n({} ago)", + l.format("%F %T"), + round_duration_humantime( + Utc::now().signed_duration_since(l.and_utc()) + ) + ) + }) + .unwrap_or("NULL".to_string()); + values.push(json!([ + platform, + result + .interval + .map(|d| humantime::format_duration(d).to_string()) + .unwrap_or("NULL".to_string()), + result.cron, + humantime::format_duration(round_duration_to_sec( + result.max_interval + )) + .to_string(), + humantime::format_duration(round_duration_to_sec( + result.extra_payment_time + )) + .to_string(), + next_process_descr, + last_process_descr, + ])); + } + Ok(CommandOutput::Table { + columns: [ + "Platform", + "Interval", + "Cron", + "Max interval", + "Extra time", + "Next process", + "Last processed", + ] + .iter() + .map(ToString::to_string) + .collect(), + values, + summary: vec![json!(["", "", "", "", ""])], + header: Some(format!( + "Batch cycle info {}", + account + .map(|a| a.to_string()) + .unwrap_or("default".to_string()) + )), + }) + } + } + } + } PaymentCli::Enter { account, amount } => CommandOutput::object( wallet::enter( BigDecimal::from_str(&amount)?, diff --git a/core/payment/src/cycle.rs b/core/payment/src/cycle.rs new file mode 100644 index 0000000000..53b8816324 --- /dev/null +++ b/core/payment/src/cycle.rs @@ -0,0 +1,175 @@ +use chrono::{DateTime, Utc}; +use std::sync::{Arc, Mutex}; +use tokio::sync::Notify; +use ya_client_model::NodeId; +use ya_core_model::payment::local as pay_local; +use ya_service_bus::{typed as bus, RpcEndpoint}; + +pub struct BatchCycleTaskManager { + platforms: Vec, + owners: Vec, + tasks: Vec, +} +impl BatchCycleTaskManager { + pub fn new() -> Self { + BatchCycleTaskManager { + platforms: Vec::new(), + owners: Vec::new(), + tasks: Vec::new(), + } + } + + pub fn add_owner(&mut self, owner_id: NodeId) { + log::info!("Adding owner: {}", owner_id); + self.owners.push(owner_id); + self.start_tasks_if_not_started(); + } + pub fn add_platform(&mut self, platform: String) { + log::info!("Adding platform: {}", platform); + self.platforms.push(platform); + self.start_tasks_if_not_started(); + } + + pub fn wake_owner_platform(&self, owner_id: NodeId, platform: String) { + for task in self.tasks.iter() { + if task.node_id == owner_id && task.platform == platform { + task.waker.notify_waiters(); + } + } + } + + fn start_tasks_if_not_started(&mut self) { + for owner_id in &self.owners { + for platform in &self.platforms { + if self + .tasks + .iter() + .any(|t| t.platform == *platform && t.node_id == *owner_id) + { + continue; + } + self.tasks + .push(BatchCycleTask::new(*owner_id, platform.clone())); + } + } + } + + async fn stop_tasks(&mut self) { + for task in self.tasks.iter() { + *task.finish.lock().unwrap() = true; + task.waker.notify_waiters(); + } + for task in self.tasks.drain(..) { + task.handle.await.unwrap(); + } + } +} + +pub struct BatchCycleTask { + node_id: NodeId, + platform: String, + waker: Arc, + finish: Arc>, + handle: tokio::task::JoinHandle<()>, +} + +pub async fn sleep_after_error() { + tokio::time::sleep(std::time::Duration::from_secs(5)).await; +} + +impl BatchCycleTask { + pub fn new(node_id: NodeId, platform: String) -> Self { + let waker = Arc::new(Notify::new()); + let finish = Arc::new(Mutex::new(false)); + BatchCycleTask { + node_id, + platform: platform.clone(), + waker: waker.clone(), + finish: finish.clone(), + handle: tokio::spawn(async move { + log::info!( + "Starting batch cycle task for owner_id: {}, platform: {}", + node_id, + platform + ); + let mut next_process: Option> = None; + loop { + let now = Utc::now(); + if let Some(next_process) = next_process { + if next_process > now { + let diff = (next_process - now).num_milliseconds(); + //do not sleep for more than 5 minutes + let diff = std::cmp::min(diff, 1000 * 60 * 5); + if diff > 0 { + log::debug!( + "Sleeping for {} before next process for owner_id: {}, platform: {}", + humantime::format_duration(std::time::Duration::from_secs_f64((diff as f64 / 1000.0).round())), + node_id, + platform + ); + tokio::select! { + _ = tokio::time::sleep(std::time::Duration::from_millis(diff as u64)) => {} + _ = waker.notified() => {}, + } + } + } else { + match bus::service(pay_local::BUS_ID) + .send(pay_local::ProcessPaymentsNow { + node_id, + platform: platform.clone(), + skip_resolve: false, + skip_send: false, + }) + .await + { + Ok(Ok(_)) => {} + Ok(Err(e)) => { + log::error!("Failed to process payments now: {:?}", e); + // prevent busy loop + sleep_after_error().await; + } + Err(e) => { + log::error!("Failed to send process payments now: {:?}", e); + // prevent busy loop + sleep_after_error().await; + } + } + } + } + if *finish.lock().unwrap() { + break; + } + + let info = match bus::service(pay_local::BUS_ID) + .send(pay_local::ProcessBatchCycleInfo { + node_id, + platform: platform.clone(), + }) + .await + { + Ok(Ok(info)) => info, + Ok(Err(e)) => { + log::error!("Failed to get batch cycle info: {:?}", e); + // prevent busy loop + sleep_after_error().await; + continue; + } + Err(e) => { + log::error!("Failed to send batch cycle info: {:?}", e); + // prevent busy loop + sleep_after_error().await; + continue; + } + }; + + next_process = Some(info.next_process.and_utc()); + } + log::info!( + "Batch cycle task finished for owner_id: {}, platform: {}", + node_id, + platform + ); + }), + } + } +} diff --git a/core/payment/src/dao.rs b/core/payment/src/dao.rs index f694c9f650..592faaafdc 100644 --- a/core/payment/src/dao.rs +++ b/core/payment/src/dao.rs @@ -1,11 +1,12 @@ mod activity; mod agreement; mod allocation; +mod batch; +mod cycle; mod debit_note; mod debit_note_event; mod invoice; mod invoice_event; -mod order; mod payment; mod sync_notifs; @@ -14,10 +15,14 @@ pub use self::agreement::AgreementDao; pub use self::allocation::AllocationDao; pub use self::allocation::AllocationReleaseStatus; pub use self::allocation::AllocationStatus; +pub use self::allocation::{spend_from_allocation, SpendFromAllocationArgs}; +pub use self::batch::{BatchDao, BatchItemFilter}; +pub use self::cycle::{ + BatchCycleDao, PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME, PAYMENT_CYCLE_DEFAULT_INTERVAL, +}; pub use self::debit_note::DebitNoteDao; pub use self::debit_note_event::DebitNoteEventDao; pub use self::invoice::InvoiceDao; pub use self::invoice_event::InvoiceEventDao; -pub use self::order::OrderDao; pub use self::payment::PaymentDao; pub use self::sync_notifs::SyncNotifsDao; diff --git a/core/payment/src/dao/activity.rs b/core/payment/src/dao/activity.rs index f2bb51932b..0ce02e184e 100644 --- a/core/payment/src/dao/activity.rs +++ b/core/payment/src/dao/activity.rs @@ -1,14 +1,16 @@ use crate::dao::{agreement, debit_note, debit_note_event}; -use crate::error::DbResult; +use crate::error::{DbError, DbResult}; use crate::models::activity::{ReadObj, WriteObj}; use crate::schema::pay_activity::dsl; use crate::schema::pay_agreement::dsl as agreement_dsl; use crate::schema::pay_debit_note::dsl as debit_note_dsl; use bigdecimal::{BigDecimal, Zero}; +use chrono::NaiveDateTime; use diesel::{ BoolExpressionMethods, ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl, RunQueryDsl, }; use std::collections::HashMap; +use std::str::FromStr; use ya_client_model::payment::{DebitNoteEventType, DocumentStatus}; use ya_client_model::NodeId; use ya_persistence::executor::{ @@ -75,7 +77,7 @@ pub fn increase_amount_scheduled( pub fn increase_amount_paid( activity_id: &String, - owner_id: &NodeId, + owner_id: NodeId, amount: &BigDecimalField, conn: &ConnType, ) -> DbResult<()> { @@ -89,23 +91,42 @@ pub fn increase_amount_paid( .set(dsl::total_amount_paid.eq(&total_amount_paid)) .execute(conn)?; - let debit_note_ids: Vec = debit_note_dsl::pay_debit_note + //extract all debit notes for this activity + //check if the total amount paid is equal to the total amount due + //we cannot do that in sql due to lack of decimal support in sqlite + let debit_note_ids = debit_note_dsl::pay_debit_note .filter(debit_note_dsl::activity_id.eq(activity_id)) .filter(debit_note_dsl::owner_id.eq(owner_id)) .filter(debit_note_dsl::status.ne_all(vec![ DocumentStatus::Cancelled.to_string(), DocumentStatus::Settled.to_string(), ])) - .filter(debit_note_dsl::total_amount_due.le(&total_amount_paid)) - .select(debit_note_dsl::id) - .load(conn)?; + .select((debit_note_dsl::id, debit_note_dsl::total_amount_due)) + .load::<(String, String)>(conn)? + .iter() + .filter_map(|(debit_note_id, total_amount_due)| { + match BigDecimal::from_str(total_amount_due) { + Ok(d) => { + if total_amount_paid.0 >= d { + Some(Ok(debit_note_id.clone())) + } else { + None + } + } + Err(e) => Some(Err(DbError::Query(format!( + "Error parsing decimal in debit note: {}", + e + )))), + } + }) + .collect::, _>>()?; - debit_note::update_status(&debit_note_ids, owner_id, &DocumentStatus::Settled, conn)?; + debit_note::update_status(&debit_note_ids, owner_id, DocumentStatus::Settled, conn)?; for debit_note_id in debit_note_ids { debit_note_event::create( debit_note_id, - *owner_id, + owner_id, DebitNoteEventType::DebitNoteSettledEvent, conn, )?; @@ -156,6 +177,8 @@ impl<'a> ActivityDao<'a> { dsl::total_amount_accepted, dsl::total_amount_scheduled, dsl::total_amount_paid, + dsl::created_ts, + dsl::updated_ts, agreement_dsl::peer_id, agreement_dsl::payee_addr, agreement_dsl::payer_addr, @@ -169,6 +192,46 @@ impl<'a> ActivityDao<'a> { .await } + pub async fn list( + &self, + role: Option, + agreement_id: Option, + ) -> DbResult> { + readonly_transaction(self.pool, "pay_activity_dao_list", move |conn| { + let mut query = dsl::pay_activity.into_boxed(); + if let Some(agreement_id) = agreement_id { + query = query.filter(dsl::agreement_id.eq(agreement_id)); + }; + if let Some(role) = role { + query = query.filter(dsl::role.eq(role)); + }; + let activities = query.load(conn)?; + Ok(activities.into_iter().collect()) + }) + .await + } + pub async fn get_for_node_id( + &self, + node_id: NodeId, + after_timestamp: Option, + max_items: Option, + ) -> DbResult> { + readonly_transaction(self.pool, "activity_dao_get_for_node_id", move |conn| { + let mut query = dsl::pay_activity.into_boxed(); + query = query.filter(dsl::owner_id.eq(node_id)); + if let Some(date) = after_timestamp { + query = query.filter(dsl::created_ts.gt(date)) + } + query = query.order_by(dsl::created_ts.asc()); + if let Some(items) = max_items { + query = query.limit(items.into()) + } + let activities: Vec = query.load(conn)?; + Ok(activities) + }) + .await + } + pub async fn create_if_not_exists( &self, id: String, diff --git a/core/payment/src/dao/agreement.rs b/core/payment/src/dao/agreement.rs index e494baded4..3feba03b21 100644 --- a/core/payment/src/dao/agreement.rs +++ b/core/payment/src/dao/agreement.rs @@ -7,10 +7,14 @@ use crate::schema::pay_invoice::dsl as invoice_dsl; use bigdecimal::BigDecimal; use chrono::NaiveDateTime; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; +use std::error::Error; +use std::fmt; +use std::str::FromStr; use ya_client_model::market::Agreement; use ya_client_model::payment::{DocumentStatus, InvoiceEventType}; use ya_client_model::NodeId; use ya_core_model::payment::local::{StatValue, StatusNotes}; + use ya_persistence::executor::{ do_with_transaction, readonly_transaction, AsDao, ConnType, PoolType, }; @@ -50,7 +54,7 @@ pub fn set_amount_due( Ok(()) } -/// Compute and set amount due based on activities +/// Compute and set amount due based on agreements pub fn compute_amount_due( agreement_id: &String, owner_id: &NodeId, @@ -106,7 +110,7 @@ pub fn increase_amount_scheduled( pub fn set_amount_accepted( agreement_id: &String, - owner_id: &NodeId, + owner_id: NodeId, total_amount_accepted: &BigDecimalField, conn: &ConnType, ) -> DbResult<()> { @@ -119,9 +123,29 @@ pub fn set_amount_accepted( Ok(()) } +#[derive(Debug)] +struct IncreaseAmountPaidError { + details: String, +} +impl IncreaseAmountPaidError { + fn new(msg: String) -> IncreaseAmountPaidError { + IncreaseAmountPaidError { details: msg } + } +} +impl fmt::Display for IncreaseAmountPaidError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.details) + } +} +impl Error for IncreaseAmountPaidError { + fn description(&self) -> &str { + &self.details + } +} + pub fn increase_amount_paid( agreement_id: &String, - owner_id: &NodeId, + owner_id: NodeId, amount: &BigDecimalField, conn: &ConnType, ) -> DbResult<()> { @@ -134,26 +158,34 @@ pub fn increase_amount_paid( .set(dsl::total_amount_paid.eq(&total_amount_paid)) .execute(conn)?; - let invoice_query: Option<(String, Role)> = invoice_dsl::pay_invoice + //extract invoice for this activity + //check if the total amount paid is equal to the total amount due + //we cannot do that in sql due to lack of decimal support in sqlite + let invoice_query: Option<(String, String, Role)> = invoice_dsl::pay_invoice .filter(invoice_dsl::agreement_id.eq(agreement_id)) .filter(invoice_dsl::owner_id.eq(owner_id)) .filter(invoice_dsl::status.ne_all(vec![ DocumentStatus::Cancelled.to_string(), DocumentStatus::Settled.to_string(), ])) - .filter(invoice_dsl::amount.le(&total_amount_paid)) - .select((invoice_dsl::id, invoice_dsl::role)) + //.filter(invoice_dsl::amount.le(&total_amount_paid)) + .select((invoice_dsl::id, invoice_dsl::amount, invoice_dsl::role)) .first(conn) .optional()?; - if let Some((invoice_id, role)) = invoice_query { - invoice::update_status(&invoice_id, owner_id, &DocumentStatus::Settled, conn)?; - invoice_event::create( - invoice_id, - *owner_id, - InvoiceEventType::InvoiceSettledEvent, - conn, - )?; + if let Some((invoice_id, amount, role)) = invoice_query { + let invoice_amount = BigDecimal::from_str(&amount) + .map_err(|e| DbError::Query(format!("Failed to parse amount from invoice: {}", e)))?; + + if invoice_amount <= total_amount_paid.0 { + invoice::update_status(&invoice_id, owner_id, DocumentStatus::Settled, conn)?; + invoice_event::create( + invoice_id, + owner_id, + InvoiceEventType::InvoiceSettledEvent, + conn, + )?; + } } Ok(()) @@ -170,8 +202,16 @@ impl<'a> AsDao<'a> for AgreementDao<'a> { } impl<'a> AgreementDao<'a> { + /*pub async fn list(&self, role: Option) -> DbResult> { + readonly_transaction(self.pool, "pay_agreement_dao_list", move |conn| { + let agreements = dsl::pay_agreement.load(conn)?; + Ok(agreements.into_iter().collect()) + }) + .await + }*/ + pub async fn get(&self, agreement_id: String, owner_id: NodeId) -> DbResult> { - readonly_transaction(self.pool, "agreement_dao_get", move |conn| { + readonly_transaction(self.pool, "pay_agreement_dao_get", move |conn| { let agreement = dsl::pay_agreement .find((agreement_id, owner_id)) .first(conn) @@ -206,6 +246,28 @@ impl<'a> AgreementDao<'a> { .await } + pub async fn get_for_node_id( + &self, + node_id: NodeId, + after_timestamp: Option, + max_items: Option, + ) -> DbResult> { + readonly_transaction(self.pool, "agreement_dao_get_for_node_id", move |conn| { + let mut query = dsl::pay_agreement.into_boxed(); + query = query.filter(dsl::owner_id.eq(node_id)); + if let Some(date) = after_timestamp { + query = query.filter(dsl::created_ts.gt(date)) + } + query = query.order_by(dsl::created_ts.asc()); + if let Some(items) = max_items { + query = query.limit(items.into()) + } + let agreements: Vec = query.load(conn)?; + Ok(agreements) + }) + .await + } + pub async fn get_transaction_balance( &self, node_id: NodeId, diff --git a/core/payment/src/dao/allocation.rs b/core/payment/src/dao/allocation.rs index baf40d8e10..b415816e79 100644 --- a/core/payment/src/dao/allocation.rs +++ b/core/payment/src/dao/allocation.rs @@ -1,10 +1,13 @@ use crate::error::{DbError, DbResult}; -use crate::models::allocation::{ReadObj, WriteObj}; +use crate::models::allocation::{AllocationExpenditureObj, ReadObj, WriteObj}; use crate::schema::pay_allocation::dsl; +use crate::schema::pay_allocation_expenditure::dsl as dsld; use bigdecimal::BigDecimal; use chrono::NaiveDateTime; -use diesel::{self, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; -use ya_client_model::payment::allocation::Deposit; +use diesel::{ + self, BoolExpressionMethods, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, +}; +use ya_client_model::payment::allocation::{AllocationExpenditure, Deposit}; use ya_client_model::payment::{Allocation, NewAllocation}; use ya_client_model::NodeId; use ya_persistence::executor::{ @@ -22,30 +25,106 @@ impl<'c> AsDao<'c> for AllocationDao<'c> { } } -pub fn spend_from_allocation( - allocation_id: &String, - amount: &BigDecimalField, - conn: &ConnType, -) -> DbResult<()> { - let allocation: ReadObj = dsl::pay_allocation.find(allocation_id).first(conn)?; - if amount > &allocation.remaining_amount { +pub struct SpendFromAllocationArgs { + pub owner_id: NodeId, + pub allocation_id: String, + pub agreement_id: String, + pub activity_id: Option, + pub amount: BigDecimal, +} + +pub fn spend_from_allocation(conn: &ConnType, args: SpendFromAllocationArgs) -> DbResult<()> { + let allocation: ReadObj = dsl::pay_allocation + .find((args.owner_id, args.allocation_id.clone())) + .first(conn)?; + if args.amount > allocation.avail_amount.0 { return Err(DbError::Query(format!( "Not enough funds in allocation. Needed: {} Remaining: {}", - amount, allocation.remaining_amount + args.amount, allocation.avail_amount.0 ))); } - let spent_amount = &allocation.spent_amount + amount; - let remaining_amount = &allocation.remaining_amount - amount; - diesel::update(&allocation) + let spent_amount: BigDecimalField = (allocation.spent_amount.0 + &args.amount).into(); + let avail_amount: BigDecimalField = (allocation.avail_amount.0 - &args.amount).into(); + diesel::update(dsl::pay_allocation) .set(( dsl::spent_amount.eq(spent_amount), - dsl::remaining_amount.eq(remaining_amount), + dsl::avail_amount.eq(avail_amount), )) + .filter(dsl::id.eq(&args.allocation_id)) + .filter(dsl::owner_id.eq(args.owner_id)) .execute(conn)?; + + let query = dsld::pay_allocation_expenditure + .select(dsld::accepted_amount) + .filter(dsld::owner_id.eq(args.owner_id)) + .filter(dsld::allocation_id.eq(&args.allocation_id)) + .filter(dsld::agreement_id.eq(&args.agreement_id)) + .into_boxed(); + + let query = if let Some(activity_id) = &args.activity_id { + query.filter(dsld::activity_id.eq(activity_id)) + } else { + query.filter(dsld::activity_id.is_null()) + }; + + if let Some(accepted_amount) = query.first::(conn).optional()? { + let new_document_amount: BigDecimalField = (accepted_amount.0 + &args.amount).into(); + + let query = diesel::update(dsld::pay_allocation_expenditure) + .set(dsld::accepted_amount.eq(new_document_amount)) + .filter(dsld::owner_id.eq(args.owner_id)) + .filter(dsld::allocation_id.eq(&args.allocation_id)) + .filter(dsld::agreement_id.eq(&args.agreement_id)) + .into_boxed(); + + let query = if let Some(activity_id) = &args.activity_id { + query.filter(dsld::activity_id.eq(activity_id)) + } else { + query.filter(dsld::activity_id.is_null()) + }; + query.execute(conn)?; + } else { + diesel::insert_into(dsld::pay_allocation_expenditure) + .values(( + dsld::owner_id.eq(args.owner_id), + dsld::allocation_id.eq(&args.allocation_id), + dsld::agreement_id.eq(&args.agreement_id), + dsld::activity_id.eq(&args.activity_id), + dsld::accepted_amount.eq(BigDecimalField::from(args.amount)), + dsld::scheduled_amount.eq(BigDecimalField::default()), + )) + .execute(conn)?; + } + Ok(()) } impl<'c> AllocationDao<'c> { + pub async fn spend_from_allocation_transaction( + &self, + args: SpendFromAllocationArgs, + ) -> DbResult<()> { + do_with_transaction(self.pool, "spend_from_allocation_transaction", |conn| { + spend_from_allocation(conn, args) + }) + .await + } + + pub async fn get_expenditures( + &self, + owner_id: NodeId, + allocation_id: String, + ) -> DbResult> { + readonly_transaction(self.pool, "allocation_dao_get_expenditures", move |conn| { + let r: Vec = dsld::pay_allocation_expenditure + .filter(dsld::owner_id.eq(owner_id)) + .filter(dsld::allocation_id.eq(allocation_id)) + .load(conn)?; + Ok(r.into_iter().map(Into::into).collect()) + }) + .await + } + pub async fn create( &self, allocation: NewAllocation, @@ -83,7 +162,7 @@ impl<'c> AllocationDao<'c> { let allocation: Option = dsl::pay_allocation .filter(dsl::owner_id.eq(owner_id)) .filter(dsl::released.eq(false)) - .find(allocation_id) + .find((owner_id, allocation_id)) .first(conn) .optional()?; @@ -99,6 +178,79 @@ impl<'c> AllocationDao<'c> { .await } + pub async fn get_allocations_to_close( + &self, + owner_id: NodeId, + platform: String, + ) -> DbResult> { + readonly_transaction( + self.pool, + "allocation_dao_get_allocations_to_close", + move |conn| { + let allocations: Vec = dsl::pay_allocation + .filter( + dsl::owner_id + .eq(owner_id) + .and(dsl::released.eq(true)) + .and(dsl::deposit.is_not_null()) + .and(dsl::payment_platform.eq(platform)) + .and(dsl::deposit_status.eq("open")), + ) + .load(conn)?; + Ok(allocations.into_iter().map(Into::into).collect()) + }, + ) + .await + } + + pub async fn mark_allocation_closing( + &self, + allocation_id: String, + owner_id: NodeId, + ) -> DbResult { + do_with_transaction( + self.pool, + "allocation_dao_mark_allocation_closing", + move |conn| { + let count = diesel::update(dsl::pay_allocation) + .filter(dsl::id.eq(allocation_id.clone())) + .filter(dsl::owner_id.eq(owner_id)) + .filter(dsl::released.eq(true)) + .filter(dsl::deposit.is_not_null()) + .filter(dsl::deposit_status.eq("open")) + .set(dsl::deposit_status.eq("closing")) + .execute(conn)?; + + Ok(count == 1) + }, + ) + .await + } + + pub async fn mark_allocation_closed( + &self, + allocation_id: String, + owner_id: NodeId, + ) -> DbResult { + do_with_transaction( + self.pool, + "allocation_dao_mark_allocation_closed", + move |conn| { + let count = diesel::update(dsl::pay_allocation) + .filter(dsl::id.eq(allocation_id.clone())) + .filter(dsl::owner_id.eq(owner_id)) + .filter(dsl::released.eq(true)) + .filter(dsl::deposit.is_not_null()) + .filter(dsl::deposit_status.eq("closing")) + .set(dsl::deposit_status.eq("closed")) + .execute(conn)?; + + Ok(count == 1) + }, + ) + .await + } + pub async fn get_many( &self, allocation_ids: Vec, @@ -168,7 +320,7 @@ impl<'c> AllocationDao<'c> { query = query.filter(dsl::owner_id.eq(owner_id)) } if let Some(after_timestamp) = after_timestamp { - query = query.filter(dsl::timestamp.gt(after_timestamp)) + query = query.filter(dsl::timeout.gt(after_timestamp)) } if let Some(payment_platform) = payment_platform { query = query.filter(dsl::payment_platform.eq(payment_platform)) @@ -179,7 +331,7 @@ impl<'c> AllocationDao<'c> { if let Some(max_items) = max_items { query = query.limit(max_items.into()) } - let allocations: Vec = query.order_by(dsl::timestamp.asc()).load(conn)?; + let allocations: Vec = query.order_by(dsl::updated_ts.asc()).load(conn)?; Ok(allocations.into_iter().map(Into::into).collect()) }) .await @@ -188,21 +340,19 @@ impl<'c> AllocationDao<'c> { pub async fn release( &self, allocation_id: String, - owner_id: Option, + owner_id: NodeId, ) -> DbResult { let id = allocation_id.clone(); do_with_transaction(self.pool, "allocation_dao_release", move |conn| { let allocation: Option = dsl::pay_allocation - .find(id.clone()) + .find((owner_id, allocation_id.clone())) .first(conn) .optional()?; let (deposit, platform) = match allocation { Some(allocation) => { - if let Some(owner_id) = owner_id { - if owner_id != allocation.owner_id { - return Ok(AllocationReleaseStatus::NotFound); - } + if owner_id != allocation.owner_id { + return Ok(AllocationReleaseStatus::NotFound); } if allocation.released { @@ -244,11 +394,11 @@ impl<'c> AllocationDao<'c> { "allocation_dao_total_remaining_allocation", move |conn| { let total_remaining_amount = dsl::pay_allocation - .select(dsl::remaining_amount) + .select(dsl::avail_amount) .filter(dsl::payment_platform.eq(platform)) .filter(dsl::address.eq(address)) .filter(dsl::released.eq(false)) - .filter(dsl::timestamp.gt(after_timestamp)) + .filter(dsl::timeout.gt(after_timestamp)) .get_results::(conn)? .sum(); diff --git a/core/payment/src/dao/batch.rs b/core/payment/src/dao/batch.rs new file mode 100644 index 0000000000..9c81811b46 --- /dev/null +++ b/core/payment/src/dao/batch.rs @@ -0,0 +1,993 @@ +use bigdecimal::BigDecimal; +use chrono::{DateTime, NaiveDateTime, Utc}; +use diesel::prelude::*; +use diesel::sql_types::{Text, Timestamp}; +use std::collections::{hash_map, HashMap}; +use std::iter::zip; +use std::str::FromStr; +use uuid::Uuid; +use ya_core_model::NodeId; +use ya_persistence::executor::{ + do_with_transaction, readonly_transaction, AsDao, ConnType, PoolType, +}; +use ya_persistence::types::{AdaptTimestamp, BigDecimalField}; + +use crate::error::{DbError, DbResult}; +use crate::models::allocation::AllocationExpenditureObj; +use crate::models::batch::*; +use crate::schema::pay_allocation::dsl as padsl; +use crate::schema::pay_batch_order::dsl; +use crate::schema::pay_batch_order_item::dsl as oidsl; + +pub struct BatchDao<'c> { + pool: &'c PoolType, +} + +impl<'c> AsDao<'c> for BatchDao<'c> { + fn as_dao(pool: &'c PoolType) -> Self { + Self { pool } + } +} + +#[derive(Debug, Clone, Default)] +pub struct BatchItemFilter { + pub order_id: Option, + pub payee_addr: Option, + pub allocation_id: Option, + pub agreement_id: Option, + pub activity_id: Option, + pub payment_id: Option, +} + +table! { + sql_activity_join_agreement (id, owner_id) { + id -> Text, + owner_id -> Text, + role -> Text, + peer_id -> Text, + payee_addr -> Text, + agreement_id -> Text, + total_amount_due -> Text, + total_amount_accepted -> Text, + total_amount_scheduled -> Text, + total_amount_paid -> Text, + debit_note_id -> Nullable, + } +} + +#[derive(QueryableByName)] +#[table_name = "sql_activity_join_agreement"] +struct ActivityJoinAgreement { + id: String, + peer_id: NodeId, + payee_addr: String, + total_amount_accepted: BigDecimalField, + total_amount_scheduled: BigDecimalField, + agreement_id: String, + debit_note_id: Option, +} + +pub fn resolve_invoices_agreement_part( + args: &ResolveInvoiceArgs, + total_amount: BigDecimal, + payments: HashMap, +) -> DbResult<(HashMap, BigDecimal)> { + let conn = args.conn; + let owner_id = args.owner_id; + let payer_addr = args.payer_addr; + let platform = args.platform; + let since = args.since; + let mut total_amount = total_amount; + let mut payments = payments; + use crate::schema::pay_agreement::dsl as pa; + use crate::schema::pay_invoice::dsl as iv; + + let invoices = iv::pay_invoice + .inner_join( + pa::pay_agreement.on(pa::id + .eq(iv::agreement_id) + .and(pa::owner_id.eq(iv::owner_id))), + ) + .filter(iv::owner_id.eq(owner_id)) + .filter(iv::role.eq("R")) + .filter(pa::payer_addr.eq(payer_addr)) + .filter(pa::payment_platform.eq(platform)) + .filter(iv::timestamp.gt(since.naive_utc())) + .filter(iv::status.eq("ACCEPTED")) + .select(( + pa::id, + pa::peer_id, + pa::payee_addr, + pa::total_amount_accepted, + pa::total_amount_scheduled, + iv::id, + iv::amount, + )) + .load::<( + String, + NodeId, + String, + BigDecimalField, + BigDecimalField, + String, + BigDecimalField, + )>(conn)?; + if invoices.len() > 0 { + log::info!("found [{}] invoices", invoices.len()); + } + + let zero = BigDecimal::from(0u32); + for ( + agreement_id, + peer_id, + payee_addr, + total_amount_accepted, + total_amount_scheduled, + invoice_id, + invoice_amount, + ) in invoices + { + let amount_to_pay = total_amount_accepted.0 - total_amount_scheduled.0; + log::info!( + "[{}] to pay {} - {}", + invoice_id, + amount_to_pay, + agreement_id + ); + if amount_to_pay <= zero { + continue; + } + + total_amount += &amount_to_pay; + + let obligation = BatchPaymentObligation::Invoice { + id: invoice_id, + amount: amount_to_pay.clone(), + agreement_id: agreement_id.clone(), + }; + + let payee_addr_n = NodeId::from_str(&payee_addr).map_err(|e| { + log::error!("Error parsing payee_addr: {}", e); + DbError::Integrity("payee address parsing error".to_string()) + })?; + + match payments.entry(payee_addr) { + hash_map::Entry::Occupied(mut e) => { + let payment = e.get_mut(); + payment.amount += &amount_to_pay; + match payment.peer_obligation.entry(payee_addr_n) { + hash_map::Entry::Occupied(mut e) => e.get_mut().push(obligation), + hash_map::Entry::Vacant(e) => { + e.insert(vec![obligation]); + } + } + } + hash_map::Entry::Vacant(e) => { + let mut peer_obligation = HashMap::new(); + peer_obligation.insert(payee_addr_n, vec![obligation]); + let amount = amount_to_pay.clone(); + e.insert(BatchPayment { + amount, + peer_obligation, + }); + } + } + log::debug!( + "increase_amount_scheduled agreement_id={} by {}", + agreement_id, + amount_to_pay + ); + super::agreement::increase_amount_scheduled( + &agreement_id, + &owner_id, + &amount_to_pay, + conn, + )?; + } + Ok((payments, total_amount)) +} + +pub fn resolve_invoices_activity_part( + args: &ResolveInvoiceArgs, + total_amount: BigDecimal, + payments: HashMap, +) -> DbResult<(HashMap, BigDecimal)> { + let conn = args.conn; + let owner_id = args.owner_id; + let payer_addr = args.payer_addr; + let platform = args.platform; + let since = args.since; + let mut total_amount = total_amount; + let mut payments = payments; + let zero = BigDecimal::from(0u32); + + { + // query explanation + // select all activities that are not fully paid + // for each activity, find the last accepted debit note in debit note chain + + let query_res = diesel::sql_query( + r#" + SELECT a.id, + pa.peer_id, + pa.payee_addr, + a.total_amount_accepted, + a.total_amount_scheduled, + pa.id agreement_id, + (SELECT dn.id + FROM pay_debit_note dn + WHERE dn.activity_id = a.id + AND dn.owner_id = a.owner_id + AND dn.status = 'ACCEPTED' + ORDER BY dn.debit_nonce DESC + LIMIT 1 + ) debit_note_id + FROM pay_activity a JOIN pay_agreement pa + ON a.owner_id = pa.owner_id + AND a.agreement_id = pa.id + AND a.role = pa.role + WHERE a.role='R' + AND a.total_amount_accepted != '0' + AND a.total_amount_scheduled != a.total_amount_accepted + AND pa.updated_ts > ? + AND pa.payment_platform = ? + AND pa.owner_id = ? + "#, + ) + .bind::(since.naive_utc()) + .bind::(&platform) + .bind::(owner_id) + .load::(conn)?; + + if query_res.len() > 0 { + log::info!("Pay for activities - {} found to check", query_res.len()); + } + for a in query_res { + let amount_to_pay = + a.total_amount_accepted.0.clone() - a.total_amount_scheduled.0.clone(); + if amount_to_pay < zero { + log::warn!("Activity {} has total_amount_scheduled: {} greater than total_amount_accepted: {}, which can be a bug", + a.id, + a.total_amount_scheduled.0.clone(), + a.total_amount_accepted.0.clone()); + continue; + } + total_amount += &amount_to_pay; + super::activity::increase_amount_scheduled(&a.id, &owner_id, &amount_to_pay, conn)?; + + let obligation = BatchPaymentObligation::DebitNote { + debit_note_id: a.debit_note_id, + amount: amount_to_pay.clone(), + agreement_id: a.agreement_id.clone(), + activity_id: a.id, + }; + + let payee_addr = NodeId::from_str(&a.payee_addr).map_err(|e| { + log::error!("Error parsing payee_addr: {}", e); + DbError::Integrity("payee address parsing error".to_string()) + })?; + + match payments.entry(a.payee_addr) { + hash_map::Entry::Occupied(mut e) => { + let payment = e.get_mut(); + payment.amount += &amount_to_pay; + match payment.peer_obligation.entry(payee_addr) { + hash_map::Entry::Occupied(mut e) => e.get_mut().push(obligation), + hash_map::Entry::Vacant(e) => { + e.insert(vec![obligation]); + } + } + } + hash_map::Entry::Vacant(e) => { + let mut peer_obligation = HashMap::new(); + peer_obligation.insert(payee_addr, vec![obligation]); + let amount = amount_to_pay.clone(); + e.insert(BatchPayment { + amount, + peer_obligation, + }); + } + } + } + } + Ok((payments, total_amount)) +} + +pub struct ResolveInvoiceArgs<'a> { + pub conn: &'a ConnType, + pub owner_id: NodeId, + pub payer_addr: &'a str, + pub platform: &'a str, + pub since: DateTime, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +struct AllocationPayeeKey { + pub payee_addr: NodeId, + pub allocation_id: String, +} + +fn insert_or_update_allocation_entry( + payment_allocations: &mut HashMap, + allocation_payer_key: AllocationPayeeKey, + obligation: BatchPaymentObligationAllocation, +) -> DbResult<()> { + let payer_all = payment_allocations + .entry(allocation_payer_key.clone()) + .or_default(); + let all = payer_all + .peer_obligation + .entry(allocation_payer_key.payee_addr) + .or_default(); + match obligation { + BatchPaymentObligationAllocation::Invoice { + id, + amount, + agreement_id, + allocation_id, + } => { + payer_all.amount += amount.clone(); + all.push(BatchPaymentObligationAllocation::Invoice { + id, + amount, + agreement_id, + allocation_id, + }); + } + BatchPaymentObligationAllocation::DebitNote { + debit_note_id, + amount, + agreement_id, + activity_id, + allocation_id, + } => { + payer_all.amount += amount.clone(); + all.push(BatchPaymentObligationAllocation::DebitNote { + debit_note_id, + amount, + agreement_id, + activity_id, + allocation_id, + }); + } + } + + Ok(()) +} + +fn use_expenditures_on_payments( + expenditures: &mut [AllocationExpenditureObj], + payments: HashMap, +) -> DbResult> { + let mut payments_allocations: HashMap = + HashMap::new(); + let mut payments = payments; + for payment in &mut payments { + let batch_payment = payment.1; + for peer_obligation in &mut batch_payment.peer_obligation { + for obligation in peer_obligation.1 { + let matching_expenditures = match &obligation { + BatchPaymentObligation::Invoice { + id, + amount, + agreement_id, + } => expenditures + .iter_mut() + .filter(|e| { + e.agreement_id == agreement_id.clone() + && e.activity_id.is_none() + && e.accepted_amount.0 > e.scheduled_amount.0 + }) + .collect::>(), + BatchPaymentObligation::DebitNote { + debit_note_id, + amount, + agreement_id, + activity_id, + } => expenditures + .iter_mut() + .filter(|e| { + e.agreement_id == agreement_id.clone() + && e.activity_id == Some(activity_id.clone()) + && e.accepted_amount.0 > e.scheduled_amount.0 + }) + .collect::>(), + }; + log::info!( + "Found {} matching expenditures for obligation {:?}", + matching_expenditures.len(), + obligation + ); + let amount_to_be_covered = match &obligation { + BatchPaymentObligation::Invoice { + id, + amount, + agreement_id, + } => amount, + BatchPaymentObligation::DebitNote { + debit_note_id, + amount, + agreement_id, + activity_id, + } => amount, + }; + let mut amount_covered = BigDecimal::from(0u32); + for expenditure in matching_expenditures { + let max_amount_to_get = expenditure.accepted_amount.0.clone() + - expenditure.scheduled_amount.0.clone(); + + let cover_amount = std::cmp::min( + amount_to_be_covered.clone() - amount_covered.clone(), + max_amount_to_get, + ); + expenditure.scheduled_amount = + (expenditure.scheduled_amount.0.clone() + cover_amount.clone()).into(); + + match &obligation { + BatchPaymentObligation::Invoice { + id, + amount, + agreement_id, + } => { + insert_or_update_allocation_entry( + &mut payments_allocations, + AllocationPayeeKey { + payee_addr: *peer_obligation.0, + allocation_id: expenditure.allocation_id.clone(), + }, + BatchPaymentObligationAllocation::Invoice { + id: id.clone(), + amount: cover_amount.clone(), + agreement_id: agreement_id.clone(), + allocation_id: expenditure.allocation_id.clone(), + }, + )?; + } + BatchPaymentObligation::DebitNote { + debit_note_id, + amount, + agreement_id, + activity_id, + } => { + insert_or_update_allocation_entry( + &mut payments_allocations, + AllocationPayeeKey { + payee_addr: *peer_obligation.0, + allocation_id: expenditure.allocation_id.clone(), + }, + BatchPaymentObligationAllocation::DebitNote { + debit_note_id: debit_note_id.clone(), + amount: cover_amount.clone(), + agreement_id: agreement_id.clone(), + activity_id: activity_id.clone(), + allocation_id: expenditure.allocation_id.clone(), + }, + )?; + } + } + match &obligation { + BatchPaymentObligation::Invoice { + id, + amount, + agreement_id, + } => { + log::info!("Covered invoice obligation {} with {} of {} from allocation {} - agreement id: {}", id, cover_amount, amount, expenditure.allocation_id, agreement_id); + } + BatchPaymentObligation::DebitNote { + debit_note_id, + amount, + agreement_id, + activity_id, + } => { + log::info!("Covered debit note obligation {:?} with {} of {} from allocation {} - agreement id: {} - activity id: {}", debit_note_id, cover_amount, amount, expenditure.allocation_id, agreement_id, activity_id); + } + } + amount_covered += cover_amount; + } + match &obligation { + BatchPaymentObligation::Invoice { + id, + amount, + agreement_id, + } => { + log::info!( + "Total covered invoice obligation {} with {} of {} from allocations", + id, + amount_covered, + amount + ); + } + BatchPaymentObligation::DebitNote { + debit_note_id, + amount, + agreement_id, + activity_id, + } => { + log::info!("Total covered debit note obligation {:?} with {} of {} from allocations", debit_note_id, amount_covered, amount); + } + } + } + } + } + Ok(payments_allocations) +} + +pub fn resolve_invoices(args: &ResolveInvoiceArgs) -> DbResult> { + let conn = args.conn; + let owner_id = args.owner_id; + let payer_addr = args.payer_addr; + let platform = args.platform; + let since = args.since; + let zero = BigDecimal::from(0u32); + + let total_amount = BigDecimal::default(); + let payments = HashMap::::new(); + + let total_amount = BigDecimal::from(0u32); + + log::debug!("Resolving invoices for {} - {}", owner_id, platform); + let (payments, total_amount) = resolve_invoices_activity_part(args, total_amount, payments)?; + + log::debug!("Resolving agreements for {}", owner_id); + let (payments, total_amount) = resolve_invoices_agreement_part(args, total_amount, payments)?; + + if total_amount == zero { + return Ok(None); + } + + //get allocation expenditures + + use crate::schema::pay_allocation::dsl as pa_dsl; + use crate::schema::pay_allocation_expenditure::dsl as pae_dsl; + let expenditures_orig: Vec = pae_dsl::pay_allocation_expenditure + .select(pae_dsl::pay_allocation_expenditure::all_columns()) + .inner_join( + crate::schema::pay_allocation::dsl::pay_allocation.on(pae_dsl::allocation_id + .eq(pa_dsl::id) + .and(pae_dsl::owner_id.eq(pa_dsl::owner_id)) + .and(pa_dsl::payment_platform.eq(args.platform)) + .and(pa_dsl::updated_ts.gt(args.since.naive_utc())) + .and(pa_dsl::owner_id.eq(args.owner_id))), + ) + .filter(pae_dsl::accepted_amount.ne(pae_dsl::scheduled_amount)) + .load(conn)?; + let mut expenditures = expenditures_orig.clone(); + + if expenditures.len() > 0 { + log::debug!("Found total of {} expenditures", expenditures.len()); + } + + let payments_allocations = + use_expenditures_on_payments(&mut expenditures, payments).map_err(|e| { + log::error!("Error using expenditures on payments: {:?}", e); + e + })?; + + // upload the updated expenditures to database (if changed) + for (expenditure_new, expenditure_old) in zip(expenditures.iter(), expenditures_orig.iter()) { + if expenditure_new.scheduled_amount != expenditure_old.scheduled_amount { + log::debug!("Updating expenditure {:?}", expenditure_new); + let mut query = diesel::update(pae_dsl::pay_allocation_expenditure) + .filter(pae_dsl::owner_id.eq(&expenditure_new.owner_id)) + .filter(pae_dsl::allocation_id.eq(&expenditure_new.allocation_id)) + .filter(pae_dsl::agreement_id.eq(&expenditure_new.agreement_id)) + .into_boxed(); + if let Some(activity_id) = &expenditure_new.activity_id { + query = query.filter(pae_dsl::activity_id.eq(activity_id)); + } else { + query = query.filter(pae_dsl::activity_id.is_null()); + } + query + .set(pae_dsl::scheduled_amount.eq(&expenditure_new.scheduled_amount)) + .execute(conn)?; + } else { + log::debug!("Expenditure {:?} not changed", expenditure_new); + } + } + + let order_id = Uuid::new_v4().to_string(); + { + use crate::schema::pay_batch_order::dsl as odsl; + + let _ = diesel::insert_into(odsl::pay_batch_order) + .values(( + odsl::id.eq(&order_id), + odsl::owner_id.eq(owner_id), + odsl::payer_addr.eq(&payer_addr), + odsl::platform.eq(&platform), + odsl::total_amount.eq(total_amount.to_string()), + odsl::paid_amount.eq("0"), + )) + .execute(conn)?; + } + { + for (key, payment) in payments_allocations { + let payee_addr = key.payee_addr; + let allocation_id = key.allocation_id; + diesel::insert_into(oidsl::pay_batch_order_item) + .values(( + oidsl::order_id.eq(&order_id), + oidsl::owner_id.eq(owner_id), + oidsl::payee_addr.eq(&payee_addr), + oidsl::amount.eq(BigDecimalField(payment.amount.clone())), + oidsl::allocation_id.eq(&allocation_id), + )) + .execute(conn)?; + for (payee_id, obligations) in payment.peer_obligation { + for obligation in &obligations { + log::debug!("obligation: {:?}", obligation); + match obligation { + BatchPaymentObligationAllocation::Invoice { + id, + amount, + agreement_id, + allocation_id, + } => { + use crate::schema::pay_batch_order_item_document::dsl; + diesel::insert_into(dsl::pay_batch_order_item_document) + .values(( + dsl::order_id.eq(&order_id), + dsl::owner_id.eq(owner_id), + dsl::payee_addr.eq(&payee_addr), + dsl::allocation_id.eq(allocation_id), + dsl::agreement_id.eq(agreement_id), + dsl::invoice_id.eq(id), + dsl::activity_id.eq(None::), + dsl::debit_note_id.eq(None::), + dsl::amount.eq(BigDecimalField(amount.clone())), + )) + .execute(conn)?; + } + BatchPaymentObligationAllocation::DebitNote { + amount, + debit_note_id, + agreement_id, + activity_id, + allocation_id, + } => { + use crate::schema::pay_batch_order_item_document::dsl; + diesel::insert_into(dsl::pay_batch_order_item_document) + .values(( + dsl::order_id.eq(&order_id), + dsl::owner_id.eq(owner_id), + dsl::payee_addr.eq(&payee_addr), + dsl::allocation_id.eq(allocation_id), + dsl::agreement_id.eq(agreement_id), + dsl::invoice_id.eq(None::), + dsl::activity_id.eq(activity_id), + dsl::debit_note_id.eq(debit_note_id), + dsl::amount.eq(BigDecimalField(amount.clone())), + )) + .execute(conn)?; + } + } + } + } + } + } + Ok(Some(order_id)) +} + +pub fn get_batch_orders( + conn: &ConnType, + ids: &[String], + platform: &str, +) -> DbResult> { + let batch_orders: Vec = oidsl::pay_batch_order_item + .filter(oidsl::payment_id.eq_any(ids)) + .load(conn)?; + + Ok(batch_orders) +} + +impl<'c> BatchDao<'c> { + pub async fn get_batch_order( + &self, + batch_order_id: String, + node_id: NodeId, + ) -> DbResult> { + readonly_transaction(self.pool, "batch_dao_get", move |conn| { + Ok(dsl::pay_batch_order + .filter(dsl::owner_id.eq(node_id).and(dsl::id.eq(batch_order_id))) + .first(conn) + .optional()?) + }) + .await + } + + pub async fn get_batch_order_items( + &self, + batch_order_id: String, + node_id: NodeId, + ) -> DbResult> { + readonly_transaction(self.pool, "batch_dao_get_items", move |conn| { + Ok(oidsl::pay_batch_order_item + .filter( + oidsl::owner_id + .eq(node_id) + .and(oidsl::order_id.eq(batch_order_id)), + ) + .load(conn)?) + }) + .await + } + + pub async fn get_batch_order_items_by_payment_id( + &self, + payment_id: String, + node_id: NodeId, + ) -> DbResult> { + readonly_transaction(self.pool, "batch_dao_get_items", move |conn| { + Ok(oidsl::pay_batch_order_item + .filter( + oidsl::owner_id + .eq(node_id) + .and(oidsl::payment_id.eq(payment_id)), + ) + .load(conn)?) + }) + .await + } + + pub async fn get_for_node_id( + &self, + node_id: NodeId, + after_timestamp: Option, + max_items: Option, + ) -> DbResult> { + readonly_transaction(self.pool, "batch_dao_get_for_node_id", move |conn| { + let mut query = dsl::pay_batch_order + .filter(dsl::owner_id.eq(node_id)) + .into_boxed(); + if let Some(date) = after_timestamp { + query = query.filter(dsl::created_ts.gt(date)) + } + if let Some(items) = max_items { + query = query.limit(items.into()) + } + query = query.order_by(dsl::created_ts.desc()); + Ok(query.load(conn)?) + }) + .await + } + + pub async fn resolve( + &self, + owner_id: NodeId, + payer_addr: String, + platform: String, + since: DateTime, + ) -> DbResult> { + do_with_transaction(self.pool, "batch_dao_resolve", move |conn| { + resolve_invoices(&ResolveInvoiceArgs { + conn, + owner_id, + payer_addr: &payer_addr, + platform: &platform, + since, + }) + }) + .await + } + pub async fn list_debit_notes( + &self, + owner_id: NodeId, + payment_platform: String, + since: DateTime, + ) -> DbResult> { + use crate::schema::pay_activity; + + #[derive(QueryableByName)] + #[table_name = "pay_activity"] + struct Activity { + id: String, + total_amount_accepted: BigDecimalField, + total_amount_scheduled: BigDecimalField, + } + + do_with_transaction(self.pool, "last_debit_notes", move |conn| { + let v: Vec = diesel::sql_query(r#" + SELECT a.id, a.total_amount_accepted, a.total_amount_scheduled + FROM pay_activity a join pay_agreement pa on a.owner_id = pa.owner_id and a.agreement_id = pa.id and a.role = pa.role + where a.role='R' and a.total_amount_accepted > 0 + and cast(a.total_amount_scheduled as float) < cast(a.total_amount_accepted as float) + and not exists (select 1 from pay_invoice where agreement_id = a.agreement_id and owner_id = a.owner_id and role = 'R') + and pa.updated_ts > ? and pa.payment_platform = ? and pa.owner_id = ? + "#) + .bind::(since.naive_utc()) + .bind::(&payment_platform) + .bind::(owner_id) + .load::(conn)?; + Ok(v.into_iter().map(|a| (a.id, a.total_amount_accepted, a.total_amount_scheduled)).collect()) + }).await + } + + pub async fn get_unsent_batch_items( + &self, + owner_id: NodeId, + order_id: String, + ) -> DbResult> { + readonly_transaction(self.pool, "get_unsent_batch_items", move |conn| { + Ok(oidsl::pay_batch_order_item + .inner_join( + padsl::pay_allocation.on(oidsl::allocation_id + .eq(padsl::id) + .and(oidsl::owner_id.eq(padsl::owner_id))), + ) + .inner_join( + dsl::pay_batch_order.on(oidsl::order_id + .eq(dsl::id) + .and(oidsl::owner_id.eq(dsl::owner_id)) + .and(dsl::owner_id.eq(owner_id)) + .and(dsl::id.eq(&order_id))), + ) + .select(( + oidsl::order_id, + dsl::platform, + oidsl::owner_id, + dsl::payer_addr, + oidsl::payee_addr, + oidsl::allocation_id, + padsl::deposit, + oidsl::amount, + oidsl::payment_id, + oidsl::paid, + )) + .filter( + oidsl::owner_id + .eq(owner_id) + .and(oidsl::order_id.eq(&order_id)) + .and(oidsl::payment_id.is_null()) + .and(oidsl::paid.eq(false)), + ) + .load::(conn)?) + }) + .await + } + + pub async fn get_batch_items( + &self, + owner_id: NodeId, + filter: BatchItemFilter, + ) -> DbResult> { + readonly_transaction(self.pool, "get_batch_items_filtered", move |conn| { + use crate::schema::pay_batch_order::dsl as order_dsl; + use crate::schema::pay_batch_order_item::dsl as order_item_dsl; + use crate::schema::pay_batch_order_item_document::dsl as aggr_item_dsl; + let mut query = order_item_dsl::pay_batch_order_item + .filter(order_item_dsl::owner_id.eq(owner_id)) + .inner_join( + aggr_item_dsl::pay_batch_order_item_document.on(order_item_dsl::order_id + .eq(aggr_item_dsl::order_id) + .and(order_item_dsl::owner_id.eq(aggr_item_dsl::owner_id)) + .and(order_item_dsl::allocation_id.eq(aggr_item_dsl::allocation_id)) + .and(order_item_dsl::payee_addr.eq(aggr_item_dsl::payee_addr))), + ) + .inner_join( + order_dsl::pay_batch_order.on(order_item_dsl::order_id + .eq(order_dsl::id) + .and(order_item_dsl::owner_id.eq(order_dsl::owner_id))), + ) + .into_boxed(); + + if let Some(order_id) = filter.order_id { + query = query.filter(order_item_dsl::order_id.eq(order_id)); + } + if let Some(payee_addr) = filter.payee_addr { + query = query.filter(order_item_dsl::payee_addr.eq(payee_addr)); + } + if let Some(allocation_id) = filter.allocation_id { + query = query.filter(order_item_dsl::allocation_id.eq(allocation_id)); + } + if let Some(agreement_id) = filter.agreement_id { + query = query.filter(aggr_item_dsl::agreement_id.eq(agreement_id)); + } + if let Some(activity_id) = filter.activity_id { + query = query.filter(aggr_item_dsl::activity_id.eq(activity_id)); + } + if let Some(payment_id) = filter.payment_id { + query = query.filter(order_item_dsl::payment_id.eq(payment_id)); + } + + Ok(query + .select(( + order_dsl::created_ts, + order_dsl::updated_ts, + order_item_dsl::order_id, + order_item_dsl::owner_id, + order_item_dsl::payee_addr, + order_item_dsl::allocation_id, + aggr_item_dsl::amount, + aggr_item_dsl::agreement_id, + aggr_item_dsl::invoice_id, + aggr_item_dsl::activity_id, + aggr_item_dsl::debit_note_id, + )) + .order_by(order_dsl::created_ts.desc()) + .load(conn)?) + }) + .await + } + + pub async fn batch_order_item_send( + &self, + order_id: String, + owner_id: NodeId, + payee_addr: String, + allocation_id: String, + payment_id: String, + ) -> DbResult { + do_with_transaction(self.pool, "batch_order_item_send", move |conn| { + Ok(diesel::update(oidsl::pay_batch_order_item) + .filter( + oidsl::order_id + .eq(order_id) + .and(oidsl::payee_addr.eq(payee_addr)) + .and(oidsl::allocation_id.eq(allocation_id)) + .and(oidsl::owner_id.eq(owner_id)), + ) + .set(oidsl::payment_id.eq(payment_id)) + .execute(conn)?) + }) + .await + } + + pub async fn batch_order_item_paid( + &self, + order_id: String, + owner_id: NodeId, + payee_addr: String, + allocation_id: String, + ) -> DbResult { + do_with_transaction(self.pool, "batch_order_item_paid", move |conn| { + use crate::schema::pay_batch_order::dsl as odsl; + //use crate::schema::pay_batch_order_item_document::dsl as d; + let order: DbBatchOrder = odsl::pay_batch_order + .filter(odsl::id.eq(&order_id)) + .get_result(conn)?; + + let updated_count = diesel::update(oidsl::pay_batch_order_item) + .filter( + oidsl::order_id + .eq(&order_id) + .and(oidsl::payee_addr.eq(&payee_addr)) + .and(oidsl::allocation_id.eq(allocation_id.clone())) + .and(oidsl::owner_id.eq(owner_id)) + .and(oidsl::paid.eq(false)), + ) + .set(oidsl::paid.eq(true)) + .execute(conn)?; + if updated_count == 0 { + return Ok(false); + } + if updated_count > 2 { + return Err(DbError::Integrity("More than 1 rows updated".to_string())); + } + + let current_order_item = oidsl::pay_batch_order_item + .filter( + oidsl::order_id + .eq(&order_id) + .and(oidsl::payee_addr.eq(&payee_addr)) + .and(oidsl::allocation_id.eq(allocation_id)) + .and(oidsl::owner_id.eq(owner_id)), + ) + .first::(conn)?; + + //update amount paid on batch order + let current_order = dsl::pay_batch_order + .filter(dsl::id.eq(&order_id).and(dsl::owner_id.eq(owner_id))) + .get_result::(conn)?; + + let updated_amount = current_order.paid_amount + current_order_item.amount; + let now = Utc::now().adapt(); + diesel::update(dsl::pay_batch_order) + .filter(dsl::id.eq(&order_id).and(dsl::owner_id.eq(owner_id))) + .set((dsl::paid_amount.eq(updated_amount), dsl::updated_ts.eq(now))) + .execute(conn)?; + + Ok(true) + }) + .await + } +} diff --git a/core/payment/src/dao/cycle.rs b/core/payment/src/dao/cycle.rs new file mode 100644 index 0000000000..3ab42491af --- /dev/null +++ b/core/payment/src/dao/cycle.rs @@ -0,0 +1,294 @@ +use crate::diesel::ExpressionMethods; +use crate::error::DbError; +use crate::error::DbResult; +use crate::models::cycle::{ + create_batch_cycle_based_on_cron, create_batch_cycle_based_on_interval, parse_cron_str, + DbPayBatchCycle, +}; +use crate::schema::pay_batch_cycle::dsl; +use chrono::{DateTime, Duration, Utc}; +use diesel::{self, BoolExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; +use lazy_static::lazy_static; +use std::env; +use ya_client_model::NodeId; +use ya_persistence::executor::{do_with_transaction, AsDao, ConnType, PoolType}; +use ya_persistence::types::AdaptTimestamp; + +pub struct BatchCycleDao<'c> { + pool: &'c PoolType, +} + +impl<'c> AsDao<'c> for BatchCycleDao<'c> { + fn as_dao(pool: &'c PoolType) -> Self { + Self { pool } + } +} + +fn get_default_payment_testnet_cycle_interval() -> chrono::Duration { + Duration::from_std( + humantime::parse_duration( + &env::var("PAYMENT_CYCLE_DEFAULT_INTERVAL").unwrap_or("5m".to_string()), + ) + .expect("Failed to parse PAYMENT_CYCLE_DEFAULT_INTERVAL"), + ) + .expect("Failed to convert PAYMENT_CYCLE_DEFAULT_INTERVAL to chrono::Duration") +} + +fn get_default_payment_testnet_cycle_extra_pay_time() -> chrono::Duration { + Duration::from_std( + humantime::parse_duration( + &env::var("PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME").unwrap_or("4m".to_string()), + ) + .expect("Failed to parse PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME"), + ) + .expect("Failed to convert PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME to chrono::Duration") +} + +fn get_default_payment_cycle_interval() -> chrono::Duration { + Duration::from_std( + humantime::parse_duration( + &env::var("PAYMENT_CYCLE_DEFAULT_INTERVAL").unwrap_or("24h".to_string()), + ) + .expect("Failed to parse PAYMENT_CYCLE_DEFAULT_INTERVAL"), + ) + .expect("Failed to convert PAYMENT_CYCLE_DEFAULT_INTERVAL to chrono::Duration") +} + +fn get_default_payment_cycle_extra_pay_time() -> chrono::Duration { + Duration::from_std( + humantime::parse_duration( + &env::var("PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME").unwrap_or("1h".to_string()), + ) + .expect("Failed to parse PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME"), + ) + .expect("Failed to convert PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME to chrono::Duration") +} + +lazy_static! { + pub static ref PAYMENT_CYCLE_DEFAULT_INTERVAL: Duration = get_default_payment_cycle_interval(); + pub static ref PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME: Duration = + get_default_payment_cycle_extra_pay_time(); + pub static ref PAYMENT_CYCLE_DEFAULT_TESTNET_INTERVAL: Duration = + get_default_payment_testnet_cycle_interval(); + pub static ref PAYMENT_CYCLE_DEFAULT_TESTNET_EXTRA_PAY_TIME: Duration = + get_default_payment_testnet_cycle_extra_pay_time(); +} + +fn get_or_insert_default_entry_private( + conn: &ConnType, + node_id: NodeId, + platform: String, +) -> DbResult { + let mut loop_count = 0; + loop { + let existing_entry: Option = dsl::pay_batch_cycle + .filter( + dsl::owner_id + .eq(node_id.to_string()) + .and(dsl::platform.eq(platform.clone())), + ) + .first(conn) + .optional()?; + + if let Some(entry) = existing_entry { + break Ok(entry); + } else { + let batch_cycle = if platform.contains("-tglm") { + create_batch_cycle_based_on_interval( + node_id, + platform.clone(), + *PAYMENT_CYCLE_DEFAULT_TESTNET_INTERVAL, + *PAYMENT_CYCLE_DEFAULT_TESTNET_EXTRA_PAY_TIME, + ) + .expect("Failed to create default testnet batch cycle") + } else { + create_batch_cycle_based_on_interval( + node_id, + platform.clone(), + *PAYMENT_CYCLE_DEFAULT_INTERVAL, + *PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME, + ) + .expect("Failed to create default batch cycle") + }; + + diesel::insert_into(dsl::pay_batch_cycle) + .values(batch_cycle) + .execute(conn)?; + } + loop_count += 1; + if loop_count > 1 { + return Err(DbError::Query( + "Failed to insert default batch cycle".to_string(), + )); + } + } +} + +impl<'c> BatchCycleDao<'c> { + pub async fn get_or_insert_default( + &self, + node_id: NodeId, + platform: String, + ) -> DbResult { + do_with_transaction( + self.pool, + "pay_batch_cycle_get_or_insert_default", + move |conn| get_or_insert_default_entry_private(conn, node_id, platform), + ) + .await + } + + pub async fn mark_process_and_next( + &self, + node_id: NodeId, + platform: String, + ) -> DbResult { + do_with_transaction(self.pool, "pay_batch_cycle_update", move |conn| { + let mut entry = get_or_insert_default_entry_private(conn, node_id, platform.clone())?; + + let now = Utc::now(); + let cycle_last_process = Some(now.adapt()); + if let Some(cycle_interval) = entry.cycle_interval.clone() { + entry.cycle_next_process = (now + cycle_interval.0).adapt(); + } else if let Some(cycle_cron) = &entry.cycle_cron { + let schedule = parse_cron_str(cycle_cron).map_err(|err| { + DbError::Query(format!( + "Failed to parse cron expression: {} {}", + cycle_cron, err + )) + })?; + + entry.cycle_next_process = schedule + .upcoming(Utc) + .next() + .ok_or(DbError::Query(format!( + "Failed to get next running time for cron expression: {}", + cycle_cron + )))? + .adapt(); + } + diesel::update( + dsl::pay_batch_cycle.filter( + dsl::owner_id + .eq(node_id.to_string()) + .and(dsl::platform.eq(platform.clone())), + ), + ) + .set(( + dsl::cycle_last_process.eq(cycle_last_process), + dsl::cycle_next_process.eq(entry.cycle_next_process.clone()), + )) + .execute(conn)?; + Ok(entry) + }) + .await + } + + pub async fn create_or_update( + &self, + owner_id: NodeId, + platform: String, + interval: Option, + cron: Option, + safe_payout: Option, + next_running_time: Option>, + ) -> DbResult { + let now = Utc::now().adapt(); + let cycle = if let Some(interval) = interval { + match create_batch_cycle_based_on_interval( + owner_id, + platform.clone(), + interval, + safe_payout.unwrap_or(*PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME), + ) { + Ok(cycle) => cycle, + Err(err) => { + return Err(DbError::Query(format!( + "Error creating batch cycle based on interval {}", + err + ))); + } + } + } else if let Some(cron) = cron { + match create_batch_cycle_based_on_cron( + owner_id, + platform.clone(), + &cron, + safe_payout.unwrap_or(*PAYMENT_CYCLE_DEFAULT_EXTRA_PAY_TIME), + ) { + Ok(cycle) => cycle, + Err(err) => { + return Err(DbError::Query(format!( + "Error creating batch cycle based on cron {}", + err + ))); + } + } + } else { + return Err(DbError::Query( + "Either interval or cron must be provided".to_string(), + )); + }; + do_with_transaction(self.pool, "pay_batch_cycle_create", move |conn| { + let existing_entry: Option = dsl::pay_batch_cycle + .filter( + dsl::owner_id + .eq(owner_id.to_string()) + .and(dsl::platform.eq(platform.clone())), + ) + .first(conn) + .optional()?; + if let Some(mut entry) = existing_entry { + entry.cycle_interval = cycle.cycle_interval.clone(); + entry.cycle_cron = cycle.cycle_cron; + entry.cycle_next_process = cycle.cycle_next_process; + entry.cycle_max_interval = cycle.cycle_max_interval; + entry.cycle_extra_pay_time = cycle.cycle_extra_pay_time; + + if let Some(cycle_last_process) = entry.cycle_last_process.clone() { + if let Some(interval) = cycle.cycle_interval { + let max_next_running_time = cycle_last_process.0 + interval.0; + if let Some(next_running_time) = next_running_time { + entry.cycle_next_process = + std::cmp::min(next_running_time, max_next_running_time.and_utc()) + .adapt(); + } else { + entry.cycle_next_process = max_next_running_time.adapt(); + } + } + } + log::info!("Updating batch cycle {:?}", entry); + diesel::update( + dsl::pay_batch_cycle.filter( + dsl::owner_id + .eq(owner_id.to_string()) + .and(dsl::platform.eq(platform.clone())), + ), + ) + .set(( + dsl::updated_ts.eq(now), + dsl::cycle_interval.eq(entry.cycle_interval), + dsl::cycle_cron.eq(entry.cycle_cron), + dsl::cycle_last_process.eq(entry.cycle_last_process), + dsl::cycle_next_process.eq(entry.cycle_next_process), + dsl::cycle_max_interval.eq(entry.cycle_max_interval), + dsl::cycle_extra_pay_time.eq(entry.cycle_extra_pay_time), + )) + .execute(conn)?; + } else { + diesel::insert_into(dsl::pay_batch_cycle) + .values(&cycle) + .execute(conn)?; + }; + let existing_entry: DbPayBatchCycle = dsl::pay_batch_cycle + .filter( + dsl::owner_id + .eq(owner_id.to_string()) + .and(dsl::platform.eq(platform.clone())), + ) + .first(conn)?; + Ok(existing_entry) + }) + .await + } +} diff --git a/core/payment/src/dao/debit_note.rs b/core/payment/src/dao/debit_note.rs index a6352b2c93..5ef1544403 100644 --- a/core/payment/src/dao/debit_note.rs +++ b/core/payment/src/dao/debit_note.rs @@ -1,6 +1,6 @@ use crate::dao::{activity, debit_note_event}; use crate::error::{DbError, DbResult}; -use crate::models::debit_note::{ReadObj, WriteObj}; +use crate::models::debit_note::{DebitNoteForApi, ReadObj, WriteObj}; use crate::schema::pay_activity::dsl as activity_dsl; use crate::schema::pay_agreement::dsl as agreement_dsl; use crate::schema::pay_debit_note::dsl; @@ -54,6 +54,7 @@ macro_rules! query { dsl::total_amount_due, dsl::usage_counter_vector, dsl::payment_due_date, + dsl::debit_nonce, activity_dsl::agreement_id, agreement_dsl::peer_id, agreement_dsl::payee_addr, @@ -65,8 +66,8 @@ macro_rules! query { pub fn update_status( debit_note_ids: &Vec, - owner_id: &NodeId, - status: &DocumentStatus, + owner_id: NodeId, + status: DocumentStatus, conn: &ConnType, ) -> DbResult<()> { diesel::update( @@ -119,14 +120,21 @@ impl<'c> DebitNoteDao<'c> { issuer_id: NodeId, ) -> DbResult { do_with_transaction(self.pool, "debit_note_dao_create_new", move |conn| { - let previous_debit_note_id = dsl::pay_debit_note - .select(dsl::id) + let (previous_debit_note_id, debit_nonce) = match dsl::pay_debit_note + .select((dsl::id, dsl::debit_nonce)) .filter(dsl::activity_id.eq(&debit_note.activity_id)) .filter(dsl::owner_id.eq(&issuer_id)) - .order_by(dsl::timestamp.desc()) - .first(conn) - .optional()?; - let debit_note = WriteObj::issued(debit_note, previous_debit_note_id, issuer_id); + .order_by(dsl::debit_nonce.desc()) + .first::<(String, i32)>(conn) + .optional()? + { + Some((id, nonce)) => (Some(id), Some(nonce)), + None => (None, None), + }; + + let next_nonce = debit_nonce.map(|nonce| nonce + 1).unwrap_or(0); + let debit_note = + WriteObj::issued(debit_note, next_nonce, previous_debit_note_id, issuer_id); let debit_note_id = debit_note.id.clone(); let owner_id = debit_note.owner_id; activity::set_amount_due( @@ -151,13 +159,21 @@ impl<'c> DebitNoteDao<'c> { pub async fn insert_received(&self, debit_note: DebitNote) -> DbResult<()> { do_with_transaction(self.pool, "debit_note_dao_insert_received", move |conn| { - let previous_debit_note_id = dsl::pay_debit_note - .select(dsl::id) + let (previous_debit_note_id, debit_nonce) = match dsl::pay_debit_note + .select((dsl::id, dsl::debit_nonce)) .filter(dsl::activity_id.eq(&debit_note.activity_id)) - .order_by(dsl::timestamp.desc()) - .first(conn) - .optional()?; - let debit_note = WriteObj::received(debit_note, previous_debit_note_id); + .order_by(dsl::debit_nonce.desc()) + .first::<(String, i32)>(conn) + .optional()? + { + Some((id, nonce)) => (Some(id), Some(nonce)), + None => (None, None), + }; + let debit_note = WriteObj::received( + debit_note, + debit_nonce.map(|n| n + 1).unwrap_or(0), + previous_debit_note_id, + ); let debit_note_id = debit_note.id.clone(); let owner_id = debit_note.owner_id; activity::set_amount_due( @@ -183,17 +199,22 @@ impl<'c> DebitNoteDao<'c> { pub async fn get( &self, debit_note_id: String, - owner_id: NodeId, + owner_id: Option, ) -> DbResult> { readonly_transaction(self.pool, "debit_note_dao_get", move |conn| { - let debit_note: Option = query!() - .filter(dsl::id.eq(debit_note_id)) - .filter(dsl::owner_id.eq(owner_id)) - .first(conn) - .optional()?; - match debit_note { - Some(debit_note) => Ok(Some(debit_note.try_into()?)), - None => Ok(None), + let mut query = query!().into_boxed(); + + query = query.filter(dsl::id.eq(debit_note_id)); + if let Some(owner_id) = owner_id { + query = query.filter(dsl::owner_id.eq(owner_id)) + } + + let debit_notes: Vec = query.load(conn)?; + if let Some(debit_note) = debit_notes.into_iter().next() { + let debit_note: DebitNote = debit_note.try_into()?; + Ok(Some(debit_note)) + } else { + Ok(None) } }) .await @@ -204,7 +225,8 @@ impl<'c> DebitNoteDao<'c> { role: Option, status: Option, payable: Option, - ) -> DbResult> { + activity_id: Option, + ) -> DbResult> { readonly_transaction(self.pool, "debit_note_dao_list", move |conn| { let mut query = query!().into_boxed(); if let Some(role) = role { @@ -221,6 +243,9 @@ impl<'c> DebitNoteDao<'c> { query = query.filter(dsl::payment_due_date.is_null()); } } + if let Some(activity_id) = activity_id { + query = query.filter(dsl::activity_id.eq(activity_id)); + } let debit_notes: Vec = query.order_by(dsl::timestamp.desc()).load(conn)?; debit_notes.into_iter().map(TryInto::try_into).collect() @@ -288,7 +313,7 @@ impl<'c> DebitNoteDao<'c> { .execute(conn)?; } - update_status(&vec![debit_note_id.clone()], &owner_id, &status, conn)?; + update_status(&vec![debit_note_id.clone()], owner_id, status, conn)?; activity::set_amount_accepted(&activity_id, &owner_id, &amount, conn)?; for event in events { debit_note_event::create(debit_note_id.clone(), owner_id, event, conn)?; diff --git a/core/payment/src/dao/invoice.rs b/core/payment/src/dao/invoice.rs index adc2be6225..b78081b861 100644 --- a/core/payment/src/dao/invoice.rs +++ b/core/payment/src/dao/invoice.rs @@ -57,8 +57,8 @@ macro_rules! query { pub fn update_status( invoice_id: &String, - owner_id: &NodeId, - status: &DocumentStatus, + owner_id: NodeId, + status: DocumentStatus, conn: &ConnType, ) -> DbResult<()> { diesel::update( @@ -297,7 +297,7 @@ impl<'c> InvoiceDao<'c> { pub async fn mark_received(&self, invoice_id: String, owner_id: NodeId) -> DbResult<()> { do_with_transaction(self.pool, "invoice_dao_mark_received", move |conn| { - update_status(&invoice_id, &owner_id, &DocumentStatus::Received, conn) + update_status(&invoice_id, owner_id, DocumentStatus::Received, conn) }) .await } @@ -331,8 +331,8 @@ impl<'c> InvoiceDao<'c> { .execute(conn)?; } - update_status(&invoice_id, &owner_id, &status, conn)?; - agreement::set_amount_accepted(&agreement_id, &owner_id, &amount, conn)?; + update_status(&invoice_id, owner_id, status, conn)?; + agreement::set_amount_accepted(&agreement_id, owner_id, &amount, conn)?; for event in events { invoice_event::create(invoice_id.clone(), owner_id, event, conn)?; @@ -432,7 +432,7 @@ impl<'c> InvoiceDao<'c> { .find((&invoice_id, &owner_id)) .select((dsl::agreement_id, dsl::amount, dsl::role)) .first(conn)?; - update_status(&invoice_id, &owner_id, &DocumentStatus::Rejected, conn)?; + update_status(&invoice_id, owner_id, DocumentStatus::Rejected, conn)?; if role == Role::Requestor { diesel::update( dsl::pay_invoice @@ -503,7 +503,7 @@ impl<'c> InvoiceDao<'c> { agreement::compute_amount_due(&agreement_id, &owner_id, conn)?; - update_status(&invoice_id, &owner_id, &DocumentStatus::Cancelled, conn)?; + update_status(&invoice_id, owner_id, DocumentStatus::Cancelled, conn)?; invoice_event::create( invoice_id, owner_id, diff --git a/core/payment/src/dao/order.rs b/core/payment/src/dao/order.rs deleted file mode 100644 index c74a6371cf..0000000000 --- a/core/payment/src/dao/order.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::dao::{activity, agreement, allocation}; -use crate::error::DbResult; -use crate::models::order::{ReadObj, WriteObj}; -use crate::schema::pay_debit_note::dsl as debit_note_dsl; -use crate::schema::pay_invoice::dsl as invoice_dsl; -use crate::schema::pay_order::dsl; -use diesel::{ - self, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl, - RunQueryDsl, -}; -use ya_core_model::payment::local::{ - DebitNotePayment, InvoicePayment, PaymentTitle, SchedulePayment, -}; -use ya_persistence::executor::{do_with_transaction, readonly_transaction, AsDao, PoolType}; - -pub struct OrderDao<'c> { - pool: &'c PoolType, -} - -impl<'c> AsDao<'c> for OrderDao<'c> { - fn as_dao(pool: &'c PoolType) -> Self { - Self { pool } - } -} - -impl<'c> OrderDao<'c> { - pub async fn create(&self, msg: SchedulePayment, id: String, driver: String) -> DbResult<()> { - do_with_transaction(self.pool, "order_dao_create", move |conn| { - match &msg.title { - PaymentTitle::DebitNote(DebitNotePayment { activity_id, .. }) => { - activity::increase_amount_scheduled( - activity_id, - &msg.payer_id, - &msg.amount, - conn, - )? - } - PaymentTitle::Invoice(InvoicePayment { agreement_id, .. }) => { - agreement::increase_amount_scheduled( - agreement_id, - &msg.payer_id, - &msg.amount, - conn, - )? - } - }; - let order = WriteObj::new(msg, id, driver); - allocation::spend_from_allocation(&order.allocation_id, &order.amount, conn)?; - diesel::insert_into(dsl::pay_order) - .values(order) - .execute(conn)?; - Ok(()) - }) - .await - } - - pub async fn get_many(&self, ids: Vec, driver: String) -> DbResult> { - readonly_transaction(self.pool, "order_dao_get_many", move |conn| { - let orders = dsl::pay_order - .left_join( - invoice_dsl::pay_invoice.on(dsl::invoice_id - .eq(invoice_dsl::id.nullable()) - .and(dsl::payer_id.eq(invoice_dsl::owner_id))), - ) - .left_join( - debit_note_dsl::pay_debit_note.on(dsl::debit_note_id - .eq(debit_note_dsl::id.nullable()) - .and(dsl::payer_id.eq(debit_note_dsl::owner_id))), - ) - .filter(dsl::id.eq_any(ids)) - .filter(dsl::driver.eq(driver)) - .select(( - dsl::id, - dsl::driver, - dsl::amount, - dsl::payee_id, - dsl::payer_id, - dsl::payee_addr, - dsl::payer_addr, - dsl::payment_platform, - dsl::invoice_id, - dsl::debit_note_id, - dsl::allocation_id, - dsl::is_paid, - invoice_dsl::agreement_id.nullable(), - debit_note_dsl::activity_id.nullable(), - )) - .load(conn)?; - Ok(orders) - }) - .await - } -} diff --git a/core/payment/src/dao/payment.rs b/core/payment/src/dao/payment.rs index 7fffc9c786..d8ad03eec9 100644 --- a/core/payment/src/dao/payment.rs +++ b/core/payment/src/dao/payment.rs @@ -1,13 +1,11 @@ use crate::dao::{activity, agreement}; use crate::error::DbResult; -use crate::models::payment::{ - ActivityPayment as DbActivityPayment, AgreementPayment as DbAgreementPayment, ReadObj, WriteObj, -}; +use crate::models::payment::{DocumentPayment, ReadObj, WriteObj}; use crate::schema::pay_activity::dsl as activity_dsl; -use crate::schema::pay_activity_payment::dsl as activity_pay_dsl; use crate::schema::pay_agreement::dsl as agreement_dsl; -use crate::schema::pay_agreement_payment::dsl as agreement_pay_dsl; use crate::schema::pay_payment::dsl; +use crate::schema::pay_payment_document::dsl as document_pay_dsl; + use bigdecimal::BigDecimal; use chrono::NaiveDateTime; use diesel::{ @@ -21,7 +19,7 @@ use ya_core_model::payment::local::{DriverName, NetworkName}; use ya_persistence::executor::{ do_with_transaction, readonly_transaction, AsDao, ConnType, PoolType, }; -use ya_persistence::types::Role; +use ya_persistence::types::{BigDecimalField, Role}; pub struct PaymentDao<'c> { pool: &'c PoolType, @@ -30,23 +28,43 @@ pub struct PaymentDao<'c> { fn insert_activity_payments( activity_payments: Vec, payment_id: &str, - owner_id: &NodeId, + owner_id: NodeId, + peer_id: NodeId, conn: &ConnType, ) -> DbResult<()> { log::trace!("Inserting activity payments..."); - for activity_payment in activity_payments { - let amount = activity_payment.amount.into(); - let allocation_id = activity_payment.allocation_id; - + for activity_payment in activity_payments.iter() { + let amount: BigDecimalField = activity_payment.amount.clone().into(); + let allocation_id = activity_payment.allocation_id.clone(); + + let agreement_id: String = activity_dsl::pay_activity + .select(activity_dsl::agreement_id) + .filter(activity_dsl::id.eq(&activity_payment.activity_id)) + .filter(activity_dsl::owner_id.eq(owner_id)) + .first(conn) + .map_err(|e| { + log::error!( + "Error getting agreement_id for activity_id: {}, owner_id: {}. Error: {}", + activity_payment.activity_id, + owner_id, + e + ); + e + })?; + + agreement::increase_amount_paid(&agreement_id, owner_id, &amount, conn)?; activity::increase_amount_paid(&activity_payment.activity_id, owner_id, &amount, conn)?; - diesel::insert_into(activity_pay_dsl::pay_activity_payment) - .values(DbActivityPayment { + diesel::insert_into(document_pay_dsl::pay_payment_document) + .values(DocumentPayment { payment_id: payment_id.to_string(), - activity_id: activity_payment.activity_id, - owner_id: *owner_id, + agreement_id: agreement_id.clone(), + invoice_id: None, + activity_id: Some(activity_payment.activity_id.clone()), + owner_id, amount, - allocation_id, + debit_note_id: None, + peer_id, }) .execute(conn) .map(|_| ())?; @@ -58,7 +76,8 @@ fn insert_activity_payments( fn insert_agreement_payments( agreement_payments: Vec, payment_id: &str, - owner_id: &NodeId, + owner_id: NodeId, + peer_id: NodeId, conn: &ConnType, ) -> DbResult<()> { log::trace!("Inserting agreement payments..."); @@ -68,13 +87,16 @@ fn insert_agreement_payments( agreement::increase_amount_paid(&agreement_payment.agreement_id, owner_id, &amount, conn)?; - diesel::insert_into(agreement_pay_dsl::pay_agreement_payment) - .values(DbAgreementPayment { + diesel::insert_into(document_pay_dsl::pay_payment_document) + .values(DocumentPayment { payment_id: payment_id.to_string(), agreement_id: agreement_payment.agreement_id, - owner_id: *owner_id, + invoice_id: None, + activity_id: None, + owner_id, amount, - allocation_id, + debit_note_id: None, + peer_id, }) .execute(conn) .map(|_| ())?; @@ -99,6 +121,7 @@ impl<'c> PaymentDao<'c> { let payment_id = payment.id.clone(); let owner_id = payment.owner_id; let amount = payment.amount.clone(); + let peer_id = payment.peer_id; do_with_transaction(self.pool, "payment_dao_insert", move |conn| { log::trace!("Inserting payment..."); @@ -107,8 +130,8 @@ impl<'c> PaymentDao<'c> { .execute(conn)?; log::trace!("Payment inserted."); - insert_activity_payments(activity_payments, &payment_id, &owner_id, conn)?; - insert_agreement_payments(agreement_payments, &payment_id, &owner_id, conn)?; + insert_activity_payments(activity_payments, &payment_id, owner_id, peer_id, conn)?; + insert_agreement_payments(agreement_payments, &payment_id, owner_id, peer_id, conn)?; Ok(()) }) @@ -118,6 +141,7 @@ impl<'c> PaymentDao<'c> { #[allow(clippy::too_many_arguments)] pub async fn create_new( &self, + payment_id: String, payer_id: NodeId, payee_id: NodeId, payer_addr: String, @@ -127,8 +151,9 @@ impl<'c> PaymentDao<'c> { details: Vec, activity_payments: Vec, agreement_payments: Vec, - ) -> DbResult { + ) -> DbResult<()> { let payment = WriteObj::new_sent( + payment_id.clone(), payer_id, payee_id, payer_addr, @@ -139,10 +164,9 @@ impl<'c> PaymentDao<'c> { None, None, ); - let payment_id = payment.id.clone(); self.insert(payment, activity_payments, agreement_payments) .await?; - Ok(payment_id) + Ok(()) } pub async fn insert_received( @@ -203,18 +227,11 @@ impl<'c> PaymentDao<'c> { match payment { Some(payment) => { - let activity_payments = activity_pay_dsl::pay_activity_payment - .filter(activity_pay_dsl::payment_id.eq(&payment_id)) - .filter(activity_pay_dsl::owner_id.eq(&owner_id)) + let document_payments = document_pay_dsl::pay_payment_document + .filter(document_pay_dsl::payment_id.eq(&payment_id)) + .filter(document_pay_dsl::owner_id.eq(&owner_id)) .load(conn)?; - let agreement_payments = agreement_pay_dsl::pay_agreement_payment - .filter(agreement_pay_dsl::payment_id.eq(&payment_id)) - .filter(agreement_pay_dsl::owner_id.eq(&owner_id)) - .load(conn)?; - Ok(Some(payment.into_signed_api_model( - activity_payments, - agreement_payments, - ))) + Ok(Some(payment.into_signed_api_model(document_payments))) } None => Ok(None), } @@ -236,16 +253,12 @@ impl<'c> PaymentDao<'c> { .load(conn)?; for payment in payments { - let activity_payments = activity_pay_dsl::pay_activity_payment - .filter(activity_pay_dsl::payment_id.eq(&payment.id)) - .filter(activity_pay_dsl::owner_id.eq(&payment.owner_id)) - .load(conn)?; - let agreement_payments = agreement_pay_dsl::pay_agreement_payment - .filter(agreement_pay_dsl::payment_id.eq(&payment.id)) - .filter(agreement_pay_dsl::owner_id.eq(&payment.owner_id)) + let document_payments = document_pay_dsl::pay_payment_document + .filter(document_pay_dsl::payment_id.eq(&payment.id)) + .filter(document_pay_dsl::owner_id.eq(&payment.owner_id)) .load(conn)?; - result.push(payment.into_api_model(activity_payments, agreement_payments)) + result.push(payment.into_api_model(document_payments)) } Ok(result) @@ -282,46 +295,20 @@ impl<'c> PaymentDao<'c> { let payments: Vec = query.load(conn)?; - let activity_payments = { - let mut query = activity_pay_dsl::pay_activity_payment - .inner_join( - dsl::pay_payment.on(activity_pay_dsl::owner_id - .eq(dsl::owner_id) - .and(activity_pay_dsl::payment_id.eq(dsl::id))), - ) - .inner_join( - activity_dsl::pay_activity.on(activity_pay_dsl::owner_id - .eq(activity_dsl::owner_id) - .and(activity_pay_dsl::activity_id.eq(activity_dsl::id))), - ) - .inner_join( - agreement_dsl::pay_agreement.on(activity_pay_dsl::owner_id - .eq(agreement_dsl::owner_id) - .and(activity_dsl::agreement_id.eq(agreement_dsl::id))), - ) - .filter(dsl::owner_id.eq(&node_id)) - .select(crate::schema::pay_activity_payment::all_columns) - .into_boxed(); - if let Some(app_session_id) = &app_session_id { - query = query.filter(agreement_dsl::app_session_id.eq(app_session_id)); - } - query.load(conn)? - }; - - let agreement_payments = { - let mut query = agreement_pay_dsl::pay_agreement_payment + let document_payments = { + let mut query = document_pay_dsl::pay_payment_document .inner_join( - dsl::pay_payment.on(agreement_pay_dsl::owner_id + dsl::pay_payment.on(document_pay_dsl::owner_id .eq(dsl::owner_id) - .and(agreement_pay_dsl::payment_id.eq(dsl::id))), + .and(document_pay_dsl::payment_id.eq(dsl::id))), ) .inner_join( - agreement_dsl::pay_agreement.on(agreement_pay_dsl::owner_id + agreement_dsl::pay_agreement.on(document_pay_dsl::owner_id .eq(agreement_dsl::owner_id) - .and(agreement_pay_dsl::agreement_id.eq(agreement_dsl::id))), + .and(document_pay_dsl::agreement_id.eq(agreement_dsl::id))), ) .filter(dsl::owner_id.eq(&node_id)) - .select(crate::schema::pay_agreement_payment::all_columns) + .select(crate::schema::pay_payment_document::all_columns) .into_boxed(); if let Some(app_session_id) = &app_session_id { query = query.filter(agreement_dsl::app_session_id.eq(app_session_id)); @@ -329,11 +316,7 @@ impl<'c> PaymentDao<'c> { query.load(conn)? }; - let mut payments = join_activity_and_agreement_payments( - payments, - activity_payments, - agreement_payments, - ); + let mut payments = join_document_payments(payments, document_payments); // A trick to filter payments by app_session_id. Payments are not directly linked to any // particular agreement but they always have at least one related activity_payment or @@ -369,14 +352,12 @@ impl<'c> PaymentDao<'c> { let mut payments = Vec::default(); for payment in read { - let activity_payments = activity_pay_dsl::pay_activity_payment - .filter(activity_pay_dsl::payment_id.eq(&payment.id)) - .load(conn)?; - let agreement_payments = agreement_pay_dsl::pay_agreement_payment - .filter(agreement_pay_dsl::payment_id.eq(&payment.id)) + let document_payments = document_pay_dsl::pay_payment_document + .filter(document_pay_dsl::payment_id.eq(&payment.id)) + .filter(document_pay_dsl::owner_id.eq(&payment.owner_id)) .load(conn)?; - payments.push(payment.into_api_model(activity_payments, agreement_payments)) + payments.push(payment.into_api_model(document_payments)) } Ok(payments) @@ -386,39 +367,27 @@ impl<'c> PaymentDao<'c> { } #[allow(clippy::unwrap_or_default)] -fn join_activity_and_agreement_payments( +fn join_document_payments( payments: Vec, - activity_payments: Vec, - agreement_payments: Vec, + document_payments: Vec, ) -> Vec> { - let mut activity_payments_map = - activity_payments - .into_iter() - .fold(HashMap::new(), |mut map, activity_payment| { - map.entry(activity_payment.payment_id.clone()) - .or_insert_with(Vec::new) - .push(activity_payment); - map - }); - let mut agreement_payments_map = - agreement_payments + let mut document_payments_map = + document_payments .into_iter() - .fold(HashMap::new(), |mut map, agreement_payment| { - map.entry(agreement_payment.payment_id.clone()) + .fold(HashMap::new(), |mut map, document_payment| { + map.entry(document_payment.payment_id.clone()) .or_insert_with(Vec::new) - .push(agreement_payment); + .push(document_payment); map }); + payments .into_iter() .map(|payment| { - let activity_payments = activity_payments_map - .remove(&payment.id) - .unwrap_or_default(); - let agreement_payments = agreement_payments_map + let document_payments = document_payments_map .remove(&payment.id) .unwrap_or_default(); - payment.into_signed_api_model(activity_payments, agreement_payments) + payment.into_signed_api_model(document_payments) }) .collect() } diff --git a/core/payment/src/error.rs b/core/payment/src/error.rs index 2c5b09a791..d39341ab4b 100644 --- a/core/payment/src/error.rs +++ b/core/payment/src/error.rs @@ -108,9 +108,7 @@ pub mod processor { use super::DbError; use crate::models::activity::ReadObj as Activity; use crate::models::agreement::ReadObj as Agreement; - use crate::models::order::ReadObj as Order; use bigdecimal::BigDecimal; - use std::fmt::Display; use tokio::time::error::Elapsed; use ya_core_model::driver::AccountMode; use ya_core_model::payment::local::{ @@ -163,55 +161,8 @@ pub mod processor { } } - #[derive(thiserror::Error, Debug)] - #[error("{0}")] - pub struct OrderValidationError(String); - - impl OrderValidationError { - pub fn new(e: T) -> Self { - Self(e.to_string()) - } - - pub fn platform(order: &Order, platform: &str) -> Result<(), Self> { - Err(Self(format!( - "Invalid platform for payment order {}: {} != {}", - order.id, order.payment_platform, platform - ))) - } - - pub fn payer_addr(order: &Order, payer_addr: &str) -> Result<(), Self> { - Err(Self(format!( - "Invalid payer address for payment order {}: {} != {}", - order.id, order.payer_addr, payer_addr - ))) - } - - pub fn payee_addr(order: &Order, payee_addr: &str) -> Result<(), Self> { - Err(Self(format!( - "Invalid payee address for payment order {}: {} != {}", - order.id, order.payee_addr, payee_addr - ))) - } - - pub fn amount(expected: &BigDecimal, actual: &BigDecimal) -> Result<(), Self> { - Err(Self(format!( - "Invalid payment amount: {} != {}", - expected, actual - ))) - } - - pub fn zero_amount(order: &Order) -> Result<(), Self> { - Err(Self(format!( - "Payment order can not have 0 amount. order_id={}", - order.id - ))) - } - } - #[derive(thiserror::Error, Debug)] pub enum NotifyPaymentError { - #[error("{0}")] - Validation(#[from] OrderValidationError), #[error("Service bus error: {0}")] ServiceBus(#[from] ya_service_bus::error::Error), #[error("Error while sending payment: {0}")] @@ -222,15 +173,8 @@ pub mod processor { Sign(#[from] ya_core_model::driver::GenericError), #[error("Internal timeout")] InternalTimeout(#[from] Elapsed), - } - - impl NotifyPaymentError { - pub fn invalid_order(order: &Order) -> Result<(), Self> { - Err(Self::Validation(OrderValidationError::new(format!( - "Invalid payment order retrieved from database: {:?}", - order - )))) - } + #[error("Other")] + Other(String), } impl From for GenericError { diff --git a/core/payment/src/lib.rs b/core/payment/src/lib.rs index 1e28cd8811..7a2c8628d5 100644 --- a/core/payment/src/lib.rs +++ b/core/payment/src/lib.rs @@ -16,12 +16,15 @@ extern crate diesel; pub mod accounts; pub mod api; +mod batch; mod cli; pub mod config; +mod cycle; pub mod dao; pub mod error; pub mod models; pub mod payment_sync; +mod post_migrations; pub mod processor; pub mod schema; pub mod service; @@ -29,6 +32,8 @@ pub mod timeout_lock; pub mod utils; mod wallet; +pub use batch::send_batch_payments; + pub mod migrations { #[derive(diesel_migrations::EmbedMigrations)] struct _Dummy; @@ -54,13 +59,16 @@ impl Service for PaymentService { impl PaymentService { pub async fn gsb>(context: &Context) -> anyhow::Result<()> { let db = context.component(); - db.apply_migration(migrations::run_with_output)?; + db.apply_migration(migrations::run_with_output) + .map_err(|e| anyhow::anyhow!("Failed to apply payment service migrations: {}", e))?; let config = Arc::new(Config::from_env()?); let processor = Arc::new(PaymentProcessor::new(db.clone())); self::service::bind_service(&db, processor.clone(), config); + processor.process_post_migration_jobs().await?; + tokio::task::spawn(async move { processor.release_allocations(false).await; }); diff --git a/core/payment/src/models.rs b/core/payment/src/models.rs index 0ed350e6d9..52fb212a91 100644 --- a/core/payment/src/models.rs +++ b/core/payment/src/models.rs @@ -1,10 +1,11 @@ pub mod activity; pub mod agreement; pub mod allocation; +pub mod batch; +pub mod cycle; pub mod debit_note; pub mod debit_note_event; pub mod invoice; pub mod invoice_event; -pub mod order; pub mod payment; pub mod sync_notifs; diff --git a/core/payment/src/models/activity.rs b/core/payment/src/models/activity.rs index 8e237c57a1..5e6d9a8be6 100644 --- a/core/payment/src/models/activity.rs +++ b/core/payment/src/models/activity.rs @@ -1,9 +1,12 @@ use crate::schema::pay_activity; +use chrono::{NaiveDateTime, Timelike, Utc}; +use serde::Serialize; use ya_client_model::NodeId; use ya_persistence::types::{BigDecimalField, Role}; -#[derive(Debug, Insertable, Queryable, Identifiable)] +#[derive(Debug, Insertable, Queryable, Identifiable, Serialize)] #[table_name = "pay_activity"] +#[serde(rename_all = "camelCase")] #[primary_key(id, owner_id)] pub struct WriteObj { pub id: String, @@ -14,10 +17,15 @@ pub struct WriteObj { pub total_amount_accepted: BigDecimalField, pub total_amount_scheduled: BigDecimalField, pub total_amount_paid: BigDecimalField, + pub created_ts: Option, + pub updated_ts: Option, } impl WriteObj { pub fn new(id: String, owner_id: NodeId, role: Role, agreement_id: String) -> Self { + let now = Utc::now(); + let created_ts = Some(now.naive_utc()).and_then(|v| v.with_nanosecond(0)); + let updated_ts = created_ts; Self { id, owner_id, @@ -27,12 +35,15 @@ impl WriteObj { total_amount_accepted: Default::default(), total_amount_scheduled: Default::default(), total_amount_paid: Default::default(), + created_ts, + updated_ts, } } } -#[derive(Queryable, Debug, Identifiable)] +#[derive(Queryable, Debug, Identifiable, Serialize)] #[table_name = "pay_activity"] +#[serde(rename_all = "camelCase")] #[primary_key(id, owner_id)] pub struct ReadObj { pub id: String, @@ -43,6 +54,8 @@ pub struct ReadObj { pub total_amount_accepted: BigDecimalField, pub total_amount_scheduled: BigDecimalField, pub total_amount_paid: BigDecimalField, + pub created_ts: Option, + pub updated_ts: Option, pub peer_id: NodeId, // From Agreement pub payee_addr: String, // From Agreement diff --git a/core/payment/src/models/agreement.rs b/core/payment/src/models/agreement.rs index 010b5beaec..81841633a2 100644 --- a/core/payment/src/models/agreement.rs +++ b/core/payment/src/models/agreement.rs @@ -1,12 +1,15 @@ use crate::schema::pay_agreement; +use chrono::{NaiveDateTime, Timelike, Utc}; +use serde::Serialize; use serde_json::Value; use ya_agreement_utils::agreement::{expand, TypedPointer}; use ya_client_model::market::Agreement; use ya_client_model::NodeId; use ya_persistence::types::{BigDecimalField, Role}; -#[derive(Queryable, Debug, Identifiable, Insertable)] +#[derive(Queryable, Debug, Identifiable, Insertable, Serialize)] #[table_name = "pay_agreement"] +#[serde(rename_all = "camelCase")] #[primary_key(id, owner_id)] pub struct WriteObj { pub id: String, @@ -21,6 +24,8 @@ pub struct WriteObj { pub total_amount_scheduled: BigDecimalField, pub total_amount_paid: BigDecimalField, pub app_session_id: Option, + pub created_ts: Option, + pub updated_ts: Option, } impl WriteObj { @@ -51,6 +56,9 @@ impl WriteObj { .map(ToOwned::to_owned) .unwrap_or_else(|_| requestor_id.to_string().to_lowercase()); + let now = Utc::now(); + let created_ts = Some(now.naive_utc()).and_then(|v| v.with_nanosecond(0)); + let updated_ts = created_ts; Ok(Self { id: agreement.agreement_id, owner_id, @@ -64,6 +72,8 @@ impl WriteObj { total_amount_scheduled: Default::default(), total_amount_paid: Default::default(), app_session_id: agreement.app_session_id, + created_ts, + updated_ts, }) } } diff --git a/core/payment/src/models/allocation.rs b/core/payment/src/models/allocation.rs index 301ef0aed6..6233a701ca 100644 --- a/core/payment/src/models/allocation.rs +++ b/core/payment/src/models/allocation.rs @@ -1,6 +1,7 @@ -use crate::schema::pay_allocation; -use chrono::{NaiveDateTime, TimeZone, Utc}; +use crate::schema::{pay_allocation, pay_allocation_expenditure}; +use chrono::{Days, NaiveDateTime, TimeZone, Utc}; use uuid::Uuid; +use ya_client_model::payment::allocation::AllocationExpenditure; use ya_client_model::payment::{Allocation, NewAllocation}; use ya_client_model::NodeId; use ya_persistence::types::BigDecimalField; @@ -12,12 +13,13 @@ pub struct WriteObj { pub owner_id: NodeId, pub payment_platform: String, pub address: String, - pub total_amount: BigDecimalField, + pub avail_amount: BigDecimalField, pub spent_amount: BigDecimalField, - pub remaining_amount: BigDecimalField, - pub timeout: Option, - pub make_deposit: bool, + pub created_ts: NaiveDateTime, + pub updated_ts: NaiveDateTime, + pub timeout: NaiveDateTime, pub deposit: Option, + pub deposit_status: Option, pub released: bool, } @@ -28,14 +30,14 @@ pub struct ReadObj { pub owner_id: NodeId, pub payment_platform: String, pub address: String, - pub total_amount: BigDecimalField, + pub avail_amount: BigDecimalField, pub spent_amount: BigDecimalField, - pub remaining_amount: BigDecimalField, - pub timestamp: NaiveDateTime, - pub timeout: Option, - pub make_deposit: bool, - pub deposit: Option, + pub created_ts: NaiveDateTime, + pub updated_ts: NaiveDateTime, + pub timeout: NaiveDateTime, pub released: bool, + pub deposit: Option, + pub deposit_status: Option, } impl WriteObj { @@ -45,19 +47,27 @@ impl WriteObj { payment_platform: String, address: String, ) -> Self { + let now = Utc::now().naive_utc(); Self { id: Uuid::new_v4().to_string(), owner_id, payment_platform, address, - total_amount: allocation.total_amount.clone().into(), + avail_amount: allocation.total_amount.clone().into(), spent_amount: Default::default(), - remaining_amount: allocation.total_amount.into(), - timeout: allocation.timeout.map(|v| v.naive_utc()), - make_deposit: allocation.make_deposit, + created_ts: now, + updated_ts: now, + timeout: allocation.timeout.map(|v| v.naive_utc()).unwrap_or( + Utc::now() + .checked_add_days(Days::new(365 * 10)) + .unwrap() + .naive_utc(), + ), deposit: allocation .deposit + .as_ref() .map(|deposit| serde_json::to_string(&deposit).unwrap()), + deposit_status: allocation.deposit.map(|_| "open".to_string()), released: false, } } @@ -68,14 +78,22 @@ impl WriteObj { owner_id, payment_platform: allocation.payment_platform, address: allocation.address, - total_amount: allocation.total_amount.into(), + avail_amount: (allocation.total_amount.clone() - allocation.spent_amount.clone()) + .into(), spent_amount: allocation.spent_amount.into(), - remaining_amount: allocation.remaining_amount.into(), - timeout: allocation.timeout.map(|v| v.naive_utc()), - make_deposit: allocation.make_deposit, + created_ts: allocation.timestamp.naive_utc(), + updated_ts: allocation.timestamp.naive_utc(), + timeout: allocation.timeout.map(|v| v.naive_utc()).unwrap_or( + Utc::now() + .checked_add_days(Days::new(365 * 10)) + .unwrap() + .naive_utc(), + ), deposit: allocation .deposit + .as_ref() .map(|deposit| serde_json::to_string(&deposit).unwrap()), + deposit_status: allocation.deposit.map(|_| "open".to_string()), released: false, } } @@ -87,16 +105,42 @@ impl From for Allocation { allocation_id: allocation.id, address: allocation.address, payment_platform: allocation.payment_platform, - total_amount: allocation.total_amount.into(), + total_amount: (allocation.avail_amount.clone() + allocation.spent_amount.clone()) + .into(), spent_amount: allocation.spent_amount.into(), - remaining_amount: allocation.remaining_amount.into(), - timestamp: Utc.from_utc_datetime(&allocation.timestamp), - timeout: allocation.timeout.map(|v| Utc.from_utc_datetime(&v)), - make_deposit: allocation.make_deposit, + remaining_amount: allocation.avail_amount.into(), + timestamp: Utc.from_utc_datetime(&allocation.updated_ts), + timeout: Some(Utc.from_utc_datetime(&allocation.timeout)), + make_deposit: false, deposit: allocation .deposit .and_then(|s| serde_json::from_str(&s).ok()), extend_timeout: None, + created_ts: Utc.from_utc_datetime(&allocation.created_ts), + updated_ts: Utc.from_utc_datetime(&allocation.updated_ts), + } + } +} + +#[derive(Queryable, Debug, Clone, Insertable, AsChangeset)] +#[table_name = "pay_allocation_expenditure"] +pub struct AllocationExpenditureObj { + pub owner_id: NodeId, + pub allocation_id: String, + pub agreement_id: String, + pub activity_id: Option, + pub accepted_amount: BigDecimalField, + pub scheduled_amount: BigDecimalField, +} + +impl From for AllocationExpenditure { + fn from(expenditure: AllocationExpenditureObj) -> Self { + Self { + allocation_id: expenditure.allocation_id, + agreement_id: expenditure.agreement_id, + activity_id: expenditure.activity_id, + accepted_amount: expenditure.accepted_amount.0, + scheduled_amount: expenditure.scheduled_amount.0, } } } diff --git a/core/payment/src/models/batch.rs b/core/payment/src/models/batch.rs new file mode 100644 index 0000000000..e3f4e1453c --- /dev/null +++ b/core/payment/src/models/batch.rs @@ -0,0 +1,130 @@ +use std::collections::HashMap; + +use bigdecimal::BigDecimal; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +use ya_core_model::NodeId; +use ya_persistence::types::BigDecimalField; + +use crate::schema::*; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum BatchPaymentObligation { + Invoice { + id: String, + amount: BigDecimal, + agreement_id: String, + }, + DebitNote { + debit_note_id: Option, + amount: BigDecimal, + agreement_id: String, + activity_id: String, + }, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum BatchPaymentObligationAllocation { + Invoice { + id: String, + amount: BigDecimal, + agreement_id: String, + allocation_id: String, + }, + DebitNote { + debit_note_id: Option, + amount: BigDecimal, + agreement_id: String, + activity_id: String, + allocation_id: String, + }, +} + +pub struct BatchItem { + pub payee_addr: String, + pub payments: Vec, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct BatchPaymentAllocation { + pub amount: BigDecimal, + pub peer_obligation: HashMap>, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct BatchPayment { + pub amount: BigDecimal, + pub peer_obligation: HashMap>, +} + +#[derive(Queryable, Debug, Serialize, Identifiable, Insertable)] +#[table_name = "pay_batch_order"] +#[serde(rename_all = "camelCase")] +pub struct DbBatchOrder { + pub id: String, + pub created_ts: NaiveDateTime, + pub updated_ts: NaiveDateTime, + pub owner_id: NodeId, + pub payer_addr: String, + pub platform: String, + pub total_amount: BigDecimalField, + pub paid_amount: BigDecimalField, +} + +#[derive(Queryable, Debug, Serialize, Insertable)] +#[table_name = "pay_batch_order_item"] +#[serde(rename_all = "camelCase")] +pub struct DbBatchOrderItem { + pub order_id: String, + pub owner_id: String, + pub payee_addr: String, + pub allocation_id: String, + pub amount: BigDecimalField, + pub payment_id: Option, + pub paid: bool, +} + +#[derive(Queryable, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DbBatchOrderItemFullInfo { + pub order_id: String, + pub platform: String, + pub owner_id: String, + pub payer_addr: String, + pub payee_addr: String, + pub allocation_id: String, + pub deposit: Option, + pub amount: BigDecimalField, + pub payment_id: Option, + pub paid: bool, +} + +#[derive(Queryable, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DbAgreementBatchOrderItem { + pub created_ts: NaiveDateTime, + pub updated_ts: NaiveDateTime, + pub order_id: String, + pub owner_id: String, + pub payee_addr: String, + pub allocation_id: String, + pub amount: BigDecimalField, + pub agreement_id: String, + pub invoice_id: Option, + pub activity_id: Option, + pub debit_note_id: Option, +} + +#[derive(Queryable, Debug, Insertable)] +#[table_name = "pay_batch_order_item_document"] +pub struct DbBatchOrderItemAgreement { + pub order_id: String, + pub owner_id: NodeId, + pub payee_addr: String, + pub allocation_id: String, + pub agreement_id: String, + pub invoice_id: Option, + pub activity_id: String, + pub debit_note_id: Option, +} diff --git a/core/payment/src/models/cycle.rs b/core/payment/src/models/cycle.rs new file mode 100644 index 0000000000..f8f5db82fb --- /dev/null +++ b/core/payment/src/models/cycle.rs @@ -0,0 +1,118 @@ +use crate::schema::*; +use anyhow::anyhow; +use chrono::{Duration, Utc}; +use cron::Schedule; +use serde::Serialize; +use std::str::FromStr; +use ya_client_model::NodeId; +use ya_persistence::types::{AdaptDuration, AdaptTimestamp, DurationAdapter, TimestampAdapter}; + +#[derive(Debug, Clone, Queryable, Insertable, AsChangeset, Serialize)] +#[serde(rename_all = "camelCase")] +#[primary_key(owner_id)] +#[table_name = "pay_batch_cycle"] +pub struct DbPayBatchCycle { + pub owner_id: NodeId, + pub platform: String, + pub created_ts: TimestampAdapter, + pub updated_ts: TimestampAdapter, + pub cycle_interval: Option, + pub cycle_cron: Option, + pub cycle_last_process: Option, + pub cycle_next_process: TimestampAdapter, + pub cycle_max_interval: DurationAdapter, + pub cycle_extra_pay_time: DurationAdapter, +} + +pub fn create_batch_cycle_based_on_interval( + owner_id: NodeId, + platform: String, + interval: Duration, + extra_pay_time: Duration, +) -> anyhow::Result { + if interval < Duration::seconds(5) { + return Err(anyhow::anyhow!( + "Interval must be greater than 5 seconds (at least 5 minutes suggested)" + )); + } + if extra_pay_time < Duration::seconds(5) { + return Err(anyhow::anyhow!( + "Extra time for payment must be greater than 5 seconds" + )); + } + let now = Utc::now(); + let next_running_time = now + interval; + Ok(DbPayBatchCycle { + owner_id, + platform, + created_ts: now.adapt(), + updated_ts: now.adapt(), + cycle_interval: Some(interval.adapt()), + cycle_cron: None, + cycle_last_process: None, + cycle_next_process: next_running_time.adapt(), + cycle_max_interval: interval.adapt(), + cycle_extra_pay_time: extra_pay_time.adapt(), + }) +} +pub fn parse_cron_str(cron_str: &str) -> anyhow::Result { + let cron_len = cron_str.split(' ').filter(|s| !s.is_empty()).count(); + if cron_len < 5 { + return Err(anyhow!("Invalid cron expression: {}", cron_str)); + }; + if cron_len > 6 { + return Err(anyhow!("Invalid cron expression: {}", cron_str)); + }; + let cron_str = if cron_len == 5 { + "0 ".to_string() + cron_str + } else { + cron_str.to_string() + }; + + Schedule::from_str(&cron_str) + .map_err(|err| anyhow!("Failed to parse cron expression: {} {}", cron_str, err)) +} + +pub fn create_batch_cycle_based_on_cron( + owner_id: NodeId, + platform: String, + cron_str: &str, + extra_pay_time: Duration, +) -> anyhow::Result { + let schedule = parse_cron_str(cron_str) + .map_err(|err| anyhow!("Failed to parse cron expression: {} {}", cron_str, err))?; + + let mut max_interval_s = 0; + //check max interval + + let mut prev_ms = None; + for dt in schedule.upcoming(Utc).take(100) { + let next = dt.timestamp_millis() / 1000; + if let Some(prev) = prev_ms { + let diff = next - prev; + if diff > max_interval_s { + max_interval_s = diff; + } + } + prev_ms = Some(next); + } + let max_interval = Duration::seconds(max_interval_s); + let now = Utc::now(); + let next_running_time = schedule.upcoming(Utc).next().ok_or(anyhow!( + "Failed to get next running time for cron expression: {}", + cron_str + ))?; + + Ok(DbPayBatchCycle { + owner_id, + platform, + created_ts: now.adapt(), + updated_ts: now.adapt(), + cycle_interval: None, + cycle_cron: Some(cron_str.to_string()), + cycle_last_process: None, + cycle_next_process: next_running_time.adapt(), + cycle_max_interval: max_interval.adapt(), + cycle_extra_pay_time: extra_pay_time.adapt(), + }) +} diff --git a/core/payment/src/models/debit_note.rs b/core/payment/src/models/debit_note.rs index 9b10fd051e..9d082a8cb1 100644 --- a/core/payment/src/models/debit_note.rs +++ b/core/payment/src/models/debit_note.rs @@ -1,7 +1,9 @@ use crate::error::{DbError, DbResult}; use crate::schema::pay_debit_note; use crate::utils::json_from_str; -use chrono::{NaiveDateTime, TimeZone, Utc}; +use bigdecimal::BigDecimal; +use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; +use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; use uuid::Uuid; use ya_client_model::payment::{DebitNote, DocumentStatus, NewDebitNote}; @@ -20,11 +22,13 @@ pub struct WriteObj { pub total_amount_due: BigDecimalField, pub usage_counter_vector: Option>, pub payment_due_date: Option, + pub debit_nonce: i32, } impl WriteObj { pub fn issued( debit_note: NewDebitNote, + debit_nonce: i32, previous_debit_note_id: Option, issuer_id: NodeId, ) -> Self { @@ -40,10 +44,15 @@ impl WriteObj { .usage_counter_vector .map(|v| v.to_string().into_bytes()), payment_due_date: debit_note.payment_due_date.map(|d| d.naive_utc()), + debit_nonce, } } - pub fn received(debit_note: DebitNote, previous_debit_note_id: Option) -> Self { + pub fn received( + debit_note: DebitNote, + debit_nonce: i32, + previous_debit_note_id: Option, + ) -> Self { Self { id: debit_note.debit_note_id, owner_id: debit_note.recipient_id, @@ -56,6 +65,7 @@ impl WriteObj { .usage_counter_vector .map(|v| v.to_string().into_bytes()), payment_due_date: debit_note.payment_due_date.map(|d| d.naive_utc()), + debit_nonce, } } } @@ -74,6 +84,7 @@ pub struct ReadObj { pub total_amount_due: BigDecimalField, pub usage_counter_vector: Option>, pub payment_due_date: Option, + pub debit_nonce: i32, pub agreement_id: String, // From activity pub peer_id: NodeId, // From agreement @@ -97,6 +108,28 @@ impl ReadObj { } } } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DebitNoteForApi { + pub debit_note_id: String, + pub issuer_id: NodeId, + pub recipient_id: NodeId, + pub payee_addr: String, + pub payer_addr: String, + pub payment_platform: String, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub previous_debit_note_id: Option, + pub timestamp: DateTime, + pub agreement_id: String, + pub activity_id: String, + pub total_amount_due: BigDecimal, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub usage_counter_vector: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub payment_due_date: Option>, + pub status: DocumentStatus, + pub debit_nonce: i32, +} impl TryFrom for DebitNote { type Error = DbError; @@ -128,3 +161,35 @@ impl TryFrom for DebitNote { }) } } + +impl TryFrom for DebitNoteForApi { + type Error = DbError; + + fn try_from(debit_note: ReadObj) -> DbResult { + let issuer_id = debit_note.issuer_id(); + let recipient_id = debit_note.recipient_id(); + let usage_counter_vector = match debit_note.usage_counter_vector { + Some(v) => Some(json_from_str(&String::from_utf8(v)?)?), + None => None, + }; + Ok(DebitNoteForApi { + debit_note_id: debit_note.id, + issuer_id, + recipient_id, + payee_addr: debit_note.payee_addr, + payer_addr: debit_note.payer_addr, + payment_platform: debit_note.payment_platform, + previous_debit_note_id: debit_note.previous_debit_note_id, + timestamp: Utc.from_utc_datetime(&debit_note.timestamp), + agreement_id: debit_note.agreement_id, + activity_id: debit_note.activity_id, + total_amount_due: debit_note.total_amount_due.into(), + usage_counter_vector, + payment_due_date: debit_note + .payment_due_date + .map(|d| Utc.from_utc_datetime(&d)), + status: debit_note.status.try_into()?, + debit_nonce: debit_note.debit_nonce, + }) + } +} diff --git a/core/payment/src/models/order.rs b/core/payment/src/models/order.rs deleted file mode 100644 index 5d8666360f..0000000000 --- a/core/payment/src/models/order.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::schema::pay_order; -use ya_client_model::NodeId; -use ya_core_model::payment::local::{PaymentTitle, SchedulePayment}; -use ya_persistence::types::BigDecimalField; - -#[derive(Debug, Insertable)] -#[table_name = "pay_order"] -pub struct WriteObj { - pub id: String, - pub driver: String, - pub amount: BigDecimalField, - pub payee_id: NodeId, - pub payer_id: NodeId, - pub payee_addr: String, - pub payer_addr: String, - pub payment_platform: String, - pub invoice_id: Option, - pub debit_note_id: Option, - pub allocation_id: String, - pub is_paid: bool, -} - -#[derive(Queryable, Debug, Identifiable)] -#[table_name = "pay_order"] -pub struct ReadObj { - pub id: String, - pub driver: String, - pub amount: BigDecimalField, - pub payee_id: NodeId, - pub payer_id: NodeId, - pub payee_addr: String, - pub payer_addr: String, - pub payment_platform: String, - pub invoice_id: Option, - pub debit_note_id: Option, - pub allocation_id: String, - pub is_paid: bool, - - pub agreement_id: Option, // From invoice - pub activity_id: Option, // From debit note -} - -impl WriteObj { - pub fn new(msg: SchedulePayment, id: String, driver: String) -> Self { - let (invoice_id, debit_note_id) = match msg.title { - PaymentTitle::DebitNote(title) => (None, Some(title.debit_note_id)), - PaymentTitle::Invoice(title) => (Some(title.invoice_id), None), - }; - Self { - id, - driver, - amount: msg.amount.into(), - payee_id: msg.payee_id, - payer_id: msg.payer_id, - payee_addr: msg.payee_addr, - payer_addr: msg.payer_addr, - payment_platform: msg.payment_platform, - invoice_id, - debit_note_id, - allocation_id: msg.allocation_id, - is_paid: false, - } - } -} diff --git a/core/payment/src/models/payment.rs b/core/payment/src/models/payment.rs index 3bf4ed0c13..77d15ca76a 100644 --- a/core/payment/src/models/payment.rs +++ b/core/payment/src/models/payment.rs @@ -1,8 +1,7 @@ use crate::error::{DbError, DbResult}; -use crate::schema::{pay_activity_payment, pay_agreement_payment, pay_payment}; +use crate::schema::{pay_payment, pay_payment_document}; use bigdecimal::BigDecimal; use chrono::{NaiveDateTime, TimeZone, Utc}; -use uuid::Uuid; use ya_client_model::payment as api_model; use ya_client_model::payment::payment::Signature; use ya_client_model::NodeId; @@ -28,6 +27,7 @@ pub struct WriteObj { impl WriteObj { #[allow(clippy::too_many_arguments)] pub fn new_sent( + payment_id: String, payer_id: NodeId, payee_id: NodeId, payer_addr: String, @@ -39,7 +39,7 @@ impl WriteObj { signed_bytes: Option>, ) -> Self { Self { - id: Uuid::new_v4().to_string(), + id: payment_id, owner_id: payer_id, peer_id: payee_id, payer_addr, @@ -112,11 +112,24 @@ impl ReadObj { } } - pub fn into_api_model( - self, - activity_payments: Vec, - agreement_payments: Vec, - ) -> api_model::Payment { + pub fn into_api_model(self, document_payment: Vec) -> api_model::Payment { + let mut activity_payments = vec![]; + let mut agreement_payments = vec![]; + for dp in &document_payment { + if let Some(activity_id) = &dp.activity_id { + activity_payments.push(api_model::ActivityPayment { + activity_id: activity_id.clone(), + amount: dp.amount.0.clone(), + allocation_id: None, + }); + } else { + agreement_payments.push(api_model::AgreementPayment { + agreement_id: dp.agreement_id.clone(), + amount: dp.amount.0.clone(), + allocation_id: None, + }); + } + } api_model::Payment { payer_id: self.payer_id(), payee_id: self.payee_id(), @@ -134,13 +147,10 @@ impl ReadObj { pub fn into_signed_api_model( self, - activity_payments: Vec, - agreement_payments: Vec, + document_payments: Vec, ) -> api_model::Signed { api_model::Signed { - payload: self - .clone() - .into_api_model(activity_payments, agreement_payments), + payload: self.clone().into_api_model(document_payments), signature: if self.signature.is_some() && self.signed_bytes.is_some() { Some(Signature { signature: self.signature.unwrap(), @@ -154,43 +164,15 @@ impl ReadObj { } #[derive(Queryable, Debug, Identifiable, Insertable)] -#[table_name = "pay_activity_payment"] -#[primary_key(payment_id, activity_id, owner_id)] -pub struct ActivityPayment { +#[table_name = "pay_payment_document"] +#[primary_key(owner_id, payment_id, peer_id, agreement_id, activity_id)] +pub struct DocumentPayment { pub payment_id: String, - pub activity_id: String, pub owner_id: NodeId, - pub amount: BigDecimalField, - pub allocation_id: Option, -} - -impl From for api_model::ActivityPayment { - fn from(ap: ActivityPayment) -> Self { - Self { - activity_id: ap.activity_id, - amount: ap.amount.0, - allocation_id: ap.allocation_id, - } - } -} - -#[derive(Queryable, Debug, Identifiable, Insertable)] -#[table_name = "pay_agreement_payment"] -#[primary_key(payment_id, agreement_id, owner_id)] -pub struct AgreementPayment { - pub payment_id: String, + pub peer_id: NodeId, pub agreement_id: String, - pub owner_id: NodeId, + pub invoice_id: Option, + pub activity_id: Option, + pub debit_note_id: Option, pub amount: BigDecimalField, - pub allocation_id: Option, -} - -impl From for api_model::AgreementPayment { - fn from(ap: AgreementPayment) -> Self { - Self { - agreement_id: ap.agreement_id, - amount: ap.amount.0, - allocation_id: ap.allocation_id, - } - } } diff --git a/core/payment/src/post_migrations.rs b/core/payment/src/post_migrations.rs new file mode 100644 index 0000000000..986df6f12e --- /dev/null +++ b/core/payment/src/post_migrations.rs @@ -0,0 +1,151 @@ +use crate::error::DbResult; +use crate::timeout_lock::MutexTimeoutExt; + +use bigdecimal::{BigDecimal, Zero}; +use diesel::sql_types::Text; +use diesel::RunQueryDsl; +use std::str::FromStr; +use std::sync::Arc; + +use crate::processor::DB_LOCK_TIMEOUT; +use ya_persistence::executor::{do_with_transaction, DbExecutor}; + +pub async fn process_post_migration_jobs( + db_executor: Arc>, +) -> DbResult<()> { + let db_executor = db_executor + .timeout_lock(DB_LOCK_TIMEOUT) + .await + .expect("db lock timeout"); + + /* + -- we have to run this query but by hand because of lack of decimal support: + UPDATE pay_agreement + SET total_amount_paid = cast(total_amount_paid + (SELECT sum(total_amount_paid) + FROM pay_activity s + WHERE s.owner_id = pay_agreement.owner_id + AND s.role = pay_agreement.role + AND s.agreement_id = pay_agreement.id) AS VARCHAR) + WHERE EXISTS (SELECT 1 FROM pay_activity s2 WHERE s2.owner_id = pay_agreement.owner_id + AND s2.role = pay_agreement.role + AND s2.agreement_id = pay_agreement.id); + */ + + #[derive(QueryableByName, PartialEq, Debug)] + struct JobRecord { + #[sql_type = "Text"] + job: String, + } + + #[derive(QueryableByName, PartialEq, Debug)] + struct AgreementActivityRecord { + #[sql_type = "Text"] + agreement_id: String, + #[sql_type = "Text"] + owner_id: String, + #[sql_type = "Text"] + role: String, + #[sql_type = "Text"] + total_amount_paid_agreement: String, + #[sql_type = "Text"] + total_amount_paid_activity: String, + } + + do_with_transaction(&db_executor.pool, "run_post_migration", move |conn| { + const JOB_NAME: &str = "sum_activities_into_agreement"; + let job_records = diesel::sql_query( + r#" + SELECT job FROM pay_post_migration WHERE done IS NULL AND job = ? + "#, + ) + .bind::(JOB_NAME) + .load::(conn)?; + let job_record = job_records.first(); + + if let Some(job_record) = job_record { + log::info!("Running post migration job: sum_activities_into_agreement"); + + let records: Vec = diesel::sql_query( + r#" + SELECT pag.id AS agreement_id, + pag.owner_id AS owner_id, + pag.role AS role, + pag.total_amount_paid AS total_amount_paid_agreement, + pac.total_amount_paid AS total_amount_paid_activity + FROM pay_agreement AS pag + JOIN pay_activity AS pac + ON pac.agreement_id = pag.id + AND pac.owner_id = pag.owner_id + AND pac.role = pag.role + ORDER BY agreement_id + "#, + ) + .load(conn)?; + + let mut current_idx: usize = 0; + if let Some(first_record) = records.get(current_idx) { + let mut current_sum: BigDecimal = Zero::zero(); + let mut current_agreement_id = first_record.agreement_id.clone(); + + while current_idx < records.len() { + let record = &records + .get(current_idx) + .expect("record has to be found on index"); + + current_sum += + BigDecimal::from_str(&records[current_idx].total_amount_paid_activity) + .unwrap_or_default(); + + let write_total_sum = records + .get(current_idx + 1) + .map(|rec| rec.agreement_id != current_agreement_id.as_str()) + .unwrap_or(true); + if write_total_sum { + current_sum += BigDecimal::from_str(&record.total_amount_paid_agreement) + .unwrap_or_default(); + + diesel::sql_query( + r#" + UPDATE pay_agreement + SET total_amount_paid = $1 + WHERE id = $2 + AND owner_id = $3 + AND role = $4 + "#, + ) + .bind::(current_sum.to_string()) + .bind::(current_agreement_id) + .bind::(&record.owner_id) + .bind::(&record.role) + .execute(conn)?; + current_sum = Zero::zero(); + current_agreement_id = records + .get(current_idx + 1) + .map(|rec| rec.agreement_id.clone()) + .unwrap_or_default(); + } + current_idx += 1; + } + } + + log::info!("Post migration job: sum_activities_into_agreement done. Marking as done."); + let marked = diesel::sql_query( + r#" + UPDATE pay_post_migration + SET done = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW'), + result = 'ok' + WHERE job = ? + "#, + ) + .bind::(JOB_NAME) + .execute(conn)?; + if marked != 1 { + log::error!("Post migration job: sum_activities_into_agreement not marked as done"); + } + } else { + log::info!("No post migration jobs to run"); + } + Ok(()) + }) + .await +} diff --git a/core/payment/src/processor.rs b/core/payment/src/processor.rs index 275734b9f8..021a4af5a9 100644 --- a/core/payment/src/processor.rs +++ b/core/payment/src/processor.rs @@ -1,12 +1,15 @@ use crate::api::allocations::{forced_release_allocation, release_allocation_after}; +use crate::cycle::BatchCycleTaskManager; use crate::dao::{ - ActivityDao, AgreementDao, AllocationDao, AllocationStatus, OrderDao, PaymentDao, SyncNotifsDao, + ActivityDao, AgreementDao, AllocationDao, BatchCycleDao, BatchDao, BatchItemFilter, PaymentDao, + SyncNotifsDao, }; use crate::error::processor::{ - AccountNotRegistered, GetStatusError, NotifyPaymentError, OrderValidationError, - SchedulePaymentError, ValidateAllocationError, VerifyPaymentError, + AccountNotRegistered, GetStatusError, NotifyPaymentError, ValidateAllocationError, + VerifyPaymentError, }; -use crate::models::order::ReadObj as DbOrder; +use crate::error::DbResult; +use crate::models::cycle::DbPayBatchCycle; use crate::payment_sync::SYNC_NOTIFS_NOTIFY; use crate::timeout_lock::{MutexTimeoutExt, RwLockTimeoutExt}; @@ -17,12 +20,16 @@ use futures::{FutureExt, TryFutureExt}; use metrics::counter; use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::ops::Sub; +use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use std::time::Duration; +use std::time::{Duration, Instant}; use thiserror::Error; use tokio::sync::{Mutex, RwLock}; +use crate::post_migrations::process_post_migration_jobs; +use crate::send_batch_payments; use ya_client_model::payment::allocation::Deposit; use ya_client_model::payment::{ Account, ActivityPayment, AgreementPayment, DriverDetails, Network, Payment, @@ -33,58 +40,29 @@ use ya_core_model::driver::{ ValidateAllocationResult, }; use ya_core_model::payment::local::{ - GenericError, GetAccountsError, GetDriversError, NotifyPayment, RegisterAccount, - RegisterAccountError, RegisterDriver, RegisterDriverError, ReleaseDeposit, SchedulePayment, - UnregisterAccount, UnregisterAccountError, UnregisterDriver, UnregisterDriverError, + GenericError, GetAccountsError, GetDriversError, NotifyPayment, ProcessBatchCycleError, + ProcessBatchCycleInfo, ProcessBatchCycleResponse, ProcessBatchCycleSet, ProcessPaymentsError, + ProcessPaymentsNow, ProcessPaymentsNowResponse, RegisterAccount, RegisterAccountError, + RegisterDriver, RegisterDriverError, UnregisterAccount, UnregisterAccountError, + UnregisterDriver, UnregisterDriverError, }; use ya_core_model::payment::public::{SendPayment, SendSignedPayment, BUS_ID}; -use ya_core_model::NodeId; +use ya_core_model::{identity, NodeId}; use ya_net::RemoteEndpoint; use ya_persistence::executor::DbExecutor; use ya_persistence::types::Role; -use ya_service_bus::typed::Endpoint; +use ya_service_bus::typed::{service, Endpoint}; use ya_service_bus::{typed as bus, RpcEndpoint, RpcMessage}; -fn driver_endpoint(driver: &str) -> Endpoint { - bus::service(driver_bus_id(driver)) +pub struct ReleaseDeposit { + pub platform: String, + pub from: String, + pub deposit_contract: String, + pub deposit_id: String, } -fn validate_orders( - orders: &[DbOrder], - platform: &str, - payer_addr: &str, - payee_addr: &str, - amount: &BigDecimal, -) -> Result<(), OrderValidationError> { - if orders.is_empty() { - return Err(OrderValidationError::new( - "orders not found in the database", - )); - } - - let mut total_amount = BigDecimal::zero(); - for order in orders.iter() { - if order.amount.0 == BigDecimal::zero() { - return OrderValidationError::zero_amount(order); - } - if order.payment_platform != platform { - return OrderValidationError::platform(order, platform); - } - if order.payer_addr != payer_addr { - return OrderValidationError::payer_addr(order, payer_addr); - } - if order.payee_addr != payee_addr { - return OrderValidationError::payee_addr(order, payee_addr); - } - - total_amount += &order.amount.0; - } - - if &total_amount > amount { - return OrderValidationError::amount(&total_amount, amount); - } - - Ok(()) +fn driver_endpoint(driver: &str) -> Endpoint { + bus::service(driver_bus_id(driver)) } #[derive(Clone, Debug)] @@ -157,6 +135,14 @@ impl DriverRegistry { } pub fn register_account(&mut self, msg: RegisterAccount) -> Result<(), RegisterAccountError> { + log::info!( + "register_account: driver={} network={} token={} address={} mode={:?}", + msg.driver, + msg.network, + msg.token, + msg.address, + msg.mode + ); let driver_details = match self.drivers.get(&msg.driver) { None => return Err(RegisterAccountError::DriverNotRegistered(msg.driver)), Some(details) => details, @@ -314,13 +300,16 @@ impl DriverRegistry { } } -const DB_LOCK_TIMEOUT: Duration = Duration::from_secs(30); +pub(crate) const DB_LOCK_TIMEOUT: Duration = Duration::from_secs(30); +const SCHEDULE_PAYMENT_LOCK_TIMEOUT: Duration = Duration::from_secs(60); const REGISTRY_LOCK_TIMEOUT: Duration = Duration::from_secs(30); pub struct PaymentProcessor { + batch_cycle_tasks: Arc>, db_executor: Arc>, registry: RwLock, in_shutdown: AtomicBool, + schedule_payment_guard: Arc>, } #[derive(Debug, PartialEq, Error)] @@ -332,6 +321,45 @@ enum PaymentSendToGsbError { #[error("payment Send to Gsb has been rejected")] Rejected, } +pub async fn list_unlocked_identities() -> Result, ya_core_model::driver::GenericError> +{ + log::debug!("list_unlocked_identities"); + let message = identity::List {}; + let result = service(identity::BUS_ID) + .send(message) + .await + .map_err(ya_core_model::driver::GenericError::new)? + .map_err(ya_core_model::driver::GenericError::new)?; + let unlocked_list = result + .iter() + .filter(|n| !n.is_locked) + .map(|n| n.node_id) + .collect(); + log::debug!( + "list_unlocked_identities completed. result={:?}", + unlocked_list + ); + Ok(unlocked_list) +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] +struct PaymentNotificationKey { + peer_id: NodeId, +} + +#[derive(Debug, Clone)] +struct PaymentNotificationValue { + payment_id: String, + payer_id: NodeId, + payee_id: NodeId, + payer_addr: String, + payee_addr: String, + payment_platform: String, + amount: BigDecimal, + details: Vec, + activity_payments: Vec, + agreement_payments: Vec, +} impl PaymentProcessor { pub fn new(db_executor: DbExecutor) -> Self { @@ -339,10 +367,36 @@ impl PaymentProcessor { db_executor: Arc::new(Mutex::new(db_executor)), registry: Default::default(), in_shutdown: AtomicBool::new(false), + schedule_payment_guard: Arc::new(Mutex::new(())), + batch_cycle_tasks: Arc::new(std::sync::Mutex::new(BatchCycleTaskManager::new())), } } pub async fn register_driver(&self, msg: RegisterDriver) -> Result<(), RegisterDriverError> { + { + let unlocked_identities = list_unlocked_identities().await.map_err(|err| { + RegisterDriverError::Other(format!("Error getting unlocked identities: {err}")) + })?; + for identity in unlocked_identities { + self.batch_cycle_tasks.lock().unwrap().add_owner(identity); + } + } + for network in msg.details.networks.keys() { + self.batch_cycle_tasks.lock().unwrap().add_platform( + format!( + "{}-{}-{}", + msg.driver_name, + network, + msg.details + .networks + .get(network) + .map(|n| n.default_token.clone()) + .unwrap_or_default() + ) + .to_lowercase(), + ); + } + self.registry .timeout_write(REGISTRY_LOCK_TIMEOUT) .await @@ -427,144 +481,523 @@ impl PaymentProcessor { .get_platform(driver, network, token) } + pub async fn process_post_migration_jobs(&self) -> DbResult<()> { + process_post_migration_jobs(self.db_executor.clone()).await + } + + async fn send_batch_order_payments(&self, owner: NodeId, order_id: &str) -> anyhow::Result<()> { + send_batch_payments(self.db_executor.clone(), owner, order_id).await + } + + async fn send_close_deposit_after_payments( + &self, + owner: NodeId, + platform: String, + ) -> Result<(), ProcessPaymentsError> { + let driver = self + .registry + .timeout_read(REGISTRY_LOCK_TIMEOUT) + .await + .map_err(|_| { + ProcessPaymentsError::ProcessPaymentsError("Internal timeout".to_string()) + })? + .driver(&platform, &owner.to_string(), AccountMode::SEND) + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!("Error getting driver: {err}")) + })?; + + let allocations_to_close = { + let db_executor = self + .db_executor + .timeout_lock(DB_LOCK_TIMEOUT) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!( + "Db timeout lock when sending payments {err}" + )) + })?; + + db_executor + .as_dao::() + .get_allocations_to_close(owner, platform.clone()) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!("db error: {}", err)) + })? + }; + + log::info!("got {} allocations to close", allocations_to_close.len()); + + for allocation in allocations_to_close { + let Some(deposit) = allocation.deposit else { + return Err(ProcessPaymentsError::ProcessPaymentsError(format!( + "Deposit not found, it should be present on {:?}", + allocation + ))); + }; + if platform != allocation.payment_platform { + return Err(ProcessPaymentsError::ProcessPaymentsError(format!( + "Platform mismatch, expected: {}, got: {}", + allocation.payment_platform, platform + ))); + } + match driver_endpoint(&driver) + .send(DriverReleaseDeposit { + platform: platform.clone(), + from: owner.to_string(), + deposit_contract: deposit.contract, + deposit_id: deposit.id, + }) + .await + { + Ok(Ok(_)) => {} + Ok(Err(e)) => { + return Err(ProcessPaymentsError::ProcessPaymentsError(format!( + "Error releasing deposit 1: {e}" + ))) + } + Err(e) => { + return Err(ProcessPaymentsError::ProcessPaymentsError(format!( + "Error releasing deposit 2: {e}" + ))) + } + } + + { + let db_executor = self + .db_executor + .timeout_lock(DB_LOCK_TIMEOUT) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!( + "Db timeout lock when closing allocation {err}" + )) + })?; + db_executor + .as_dao::() + .mark_allocation_closing(allocation.allocation_id, owner) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!("db error: {}", err)) + })?; + } + } + Ok(()) + } + + fn db_batch_cycle_to_response(&self, el: DbPayBatchCycle) -> ProcessBatchCycleResponse { + ProcessBatchCycleResponse { + node_id: el.owner_id, + platform: el.platform, + interval: el.cycle_interval.map(|d| d.0.to_std().unwrap_or_default()), + cron: el.cycle_cron, + extra_payment_time: el.cycle_extra_pay_time.0.to_std().unwrap_or_default(), + max_interval: el.cycle_max_interval.0.to_std().unwrap_or_default(), + next_process: el.cycle_next_process.0, + last_process: el.cycle_last_process.map(|d| d.0), + } + } + + pub async fn process_cycle_info( + &self, + msg: ProcessBatchCycleInfo, + ) -> Result { + let db_executor = self + .db_executor + .timeout_lock(DB_LOCK_TIMEOUT) + .await + .map_err(|err| { + ProcessBatchCycleError::ProcessBatchCycleError(format!( + "Db timeout lock when process payments {err}" + )) + })?; + + let el = db_executor + .as_dao::() + .get_or_insert_default(msg.node_id, msg.platform) + .await + .map_err(|err| { + ProcessBatchCycleError::ProcessBatchCycleError(format!("db error: {}", err)) + })?; + + Ok(self.db_batch_cycle_to_response(el)) + } + + pub async fn process_cycle_set( + &self, + msg: ProcessBatchCycleSet, + ) -> Result { + let db_executor = self + .db_executor + .timeout_lock(DB_LOCK_TIMEOUT) + .await + .map_err(|err| { + ProcessBatchCycleError::ProcessBatchCycleError(format!( + "Db timeout lock when process payments {err}" + )) + })?; + + let el = db_executor + .as_dao::() + .create_or_update( + msg.node_id, + msg.platform.clone(), + msg.interval + .map(|d| chrono::Duration::from_std(d).unwrap_or_default()), + msg.cron, + msg.safe_payout + .map(|d| chrono::Duration::from_std(d).unwrap_or_default()), + msg.next_update, + ) + .await + .map_err(|err| { + ProcessBatchCycleError::ProcessBatchCycleError(format!( + "create or update error: {err}" + )) + })?; + + self.batch_cycle_tasks + .lock() + .unwrap() + .wake_owner_platform(msg.node_id, msg.platform.clone()); + Ok(self.db_batch_cycle_to_response(el)) + } + + pub async fn process_payments_now( + &self, + msg: ProcessPaymentsNow, + ) -> Result { + { + let operation_start = Instant::now(); + + let mut resolve_time_ms = 0.0f64; + let mut order_id = None; + + if !msg.skip_resolve { + let db_executor = self + .db_executor + .timeout_lock(DB_LOCK_TIMEOUT) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!( + "Db timeout lock when process payments {err}" + )) + })?; + db_executor + .as_dao::() + .mark_process_and_next(msg.node_id, msg.platform.clone()) + .await + .map_err(|err| { + ProcessPaymentsError::ProcessPaymentsError(format!( + "Db error when mark_process_and_next payments {err}" + )) + })?; + + match db_executor + .as_dao::() + .resolve( + msg.node_id, + msg.node_id.to_string(), + msg.platform.clone(), + Utc::now().sub(chrono::Duration::days(30)), + ) + .await + { + Ok(res) => { + resolve_time_ms = operation_start.elapsed().as_secs_f64() / 1000.0; + order_id = res; + } + Err(err) => { + log::error!("Error processing payments: {:?}", err); + return Err(ProcessPaymentsError::ProcessPaymentsError(format!( + "Error processing payments: {:?}", + err + ))); + } + }; + }; + let send_time_now = Instant::now(); + let mut send_time_ms = 0.0; + if !msg.skip_send { + if let Some(order_id) = order_id { + match self.send_batch_order_payments(msg.node_id, &order_id).await { + Ok(()) => {} + Err(err) => { + log::error!("Error when sending payments {}", err); + return Err(ProcessPaymentsError::ProcessPaymentsError(format!( + "Error when sending payments {}", + err + ))); + } + } + match self + .send_close_deposit_after_payments(msg.node_id, msg.platform.clone()) + .await + { + Ok(()) => { + send_time_ms = send_time_now.elapsed().as_secs_f64() / 1000.0; + } + Err(err) => { + log::error!("Error when closing deposits {}", err); + return Err(ProcessPaymentsError::ProcessPaymentsError(format!( + "Error when closing deposits {}", + err + ))); + } + } + } + }; + Ok(ProcessPaymentsNowResponse { + resolve_time_ms, + send_time_ms, + }) + } + } + pub async fn notify_payment(&self, msg: NotifyPayment) -> Result<(), NotifyPaymentError> { let driver = msg.driver; let payment_platform = msg.platform; - let payer_addr = msg.sender; + let payer_addr = msg.sender.clone(); let payee_addr = msg.recipient; - if msg.order_ids.is_empty() { - return Err(OrderValidationError::new("order_ids is empty").into()); + let payer_id = NodeId::from_str(&msg.sender) + .map_err(|err| NotifyPaymentError::Other(format!("Invalid payer address: {err}")))?; + // let payee_id = NodeId::from_str(&payee_addr) + // .map_err(|err| NotifyPaymentError::Other(format!("Invalid payee address: {err}")))?; + + if payer_addr == payee_addr { + log::warn!( + "Payer and payee addresses are the same: {} - skip notification", + payer_addr + ); + return Ok(()); } - let payer_id: NodeId; - let payee_id: NodeId; - let payment_id: String; + let payment_id = msg.payment_id.clone(); - let payment: Payment = { + let mut peers_to_sent_to: HashMap = + HashMap::new(); + + //separate scope so db lock can be released + let payments: Vec = { let db_executor = self.db_executor.timeout_lock(DB_LOCK_TIMEOUT).await?; - let orders = db_executor - .as_dao::() - .get_many(msg.order_ids, driver.clone()) + let order_items = db_executor + .as_dao::() + .get_batch_order_items_by_payment_id(msg.payment_id, payer_id) .await?; - validate_orders( - &orders, - &payment_platform, - &payer_addr, - &payee_addr, - &msg.amount, - )?; - let mut activity_payments = vec![]; - let mut agreement_payments = vec![]; - for order in orders.iter() { - let amount = order.amount.clone().into(); - match (order.activity_id.clone(), order.agreement_id.clone()) { - (Some(activity_id), None) => activity_payments.push(ActivityPayment { - activity_id, - amount, - allocation_id: Some(order.allocation_id.clone()), - }), - (None, Some(agreement_id)) => agreement_payments.push(AgreementPayment { - agreement_id, - amount, - allocation_id: Some(order.allocation_id.clone()), - }), - _ => return NotifyPaymentError::invalid_order(order), + for order_item in order_items.iter() { + let order_documents = match db_executor + .as_dao::() + .get_batch_items( + payer_id, + BatchItemFilter { + order_id: Some(order_item.order_id.clone()), + payee_addr: Some(order_item.payee_addr.clone()), + allocation_id: Some(order_item.allocation_id.clone()), + ..Default::default() + }, + ) + .await + { + Ok(items) => items, + Err(e) => { + return Err(NotifyPaymentError::Other(format!( + "Error getting batch items: {e}" + ))); + } + }; + + db_executor + .as_dao::() + .batch_order_item_paid( + order_item.order_id.clone(), + payer_id, + order_item.payee_addr.clone(), + order_item.allocation_id.clone(), + ) + .await?; + for order in order_documents.iter() { + //get agreement by id + let agreement = db_executor + .as_dao::() + .get(order.agreement_id.clone(), payer_id) + .await? + .ok_or(NotifyPaymentError::Other(format!( + "Agreement not found: {}", + order.agreement_id + )))?; + + let peer_id = agreement.peer_id; + + let amount = order.amount.clone().into(); + + let entr = peers_to_sent_to + .entry(PaymentNotificationKey { peer_id }) + .or_insert_with(|| PaymentNotificationValue { + payment_id: payment_id.clone(), + payer_id, + payee_id: peer_id, + payer_addr: payer_addr.clone(), + payee_addr: payee_addr.clone(), + payment_platform: payment_platform.clone(), + amount: BigDecimal::zero(), + details: Vec::new(), + activity_payments: vec![], + agreement_payments: vec![], + }); + + match order.activity_id.clone() { + Some(activity_id) => entr.activity_payments.push(ActivityPayment { + activity_id, + amount, + allocation_id: None, + }), + None => entr.agreement_payments.push(AgreementPayment { + agreement_id: order.agreement_id.clone(), + amount, + allocation_id: None, + }), + } } } - // FIXME: This is a hack. Payment orders realized by a single transaction are not guaranteed - // to have the same payer and payee IDs. Fixing this requires a major redesign of the - // data model. Payments can no longer by assigned to a single payer and payee. - payer_id = orders.get(0).unwrap().payer_id; - payee_id = orders.get(0).unwrap().payee_id; - - let payment_dao: PaymentDao = db_executor.as_dao(); - - payment_id = payment_dao - .create_new( - payer_id, - payee_id, - payer_addr, - payee_addr, - payment_platform.clone(), - msg.amount.clone(), - msg.confirmation.confirmation, - activity_payments, - agreement_payments, - ) - .await?; - - let signed_payment = payment_dao - .get(payment_id.clone(), payer_id) - .await? - .unwrap(); - signed_payment.payload - }; - - let signature_canonical = driver_endpoint(&driver) - .send(driver::SignPaymentCanonicalized(payment.clone())) - .await??; - let signature = driver_endpoint(&driver) - .send(driver::SignPayment(payment.clone())) - .await??; + // Sum up the amounts for each peer + for (key, value) in peers_to_sent_to.iter_mut() { + for val in &value.activity_payments { + let total_amount = value.amount.clone() + val.amount.clone(); + value.amount = total_amount; + } + for val in &value.agreement_payments { + let total_amount = value.amount.clone() + val.amount.clone(); + value.amount = total_amount; + } + } - counter!("payment.amount.sent", ya_metrics::utils::cryptocurrency_to_u64(&msg.amount), "platform" => payment_platform); - // This is unconditional because at this point the invoice *has been paid*. - // Whether the provider was correctly notified of this fact is another matter. - counter!("payment.invoices.requestor.paid", 1); - let msg = SendPayment::new(payment.clone(), signature); - let msg_with_bytes = SendSignedPayment::new(payment.clone(), signature_canonical); - - let db_executor = Arc::clone(&self.db_executor); - - tokio::task::spawn_local( - async move { - let send_result = - Self::send_to_gsb(payer_id, payee_id, msg_with_bytes.clone()).await; - - let mark_sent = match send_result { - Ok(_) => true, - // If sending SendPaymentWithBytes is not supported then use SendPayment as fallback. - Err(PaymentSendToGsbError::NotSupported) => { - match Self::send_to_gsb(payer_id, payee_id, msg).await { - Ok(_) => true, - Err(PaymentSendToGsbError::Rejected) => true, - Err(PaymentSendToGsbError::Failed) => false, - Err(PaymentSendToGsbError::NotSupported) => false, + // Deduplicate activity and agreement payments + // duplication can happen if multiple allocations are merged into a single payment + for (key, value) in peers_to_sent_to.iter_mut() { + let mut activity_map: HashMap = HashMap::new(); + for val in &value.activity_payments { + match activity_map.get_mut(&val.activity_id) { + Some(existing) => { + let total_amount = existing.amount.clone() + val.amount.clone(); + existing.amount = total_amount; + } + None => { + activity_map.insert(val.activity_id.clone(), val.clone()); } } - Err(_) => false, - }; - - let db_executor = db_executor.timeout_lock(DB_LOCK_TIMEOUT).await?; + } + value.activity_payments = activity_map.into_values().collect(); + + let mut agreement_map: HashMap = HashMap::new(); + for val in &value.agreement_payments { + match agreement_map.get_mut(&val.agreement_id) { + Some(existing) => { + let total_amount = existing.amount.clone() + val.amount.clone(); + existing.amount = total_amount; + } + None => { + agreement_map.insert(val.agreement_id.clone(), val.clone()); + } + } + } + value.agreement_payments = agreement_map.into_values().collect(); + } + let mut payloads = Vec::new(); + for (key, value) in peers_to_sent_to.iter() { let payment_dao: PaymentDao = db_executor.as_dao(); - let sync_dao: SyncNotifsDao = db_executor.as_dao(); - // Always add new type of signature. Compatibility is for older Provider nodes only. payment_dao - .add_signature( - payment_id.clone(), - msg_with_bytes.signature.clone(), - msg_with_bytes.signed_bytes.clone(), + .create_new( + value.payment_id.clone(), + value.payer_id, + value.payee_id, + value.payer_addr.clone(), + value.payee_addr.clone(), + value.payment_platform.clone(), + value.amount.clone(), + msg.confirmation.confirmation.clone(), + value.activity_payments.clone(), + value.agreement_payments.clone(), ) .await?; - if mark_sent { - payment_dao.mark_sent(payment_id).await?; - } else { - sync_dao.upsert(payee_id).await?; - SYNC_NOTIFS_NOTIFY.notify_one(); - log::debug!("Failed to call SendPayment on [{payee_id}]"); - } - - anyhow::Ok(()) + let signed_payment = payment_dao + .get(payment_id.clone(), payer_id) + .await? + .unwrap(); + payloads.push(signed_payment.payload); } - .inspect_err(|e| log::error!("Notify payment task failed: {e}")), - ); + payloads + }; + + // End of db operations - do some message sending + for payment in payments { + let signature_canonical = driver_endpoint(&driver) + .send(driver::SignPaymentCanonicalized(payment.clone())) + .await??; + let signature = driver_endpoint(&driver) + .send(driver::SignPayment(payment.clone())) + .await??; + + counter!("payment.amount.sent", ya_metrics::utils::cryptocurrency_to_u64(&msg.amount), "platform" => payment_platform.clone()); + // This is unconditional because at this point the invoice *has been paid*. + // Whether the provider was correctly notified of this fact is another matter. + counter!("payment.invoices.requestor.paid", 1); + let msg = SendPayment::new(payment.clone(), signature); + let msg_with_bytes = SendSignedPayment::new(payment.clone(), signature_canonical); + + let db_executor = Arc::clone(&self.db_executor); + + tokio::task::spawn_local( + async move { + let send_result = + Self::send_to_gsb(payer_id, payment.payee_id, msg_with_bytes.clone()).await; + + let mark_sent = match send_result { + Ok(_) => true, + // If sending SendPaymentWithBytes is not supported then use SendPayment as fallback. + Err(PaymentSendToGsbError::NotSupported) => { + match Self::send_to_gsb(payer_id, payment.payee_id, msg).await { + Ok(_) => true, + Err(PaymentSendToGsbError::Rejected) => true, + Err(PaymentSendToGsbError::Failed) => false, + Err(PaymentSendToGsbError::NotSupported) => false, + } + } + Err(_) => false, + }; + + let db_executor = db_executor.timeout_lock(DB_LOCK_TIMEOUT).await?; + + let payment_dao: PaymentDao = db_executor.as_dao(); + let sync_dao: SyncNotifsDao = db_executor.as_dao(); + + // Always add new type of signature. Compatibility is for older Provider nodes only. + payment_dao + .add_signature( + payment.payment_id.clone(), + msg_with_bytes.signature.clone(), + msg_with_bytes.signed_bytes.clone(), + ) + .await?; + + if mark_sent { + payment_dao.mark_sent(payment.payment_id.clone()).await?; + } else { + sync_dao.upsert(payment.payee_id).await?; + SYNC_NOTIFS_NOTIFY.notify_one(); + log::debug!("Failed to call SendPayment on [{}]", payment.payee_id); + } + anyhow::Ok(()) + } + .inspect_err(|e| log::error!("Notify payment task failed: {e}")), + ); + } Ok(()) } @@ -595,58 +1028,6 @@ impl PaymentProcessor { .await } - pub async fn schedule_payment(&self, msg: SchedulePayment) -> Result<(), SchedulePaymentError> { - if self.in_shutdown.load(Ordering::SeqCst) { - return Err(SchedulePaymentError::Shutdown); - } - let amount = msg.amount.clone(); - if amount <= BigDecimal::zero() { - return Err(SchedulePaymentError::InvalidInput(format!( - "Can not schedule payment with <=0 amount: {}", - &amount - ))); - } - - let allocation_status = self - .db_executor - .timeout_lock(DB_LOCK_TIMEOUT) - .await? - .as_dao::() - .get(msg.allocation_id.clone(), msg.payer_id) - .await?; - let deposit_id = if let AllocationStatus::Active(allocation) = allocation_status { - allocation.deposit - } else { - None - }; - - let driver = self - .registry - .timeout_read(REGISTRY_LOCK_TIMEOUT) - .await? - .driver(&msg.payment_platform, &msg.payer_addr, AccountMode::SEND)?; - - let order_id = driver_endpoint(&driver) - .send(driver::SchedulePayment::new( - amount, - msg.payer_addr.clone(), - msg.payee_addr.clone(), - msg.payment_platform.clone(), - deposit_id, - msg.due_date, - )) - .await??; - - self.db_executor - .timeout_lock(DB_LOCK_TIMEOUT) - .await? - .as_dao::() - .create(msg, order_id, driver) - .await?; - - Ok(()) - } - pub async fn verify_payment( &self, payment: Payment, @@ -930,14 +1311,48 @@ impl PaymentProcessor { if !allocations.is_empty() { for allocation in allocations { if force { - forced_release_allocation(db.clone(), allocation.allocation_id, None) - .await + forced_release_allocation( + db.clone(), + allocation.allocation_id.clone(), + NodeId::from_str(&allocation.address.clone()) + .map_err(|e| { + GenericError::new(format!( + "Invalid node id: {} {}", + allocation.address.clone(), + e + )) + }) + .unwrap_or_else(|err| { + log::error!( + "Invalid node id: {} {}", + allocation.address.clone(), + err + ); + NodeId::default() + }), + ) + .await } else { release_allocation_after( db.clone(), - allocation.allocation_id, + allocation.allocation_id.clone(), allocation.timeout, - None, + NodeId::from_str(&allocation.address.clone()) + .map_err(|e| { + GenericError::new(format!( + "Invalid node id: {} {}", + allocation.address.clone(), + e + )) + }) + .unwrap_or_else(|err| { + log::error!( + "Invalid node id: {} {}", + allocation.address.clone(), + err + ); + NodeId::default() + }), ) .await } @@ -952,29 +1367,6 @@ impl PaymentProcessor { } } - pub async fn release_deposit(&self, msg: ReleaseDeposit) -> Result<(), GenericError> { - let driver = self - .registry - .timeout_read(REGISTRY_LOCK_TIMEOUT) - .await - .map_err(GenericError::new)? - .driver(&msg.platform, &msg.from, AccountMode::SEND) - .map_err(GenericError::new)?; - - driver_endpoint(&driver) - .send(DriverReleaseDeposit { - platform: msg.platform, - from: msg.from, - deposit_contract: msg.deposit_contract, - deposit_id: msg.deposit_id, - }) - .await - .map_err(GenericError::new)? - .map_err(GenericError::new)?; - - Ok(()) - } - pub async fn shut_down( &self, timeout: Duration, diff --git a/core/payment/src/schema.rs b/core/payment/src/schema.rs index e3dace0085..fec81e9f6c 100644 --- a/core/payment/src/schema.rs +++ b/core/payment/src/schema.rs @@ -8,16 +8,8 @@ table! { total_amount_accepted -> Text, total_amount_scheduled -> Text, total_amount_paid -> Text, - } -} - -table! { - pay_activity_payment (payment_id, activity_id, owner_id) { - payment_id -> Text, - activity_id -> Text, - owner_id -> Text, - amount -> Text, - allocation_id -> Nullable, + created_ts -> Nullable, + updated_ts -> Nullable, } } @@ -35,33 +27,90 @@ table! { total_amount_scheduled -> Text, total_amount_paid -> Text, app_session_id -> Nullable, + created_ts -> Nullable, + updated_ts -> Nullable, } } table! { - pay_agreement_payment (payment_id, agreement_id, owner_id) { - payment_id -> Text, + pay_allocation (owner_id, id) { + id -> Text, + owner_id -> Text, + payment_platform -> Text, + address -> Text, + avail_amount -> Text, + spent_amount -> Text, + created_ts -> Timestamp, + updated_ts -> Timestamp, + timeout -> Timestamp, + released -> Bool, + deposit -> Nullable, + deposit_status -> Nullable, + } +} + +table! { + pay_allocation_expenditure (owner_id, allocation_id, agreement_id, activity_id) { + owner_id -> Text, + allocation_id -> Text, agreement_id -> Text, + activity_id -> Nullable, + accepted_amount -> Text, + scheduled_amount -> Text, + } +} + +table! { + pay_batch_cycle (owner_id, platform) { owner_id -> Text, - amount -> Text, - allocation_id -> Nullable, + platform -> Text, + created_ts -> Timestamp, + updated_ts -> Timestamp, + cycle_interval -> Nullable, + cycle_cron -> Nullable, + cycle_last_process -> Nullable, + cycle_next_process -> Timestamp, + cycle_max_interval -> Text, + cycle_extra_pay_time -> Text, } } table! { - pay_allocation (id) { + pay_batch_order (owner_id, id) { id -> Text, + created_ts -> Timestamp, + updated_ts -> Timestamp, owner_id -> Text, - payment_platform -> Text, - address -> Text, + payer_addr -> Text, + platform -> Text, total_amount -> Text, - spent_amount -> Text, - remaining_amount -> Text, - timestamp -> Timestamp, - timeout -> Nullable, - make_deposit -> Bool, - deposit -> Nullable, - released -> Bool, + paid_amount -> Text, + } +} + +table! { + pay_batch_order_item (owner_id, order_id, payee_addr, allocation_id) { + order_id -> Text, + owner_id -> Text, + payee_addr -> Text, + allocation_id -> Text, + amount -> Text, + payment_id -> Nullable, + paid -> Bool, + } +} + +table! { + pay_batch_order_item_document (owner_id, order_id, payee_addr, allocation_id, agreement_id, activity_id) { + order_id -> Text, + owner_id -> Text, + payee_addr -> Text, + allocation_id -> Text, + agreement_id -> Text, + invoice_id -> Nullable, + activity_id -> Nullable, + debit_note_id -> Nullable, + amount -> Text, } } @@ -74,6 +123,7 @@ table! { activity_id -> Text, status -> Text, timestamp -> Timestamp, + debit_nonce -> Integer, send_accept -> Bool, total_amount_due -> Text, usage_counter_vector -> Nullable, @@ -162,24 +212,7 @@ table! { } table! { - pay_order (id, driver) { - id -> Text, - driver -> Text, - amount -> Text, - payee_id -> Text, - payer_id -> Text, - payee_addr -> Text, - payer_addr -> Text, - payment_platform -> Text, - invoice_id -> Nullable, - debit_note_id -> Nullable, - allocation_id -> Text, - is_paid -> Bool, - } -} - -table! { - pay_payment (id, owner_id) { + pay_payment (id, owner_id, peer_id) { id -> Text, owner_id -> Text, peer_id -> Text, @@ -196,6 +229,19 @@ table! { } } +table! { + pay_payment_document (payment_id, owner_id, peer_id, agreement_id, activity_id) { + payment_id -> Text, + owner_id -> Text, + peer_id -> Text, + agreement_id -> Text, + invoice_id -> Nullable, + activity_id -> Nullable, + debit_note_id -> Nullable, + amount -> Text, + } +} + table! { pay_sync_needed_notifs (id) { id -> Text, @@ -204,20 +250,20 @@ table! { } } -joinable!(pay_activity_payment -> pay_allocation (allocation_id)); -joinable!(pay_agreement_payment -> pay_allocation (allocation_id)); joinable!(pay_debit_note -> pay_document_status (status)); joinable!(pay_debit_note_event -> pay_event_type (event_type)); joinable!(pay_invoice -> pay_document_status (status)); joinable!(pay_invoice_event -> pay_event_type (event_type)); -joinable!(pay_order -> pay_allocation (allocation_id)); allow_tables_to_appear_in_same_query!( pay_activity, - pay_activity_payment, pay_agreement, - pay_agreement_payment, pay_allocation, + pay_allocation_expenditure, + pay_batch_cycle, + pay_batch_order, + pay_batch_order_item, + pay_batch_order_item_document, pay_debit_note, pay_debit_note_event, pay_debit_note_event_read, @@ -227,6 +273,6 @@ allow_tables_to_appear_in_same_query!( pay_invoice_event, pay_invoice_event_read, pay_invoice_x_activity, - pay_order, pay_payment, + pay_payment_document, ); diff --git a/core/payment/src/service.rs b/core/payment/src/service.rs index ebf5428b7c..a897f73673 100644 --- a/core/payment/src/service.rs +++ b/core/payment/src/service.rs @@ -51,7 +51,6 @@ mod local { log::debug!("Binding payment local service to service bus"); ServiceBinder::new(BUS_ID, db, processor) - .bind_with_processor(schedule_payment) .bind_with_processor(register_driver) .bind_with_processor(unregister_driver) .bind_with_processor(register_account) @@ -63,10 +62,12 @@ mod local { .bind_with_processor(get_accounts) .bind_with_processor(validate_allocation) .bind_with_processor(release_allocations) + .bind_with_processor(process_payments_now) + .bind_with_processor(process_cycle_info) + .bind_with_processor(process_cycle_set) .bind_with_processor(get_drivers) .bind_with_processor(payment_driver_status) .bind_with_processor(handle_status_change) - .bind_with_processor(release_deposit) .bind_with_processor(shut_down); // Initialize counters to 0 value. Otherwise they won't appear on metrics endpoint @@ -112,29 +113,6 @@ mod local { log::debug!("Successfully bound payment local service to service bus"); } - async fn schedule_payment( - db: DbExecutor, - processor: Arc, - sender: String, - msg: SchedulePayment, - ) -> Result<(), GenericError> { - let id = msg.document_id(); - debug!( - entity = "payment", - action = "schedule", - id, - "Schedule payment started" - ); - let res = processor.schedule_payment(msg).await; - trace!( - entity = "payment", - action = "schedule", - id, - "Schedule payment finished" - ); - Ok(res?) - } - async fn register_driver( db: DbExecutor, processor: Arc, @@ -486,6 +464,33 @@ mod local { Ok(()) } + async fn process_cycle_info( + db: DbExecutor, + processor: Arc, + _caller: String, + msg: ProcessBatchCycleInfo, + ) -> Result { + processor.process_cycle_info(msg).await + } + + async fn process_cycle_set( + db: DbExecutor, + processor: Arc, + _caller: String, + msg: ProcessBatchCycleSet, + ) -> Result { + processor.process_cycle_set(msg).await + } + + async fn process_payments_now( + db: DbExecutor, + processor: Arc, + _caller: String, + msg: ProcessPaymentsNow, + ) -> Result { + processor.process_payments_now(msg).await + } + async fn get_drivers( db: DbExecutor, processor: Arc, @@ -623,6 +628,7 @@ mod local { Some(Role::Requestor), Some(DocumentStatus::Accepted), Some(true), + None, ) .await .map_err(GenericError::new)?; @@ -767,18 +773,6 @@ mod local { Ok(Ack {}) } - async fn release_deposit( - db: DbExecutor, - processor: Arc, - sender: String, - msg: ReleaseDeposit, - ) -> Result<(), GenericError> { - log::debug!("Schedule payment processor started"); - let res = processor.release_deposit(msg).await; - log::debug!("Schedule payment processor finished"); - res - } - async fn shut_down( db: DbExecutor, processor: Arc, @@ -923,7 +917,7 @@ mod public { counter!("payment.debit_notes.provider.accepted.call", 1); let dao: DebitNoteDao = db.as_dao(); - let debit_note: DebitNote = match dao.get(debit_note_id.clone(), node_id).await { + let debit_note: DebitNote = match dao.get(debit_note_id.clone(), Some(node_id)).await { Ok(Some(debit_note)) => debit_note, Ok(None) => return Err(AcceptRejectError::ObjectNotFound), Err(e) => return Err(AcceptRejectError::ServiceError(e.to_string())), diff --git a/core/persistence/Cargo.toml b/core/persistence/Cargo.toml index b2b649272d..7fc75cea58 100644 --- a/core/persistence/Cargo.toml +++ b/core/persistence/Cargo.toml @@ -28,10 +28,12 @@ dotenv = "0.15.0" libsqlite3-sys = { workspace = true } log = "0.4" r2d2 = "0.8" +serde = "1.0" serde_json = "1.0" structopt = { version = "0.3", optional = true } thiserror = "1.0.9" tokio = { version = "1", features = [] } +humantime = "2.1.0" [dev-dependencies] tempdir = "0.3.7" diff --git a/core/persistence/src/duration.rs b/core/persistence/src/duration.rs new file mode 100644 index 0000000000..67ffa35cc2 --- /dev/null +++ b/core/persistence/src/duration.rs @@ -0,0 +1,83 @@ +use diesel::backend::Backend; +use diesel::deserialize::{FromSql, FromSqlRow}; +use diesel::serialize::{Output, ToSql}; +use diesel::sql_types::Text; +use diesel::sqlite::Sqlite; +use diesel::{deserialize, serialize, Queryable}; +use serde::Serialize; +use std::io::Write; + +pub trait AdaptDuration { + fn adapt(self) -> DurationAdapter; +} + +/// Sqlite Timestamp formatting omits sub-second parts if it is equal to zero. +/// This results in invalid comparison between `2022-07-29 12:33:14` and `2022-07-29 12:33:14.000`, +/// because sqlite compares text. +/// +/// This adapter enforces timestamp format in database so it is suitable for comparison. +/// +/// Check description of related issues: +/// https://github.com/golemfactory/yagna/issues/2145 +/// https://github.com/golemfactory/yagna/pull/2086 +#[derive(Clone, Debug, AsExpression)] +#[sql_type = "Text"] +pub struct DurationAdapter(pub chrono::Duration); + +impl Serialize for DurationAdapter { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + humantime::format_duration(self.0.to_std().unwrap_or_default()) + .to_string() + .serialize(serializer) + } +} + +impl FromSqlRow for DurationAdapter +where + DB: Backend, + String: FromSql, +{ + fn build_from_row>(row: &mut R) -> deserialize::Result { + let value = String::build_from_row(row)?; + let chrono_duration = chrono::Duration::from_std(humantime::parse_duration(&value)?)?; + Ok(DurationAdapter(chrono_duration)) + } +} + +impl Queryable for DurationAdapter { + type Row = DurationAdapter; + + fn build(row: Self::Row) -> Self { + row + } +} + +impl FromSql for DurationAdapter +where + DB: Backend, + String: FromSql, +{ + fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result { + let value = String::from_sql(bytes)?; + + let chrono_duration = chrono::Duration::from_std(humantime::parse_duration(&value)?)?; + + Ok(chrono_duration.adapt()) + } +} + +impl ToSql for DurationAdapter { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let f = humantime::format_duration(self.0.to_std().unwrap_or_default()).to_string(); + ToSql::::to_sql(&f, out) + } +} + +impl AdaptDuration for chrono::Duration { + fn adapt(self) -> DurationAdapter { + DurationAdapter(self) + } +} diff --git a/core/persistence/src/lib.rs b/core/persistence/src/lib.rs index 5d08909503..78aa81cd9b 100644 --- a/core/persistence/src/lib.rs +++ b/core/persistence/src/lib.rs @@ -1,10 +1,10 @@ #[macro_use] extern crate diesel; +mod duration; pub mod executor; #[cfg(feature = "service")] pub mod service; mod timestamp; pub mod types; - pub use executor::Error; diff --git a/core/persistence/src/timestamp.rs b/core/persistence/src/timestamp.rs index a072c7db6a..bc38600450 100644 --- a/core/persistence/src/timestamp.rs +++ b/core/persistence/src/timestamp.rs @@ -4,7 +4,8 @@ use diesel::deserialize::FromSql; use diesel::serialize::{Output, ToSql}; use diesel::sql_types::{Text, Timestamp}; use diesel::sqlite::Sqlite; -use diesel::{deserialize, serialize}; +use diesel::{deserialize, serialize, Queryable}; +use serde::Serialize; use std::io::Write; pub trait AdaptTimestamp { @@ -20,10 +21,18 @@ pub trait AdaptTimestamp { /// Check description of related issues: /// https://github.com/golemfactory/yagna/issues/2145 /// https://github.com/golemfactory/yagna/pull/2086 -#[derive(Clone, Debug, AsExpression)] +#[derive(Clone, Debug, AsExpression, Serialize)] #[sql_type = "Timestamp"] pub struct TimestampAdapter(pub NaiveDateTime); +impl Queryable for TimestampAdapter { + type Row = NaiveDateTime; + + fn build(row: Self::Row) -> Self { + TimestampAdapter(row) + } +} + impl FromSql for TimestampAdapter { fn from_sql(value: Option<&::RawValue>) -> deserialize::Result { Ok(NaiveDateTime::from_sql(value)?.adapt()) diff --git a/core/persistence/src/types.rs b/core/persistence/src/types.rs index 1dd1b88e8c..14b4cb45c8 100644 --- a/core/persistence/src/types.rs +++ b/core/persistence/src/types.rs @@ -1,19 +1,29 @@ +pub use crate::duration::{AdaptDuration, DurationAdapter}; +pub use crate::timestamp::{AdaptTimestamp, TimestampAdapter}; use bigdecimal::{BigDecimal, Zero}; use diesel::backend::Backend; use diesel::deserialize::{FromSql, Result as DeserializeResult}; use diesel::serialize::{Output, Result as SerializeResult, ToSql}; use diesel::sql_types::Text; +use serde::Serialize; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::io::Write; use std::ops::{Add, Sub}; use std::str::FromStr; -pub use crate::timestamp::{AdaptTimestamp, TimestampAdapter}; - #[derive(Debug, Clone, AsExpression, FromSqlRow, Default, PartialEq, PartialOrd, Eq, Ord)] #[sql_type = "Text"] pub struct BigDecimalField(pub BigDecimal); +impl Serialize for BigDecimalField { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.0.to_string().serialize(serializer) + } +} + impl From for BigDecimal { fn from(x: BigDecimalField) -> Self { x.0 @@ -127,6 +137,14 @@ pub enum Role { Provider, Requestor, } +impl Serialize for Role { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.to_string().serialize(serializer) + } +} #[derive(Debug, thiserror::Error)] #[error("Invalid role string: {0}")] diff --git a/goth_tests/domain/payments/test_deposit_payments.py b/goth_tests/domain/payments/test_deposit_payments.py index efca1f9bd6..70731ac324 100644 --- a/goth_tests/domain/payments/test_deposit_payments.py +++ b/goth_tests/domain/payments/test_deposit_payments.py @@ -138,7 +138,7 @@ async def test_deposit_agreement_payments( pass # this test is failing too much, so not expect exact amount paid, - # but at least two payments have to be made + # but at least one payment have to be made assert stats.amount > 0 assert amount > 0 - assert number_of_payments >= 2 + assert number_of_payments >= 1 diff --git a/goth_tests/domain/payments/test_mid_payments.py b/goth_tests/domain/payments/test_mid_payments.py index 85a7643851..fe1e055d6b 100644 --- a/goth_tests/domain/payments/test_mid_payments.py +++ b/goth_tests/domain/payments/test_mid_payments.py @@ -116,7 +116,7 @@ async def test_mid_agreement_payments( pass # this test is failing too much, so not expect exact amount paid, - # but at least two payments have to be made + # but at least one payment have to be made assert stats.amount > 0 assert amount > 0 - assert number_of_payments >= 2 + assert number_of_payments >= 1 diff --git a/goth_tests/poetry.lock b/goth_tests/poetry.lock index adc2eae878..4f1d6e6262 100644 --- a/goth_tests/poetry.lock +++ b/goth_tests/poetry.lock @@ -1,1481 +1,1517 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. - -[[package]] -name = "aiohappyeyeballs" -version = "2.4.4" -description = "Happy Eyeballs for asyncio" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, - {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, -] - -[[package]] -name = "aiohttp" -version = "3.11.11" -description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, - {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, - {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"}, - {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"}, - {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"}, - {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"}, - {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"}, - {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"}, - {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"}, - {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"}, - {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"}, - {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"}, - {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"}, - {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"}, -] - -[package.dependencies] -aiohappyeyeballs = ">=2.3.0" -aiosignal = ">=1.1.2" -attrs = ">=17.3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -propcache = ">=0.2.0" -yarl = ">=1.17.0,<2.0" - -[package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] - -[[package]] -name = "aiosignal" -version = "1.3.2" -description = "aiosignal: a list of registered asynchronous callbacks" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, -] - -[package.dependencies] -frozenlist = ">=1.1.0" - -[[package]] -name = "ansicolors" -version = "1.1.8" -description = "ANSI colors for Python" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "ansicolors-1.1.8-py2.py3-none-any.whl", hash = "sha256:00d2dde5a675579325902536738dd27e4fac1fd68f773fe36c21044eb559e187"}, - {file = "ansicolors-1.1.8.zip", hash = "sha256:99f94f5e3348a0bcd43c82e5fc4414013ccc19d70bd939ad71e0133ce9c372e0"}, -] - -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - -[[package]] -name = "attrs" -version = "24.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, - {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, -] - -[package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] - -[[package]] -name = "black" -version = "21.7b0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.6.2" -groups = ["dev"] -files = [ - {file = "black-21.7b0-py3-none-any.whl", hash = "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116"}, - {file = "black-21.7b0.tar.gz", hash = "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219"}, -] - -[package.dependencies] -appdirs = "*" -click = ">=7.1.2" -mypy-extensions = ">=0.4.3" -pathspec = ">=0.8.1,<1" -regex = ">=2020.1.8" -tomli = ">=0.2.6,<2.0.0" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"] -python2 = ["typed-ast (>=1.4.2)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "certifi" -version = "2020.12.5" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, - {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, -] - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -markers = {main = "sys_platform == \"win32\"", dev = "platform_system == \"Windows\""} - -[[package]] -name = "docker" -version = "7.1.0" -description = "A Python library for the Docker Engine API." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, - {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, -] - -[package.dependencies] -pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} -requests = ">=2.26.0" -urllib3 = ">=1.26.0" - -[package.extras] -dev = ["coverage (==7.2.7)", "pytest (==7.4.2)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.1.0)", "ruff (==0.1.8)"] -docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] -ssh = ["paramiko (>=2.4.3)"] -websockets = ["websocket-client (>=1.3.0)"] - -[[package]] -name = "dpath" -version = "2.2.0" -description = "Filesystem-like pathing and searching for dictionaries" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576"}, - {file = "dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e"}, -] - -[[package]] -name = "fastcore" -version = "1.7.28" -description = "Python supercharged for fastai development" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "fastcore-1.7.28-py3-none-any.whl", hash = "sha256:ffa1ab1b34518795a4342b85ebb9cd2b30588210c21df028a11e420678a59e20"}, - {file = "fastcore-1.7.28.tar.gz", hash = "sha256:606e4507eb4b8892e4c83ddf5462fbcf32f4bde4fa6caf56ca67ee5e2dbe2b1e"}, -] - -[package.dependencies] -packaging = "*" - -[package.extras] -dev = ["llms-txt", "matplotlib", "nbclassic", "nbdev (>=0.2.39)", "numpy", "pandas", "pillow", "pysymbol_llm", "torch"] - -[[package]] -name = "frozenlist" -version = "1.5.0" -description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, - {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, - {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, - {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, - {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, - {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, - {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, - {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, - {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, - {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, - {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, - {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, - {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, - {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, - {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, -] - -[[package]] -name = "func-timeout" -version = "4.3.5" -description = "Python module which allows you to specify timeouts when calling any existing function. Also provides support for stoppable-threads" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "func_timeout-4.3.5.tar.gz", hash = "sha256:74cd3c428ec94f4edfba81f9b2f14904846d5ffccc27c92433b8b5939b5575dd"}, -] - -[[package]] -name = "ghapi" -version = "1.0.6" -description = "A python client for the GitHub API" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "ghapi-1.0.6-py3-none-any.whl", hash = "sha256:b3d96bf18fcaa2cb7131bad9de2948e2a1c2bb226377a25826f6c80950c57854"}, - {file = "ghapi-1.0.6.tar.gz", hash = "sha256:64fdd9f06d8e3373065c42c2a03e067e2bbb9ca18b583cd6e38a28aaad0224f6"}, -] - -[package.dependencies] -fastcore = ">=1.7.2" -packaging = "*" - -[package.extras] -dev = ["jsonref", "matplotlib"] - -[[package]] -name = "goth" -version = "0.19.0" -description = "Golem Test Harness - integration testing framework" -optional = false -python-versions = "^3.10.1" -groups = ["main"] -files = [] -develop = false - -[package.dependencies] -aiohttp = "^3.9.5" -ansicolors = "^1.1.8" -docker = "^7.0" -dpath = "^2.1" -func_timeout = "^4.3" -ghapi = "^1.0" -markupsafe = "^2.1" -pylproxy = "=0.2.3" -pyyaml = "^6.0" -transitions = "^0.9.0" -typing_extensions = "^4.5" -ya-tmp-pyapi = "=0.7.0" - -[package.source] -type = "git" -url = "https://github.com/golemfactory/goth.git" -reference = "77036c67b9c27c2b80aa14a5e13e20971f9217fb" -resolved_reference = "77036c67b9c27c2b80aa14a5e13e20971f9217fb" - -[[package]] -name = "idna" -version = "3.10" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "markupsafe" -version = "2.1.5" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, -] - -[[package]] -name = "multidict" -version = "6.1.0" -description = "multidict implementation" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, - {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, - {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, - {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, - {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, - {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, - {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, - {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, - {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, - {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, - {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, - {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, - {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, - {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, - {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, -] - -[[package]] -name = "mypy" -version = "1.14.1" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, - {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, - {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, - {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, - {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, - {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, - {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, - {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, - {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, - {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, - {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, - {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, - {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, - {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, - {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, - {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -groups = ["dev"] -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "packaging" -version = "24.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, -] - -[[package]] -name = "pastel" -version = "0.2.1" -description = "Bring colors to your terminal." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["dev"] -files = [ - {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, - {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "poethepoet" -version = "0.22.1" -description = "A task runner that works well with poetry." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "poethepoet-0.22.1-py3-none-any.whl", hash = "sha256:1da4cd00d3b2c44b811c91616a744cf71094a26a299ea9956025162d34eef1a5"}, - {file = "poethepoet-0.22.1.tar.gz", hash = "sha256:e758bcac731fa9ac0b812389589541e32b825c4a1894e16fa90aeb1946ba2823"}, -] - -[package.dependencies] -pastel = ">=0.2.1,<0.3.0" -tomli = ">=1.2.2" - -[package.extras] -poetry-plugin = ["poetry (>=1.0,<2.0)"] - -[[package]] -name = "propcache" -version = "0.2.1" -description = "Accelerated property cache" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, - {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, - {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, - {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, - {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, - {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, - {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, - {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, - {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, - {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, - {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, - {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, - {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, -] - -[[package]] -name = "pylproxy" -version = "0.2.3" -description = "Proxy for use in Goth instead of mitmproxy" -optional = false -python-versions = "<4.0,>=3.10" -groups = ["main"] -files = [ - {file = "pylproxy-0.2.3-py3-none-any.whl", hash = "sha256:e60b5910f027b6416c15ace0854155ed0a5e0d37d496dc235412d81489896430"}, - {file = "pylproxy-0.2.3.tar.gz", hash = "sha256:133d26832632de43d589ea6a63e54dc66afa815c833b4fd55941d77c86fa9a41"}, -] - -[package.dependencies] -aiohttp = ">=3.9,<4.0" - -[[package]] -name = "pytest" -version = "7.4.4" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-asyncio" -version = "0.21.0" -description = "Pytest support for asyncio" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "pytest-asyncio-0.21.0.tar.gz", hash = "sha256:2b38a496aef56f56b0e87557ec313e11e1ab9276fc3863f6a7be0f1d0e415e1b"}, - {file = "pytest_asyncio-0.21.0-py3-none-any.whl", hash = "sha256:f2b3366b7cd501a4056858bd39349d5af19742aed2d81660b7998b6341c7eb9c"}, -] - -[package.dependencies] -pytest = ">=7.0.0" - -[package.extras] -docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] -testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] - -[[package]] -name = "pytest-split" -version = "0.8.2" -description = "Pytest plugin which splits the test suite to equally sized sub suites based on test execution time." -optional = false -python-versions = ">=3.7.1,<4.0" -groups = ["main"] -files = [ - {file = "pytest_split-0.8.2-py3-none-any.whl", hash = "sha256:b7fa704659cb224b9f7f5c24536bc04eff351f42d852bf0312e03774fd9c0972"}, - {file = "pytest_split-0.8.2.tar.gz", hash = "sha256:446f330e3607572027f3861058c27d9b3eaa80d83dc86675abe2978bbf50c02f"}, -] - -[package.dependencies] -pytest = ">=5,<9" - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pywin32" -version = "308" -description = "Python for Window Extensions" -optional = false -python-versions = "*" -groups = ["main"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, - {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, - {file = "pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c"}, - {file = "pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a"}, - {file = "pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b"}, - {file = "pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6"}, - {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"}, - {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"}, - {file = "pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091"}, - {file = "pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed"}, - {file = "pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4"}, - {file = "pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd"}, - {file = "pywin32-308-cp37-cp37m-win32.whl", hash = "sha256:1f696ab352a2ddd63bd07430080dd598e6369152ea13a25ebcdd2f503a38f1ff"}, - {file = "pywin32-308-cp37-cp37m-win_amd64.whl", hash = "sha256:13dcb914ed4347019fbec6697a01a0aec61019c1046c2b905410d197856326a6"}, - {file = "pywin32-308-cp38-cp38-win32.whl", hash = "sha256:5794e764ebcabf4ff08c555b31bd348c9025929371763b2183172ff4708152f0"}, - {file = "pywin32-308-cp38-cp38-win_amd64.whl", hash = "sha256:3b92622e29d651c6b783e368ba7d6722b1634b8e70bd376fd7610fe1992e19de"}, - {file = "pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341"}, - {file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "regex" -version = "2024.11.6" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, - {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, - {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, - {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, - {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, - {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, - {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, - {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, - {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, - {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, - {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, - {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, - {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, - {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, -] - -[[package]] -name = "requests" -version = "2.32.3" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, -] - -[[package]] -name = "tomli" -version = "1.2.3" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, - {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, -] - -[[package]] -name = "transitions" -version = "0.9.2" -description = "A lightweight, object-oriented Python state machine implementation with many extensions." -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "transitions-0.9.2-py2.py3-none-any.whl", hash = "sha256:f7b40c9b4a93869f36c4d1c33809aeb18cdeeb065fd1adba018ee39c3db216f3"}, - {file = "transitions-0.9.2.tar.gz", hash = "sha256:2f8490dbdbd419366cef1516032ab06d07ccb5839ef54905e842a472692d4204"}, -] - -[package.dependencies] -six = "*" - -[package.extras] -diagrams = ["pygraphviz"] -test = ["pytest"] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "urllib3" -version = "2.3.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "ya-tmp-pyapi" -version = "0.7.0" -description = "" -optional = false -python-versions = "<4.0,>=3.6" -groups = ["main"] -files = [ - {file = "ya_tmp_pyapi-0.7.0-py3-none-any.whl", hash = "sha256:a6d75aba269b82e603971e7b6cc90b3ed195abed66d44c9af4d5f5030ed25e0f"}, - {file = "ya_tmp_pyapi-0.7.0.tar.gz", hash = "sha256:27e4fda37a46477fa62d6a14118a8e05c5fed9befd5814b55b632bda4fd3bc86"}, -] - -[package.dependencies] -aiohttp = ">=3.6.2,<4.0.0" -certifi = ">=2020.6.20,<2021.0.0" -python-dateutil = ">=2.8.1,<3.0.0" - -[[package]] -name = "yarl" -version = "1.18.3" -description = "Yet another URL library" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, - {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, - {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, - {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, - {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, - {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, - {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, - {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, - {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, - {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, - {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, - {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, - {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" -propcache = ">=0.2.0" - -[metadata] -lock-version = "2.1" -python-versions = "^3.12.8" -content-hash = "1c7378ea9cb0ba3edafc16b7d108ae32de7d4e4632c2cba11d01e3a3114b75f8" +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.6" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohappyeyeballs-2.4.6-py3-none-any.whl", hash = "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1"}, + {file = "aiohappyeyeballs-2.4.6.tar.gz", hash = "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0"}, +] + +[[package]] +name = "aiohttp" +version = "3.11.12" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f"}, + {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854"}, + {file = "aiohttp-3.11.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:584096938a001378484aa4ee54e05dc79c7b9dd933e271c744a97b3b6f644957"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392432a2dde22b86f70dd4a0e9671a349446c93965f261dbaecfaf28813e5c42"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88d385b8e7f3a870146bf5ea31786ef7463e99eb59e31db56e2315535d811f55"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b10a47e5390c4b30a0d58ee12581003be52eedd506862ab7f97da7a66805befb"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5263dcede17b6b0c41ef0c3ccce847d82a7da98709e75cf7efde3e9e3b5cae"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50c5c7b8aa5443304c55c262c5693b108c35a3b61ef961f1e782dd52a2f559c7"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1c031a7572f62f66f1257db37ddab4cb98bfaf9b9434a3b4840bf3560f5e788"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7e44eba534381dd2687be50cbd5f2daded21575242ecfdaf86bbeecbc38dae8e"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:145a73850926018ec1681e734cedcf2716d6a8697d90da11284043b745c286d5"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2c311e2f63e42c1bf86361d11e2c4a59f25d9e7aabdbdf53dc38b885c5435cdb"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ea756b5a7bac046d202a9a3889b9a92219f885481d78cd318db85b15cc0b7bcf"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:526c900397f3bbc2db9cb360ce9c35134c908961cdd0ac25b1ae6ffcaa2507ff"}, + {file = "aiohttp-3.11.12-cp310-cp310-win32.whl", hash = "sha256:b8d3bb96c147b39c02d3db086899679f31958c5d81c494ef0fc9ef5bb1359b3d"}, + {file = "aiohttp-3.11.12-cp310-cp310-win_amd64.whl", hash = "sha256:7fe3d65279bfbee8de0fb4f8c17fc4e893eed2dba21b2f680e930cc2b09075c5"}, + {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb"}, + {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9"}, + {file = "aiohttp-3.11.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b"}, + {file = "aiohttp-3.11.12-cp311-cp311-win32.whl", hash = "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16"}, + {file = "aiohttp-3.11.12-cp311-cp311-win_amd64.whl", hash = "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6"}, + {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250"}, + {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1"}, + {file = "aiohttp-3.11.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9"}, + {file = "aiohttp-3.11.12-cp312-cp312-win32.whl", hash = "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a"}, + {file = "aiohttp-3.11.12-cp312-cp312-win_amd64.whl", hash = "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802"}, + {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:413ad794dccb19453e2b97c2375f2ca3cdf34dc50d18cc2693bd5aed7d16f4b9"}, + {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a93d28ed4b4b39e6f46fd240896c29b686b75e39cc6992692e3922ff6982b4c"}, + {file = "aiohttp-3.11.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d589264dbba3b16e8951b6f145d1e6b883094075283dafcab4cdd564a9e353a0"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5148ca8955affdfeb864aca158ecae11030e952b25b3ae15d4e2b5ba299bad2"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:525410e0790aab036492eeea913858989c4cb070ff373ec3bc322d700bdf47c1"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bd8695be2c80b665ae3f05cb584093a1e59c35ecb7d794d1edd96e8cc9201d7"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0203433121484b32646a5f5ea93ae86f3d9559d7243f07e8c0eab5ff8e3f70e"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40cd36749a1035c34ba8d8aaf221b91ca3d111532e5ccb5fa8c3703ab1b967ed"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7442662afebbf7b4c6d28cb7aab9e9ce3a5df055fc4116cc7228192ad6cb484"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8a2fb742ef378284a50766e985804bd6adb5adb5aa781100b09befdbfa757b65"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cee3b117a8d13ab98b38d5b6bdcd040cfb4181068d05ce0c474ec9db5f3c5bb"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f6a19bcab7fbd8f8649d6595624856635159a6527861b9cdc3447af288a00c00"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e4cecdb52aaa9994fbed6b81d4568427b6002f0a91c322697a4bfcc2b2363f5a"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:30f546358dfa0953db92ba620101fefc81574f87b2346556b90b5f3ef16e55ce"}, + {file = "aiohttp-3.11.12-cp313-cp313-win32.whl", hash = "sha256:ce1bb21fc7d753b5f8a5d5a4bae99566386b15e716ebdb410154c16c91494d7f"}, + {file = "aiohttp-3.11.12-cp313-cp313-win_amd64.whl", hash = "sha256:f7914ab70d2ee8ab91c13e5402122edbc77821c66d2758abb53aabe87f013287"}, + {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c3623053b85b4296cd3925eeb725e386644fd5bc67250b3bb08b0f144803e7b"}, + {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67453e603cea8e85ed566b2700efa1f6916aefbc0c9fcb2e86aaffc08ec38e78"}, + {file = "aiohttp-3.11.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6130459189e61baac5a88c10019b21e1f0c6d00ebc770e9ce269475650ff7f73"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9060addfa4ff753b09392efe41e6af06ea5dd257829199747b9f15bfad819460"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34245498eeb9ae54c687a07ad7f160053911b5745e186afe2d0c0f2898a1ab8a"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dc0fba9a74b471c45ca1a3cb6e6913ebfae416678d90529d188886278e7f3f6"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a478aa11b328983c4444dacb947d4513cb371cd323f3845e53caeda6be5589d5"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c160a04283c8c6f55b5bf6d4cad59bb9c5b9c9cd08903841b25f1f7109ef1259"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:edb69b9589324bdc40961cdf0657815df674f1743a8d5ad9ab56a99e4833cfdd"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ee84c2a22a809c4f868153b178fe59e71423e1f3d6a8cd416134bb231fbf6d3"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bf4480a5438f80e0f1539e15a7eb8b5f97a26fe087e9828e2c0ec2be119a9f72"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b2732ef3bafc759f653a98881b5b9cdef0716d98f013d376ee8dfd7285abf1"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f752e80606b132140883bb262a457c475d219d7163d996dc9072434ffb0784c4"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ab3247d58b393bda5b1c8f31c9edece7162fc13265334217785518dd770792b8"}, + {file = "aiohttp-3.11.12-cp39-cp39-win32.whl", hash = "sha256:0d5176f310a7fe6f65608213cc74f4228e4f4ce9fd10bcb2bb6da8fc66991462"}, + {file = "aiohttp-3.11.12-cp39-cp39-win_amd64.whl", hash = "sha256:74bd573dde27e58c760d9ca8615c41a57e719bff315c9adb6f2a4281a28e8798"}, + {file = "aiohttp-3.11.12.tar.gz", hash = "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.2" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "ansicolors" +version = "1.1.8" +description = "ANSI colors for Python" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "ansicolors-1.1.8-py2.py3-none-any.whl", hash = "sha256:00d2dde5a675579325902536738dd27e4fac1fd68f773fe36c21044eb559e187"}, + {file = "ansicolors-1.1.8.zip", hash = "sha256:99f94f5e3348a0bcd43c82e5fc4414013ccc19d70bd939ad71e0133ce9c372e0"}, +] + +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.11\"" +files = [ + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, +] + +[[package]] +name = "attrs" +version = "25.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, + {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "black" +version = "21.7b0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.6.2" +groups = ["dev"] +files = [ + {file = "black-21.7b0-py3-none-any.whl", hash = "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116"}, + {file = "black-21.7b0.tar.gz", hash = "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219"}, +] + +[package.dependencies] +appdirs = "*" +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.8.1,<1" +regex = ">=2020.1.8" +tomli = ">=0.2.6,<2.0.0" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"] +python2 = ["typed-ast (>=1.4.2)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2020.12.5" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, +] + +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +markers = {main = "sys_platform == \"win32\"", dev = "platform_system == \"Windows\""} + +[[package]] +name = "docker" +version = "7.1.0" +description = "A Python library for the Docker Engine API." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, + {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, +] + +[package.dependencies] +pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} +requests = ">=2.26.0" +urllib3 = ">=1.26.0" + +[package.extras] +dev = ["coverage (==7.2.7)", "pytest (==7.4.2)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.1.0)", "ruff (==0.1.8)"] +docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] +ssh = ["paramiko (>=2.4.3)"] +websockets = ["websocket-client (>=1.3.0)"] + +[[package]] +name = "dpath" +version = "2.2.0" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576"}, + {file = "dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +markers = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastcore" +version = "1.7.29" +description = "Python supercharged for fastai development" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "fastcore-1.7.29-py3-none-any.whl", hash = "sha256:76fd4815eabbed704faca3abfea4b7e1f98b6351ba6c869a2d405f37bc4b0074"}, + {file = "fastcore-1.7.29.tar.gz", hash = "sha256:e7e734cbe58805a22c205341c6671de562a8abba54b13eeb24cdb4486d066e31"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +dev = ["llms-txt", "matplotlib", "nbclassic", "nbdev (>=0.2.39)", "numpy", "pandas", "pillow", "pysymbol_llm", "torch"] + +[[package]] +name = "frozenlist" +version = "1.5.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, +] + +[[package]] +name = "func-timeout" +version = "4.3.5" +description = "Python module which allows you to specify timeouts when calling any existing function. Also provides support for stoppable-threads" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "func_timeout-4.3.5.tar.gz", hash = "sha256:74cd3c428ec94f4edfba81f9b2f14904846d5ffccc27c92433b8b5939b5575dd"}, +] + +[[package]] +name = "ghapi" +version = "1.0.6" +description = "A python client for the GitHub API" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "ghapi-1.0.6-py3-none-any.whl", hash = "sha256:b3d96bf18fcaa2cb7131bad9de2948e2a1c2bb226377a25826f6c80950c57854"}, + {file = "ghapi-1.0.6.tar.gz", hash = "sha256:64fdd9f06d8e3373065c42c2a03e067e2bbb9ca18b583cd6e38a28aaad0224f6"}, +] + +[package.dependencies] +fastcore = ">=1.7.2" +packaging = "*" + +[package.extras] +dev = ["jsonref", "matplotlib"] + +[[package]] +name = "goth" +version = "0.19.0" +description = "Golem Test Harness - integration testing framework" +optional = false +python-versions = "^3.10.1" +groups = ["main"] +files = [] +develop = false + +[package.dependencies] +aiohttp = "^3.9.5" +ansicolors = "^1.1.8" +docker = "^7.0" +dpath = "^2.1" +func_timeout = "^4.3" +ghapi = "^1.0" +markupsafe = "^2.1" +pylproxy = "=0.2.3" +pyyaml = "^6.0" +transitions = "^0.9.0" +typing_extensions = "^4.5" +ya-tmp-pyapi = "=0.7.0" + +[package.source] +type = "git" +url = "https://github.com/golemfactory/goth.git" +reference = "77036c67b9c27c2b80aa14a5e13e20971f9217fb" +resolved_reference = "77036c67b9c27c2b80aa14a5e13e20971f9217fb" + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "multidict" +version = "6.1.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "mypy" +version = "1.15.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, + {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, + {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, + {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, + {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, + {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, + {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, + {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, + {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, + {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, + {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, + {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, + {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, + {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, +] + +[package.dependencies] +mypy_extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing_extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "poethepoet" +version = "0.22.1" +description = "A task runner that works well with poetry." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "poethepoet-0.22.1-py3-none-any.whl", hash = "sha256:1da4cd00d3b2c44b811c91616a744cf71094a26a299ea9956025162d34eef1a5"}, + {file = "poethepoet-0.22.1.tar.gz", hash = "sha256:e758bcac731fa9ac0b812389589541e32b825c4a1894e16fa90aeb1946ba2823"}, +] + +[package.dependencies] +pastel = ">=0.2.1,<0.3.0" +tomli = ">=1.2.2" + +[package.extras] +poetry-plugin = ["poetry (>=1.0,<2.0)"] + +[[package]] +name = "propcache" +version = "0.2.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, +] + +[[package]] +name = "pylproxy" +version = "0.2.3" +description = "Proxy for use in Goth instead of mitmproxy" +optional = false +python-versions = "<4.0,>=3.10" +groups = ["main"] +files = [ + {file = "pylproxy-0.2.3-py3-none-any.whl", hash = "sha256:e60b5910f027b6416c15ace0854155ed0a5e0d37d496dc235412d81489896430"}, + {file = "pylproxy-0.2.3.tar.gz", hash = "sha256:133d26832632de43d589ea6a63e54dc66afa815c833b4fd55941d77c86fa9a41"}, +] + +[package.dependencies] +aiohttp = ">=3.9,<4.0" + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.21.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pytest-asyncio-0.21.0.tar.gz", hash = "sha256:2b38a496aef56f56b0e87557ec313e11e1ab9276fc3863f6a7be0f1d0e415e1b"}, + {file = "pytest_asyncio-0.21.0-py3-none-any.whl", hash = "sha256:f2b3366b7cd501a4056858bd39349d5af19742aed2d81660b7998b6341c7eb9c"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "pytest-split" +version = "0.8.2" +description = "Pytest plugin which splits the test suite to equally sized sub suites based on test execution time." +optional = false +python-versions = ">=3.7.1,<4.0" +groups = ["main"] +files = [ + {file = "pytest_split-0.8.2-py3-none-any.whl", hash = "sha256:b7fa704659cb224b9f7f5c24536bc04eff351f42d852bf0312e03774fd9c0972"}, + {file = "pytest_split-0.8.2.tar.gz", hash = "sha256:446f330e3607572027f3861058c27d9b3eaa80d83dc86675abe2978bbf50c02f"}, +] + +[package.dependencies] +pytest = ">=5,<9" + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pywin32" +version = "308" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, + {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, + {file = "pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c"}, + {file = "pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a"}, + {file = "pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b"}, + {file = "pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6"}, + {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"}, + {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"}, + {file = "pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091"}, + {file = "pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed"}, + {file = "pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4"}, + {file = "pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd"}, + {file = "pywin32-308-cp37-cp37m-win32.whl", hash = "sha256:1f696ab352a2ddd63bd07430080dd598e6369152ea13a25ebcdd2f503a38f1ff"}, + {file = "pywin32-308-cp37-cp37m-win_amd64.whl", hash = "sha256:13dcb914ed4347019fbec6697a01a0aec61019c1046c2b905410d197856326a6"}, + {file = "pywin32-308-cp38-cp38-win32.whl", hash = "sha256:5794e764ebcabf4ff08c555b31bd348c9025929371763b2183172ff4708152f0"}, + {file = "pywin32-308-cp38-cp38-win_amd64.whl", hash = "sha256:3b92622e29d651c6b783e368ba7d6722b1634b8e70bd376fd7610fe1992e19de"}, + {file = "pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341"}, + {file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "regex" +version = "2024.11.6" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "tomli" +version = "1.2.3" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.6" +groups = ["main", "dev"] +files = [ + {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, + {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, +] +markers = {main = "python_version < \"3.11\""} + +[[package]] +name = "transitions" +version = "0.9.2" +description = "A lightweight, object-oriented Python state machine implementation with many extensions." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "transitions-0.9.2-py2.py3-none-any.whl", hash = "sha256:f7b40c9b4a93869f36c4d1c33809aeb18cdeeb065fd1adba018ee39c3db216f3"}, + {file = "transitions-0.9.2.tar.gz", hash = "sha256:2f8490dbdbd419366cef1516032ab06d07ccb5839ef54905e842a472692d4204"}, +] + +[package.dependencies] +six = "*" + +[package.extras] +diagrams = ["pygraphviz"] +test = ["pytest"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "ya-tmp-pyapi" +version = "0.7.0" +description = "" +optional = false +python-versions = "<4.0,>=3.6" +groups = ["main"] +files = [ + {file = "ya_tmp_pyapi-0.7.0-py3-none-any.whl", hash = "sha256:a6d75aba269b82e603971e7b6cc90b3ed195abed66d44c9af4d5f5030ed25e0f"}, + {file = "ya_tmp_pyapi-0.7.0.tar.gz", hash = "sha256:27e4fda37a46477fa62d6a14118a8e05c5fed9befd5814b55b632bda4fd3bc86"}, +] + +[package.dependencies] +aiohttp = ">=3.6.2,<4.0.0" +certifi = ">=2020.6.20,<2021.0.0" +python-dateutil = ">=2.8.1,<3.0.0" + +[[package]] +name = "yarl" +version = "1.18.3" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.0" + +[metadata] +lock-version = "2.1" +python-versions = "^3.10.1,<3.13" +content-hash = "cac81209d97ca794e87ad196f883d46e001f40b962890be5f842412db05493c1" diff --git a/goth_tests/pyproject.toml b/goth_tests/pyproject.toml index 7a0b4e8e13..e7fa736f7a 100644 --- a/goth_tests/pyproject.toml +++ b/goth_tests/pyproject.toml @@ -21,7 +21,7 @@ readme = "README.md" # secondary = true [tool.poetry.dependencies] -python = "^3.12.8" +python = "^3.10.1,<3.13" pytest = "^7.4" pytest-asyncio = "0.21" pytest-split = "^0.8.1"