From 9f34dd515d32dec0adcdc5b65228397e9e11ded4 Mon Sep 17 00:00:00 2001 From: "A. Riddell" Date: Wed, 11 Mar 2026 18:17:31 -0400 Subject: [PATCH 1/6] fix: Use importlib.metadata instead of pkg_resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setuptools 82.0.0 removes pkg_resources, breaking this software. This patch replaces pkg_resources with the recommended importlib.metadata. PyStan was using pkg_resources for compatibility with versions older than Python 3.12, versions which should use importlib.metadata instead (see https://docs.python.org/3/library/importlib.metadata.html#entry-points). Also updates pyproject.toml to require Python ≥3.12. Older versions will need to use setuptools < 82.0.0. See https://github.com/pypa/setuptools/issues/5174 for tips on how to deal with broken builds. Also uncap the version of pysimdjson. The package does not seem to use semantic versioning and users will likely want to use wheels that are (only) being built for newer Pythons. --- pyproject.toml | 4 ++-- stan/plugins.py | 8 +++----- tests/test_plugins.py | 13 +++++++------ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6a582e6..a5471ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,10 +22,10 @@ classifiers = [ ] [tool.poetry.dependencies] -python = "^3.10" +python = "^3.12" aiohttp = "^3.6" httpstan = "~4.13" -pysimdjson = ">=5.0.2,<7" +pysimdjson = ">=5.0.2" numpy = ">=1.19" clikit = "^0.6" setuptools = "*" diff --git a/stan/plugins.py b/stan/plugins.py index 0a59d43..ea0d9dc 100644 --- a/stan/plugins.py +++ b/stan/plugins.py @@ -1,14 +1,12 @@ import abc -from typing import Generator - -import pkg_resources +import importlib.metadata import stan.fit -def get_plugins() -> Generator[pkg_resources.EntryPoint, None, None]: +def get_plugins() -> importlib.metadata.EntryPoints: """Iterate over available plugins.""" - return pkg_resources.iter_entry_points(group="stan.plugins") + return importlib.metadata.entry_points(group="stan.plugins") class PluginBase(abc.ABC): diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 628296c..e3fa365 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -1,4 +1,5 @@ -import pkg_resources +import importlib.metadata + import pytest import stan @@ -20,7 +21,7 @@ def load(): return DummyPlugin -def mock_iter_entry_points(group): +def mock_entry_points(group): return iter([MockEntryPoint]) @@ -31,7 +32,7 @@ def normal_posterior(): def test_get_plugins(monkeypatch): - monkeypatch.setattr(pkg_resources, "iter_entry_points", mock_iter_entry_points) + monkeypatch.setattr(importlib.metadata, "entry_points", mock_entry_points) entry_points = stan.plugins.get_plugins() Plugin = next(entry_points).load() @@ -40,7 +41,7 @@ def test_get_plugins(monkeypatch): def test_dummy_plugin(monkeypatch, capsys, normal_posterior): - monkeypatch.setattr(pkg_resources, "iter_entry_points", mock_iter_entry_points) + monkeypatch.setattr(importlib.metadata, "entry_points", mock_entry_points) fit = normal_posterior.sample(stepsize=0.001) assert fit is not None and "y" in fit @@ -65,10 +66,10 @@ def load(): def test_two_plugins(monkeypatch, capsys, normal_posterior): """Make sure that both plugins are used.""" - def mock_iter_entry_points(group): + def mock_entry_points(group): return iter([MockEntryPoint, OtherMockEntryPoint]) - monkeypatch.setattr(pkg_resources, "iter_entry_points", mock_iter_entry_points) + monkeypatch.setattr(importlib.metadata, "entry_points", mock_entry_points) fit = normal_posterior.sample(stepsize=0.001) assert fit is not None and "y" in fit From 380e4da214a527355a5b485c4c88b177bc566747 Mon Sep 17 00:00:00 2001 From: "A. Riddell" Date: Wed, 11 Mar 2026 18:17:33 -0400 Subject: [PATCH 2/6] ci: Update Python versions used in CI for 2026 --- .github/workflows/main.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0a227a3..6917dd1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,12 +10,10 @@ jobs: strategy: matrix: include: - - {runs-on: ubuntu-24.04, python-version: "3.10"} - - {runs-on: ubuntu-24.04, python-version: "3.11"} - {runs-on: ubuntu-24.04, python-version: "3.12"} - - {runs-on: macos-13, python-version: "3.10"} - - {runs-on: macos-13, python-version: "3.11"} - - {runs-on: macos-13, python-version: "3.12"} + - {runs-on: ubuntu-24.04, python-version: "3.13"} + - {runs-on: ubuntu-24.04, python-version: "3.14"} + - {runs-on: macos-15-intel, python-version: "3.12"} steps: - name: Check out repository uses: actions/checkout@v4 From 43d63132c0fed5735b9b8cd4e6d0e1c13e9ad5be Mon Sep 17 00:00:00 2001 From: "A. Riddell" Date: Wed, 11 Mar 2026 18:17:34 -0400 Subject: [PATCH 3/6] ci: Update mypy to a version that supports recent Pythons --- pyproject.toml | 2 +- stan/fit.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a5471ee..6719727 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ pytest-asyncio = "^0.18.3" pandas = ">=1.0,<3" black = "22.6.0" isort = "^5.9" -mypy = "0.982" +mypy = "1.19" types-setuptools = "^57.4" # documentation # NOTE: when changing these, update docs-requirements.txt diff --git a/stan/fit.py b/stan/fit.py index 3dda627..978a484 100644 --- a/stan/fit.py +++ b/stan/fit.py @@ -96,6 +96,7 @@ def __init__( ) draw_row = tuple(msg["values"].values()) # a "row" of values from a single draw from Stan C++ + draw_row = cast(Tuple[float, ...], draw_row) self._draws[:, draw_index, chain_index] = draw_row draw_index += 1 finally: From 2afcbce74f3e2a7e28436f31575f8ed2d0d65ce6 Mon Sep 17 00:00:00 2001 From: "A. Riddell" Date: Wed, 11 Mar 2026 18:21:39 -0400 Subject: [PATCH 4/6] ci: Update black to a version that supports recent Pythons --- pyproject.toml | 2 +- stan/common.py | 1 + tests/conftest.py | 1 + tests/test_build_exceptions.py | 1 + tests/test_build_program.py | 1 + tests/test_constrain_pars.py | 1 + tests/test_corr_matrix.py | 1 + tests/test_eight_schools_large.py | 1 + tests/test_fit_basic_array.py | 1 + tests/test_fit_basic_matrix.py | 1 + tests/test_fit_basic_scalar.py | 1 + tests/test_fit_basic_vector.py | 1 + tests/test_fit_cache.py | 1 + tests/test_fit_shape.py | 1 + tests/test_grad_log_prob.py | 1 + tests/test_log_prob.py | 1 + tests/test_model_build_data.py | 1 + tests/test_nan_inf.py | 1 + tests/test_sample_exceptions.py | 1 + tests/test_stanc_warnings.py | 1 + tests/test_transformed_data_rng.py | 1 + tests/test_unconstrain_pars.py | 1 + 22 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6719727..de24d5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ setuptools = "*" pytest = "^6.2" pytest-asyncio = "^0.18.3" pandas = ">=1.0,<3" -black = "22.6.0" +black = "26.3.0" isort = "^5.9" mypy = "1.19" types-setuptools = "^57.4" diff --git a/stan/common.py b/stan/common.py index d60a2de..60000ab 100644 --- a/stan/common.py +++ b/stan/common.py @@ -1,4 +1,5 @@ """Common routines""" + import socket import typing diff --git a/tests/conftest.py b/tests/conftest.py index 64adb55..3fd181e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ """pytest configuration for all tests.""" + import pytest diff --git a/tests/test_build_exceptions.py b/tests/test_build_exceptions.py index fbecc06..5c3c0b5 100644 --- a/tests/test_build_exceptions.py +++ b/tests/test_build_exceptions.py @@ -1,4 +1,5 @@ """Tests for build related exceptions.""" + import pytest import stan diff --git a/tests/test_build_program.py b/tests/test_build_program.py index 334af2c..04756c7 100644 --- a/tests/test_build_program.py +++ b/tests/test_build_program.py @@ -1,4 +1,5 @@ """Test building a Stan program.""" + import pytest import stan diff --git a/tests/test_constrain_pars.py b/tests/test_constrain_pars.py index 4bf6723..a9f7cc7 100644 --- a/tests/test_constrain_pars.py +++ b/tests/test_constrain_pars.py @@ -1,4 +1,5 @@ """Test constrain parameters.""" + import random import numpy as np diff --git a/tests/test_corr_matrix.py b/tests/test_corr_matrix.py index bf5f2d5..c9fd206 100644 --- a/tests/test_corr_matrix.py +++ b/tests/test_corr_matrix.py @@ -1,4 +1,5 @@ """CPU-intensive test, useful for testing progress bars.""" + import stan program_code = """ diff --git a/tests/test_eight_schools_large.py b/tests/test_eight_schools_large.py index 7cad048..9f1af11 100644 --- a/tests/test_eight_schools_large.py +++ b/tests/test_eight_schools_large.py @@ -3,6 +3,7 @@ Introduced in response to a macOS bug that only triggered when a larger number of parameters were used. """ + import pytest import stan diff --git a/tests/test_fit_basic_array.py b/tests/test_fit_basic_array.py index 522ca65..1172334 100644 --- a/tests/test_fit_basic_array.py +++ b/tests/test_fit_basic_array.py @@ -1,4 +1,5 @@ """Test model with array parameter.""" + import numpy as np import pytest diff --git a/tests/test_fit_basic_matrix.py b/tests/test_fit_basic_matrix.py index d0e02f2..8d05200 100644 --- a/tests/test_fit_basic_matrix.py +++ b/tests/test_fit_basic_matrix.py @@ -1,4 +1,5 @@ """Test model with a matrix parameter.""" + import pytest import stan diff --git a/tests/test_fit_basic_scalar.py b/tests/test_fit_basic_scalar.py index 6c6c7e2..9e58452 100644 --- a/tests/test_fit_basic_scalar.py +++ b/tests/test_fit_basic_scalar.py @@ -1,4 +1,5 @@ """Test model with a scalar parameter.""" + import numpy as np import pytest diff --git a/tests/test_fit_basic_vector.py b/tests/test_fit_basic_vector.py index cf8f0e2..a8c6b4e 100644 --- a/tests/test_fit_basic_vector.py +++ b/tests/test_fit_basic_vector.py @@ -1,4 +1,5 @@ """Test model with a vector parameter.""" + import numpy as np import pytest diff --git a/tests/test_fit_cache.py b/tests/test_fit_cache.py index 66f0987..a0c4492 100644 --- a/tests/test_fit_cache.py +++ b/tests/test_fit_cache.py @@ -1,4 +1,5 @@ """Tests related to cached fits.""" + import os import pathlib import random diff --git a/tests/test_fit_shape.py b/tests/test_fit_shape.py index 8122c22..ac2f47d 100644 --- a/tests/test_fit_shape.py +++ b/tests/test_fit_shape.py @@ -1,4 +1,5 @@ """Test model parameter shapes.""" + import pytest import stan diff --git a/tests/test_grad_log_prob.py b/tests/test_grad_log_prob.py index 96a17e6..31848b4 100644 --- a/tests/test_grad_log_prob.py +++ b/tests/test_grad_log_prob.py @@ -1,4 +1,5 @@ """Test model with array parameter.""" + import random import numpy as np diff --git a/tests/test_log_prob.py b/tests/test_log_prob.py index 18f8407..810aba2 100644 --- a/tests/test_log_prob.py +++ b/tests/test_log_prob.py @@ -1,4 +1,5 @@ """Test model with array parameter.""" + import numpy as np import pytest diff --git a/tests/test_model_build_data.py b/tests/test_model_build_data.py index 39605fa..ef823aa 100644 --- a/tests/test_model_build_data.py +++ b/tests/test_model_build_data.py @@ -1,4 +1,5 @@ """Test handling of `data` dictionary.""" + import copy import numpy as np diff --git a/tests/test_nan_inf.py b/tests/test_nan_inf.py index 0926349..77f8094 100644 --- a/tests/test_nan_inf.py +++ b/tests/test_nan_inf.py @@ -1,4 +1,5 @@ """Test serialization of nan and inf values.""" + import math import stan diff --git a/tests/test_sample_exceptions.py b/tests/test_sample_exceptions.py index 09bf919..89db294 100644 --- a/tests/test_sample_exceptions.py +++ b/tests/test_sample_exceptions.py @@ -1,4 +1,5 @@ """Tests for sampling related exceptions.""" + import pytest import stan diff --git a/tests/test_stanc_warnings.py b/tests/test_stanc_warnings.py index baf561e..154cdc9 100644 --- a/tests/test_stanc_warnings.py +++ b/tests/test_stanc_warnings.py @@ -1,4 +1,5 @@ """Test that stanc warnings are visible.""" + import contextlib import io diff --git a/tests/test_transformed_data_rng.py b/tests/test_transformed_data_rng.py index 637303f..813af5d 100644 --- a/tests/test_transformed_data_rng.py +++ b/tests/test_transformed_data_rng.py @@ -1,4 +1,5 @@ """Verify that the RNG in the transformed data block uses the overall seed.""" + import numpy as np import stan diff --git a/tests/test_unconstrain_pars.py b/tests/test_unconstrain_pars.py index bd6b1ce..06f88ca 100644 --- a/tests/test_unconstrain_pars.py +++ b/tests/test_unconstrain_pars.py @@ -1,4 +1,5 @@ """Test constrain parameters.""" + import random import numpy as np From 6ff0dda07c8d608ca99f550d34ca3b529c1033ec Mon Sep 17 00:00:00 2001 From: "A. Riddell" Date: Wed, 11 Mar 2026 18:39:18 -0400 Subject: [PATCH 5/6] ci: Update pytest to version better supported on recent Python --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index de24d5a..90714d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,8 @@ clikit = "^0.6" setuptools = "*" [tool.poetry.dev-dependencies] -pytest = "^6.2" -pytest-asyncio = "^0.18.3" +pytest = "^9.0.2" +pytest-asyncio = "^1.3" pandas = ">=1.0,<3" black = "26.3.0" isort = "^5.9" From 14e44eed6691fdae25786c6d24f0fb2e58fd65f5 Mon Sep 17 00:00:00 2001 From: "A. Riddell" Date: Thu, 12 Mar 2026 13:33:50 -0400 Subject: [PATCH 6/6] chore: bump version Bump pystan version to trigger new release. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 90714d4..28c9cba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pystan" -version = "3.10.0" +version = "3.10.1" description = "Python interface to Stan, a package for Bayesian inference" authors = [ "Allen Riddell ",