From 59ed9c1787b1e5243b495fed3ecc3369b807f8c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 19:25:40 +0000 Subject: [PATCH 1/9] Initial plan From f26224abf63ea0cb923810fe2453f6831872a519 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 19:35:11 +0000 Subject: [PATCH 2/9] feat: enhance primary_key ValidationError with distinct combinations and examples Co-authored-by: borchero <22455425+borchero@users.noreply.github.com> --- dataframely/schema.py | 19 ++++++++++++++++--- tests/schema/test_validate.py | 4 +++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/dataframely/schema.py b/dataframely/schema.py index b64f061..4268183 100644 --- a/dataframely/schema.py +++ b/dataframely/schema.py @@ -572,9 +572,22 @@ def validate( if eager: out, failure = cls.filter(df, cast=cast, eager=True) if len(failure) > 0: - raise ValidationError( - format_rule_failures(list(failure.counts().items())) - ) + counts = failure.counts() + msg = format_rule_failures(list(counts.items())) + if "primary_key" in counts: + pk_cols = cls.primary_key() + distinct_duplicate_keys = ( + failure._df.filter(pl.col("primary_key").not_()) + .select(pk_cols) + .unique() + ) + n_distinct = len(distinct_duplicate_keys) + examples = distinct_duplicate_keys.head(5).to_dicts() + suffix = "combination" if n_distinct == 1 else "combinations" + old = f"'primary_key' failed for {counts['primary_key']:,} rows" + new = old + f" with {n_distinct} distinct {suffix}, examples: {examples}" + msg = msg.replace(old, new) + raise ValidationError(msg) return out else: lf = df.lazy().pipe( diff --git a/tests/schema/test_validate.py b/tests/schema/test_validate.py index d496727..8ace495 100644 --- a/tests/schema/test_validate.py +++ b/tests/schema/test_validate.py @@ -130,8 +130,10 @@ def test_invalid_primary_key( with pytest.raises( ValidationError if eager else plexc.ComputeError, match=r"1 rules failed validation", - ): + ) as exc_info: _validate_and_collect(MySchema, df, eager=eager) + if eager: + exc_info.match(r"with 1 distinct combination, examples: \[") assert not MySchema.is_valid(df) From 462f8ce5bd69e912763dede46df79c3e8293ce5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 19:58:04 +0000 Subject: [PATCH 3/9] feat: extend examples to all rules in both eager and lazy validation paths Co-authored-by: borchero <22455425+borchero@users.noreply.github.com> --- dataframely/_native.pyi | 8 ++- dataframely/_plugin.py | 15 +++++- dataframely/collection/collection.py | 4 +- dataframely/filter_result.py | 38 +++++++++++++ dataframely/schema.py | 27 ++++------ src/polars_plugin/mod.rs | 78 ++++++++++++++++++++++++++- src/polars_plugin/validation_error.rs | 42 ++++++++++++--- tests/schema/test_validate.py | 3 +- 8 files changed, 184 insertions(+), 31 deletions(-) diff --git a/dataframely/_native.pyi b/dataframely/_native.pyi index 1d670c3..2fea95d 100644 --- a/dataframely/_native.pyi +++ b/dataframely/_native.pyi @@ -1,6 +1,9 @@ from typing import overload -def format_rule_failures(failures: list[tuple[str, int]]) -> str: +def format_rule_failures( + failures: list[tuple[str, int]], + examples: dict[str, list[str]] | None = None, +) -> str: """ Format rule failures with the same logic that produces validation errors from the polars plugin. @@ -8,6 +11,9 @@ def format_rule_failures(failures: list[tuple[str, int]]) -> str: Args: failures: The name of the failures and their counts. This should only include failures with a count of at least 1. + examples: Optional mapping from rule name to a list of example row strings. + When provided, up to ``len(examples[rule])`` distinct examples are included + in the formatted message for each rule. Returns: The formatted rule failures. diff --git a/dataframely/_plugin.py b/dataframely/_plugin.py index 7616af3..4bab60b 100644 --- a/dataframely/_plugin.py +++ b/dataframely/_plugin.py @@ -58,6 +58,7 @@ def all_rules_required( *, null_is_valid: bool = True, schema_name: str, + data_columns: Iterable[IntoExpr] | None = None, ) -> pl.Expr: """Execute :mod:`~polars.all_horizontal` and `.all` for a set of rules. @@ -70,15 +71,25 @@ def all_rules_required( schema_name: The name of the schema being validated. This is used to produce better error messages. null_is_valid: Whether to treat null values as valid (i.e., `true`). + data_columns: Optional data columns to include for generating example rows in + error messages. If provided, up to 5 distinct example rows are included + for each failing rule. Returns: A scalar boolean expression. """ + rules_list = [rules] if isinstance(rules, pl.Expr) else list(rules) + num_rule_columns = len(rules_list) + data_columns_list = list(data_columns) if data_columns is not None else [] return register_plugin_function( plugin_path=PLUGIN_PATH, function_name="all_rules_required", - args=rules, - kwargs={"null_is_valid": null_is_valid, "schema_name": schema_name}, + args=[*rules_list, *data_columns_list], + kwargs={ + "null_is_valid": null_is_valid, + "schema_name": schema_name, + "num_rule_columns": num_rule_columns, + }, use_abs_path=True, returns_scalar=True, ) diff --git a/dataframely/collection/collection.py b/dataframely/collection/collection.py index 665b85a..c822fb6 100644 --- a/dataframely/collection/collection.py +++ b/dataframely/collection/collection.py @@ -410,7 +410,9 @@ def validate( filtered, failures = cls.filter(data, cast=cast, eager=True) if any(len(failure) > 0 for failure in failures.values()): errors = { - member: format_rule_failures(list(failure.counts().items())) + member: format_rule_failures( + list(failure.counts().items()), failure.examples() + ) for member, failure in failures.items() if len(failure) > 0 } diff --git a/dataframely/filter_result.py b/dataframely/filter_result.py index f2f88b7..469d580 100644 --- a/dataframely/filter_result.py +++ b/dataframely/filter_result.py @@ -146,6 +146,22 @@ def counts(self) -> dict[str, int]: """ return _compute_counts(self._df, self._rule_columns) + def examples(self, max_examples: int = 5) -> dict[str, list[str]]: + """Example rows for each failing rule. + + For each rule that has at least one failure, returns up to `max_examples` + distinct example rows (as formatted strings) from the original data columns. + + Args: + max_examples: The maximum number of distinct example rows to return per + rule. + + Returns: + A mapping from rule name to a list of example row strings. Rules with no + failures are not included. + """ + return _compute_examples(self._df, self._rule_columns, max_examples) + def cooccurrence_counts(self) -> dict[frozenset[str], int]: """The number of validation failures per co-occurring rule validation failure. @@ -409,6 +425,28 @@ def _compute_counts(df: pl.DataFrame, rule_columns: list[str]) -> dict[str, int] } +def _compute_examples( + df: pl.DataFrame, rule_columns: list[str], max_examples: int +) -> dict[str, list[str]]: + if len(rule_columns) == 0: + return {} + + data_columns = [c for c in df.columns if c not in rule_columns] + if not data_columns: + return {} + + result = {} + for rule_name in rule_columns: + failing = df.filter(pl.col(rule_name).not_()) + if len(failing) == 0: + continue + examples_df = ( + failing.select(data_columns).unique(maintain_order=True).head(max_examples) + ) + result[rule_name] = [str(row) for row in examples_df.to_dicts()] + return result + + def _compute_cooccurrence_counts( df: pl.DataFrame, rule_columns: list[str] ) -> dict[frozenset[str], int]: diff --git a/dataframely/schema.py b/dataframely/schema.py index 4268183..d1aa083 100644 --- a/dataframely/schema.py +++ b/dataframely/schema.py @@ -572,22 +572,11 @@ def validate( if eager: out, failure = cls.filter(df, cast=cast, eager=True) if len(failure) > 0: - counts = failure.counts() - msg = format_rule_failures(list(counts.items())) - if "primary_key" in counts: - pk_cols = cls.primary_key() - distinct_duplicate_keys = ( - failure._df.filter(pl.col("primary_key").not_()) - .select(pk_cols) - .unique() + raise ValidationError( + format_rule_failures( + list(failure.counts().items()), failure.examples() ) - n_distinct = len(distinct_duplicate_keys) - examples = distinct_duplicate_keys.head(5).to_dicts() - suffix = "combination" if n_distinct == 1 else "combinations" - old = f"'primary_key' failed for {counts['primary_key']:,} rows" - new = old + f" with {n_distinct} distinct {suffix}, examples: {examples}" - msg = msg.replace(old, new) - raise ValidationError(msg) + ) return out else: lf = df.lazy().pipe( @@ -596,7 +585,13 @@ def validate( if rules := cls._validation_rules(with_cast=False): lf = ( lf.pipe(with_evaluation_rules, rules) - .filter(all_rules_required(rules.keys(), schema_name=cls.__name__)) + .filter( + all_rules_required( + rules.keys(), + schema_name=cls.__name__, + data_columns=cls.column_names(), + ) + ) .drop(rules.keys()) ) return lf # type: ignore diff --git a/src/polars_plugin/mod.rs b/src/polars_plugin/mod.rs index 06cb9b9..4d20a9c 100644 --- a/src/polars_plugin/mod.rs +++ b/src/polars_plugin/mod.rs @@ -2,6 +2,8 @@ mod rule_failure; mod utils; mod validation_error; +use std::collections::{HashMap, HashSet}; + use polars::prelude::*; use polars_core::POOL; use pyo3_polars::derive::polars_expr; @@ -62,18 +64,71 @@ pub fn all_rules(inputs: &[Series]) -> PolarsResult { struct RequiredValidationKwargs { schema_name: String, null_is_valid: bool, + #[serde(default)] + num_rule_columns: Option, +} + +/// The maximum number of distinct example rows included in validation error messages. +const MAX_EXAMPLES: usize = 5; + +/// Format a single data row (at `row_idx`) from the given data series as a Python-like dict string. +fn format_example_row(data_series: &[Series], row_idx: usize) -> String { + let kvs: Vec = data_series + .iter() + .map(|s| { + let val = s.get(row_idx).unwrap_or(AnyValue::Null); + format!("'{}': {}", s.name(), val) + }) + .collect(); + format!("{{{}}}", kvs.join(", ")) +} + +/// Compute up to `max_examples` distinct example rows for a failing rule. +fn compute_examples( + bool_ca: &BooleanChunked, + null_is_valid: bool, + data_series: &[Series], + max_examples: usize, +) -> Vec { + let mut seen: HashSet = HashSet::new(); + let mut examples: Vec = Vec::new(); + + for (i, val) in bool_ca.iter().enumerate() { + let is_failure = match val { + Some(false) => true, + None => !null_is_valid, + _ => false, + }; + if is_failure { + let row_str = format_example_row(data_series, i); + if seen.insert(row_str.clone()) { + examples.push(row_str); + if examples.len() >= max_examples { + break; + } + } + } + } + + examples } /// Reduce a set of boolean columns into a single boolean scalar, AND-ing all values. /// Null values are treated as `true`. /// In contrast to `all_rules`, this function raises an error if the returned value would be /// `false`, including details about the `false` values (i.e. "rules" that failed). +/// The first `num_rule_columns` inputs are boolean rule columns; any remaining inputs are +/// data columns used to generate example rows in error messages. #[polars_expr(output_type=Boolean)] pub fn all_rules_required( inputs: &[Series], kwargs: RequiredValidationKwargs, ) -> PolarsResult { - let failures = compute_rule_failures(inputs, kwargs.null_is_valid)?; + let num_rule = kwargs.num_rule_columns.unwrap_or(inputs.len()); + let rule_inputs = &inputs[..num_rule]; + let data_inputs = &inputs[num_rule..]; + + let failures = compute_rule_failures(rule_inputs, kwargs.null_is_valid)?; // If there's any failure, we know that validation failed and use the failure object for an // informative error message. If no failure exists, we simply return a series with a single @@ -84,7 +139,26 @@ pub fn all_rules_required( return Ok(BooleanChunked::new(PlSmallStr::EMPTY, [true]).into_series()); } + // Compute examples for each failing rule using the data columns. + let examples: HashMap> = if data_inputs.is_empty() { + HashMap::new() + } else { + failures + .iter() + .map(|failure| { + let rule_series = rule_inputs + .iter() + .find(|s| s.name().as_str() == failure.rule) + .expect("failing rule not found in inputs"); + let bool_ca = as_bool(rule_series)?; + let examples = + compute_examples(bool_ca, kwargs.null_is_valid, data_inputs, MAX_EXAMPLES); + Ok((failure.rule.to_string(), examples)) + }) + .collect::>>()? + }; + // Aggregate failure counts into a validation error. let error = RuleValidationError::new(failures); - Err(polars_err!(ComputeError: format!("\n{}", error.to_string(Some(&kwargs.schema_name))))) + Err(polars_err!(ComputeError: format!("\n{}", error.to_string(Some(&kwargs.schema_name), Some(&examples))))) } diff --git a/src/polars_plugin/validation_error.rs b/src/polars_plugin/validation_error.rs index b2ca718..c82e2c4 100644 --- a/src/polars_plugin/validation_error.rs +++ b/src/polars_plugin/validation_error.rs @@ -2,6 +2,7 @@ use itertools::Itertools; use num_format::{Locale, ToFormattedString}; use polars::prelude::*; use pyo3::{create_exception, exceptions::PyException, prelude::*}; +use std::collections::HashMap; use super::RuleFailure; @@ -39,7 +40,11 @@ impl<'a> RuleValidationError<'a> { } } - pub fn to_string(&self, schema: Option<&str>) -> String { + pub fn to_string( + &self, + schema: Option<&str>, + examples: Option<&HashMap>>, + ) -> String { let mut result = if let Some(schema) = schema { format!( "{} rules failed validation for schema '{schema}':", @@ -49,10 +54,12 @@ impl<'a> RuleValidationError<'a> { format!("{} rules failed validation:", self.num_rule_failures) }; self.schema_errors.iter().for_each(|failure| { + let examples_str = format_examples(failure.rule, examples); result += format!( - "\n - '{}' failed for {} rows", + "\n - '{}' failed for {} rows{}", failure.rule, - failure.count.to_formatted_string(&Locale::en) + failure.count.to_formatted_string(&Locale::en), + examples_str, ) .as_str(); }); @@ -63,10 +70,13 @@ impl<'a> RuleValidationError<'a> { ) .as_str(); errors.iter().for_each(|failure| { + let full_rule = format!("{}|{}", column, failure.rule); + let examples_str = format_examples(&full_rule, examples); result += format!( - "\n - '{}' failed for {} rows", + "\n - '{}' failed for {} rows{}", failure.rule, - failure.count.to_formatted_string(&Locale::en) + failure.count.to_formatted_string(&Locale::en), + examples_str, ) .as_str(); }); @@ -75,8 +85,26 @@ impl<'a> RuleValidationError<'a> { } } +fn format_examples(rule: &str, examples: Option<&HashMap>>) -> String { + match examples.and_then(|ex| ex.get(rule)) { + Some(ex) if !ex.is_empty() => { + let suffix = if ex.len() == 1 { + "example".to_string() + } else { + "examples".to_string() + }; + format!(" with {} distinct {}: [{}]", ex.len(), suffix, ex.join(", ")) + } + _ => String::new(), + } +} + #[pyfunction] -pub fn format_rule_failures(failures: Vec<(String, IdxSize)>) -> String { +#[pyo3(signature = (failures, examples=None))] +pub fn format_rule_failures( + failures: Vec<(String, IdxSize)>, + examples: Option>>, +) -> String { let validation_error = RuleValidationError::new( failures .iter() @@ -86,5 +114,5 @@ pub fn format_rule_failures(failures: Vec<(String, IdxSize)>) -> String { }) .collect(), ); - return validation_error.to_string(None); + return validation_error.to_string(None, examples.as_ref()); } diff --git a/tests/schema/test_validate.py b/tests/schema/test_validate.py index 8ace495..3d5a2b8 100644 --- a/tests/schema/test_validate.py +++ b/tests/schema/test_validate.py @@ -132,8 +132,7 @@ def test_invalid_primary_key( match=r"1 rules failed validation", ) as exc_info: _validate_and_collect(MySchema, df, eager=eager) - if eager: - exc_info.match(r"with 1 distinct combination, examples: \[") + exc_info.match(r"with 2 distinct examples") assert not MySchema.is_valid(df) From bc9b27e1a59843acdeed896c4600784d760fe39a Mon Sep 17 00:00:00 2001 From: Oliver Borchert Date: Sun, 24 May 2026 21:19:55 +0200 Subject: [PATCH 4/9] Update --- Cargo.lock | 379 +++++++++----------------- dataframely/_native.pyi | 17 +- dataframely/_plugin.py | 4 + dataframely/collection/collection.py | 24 +- dataframely/filter_result.py | 38 --- dataframely/schema.py | 7 +- src/polars_plugin/mod.rs | 90 ++---- src/polars_plugin/validation_error.rs | 167 ++++++++++-- 8 files changed, 326 insertions(+), 400 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b350d06..b10f881 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "ar_archive_writer" @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base64" @@ -151,9 +151,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" dependencies = [ "serde_core", ] @@ -175,9 +175,9 @@ checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e" [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -225,9 +225,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.56" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "jobserver", @@ -388,9 +388,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" dependencies = [ "serde", ] @@ -408,14 +408,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] name = "ethnum" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" [[package]] name = "fallible-streaming-iterator" @@ -530,9 +530,9 @@ checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" [[package]] name = "futures-util" @@ -583,20 +583,20 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "wasip2", "wasip3", ] @@ -646,6 +646,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "heck" version = "0.5.0" @@ -658,7 +664,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -693,12 +699,12 @@ checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -729,9 +735,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" @@ -745,10 +751,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -761,9 +769,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -851,13 +859,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -971,9 +979,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "parking_lot" @@ -1024,15 +1032,15 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "planus" @@ -1450,9 +1458,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] @@ -1478,9 +1486,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit", ] @@ -1496,9 +1504,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" dependencies = [ "ar_archive_writer", "cc", @@ -1603,9 +1611,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -1616,11 +1624,17 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha", "rand_core", @@ -1672,9 +1686,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -1744,9 +1758,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "relative-path" @@ -1804,9 +1818,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -1837,9 +1851,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1886,9 +1900,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -1927,9 +1941,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e" +checksum = "b2a0c28ca5908dbdbcd52e6fdaa00358ab88637f8ab33e1f188dd510eb44b53d" dependencies = [ "libc", "signal-hook-registry", @@ -1947,9 +1961,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simdutf8" @@ -1959,9 +1973,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -1987,25 +2001,25 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys", ] [[package]] name = "stacker" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" dependencies = [ "cc", "cfg-if", "libc", "psm", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -2049,9 +2063,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.116" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2106,31 +2120,31 @@ dependencies = [ [[package]] name = "tokio" -version = "1.49.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "libc", "mio", "pin-project-lite", "socket2", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap", "toml_datetime", @@ -2140,18 +2154,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.8+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ "winnow", ] [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "unicode-ident" @@ -2179,11 +2193,11 @@ checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" [[package]] name = "uuid" -version = "1.21.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ - "getrandom 0.4.1", + "getrandom 0.4.2", "js-sys", "serde_core", "wasm-bindgen", @@ -2209,11 +2223,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -2222,14 +2236,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -2240,9 +2254,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2250,9 +2264,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -2263,9 +2277,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -2369,24 +2383,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -2396,140 +2392,11 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link 0.2.1", - "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]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" -version = "0.7.14" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -2543,6 +2410,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -2630,18 +2503,18 @@ checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", diff --git a/dataframely/_native.pyi b/dataframely/_native.pyi index 2fea95d..527dca2 100644 --- a/dataframely/_native.pyi +++ b/dataframely/_native.pyi @@ -1,8 +1,13 @@ from typing import overload +import polars as pl + def format_rule_failures( failures: list[tuple[str, int]], - examples: dict[str, list[str]] | None = None, + *, + failures_from: pl.DataFrame | None, + examples_from: pl.DataFrame | None, + primary_key_columns: list[str], ) -> str: """ Format rule failures with the same logic that produces validation errors from the @@ -11,9 +16,13 @@ def format_rule_failures( Args: failures: The name of the failures and their counts. This should only include failures with a count of at least 1. - examples: Optional mapping from rule name to a list of example row strings. - When provided, up to ``len(examples[rule])`` distinct examples are included - in the formatted message for each rule. + failures_from: The data frame containing the rule columns providing the + failures. + max_examples: The maximum number of examples to include for each failure. No + effect if `examples_from` is not provided. + primary_key_columns: The primary key columns of the schema for which to format + rule failures. This is only relevant if `examples_from` is provided and + allows for better error messages for the "primary_key" rule. Returns: The formatted rule failures. diff --git a/dataframely/_plugin.py b/dataframely/_plugin.py index ae2f885..febc631 100644 --- a/dataframely/_plugin.py +++ b/dataframely/_plugin.py @@ -60,6 +60,7 @@ def all_rules_required( null_is_valid: bool = True, schema_name: str, data_columns: Iterable[IntoExpr] | None = None, + primary_key_columns: list[str] | None, ) -> pl.Expr: """Execute :mod:`~polars.all_horizontal` and `.all` for a set of rules. @@ -80,6 +81,8 @@ def all_rules_required( data_columns: Optional data columns to include for generating example rows in error messages. If provided, up to 5 distinct example rows are included for each failing rule. + primary_key_columns: Optional list of primary key columns which are used for + better error messages if data columns are provided. Returns: A scalar boolean expression. @@ -95,6 +98,7 @@ def all_rules_required( "null_is_valid": null_is_valid, "schema_name": schema_name, "num_rule_columns": num_rule_columns, + "primary_key_columns": primary_key_columns or [], }, use_abs_path=True, is_elementwise=True, diff --git a/dataframely/collection/collection.py b/dataframely/collection/collection.py index c822fb6..aa2c7b4 100644 --- a/dataframely/collection/collection.py +++ b/dataframely/collection/collection.py @@ -409,13 +409,19 @@ def validate( # information to properly construct a useful error message. filtered, failures = cls.filter(data, cast=cast, eager=True) if any(len(failure) > 0 for failure in failures.values()): - errors = { - member: format_rule_failures( - list(failure.counts().items()), failure.examples() + errors: dict[str, str] = {} + for member, failure in failures.items(): + if len(failure) == 0: + continue + + counts = failure.counts() + errors[member] = format_rule_failures( + list(counts.items()), + failures_from=failure._df.select(counts.keys()), + examples_from=failure.invalid(), + primary_key_columns=cls.member_schemas()[member].primary_key(), ) - for member, failure in failures.items() - if len(failure) > 0 - } + details = [ f" > Member '{member}' failed validation:\n" + textwrap.indent(error, " ") @@ -453,7 +459,11 @@ def validate( ) .filter( all_rules_required( - filter_names, null_is_valid=False, schema_name=name + filter_names, + null_is_valid=False, + schema_name=name, + data_columns=cls.common_primary_key(), + primary_key_columns=cls.common_primary_key(), ) ) .drop(filter_names) diff --git a/dataframely/filter_result.py b/dataframely/filter_result.py index 469d580..f2f88b7 100644 --- a/dataframely/filter_result.py +++ b/dataframely/filter_result.py @@ -146,22 +146,6 @@ def counts(self) -> dict[str, int]: """ return _compute_counts(self._df, self._rule_columns) - def examples(self, max_examples: int = 5) -> dict[str, list[str]]: - """Example rows for each failing rule. - - For each rule that has at least one failure, returns up to `max_examples` - distinct example rows (as formatted strings) from the original data columns. - - Args: - max_examples: The maximum number of distinct example rows to return per - rule. - - Returns: - A mapping from rule name to a list of example row strings. Rules with no - failures are not included. - """ - return _compute_examples(self._df, self._rule_columns, max_examples) - def cooccurrence_counts(self) -> dict[frozenset[str], int]: """The number of validation failures per co-occurring rule validation failure. @@ -425,28 +409,6 @@ def _compute_counts(df: pl.DataFrame, rule_columns: list[str]) -> dict[str, int] } -def _compute_examples( - df: pl.DataFrame, rule_columns: list[str], max_examples: int -) -> dict[str, list[str]]: - if len(rule_columns) == 0: - return {} - - data_columns = [c for c in df.columns if c not in rule_columns] - if not data_columns: - return {} - - result = {} - for rule_name in rule_columns: - failing = df.filter(pl.col(rule_name).not_()) - if len(failing) == 0: - continue - examples_df = ( - failing.select(data_columns).unique(maintain_order=True).head(max_examples) - ) - result[rule_name] = [str(row) for row in examples_df.to_dicts()] - return result - - def _compute_cooccurrence_counts( df: pl.DataFrame, rule_columns: list[str] ) -> dict[frozenset[str], int]: diff --git a/dataframely/schema.py b/dataframely/schema.py index fd46dcd..6186013 100644 --- a/dataframely/schema.py +++ b/dataframely/schema.py @@ -576,9 +576,13 @@ def validate( if eager: out, failure = cls.filter(df, cast=cast, eager=True) if len(failure) > 0: + counts = failure.counts() raise ValidationError( format_rule_failures( - list(failure.counts().items()), failure.examples() + list(counts.items()), + failures_from=failure._df.select(counts.keys()), + examples_from=failure.invalid(), + primary_key_columns=cls.primary_key(), ) ) return out @@ -594,6 +598,7 @@ def validate( rules.keys(), schema_name=cls.__name__, data_columns=cls.column_names(), + primary_key_columns=cls.primary_key(), ) ) .drop(rules.keys()) diff --git a/src/polars_plugin/mod.rs b/src/polars_plugin/mod.rs index 4db5e68..09534cc 100644 --- a/src/polars_plugin/mod.rs +++ b/src/polars_plugin/mod.rs @@ -2,8 +2,6 @@ mod rule_failure; mod utils; mod validation_error; -use std::collections::{HashMap, HashSet}; - use polars::prelude::*; use polars_core::POOL; use pyo3_polars::derive::polars_expr; @@ -64,55 +62,11 @@ pub fn all_rules(inputs: &[Series]) -> PolarsResult { struct RequiredValidationKwargs { schema_name: String, null_is_valid: bool, + primary_key_columns: Option>, #[serde(default)] num_rule_columns: Option, } -/// The maximum number of distinct example rows included in validation error messages. -const MAX_EXAMPLES: usize = 5; - -/// Format a single data row (at `row_idx`) from the given data series as a Python-like dict string. -fn format_example_row(data_series: &[Series], row_idx: usize) -> String { - let kvs: Vec = data_series - .iter() - .map(|s| { - let val = s.get(row_idx).unwrap_or(AnyValue::Null); - format!("'{}': {}", s.name(), val) - }) - .collect(); - format!("{{{}}}", kvs.join(", ")) -} - -/// Compute up to `max_examples` distinct example rows for a failing rule. -fn compute_examples( - bool_ca: &BooleanChunked, - null_is_valid: bool, - data_series: &[Series], - max_examples: usize, -) -> Vec { - let mut seen: HashSet = HashSet::new(); - let mut examples: Vec = Vec::new(); - - for (i, val) in bool_ca.iter().enumerate() { - let is_failure = match val { - Some(false) => true, - None => !null_is_valid, - _ => false, - }; - if is_failure { - let row_str = format_example_row(data_series, i); - if seen.insert(row_str.clone()) { - examples.push(row_str); - if examples.len() >= max_examples { - break; - } - } - } - } - - examples -} - /// Reduce a set of boolean columns into a single boolean scalar, AND-ing all values. /// Null values are treated as `true`. /// In contrast to `all_rules`, this function raises an error if the returned value would be @@ -140,26 +94,26 @@ pub fn all_rules_required( return Ok(column.take_materialized_series()); } - // Compute examples for each failing rule using the data columns. - let examples: HashMap> = if data_inputs.is_empty() { - HashMap::new() - } else { - failures + // Aggregate failures into a validation error + let failures_from = DataFrame::new( + rule_inputs[0].len(), + rule_inputs .iter() - .map(|failure| { - let rule_series = rule_inputs - .iter() - .find(|s| s.name().as_str() == failure.rule) - .expect("failing rule not found in inputs"); - let bool_ca = as_bool(rule_series)?; - let examples = - compute_examples(bool_ca, kwargs.null_is_valid, data_inputs, MAX_EXAMPLES); - Ok((failure.rule.to_string(), examples)) - }) - .collect::>>()? - }; - - // Aggregate failure counts into a validation error. - let error = RuleValidationError::new(failures); - Err(polars_err!(ComputeError: format!("\n{}", error.to_string(Some(&kwargs.schema_name), Some(&examples))))) + .map(|s| s.clone().into_column()) + .collect(), + )?; + let examples_from = DataFrame::new( + data_inputs[0].len(), + data_inputs + .iter() + .map(|s| s.clone().into_column()) + .collect(), + )?; + let error = RuleValidationError::new( + failures, + Some(failures_from), + Some(examples_from), + kwargs.primary_key_columns.unwrap_or_default(), + ); + Err(polars_err!(ComputeError: format!("\n{}", error.to_string(Some(&kwargs.schema_name))))) } diff --git a/src/polars_plugin/validation_error.rs b/src/polars_plugin/validation_error.rs index c82e2c4..402aeca 100644 --- a/src/polars_plugin/validation_error.rs +++ b/src/polars_plugin/validation_error.rs @@ -1,25 +1,41 @@ +use std::ops::Not; + use itertools::Itertools; use num_format::{Locale, ToFormattedString}; use polars::prelude::*; use pyo3::{create_exception, exceptions::PyException, prelude::*}; -use std::collections::HashMap; +use pyo3_polars::PyDataFrame; use super::RuleFailure; create_exception!(exc, PyRuleValidationError, PyException); +const MAX_EXAMPLES_ENV_NAME: &str = "DATAFRAMELY_MAX_VALIDATION_FAILURE_EXAMPLES"; +const DEFAULT_MAX_EXAMPLES: usize = 0; + +/* -------------------------------------- VALIDATION ERROR ------------------------------------- */ + pub struct RuleValidationError<'a> { num_rule_failures: usize, - schema_errors: Vec>, - column_errors: Vec<(&'a str, Vec>)>, + schema_errors: Vec>, + column_errors: Vec<(&'a str, Vec>)>, } impl<'a> RuleValidationError<'a> { - pub fn new(failure_counts: Vec>) -> Self { + pub fn new( + failure_counts: Vec>, + failures_from: Option, + examples_from: Option, + primary_key_columns: Vec, + ) -> Self { let num_rule_failures = failure_counts.len(); let (flat_column_errors, schema_errors): (Vec<_>, Vec<_>) = failure_counts .into_iter() .partition(|item| item.rule.contains("|")); + + // For column errors, we only include the data column referencing the column to + // gather examples. Other values are not included to avoid creating outputs that + // are too wide. let column_errors = flat_column_errors .into_iter() .chunk_by(|item| item.rule.split_once("|").unwrap().0) @@ -28,11 +44,41 @@ impl<'a> RuleValidationError<'a> { ( key, chunk - .map(|failure| failure.split_off_column_name()) + .map(|failure| { + RuleFailureInfo::new( + failure.rule, + failure.split_off_column_name(), + failures_from.as_ref(), + examples_from.as_ref(), + Some(vec![key.to_string()]), + ) + }) .collect::>(), ) }) .collect::>(); + + // For schema errors, we include all data columns because we do not know what columns + // are relevant to each rule. The only exception is the `primary_key` rule where we + // can limit ourselves to the `primary_key_columns`. + let schema_errors = schema_errors + .into_iter() + .map(|failure| { + let data_columns = if failure.rule == "primary_key" { + Some(primary_key_columns.clone()) + } else { + None + }; + RuleFailureInfo::new( + failure.rule, + failure, + failures_from.as_ref(), + examples_from.as_ref(), + data_columns, + ) + }) + .collect(); + Self { num_rule_failures, schema_errors: schema_errors, @@ -40,11 +86,7 @@ impl<'a> RuleValidationError<'a> { } } - pub fn to_string( - &self, - schema: Option<&str>, - examples: Option<&HashMap>>, - ) -> String { + pub fn to_string(&self, schema: Option<&str>) -> String { let mut result = if let Some(schema) = schema { format!( "{} rules failed validation for schema '{schema}':", @@ -54,11 +96,11 @@ impl<'a> RuleValidationError<'a> { format!("{} rules failed validation:", self.num_rule_failures) }; self.schema_errors.iter().for_each(|failure| { - let examples_str = format_examples(failure.rule, examples); + let examples_str = format_examples(failure.examples.as_ref()); result += format!( "\n - '{}' failed for {} rows{}", - failure.rule, - failure.count.to_formatted_string(&Locale::en), + failure.failure.rule, + failure.failure.count.to_formatted_string(&Locale::en), examples_str, ) .as_str(); @@ -70,12 +112,11 @@ impl<'a> RuleValidationError<'a> { ) .as_str(); errors.iter().for_each(|failure| { - let full_rule = format!("{}|{}", column, failure.rule); - let examples_str = format_examples(&full_rule, examples); + let examples_str = format_examples(failure.examples.as_ref()); result += format!( "\n - '{}' failed for {} rows{}", - failure.rule, - failure.count.to_formatted_string(&Locale::en), + failure.failure.rule, + failure.failure.count.to_formatted_string(&Locale::en), examples_str, ) .as_str(); @@ -85,25 +126,90 @@ impl<'a> RuleValidationError<'a> { } } -fn format_examples(rule: &str, examples: Option<&HashMap>>) -> String { - match examples.and_then(|ex| ex.get(rule)) { - Some(ex) if !ex.is_empty() => { - let suffix = if ex.len() == 1 { - "example".to_string() - } else { - "examples".to_string() +fn format_examples(examples: Option<&DataFrame>) -> String { + let Some(df) = examples else { + return String::new(); + }; + format!( + "; examples: [{}]", + (0..df.height()) + .map(|i| format!("{:#?}", df.get_row(i).unwrap())) + .join(", ") + ) +} + +/* ---------------------------------------- FAILURE INFO --------------------------------------- */ + +struct RuleFailureInfo<'a> { + failure: RuleFailure<'a>, + examples: Option, +} + +impl<'a> RuleFailureInfo<'a> { + fn new( + rule_name: &str, + failure: RuleFailure<'a>, + failures_from: Option<&DataFrame>, + examples_from: Option<&DataFrame>, + data_columns: Option>, + ) -> Self { + // Check if we should return any examples at all + let max_examples = std::env::var(MAX_EXAMPLES_ENV_NAME) + .ok() + .and_then(|val| val.parse::().ok()) + .unwrap_or(DEFAULT_MAX_EXAMPLES); + if max_examples == 0 { + return Self { + failure, + examples: None, + }; + } + + // Also check if we even have the necessary data to compute examples + let (Some(failures_from), Some(examples_from)) = + (failures_from.as_ref(), examples_from.as_ref()) + else { + return Self { + failure, + examples: None, }; - format!(" with {} distinct {}: [{}]", ex.len(), suffix, ex.join(", ")) + }; + + // If we should compute examples and have the necessary data, let's do so + let rule_ca = failures_from.column(rule_name).unwrap().bool().unwrap(); + let data_columns = data_columns.unwrap_or_else(|| { + examples_from + .get_column_names() + .into_iter() + .map(|s| s.to_string()) + .collect() + }); + let examples = examples_from + .select(&data_columns) + .unwrap() + .filter(&rule_ca.not()) + .unwrap() + .unique::<(), ()>( + Some(&data_columns), + UniqueKeepStrategy::First, + Some((0, max_examples)), + ) + .unwrap(); + Self { + failure, + examples: Some(examples), } - _ => String::new(), } } +/* --------------------------------- STANDALONE PYTHON FUNCTION -------------------------------- */ + #[pyfunction] -#[pyo3(signature = (failures, examples=None))] pub fn format_rule_failures( failures: Vec<(String, IdxSize)>, - examples: Option>>, + failures_from: Option, + examples_from: Option, + primary_key_columns: Vec, ) -> String { let validation_error = RuleValidationError::new( failures @@ -113,6 +219,9 @@ pub fn format_rule_failures( count: *count, }) .collect(), + failures_from.map(|df| df.into()), + examples_from.map(|df| df.into()), + primary_key_columns, ); - return validation_error.to_string(None, examples.as_ref()); + return validation_error.to_string(None); } From 52b24103e61cdb440e6e14c816c3c05af4d70581 Mon Sep 17 00:00:00 2001 From: Oliver Borchert Date: Sun, 24 May 2026 21:45:30 +0200 Subject: [PATCH 5/9] Update --- Cargo.lock | 1613 ++++++++++++++++++++++++- Cargo.toml | 9 +- src/polars_plugin/validation_error.rs | 31 +- 3 files changed, 1620 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b10f881..4fa53e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -69,12 +82,30 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed51fe0f224d1d4ea768be38c51f9f831dee9d05c163c11fba0b8c44387b1fc3" +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -117,6 +148,12 @@ dependencies = [ "debug_unsafe", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.1" @@ -158,6 +195,20 @@ dependencies = [ "serde_core", ] +[[package]] +name = "blake3" +version = "1.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.0", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -241,6 +292,23 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.41" @@ -279,6 +347,31 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -294,6 +387,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -303,6 +405,15 @@ dependencies = [ "cfg-if", ] +[[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-deque" version = "0.8.6" @@ -322,6 +433,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -355,7 +475,7 @@ dependencies = [ "polars-core", "pyo3", "pyo3-polars", - "rand", + "rand 0.9.4", "rayon", "regex", "regex-syntax", @@ -380,6 +500,17 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dyn-clone" version = "1.0.20" @@ -408,7 +539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -417,6 +548,27 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fallible-streaming-iterator" version = "0.1.9" @@ -445,6 +597,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.5" @@ -457,6 +624,25 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs4" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "futures" version = "0.3.32" @@ -597,6 +783,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -607,6 +794,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.7.1" @@ -621,6 +827,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "halfbrown" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed2f2edad8a14c8186b847909a41fbb9c3eafa44f88bd891114ed5019da09" +dependencies = [ + "hashbrown 0.16.1", + "serde", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -658,13 +874,124 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "home" version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", ] [[package]] @@ -691,12 +1018,115 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "id-arena" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -718,6 +1148,12 @@ dependencies = [ "rustversion", ] +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + [[package]] name = "iter-read" version = "1.1.0" @@ -779,6 +1215,18 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + [[package]] name = "lock_api" version = "0.4.14" @@ -795,7 +1243,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] -name = "lz4" +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lz4" version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4" @@ -865,7 +1319,7 @@ checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -977,12 +1431,61 @@ dependencies = [ "memchr", ] +[[package]] +name = "object_store" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49" +dependencies = [ + "async-trait", + "base64", + "bytes", + "chrono", + "form_urlencoded", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body-util", + "humantime", + "hyper", + "itertools", + "parking_lot", + "percent-encoding", + "quick-xml", + "rand 0.10.1", + "reqwest", + "ring", + "serde", + "serde_json", + "serde_urlencoded", + "thiserror 2.0.18", + "tokio", + "tracing", + "url", + "walkdir", + "wasm-bindgen-futures", + "web-time", +] + [[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -1065,6 +1568,12 @@ dependencies = [ "polars-compute", "polars-core", "polars-error", + "polars-io", + "polars-lazy", + "polars-ops", + "polars-plan", + "polars-sql", + "polars-time", "polars-utils", "version_check", ] @@ -1075,6 +1584,7 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f672743a042b72ace4f88b29f8205ab200b29c5ac976c0560899680c07d2d09" dependencies = [ + "atoi_simd", "bitflags", "bytemuck", "bytes", @@ -1087,6 +1597,7 @@ dependencies = [ "getrandom 0.3.4", "half", "hashbrown 0.16.1", + "itoa", "lz4", "num-traits", "polars-arrow-format", @@ -1135,6 +1646,7 @@ dependencies = [ "chrono", "either", "fast-float2", + "half", "hashbrown 0.16.1", "itoa", "num-traits", @@ -1142,7 +1654,7 @@ dependencies = [ "polars-buffer", "polars-error", "polars-utils", - "rand", + "rand 0.9.4", "serde", "strength_reduce", "strum_macros", @@ -1160,6 +1672,7 @@ dependencies = [ "boxcar", "bytemuck", "chrono", + "chrono-tz", "either", "getrandom 0.3.4", "hashbrown 0.16.1", @@ -1174,7 +1687,7 @@ dependencies = [ "polars-row", "polars-schema", "polars-utils", - "rand", + "rand 0.9.4", "rand_distr", "rayon", "regex", @@ -1207,6 +1720,7 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c13126f8baebc13dadf26a80dcf69a607977fc8a67b18671ad2cefc713a7bdd" dependencies = [ + "object_store", "parking_lot", "polars-arrow-format", "pyo3", @@ -1215,6 +1729,32 @@ dependencies = [ "simdutf8", ] +[[package]] +name = "polars-expr" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2151f54b0ae5d6b86c3c47df0898ff90edfe774807823f742f36e44973d51ea1" +dependencies = [ + "bitflags", + "hashbrown 0.16.1", + "num-traits", + "polars-arrow", + "polars-buffer", + "polars-compute", + "polars-core", + "polars-io", + "polars-ops", + "polars-plan", + "polars-row", + "polars-time", + "polars-utils", + "rand 0.9.4", + "rayon", + "recursive", + "regex", + "version_check", +] + [[package]] name = "polars-ffi" version = "0.53.0" @@ -1233,8 +1773,11 @@ checksum = "059724d7762d7332cbc225e6504d996091b28fa1337716e06e5a81d9e54a34ad" dependencies = [ "async-trait", "atoi_simd", + "blake3", "bytes", + "chrono", "fast-float2", + "fs4", "futures", "glob", "hashbrown 0.16.1", @@ -1243,23 +1786,95 @@ dependencies = [ "memchr", "memmap2", "num-traits", + "object_store", "percent-encoding", "polars-arrow", "polars-buffer", "polars-compute", "polars-core", "polars-error", + "polars-json", "polars-parquet", "polars-schema", + "polars-time", "polars-utils", "rayon", "regex", + "reqwest", "serde", + "serde_json", "simdutf8", "tokio", "zmij", ] +[[package]] +name = "polars-json" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55581d4cc8f4122cae92d12aec997e6713ac483871391a7db09501604007be4b" +dependencies = [ + "chrono", + "fallible-streaming-iterator", + "hashbrown 0.16.1", + "indexmap", + "itoa", + "num-traits", + "polars-arrow", + "polars-compute", + "polars-error", + "polars-utils", + "simd-json", + "streaming-iterator", + "zmij", +] + +[[package]] +name = "polars-lazy" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e1e24d4db8c349e9576564cfff47a3f08bb831dba9168f6599be178bc725e8" +dependencies = [ + "bitflags", + "chrono", + "either", + "memchr", + "polars-arrow", + "polars-buffer", + "polars-compute", + "polars-core", + "polars-expr", + "polars-io", + "polars-mem-engine", + "polars-ops", + "polars-plan", + "polars-stream", + "polars-time", + "polars-utils", + "rayon", + "version_check", +] + +[[package]] +name = "polars-mem-engine" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394e4cd90186043d4051ce118e90794afbe81ac5eb9a51e358a56728e8ebde3" +dependencies = [ + "memmap2", + "polars-arrow", + "polars-core", + "polars-error", + "polars-expr", + "polars-io", + "polars-ops", + "polars-plan", + "polars-time", + "polars-utils", + "rayon", + "recursive", +] + [[package]] name = "polars-ops" version = "0.53.0" @@ -1267,9 +1882,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e47b2d9b3627662650da0a8c76ce5101ed1c61b104cb2b3663e0dc711571b12" dependencies = [ "argminmax", + "base64", "bytemuck", + "chrono", + "chrono-tz", "either", "hashbrown 0.16.1", + "hex", "indexmap", "libm", "memchr", @@ -1286,6 +1905,8 @@ dependencies = [ "regex-syntax", "serde", "strum_macros", + "unicode-normalization", + "unicode-reverse", "version_check", ] @@ -1331,8 +1952,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7930d5ae1d006179e65f01af57c859307b5875a4cc078dc75257250b9ae5162" dependencies = [ "bitflags", + "blake3", "bytemuck", "bytes", + "chrono", + "chrono-tz", "either", "futures", "hashbrown 0.16.1", @@ -1352,6 +1976,7 @@ dependencies = [ "pyo3", "rayon", "recursive", + "regex", "serde", "sha2", "slotmap", @@ -1389,6 +2014,70 @@ dependencies = [ "version_check", ] +[[package]] +name = "polars-sql" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "100415f86069d7e9fbf54737148fc161a7c7316a6a7d375fb6cfc7fc64f570ae" +dependencies = [ + "bitflags", + "hex", + "polars-core", + "polars-error", + "polars-lazy", + "polars-ops", + "polars-plan", + "polars-time", + "polars-utils", + "regex", + "serde", + "sqlparser", +] + +[[package]] +name = "polars-stream" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65a0c054bdf16efd16bbc587e8d5418ae28464d61afd735513579cd3c338fa70" +dependencies = [ + "async-channel", + "async-trait", + "atomic-waker", + "bitflags", + "bytes", + "chrono-tz", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-queue", + "crossbeam-utils", + "futures", + "memchr", + "memmap2", + "num-traits", + "parking_lot", + "percent-encoding", + "pin-project-lite", + "polars-arrow", + "polars-buffer", + "polars-compute", + "polars-core", + "polars-error", + "polars-expr", + "polars-io", + "polars-mem-engine", + "polars-ops", + "polars-parquet", + "polars-plan", + "polars-time", + "polars-utils", + "rand 0.9.4", + "rayon", + "recursive", + "slotmap", + "tokio", + "version_check", +] + [[package]] name = "polars-time" version = "0.53.0" @@ -1398,6 +2087,7 @@ dependencies = [ "atoi_simd", "bytemuck", "chrono", + "chrono-tz", "now", "num-traits", "polars-arrow", @@ -1436,7 +2126,7 @@ dependencies = [ "numpy", "polars-error", "pyo3", - "rand", + "rand 0.9.4", "raw-cpuid", "rayon", "regex", @@ -1465,6 +2155,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1609,6 +2308,71 @@ dependencies = [ "syn", ] +[[package]] +name = "quick-xml" +version = "0.39.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.45" @@ -1637,7 +2401,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -1647,7 +2422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -1659,6 +2434,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_distr" version = "0.5.1" @@ -1666,7 +2447,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", - "rand", + "rand 0.9.4", ] [[package]] @@ -1733,6 +2514,26 @@ dependencies = [ "bitflags", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "regex" version = "1.12.3" @@ -1769,19 +2570,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] -name = "rmp" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "rmp-serde" -version = "1.3.1" +name = "reqwest" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" dependencies = [ "rmp", "serde", @@ -1831,6 +2688,66 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -1843,12 +2760,53 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.28" @@ -1922,6 +2880,18 @@ dependencies = [ "stacker", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.10.9" @@ -1929,7 +2899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -1965,6 +2935,22 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd-json" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4255126f310d2ba20048db6321c81ab376f6a6735608bf11f0785c41f01f64e3" +dependencies = [ + "ahash", + "halfbrown", + "once_cell", + "ref-cast", + "serde", + "serde_json", + "simdutf8", + "value-trait", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -2006,9 +2992,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] +[[package]] +name = "sqlparser" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505aa16b045c4c1375bf5f125cce3813d0176325bfe9ffc4a903f423de7774ff" +dependencies = [ + "log", + "recursive", + "sqlparser_derive", +] + +[[package]] +name = "sqlparser_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028e551d5e270b31b9f3ea271778d9d827148d4287a5d96167b6bb9787f5cc38" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "stacker" version = "0.1.24" @@ -2019,7 +3033,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -2061,6 +3075,12 @@ dependencies = [ "syn", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.117" @@ -2072,6 +3092,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "target-lexicon" version = "0.13.5" @@ -2118,17 +3158,78 @@ dependencies = [ "syn", ] +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ + "bytes", "libc", "mio", "pin-project-lite", "socket2", - "windows-sys", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] @@ -2161,6 +3262,88 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.20.0" @@ -2173,6 +3356,30 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-reverse" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6f4888ebc23094adfb574fdca9fdc891826287a6397d2cd28802ffd6f20c76" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -2185,12 +3392,36 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "unty" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.23.1" @@ -2203,6 +3434,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "value-trait" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e80f0c733af0720a501b3905d22e2f97662d8eacfe082a75ed7ffb5ab08cb59" +dependencies = [ + "float-cmp", + "halfbrown", + "itoa", + "ryu", +] + [[package]] name = "version_check" version = "0.9.5" @@ -2215,6 +3458,25 @@ version = "0.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -2252,6 +3514,16 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.122" @@ -2306,6 +3578,19 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" @@ -2318,6 +3603,35 @@ dependencies = [ "semver", ] +[[package]] +name = "web-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[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.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -2383,6 +3697,33 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -2392,6 +3733,135 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "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]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "1.0.3" @@ -2495,12 +3965,41 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + [[package]] name = "xxhash-rust" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.48" @@ -2521,6 +4020,66 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index 91a3fb3..be00f38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,11 @@ name = "dataframely" [dependencies] itertools = "*" num-format = "*" -polars = { version = ">=0.50", default-features = false, features = [] } +polars = { version = ">=0.50", default-features = false, features = [ + "dtype-full", +] } polars-arrow = ">=0.50" -polars-core = { version = ">=0.50", features = [ - "dtype-array", - "dtype-struct", -], default-features = false } +polars-core = ">=0.50" pyo3 = { version = ">=0.25", features = ["abi3-py310", "extension-module"] } pyo3-polars = { version = ">=0.23.1", features = ["derive"] } rand = { version = "0.9", features = ["std_rng"] } diff --git a/src/polars_plugin/validation_error.rs b/src/polars_plugin/validation_error.rs index 402aeca..f818adc 100644 --- a/src/polars_plugin/validation_error.rs +++ b/src/polars_plugin/validation_error.rs @@ -130,14 +130,43 @@ fn format_examples(examples: Option<&DataFrame>) -> String { let Some(df) = examples else { return String::new(); }; + let column_names: Vec<&str> = df + .get_column_names() + .into_iter() + .map(|s| s.as_str()) + .collect(); format!( "; examples: [{}]", (0..df.height()) - .map(|i| format!("{:#?}", df.get_row(i).unwrap())) + .map(|i| { + let row = df.get_row(i).unwrap(); + let fields = column_names + .iter() + .zip(row.0.iter()) + .map(|(name, value)| format!("'{}': {}", name, format_any_value(value))) + .join(", "); + format!("{{{}}}", fields) + }) .join(", ") ) } +fn format_any_value(value: &AnyValue) -> String { + match value { + AnyValue::Null => "None".to_string(), + AnyValue::Boolean(b) => { + if *b { + "True".to_string() + } else { + "False".to_string() + } + } + AnyValue::String(s) => format!("'{}'", s), + AnyValue::StringOwned(s) => format!("'{}'", s), + _ => format!("{}", value), + } +} + /* ---------------------------------------- FAILURE INFO --------------------------------------- */ struct RuleFailureInfo<'a> { From 2eace9bce94014226602adbe44d3fa6da73eddbc Mon Sep 17 00:00:00 2001 From: Oliver Borchert Date: Sun, 24 May 2026 21:58:04 +0200 Subject: [PATCH 6/9] Update --- dataframely/_native.pyi | 10 ++++++---- dataframely/_plugin.py | 3 +++ dataframely/collection/collection.py | 2 ++ dataframely/config.py | 8 ++++++++ dataframely/schema.py | 1 + src/polars_plugin/mod.rs | 4 +++- src/polars_plugin/validation_error.rs | 13 ++++++------- 7 files changed, 29 insertions(+), 12 deletions(-) diff --git a/dataframely/_native.pyi b/dataframely/_native.pyi index 527dca2..db13c5c 100644 --- a/dataframely/_native.pyi +++ b/dataframely/_native.pyi @@ -8,6 +8,7 @@ def format_rule_failures( failures_from: pl.DataFrame | None, examples_from: pl.DataFrame | None, primary_key_columns: list[str], + max_examples: int | None, ) -> str: """ Format rule failures with the same logic that produces validation errors from the @@ -18,11 +19,12 @@ def format_rule_failures( failures with a count of at least 1. failures_from: The data frame containing the rule columns providing the failures. - max_examples: The maximum number of examples to include for each failure. No - effect if `examples_from` is not provided. + examples_from: The data frame containing the example rows for each failure. primary_key_columns: The primary key columns of the schema for which to format - rule failures. This is only relevant if `examples_from` is provided and - allows for better error messages for the "primary_key" rule. + rule failures. This is only relevant if `failures_from` and `examples_from` + are provided and allows for better error messages for the "primary_key" rule. + max_examples: The maximum number of examples to include for each failure. This is + only relevant if `failures_from` and `examples_from` are provided. Returns: The formatted rule failures. diff --git a/dataframely/_plugin.py b/dataframely/_plugin.py index febc631..632e8a1 100644 --- a/dataframely/_plugin.py +++ b/dataframely/_plugin.py @@ -8,6 +8,8 @@ import polars as pl from polars.plugins import register_plugin_function +from dataframely.config import Config + PLUGIN_PATH = Path(__file__).parent IntoExpr: TypeAlias = pl.Expr | str @@ -99,6 +101,7 @@ def all_rules_required( "schema_name": schema_name, "num_rule_columns": num_rule_columns, "primary_key_columns": primary_key_columns or [], + "max_failure_examples": Config.options["max_failure_examples"], }, use_abs_path=True, is_elementwise=True, diff --git a/dataframely/collection/collection.py b/dataframely/collection/collection.py index aa2c7b4..05af1cd 100644 --- a/dataframely/collection/collection.py +++ b/dataframely/collection/collection.py @@ -33,6 +33,7 @@ from dataframely._storage.delta import DeltaStorageBackend from dataframely._storage.parquet import ParquetStorageBackend from dataframely._typing import LazyFrame, Validation +from dataframely.config import Config from dataframely.exc import ( DeserializationError, ValidationError, @@ -420,6 +421,7 @@ def validate( failures_from=failure._df.select(counts.keys()), examples_from=failure.invalid(), primary_key_columns=cls.member_schemas()[member].primary_key(), + max_examples=Config.options["max_failure_examples"], ) details = [ diff --git a/dataframely/config.py b/dataframely/config.py index c597745..cb29416 100644 --- a/dataframely/config.py +++ b/dataframely/config.py @@ -15,11 +15,14 @@ class Options(TypedDict): #: The maximum number of iterations to use for "fuzzy" sampling. max_sampling_iterations: int + #: The maximum number of examples to include in failure messages. + max_failure_examples: int def default_options() -> Options: return { "max_sampling_iterations": 10_000, + "max_failure_examples": 0, } @@ -40,6 +43,11 @@ def set_max_sampling_iterations(iterations: int) -> None: :meth:`Schema.sample`.""" Config.options["max_sampling_iterations"] = iterations + @staticmethod + def set_max_failure_examples(max_examples: int) -> None: + """Set the maximum number of examples to include in failure messages.""" + Config.options["max_failure_examples"] = max_examples + @staticmethod def restore_defaults() -> None: """Restore the defaults of the configuration.""" diff --git a/dataframely/schema.py b/dataframely/schema.py index 6186013..c7fc92c 100644 --- a/dataframely/schema.py +++ b/dataframely/schema.py @@ -583,6 +583,7 @@ def validate( failures_from=failure._df.select(counts.keys()), examples_from=failure.invalid(), primary_key_columns=cls.primary_key(), + max_examples=Config.options["max_failure_examples"], ) ) return out diff --git a/src/polars_plugin/mod.rs b/src/polars_plugin/mod.rs index 09534cc..ae459e4 100644 --- a/src/polars_plugin/mod.rs +++ b/src/polars_plugin/mod.rs @@ -62,9 +62,10 @@ pub fn all_rules(inputs: &[Series]) -> PolarsResult { struct RequiredValidationKwargs { schema_name: String, null_is_valid: bool, - primary_key_columns: Option>, #[serde(default)] num_rule_columns: Option, + primary_key_columns: Option>, + max_failure_examples: usize, } /// Reduce a set of boolean columns into a single boolean scalar, AND-ing all values. @@ -114,6 +115,7 @@ pub fn all_rules_required( Some(failures_from), Some(examples_from), kwargs.primary_key_columns.unwrap_or_default(), + kwargs.max_failure_examples, ); Err(polars_err!(ComputeError: format!("\n{}", error.to_string(Some(&kwargs.schema_name))))) } diff --git a/src/polars_plugin/validation_error.rs b/src/polars_plugin/validation_error.rs index f818adc..e13bdbc 100644 --- a/src/polars_plugin/validation_error.rs +++ b/src/polars_plugin/validation_error.rs @@ -10,9 +10,6 @@ use super::RuleFailure; create_exception!(exc, PyRuleValidationError, PyException); -const MAX_EXAMPLES_ENV_NAME: &str = "DATAFRAMELY_MAX_VALIDATION_FAILURE_EXAMPLES"; -const DEFAULT_MAX_EXAMPLES: usize = 0; - /* -------------------------------------- VALIDATION ERROR ------------------------------------- */ pub struct RuleValidationError<'a> { @@ -27,6 +24,7 @@ impl<'a> RuleValidationError<'a> { failures_from: Option, examples_from: Option, primary_key_columns: Vec, + max_examples: usize, ) -> Self { let num_rule_failures = failure_counts.len(); let (flat_column_errors, schema_errors): (Vec<_>, Vec<_>) = failure_counts @@ -51,6 +49,7 @@ impl<'a> RuleValidationError<'a> { failures_from.as_ref(), examples_from.as_ref(), Some(vec![key.to_string()]), + max_examples, ) }) .collect::>(), @@ -75,6 +74,7 @@ impl<'a> RuleValidationError<'a> { failures_from.as_ref(), examples_from.as_ref(), data_columns, + max_examples, ) }) .collect(); @@ -181,12 +181,9 @@ impl<'a> RuleFailureInfo<'a> { failures_from: Option<&DataFrame>, examples_from: Option<&DataFrame>, data_columns: Option>, + max_examples: usize, ) -> Self { // Check if we should return any examples at all - let max_examples = std::env::var(MAX_EXAMPLES_ENV_NAME) - .ok() - .and_then(|val| val.parse::().ok()) - .unwrap_or(DEFAULT_MAX_EXAMPLES); if max_examples == 0 { return Self { failure, @@ -239,6 +236,7 @@ pub fn format_rule_failures( failures_from: Option, examples_from: Option, primary_key_columns: Vec, + max_examples: usize, ) -> String { let validation_error = RuleValidationError::new( failures @@ -251,6 +249,7 @@ pub fn format_rule_failures( failures_from.map(|df| df.into()), examples_from.map(|df| df.into()), primary_key_columns, + max_examples, ); return validation_error.to_string(None); } From 977e3dbb03abe4616245c595b8b45181c43f8467 Mon Sep 17 00:00:00 2001 From: Oliver Borchert Date: Sun, 24 May 2026 22:09:08 +0200 Subject: [PATCH 7/9] Update --- dataframely/config.py | 2 +- docs/guides/features/lazy-validation.md | 24 +++++++++++++++++++ tests/schema/test_validate.py | 31 +++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/dataframely/config.py b/dataframely/config.py index cb29416..415432e 100644 --- a/dataframely/config.py +++ b/dataframely/config.py @@ -12,7 +12,7 @@ from typing_extensions import Unpack -class Options(TypedDict): +class Options(TypedDict, total=False): #: The maximum number of iterations to use for "fuzzy" sampling. max_sampling_iterations: int #: The maximum number of examples to include in failure messages. diff --git a/docs/guides/features/lazy-validation.md b/docs/guides/features/lazy-validation.md index 6c9edbe..61e31be 100644 --- a/docs/guides/features/lazy-validation.md +++ b/docs/guides/features/lazy-validation.md @@ -39,3 +39,27 @@ For collections, the error message for `eager=False` is limited and non-determin information about a single member and, if multiple members fail validation, the member that the error message refers to may vary across executions. ``` + +```{note} +When the lazy frame is collected on the polars _streaming_ engine, lazy validation may not surface _all_ validation +issues: validation is aborted as soon as the first failure is encountered. As a result, both the set of rules reported +in the error message and the specific failure surfaced may be non-deterministic across executions. +``` + +## Including failure examples in error messages + +By default, validation error messages report only the name of each failing rule and the number of rows that violated it. +For easier debugging, dataframely can additionally include a few example rows for each failing rule. This is configured +via {meth}`~dataframely.Config.set_max_failure_examples` (or the `max_failure_examples` keyword on the +{class}`~dataframely.Config` context manager) and applies to both `eager=True` and `eager=False`: + +```python +import dataframely as dy + +with dy.Config(max_failure_examples=5): + MySchema.validate(df) +``` + +For column-level rules, examples include the value in the offending column. For schema-level rules, examples include all +data columns of the schema, except for the `primary_key` rule where examples are limited to the primary key columns. +The default value of `0` disables examples entirely. diff --git a/tests/schema/test_validate.py b/tests/schema/test_validate.py index b88cacd..eeb31ee 100644 --- a/tests/schema/test_validate.py +++ b/tests/schema/test_validate.py @@ -130,12 +130,39 @@ def test_invalid_primary_key( with pytest.raises( ValidationError if eager else plexc.ComputeError, match=r"1 rules failed validation", - ) as exc_info: + ): _validate_and_collect(MySchema, df, eager=eager) - exc_info.match(r"with 2 distinct examples") assert not MySchema.is_valid(df) +@pytest.mark.parametrize("df_type", [pl.DataFrame, pl.LazyFrame]) +@pytest.mark.parametrize("eager", [True, False]) +def test_invalid_primary_key_with_examples( + df_type: type[pl.DataFrame] | type[pl.LazyFrame], eager: bool +) -> None: + df = df_type({"a": [1, 1], "b": ["x", "y"], "c": ["1", "2"]}) + with dy.Config(max_failure_examples=5): + with pytest.raises( + ValidationError if eager else plexc.ComputeError, + match=r"'primary_key' failed for 2 rows; examples: \[\{'a': 1\}\]", + ): + _validate_and_collect(MySchema, df, eager=eager) + + +@pytest.mark.parametrize("df_type", [pl.DataFrame, pl.LazyFrame]) +@pytest.mark.parametrize("eager", [True, False]) +def test_invalid_column_contents_with_examples( + df_type: type[pl.DataFrame] | type[pl.LazyFrame], eager: bool +) -> None: + df = df_type({"a": [1, 2, 3], "b": ["x", "longtext", None], "c": ["1", None, "3"]}) + with dy.Config(max_failure_examples=5): + with pytest.raises( + ValidationError if eager else plexc.ComputeError, + match=r"examples: \[\{'b': 'longtext'\}\]", + ): + _validate_and_collect(MySchema, df, eager=eager) + + @pytest.mark.parametrize("df_type", [pl.DataFrame, pl.LazyFrame]) @pytest.mark.parametrize("eager", [True, False]) def test_violated_custom_rule( From d486997b6e8ea88683443685a9a54e97dc443861 Mon Sep 17 00:00:00 2001 From: Oliver Borchert Date: Sun, 24 May 2026 22:15:35 +0200 Subject: [PATCH 8/9] Fix --- tests/schema/test_validate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/schema/test_validate.py b/tests/schema/test_validate.py index eeb31ee..17599ee 100644 --- a/tests/schema/test_validate.py +++ b/tests/schema/test_validate.py @@ -158,7 +158,7 @@ def test_invalid_column_contents_with_examples( with dy.Config(max_failure_examples=5): with pytest.raises( ValidationError if eager else plexc.ComputeError, - match=r"examples: \[\{'b': 'longtext'\}\]", + match=r"examples: \[\{'b': 'longtext'\}\]" if eager else None, ): _validate_and_collect(MySchema, df, eager=eager) From 39231105eb40a0d000ff8e507deac5d010d516eb Mon Sep 17 00:00:00 2001 From: Oliver Borchert Date: Mon, 25 May 2026 02:32:40 +0200 Subject: [PATCH 9/9] Coverage --- tests/collection/test_filter_validate.py | 16 ++++++++++++++++ tests/test_config.py | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/tests/collection/test_filter_validate.py b/tests/collection/test_filter_validate.py index 926ea3a..6dd49ee 100644 --- a/tests/collection/test_filter_validate.py +++ b/tests/collection/test_filter_validate.py @@ -210,6 +210,22 @@ def test_validate_without_filter_with_rule_violation_eager( exc.match(r"'min' failed for 1 rows") +def test_validate_with_single_member_rule_violation_eager() -> None: + # Only the `second` member has a rule violation; the `first` member is valid. + # This exercises the branch that skips members without failures while still + # producing an error for the failing one. + data = { + "first": pl.LazyFrame({"a": [1, 2, 3], "b": [1, 2, 3]}), + "second": pl.LazyFrame({"a": [1, 2, 3], "b": [0, 1, 2]}), + } + with pytest.raises(ValidationError, match=r"1 members failed validation") as exc: + SimpleCollection.validate(data) + + exc.match(r"Member 'second' failed validation") + exc.match(r"'min' failed for 1 rows") + assert "Member 'first'" not in str(exc.value) + + def test_validate_without_filter_with_rule_violation_lazy( data_without_filter_with_rule_violation: tuple[pl.LazyFrame, pl.LazyFrame], ) -> None: diff --git a/tests/test_config.py b/tests/test_config.py index c63c847..63225e0 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,6 +10,15 @@ def test_config_global() -> None: dy.Config.restore_defaults() +def test_config_global_max_failure_examples() -> None: + try: + dy.Config.set_max_failure_examples(7) + assert dy.Config.options["max_failure_examples"] == 7 + finally: + dy.Config.restore_defaults() + assert dy.Config.options["max_failure_examples"] == 0 + + def test_config_local() -> None: try: with dy.Config(max_sampling_iterations=35):