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