Skip to content

Commit

Permalink
Merge pull request #126 from MODFLOW-USGS/v1.3.0
Browse files Browse the repository at this point in the history
Release 1.3.0
  • Loading branch information
wpbonelli committed Nov 21, 2023
2 parents ccf47eb + 4be45b2 commit 5d5d740
Show file tree
Hide file tree
Showing 33 changed files with 384 additions and 263 deletions.
31 changes: 10 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
name: CI
on:
push:
branches:
- main
- develop*
paths-ignore:
- '**.md'
pull_request:
branches:
- main
- develop*
- develop
paths-ignore:
- '**.md'
jobs:
Expand All @@ -19,7 +16,7 @@ jobs:
steps:

- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v4
Expand Down Expand Up @@ -83,24 +80,24 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-22.04, macos-12, windows-2022 ]
python: [ 3.8, 3.9, "3.10", "3.11" ]
python: [ 3.8, 3.9, "3.10", "3.11", "3.12" ]
env:
GCC_V: 11
steps:

- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: modflow-devtools

- name: Checkout modflow6
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: MODFLOW-USGS/modflow6
path: modflow6

- name: Checkout modflow6 examples
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: MODFLOW-USGS/modflow6-examples
path: modflow6-examples
Expand All @@ -116,7 +113,6 @@ jobs:
with:
repository: MODFLOW-USGS/modflow6-largetestmodels
path: modflow6-largetestmodels
token: ${{ github.token }}

- name: Install executables
uses: modflowpy/install-modflow-action@v1
Expand All @@ -131,10 +127,6 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
cache: 'pip'
cache-dependency-path: |
modflow-devtools/pyproject.toml
modflow6-examples/etc/requirements*.txt

- name: Install Python packages
working-directory: modflow-devtools
Expand Down Expand Up @@ -162,22 +154,19 @@ jobs:
run: python ci_build_files.py

- name: Run local tests
working-directory: modflow-devtools
working-directory: modflow-devtools/autotest
env:
BIN_PATH: ~/.local/bin/modflow
REPOS_PATH: ${{ github.workspace }}
GITHUB_TOKEN: ${{ github.token }}
# use --dist loadfile to so tests requiring pytest-virtualenv run on the same worker
run: pytest -v -n auto --dist loadfile --durations 0 --ignore modflow_devtools/test/test_download.py
run: pytest -v -n auto --dist loadfile --durations 0 --ignore test_download.py

- name: Run network-dependent tests
# only invoke the GH API on one OS and Python version
# to avoid rate limits (1000 rqs / hour / repository)
# https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits
if: runner.os == 'Linux' && matrix.python == '3.8'
working-directory: modflow-devtools
working-directory: modflow-devtools/autotest
env:
BIN_PATH: ~/.local/bin/modflow
REPOS_PATH: ${{ github.workspace }}
GITHUB_TOKEN: ${{ github.token }}
run: pytest -v -n auto --durations 0 modflow_devtools/test/test_download.py
run: pytest -v -n auto --durations 0 test_download.py
18 changes: 12 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ jobs:
permissions:
contents: write
pull-requests: write
id-token: write # mandatory for trusted publishing
environment: # requires a 'release' environment in repo settings
name: release
url: https://pypi.org/p/modflow-devtools
steps:

- name: Checkout main branch
Expand All @@ -188,12 +192,14 @@ jobs:
- name: Check package
run: twine check --strict dist/*

- name: Publish package
if: ${{ env.TWINE_USERNAME != '' }}
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: twine upload dist/*
- name: Upload package
uses: actions/upload-artifact@v3
with:
name: dist
path: dist

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

reset:
name: Draft reset PR
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ dmypy.json
# Pyre type checker
.pyre/

# pycharme
# IDEs
.idea/
.vscode/

# downloaded exe
modflow_devtools/bin/
Expand Down
10 changes: 3 additions & 7 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,37 +38,33 @@ This repository's tests use [`pytest`](https://docs.pytest.org/en/latest/) and s

This repository's tests expect a few environment variables:

- `BIN_PATH`: path to MODFLOW 6 and related executables
- `REPOS_PATH`: the path to MODFLOW 6 example model repositories
- `GITHUB_TOKEN`: a GitHub authentication token

These may be set manually, but the recommended approach is to configure environment variables in a `.env` file in the project root, for instance:

```
BIN_PATH=/path/to/modflow/executables
REPOS_PATH=/path/to/repos
GITHUB_TOKEN=yourtoken...
```

The tests use [`pytest-dotenv`](https://github.com/quiqua/pytest-dotenv) to detect and load variables from this file.

**Note:** at minimum, the tests require that the `mf6` (or `mf6.exe` on Windows) executable is present in `BIN_PATH`.

### Running the tests

Tests should be run from the project root. To run the tests in parallel with verbose output:
Tests should be run from the `autotest` directory. To run the tests in parallel with verbose output:

```shell
pytest -v -n auto
```

### Writing new tests

Tests should follow a few conventions for ease of use and maintenance.
Tests follow a few conventions for ease of use and maintenance.

#### Temporary directories

Tests which must write to disk should use `pytest`'s built-in `temp_dir` fixture or one of this package's own scoped temporary directory fixtures.
Tests which must write to disk use `pytest`'s built-in `temp_dir` fixture or one of this package's own scoped temporary directory fixtures.

## Releasing

Expand Down
12 changes: 12 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
### Version 1.3.0

#### New features

* [feat(fixtures)](https://github.com/MODFLOW-USGS/modflow-devtools/commit/0ce571411b6b35bc62d4f333d1a961bd2f202784): Add --tabular pytest CLI arg and corresponding fixture (#116). Committed by wpbonelli on 2023-09-12.
* [feat(timeit)](https://github.com/MODFLOW-USGS/modflow-devtools/commit/506a238f6f31d827015a6c6f5ba1867ee55948a7): Add function timing decorator (#118). Committed by wpbonelli on 2023-09-12.
* [feat(executables)](https://github.com/MODFLOW-USGS/modflow-devtools/commit/5b61a4b393b0bcd40aafeb87d1e80b3e557e0f05): Support .get(key, default) like dict (#125). Committed by wpbonelli on 2023-11-21.

#### Refactoring

* [refactor](https://github.com/MODFLOW-USGS/modflow-devtools/commit/cd644fa90885cde04f36f24e44cfe922b2a38897): Support python 3.12, various updates (#124). Committed by wpbonelli on 2023-11-11.

### Version 1.2.0

#### New features
Expand Down
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions pytest.ini → autotest/pytest.ini
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[pytest]
addopts = -ra
addopts = -ra --color=yes
python_files =
test_*.py
*_test*.py
markers =
slow: tests that don't complete in a few seconds
slow: tests not completing in a few seconds
meta: run by other tests (e.g. testing fixtures)
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pathlib import Path

import pytest

from modflow_devtools.build import meson_build
from modflow_devtools.markers import requires_pkg

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
from flaky import flaky

from modflow_devtools.download import (
download_and_unzip,
download_artifact,
Expand Down
28 changes: 28 additions & 0 deletions autotest/test_executables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import sys
from pathlib import Path
from shutil import which

import pytest

from modflow_devtools.executables import Executables
from modflow_devtools.misc import add_sys_path, get_suffixes

ext, _ = get_suffixes(sys.platform)
exe_stem = "pytest"
exe_path = Path(which(exe_stem))
bin_path = exe_path.parent
exe = f"{exe_stem}{ext}"


@pytest.fixture
def exes():
with add_sys_path(bin_path):
yield Executables(**{exe_stem: bin_path / exe})


def test_access(exes):
# support both attribute and dictionary style access
assert exes.pytest == exes["pytest"] == exe_path
# .get() works too
assert exes.get("not a target") is None
assert exes.get("not a target", exes["pytest"]) == exes["pytest"]
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def test_keep_class_scoped_tmpdir(tmp_path, arg):
TestKeepClassScopedTmpdirInner.test_keep_class_scoped_tmpdir_inner.__name__,
"-M",
"test_keep",
"-K",
arg,
tmp_path,
]
assert pytest.main(args) == ExitCode.OK
Expand All @@ -160,19 +160,14 @@ def test_keep_module_scoped_tmpdir(tmp_path, arg):
test_keep_module_scoped_tmpdir_inner.__name__,
"-M",
"test_keep",
"-K",
arg,
tmp_path,
]
assert pytest.main(args) == ExitCode.OK
this_path = Path(__file__)
keep_path = (
tmp_path
/ f"{str(this_path.parent.parent.name)}.{str(this_path.parent.name)}.{str(this_path.stem)}0"
tmp_path / f"{str(this_path.parent.name)}.{str(this_path.stem)}0"
)
from pprint import pprint

print(keep_path)
pprint(list(keep_path.glob("*")))
assert test_keep_fname in [f.name for f in keep_path.glob("*")]


Expand All @@ -186,7 +181,7 @@ def test_keep_session_scoped_tmpdir(tmp_path, arg, request):
test_keep_session_scoped_tmpdir_inner.__name__,
"-M",
"test_keep",
"-K",
arg,
tmp_path,
]
assert pytest.main(args) == ExitCode.OK
Expand Down Expand Up @@ -316,3 +311,34 @@ def test_pandas(pandas, arg, function_tmpdir):
assert "True" in res
elif pandas == "no":
assert "False" in res


test_tabular_fname = "tabular.txt"


@pytest.mark.meta("test_tabular")
def test_tabular_inner(function_tmpdir, tabular):
with open(function_tmpdir / test_tabular_fname, "w") as f:
f.write(str(tabular))


@pytest.mark.parametrize("tabular", ["raw", "recarray", "dataframe"])
@pytest.mark.parametrize("arg", ["--tabular", "-T"])
def test_tabular(tabular, arg, function_tmpdir):
inner_fn = test_tabular_inner.__name__
args = [
__file__,
"-v",
"-s",
"-k",
inner_fn,
arg,
tabular,
"--keep",
function_tmpdir,
"-M",
"test_tabular",
]
assert pytest.main(args) == ExitCode.OK
res = open(next(function_tmpdir.rglob(test_tabular_fname))).readlines()[0]
assert tabular == res
59 changes: 59 additions & 0 deletions autotest/test_markers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from os import environ
from platform import python_version, system
from shutil import which

from packaging.version import Version

from modflow_devtools.markers import *

exe = "pytest"


@requires_exe(exe)
def test_require_exe():
assert which(exe)
require_exe(exe)
require_program(exe)


exes = [exe, "python"]


@require_exe(*exes)
def test_require_exe_multiple():
assert all(which(exe) for exe in exes)


@requires_pkg("pytest")
def test_requires_pkg():
import numpy

assert numpy is not None


@requires_pkg("pytest", "pluggy")
def test_requires_pkg_multiple():
import pluggy
import pytest

assert pluggy is not None and pytest is not None


@requires_platform("Windows")
def test_requires_platform():
assert system() == "Windows"


@excludes_platform("Darwin", ci_only=True)
def test_requires_platform_ci_only():
if "CI" in environ:
assert system() != "Darwin"


py_ver = python_version()


@pytest.mark.parametrize("version", ["3.12", "3.11"])
def test_requires_python(version):
if Version(py_ver) >= Version(version):
assert requires_python(version)
Loading

0 comments on commit 5d5d740

Please sign in to comment.