Cantera / enhancements

Repository for proposed and ongoing enhancements to Cantera
11 stars 5 forks source link

Channel C++ warnings to Python's warning system #136

Closed ischoegl closed 2 years ago

ischoegl commented 2 years ago

While exceptions raised in the C++ core are already captured and thrown by the Python API, the same is not true for the warning system. I had originally added this as a discussion topic, but it may be more appropriate to discuss this as an issue (feature request).

Discussed in https://github.com/Cantera/enhancements/discussions/112

Originally posted by **ischoegl** August 18, 2021 At the moment, the C++ code base issues warning messages via `warn_deprecated` and `warn_user` (the latter introduced in Cantera/cantera#741). In this context, @bryanwweber commented on a new deprecation warning proposed in Cantera/cantera#1084: > One other comment, and I suppose this applies to all the `warn_deprecated()` calls in C++, I wonder if it's possible to catch those in Python and turn them into real Python `DeprecationWarnings` so they can be controlled by Python's warning interfaces? (see [here](https://github.com/Cantera/cantera/pull/1084#issuecomment-901377885); the same presumably applies to `warn_user`). @speth's response was: > I think this would require something a bit like how we interface with Python for writing messages to `stdout`, that is, registering a handler in the `Application` class and in that handler making whatever the correct calls are to the Python C API. **Additional context:** * Cantera/cantera#796 * Comment about [Boost logger](https://github.com/Cantera/cantera/pull/741#pullrequestreview-314480685) in Cantera/cantera#741 **Other thoughts:** As there may be a relatively straight-forward route to this after all, it may make sense to convert this discussion to an enhancement request (i.e. issue).
ischoegl commented 2 years ago

@speth ... while I know how C++ outputs logging messages (fairly apparent in src/base/application.h) and have a hunch for how information flows (re-routed to wrappers.h and - I believe - captured with _logger in utils.pyx?), I'm not sure that I am following how those are actually caught and displayed in Python? It would be great if you could point me in the right direction.

speth commented 2 years ago

Right, the lines

cdef CxxPythonLogger* _logger = new CxxPythonLogger()
CxxSetLogger(_logger)

in utils.pyx create a new instance of PythonLogger (defined in wrappers.h) and assign that as the logwriter member of the global Application object. That PythonLogger object is then used for all calls to writelog().

I think for this purpose, what you would want is an additional method for the Logger class that's used to handle calls to warn_user instead of having that just call writelog. The implementation of this method in PythonLogger would then need to call something like PyErr_WarnEx (see https://docs.python.org/3/c-api/exceptions.html#issuing-warnings). For the base Logger class, I guess you would probably want to direct the warning to std::cerr.

ischoegl commented 2 years ago

:tada: Ha, that comment made all the difference! Thanks!! I guess the magic sauce in the current implementation is in PySys_WriteStdout and PyRun_SimpleString. This was hiding in plain sight! Exact same thing (i.e. PyRun_SimpleString) can be used for triggering warnings.