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 auto state = PyGILState_Ensure();
126 PyErr_WarnExplicit(NULL, (char *)msg, (char *)location, 0, (char *)"ROOT", NULL);
127 PyGILState_Release(state);
128 } else {
129 ::DefaultErrorHandler(level, abort, location, msg);
130 }
131 } else {
132 ::DefaultErrorHandler(level, abort, location, msg);
133 }
134}
135
136////////////////////////////////////////////////////////////////////////////
137/// \brief Install the ROOT message handler which will turn ROOT error
138/// messages into Python exceptions.
143
144////////////////////////////////////////////////////////////////////////////
145/// \brief Initialize an RPyROOTApplication.
146/// \param[in] self Always null, since this is a module function.
147/// \param[in] args [0] Boolean that tells whether to ignore the command line options.
149{
150 int argc = PyTuple_Size(args);
151 if (argc == 1) {
152 PyObject *ignoreCmdLineOpts = PyTuple_GetItem(args, 0);
153
154 if (!PyBool_Check(ignoreCmdLineOpts)) {
155 PyErr_SetString(PyExc_TypeError, "Expected boolean type as argument.");
156 return nullptr;
157 }
158
159 if (CreateApplication(PyObject_IsTrue(ignoreCmdLineOpts))) {
162 }
163 } else {
164 PyErr_Format(PyExc_TypeError, "Expected 1 argument, %d passed.", argc);
165 return nullptr;
166 }
167
169}
170
171////////////////////////////////////////////////////////////////////////////
172/// \brief Construct a TApplication for PyROOT.
173/// \param[in] name Application class name.
174/// \param[in] argc Number of arguments.
175/// \param[in] argv Arguments.
176PyROOT::RPyROOTApplication::RPyROOTApplication(const char *name, int *argc, char **argv)
177 : TApplication(name, argc, argv)
178{
179 // Save current interpreter context
180 gInterpreter->SaveContext();
181 gInterpreter->SaveGlobalsContext();
182
183 // Prevent crashes on accessing history
184 Gl_histinit((char *)"-");
185
186 // Prevent ROOT from exiting python
187 SetReturnFromRun(true);
188}
189
190namespace {
191static int (*sOldInputHook)() = nullptr;
192static PyThreadState *sInputHookEventThreadState = nullptr;
193
194static int EventInputHook()
195{
196 // This method is supposed to be called from CPython's command line and
197 // drives the GUI
198 PyEval_RestoreThread(sInputHookEventThreadState);
199 if (gPad && gPad->IsWeb())
200 gPad->UpdateAsync();
201 gSystem->ProcessEvents();
202 PyEval_SaveThread();
203
204 if (sOldInputHook)
205 return sOldInputHook();
206
207 return 0;
208}
209
210} // unnamed namespace
211
212////////////////////////////////////////////////////////////////////////////
213/// \brief Install a method hook for sending events to the GUI.
214/// \param[in] self Always null, since this is a module function.
215/// \param[in] args Pointer to an empty Python tuple.
217{
218 if (PyOS_InputHook && PyOS_InputHook != &EventInputHook)
219 sOldInputHook = PyOS_InputHook;
220
221 sInputHookEventThreadState = PyThreadState_Get();
222
223 PyOS_InputHook = (int (*)()) & EventInputHook;
224
226}
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
R__EXTERN TApplication * gApplication
R__EXTERN TBenchmark * 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
R__EXTERN TStyle * gStyle
Definition TStyle.h:442
R__EXTERN const char * gProgName
Definition TSystem.h:252
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
R__EXTERN TVirtualMutex * 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