From 4264cd49d662290cbbb144e6c70c798288745917 Mon Sep 17 00:00:00 2001 From: Stefan Wunsch <stefan.wunsch@cern.ch> Date: Fri, 12 Apr 2019 06:45:46 +0200 Subject: [PATCH] [PyROOT exp] Make DeclareCppCallable py3 compliant [PyROOT exp] long does not exist anymore in Python3, map to int [PyROOT exp] Clear error indicator for fallback (enforced since py3.6) [PyROOT] Use Python/C API utility for warnings [PyROOT exp] Dont set error indicator during fetching keyword --- .../PyROOT/src/CppCallablePyz.cxx | 61 ++++++------------- .../PyROOT/test/cppcallable.py | 4 ++ 2 files changed, 22 insertions(+), 43 deletions(-) diff --git a/bindings/pyroot_experimental/PyROOT/src/CppCallablePyz.cxx b/bindings/pyroot_experimental/PyROOT/src/CppCallablePyz.cxx index b3078fb75c4..83dd45223d2 100644 --- a/bindings/pyroot_experimental/PyROOT/src/CppCallablePyz.cxx +++ b/bindings/pyroot_experimental/PyROOT/src/CppCallablePyz.cxx @@ -634,9 +634,9 @@ PyObject* NumbaCallableImpl_call(PyObject * /*self*/, PyObject *args) bool GetKeyword(PyObject* obj, const char* name, bool defaultVal) { - auto attr = PyObject_GetAttrString(obj, name); bool prop = defaultVal; - if (attr != NULL) { + if (PyObject_HasAttrString(obj, name)) { + auto attr = PyObject_GetAttrString(obj, name); prop = PyObject_IsTrue(attr); Py_DECREF(attr); } @@ -644,41 +644,6 @@ bool GetKeyword(PyObject* obj, const char* name, bool defaultVal) } -// Emit a RuntimeWarning using the warnings module -// Note that this function returns silently if something goes wrong. -void EmitRuntimeWarning(const std::string message) -{ - // Load warnings.warn - auto warnings = PyImport_ImportModule("warnings"); - if (warnings == NULL) return; - auto warn = PyObject_GetAttrString(warnings, "warn"); - Py_DECREF(warnings); - if (warn == NULL) return; - - // Load RuntimeWarning class -#if PY_MAJOR_VERSION < 3 - auto builtins = PyImport_ImportModule("__builtin__"); -#else - auto builtins = PyImport_ImportModule("builtins"); -#endif - if (builtins == NULL) { - Py_DECREF(warn); - return; - } - auto runtimeWarning = PyObject_GetAttrString(builtins, "RuntimeWarning"); - Py_DECREF(builtins); - if (runtimeWarning == NULL) { - Py_DECREF(warn); - return; - } - - // Call warn(message, RuntimeWarning) - PyObject_CallFunction(warn, "(sO)", message.c_str(), runtimeWarning); - Py_DECREF(warn); - Py_DECREF(runtimeWarning); -} - - // Call method of class used as decorator to create either generic or numba C++ wrapper. // The call method creates the C++ wrapper class for the Python callable and // passes through the actual callable. @@ -711,8 +676,10 @@ PyObject* ProxyCallableImpl_call(PyObject * /*self*/, PyObject *args) if (pyfunc) { return pyfunc; } else { + PyErr_Clear(); if (verbose) { - EmitRuntimeWarning("Failed to compile Python callable using numba, fall back to generic implementation. Note that the generic implementation is potentially slow."); + PyErr_WarnEx(PyExc_RuntimeWarning, + "Failed to compile Python callable using numba, fall back to generic implementation. Note that the generic implementation is potentially slow and does not allow multi-threading.", 1); } return GenericCallableImpl_call(NULL, args); } @@ -739,18 +706,26 @@ PyObject *PyROOT::GetCppCallableClass(PyObject * /*self*/, PyObject * args) { // Create wrapper class for decorator auto classDict = PyDict_New(); - auto className = PyString_FromString("CppCallableImpl"); - auto callableClass = PyClass_New(NULL, classDict, className); - Py_DECREF(className); + auto className = CPyCppyy_PyUnicode_FromString("CppCallableImpl"); + auto classBases = PyTuple_New(0); // Add methods for (auto def = CallableImplMethods; def->ml_name != NULL; def++) { - PyObject *func = PyCFunction_New(def, NULL); - PyObject *method = PyMethod_New(func, NULL, callableClass); + auto func = PyCFunction_New(def, NULL); +#if PY_VERSION_HEX < 0x03000000 + auto method = PyMethod_New(func, NULL, NULL); +#else + auto method = PyInstanceMethod_New(func); +#endif PyDict_SetItemString(classDict, def->ml_name, method); Py_DECREF(func); Py_DECREF(method); } + + auto callableClass = PyObject_CallFunctionObjArgs( + (PyObject*)&PyType_Type, className, classBases, classDict, NULL); + Py_DECREF(className); + Py_DECREF(classBases); Py_DECREF(classDict); // Return implementation class diff --git a/bindings/pyroot_experimental/PyROOT/test/cppcallable.py b/bindings/pyroot_experimental/PyROOT/test/cppcallable.py index cd6aa913f31..2751036b4de 100644 --- a/bindings/pyroot_experimental/PyROOT/test/cppcallable.py +++ b/bindings/pyroot_experimental/PyROOT/test/cppcallable.py @@ -2,6 +2,10 @@ import unittest import ROOT import sys +# long does not exist anymore on Python 3, map it to int +if sys.version_info[0] > 2: + long = int + default_test_inputs = [-1.0, 0.0, 100.0] -- GitLab