Skip to content
Merged
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
1 change: 0 additions & 1 deletion bindings/pyroot/pythonizations/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

set(cpp_sources
src/PyROOTModule.cxx
src/RPyROOTApplication.cxx
src/GenericPyz.cxx
src/CPPInstancePyz.cxx
)
Expand Down
67 changes: 64 additions & 3 deletions bindings/pyroot/pythonizations/python/ROOT/_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
# For the list of contributors see $ROOTSYS/README/CREDITS. #
################################################################################

import ctypes
import os
import sys
import time

Expand All @@ -16,16 +18,75 @@ class PyROOTApplication(object):
"""
Application class for PyROOT.
Configures the interactive usage of ROOT from Python.

It is created while the ROOT module is set up, so it reaches C++ through
cppyy.gbl, as attribute lookups on the ROOT module would restart the setup.
"""

def __init__(self, config, is_ipython):
from ROOT.libROOTPythonizations import InitApplication
import cppyy
Comment thread
vepadulano marked this conversation as resolved.

if not cppyy.gbl.gApplication:
from ROOT.libROOTPythonizations import InstallWarningHandler

# Construct a TApplication for PyROOT
InitApplication(config.IgnoreCommandLineOptions)
self._create_application(config.IgnoreCommandLineOptions)
self._init_root_globals()
InstallWarningHandler()

self._is_ipython = is_ipython

@staticmethod
def _create_application(ignore_cmd_line_opts):
"""
Create the TApplication. Unless ignore_cmd_line_opts is set, it gets
the command line options in sys.argv, up to a "-" or "--" that
separates them from the options for the Python script. For example,
to enable batch mode from the command line:

python script_name.py -b -- user_arg1 ... user_argn

or, if the script takes no options:

python script_name.py -b
"""
import cppyy

gbl = cppyy.gbl

args = ["python"]
if not ignore_cmd_line_opts:
for arg in getattr(sys, "argv", [])[1:]:
if arg in ("-", "--"):
break
args.append(arg)

# Bytes as given on the command line, and argv[argc] null like for main()
args = [os.fsencode(arg) for arg in args]
argc = ctypes.c_int(len(args))
argv = (ctypes.c_char_p * (len(args) + 1))(*args, None)
app = gbl.TApplication("PyROOT", argc, ctypes.cast(argv, ctypes.POINTER(ctypes.c_char_p)))
# It is gApplication from now on
cppyy._backend.SetOwnership(app, False)

# Prevent crashes on accessing history
gbl.Gl_histinit("-")

# Prevent ROOT from exiting Python
app.SetReturnFromRun(True)

@staticmethod
def _init_root_globals():
"""Set up gBenchmark, the one ROOT global that neither TROOT nor TApplication sets"""
import cppyy

gbl = cppyy.gbl

if not gbl.gBenchmark:
# The global owns the object, so Python must not delete it
benchmark = gbl.TBenchmark()
cppyy._backend.SetOwnership(benchmark, False)
gbl.gBenchmark = benchmark

@staticmethod
def _ipython_config():
# Integrate IPython >= 5 with ROOT's event loop
Expand Down
62 changes: 58 additions & 4 deletions bindings/pyroot/pythonizations/src/PyROOTModule.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@

// Bindings
#include "PyROOTPythonize.h"
#include "RPyROOTApplication.h"

// Cppyy
#include "cpyrt/API.h"

// ROOT
#include "TError.h"
#include "TInterpreter.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TVirtualPad.h"
#include "RConfigure.h"

// Standard
Expand Down Expand Up @@ -98,15 +99,68 @@ PyObject *PyObjRefCounterAsStdAny(PyObject * /*self*/, PyObject *args)

} // namespace PyROOT

namespace {

/// Turns a ROOT warning into a Python warning, see ROOT::Internal::SetWarningHandler().
bool WarningHandler(const char *location, const char *msg)
{
PyGILState_STATE state = PyGILState_Ensure();
// Python code can't run while an exception is pending, e.g. a previous
// warning turned into an error: decline, and ROOT prints the warning.
bool handled = !PyErr_Occurred();
if (handled) {
// Prints or, per the warnings filters, raises: the exception stays
// pending until the call into C++ returns to Python.
PyErr_WarnExplicit(nullptr, msg, location, 0, "ROOT", nullptr);
}
PyGILState_Release(state);
return handled;
}

int (*gPreviousInputHook)() = nullptr;

/// Processes GUI events while the Python prompt waits for input. Runs no
/// Python code, so a Ctrl-C arriving meanwhile stays pending for the prompt.
int GUIEventInputHook()
{
PyGILState_STATE state = PyGILState_Ensure();
if (gPad && gPad->IsWeb())
gPad->UpdateAsync();
gSystem->ProcessEvents();
PyGILState_Release(state);

return gPreviousInputHook ? gPreviousInputHook() : 0;
}

/// Turn ROOT warnings into Python warnings.
PyObject *InstallWarningHandler(PyObject * /*self*/, PyObject * /*args*/)
{
ROOT::Internal::SetWarningHandler(&WarningHandler);
Py_RETURN_NONE;
}

/// Install the PyOS_InputHook that processes GUI events, chaining to the
/// previous hook.
PyObject *InstallGUIEventInputHook(PyObject * /*self*/, PyObject * /*args*/)
{
if (PyOS_InputHook != &GUIEventInputHook) {
gPreviousInputHook = PyOS_InputHook;
PyOS_InputHook = &GUIEventInputHook;
}
Py_RETURN_NONE;
}

} // namespace

// Methods offered by the interface
static PyMethodDef gPyROOTMethods[] = {
{"AddCPPInstancePickling", (PyCFunction)PyROOT::AddCPPInstancePickling, METH_NOARGS,
"Add a custom pickling mechanism for Cppyy Python proxy objects"},
{"AddPrettyPrintingPyz", (PyCFunction)PyROOT::AddPrettyPrintingPyz, METH_VARARGS,
"Add pretty printing pythonization"},
{"InitApplication", (PyCFunction)PyROOT::RPyROOTApplication::InitApplication, METH_VARARGS,
"Initialize interactive ROOT use from Python"},
{"InstallGUIEventInputHook", (PyCFunction)PyROOT::RPyROOTApplication::InstallGUIEventInputHook, METH_NOARGS,
{"InstallWarningHandler", (PyCFunction)InstallWarningHandler, METH_NOARGS,
"Turn ROOT warnings into Python warnings"},
{"InstallGUIEventInputHook", (PyCFunction)InstallGUIEventInputHook, METH_NOARGS,
"Install an input hook to process GUI events"},
{"_CPPInstance__expand__", (PyCFunction)PyROOT::CPPInstanceExpand, METH_VARARGS,
"Deserialize a pickled object"},
Expand Down
Loading
Loading