Skip to content

Instantly share code, notes, and snippets.

@mfdeveloper
Created March 4, 2021 12:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mfdeveloper/443ebb077578f988d400d552e6c6e61d to your computer and use it in GitHub Desktop.
Save mfdeveloper/443ebb077578f988d400d552e6c6e61d to your computer and use it in GitHub Desktop.
Capture the output console stdout/stderr from a c++ shared library to a variable
class CaptureOutput(ContextDecorator):
"""
Get the console output from a c/c++ shared library, and
save it into a variable
References:
- Based in stackoverflow answer: `Capture the stdout from a c++ shared library`_.
Official References
- Context Manager decorator official reference: ContextDecorator_.
- With statement official reference: WithStatement_.
- Context Manager official reference: ContextManager_.
.. _Capture the stdout from a c++ shared library: https://stackoverflow.com/a/24277852
.. _ContextDecorator: https://docs.python.org/3/library/contextlib.html#contextlib.ContextDecorator
.. _WithStatement: https://docs.python.org/3/reference/compound_stmts.html#with
.. _ContextManager: https://docs.python.org/3/library/stdtypes.html#typecontextmanager
"""
_thread: Thread
def __init__(self, stream=sys.stdout):
"""
:param stream: sys.stdout or sys.stderr from sys package/module
:Example:
# Wrap the c/c++ shared library call into a Context manager
with CaptureOutput() as cm:
mylib.print()
captured = cm.captured_output
# Warning: Don't use any output Python function here (e.g print(captured))
"""
self.stream_fileno = stream.fileno()
self.stream_save = 0
self.stdout_pipe = (0, 0)
self.captured_output = ''
def __enter__(self):
# Create pipe and dup2() the write end of it on top of stdout, saving a copy
# of the old stdout
self.stream_save = os.dup(self.stream_fileno)
self.stdout_pipe = os.pipe()
os.dup2(self.stdout_pipe[1], self.stream_fileno)
os.close(self.stdout_pipe[1])
self._thread = Thread(target=self._drain_pipe)
self._thread.start()
return self
def __exit__(self, type, value, traceback):
# Close the write end of the pipe to unblock the reader thread and trigger it
# to exit
os.close(self.stream_fileno)
self._thread.join()
# Clean up the pipe and restore the original stdout
os.close(self.stdout_pipe[0])
os.dup2(self.stream_save, self.stream_fileno)
os.close(self.stream_save)
return False
def _drain_pipe(self):
while True:
data = os.read(self.stdout_pipe[0], 1024)
if not data:
break
self.captured_output += str(data)
# Usage:
captured = ''
with CaptureOutput() as cm:
# Warning: Don't use any output Python function inside of Context manager (e.g print(captured))
pymeshlab.print_filter_parameter_list('surface_reconstruction_screened_poisson')
captured = cm.captured_output
# Here, you can do anything with 'captured' variable
print(captured)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment