18#include <nanobind/nanobind.h>
19#include <nanobind/ndarray.h>
20#include <nanobind/operators.h>
21#include <nanobind/stl/list.h>
22#include <nanobind/stl/string.h>
23#include <nanobind/stl/vector.h>
24#include <nanobind/stl/pair.h>
25#include <nanobind/stl/map.h>
30namespace nb = nanobind;
32using namespace nb::literals;
38 std::snprintf(buffer,
sizeof(buffer),
"0x%llx",
reinterpret_cast<unsigned long long>(&obj));
39 return std::string(buffer);
44 if (type_info ==
typeid(
int)) {
46 }
else if (type_info ==
typeid(
unsigned int)) {
48 }
else if (type_info ==
typeid(
short)) {
50 }
else if (type_info ==
typeid(
unsigned short)) {
52 }
else if (type_info ==
typeid(
char)) {
55 }
else if (type_info ==
typeid(
unsigned char)) {
57 }
else if (type_info ==
typeid(
float)) {
59 }
else if (type_info ==
typeid(
double)) {
61 }
else if (type_info ==
typeid(
long)) {
63 }
else if (type_info ==
typeid(
unsigned long)) {
65 }
else if (type_info ==
typeid(
bool)) {
69 return std::string(
"unknown ('") + type_info.name() +
"')";
74 if (nb::isinstance<nb::str>(value)) {
75 self.
setProperty(key, nb::cast<std::string>(value));
76 }
else if (nb::isinstance<nb::int_>(value)) {
78 }
else if (nb::isinstance<nb::float_>(value)) {
81 throw std::runtime_error(
"Unsupported property type. Must be str, int, or float.");
89#define MAKE_NDARRAY(T) \
90 nb::ndarray<nb::numpy, T, nb::ro>(static_cast<const T*>(ptr), {size}).cast()
103 throw std::runtime_error(
"getColumnVoid: unsupported column type: " + std::string(
type->name()));
110 const std::type_info* type_info =
nullptr;
111 const nb::dlpack::dtype dtype = column.dtype();
112 switch ((nb::dlpack::dtype_code) dtype.code) {
113 case nb::dlpack::dtype_code::Int:
114 switch (dtype.bits) {
118 case 8: type_info = &
typeid(char);
break;
119 case 16: type_info = &
typeid(std::int16_t);
break;
120 case 32: type_info = &
typeid(std::int32_t);
break;
121 case 64: type_info = &
typeid(std::int64_t);
break;
125 case nb::dlpack::dtype_code::UInt:
126 switch (dtype.bits) {
127 case 8: type_info = &
typeid(std::uint8_t);
break;
128 case 16: type_info = &
typeid(std::uint16_t);
break;
129 case 32: type_info = &
typeid(std::uint32_t);
break;
130 case 64: type_info = &
typeid(std::uint64_t);
break;
134 case nb::dlpack::dtype_code::Float:
135 switch (dtype.bits) {
136 case 32: type_info = &
typeid(float);
break;
137 case 64: type_info = &
typeid(double);
break;
145 if (type_info ==
nullptr)
throw std::runtime_error (
"unsupported column type passed in");
146 self.
setColumnVoid(key, column.shape(0), column.data(), *type_info, is_const);
155#ifdef XAOD_STANDALONE
158struct PyMessagePrinter :
public asg::IMessagePrinter {
159 nb::callable m_callback;
161 explicit PyMessagePrinter(nb::callable cb)
162 : m_callback(std::move(cb)) {}
164 void print(MSG::Level lvl,
const std::string& name,
165 const std::string& text)
override {
167 if (!Py_IsInitialized())
169 nb::gil_scoped_acquire gil;
170 m_callback(
static_cast<int>(lvl), name, text);
175static std::unique_ptr<PyMessagePrinter> g_printer;
176static std::unique_ptr<asg::MessagePrinterOverlay> g_overlay;
178void set_printer_from_callable(nb::callable cb) {
179 g_printer = std::make_unique<PyMessagePrinter>(std::move(cb));
181 g_overlay = std::make_unique<asg::MessagePrinterOverlay>(g_printer.get());
184void clear_printer() {
194 module.doc() = "Nanobind bindings for PythonToolHandle";
197 throw nb::import_error(
"This module can only be used in columnar access mode. Try setting up a ColumnarAnalysis release instead.");
199 module.attr("numberOfEventsName") = &columnar::eventRangeColumnName;
200 module.attr("eventRangeColumnName") = &columnar::eventRangeColumnName;
203#ifdef XAOD_STANDALONE
205 module.def("set_python_printer", &set_printer_from_callable, nb::arg("callback"),
206 "Install a Python callable(level: int, name: str, text: str) as the global "
207 "C++ message printer.");
210 module.def("set_python_printer", &clear_printer,
211 "Reset to the default stdout message printer.");
215 module.def("set_python_printer", [](nb::args, nb::kwargs) {
216 throw std::runtime_error(
217 "set_python_printer is only available in standalone "
218 "(AnalysisBase/ColumnarAnalysis) builds, not in Athena/AthAnalysis.");
219 },
"Not available in Athena/AthAnalysis builds.");
223 nb::enum_<columnar::ColumnAccessMode>(module,
"ColumnAccessMode")
230 return "<ColumnAccessMode input>";
232 return "<ColumnAccessMode output>";
234 return "<ColumnAccessMode update>";
236 return "<ColumnAccessMode update value=" + std::to_string(
static_cast<int>(mode)) +
">";
241 nb::enum_<MSG::Level>(module,
"MsgLevel", nb::is_arithmetic())
242 .value(
"NIL", MSG::NIL)
243 .value(
"VERBOSE", MSG::VERBOSE)
244 .value(
"DEBUG", MSG::DEBUG)
245 .value(
"INFO", MSG::INFO)
246 .value(
"WARNING", MSG::WARNING)
247 .value(
"ERROR", MSG::ERROR)
248 .value(
"FATAL", MSG::FATAL)
251 nb::class_<columnar::ColumnInfo>(module,
"ColumnInfo")
267 std::string access_mode;
270 access_mode =
"input";
273 access_mode =
"output";
276 access_mode =
"update";
280 access_mode =
"unknown";
282 return "<ColumnInfo name='" + self.
name +
"'" +
284 ", access_mode='" + access_mode +
"'" +
291 d[
"name"] = self.
name;
294 d[
"access_mode"] =
static_cast<int>(self.
accessMode);
305 nb::class_<columnar::PythonToolHandle>(module,
"PythonToolHandle")
313 std::cerr <<
"Warning: PythonToolHandle.type is empty."
314 <<
" Set with PythonToolHandle.set_type_and_name." << std::endl;
323 std::cerr <<
"Warning: PythonToolHandle.name is empty."
324 <<
" Set with PythonToolHandle.set_type_and_name." << std::endl;
330 .def(
"set_type_and_name",
335 "Set the type and name of the tool.")
339 "Set a property on the tool.")
343 "Set a property on the tool.")
345 .def(
"preinitialize",
347 "Preinitialize the tool.")
350 .def(
"rename_containers",
353 "Rename the columns the tool uses.")
356 .def(
"rename_containers",
358 std::vector<std::pair<std::string, std::string>> vectorized;
359 for (
const auto&
pair : renames)
360 vectorized.emplace_back(
pair);
365 "Rename the columns the tool uses.")
369 "Initialize the tool.")
371 .def(
"apply_systematic_variation",
376 "Apply a systematic variation to the tool.")
383 "Set a float column pointer.")
390 "Set a char column pointer.")
397 "Set an int column pointer.")
404 "Set a uint8_t column pointer.")
411 "Set a uint16_t column pointer.")
418 "Set a uint32_t column pointer.")
425 "Set a uint64_t column pointer.")
429 "key"_a,
"column"_a,
"is_const"_a =
true,
430 "Set a void column pointer (nanobind version).")
434 "Set a void immutable column pointer (nanobind version).")
438 "Get a column as a numpy array (zero-copy view into the tool's buffer).")
442 "Return the column names (enables dict(handle)).")
446 "Call the tool and reset the columns.")
451 "Get the expected column information."
454 .def(
"get_recommended_systematics",
456 "Get the recommended systematics.")
Definition of message levels and a helper function.
NB_MODULE(python_tool_handle, module)
nb::object getColumnVoid(columnar::PythonToolHandle &self, const std::string &key)
std::string get_type_name(const std::type_info &type_info)
void setColumnVoid(columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column, bool is_const=true)
void setProperty(columnar::PythonToolHandle &self, const std::string &key, nb::object value)
void setImmutableColumnVoid(columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column)
std::string getAddressString(const columnar::PythonToolHandle &obj)
void print(char *figname, TCanvas *c1)
constexpr unsigned columnarAccessMode
ColumnAccessMode
an enum for the different access modes for a column
@ update
an updateable column
a struct that contains meta-information about each column that's needed to interface the column with ...
std::string offsetName
the name of the offset column used for this column (or empty string for none)
std::vector< unsigned > fixedDimensions
the fixed dimensions this column has (if any)
bool isOptional
whether this column is optional
std::string variantLinkKeyColumn
if this is a variant link column, this is the name of the column with the container keys
std::string name
the name of the column
std::vector< std::string > linkTargetNames
for link columns: the name(s) of the container(s) we link to
bool isOffset
whether this is an offset column
ColumnAccessMode accessMode
the access mode for the column
std::string replacesColumn
whether this replaces another column
unsigned index
the index of the column in the data array
const std::type_info * type
the type of the individual entries in the column