From 0251711848bfc97b30b57d8d5d8f5d9394d59b4f Mon Sep 17 00:00:00 2001 From: Enric Tejedor Saavedra <enric.tejedor.saavedra@cern.ch> Date: Wed, 24 Apr 2019 16:14:41 +0200 Subject: [PATCH] [Exp PyROOT] Create an RPyROOTApplication when importing ROOT The RPyROOTApplication is a TApplication that sets up the nuts and bolts for interactive ROOT use from Python, closely following TRint. --- .../pyroot_experimental/PyROOT/CMakeLists.txt | 2 + .../PyROOT/python/ROOT/__init__.py | 4 + .../PyROOT/python/ROOT/_application.py | 14 ++ .../PyROOT/src/PyROOTModule.cxx | 3 + .../PyROOT/src/RPyROOTApplication.cxx | 151 ++++++++++++++++++ .../PyROOT/src/RPyROOTApplication.h | 47 ++++++ 6 files changed, 221 insertions(+) create mode 100644 bindings/pyroot_experimental/PyROOT/python/ROOT/_application.py create mode 100644 bindings/pyroot_experimental/PyROOT/src/RPyROOTApplication.cxx create mode 100644 bindings/pyroot_experimental/PyROOT/src/RPyROOTApplication.h diff --git a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt index 459b6a7a645..23e7a1dfe84 100644 --- a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt +++ b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt @@ -4,6 +4,7 @@ set(py_sources ROOT/__init__.py + ROOT/_application.py ROOT/_facade.py ROOT/pythonization/__init__.py ROOT/pythonization/_generic.py @@ -34,6 +35,7 @@ set(sources src/PyROOTModule.cxx src/PyROOTStrings.cxx src/PyROOTWrapper.cxx + src/RPyROOTApplication.cxx src/GenericPyz.cxx src/RDataFramePyz.cxx src/RVecPyz.cxx diff --git a/bindings/pyroot_experimental/PyROOT/python/ROOT/__init__.py b/bindings/pyroot_experimental/PyROOT/python/ROOT/__init__.py index b5bcf0a17e8..f4c8156d1d4 100644 --- a/bindings/pyroot_experimental/PyROOT/python/ROOT/__init__.py +++ b/bindings/pyroot_experimental/PyROOT/python/ROOT/__init__.py @@ -54,6 +54,10 @@ def pythonization(lazy = True): for _, module_name, _ in pkgutil.walk_packages(pyz.__path__): module = importlib.import_module(pyz.__name__ + '.' + module_name) +# Setup interactive usage from Python +from ._application import create_application +create_application() + # Configure ROOT facade module import sys from ._facade import ROOTFacade diff --git a/bindings/pyroot_experimental/PyROOT/python/ROOT/_application.py b/bindings/pyroot_experimental/PyROOT/python/ROOT/_application.py new file mode 100644 index 00000000000..3ba0b822ba3 --- /dev/null +++ b/bindings/pyroot_experimental/PyROOT/python/ROOT/_application.py @@ -0,0 +1,14 @@ +# Author: Enric Tejedor CERN 04/2019 + +################################################################################ +# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. # +# All rights reserved. # +# # +# For the licensing terms see $ROOTSYS/LICENSE. # +# For the list of contributors see $ROOTSYS/README/CREDITS. # +################################################################################ + +from libROOTPython import InitApplication + +def create_application(): + InitApplication() diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx index 2f7f15e79b7..9552c42c8f0 100644 --- a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx +++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx @@ -13,6 +13,7 @@ #include "PyROOTPythonize.h" #include "PyROOTStrings.h" #include "PyROOTWrapper.h" +#include "RPyROOTApplication.h" // Cppyy #include "CPyCppyy.h" @@ -67,6 +68,8 @@ static PyMethodDef gPyROOTMethods[] = {{(char *)"AddDirectoryWritePyz", (PyCFunc (char *)"Get object with array interface as RVec"}, {(char *)"MakeNumpyDataFrame", (PyCFunction)PyROOT::MakeNumpyDataFrame, METH_O, (char *)"Make RDataFrame from dictionary of numpy arrays"}, + {(char *)"InitApplication", (PyCFunction)PyROOT::RPyROOTApplication::InitApplication, METH_NOARGS, + (char *)"Initialize interactive ROOT use from Python"}, {NULL, NULL, 0, NULL}}; #if PY_VERSION_HEX >= 0x03000000 diff --git a/bindings/pyroot_experimental/PyROOT/src/RPyROOTApplication.cxx b/bindings/pyroot_experimental/PyROOT/src/RPyROOTApplication.cxx new file mode 100644 index 00000000000..116133b450b --- /dev/null +++ b/bindings/pyroot_experimental/PyROOT/src/RPyROOTApplication.cxx @@ -0,0 +1,151 @@ +// Author: Enric Tejedor CERN 04/2019 +// Original PyROOT code by Wim Lavrijsen, LBL + +/************************************************************************* + * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +// Bindings +#include "Python.h" +#include "RPyROOTApplication.h" + +// ROOT +#include "TInterpreter.h" +#include "TSystem.h" +#include "TBenchmark.h" +#include "TStyle.h" +#include "TError.h" +#include "Getline.h" +#include "TVirtualMutex.h" +#ifdef R__WIN32 +#include "TVirtualX.h" +#endif + +//////////////////////////////////////////////////////////////////////////// +/// \brief Create an RPyROOTApplication. +/// \return false if gApplication is not null, true otherwise. +bool PyROOT::RPyROOTApplication::CreateApplication() +{ + if (!gApplication) { + int argc = 1; + char **argv = new char *[argc]; + + // TODO: Consider parsing arguments for the RPyROOTApplication here + +#if PY_VERSION_HEX < 0x03000000 + if (Py_GetProgramName() && strlen(Py_GetProgramName()) != 0) + argv[0] = Py_GetProgramName(); + else + argv[0] = (char *)"python"; +#else + argv[0] = (char *)"python"; +#endif + + gApplication = new RPyROOTApplication("PyROOT", &argc, argv); + delete[] argv; // TApplication ctor has copied argv, so done with it + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////// +/// \brief Setup the basic ROOT globals gBenchmark, gStyle and gProgname, +/// if not already set. +void PyROOT::RPyROOTApplication::InitROOTGlobals() +{ + if (!gBenchmark) + gBenchmark = new TBenchmark(); + if (!gStyle) + gStyle = new TStyle(); + + if (!gProgName) // should have been set by TApplication +#if PY_VERSION_HEX < 0x03000000 + gSystem->SetProgname(Py_GetProgramName()); +#else + gSystem->SetProgname("python"); +#endif +} + +//////////////////////////////////////////////////////////////////////////// +/// \brief Translate ROOT error/warning to Python. +static void ErrMsgHandler(int level, Bool_t abort, const char *location, const char *msg) +{ + // Initialization from gEnv (the default handler will return w/o msg b/c level too low) + if (gErrorIgnoreLevel == kUnset) + ::DefaultErrorHandler(kUnset - 1, kFALSE, "", ""); + + if (level < gErrorIgnoreLevel) + return; + + // Turn warnings into Python warnings + if (level >= kError) { + ::DefaultErrorHandler(level, abort, location, msg); + } else if (level >= kWarning) { + static const char *emptyString = ""; + if (!location) + location = emptyString; + // This warning might be triggered while holding the ROOT lock, while + // some other thread is holding the GIL and waiting for the ROOT lock. + // That will trigger a deadlock. + // So if ROOT is in MT mode, use ROOT's error handler that doesn't take + // the GIL. + if (!gGlobalMutex) { + // Either printout or raise exception, depending on user settings + PyErr_WarnExplicit(NULL, (char *)msg, (char *)location, 0, (char *)"ROOT", NULL); + } else { + ::DefaultErrorHandler(level, abort, location, msg); + } + } else { + ::DefaultErrorHandler(level, abort, location, msg); + } +} + +//////////////////////////////////////////////////////////////////////////// +/// \brief Install the ROOT message handler which will turn ROOT error +/// messages into Python exceptions. +void PyROOT::RPyROOTApplication::InitROOTMessageCallback() +{ + SetErrorHandler((ErrorHandlerFunc_t)&ErrMsgHandler); +} + +//////////////////////////////////////////////////////////////////////////// +/// \brief Initialize an RPyROOTApplication. +PyObject *PyROOT::RPyROOTApplication::InitApplication() +{ + if (CreateApplication()) { + InitROOTGlobals(); + InitROOTMessageCallback(); + } + + Py_RETURN_NONE; +} + +//////////////////////////////////////////////////////////////////////////// +/// \brief Construct a TApplication for PyROOT. +/// \param[in] acn Application class name. +/// \param[in] argc Number of arguments. +/// \param[in] argv Arguments. +PyROOT::RPyROOTApplication::RPyROOTApplication(const char *acn, int *argc, char **argv) : TApplication(acn, argc, argv) +{ +#ifdef WIN32 + // Switch win32 proxy main thread id + if (gVirtualX) + ProcessLine("((TGWin32 *)gVirtualX)->SetUserThreadId(0);", true); +#endif + + // Save current interpreter context + gInterpreter->SaveContext(); + gInterpreter->SaveGlobalsContext(); + + // Prevent crashes on accessing history + Gl_histinit((char *)"-"); + + // Prevent ROOT from exiting python + SetReturnFromRun(true); +} diff --git a/bindings/pyroot_experimental/PyROOT/src/RPyROOTApplication.h b/bindings/pyroot_experimental/PyROOT/src/RPyROOTApplication.h new file mode 100644 index 00000000000..c116cf36014 --- /dev/null +++ b/bindings/pyroot_experimental/PyROOT/src/RPyROOTApplication.h @@ -0,0 +1,47 @@ +// Author: Enric Tejedor CERN 04/2019 +// Original PyROOT code by Wim Lavrijsen, LBL + +/************************************************************************* + * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT_PyROOTApplication +#define ROOT_PyROOTApplication + +// ROOT +#include "TApplication.h" + +namespace PyROOT { + +// clang-format off +/** +\class PyROOT::RPyROOTApplication +\brief Interactive application for Python. + + The RPyROOTApplication sets up the nuts and bolts for interactive ROOT use + from Python, closely following TRint. Note that not everything is done here, + some bits (such as e.g. the use of exception hook for shell escapes) are more + easily done in Python. +*/ +// clang-format on + +class RPyROOTApplication : public TApplication { +public: + static PyObject *InitApplication(); + + RPyROOTApplication(const char *acn, int *argc, char **argv); + virtual ~RPyROOTApplication() {} + +private: + static bool CreateApplication(); + static void InitROOTGlobals(); + static void InitROOTMessageCallback(); +}; + +} // namespace PyROOT + +#endif -- GitLab