diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 396503a2..2c42518c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,11 +68,11 @@ jobs: run: python -m pip freeze - name: Run flake8, pylint, mypy - if: matrix.python-version == '3.11' + if: matrix.python-version == '3.14' run: | flake8 cmdstanpy test pylint -v cmdstanpy test - mypy cmdstanpy + mypy cmdstanpy test - name: CmdStan installation cacheing id: cache-cmdstan diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 58a42dd8..7a5baefe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,6 @@ repos: rev: v1.5.0 hooks: - id: mypy - exclude: ^test/ additional_dependencies: [ numpy >= 1.22] # local uses the user-installed pylint, this allows dependency checking - repo: local diff --git a/cmdstanpy/cmdstan_args.py b/cmdstanpy/cmdstan_args.py index 7ca3c2b5..9a7b2049 100644 --- a/cmdstanpy/cmdstan_args.py +++ b/cmdstanpy/cmdstan_args.py @@ -643,7 +643,7 @@ def __init__( PathfinderArgs, ], data: Union[Mapping[str, Any], str, None] = None, - seed: Union[int, list[int], None] = None, + seed: Union[int, np.integer, list[int], list[np.integer], None] = None, inits: Union[int, float, str, list[str], None] = None, output_dir: OptionalPath = None, sig_figs: Optional[int] = None, diff --git a/cmdstanpy/install_cmdstan.py b/cmdstanpy/install_cmdstan.py index 12db9cbc..6eb2fde7 100644 --- a/cmdstanpy/install_cmdstan.py +++ b/cmdstanpy/install_cmdstan.py @@ -29,9 +29,10 @@ import urllib.error import urllib.request from collections import OrderedDict +from functools import cached_property from pathlib import Path from time import sleep -from typing import TYPE_CHECKING, Any, Callable, Optional, Union +from typing import Any, Callable, Optional, Union from tqdm.auto import tqdm @@ -47,18 +48,6 @@ from . import progress as progbar -if sys.version_info >= (3, 8) or TYPE_CHECKING: - # mypy only knows about the new built-in cached_property - from functools import cached_property -else: - # on older Python versions, this is the recommended - # way to get the same effect - from functools import lru_cache - - def cached_property(fun): - return property(lru_cache(maxsize=None)(fun)) - - try: # on MacOS and Linux, importing this # improves the UX of the input() function diff --git a/cmdstanpy/model.py b/cmdstanpy/model.py index 7d23bcff..f85321ef 100644 --- a/cmdstanpy/model.py +++ b/cmdstanpy/model.py @@ -13,7 +13,7 @@ from concurrent.futures import ThreadPoolExecutor from io import StringIO from multiprocessing import cpu_count -from typing import Any, Callable, Mapping, Optional, TypeVar, Union +from typing import Any, Callable, Mapping, Optional, Sequence, TypeVar, Union import numpy as np import pandas as pd @@ -461,8 +461,7 @@ def sample( Mapping[str, Any], float, str, - list[str], - list[Mapping[str, Any]], + Sequence[Union[str, Mapping[str, Any]]], None, ] = None, iter_warmup: Optional[int] = None, @@ -493,7 +492,7 @@ def sample( str, np.ndarray, Mapping[str, Any], - list[Union[str, np.ndarray, Mapping[str, Any]]], + Sequence[Union[str, np.ndarray, Mapping[str, Any]]], None, ] = None, ) -> CmdStanMCMC: @@ -1360,7 +1359,13 @@ def pathfinder( calculate_lp: bool = True, # arguments standard to all methods seed: Optional[int] = None, - inits: Union[dict[str, float], float, str, os.PathLike, None] = None, + inits: Union[ + Mapping[str, Any], + float, + str, + Sequence[Union[str, Mapping[str, Any]]], + None, + ] = None, output_dir: OptionalPath = None, sig_figs: Optional[int] = None, save_profile: bool = False, diff --git a/cmdstanpy/stanfit/__init__.py b/cmdstanpy/stanfit/__init__.py index bd13495b..35a76ca3 100644 --- a/cmdstanpy/stanfit/__init__.py +++ b/cmdstanpy/stanfit/__init__.py @@ -248,7 +248,7 @@ def from_csv( mode: CmdStanMLE = from_csv( config_dict['mode'], # type: ignore method='optimize', - ) # type: ignore + ) return CmdStanLaplace(runset, mode=mode) elif config_dict['method'] == 'pathfinder': pathfinder_args = PathfinderArgs( diff --git a/cmdstanpy/stanfit/mcmc.py b/cmdstanpy/stanfit/mcmc.py index c71b28e1..7ff7d142 100644 --- a/cmdstanpy/stanfit/mcmc.py +++ b/cmdstanpy/stanfit/mcmc.py @@ -348,7 +348,7 @@ def _validate_csv_files(self) -> dict[str, Any]: save_warmup=self._save_warmup, thin=self._thin, ) - self._chain_time.append(dzero['time']) # type: ignore + self._chain_time.append(dzero['time']) if not self._is_fixed_param: self._divergences[i] = dzero['ct_divergences'] self._max_treedepths[i] = dzero['ct_max_treedepth'] @@ -360,7 +360,7 @@ def _validate_csv_files(self) -> dict[str, Any]: save_warmup=self._save_warmup, thin=self._thin, ) - self._chain_time.append(drest['time']) # type: ignore + self._chain_time.append(drest['time']) for key in dzero: # check args that matter for parsing, plus name, version if ( diff --git a/cmdstanpy/stanfit/pathfinder.py b/cmdstanpy/stanfit/pathfinder.py index 5047c68e..0fbbe5ec 100644 --- a/cmdstanpy/stanfit/pathfinder.py +++ b/cmdstanpy/stanfit/pathfinder.py @@ -208,7 +208,7 @@ def is_resampled(self) -> bool: Returns True if the draws were resampled from several Pathfinder approximations, False otherwise. """ - return ( # type: ignore + return ( self._metadata.cmdstan_config.get("num_paths", 4) > 1 and self._metadata.cmdstan_config.get('psis_resample', 1) in (1, 'true') diff --git a/cmdstanpy/utils/__init__.py b/cmdstanpy/utils/__init__.py index d4de6e0f..b4e6c9ab 100644 --- a/cmdstanpy/utils/__init__.py +++ b/cmdstanpy/utils/__init__.py @@ -80,7 +80,7 @@ def show_versions(output: bool = True) -> str: deps_info.append((module, None)) else: try: - ver = mod.__version__ # type: ignore + ver = mod.__version__ deps_info.append((module, ver)) # pylint: disable=broad-except except Exception: diff --git a/cmdstanpy/utils/filesystem.py b/cmdstanpy/utils/filesystem.py index 1f293a05..0cc5ac80 100644 --- a/cmdstanpy/utils/filesystem.py +++ b/cmdstanpy/utils/filesystem.py @@ -8,7 +8,7 @@ import re import shutil import tempfile -from typing import Any, Iterator, Mapping, Optional, Union +from typing import Any, Iterator, Mapping, Optional, Sequence, Union import numpy as np @@ -131,10 +131,12 @@ def _temp_single_json( def _temp_multiinput( - input: Union[str, os.PathLike, Mapping[str, Any], list[Any], None], + input: Union[str, os.PathLike, Mapping[str, Any], Sequence[Any], None], base: int = 1, ) -> Iterator[Optional[str]]: - if isinstance(input, list): + if isinstance(input, Sequence) and not isinstance( + input, (str, os.PathLike) + ): # most complicated case: list of inits # for multiple chains, we need to create multiple files # which look like somename_{i}.json and then pass somename.json @@ -170,7 +172,7 @@ def _temp_multiinput( @contextlib.contextmanager def temp_metrics( metrics: Union[ - str, os.PathLike, Mapping[str, Any], np.ndarray, list[Any], None + str, os.PathLike, Mapping[str, Any], np.ndarray, Sequence[Any], None ], *, id: int = 1, @@ -200,7 +202,7 @@ def temp_metrics( @contextlib.contextmanager def temp_inits( inits: Union[ - str, os.PathLike, Mapping[str, Any], float, int, list[Any], None + str, os.PathLike, Mapping[str, Any], float, int, Sequence[Any], None ], *, allow_multiple: bool = True, @@ -212,7 +214,9 @@ def temp_inits( if allow_multiple: yield from _temp_multiinput(inits, base=id) else: - if isinstance(inits, list): + if isinstance(inits, Sequence) and not isinstance( + inits, (str, os.PathLike) + ): raise ValueError('Expected single initialization, got list') yield from _temp_single_json(inits) diff --git a/cmdstanpy/utils/stancsv.py b/cmdstanpy/utils/stancsv.py index aa8760d1..0349a4c1 100644 --- a/cmdstanpy/utils/stancsv.py +++ b/cmdstanpy/utils/stancsv.py @@ -8,7 +8,7 @@ import os import re import warnings -from typing import Any, Iterator, Mapping, Optional, Union +from typing import Any, Iterator, Mapping, Optional, Sequence, Union import numpy as np import numpy.typing as npt @@ -638,11 +638,13 @@ def try_deduce_metric_type( str, np.ndarray, Mapping[str, Any], - list[Union[str, np.ndarray, Mapping[str, Any]]], + Sequence[Union[str, np.ndarray, Mapping[str, Any]]], ], ) -> Optional[str]: """Given a user-supplied metric, try to infer the correct metric type.""" - if isinstance(inv_metric, list): + if isinstance(inv_metric, Sequence) and not isinstance( + inv_metric, (str, np.ndarray, Mapping) + ): if inv_metric: inv_metric = inv_metric[0] diff --git a/test/__init__.py b/test/__init__.py index 92f00df1..69af537c 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -5,7 +5,8 @@ import platform import re from importlib import reload -from typing import Tuple, Type +from types import ModuleType +from typing import Generator, Optional, Type from unittest import mock import pytest @@ -20,7 +21,9 @@ # pylint: disable=invalid-name @contextlib.contextmanager -def raises_nested(expected_exception: Type[Exception], match: str) -> None: +def raises_nested( + expected_exception: Type[Exception], match: str +) -> Generator[None, None, None]: """A version of assertRaisesRegex that checks the full traceback. Useful for when an exception is raised from another and you wish to @@ -28,7 +31,7 @@ def raises_nested(expected_exception: Type[Exception], match: str) -> None: """ with pytest.raises(expected_exception) as ctx: yield - exception: Exception = ctx.value + exception: Optional[BaseException] = ctx.value lines = [] while exception: lines.append(str(exception)) @@ -38,7 +41,9 @@ def raises_nested(expected_exception: Type[Exception], match: str) -> None: @contextlib.contextmanager -def without_import(library, module): +def without_import( + library: str, module: ModuleType +) -> Generator[None, None, None]: with mock.patch.dict('sys.modules', {library: None}): reload(module) yield @@ -47,7 +52,7 @@ def without_import(library, module): def check_present( caplog: pytest.LogCaptureFixture, - *conditions: Tuple, + *conditions: tuple, clear: bool = True, ) -> None: """ @@ -58,9 +63,13 @@ def check_present( if isinstance(level, str): level = getattr(logging, level) found = any( - logger == logger_ and level == level_ and message.match(message_) - if isinstance(message, re.Pattern) - else message == message_ + ( + logger == logger_ + and level == level_ + and message.match(message_) + if isinstance(message, re.Pattern) + else message == message_ + ) for logger_, level_, message_ in caplog.record_tuples ) if not found: diff --git a/test/conftest.py b/test/conftest.py index 30f8c710..a58870b3 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,6 +1,7 @@ """The global configuration for the test suite""" import os import subprocess +from typing import Generator import pytest @@ -9,7 +10,7 @@ @pytest.fixture(scope='session', autouse=True) -def cleanup_test_files(): +def cleanup_test_files() -> Generator[None, None, None]: """Remove compiled models and output files after test run.""" yield subprocess.Popen( diff --git a/test/example_script.py b/test/example_script.py deleted file mode 100644 index 3d35f8bb..00000000 --- a/test/example_script.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Example script to be runned after tests, tqdm installed.""" -import os -import sys - -# explicit import to test if it is installed -# pylint: disable=E0401,W0611,C0411 -import tqdm # noqa - -from cmdstanpy import CmdStanModel, cmdstan_path - - -def run_bernoulli_fit(): - # specify Stan file, create, compile CmdStanModel object - bernoulli_path = os.path.join( - cmdstan_path(), 'examples', 'bernoulli', 'bernoulli.stan' - ) - bernoulli_model = CmdStanModel(stan_file=bernoulli_path) - - # specify data, fit the model - bernoulli_data = {'N': 10, 'y': [0, 1, 0, 0, 0, 0, 0, 0, 0, 1]} - # Show progress - bernoulli_fit = bernoulli_model.sample( - chains=4, parallel_chains=2, data=bernoulli_data, show_progress=True - ) - - # summarize the results (wraps CmdStan `bin/stansummary`): - print(bernoulli_fit.summary()) - - -if __name__ == '__main__': - run_bernoulli_fit() - # exit explicitly - sys.exit(0) diff --git a/test/test_cmdstan_args.py b/test/test_cmdstan_args.py index 14832374..9d340a07 100644 --- a/test/test_cmdstan_args.py +++ b/test/test_cmdstan_args.py @@ -32,14 +32,14 @@ def test_args_algorithm() -> None: args.validate() args = OptimizeArgs(algorithm='Newton') args.validate() - cmd = args.compose(None, cmd=['output']) + cmd = args.compose(0, cmd=['output']) assert 'algorithm=newton' in ' '.join(cmd) def test_args_algorithm_init_alpha() -> None: args = OptimizeArgs(algorithm='bfgs', init_alpha=2e-4) args.validate() - cmd = args.compose(None, cmd=['output']) + cmd = args.compose(0, cmd=['output']) assert 'init_alpha=0.0002' in ' '.join(cmd) args = OptimizeArgs(init_alpha=2e-4) @@ -56,7 +56,7 @@ def test_args_algorithm_init_alpha() -> None: def test_args_algorithm_iter() -> None: args = OptimizeArgs(iter=400) args.validate() - cmd = args.compose(None, cmd=['output']) + cmd = args.compose(0, cmd=['output']) assert 'iter=400' in ' '.join(cmd) args = OptimizeArgs(iter=-1) with pytest.raises(ValueError): @@ -163,15 +163,15 @@ def test_bad() -> None: with pytest.raises(ValueError): args.validate(chains=2) - args = SamplerArgs(adapt_init_phase=0.88) + args = SamplerArgs(adapt_init_phase=0.88) # type: ignore with pytest.raises(ValueError): args.validate(chains=2) - args = SamplerArgs(adapt_metric_window=0.88) + args = SamplerArgs(adapt_metric_window=0.88) # type: ignore with pytest.raises(ValueError): args.validate(chains=2) - args = SamplerArgs(adapt_step_size=0.88) + args = SamplerArgs(adapt_step_size=0.88) # type: ignore with pytest.raises(ValueError): args.validate(chains=2) @@ -345,7 +345,7 @@ def test_args_good() -> None: # integer type rng = np.random.default_rng(42) - seed = rng.integers(low=0, high=int(1e7)) + seed: np.integer = rng.integers(low=0, high=int(1e7)) # type: ignore assert not isinstance(seed, int) assert isinstance(seed, np.integer) @@ -437,7 +437,10 @@ def test_args_bad() -> None: with pytest.raises( Exception, match='missing 2 required positional arguments' ): - CmdStanArgs(model_name='bernoulli', model_exe='bernoulli.exe') + CmdStanArgs( + model_name='bernoulli', + model_exe='bernoulli.exe', + ) # type: ignore with pytest.raises(ValueError, match='no such file no/such/path/to.file'): CmdStanArgs( @@ -496,7 +499,7 @@ def test_args_bad() -> None: model_name='bernoulli', model_exe='bernoulli.exe', chain_ids=[1, 2, 3, 4], - seed='badseed', + seed='badseed', # type: ignore method_args=sampler_args, ) @@ -568,7 +571,7 @@ def test_args_bad() -> None: model_exe='bernoulli.exe', chain_ids=[1, 2, 3, 4], method_args=sampler_args, - refresh="a", + refresh="a", # type: ignore ) with pytest.raises( @@ -685,7 +688,7 @@ def test_variational_args_bad() -> None: with pytest.raises(ValueError): args.validate() - args = VariationalArgs(iter=1.1) + args = VariationalArgs(iter=1.1) # type: ignore with pytest.raises(ValueError): args.validate() @@ -693,7 +696,7 @@ def test_variational_args_bad() -> None: with pytest.raises(ValueError): args.validate() - args = VariationalArgs(grad_samples=1.1) + args = VariationalArgs(grad_samples=1.1) # type: ignore with pytest.raises(ValueError): args.validate() @@ -701,7 +704,7 @@ def test_variational_args_bad() -> None: with pytest.raises(ValueError): args.validate() - args = VariationalArgs(elbo_samples=1.1) + args = VariationalArgs(elbo_samples=1.1) # type: ignore with pytest.raises(ValueError): args.validate() @@ -713,7 +716,7 @@ def test_variational_args_bad() -> None: with pytest.raises(ValueError): args.validate() - args = VariationalArgs(adapt_iter=1.1) + args = VariationalArgs(adapt_iter=1.1) # type: ignore with pytest.raises(ValueError): args.validate() @@ -725,7 +728,7 @@ def test_variational_args_bad() -> None: with pytest.raises(ValueError): args.validate() - args = VariationalArgs(eval_elbo=1.5) + args = VariationalArgs(eval_elbo=1.5) # type: ignore with pytest.raises(ValueError): args.validate() @@ -734,7 +737,7 @@ def test_variational_args_bad() -> None: args.validate() -def test_args_laplace(): +def test_args_laplace() -> None: mode = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock_mle.csv') args = LaplaceArgs(mode=mode) args.validate() @@ -752,7 +755,7 @@ def test_args_laplace(): assert 'jacobian=0' in full_cmd -def test_args_laplace_bad(): +def test_args_laplace_bad() -> None: fake_mode = os.path.join(DATAFILES_PATH, 'not_here.csv') args = LaplaceArgs(mode=fake_mode) with pytest.raises(ValueError): @@ -763,12 +766,12 @@ def test_args_laplace_bad(): with pytest.raises(ValueError): args.validate() - args = LaplaceArgs(mode=mode, draws=1.1) + args = LaplaceArgs(mode=mode, draws=1.1) # type: ignore with pytest.raises(ValueError): args.validate() -def test_args_pathfinder(): +def test_args_pathfinder() -> None: args = PathfinderArgs() args.validate() cmd = args.compose(0, cmd=[]) @@ -812,15 +815,15 @@ def test_args_pathfinder(): ('num_elbo_draws', True), ], ) -def test_args_pathfinder_bad(arg, require_int): +def test_args_pathfinder_bad(arg: str, require_int: bool) -> None: # pathfinder's only arg restrictions are on positiveness - args = PathfinderArgs(**{arg: 0}) + args = PathfinderArgs(**{arg: 0}) # type: ignore with pytest.raises(ValueError): args.validate() - args = PathfinderArgs(**{arg: -1}) + args = PathfinderArgs(**{arg: -1}) # type: ignore with pytest.raises(ValueError): args.validate() if require_int: - args = PathfinderArgs(**{arg: 1.1}) + args = PathfinderArgs(**{arg: 1.1}) # type: ignore with pytest.raises(ValueError): args.validate() diff --git a/test/test_compilation.py b/test/test_compilation.py index cf243066..4f010296 100644 --- a/test/test_compilation.py +++ b/test/test_compilation.py @@ -5,6 +5,7 @@ import logging import os from test import check_present, raises_nested +from typing import Any from unittest.mock import MagicMock, patch import pytest @@ -22,12 +23,12 @@ def test_opts_empty() -> None: assert opts.compose() == [] assert repr(opts) == 'stanc_options={}, cpp_options={}' - stanc_opts = {} + stanc_opts: dict[str, Any] = {} opts = CompilerOptions(stanc_options=stanc_opts) opts.validate() assert opts.compose() == [] - cpp_opts = {} + cpp_opts: dict[str, Any] = {} opts = CompilerOptions(cpp_options=cpp_opts) opts.validate() assert opts.compose() == [] @@ -39,7 +40,7 @@ def test_opts_empty() -> None: def test_opts_stanc(caplog: pytest.LogCaptureFixture) -> None: - stanc_opts = {} + stanc_opts: dict[str, Any] = {} opts = CompilerOptions() opts.validate() assert opts.compose() == [] @@ -114,7 +115,7 @@ def test_opts_stanc_includes() -> None: paths_str = ','.join([DATAFILES_PATH, path2]).replace('\\', '/') expect = 'STANCFLAGS+=--include-paths=' + paths_str - stanc_opts = {'include-paths': paths_str} + stanc_opts: dict[str, Any] = {'include-paths': paths_str} opts = CompilerOptions(stanc_options=stanc_opts) opts.validate() opts_list = opts.compose() @@ -128,7 +129,7 @@ def test_opts_stanc_includes() -> None: def test_opts_cpp() -> None: - cpp_opts = {} + cpp_opts: dict[str, Any] = {} opts = CompilerOptions(cpp_options=cpp_opts) opts.validate() assert opts.compose() == [] @@ -140,7 +141,7 @@ def test_opts_cpp() -> None: def test_opts_cpp_opencl() -> None: - cpp_opts = {'OPENCL_DEVICE_ID': 1} + cpp_opts: dict[str, Any] = {'OPENCL_DEVICE_ID': 1} opts = CompilerOptions(cpp_options=cpp_opts) opts.validate() opts_list = opts.compose() diff --git a/test/test_compliance.py b/test/test_compliance.py index 8093769d..1b30c568 100644 --- a/test/test_compliance.py +++ b/test/test_compliance.py @@ -12,6 +12,7 @@ def test_sample_pickle_ability() -> None: csvfiles_path = DATAFILES_PATH / 'lotka-volterra.csv' fit = cmdstanpy.from_csv(path=csvfiles_path) + assert fit is not None keys = fit.stan_variables().keys() pickled = pickle.dumps(fit) del fit @@ -22,6 +23,7 @@ def test_sample_pickle_ability() -> None: def test_sample_copy_ability() -> None: csvfiles_path = DATAFILES_PATH / 'lotka-volterra.csv' fit = cmdstanpy.from_csv(path=csvfiles_path) + assert fit is not None fit2 = copy.deepcopy(fit) assert fit.stan_variables().keys() == fit2.stan_variables().keys() @@ -29,6 +31,7 @@ def test_sample_copy_ability() -> None: def test_optimize_pickle_ability() -> None: csvfiles_path = DATAFILES_PATH / 'optimize' / 'rosenbrock_mle.csv' fit = cmdstanpy.from_csv(path=csvfiles_path) + assert fit is not None keys = fit.stan_variables().keys() pickled = pickle.dumps(fit) del fit @@ -39,6 +42,7 @@ def test_optimize_pickle_ability() -> None: def test_optimize_copy_ability() -> None: csvfiles_path = DATAFILES_PATH / 'optimize' / 'rosenbrock_mle.csv' fit = cmdstanpy.from_csv(path=csvfiles_path) + assert fit is not None fit2 = copy.deepcopy(fit) assert fit.stan_variables().keys() == fit2.stan_variables().keys() @@ -46,6 +50,7 @@ def test_optimize_copy_ability() -> None: def test_variational_pickle_ability() -> None: csvfiles_path = DATAFILES_PATH / 'variational' fit = cmdstanpy.from_csv(path=csvfiles_path) + assert fit is not None keys = fit.stan_variables().keys() pickled = pickle.dumps(fit) del fit @@ -56,5 +61,6 @@ def test_variational_pickle_ability() -> None: def test_variational_copy_ability() -> None: csvfiles_path = DATAFILES_PATH / 'variational' fit = cmdstanpy.from_csv(path=csvfiles_path) + assert fit is not None fit2 = copy.deepcopy(fit) assert fit.stan_variables().keys() == fit2.stan_variables().keys() diff --git a/test/test_generate_quantities.py b/test/test_generate_quantities.py index 0fc6ded3..c65907b2 100644 --- a/test/test_generate_quantities.py +++ b/test/test_generate_quantities.py @@ -18,6 +18,7 @@ from cmdstanpy.cmdstan_args import Method from cmdstanpy.model import CmdStanModel from cmdstanpy.stanfit import CmdStanGQ +from cmdstanpy.stanfit.mcmc import CmdStanMCMC HERE = os.path.dirname(os.path.abspath(__file__)) DATAFILES_PATH = os.path.join(HERE, 'data') @@ -92,7 +93,7 @@ def test_from_csv_files(caplog: pytest.LogCaptureFixture) -> None: ) == (["chain__", "iter__", "draw__"] + column_names) -def test_pd_xr_agreement(): +def test_pd_xr_agreement() -> None: # fitted_params sample - list of filenames goodfiles_path = os.path.join(DATAFILES_PATH, 'runset-good', 'bern') csv_files = [] @@ -533,7 +534,7 @@ def test_serialization() -> None: dumped = pickle.dumps(fit1) shutil.rmtree(fit1.runset._output_dir) - fit2: CmdStanGQ = pickle.loads(dumped) + fit2: CmdStanGQ[CmdStanMCMC] = pickle.loads(dumped) variables1 = fit1.stan_variables() variables2 = fit2.stan_variables() assert set(variables1) == set(variables2) @@ -582,11 +583,12 @@ def test_from_optimization() -> None: assert y_rep.shape == (1, 10) -def test_opt_save_iterations(caplog: pytest.LogCaptureFixture): +def test_opt_save_iterations(caplog: pytest.LogCaptureFixture) -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') bern_fit = bern_model.optimize(data=jdata, seed=12345, save_iterations=True) + assert bern_fit.optimized_iterations_np is not None iters = bern_fit.optimized_iterations_np.shape[0] # gq_model @@ -634,7 +636,7 @@ def test_opt_save_iterations(caplog: pytest.LogCaptureFixture): assert y_rep.shape == (iters, 10) -def test_opt_request_warmup_none(caplog: pytest.LogCaptureFixture): +def test_opt_request_warmup_none(caplog: pytest.LogCaptureFixture) -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -664,7 +666,7 @@ def test_opt_request_warmup_none(caplog: pytest.LogCaptureFixture): assert bern_gqs.draws(inc_iterations=True).shape == (1, 1, 10) -def test_opt_xarray(): +def test_opt_xarray() -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -681,7 +683,7 @@ def test_opt_xarray(): _ = bern_gqs.draws_xr() -def test_from_vb(): +def test_from_vb() -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -724,7 +726,7 @@ def test_from_vb(): assert y_rep.shape == (1000, 10) -def test_vb_request_warmup_none(caplog: pytest.LogCaptureFixture): +def test_vb_request_warmup_none(caplog: pytest.LogCaptureFixture) -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -753,7 +755,7 @@ def test_vb_request_warmup_none(caplog: pytest.LogCaptureFixture): ) -def test_vb_xarray(): +def test_vb_xarray() -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -776,7 +778,7 @@ def test_vb_xarray(): 'cmdstanpy.utils.cmdstan.cmdstan_version', MagicMock(return_value=(2, 27)), ) -def test_from_non_hmc_old(): +def test_from_non_hmc_old() -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') diff --git a/test/test_install_cmdstan.py b/test/test_install_cmdstan.py index b952f87f..3c798fea 100644 --- a/test/test_install_cmdstan.py +++ b/test/test_install_cmdstan.py @@ -34,7 +34,7 @@ def test_retrieve_version() -> None: ): retrieve_version('no_such_version') with pytest.raises(ValueError): - retrieve_version(None) + retrieve_version(None) # type: ignore with pytest.raises(ValueError): retrieve_version('') @@ -44,4 +44,4 @@ def test_rebuild_bad_path() -> None: with pytest.raises( CmdStanInstallError, match="you sure it is installed" ): - rebuild_cmdstan(latest_version()) + rebuild_cmdstan() diff --git a/test/test_laplace.py b/test/test_laplace.py index 14729407..28098a78 100644 --- a/test/test_laplace.py +++ b/test/test_laplace.py @@ -13,7 +13,7 @@ DATAFILES_PATH = os.path.join(HERE, 'data') -def test_laplace_from_opt_csv(): +def test_laplace_from_opt_csv() -> None: model_file = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock.stan') model = cmdstanpy.CmdStanModel(stan_file=model_file) fit = model.laplace_sample( @@ -26,7 +26,7 @@ def test_laplace_from_opt_csv(): assert isinstance(fit.mode, cmdstanpy.CmdStanMLE) -def test_laplace_from_csv(): +def test_laplace_from_csv() -> None: model_file = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock.stan') model = cmdstanpy.CmdStanModel(stan_file=model_file) fit = model.laplace_sample( @@ -55,7 +55,7 @@ def test_laplace_from_csv(): assert isinstance(fit3.mode, cmdstanpy.CmdStanMLE) -def test_laplace_runs_opt(): +def test_laplace_runs_opt() -> None: model_file = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock.stan') model = cmdstanpy.CmdStanModel(stan_file=model_file) fit1 = model.laplace_sample(data={}, seed=1234, opt_args={'iter': 1003}) @@ -66,7 +66,7 @@ def test_laplace_runs_opt(): assert fit1.mode.metadata.cmdstan_config['iter'] == 1003 -def test_laplace_bad_jacobian_mismatch(): +def test_laplace_bad_jacobian_mismatch() -> None: model_file = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock.stan') model = cmdstanpy.CmdStanModel(stan_file=model_file) with pytest.raises(ValueError): @@ -77,7 +77,7 @@ def test_laplace_bad_jacobian_mismatch(): ) -def test_laplace_bad_two_modes(): +def test_laplace_bad_two_modes() -> None: model_file = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock.stan') model = cmdstanpy.CmdStanModel(stan_file=model_file) with pytest.raises(ValueError): @@ -89,7 +89,7 @@ def test_laplace_bad_two_modes(): ) -def test_laplace_outputs(): +def test_laplace_outputs() -> None: model_file = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock.stan') model = cmdstanpy.CmdStanModel(stan_file=model_file) fit = model.laplace_sample(data={}, seed=1234, draws=123) @@ -107,7 +107,7 @@ def test_laplace_outputs(): assert fit_pd['x'].shape == (123,) -def test_laplace_create_inits(): +def test_laplace_create_inits() -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -131,13 +131,15 @@ def test_laplace_create_inits(): seeded = laplace.create_inits(seed=1234) seeded2 = laplace.create_inits(seed=1234) + assert isinstance(seeded, list) + assert isinstance(seeded2, list) assert all( init1['theta'] == init2['theta'] for init1, init2 in zip(seeded, seeded2) ) -def test_laplace_init_sampling(): +def test_laplace_init_sampling() -> None: stan = os.path.join(DATAFILES_PATH, 'logistic.stan') logistic_model = cmdstanpy.CmdStanModel(stan_file=stan) logistic_data = os.path.join(DATAFILES_PATH, 'logistic.data.R') diff --git a/test/test_logging.py b/test/test_logging.py index 67a15a85..f20388e6 100644 --- a/test/test_logging.py +++ b/test/test_logging.py @@ -1,10 +1,13 @@ """Logging control tests""" import logging + +import pytest + import cmdstanpy -def test_disable_logging(caplog): +def test_disable_logging(caplog: pytest.LogCaptureFixture) -> None: logger = cmdstanpy.utils.logging.get_logger() with caplog.at_level(logging.INFO, logger="cmdstanpy"): @@ -21,7 +24,9 @@ def test_disable_logging(caplog): logger.disabled = False -def test_disable_logging_context_manager(caplog): +def test_disable_logging_context_manager( + caplog: pytest.LogCaptureFixture, +) -> None: logger = cmdstanpy.utils.logging.get_logger() with caplog.at_level(logging.INFO, logger="cmdstanpy"): @@ -42,7 +47,9 @@ def test_disable_logging_context_manager(caplog): logger.disabled = False -def test_disable_logging_context_manager_nested(caplog): +def test_disable_logging_context_manager_nested( + caplog: pytest.LogCaptureFixture, +) -> None: logger = cmdstanpy.utils.logging.get_logger() with caplog.at_level(logging.INFO, logger="cmdstanpy"): diff --git a/test/test_model.py b/test/test_model.py index 65ba19e0..b46a124c 100644 --- a/test/test_model.py +++ b/test/test_model.py @@ -65,6 +65,7 @@ def test_ctor_compile_arg() -> None: os.remove(BERN_EXE) model = CmdStanModel(stan_file=BERN_STAN) + assert model.stan_file is not None assert os.path.samefile(model.stan_file, BERN_STAN) assert os.path.samefile(model.exe_file, BERN_EXE) exe_time = os.path.getmtime(model.exe_file) @@ -145,7 +146,7 @@ def test_bad_stanc_options() -> None: bad_opts = {'include-paths': True} CmdStanModel(stan_file=BERN_STAN, stanc_options=bad_opts) with pytest.raises(ValueError): - bad_opts = {'include-paths': 'lkjdf'} + bad_opts = {'include-paths': 'lkjdf'} # type: ignore CmdStanModel(stan_file=BERN_STAN, stanc_options=bad_opts) @@ -441,7 +442,7 @@ def test_model_includes_implicit() -> None: assert os.path.samefile(model2.exe_file, exe) -def test_diagnose(): +def test_diagnose() -> None: # Check the gradients. model = CmdStanModel(stan_file=BERN_STAN) gradients = model.diagnose(data=BERN_DATA) diff --git a/test/test_optimize.py b/test/test_optimize.py index b17c4687..1060640e 100644 --- a/test/test_optimize.py +++ b/test/test_optimize.py @@ -8,6 +8,7 @@ import pickle import shutil from test import check_present +from typing import Any import numpy as np import pytest @@ -23,13 +24,12 @@ def test_instantiate() -> None: stan = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock.stan') model = CmdStanModel(stan_file=stan) - no_data = {} args = OptimizeArgs(algorithm='Newton') cmdstan_args = CmdStanArgs( model_name=model.name, model_exe=model.exe_file, chain_ids=None, - data=no_data, + data={}, method_args=args, ) runset = RunSet(args=cmdstan_args, chains=1) @@ -49,6 +49,7 @@ def test_instantiate_from_csvfiles() -> None: DATAFILES_PATH, 'optimize', 'rosenbrock_mle.csv' ) mle = from_csv(path=csvfiles_path) + assert isinstance(mle, CmdStanMLE) assert 'CmdStanMLE: model=rosenbrock' in repr(mle) assert 'method=optimize' in repr(mle) assert mle.column_names == ('lp__', 'x', 'y') @@ -61,6 +62,7 @@ def test_instantiate_from_csvfiles_save_iterations() -> None: DATAFILES_PATH, 'optimize', 'eight_schools_mle_iters.csv' ) mle = from_csv(path=csvfiles_path) + assert isinstance(mle, CmdStanMLE) assert 'CmdStanMLE: model=eight_schools' in repr(mle) assert 'method=optimize' in repr(mle) assert mle.column_names == ( @@ -82,6 +84,7 @@ def test_instantiate_from_csvfiles_save_iterations() -> None: np.testing.assert_almost_equal( mle.optimized_params_dict['theta[1]'], 1.06401, decimal=3 ) + assert mle.optimized_iterations_np is not None assert mle.optimized_iterations_np.shape == (173, 11) @@ -134,7 +137,7 @@ def test_rosenbrock(caplog: pytest.LogCaptureFixture) -> None: mle.optimized_params_pd['x'][0], 1, decimal=3 ) np.testing.assert_almost_equal(mle.optimized_params_dict['x'], 1, decimal=3) - + assert mle.optimized_iterations_np is not None last_iter = mle.optimized_iterations_np.shape[0] - 1 assert ( mle.optimized_iterations_np[0, 1] @@ -375,7 +378,7 @@ def test_parameters_are_floats() -> None: seed=1239812093, inits=jinit, algorithm='LBFGS', - tol_obj="rabbit", + tol_obj="rabbit", # type: ignore ) with pytest.raises(ValueError, match='must be of type float'): @@ -384,7 +387,7 @@ def test_parameters_are_floats() -> None: seed=1239812093, inits=jinit, algorithm='LBFGS', - tol_rel_obj="rabbit", + tol_rel_obj="rabbit", # type: ignore ) with pytest.raises(ValueError, match='must be of type float'): @@ -393,7 +396,7 @@ def test_parameters_are_floats() -> None: seed=1239812093, inits=jinit, algorithm='LBFGS', - tol_grad="rabbit", + tol_grad="rabbit", # type: ignore ) with pytest.raises(ValueError, match='must be of type float'): @@ -402,7 +405,7 @@ def test_parameters_are_floats() -> None: seed=1239812093, inits=jinit, algorithm='LBFGS', - tol_rel_grad="rabbit", + tol_rel_grad="rabbit", # type: ignore ) with pytest.raises(ValueError, match='must be of type float'): @@ -411,7 +414,7 @@ def test_parameters_are_floats() -> None: seed=1239812093, inits=jinit, algorithm='LBFGS', - tol_param="rabbit", + tol_param="rabbit", # type: ignore ) with pytest.raises(ValueError, match='must be of type int'): @@ -420,7 +423,7 @@ def test_parameters_are_floats() -> None: seed=1239812093, inits=jinit, algorithm='LBFGS', - history_size="rabbit", + history_size="rabbit", # type: ignore ) @@ -550,7 +553,7 @@ def test_optimize_no_data() -> None: def test_optimize_bad() -> None: stan = os.path.join(DATAFILES_PATH, 'optimize', 'exponential_boundary.stan') exp_bound_model = CmdStanModel(stan_file=stan) - no_data = {} + no_data: dict[str, Any] = {} with pytest.raises(RuntimeError, match='Error during optimization'): exp_bound_model.optimize( data=no_data, seed=1239812093, inits=None, algorithm='BFGS' diff --git a/test/test_pathfinder.py b/test/test_pathfinder.py index c4d8bb43..ebf5e510 100644 --- a/test/test_pathfinder.py +++ b/test/test_pathfinder.py @@ -16,7 +16,7 @@ DATAFILES_PATH = HERE / 'data' -def test_pathfinder_outputs(): +def test_pathfinder_outputs() -> None: stan = DATAFILES_PATH / 'bernoulli.stan' bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = str(DATAFILES_PATH / 'bernoulli.data.json') @@ -39,7 +39,7 @@ def test_pathfinder_outputs(): assert pathfinder.draws().shape == (draws, 4) -def test_pathfinder_from_csv(): +def test_pathfinder_from_csv() -> None: pathfinder_outputs = DATAFILES_PATH / 'pathfinder' pathfinder = from_csv(pathfinder_outputs) assert isinstance(pathfinder, cmdstanpy.CmdStanPathfinder) @@ -51,7 +51,7 @@ def test_pathfinder_from_csv(): assert 0.23 < theta.mean() < 0.27 -def test_single_pathfinder(): +def test_single_pathfinder() -> None: stan = DATAFILES_PATH / 'bernoulli.stan' bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = str(DATAFILES_PATH / 'bernoulli.data.json') @@ -86,7 +86,7 @@ def test_single_pathfinder(): ) -def test_pathfinder_create_inits(): +def test_pathfinder_create_inits() -> None: stan = DATAFILES_PATH / 'bernoulli.stan' bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = str(DATAFILES_PATH / 'bernoulli.data.json') @@ -110,13 +110,15 @@ def test_pathfinder_create_inits(): seeded = pathfinder.create_inits(seed=1234) seeded2 = pathfinder.create_inits(seed=1234) + assert isinstance(seeded, list) + assert isinstance(seeded2, list) assert all( init1['theta'] == init2['theta'] for init1, init2 in zip(seeded, seeded2) ) -def test_pathfinder_init_sampling(): +def test_pathfinder_init_sampling() -> None: logistic_stan = DATAFILES_PATH / 'logistic.stan' logistic_model = cmdstanpy.CmdStanModel(stan_file=logistic_stan) logistic_data = str(DATAFILES_PATH / 'logistic.data.R') @@ -131,7 +133,7 @@ def test_pathfinder_init_sampling(): assert fit.draws().shape == (1000, 4, 9) -def test_inits_for_pathfinder(): +def test_inits_for_pathfinder() -> None: stan = DATAFILES_PATH / 'bernoulli.stan' bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = str(DATAFILES_PATH / 'bernoulli.data.json') @@ -151,7 +153,7 @@ def test_inits_for_pathfinder(): assert "Bounded variable is 1.1" in captured.getvalue() -def test_pathfinder_no_psis(): +def test_pathfinder_no_psis() -> None: stan = DATAFILES_PATH / 'bernoulli.stan' bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = str(DATAFILES_PATH / 'bernoulli.data.json') @@ -162,7 +164,7 @@ def test_pathfinder_no_psis(): assert pathfinder.draws().shape == (4000, 4) -def test_pathfinder_no_lp_calc(): +def test_pathfinder_no_lp_calc() -> None: stan = DATAFILES_PATH / 'bernoulli.stan' bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = str(DATAFILES_PATH / 'bernoulli.data.json') @@ -176,7 +178,7 @@ def test_pathfinder_no_lp_calc(): assert n_lp_nan > 3000 # but most are not -def test_pathfinder_threads(): +def test_pathfinder_threads() -> None: stan = DATAFILES_PATH / 'bernoulli.stan' bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = str(DATAFILES_PATH / 'bernoulli.data.json') diff --git a/test/test_sample.py b/test/test_sample.py index 1dff1dc4..6571dac4 100644 --- a/test/test_sample.py +++ b/test/test_sample.py @@ -53,7 +53,7 @@ 'path~with~tilde/bernoulli_path_with_tilde.stan', ], ) -def test_bernoulli_good(stanfile: str): +def test_bernoulli_good(stanfile: str) -> None: stan = os.path.join(DATAFILES_PATH, stanfile) bern_model = CmdStanModel(stan_file=stan, force_compile=True) @@ -88,7 +88,9 @@ def test_bernoulli_good(stanfile: str): assert bern_fit.draws().shape == (100, 2, len(BERNOULLI_COLS)) assert bern_fit.metric_type == 'diag_e' + assert bern_fit.step_size is not None assert bern_fit.step_size.shape == (2,) + assert bern_fit.inv_metric is not None assert bern_fit.inv_metric.shape == (2, 1) assert bern_fit.draws(concat_chains=True).shape == ( @@ -124,7 +126,9 @@ def test_bernoulli_good(stanfile: str): bern_sample = bern_fit.draws() assert bern_sample.shape == (100, 2, len(BERNOULLI_COLS)) assert bern_fit.metric_type == 'dense_e' + assert bern_fit.step_size is not None assert bern_fit.step_size.shape == (2,) + assert bern_fit.inv_metric is not None assert bern_fit.inv_metric.shape == (2, 1, 1) bern_fit = bern_model.sample( @@ -203,6 +207,7 @@ def test_bernoulli_unit_e(stanfile: str) -> None: ) assert bern_fit.metric_type == 'unit_e' assert bern_fit.inv_metric is None + assert bern_fit.step_size is not None assert bern_fit.step_size.shape == (2,) assert bern_fit.draws().shape == (100, 2, len(BERNOULLI_COLS)) @@ -640,6 +645,7 @@ def test_sample_no_params() -> None: assert datagen_fit.step_size is None else: assert 'lp__' in list(summary.index) + assert datagen_fit.step_size is not None assert np.isnan(datagen_fit.step_size).all() exe_only = os.path.join(DATAFILES_PATH, 'exe_only') @@ -654,6 +660,7 @@ def test_sample_no_params() -> None: assert datagen2_fit.step_size is None assert 'lp__' not in list(summary.index) else: + assert datagen2_fit.step_size is not None assert np.isnan(datagen2_fit.step_size).all() assert 'lp__' in list(summary.index) @@ -666,7 +673,7 @@ def test_index_bounds_error() -> None: oob_model.sample() -def test_show_console(stanfile='bernoulli.stan'): +def test_show_console(stanfile: str = 'bernoulli.stan') -> None: stan = os.path.join(DATAFILES_PATH, stanfile) bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -687,7 +694,7 @@ def test_show_console(stanfile='bernoulli.stan'): assert 'Chain [2] method = sample' in console -def test_show_progress(stanfile='bernoulli.stan'): +def test_show_progress(stanfile: str = 'bernoulli.stan') -> None: stan = os.path.join(DATAFILES_PATH, stanfile) bern_model = CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -817,6 +824,7 @@ def test_validate_good_run() -> None: fit.summary(percentiles=[-1]) diagnostics = fit.diagnose() + assert diagnostics is not None assert 'Treedepth satisfactory for all transitions.' in diagnostics assert 'No divergent transitions found.' in diagnostics assert 'E-BFMI satisfactory' in diagnostics @@ -845,7 +853,9 @@ def test_validate_big_run() -> None: assert fit.num_draws_sampling == 1000 assert fit.column_names == tuple(column_names) assert fit.metric_type == 'diag_e' + assert fit.step_size is not None assert fit.step_size.shape == (2,) + assert fit.inv_metric is not None assert fit.inv_metric.shape == (2, 2095) assert fit.draws().shape == (1000, 2, 2102) assert fit.draws_pd(vars=['phi']).shape == (2000, 2095) @@ -856,6 +866,7 @@ def test_validate_big_run() -> None: def test_instantiate_from_csvfiles() -> None: csvfiles_path = os.path.join(DATAFILES_PATH, 'runset-good') bern_fit = from_csv(path=csvfiles_path) + assert isinstance(bern_fit, CmdStanMCMC) draws_pd = bern_fit.draws_pd() assert draws_pd.shape == ( bern_fit.runset.chains * bern_fit.num_draws_sampling, @@ -863,6 +874,7 @@ def test_instantiate_from_csvfiles() -> None: ) csvfiles_path = os.path.join(DATAFILES_PATH, 'runset-big') big_fit = from_csv(path=csvfiles_path) + assert isinstance(big_fit, CmdStanMCMC) draws_pd = big_fit.draws_pd() assert draws_pd.shape == ( big_fit.runset.chains * big_fit.num_draws_sampling, @@ -875,7 +887,7 @@ def test_instantiate_from_csvfiles() -> None: if file.endswith(".csv"): csvfiles.append(os.path.join(csvfiles_path, file)) bern_fit = from_csv(path=csvfiles) - + assert isinstance(bern_fit, CmdStanMCMC) draws_pd = bern_fit.draws_pd() assert draws_pd.shape == ( bern_fit.runset.chains * bern_fit.num_draws_sampling, @@ -883,6 +895,7 @@ def test_instantiate_from_csvfiles() -> None: ) # single csvfile bern_fit = from_csv(path=csvfiles[0]) + assert isinstance(bern_fit, CmdStanMCMC) draws_pd = bern_fit.draws_pd() assert draws_pd.shape == ( bern_fit.num_draws_sampling, @@ -891,6 +904,7 @@ def test_instantiate_from_csvfiles() -> None: # glob csvfiles_path = os.path.join(csvfiles_path, '*.csv') big_fit = from_csv(path=csvfiles_path) + assert isinstance(big_fit, CmdStanMCMC) draws_pd = big_fit.draws_pd() assert draws_pd.shape == ( big_fit.runset.chains * big_fit.num_draws_sampling, @@ -898,10 +912,10 @@ def test_instantiate_from_csvfiles() -> None: ) -def test_pd_xr_agreement(): +def test_pd_xr_agreement() -> None: csvfiles_path = os.path.join(DATAFILES_PATH, 'runset-good', '*.csv') bern_fit = from_csv(path=csvfiles_path) - + assert isinstance(bern_fit, CmdStanMCMC) draws_pd = bern_fit.draws_pd() draws_xr = bern_fit.draws_xr() @@ -933,7 +947,7 @@ def test_instantiate_from_csvfiles_fail( ): from_csv(csvfiles_path, 'optimize') - csvfiles = [] + csvfiles: list[str] = [] with pytest.raises(ValueError, match=r'No CSV files found'): from_csv(csvfiles, 'sample') @@ -980,12 +994,14 @@ def test_instantiate_from_csvfiles_fail( def test_from_csv_fixed_param() -> None: csv_path = os.path.join(DATAFILES_PATH, 'fixed_param_sample.csv') fixed_param_sample = from_csv(path=csv_path) + assert isinstance(fixed_param_sample, CmdStanMCMC) assert fixed_param_sample.draws_pd().shape == (100, 88) def test_from_csv_no_param_hmc() -> None: csv_path = os.path.join(DATAFILES_PATH, 'no_param_hmc_sample.csv') no_parameters_sample = from_csv(path=csv_path) + assert isinstance(no_parameters_sample, CmdStanMCMC) assert no_parameters_sample.draws_pd().shape == (100, 93) @@ -1012,6 +1028,7 @@ def test_custom_metric(force_one_process_per_chain: bool) -> None: inv_metric=jmetric, force_one_process_per_chain=force_one_process_per_chain, ) + assert fit1.inv_metric is not None np.testing.assert_allclose( fit1.inv_metric[0], metric_dict_1['inv_metric'], atol=1e-6 ) @@ -1029,6 +1046,7 @@ def test_custom_metric(force_one_process_per_chain: bool) -> None: inv_metric=[jmetric, jmetric2], force_one_process_per_chain=force_one_process_per_chain, ) + assert fit2.inv_metric is not None np.testing.assert_allclose( fit2.inv_metric[0], metric_dict_1['inv_metric'], atol=1e-6 ) @@ -1046,6 +1064,7 @@ def test_custom_metric(force_one_process_per_chain: bool) -> None: inv_metric=metric_dict_1, force_one_process_per_chain=force_one_process_per_chain, ) + assert fit3.inv_metric is not None for i in range(4): np.testing.assert_allclose( fit3.inv_metric[i], metric_dict_1['inv_metric'], atol=1e-6 @@ -1059,6 +1078,7 @@ def test_custom_metric(force_one_process_per_chain: bool) -> None: inv_metric=[metric_dict_1, metric_dict_2], force_one_process_per_chain=force_one_process_per_chain, ) + assert fit4.inv_metric is not None np.testing.assert_allclose( fit4.inv_metric[0], metric_dict_1['inv_metric'], atol=1e-6 ) @@ -1075,6 +1095,7 @@ def test_custom_metric(force_one_process_per_chain: bool) -> None: inv_metric=[np.array(metric_dict_1['inv_metric']), jmetric2], force_one_process_per_chain=force_one_process_per_chain, ) + assert fit5.inv_metric is not None np.testing.assert_allclose( fit5.inv_metric[0], metric_dict_1['inv_metric'], atol=1e-6 ) @@ -1282,6 +1303,7 @@ def test_diagnose_divergences() -> None: ] diagnose = fit.diagnose() + assert diagnose is not None for e in expected: assert e in diagnose @@ -1546,6 +1568,7 @@ def test_variable_bern() -> None: def test_variables_2d() -> None: csvfiles_path = os.path.join(DATAFILES_PATH, 'lotka-volterra.csv') fit = from_csv(path=csvfiles_path) + assert isinstance(fit, CmdStanMCMC) assert 20 == fit.num_draws_sampling assert 8 == len(fit.metadata.stan_vars) assert 'z' in fit.metadata.stan_vars @@ -1562,6 +1585,7 @@ def test_variables_3d() -> None: # construct fit using existing sampler output csvfiles_path = os.path.join(DATAFILES_PATH, 'multidim_vars.csv') fit = from_csv(path=csvfiles_path) + assert isinstance(fit, CmdStanMCMC) assert 20 == fit.num_draws_sampling assert 3 == len(fit.metadata.stan_vars) assert 'y_rep' in fit.metadata.stan_vars @@ -1627,7 +1651,7 @@ def test_validate() -> None: assert bern_fit.metric_type == 'diag_e' -def test_validate_sample_sig_figs(stanfile='bernoulli.stan'): +def test_validate_sample_sig_figs(stanfile: str = 'bernoulli.stan') -> None: if not cmdstan_version_before(2, 25): stan = os.path.join(DATAFILES_PATH, stanfile) bern_model = CmdStanModel(stan_file=stan) @@ -1680,6 +1704,7 @@ def test_validate_summary_sig_figs() -> None: os.path.join(DATAFILES_PATH, 'logistic_output_4.csv'), ] ) + assert isinstance(fit, CmdStanMCMC) sum_default = fit.summary() @@ -2071,7 +2096,7 @@ def test_tuple_data_in() -> None: data_model.sample(data, chains=1, iter_warmup=1, iter_sampling=1) -def test_csv_roundtrip(): +def test_csv_roundtrip() -> None: stan = os.path.join(DATAFILES_PATH, 'matrix_var.stan') model = CmdStanModel(stan_file=stan) fit = model.sample( @@ -2085,6 +2110,7 @@ def test_csv_roundtrip(): # mostly just asserting that from_csv always succeeds # in parsing latest cmdstan headers fit_from_csv = from_csv(fit.runset.csv_files) + assert isinstance(fit_from_csv, CmdStanMCMC) z_from_csv = fit_from_csv.stan_variable(var="z") assert z_from_csv.shape == (20, 4, 3) z_with_warmup_from_csv = fit.stan_variable(var="z", inc_warmup=True) @@ -2092,7 +2118,7 @@ def test_csv_roundtrip(): @pytest.mark.order(before="test_no_xarray") -def test_serialization(stanfile='bernoulli.stan'): +def test_serialization(stanfile: str = 'bernoulli.stan') -> None: # This test must before any test that uses the `without_import` context # manager because the latter uses `reload` with side effects that affect # the consistency of classes. @@ -2119,7 +2145,7 @@ def test_serialization(stanfile='bernoulli.stan'): np.testing.assert_array_equal(value1, variables2[key]) -def test_mcmc_create_inits(): +def test_mcmc_create_inits() -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') bern_model = cmdstanpy.CmdStanModel(stan_file=stan) jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') @@ -2143,13 +2169,15 @@ def test_mcmc_create_inits(): seeded = mcmc.create_inits(seed=1234) seeded2 = mcmc.create_inits(seed=1234) + assert isinstance(seeded, list) + assert isinstance(seeded2, list) assert all( init1['theta'] == init2['theta'] for init1, init2 in zip(seeded, seeded2) ) -def test_mcmc_init_sampling(): +def test_mcmc_init_sampling() -> None: stan = os.path.join(DATAFILES_PATH, 'logistic.stan') logistic_model = cmdstanpy.CmdStanModel(stan_file=stan) logistic_data = os.path.join(DATAFILES_PATH, 'logistic.data.R') @@ -2163,7 +2191,7 @@ def test_mcmc_init_sampling(): assert fit.draws().shape == (1000, 4, 9) -def test_sample_dense_mass_matrix(): +def test_sample_dense_mass_matrix() -> None: stan = os.path.join(DATAFILES_PATH, 'linear_regression.stan') jdata = os.path.join(DATAFILES_PATH, 'linear_regression.data.json') linear_model = CmdStanModel(stan_file=stan) @@ -2173,7 +2201,7 @@ def test_sample_dense_mass_matrix(): assert fit.inv_metric.shape == (2, 3, 3) -def test_no_output_draws(): +def test_no_output_draws() -> None: stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan') model = cmdstanpy.CmdStanModel(stan_file=stan) data = os.path.join(DATAFILES_PATH, 'bernoulli.data.json') diff --git a/test/test_stancsv.py b/test/test_stancsv.py index 0fcad8fe..7c648163 100644 --- a/test/test_stancsv.py +++ b/test/test_stancsv.py @@ -16,7 +16,7 @@ DATAFILES_PATH = os.path.join(HERE, 'data') -def test_csv_bytes_to_numpy(): +def test_csv_bytes_to_numpy() -> None: lines = [ b"-6.76206,1,0.787025,1,1,0,6.81411,0.229458\n", b"-6.81411,0.983499,0.787025,1,1,0,6.8147,0.20649\n", @@ -37,7 +37,7 @@ def test_csv_bytes_to_numpy(): assert arr_out[0].dtype == np.float64 -def test_csv_bytes_to_numpy_no_polars(): +def test_csv_bytes_to_numpy_no_polars() -> None: lines = [ b"-6.76206,1,0.787025,1,1,0,6.81411,0.229458\n", b"-6.81411,0.983499,0.787025,1,1,0,6.8147,0.20649\n", @@ -59,7 +59,7 @@ def test_csv_bytes_to_numpy_no_polars(): assert arr_out[0].dtype == np.float64 -def test_csv_bytes_to_numpy_single_element(): +def test_csv_bytes_to_numpy_single_element() -> None: lines = [ b"-6.76206\n", ] @@ -73,7 +73,7 @@ def test_csv_bytes_to_numpy_single_element(): assert np.array_equal(arr_out, expected) -def test_csv_bytes_to_numpy_single_element_no_polars(): +def test_csv_bytes_to_numpy_single_element_no_polars() -> None: lines = [ b"-6.76206\n", ] @@ -88,13 +88,12 @@ def test_csv_bytes_to_numpy_single_element_no_polars(): assert np.array_equal(arr_out, expected) -def test_csv_bytes_empty(): - lines = [] - arr = stancsv.csv_bytes_list_to_numpy(lines) +def test_csv_bytes_empty() -> None: + arr = stancsv.csv_bytes_list_to_numpy([]) assert np.array_equal(arr, np.empty((0, 0))) -def test_parse_comments_header_and_draws(): +def test_parse_comments_header_and_draws() -> None: lines: List[bytes] = [b"# 1\n", b"a\n", b"3\n", b"# 4\n"] ( comment_lines, @@ -107,7 +106,7 @@ def test_parse_comments_header_and_draws(): assert draws_lines == [b"3\n"] -def test_parsing_adaptation_lines(): +def test_parsing_adaptation_lines() -> None: lines = [ b"# Adaptation terminated\n", b"# Step size = 0.787025\n", @@ -121,7 +120,7 @@ def test_parsing_adaptation_lines(): assert mass_matrix == 1 -def test_parsing_adaptation_lines_diagonal(): +def test_parsing_adaptation_lines_diagonal() -> None: lines = [ b"diag_e", # Will be present in the Stan CSV config b"# Adaptation terminated\n", @@ -135,7 +134,7 @@ def test_parsing_adaptation_lines_diagonal(): assert np.array_equal(mass_matrix, np.array([1, 2, 3])) -def test_parsing_adaptation_lines_dense(): +def test_parsing_adaptation_lines_dense() -> None: lines = [ b"# Adaptation terminated\n", b"# Step size = 0.775147\n", @@ -158,7 +157,7 @@ def test_parsing_adaptation_lines_dense(): assert np.array_equal(mass_matrix, expected) -def test_parsing_adaptation_lines_missing_everything(): +def test_parsing_adaptation_lines_missing_everything() -> None: lines = [ b"# Adaptation terminated\n", b"# Elements of inverse mass matrix:\n", @@ -166,7 +165,7 @@ def test_parsing_adaptation_lines_missing_everything(): assert stancsv.parse_hmc_adaptation_lines(lines) == (None, None) -def test_parsing_adaptation_lines_no_free_params(): +def test_parsing_adaptation_lines_no_free_params() -> None: lines = [ b"# Adaptation terminated\n", b"# Step size = 1.77497\n", @@ -176,7 +175,7 @@ def test_parsing_adaptation_lines_no_free_params(): assert mass_matrix is None -def test_csv_polars_and_numpy_equiv(): +def test_csv_polars_and_numpy_equiv() -> None: lines = [ b"-6.76206,1,0.787025,1,1,0,6.81411,0.229458\n", b"-6.81411,0.983499,0.787025,1,1,0,6.8147,0.20649\n", @@ -189,7 +188,7 @@ def test_csv_polars_and_numpy_equiv(): assert np.array_equal(arr_out_polars, arr_out_numpy) -def test_csv_polars_and_numpy_equiv_one_line(): +def test_csv_polars_and_numpy_equiv_one_line() -> None: lines = [ b"-6.76206,1,0.787025,1,1,0,6.81411,0.229458\n", ] @@ -199,7 +198,7 @@ def test_csv_polars_and_numpy_equiv_one_line(): assert np.array_equal(arr_out_polars, arr_out_numpy) -def test_csv_polars_and_numpy_equiv_one_element(): +def test_csv_polars_and_numpy_equiv_one_element() -> None: lines = [ b"-6.76206\n", ] @@ -209,7 +208,7 @@ def test_csv_polars_and_numpy_equiv_one_element(): assert np.array_equal(arr_out_polars, arr_out_numpy) -def test_parse_stan_csv_from_file(): +def test_parse_stan_csv_from_file() -> None: csv_path = os.path.join(DATAFILES_PATH, "bernoulli_output_1.csv") ( @@ -235,7 +234,7 @@ def test_parse_stan_csv_from_file(): assert draws_lines == draws_lines_path -def test_config_parsing(): +def test_config_parsing() -> None: csv_path = os.path.join(DATAFILES_PATH, "bernoulli_output_1.csv") comment_lines, *_ = stancsv.parse_comments_header_and_draws(csv_path) @@ -278,7 +277,7 @@ def test_config_parsing(): assert config == expected -def test_config_parsing_data_transforms(): +def test_config_parsing_data_transforms() -> None: comments = [ b"# bool_t = true\n", b"# bool_f = false\n", @@ -289,28 +288,28 @@ def test_config_parsing_data_transforms(): assert stancsv.parse_config(comments) == expected -def test_column_filter_basic(): +def test_column_filter_basic() -> None: data = [b"1,2,3\n", b"4,5,6\n"] indexes = [0, 2] expected = [b"1,3\n", b"4,6\n"] assert stancsv.filter_csv_bytes_by_columns(data, indexes) == expected -def test_column_filter_empty_input(): +def test_column_filter_empty_input() -> None: assert not stancsv.filter_csv_bytes_by_columns([], [0]) -def test_column_filter_empty_indexes(): +def test_column_filter_empty_indexes() -> None: data = [b"1,2,3\n", b"4,5,6\n"] assert stancsv.filter_csv_bytes_by_columns(data, []) == [b"\n", b"\n"] -def test_column_filter_single_column(): +def test_column_filter_single_column() -> None: data = [b"a,b,c\n", b"d,e,f\n"] assert stancsv.filter_csv_bytes_by_columns(data, [1]) == [b"b\n", b"e\n"] -def test_column_filter_non_consecutive_indexes(): +def test_column_filter_non_consecutive_indexes() -> None: data = [b"9,8,7,6\n", b"5,4,3,2\n"] assert stancsv.filter_csv_bytes_by_columns(data, [2, 0]) == [ b"7,9\n", @@ -318,7 +317,7 @@ def test_column_filter_non_consecutive_indexes(): ] -def test_parse_header(): +def test_parse_header() -> None: header = ( "lp__,accept_stat__,stepsize__,treedepth__" ",n_leapfrog__,divergent__,energy__,theta.1" @@ -337,7 +336,7 @@ def test_parse_header(): assert parsed == expected -def test_extract_config_and_header_info(): +def test_extract_config_and_header_info() -> None: comments = [b"# stan_version_major = 2\n"] header = "lp__,theta.1" out = stancsv.construct_config_header_dict(comments, header) @@ -346,14 +345,14 @@ def test_extract_config_and_header_info(): assert out["column_names"] == ("lp__", "theta[1]") -def test_parse_variational_eta(): +def test_parse_variational_eta() -> None: csv_path = os.path.join(DATAFILES_PATH, "variational", "eta_big_output.csv") comments, *_ = stancsv.parse_comments_header_and_draws(csv_path) eta = stancsv.parse_variational_eta(comments) assert eta == 100.0 -def test_parse_variational_eta_no_block(): +def test_parse_variational_eta_no_block() -> None: comments = [ b"# stanc_version = stanc3 v2.28.0\n", b"# stancflags = \n", @@ -367,7 +366,7 @@ def test_parse_variational_eta_no_block(): stancsv.parse_variational_eta(comments) -def test_max_treedepth_and_divergence_counts(): +def test_max_treedepth_and_divergence_counts() -> None: header = ( "lp__,accept_stat__,stepsize__,treedepth__," "n_leapfrog__,divergent__,energy__,theta\n" @@ -388,7 +387,7 @@ def test_max_treedepth_and_divergence_counts(): assert out == (2, 1) -def test_max_treedepth_and_divergence_counts_warmup_draws(): +def test_max_treedepth_and_divergence_counts_warmup_draws() -> None: header = ( "lp__,accept_stat__,stepsize__,treedepth__," "n_leapfrog__,divergent__,energy__,theta\n" @@ -409,19 +408,16 @@ def test_max_treedepth_and_divergence_counts_warmup_draws(): assert out == (1, 1) -def test_max_treedepth_and_divergence_counts_no_draws(): +def test_max_treedepth_and_divergence_counts_no_draws() -> None: header = ( "lp__,accept_stat__,stepsize__,treedepth__," "n_leapfrog__,divergent__,energy__,theta\n" ) - draws = [] - out = stancsv.extract_max_treedepth_and_divergence_counts( - header, draws, 10, 0 - ) + out = stancsv.extract_max_treedepth_and_divergence_counts(header, [], 10, 0) assert out == (0, 0) -def test_max_treedepth_and_divergence_invalid(): +def test_max_treedepth_and_divergence_invalid() -> None: header = "lp__,accept_stat__,stepsize__,n_leapfrog__,energy__,theta\n" draws = [ b"-4.78686,0.986298,1.09169,3,5.29492,0.550024\n", @@ -431,7 +427,7 @@ def test_max_treedepth_and_divergence_invalid(): ) == (0, 0) -def test_sneaky_fixed_param_check(): +def test_sneaky_fixed_param_check() -> None: sneaky_header = "lp__,accept_stat__,N,y_sim.1" normal_header = ( "lp__,accept_stat__,stepsize__,treedepth__," @@ -442,12 +438,12 @@ def test_sneaky_fixed_param_check(): assert not stancsv.is_sneaky_fixed_param(normal_header) -def test_warmup_sampling_draw_counts(): +def test_warmup_sampling_draw_counts() -> None: csv_path = os.path.join(DATAFILES_PATH, "bernoulli_output_1.csv") assert stancsv.count_warmup_and_sampling_draws(csv_path) == (0, 10) -def test_warmup_sampling_draw_counts_with_warmup(): +def test_warmup_sampling_draw_counts_with_warmup() -> None: lines = [ b"# algorithm = hmc (Default)\n", ( @@ -467,7 +463,7 @@ def test_warmup_sampling_draw_counts_with_warmup(): assert stancsv.count_warmup_and_sampling_draws(fio) == (1, 1) -def test_warmup_sampling_draw_counts_fixed_param(): +def test_warmup_sampling_draw_counts_fixed_param() -> None: lines = [ b"# algorithm = fixed_param\n", ( @@ -483,7 +479,7 @@ def test_warmup_sampling_draw_counts_fixed_param(): assert stancsv.count_warmup_and_sampling_draws(fio) == (0, 2) -def test_warmup_sampling_draw_counts_no_draws(): +def test_warmup_sampling_draw_counts_no_draws() -> None: lines = [ b"# algorithm = fixed_param\n", ( @@ -497,7 +493,7 @@ def test_warmup_sampling_draw_counts_no_draws(): assert stancsv.count_warmup_and_sampling_draws(fio) == (0, 0) -def test_warmup_sampling_draw_counts_invalid(): +def test_warmup_sampling_draw_counts_invalid() -> None: lines = [ b"# algorithm = fixed_param\n", ] @@ -506,25 +502,24 @@ def test_warmup_sampling_draw_counts_invalid(): stancsv.count_warmup_and_sampling_draws(fio) -def test_inconsistent_draws_shape(): +def test_inconsistent_draws_shape() -> None: header = "a,b" draws = [b"0,1,2\n"] with pytest.raises(ValueError): stancsv.raise_on_inconsistent_draws_shape(header, draws) -def test_inconsistent_draws_shape_empty(): - draws = [] - stancsv.raise_on_inconsistent_draws_shape("", draws) +def test_inconsistent_draws_shape_empty() -> None: + stancsv.raise_on_inconsistent_draws_shape("", []) -def test_invalid_adaptation_block_good(): +def test_invalid_adaptation_block_good() -> None: csv_path = os.path.join(DATAFILES_PATH, "bernoulli_output_1.csv") comments, *_ = stancsv.parse_comments_header_and_draws(csv_path) stancsv.raise_on_invalid_adaptation_block(comments) -def test_invalid_adaptation_block_missing(): +def test_invalid_adaptation_block_missing() -> None: lines = [ b"# metric = diag_e (Default)\n", ( @@ -539,7 +534,7 @@ def test_invalid_adaptation_block_missing(): stancsv.raise_on_invalid_adaptation_block(lines) -def test_invalid_adaptation_block_no_metric(): +def test_invalid_adaptation_block_no_metric() -> None: lines = [ ( b"lp__,accept_stat__,stepsize__,treedepth__," @@ -554,7 +549,7 @@ def test_invalid_adaptation_block_no_metric(): stancsv.raise_on_invalid_adaptation_block(lines) -def test_invalid_adaptation_block_invalid_step_size(): +def test_invalid_adaptation_block_invalid_step_size() -> None: lines = [ b"# metric = diag_e (Default)\n", ( @@ -570,7 +565,7 @@ def test_invalid_adaptation_block_invalid_step_size(): stancsv.raise_on_invalid_adaptation_block(lines) -def test_invalid_adaptation_block_mismatched_structure(): +def test_invalid_adaptation_block_mismatched_structure() -> None: lines = [ b"# metric = diag_e (Default)\n", ( @@ -586,7 +581,7 @@ def test_invalid_adaptation_block_mismatched_structure(): stancsv.raise_on_invalid_adaptation_block(lines) -def test_invalid_adaptation_block_missing_step_size(): +def test_invalid_adaptation_block_missing_step_size() -> None: lines = [ b"# metric = diag_e (Default)\n", ( @@ -601,7 +596,7 @@ def test_invalid_adaptation_block_missing_step_size(): stancsv.raise_on_invalid_adaptation_block(lines) -def test_invalid_adaptation_block_unit_e(): +def test_invalid_adaptation_block_unit_e() -> None: lines = [ b"# metric = unit_e\n", ( @@ -615,7 +610,7 @@ def test_invalid_adaptation_block_unit_e(): stancsv.raise_on_invalid_adaptation_block(lines) -def test_invalid_adaptation_block_dense_e_valid(): +def test_invalid_adaptation_block_dense_e_valid() -> None: lines = [ b"# metric = dense_e\n", ( @@ -632,7 +627,7 @@ def test_invalid_adaptation_block_dense_e_valid(): stancsv.raise_on_invalid_adaptation_block(lines) -def test_invalid_adaptation_block_dense_e_invalid(): +def test_invalid_adaptation_block_dense_e_invalid() -> None: lines = [ b"# metric = dense_e\n", ( @@ -650,7 +645,7 @@ def test_invalid_adaptation_block_dense_e_invalid(): stancsv.raise_on_invalid_adaptation_block(lines) -def test_parsing_timing_lines(): +def test_parsing_timing_lines() -> None: lines = [ b"# \n", b"# Elapsed Time: 0.001332 seconds (Warm-up)\n", @@ -666,7 +661,7 @@ def test_parsing_timing_lines(): assert out['Total'] == 0.001581 -def test_munge_varname(): +def test_munge_varname() -> None: name1 = "a" name2 = "a:1" name3 = "a:1.2" diff --git a/test/test_utils.py b/test/test_utils.py index f3ba4cdd..dcf687b8 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -12,6 +12,7 @@ import stat import tempfile from test import check_present, mark_windows_only, raises_nested +from typing import Generator from unittest import mock import numpy as np @@ -63,7 +64,7 @@ def test_default_path() -> None: else: cmdstan_dir = os.path.expanduser(os.path.join('~', _DOT_CMDSTAN)) install_version = os.path.join( - cmdstan_dir, get_latest_cmdstan(cmdstan_dir) + cmdstan_dir, get_latest_cmdstan(cmdstan_dir) # type: ignore ) assert os.path.samefile(cmdstan_path(), install_version) assert 'CMDSTAN' in os.environ @@ -88,10 +89,8 @@ def test_non_special_chars_location(bad_dir: str, bad_name: str) -> None: stan_bad = os.path.join(bad_path, bad_name) shutil.copy(stan, stan_bad) - stan_copied = None try: - with SanitizedOrTmpFilePath(stan_bad) as (pth, is_changed): - stan_copied = pth + with SanitizedOrTmpFilePath(stan_bad) as (stan_copied, is_changed): assert os.path.exists(stan_copied) assert ' ' not in stan_copied @@ -126,7 +125,7 @@ def test_set_path() -> None: else: cmdstan_dir = os.path.expanduser(os.path.join('~', _DOT_CMDSTAN)) install_version = os.path.join( - cmdstan_dir, get_latest_cmdstan(cmdstan_dir) + cmdstan_dir, get_latest_cmdstan(cmdstan_dir) # type: ignore ) set_cmdstan_path(install_version) assert os.path.samefile(install_version, cmdstan_path()) @@ -134,7 +133,7 @@ def test_set_path() -> None: @contextlib.contextmanager -def temporary_cmdstan_path(path: str) -> None: +def temporary_cmdstan_path(path: str) -> Generator[None, None, None]: prev = cmdstan_path() try: set_cmdstan_path(path) @@ -145,12 +144,12 @@ def temporary_cmdstan_path(path: str) -> None: def test_validate_path() -> None: if 'CMDSTAN' in os.environ: - install_version = os.environ.get('CMDSTAN') + install_version = os.environ.get('CMDSTAN', '') else: cmdstan_dir = os.path.expanduser(os.path.join('~', _DOT_CMDSTAN)) install_version = os.path.join( - cmdstan_dir, get_latest_cmdstan(cmdstan_dir) + cmdstan_dir, get_latest_cmdstan(cmdstan_dir) # type: ignore ) set_cmdstan_path(install_version) @@ -246,13 +245,14 @@ def test_cmdstan_version(caplog: pytest.LogCaptureFixture) -> None: def test_dict_to_file() -> None: file_good = os.path.join(DATAFILES_PATH, 'bernoulli_output_1.csv') dict_good = {'a': 0.5} - created_tmp = None with temp_single_json(file_good) as fg1: + assert fg1 is not None assert os.path.exists(fg1) assert os.path.exists(file_good) with temp_single_json(dict_good) as fg2: + assert fg2 is not None assert os.path.exists(fg2) with open(fg2) as fg2_d: assert json.load(fg2_d) == dict_good @@ -261,13 +261,14 @@ def test_dict_to_file() -> None: assert not os.path.exists(created_tmp) with pytest.raises(AttributeError): - with temp_single_json(123) as _: + with temp_single_json(123) as _: # type: ignore pass -def test_temp_inits(): +def test_temp_inits() -> None: dict_good = {'a': 0.5} with temp_inits([dict_good, dict_good]) as base_file: + assert isinstance(base_file, str) fg1 = base_file[:-5] + '_1.json' fg2 = base_file[:-5] + '_2.json' assert os.path.exists(fg1) @@ -519,26 +520,34 @@ def test_windows_short_path_file_with_space() -> None: def test_rload_metric() -> None: dfile = os.path.join(DATAFILES_PATH, 'metric_diag.data.R') data_dict = rload(dfile) + assert data_dict is not None + assert isinstance(data_dict['inv_metric'], np.ndarray) assert data_dict['inv_metric'].shape == (3,) dfile = os.path.join(DATAFILES_PATH, 'metric_dense.data.R') data_dict = rload(dfile) + assert data_dict is not None + assert isinstance(data_dict['inv_metric'], np.ndarray) assert data_dict['inv_metric'].shape == (3, 3) def test_rload_data() -> None: dfile = os.path.join(DATAFILES_PATH, 'rdump_test.data.R') data_dict = rload(dfile) + assert data_dict is not None assert data_dict['N'] == 128 assert data_dict['M'] == 2 + assert isinstance(data_dict['x'], np.ndarray) assert data_dict['x'].shape == (128, 2) def test_rload_jags_data() -> None: dfile = os.path.join(DATAFILES_PATH, 'rdump_jags.data.R') data_dict = rload(dfile) + assert data_dict is not None assert data_dict['N'] == 128 assert data_dict['M'] == 2 + assert isinstance(data_dict['y'], np.ndarray) assert data_dict['y'].shape == (128,) @@ -569,6 +578,7 @@ def test_rload_bad_data_3() -> None: def test_parse_rdump_value() -> None: struct1 = 'structure(c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),.Dim=c(2,8))' v_struct1 = parse_rdump_value(struct1) + assert isinstance(v_struct1, np.ndarray) assert v_struct1.shape == (2, 8) assert v_struct1[1, 0] == 2 assert v_struct1[0, 7] == 15 @@ -577,10 +587,12 @@ def test_parse_rdump_value() -> None: 'structure(c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),.Dim=c(1,16))' ) v_struct2 = parse_rdump_value(struct2) + assert isinstance(v_struct2, np.ndarray) assert v_struct2.shape == (1, 16) struct3 = 'structure(c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16),.Dim=c(8,2))' v_struct3 = parse_rdump_value(struct3) + assert isinstance(v_struct3, np.ndarray) assert v_struct3.shape == (8, 2) assert v_struct3[1, 0] == 2 assert v_struct3[7, 0] == 8 diff --git a/test/test_variational.py b/test/test_variational.py index b163bc14..66c47826 100644 --- a/test/test_variational.py +++ b/test/test_variational.py @@ -8,6 +8,7 @@ import shutil from math import fabs from test import check_present +from typing import Any import numpy as np import pytest @@ -24,7 +25,7 @@ def test_instantiate() -> None: stan = os.path.join(DATAFILES_PATH, 'variational', 'eta_should_be_big.stan') model = CmdStanModel(stan_file=stan) - no_data = {} + no_data: dict[str, Any] = {} args = VariationalArgs(algorithm='meanfield') cmdstan_args = CmdStanArgs( model_name=model.name, @@ -61,6 +62,7 @@ def test_instantiate() -> None: def test_instantiate_from_csvfiles() -> None: csvfiles_path = os.path.join(DATAFILES_PATH, 'variational') variational = from_csv(path=csvfiles_path) + assert isinstance(variational, CmdStanVB) assert 'CmdStanVB: model=eta_should_be_big' in repr(variational) assert 'method=variational' in repr(variational) assert variational.column_names == ( @@ -368,6 +370,9 @@ def test_variational_create_inits() -> None: seeded = vb.create_inits(seed=1234) seeded2 = vb.create_inits(seed=1234) + assert isinstance(seeded, list) + assert isinstance(seeded2, list) + assert len(seeded) == len(seeded2) == 4 assert all( init1['theta'] == init2['theta'] for init1, init2 in zip(seeded, seeded2)