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 "CPyCppyy.h"
15#include "RPyROOTApplication.h"
16
17// ROOT
18#include "TInterpreter.h"
19#include "TSystem.h"
20#include "TBenchmark.h"
21#include "TStyle.h"
22#include "TError.h"
23#include "Getline.h"
24#include "TVirtualMutex.h"
25#include "TVirtualPad.h"
26#include "TROOT.h"
27
28////////////////////////////////////////////////////////////////////////////
29/// \brief Create an RPyROOTApplication.
30/// \param[in] ignoreCmdLineOpts True if Python command line options should
31/// be ignored.
32/// \return false if gApplication is not null, true otherwise.
33///
34/// If ignoreCmdLineOpts is false, this method processes the command line
35/// arguments from sys.argv. A distinction between arguments for
36/// TApplication and user arguments can be made by using "-" or "--" as a
37/// separator on the command line.
38///
39/// For example, to enable batch mode from the command line:
40/// > python script_name.py -b -- user_arg1 ... user_argn
41/// or, if the user script receives no arguments:
42/// > python script_name.py -b
44{
45 if (!gApplication) {
46 int argc = 1;
47 char **argv = nullptr;
48
49 if (ignoreCmdLineOpts) {
50 argv = new char *[argc];
51 } else {
52 // Retrieve sys.argv list from Python
53 PyObject *argl = PySys_GetObject(const_cast<char *>("argv"));
54
55 if (argl && 0 < PyList_Size(argl))
56 argc = (int)PyList_GET_SIZE(argl);
57
58 argv = new char *[argc];
59 for (int i = 1; i < argc; ++i) {
60 char *argi = const_cast<char *>(CPyCppyy_PyText_AsString(PyList_GET_ITEM(argl, i)));
61 if (strcmp(argi, "-") == 0 || strcmp(argi, "--") == 0) {
62 // Stop collecting options, the remaining are for the Python script
63 argc = i; // includes program name
64 break;
65 }
66 argv[i] = argi;
67 }
68 }
69
70#if PY_VERSION_HEX < 0x03000000
71 if (Py_GetProgramName() && strlen(Py_GetProgramName()) != 0)
72 argv[0] = Py_GetProgramName();
73 else
74 argv[0] = (char *)"python";
75#else
76 argv[0] = (char *)"python";
77#endif
78
79 gApplication = new RPyROOTApplication("PyROOT", &argc, argv);
80 delete[] argv; // TApplication ctor has copied argv, so done with it
81
82 return true;
83 }
84
85 return false;
86}
87
88////////////////////////////////////////////////////////////////////////////
89/// \brief Setup the basic ROOT globals gBenchmark, gStyle and gProgname,
90/// if not already set.
92{
93 if (!gBenchmark)
94 gBenchmark = new TBenchmark();
95 if (!gStyle)
96 gStyle = new TStyle();
97
98 if (!gProgName) // should have been set by TApplication
99#if PY_VERSION_HEX < 0x03000000
100 gSystem->SetProgname(Py_GetProgramName());
101#else
102 gSystem->SetProgname("python");
103#endif
104}
105
106////////////////////////////////////////////////////////////////////////////
107/// \brief Translate ROOT error/warning to Python.
108static void ErrMsgHandler(int level, Bool_t abort, const char *location, const char *msg)
109{
110 // Initialization from gEnv (the default handler will return w/o msg b/c level too low)
112 ::DefaultErrorHandler(kUnset - 1, kFALSE, "", "");
113
114 if (level < gErrorIgnoreLevel)
115 return;
116
117 // Turn warnings into Python warnings
118 if (level >= kError) {
119 ::DefaultErrorHandler(level, abort, location, msg);
120 } else if (level >= kWarning) {
121 static const char *emptyString = "";
122 if (!location)
123 location = emptyString;
124 // This warning might be triggered while holding the ROOT lock, while
125 // some other thread is holding the GIL and waiting for the ROOT lock.
126 // That will trigger a deadlock.
127 // So if ROOT is in MT mode, use ROOT's error handler that doesn't take
128 // the GIL.
129 if (!gGlobalMutex) {
130 // Either printout or raise exception, depending on user settings
131 PyErr_WarnExplicit(NULL, (char *)msg, (char *)location, 0, (char *)"ROOT", NULL);
132 } else {
133 ::DefaultErrorHandler(level, abort, location, msg);
134 }
135 } else {
136 ::DefaultErrorHandler(level, abort, location, msg);
137 }
138}
139
140////////////////////////////////////////////////////////////////////////////
141/// \brief Install the ROOT message handler which will turn ROOT error
142/// messages into Python exceptions.
144{
146}
147
148////////////////////////////////////////////////////////////////////////////
149/// \brief Initialize an RPyROOTApplication.
150/// \param[in] self Always null, since this is a module function.
151/// \param[in] args [0] Boolean that tells whether to ignore the command line options.
153{
154 int argc = PyTuple_GET_SIZE(args);
155 if (argc == 1) {
156 PyObject *ignoreCmdLineOpts = PyTuple_GetItem(args, 0);
157
158 if (!PyBool_Check(ignoreCmdLineOpts)) {
159 PyErr_SetString(PyExc_TypeError, "Expected boolean type as argument.");
160 return nullptr;
161 }
162
163 if (CreateApplication(PyObject_IsTrue(ignoreCmdLineOpts))) {
164 InitROOTGlobals();
165 InitROOTMessageCallback();
166 }
167 } else {
168 PyErr_Format(PyExc_TypeError, "Expected 1 argument, %d passed.", argc);
169 return nullptr;
170 }
171
173}
174
175////////////////////////////////////////////////////////////////////////////
176/// \brief Construct a TApplication for PyROOT.
177/// \param[in] acn Application class name.
178/// \param[in] argc Number of arguments.
179/// \param[in] argv Arguments.
180PyROOT::RPyROOTApplication::RPyROOTApplication(const char *acn, int *argc, char **argv) : TApplication(acn, argc, argv)
181{
182 // Save current interpreter context
183 gInterpreter->SaveContext();
184 gInterpreter->SaveGlobalsContext();
185
186 // Prevent crashes on accessing history
187 Gl_histinit((char *)"-");
188
189 // Prevent ROOT from exiting python
190 SetReturnFromRun(true);
191}
192
193namespace {
194static int (*sOldInputHook)() = nullptr;
195static PyThreadState *sInputHookEventThreadState = nullptr;
196
197static int EventInputHook()
198{
199 // This method is supposed to be called from CPython's command line and
200 // drives the GUI
201 PyEval_RestoreThread(sInputHookEventThreadState);
202 if (gPad && gPad->IsWeb())
203 gPad->UpdateAsync();
205 PyEval_SaveThread();
206
207 if (sOldInputHook)
208 return sOldInputHook();
209
210 return 0;
211}
212
213} // unnamed namespace
214
215////////////////////////////////////////////////////////////////////////////
216/// \brief Install a method hook for sending events to the GUI.
217/// \param[in] self Always null, since this is a module function.
218/// \param[in] args Pointer to an empty Python tuple.
220{
221 if (PyOS_InputHook && PyOS_InputHook != &EventInputHook)
222 sOldInputHook = PyOS_InputHook;
223
224 sInputHookEventThreadState = PyThreadState_Get();
225
226 PyOS_InputHook = (int (*)()) & EventInputHook;
227
229}
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:97
#define Py_RETURN_NONE
Definition CPyCppyy.h:289
_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
Int_t gErrorIgnoreLevel
Error handling routines.
Definition TError.cxx:31
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
#define gInterpreter
R__EXTERN TStyle * gStyle
Definition TStyle.h:433
R__EXTERN const char * gProgName
Definition TSystem.h:242
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
R__EXTERN TVirtualMutex * gGlobalMutex
#define gPad
Interactive application for Python.
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.
static PyObject * InitApplication(PyObject *self, PyObject *args)
Initialize an RPyROOTApplication.
RPyROOTApplication(const char *acn, int *argc, char **argv)
Construct a TApplication for PyROOT.
static void InitROOTGlobals()
Setup the basic ROOT globals gBenchmark, gStyle and gProgname, if not already set.
This class creates the ROOT Application Environment that interfaces to the windowing system eventloop...
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
virtual void SetProgname(const char *name)
Set the application name (from command line, argv[0]) and copy it in gProgName.
Definition TSystem.cxx:225
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:403