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
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] ignoreCmdLineOpts True if Python command line options should
150/// be ignored.
152{
153 int argc = PyTuple_GET_SIZE(args);
154 if (argc == 1) {
155 PyObject *ignoreCmdLineOpts = PyTuple_GetItem(args, 0);
156
157 if (!PyBool_Check(ignoreCmdLineOpts)) {
158 PyErr_SetString(PyExc_TypeError, "Expected boolean type as argument.");
159 return nullptr;
160 }
161
162 if (CreateApplication(PyObject_IsTrue(ignoreCmdLineOpts))) {
163 InitROOTGlobals();
164 InitROOTMessageCallback();
165 }
166 } else {
167 PyErr_Format(PyExc_TypeError, "Expected 1 argument, %d passed.", argc);
168 return nullptr;
169 }
170
172}
173
174////////////////////////////////////////////////////////////////////////////
175/// \brief Construct a TApplication for PyROOT.
176/// \param[in] acn Application class name.
177/// \param[in] argc Number of arguments.
178/// \param[in] argv Arguments.
179PyROOT::RPyROOTApplication::RPyROOTApplication(const char *acn, int *argc, char **argv) : TApplication(acn, argc, argv)
180{
181 // Save current interpreter context
182 gInterpreter->SaveContext();
183 gInterpreter->SaveGlobalsContext();
184
185 // Prevent crashes on accessing history
186 Gl_histinit((char *)"-");
187
188 // Prevent ROOT from exiting python
189 SetReturnFromRun(true);
190}
191
192namespace {
193static int (*sOldInputHook)() = nullptr;
194static PyThreadState *sInputHookEventThreadState = nullptr;
195
196static int EventInputHook()
197{
198 // This method is supposed to be called from CPython's command line and
199 // drives the GUI
200 PyEval_RestoreThread(sInputHookEventThreadState);
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}
#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.
const Bool_t kFALSE
Definition RtypesCore.h:101
bool Bool_t
Definition RtypesCore.h:63
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
R__EXTERN Int_t gErrorIgnoreLevel
Definition TError.h:127
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
R__EXTERN TStyle * gStyle
Definition TStyle.h:413
R__EXTERN const char * gProgName
Definition TSystem.h:242
R__EXTERN TSystem * gSystem
Definition TSystem.h:559
R__EXTERN TVirtualMutex * gGlobalMutex
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:419