From ca66b2c79d2aaa0010678008e829e0750b234def Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Wed, 4 Nov 2020 17:42:07 -0500 Subject: [PATCH] build on run instead of build on import --- .archive/legacy.py | 2 +- .github/workflows/ci.yml | 11 +++++------ .mypy.ini | 2 ++ README.md | 2 +- setup.cfg | 2 +- src/iri2016/base.py | 22 +++++++--------------- src/iri2016/build.py | 17 +++++++++++++++++ src/iri2016/profile.py | 6 ++---- 8 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 src/iri2016/build.py diff --git a/.archive/legacy.py b/.archive/legacy.py index 7c16201..4597671 100644 --- a/.archive/legacy.py +++ b/.archive/legacy.py @@ -12,7 +12,7 @@ def Switches(): """ - IRI switches to turn on/off several options + IRI switches to turn on/off several options """ jf = np.ones(50, dtype=bool) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d50c5f7..deb66c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,18 +20,17 @@ jobs: timeout-minutes: 2 - run: flake8 - - run: mypy . src + - run: mypy - run: pytest env: CC: gcc-9 FC: gfortran-9 -# codecov coverage - - run: pip install codecov pytest-cov - - run: pytest --cov --cov-report=xml - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 + # - run: pip install codecov pytest-cov + # - run: pytest --cov --cov-report=xml + # - name: Upload coverage to Codecov + # uses: codecov/codecov-action@v1 macos: diff --git a/.mypy.ini b/.mypy.ini index 2882ee5..a4295f0 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,4 +1,6 @@ [mypy] +files = src/ + ignore_missing_imports = True strict_optional = False allow_redefinition = True diff --git a/README.md b/README.md index 47a1770..c4bdce5 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ pip install -e iri2016 ``` This Python wrapper of IRI2016 uses our build-on-run technique. -On the first `import iri2016` the Fortran code is built. +On the first run or `iri2016.IRI()` the Fortran code is built--we call this "build on run". If you have errors about building on the first run, ensure that your Fortran compiler is specified in environment variable FC--this is what most build systems use to indicate the desired Fortran compiler (name or full path). diff --git a/setup.cfg b/setup.cfg index 8b43c01..45e6ace 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = iri2016 -version = 1.11.0 +version = 1.11.1 author = Michael Hirsch, Ph.D.; Ronald Ilma author_email = scivision@users.noreply.github.com description = IRI2016 International Reference Ionosphere from Python diff --git a/src/iri2016/base.py b/src/iri2016/base.py index 48e1382..20028c7 100644 --- a/src/iri2016/base.py +++ b/src/iri2016/base.py @@ -6,23 +6,9 @@ import os import numpy as np import typing as T -import shutil import importlib.resources -iri_name = "iri2016_driver" -if os.name == "nt": - iri_name += ".exe" - -if not importlib.resources.is_resource(__package__, iri_name): - ctest = shutil.which("ctest") - if not ctest: - raise ImportError("could not find CMake, which is used to build IRI2016") - if not (shutil.which("ninja") or shutil.which("make")): - raise ImportError("Ninja not found. Please do 'python -m pip install ninja'") - with importlib.resources.path(__package__, "setup.cmake") as setup: - ret = subprocess.run([ctest, "-S", str(setup), "-VV"]) - if ret.returncode != 0: - raise ImportError("not able to compile IRI2016 Fortran code.") +from .build import build SIMOUT = ["ne", "Tn", "Ti", "Te", "nO+", "nH+", "nHe+", "nO2+", "nNO+", "nCI", "nN+"] @@ -36,7 +22,13 @@ def IRI(time: T.Union[str, datetime], altkmrange: T.Sequence[float], glat: float assert len(altkmrange) == 3, "altitude (km) min, max, step" assert isinstance(glat, (int, float)) and isinstance(glon, (int, float)), "glat, glon is scalar" + # %% build IRI executable if needed + iri_name = "iri2016_driver" + if os.name == "nt": + iri_name += ".exe" + build(iri_name) + # %% run IRI with importlib.resources.path(__package__, iri_name) as exe: cmd = [ str(exe), diff --git a/src/iri2016/build.py b/src/iri2016/build.py new file mode 100644 index 0000000..e1bd7d4 --- /dev/null +++ b/src/iri2016/build.py @@ -0,0 +1,17 @@ +import importlib.resources +import shutil +import subprocess + + +def build(exe_name: str): + + if not importlib.resources.is_resource(__package__, exe_name): + ctest = shutil.which("ctest") + if not ctest: + raise RuntimeError("could not find CMake") + if not (shutil.which("ninja") or shutil.which("make")): + raise RuntimeError("Ninja not found. Please do 'python -m pip install ninja'") + with importlib.resources.path(__package__, "setup.cmake") as setup: + ret = subprocess.run([ctest, "-S", str(setup), "-VV"]) + if ret.returncode != 0: + raise RuntimeError(f"not able to build {exe_name}") diff --git a/src/iri2016/profile.py b/src/iri2016/profile.py index 17dde64..5b43d39 100644 --- a/src/iri2016/profile.py +++ b/src/iri2016/profile.py @@ -25,8 +25,7 @@ def datetimerange(start: datetime, end: datetime, step: timedelta) -> T.List[dat def timeprofile(tlim: tuple, dt: timedelta, altkmrange: T.Sequence[float], glat: float, glon: float) -> xarray.Dataset: - """compute IRI altitude profile over time range for fixed lat/lon - """ + """compute IRI altitude profile over time range for fixed lat/lon""" times = datetimerange(tlim[0], tlim[1], dt) @@ -52,8 +51,7 @@ def timeprofile(tlim: tuple, dt: timedelta, altkmrange: T.Sequence[float], glat: def geoprofile(latrange: T.Sequence[float], glon: float, altkm: float, time: T.Union[str, datetime]) -> xarray.Dataset: - """compute IRI altitude profiles at time, over lat or lon range - """ + """compute IRI altitude profiles at time, over lat or lon range""" glat = np.arange(*latrange)