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
5 changes: 4 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ jobs:
needs: [set-os, prepare-nonhindcast-cache]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
module:
[wave, tidal, river, dolfyn, power, loads, mooring, acoustics, utils]
Expand All @@ -478,15 +479,17 @@ jobs:
path: ~/.cache/mhkit

- name: Install system dependencies
if: matrix.module != 'river' || matrix.module != 'power' || matrix.module != 'utils' || matrix.module != 'loads'
run: sudo apt-get install -y libhdf5-dev libnetcdf-dev

- name: Install MHKiT with optional dependency
run: |
python -m pip install --upgrade pip
pip install "mhkit[${{ matrix.module }}]"
pip install -e ".[${{ matrix.module }}]"
pip install pytest

- name: Reinstall h5py and netCDF4 with system libraries
if: matrix.module != 'river' || matrix.module != 'power' || matrix.module != 'utils' || matrix.module != 'loads'
run: "pip install --force-reinstall --no-binary=:all: h5py netCDF4"

- name: Run tests for ${{ matrix.module }}
Expand Down
34 changes: 33 additions & 1 deletion mhkit/wave/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
import importlib

from mhkit.wave import resource
from mhkit.wave import io
from mhkit.wave import graphics
from mhkit.wave import performance
from mhkit.wave import contours


def __getattr__(name):
"""
Lazy load the wave.io submodule using PEP 562 module-level __getattr__.

This defers importing heavy wave.io dependencies (rex, netCDF4, etc,) until
they are actually accessed, improving import time for users who don't need
all wave submodules, and avoiding import errors for users who have specified
module level installs that need wave module functions, but not wave.io functions.
"""
if name == "io":
# This uses importlib.import_module() here, not "from mhkit.wave import io"
# because when Python executes getattr(), it looks for 'io' as an attribute of
# mhkit.wave. At this point in the module loading code 'io' doesn't exist yet and
# Python calls __getattr__('io') again. This triggers the same "from" statement,
# which calls __getattr__('io') again yielding a RecursionError.
#
# To fix this uses importlib.import_module("mhkit.wave.io") which loads the module directly
# using the absolute path without doing attribute lookup on the parent.
#
# The statement "from mhkit.wave import io" is equivalent to:
# io = getattr(mhkit.wave, 'io')
#
io = importlib.import_module("mhkit.wave.io")

# Cache the module so subsequent accesses bypass __getattr__ entirely
globals()[name] = io
return io

raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies = [
"xarray>=2024.6.0",
"matplotlib>=3.9.1",
"pecos>=0.3.0",
"requests",
]

[project.optional-dependencies]
Expand All @@ -38,20 +39,17 @@ wave = [
"pytz",
"NREL-rex>=0.2.63",
"beautifulsoup4",
"requests",
"bottleneck",
"lxml"
]

tidal = [
"netCDF4>=1.7.1.post1",
"requests",
"bottleneck"
]

river = [
"netCDF4>=1.7.1.post1",
"requests",
"bottleneck",
]

Expand All @@ -66,7 +64,9 @@ power = [
]

loads = [
"fatpack"
"fatpack",
"statsmodels>=0.14.2",
"scikit-learn>=1.5.1",
]

mooring = [
Expand Down
Loading