Python interface: PyROOT

With PyROOT, ROOT’s Python-C++ bindings, you can use ROOT from Python. PyROOT is HEP’s entrance to all C++ from Python, for example, for frameworks and their steering code. The PyROOT bindings are automatic and dynamic: no pre-generation of Python wrappers is necessary.

With PyROOT you can access the full ROOT functionality from Python while benefiting from the performance of the ROOT C++ libraries.

PyROOT is compatible with both Python2 (>= 2.7) and Python3.

The usage of PyROOT requires working knowledge of Python.
For detailed information on Python, refer to the Python Language Reference.

Together with ROOT 6.22, a major revision of PyROOT has been released. The new PyROOT has extensive support for modern C++ (it operates on top of cppyy), is more pythonic and is able to interoperate with widely-used Python data-science libraries (for example, NumPy, pandas, Numba).
Therefore, we strongly recommend to use the new PyROOT.

→ PyROOT tutorials

Getting started

When ROOT is installed, you can use PyROOT both from the Python prompt and from a Python script. The entry point to PyROOT is the ROOT module, which you must import first:

import ROOT

Then you can access the ROOT C++ classes, functions, etc. via the ROOT module.

Example

This examples shows how you can create a histogram (an instance of the TH1F class) and randomly fill it with a gaussian distribution.

h = ROOT.TH1F("myHist", "myTitle", 64, -4, 4)
h.FillRandom("gaus")

Interactive graphics

Just like from C++, you can also create interactive ROOT graphics from Python with PyROOT.

Example

A one-dimensional function is created and drawn:

>>> import ROOT
>>> f = ROOT.TF1("f1", "sin(x)/x", 0., 10.)
>>> f.Draw()

When the above code is executed from the Python prompt, a new canvas opens and displays the drawn function:

Figure: Example of graphics generated with PyROOT.

Note

You can also write the above code in a script file and execute it with Python. In that case, the script runs to completion and the Python process is terminated, so the generated canvas disappears.
If you want to keep the Python process alive and thus inspect your canvas, execute the script with:
python -i script_name.py

User interface

Besides being the entry point to all ROOT functionality, the ROOT module also provides an interface to configure PyROOT and to manipulate PyROOT objects at a lower level.

Configuration options

After importing the ROOT module, you can access the PyROOT configuration object as ROOT.PyConfig. Such an object has a set of properties that you can modify to steer the behaviour of PyROOT. For the configuration to be taken into account, it needs to be applied right after the ROOT module is imported.

Example

import ROOT
ROOT.PyConfig.DisableRootLogon = True
ROOT.PyConfig.IgnoreCommandLineOptions = False

The available configuration options are:

  • DisableRootLogon (default False): Just like in C++, PyROOT also supports rootlogon. When PyROOT starts, it looks for a file called .rootlogon.py in the user’s home directory. If such file exists, PyROOT imports it. You can use it to make some settings every time PyROOT is launched, for example, defining some style for your plots:
# Content of .rootlogon.py
import ROOT
myStyle = ROOT.TStyle('MyStyle','My graphics style')
myStyle.SetCanvasColor(ROOT.kBlue) # My canvases are blue!
ROOT.gROOT.SetStyle('MyStyle')

If PyROOT cannot find .rootlogon.py in the user’s home directory, it looks for the equivalent in C++ (.rootlogon.C), first in ROOT’s etc directory, then in the user’s home directory and finally in the current working directory.

Note that it is also possible to use both the Python and the C++ rootlogons, since the latter can be loaded from the former, for example with ROOT.gROOT.LoadMacro('.rootlogon.C').

If you want to disable the rootlogon functionality, set PyConfig.DisableRootLogon to True.

  • IgnoreCommandLineOptions (default True): If a PyROOT script is run with some command line arguments, ROOT ignores them by default, so you can process them as you wish. However, by setting PyConfig.IgnoreCommandLineOptions to False, those arguments are forwarded to ROOT for parsing, for example, to enable the batch mode from the command line.
    For a complete list of the arguments accepted by ROOT, → see Starting ROOT with command line options.

  • ShutDown (default True): When the application is finished, more precisely during PyROOT’s cleanup phase, the ROOT C++ interpreter is shut down by default.
    If PyROOT is executed as part of a longer-running application that needs the C++ interpreter, you can set PyConfig.ShutDown to False to prevent that shutdown.

  • StartGUIThread (default True): Unless executed from IPython, a Jupyter notebook or in interactive mode, PyROOT starts a thread that periodically polls for ROOT events (for example GUI events) to process them. If a given PyROOT application does not need this event processing, you can prevent the creation of the thread by setting PyConfig.StartGUIThread to False.

Enabling batch mode

When running in batch mode, PyROOT does not display any graphics. You can activate the batch mode as follows:

  1. Pass -b as a command line argument, for example, python -b my_pyroot_script.py.
    For this to work, you must set PyConfig.IgnoreCommandLineOptions to False inside the PyROOT script, see → Configuration options.

  2. Call gROOT.SetBatch in the PyROOT script, right after importing ROOT:

import ROOT
ROOT.gROOT.SetBatch(True)

Low-level manipulation of objects

When instantiating a C++ class from Python via PyROOT, both a C++ object and its Python proxy object are created. Such a Python object forwards any access to its internal C++ object, thus acting as a proxy. PyROOT provides functions to inspect or manipulate Python proxies and their C++ counterparts, based on the functionality provided by cppyy.
You can access these functions with the ROOT.NameOfFunction pattern as follows:

  • AddressOf(obj): When applied to a Proxy object obj, it returns an indexable buffer of length 1, whose only element is the address of the C++ object proxied by obj. The address of the buffer is the same as the address of the address of the C++ object. This function is kept for backwards compatibility with old PyROOT versions.

  • addressof(obj, field, byref): Similarly to AddressOf, you can use it to obtain the address of an internal C++ object from its Python proxy. However, addressof returns that address as an integer, not as an indexable buffer. Furthermore, addressof accepts two more parameters: field and byref. You can use field to specify the name of a field in a struct, in order to get the address of that field. If you set byref to True, addressof returns the address of the address of the C++ object. This function is equivalent to cppyy’s addressof.

Example

>>> import ROOT
>>> ROOT.gInterpreter.Declare("struct MyStruct { int foo; float bar; };")
True
>>> mys = ROOT.MyStruct()
>>> ROOT.addressof(mys) # Address of mys' C++ object
94352024283040L
>>> ROOT.addressof(mys, 'foo') # Address of "foo" field (same as address of object)
94352024283040L
>>> ROOT.addressof(mys, 'bar') # Address of "bar" field
94352024283044L
>>> ROOT.addressof(mys, byref = True) # Address of address of mys' C++ object
140376843834640L

Moreover, you can use addressof in conjunction with TTree::Branch from Python as shown in ttree_branch.py.

  • AsCObject: Equivalent to cppyy’s as_cobject.

  • BindObject: Equivalent to cppyy’s bind_object.

  • MakeNullPointer(class_proxy): Equivalent to BindObject(0, class_proxy), it returns a proxy object of the class represented by class_proxy that is bound to a C++ null pointer.
    For example, MakeNullPointer(TTree) returns a TTree proxy object that internally points to null. This function is kept for backwards compatibility with old PyROOT versions.

  • SetOwnership(obj, python_owns): A Python proxy can either own or not own its internal C++ object. If a Python proxy owns its C++ object and the proxy is being destroyed, the C++ object is deleted too. This ownership can be modified for a given Python proxy with SetOwnership.
    For example, by calling SetOwnership(obj, False), you make sure that the C++ object proxied by obj is not deleted by PyROOT when obj is garbage collected. This is useful for example, if you know that the deletion will happen in C++ and you want to prevent a double delete.
    Use this functionality with care not to produce any memory leaks.

Loading user libraries and Just-In-Time compilation (JITting)

With PyROOT you can use any C++ library from Python, not only the ROOT libraries. This is possible because of the automatic and dynamic bindings between Python and C++ that PyROOT provides. Without any prior generation of wrappers, at execution time, PyROOT can load C++ code and call into it.

This enables you to write high-performance C++, compile it and use it from Python. The following options are available, ordered by complexity and performance:

Just-in-time compilation of small strings

ROOT has a C++ interpreter, which can process C++ code. Sometimes such a code is short, e.g. for the definition of a small function or a class or for rapid exploration or debugging. To do this, place the C++ code in a Python string, which is passed to the interpreter. The code is just-in-time compiled (jitted) and it is immediately available for invocation, as shown in the following example. Here, the constructor of the C++ class A and the function f are called from Python after defining them via the interpreter.

Example

import ROOT

# Write some C++ code in a string
cpp_code = """
// Function definition
int f(int i) { return i*i; }

// Class definition
class A {
public:
    A() { cout << "Hello PyROOT!" << endl; }
};
"""

# Inject the code in the ROOT interpreter
ROOT.gInterpreter.ProcessLine(cpp_code)

# We find all the C++ entities in Python, right away!
a = ROOT.A()   # prints Hello PyROOT!
x = ROOT.f(3)  # x = 9

Just-in-time compilation of entire files

If you want to use the C++ code in a header, you can use the interpreter to include and compile it on the fly.

Example

There is a header called my_header.h with the following content:

int f(int i) { return i*i; }

class A {
public:
    A() { cout << "Hello PyROOT!" << endl; }
};

In Python, you can access it like this:

 # Make the header known to the interpreter
 ROOT.gInterpreter.ProcessLine('#include "my_header.h"')

 # We find all the C++ entities in Python, right away!
 a = ROOT.A()   # prints Hello PyROOT!
 x = ROOT.f(3)  # x = 9

Loading C++ libraries, JITting headers

You can dynamically load C++ libraries with PyROOT, and call the functions from the library. The advantage of this method is that all code in the library needs to be compiled only once, possibly with optimizations (-O2, -O3). You can use it again from Python without any further JITting. However, it is necessary to JIT the headers to make the code usable in Python.

Example

With the following header my_header.h:

int f(int i);

class A {
public:
    A();
};

and the source file:

#include "my_header.h"

int f(int i) { return i*i; }

A::A() { cout << "Hello PyROOT!" << endl; }

you can create the library my_cpp_library.so. In Python, you can load and use it as follows:

 # First include header, then load C++ library
ROOT.gInterpreter.ProcessLine('#include "my_header.h"')`
ROOT.gSystem.Load('./my_cpp_library.so')

# We find all the C++ entities in Python, right away!
a = ROOT.A()   # prints Hello PyROOT!
x = ROOT.f(3)  # x = 9

Loading C++ libraries with dictionaries

For larger analysis frameworks, one may not want to compile the headers each time the Python interpreter is started. One may also want to read or write custom C++/C objects in ROOT files, and use them with RDataFrame or similar tools.

A large analysis framework might further have multiple libraries. In these cases, you generate ROOT dictionaries, and add these to the libraries, which provides ROOT with the necessary information on how to generate Python bindings on the fly. This is what the large LHC experiments do to steer their analysis frameworks from Python.

  1. Create one or multiple C++ libraries.
    For example create a library as a CMake project that uses ROOT, see → CMake details
  2. [Optional] Add ClassDef macros for classes that should be read or written from or into files.
  3. Generate a dictionary of all classes that should receive I/O capabilities, i.e. that can be written into ROOT files, see → Generating dictionaries
    Use a LinkDef.h file to select which classes or functions ROOT should be included in the dictionary.

    The corresponding cmake instructions would look similar to this:

    # Generate G__AnalysisLib.cxx with all the necessary class info:
    ROOT_GENERATE_DICTIONARY(G__AnalysisLib inc/classA.h inc/classB.h ...
                              LINKDEF LinkDef.h)
    
     add_library(AnalysisLib SHARED
        src/classA.cxx
        src/classB.cxx
        ...
        G__AnalysisLib.cxx) # Important: Here, all dictionary info is directly compiled into the library.
    
     # Ensure dictionary is generated before compiling the library:
     add_dependencies(AnalysisLib G__AnalysisLib)
    
  4. Implement the C++ side, and compile the library using CMake.
  5. On the Python side, load the libraries with high-performance C++ in one step:

    import ROOT
    ROOT.gSystem.Load('./libAnalysisLib.so')
    

    Note If either ROOT or the headers that were used to create the dictionaries were moved to a different location, and ROOT cannot find them (it returns an error in this case), the location of the headers has to be communicated to ROOT:

    import ROOT
    ROOT.gInterpreter.AddIncludePath('path/to/headers/')
    ROOT.gSystem.Load('./libAnalysisLib.so')
    

    The loading of C++ libraries can even be automated using the __init__.py of the Python package.

TPython: running Python from C++

With ROOT you can execute Python code from C++ via the TPython class.

The following example shows how you can use Exec to execute a Python statement. Eval is used to evaluate a Python expression and get its result back in C++ and Prompt to start an interactive Python session.

Example

root [0] TPython::Exec( "print(1 + 1)" )
2
root [1] auto b = (TBrowser*)TPython::Eval( "ROOT.TBrowser()" )
(TBrowser *) @0x7ffec81094f8
root [2] TPython::Prompt()
>>> i = 2
>>> print(i)
2

For a more complete description of the TPython interface, see → TPython .

JupyROOT: (Py)ROOT for Jupyter notebooks

Jupyter notebooks provide a web-based interface that you can use for interactive computing with ROOT.

How to use ROOT with Jupyter notebooks

Two kernels (or language flavours) allow to run ROOT from Jupyter: Python (2 and 3) and C++. In order to use such kernels, there are mainly two options:

  • Local ROOT installation: to install ROOT locally, see → Installing ROOT.
    In addition to ROOT, two Python packages are required that you can install, for example, with pip:
pip install jupyter
pip install metakernel

When all requirements fulfilled, you can start a Jupyter server with the Python and C++ kernels from a terminal:

root --notebook
  • SWAN: CERN provides an online service to create and run Jupyter notebooks, called SWAN. With this option no installation is required: a browser and a working Internet connection are enough. Both the Python and C++ kernels are available in SWAN.

Many ROOT tutorials are available as Jupyter notebooks. For example, most PyROOT tutorials are listed with two buttons below to obtain their notebook version and to open them in SWAN, respectively.

JavaScript graphics

The ROOT graphics are also available in Jupyter, both in Python and C++. Moreover, you can choose between two modes:

  • Static (default): The graphics are displayed as a static image in the output of the notebook cell.
  • Interactive: The graphics are shown as an interactive JavaScript display. In order to activate this mode, include %jsroot on in a cell. Once enabled, the mode stays on until it is disabled with %jsroot off (i.e. no need to enable it in every cell).

Example

%jsroot on
c = ROOT.TCanvas()
f1 = ROOT.TF1("func1", "sin(x)", 0, 10)
f1.Draw()
c.Draw() # Necessary to make the graphics show!

If you execute the above code in a cell, the output shows the following interactive canvas:

Note

The creation and drawing of a canvas are necessary when displaying ROOT graphics in a notebook. If no canvas is drawn in the cell, no graphics output is shown.

New PyROOT: Backwards-incompatible changes

The new PyROOT has the following backwards-incompatible changes with respect to its predecessor:

  • Instantiation of function templates must be done using square brackets instead of parentheses.

Example

Consider the following code snippet:

>>> import ROOT

>>> ROOT.gInterpreter.Declare("""
template<typename T> T foo(T arg) { return arg; }
""")

>>> ROOT.foo['int']  # instantiation
cppyy template proxy (internal)

>>> ROOT.foo('int')  # call (auto-instantiation from arg type)
'int'

Note that the above code does not affect class templates, which can be instantiated either with parenthesis or square brackets:

>>> ROOT.std.vector['int'] # instantiation
<class cppyy.gbl.std.vector<int> at 0x5528378>

>>> ROOT.std.vector('int') # instantiation
<class cppyy.gbl.std.vector<int> at 0x5528378>
  • Overload resolution in new cppyy has been significantly rewritten, which sometimes can lead to a different overload choice (but still a compatible one).

Example

For example, for the following overloads of std::string:

string (const char* s, size_t n)                           (1)
string (const string& str, size_t pos, size_t len = npos)  (2)

when invoking ROOT.std.string(s, len(s)), where s is a Python string. The new PyROOT picks (2) whereas the old picks (1).

  • The conversion between None and C++ pointer types is not allowed anymore. Use ROOT.nullptr instead:
>>> ROOT.gInterpreter.Declare("""
class A {};
void foo(A* a) {}
""")

>>> ROOT.foo(ROOT.nullptr)  # ok

>>> ROOT.foo(None)          # fails
TypeError: void ::foo(A* a) =>
TypeError: could not convert argument 1
  • Old PyROOT has ROOT.Long and ROOT.Double to pass integer and floating point numbers by reference. In the new PyROOT, you must use ctypes instead.
>>> ROOT.gInterpreter.Declare("""
void foo(int& i) { ++i; }
void foo(double& d) { ++d; }
""")

>>> import ctypes

>>> i = ctypes.c_int(1)
>>> d = ctypes.c_double(1.)

>>> ROOT.foo(i); i
c_int(2)

>>> ROOT.foo(d); d
c_double(2.0)
  • When a character array is converted to a Python string, the new PyROOT only considers the characters before the end-of-string character:
>>> ROOT.gInterpreter.Declare('char MyWord[] = "Hello";')

>>> mw = ROOT.MyWord

>>> type(mw)
<class 'str'>

>>> mw  # '\x00' is not part of the string
'Hello'
  • Any Python class derived from a base C++ class now requires the base class to define a virtual destructor:
>>> ROOT.gInterpreter.Declare("class CppBase {};")
 True
>>> class PyDerived(ROOT.CppBase): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: CppBase not an acceptable base: no virtual destructor
  • There are the following name changes for what concerns cppyy APIs and proxy object attributes:
Old PyROOT/cppyy New PyROOT/cppyy
cppyy.gbl.MakeNullPointer(klass) cppyy.bind_object(0, klass)
cppyy.gbl.BindObject / cppyy.bind_object cppyy.bind_object
cppyy.AsCObject libcppyy.as_cobject
cppyy.add_pythonization cppyy.py.add_pythonization
cppyy.compose_method cppyy.py.compose_method
cppyy.make_interface cppyy.py.pin_type
cppyy.gbl.nullptr cppyy.nullptr
cppyy.gbl.PyROOT.TPyException cppyy.gbl.CPyCppyy.TPyException
buffer.SetSize(N) buffer.reshape((N,))
obj.__cppname__ type(obj).__cpp_name__
obj._get_smart_ptr obj.__smartptr__
callable._creates callable.__creates__
callable._mempolicy callable.__mempolicy__
callable._threaded callable.__release_gil__
  • New PyROOT does not parse command line arguments by default anymore.
    For example, when running python my_script.py -b, the -b argument is not parsed by PyROOT, and therefore the batch mode is not activated. If you want to enable the PyROOT argument parsing again, start your Python script with:
import ROOT
ROOT.PyConfig.IgnoreCommandLineOptions = False
  • In new PyROOT, use addressof to retrieve the address of fields in a struct.
>>> ROOT.gInterpreter.Declare("""
struct MyStruct {
  int a;
  float b;
};
""")

>>> s = ROOT.MyStruct()

>>> ROOT.addressof(s, 'a')
94015521402096L

>>> ROOT.addressof(s, 'b')
94015521402100L

>>> ROOT.addressof(s)
94015521402096L

In old PyROOT, AddressOf could be used for that purpose too, but its behaviour was inconsistent. AddressOf(o) returned a buffer whose first position contained the address of object o, but Address(o, 'field') returned a buffer whose address was the address of the field, instead of such address being contained in the first position of the buffer.

Note that in the new PyROOT AddressOf(o) can still be invoked, and it still returns a buffer whose first position contains the address of object o.

  • In old PyROOT, there were two C++ classes called TPyMultiGenFunction and TPyMultiGradFunction which inherited from ROOT::Math::IMultiGenFunction and ROOT::Math::IMultiGradFunction, respectively. The purpose of these classes was to serve as a base class for Python classes that wanted to inherit from the ROOT::Math classes. This allowed to define Python functions that could be used for fitting in Fit::Fitter.
    In the new PyROOT, TPyMultiGenFunction and TPyMultiGradFunction do not exist anymore, since their functionality is automatically provided by new cppyy: if a Python class inherits from a C++ class, a wrapper C++ class is automatically generated. That wrapper class redirects any call from C++ to the methods implemented by the Python class.

Example

You can make your Python function classes inherit directly from the ROOT::Math C++ classes.

import ROOT

from array import array

class MyMultiGenFCN( ROOT.Math.IMultiGenFunction ):
    def NDim( self ):
        return 1

    def DoEval( self, x ):
        return (x[0] - 42) * (x[0] - 42)

    def Clone( self ):
        x = MyMultiGenFCN()
        ROOT.SetOwnership(x, False)
        return x

def main():
    fitter = ROOT.Fit.Fitter()
    myMultiGenFCN = MyMultiGenFCN()
    params = array('d', [1.])
    fitter.FitFCN(myMultiGenFCN, params)
    fitter.Result().Print(ROOT.std.cout, True)

if __name__ == '__main__':
    main()
  • When iterating over an std::vector<std::string> from Python, the elements returned by the iterator are no longer of type Python str, but cppyy.gbl.std.string. This is an optimization to make the iteration faster (copies are avoided) and it allows to call modifier methods on the std::string objects.
>>> import cppyy

>>> cppyy.cppdef('std::vector<std::string> foo() { return std::vector<std::string>{"foo","bar"};}')

>>> v = cppyy.gbl.foo()

>>> type(v)
<class cppyy.gbl.std.vector<string> at 0x4ad8220>

>>> for s in v:
...   print(type(s))  # s is no longer a Python string, but an std::string
...
<class cppyy.gbl.std.string at 0x4cd41b0>
<class cppyy.gbl.std.string at 0x4cd41b0>
  • When obtaining the boolean value of a C++ instance proxy, both old and new PyROOT return False when such proxy points to null. On the other hand, when the proxy points to a C++ object, old PyROOT just returns True, while new PyROOT has slightly modified this behaviour: in new cppyy, if __len__ is available, the result of __len__ is used to determine truth. This is done to comply with the default Python behaviour, where __len__ is tried if __bool__ is not present (see → object.bool).

Example

The following code shows how the insertion of __len__ changes the boolean value of an instance proxy.

>>> import ROOT
>>> ROOT.gInterpreter.Declare('class A {};')

>>> a = ROOT.A()
>>> bool(a)
True

>>> ROOT.A.__len__ = lambda self : 0
>>> bool(a)  # False because len(a) == 0
False
  • In new cppyy, buffer objects are represented by the LowLevelView type. If such a buffer points to null, it is not iterable, unlike in the old PyROOT.
    For example, in the following code, GetX() returns a null pointer and therefore an exception is thrown when calling list(x):
>> import ROOT

>> g = ROOT.TGraph()
>> x = g.GetX()

>> xl = list(x)  # throws ReferenceError

The code above can be protected by checking for the validity of x:

>>> xl = list(x) if x else 'your default'