Skip to content
Open
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
30 changes: 15 additions & 15 deletions Modules/private/CreateCoverageTargets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ find_program(LCOV_EXECUTABLE lcov)
find_program(GENHTML_EXECUTABLE genhtml)
find_program(GCOVR_EXECUTABLE gcovr)

# Find Python3 and normalization scripts
find_package(Python3 COMPONENTS Interpreter)
# Find Python and normalization scripts
find_package(Python COMPONENTS Interpreter)

# Find CTest coverage tool
find_program(LLVM_COV_EXECUTABLE NAMES llvm-cov-21 llvm-cov DOC "LLVM coverage tool")
Expand Down Expand Up @@ -183,7 +183,7 @@ function(_create_coverage_targets_impl)
${CMAKE_COMMAND} -E echo
"[Coverage] Exporting LLVM coverage data to LCOV (${LLVM_COV_LCOV_OUTPUT})"
COMMAND
${Python3_EXECUTABLE} "${LLVM_COV_EXPORT_SCRIPT}" ${LLVM_COV_LCOV_OUTPUT}
${Python_EXECUTABLE} "${LLVM_COV_EXPORT_SCRIPT}" ${LLVM_COV_LCOV_OUTPUT}
${LLVM_COV_EXECUTABLE} export ${LLVM_COV_OBJECTS} -instr-profile=${LLVM_PROFDATA_OUTPUT}
"-ignore-filename-regex=${LLVM_COV_EXCLUDE_REGEX}" --format=lcov
COMMENT "Exporting LLVM coverage data to LCOV"
Expand All @@ -196,12 +196,12 @@ function(_create_coverage_targets_impl)
# Normalization target for llvm-cov output (if Python script exists)
set(_normalize_llvm_script "${PROJECT_SOURCE_DIR}/scripts/normalize_coverage_lcov.py")
set(LLVM_COV_NORMALIZED_STAMP ${CMAKE_BINARY_DIR}/coverage-llvm-normalized.stamp)
if(Python3_FOUND AND EXISTS "${_normalize_llvm_script}")
if(Python_FOUND AND EXISTS "${_normalize_llvm_script}")
add_custom_command(
OUTPUT ${LLVM_COV_NORMALIZED_STAMP}
DEPENDS ${LLVM_COV_LCOV_OUTPUT}
COMMAND
${Python3_EXECUTABLE} "${_normalize_llvm_script}" --repo-root "${PROJECT_SOURCE_DIR}"
${Python_EXECUTABLE} "${_normalize_llvm_script}" --repo-root "${PROJECT_SOURCE_DIR}"
--coverage-root "${PROJECT_SOURCE_DIR}" --coverage-alias "${PROJECT_SOURCE_DIR}"
"${LLVM_COV_LCOV_OUTPUT}"
COMMAND ${CMAKE_COMMAND} -E touch ${LLVM_COV_NORMALIZED_STAMP}
Expand All @@ -216,7 +216,7 @@ function(_create_coverage_targets_impl)
coverage-llvm-normalize
COMMAND
${CMAKE_COMMAND} -E echo
"ERROR: Python3 or normalize_coverage_lcov.py not found. Cannot normalize LLVM coverage report."
"ERROR: Python or normalize_coverage_lcov.py not found. Cannot normalize LLVM coverage report."
COMMAND exit 1
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Failed to normalize LLVM coverage report"
Expand Down Expand Up @@ -287,11 +287,11 @@ function(_create_coverage_targets_impl)

set(_coverage_symlink_script "${PROJECT_SOURCE_DIR}/scripts/create_coverage_symlinks.py")
set(_coverage_symlink_root "${PROJECT_SOURCE_DIR}/.coverage-generated")
if(Python3_FOUND AND EXISTS "${_coverage_symlink_script}")
if(Python_FOUND AND EXISTS "${_coverage_symlink_script}")
add_custom_target(
coverage-symlink-prepare
COMMAND
${Python3_EXECUTABLE} "${_coverage_symlink_script}" --build-root "${CMAKE_BINARY_DIR}"
${Python_EXECUTABLE} "${_coverage_symlink_script}" --build-root "${CMAKE_BINARY_DIR}"
--output-root "${_coverage_symlink_root}"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Preparing generated symlink tree (.coverage-generated)"
Expand All @@ -304,7 +304,7 @@ function(_create_coverage_targets_impl)
COMMAND ${CMAKE_COMMAND} -E make_directory "${_coverage_symlink_root}"
COMMAND
${CMAKE_COMMAND} -E echo
"WARNING: Python3 or create_coverage_symlinks.py missing; generated symlink tree will be empty."
"WARNING: Python or create_coverage_symlinks.py missing; generated symlink tree will be empty."
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Preparing generated symlink tree (.coverage-generated)"
)
Expand Down Expand Up @@ -383,11 +383,11 @@ function(_create_coverage_targets_impl)
)

# HTML normalization target (depends on symlink preparation)
if(Python3_FOUND AND EXISTS "${_normalize_lcov_script}")
if(Python_FOUND AND EXISTS "${_normalize_lcov_script}")
add_custom_target(
coverage-html-normalize
COMMAND
${Python3_EXECUTABLE} "${_normalize_lcov_script}" --repo-root "${PROJECT_SOURCE_DIR}"
${Python_EXECUTABLE} "${_normalize_lcov_script}" --repo-root "${PROJECT_SOURCE_DIR}"
--coverage-root "${PROJECT_SOURCE_DIR}" --coverage-alias "${PROJECT_SOURCE_DIR}"
"${CMAKE_BINARY_DIR}/coverage.info.final"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
Expand All @@ -400,7 +400,7 @@ function(_create_coverage_targets_impl)
coverage-html-normalize
COMMAND
${CMAKE_COMMAND} -E echo
"ERROR: Python3 or normalize_coverage_lcov.py not found. Cannot normalize HTML coverage report."
"ERROR: Python or normalize_coverage_lcov.py not found. Cannot normalize HTML coverage report."
COMMAND exit 1
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Failed to normalize LCOV HTML coverage report"
Expand Down Expand Up @@ -469,11 +469,11 @@ function(_create_coverage_targets_impl)
)

# XML normalization target (depends on symlink preparation)
if(Python3_FOUND AND EXISTS "${_normalize_xml_script}")
if(Python_FOUND AND EXISTS "${_normalize_xml_script}")
add_custom_target(
coverage-xml-normalize
COMMAND
${Python3_EXECUTABLE} "${_normalize_xml_script}" --repo-root "${PROJECT_SOURCE_DIR}"
${Python_EXECUTABLE} "${_normalize_xml_script}" --repo-root "${PROJECT_SOURCE_DIR}"
--source-dir "${PROJECT_SOURCE_DIR}" --path-map
"${CMAKE_BINARY_DIR}=${PROJECT_SOURCE_DIR}/.coverage-generated"
"${CMAKE_BINARY_DIR}/coverage.xml"
Expand All @@ -487,7 +487,7 @@ function(_create_coverage_targets_impl)
coverage-xml-normalize
COMMAND
${CMAKE_COMMAND} -E echo
"ERROR: Python3 or normalize_coverage_xml.py not found. Cannot normalize XML coverage report."
"ERROR: Python or normalize_coverage_xml.py not found. Cannot normalize XML coverage report."
COMMAND exit 1
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Failed to normalize XML coverage report"
Expand Down
95 changes: 22 additions & 73 deletions plugins/python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,75 +1,24 @@
find_package(Python 3.12 COMPONENTS Interpreter Development QUIET)
find_package(Python 3.12 COMPONENTS Interpreter Development NumPy REQUIRED)

if(Python_FOUND)
# Verify installation of necessary python modules for specific tests

function(check_python_module_version MODULE_NAME MIN_VERSION OUT_VAR)
execute_process(
COMMAND
${Python_EXECUTABLE} -c
"import sys
try:
import ${MODULE_NAME}
from packaging.version import parse as parse_version
installed_version = getattr(${MODULE_NAME}, '__version__', None)
if parse_version(installed_version) >= parse_version('${MIN_VERSION}'):
sys.exit(0)
else:
sys.exit(2) # Version too low
except ImportError:
sys.exit(1)"
RESULT_VARIABLE _module_check_result
)

if(_module_check_result EQUAL 0)
set(${OUT_VAR} TRUE PARENT_SCOPE)
elseif(_module_check_result EQUAL 1)
set(${OUT_VAR} FALSE PARENT_SCOPE) # silent b/c common
elseif(_module_check_result EQUAL 2)
message(
WARNING
"Python module '${MODULE_NAME}' found but version too low (min required: ${MIN_VERSION})."
)
set(${OUT_VAR} FALSE PARENT_SCOPE)
else()
message(WARNING "Unknown error while checking Python module '${MODULE_NAME}'.")
set(${OUT_VAR} FALSE PARENT_SCOPE)
endif()
endfunction()

check_python_module_version("numpy" "2.0.0" HAS_NUMPY)

# Phlex module to run Python algorithms
add_library(
pymodule
MODULE
src/pymodule.cpp
src/modulewrap.cpp
src/configwrap.cpp
src/lifelinewrap.cpp
src/errorwrap.cpp
if(Python_NumPy_VERSION VERSION_LESS "2.0.0")
message(
FATAL_ERROR
"NumPy version is too low: ${Python_NumPy_VERSION} found, at least 2.0.0 required"
)
include_directories(pymodule, ${Python_INCLUDE_DIRS})
target_link_libraries(pymodule PRIVATE phlex::module ${Python_LIBRARIES} PUBLIC Python::Python)

install(TARGETS pymodule LIBRARY DESTINATION lib)

# numpy support if installed
if(HAS_NUMPY)
# locate numpy's header directory
execute_process(
COMMAND "${Python_EXECUTABLE}" -c "import numpy; print(numpy.get_include(), end='')"
RESULT_VARIABLE NUMPY_RESULT
OUTPUT_VARIABLE NUMPY_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

if(NUMPY_RESULT EQUAL 0)
include_directories(pymodule PRIVATE ${NUMPY_INCLUDE_DIR})
target_compile_definitions(
pymodule
PRIVATE PHLEX_HAVE_NUMPY=1 NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION
)
endif()
endif()
endif() # Python available
endif()

# Phlex module to run Python algorithms
add_library(
pymodule
MODULE
src/pymodule.cpp
src/modulewrap.cpp
src/configwrap.cpp
src/lifelinewrap.cpp
src/errorwrap.cpp
)
target_link_libraries(pymodule PRIVATE phlex::module Python::Python Python::NumPy)

target_compile_definitions(pymodule PRIVATE NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION)

install(TARGETS pymodule LIBRARY DESTINATION lib)
14 changes: 2 additions & 12 deletions plugins/python/src/modulewrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
#include <stdexcept>
#include <vector>

#ifdef PHLEX_HAVE_NUMPY
#define NO_IMPORT_ARRAY
#define PY_ARRAY_UNIQUE_SYMBOL phlex_ARRAY_API
#include <numpy/arrayobject.h>
#endif

using namespace phlex::experimental;
using phlex::concurrency;
Expand Down Expand Up @@ -316,7 +314,6 @@ namespace {
BASIC_CONVERTER(float, float, PyFloat_FromDouble, PyFloat_AsDouble)
BASIC_CONVERTER(double, double, PyFloat_FromDouble, PyFloat_AsDouble)

#ifdef PHLEX_HAVE_NUMPY
#define VECTOR_CONVERTER(name, cpptype, nptype) \
static intptr_t name##_to_py(std::shared_ptr<std::vector<cpptype>> const& v) \
{ \
Expand Down Expand Up @@ -394,7 +391,6 @@ namespace {
NUMPY_ARRAY_CONVERTER(vulong, unsigned long, NPY_ULONG)
NUMPY_ARRAY_CONVERTER(vfloat, float, NPY_FLOAT)
NUMPY_ARRAY_CONVERTER(vdouble, double, NPY_DOUBLE)
#endif

} // unnamed namespace

Expand Down Expand Up @@ -549,7 +545,6 @@ static bool insert_input_converters(py_phlex_module* mod,
INSERT_INPUT_CONVERTER(float, cname, inp);
else if (inp_type == "double")
INSERT_INPUT_CONVERTER(double, cname, inp);
#ifdef PHLEX_HAVE_NUMPY
else if (inp_type.compare(0, 13, "numpy.ndarray") == 0) {
// TODO: these are hard-coded std::vector <-> numpy array mappings, which is
// way too simplistic for real use. It only exists for demonstration purposes,
Expand Down Expand Up @@ -595,9 +590,7 @@ static bool insert_input_converters(py_phlex_module* mod,
PyErr_Format(PyExc_TypeError, "unsupported array input type \"%s\"", inp_type.c_str());
return false;
}
}
#endif
else {
} else {
PyErr_Format(PyExc_TypeError, "unsupported input type \"%s\"", inp_type.c_str());
return false;
}
Expand Down Expand Up @@ -676,7 +669,6 @@ static PyObject* md_transform(py_phlex_module* mod, PyObject* args, PyObject* kw
INSERT_OUTPUT_CONVERTER(float, cname, output);
else if (output_type == "double")
INSERT_OUTPUT_CONVERTER(double, cname, output);
#ifdef PHLEX_HAVE_NUMPY
else if (output_type.compare(0, 13, "numpy.ndarray") == 0) {
// TODO: just like for input types, these are hard-coded, but should be handled by
// an IDL instead.
Expand Down Expand Up @@ -721,9 +713,7 @@ static PyObject* md_transform(py_phlex_module* mod, PyObject* args, PyObject* kw
PyErr_Format(PyExc_TypeError, "unsupported array output type \"%s\"", output_type.c_str());
return nullptr;
}
}
#endif
else {
} else {
PyErr_Format(PyExc_TypeError, "unsupported output type \"%s\"", output_type.c_str());
return nullptr;
}
Expand Down
8 changes: 0 additions & 8 deletions plugins/python/src/pymodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@

#include "wrap.hpp"

#ifdef PHLEX_HAVE_NUMPY
#define PY_ARRAY_UNIQUE_SYMBOL phlex_ARRAY_API
#include <numpy/arrayobject.h>
#endif

using namespace phlex::experimental;

Expand Down Expand Up @@ -48,7 +46,6 @@ PHLEX_REGISTER_ALGORITHMS(m, config)
}
}

#ifdef PHLEX_HAVE_NUMPY
static void import_numpy(bool control_interpreter)
{
static std::atomic<bool> numpy_imported{false};
Expand All @@ -61,14 +58,11 @@ static void import_numpy(bool control_interpreter)
}
}
}
#endif

static bool initialize()
{
if (Py_IsInitialized()) {
#ifdef PHLEX_HAVE_NUMPY
import_numpy(false);
#endif
return true;
}

Expand Down Expand Up @@ -122,9 +116,7 @@ static bool initialize()
return false;

// load numpy (see also above, if already initialized)
#ifdef PHLEX_HAVE_NUMPY
import_numpy(true);
#endif

// TODO: the GIL should first be released on the main thread and this seems
// to be the only place to do it. However, there is no equivalent place to
Expand Down
Loading
Loading