[PyROOT] error handling with cppyy and overloading
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.
@etejedor would you please have a look at this?
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
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?
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).