[Python] Defer import of h5py until time of use

This avoids potential conflicts between the versions of libhdf5 linked
to the Cantera library and h5py, which could occur when a C++ main
application made use of the Python ExtensibleRate class.
This commit is contained in:
Ray Speth
2022-12-20 16:10:51 -05:00
committed by Ray Speth
parent 0aca5f7004
commit f550faa477
5 changed files with 38 additions and 40 deletions

View File

@@ -53,7 +53,7 @@ jobs:
run: |
python3 -m pip install ruamel.yaml scons==3.1.2 numpy cython pandas pytest \
pytest-github-actions-annotate-failures
python3 -m pip install h5py || python3 -m pip install --no-binary=h5py h5py
python3 -m pip install h5py
- name: Build Cantera
run: |
python3 `which scons` build env_vars=all -j2 debug=n --debug=time \
@@ -94,8 +94,7 @@ jobs:
run: python3 -m pip install -U pip setuptools wheel
- name: Install Python dependencies
run: |
python3 -m pip install ruamel.yaml scons numpy cython pandas pytest pytest-github-actions-annotate-failures
python3 -m pip install --no-binary=h5py h5py
python3 -m pip install ruamel.yaml scons numpy cython pandas h5py pytest pytest-github-actions-annotate-failures
- name: Build Cantera
run: python3 `which scons` build env_vars=all
CXX=clang++-12 CC=clang-12 f90_interface=n extra_lib_dirs=/usr/lib/llvm/lib
@@ -194,9 +193,8 @@ jobs:
run: python3 -m pip install -U pip setuptools wheel
- name: Install Python dependencies
run: |
python3 -m pip install ruamel.yaml scons numpy cython pandas scipy pytest \
python3 -m pip install ruamel.yaml scons numpy cython pandas scipy pytest h5py \
pytest-github-actions-annotate-failures pytest-cov gcovr
python3 -m pip install --no-binary=h5py h5py
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v2
with:
@@ -366,8 +364,7 @@ jobs:
run: python3 -m pip install -U pip setuptools wheel
- name: Install Python dependencies
run: |
python3 -m pip install ruamel.yaml scons numpy cython pandas matplotlib scipy
python3 -m pip install --no-binary=h5py h5py
python3 -m pip install ruamel.yaml scons numpy cython pandas matplotlib scipy h5py
- name: Build Cantera
# compile with GCC 9.4.0 on ubuntu-20.04 as an alternative to the default
# (GCC 7.5.0 is both default and oldest supported version)
@@ -619,9 +616,8 @@ jobs:
run: python3 -m pip install -U pip setuptools wheel
- name: Install Python dependencies
run: |
python3 -m pip install ruamel.yaml scons numpy cython pandas pytest \
python3 -m pip install ruamel.yaml scons numpy cython pandas h5py pytest \
pytest-github-actions-annotate-failures
python3 -m pip install --no-binary=h5py h5py
- name: Setup Intel oneAPI environment
run: |
source /opt/intel/oneapi/setvars.sh

View File

@@ -7,17 +7,20 @@ import numpy as np
from collections import OrderedDict
import csv as _csv
import pkg_resources
# avoid explicit dependence of cantera on h5py
try:
pkg_resources.get_distribution('h5py')
except pkg_resources.DistributionNotFound:
_h5py = ImportError('Method requires a working h5py installation.')
else:
import h5py as _h5py
def _import_h5py():
# avoid explicit dependence of cantera on h5py
import pkg_resources # local import to reduce overall import time
try:
pkg_resources.get_distribution('h5py')
except pkg_resources.DistributionNotFound:
raise ImportError('Method requires a working h5py installation.')
else:
import h5py
return h5py
# avoid explicit dependence of cantera on pandas
import pkg_resources
try:
pkg_resources.get_distribution('pandas')
except pkg_resources.DistributionNotFound:
@@ -1311,8 +1314,7 @@ class SolutionArray:
requires a working installation of *h5py* (``h5py`` can be installed using
pip or conda).
"""
if isinstance(_h5py, ImportError):
raise _h5py
h5py = _import_h5py()
# collect data
data = self.collect_data(*args, cols=cols, **kwargs)
@@ -1322,7 +1324,7 @@ class SolutionArray:
hdf_kwargs = {k: v for k, v in hdf_kwargs.items() if v is not None}
# save to container file
with _h5py.File(filename, mode) as hdf:
with h5py.File(filename, mode) as hdf:
# check existence of tagged item
if not group:
@@ -1395,10 +1397,9 @@ class SolutionArray:
The method imports data using `restore_data` and requires a working
installation of *h5py* (``h5py`` can be installed using pip or conda).
"""
if isinstance(_h5py, ImportError):
raise _h5py
h5py = _import_h5py()
with _h5py.File(filename, 'r') as hdf:
with h5py.File(filename, 'r') as hdf:
groups = list(hdf.keys())
if not len(groups):
@@ -1417,7 +1418,7 @@ class SolutionArray:
# identify subgroup
if subgroup is not None:
sub_names = [key for key, value in root.items()
if isinstance(value, _h5py.Group)]
if isinstance(value, h5py.Group)]
if not len(sub_names):
msg = "HDF group '{}' does not contain valid data"
raise IOError(msg.format(group))
@@ -1454,13 +1455,13 @@ class SolutionArray:
self._meta = dict(dgroup.attrs.items())
for name, value in dgroup.items():
# support one level of recursion
if isinstance(value, _h5py.Group):
if isinstance(value, h5py.Group):
self._meta[name] = dict(value.attrs.items())
# load data
data = OrderedDict()
for name, value in dgroup.items():
if isinstance(value, _h5py.Group):
if isinstance(value, h5py.Group):
continue
elif value.dtype.type == np.bytes_:
data[name] = np.array(value).astype('U')

View File

@@ -518,10 +518,6 @@ TEST(Reaction, PythonExtensibleRate)
#ifndef CT_HAS_PYTHON
GTEST_SKIP();
#endif
#ifdef CT_USE_HDF5
// potential mismatch of HDF libraries between h5py and HighFive
GTEST_SKIP();
#endif
auto sol = newSolution("extensible-reactions.yaml");
auto R = sol->kinetics()->reaction(0);
EXPECT_EQ(R->type(), "square-rate");

View File

@@ -5,7 +5,14 @@ from collections import OrderedDict
import pickle
import cantera as ct
from cantera.composite import _h5py, _pandas
try:
h5py = ct.composite._import_h5py()
have_h5py = True
except ImportError:
have_h5py = False
from cantera.composite import _pandas
from . import utilities
@@ -255,7 +262,7 @@ class TestSolutionArrayIO(utilities.CanteraTest):
self.assertEqual(states[0].P, gas.P)
self.assertArrayNear(states[0].Y, gas.Y)
@utilities.unittest.skipIf(isinstance(_h5py, ImportError), "h5py is not installed")
@utilities.unittest.skipIf(not have_h5py, "h5py is not installed")
def test_import_no_norm_data(self):
outfile = self.test_work_path / "solutionarray.h5"
# In Python >= 3.8, this can be replaced by the missing_ok argument
@@ -336,7 +343,7 @@ class TestSolutionArrayIO(utilities.CanteraTest):
with self.assertRaisesRegex(NotImplementedError, 'not supported'):
states.to_pandas()
@utilities.unittest.skipIf(isinstance(_h5py, ImportError), "h5py is not installed")
@utilities.unittest.skipIf(not have_h5py, "h5py is not installed")
def test_write_hdf(self):
outfile = self.test_work_path / "solutionarray.h5"
# In Python >= 3.8, this can be replaced by the missing_ok argument
@@ -365,7 +372,7 @@ class TestSolutionArrayIO(utilities.CanteraTest):
gas = ct.Solution('gri30.yaml', transport_model=None)
ct.SolutionArray(gas, 10).write_hdf(outfile)
with _h5py.File(outfile, 'a') as hdf:
with h5py.File(outfile, 'a') as hdf:
hdf.create_group('spam')
c = ct.SolutionArray(self.gas)
@@ -382,7 +389,7 @@ class TestSolutionArrayIO(utilities.CanteraTest):
c.read_hdf(outfile, group='foo/bar/baz')
self.assertArrayNear(states.T, c.T)
@utilities.unittest.skipIf(isinstance(_h5py, ImportError), "h5py is not installed")
@utilities.unittest.skipIf(not have_h5py, "h5py is not installed")
def test_write_hdf_str_column(self):
outfile = self.test_work_path / "solutionarray.h5"
# In Python >= 3.8, this can be replaced by the missing_ok argument
@@ -396,7 +403,7 @@ class TestSolutionArrayIO(utilities.CanteraTest):
b.read_hdf(outfile)
self.assertEqual(list(states.spam), list(b.spam))
@utilities.unittest.skipIf(isinstance(_h5py, ImportError), "h5py is not installed")
@utilities.unittest.skipIf(not have_h5py, "h5py is not installed")
def test_write_hdf_multidim_column(self):
outfile = self.test_work_path / "solutionarray.h5"
# In Python >= 3.8, this can be replaced by the missing_ok argument
@@ -571,7 +578,7 @@ class TestRestorePureFluid(utilities.CanteraTest):
b.restore_data(data)
check(a, b)
@utilities.unittest.skipIf(isinstance(_h5py, ImportError), "h5py is not installed")
@utilities.unittest.skipIf(not have_h5py, "h5py is not installed")
def test_import_no_norm_water(self):
outfile = self.test_work_path / "solutionarray.h5"
# In Python >= 3.8, this can be replaced by the missing_ok argument

View File

@@ -4,8 +4,6 @@ import numpy as np
from .utilities import allow_deprecated
import pytest
from cantera.composite import _h5py
class TestOnedim(utilities.CanteraTest):
def test_instantiate(self):