From 4554788b739993109fdbd4182dad448d40e736f8 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 27 Aug 2025 12:44:39 -0400 Subject: [PATCH 01/12] Add instrumented object store to pyo3-object-store --- pyo3-object_store/Cargo.toml | 2 + pyo3-object_store/src/aws/store.rs | 20 +- .../src/instrumented_object_store.rs | 475 ++++++++++++++++++ pyo3-object_store/src/lib.rs | 2 + 4 files changed, 493 insertions(+), 6 deletions(-) create mode 100644 pyo3-object_store/src/instrumented_object_store.rs diff --git a/pyo3-object_store/Cargo.toml b/pyo3-object_store/Cargo.toml index 76a71f19..bf0bd2ad 100644 --- a/pyo3-object_store/Cargo.toml +++ b/pyo3-object_store/Cargo.toml @@ -32,6 +32,8 @@ pyo3-async-runtimes = { version = "0.25", features = ["tokio-runtime"] } serde = "1" thiserror = "1" tokio = { version = "1.40", features = ["rt-multi-thread"] } +tracing = "0.1" +tracing-futures = { version = "0.2", features = ["futures-03"] } url = "2" [lib] diff --git a/pyo3-object_store/src/aws/store.rs b/pyo3-object_store/src/aws/store.rs index e87c6fc8..8aaaf76f 100644 --- a/pyo3-object_store/src/aws/store.rs +++ b/pyo3-object_store/src/aws/store.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use itertools::Itertools; use object_store::aws::{AmazonS3, AmazonS3Builder, AmazonS3ConfigKey}; -use object_store::ObjectStoreScheme; +use object_store::{ObjectStore, ObjectStoreScheme}; use pyo3::prelude::*; use pyo3::pybacked::PyBackedStr; use pyo3::types::{PyDict, PyString, PyTuple, PyType}; @@ -14,6 +14,7 @@ use crate::aws::credentials::PyAWSCredentialProvider; use crate::client::PyClientOptions; use crate::config::PyConfigValue; use crate::error::{GenericError, ParseUrlError, PyObjectStoreError, PyObjectStoreResult}; +use crate::instrumented_object_store::InstrumentedObjectStore; use crate::path::PyPath; use crate::prefix::MaybePrefixedStore; use crate::retry::PyRetryConfig; @@ -63,20 +64,26 @@ impl S3Config { #[derive(Debug, Clone)] #[pyclass(name = "S3Store", frozen, subclass)] pub struct PyS3Store { - store: Arc>, + store: Arc>>, /// A config used for pickling. This must stay in sync with the underlying store's config. config: S3Config, } -impl AsRef>> for PyS3Store { - fn as_ref(&self) -> &Arc> { +impl AsRef>>> for PyS3Store { + fn as_ref(&self) -> &Arc>> { &self.store } } +// impl AsRef> for PyS3Store { +// fn as_ref(&self) -> &MaybePrefixedStore { +// &self.store.inner() +// } +// } + impl PyS3Store { /// Consume self and return the underlying [`AmazonS3`]. - pub fn into_inner(self) -> Arc> { + pub fn into_inner(self) -> Arc>> { self.store } } @@ -126,8 +133,9 @@ impl PyS3Store { builder = combined_config.clone().apply_config(builder); + let store = MaybePrefixedStore::new(builder.build()?, prefix.clone()); Ok(Self { - store: Arc::new(MaybePrefixedStore::new(builder.build()?, prefix.clone())), + store: Arc::new(InstrumentedObjectStore::new(store, "S3Store")), config: S3Config { prefix, config: combined_config, diff --git a/pyo3-object_store/src/instrumented_object_store.rs b/pyo3-object_store/src/instrumented_object_store.rs new file mode 100644 index 00000000..96c85878 --- /dev/null +++ b/pyo3-object_store/src/instrumented_object_store.rs @@ -0,0 +1,475 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/) Copyright 2025 Datadog, Inc. + +// This is vendored from instrumented_object_store +// https://github.com/datafusion-contrib/datafusion-tracing/blob/8fc214b192ad67114742f8a582292bc06e2b5247/instrumented-object-store/src/instrumented_object_store.rs + +use async_trait::async_trait; +use bytes::Bytes; +use futures::stream::BoxStream; +use futures::StreamExt; +use object_store::{ + path::Path, GetOptions, GetResult, ListResult, MultipartUpload, ObjectMeta, ObjectStore, + PutMultipartOptions, PutOptions, PutPayload, PutResult, Result, UploadPart, +}; +use std::fmt::{Display, Formatter}; +use std::ops::Range; +use tracing::{instrument, Span}; +use tracing_futures::Instrument; + +/// A wrapper around an `ObjectStore` that instruments all public methods with tracing. +#[derive(Clone, Debug)] +pub struct InstrumentedObjectStore { + inner: T, + name: String, +} + +impl InstrumentedObjectStore { + /// Creates a new `InstrumentedObjectStore` wrapping the provided `ObjectStore`. + /// + /// # Arguments + /// + /// * `store` - An `Arc`-wrapped `dyn ObjectStore` to be instrumented. + pub fn new(store: T, name: &str) -> Self { + Self { + inner: store, + name: name.to_owned(), + } + } + + pub fn inner(&self) -> &T { + &self.inner + } +} + +impl AsRef for InstrumentedObjectStore { + fn as_ref(&self) -> &T { + &self.inner + } +} + +trait Instrumentable { + fn record_fields(&self, _: &Span); +} + +impl Instrumentable for () { + fn record_fields(&self, _: &Span) {} +} + +impl Instrumentable for ObjectMeta { + fn record_fields(&self, span: &Span) { + span.record("object_store.result.meta", format!("{self:?}")); + } +} + +impl Instrumentable for GetResult { + fn record_fields(&self, span: &Span) { + self.meta.record_fields(span); + span.record("object_store.result.range", format!("{:?}", self.range)); + } +} + +impl Instrumentable for PutResult { + fn record_fields(&self, span: &Span) { + span.record("object_store.result.e_tag", format!("{:?}", self.e_tag)); + span.record("object_store.result.version", format!("{:?}", self.version)); + } +} + +impl Instrumentable for ListResult { + fn record_fields(&self, span: &Span) { + span.record( + "object_store.result.object_count", + format!("{:?}", self.objects.len()), + ); + } +} + +impl Instrumentable for Bytes { + fn record_fields(&self, span: &Span) { + span.record("object_store.result.content_length", self.len()); + } +} + +impl Instrumentable for Vec { + fn record_fields(&self, span: &Span) { + span.record( + "object_store.result.content_length", + self.iter().map(|b| b.len()).sum::(), + ); + } +} + +fn instrument_result(result: Result) -> Result +where + T: Instrumentable, + E: std::error::Error, +{ + let span = Span::current(); + match &result { + Ok(value) => value.record_fields(&span), + Err(e) => { + span.record("object_store.result.err", format!("{e}")); + } + } + result +} + +impl Display for InstrumentedObjectStore { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.inner, f) + } +} + +#[async_trait] +impl ObjectStore for InstrumentedObjectStore { + /// Save the provided bytes to the specified location with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.put", self.name), + object_store.location = %location, + object_store.content_length = %payload.content_length(), + object_store.result.err = tracing::field::Empty, + object_store.result.e_tag = tracing::field::Empty, + object_store.result.version = tracing::field::Empty, + ) + )] + async fn put(&self, location: &Path, payload: PutPayload) -> Result { + instrument_result(self.inner.put(location, payload).await) + } + + /// Save the provided payload to location with the given options and tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.put_opts", self.name), + object_store.location = %location, + object_store.content_length = %payload.content_length(), + object_store.result.err = tracing::field::Empty, + object_store.result.e_tag = tracing::field::Empty, + object_store.result.version = tracing::field::Empty, + ) + )] + async fn put_opts( + &self, + location: &Path, + payload: PutPayload, + opts: PutOptions, + ) -> Result { + instrument_result(self.inner.put_opts(location, payload, opts).await) + } + + /// Perform a multipart upload with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.put_multipart", self.name), + object_store.location = %location, + ) + )] + async fn put_multipart(&self, location: &Path) -> Result> { + let result = self.inner.put_multipart(location).await?; + Ok(Box::new(InstrumentedMultiPartUpload::new( + result, &self.name, + ))) + } + + /// Perform a multipart upload with options and tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.put_multipart_opts", self.name), + object_store.location = %location, + object_store.options = ?opts, + ) + )] + async fn put_multipart_opts( + &self, + location: &Path, + opts: PutMultipartOptions, + ) -> Result> { + let result = self.inner.put_multipart_opts(location, opts).await?; + Ok(Box::new(InstrumentedMultiPartUpload::new( + result, &self.name, + ))) + } + + /// Return the bytes that are stored at the specified location with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.get", self.name), + object_store.location = %location, + object_store.result.err = tracing::field::Empty, + object_store.result.meta = tracing::field::Empty, + object_store.result.range = tracing::field::Empty, + ) + )] + async fn get(&self, location: &Path) -> Result { + instrument_result(self.inner.get(location).await) + } + + /// Perform a get request with options and tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.get_opts", self.name), + object_store.location = %location, + object_store.options = ?options, + object_store.result.err = tracing::field::Empty, + object_store.result.meta = tracing::field::Empty, + object_store.result.range = tracing::field::Empty, + ) + )] + async fn get_opts(&self, location: &Path, options: GetOptions) -> Result { + instrument_result(self.inner.get_opts(location, options).await) + } + + /// Return the bytes that are stored at the specified location in the given byte range with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.get_range", self.name), + object_store.location = %location, + object_store.range = ?range, + object_store.result.err = tracing::field::Empty, + object_store.result.content_length = tracing::field::Empty, + ) + )] + async fn get_range(&self, location: &Path, range: Range) -> Result { + instrument_result(self.inner.get_range(location, range).await) + } + + /// Return the bytes that are stored at the specified location in the given byte ranges with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.get_ranges", self.name), + object_store.location = %location, + object_store.ranges = ?ranges, + object_store.result.err = tracing::field::Empty, + object_store.result.content_length = tracing::field::Empty, + ) + )] + async fn get_ranges(&self, location: &Path, ranges: &[Range]) -> Result> { + instrument_result(self.inner.get_ranges(location, ranges).await) + } + + /// Return the metadata for the specified location with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.head", self.name), + object_store.location = %location, + object_store.result.err = tracing::field::Empty, + object_store.result.meta = tracing::field::Empty, + ) + )] + async fn head(&self, location: &Path) -> Result { + instrument_result(self.inner.head(location).await) + } + + /// Delete the object at the specified location with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.delete", self.name), + object_store.location = %location, + object_store.result.err = tracing::field::Empty, + ) + )] + async fn delete(&self, location: &Path) -> Result<()> { + instrument_result(self.inner.delete(location).await) + } + + /// Delete all the objects at the specified locations with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.delete", self.name), + ) + )] + fn delete_stream<'a>( + &'a self, + locations: BoxStream<'a, Result>, + ) -> BoxStream<'a, Result> { + self.inner + .delete_stream(locations) + .in_current_span() + .boxed() + } + + /// List all the objects with the given prefix with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.list", self.name), + object_store.prefix = %prefix.unwrap_or(&Path::default()), + ) + )] + fn list(&self, prefix: Option<&Path>) -> BoxStream<'static, Result> { + self.inner.list(prefix).in_current_span().boxed() + } + + /// List all the objects with the given prefix and offset with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.list_with_offset", self.name), + object_store.prefix = %prefix.unwrap_or(&Path::default()), + object_store.offset = %offset, + ) + )] + fn list_with_offset( + &self, + prefix: Option<&Path>, + offset: &Path, + ) -> BoxStream<'static, Result> { + self.inner + .list_with_offset(prefix, offset) + .in_current_span() + .boxed() + } + + /// List objects with the given prefix and delimiter with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.list_with_delimiter", self.name), + object_store.prefix = %prefix.unwrap_or(&Path::default()), + object_store.result.err = tracing::field::Empty, + object_store.result.object_count = tracing::field::Empty, + ) + )] + async fn list_with_delimiter(&self, prefix: Option<&Path>) -> Result { + instrument_result(self.inner.list_with_delimiter(prefix).await) + } + + /// Copy an object from one path to another with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.copy", self.name), + object_store.from = %from, + object_store.to = %to, + object_store.result.err = tracing::field::Empty, + ) + )] + async fn copy(&self, from: &Path, to: &Path) -> Result<()> { + instrument_result(self.inner.copy(from, to).await) + } + + /// Move an object from one path to another with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.rename", self.name), + object_store.from = %from, + object_store.to = %to, + object_store.result.err = tracing::field::Empty, + ) + )] + async fn rename(&self, from: &Path, to: &Path) -> Result<()> { + instrument_result(self.inner.rename(from, to).await) + } + + /// Copy an object only if the destination does not exist with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.copy_if_not_exists", self.name), + object_store.from = %from, + object_store.to = %to, + object_store.result.err = tracing::field::Empty, + ) + )] + async fn copy_if_not_exists(&self, from: &Path, to: &Path) -> Result<()> { + instrument_result(self.inner.copy_if_not_exists(from, to).await) + } + + /// Move an object only if the destination does not exist with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.rename_if_not_exists", self.name), + object_store.from = %from, + object_store.to = %to, + object_store.result.err = tracing::field::Empty, + ) + )] + async fn rename_if_not_exists(&self, from: &Path, to: &Path) -> Result<()> { + instrument_result(self.inner.rename_if_not_exists(from, to).await) + } +} + +/// A wrapper around an `ObjectStore` that instruments all public methods with tracing. +#[derive(Debug)] +struct InstrumentedMultiPartUpload { + inner: Box, + name: String, +} + +impl InstrumentedMultiPartUpload { + /// Creates a new `InstrumentedMultiPartUpload` wrapping the provided `MultipartUpload`. + /// + /// # Arguments + /// + /// * `upload` - A boxed `dyn MultipartUpload` to be instrumented. + fn new(upload: Box, name: &str) -> Self { + Self { + inner: upload, + name: name.to_owned(), + } + } +} + +#[async_trait] +impl MultipartUpload for InstrumentedMultiPartUpload { + /// Upload a part without tracing (too many parts to trace). + fn put_part(&mut self, data: PutPayload) -> UploadPart { + self.inner.put_part(data) + } + + /// Complete the multipart upload with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.complete", self.name), + object_store.result.err = tracing::field::Empty, + object_store.result.e_tag = tracing::field::Empty, + object_store.result.version = tracing::field::Empty, + ) + )] + async fn complete(&mut self) -> Result { + instrument_result(self.inner.complete().await) + } + + /// Abort the multipart upload with tracing. + #[instrument( + skip_all, + fields( + otel.name = format!("{}.abort", self.name), + object_store.result.err = tracing::field::Empty, + ) + )] + async fn abort(&mut self) -> Result<()> { + instrument_result(self.inner.abort().await) + } +} diff --git a/pyo3-object_store/src/lib.rs b/pyo3-object_store/src/lib.rs index 0d7d7fdf..1f57d622 100644 --- a/pyo3-object_store/src/lib.rs +++ b/pyo3-object_store/src/lib.rs @@ -10,6 +10,7 @@ mod credentials; pub(crate) mod error; mod gcp; mod http; +mod instrumented_object_store; mod local; mod memory; mod path; @@ -26,6 +27,7 @@ pub use client::{PyClientConfigKey, PyClientOptions}; pub use error::{PyObjectStoreError, PyObjectStoreResult}; pub use gcp::PyGCSStore; pub use http::PyHttpStore; +pub use instrumented_object_store::InstrumentedObjectStore; pub use local::PyLocalStore; pub use memory::PyMemoryStore; pub use path::PyPath; From bbdefd4478bc47e675faa594565b7624ec51883d Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 27 Aug 2025 14:02:21 -0400 Subject: [PATCH 02/12] Add tracing wrappers to S3/GCS/Azure in pyo3-object-store --- pyo3-object_store/src/aws/store.rs | 8 +------- pyo3-object_store/src/azure/store.rs | 15 +++++++++------ pyo3-object_store/src/gcp/store.rs | 17 +++++++++++------ .../src/instrumented_object_store.rs | 1 + 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/pyo3-object_store/src/aws/store.rs b/pyo3-object_store/src/aws/store.rs index 8aaaf76f..26130fce 100644 --- a/pyo3-object_store/src/aws/store.rs +++ b/pyo3-object_store/src/aws/store.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use itertools::Itertools; use object_store::aws::{AmazonS3, AmazonS3Builder, AmazonS3ConfigKey}; -use object_store::{ObjectStore, ObjectStoreScheme}; +use object_store::ObjectStoreScheme; use pyo3::prelude::*; use pyo3::pybacked::PyBackedStr; use pyo3::types::{PyDict, PyString, PyTuple, PyType}; @@ -75,12 +75,6 @@ impl AsRef>>> for PyS3S } } -// impl AsRef> for PyS3Store { -// fn as_ref(&self) -> &MaybePrefixedStore { -// &self.store.inner() -// } -// } - impl PyS3Store { /// Consume self and return the underlying [`AmazonS3`]. pub fn into_inner(self) -> Arc>> { diff --git a/pyo3-object_store/src/azure/store.rs b/pyo3-object_store/src/azure/store.rs index f643e92b..b74f32c2 100644 --- a/pyo3-object_store/src/azure/store.rs +++ b/pyo3-object_store/src/azure/store.rs @@ -15,7 +15,7 @@ use crate::config::PyConfigValue; use crate::error::{GenericError, ParseUrlError, PyObjectStoreError, PyObjectStoreResult}; use crate::path::PyPath; use crate::retry::PyRetryConfig; -use crate::{MaybePrefixedStore, PyUrl}; +use crate::{InstrumentedObjectStore, MaybePrefixedStore, PyUrl}; #[derive(Debug, Clone, PartialEq)] struct AzureConfig { @@ -69,20 +69,20 @@ impl AzureConfig { #[derive(Debug, Clone)] #[pyclass(name = "AzureStore", frozen, subclass)] pub struct PyAzureStore { - store: Arc>, + store: Arc>>, /// A config used for pickling. This must stay in sync with the underlying store's config. config: AzureConfig, } -impl AsRef>> for PyAzureStore { - fn as_ref(&self) -> &Arc> { +impl AsRef>>> for PyAzureStore { + fn as_ref(&self) -> &Arc>> { &self.store } } impl PyAzureStore { /// Consume self and return the underlying [`MicrosoftAzure`]. - pub fn into_inner(self) -> Arc> { + pub fn into_inner(self) -> Arc>> { self.store } } @@ -144,7 +144,10 @@ impl PyAzureStore { builder = combined_config.clone().apply_config(builder); Ok(Self { - store: Arc::new(MaybePrefixedStore::new(builder.build()?, prefix.clone())), + store: Arc::new(InstrumentedObjectStore::new( + MaybePrefixedStore::new(builder.build()?, prefix.clone()), + "AzureStore", + )), config: AzureConfig { prefix, config: combined_config, diff --git a/pyo3-object_store/src/gcp/store.rs b/pyo3-object_store/src/gcp/store.rs index 4c28193b..a7e6b0e3 100644 --- a/pyo3-object_store/src/gcp/store.rs +++ b/pyo3-object_store/src/gcp/store.rs @@ -15,7 +15,7 @@ use crate::error::{GenericError, ParseUrlError, PyObjectStoreError, PyObjectStor use crate::gcp::credentials::PyGcpCredentialProvider; use crate::path::PyPath; use crate::retry::PyRetryConfig; -use crate::{MaybePrefixedStore, PyUrl}; +use crate::{InstrumentedObjectStore, MaybePrefixedStore, PyUrl}; #[derive(Debug, Clone, PartialEq)] struct GCSConfig { @@ -61,20 +61,22 @@ impl GCSConfig { #[derive(Debug, Clone)] #[pyclass(name = "GCSStore", frozen, subclass)] pub struct PyGCSStore { - store: Arc>, + store: Arc>>, /// A config used for pickling. This must stay in sync with the underlying store's config. config: GCSConfig, } -impl AsRef>> for PyGCSStore { - fn as_ref(&self) -> &Arc> { +impl AsRef>>> for PyGCSStore { + fn as_ref(&self) -> &Arc>> { &self.store } } impl PyGCSStore { /// Consume self and return the underlying [`GoogleCloudStorage`]. - pub fn into_inner(self) -> Arc> { + pub fn into_inner( + self, + ) -> Arc>> { self.store } } @@ -112,7 +114,10 @@ impl PyGCSStore { builder = builder.with_credentials(Arc::new(credential_provider)); } Ok(Self { - store: Arc::new(MaybePrefixedStore::new(builder.build()?, prefix.clone())), + store: Arc::new(InstrumentedObjectStore::new( + MaybePrefixedStore::new(builder.build()?, prefix.clone()), + "GCSStore", + )), config: GCSConfig { prefix, config: combined_config, diff --git a/pyo3-object_store/src/instrumented_object_store.rs b/pyo3-object_store/src/instrumented_object_store.rs index 96c85878..cc499223 100644 --- a/pyo3-object_store/src/instrumented_object_store.rs +++ b/pyo3-object_store/src/instrumented_object_store.rs @@ -53,6 +53,7 @@ impl InstrumentedObjectStore { } } + /// Returns a reference to the inner `ObjectStore`. pub fn inner(&self) -> &T { &self.inner } From bfd7b23c02a546124d4e92bb4320f3ea8c661916 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 27 Aug 2025 14:03:42 -0400 Subject: [PATCH 03/12] Fix compile signer --- obstore/src/signer.rs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/obstore/src/signer.rs b/obstore/src/signer.rs index 9fa11c91..dec07a70 100644 --- a/obstore/src/signer.rs +++ b/obstore/src/signer.rs @@ -15,8 +15,8 @@ use pyo3::prelude::*; use pyo3::pybacked::PyBackedStr; use pyo3_async_runtimes::tokio::get_runtime; use pyo3_object_store::{ - MaybePrefixedStore, PyAzureStore, PyGCSStore, PyObjectStoreError, PyObjectStoreResult, - PyS3Store, PyUrl, + InstrumentedObjectStore, MaybePrefixedStore, PyAzureStore, PyGCSStore, PyObjectStoreError, + PyObjectStoreResult, PyS3Store, PyUrl, }; use url::Url; @@ -24,9 +24,9 @@ use crate::path::PyPaths; #[derive(Debug)] pub(crate) enum SignCapableStore { - S3(Arc>), - Gcs(Arc>), - Azure(Arc>), + S3(Arc>>), + Gcs(Arc>>), + Azure(Arc>>), } impl<'py> FromPyObject<'py> for SignCapableStore { @@ -78,9 +78,21 @@ impl Signer for SignCapableStore { Self: 'async_trait, { match self { - Self::S3(inner) => inner.as_ref().inner().signed_url(method, path, expires_in), - Self::Gcs(inner) => inner.as_ref().inner().signed_url(method, path, expires_in), - Self::Azure(inner) => inner.as_ref().inner().signed_url(method, path, expires_in), + Self::S3(inner) => inner + .as_ref() + .inner() + .inner() + .signed_url(method, path, expires_in), + Self::Gcs(inner) => inner + .as_ref() + .inner() + .inner() + .signed_url(method, path, expires_in), + Self::Azure(inner) => inner + .as_ref() + .inner() + .inner() + .signed_url(method, path, expires_in), } } @@ -99,14 +111,17 @@ impl Signer for SignCapableStore { Self::S3(inner) => inner .as_ref() .inner() + .inner() .signed_urls(method, paths, expires_in), Self::Gcs(inner) => inner .as_ref() .inner() + .inner() .signed_urls(method, paths, expires_in), Self::Azure(inner) => inner .as_ref() .inner() + .inner() .signed_urls(method, paths, expires_in), } } From 6316c20421da29211e70d2139408a4c732f1098b Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 27 Aug 2025 14:45:51 -0400 Subject: [PATCH 04/12] Add and document `init_log` --- .gitignore | 1 - Cargo.lock | 246 +++++++++++++++++++++++++++- obstore/Cargo.toml | 3 + obstore/python/obstore/_obstore.pyi | 2 + obstore/python/obstore/_tracing.pyi | 50 ++++++ obstore/src/lib.rs | 7 + obstore/src/tracing.rs | 97 +++++++++++ 7 files changed, 401 insertions(+), 5 deletions(-) create mode 100644 obstore/python/obstore/_tracing.pyi create mode 100644 obstore/src/tracing.rs diff --git a/.gitignore b/.gitignore index 73102171..80a65764 100644 --- a/.gitignore +++ b/.gitignore @@ -82,7 +82,6 @@ cover/ *.pot # Django stuff: -*.log local_settings.py db.sqlite3 db.sqlite3-journal diff --git a/Cargo.lock b/Cargo.lock index fcd1b40c..2657b4a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -264,7 +264,7 @@ dependencies = [ "memchr", "num", "regex", - "regex-syntax", + "regex-syntax 0.8.5", ] [[package]] @@ -488,6 +488,21 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.4" @@ -525,6 +540,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + [[package]] name = "digest" version = "0.10.7" @@ -1094,6 +1118,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "lexical-core" version = "1.0.5" @@ -1204,6 +1234,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matrixmultiply" version = "0.3.10" @@ -1274,6 +1313,16 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num" version = "0.4.3" @@ -1307,6 +1356,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -1432,6 +1487,9 @@ dependencies = [ "pyo3-object_store", "reqwest", "tokio", + "tracing", + "tracing-appender", + "tracing-subscriber", "url", ] @@ -1447,6 +1505,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.4" @@ -1494,6 +1558,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1530,6 +1614,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1692,6 +1782,8 @@ dependencies = [ "serde", "thiserror 1.0.69", "tokio", + "tracing", + "tracing-futures", "url", ] @@ -1827,8 +1919,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1839,9 +1940,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -2115,6 +2222,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2278,6 +2394,46 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -2461,6 +2617,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror 1.0.69", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.30" @@ -2479,6 +2647,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "futures", + "futures-task", + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -2546,6 +2756,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version_check" version = "0.9.5" @@ -2690,6 +2906,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -2699,6 +2931,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.61.2" diff --git a/obstore/Cargo.toml b/obstore/Cargo.toml index 65900e80..a9e0c54c 100644 --- a/obstore/Cargo.toml +++ b/obstore/Cargo.toml @@ -37,6 +37,9 @@ tokio = { workspace = true, features = [ "rt-multi-thread", "sync", ] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-appender = "0.2" url = { workspace = true } # We opt-in to using rustls as the TLS provider for reqwest, which is the HTTP diff --git a/obstore/python/obstore/_obstore.pyi b/obstore/python/obstore/_obstore.pyi index c9b1f925..93c4d7a5 100644 --- a/obstore/python/obstore/_obstore.pyi +++ b/obstore/python/obstore/_obstore.pyi @@ -40,6 +40,7 @@ from ._put import PutMode, PutResult, UpdateVersion, put, put_async from ._rename import rename, rename_async from ._scheme import parse_scheme from ._sign import HTTP_METHOD, SignCapableStore, sign, sign_async +from ._tracing import init_log __version__: str _object_store_version: str @@ -83,6 +84,7 @@ __all__ = [ "get_ranges_async", "head", "head_async", + "init_log", "list", "list_with_delimiter", "list_with_delimiter_async", diff --git a/obstore/python/obstore/_tracing.pyi b/obstore/python/obstore/_tracing.pyi new file mode 100644 index 00000000..f05881e4 --- /dev/null +++ b/obstore/python/obstore/_tracing.pyi @@ -0,0 +1,50 @@ +from pathlib import Path +from typing import Literal + +def init_log( + dir: Path | str, # noqa: A002 + prefix: str, + *, + suffix: str | None = None, + max_log_files: int | None = None, + rotation: Literal["minutely", "hourly", "daily", "never"] = "never", + level: str | None = None, +) -> None: + """Initialize Rust-level tracing to log to files in the specified directory. + + This function can only be called once per process. If tracing has already been initialized, this function will error without making any changes. + + Args: + dir: Directory to write log files to. + prefix: The prefix for log filenames. The prefix is output before the timestamp in the file name, and if it is non-empty, it is followed by a dot (`.`). + + Keyword Args: + suffix: Sets the suffix for log filenames. The suffix is output after the timestamp in the file name, and if it is non-empty, it is preceded by a dot (.). + max_log_files: Keeps the last n log files on disk. + + When a new log file is created, if there are `n` or more existing log files in the directory, the oldest will be deleted. If no value is supplied, no files will be removed. + + Files are considered candidates for deletion based on the following criteria: + + - The file must not be a directory or symbolic link. + - If `prefix` is passed, the file name must start with that prefix. + - If `suffix` is passed, the file name must end with that suffix. + - If neither `prefix` nor `suffix` are passed, then the file name must parse as a valid date based on the date format. + + Files matching these criteria may be deleted if the maximum number of log files in the directory has been reached. + rotation: How often to rotate log files. Options are: + - "minutely": Create a new log file every minute. + - "hourly": Create a new log file every hour. + - "daily": Create a new log file every day. + - "never": Do not rotate log files (all logs go to a single file). + level: Minimum log level to record. Options include, from lowest to highest importance: + - `"trace"` + - `"debug"` + - `"info"` + - `"warn"` + - `"error"` + - `"off"` + + If not set explicitly, this will also check the `"RUST_LOG"` environment variable, falling back to `"info"` if neither is set. + + """ diff --git a/obstore/src/lib.rs b/obstore/src/lib.rs index 1e10db0b..deb34275 100644 --- a/obstore/src/lib.rs +++ b/obstore/src/lib.rs @@ -1,5 +1,6 @@ // Except for explicit areas where we enable unsafe #![deny(unsafe_code)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] mod attributes; mod buffered; @@ -14,9 +15,12 @@ mod rename; mod scheme; mod signer; mod tags; +mod tracing; mod utils; use pyo3::prelude::*; +// We declare a dependency on reqwest to opt-in to using rustls as the TLS provider +use reqwest as _; const VERSION: &str = env!("CARGO_PKG_VERSION"); const OBJECT_STORE_VERSION: &str = env!("OBJECT_STORE_VERSION"); @@ -85,5 +89,8 @@ fn _obstore(py: Python, m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(signer::sign_async))?; m.add_wrapped(wrap_pyfunction!(signer::sign))?; + // Tracing + m.add_function(wrap_pyfunction!(tracing::init_log, m)?)?; + Ok(()) } diff --git a/obstore/src/tracing.rs b/obstore/src/tracing.rs new file mode 100644 index 00000000..e9c4781e --- /dev/null +++ b/obstore/src/tracing.rs @@ -0,0 +1,97 @@ +use pyo3::exceptions::{PyOSError, PyValueError}; +use pyo3::prelude::*; +use pyo3::pybacked::PyBackedStr; +use pyo3::sync::GILOnceCell; +use std::fs; +use tracing_appender::non_blocking::WorkerGuard; +use tracing_appender::rolling::{RollingFileAppender, Rotation}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; + +static APPENDER_GUARD: GILOnceCell = GILOnceCell::new(); + +pub(crate) struct PyRotation(Rotation); + +impl<'py> FromPyObject<'py> for PyRotation { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + let s = ob.extract::()?.to_ascii_lowercase(); + match s.as_str() { + "minutely" => Ok(Self(Rotation::MINUTELY)), + "hourly" => Ok(Self(Rotation::HOURLY)), + "daily" => Ok(Self(Rotation::DAILY)), + "never" => Ok(Self(Rotation::NEVER)), + other => Err(PyErr::new::(format!( + "rotation must be 'minutely', 'daily', 'hourly', or 'never', got '{other}'" + ))), + } + } +} + +/// Initialize Rust `tracing` to write to a rotating file. +/// - `dir`: directory for logs (created if missing) +/// - `filename`: base file name, e.g. "app.log" +/// - `rotation`: "daily" (default) or "hourly" +/// - `level`: optional EnvFilter string; falls back to RUST_LOG; else "info" +/// Returns: True if this call performed initialization; False if tracing was already set. +#[pyfunction(signature = (dir, prefix, *, suffix=None, max_log_files=None, rotation=PyRotation(Rotation::NEVER), level=None))] +pub(crate) fn init_log( + py: Python, + dir: std::path::PathBuf, + prefix: String, + suffix: Option, + max_log_files: Option, + rotation: PyRotation, + level: Option<&str>, +) -> PyResult<()> { + // Ensure log directory exists + fs::create_dir_all(&dir).map_err(|e| PyOSError::new_err(format!("create_dir_all: {e}")))?; + + let mut file_appender_builder = RollingFileAppender::builder() + .rotation(rotation.0) + .filename_prefix(prefix); + if let Some(suffix) = suffix { + file_appender_builder = file_appender_builder.filename_suffix(suffix); + } + if let Some(n) = max_log_files { + if n == 0 { + return Err(PyValueError::new_err("max_log_files must be positive")); + } + file_appender_builder = file_appender_builder.max_log_files(n); + } + let file_appender = file_appender_builder + .build(dir) + .map_err(|err| PyOSError::new_err(format!("Error building log appender: {err}")))?; + + // Non-blocking writer + guard (guard must be held for lifetime of program to flush on drop) + let (nb_writer, guard) = tracing_appender::non_blocking(file_appender); + + // Try to store the guard to keep the non-blocking writer alive + // If already set, we might have a problem + if APPENDER_GUARD.set(py, guard).is_err() { + // Guard is already set - this might cause issues with the current call + // But we'll proceed anyway since the subscriber might still work + } + + let filter = if let Some(spec) = level { + EnvFilter::try_new(spec).map_err(|err| PyValueError::new_err(err.to_string()))? + } else if let Ok(from_env) = EnvFilter::try_from_default_env() { + from_env + } else { + EnvFilter::new("info") + }; + + // Try to install the global subscriber. If already set (by us or someone else), + // try_init() returns Err; we report that we didn't initialize. + tracing_subscriber::registry() + .with(filter) + .with( + tracing_subscriber::fmt::layer() + .with_writer(nb_writer) + .with_ansi(false) // plain text in files + .with_target(true) // include module path (useful in prod) + .compact(), + ) + .try_init().map_err(|err| PyValueError::new_err(format!( + "Error initializing tracing subscriber: {err}. This usually means tracing was already initialized by another library or a previous call to init_log.")))?; + + Ok(()) +} From 98a0bc7531feab051262af5c89a176c309b9911e Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 27 Aug 2025 14:48:25 -0400 Subject: [PATCH 05/12] Add to local and http --- pyo3-object_store/src/http.rs | 12 ++++++------ pyo3-object_store/src/local.rs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pyo3-object_store/src/http.rs b/pyo3-object_store/src/http.rs index 87e7e683..9729240c 100644 --- a/pyo3-object_store/src/http.rs +++ b/pyo3-object_store/src/http.rs @@ -7,7 +7,7 @@ use pyo3::{intern, IntoPyObjectExt}; use crate::error::PyObjectStoreResult; use crate::retry::PyRetryConfig; -use crate::{PyClientOptions, PyUrl}; +use crate::{InstrumentedObjectStore, PyClientOptions, PyUrl}; #[derive(Debug, Clone, PartialEq)] struct HTTPConfig { @@ -38,20 +38,20 @@ impl HTTPConfig { pub struct PyHttpStore { // Note: we don't need to wrap this in a MaybePrefixedStore because the HttpStore manages its // own prefix. - store: Arc, + store: Arc>, /// A config used for pickling. This must stay in sync with the underlying store's config. config: HTTPConfig, } -impl AsRef> for PyHttpStore { - fn as_ref(&self) -> &Arc { +impl AsRef>> for PyHttpStore { + fn as_ref(&self) -> &Arc> { &self.store } } impl PyHttpStore { /// Consume self and return the underlying [`HttpStore`]. - pub fn into_inner(self) -> Arc { + pub fn into_inner(self) -> Arc> { self.store } } @@ -73,7 +73,7 @@ impl PyHttpStore { builder = builder.with_retry(retry_config.into()) } Ok(Self { - store: Arc::new(builder.build()?), + store: Arc::new(InstrumentedObjectStore::new(builder.build()?, "HTTPStore")), config: HTTPConfig { url, client_options, diff --git a/pyo3-object_store/src/local.rs b/pyo3-object_store/src/local.rs index 9b416d5c..3c3e8fe7 100644 --- a/pyo3-object_store/src/local.rs +++ b/pyo3-object_store/src/local.rs @@ -9,7 +9,7 @@ use pyo3::types::{PyDict, PyTuple, PyType}; use pyo3::{intern, IntoPyObjectExt}; use crate::error::PyObjectStoreResult; -use crate::PyUrl; +use crate::{InstrumentedObjectStore, PyUrl}; #[derive(Clone, Debug, PartialEq)] struct LocalConfig { @@ -32,19 +32,19 @@ impl LocalConfig { #[derive(Debug, Clone)] #[pyclass(name = "LocalStore", frozen, subclass)] pub struct PyLocalStore { - store: Arc, + store: Arc>, config: LocalConfig, } -impl AsRef> for PyLocalStore { - fn as_ref(&self) -> &Arc { +impl AsRef>> for PyLocalStore { + fn as_ref(&self) -> &Arc> { &self.store } } impl PyLocalStore { /// Consume self and return the underlying [`LocalFileSystem`]. - pub fn into_inner(self) -> Arc { + pub fn into_inner(self) -> Arc> { self.store } } @@ -68,7 +68,7 @@ impl PyLocalStore { }; let fs = fs.with_automatic_cleanup(automatic_cleanup); Ok(Self { - store: Arc::new(fs), + store: Arc::new(InstrumentedObjectStore::new(fs, "LocalStore")), config: LocalConfig { prefix, automatic_cleanup, From 45dc474f0ca1e77a73155c9afc536fadf2a39d91 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 27 Aug 2025 15:06:24 -0400 Subject: [PATCH 06/12] Add tracing test --- obstore/python/obstore/_tracing.pyi | 4 +++- tests/test_tracing.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/test_tracing.py diff --git a/obstore/python/obstore/_tracing.pyi b/obstore/python/obstore/_tracing.pyi index f05881e4..149171f0 100644 --- a/obstore/python/obstore/_tracing.pyi +++ b/obstore/python/obstore/_tracing.pyi @@ -8,7 +8,9 @@ def init_log( suffix: str | None = None, max_log_files: int | None = None, rotation: Literal["minutely", "hourly", "daily", "never"] = "never", - level: str | None = None, + level: Literal["trace", "debug", "info", "warn", "error", "off"] + | str + | None = None, ) -> None: """Initialize Rust-level tracing to log to files in the specified directory. diff --git a/tests/test_tracing.py b/tests/test_tracing.py new file mode 100644 index 00000000..c9f37c6b --- /dev/null +++ b/tests/test_tracing.py @@ -0,0 +1,23 @@ +from pathlib import Path +from tempfile import TemporaryDirectory + +from obstore import init_log +from obstore.store import S3Store + + +def test_tracing(s3_store: S3Store): + # Create a temp directory for logs + with TemporaryDirectory() as temp_dir: + log_dir = Path(temp_dir) / "logs" + log_file = "test_trace.log" + + init_log(log_dir, log_file, rotation="never", level="trace") + + _items = s3_store.list().collect() + + with (log_dir / log_file).open() as f: + content = f.read() + + assert "TRACE list" in content + + print(log_dir / log_file) # noqa: T201 From 687fc9ab76450e5d879ed26d5305369446542d2c Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 28 Aug 2025 16:59:53 -0400 Subject: [PATCH 07/12] JSON log output --- Cargo.lock | 13 +++++++++++++ obstore/Cargo.toml | 2 +- obstore/src/tracing.rs | 2 +- tests/test_tracing.py | 7 ++++--- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2657b4a2..6fa88e30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2673,6 +2673,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.19" @@ -2683,12 +2693,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] diff --git a/obstore/Cargo.toml b/obstore/Cargo.toml index a9e0c54c..2a967efc 100644 --- a/obstore/Cargo.toml +++ b/obstore/Cargo.toml @@ -38,7 +38,7 @@ tokio = { workspace = true, features = [ "sync", ] } tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } tracing-appender = "0.2" url = { workspace = true } diff --git a/obstore/src/tracing.rs b/obstore/src/tracing.rs index e9c4781e..22c415ed 100644 --- a/obstore/src/tracing.rs +++ b/obstore/src/tracing.rs @@ -85,10 +85,10 @@ pub(crate) fn init_log( .with(filter) .with( tracing_subscriber::fmt::layer() + .json() .with_writer(nb_writer) .with_ansi(false) // plain text in files .with_target(true) // include module path (useful in prod) - .compact(), ) .try_init().map_err(|err| PyValueError::new_err(format!( "Error initializing tracing subscriber: {err}. This usually means tracing was already initialized by another library or a previous call to init_log.")))?; diff --git a/tests/test_tracing.py b/tests/test_tracing.py index c9f37c6b..b25fcde6 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -1,3 +1,4 @@ +import json from pathlib import Path from tempfile import TemporaryDirectory @@ -7,7 +8,7 @@ def test_tracing(s3_store: S3Store): # Create a temp directory for logs - with TemporaryDirectory() as temp_dir: + with TemporaryDirectory(delete=False) as temp_dir: log_dir = Path(temp_dir) / "logs" log_file = "test_trace.log" @@ -16,8 +17,8 @@ def test_tracing(s3_store: S3Store): _items = s3_store.list().collect() with (log_dir / log_file).open() as f: - content = f.read() + lines = [json.loads(line) for line in f if line.strip()] - assert "TRACE list" in content + assert lines[0]["level"] == "TRACE" print(log_dir / log_file) # noqa: T201 From 9bd2c2cbd2fd54eee3639e909e385fe82a8e268a Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 28 Aug 2025 17:01:49 -0400 Subject: [PATCH 08/12] Remove delete=False --- tests/test_tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_tracing.py b/tests/test_tracing.py index b25fcde6..559456d9 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -8,7 +8,7 @@ def test_tracing(s3_store: S3Store): # Create a temp directory for logs - with TemporaryDirectory(delete=False) as temp_dir: + with TemporaryDirectory() as temp_dir: log_dir = Path(temp_dir) / "logs" log_file = "test_trace.log" From 75134e01c112ee1ac1091c98934555c425029929 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 28 Aug 2025 18:22:53 -0400 Subject: [PATCH 09/12] config for each stdout/stderr --- obstore/src/tracing.rs | 221 +++++++++++++++++++++++++++++++++-------- tests/test_tracing.py | 21 ++-- 2 files changed, 196 insertions(+), 46 deletions(-) diff --git a/obstore/src/tracing.rs b/obstore/src/tracing.rs index 22c415ed..98ceb633 100644 --- a/obstore/src/tracing.rs +++ b/obstore/src/tracing.rs @@ -5,10 +5,13 @@ use pyo3::sync::GILOnceCell; use std::fs; use tracing_appender::non_blocking::WorkerGuard; use tracing_appender::rolling::{RollingFileAppender, Rotation}; +use tracing_subscriber::fmt::MakeWriter; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; +use tracing_subscriber::{Layer, Registry}; static APPENDER_GUARD: GILOnceCell = GILOnceCell::new(); +#[derive(Clone)] pub(crate) struct PyRotation(Rotation); impl<'py> FromPyObject<'py> for PyRotation { @@ -32,45 +35,45 @@ impl<'py> FromPyObject<'py> for PyRotation { /// - `rotation`: "daily" (default) or "hourly" /// - `level`: optional EnvFilter string; falls back to RUST_LOG; else "info" /// Returns: True if this call performed initialization; False if tracing was already set. -#[pyfunction(signature = (dir, prefix, *, suffix=None, max_log_files=None, rotation=PyRotation(Rotation::NEVER), level=None))] +#[pyfunction(signature = ( *, stderr=None, stdout=None, file=None, level=None))] pub(crate) fn init_log( py: Python, - dir: std::path::PathBuf, - prefix: String, - suffix: Option, - max_log_files: Option, - rotation: PyRotation, + stderr: Option, + stdout: Option, + file: Option, level: Option<&str>, ) -> PyResult<()> { - // Ensure log directory exists - fs::create_dir_all(&dir).map_err(|e| PyOSError::new_err(format!("create_dir_all: {e}")))?; - - let mut file_appender_builder = RollingFileAppender::builder() - .rotation(rotation.0) - .filename_prefix(prefix); - if let Some(suffix) = suffix { - file_appender_builder = file_appender_builder.filename_suffix(suffix); + // Collect all layers into a Vec + let mut layers: Vec + Send + Sync>> = Vec::new(); + + if let Some(stderr_config) = stderr { + layers.push(create_log_layer(stderr_config, std::io::stderr)); } - if let Some(n) = max_log_files { - if n == 0 { - return Err(PyValueError::new_err("max_log_files must be positive")); - } - file_appender_builder = file_appender_builder.max_log_files(n); + if let Some(stdout_config) = stdout { + layers.push(create_log_layer(stdout_config, std::io::stdout)); } - let file_appender = file_appender_builder - .build(dir) - .map_err(|err| PyOSError::new_err(format!("Error building log appender: {err}")))?; - - // Non-blocking writer + guard (guard must be held for lifetime of program to flush on drop) - let (nb_writer, guard) = tracing_appender::non_blocking(file_appender); - - // Try to store the guard to keep the non-blocking writer alive - // If already set, we might have a problem - if APPENDER_GUARD.set(py, guard).is_err() { - // Guard is already set - this might cause issues with the current call - // But we'll proceed anyway since the subscriber might still work + if let Some(file_config) = file { + let appender = file_config.file.create_appender()?; + let (nb_writer, guard) = tracing_appender::non_blocking(appender); + // Try to store the guard to keep the non-blocking writer alive + // If already set, we might have a problem + if APPENDER_GUARD.set(py, guard).is_err() { + // Guard is already set - this might cause issues with the current call + // But we'll proceed anyway since the subscriber might still work + } + let file_layer = create_log_layer(file_config.into(), nb_writer); + layers.push(file_layer); } + // // Add file layer + // let file_layer = tracing_subscriber::fmt::layer() + // .with_writer(nb_writer) + // .with_ansi(false) // plain text in files + // .with_target(true) // include module path (useful in prod) + // .json() + // .boxed(); + // layers.push(file_layer); + let filter = if let Some(spec) = level { EnvFilter::try_new(spec).map_err(|err| PyValueError::new_err(err.to_string()))? } else if let Ok(from_env) = EnvFilter::try_from_default_env() { @@ -79,19 +82,157 @@ pub(crate) fn init_log( EnvFilter::new("info") }; - // Try to install the global subscriber. If already set (by us or someone else), - // try_init() returns Err; we report that we didn't initialize. + // Build subscriber with all layers at once using Vec - layers first, then filter tracing_subscriber::registry() + .with(layers) .with(filter) - .with( - tracing_subscriber::fmt::layer() - .json() - .with_writer(nb_writer) - .with_ansi(false) // plain text in files - .with_target(true) // include module path (useful in prod) - ) .try_init().map_err(|err| PyValueError::new_err(format!( "Error initializing tracing subscriber: {err}. This usually means tracing was already initialized by another library or a previous call to init_log.")))?; Ok(()) } + +#[derive(Clone)] +enum LogFormat { + Compact, + Pretty, + Json, +} + +impl<'py> FromPyObject<'py> for LogFormat { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + let s = ob.extract::()?.to_ascii_lowercase(); + match s.as_str() { + "compact" => Ok(Self::Compact), + "pretty" => Ok(Self::Pretty), + "json" => Ok(Self::Json), + other => Err(PyErr::new::(format!( + "format must be 'compact', 'pretty', or 'json', got '{other}'" + ))), + } + } +} + +#[derive(Clone, FromPyObject)] +struct FileConfig { + dir: std::path::PathBuf, + prefix: String, + suffix: Option, + max_log_files: Option, + rotation: PyRotation, +} + +impl FileConfig { + fn create_appender(&self) -> PyResult { + // Ensure log directory exists + fs::create_dir_all(&self.dir) + .map_err(|e| PyOSError::new_err(format!("create_dir_all: {e}")))?; + + let mut builder = RollingFileAppender::builder() + .rotation(self.rotation.0.clone()) + .filename_prefix(&self.prefix); + if let Some(suffix) = &self.suffix { + builder = builder.filename_suffix(suffix); + } + if let Some(n) = self.max_log_files { + if n == 0 { + return Err(PyValueError::new_err("max_log_files must be positive")); + } + builder = builder.max_log_files(n); + } + builder + .build(&self.dir) + .map_err(|err| PyOSError::new_err(format!("Error building log appender: {err}"))) + } +} + +#[derive(Clone, FromPyObject)] +#[pyo3(from_item_all)] +pub(crate) struct FileLogConfig { + file: FileConfig, + format: LogFormat, + #[pyo3(default)] + ansi: Option, + #[pyo3(default)] + show_target: Option, + #[pyo3(default)] + show_thread_names: Option, + #[pyo3(default)] + show_thread_ids: Option, + #[pyo3(default)] + show_level: Option, + #[pyo3(default)] + show_filename: Option, + #[pyo3(default)] + show_line_number: Option, +} + +impl From for LogLayerConfig { + fn from(file_log_config: FileLogConfig) -> Self { + LogLayerConfig { + format: file_log_config.format, + ansi: file_log_config.ansi, + show_target: file_log_config.show_target, + show_thread_names: file_log_config.show_thread_names, + show_thread_ids: file_log_config.show_thread_ids, + show_level: file_log_config.show_level, + show_filename: file_log_config.show_filename, + show_line_number: file_log_config.show_line_number, + } + } +} + +#[derive(Clone, FromPyObject)] +#[pyo3(from_item_all)] +pub(crate) struct LogLayerConfig { + format: LogFormat, + #[pyo3(default)] + ansi: Option, + #[pyo3(default)] + show_target: Option, + #[pyo3(default)] + show_thread_names: Option, + #[pyo3(default)] + show_thread_ids: Option, + #[pyo3(default)] + show_level: Option, + #[pyo3(default)] + show_filename: Option, + #[pyo3(default)] + show_line_number: Option, +} + +fn create_log_layer MakeWriter<'writer> + Send + Sync + 'static>( + config: LogLayerConfig, + writer: W2, +) -> Box + Send + Sync> { + let mut configured_layer = tracing_subscriber::fmt::layer().with_writer(writer); + + if let Some(ansi) = config.ansi { + configured_layer = configured_layer.with_ansi(ansi); + } + if let Some(show_target) = config.show_target { + configured_layer = configured_layer.with_target(show_target); + } + if let Some(show_thread_names) = config.show_thread_names { + configured_layer = configured_layer.with_thread_names(show_thread_names); + } + if let Some(show_thread_ids) = config.show_thread_ids { + configured_layer = configured_layer.with_thread_ids(show_thread_ids); + } + if let Some(show_level) = config.show_level { + configured_layer = configured_layer.with_level(show_level); + } + if let Some(show_filename) = config.show_filename { + configured_layer = configured_layer.with_file(show_filename); + } + if let Some(show_line_number) = config.show_line_number { + configured_layer = configured_layer.with_line_number(show_line_number); + } + + match config.format { + LogFormat::Compact => configured_layer.compact().boxed(), + LogFormat::Pretty => configured_layer.pretty().boxed(), + LogFormat::Json => configured_layer.json().boxed(), + } +} diff --git a/tests/test_tracing.py b/tests/test_tracing.py index 559456d9..729db40b 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -1,4 +1,3 @@ -import json from pathlib import Path from tempfile import TemporaryDirectory @@ -8,17 +7,27 @@ def test_tracing(s3_store: S3Store): # Create a temp directory for logs - with TemporaryDirectory() as temp_dir: + with TemporaryDirectory(delete=False) as temp_dir: log_dir = Path(temp_dir) / "logs" log_file = "test_trace.log" - init_log(log_dir, log_file, rotation="never", level="trace") + stderr_config = { + "format": "json", + "show_target": True, + "show_level": True, + } + stdout_config = { + "format": "pretty", + "show_target": False, + "show_level": False, + } + init_log(stderr=stderr_config, stdout=stdout_config, level="trace") _items = s3_store.list().collect() - with (log_dir / log_file).open() as f: - lines = [json.loads(line) for line in f if line.strip()] + # with (log_dir / log_file).open() as f: + # lines = [json.loads(line) for line in f if line.strip()] - assert lines[0]["level"] == "TRACE" + # assert lines[0]["level"] == "TRACE" print(log_dir / log_file) # noqa: T201 From d0d866813fa6c3a56ab7f6a8f03f02dbda8f96a6 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 28 Aug 2025 18:24:43 -0400 Subject: [PATCH 10/12] config for each stdout/stderr --- obstore/src/tracing.rs | 10 +++++----- tests/test_tracing.py | 10 ++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/obstore/src/tracing.rs b/obstore/src/tracing.rs index 98ceb633..b1fb8cfe 100644 --- a/obstore/src/tracing.rs +++ b/obstore/src/tracing.rs @@ -152,7 +152,7 @@ pub(crate) struct FileLogConfig { file: FileConfig, format: LogFormat, #[pyo3(default)] - ansi: Option, + show_ansi: Option, #[pyo3(default)] show_target: Option, #[pyo3(default)] @@ -171,7 +171,7 @@ impl From for LogLayerConfig { fn from(file_log_config: FileLogConfig) -> Self { LogLayerConfig { format: file_log_config.format, - ansi: file_log_config.ansi, + show_ansi: file_log_config.show_ansi, show_target: file_log_config.show_target, show_thread_names: file_log_config.show_thread_names, show_thread_ids: file_log_config.show_thread_ids, @@ -187,7 +187,7 @@ impl From for LogLayerConfig { pub(crate) struct LogLayerConfig { format: LogFormat, #[pyo3(default)] - ansi: Option, + show_ansi: Option, #[pyo3(default)] show_target: Option, #[pyo3(default)] @@ -208,8 +208,8 @@ fn create_log_layer MakeWriter<'writer> + Send + Sync + 'static ) -> Box + Send + Sync> { let mut configured_layer = tracing_subscriber::fmt::layer().with_writer(writer); - if let Some(ansi) = config.ansi { - configured_layer = configured_layer.with_ansi(ansi); + if let Some(show_ansi) = config.show_ansi { + configured_layer = configured_layer.with_ansi(show_ansi); } if let Some(show_target) = config.show_target { configured_layer = configured_layer.with_target(show_target); diff --git a/tests/test_tracing.py b/tests/test_tracing.py index 729db40b..91d559ad 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -13,13 +13,15 @@ def test_tracing(s3_store: S3Store): stderr_config = { "format": "json", - "show_target": True, - "show_level": True, + "show_ansi": False, + "show_target": False, + "show_level": False, } stdout_config = { "format": "pretty", - "show_target": False, - "show_level": False, + "show_ansi": True, + "show_target": True, + "show_level": True, } init_log(stderr=stderr_config, stdout=stdout_config, level="trace") From 545b83bcdb0656763255fce2e2468e5940d4a341 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 28 Aug 2025 18:31:05 -0400 Subject: [PATCH 11/12] destination-specific level specifier --- obstore/src/tracing.rs | 25 ++++++++++++++++++++----- tests/test_tracing.py | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/obstore/src/tracing.rs b/obstore/src/tracing.rs index b1fb8cfe..9d4375fa 100644 --- a/obstore/src/tracing.rs +++ b/obstore/src/tracing.rs @@ -47,10 +47,10 @@ pub(crate) fn init_log( let mut layers: Vec + Send + Sync>> = Vec::new(); if let Some(stderr_config) = stderr { - layers.push(create_log_layer(stderr_config, std::io::stderr)); + layers.push(create_log_layer(stderr_config, std::io::stderr)?); } if let Some(stdout_config) = stdout { - layers.push(create_log_layer(stdout_config, std::io::stdout)); + layers.push(create_log_layer(stdout_config, std::io::stdout)?); } if let Some(file_config) = file { let appender = file_config.file.create_appender()?; @@ -61,7 +61,7 @@ pub(crate) fn init_log( // Guard is already set - this might cause issues with the current call // But we'll proceed anyway since the subscriber might still work } - let file_layer = create_log_layer(file_config.into(), nb_writer); + let file_layer = create_log_layer(file_config.into(), nb_writer)?; layers.push(file_layer); } @@ -165,6 +165,8 @@ pub(crate) struct FileLogConfig { show_filename: Option, #[pyo3(default)] show_line_number: Option, + #[pyo3(default)] + level: Option, } impl From for LogLayerConfig { @@ -178,6 +180,7 @@ impl From for LogLayerConfig { show_level: file_log_config.show_level, show_filename: file_log_config.show_filename, show_line_number: file_log_config.show_line_number, + level: file_log_config.level, } } } @@ -200,12 +203,14 @@ pub(crate) struct LogLayerConfig { show_filename: Option, #[pyo3(default)] show_line_number: Option, + #[pyo3(default)] + level: Option, } fn create_log_layer MakeWriter<'writer> + Send + Sync + 'static>( config: LogLayerConfig, writer: W2, -) -> Box + Send + Sync> { +) -> PyResult + Send + Sync>> { let mut configured_layer = tracing_subscriber::fmt::layer().with_writer(writer); if let Some(show_ansi) = config.show_ansi { @@ -230,9 +235,19 @@ fn create_log_layer MakeWriter<'writer> + Send + Sync + 'static configured_layer = configured_layer.with_line_number(show_line_number); } - match config.format { + let base_layer = match config.format { LogFormat::Compact => configured_layer.compact().boxed(), LogFormat::Pretty => configured_layer.pretty().boxed(), LogFormat::Json => configured_layer.json().boxed(), + }; + + // Apply per-layer filter if specified + if let Some(level_spec) = config.level { + let filter = EnvFilter::try_new(&level_spec).map_err(|err| { + PyValueError::new_err(format!("Invalid level filter '{}': {}", level_spec, err)) + })?; + Ok(base_layer.with_filter(filter).boxed()) + } else { + Ok(base_layer) } } diff --git a/tests/test_tracing.py b/tests/test_tracing.py index 91d559ad..fe017002 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -16,12 +16,14 @@ def test_tracing(s3_store: S3Store): "show_ansi": False, "show_target": False, "show_level": False, + "level": "trace", } stdout_config = { "format": "pretty", "show_ansi": True, "show_target": True, "show_level": True, + "level": "debug", } init_log(stderr=stderr_config, stdout=stdout_config, level="trace") From da60e1100b7e0043f48a16b834c807d4994feed0 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Tue, 14 Oct 2025 09:45:27 -0400 Subject: [PATCH 12/12] update lockfile --- Cargo.lock | 573 +++++++++++++++++++++-------------------------------- 1 file changed, 225 insertions(+), 348 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 937ddd10..bf447ea5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "ahash" version = "0.8.12" @@ -51,9 +36,9 @@ dependencies = [ [[package]] name = "arrow" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c26b57282a08ae92f727497805122fec964c6245cfa0e13f0e75452eaf3bc41f" +checksum = "6e833808ff2d94ed40d9379848a950d995043c7fb3e81a30b383f4c6033821cc" dependencies = [ "arrow-arith", "arrow-array", @@ -72,9 +57,9 @@ dependencies = [ [[package]] name = "arrow-arith" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cebf38ca279120ff522f4954b81a39527425b6e9f615e6b72842f4de1ffe02b8" +checksum = "ad08897b81588f60ba983e3ca39bda2b179bdd84dced378e7df81a5313802ef8" dependencies = [ "arrow-array", "arrow-buffer", @@ -86,9 +71,9 @@ dependencies = [ [[package]] name = "arrow-array" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744109142cdf8e7b02795e240e20756c2a782ac9180d4992802954a8f871c0de" +checksum = "8548ca7c070d8db9ce7aa43f37393e4bfcf3f2d3681df278490772fd1673d08d" dependencies = [ "ahash", "arrow-buffer", @@ -97,15 +82,15 @@ dependencies = [ "chrono", "chrono-tz", "half", - "hashbrown 0.15.5", + "hashbrown", "num", ] [[package]] name = "arrow-buffer" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601bb103c4c374bcd1f62c66bcea67b42a2ee91a690486c37d4c180236f11ccc" +checksum = "e003216336f70446457e280807a73899dd822feaf02087d31febca1363e2fccc" dependencies = [ "bytes", "half", @@ -114,9 +99,9 @@ dependencies = [ [[package]] name = "arrow-cast" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed61d9d73eda8df9e3014843def37af3050b5080a9acbe108f045a316d5a0be" +checksum = "919418a0681298d3a77d1a315f625916cb5678ad0d74b9c60108eb15fd083023" dependencies = [ "arrow-array", "arrow-buffer", @@ -135,9 +120,9 @@ dependencies = [ [[package]] name = "arrow-csv" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95b96ce0c06b4d33ac958370db8c0d31e88e54f9d6e08b0353d18374d9f991" +checksum = "bfa9bf02705b5cf762b6f764c65f04ae9082c7cfc4e96e0c33548ee3f67012eb" dependencies = [ "arrow-array", "arrow-cast", @@ -150,9 +135,9 @@ dependencies = [ [[package]] name = "arrow-data" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43407f2c6ba2367f64d85d4603d6fb9c4b92ed79d2ffd21021b37efa96523e12" +checksum = "a5c64fff1d142f833d78897a772f2e5b55b36cb3e6320376f0961ab0db7bd6d0" dependencies = [ "arrow-buffer", "arrow-schema", @@ -162,9 +147,9 @@ dependencies = [ [[package]] name = "arrow-ipc" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b0487c4d2ad121cbc42c4db204f1509f8618e589bc77e635e9c40b502e3b90" +checksum = "1d3594dcddccc7f20fd069bc8e9828ce37220372680ff638c5e00dea427d88f5" dependencies = [ "arrow-array", "arrow-buffer", @@ -176,9 +161,9 @@ dependencies = [ [[package]] name = "arrow-json" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d747573390905905a2dc4c5a61a96163fe2750457f90a04ee2a88680758c79" +checksum = "88cf36502b64a127dc659e3b305f1d993a544eab0d48cce704424e62074dc04b" dependencies = [ "arrow-array", "arrow-buffer", @@ -198,9 +183,9 @@ dependencies = [ [[package]] name = "arrow-ord" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c142a147dceb59d057bad82400f1693847c80dca870d008bf7b91caf902810ae" +checksum = "3c8f82583eb4f8d84d4ee55fd1cb306720cddead7596edce95b50ee418edf66f" dependencies = [ "arrow-array", "arrow-buffer", @@ -211,9 +196,9 @@ dependencies = [ [[package]] name = "arrow-row" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac6620667fccdab4204689ca173bd84a15de6bb6b756c3a8764d4d7d0c2fc04" +checksum = "9d07ba24522229d9085031df6b94605e0f4b26e099fb7cdeec37abd941a73753" dependencies = [ "arrow-array", "arrow-buffer", @@ -224,18 +209,18 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfa93af9ff2bb80de539e6eb2c1c8764abd0f4b73ffb0d7c82bf1f9868785e66" +checksum = "b3aa9e59c611ebc291c28582077ef25c97f1975383f1479b12f3b9ffee2ffabe" dependencies = [ "bitflags", ] [[package]] name = "arrow-select" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8b2e0052cd20d36d64f32640b68a5ab54d805d24a473baee5d52017c85536c" +checksum = "8c41dbbd1e97bfcaee4fcb30e29105fb2c75e4d82ae4de70b792a5d3f66b2e7a" dependencies = [ "ahash", "arrow-array", @@ -247,9 +232,9 @@ dependencies = [ [[package]] name = "arrow-string" -version = "56.1.0" +version = "56.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2155e26e17f053c8975c546fc70cf19c00542f9abf43c23a88a46ef7204204f" +checksum = "53f5183c150fbc619eede22b861ea7c0eebed8eaac0333eaa7f6da5205fd504d" dependencies = [ "arrow-array", "arrow-buffer", @@ -259,7 +244,7 @@ dependencies = [ "memchr", "num", "regex", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] @@ -294,21 +279,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base64" version = "0.22.1" @@ -350,9 +320,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "camino" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" dependencies = [ "serde_core", ] @@ -393,9 +363,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.38" +version = "1.2.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" dependencies = [ "find-msvc-tools", "shlex", @@ -424,7 +394,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -439,11 +409,12 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.2.1" +version = "7.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b7db8e0b4b2fdad6c551e634134e99ec000e5c8c3b6856c65e8bbaded7a3b" +checksum = "e0d05af1e006a2407bedef5af410552494ce5be9090444dbbcb57258c1af3d56" dependencies = [ - "unicode-segmentation", + "strum", + "strum_macros", "unicode-width", ] @@ -537,9 +508,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", ] @@ -584,7 +555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -604,15 +575,15 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "flatbuffers" -version = "25.2.10" +version = "25.9.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1045398c1bfd89168b5fd3f1fc11f6e70b34f6f66300c87d44d3de849463abf1" +checksum = "09b6620799e7340ebd9968d2e0708eb82cf1971e9a16821e2091b6d6e475eed5" dependencies = [ "bitflags", "rustc_version", @@ -724,9 +695,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -759,12 +730,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "glob" version = "0.3.3" @@ -792,21 +757,16 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", "num-traits", + "zerocopy", ] -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" - [[package]] name = "hashbrown" version = "0.16.0" @@ -1066,7 +1026,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown", ] [[package]] @@ -1075,17 +1035,6 @@ version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -1119,9 +1068,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.80" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -1135,9 +1084,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lexical-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b765c31809609075565a70b4b71402281283aeda7ecaf4818ac14a7b2ade8958" +checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594" dependencies = [ "lexical-parse-float", "lexical-parse-integer", @@ -1148,60 +1097,53 @@ dependencies = [ [[package]] name = "lexical-parse-float" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" +checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" dependencies = [ "lexical-parse-integer", "lexical-util", - "static_assertions", ] [[package]] name = "lexical-parse-integer" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" +checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" dependencies = [ "lexical-util", - "static_assertions", ] [[package]] name = "lexical-util" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" -dependencies = [ - "static_assertions", -] +checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" [[package]] name = "lexical-write-float" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" +checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361" dependencies = [ "lexical-util", "lexical-write-integer", - "static_assertions", ] [[package]] name = "lexical-write-integer" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" +checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df" dependencies = [ "lexical-util", - "static_assertions", ] [[package]] name = "libc" -version = "0.2.175" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" @@ -1223,11 +1165,10 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] @@ -1245,11 +1186,11 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -1274,9 +1215,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memoffset" @@ -1287,15 +1228,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" version = "1.0.4" @@ -1324,12 +1256,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -1429,15 +1360,6 @@ dependencies = [ "rustc-hash", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "object_store" version = "0.12.4" @@ -1467,7 +1389,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", "url", @@ -1514,17 +1436,11 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1532,15 +1448,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1820,7 +1736,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -1841,7 +1757,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -1863,9 +1779,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -1913,56 +1829,47 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64", "bytes", @@ -2014,12 +1921,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -2045,7 +1946,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -2095,9 +1996,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.6" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "ring", "rustls-pki-types", @@ -2131,7 +2032,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -2142,9 +2043,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "3.4.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags", "core-foundation", @@ -2175,9 +2076,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -2185,18 +2086,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2293,25 +2194,38 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] -name = "static_assertions" -version = "1.1.0" +name = "strum" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] [[package]] name = "subtle" @@ -2358,15 +2272,15 @@ checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "tempfile" -version = "3.22.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -2380,11 +2294,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -2400,9 +2314,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -2420,9 +2334,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -2435,15 +2349,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -2485,27 +2399,24 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "pin-project-lite", - "slab", "socket2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -2514,9 +2425,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -2700,14 +2611,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "serde", "serde_json", "sharded-slab", @@ -2727,9 +2638,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicase" @@ -2743,17 +2654,11 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unindent" @@ -2842,9 +2747,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", @@ -2855,9 +2760,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", @@ -2869,9 +2774,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.53" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", "js-sys", @@ -2882,9 +2787,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2892,9 +2797,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", @@ -2905,9 +2810,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] @@ -2927,9 +2832,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.80" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -2945,55 +2850,33 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" -version = "0.62.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.0", + "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -3002,9 +2885,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -3013,32 +2896,26 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -3065,16 +2942,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -3095,19 +2972,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -3118,9 +2995,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -3130,9 +3007,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -3142,9 +3019,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -3154,9 +3031,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -3166,9 +3043,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -3178,9 +3055,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -3190,9 +3067,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -3202,9 +3079,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -3294,9 +3171,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie"