Skip to content

Commit

Permalink
Check for match between shared lib and Python module
Browse files Browse the repository at this point in the history
When the Python module is linked to the Cantera shared library,
it is possible that a user has multiple incompatible versions of
the library installed. This checks that the Cantera version and
Git commit are the same when importing the Python module, to avoid
crashes or erroneous behavior due to ABI mismatches.
  • Loading branch information
speth authored and ischoegl committed Feb 3, 2023
1 parent 57a9578 commit 360e85b
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 10 deletions.
5 changes: 5 additions & 0 deletions include/cantera/base/global.h
Expand Up @@ -102,6 +102,11 @@ void appdelete();
//! @copydoc Application::thread_complete
void thread_complete();

//! Returns the Cantera version. This function when needing to access the version from a
//! library, rather than the `CANTERA_VERSION` macro that is available at compile time.
//! @since New in Cantera 3.0
string version();

//! Returns the hash of the git commit from which Cantera was compiled, if known
std::string gitCommit();

Expand Down
17 changes: 14 additions & 3 deletions include/cantera/cython/utils_utils.h
Expand Up @@ -25,12 +25,23 @@ static std::map<std::string, PyObject*> mapped_PyWarnings = {
{"User", PyExc_UserWarning}
};

// Wrappers for preprocessor defines
inline std::string get_cantera_version()
// Cantera version for Python module compilation
inline std::string get_cantera_version_py()
{
return CANTERA_VERSION;
}

// Git commit for Python module compilation
inline std::string get_cantera_git_commit_py()
{
return std::string(CANTERA_VERSION);
#ifdef GIT_COMMIT
return GIT_COMMIT;
#else
return "unknown";
#endif
}

// Wrappers for preprocessor defines
inline int get_sundials_version()
{
return CT_SUNDIALS_VERSION;
Expand Down
15 changes: 11 additions & 4 deletions interfaces/cython/SConscript
Expand Up @@ -33,14 +33,21 @@ if env["coverage"]:
# Build the Python module
cython_obj = []
for pyxfile in multi_glob(localenv, "cantera", "pyx"):
cythonized = localenv.Command(
if pyxfile.name == "_utils.pyx":
# Define GIT_COMMIT only in _utils.pyx to avoid unnecessary recompilations
cython_env = localenv.Clone()
cython_env.Append(CPPDEFINES={'GIT_COMMIT': '\\"{0}\\"'.format(env['git_commit'])})
else:
cython_env = localenv

cythonized = cython_env.Command(
f"cantera/{pyxfile.name.replace('.pyx', '.cpp')}", pyxfile,
f'''${{python_cmd}} -c "import Cython.Build; Cython.Build.cythonize(r'${{SOURCE}}', compiler_directives={directives!r})"'''
)
for pxd in multi_glob(localenv, "cantera", "pxd"):
localenv.Depends(cythonized, pxd)
for pxd in multi_glob(cython_env, "cantera", "pxd"):
cython_env.Depends(cythonized, pxd)

obj = localenv.SharedObject(
obj = cython_env.SharedObject(
f"#build/temp-py/{pyxfile.name.split('.')[0]}", cythonized)
cython_obj.append(obj)
cython_obj.extend(env['python_ext_objects'])
Expand Down
2 changes: 1 addition & 1 deletion interfaces/cython/cantera/_cantera.pyx
Expand Up @@ -30,8 +30,8 @@ def bootstrap_cython_submodules():
bootstrap_cython_submodules()

# Import the contents of the individual .pyx files
from ._onedim import *
from ._utils import *
from ._onedim import *
from .solutionbase import *
from .delegator import *
from .func1 import *
Expand Down
4 changes: 3 additions & 1 deletion interfaces/cython/cantera/_utils.pxd
Expand Up @@ -72,12 +72,14 @@ cdef extern from "cantera/base/global.h" namespace "Cantera":
cdef void Cxx_suppress_thermo_warnings "Cantera::suppress_thermo_warnings" (cbool)
cdef void Cxx_use_legacy_rate_constants "Cantera::use_legacy_rate_constants" (cbool)
cdef string CxxGitCommit "Cantera::gitCommit" ()
cdef string CxxVersion "Cantera::version" ()
cdef cbool CxxUsesHDF5 "Cantera::usesHDF5" ()
cdef cbool CxxDebugModeEnabled "Cantera::debugModeEnabled" ()


cdef extern from "cantera/cython/utils_utils.h":
cdef string get_cantera_version()
cdef string get_cantera_version_py()
cdef string get_cantera_git_commit_py()
cdef int get_sundials_version()
cdef cppclass CxxPythonLogger "PythonLogger":
pass
Expand Down
12 changes: 11 additions & 1 deletion interfaces/cython/cantera/_utils.pyx
Expand Up @@ -49,10 +49,20 @@ def get_data_directories():

__sundials_version__ = '.'.join(str(get_sundials_version()))

__version__ = pystr(get_cantera_version())
__version__ = pystr(CxxVersion())

if __version__ != pystr(get_cantera_version_py()):
raise ImportError("Mismatch betweeen Cantera Python module version "
f"({pystr(get_cantera_version_py())}) and Cantera shared library "
f"version ({__version__})")

__git_commit__ = pystr(CxxGitCommit())

if __git_commit__ != pystr(get_cantera_git_commit_py()):
raise ImportError("Mismatch betweeen Cantera Python module Git commit "
f"({pystr(get_cantera_git_commit_py())}) and Cantera shared library "
f"git commit ({__git_commit__})")

_USE_SPARSE = False

def debug_mode_enabled():
Expand Down
5 changes: 5 additions & 0 deletions src/base/global.cpp
Expand Up @@ -122,6 +122,11 @@ void thread_complete()
app()->thread_complete();
}

string version()
{
return CANTERA_VERSION;
}

std::string gitCommit()
{
#ifdef GIT_COMMIT
Expand Down

0 comments on commit 360e85b

Please sign in to comment.