Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
RPyROOTApplication.cxx
Go to the documentation of this file.
1// Author: Enric Tejedor CERN 04/2019
2// Original PyROOT code by Wim Lavrijsen, LBL
3
4/*************************************************************************
5 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12// Bindings
13#include <Python.h>
14#include "RPyROOTApplication.h"
15
16// ROOT
17#include "TInterpreter.h"
18#include "TSystem.h"
19#include "TBenchmark.h"
20#include "TStyle.h"
21#include "TError.h"
22#include "Getline.h"
23#include "TVirtualMutex.h"
24#include "TVirtualPad.h"
25#include "TROOT.h"
26
27////////////////////////////////////////////////////////////////////////////
28/// \brief Create an RPyROOTApplication.
29/// \param[in] ignoreCmdLineOpts True if Python command line options should
30/// be ignored.
31/// \return false if gApplication is not null, true otherwise.
32///
33/// If ignoreCmdLineOpts is false, this method processes the command line
34/// arguments from sys.argv. A distinction between arguments for
35/// TApplication and user arguments can be made by using "-" or "--" as a
36/// separator on the command line.
37///
38/// For example, to enable batch mode from the command line:
39/// > python script_name.py -b -- user_arg1 ... user_argn
40/// or, if the user script receives no arguments:
41/// > python script_name.py -b
43{
44 if (!gApplication) {
45 int argc = 1;
46 char **argv = nullptr;
47
48 if (ignoreCmdLineOpts) {
49 argv = new char *[argc];
50 } else {
51 // Retrieve sys.argv list from Python
52 PyObject *argl = PySys_GetObject("argv");
53
54 if (argl) {
55 Py_ssize_t size = PyList_Size(argl);
56 if (size > 0)
57 argc = static_cast<int>(size);
58 }
59
60 argv = new char *[argc];
61
62 for (int i = 1; i < argc; ++i) {
63 PyObject *item = PyList_GetItem(argl, i);
64 const char *argi = PyUnicode_AsUTF8AndSize(item, nullptr);
65
66 if (strcmp(argi, "-") == 0 || strcmp(argi, "--") == 0) {
67 // Stop collecting options, the remaining are for the Python script
68 argc = i; // includes program name
69 break;
70 }
71 argv[i] = const_cast<char *>(argi);
72 }
73 }
74
75 argv[0] = (char *)"python";
76
77 gApplication = new RPyROOTApplication("PyROOT", &argc, argv);
78 delete[] argv; // TApplication ctor has copied argv, so done with it
79
80 return true;
81 }
82
83 return false;
84}
85
86////////////////////////////////////////////////////////////////////////////
87/// \brief Setup the basic ROOT globals gBenchmark, gStyle and gProgname,
88/// if not already set.
90{
91 if (!gBenchmark)
92 gBenchmark = new TBenchmark();
93 if (!gStyle)
94 gStyle = new TStyle();
95
96 if (!gProgName) // should have been set by TApplication
97 gSystem->SetProgname("python");
98}
99
100////////////////////////////////////////////////////////////////////////////
101/// \brief Translate ROOT error/warning to Python.
102static void ErrMsgHandler(int level, Bool_t abort, const char *location, const char *msg)
103{
104 // Initialization from gEnv (the default handler will return w/o msg b/c level too low)
106 ::DefaultErrorHandler(kUnset - 1, kFALSE, "", "");
107
108 if (level < gErrorIgnoreLevel)
109 return;
110
111 // Turn warnings into Python warnings
112 if (level >= kError) {
113 ::DefaultErrorHandler(level, abort, location, msg);
114 } else if (level >= kWarning) {
115 static const char *emptyString = "";
116 if (!location)
117 location = emptyString;
118 // This warning might be triggered while holding the ROOT lock, while
119 // some other thread is holding the GIL and waiting for the ROOT lock.
120 // That will trigger a deadlock.
121 // So if ROOT is in MT mode, use ROOT's error handler that doesn't take
122 // the GIL.
123 if (!gGlobalMutex) {
124 // Either printout or raise exception, depending on user settings
125 PyErr_WarnExplicit(NULL, (char *)msg, (char *)location, 0, (char *)"ROOT", NULL);
126 } else {
127 ::DefaultErrorHandler(level, abort, location, msg);
128 }
129 } else {
130 ::DefaultErrorHandler(level, abort, location, msg);
131 }
132}
133
134////////////////////////////////////////////////////////////////////////////
135/// \brief Install the ROOT message handler which will turn ROOT error
136/// messages into Python exceptions.
141
142////////////////////////////////////////////////////////////////////////////
143/// \brief Initialize an RPyROOTApplication.
144/// \param[in] self Always null, since this is a module function.
145/// \param[in] args [0] Boolean that tells whether to ignore the command line options.
147{
148 int argc = PyTuple_Size(args);
149 if (argc == 1) {
150 PyObject *ignoreCmdLineOpts = PyTuple_GetItem(args, 0);
151
152 if (!PyBool_Check(ignoreCmdLineOpts)) {
153 PyErr_SetString(PyExc_TypeError, "Expected boolean type as argument.");
154 return nullptr;
155 }
156
157 if (CreateApplication(PyObject_IsTrue(ignoreCmdLineOpts))) {
160 }
161 } else {
162 PyErr_Format(PyExc_TypeError, "Expected 1 argument, %d passed.", argc);
163 return nullptr;
164 }
165
167}
168
169////////////////////////////////////////////////////////////////////////////
170/// \brief Construct a TApplication for PyROOT.
171/// \param[in] name Application class name.
172/// \param[in] argc Number of arguments.
173/// \param[in] argv Arguments.
174PyROOT::RPyROOTApplication::RPyROOTApplication(const char *name, int *argc, char **argv)
175 : TApplication(name, argc, argv)
176{
177 // Save current interpreter context
178 gInterpreter->SaveContext();
179 gInterpreter->SaveGlobalsContext();
180
181 // Prevent crashes on accessing history
182 Gl_histinit((char *)"-");
183
184 // Prevent ROOT from exiting python
185 SetReturnFromRun(true);
186}
187
188namespace {
189static int (*sOldInputHook)() = nullptr;
190static PyThreadState *sInputHookEventThreadState = nullptr;
191
192static int EventInputHook()
193{
194 // This method is supposed to be called from CPython's command line and
195 // drives the GUI
196 PyEval_RestoreThread(sInputHookEventThreadState);
197 if (gPad && gPad->IsWeb())
198 gPad->UpdateAsync();
199 gSystem->ProcessEvents();
200 PyEval_SaveThread();
201
202 if (sOldInputHook)
203 return sOldInputHook();
204
205 return 0;
206}
207
208} // unnamed namespace
209
210////////////////////////////////////////////////////////////////////////////
211/// \brief Install a method hook for sending events to the GUI.
212/// \param[in] self Always null, since this is a module function.
213/// \param[in] args Pointer to an empty Python tuple.
215{
216 if (PyOS_InputHook && PyOS_InputHook != &EventInputHook)
217 sOldInputHook = PyOS_InputHook;
218
219 sInputHookEventThreadState = PyThreadState_Get();
220
221 PyOS_InputHook = (int (*)()) & EventInputHook;
222
224}
int Py_ssize_t
Definition CPyCppyy.h:215
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
_object PyObject
static void ErrMsgHandler(int level, Bool_t abort, const char *location, const char *msg)
Translate ROOT error/warning to Python.
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
bool Bool_t
Boolean (0=false, 1=true) (bool).
Definition RtypesCore.h:77
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
externTApplication * gApplication
externTBenchmark * gBenchmark
Definition TBenchmark.h:59
void DefaultErrorHandler(Int_t level, Bool_t abort_bool, const char *location, const char *msg)
The default error handler function.
constexpr Int_t kError
Definition TError.h:47
constexpr Int_t kWarning
Definition TError.h:46
void(* ErrorHandlerFunc_t)(int level, Bool_t abort, const char *location, const char *msg)
Definition TError.h:71
externInt_t gErrorIgnoreLevel
errors with level below this value will be ignored. Default is kUnset.
Definition TError.h:140
ErrorHandlerFunc_t SetErrorHandler(ErrorHandlerFunc_t newhandler)
Set an errorhandler function. Returns the old handler.
Definition TError.cxx:92
constexpr Int_t kUnset
Definition TError.h:43
char name[80]
Definition TGX11.cxx:148
#define gInterpreter
externTStyle * gStyle
Definition TStyle.h:442
externconst char * gProgName
Definition TSystem.h:252
externTSystem * gSystem
Definition TSystem.h:582
externTVirtualMutex * gGlobalMutex
#define gPad
static PyObject * InstallGUIEventInputHook(PyObject *self, PyObject *args)
Install a method hook for sending events to the GUI.
static void InitROOTMessageCallback()
Install the ROOT message handler which will turn ROOT error messages into Python exceptions.
RPyROOTApplication(const char *name, int *argc, char **argv)
Construct a TApplication for PyROOT.
static PyObject * InitApplication(PyObject *self, PyObject *args)
Initialize an RPyROOTApplication.
static bool CreateApplication(int ignoreCmdLineOpts)
Create an RPyROOTApplication.
static void InitROOTGlobals()
Setup the basic ROOT globals gBenchmark, gStyle and gProgname, if not already set.
TApplication(const TApplication &)=delete
void SetReturnFromRun(Bool_t ret)
static void CreateApplication()
Static function used to create a default application environment.
This class is a ROOT utility to help benchmarking applications.
Definition TBenchmark.h:29
TStyle objects may be created to define special styles.
Definition TStyle.h:29