Skip to content

Commit 68fe393

Browse files
ssolsonsimmsaakeeste
authored
Modernize Package Configuration (#400)
This PR modernizes the package configuration by migrating from `setup.py` to `pyproject.toml` and adds a new optional dependency group for examples. It also includes various code improvements and CI workflow updates. - Migrated from `setup.py` to `pyproject.toml` for modern Python packaging - Removed `requirements.txt` in favor of `pyproject.toml` dependency management - Added new optional dependency groups for pip installs: E.g: - `examples`: Includes all dependencies needed for running examples - `all`: Includes all module dependencies - Individual module groups (wave, tidal, river, etc.) - Updated GitHub Actions workflows to use `pip install -e ".[all, dev]"` instead of `pip install -e .` --------- Co-authored-by: Andrew Simms <[email protected]> Co-authored-by: akeeste <[email protected]>
1 parent 650dc95 commit 68fe393

File tree

9 files changed

+229
-145
lines changed

9 files changed

+229
-145
lines changed

.github/workflows/main.yml

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
shell: bash -l {0}
8484
run: |
8585
conda activate mhkit-env
86-
pip install -e . --no-deps
86+
pip install -e ".[all,dev]" --no-deps
8787
8888
- name: Prepare non-hindcast API data
8989
shell: bash -l {0}
@@ -134,7 +134,7 @@ jobs:
134134
shell: bash -l {0}
135135
run: |
136136
conda activate mhkit-env
137-
pip install -e . --no-deps
137+
pip install -e ".[all,dev]" --no-deps
138138
139139
- name: Prepare Wave Hindcast data
140140
shell: bash -l {0}
@@ -183,7 +183,7 @@ jobs:
183183
shell: bash -l {0}
184184
run: |
185185
conda activate mhkit-env
186-
pip install -e . --no-deps
186+
pip install -e ".[all,dev]" --no-deps
187187
188188
- name: Prepare Wind Hindcast data
189189
shell: bash -l {0}
@@ -286,7 +286,7 @@ jobs:
286286
shell: bash -l {0}
287287
run: |
288288
python -m pip install --upgrade pip wheel
289-
pip install coverage pytest coveralls .
289+
pip install -e ".[all,dev]"
290290
291291
- name: Install setuptools for Python 3.12
292292
if: matrix.python-version == '3.12'
@@ -354,7 +354,7 @@ jobs:
354354
shell: bash -l {0}
355355
run: |
356356
conda activate mhkit-env
357-
pip install -e . --no-deps
357+
pip install -e ".[all,dev]" --no-deps
358358
359359
- name: Download Wave Hindcast data from artifact
360360
uses: actions/download-artifact@v4
@@ -390,6 +390,39 @@ jobs:
390390
parallel: true
391391
path-to-lcov: ./coverage.lcov
392392

393+
test-optional-pip-dependencies:
394+
needs: [set-os, prepare-nonhindcast-cache]
395+
runs-on: ubuntu-latest
396+
strategy:
397+
matrix:
398+
module:
399+
[wave, tidal, river, dolfyn, power, loads, mooring, acoustics, utils]
400+
python-version: ['3.12']
401+
402+
steps:
403+
- uses: actions/checkout@v4
404+
405+
- name: Set up Python ${{ matrix.python-version }}
406+
uses: actions/setup-python@v5
407+
with:
408+
python-version: ${{ matrix.python-version }}
409+
410+
- name: Download non-hindcast data
411+
uses: actions/download-artifact@v4
412+
with:
413+
name: data
414+
path: ~/.cache/mhkit
415+
416+
- name: Install MHKiT with optional dependency
417+
run: |
418+
python -m pip install --upgrade pip
419+
pip install "mhkit[${{ matrix.module }}]"
420+
pip install pytest
421+
422+
- name: Run tests for ${{ matrix.module }}
423+
run: |
424+
python -m pytest -c .github/workflows/pytest.ini mhkit/tests/${{ matrix.module }}/
425+
393426
notebook-matrix:
394427
runs-on: ubuntu-latest
395428
needs:
@@ -476,7 +509,7 @@ jobs:
476509
shell: bash -l {0}
477510
run: |
478511
conda activate mhkit-env
479-
pip install -e . --no-deps
512+
pip install -e ".[all,dev]" --no-deps
480513
481514
- name: Download non-hindcast data
482515
uses: actions/download-artifact@v4

.github/workflows/pylint.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ jobs:
1818
- name: Install dependencies
1919
run: |
2020
python -m pip install --upgrade pip wheel
21-
pip install pylint
22-
pip install .
21+
pip install ".[all,dev]"
2322
2423
- name: Run Pylint on mhkit/loads/
2524
run: |

mhkit/__init__.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
import warnings as _warn
2-
from mhkit import wave
3-
from mhkit import river
4-
from mhkit import tidal
5-
from mhkit import qc
6-
from mhkit import utils
7-
from mhkit import power
8-
from mhkit import loads
9-
from mhkit import dolfyn
10-
from mhkit import mooring
11-
from mhkit import acoustics
2+
import importlib
123

134
# Register datetime converter for a matplotlib plotting methods
145
from pandas.plotting import register_matplotlib_converters as _rmc
@@ -28,3 +19,33 @@
2819
retains certain rights in this software."""
2920

3021
__license__ = "Revised BSD License"
22+
23+
24+
def __getattr__(name):
25+
"""Lazy import modules to handle pip optional dependencies."""
26+
known_modules = [
27+
"wave",
28+
"river",
29+
"tidal",
30+
"qc",
31+
"utils",
32+
"power",
33+
"loads",
34+
"dolfyn",
35+
"mooring",
36+
"acoustics",
37+
]
38+
39+
if name in known_modules:
40+
try:
41+
return importlib.import_module(f"mhkit.{name}")
42+
except ModuleNotFoundError:
43+
error_msg = "Module dependencies not found.\n"
44+
error_msg += f"To install the {name} module, run:\n"
45+
error_msg += f" pip install mhkit[{name}]\n\n"
46+
error_msg += "Or install all modules with:\n"
47+
error_msg += " pip install mhkit[all]"
48+
else:
49+
error_msg = f"module 'mhkit' has no attribute '{name}'"
50+
51+
raise AttributeError(error_msg)

mhkit/acoustics/graphics.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def plot_spectrogram(
3333
fmax: int = 100000,
3434
fig: plt.figure = None,
3535
ax: plt.Axes = None,
36-
**kwargs
36+
**kwargs,
3737
) -> Tuple[plt.figure, plt.Axes]:
3838
"""
3939
Plots the spectrogram of the sound pressure spectral density level.
@@ -86,7 +86,7 @@ def plot_spectrogram(
8686
spsdl[freq].values,
8787
spsdl.transpose(freq, time),
8888
shading="nearest",
89-
**kwargs
89+
**kwargs,
9090
)
9191
fig.colorbar(h, ax=ax, label=getattr(spsdl, "units", None))
9292
ax.set(ylim=(fmin, fmax), xlabel="Time", ylabel="Frequency [Hz]")
@@ -100,7 +100,7 @@ def plot_spectra(
100100
fmax: int = 100000,
101101
fig: plt.figure = None,
102102
ax: plt.Axes = None,
103-
**kwargs
103+
**kwargs,
104104
) -> Tuple[plt.figure, plt.Axes]:
105105
"""
106106
Plots spectral density. X axis is log-transformed.

mhkit/dolfyn/adp/discharge.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import numpy as np
22
import xarray as xr
3-
import cartopy.crs as ccrs
43

54

65
def discharge(ds, water_depth, rho, mu=None, surface_offset=0, utm_zone=10):
@@ -54,6 +53,9 @@ def discharge(ds, water_depth, rho, mu=None, surface_offset=0, utm_zone=10):
5453
- `reynolds_number`: (1) Reynolds number, unitless
5554
"""
5655

56+
# Lazy import cartopy
57+
import cartopy.crs as ccrs
58+
5759
def _extrapolate_to_bottom(vel, bottom, rng):
5860
"""
5961
Linearly extrapolate velocity values from the deepest valid bin down to zero at the seafloor.

mhkit/river/io/usgs.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,23 @@ def request_usgs_data(
199199

200200
print("Data request URL: ", data_url + api_query)
201201

202-
response = requests.get(url=data_url + api_query, proxies=proxy, timeout=timeout)
203-
text = json.loads(response.text)
202+
max_retries = 3
203+
retry_count = 0
204+
while retry_count < max_retries:
205+
try:
206+
response = requests.get(
207+
url=data_url + api_query, proxies=proxy, timeout=timeout, verify=True
208+
)
209+
text = json.loads(response.text)
210+
break
211+
except requests.exceptions.SSLError as e:
212+
retry_count += 1
213+
if retry_count == max_retries:
214+
raise e
215+
print(
216+
f"SSL Error occurred, retrying... (Attempt {retry_count}/{max_retries})"
217+
)
218+
continue
204219

205220
# handle_caching is only set-up for pandas, so force this data to output as pandas for now
206221
data = _read_usgs_json(text, True)

pyproject.toml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
[build-system]
2+
requires = ["setuptools>=61.0", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "mhkit"
7+
# `version` is read from `[tools.setuptools.dynamic] version` during build: https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata
8+
dynamic = ["version"]
9+
description = "Marine and Hydrokinetic Toolkit"
10+
readme = "README.md"
11+
authors = [
12+
{name = "MHKiT developers"}
13+
]
14+
license = {text = "Revised BSD"}
15+
classifiers = [
16+
"Development Status :: 3 - Alpha",
17+
"Programming Language :: Python :: 3",
18+
"Topic :: Scientific/Engineering",
19+
"Intended Audience :: Science/Research",
20+
"Operating System :: OS Independent",
21+
]
22+
requires-python = ">=3.10"
23+
dependencies = [
24+
"numpy>=2.0.0",
25+
"pandas>=2.2.2",
26+
"scipy>=1.14.0",
27+
"xarray>=2024.6.0",
28+
"matplotlib>=3.9.1",
29+
"pecos>=0.3.0",
30+
]
31+
32+
[project.optional-dependencies]
33+
# Core dependencies for each module
34+
wave = [
35+
"scikit-learn>=1.5.1",
36+
"statsmodels>=0.14.2",
37+
"netCDF4>=1.7.1.post1",
38+
"pytz",
39+
"NREL-rex>=0.2.63",
40+
"beautifulsoup4",
41+
"requests",
42+
"bottleneck",
43+
"lxml"
44+
]
45+
46+
tidal = [
47+
"netCDF4>=1.7.1.post1",
48+
"requests",
49+
"bottleneck"
50+
]
51+
52+
river = [
53+
"netCDF4>=1.7.1.post1",
54+
"requests",
55+
"bottleneck",
56+
]
57+
58+
dolfyn = [
59+
"h5py>=3.11.0",
60+
"h5pyd>=0.18.0",
61+
"netCDF4>=1.7.1.post1",
62+
"cartopy",
63+
]
64+
65+
power = [
66+
]
67+
68+
loads = [
69+
"fatpack"
70+
]
71+
72+
mooring = [
73+
]
74+
75+
acoustics = [
76+
77+
]
78+
79+
qc = [
80+
81+
]
82+
83+
utils = [
84+
85+
]
86+
87+
# Development dependencies
88+
dev = [
89+
"pytest",
90+
"pylint",
91+
"pytest-cov",
92+
"pre-commit",
93+
"coverage",
94+
"coveralls"
95+
]
96+
97+
98+
99+
# Install all optional dependencies
100+
all = [
101+
"mhkit[wave]",
102+
"mhkit[tidal]",
103+
"mhkit[river]",
104+
"mhkit[dolfyn]",
105+
"mhkit[power]",
106+
"mhkit[loads]",
107+
"mhkit[mooring]",
108+
"mhkit[acoustics]",
109+
"mhkit[qc]",
110+
"mhkit[utils]",
111+
]
112+
113+
# Examples dependencies
114+
examples = [
115+
"jupyter",
116+
"notebook",
117+
"ipykernel",
118+
"nbval",
119+
"utm",
120+
"folium",
121+
"mhkit[all]",
122+
123+
]
124+
125+
[project.urls]
126+
Homepage = "https://github.com/MHKiT-Software/mhkit-python"
127+
Documentation = "https://mhkit-software.github.io/MHKiT"
128+
129+
[tool.setuptools]
130+
packages = ["mhkit"]
131+
zip-safe = false
132+
include-package-data = true
133+
134+
[tool.setuptools.dynamic]
135+
version = {attr = "mhkit.__version__"}

requirements.txt

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)