Issues with class definitions lookup at instantiation outside main frame

Hi All,

It’s possible this has been asked before, I am not really sure what to search for to look for it. The title of this post is also pretty vague.

Currently running ROOT 6.08.02, Python 3.4.

Ok, so here is the issue.

I have a ROOT class A that has methods that depend on a ROOT enum:

namespace MyNamespace {
  enum MyType {
    kThis,
    kThat
  };

  struct Base {
    Base(MyType thisOrThat);
  };
};

LinkDef:
#pragma link C++ enum MyNamespace::MyType;
#pragma link C++ class MyNamespace::Base+;

Now, I have a python class that inherits from this one, and winds up trying to instantiate it with super().init. But, I can reproduce the problem simply by trying to return an instance of the class from a function.

import ROOT
def ThisClass():
    return ROOT.MyNamespace.Base(ROOT.MyNamespace.kThis)

Now, the base class runs fine in ROOT. But in python I have the following behavior:

Instantiating ThisClass first does not work

>> import ROOT
>> from myModule import ThisClass
>> myObj = ThisClass()
TypeError: none of the 3 overloaded methods succeeded. Full details:
... 
(Copy ctors dont work)
...
 MyNamespace::Base::MyNamespace::Base(MyNamespace::MyType thisOrThat) =>
    could not convert argument 1 (void/unknown arguments can't be set)
>> ROOT.MyNamespace.Base(ROOT.MyNamespace.kThis)
TypeError: none of the 3 overloaded methods succeeded. Full details:
...
(Copy ctors dont work)
...
 MyNamespace::Base::MyNamespace::Base(MyNamespace::MyType thisOrThat) =>
    could not convert argument 1 (void/unknown arguments can't be set)

But, if I do it the other way around, it does work:

>> import ROOT
>> from myModule import ThisClass
>> ROOT.MyNamespace.Base(ROOT.MyNamespace.kThis)
>> myObj = ThisClass()

In fact, simply executing

>> exec('ROOT.MyNamespace.Base')

before calling ThisClass fixes the problem. Calling

>> exec('ROOT.MyNamespace.kThis')

does NOT fix the problem. This seems to be a problem with the class, not the enums.

This was NOT an issue in ROOT5. So, it seems to me that this has to do with how python is looking up the class dictionary and deciding how to pass arguments to underlying ROOT class. Unless it is being instantiated in the global frame python is not properly figuring out the definitions?


On a different topic. Is there a way I can load the enums into the module frame without knowing their names? I am currently using

for k in vars(ROOT.MyNamespace):
    if k.startswith('k'):
       globals()[k]=ROOT.MyNamespace.__getattribute__(ROOT.MyNamespace,k)

But this seems pretty hackish. ROOT.MyNamespace.dict[‘kThis’] seems to point to a ROOT.PropertyProxy. Hence I have to use the getattribute method. Which is not a static class? Hence I need to pass the namespace in place of self? I don’t know, it seems like a hack to me. Should I open this one up into a different thread?

Anyway, hopefully I described the issue clearly. Thanks in advance.

Cheers,
–Jon

Hi Jon. I have tries to reproduce your problem without success. I can run the following script whiteout any problem.

import ROOT
ROOT.gInterpreter.ProcessLine("""
namespace MyNamespace {
  enum MyType {
    kThis,
    kThat
  };

  struct Base {
    Base(MyType thisOrThat){}
  };
}
""")

def ThisClass():
    return ROOT.MyNamespace.Base(ROOT.MyNamespace.kThis)

myObj = ThisClass()
print myObj

Notice that my Base::Base(MyType) constructor has an implementation (dummy) and not in your case.
Cheers, Pere

Opps. I tested also with python3, which was another difference (after changing the print statement :wink: ) successfully.
Pere

Hi Pere,

Thanks for looking into this.

I am not sure that the problem would exist if the class is declared with ProcessLine. I do not see this issue with ROOT5. I suspect that the issue has to do with where and how the class definition is loaded. Or perhaps it is loaded and new symbols found within are not recursively loaded? (I really don’t understand what ROOT6/pyROOT is doing well enough to describe exactly where the problem is.)

But my understanding of what is happening with ROOT5 is this:
[ul][li] The from myModule import ThisClass command tells python to load ROOT, and the first running of ROOT.MyNamespace.Base causes ROOT to look into the rootmap files, find MyNamespace::Base and autoload the library that contains that class.[/li]
[li] In ROOT5, this loads the class dictionary generated by rootcint and placed in the library, which causes ROOT to understand MyNamespace::MyType. [/li]
[li] pyROOT then creates an instance and has now figured out (I am not sure how because I dont know pyROOT that well), that it needs to cast kThis (which python reads as an int) as a MyType and it presumably has figured out how to do that.[/li][/ul]

However, in ROOT6:
The dictionary is generated on the fly? (I am not sure.) Which means that either ROOT6 does not properly figure out what MyNamespace::MyType is (I doubt this, because it seems to work in ROOT interpreter). Or python does not figure out that it also needs to look up MyNamespace::MyType so it falls back on void. And so it’s definition of the ctor is Base::Base(void thisOrThat). Which will never work. This is why the second call directly to ROOT.MyNamespace.Base() fails, because python has already written down its (flawed) pyROOT definition for Base.

Whereas, in the second approach (where ROOT.MyNamespace.Base is called first) pyROOT IS able to correctly decipher the class dictionary. Thus when the class is called in MyClass(), it is already correctly implemented. I don’t know why the two approaches of loading ROOT.MyNamespace.Base (in global frame vs function frame) are different.

I think by defining the class through ROOT.gInterpreter.ProcessLine(), the definition is already there, and it does not need to look it up?

Maybe now that I have typed that all out, a shorter response would have been to say, I think ROOT6’s rootcling is part of the problem? Also, just to mention, Base::Base(MyType) is implemented in a .cxx file. But the contents are irrelevant, since pyROOT is not figuring out which method to run.

Cheers,
–Jon

Thanks for the explanation. What we need in order to fix the problem is to be able to reproduce it. I understood that the class an enum is defined in a .cxx file and you produce the dictionary of it (with the Linked.h file). How do you do it? Are you also generating the roortmap file?
Cheers, Pere