ROOT's python bindings (SetBranchAddress not working)

Hello,

Recently I have installed ROOT 5.34.36 using python 3.5. I used this python version to make ROOT compatible with another package environment which requires python 3.5. I provide the piece of code which is now giving me a problem. When I compiled ROOT 5.34.36 with python 2.7, the same piece of code worked perfectly. Could you please see how this problem can be solved?

import ROOT
from ROOT import *
-------
gSystem.Load("$PATHFOLDER/mylibrary.so")
f = ROOT.TFile("data.root")
# Getting 'Event' Tree
t=f.Get("Events")
# Setting branch address for Event class 
t.SetBranchStatus("*", 0);
t.SetBranchStatus("MEvt.", 1)
# calling the class (MEvt is a class from mylibrary.so)
cam_event = MEvt()

t.SetBranchAddress("MEvt.",cam_event)

It was working perfectly with python2.7. Now with this python3.5 version, it is giving me following error.

Traceback (most recent call last):
  File "read_root.py", line 28, in <module>
    t.SetBranchAddress("MEvt.",cam_event)
TypeError: none of the 3 overloaded methods succeeded. Full details:
  Int_t TTree::SetBranchAddress(const char* bname, void** add, TBranch** ptr = 0) =>
    could not convert argument 1 (SetBranchAddress() argument 1 must be bytes, not str)
  Int_t TTree::SetBranchAddress(const char* bname, void* add, TClass* realClass, EDataType datatype, Bool_t isptr) =>
    takes at least 5 arguments (2 given)
  Int_t TTree::SetBranchAddress(const char* bname, void* add, TBranch** ptr, TClass* realClass, EDataType datatype, Bool_t isptr) =>
    takes at least 6 arguments (2 given)

Side note:

To show code you should simply enclose it with ```

Like:

```
The_code_you_want_to_show
```

it will produce:

The_code_you_want_to_show

the way you posted it made your post unreadable (I have fixed it)

In Python 3.x, the construct from ROOT import * is not supported. Please try to import only the things you need when using Python 3.x. If that still doesnā€™t work, let us know. @wlav may know better what is going on.

I now imported only gSystem from ROOT to load my library. It is still showing me the same problem about SetBranchAddress.

import ROOT
from ROOT import gSystem
-------
gSystem.Load("$PATHFOLDER/mylibrary.so")
f = ROOT.TFile("data.root")
# Getting 'Event' Tree
t=f.Get("Events")
# Setting branch address for Event class 
t.SetBranchStatus("*", 0);
t.SetBranchStatus("MEvt.", 1)
# calling the class (MEvt is a class from mylibrary.so)
cam_event = MEvt()

t.SetBranchAddress("MEvt.",cam_event)

@couet The piece of code in python script

import ROOT
from ROOT import gSystem
-------
gSystem.Load("$PATHFOLDER/mylibrary.so")
f = ROOT.TFile("data.root")
# Getting 'Event' Tree
t=f.Get("Events")
# Setting branch address for Event class 
t.SetBranchStatus("*", 0);
t.SetBranchStatus("MEvt.", 1)
# calling the class (MEvt is a class from mylibrary.so)
cam_event = MEvt()

t.SetBranchAddress("MEvt.",cam_event)

Plenty of completely unrelated things here.

First, @amadio this has nothing to do with ā€œfrom ROOT import *ā€. As explained in detail in an e-mail to the ROOT team, the only thing broken in 3.6 (cygnus is using 3.5 here) is an interface change. Fixing that is so trivial that it will take less than 1/100th of the time already spend discussing the issue. Needless to say that after having set out all the details in that e-mail (and in part in the JIRA ticket), Iā€™m no longer going to respond to anything related to ā€œfrom ROOT import *ā€, as that is just a total waste of my time ā€¦

Second, @cygnus: the error is ā€œmust be bytes, not strā€. This means that the p3 string, which is unicode, is not properly converted by TTreeSetBranchAddress in Pythonize.cxx. Again, that is a trivial fix, so you can file a JIRA ticket to whoever is in charge of PyROOT. For your own code, however, simply adding a ā€œbā€ should be a solution (havenā€™t tried):

t.SetBranchAddress(b"MEvt.",cam_event)

What this does, is turning the unicode string into a bytes object, which consist of 1-byte chars, just ast TTree::SetBranchAddress likes it

Third, @cygnus if you do ā€œimport ROOTā€, Iā€™d recommend not mixing it with ā€œfrom ROOT import ā€¦ā€. The problem is two-fold, first, things that appear late (b/c of the loading of mylibrary.so, for example), always need to be reimported or looked up on module ROOT, and cam_event = ROOT.MEvt() looks cleaner to my eyes than from ROOT import MEvt; cam_event = MEvt(). Of course, tastes vary, but the latter is moderately faster, too. Second, there is actually a ROOT C++ namespace. I.e. ROOT.ROOT. So being inconsistent can lead to confusing results as something that is available on module ROOT may not be on namespace ROOT (the opposite is never a problem: full namespace ROOT is available in module ROOT at the top level).

1 Like

@wlav Thanks a lot for your reply. I have tried with

t.SetBranchAddress(b"MEvt.",cam_event)

but it didnā€™t work. I got following error this time

Error in <TTree::SetBranchAddress>: unknown branch -> (null)
TypeError: bad argument type for built-in operation

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "read_root.py", line 31, in <module>
    t.SetBranchAddress(b"MEvt.",cam_event)
SystemError: <ROOT.MethodProxy object at 0x7f006ca9c4e0> returned a result with an error set

I would like to metion here that ā€˜MEvtā€™ is a class object from ā€˜mylibrary.soā€™. In an equivalent c-code I define this as follows

MEvt *cam_event = new MEvt();

However, in my python script I am just using

cam_event = MEvt()

This same piece of code worked fine when I compiled ROOT 5.34.36 with python 2.7. Hence I assume, I have defined it correctly. I am not sure if I need to call ā€˜MEvtā€™ in a different way.

I know this had nothing to do with the ā€œfrom ROOT import *ā€ issue. But it was decided on that JIRA issue, for the reasons you explained in your email about the limitations involved, that ROOT will no longer support that, not only in Python 3.6, but any version 3.x. I was informing the user and asking him to change his script. Since Iā€™m not very familiar with PyROOT, I pinged you in case youā€™d know the answer to his actual problem. However, from your reaction, I see that itā€™s better to ask to someone else next time.

Hum ā€¦ In Pythonize.cxx, TTreeSetBranchAddres does this:

            if ( PyArg_ParseTuple( args, const_cast< char* >( "SO:SetBranchAddress" ),
                    &name, &address ) ) {

So that ā€˜Sā€™ is where it wants a bytes object, as per getargs.c in python:

    case 'S': { /* PyBytes object */
        PyObject **p = va_arg(*p_va, PyObject **);
        if (PyBytes_Check(arg))
            *p = arg;
        else
            return converterr("bytes", arg, msgbuf, bufsize);
        break;
    }

which was your first error message. Now that you provided that bytes argument, however, the ParseTuple code succeeds, but the next step is this:

                  tree->SetBranchAddress( PyROOT_PyUnicode_AsString( name ), buf );

As you can see, that now wants a unicode object, which fails, so in your second case, TTree gets a ā€œnullptrā€ name (hence its complaint), and there is no error checking/handling, hence the rest of the complaints from python. (Ordinarily, no error checking would be needed as thatā€™s what the ā€œSā€ in the format was for.)

The solution is probably along the lines to redeclare the name variable as const char* and have ParseTuple take a s#. But thatā€™s got to happen in Pythonize.cxx. Anyway, Iā€™d argue itā€™s worth a JIRA ticket, as itā€™s a clear bug.

Only workaround that may still get you somewhere is (havenā€™t tried):

t.SetBranchAddress("MEvt.",ROOT.addressof(cam_event), ROOT.nullptr)

The idea is to fail the ParseTuple (by providing 3 arguments) and then call the normal (non-pythonized) SetBranchAddress with a proper address.

Alternatively, you can select the specific overload with disp(), although Iā€™m not sure whether that will do the right thing (I forget how SetBranchAddress was inserted). Or if all that fails, if the final goal is to loop over the tree, than going through getattr(t, "MEvt.") might get you there (I just worry about that trailing dot syntax).

@amadio: Indeed. Iā€™m interested in genuine p3 errors as the cppyy main development supports it b/c of active p3 users and some of the p3 errors latent in PyROOT may still be present in cppyy, so I want to know about (and fix) them. But general ROOT and PyROOT specific questions really have to be answered by the ROOT team.

@wlav @amadio Thanks to you all for your support on this issue.

@wlav I already submitted a JIRA ticket for this issue.

I tried with the options you suggested.

t.SetBranchAddress("MEvt.",ROOT.addressof(cam_event), ROOT.nullptr)

didnā€™t work, Please find below the error message.

Traceback (most recent call last):
  File "read_root.py", line 48, in <module>
    t.SetBranchAddress("MEvt.",AddressOf(cam_event),ROOT.nullptr)
  File "/users/pkgs/root/lib/ROOT.py", line 486, in __getattr2
    attr = _root.LookupRootEntity( name )
AttributeError: nullptr

However, I was succesful with your other suggestion: getattr(t, "MEvt.") I am giving the code below in case other users find it useful

import ROOT
from ROOT import gSystem
-------
gSystem.Load("$PATHFOLDER/mylibrary.so")
f = ROOT.TFile("data.root")
# Getting 'Event' Tree
t=f.Get("Events")
# Setting branch address for Event class 
t.SetBranchStatus("*", 0);
t.SetBranchStatus("MEvt.", 1)
# calling the class (MEvt is a class from mylibrary.so)

for event in t:
    cam_event=getattr(event, "MEvt.")
    val = cam_event.MyFunction()

It worked well with the trailing dot still there.
Thanks a lot for your help.

Only now did it hit me what version of ROOT you are using ā€¦ 5.34. The particular problem here is still an issue w/ current ROOT6 (the code I quote above is from master), and hence the JIRA ticket is justified, but overall the support of p3 in 5.34 was left at ā€œmake it compile.ā€ With 6 at least it came to ā€œmake all roottest tests and python tutorials succeedā€. (And baseline cppyy has had some more p3 fixes since.)

All that said, if I check 5.34, there is a nullptr but also some comment of trouble next to it, and an alias of NULL to 0 (integer zero). ROOT.NULL may thus work instead.

@wlav Thanks.
I tried with

t.SetBranchAddress("MEvt.",ROOT.addressof(cam_event), ROOT.null)

now I am getting similar errors as I got first time

Traceback (most recent call last):
  File "test_root.py", line 48, in <module>
    t.SetBranchAddress("MEvt.",AddressOf(cam_event),ROOT.NULL)
TypeError: none of the 3 overloaded methods succeeded. Full details:
  Int_t TTree::SetBranchAddress(const char* bname, void** add, TBranch** ptr = 0) =>
    could not convert argument 3
  Int_t TTree::SetBranchAddress(const char* bname, void* add, TClass* realClass, EDataType datatype, Bool_t isptr) =>
    takes at least 5 arguments (3 given)
  Int_t TTree::SetBranchAddress(const char* bname, void* add, TBranch** ptr, TClass* realClass, EDataType datatype, Bool_t isptr) =>
    takes at least 6 arguments (3 given)

Well, then Iā€™ve run clean out of ideas ā€¦ (I tried disp(), but that doesnā€™t work b/c the pythonized method hides the other ones).

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.