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
2 changes: 1 addition & 1 deletion plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Phlex provided core plugins

# plugin for running Python algorithms in phlex
# add_subdirectory(python)
add_subdirectory(python)

add_library(layer_generator layer_generator.cpp)
target_link_libraries(layer_generator PRIVATE phlex::core)
Expand Down
55 changes: 42 additions & 13 deletions plugins/python/src/configwrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,12 @@
};
// clang-format on

PyObject* phlex::experimental::wrap_configuration(configuration const* config)
PyObject* phlex::experimental::wrap_configuration(configuration const& config)
{
if (!config) {
PyErr_SetString(PyExc_ValueError, "provided configuration is null");
return nullptr;
}

py_config_map* pyconfig =
(py_config_map*)PhlexConfig_Type.tp_new(&PhlexConfig_Type, nullptr, nullptr);

pyconfig->ph_config = config;
pyconfig->ph_config = &config;

return (PyObject*)pyconfig;
}
Expand Down Expand Up @@ -73,6 +68,11 @@

std::string ckey = PyUnicode_AsUTF8(pykey);

// Note: Python3.14 adds PyLong_FromInt64/PyLong_FromUInt64 to replace the
// long long variants
static_assert(sizeof(long long) >= sizeof(int64_t));
static_assert(sizeof(unsigned long long) >= sizeof(uint64_t));

try {
auto k = pycmap->ph_config->prototype_internal_kind(ckey);
if (k.second /* is array */) {
Expand All @@ -87,14 +87,16 @@
auto const& cvalue = pycmap->ph_config->get<std::vector<std::int64_t>>(ckey);
pyvalue = PyTuple_New(cvalue.size());
for (Py_ssize_t i = 0; i < (Py_ssize_t)cvalue.size(); ++i) {
PyObject* item = PyLong_FromLong(cvalue[i]);
// Note Python3.14 is expected to add PyLong_FromInt64
PyObject* item = PyLong_FromLongLong(cvalue[i]);
PyTuple_SetItem(pyvalue, i, item);
}
} else if (k.first == boost::json::kind::uint64) {
auto const& cvalue = pycmap->ph_config->get<std::vector<std::uint64_t>>(ckey);
pyvalue = PyTuple_New(cvalue.size());
for (Py_ssize_t i = 0; i < (Py_ssize_t)cvalue.size(); ++i) {
PyObject* item = PyLong_FromUnsignedLong(cvalue[i]);
// Note Python3.14 is expected to add PyLong_FromUInt64
PyObject* item = PyLong_FromUnsignedLongLong(cvalue[i]);

Check warning on line 99 in plugins/python/src/configwrap.cpp

View check run for this annotation

Codecov / codecov/patch

plugins/python/src/configwrap.cpp#L99

Added line #L99 was not covered by tests
PyTuple_SetItem(pyvalue, i, item);
}
} else if (k.first == boost::json::kind::double_) {
Expand All @@ -111,32 +113,59 @@
PyObject* item = PyUnicode_FromStringAndSize(cvalue[i].c_str(), cvalue[i].size());
PyTuple_SetItem(pyvalue, i, item);
}
} else if (k.first == boost::json::kind::object) {
auto cvalue = pycmap->ph_config->get<std::vector<std::map<std::string, std::string>>>(ckey);
pyvalue = PyTuple_New(cvalue.size());
for (Py_ssize_t i = 0; i < (Py_ssize_t)cvalue.size(); ++i) {
PyObject* item = PyDict_New();
for (auto const& kv : cvalue[i]) {
PyObject* val = PyUnicode_FromStringAndSize(kv.second.c_str(), kv.second.size());
PyDict_SetItemString(item, kv.first.c_str(), val);
Py_DECREF(val);
}
PyTuple_SetItem(pyvalue, i, item);
}
} else if (k.first == boost::json::kind::null) {
// special case: empty array
pyvalue = PyTuple_New(0);
}
} else {
if (k.first == boost::json::kind::bool_) {
auto cvalue = pycmap->ph_config->get<bool>(ckey);
pyvalue = PyBool_FromLong((long)cvalue);
} else if (k.first == boost::json::kind::int64) {
auto cvalue = pycmap->ph_config->get<std::int64_t>(ckey);
pyvalue = PyLong_FromLong(cvalue);
// Note Python3.14 is expected to add PyLong_FromInt64
pyvalue = PyLong_FromLongLong(cvalue);
} else if (k.first == boost::json::kind::uint64) {
auto cvalue = pycmap->ph_config->get<std::uint64_t>(ckey);
pyvalue = PyLong_FromUnsignedLong(cvalue);
// Note Python3.14 is expected to add PyLong_FromUInt64
pyvalue = PyLong_FromUnsignedLongLong(cvalue);

Check warning on line 143 in plugins/python/src/configwrap.cpp

View check run for this annotation

Codecov / codecov/patch

plugins/python/src/configwrap.cpp#L143

Added line #L143 was not covered by tests
} else if (k.first == boost::json::kind::double_) {
auto cvalue = pycmap->ph_config->get<double>(ckey);
pyvalue = PyFloat_FromDouble(cvalue);
} else if (k.first == boost::json::kind::string) {
auto const& cvalue = pycmap->ph_config->get<std::string>(ckey);
pyvalue = PyUnicode_FromStringAndSize(cvalue.c_str(), cvalue.size());
} else if (k.first == boost::json::kind::object) {
auto cvalue = pycmap->ph_config->get<std::map<std::string, std::string>>(ckey);
pyvalue = PyDict_New();
for (auto const& kv : cvalue) {
PyObject* val = PyUnicode_FromStringAndSize(kv.second.c_str(), kv.second.size());
PyDict_SetItemString(pyvalue, kv.first.c_str(), val);
Py_DECREF(val);
}
}
}
} catch (std::runtime_error const&) {
PyErr_Format(PyExc_KeyError, "property \"%s\" does not exist", ckey.c_str());
} catch (std::runtime_error const& e) {
PyErr_Format(PyExc_KeyError, "failed to retrieve property \"%s\" (%s)", ckey.c_str(), e.what());
}

// cache if found
if (pyvalue) {
PyDict_SetItem(pycmap->ph_config_cache, pykey, pyvalue);
} else if (!PyErr_Occurred()) {
PyErr_Format(PyExc_KeyError, "property \"%s\" is of unknown type", ckey.c_str());

Check warning on line 168 in plugins/python/src/configwrap.cpp

View check run for this annotation

Codecov / codecov/patch

plugins/python/src/configwrap.cpp#L168

Added line #L168 was not covered by tests
}

return pyvalue;
Expand Down
14 changes: 14 additions & 0 deletions plugins/python/src/errorwrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

#include <string>

// This code has excluded several error checking paths from code coverage,
// because the conditions to create the errors (trace formatting problems)
// are rare and too hard to recreate in a test, while the resolution (fall
// back to a generic error messsage) is rather straightforward and thus
// does not need testing.

using namespace phlex::experimental;

static bool format_traceback(std::string& msg,
Expand All @@ -25,27 +31,33 @@ static bool format_traceback(std::string& msg,
#endif
Py_DECREF(format_exception);

// LCOV_EXCL_START
if (!formatted_tb) {
PyErr_Clear();
return false;
}
// LCOV_EXCL_STOP

PyObject* py_msg_empty = PyUnicode_FromString("");
PyObject* py_msg = PyUnicode_Join(py_msg_empty, formatted_tb);
Py_DECREF(py_msg_empty);
Py_DECREF(formatted_tb);

// LCOV_EXCL_START
if (!py_msg) {
PyErr_Clear();
return false;
}
// LCOV_EXCL_STOP

char const* c_msg = PyUnicode_AsUTF8(py_msg);
// LCOV_EXCL_START
if (c_msg) {
msg = c_msg;
Py_DECREF(py_msg);
return true;
}
// LCOV_EXCL_STOP

PyErr_Clear();
Py_DECREF(py_msg);
Expand All @@ -66,11 +78,13 @@ bool phlex::experimental::msg_from_py_error(std::string& msg, bool check_error)
PyErr_Fetch(&type, &value, &traceback);
if (value) {
bool tb_ok = format_traceback(msg, type, value, traceback);
// LCOV_EXCL_START
if (!tb_ok) {
PyObject* pymsg = PyObject_Str(value);
msg = PyUnicode_AsUTF8(pymsg);
Py_DECREF(pymsg);
}
// LCOV_EXCL_STOP
} else {
msg = "unknown Python error occurred";
}
Expand Down
Loading
Loading