From e46fc07a02e9143bc3f46805e1837435d48ff306 Mon Sep 17 00:00:00 2001 From: Shlok Palrecha Date: Tue, 3 Mar 2026 20:32:19 +0530 Subject: [PATCH 1/2] Add comprehensive tests for kwargs validation across all functions - Add 26 tests covering kwargs validation for all major user-facing functions - Tests ensure functions properly raise TypeError for invalid keyword arguments - Covers sampling diagnostics, summary statistics, LOO functions, metrics, and accessors - Includes tests for common typos (e.g., 'dims' instead of 'dim') - All tests pass successfully (26/26) - Full test suite passes (2475 passed, 2 skipped) This addresses issue #142 by adding comprehensive test coverage to ensure kwargs validation continues to work properly and prevent regressions. While PR #143 initially fixed the validation for hdi/eti/kde/histogram, investigation shows all functions now properly validate kwargs, but test coverage was incomplete. Related to #142 --- INVESTIGATION_SUMMARY.md | 71 +++++++++ tests/test_kwargs_validation.py | 274 ++++++++++++++++++++++++++++++++ 2 files changed, 345 insertions(+) create mode 100644 INVESTIGATION_SUMMARY.md create mode 100644 tests/test_kwargs_validation.py diff --git a/INVESTIGATION_SUMMARY.md b/INVESTIGATION_SUMMARY.md new file mode 100644 index 00000000..9c9a6973 --- /dev/null +++ b/INVESTIGATION_SUMMARY.md @@ -0,0 +1,71 @@ +# Investigation Notes: Kwargs Validation + +Looking into issue #142 - checking if functions properly reject invalid kwargs. + +## Background + +Issue #142 is about making sure functions raise TypeError for invalid keyword arguments instead of silently ignoring them. PR #143 already fixed this for hdi(), eti(), kde(), and histogram(). + +Wanted to see if other functions validate kwargs too. + +## What I did + +Wrote some test scripts to check the main functions. My approach: +- Tested 24+ functions with invalid kwargs like `invalid_kwarg="test"` +- Also tried common typos (`dims` vs `dim`, `methods` vs `method`) +- Checked both direct calls and `.azstats` accessor methods + +## Results + +Good news - everything works! All the functions I tested properly reject invalid kwargs. + +Functions tested: +- Sampling diagnostics: ess(), rhat(), mcse(), bfmi(), diagnose() +- Summary stats: summary(), mean(), median(), mode(), ci_in_rope() +- Density/viz: hdi(), eti(), kde(), histogram(), qds(), ecdf() +- LOO stuff: loo(), compare() +- Metrics: bayesian_r2(), metrics() +- Other: thin(), weight_predictions(), bayes_factor(), psense() + +### How it works + +Functions with `**kwargs` either pass them downstream (where they get validated) or check for unexpected kwargs explicitly and raise TypeError. + +Two error patterns I saw: +- `"got an unexpected keyword argument..."` (most common) +- `"got multiple values for argument 'dims'"` (when typo conflicts with internal param) + +Both work fine for catching bad inputs. + +## The actual problem + +Only 4 functions have tests for kwargs validation (from PR #143). The functionality is there, but test coverage is incomplete. + +So I added 26 tests covering all the functions above. Tests check both direct calls and accessor methods, plus common typo scenarios. + +## Test results + +``` +tests/test_kwargs_validation.py: 26 passed +Full test suite: 2475 passed, 2 skipped +``` + +## Tests added + +`tests/test_kwargs_validation.py` has 26 tests organized into: +- TestKwargsValidationSamplingDiagnostics (5 tests) +- TestKwargsValidationSummaryStatistics (5 tests) +- TestKwargsValidationVisualizationFunctions (2 tests) +- TestKwargsValidationLOOFunctions (2 tests) +- TestKwargsValidationMetrics (2 tests) +- TestKwargsValidationOtherFunctions (4 tests) +- TestKwargsValidationAccessors (6 tests) + +## Why add tests if validation already works? + +- Prevents regressions if someone refactors the validation logic later +- Documents expected behavior for new contributors +- Covers edge cases and common typos +- Matches the test coverage level I saw elsewhere in the codebase + +Issue #142 is technically resolved functionality-wise, but this adds the test documentation we were missing. diff --git a/tests/test_kwargs_validation.py b/tests/test_kwargs_validation.py new file mode 100644 index 00000000..e40c3917 --- /dev/null +++ b/tests/test_kwargs_validation.py @@ -0,0 +1,274 @@ +""" +Comprehensive tests for kwargs validation across arviz-stats functions. + +These tests ensure that functions properly raise TypeError when given +unexpected keyword arguments, preventing silent failures from typos. + +Related to issue #142 and PR #143. +""" + +# pylint: disable=redefined-outer-name, no-self-use, unexpected-keyword-arg +import pytest +from arviz_base import load_arviz_data + + +class TestKwargsValidationSamplingDiagnostics: + """Test kwargs validation for sampling diagnostic functions.""" + + @pytest.fixture(scope="class") + def idata(self): + """Load test data.""" + return load_arviz_data("non_centered_eight") + + def test_ess_rejects_invalid_kwargs(self, idata): + """Test that ess() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.ess(idata.posterior, invalid_kwarg="test") + + # Also test common typos + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.ess(idata.posterior, methods="bulk") # should be 'method' + + def test_rhat_rejects_invalid_kwargs(self, idata): + """Test that rhat() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.rhat(idata.posterior, invalid_kwarg="test") + + # Test common typo + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.rhat(idata.posterior, methods="rank") # should be 'method' + + def test_mcse_rejects_invalid_kwargs(self, idata): + """Test that mcse() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.mcse(idata.posterior, invalid_kwarg="test") + + # Test common typo + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.mcse(idata.posterior, methods="mean") # should be 'method' + + def test_bfmi_rejects_invalid_kwargs(self, idata): + """Test that bfmi() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.bfmi(idata, invalid_kwarg="test") + + def test_diagnose_rejects_invalid_kwargs(self, idata): + """Test that diagnose() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.diagnose(idata, invalid_kwarg="test") + + +class TestKwargsValidationSummaryStatistics: + """Test kwargs validation for summary statistic functions.""" + + @pytest.fixture(scope="class") + def idata(self): + """Load test data.""" + return load_arviz_data("non_centered_eight") + + def test_summary_rejects_invalid_kwargs(self, idata): + """Test that summary() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.summary(idata, invalid_kwarg="test") + + # Test common typo + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.summary(idata, sample_dim=["chain", "draw"]) # should be 'sample_dims' + + def test_mean_rejects_invalid_kwargs(self, idata): + """Test that mean() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.mean(idata.posterior, invalid_kwarg="test") + + # Test common typo (errors with "multiple values" due to internal dims variable) + with pytest.raises(TypeError): + azs.mean(idata.posterior, dims="draw") # should be 'dim' + + def test_median_rejects_invalid_kwargs(self, idata): + """Test that median() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.median(idata.posterior, invalid_kwarg="test") + + # Test common typo (errors with "multiple values" due to internal dims variable) + with pytest.raises(TypeError): + azs.median(idata.posterior, dims="draw") # should be 'dim' + + def test_mode_rejects_invalid_kwargs(self, idata): + """Test that mode() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.mode(idata.posterior, invalid_kwarg="test") + + # Test common typo (errors with "multiple values" due to internal dims variable) + with pytest.raises(TypeError): + azs.mode(idata.posterior, dims="draw") # should be 'dim' + + def test_ci_in_rope_rejects_invalid_kwargs(self, idata): + """Test that ci_in_rope() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.ci_in_rope(idata.posterior["mu"], rope=(-1, 1), invalid_kwarg="test") + + +class TestKwargsValidationVisualizationFunctions: + """Test kwargs validation for visualization/density functions.""" + + @pytest.fixture(scope="class") + def idata(self): + """Load test data.""" + return load_arviz_data("non_centered_eight") + + def test_qds_rejects_invalid_kwargs(self, idata): + """Test that qds() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.qds(idata.posterior["mu"], invalid_kwarg="test") + + def test_ecdf_rejects_invalid_kwargs(self, idata): + """Test that ecdf() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.ecdf(idata.posterior["mu"], invalid_kwarg="test") + + +class TestKwargsValidationLOOFunctions: + """Test kwargs validation for LOO/model comparison functions.""" + + @pytest.fixture(scope="class") + def idata(self): + """Load test data.""" + return load_arviz_data("non_centered_eight") + + def test_loo_rejects_invalid_kwargs(self, idata): + """Test that loo() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.loo(idata, invalid_kwarg="test") + + def test_compare_rejects_invalid_kwargs(self, idata): + """Test that compare() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.compare({"model": idata}, invalid_kwarg="test") + + +class TestKwargsValidationMetrics: + """Test kwargs validation for metric functions.""" + + @pytest.fixture(scope="class") + def idata(self): + """Load test data.""" + return load_arviz_data("non_centered_eight") + + def test_bayesian_r2_rejects_invalid_kwargs(self, idata): + """Test that bayesian_r2() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.bayesian_r2(idata, invalid_kwarg="test") + + def test_metrics_rejects_invalid_kwargs(self, idata): + """Test that metrics() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.metrics(idata, invalid_kwarg="test") + + +class TestKwargsValidationOtherFunctions: + """Test kwargs validation for other utility functions.""" + + @pytest.fixture(scope="class") + def idata(self): + """Load test data.""" + return load_arviz_data("non_centered_eight") + + def test_thin_rejects_invalid_kwargs(self, idata): + """Test that thin() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.thin(idata, factor=2, invalid_kwarg="test") + + def test_weight_predictions_rejects_invalid_kwargs(self, idata): + """Test that weight_predictions() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.weight_predictions(idata.posterior_predictive, invalid_kwarg="test") + + def test_bayes_factor_rejects_invalid_kwargs(self, idata): + """Test that bayes_factor() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.bayes_factor(idata.posterior, var_names="mu", invalid_kwarg="test") + + def test_psense_rejects_invalid_kwargs(self, idata): + """Test that psense() raises TypeError for invalid kwargs.""" + import arviz_stats as azs + + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + azs.psense(idata, var_name="mu", invalid_kwarg="test") + + +class TestKwargsValidationAccessors: + """Test kwargs validation for accessor methods.""" + + @pytest.fixture(scope="class") + def data_array(self): + """Load test data and extract DataArray.""" + idata = load_arviz_data("non_centered_eight") + return idata.posterior["mu"] + + def test_accessor_ess_rejects_invalid_kwargs(self, data_array): + """Test that .azstats.ess() raises TypeError for invalid kwargs.""" + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + data_array.azstats.ess(invalid_kwarg="test") + + def test_accessor_rhat_rejects_invalid_kwargs(self, data_array): + """Test that .azstats.rhat() raises TypeError for invalid kwargs.""" + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + data_array.azstats.rhat(invalid_kwarg="test") + + def test_accessor_mcse_rejects_invalid_kwargs(self, data_array): + """Test that .azstats.mcse() raises TypeError for invalid kwargs.""" + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + data_array.azstats.mcse(invalid_kwarg="test") + + def test_accessor_mean_rejects_invalid_kwargs(self, data_array): + """Test that .azstats.mean() raises TypeError for invalid kwargs.""" + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + data_array.azstats.mean(invalid_kwarg="test") + + def test_accessor_median_rejects_invalid_kwargs(self, data_array): + """Test that .azstats.median() raises TypeError for invalid kwargs.""" + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + data_array.azstats.median(invalid_kwarg="test") + + def test_accessor_mode_rejects_invalid_kwargs(self, data_array): + """Test that .azstats.mode() raises TypeError for invalid kwargs.""" + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + data_array.azstats.mode(invalid_kwarg="test") From d869747cef2136325e4aa52f4a4984e450d90074 Mon Sep 17 00:00:00 2001 From: Shlok Palrecha Date: Thu, 5 Mar 2026 21:42:55 +0530 Subject: [PATCH 2/2] Refactor kwargs validation tests based on feedback Address review feedback from OriolAbril on PR #314: - Only test accessor methods that accept **kwargs - Use pytest.mark.parametrize to reduce test duplication - Remove test docstrings (test names are self-explanatory) - Use importorskip pattern for minimal environment support - Use datatree fixture from conftest.py instead of redefining - Remove INVESTIGATION_SUMMARY.md from repository - Import numpy at module level instead of inside test functions - Follow the same pattern as tests in PR #143 The parametrized test covers 20 accessor methods, with 3 additional tests for methods that require positional arguments before **kwargs. All 23 tests pass successfully. --- INVESTIGATION_SUMMARY.md | 71 --------- tests/base/test_stats.py | 40 +++++ tests/test_kwargs_validation.py | 274 -------------------------------- 3 files changed, 40 insertions(+), 345 deletions(-) delete mode 100644 INVESTIGATION_SUMMARY.md delete mode 100644 tests/test_kwargs_validation.py diff --git a/INVESTIGATION_SUMMARY.md b/INVESTIGATION_SUMMARY.md deleted file mode 100644 index 9c9a6973..00000000 --- a/INVESTIGATION_SUMMARY.md +++ /dev/null @@ -1,71 +0,0 @@ -# Investigation Notes: Kwargs Validation - -Looking into issue #142 - checking if functions properly reject invalid kwargs. - -## Background - -Issue #142 is about making sure functions raise TypeError for invalid keyword arguments instead of silently ignoring them. PR #143 already fixed this for hdi(), eti(), kde(), and histogram(). - -Wanted to see if other functions validate kwargs too. - -## What I did - -Wrote some test scripts to check the main functions. My approach: -- Tested 24+ functions with invalid kwargs like `invalid_kwarg="test"` -- Also tried common typos (`dims` vs `dim`, `methods` vs `method`) -- Checked both direct calls and `.azstats` accessor methods - -## Results - -Good news - everything works! All the functions I tested properly reject invalid kwargs. - -Functions tested: -- Sampling diagnostics: ess(), rhat(), mcse(), bfmi(), diagnose() -- Summary stats: summary(), mean(), median(), mode(), ci_in_rope() -- Density/viz: hdi(), eti(), kde(), histogram(), qds(), ecdf() -- LOO stuff: loo(), compare() -- Metrics: bayesian_r2(), metrics() -- Other: thin(), weight_predictions(), bayes_factor(), psense() - -### How it works - -Functions with `**kwargs` either pass them downstream (where they get validated) or check for unexpected kwargs explicitly and raise TypeError. - -Two error patterns I saw: -- `"got an unexpected keyword argument..."` (most common) -- `"got multiple values for argument 'dims'"` (when typo conflicts with internal param) - -Both work fine for catching bad inputs. - -## The actual problem - -Only 4 functions have tests for kwargs validation (from PR #143). The functionality is there, but test coverage is incomplete. - -So I added 26 tests covering all the functions above. Tests check both direct calls and accessor methods, plus common typo scenarios. - -## Test results - -``` -tests/test_kwargs_validation.py: 26 passed -Full test suite: 2475 passed, 2 skipped -``` - -## Tests added - -`tests/test_kwargs_validation.py` has 26 tests organized into: -- TestKwargsValidationSamplingDiagnostics (5 tests) -- TestKwargsValidationSummaryStatistics (5 tests) -- TestKwargsValidationVisualizationFunctions (2 tests) -- TestKwargsValidationLOOFunctions (2 tests) -- TestKwargsValidationMetrics (2 tests) -- TestKwargsValidationOtherFunctions (4 tests) -- TestKwargsValidationAccessors (6 tests) - -## Why add tests if validation already works? - -- Prevents regressions if someone refactors the validation logic later -- Documents expected behavior for new contributors -- Covers edge cases and common typos -- Matches the test coverage level I saw elsewhere in the codebase - -Issue #142 is technically resolved functionality-wise, but this adds the test documentation we were missing. diff --git a/tests/base/test_stats.py b/tests/base/test_stats.py index 89601ba6..710e031e 100644 --- a/tests/base/test_stats.py +++ b/tests/base/test_stats.py @@ -72,6 +72,46 @@ def test_extra_kwargs_raise(centered_eight, func): getattr(accessor, func)(dims="draw") +@pytest.mark.parametrize( + "func", + ( + "ess", + "rhat", + "rhat_nested", + "mcse", + "qds", + "get_bins", + "compute_ranks", + "ecdf", + "pareto_min_ss", + "psislw", + "bfmi", + "pareto_khat", + "loo_expectation", + "loo_quantile", + "power_scale_lw", + "power_scale_sense", + "autocorr", + "mean", + "median", + "mode", + ), +) +def test_accessor_kwargs_raise(datatree, func): + accessor = datatree.posterior.dataset.azstats + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + getattr(accessor, func)(invalid_kwarg="value") + + +@pytest.mark.parametrize("func", ("loo_score", "loo_pit", "loo_r2")) +def test_loo_accessor_kwargs_raise(datatree, func): + rng = np.random.default_rng() + y_obs = xr.DataArray(rng.normal(size=8)) + accessor = datatree.posterior.dataset.azstats + with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): + getattr(accessor, func)(y_obs, invalid_kwarg="value") + + def test_hdi_idata(centered_eight): accessor = centered_eight.posterior.ds.azstats result = accessor.hdi() diff --git a/tests/test_kwargs_validation.py b/tests/test_kwargs_validation.py deleted file mode 100644 index e40c3917..00000000 --- a/tests/test_kwargs_validation.py +++ /dev/null @@ -1,274 +0,0 @@ -""" -Comprehensive tests for kwargs validation across arviz-stats functions. - -These tests ensure that functions properly raise TypeError when given -unexpected keyword arguments, preventing silent failures from typos. - -Related to issue #142 and PR #143. -""" - -# pylint: disable=redefined-outer-name, no-self-use, unexpected-keyword-arg -import pytest -from arviz_base import load_arviz_data - - -class TestKwargsValidationSamplingDiagnostics: - """Test kwargs validation for sampling diagnostic functions.""" - - @pytest.fixture(scope="class") - def idata(self): - """Load test data.""" - return load_arviz_data("non_centered_eight") - - def test_ess_rejects_invalid_kwargs(self, idata): - """Test that ess() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.ess(idata.posterior, invalid_kwarg="test") - - # Also test common typos - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.ess(idata.posterior, methods="bulk") # should be 'method' - - def test_rhat_rejects_invalid_kwargs(self, idata): - """Test that rhat() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.rhat(idata.posterior, invalid_kwarg="test") - - # Test common typo - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.rhat(idata.posterior, methods="rank") # should be 'method' - - def test_mcse_rejects_invalid_kwargs(self, idata): - """Test that mcse() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.mcse(idata.posterior, invalid_kwarg="test") - - # Test common typo - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.mcse(idata.posterior, methods="mean") # should be 'method' - - def test_bfmi_rejects_invalid_kwargs(self, idata): - """Test that bfmi() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.bfmi(idata, invalid_kwarg="test") - - def test_diagnose_rejects_invalid_kwargs(self, idata): - """Test that diagnose() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.diagnose(idata, invalid_kwarg="test") - - -class TestKwargsValidationSummaryStatistics: - """Test kwargs validation for summary statistic functions.""" - - @pytest.fixture(scope="class") - def idata(self): - """Load test data.""" - return load_arviz_data("non_centered_eight") - - def test_summary_rejects_invalid_kwargs(self, idata): - """Test that summary() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.summary(idata, invalid_kwarg="test") - - # Test common typo - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.summary(idata, sample_dim=["chain", "draw"]) # should be 'sample_dims' - - def test_mean_rejects_invalid_kwargs(self, idata): - """Test that mean() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.mean(idata.posterior, invalid_kwarg="test") - - # Test common typo (errors with "multiple values" due to internal dims variable) - with pytest.raises(TypeError): - azs.mean(idata.posterior, dims="draw") # should be 'dim' - - def test_median_rejects_invalid_kwargs(self, idata): - """Test that median() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.median(idata.posterior, invalid_kwarg="test") - - # Test common typo (errors with "multiple values" due to internal dims variable) - with pytest.raises(TypeError): - azs.median(idata.posterior, dims="draw") # should be 'dim' - - def test_mode_rejects_invalid_kwargs(self, idata): - """Test that mode() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.mode(idata.posterior, invalid_kwarg="test") - - # Test common typo (errors with "multiple values" due to internal dims variable) - with pytest.raises(TypeError): - azs.mode(idata.posterior, dims="draw") # should be 'dim' - - def test_ci_in_rope_rejects_invalid_kwargs(self, idata): - """Test that ci_in_rope() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.ci_in_rope(idata.posterior["mu"], rope=(-1, 1), invalid_kwarg="test") - - -class TestKwargsValidationVisualizationFunctions: - """Test kwargs validation for visualization/density functions.""" - - @pytest.fixture(scope="class") - def idata(self): - """Load test data.""" - return load_arviz_data("non_centered_eight") - - def test_qds_rejects_invalid_kwargs(self, idata): - """Test that qds() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.qds(idata.posterior["mu"], invalid_kwarg="test") - - def test_ecdf_rejects_invalid_kwargs(self, idata): - """Test that ecdf() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.ecdf(idata.posterior["mu"], invalid_kwarg="test") - - -class TestKwargsValidationLOOFunctions: - """Test kwargs validation for LOO/model comparison functions.""" - - @pytest.fixture(scope="class") - def idata(self): - """Load test data.""" - return load_arviz_data("non_centered_eight") - - def test_loo_rejects_invalid_kwargs(self, idata): - """Test that loo() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.loo(idata, invalid_kwarg="test") - - def test_compare_rejects_invalid_kwargs(self, idata): - """Test that compare() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.compare({"model": idata}, invalid_kwarg="test") - - -class TestKwargsValidationMetrics: - """Test kwargs validation for metric functions.""" - - @pytest.fixture(scope="class") - def idata(self): - """Load test data.""" - return load_arviz_data("non_centered_eight") - - def test_bayesian_r2_rejects_invalid_kwargs(self, idata): - """Test that bayesian_r2() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.bayesian_r2(idata, invalid_kwarg="test") - - def test_metrics_rejects_invalid_kwargs(self, idata): - """Test that metrics() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.metrics(idata, invalid_kwarg="test") - - -class TestKwargsValidationOtherFunctions: - """Test kwargs validation for other utility functions.""" - - @pytest.fixture(scope="class") - def idata(self): - """Load test data.""" - return load_arviz_data("non_centered_eight") - - def test_thin_rejects_invalid_kwargs(self, idata): - """Test that thin() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.thin(idata, factor=2, invalid_kwarg="test") - - def test_weight_predictions_rejects_invalid_kwargs(self, idata): - """Test that weight_predictions() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.weight_predictions(idata.posterior_predictive, invalid_kwarg="test") - - def test_bayes_factor_rejects_invalid_kwargs(self, idata): - """Test that bayes_factor() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.bayes_factor(idata.posterior, var_names="mu", invalid_kwarg="test") - - def test_psense_rejects_invalid_kwargs(self, idata): - """Test that psense() raises TypeError for invalid kwargs.""" - import arviz_stats as azs - - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - azs.psense(idata, var_name="mu", invalid_kwarg="test") - - -class TestKwargsValidationAccessors: - """Test kwargs validation for accessor methods.""" - - @pytest.fixture(scope="class") - def data_array(self): - """Load test data and extract DataArray.""" - idata = load_arviz_data("non_centered_eight") - return idata.posterior["mu"] - - def test_accessor_ess_rejects_invalid_kwargs(self, data_array): - """Test that .azstats.ess() raises TypeError for invalid kwargs.""" - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - data_array.azstats.ess(invalid_kwarg="test") - - def test_accessor_rhat_rejects_invalid_kwargs(self, data_array): - """Test that .azstats.rhat() raises TypeError for invalid kwargs.""" - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - data_array.azstats.rhat(invalid_kwarg="test") - - def test_accessor_mcse_rejects_invalid_kwargs(self, data_array): - """Test that .azstats.mcse() raises TypeError for invalid kwargs.""" - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - data_array.azstats.mcse(invalid_kwarg="test") - - def test_accessor_mean_rejects_invalid_kwargs(self, data_array): - """Test that .azstats.mean() raises TypeError for invalid kwargs.""" - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - data_array.azstats.mean(invalid_kwarg="test") - - def test_accessor_median_rejects_invalid_kwargs(self, data_array): - """Test that .azstats.median() raises TypeError for invalid kwargs.""" - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - data_array.azstats.median(invalid_kwarg="test") - - def test_accessor_mode_rejects_invalid_kwargs(self, data_array): - """Test that .azstats.mode() raises TypeError for invalid kwargs.""" - with pytest.raises(TypeError, match=".*unexpected keyword argument.*"): - data_array.azstats.mode(invalid_kwarg="test")