Search code examples
pythonc++error-handlingoverloadingroot-framework

confusion with cppyy for overloaded methods and error handling


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 - I don't do this myself, this is all part of the framework I'm using (CERN ROOT, in case you're wondering).

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. Also, what determines the actual error class I get in the end - does it depend on the order in which the overloads appear in the header file?

Any help, suggestions or pointers on where to find more information on this would be highly appreciated.


Solution

  • cppyy doesn't use try/except for overload resolution, hence there are also no __context__ and __cause__ set. To be more precise: the C++ exception is not an error that occurs during a handler. Rather, as-yet unresolved overloads are prioritized, then tried in order, with no distinction made between a Python failure (e.g. from an argument conversion) or a C++ failure (any exception that was automatically converted into a Python exception). This is a historic artifact predating run-time template instantiation and SFINAE: it allowed for more detailed run-time type matching in pre-instantiated templates.

    If all overloads fail (Python or C++), the collected errors are summarized. Python still requires an exception type, however, and if the exception types across the collected types differ, a generic TypeError is raised, with a message string made up of all the collected exceptions. This is what happens here: there is ConfigFileNotFoundError raised by C++ in one overload and TypeError from argument conversion failure in the other.

    There's an improvement now in the cppyy repo; to be released with 2.3.0, where in clear cases such as this one (a single overload succeeding in argument match but failing in the callee), you'll get the actual ConfigFileNotFoundError instance as long as its class is publicly derived from std::exception (but I think it already does, otherwise the error report you posted would have looked quite different).

    (Note that CERN's ROOT contains an old fork of cppyy that has quite a bit diverged; you'll have to request them for a separate update there if that fork matters to you.)