Can't redirect error stream from Cython
Asked Answered
U

1

1

The SFML library that I'm trying to cythonize defines this function below that allows to change where errors are printed to (by default SFML writes error messages to the console when this function is not called):

namespace sf {
    std::ostream& err() {
        static DefaultErrStreamBuf buffer;
        static std::ostream stream(&buffer);
        return stream;
    }
}

My simplified .pxd file for the above function:

cdef extern from 'SFML/System.hpp' namespace 'sf':
    ostream& cerr 'sf::err' ()

And my .pyx module, which compiles and runs fine, but doesn't redirect the error messages (they are still printed to the console).

cdef void set_error_handler():
    cerr().rdbuf(NULL)  # This call should prevent errors appearing in the console but it silently fails

set_error_handler()

I'm using MSVC and linking statically with the C++ code.

Edit

Below is example how the SFML library logs errors in its own code (full source):

...
// Error, failed to load the image
err() << "Failed to load image \"" << filename << "\". Reason: " << stbi_failure_reason() << std::endl;
...

My goal is to suppress the error messages like the one above from appearing in the console and eventually later redirect them into own buffer.

Unbending answered 27/7, 2017 at 22:24 Comment(4)
Could you provide a minimal pyx- file, where you actually use the logging?Invasive
@Invasive Sure, I've updated the question. I don't log anything to the sf:err(), the SFML library does this and I want to suppress that or redirect.Unbending
What happens, if you write set_error_handler() in c++ and call it from cython. Does it work then? Just trying to rule out, that this is a not a sfml-issue.Invasive
@Invasive I can confirm that pure C++ code (without Cython) successfully suppresses or redirects the error buffer. But I would like to rather avoid writing C++ and just hoping that pure Cython would work.Unbending
I
1

There are two ingredients for your problem and both are in your setup-file.

The first ingredient is that you have two extensions:

ext_modules = [
    Extension('nebula.sfml.system', ['nebula/sfml/system.pyx'],
              language='c++', ...),
    Extension('nebula.sfml.graphics', ['nebula/sfml/graphics.pyx'],
              language='c++', ...),
] 

that means cython will create two different shared libraries: system.dll and graphics.dll which will be both loaded later on dynamically by python.

The second ingredient: the sfml-library is linked statically but contains a singleton (the error-stream in question) and this is a recipe for disaster: With your set-up it is no longer a singleton, but there are two different error-streams: The one from system.dll and the one from graphics.dll. So you are silencing the error-stream from the system.dll (because your call set_error_handler() lives there), but write to the error-stream from the graphics.dll (this where image_load_test lives).

So what can be done? There are two options:

  1. Use shared sfml-libraries (at least sfml-system-s), thus the singleton will stay a singleton.
  2. Put the content of both pyx-files in the same pyx-file/Extension/shared library. At least right now, the content of system.pyx is only needed for graphics.pyx.
Invasive answered 28/7, 2017 at 11:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.