Skip to content

Commit

Permalink
Merge pull request #46 from wpbonelli/ruff
Browse files Browse the repository at this point in the history
chore: switch to ruff, use devtools fixtures for tests
  • Loading branch information
jlarsen-usgs authored Mar 13, 2024
2 parents 18bf0e1 + 7c1e168 commit c6811fd
Show file tree
Hide file tree
Showing 15 changed files with 122 additions and 159 deletions.
34 changes: 7 additions & 27 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ jobs:
defaults:
run:
shell: bash

steps:

# check out repo
- name: Checkout repo
uses: actions/checkout@v3

Expand All @@ -45,19 +43,16 @@ jobs:
run: |
python -c "import modflowapi; print(modflowapi.__version__)"
lint:
name: lint
runs-on: ubuntu-latest
if: github.event_name != 'schedule'
strategy:
fail-fast: false
defaults:
run:
shell: bash

if: github.event_name != 'schedule'
steps:
# check out repo
- name: Checkout repo
uses: actions/checkout@v3

Expand All @@ -78,20 +73,11 @@ jobs:
python -m pip install --upgrade pip
pip install -e .[lint]
- name: Run black
run: |
echo "if black check fails run"
echo " black ./modflowapi"
echo "and then commit the changes."
black --check ./modflowapi
- name: Run flake8
run: |
flake8 --count --show-source --exit-zero ./modflowapi
- name: Lint
run: ruff check .

- name: Run pylint
run: |
pylint --jobs=2 --errors-only --exit-zero ./modflowapi
- name: Check format
run: ruff format . --check

autotest_extensions:
name: modflowapi extensions autotests
Expand All @@ -105,9 +91,7 @@ jobs:
defaults:
run:
shell: bash

steps:
# check out repo
- name: Checkout repo
uses: actions/checkout@v3

Expand Down Expand Up @@ -148,9 +132,7 @@ jobs:
defaults:
run:
shell: bash

steps:
# check out repo
- name: Checkout repo
uses: actions/checkout@v3

Expand Down Expand Up @@ -192,9 +174,7 @@ jobs:
defaults:
run:
shell: bash

steps:
# check out repo
- name: Checkout repo
uses: actions/checkout@v3

Expand All @@ -212,12 +192,12 @@ jobs:
pip install git+https://[email protected]/MODFLOW-USGS/modflow-devtools@develop
pip install .[test]
- name: Install modflow executables
- name: Install modflow6 nightly build
uses: modflowpy/install-modflow-action@v1
with:
path: ${{ github.workspace }}/autotest
repo: modflow6-nightly-build

- name: Run autotests
working-directory: ./autotest
shell: bash -l {0}
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,8 @@ dmypy.json

# pycharm
.idea/

# library files
**.dll
**.so
**.dylib
50 changes: 5 additions & 45 deletions autotest/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@
import pytest
from filelock import FileLock

from modflow_devtools.download import download_and_unzip

# import modflow-devtools fixtures
pytest_plugins = ["modflow_devtools.fixtures"]


__mf6_examples = "mf6_examples"
__mf6_examples_path = Path(gettempdir()) / __mf6_examples
__mf6_examples_lock = FileLock(Path(gettempdir()) / f"{__mf6_examples}.lock")


def get_mf6_examples_path() -> Path:
pytest.importorskip("modflow_devtools")
from modflow_devtools.download import download_and_unzip

# use file lock so mf6 distribution is downloaded once,
# even when tests are run in parallel with pytest-xdist
__mf6_examples_lock.acquire()
Expand Down Expand Up @@ -95,45 +97,3 @@ def simulation_name_from_model_namfiles(mnams):
metafunc.parametrize(
key, simulations, ids=simulation_name_from_model_namfiles
)


@pytest.fixture(scope="function")
def tmpdir(tmpdir_factory, request) -> Path:
node = (
request.node.name.replace("/", "_")
.replace("\\", "_")
.replace(":", "_")
)
temp = Path(tmpdir_factory.mktemp(node))
yield Path(temp)

keep = request.config.getoption("--keep")
if keep:
copytree(temp, Path(keep) / temp.name)

keep_failed = request.config.getoption("--keep-failed")
if keep_failed and request.node.rep_call.failed:
copytree(temp, Path(keep_failed) / temp.name)


def pytest_addoption(parser):
parser.addoption(
"-K",
"--keep",
action="store",
default=None,
help="Move the contents of temporary test directories to correspondingly named subdirectories at the given "
"location after tests complete. This option can be used to exclude test results from automatic cleanup, "
"e.g. for manual inspection. The provided path is created if it does not already exist. An error is "
"thrown if any matching files already exist.",
)

parser.addoption(
"--keep-failed",
action="store",
default=None,
help="Move the contents of temporary test directories to correspondingly named subdirectories at the given "
"location if the test case fails. This option automatically saves the outputs of failed tests in the "
"given location. The path is created if it doesn't already exist. An error is thrown if files with the "
"same names already exist in the given location.",
)
49 changes: 26 additions & 23 deletions autotest/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
AdvancedPackage,
ArrayPackage,
ListPackage,
pkgvars,
)

data_pth = Path("../examples/data")
Expand All @@ -20,35 +19,39 @@
so = "libmf6" + (
".so"
if os == "Linux"
else ".dylib" if os == "Darwin" else ".dll" if os == "Windows" else None
else ".dylib"
if os == "Darwin"
else ".dll"
if os == "Windows"
else None
)
if so is None:
pytest.skip("Unsupported operating system", allow_module_level=True)


@pytest.mark.parametrize("use_str", [True, False])
def test_ctor_finds_libmf6_by_name(use_str):
api = ModflowApi(so if use_str else Path(so))
ModflowApi(so if use_str else Path(so))


@pytest.mark.parametrize("use_str", [True, False])
def test_ctor_finds_libmf6_by_relpath(tmpdir, use_str):
shutil.copy(so, tmpdir)
inner = tmpdir / "inner"
def test_ctor_finds_libmf6_by_relpath(function_tmpdir, use_str):
shutil.copy(so, function_tmpdir)
inner = function_tmpdir / "inner"
inner.mkdir()
with set_dir(inner):
so_path = f"../{so}"
api = ModflowApi(so_path if use_str else Path(so_path))
ModflowApi(so_path if use_str else Path(so_path))


@pytest.mark.parametrize("use_str", [True, False])
def test_ctor_finds_libmf6_by_abspath(tmpdir, use_str):
shutil.copy(so, tmpdir)
so_path = tmpdir / so
api = ModflowApi(str(so_path) if use_str else so_path)
def test_ctor_finds_libmf6_by_abspath(function_tmpdir, use_str):
shutil.copy(so, function_tmpdir)
so_path = function_tmpdir / so
ModflowApi(str(so_path) if use_str else so_path)


def test_dis_model(tmpdir):
def test_dis_model(function_tmpdir):
def callback(sim, step):
"""
Callback function
Expand Down Expand Up @@ -134,7 +137,7 @@ def callback(sim, step):

name = "dis_model"
sim_pth = data_pth / name
test_pth = tmpdir / name
test_pth = function_tmpdir / name
shutil.copytree(sim_pth, test_pth, dirs_exist_ok=True)

try:
Expand All @@ -143,7 +146,7 @@ def callback(sim, step):
raise Exception(e)


def test_disv_model(tmpdir):
def test_disv_model(function_tmpdir):
def callback(sim, step):
"""
Callback function
Expand Down Expand Up @@ -220,7 +223,7 @@ def callback(sim, step):

name = "disv_model"
sim_pth = data_pth / name
test_pth = tmpdir / name
test_pth = function_tmpdir / name
shutil.copytree(sim_pth, test_pth, dirs_exist_ok=True)

try:
Expand All @@ -229,7 +232,7 @@ def callback(sim, step):
raise Exception(e)


def test_disu_model(tmpdir):
def test_disu_model(function_tmpdir):
def callback(sim, step):
"""
Callback function
Expand Down Expand Up @@ -300,7 +303,7 @@ def callback(sim, step):

name = "disu_model"
sim_pth = data_pth / name
test_pth = tmpdir / name
test_pth = function_tmpdir / name
shutil.copytree(sim_pth, test_pth, dirs_exist_ok=True)

try:
Expand All @@ -309,7 +312,7 @@ def callback(sim, step):
raise Exception(e)


def test_two_models(tmpdir):
def test_two_models(function_tmpdir):
def callback(sim, step):
"""
Callback function
Expand All @@ -326,7 +329,7 @@ def callback(sim, step):

name = "two_models"
sim_pth = data_pth / name
test_pth = tmpdir / name
test_pth = function_tmpdir / name
shutil.copytree(sim_pth, test_pth, dirs_exist_ok=True)

try:
Expand All @@ -335,7 +338,7 @@ def callback(sim, step):
raise Exception(e)


def test_ats_model(tmpdir):
def test_ats_model(function_tmpdir):
def callback(sim, step):
if step == Callbacks.stress_period_start:
if sim.kper == 0 and sim.kstp == 0:
Expand All @@ -350,7 +353,7 @@ def callback(sim, step):

name = "ats0"
sim_pth = data_pth / name
test_pth = tmpdir / name
test_pth = function_tmpdir / name
shutil.copytree(sim_pth, test_pth, dirs_exist_ok=True)

try:
Expand All @@ -359,7 +362,7 @@ def callback(sim, step):
raise Exception(e)


def test_rhs_hcof_advanced(tmpdir):
def test_rhs_hcof_advanced(function_tmpdir):
def callback(sim, step):
model = sim.test_model
if step == Callbacks.timestep_start:
Expand Down Expand Up @@ -410,7 +413,7 @@ def callback(sim, step):

name = "dis_model"
sim_pth = data_pth / name
test_pth = tmpdir / name
test_pth = function_tmpdir / name
shutil.copytree(sim_pth, test_pth, dirs_exist_ok=True)

try:
Expand Down
16 changes: 10 additions & 6 deletions autotest/test_mf6_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,26 @@
dll = "libmf6"


def test_mf6_example_simulations(tmpdir, mf6_example_namfiles):
def test_mf6_example_simulations(function_tmpdir, mf6_example_namfiles):
"""
MF6 examples parametrized by simulation. `mf6_example_namfiles` is a list
of models to run in order provided. Coupled models share the same tempdir
Parameters
----------
tmpdir: function-scoped temporary directory fixture
function_tmpdir: function-scoped temporary directory fixture
mf6_example_namfiles: ordered list of namfiles for 1+ coupled models
"""
if len(mf6_example_namfiles) == 0:
pytest.skip("No namfiles (expected ordered collection)")
namfile = Path(mf6_example_namfiles[0])

nested = is_nested(namfile)
tmpdir = Path(tmpdir / "workspace")
function_tmpdir = Path(function_tmpdir / "workspace")

copytree(
src=namfile.parent.parent if nested else namfile.parent, dst=tmpdir
src=namfile.parent.parent if nested else namfile.parent,
dst=function_tmpdir,
)

def callback(sim, step):
Expand All @@ -37,12 +38,15 @@ def run_models():
# run models in order received (should be alphabetical, so gwf precedes gwt)
for namfile in mf6_example_namfiles:
namfile_path = Path(namfile).resolve()
namfile_name = namfile_path.name
model_path = namfile_path.parent

# working directory must be named according to the name file's parent (e.g.
# 'mf6gwf') because coupled models refer to each other with relative paths
wrkdir = Path(tmpdir / model_path.name) if nested else tmpdir
wrkdir = (
Path(function_tmpdir / model_path.name)
if nested
else function_tmpdir
)
try:
run_simulation(dll, wrkdir, callback, verbose=True)
except Exception as e:
Expand Down
Loading

0 comments on commit c6811fd

Please sign in to comment.