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 && 0 < PyList_Size(argl))
55 argc = (int)PyList_GET_SIZE(argl);
56
57 argv = new char *[argc];
58 for (int i = 1; i < argc; ++i) {
59 char *argi = const_cast<char *>(PyUnicode_AsUTF8(PyList_GET_ITEM(argl, i)));
60 if (strcmp(argi, "-") == 0 || strcmp(argi, "--") == 0) {
61 // Stop collecting options, the remaining are for the Python script
62 argc = i; // includes program name
63 break;
64 }
65 argv[i] = argi;
66 }
67 }
68
69 argv[0] = (char *)"python";
70
71 gApplication = new RPyROOTApplication("PyROOT", &argc, argv);
72 delete[] argv; // TApplication ctor has copied argv, so done with it
73
74 return true;
75 }
76
77 return false;
78}
79
80////////////////////////////////////////////////////////////////////////////
81/// \brief Setup the basic ROOT globals gBenchmark, gStyle and gProgname,
82/// if not already set.
84{
85 if (!gBenchmark)
86 gBenchmark = new TBenchmark();
87 if (!gStyle)
88 gStyle = new TStyle();
89
90 if (!gProgName) // should have been set by TApplication
91 gSystem->SetProgname("python");
92}
93
94////////////////////////////////////////////////////////////////////////////
95/// \brief Translate ROOT error/warning to Python.
96static void ErrMsgHandler(int level, Bool_t abort, const char *location, const char *msg)
97{
98 // Initialization from gEnv (the default handler will return w/o msg b/c level too low)
100 ::DefaultErrorHandler(kUnset - 1, kFALSE, "", "");
101
102 if (level < gErrorIgnoreLevel)
103 return;
104
105 // Turn warnings into Python warnings
106 if (level >= kError) {
107 ::DefaultErrorHandler(level, abort, location, msg);
108 } else if (level >= kWarning) {
109 static const char *emptyString = "";
110 if (!location)
111 location = emptyString;
112 // This warning might be triggered while holding the ROOT lock, while
113 // some other thread is holding the GIL and waiting for the ROOT lock.
114 // That will trigger a deadlock.
115 // So if ROOT is in MT mode, use ROOT's error handler that doesn't take
116 // the GIL.
117 if (!gGlobalMutex) {
118 // Either printout or raise exception, depending on user settings
119 PyErr_WarnExplicit(NULL, (char *)msg, (char *)location, 0, (char *)"ROOT", NULL);
120 } else {
121 ::DefaultErrorHandler(level, abort, location, msg);
122 }
123 } else {
124 ::DefaultErrorHandler(level, abort, location, msg);
125 }
126}
127
128////////////////////////////////////////////////////////////////////////////
129/// \brief Install the ROOT message handler which will turn ROOT error
130/// messages into Python exceptions.
135
136////////////////////////////////////////////////////////////////////////////
137/// \brief Initialize an RPyROOTApplication.
138/// \param[in] self Always null, since this is a module function.
139/// \param[in] args [0] Boolean that tells whether to ignore the command line options.
141{
142 int argc = PyTuple_GET_SIZE(args);
143 if (argc == 1) {
144 PyObject *ignoreCmdLineOpts = PyTuple_GetItem(args, 0);
145
146 if (!PyBool_Check(ignoreCmdLineOpts)) {
147 PyErr_SetString(PyExc_TypeError, "Expected boolean type as argument.");
148 return nullptr;
149 }
150
151 if (CreateApplication(PyObject_IsTrue(ignoreCmdLineOpts))) {
154 }
155 } else {
156 PyErr_Format(PyExc_TypeError, "Expected 1 argument, %d passed.", argc);
157 return nullptr;
158 }
159
161}
162
163////////////////////////////////////////////////////////////////////////////
164/// \brief Construct a TApplication for PyROOT.
165/// \param[in] name Application class name.
166/// \param[in] argc Number of arguments.
167/// \param[in] argv Arguments.
168PyROOT::RPyROOTApplication::RPyROOTApplication(const char *name, int *argc, char **argv)
169 : TApplication(name, argc, argv)
170{
171 // Save current interpreter context
172 gInterpreter->SaveContext();
173 gInterpreter->SaveGlobalsContext();
174
175 // Prevent crashes on accessing history
176 Gl_histinit((char *)"-");
177
178 // Prevent ROOT from exiting python
179 SetReturnFromRun(true);
180}
181
182namespace {
183static int (*sOldInputHook)() = nullptr;
184static PyThreadState *sInputHookEventThreadState = nullptr;
185
186static int EventInputHook()
187{
188 // This method is supposed to be called from CPython's command line and
189 // drives the GUI
190 PyEval_RestoreThread(sInputHookEventThreadState);
191 if (gPad && gPad->IsWeb())
192 gPad->UpdateAsync();
193 gSystem->ProcessEvents();
194 PyEval_SaveThread();
195
196 if (sOldInputHook)
197 return sOldInputHook();
198
199 return 0;
200}
201
202} // unnamed namespace
203
204////////////////////////////////////////////////////////////////////////////
205/// \brief Install a method hook for sending events to the GUI.
206/// \param[in] self Always null, since this is a module function.
207/// \param[in] args Pointer to an empty Python tuple.
209{
210 if (PyOS_InputHook && PyOS_InputHook != &EventInputHook)
211 sOldInputHook = PyOS_InputHook;
212
213 sInputHookEventThreadState = PyThreadState_Get();
214
215 PyOS_InputHook = (int (*)()) & EventInputHook;
216
218}
#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.
bool Bool_t
Definition RtypesCore.h:63
constexpr Bool_t kFALSE
Definition RtypesCore.h:101
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:46
constexpr Int_t kWarning
Definition TError.h:45
void(* ErrorHandlerFunc_t)(int level, Bool_t abort, const char *location, const char *msg)
Definition TError.h:70
externInt_t gErrorIgnoreLevel
Definition TError.h:127
ErrorHandlerFunc_t SetErrorHandler(ErrorHandlerFunc_t newhandler)
Set an errorhandler function. Returns the old handler.
Definition TError.cxx:90
constexpr Int_t kUnset
Definition TError.h:42
char name[80]
Definition TGX11.cxx:110
Int_t i
#define gInterpreter
R__EXTERN TStyle * gStyle
Definition TStyle.h:433
R__EXTERN const char * gProgName
Definition TSystem.h:246
R__EXTERN TSystem * gSystem
Definition TSystem.h:566
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