Skip to content
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
35 changes: 20 additions & 15 deletions .github/workflows/commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ jobs:
run:
shell: bash
timeout-minutes: 10
env:
PYTHON_VERSION: 3.8

steps:
- name: Checkout repo
Expand All @@ -114,33 +116,37 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: pyproject.toml

- name: Install Python dependencies
run: |
pip install --upgrade pip
pip install .
pip install ".[test, optional]"

pip install ".[test,optional]"
- name: Install Modflow executables
uses: modflowpy/install-modflow-action@v1

- name: Run smoke tests
working-directory: ./autotest
run: |
pytest -v -n=auto --smoke --durations=0 --keep-failed=.failed

- name: Smoke test
working-directory: autotest
run: pytest -v -n=auto --smoke --cov=flopy --cov-report=xml --durations=0 --keep-failed=.failed
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload failed test outputs
uses: actions/upload-artifact@v3
if: failure()
with:
name: failed-smoke-${{ matrix.os }}-${{ matrix.python-version }}
path: |
./autotest/.failed/**
name: failed-smoke-${{ runner.os }}-${{ env.PYTHON_VERSION }}
path: ./autotest/.failed/**

- name: Upload coverage
if: github.repository_owner == 'modflowpy' && (github.event_name == 'push' || github.event_name == 'pull_request')
uses: codecov/codecov-action@v3
with:
files: ./autotest/coverage.xml

test:
name: Test
Expand Down Expand Up @@ -220,7 +226,7 @@ jobs:
if: runner.os != 'Windows'
working-directory: ./autotest
run: |
pytest -v -m="not example and not regression" -n=auto --cov=flopy --cov-report=xml --durations=0 --keep-failed=.failed --dist loadfile
pytest -v -m="not example and not regression" -n=auto --cov=flopy --cov-append --cov-report=xml --durations=0 --keep-failed=.failed --dist loadfile
coverage report
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -230,7 +236,7 @@ jobs:
shell: bash -l {0}
working-directory: ./autotest
run: |
pytest -v -m="not example and not regression" -n=auto --cov=flopy --cov-report=xml --durations=0 --keep-failed=.failed --dist loadfile
pytest -v -m="not example and not regression" -n=auto --cov=flopy --cov-append --cov-report=xml --durations=0 --keep-failed=.failed --dist loadfile
coverage report
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -244,8 +250,7 @@ jobs:
./autotest/.failed/**

- name: Upload coverage
if:
github.repository_owner == 'modflowpy' && (github.event_name == 'push' || github.event_name == 'pull_request')
if: github.repository_owner == 'modflowpy' && (github.event_name == 'push' || github.event_name == 'pull_request')
uses: codecov/codecov-action@v3
with:
files: ./autotest/coverage.xml
75 changes: 75 additions & 0 deletions .github/workflows/optional.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: FloPy optional dependency testing
on:
schedule:
- cron: '0 8 * * *' # run at 8 AM UTC (12 am PST)
jobs:
test:
name: Test
runs-on: ubuntu-latest
defaults:
run:
shell: bash
timeout-minutes: 10
env:
PYTHON_VERSION: 3.8
strategy:
fail-fast: false
matrix:
optdeps:
# - "all optional dependencies"
- "no optional dependencies"
- "some optional dependencies"

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

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: pyproject.toml

- name: Install Python dependencies
run: |
pip install --upgrade pip
pip install .
pip install ".[test]"

# Install optional dependencies according to matrix.optdeps.
# If matrix.optdeps is "some" remove 3 optional dependencies
# selected randomly using the current date as the seed.
if [[ ! "${{ matrix.optdeps }}" == *"no"* ]]; then
pip install ".[optional]"
fi
if [[ "${{ matrix.optdeps }}" == *"some"* ]]; then
deps=$(sed '/optional =/,/]/!d' pyproject.toml | sed -e '1d;$d' -e 's/\"//g' -e 's/,//g' | tr -d ' ' | cut -f 1 -d ';')
rmvd=$(echo $deps | tr ' ' '\n' | shuf --random-source <(yes date +%d.%m.%y) | head -n 3)
echo "Removing optional dependencies: $rmvd" >> removed_dependencies.txt
cat removed_dependencies.txt
pip uninstall --yes $rmvd
fi

- name: Upload removed dependencies log
uses: actions/upload-artifact@v3
with:
name: smoke-test-removed-dependencies
path: ./removed_dependencies.txt

- name: Install Modflow executables
uses: modflowpy/install-modflow-action@v1

- name: Smoke test (${{ matrix.optdeps }})
working-directory: autotest
run: pytest -v -n=auto --smoke --cov=flopy --cov-report=xml --durations=0 --keep-failed=.failed
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload failed test outputs
uses: actions/upload-artifact@v3
if: failure()
with:
name: failed-smoke-${{ runner.os }}-${{ env.PYTHON_VERSION }}
path: ./autotest/.failed/**

12 changes: 7 additions & 5 deletions autotest/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
from flopy.utils.geometry import Polygon


HAS_PYPROJ = has_pkg("pyproj", strict=True)
if HAS_PYPROJ:
import pyproj


def namfiles() -> List[Path]:
mf2005_path = get_example_data_path() / "mf2005_test"
return list(mf2005_path.rglob("*.nam"))
Expand Down Expand Up @@ -330,10 +335,7 @@ def test_write_gridlines_shapefile(function_tmpdir):

for suffix in [".dbf", ".shp", ".shx"]:
assert outshp.with_suffix(suffix).exists()
if has_pkg("pyproj"):
assert outshp.with_suffix(".prj").exists()
else:
assert not outshp.with_suffix(".prj").exists()
assert outshp.with_suffix(".prj").exists() == HAS_PYPROJ

with shapefile.Reader(str(outshp)) as sf:
assert sf.shapeType == shapefile.POLYLINE
Expand Down Expand Up @@ -1378,7 +1380,7 @@ def test_vtk_unstructured(function_tmpdir, example_data_path):
assert np.allclose(np.ravel(top), top2), "Field data not properly written"


@requires_pkg("pyvista")
@requires_pkg("vtk", "pyvista")
def test_vtk_to_pyvista(function_tmpdir, example_data_path):
from autotest.test_mp7_cases import Mp7Cases

Expand Down
26 changes: 19 additions & 7 deletions autotest/test_grid.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import re
import warnings
from contextlib import nullcontext
from warnings import warn

import matplotlib
Expand All @@ -22,7 +23,7 @@
from flopy.utils.triangle import Triangle
from flopy.utils.voronoi import VoronoiGrid

HAS_PYPROJ = has_pkg("pyproj")
HAS_PYPROJ = has_pkg("pyproj", strict=True)
if HAS_PYPROJ:
import pyproj

Expand Down Expand Up @@ -594,9 +595,14 @@ def do_checks(g):
do_checks(UnstructuredGrid(**d, crs=crs))
do_checks(VertexGrid(vertices=d["vertices"], crs=crs))

# only check deprecations if pyproj is available
pyproj_avail_context = (
pytest.deprecated_call() if HAS_PYPROJ else nullcontext()
)

# test deprecated 'epsg' parameter
if isinstance(crs, int):
with pytest.deprecated_call():
with pyproj_avail_context:
do_checks(StructuredGrid(delr=delr, delc=delc, epsg=crs))

if HAS_PYPROJ and crs == 26916:
Expand All @@ -612,14 +618,14 @@ def do_checks(g):
do_checks(StructuredGrid(delr=delr, delc=delc, prjfile=prjfile))

# test deprecated 'prj' parameter
with pytest.deprecated_call():
with pyproj_avail_context:
do_checks(StructuredGrid(delr=delr, delc=delc, prj=prjfile))

# test deprecated 'proj4' parameter
with warnings.catch_warnings():
warnings.simplefilter("ignore") # pyproj warning about conversion
proj4 = crs_obj.to_proj4()
with pytest.deprecated_call():
with pyproj_avail_context:
do_checks(StructuredGrid(delr=delr, delc=delc, proj4=proj4))


Expand Down Expand Up @@ -675,9 +681,14 @@ def do_checks(g, *, exp_srs=expected_srs, exp_epsg=expected_epsg):
sg.set_coord_info(crs=26915, merge_coord_info=False)
do_checks(sg, exp_srs="EPSG:26915", exp_epsg=26915)

# only check deprecations if pyproj is available
pyproj_avail_context = (
pytest.deprecated_call() if HAS_PYPROJ else nullcontext()
)

# test deprecated 'epsg' parameter
if isinstance(crs, int):
with pytest.deprecated_call():
with pyproj_avail_context:
sg.set_coord_info(epsg=crs)
do_checks(sg)

Expand Down Expand Up @@ -827,8 +838,9 @@ def test_grid_crs_exceptions():

# test non-existing file
not_a_file = "not-a-file"
with pytest.raises(FileNotFoundError):
StructuredGrid(delr=delr, delc=delc, prjfile=not_a_file)
if HAS_PYPROJ:
with pytest.raises(FileNotFoundError):
StructuredGrid(delr=delr, delc=delc, prjfile=not_a_file)
# note "sg.prjfile = not_a_file" intentionally does not raise anything

# test unhandled keyword
Expand Down
1 change: 1 addition & 0 deletions autotest/test_gridgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from flopy.utils.gridgen import Gridgen


@requires_exe("gridgen")
def test_ctor_accepts_path_or_string(function_tmpdir):
grid = GridCases().structured_small()

Expand Down
4 changes: 2 additions & 2 deletions autotest/test_gridintersect.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from flopy.utils.gridintersect import GridIntersect
from flopy.utils.triangle import Triangle

if has_pkg("shapely"):
if has_pkg("shapely", strict=True):
from shapely.geometry import (
LineString,
MultiLineString,
Expand Down Expand Up @@ -1221,7 +1221,7 @@ def test_polygon_offset_rot_structured_grid_shapely(rtree):
# %% test rasters


@requires_pkg("rasterstats", "scipy")
@requires_pkg("rasterstats", "scipy", "shapely")
def test_rasters(example_data_path):
ws = example_data_path / "options"
raster_name = "dem.img"
Expand Down
3 changes: 2 additions & 1 deletion autotest/test_mf6.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import numpy as np
import pytest
from modflow_devtools.markers import requires_exe
from modflow_devtools.markers import requires_exe, requires_pkg
from modflow_devtools.misc import set_dir

import flopy
Expand Down Expand Up @@ -637,6 +637,7 @@ def test_binary_write(function_tmpdir, layered):


@requires_exe("mf6")
@requires_pkg("shapely", "scipy")
@pytest.mark.parametrize("layered", [True, False])
def test_vor_binary_write(function_tmpdir, layered):
# build voronoi grid
Expand Down
1 change: 1 addition & 0 deletions autotest/test_model_splitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ def test_metis_splitting_with_lak_sfr(function_tmpdir):


@requires_exe("mf6")
@requires_pkg("pymetis")
def test_save_load_node_mapping(function_tmpdir):
sim_path = get_example_data_path() / "mf6-freyberg"
new_sim_path = function_tmpdir / "mf6-freyberg/split_model"
Expand Down