Skip to content

Commit

Permalink
Merge branch 'develop' into ci/try-numpy-2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
WardBrian committed Mar 21, 2024
2 parents 5212996 + 742b409 commit 7d88896
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 217 deletions.
37 changes: 10 additions & 27 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,16 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies (python)
- name: Install dependencies
run: python -m pip install --upgrade pip wheel build

- name: Build package
run: |
python -m pip install --upgrade pip wheel build
pip install -r requirements.txt
pip install -r requirements-test.txt
pip install codecov
python -m build
python -m pip install .[test]
- name: Show libraries
run: python -m pip freeze

- name: Install numpy, scipy nightlies
if: matrix.python-version == '3.11' && matrix.os != 'windows-latest'
Expand All @@ -75,22 +79,6 @@ jobs:
pylint -v cmdstanpy test
mypy cmdstanpy
- name: Build wheel
run: python -m build

- name: Install wheel (Linux, macOS)
if: matrix.os != 'windows-latest'
run: pip install dist/*.whl

- name: Install wheel (Windows)
if: matrix.os == 'windows-latest'
run: |
$whl = Get-ChildItem -Path dist -Filter *.whl | Select-Object -First 1
pip install "$whl"
- name: Show libraries
run: python -m pip freeze

- name: CmdStan installation cacheing
id: cache-cmdstan
if: ${{ !startswith(needs.get-cmdstan-version.outputs.version, 'git:') }}
Expand Down Expand Up @@ -123,13 +111,8 @@ jobs:
cd run_tests
pytest -v ../test --cov=../cmdstanpy
- name: Run model with requirements-optional.txt
run: |
cd run_tests
python -m pip install -r ../requirements-optional.txt
python ../test/example_script.py
- name: Submit codecov
run: |
pip install codecov
cd run_tests
codecov
7 changes: 3 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ jobs:
- name: Install dependencies (python)
run: |
python -m pip install --upgrade pip wheel twine codecov "sphinx>5,<6" nbsphinx ipython ipykernel "pydata-sphinx-theme<0.9" requests sphinx-copybutton xarray matplotlib
pip install -r requirements.txt
pip install -e .
python -m pip install --upgrade pip wheel build twine requests
pip install -e .[doc,test]
- name: Install CmdStan
run: |
Expand Down Expand Up @@ -80,7 +79,7 @@ jobs:
git push -f origin master
- name: Build wheel
run: python setup.py sdist bdist_wheel
run: python -m build

- name: Install bdist_wheel
run: pip install dist/*.whl
Expand Down
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ repos:
rev: v1.5.0
hooks:
- id: mypy
# Copied from setup.cfg
exclude: ^test/
additional_dependencies: [ numpy >= 1.22]
# local uses the user-installed pylint, this allows dependency checking
Expand Down
2 changes: 0 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# This file is used to include extra files in source distributions.
# (Source distributions are generated by running `python setup.py sdist`.)

include requirements*.txt
include LICENSE.md
include cmdstanpy/py.typed
3 changes: 2 additions & 1 deletion cmdstanpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def _cleanup_tmpdir() -> None:


from ._version import __version__ # noqa
from .compilation import compile_stan_file
from .compilation import compile_stan_file, format_stan_file
from .install_cmdstan import rebuild_cmdstan
from .model import CmdStanModel
from .stanfit import (
Expand Down Expand Up @@ -50,6 +50,7 @@ def _cleanup_tmpdir() -> None:
'set_make_env',
'install_cmdstan',
'compile_stan_file',
'format_stan_file',
'CmdStanMCMC',
'CmdStanMLE',
'CmdStanGQ',
Expand Down
103 changes: 102 additions & 1 deletion cmdstanpy/compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@
import shutil
import subprocess
from copy import copy
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional, Union

from cmdstanpy.utils import get_logger
from cmdstanpy.utils.cmdstan import EXTENSION, cmdstan_path
from cmdstanpy.utils.cmdstan import (
EXTENSION,
cmdstan_path,
cmdstan_version,
cmdstan_version_before,
)
from cmdstanpy.utils.command import do_command
from cmdstanpy.utils.filesystem import SanitizedOrTmpFilePath

Expand Down Expand Up @@ -476,3 +482,98 @@ def compile_stan_file(
f"Failed to compile Stan model '{src}'. " f"Console:\n{console}"
)
return str(exe_target)


def format_stan_file(
stan_file: Union[str, os.PathLike],
*,
overwrite_file: bool = False,
canonicalize: Union[bool, str, Iterable[str]] = False,
max_line_length: int = 78,
backup: bool = True,
stanc_options: Optional[Dict[str, Any]] = None,
) -> None:
"""
Run stanc's auto-formatter on the model code. Either saves directly
back to the file or prints for inspection
:param stan_file: Path to Stan program file.
:param overwrite_file: If True, save the updated code to disk, rather
than printing it. By default False
:param canonicalize: Whether or not the compiler should 'canonicalize'
the Stan model, removing things like deprecated syntax. Default is
False. If True, all canonicalizations are run. If it is a list of
strings, those options are passed to stanc (new in Stan 2.29)
:param max_line_length: Set the wrapping point for the formatter. The
default value is 78, which wraps most lines by the 80th character.
:param backup: If True, create a stanfile.bak backup before
writing to the file. Only disable this if you're sure you have other
copies of the file or are using a version control system like Git.
:param stanc_options: Additional options to pass to the stanc compiler.
"""
stan_file = Path(stan_file).resolve()

if not stan_file.exists():
raise ValueError(f'File does not exist: {stan_file}')

try:
cmd = (
[os.path.join(cmdstan_path(), 'bin', 'stanc' + EXTENSION)]
# handle include-paths, allow-undefined etc
+ CompilerOptions(stanc_options=stanc_options).compose_stanc(None)
+ [str(stan_file)]
)

if canonicalize:
if cmdstan_version_before(2, 29):
if isinstance(canonicalize, bool):
cmd.append('--print-canonical')
else:
raise ValueError(
"Invalid arguments passed for current CmdStan"
+ " version({})\n".format(
cmdstan_version() or "Unknown"
)
+ "--canonicalize requires 2.29 or higher"
)
else:
if isinstance(canonicalize, str):
cmd.append('--canonicalize=' + canonicalize)
elif isinstance(canonicalize, Iterable):
cmd.append('--canonicalize=' + ','.join(canonicalize))
else:
cmd.append('--print-canonical')

# before 2.29, having both --print-canonical
# and --auto-format printed twice
if not (cmdstan_version_before(2, 29) and canonicalize):
cmd.append('--auto-format')

if not cmdstan_version_before(2, 29):
cmd.append(f'--max-line-length={max_line_length}')
elif max_line_length != 78:
raise ValueError(
"Invalid arguments passed for current CmdStan version"
+ " ({})\n".format(cmdstan_version() or "Unknown")
+ "--max-line-length requires 2.29 or higher"
)

out = subprocess.run(cmd, capture_output=True, text=True, check=True)
if out.stderr:
get_logger().warning(out.stderr)
result = out.stdout
if overwrite_file:
if result:
if backup:
shutil.copyfile(
stan_file,
str(stan_file)
+ '.bak-'
+ datetime.now().strftime("%Y%m%d%H%M%S"),
)
stan_file.write_text(result)
else:
print(result)

except (ValueError, RuntimeError) as e:
raise RuntimeError("Stanc formatting failed") from e
84 changes: 18 additions & 66 deletions cmdstanpy/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import threading
from collections import OrderedDict
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from io import StringIO
from multiprocessing import cpu_count
from typing import (
Expand Down Expand Up @@ -57,9 +56,7 @@
from_csv,
)
from cmdstanpy.utils import (
EXTENSION,
cmdstan_path,
cmdstan_version,
cmdstan_version_before,
do_command,
get_logger,
Expand Down Expand Up @@ -320,6 +317,7 @@ def src_info(self) -> Dict[str, Any]:
return {}
return compilation.src_info(str(self.stan_file), self._compiler_options)

# TODO(2.0) remove
def format(
self,
overwrite_file: bool = False,
Expand All @@ -329,6 +327,8 @@ def format(
backup: bool = True,
) -> None:
"""
Deprecated: Use :func:`cmdstanpy.format_stan_file()` instead.
Run stanc's auto-formatter on the model code. Either saves directly
back to the file or prints for inspection
Expand All @@ -345,72 +345,24 @@ def format(
writing to the file. Only disable this if you're sure you have other
copies of the file or are using a version control system like Git.
"""
if self.stan_file is None or not os.path.isfile(self.stan_file):
raise ValueError("No Stan file found for this module")
try:
cmd = (
[os.path.join(cmdstan_path(), 'bin', 'stanc' + EXTENSION)]
# handle include-paths, allow-undefined etc
+ self._compiler_options.compose_stanc(None)
+ [str(self.stan_file)]
)

if canonicalize:
if cmdstan_version_before(2, 29):
if isinstance(canonicalize, bool):
cmd.append('--print-canonical')
else:
raise ValueError(
"Invalid arguments passed for current CmdStan"
+ " version({})\n".format(
cmdstan_version() or "Unknown"
)
+ "--canonicalize requires 2.29 or higher"
)
else:
if isinstance(canonicalize, str):
cmd.append('--canonicalize=' + canonicalize)
elif isinstance(canonicalize, Iterable):
cmd.append('--canonicalize=' + ','.join(canonicalize))
else:
cmd.append('--print-canonical')

# before 2.29, having both --print-canonical
# and --auto-format printed twice
if not (cmdstan_version_before(2, 29) and canonicalize):
cmd.append('--auto-format')

if not cmdstan_version_before(2, 29):
cmd.append(f'--max-line-length={max_line_length}')
elif max_line_length != 78:
raise ValueError(
"Invalid arguments passed for current CmdStan version"
+ " ({})\n".format(cmdstan_version() or "Unknown")
+ "--max-line-length requires 2.29 or higher"
)
get_logger().warning(
"CmdStanModel.format() is deprecated and will be "
"removed in the next major version.\n"
"Use cmdstanpy.format_stan_file() instead."
)

out = subprocess.run(
cmd, capture_output=True, text=True, check=True
)
if out.stderr:
get_logger().warning(out.stderr)
result = out.stdout
if overwrite_file:
if result:
if backup:
shutil.copyfile(
self.stan_file,
str(self.stan_file)
+ '.bak-'
+ datetime.now().strftime("%Y%m%d%H%M%S"),
)
with open(self.stan_file, 'w') as file_handle:
file_handle.write(result)
else:
print(result)
if self.stan_file is None:
raise ValueError("No Stan file found for this module")

except (ValueError, RuntimeError) as e:
raise RuntimeError("Stanc formatting failed") from e
compilation.format_stan_file(
self.stan_file,
overwrite_file=overwrite_file,
max_line_length=max_line_length,
canonicalize=canonicalize,
backup=backup,
stanc_options=self.stanc_options,
)

@property
def stanc_options(self) -> Dict[str, Union[bool, int, str]]:
Expand Down
11 changes: 11 additions & 0 deletions docsrc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ CmdStanGQ
Functions
*********

compile_stan_model
=============

.. autofunction:: cmdstanpy.compile_stan_model


format_stan_model
=============

.. autofunction:: cmdstanpy.format_stan_model

show_versions
=============

Expand Down
10 changes: 2 additions & 8 deletions docsrc/env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,12 @@ channels:
dependencies:
- python=3.9
- ipykernel
- ipython
- ipywidgets
- numpy>=1.15
- numpy
- pandas
- xarray
- sphinx>5,<6
- nbsphinx
- pydata-sphinx-theme>0.7,<0.9
- sphinx-copybutton
- matplotlib-base
- pip
- cmdstan
- tqdm
- pip:
- -e ../
- -e ../.[docs]
Loading

0 comments on commit 7d88896

Please sign in to comment.