root icon indicating copy to clipboard operation
root copied to clipboard

[PyROOT] error handling with cppyy and overloading

Open cburgard opened this issue 3 years ago • 4 comments

Explain what you would like to see improved

I have a c++ class with several constructors:

MyClass(const std::string& configfilename);
MyClass(const MyClass& other);

I have python bindings for this class that were generated with cppyy.

Now, I have a piece of python code that instantiates my class, with a nice try-except block:

try:
  obj = MyClass(args.config)
except ConfigFileNotFoundError:
  print("config file "+args.config+" was not found!")
  exit(0)

Now, to test, I'm executing this with a wrong config file. But what I get is roughly this:

TypeError: none of the 2 overloaded methods succeeded. Full details:
MyClass(const std::string&) => ConfigFileNotFoundError
MyClass::MyClass(const MyClass&) =>  TypeError

So I'm wondering: Since cppyy seems to handle function overloading with a try/except block, is there any reasonable way to do error handling for such applications?

I'd love to actually get the ConfigFileNotFoundError to handle it properly, rather than getting this TypeError.

I've started to look into using __overload__, but this seems less than elegant. Also, I was unable to figure out how to ask __overload__ for the accepted signatures.

cburgard avatar Feb 16 '22 15:02 cburgard

@etejedor would you please have a look at this?

cburgard avatar Feb 16 '22 15:02 cburgard

Hi @cburgard , indeed when you have multiple overloads, you call the method and none of the overloads work for some reason, then cppyy throws a TypeError that prints a summary of what happened with every overload. If you just had one overload and threw an exception from it, you would be able to catch it is as you want.

Now, to do what you want you can use __overload__ as you mention, so that only one particular overload is attempted. As described in the cppyy docs, you can check __doc__ in the method to see all the available overloads.

Another option, instead of throwing the ConfigFileNotFoundError from C++, as I assume you are doing now, is to raise the error from Python, in particular from a pythonization of the constructor of your C++ class. Something like this (@pythonization is available from 6.26):

def my_init(self, *args):
  # Look for configfilename in args, raise ConfigFileNotFoundError if it can't be found
  ...
   
  self._original_init(*args)
  
@pythonization("MyClass")
def myclass_pythonizor(klass):
    klass._original_init = klass.__init__
    klass.__init__ = my_init  

etejedor avatar Feb 16 '22 19:02 etejedor

I've tried with __overload__ now, but I get this:

unbound method ... mutt be called with a ... instance as first argument

Does this not work for constructors?

cburgard avatar Feb 17 '22 10:02 cburgard

It does, but it needs an extra step to create an empty proxy object that will be initialized. For example, you can do:

MyClass = ROOT.MyClass
mc = MyClass.__new__(MyClass)
MyClass.__init__.__overload__('int')(mc, 1) # this initializes mc

Alternatively, instead of mc = MyClass.__new__(MyClass), you can also do mc = ROOT.MakeNullPointer(MyClass).

etejedor avatar Feb 17 '22 11:02 etejedor