cmdstanpy 1.2.4__tar.gz → 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {cmdstanpy-1.2.4/cmdstanpy.egg-info → cmdstanpy-1.3.0}/PKG-INFO +7 -4
  2. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/__init__.py +4 -0
  3. cmdstanpy-1.3.0/cmdstanpy/_version.py +3 -0
  4. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/cmdstan_args.py +38 -152
  5. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/compilation.py +20 -19
  6. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/install_cmdstan.py +12 -13
  7. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/install_cxx_toolchain.py +5 -4
  8. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/model.py +269 -115
  9. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/progress.py +2 -0
  10. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/__init__.py +67 -58
  11. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/gq.py +58 -62
  12. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/laplace.py +51 -30
  13. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/mcmc.py +114 -98
  14. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/metadata.py +34 -6
  15. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/mle.py +52 -15
  16. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/pathfinder.py +18 -22
  17. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/runset.py +11 -11
  18. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/stanfit/vb.py +66 -17
  19. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/utils/__init__.py +6 -25
  20. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/utils/cmdstan.py +24 -16
  21. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/utils/command.py +5 -19
  22. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/utils/data_munging.py +4 -3
  23. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/utils/filesystem.py +39 -6
  24. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/utils/json.py +1 -0
  25. cmdstanpy-1.3.0/cmdstanpy/utils/logging.py +62 -0
  26. cmdstanpy-1.3.0/cmdstanpy/utils/stancsv.py +670 -0
  27. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0/cmdstanpy.egg-info}/PKG-INFO +7 -4
  28. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy.egg-info/SOURCES.txt +2 -0
  29. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy.egg-info/requires.txt +3 -1
  30. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/pyproject.toml +7 -3
  31. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_cmdstan_args.py +7 -16
  32. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_laplace.py +44 -0
  33. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_log_prob.py +1 -1
  34. cmdstanpy-1.3.0/test/test_logging.py +67 -0
  35. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_metadata.py +0 -1
  36. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_model.py +38 -2
  37. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_optimize.py +27 -0
  38. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_pathfinder.py +4 -4
  39. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_sample.py +189 -82
  40. cmdstanpy-1.3.0/test/test_stancsv.py +675 -0
  41. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_utils.py +38 -24
  42. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_variational.py +44 -0
  43. cmdstanpy-1.2.4/cmdstanpy/_version.py +0 -3
  44. cmdstanpy-1.2.4/cmdstanpy/utils/logging.py +0 -25
  45. cmdstanpy-1.2.4/cmdstanpy/utils/stancsv.py +0 -486
  46. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/LICENSE.md +0 -0
  47. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/MANIFEST.in +0 -0
  48. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/README.md +0 -0
  49. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy/py.typed +0 -0
  50. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy.egg-info/dependency_links.txt +0 -0
  51. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy.egg-info/entry_points.txt +0 -0
  52. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/cmdstanpy.egg-info/top_level.txt +0 -0
  53. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/setup.cfg +0 -0
  54. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_compiler_opts.py +0 -0
  55. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_compliance.py +0 -0
  56. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_cxx_installation.py +0 -0
  57. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_generate_quantities.py +0 -0
  58. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_install_cmdstan.py +0 -0
  59. {cmdstanpy-1.2.4 → cmdstanpy-1.3.0}/test/test_runset.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: cmdstanpy
3
- Version: 1.2.4
3
+ Version: 1.3.0
4
4
  Summary: Python interface to CmdStan
5
5
  Author: Stan Dev Team
6
6
  License: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Intended Audience :: Science/Research
14
14
  Classifier: Natural Language :: English
15
15
  Classifier: Programming Language :: Python
16
16
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
17
- Requires-Python: >=3.8
17
+ Requires-Python: >=3.9
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE.md
20
20
  Requires-Dist: pandas
@@ -23,6 +23,7 @@ Requires-Dist: tqdm
23
23
  Requires-Dist: stanio<2.0.0,>=0.4.0
24
24
  Provides-Extra: all
25
25
  Requires-Dist: xarray; extra == "all"
26
+ Requires-Dist: polars>=1.8.2; extra == "all"
26
27
  Provides-Extra: test
27
28
  Requires-Dist: flake8; extra == "test"
28
29
  Requires-Dist: pylint; extra == "test"
@@ -31,16 +32,18 @@ Requires-Dist: pytest-cov; extra == "test"
31
32
  Requires-Dist: pytest-order; extra == "test"
32
33
  Requires-Dist: mypy; extra == "test"
33
34
  Requires-Dist: xarray; extra == "test"
35
+ Requires-Dist: polars>=1.8.2; extra == "test"
34
36
  Provides-Extra: docs
35
37
  Requires-Dist: sphinx<6,>5; extra == "docs"
36
38
  Requires-Dist: pydata-sphinx-theme<0.9; extra == "docs"
37
39
  Requires-Dist: nbsphinx; extra == "docs"
38
40
  Requires-Dist: ipython; extra == "docs"
39
- Requires-Dist: ipykernel; extra == "docs"
41
+ Requires-Dist: ipykernel<7; extra == "docs"
40
42
  Requires-Dist: ipywidgets; extra == "docs"
41
43
  Requires-Dist: sphinx-copybutton; extra == "docs"
42
44
  Requires-Dist: xarray; extra == "docs"
43
45
  Requires-Dist: matplotlib; extra == "docs"
46
+ Dynamic: license-file
44
47
 
45
48
  # CmdStanPy
46
49
 
@@ -37,6 +37,8 @@ from .stanfit import (
37
37
  from .utils import (
38
38
  cmdstan_path,
39
39
  cmdstan_version,
40
+ disable_logging,
41
+ enable_logging,
40
42
  install_cmdstan,
41
43
  set_cmdstan_path,
42
44
  set_make_env,
@@ -63,4 +65,6 @@ __all__ = [
63
65
  'show_versions',
64
66
  'rebuild_cmdstan',
65
67
  'cmdstan_version',
68
+ "enable_logging",
69
+ "disable_logging",
66
70
  ]
@@ -0,0 +1,3 @@
1
+ """PyPi Version"""
2
+
3
+ __version__ = '1.3.0'
@@ -1,23 +1,16 @@
1
1
  """
2
2
  CmdStan arguments
3
3
  """
4
+
4
5
  import os
5
6
  from enum import Enum, auto
6
7
  from time import time
7
- from typing import Any, Dict, List, Mapping, Optional, Union
8
+ from typing import Any, Mapping, Optional, Union
8
9
 
9
10
  import numpy as np
10
11
  from numpy.random import default_rng
11
12
 
12
- from cmdstanpy import _TMPDIR
13
- from cmdstanpy.utils import (
14
- cmdstan_path,
15
- cmdstan_version_before,
16
- create_named_text_file,
17
- get_logger,
18
- read_metric,
19
- write_stan_json,
20
- )
13
+ from cmdstanpy.utils import cmdstan_path, cmdstan_version_before, get_logger
21
14
 
22
15
  OptionalPath = Union[str, os.PathLike, None]
23
16
 
@@ -64,10 +57,9 @@ class SamplerArgs:
64
57
  save_warmup: bool = False,
65
58
  thin: Optional[int] = None,
66
59
  max_treedepth: Optional[int] = None,
67
- metric: Union[
68
- str, Dict[str, Any], List[str], List[Dict[str, Any]], None
69
- ] = None,
70
- step_size: Union[float, List[float], None] = None,
60
+ metric_type: Optional[str] = None,
61
+ metric_file: Union[str, list[str], None] = None,
62
+ step_size: Union[float, list[float], None] = None,
71
63
  adapt_engaged: bool = True,
72
64
  adapt_delta: Optional[float] = None,
73
65
  adapt_init_phase: Optional[int] = None,
@@ -82,9 +74,8 @@ class SamplerArgs:
82
74
  self.save_warmup = save_warmup
83
75
  self.thin = thin
84
76
  self.max_treedepth = max_treedepth
85
- self.metric = metric
86
- self.metric_type: Optional[str] = None
87
- self.metric_file: Union[str, List[str], None] = None
77
+ self.metric_type: Optional[str] = metric_type
78
+ self.metric_file: Union[str, list[str], None] = metric_file
88
79
  self.step_size = step_size
89
80
  self.adapt_engaged = adapt_engaged
90
81
  self.adapt_delta = adapt_delta
@@ -161,8 +152,9 @@ class SamplerArgs:
161
152
  ):
162
153
  if self.step_size <= 0:
163
154
  raise ValueError(
164
- 'Argument "step_size" must be > 0, '
165
- 'found {}.'.format(self.step_size)
155
+ 'Argument "step_size" must be > 0, found {}.'.format(
156
+ self.step_size
157
+ )
166
158
  )
167
159
  else:
168
160
  if len(self.step_size) != chains:
@@ -176,124 +168,15 @@ class SamplerArgs:
176
168
  'Argument "step_size" must be > 0, '
177
169
  'chain {}, found {}.'.format(i + 1, step_size)
178
170
  )
179
- if self.metric is not None:
180
- if isinstance(self.metric, str):
181
- if self.metric in ['diag', 'diag_e']:
182
- self.metric_type = 'diag_e'
183
- elif self.metric in ['dense', 'dense_e']:
184
- self.metric_type = 'dense_e'
185
- elif self.metric in ['unit', 'unit_e']:
186
- self.metric_type = 'unit_e'
187
- else:
188
- if not os.path.exists(self.metric):
189
- raise ValueError('no such file {}'.format(self.metric))
190
- dims = read_metric(self.metric)
191
- if len(dims) == 1:
192
- self.metric_type = 'diag_e'
193
- else:
194
- self.metric_type = 'dense_e'
195
- self.metric_file = self.metric
196
- elif isinstance(self.metric, dict):
197
- if 'inv_metric' not in self.metric:
198
- raise ValueError(
199
- 'Entry "inv_metric" not found in metric dict.'
200
- )
201
- dims = list(np.asarray(self.metric['inv_metric']).shape)
202
- if len(dims) == 1:
203
- self.metric_type = 'diag_e'
204
- else:
205
- self.metric_type = 'dense_e'
206
- dict_file = create_named_text_file(
207
- dir=_TMPDIR, prefix="metric", suffix=".json"
208
- )
209
- write_stan_json(dict_file, self.metric)
210
- self.metric_file = dict_file
211
- elif isinstance(self.metric, (list, tuple)):
212
- if len(self.metric) != chains:
213
- raise ValueError(
214
- 'Number of metric files must match number of chains,'
215
- ' found {} metric files for {} chains.'.format(
216
- len(self.metric), chains
217
- )
218
- )
219
- if all(isinstance(elem, dict) for elem in self.metric):
220
- metric_files: List[str] = []
221
- for i, metric in enumerate(self.metric):
222
- metric_dict: Dict[str, Any] = metric # type: ignore
223
- if 'inv_metric' not in metric_dict:
224
- raise ValueError(
225
- 'Entry "inv_metric" not found in metric dict '
226
- 'for chain {}.'.format(i + 1)
227
- )
228
- if i == 0:
229
- dims = list(
230
- np.asarray(metric_dict['inv_metric']).shape
231
- )
232
- else:
233
- dims2 = list(
234
- np.asarray(metric_dict['inv_metric']).shape
235
- )
236
- if dims != dims2:
237
- raise ValueError(
238
- 'Found inconsistent "inv_metric" entry '
239
- 'for chain {}: entry has dims '
240
- '{}, expected {}.'.format(
241
- i + 1, dims, dims2
242
- )
243
- )
244
- dict_file = create_named_text_file(
245
- dir=_TMPDIR, prefix="metric", suffix=".json"
246
- )
247
- write_stan_json(dict_file, metric_dict)
248
- metric_files.append(dict_file)
249
- if len(dims) == 1:
250
- self.metric_type = 'diag_e'
251
- else:
252
- self.metric_type = 'dense_e'
253
- self.metric_file = metric_files
254
- elif all(isinstance(elem, str) for elem in self.metric):
255
- metric_files = []
256
- for i, metric in enumerate(self.metric):
257
- assert isinstance(metric, str) # typecheck
258
- if not os.path.exists(metric):
259
- raise ValueError('no such file {}'.format(metric))
260
- if i == 0:
261
- dims = read_metric(metric)
262
- else:
263
- dims2 = read_metric(metric)
264
- if len(dims) != len(dims2):
265
- raise ValueError(
266
- 'Metrics files {}, {},'
267
- ' inconsistent metrics'.format(
268
- self.metric[0], metric
269
- )
270
- )
271
- if dims != dims2:
272
- raise ValueError(
273
- 'Metrics files {}, {},'
274
- ' inconsistent metrics'.format(
275
- self.metric[0], metric
276
- )
277
- )
278
- metric_files.append(metric)
279
- if len(dims) == 1:
280
- self.metric_type = 'diag_e'
281
- else:
282
- self.metric_type = 'dense_e'
283
- self.metric_file = metric_files
284
- else:
285
- raise ValueError(
286
- 'Argument "metric" must be a list of pathnames or '
287
- 'Python dicts, found list of {}.'.format(
288
- type(self.metric[0])
289
- )
290
- )
291
- else:
171
+ if self.metric_type is not None:
172
+ if self.metric_type in ['diag', 'dense', 'unit']:
173
+ self.metric_type += '_e'
174
+ if self.metric_type not in ['diag_e', 'dense_e', 'unit_e']:
292
175
  raise ValueError(
293
- 'Invalid metric specified, not a recognized metric type, '
294
- 'must be either a metric type name, a filepath, dict, '
295
- 'or list of per-chain filepaths or dicts. Found '
296
- 'an object of type {}.'.format(type(self.metric))
176
+ 'Argument "metric" must be one of [diag, dense, unit,'
177
+ ' diag_e, dense_e, unit_e], found {}.'.format(
178
+ self.metric_type
179
+ )
297
180
  )
298
181
 
299
182
  if self.adapt_delta is not None:
@@ -330,7 +213,8 @@ class SamplerArgs:
330
213
 
331
214
  if self.fixed_param and (
332
215
  self.max_treedepth is not None
333
- or self.metric is not None
216
+ or self.metric_type is not None
217
+ or self.metric_file is not None
334
218
  or self.step_size is not None
335
219
  or not (
336
220
  self.adapt_delta is None
@@ -343,7 +227,7 @@ class SamplerArgs:
343
227
  'When fixed_param=True, cannot specify adaptation parameters.'
344
228
  )
345
229
 
346
- def compose(self, idx: int, cmd: List[str]) -> List[str]:
230
+ def compose(self, idx: int, cmd: list[str]) -> list[str]:
347
231
  """
348
232
  Compose CmdStan command for method-specific non-default arguments.
349
233
  """
@@ -369,7 +253,7 @@ class SamplerArgs:
369
253
  cmd.append(f'stepsize={self.step_size}')
370
254
  else:
371
255
  cmd.append(f'stepsize={self.step_size[idx]}')
372
- if self.metric is not None:
256
+ if self.metric_type is not None:
373
257
  cmd.append(f'metric={self.metric_type}')
374
258
  if self.metric_file is not None:
375
259
  if not isinstance(self.metric_file, list):
@@ -467,7 +351,7 @@ class OptimizeArgs:
467
351
  positive_float(self.tol_param, 'tol_param')
468
352
  positive_int(self.history_size, 'history_size')
469
353
 
470
- def compose(self, _idx: int, cmd: List[str]) -> List[str]:
354
+ def compose(self, _idx: int, cmd: list[str]) -> list[str]:
471
355
  """compose command string for CmdStan for non-default arg values."""
472
356
  cmd.append('method=optimize')
473
357
  if self.algorithm:
@@ -511,7 +395,7 @@ class LaplaceArgs:
511
395
  raise ValueError(f'Invalid path for mode file: {self.mode}')
512
396
  positive_int(self.draws, 'draws')
513
397
 
514
- def compose(self, _idx: int, cmd: List[str]) -> List[str]:
398
+ def compose(self, _idx: int, cmd: list[str]) -> list[str]:
515
399
  """compose command string for CmdStan for non-default arg values."""
516
400
  cmd.append('method=laplace')
517
401
  cmd.append(f'mode={self.mode}')
@@ -579,7 +463,7 @@ class PathfinderArgs:
579
463
  positive_int(self.num_draws, 'num_draws')
580
464
  positive_int(self.num_elbo_draws, 'num_elbo_draws')
581
465
 
582
- def compose(self, _idx: int, cmd: List[str]) -> List[str]:
466
+ def compose(self, _idx: int, cmd: list[str]) -> list[str]:
583
467
  """compose command string for CmdStan for non-default arg values."""
584
468
  cmd.append('method=pathfinder')
585
469
 
@@ -624,12 +508,13 @@ class PathfinderArgs:
624
508
  class GenerateQuantitiesArgs:
625
509
  """Arguments needed for generate_quantities method."""
626
510
 
627
- def __init__(self, csv_files: List[str]) -> None:
511
+ def __init__(self, csv_files: list[str]) -> None:
628
512
  """Initialize object."""
629
513
  self.sample_csv_files = csv_files
630
514
 
631
515
  def validate(
632
- self, chains: Optional[int] = None # pylint: disable=unused-argument
516
+ self,
517
+ chains: Optional[int] = None, # pylint: disable=unused-argument
633
518
  ) -> None:
634
519
  """
635
520
  Check arguments correctness and consistency.
@@ -642,7 +527,7 @@ class GenerateQuantitiesArgs:
642
527
  'Invalid path for sample csv file: {}'.format(csv)
643
528
  )
644
529
 
645
- def compose(self, idx: int, cmd: List[str]) -> List[str]:
530
+ def compose(self, idx: int, cmd: list[str]) -> list[str]:
646
531
  """
647
532
  Compose CmdStan command for method-specific non-default arguments.
648
533
  """
@@ -681,7 +566,8 @@ class VariationalArgs:
681
566
  self.output_samples = output_samples
682
567
 
683
568
  def validate(
684
- self, chains: Optional[int] = None # pylint: disable=unused-argument
569
+ self,
570
+ chains: Optional[int] = None, # pylint: disable=unused-argument
685
571
  ) -> None:
686
572
  """
687
573
  Check arguments correctness and consistency.
@@ -705,7 +591,7 @@ class VariationalArgs:
705
591
  positive_int(self.output_samples, 'output_samples')
706
592
 
707
593
  # pylint: disable=unused-argument
708
- def compose(self, idx: int, cmd: List[str]) -> List[str]:
594
+ def compose(self, idx: int, cmd: list[str]) -> list[str]:
709
595
  """
710
596
  Compose CmdStan command for method-specific non-default arguments.
711
597
  """
@@ -747,7 +633,7 @@ class CmdStanArgs:
747
633
  self,
748
634
  model_name: str,
749
635
  model_exe: OptionalPath,
750
- chain_ids: Optional[List[int]],
636
+ chain_ids: Optional[list[int]],
751
637
  method_args: Union[
752
638
  SamplerArgs,
753
639
  OptimizeArgs,
@@ -757,8 +643,8 @@ class CmdStanArgs:
757
643
  PathfinderArgs,
758
644
  ],
759
645
  data: Union[Mapping[str, Any], str, None] = None,
760
- seed: Union[int, List[int], None] = None,
761
- inits: Union[int, float, str, List[str], None] = None,
646
+ seed: Union[int, list[int], None] = None,
647
+ inits: Union[int, float, str, list[str], None] = None,
762
648
  output_dir: OptionalPath = None,
763
649
  sig_figs: Optional[int] = None,
764
650
  save_latent_dynamics: bool = False,
@@ -959,11 +845,11 @@ class CmdStanArgs:
959
845
  *,
960
846
  diagnostic_file: Optional[str] = None,
961
847
  profile_file: Optional[str] = None,
962
- ) -> List[str]:
848
+ ) -> list[str]:
963
849
  """
964
850
  Compose CmdStan command for non-default arguments.
965
851
  """
966
- cmd: List[str] = []
852
+ cmd: list[str] = []
967
853
  if idx is not None and self.chain_ids is not None:
968
854
  if idx < 0 or idx > len(self.chain_ids) - 1:
969
855
  raise ValueError(
@@ -11,7 +11,7 @@ import subprocess
11
11
  from copy import copy
12
12
  from datetime import datetime
13
13
  from pathlib import Path
14
- from typing import Any, Dict, Iterable, List, Optional, Union
14
+ from typing import Any, Iterable, Optional, Union
15
15
 
16
16
  from cmdstanpy.utils import get_logger
17
17
  from cmdstanpy.utils.cmdstan import (
@@ -19,6 +19,7 @@ from cmdstanpy.utils.cmdstan import (
19
19
  cmdstan_path,
20
20
  cmdstan_version,
21
21
  cmdstan_version_before,
22
+ stanc_path,
22
23
  )
23
24
  from cmdstanpy.utils.command import do_command
24
25
  from cmdstanpy.utils.filesystem import SanitizedOrTmpFilePath
@@ -80,8 +81,8 @@ class CompilerOptions:
80
81
  def __init__(
81
82
  self,
82
83
  *,
83
- stanc_options: Optional[Dict[str, Any]] = None,
84
- cpp_options: Optional[Dict[str, Any]] = None,
84
+ stanc_options: Optional[dict[str, Any]] = None,
85
+ cpp_options: Optional[dict[str, Any]] = None,
85
86
  user_header: OptionalPath = None,
86
87
  ) -> None:
87
88
  """Initialize object."""
@@ -115,12 +116,12 @@ class CompilerOptions:
115
116
  )
116
117
 
117
118
  @property
118
- def stanc_options(self) -> Dict[str, Union[bool, int, str, Iterable[str]]]:
119
+ def stanc_options(self) -> dict[str, Union[bool, int, str, Iterable[str]]]:
119
120
  """Stanc compiler options."""
120
121
  return self._stanc_options
121
122
 
122
123
  @property
123
- def cpp_options(self) -> Dict[str, Union[bool, int]]:
124
+ def cpp_options(self) -> dict[str, Union[bool, int]]:
124
125
  """C++ compiler options."""
125
126
  return self._cpp_options
126
127
 
@@ -164,8 +165,8 @@ class CompilerOptions:
164
165
  del self._stanc_options[deprecated]
165
166
  else:
166
167
  get_logger().warning(
167
- 'compiler option "%s" is deprecated and '
168
- 'should not be used',
168
+ 'compiler option "%s" is deprecated and should '
169
+ 'not be used',
169
170
  deprecated,
170
171
  )
171
172
  for key, val in self._stanc_options.items():
@@ -224,8 +225,8 @@ class CompilerOptions:
224
225
  val = self._cpp_options[key]
225
226
  if not isinstance(val, int) or val < 0:
226
227
  raise ValueError(
227
- f'{key} must be a non-negative integer value,'
228
- f' found {val}.'
228
+ f'{key} must be a non-negative integer '
229
+ f'value, found {val}.'
229
230
  )
230
231
 
231
232
  def validate_user_header(self) -> None:
@@ -297,7 +298,7 @@ class CompilerOptions:
297
298
  elif path not in self._stanc_options['include-paths']:
298
299
  self._stanc_options['include-paths'].append(path)
299
300
 
300
- def compose_stanc(self, filename_in_msg: Optional[str]) -> List[str]:
301
+ def compose_stanc(self, filename_in_msg: Optional[str]) -> list[str]:
301
302
  opts = []
302
303
 
303
304
  if filename_in_msg is not None:
@@ -321,7 +322,7 @@ class CompilerOptions:
321
322
  opts.append(f'--{key}')
322
323
  return opts
323
324
 
324
- def compose(self, filename_in_msg: Optional[str] = None) -> List[str]:
325
+ def compose(self, filename_in_msg: Optional[str] = None) -> list[str]:
325
326
  """
326
327
  Format makefile options as list of strings.
327
328
 
@@ -343,7 +344,7 @@ class CompilerOptions:
343
344
 
344
345
  def src_info(
345
346
  stan_file: str, compiler_options: CompilerOptions
346
- ) -> Dict[str, Any]:
347
+ ) -> dict[str, Any]:
347
348
  """
348
349
  Get source info for Stan program file.
349
350
 
@@ -351,7 +352,7 @@ def src_info(
351
352
  :meth:`CmdStanModel.src_info`, and should not be called directly.
352
353
  """
353
354
  cmd = (
354
- [os.path.join(cmdstan_path(), 'bin', 'stanc' + EXTENSION)]
355
+ [stanc_path()]
355
356
  # handle include-paths, allow-undefined etc
356
357
  + compiler_options.compose_stanc(None)
357
358
  + ['--info', str(stan_file)]
@@ -362,15 +363,15 @@ def src_info(
362
363
  f"Failed to get source info for Stan model "
363
364
  f"'{stan_file}'. Console:\n{proc.stderr}"
364
365
  )
365
- result: Dict[str, Any] = json.loads(proc.stdout)
366
+ result: dict[str, Any] = json.loads(proc.stdout)
366
367
  return result
367
368
 
368
369
 
369
370
  def compile_stan_file(
370
371
  src: Union[str, Path],
371
372
  force: bool = False,
372
- stanc_options: Optional[Dict[str, Any]] = None,
373
- cpp_options: Optional[Dict[str, Any]] = None,
373
+ stanc_options: Optional[dict[str, Any]] = None,
374
+ cpp_options: Optional[dict[str, Any]] = None,
374
375
  user_header: OptionalPath = None,
375
376
  ) -> str:
376
377
  """
@@ -479,7 +480,7 @@ def compile_stan_file(
479
480
  "If the issue persists please open a bug report"
480
481
  )
481
482
  raise ValueError(
482
- f"Failed to compile Stan model '{src}'. " f"Console:\n{console}"
483
+ f"Failed to compile Stan model '{src}'. Console:\n{console}"
483
484
  )
484
485
  return str(exe_target)
485
486
 
@@ -491,7 +492,7 @@ def format_stan_file(
491
492
  canonicalize: Union[bool, str, Iterable[str]] = False,
492
493
  max_line_length: int = 78,
493
494
  backup: bool = True,
494
- stanc_options: Optional[Dict[str, Any]] = None,
495
+ stanc_options: Optional[dict[str, Any]] = None,
495
496
  ) -> None:
496
497
  """
497
498
  Run stanc's auto-formatter on the model code. Either saves directly
@@ -518,7 +519,7 @@ def format_stan_file(
518
519
 
519
520
  try:
520
521
  cmd = (
521
- [os.path.join(cmdstan_path(), 'bin', 'stanc' + EXTENSION)]
522
+ [stanc_path()]
522
523
  # handle include-paths, allow-undefined etc
523
524
  + CompilerOptions(stanc_options=stanc_options).compose_stanc(None)
524
525
  + [str(stan_file)]
@@ -17,6 +17,7 @@ Optional command line arguments:
17
17
  --cores: int, number of cores to use when building, defaults to 1
18
18
  -c, --compiler : flag, add C++ compiler to path (Windows only)
19
19
  """
20
+
20
21
  import argparse
21
22
  import json
22
23
  import os
@@ -30,7 +31,7 @@ import urllib.request
30
31
  from collections import OrderedDict
31
32
  from pathlib import Path
32
33
  from time import sleep
33
- from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union
34
+ from typing import TYPE_CHECKING, Any, Callable, Optional, Union
34
35
 
35
36
  from tqdm.auto import tqdm
36
37
 
@@ -85,7 +86,7 @@ MAKE = os.getenv('MAKE', 'make' if not is_windows() else 'mingw32-make')
85
86
  EXTENSION = '.exe' if is_windows() else ''
86
87
 
87
88
 
88
- def get_headers() -> Dict[str, str]:
89
+ def get_headers() -> dict[str, str]:
89
90
  """Create headers dictionary."""
90
91
  headers = {}
91
92
  GITHUB_PAT = os.environ.get("GITHUB_PAT") # pylint:disable=invalid-name
@@ -287,13 +288,13 @@ def build(verbose: bool = False, progress: bool = True, cores: int = 1) -> None:
287
288
  raise CmdStanInstallError(f'Command "make build" failed\n{str(e)}')
288
289
  if not os.path.exists(os.path.join('bin', 'stansummary' + EXTENSION)):
289
290
  raise CmdStanInstallError(
290
- f'bin/stansummary{EXTENSION} not found'
291
- ', please rebuild or report a bug!'
291
+ f'bin/stansummary{EXTENSION} not found, please rebuild or '
292
+ 'report a bug!'
292
293
  )
293
294
  if not os.path.exists(os.path.join('bin', 'diagnose' + EXTENSION)):
294
295
  raise CmdStanInstallError(
295
- f'bin/stansummary{EXTENSION} not found'
296
- ', please rebuild or report a bug!'
296
+ f'bin/stansummary{EXTENSION} not found, please rebuild or '
297
+ 'report a bug!'
297
298
  )
298
299
 
299
300
  if is_windows():
@@ -417,8 +418,8 @@ def install_version(
417
418
  )
418
419
  if overwrite and os.path.exists('.'):
419
420
  print(
420
- 'Overwrite requested, remove existing build of version '
421
- '{}'.format(cmdstan_version)
421
+ 'Overwrite requested, remove existing build '
422
+ 'of version {}'.format(cmdstan_version)
422
423
  )
423
424
  clean_all(verbose)
424
425
  print('Rebuilding version {}'.format(cmdstan_version))
@@ -496,10 +497,8 @@ def retrieve_version(version: str, progress: bool = True) -> None:
496
497
  break
497
498
  except urllib.error.HTTPError as e:
498
499
  raise CmdStanRetrieveError(
499
- 'HTTPError: {}\n'
500
- 'Version {} not available from github.com.'.format(
501
- e.code, version
502
- )
500
+ 'HTTPError: {}\nVersion {} not available from '
501
+ 'github.com.'.format(e.code, version)
503
502
  ) from e
504
503
  except urllib.error.URLError as e:
505
504
  print(
@@ -645,7 +644,7 @@ def run_install(args: Union[InteractiveSettings, InstallationSettings]) -> None:
645
644
  compile_example(args.verbose)
646
645
 
647
646
 
648
- def parse_cmdline_args() -> Dict[str, Any]:
647
+ def parse_cmdline_args() -> dict[str, Any]:
649
648
  parser = argparse.ArgumentParser("install_cmdstan")
650
649
  parser.add_argument(
651
650
  '--interactive',
@@ -12,6 +12,7 @@ Optional command line arguments:
12
12
  -m --no-make : don't install mingw32-make (Windows RTools 4.0 only)
13
13
  --progress : flag, when specified show progress bar for RTools download
14
14
  """
15
+
15
16
  import argparse
16
17
  import os
17
18
  import platform
@@ -21,7 +22,7 @@ import sys
21
22
  import urllib.request
22
23
  from collections import OrderedDict
23
24
  from time import sleep
24
- from typing import Any, Dict, List
25
+ from typing import Any
25
26
 
26
27
  from cmdstanpy import _DOT_CMDSTAN
27
28
  from cmdstanpy.utils import pushd, validate_dir, wrap_url_progress_hook
@@ -44,7 +45,7 @@ def usage() -> None:
44
45
  )
45
46
 
46
47
 
47
- def get_config(dir: str, silent: bool) -> List[str]:
48
+ def get_config(dir: str, silent: bool) -> list[str]:
48
49
  """Assemble config info."""
49
50
  config = []
50
51
  if platform.system() == 'Windows':
@@ -260,7 +261,7 @@ def get_toolchain_version(name: str, version: str) -> str:
260
261
  return toolchain_folder
261
262
 
262
263
 
263
- def run_rtools_install(args: Dict[str, Any]) -> None:
264
+ def run_rtools_install(args: dict[str, Any]) -> None:
264
265
  """Main."""
265
266
  if platform.system() not in {'Windows'}:
266
267
  raise NotImplementedError(
@@ -333,7 +334,7 @@ def run_rtools_install(args: Dict[str, Any]) -> None:
333
334
  install_mingw32_make(toolchain_folder, verbose)
334
335
 
335
336
 
336
- def parse_cmdline_args() -> Dict[str, Any]:
337
+ def parse_cmdline_args() -> dict[str, Any]:
337
338
  parser = argparse.ArgumentParser()
338
339
  parser.add_argument('--version', '-v', help="version, defaults to latest")
339
340
  parser.add_argument(