Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 1.3.0 #126

Merged
merged 13 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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