From 3d2b2cb8b39018ed920361be8909718e9e278686 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:47:34 -0700 Subject: [PATCH 01/30] remove the manifest file --- MANIFEST.in | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 5c3f617..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -include .pre-commit-config.yaml -include README.rst -include noxfile.py -include pyproject.toml -include requirements.in - -recursive-include requirements *.md *.txt -recursive-include src/bmipy *.typed -recursive-include tests *.py From 6b478df4b6797f6f877bc82bfaed6a23e908882d Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:47:45 -0700 Subject: [PATCH 02/30] rename test files to have _test suffix --- tests/{test_bmipy.py => bmipy_test.py} | 0 tests/{test_cli.py => cli_test.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/{test_bmipy.py => bmipy_test.py} (100%) rename tests/{test_cli.py => cli_test.py} (100%) diff --git a/tests/test_bmipy.py b/tests/bmipy_test.py similarity index 100% rename from tests/test_bmipy.py rename to tests/bmipy_test.py diff --git a/tests/test_cli.py b/tests/cli_test.py similarity index 100% rename from tests/test_cli.py rename to tests/cli_test.py From cf8c6860095b1122ecba90be939151141023a2a3 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:48:21 -0700 Subject: [PATCH 03/30] clean up the gitignore file --- .gitignore | 106 ++--------------------------------------------------- 1 file changed, 4 insertions(+), 102 deletions(-) diff --git a/.gitignore b/.gitignore index 951397c..edfe276 100644 --- a/.gitignore +++ b/.gitignore @@ -1,105 +1,7 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ +*.egg-info/ *.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python +.coverage +.nox/ +__pycache__/ build/ -develop-eggs/ dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.nox -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ From b04b0837bce6daebf103cd4699792307c8ecf96d Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:48:58 -0700 Subject: [PATCH 04/30] use python 3.12, drop python 3.9 --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ec14ef7..b106b80 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 @@ -51,7 +51,7 @@ jobs: - uses: actions/checkout@v4 - uses: wntrblm/nox@2023.04.22 with: - python-versions: "3.9" + python-versions: "3.12" - name: Lint run: nox --non-interactive --error-on-missing-interpreter --session "lint" From 561bd322f655ec7f571c0ed83e7e22d47ceadd0b Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:49:35 -0700 Subject: [PATCH 05/30] update hooks; add a couple nwe ones --- .pre-commit-config.yaml | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 39861e7..9ea1861 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.12.1 hooks: - id: black name: black @@ -23,7 +23,7 @@ repos: additional_dependencies: [".[jupyter]"] - repo: https://github.com/pycqa/flake8 - rev: 6.0.0 + rev: 7.0.0 hooks: - id: flake8 additional_dependencies: @@ -42,19 +42,19 @@ repos: language: python - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.15.0 hooks: - id: pyupgrade - args: [--py39-plus] + args: [--py310-plus] -- repo: https://github.com/PyCQA/isort - rev: 5.12.0 +- repo: https://github.com/asottile/reorder-python-imports + rev: v3.12.0 hooks: - - id: isort - files: \.py$ + - id: reorder-python-imports + args: [--py310-plus, --add-import, "from __future__ import annotations"] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-builtin-literals - id: check-added-large-files @@ -66,9 +66,12 @@ repos: - id: forbid-new-submodules - id: mixed-line-ending - id: trailing-whitespace + - id: name-tests-test + - id: file-contents-sorter + files: requirements.in|.gitignore - repo: https://github.com/regebro/pyroma - rev: "4.1" + rev: "4.2" hooks: - id: pyroma args: ["-d", "--min=10", "."] @@ -76,17 +79,8 @@ repos: - numpy - cython -- repo: https://github.com/mgedmin/check-manifest - rev: "0.49" - hooks: - - id: check-manifest - args: ["--ignore=.nox,build", "--no-build-isolation"] - additional_dependencies: - - numpy - - cython - - repo: https://github.com/PyCQA/pydocstyle - rev: 6.1.1 + rev: 6.3.0 hooks: - id: pydocstyle files: bmipy/.*\.py$ @@ -96,7 +90,9 @@ repos: additional_dependencies: [".[toml]"] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.982 + rev: v1.8.0 hooks: - id: mypy + language_version: python3.12 additional_dependencies: [types-all] + files: src/.*\.py$ From 684f7e55666e26e8c750f4a2eb9e9c21749f51a0 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:50:26 -0700 Subject: [PATCH 06/30] update pyproject.toml and tidy it up --- pyproject.toml | 121 +++++++++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 44 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 106f8cc..296e02e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,23 +1,23 @@ -[build-system] -requires = ["setuptools >=61"] -build-backend = "setuptools.build_meta" - [project] name = "bmipy" +requires-python = ">=3.10" description = "Basic Model Interface for Python" -readme = "README.rst" +keywords = [ + "BMI", + "Basic Model Interface", +] authors = [ - {name = "Eric Hutton", email = "huttone@colorado.edu"}, + { name = "Eric Hutton", email = "huttone@colorado.edu" }, +] +maintainers = [ + { name = "Eric Hutton", email = "huttone@colorado.edu" }, ] -keywords = ["BMI", "Basic Model Interface"] -license = {text = "MIT"} classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -25,51 +25,84 @@ classifiers = [ "Topic :: Scientific/Engineering :: Hydrology", "Topic :: Scientific/Engineering :: Physics", ] -requires-python = ">=3.9" dependencies = [ "black", - "click", "jinja2", "numpy", ] -dynamic = ["version"] +dynamic = [ + "version", + "readme", +] - [project.scripts] - bmipy-render = "bmipy.cmd:main" +[project.license] +text = "MIT" - [project.urls] - Documentation = "https://bmi.readthedocs.io" - Source = "https://github.com/csdms/bmi-python" +[project.scripts] +bmipy-render = "bmipy.cmd:main" -[tool] +[project.urls] +Documentation = "https://bmi.readthedocs.io" +Source = "https://github.com/csdms/bmi-python" - [tool.setuptools.package-data] - bmipy = ["py.typed"] +[build-system] +requires = [ + "setuptools >=61", +] +build-backend = "setuptools.build_meta" - [tool.setuptools.dynamic] - version = {attr = "bmipy._version.__version__"} +[tool.setuptools.dynamic.readme] +file = "README.rst" +content-type = "text/x-rst" - [tool.pytest.ini_options] - minversion = "6.0" - testpaths = ["src", "tests"] - norecursedirs = [".*", "*.egg*", "build", "dist"] - addopts = """ - --ignore setup.py - --tb native - --durations 16 - --strict-markers - --doctest-modules - -vvv - """ - doctest_optionflags = [ - "NORMALIZE_WHITESPACE", - "IGNORE_EXCEPTION_DETAIL", - "ALLOW_UNICODE" - ] +[tool.setuptools.dynamic.version] +attr = "bmipy._version.__version__" - [tool.isort] - combine_as_imports = true - profile = "black" +[tool.setuptools.package-data] +bmipy = [ + "py.typed", +] + +[tool.check-manifest] +ignore = [ + ".nox", + "build", +] - [tool.check-manifest] - ignore = [".nox", "build"] +[tool.isort] +combine_as_imports = true +profile = "black" + +[tool.mypy] +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +warn_redundant_casts = true +warn_unused_ignores = true + +[tool.pytest.ini_options] +minversion = "6.0" +testpaths = [ + "src", + "tests", +] +norecursedirs = [ + ".*", + "*.egg*", + "build", + "dist", +] +addopts = [ + "--ignore=setup.py", + "--tb=native", + "--durations=16", + "--strict-markers", + "--doctest-modules", + "-vvv", +] +doctest_optionflags = [ + "NORMALIZE_WHITESPACE", + "IGNORE_EXCEPTION_DETAIL", + "ALLOW_UNICODE", +] From 83a1a6017a07773df324ac2417b4fb3be0c4df1a Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:52:06 -0700 Subject: [PATCH 07/30] import annotation from the future --- noxfile.py | 2 ++ setup.py | 2 ++ src/bmipy/__init__.py | 1 + src/bmipy/_version.py | 2 ++ tests/bmipy_test.py | 3 ++- tests/cli_test.py | 5 +++-- 6 files changed, 12 insertions(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index 882776f..cf83476 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import pathlib import shutil diff --git a/setup.py b/setup.py index df54f71..2dcf83d 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os.path import sys diff --git a/src/bmipy/__init__.py b/src/bmipy/__init__.py index b93094a..13dc3b3 100644 --- a/src/bmipy/__init__.py +++ b/src/bmipy/__init__.py @@ -1,4 +1,5 @@ """The Basic Model Interface (BMI) for Python.""" +from __future__ import annotations from ._version import __version__ from .bmi import Bmi diff --git a/src/bmipy/_version.py b/src/bmipy/_version.py index 90956f3..6ad6d9a 100644 --- a/src/bmipy/_version.py +++ b/src/bmipy/_version.py @@ -1 +1,3 @@ +from __future__ import annotations + __version__ = "2.0.2.dev0" diff --git a/tests/bmipy_test.py b/tests/bmipy_test.py index d4bad78..059aaa6 100644 --- a/tests/bmipy_test.py +++ b/tests/bmipy_test.py @@ -1,5 +1,6 @@ -import pytest +from __future__ import annotations +import pytest from bmipy import Bmi diff --git a/tests/cli_test.py b/tests/cli_test.py index 839758f..0cedf34 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -1,9 +1,10 @@ +from __future__ import annotations + import sys import pytest -from click.testing import CliRunner - from bmipy.cmd import main +from click.testing import CliRunner def test_cli_version(): From 3c481e17b2ba3856c49244dc623bc09514a77edf Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:52:41 -0700 Subject: [PATCH 08/30] add NDArray annotation with data types --- src/bmipy/bmi.py | 50 +++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/bmipy/bmi.py b/src/bmipy/bmi.py index 41ed450..f3126d7 100644 --- a/src/bmipy/bmi.py +++ b/src/bmipy/bmi.py @@ -3,10 +3,14 @@ This language specification is derived from the Scientific Interface Definition Language (SIDL) file `bmi.sidl `_. """ +from __future__ import annotations -from abc import ABC, abstractmethod +from abc import ABC +from abc import abstractmethod +from typing import Any import numpy as np +from numpy.typing import NDArray class Bmi(ABC): @@ -334,7 +338,7 @@ def get_time_step(self) -> float: ... @abstractmethod - def get_value(self, name: str, dest: np.ndarray) -> np.ndarray: + def get_value(self, name: str, dest: NDArray[Any]) -> NDArray[Any]: """Get a copy of values of the given variable. This is a getter for the model, used to access the model's @@ -356,7 +360,7 @@ def get_value(self, name: str, dest: np.ndarray) -> np.ndarray: ... @abstractmethod - def get_value_ptr(self, name: str) -> np.ndarray: + def get_value_ptr(self, name: str) -> NDArray[Any]: """Get a reference to values of the given variable. This is a getter for the model, used to access the model's @@ -377,8 +381,8 @@ def get_value_ptr(self, name: str) -> np.ndarray: @abstractmethod def get_value_at_indices( - self, name: str, dest: np.ndarray, inds: np.ndarray - ) -> np.ndarray: + self, name: str, dest: NDArray[Any], inds: NDArray[np.int_] + ) -> NDArray[Any]: """Get values at particular indices. Parameters @@ -398,7 +402,7 @@ def get_value_at_indices( ... @abstractmethod - def set_value(self, name: str, src: np.ndarray) -> None: + def set_value(self, name: str, src: NDArray[Any]) -> None: """Specify a new value for a model variable. This is the setter for the model, used to change the model's @@ -417,7 +421,7 @@ def set_value(self, name: str, src: np.ndarray) -> None: @abstractmethod def set_value_at_indices( - self, name: str, inds: np.ndarray, src: np.ndarray + self, name: str, inds: NDArray[np.int_], src: NDArray[Any] ) -> None: """Specify a new value for a model variable at particular indices. @@ -483,7 +487,7 @@ def get_grid_type(self, grid: int) -> str: # Uniform rectilinear @abstractmethod - def get_grid_shape(self, grid: int, shape: np.ndarray) -> np.ndarray: + def get_grid_shape(self, grid: int, shape: NDArray[np.int_]) -> NDArray[np.int_]: """Get dimensions of the computational grid. Parameters @@ -501,7 +505,9 @@ def get_grid_shape(self, grid: int, shape: np.ndarray) -> np.ndarray: ... @abstractmethod - def get_grid_spacing(self, grid: int, spacing: np.ndarray) -> np.ndarray: + def get_grid_spacing( + self, grid: int, spacing: NDArray[np.float64] + ) -> NDArray[np.float64]: """Get distance between nodes of the computational grid. Parameters @@ -519,7 +525,9 @@ def get_grid_spacing(self, grid: int, spacing: np.ndarray) -> np.ndarray: ... @abstractmethod - def get_grid_origin(self, grid: int, origin: np.ndarray) -> np.ndarray: + def get_grid_origin( + self, grid: int, origin: NDArray[np.float64] + ) -> NDArray[np.float64]: """Get coordinates for the lower-left corner of the computational grid. Parameters @@ -540,7 +548,7 @@ def get_grid_origin(self, grid: int, origin: np.ndarray) -> np.ndarray: # Non-uniform rectilinear, curvilinear @abstractmethod - def get_grid_x(self, grid: int, x: np.ndarray) -> np.ndarray: + def get_grid_x(self, grid: int, x: NDArray[np.float64]) -> NDArray[np.float64]: """Get coordinates of grid nodes in the x direction. Parameters @@ -558,7 +566,7 @@ def get_grid_x(self, grid: int, x: np.ndarray) -> np.ndarray: ... @abstractmethod - def get_grid_y(self, grid: int, y: np.ndarray) -> np.ndarray: + def get_grid_y(self, grid: int, y: NDArray[np.float64]) -> NDArray[np.float64]: """Get coordinates of grid nodes in the y direction. Parameters @@ -576,7 +584,7 @@ def get_grid_y(self, grid: int, y: np.ndarray) -> np.ndarray: ... @abstractmethod - def get_grid_z(self, grid: int, z: np.ndarray) -> np.ndarray: + def get_grid_z(self, grid: int, z: NDArray[np.float64]) -> NDArray[np.float64]: """Get coordinates of grid nodes in the z direction. Parameters @@ -642,7 +650,9 @@ def get_grid_face_count(self, grid: int) -> int: ... @abstractmethod - def get_grid_edge_nodes(self, grid: int, edge_nodes: np.ndarray) -> np.ndarray: + def get_grid_edge_nodes( + self, grid: int, edge_nodes: NDArray[np.int_] + ) -> NDArray[np.int_]: """Get the edge-node connectivity. Parameters @@ -662,7 +672,9 @@ def get_grid_edge_nodes(self, grid: int, edge_nodes: np.ndarray) -> np.ndarray: ... @abstractmethod - def get_grid_face_edges(self, grid: int, face_edges: np.ndarray) -> np.ndarray: + def get_grid_face_edges( + self, grid: int, face_edges: NDArray[np.int_] + ) -> NDArray[np.int_]: """Get the face-edge connectivity. Parameters @@ -680,7 +692,9 @@ def get_grid_face_edges(self, grid: int, face_edges: np.ndarray) -> np.ndarray: ... @abstractmethod - def get_grid_face_nodes(self, grid: int, face_nodes: np.ndarray) -> np.ndarray: + def get_grid_face_nodes( + self, grid: int, face_nodes: NDArray[np.int_] + ) -> NDArray[np.int_]: """Get the face-node connectivity. Parameters @@ -701,8 +715,8 @@ def get_grid_face_nodes(self, grid: int, face_nodes: np.ndarray) -> np.ndarray: @abstractmethod def get_grid_nodes_per_face( - self, grid: int, nodes_per_face: np.ndarray - ) -> np.ndarray: + self, grid: int, nodes_per_face: NDArray[np.int_] + ) -> NDArray[np.int_]: """Get the number of nodes for each face. Parameters From 886b8fb34dd3025f6f320313fe5be4d4e76d1f4a Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:53:32 -0700 Subject: [PATCH 09/30] remove click; black is optional; add annotations --- src/bmipy/cmd.py | 98 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/src/bmipy/cmd.py b/src/bmipy/cmd.py index f167a6b..04786e9 100644 --- a/src/bmipy/cmd.py +++ b/src/bmipy/cmd.py @@ -1,22 +1,31 @@ """Command line interface that create template BMI implementations.""" +from __future__ import annotations + +import argparse +import functools import inspect import keyword import re +import sys -import black as blk -import click import jinja2 +from bmipy._version import __version__ +from bmipy.bmi import Bmi -from bmipy import Bmi +try: + import black as blk +except ModuleNotFoundError: + WITH_BLACK = False +else: + WITH_BLACK = True -BMI_TEMPLATE = """# -*- coding: utf-8 -*- -{% if with_hints -%} -from typing import Tuple -{%- endif %} +BMI_TEMPLATE = """\ +from __future__ import annotations -from bmipy import Bmi import numpy +from bmipy.bmi import Bmi + class {{ name }}(Bmi): {% for func in funcs %} @@ -26,8 +35,11 @@ def {{ func }}{{ funcs[func].sig }}: {% endfor %} """ +err = functools.partial(print, file=sys.stderr) +out = functools.partial(print, file=sys.stderr) -def _remove_hints_from_signature(signature): + +def _remove_hints_from_signature(signature: inspect.Signature) -> inspect.Signature: """Remove hint annotation from a signature.""" params = [] for _, param in signature.parameters.items(): @@ -37,12 +49,12 @@ def _remove_hints_from_signature(signature): ) -def _is_valid_class_name(name): +def _is_valid_class_name(name: str) -> bool: p = re.compile(r"^[^\d\W]\w*\Z", re.UNICODE) - return p.match(name) and not keyword.iskeyword(name) + return bool(p.match(name)) and not keyword.iskeyword(name) -def render_bmi(name, black=True, hints=True): +def render_bmi(name: str, black: bool = True, hints: bool = True) -> str: """Render a template BMI implementation in Python. Parameters @@ -83,20 +95,52 @@ def render_bmi(name, black=True, hints=True): raise ValueError(f"invalid class name ({name})") -@click.command() -@click.version_option() -@click.option("--black / --no-black", default=True, help="format output with black") -@click.option("--hints / --no-hints", default=True, help="include type hint annotation") -@click.argument("name") -@click.pass_context -def main(ctx, name, black, hints): +def main() -> int: """Render a template BMI implementation in Python for class NAME.""" - if _is_valid_class_name(name): - print(render_bmi(name, black=black, hints=hints)) - else: - click.secho( - f"πŸ’₯ πŸ’” πŸ’₯ {name!r} is not a valid class name in Python", - err=True, - fg="red", + parser = argparse.ArgumentParser() + parser.add_argument("--version", action="version", version=f"bmipy {__version__}") + parser.add_argument("name") + + black_parser = parser.add_mutually_exclusive_group() + if WITH_BLACK: + black_parser.add_argument( + "--black", + action="store_true", + dest="black", + default=False, + help="format output with black", ) - ctx.exit(code=1) + black_parser.add_argument( + "--no-black", + action="store_false", + dest="black", + default=False, + help="format output with black", + ) + hints_group = parser.add_mutually_exclusive_group() + hints_group.add_argument( + "--hints", + action="store_true", + dest="hints", + help="include type hint annotation", + ) + hints_group.add_argument( + "--no-hints", + action="store_false", + dest="hints", + help="include type hint annotation", + ) + + args = parser.parse_args() + + if _is_valid_class_name(args.name): + print(render_bmi(args.name, black=args.black, hints=args.hints)) + else: + err(f"πŸ’₯ πŸ’” πŸ’₯ {args.name!r} is not a valid class name in Python") + return 1 + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 2f3c9661d2382c522745aff3efc8c9f604cb2003 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 13:56:42 -0700 Subject: [PATCH 10/30] drop black as a requirement; add some urls --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 296e02e..47282e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,6 @@ classifiers = [ "Topic :: Scientific/Engineering :: Physics", ] dependencies = [ - "black", "jinja2", "numpy", ] @@ -42,8 +41,9 @@ text = "MIT" bmipy-render = "bmipy.cmd:main" [project.urls] -Documentation = "https://bmi.readthedocs.io" -Source = "https://github.com/csdms/bmi-python" +documentation = "https://bmi.readthedocs.io" +homepage = "https://bmi.readthedocs.io" +repository = "https://github.com/csdms/bmi-python" [build-system] requires = [ From a8ab6cdfe54e06b6c426c0be7964cd89b4275ce2 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 14:38:26 -0700 Subject: [PATCH 11/30] remove click, black from pinned requirements --- requirements/requires.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/requirements/requires.txt b/requirements/requires.txt index 10b006d..904ee9f 100644 --- a/requirements/requires.txt +++ b/requirements/requires.txt @@ -1,4 +1,2 @@ -black==23.10.1 -click==8.1.7 jinja2==3.1.2 numpy==1.26.3 From 341357b7ca05899f74f5b0ff3263630ca5df3451 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 14:40:54 -0700 Subject: [PATCH 12/30] add optional args argument to main --- src/bmipy/cmd.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/bmipy/cmd.py b/src/bmipy/cmd.py index 04786e9..4095d10 100644 --- a/src/bmipy/cmd.py +++ b/src/bmipy/cmd.py @@ -95,7 +95,7 @@ def render_bmi(name: str, black: bool = True, hints: bool = True) -> str: raise ValueError(f"invalid class name ({name})") -def main() -> int: +def main(args: tuple[str, ...] | None = None) -> int: """Render a template BMI implementation in Python for class NAME.""" parser = argparse.ArgumentParser() parser.add_argument("--version", action="version", version=f"bmipy {__version__}") @@ -121,6 +121,7 @@ def main() -> int: hints_group.add_argument( "--hints", action="store_true", + default=True, dest="hints", help="include type hint annotation", ) @@ -128,15 +129,20 @@ def main() -> int: "--no-hints", action="store_false", dest="hints", + default=True, help="include type hint annotation", ) - args = parser.parse_args() + parsed_args = parser.parse_args(args) - if _is_valid_class_name(args.name): - print(render_bmi(args.name, black=args.black, hints=args.hints)) + if _is_valid_class_name(parsed_args.name): + print( + render_bmi( + parsed_args.name, black=parsed_args.black, hints=parsed_args.hints + ) + ) else: - err(f"πŸ’₯ πŸ’” πŸ’₯ {args.name!r} is not a valid class name in Python") + err(f"πŸ’₯ πŸ’” πŸ’₯ {parsed_args.name!r} is not a valid class name in Python") return 1 return 0 From 3101343c5baba083e6360b1fa53d99c8c9b6278f Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 14:41:20 -0700 Subject: [PATCH 13/30] rewrite cli tests to not use click --- tests/cli_test.py | 80 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/cli_test.py b/tests/cli_test.py index 0cedf34..f640a43 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -4,75 +4,75 @@ import pytest from bmipy.cmd import main -from click.testing import CliRunner +from bmipy.cmd import WITH_BLACK -def test_cli_version(): - runner = CliRunner() - result = runner.invoke(main, ["--version"]) - assert result.exit_code == 0 - assert "version" in result.output +def test_cli_version(capsys): + try: + assert main(["--version"]) == 0 + except SystemExit: + pass + output = capsys.readouterr().out + assert "bmipy" in output -def test_cli_help(): - runner = CliRunner() - result = runner.invoke(main, ["--help"]) - assert result.exit_code == 0 - assert "help" in result.output + +def test_cli_help(capsys): + try: + assert main(["--help"]) == 0 + except SystemExit: + pass + output = capsys.readouterr().out + + assert "help" in output @pytest.mark.skipif( sys.platform == "win32", reason="See https://github.com/csdms/bmi-python/issues/10" ) -def test_cli_default(tmpdir): +def test_cli_default(capsys, tmpdir): import importlib import sys - runner = CliRunner() with tmpdir.as_cwd(): - result = runner.invoke(main, ["MyBmi"]) - assert result.exit_code == 0 + assert main(["MyBmi"]) == 0 + output = capsys.readouterr().out with open("mybmi.py", "w") as fp: - fp.write(result.output) + fp.write(output) sys.path.append(".") mod = importlib.import_module("mybmi") assert "MyBmi" in mod.__dict__ -def test_cli_with_hints(tmpdir): - runner = CliRunner() +def test_cli_with_hints(capsys, tmpdir): with tmpdir.as_cwd(): - result = runner.invoke(main, ["MyBmiWithHints", "--hints"]) - assert result.exit_code == 0 - assert "->" in result.output + assert main(["MyBmiWithHints", "--hints"]) == 0 + output = capsys.readouterr().out + assert "->" in output -def test_cli_without_hints(tmpdir): - runner = CliRunner() +def test_cli_without_hints(capsys, tmpdir): with tmpdir.as_cwd(): - result = runner.invoke(main, ["MyBmiWithoutHints", "--no-hints"]) - assert result.exit_code == 0 - assert "->" not in result.output + assert main(["MyBmiWithoutHints", "--no-hints"]) == 0 + output = capsys.readouterr().out + assert "->" not in output -def test_cli_with_black(tmpdir): - runner = CliRunner() +@pytest.mark.skipif(not WITH_BLACK, reason="black is not installed") +def test_cli_with_black(capsys, tmpdir): with tmpdir.as_cwd(): - result = runner.invoke(main, ["MyBmiWithHints", "--black"]) - assert result.exit_code == 0 - assert max([len(line) for line in result.output.splitlines()]) <= 88 + assert main(["MyBmiWithHints", "--black"]) == 0 + output = capsys.readouterr().out + assert max(len(line) for line in output.splitlines()) <= 88 -def test_cli_without_black(tmpdir): - runner = CliRunner() +def test_cli_without_black(capsys, tmpdir): with tmpdir.as_cwd(): - result = runner.invoke(main, ["MyBmiWithoutHints", "--no-black"]) - assert result.exit_code == 0 - assert max([len(line) for line in result.output.splitlines()]) > 88 + assert main(["MyBmiWithHints", "--hints", "--no-black"]) == 0 + output = capsys.readouterr().out + assert max(len(line) for line in output.splitlines()) > 88 @pytest.mark.parametrize("bad_name", ["True", "0Bmi"]) -def test_cli_with_bad_class_name(tmpdir, bad_name): - runner = CliRunner() - result = runner.invoke(main, [bad_name]) - assert result.exit_code == 1 +def test_cli_with_bad_class_name(capsys, tmpdir, bad_name): + assert main([bad_name]) != 0 From 08b7beb2706f4a388e3b690e4b6dff78589760bd Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 14:42:43 -0700 Subject: [PATCH 14/30] add a main module --- src/bmipy/__init__.py | 4 ++-- src/bmipy/__main__.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/bmipy/__main__.py diff --git a/src/bmipy/__init__.py b/src/bmipy/__init__.py index 13dc3b3..27e652a 100644 --- a/src/bmipy/__init__.py +++ b/src/bmipy/__init__.py @@ -1,7 +1,7 @@ """The Basic Model Interface (BMI) for Python.""" from __future__ import annotations -from ._version import __version__ -from .bmi import Bmi +from bmipy._version import __version__ +from bmipy.bmi import Bmi __all__ = ["__version__", "Bmi"] diff --git a/src/bmipy/__main__.py b/src/bmipy/__main__.py new file mode 100644 index 0000000..004e1f5 --- /dev/null +++ b/src/bmipy/__main__.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from bmipy.cmd import main + +if __name__ == '__main__': + raise SystemExit(main()) From 284721c09999056df1d241dc28386b5eb9c17088 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 14:44:43 -0700 Subject: [PATCH 15/30] remove a bit of lint --- src/bmipy/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bmipy/__main__.py b/src/bmipy/__main__.py index 004e1f5..6820461 100644 --- a/src/bmipy/__main__.py +++ b/src/bmipy/__main__.py @@ -1,6 +1,7 @@ +"""The bmipy-render command.""" from __future__ import annotations from bmipy.cmd import main -if __name__ == '__main__': +if __name__ == "__main__": raise SystemExit(main()) From 6673629cf45bd015489eab0ec0168e51e25a247d Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 14:46:00 -0700 Subject: [PATCH 16/30] remove win32 skip --- tests/cli_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cli_test.py b/tests/cli_test.py index f640a43..3b6e305 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -27,9 +27,9 @@ def test_cli_help(capsys): assert "help" in output -@pytest.mark.skipif( - sys.platform == "win32", reason="See https://github.com/csdms/bmi-python/issues/10" -) +# @pytest.mark.skipif( +# sys.platform == "win32", reason="See https://github.com/csdms/bmi-python/issues/10" +# ) def test_cli_default(capsys, tmpdir): import importlib import sys From c520d6f35cd9391013673d23ee447a66d7ef4f36 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 15:45:37 -0700 Subject: [PATCH 17/30] remove non-ascii characters --- src/bmipy/bmi.py | 4 ++-- tests/cli_test.py | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/bmipy/bmi.py b/src/bmipy/bmi.py index f3126d7..2158a5e 100644 --- a/src/bmipy/bmi.py +++ b/src/bmipy/bmi.py @@ -252,8 +252,8 @@ def get_var_location(self, name: str) -> str: *face* A plane or surface enclosed by a set of edges. In a 2D - horizontal application one may consider the word β€œpolygon”, - but in the hierarchy of elements the word β€œface” is most common. + horizontal application one may consider the word "polygon", + but in the hierarchy of elements the word "face" is most common. Parameters ---------- diff --git a/tests/cli_test.py b/tests/cli_test.py index 3b6e305..46f981b 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -1,7 +1,5 @@ from __future__ import annotations -import sys - import pytest from bmipy.cmd import main from bmipy.cmd import WITH_BLACK @@ -27,9 +25,6 @@ def test_cli_help(capsys): assert "help" in output -# @pytest.mark.skipif( -# sys.platform == "win32", reason="See https://github.com/csdms/bmi-python/issues/10" -# ) def test_cli_default(capsys, tmpdir): import importlib import sys From e746500f259de6263b35a928a63a4c9b7c64c177 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 15:55:02 -0700 Subject: [PATCH 18/30] remove the lint workflow, use pre-commit.ci instead --- .github/workflows/test.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b106b80..df6867b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,17 +45,6 @@ jobs: debug: true - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: wntrblm/nox@2023.04.22 - with: - python-versions: "3.12" - - name: Lint - run: nox --non-interactive --error-on-missing-interpreter --session "lint" - - coveralls_finish: needs: build-and-test runs-on: ubuntu-latest From aecc18e6b2a984f226169dce5fb5bceee28672bb Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 17:13:00 -0700 Subject: [PATCH 19/30] remove unnecessary configuration from pyproject.toml --- pyproject.toml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 47282e1..5a5b267 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,21 +58,6 @@ content-type = "text/x-rst" [tool.setuptools.dynamic.version] attr = "bmipy._version.__version__" -[tool.setuptools.package-data] -bmipy = [ - "py.typed", -] - -[tool.check-manifest] -ignore = [ - ".nox", - "build", -] - -[tool.isort] -combine_as_imports = true -profile = "black" - [tool.mypy] check_untyped_defs = true disallow_any_generics = true @@ -101,8 +86,3 @@ addopts = [ "--doctest-modules", "-vvv", ] -doctest_optionflags = [ - "NORMALIZE_WHITESPACE", - "IGNORE_EXCEPTION_DETAIL", - "ALLOW_UNICODE", -] From d53fc6704ab34cc02682a252ffe25e78192d1fbb Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 5 Jan 2024 17:20:05 -0700 Subject: [PATCH 20/30] remove old requirements; sort requirements files --- .pre-commit-config.yaml | 16 ++++++---------- requirements.in | 2 -- requirements/testing.txt | 2 +- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9ea1861..5910e0a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -68,16 +68,12 @@ repos: - id: trailing-whitespace - id: name-tests-test - id: file-contents-sorter - files: requirements.in|.gitignore - -- repo: https://github.com/regebro/pyroma - rev: "4.2" - hooks: - - id: pyroma - args: ["-d", "--min=10", "."] - additional_dependencies: - - numpy - - cython + files: | + (?x)^( + .*requirements(-\w+)?.(in|txt)| + requirements/.*\.txt| + .gitignore + ) - repo: https://github.com/PyCQA/pydocstyle rev: 6.3.0 diff --git a/requirements.in b/requirements.in index 666203b..7e602f5 100644 --- a/requirements.in +++ b/requirements.in @@ -1,4 +1,2 @@ -black -click jinja2 numpy diff --git a/requirements/testing.txt b/requirements/testing.txt index 42114c5..70c4cbd 100644 --- a/requirements/testing.txt +++ b/requirements/testing.txt @@ -1,3 +1,3 @@ coveralls==3.3.1 -pytest==7.4.2 pytest-cov==4.1.0 +pytest==7.4.2 From d01fb5e2cac74a32b5c4c2dbf902752c63b005f8 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sat, 6 Jan 2024 10:34:58 -0700 Subject: [PATCH 21/30] convert readme to markdown --- README.md | 53 ++++++++++++++++++++++++++++++++++++++++ README.rst | 65 -------------------------------------------------- pyproject.toml | 9 +++++-- 3 files changed, 60 insertions(+), 67 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 0000000..7158f9c --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# BMI for Python + +Python bindings for the CSDMS [Basic Model Interface](https://bmi.readthedocs.io). + +![[DOI][doi-link]][doi-badge] +![[Build Status][build-link]][build-badge] +![[Build Status][anaconda-link]][anaconda-badge] + +[anaconda-badge]: https://anaconda.org/conda-forge/bmipy/badges/version.svg +[anaconda-link]: https://anaconda.org/conda-forge/bmipy +[build-badge]: https://github.com/csdms/bmi-python/actions/workflows/test.yml/badge.svg +[build-link]: https://github.com/csdms/bmi-python/actions/workflows/test.yml +[doi-badge]: https://zenodo.org/badge/179283861.svg +[doi-link]: https://zenodo.org/badge/latestdoi/179283861 + +## Install + +Install *bmipy* with *pip*, + +```bash +pip install bmipy +``` + +If you're using Anaconda, you can also install *bmipy* +with conda from the *conda-forge* channel, + +```bash +conda install bmipy -c conda-forge +``` + +To build and install *bmipy* from source, + +```bash +git clone https://github.com/csdms/bmi-python +cd bmi-python +pip install . +``` + +## Usage + +```python +from bmipy import Bmi + + +class MyBmi(Bmi): + + def initialize(self, config_file): + # Your implementation goes here +``` + +A complete sample implementation is given in the + +repository. diff --git a/README.rst b/README.rst deleted file mode 100644 index 38250e1..0000000 --- a/README.rst +++ /dev/null @@ -1,65 +0,0 @@ -BMI for Python -============== - -Python bindings for the CSDMS `Basic Model Interface `_. - -.. image:: https://zenodo.org/badge/179283861.svg - :target: https://zenodo.org/badge/latestdoi/179283861 - :alt: DOI - -.. image:: https://github.com/csdms/bmi-python/actions/workflows/test.yml/badge.svg - :target: https://github.com/csdms/bmi-python/actions/workflows/test.yml - :alt: Build Status - -.. image:: https://anaconda.org/conda-forge/bmipy/badges/version.svg - :target: https://anaconda.org/conda-forge/bmipy - :alt: Anaconda-Server Badge - -.. image:: https://anaconda.org/conda-forge/bmipy/badges/platforms.svg - :target: https://anaconda.org/conda-forge/bmipy - :alt: Anaconda-Server Badge - -.. image:: https://anaconda.org/conda-forge/bmipy/badges/downloads.svg - :target: https://anaconda.org/conda-forge/bmipy - :alt: Anaconda-Server Badge - -Install -------- - -Install *bmipy* with *pip*, - -.. code-block:: bash - - $ pip install bmipy - -If you're using Anaconda, you can also install *bmipy* -with conda from the *conda-forge* channel, - -.. code-block:: bash - - $ conda install bmipy -c conda-forge - -To build and install *bmipy* from source, - -.. code-block:: bash - - $ git clone https://github.com/csdms/bmi-python - $ cd bmi-python - $ pip install . - -Usage ------ - -.. code-block:: python - - from bmipy import Bmi - - - class MyBmi(Bmi): - - def initialize(self, config_file): - # Your implementation goes here - -A complete sample implementation is given in the -https://github.com/csdms/bmi-example-python -repository. diff --git a/pyproject.toml b/pyproject.toml index 5a5b267..84c7c5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,12 +52,17 @@ requires = [ build-backend = "setuptools.build_meta" [tool.setuptools.dynamic.readme] -file = "README.rst" -content-type = "text/x-rst" +file = "README.md" +content-type = "text/markdown" [tool.setuptools.dynamic.version] attr = "bmipy._version.__version__" +[tool.setuptools.packages.find] +where = [ + "src", +] + [tool.mypy] check_untyped_defs = true disallow_any_generics = true From 435f5c593f6a9dfa0aca6f798ab0520814222bff Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sat, 6 Jan 2024 11:57:25 -0700 Subject: [PATCH 22/30] add a few more badges --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 7158f9c..3c25036 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,10 @@ Python bindings for the CSDMS [Basic Model Interface](https://bmi.readthedocs.io). +![[Python][pypi-link]][python-badge] ![[DOI][doi-link]][doi-badge] ![[Build Status][build-link]][build-badge] +![[PyPI][pypi-link]][pypi-badge] ![[Build Status][anaconda-link]][anaconda-badge] [anaconda-badge]: https://anaconda.org/conda-forge/bmipy/badges/version.svg @@ -12,6 +14,9 @@ Python bindings for the CSDMS [Basic Model Interface](https://bmi.readthedocs.io [build-link]: https://github.com/csdms/bmi-python/actions/workflows/test.yml [doi-badge]: https://zenodo.org/badge/179283861.svg [doi-link]: https://zenodo.org/badge/latestdoi/179283861 +[pypi-badge]: https://badge.fury.io/py/bmipy.svg +[pypi-link]: https://pypi.org/project/bmipy/ +[python-badge]: https://img.shields.io/pypi/pyversions/bmipy.svg ## Install From 8af035fc1413ef2ad3553c9a0500c03ff5c9fc9a Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 8 Jan 2024 12:03:25 -0700 Subject: [PATCH 23/30] move coverage and zest.releaser config into pyproject.toml --- pyproject.toml | 7 +++++++ setup.cfg | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 84c7c5c..b6517fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,9 @@ where = [ "src", ] +[tool.coverage.run] +relative_files = true + [tool.mypy] check_untyped_defs = true disallow_any_generics = true @@ -91,3 +94,7 @@ addopts = [ "--doctest-modules", "-vvv", ] + +[tool.zest-releaser] +tag-format = "v{version}" +python-file-with-version = "src/bmipy/_version.py" diff --git a/setup.cfg b/setup.cfg index f701faa..c5c0cdf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,10 +4,3 @@ ignore = C901, E203, E266, E501, W503, B905 max-line-length = 88 max-complexity = 18 select = B,C,E,F,W,T4,B9 - -[zest.releaser] -tag-format = v{version} -python-file-with-version = src/bmipy/_version.py - -[coverage:run] -relative_files = True From ed7584acb543df600b533c7705924969973836de Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 8 Jan 2024 12:18:39 -0700 Subject: [PATCH 24/30] remove setup.py --- setup.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 2dcf83d..0000000 --- a/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -import os.path -import sys - -from setuptools import setup - -sys.path.append(os.path.dirname(__file__)) - -# See pyproject.toml for project metadata -setup( - name="bmipy", # need by GitHub dependency graph -) From 2e70439401ed72b8148b8aebf8bdc0b34a71a364 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 8 Jan 2024 16:09:30 -0700 Subject: [PATCH 25/30] remove bmipy.cmd bmipy._cmd --- pyproject.toml | 2 +- src/bmipy/__main__.py | 2 +- src/bmipy/{cmd.py => _cmd.py} | 0 src/bmipy/_template.py | 2 +- tests/cli_test.py | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/bmipy/{cmd.py => _cmd.py} (100%) diff --git a/pyproject.toml b/pyproject.toml index d921161..e8fec2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ dynamic = [ text = "MIT" [project.scripts] -bmipy-render = "bmipy.cmd:main" +bmipy-render = "bmipy._cmd:main" [project.urls] documentation = "https://bmi.readthedocs.io" diff --git a/src/bmipy/__main__.py b/src/bmipy/__main__.py index 6820461..19d6a0c 100644 --- a/src/bmipy/__main__.py +++ b/src/bmipy/__main__.py @@ -1,7 +1,7 @@ """The bmipy-render command.""" from __future__ import annotations -from bmipy.cmd import main +from bmipy._cmd import main if __name__ == "__main__": raise SystemExit(main()) diff --git a/src/bmipy/cmd.py b/src/bmipy/_cmd.py similarity index 100% rename from src/bmipy/cmd.py rename to src/bmipy/_cmd.py diff --git a/src/bmipy/_template.py b/src/bmipy/_template.py index b12907e..3a7be02 100644 --- a/src/bmipy/_template.py +++ b/src/bmipy/_template.py @@ -52,7 +52,7 @@ def _render_func(self, name: str) -> str: return textwrap.indent(os.linesep.join(parts), " ") -def dedent_docstring(text: str | None, tabsize=4) -> str: +def dedent_docstring(text: str | None, tabsize: int = 4) -> str: """Dedent a docstring, ignoring indentation of the first line. Parameters diff --git a/tests/cli_test.py b/tests/cli_test.py index 4a00343..ff3adbc 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -4,7 +4,7 @@ import sys import pytest -from bmipy.cmd import main +from bmipy._cmd import main def test_cli_version(capsys): From 400d5e5d98ade068cf351e1efa3824d77ef3c5fb Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 8 Jan 2024 20:02:33 -0700 Subject: [PATCH 26/30] instructions to pip-install from github --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c25036..2ced3e8 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,7 @@ conda install bmipy -c conda-forge To build and install *bmipy* from source, ```bash -git clone https://github.com/csdms/bmi-python -cd bmi-python -pip install . +pip install git+https://github.com/csdms/bmi-python.git ``` ## Usage From 8a25868742209cc62644c1e33c989c4dc2700c62 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 8 Jan 2024 20:24:39 -0700 Subject: [PATCH 27/30] remove silly bmipy test --- tests/bmipy_test.py | 144 -------------------------------------------- 1 file changed, 144 deletions(-) delete mode 100644 tests/bmipy_test.py diff --git a/tests/bmipy_test.py b/tests/bmipy_test.py deleted file mode 100644 index 059aaa6..0000000 --- a/tests/bmipy_test.py +++ /dev/null @@ -1,144 +0,0 @@ -from __future__ import annotations - -import pytest -from bmipy import Bmi - - -class EmptyBmi(Bmi): - def __init__(self): - pass - - def initialize(self, config_file): - pass - - def update(self): - pass - - def update_until(self, then): - pass - - def finalize(self): - pass - - def get_var_type(self, var_name): - pass - - def get_var_units(self, var_name): - pass - - def get_var_nbytes(self, var_name): - pass - - def get_var_itemsize(self, name): - pass - - def get_var_location(self, name): - pass - - def get_var_grid(self, var_name): - pass - - def get_grid_rank(self, grid_id): - pass - - def get_grid_size(self, grid_id): - pass - - def get_value_ptr(self, var_name): - pass - - def get_value(self, var_name): - pass - - def get_value_at_indices(self, var_name, indices): - pass - - def set_value(self, var_name, src): - pass - - def set_value_at_indices(self, var_name, src, indices): - pass - - def get_component_name(self): - pass - - def get_input_item_count(self): - pass - - def get_output_item_count(self): - pass - - def get_input_var_names(self): - pass - - def get_output_var_names(self): - pass - - def get_grid_shape(self, grid_id): - pass - - def get_grid_spacing(self, grid_id): - pass - - def get_grid_origin(self, grid_id): - pass - - def get_grid_type(self, grid_id): - pass - - def get_start_time(self): - pass - - def get_end_time(self): - pass - - def get_current_time(self): - pass - - def get_time_step(self): - pass - - def get_time_units(self): - pass - - def get_grid_edge_count(self, grid): - pass - - def get_grid_edge_nodes(self, grid, edge_nodes): - pass - - def get_grid_face_count(self, grid): - pass - - def get_grid_face_nodes(self, grid, face_nodes): - pass - - def get_grid_face_edges(self, grid, face_edges): - pass - - def get_grid_node_count(self, grid): - pass - - def get_grid_nodes_per_face(self, grid, nodes_per_face): - pass - - def get_grid_x(self, grid, x): - pass - - def get_grid_y(self, grid, y): - pass - - def get_grid_z(self, grid, z): - pass - - -def test_bmi_not_implemented(): - class MyBmi(Bmi): - pass - - with pytest.raises(TypeError): - Bmi() - - -def test_bmi_implemented(): - assert isinstance(EmptyBmi(), Bmi) From d34409b68e9f939dd582228ccd7b726ec9a20040 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 8 Jan 2024 20:43:43 -0700 Subject: [PATCH 28/30] use exec to test if generated bmi works --- tests/cli_test.py | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/tests/cli_test.py b/tests/cli_test.py index ff3adbc..23541cc 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -1,8 +1,5 @@ from __future__ import annotations -import importlib -import sys - import pytest from bmipy._cmd import main @@ -27,31 +24,24 @@ def test_cli_help(capsys): assert "help" in output -def test_cli_default(capsys, tmpdir): - with tmpdir.as_cwd(): - assert main(["MyBmi"]) == 0 - output = capsys.readouterr().out - with open("mybmi.py", "w") as fp: - fp.write(output) - sys.path.append(".") - mod = importlib.import_module("mybmi") - assert "MyBmi" in mod.__dict__ +def test_cli_default(capsys): + assert main(["MyBmi"]) == 0 + exec(capsys.readouterr().out) + assert "MyBmi" in globals() -def test_cli_wraps_lines(capsys, tmpdir): - with tmpdir.as_cwd(): - assert main(["MyBmi"]) == 0 - output = capsys.readouterr().out - assert max(len(line) for line in output.splitlines()) <= 88 +def test_cli_wraps_lines(capsys): + assert main(["MyBmi"]) == 0 + output = capsys.readouterr().out + assert max(len(line) for line in output.splitlines()) <= 88 -def test_cli_with_hints(capsys, tmpdir): - with tmpdir.as_cwd(): - assert main(["MyBmiWithHints"]) == 0 - output = capsys.readouterr().out - assert "->" in output +def test_cli_with_hints(capsys): + assert main(["MyBmiWithHints"]) == 0 + output = capsys.readouterr().out + assert "->" in output @pytest.mark.parametrize("bad_name", ["True", "0Bmi"]) -def test_cli_with_bad_class_name(capsys, tmpdir, bad_name): +def test_cli_with_bad_class_name(capsys, bad_name): assert main([bad_name]) != 0 From ade3dad373aab3b9b6c97852681cbd7d7d5b22d4 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 8 Jan 2024 21:03:20 -0700 Subject: [PATCH 29/30] import some type annotations to the generated bmi --- src/bmipy/_template.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bmipy/_template.py b/src/bmipy/_template.py index 3a7be02..acb9981 100644 --- a/src/bmipy/_template.py +++ b/src/bmipy/_template.py @@ -19,7 +19,10 @@ def render(self) -> str: prefix = f"""\ from __future__ import annotations +from typing import Any + import numpy as np +from numpy.typing import NDArray from bmipy.bmi import Bmi From fd5c3b3320ccbcef710d7a3cf6b6edabbe415d88 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 8 Jan 2024 21:04:26 -0700 Subject: [PATCH 30/30] fix a flaky test --- tests/cli_test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/cli_test.py b/tests/cli_test.py index 23541cc..c4f4700 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -25,9 +25,10 @@ def test_cli_help(capsys): def test_cli_default(capsys): - assert main(["MyBmi"]) == 0 - exec(capsys.readouterr().out) - assert "MyBmi" in globals() + assert main(["MyUniqueBmi"]) == 0 + globs = {} + exec(capsys.readouterr().out, globs) + assert "MyUniqueBmi" in globs def test_cli_wraps_lines(capsys):