| 1 | # This is the answer to a question asked on stackoverflow:
 | 
|---|
| 2 | # https://stackoverflow.com/questions/35745541/how-to-get-printed-output-from-ctypes-c-functions-into-jupyter-ipython-notebook
 | 
|---|
| 3 | #
 | 
|---|
| 4 | # This is the code by Denilson Sá Maia
 | 
|---|
| 5 | 
 | 
|---|
| 6 | import io
 | 
|---|
| 7 | import os
 | 
|---|
| 8 | import sys
 | 
|---|
| 9 | import tempfile
 | 
|---|
| 10 | from contextlib import contextmanager
 | 
|---|
| 11 | 
 | 
|---|
| 12 | from pyMoleCuilder_capture import fflush_stderr, fflush_stdout, \
 | 
|---|
| 13 |     disable_stderr_buffering, disable_stdout_buffering
 | 
|---|
| 14 | 
 | 
|---|
| 15 | @contextmanager
 | 
|---|
| 16 | def capture_std_out_err(encoding='utf8'):
 | 
|---|
| 17 |     # Flushing, it's a good practice.
 | 
|---|
| 18 |     sys.stdout.flush()
 | 
|---|
| 19 |     sys.stderr.flush()
 | 
|---|
| 20 |     fflush_stdout()
 | 
|---|
| 21 |     fflush_stderr()
 | 
|---|
| 22 | 
 | 
|---|
| 23 |     # We need to use a actual file because we need the file descriptor number.
 | 
|---|
| 24 |     with tempfile.TemporaryFile(buffering=0) as temp:
 | 
|---|
| 25 |         # Saving a copy of the original stdout.
 | 
|---|
| 26 |         prev_sys_stdout = sys.stdout
 | 
|---|
| 27 |         prev_stdout_fd = os.dup(1)
 | 
|---|
| 28 |         os.close(1)
 | 
|---|
| 29 | 
 | 
|---|
| 30 |         prev_sys_stderr = sys.stderr
 | 
|---|
| 31 |         prev_stderr_fd = os.dup(2)
 | 
|---|
| 32 |         os.close(2)
 | 
|---|
| 33 | 
 | 
|---|
| 34 |         # Duplicating the temporary file fd into the stdout fd.
 | 
|---|
| 35 |         # In other words, replacing the stdout.
 | 
|---|
| 36 |         os.dup2(temp.fileno(), 1)
 | 
|---|
| 37 |         os.dup2(temp.fileno(), 2)
 | 
|---|
| 38 |         log_to_fileno(temp.fileno())
 | 
|---|
| 39 | 
 | 
|---|
| 40 |         # Replacing sys.stdout for Python code.
 | 
|---|
| 41 |         #
 | 
|---|
| 42 |         # IPython Notebook version of sys.stdout is actually an
 | 
|---|
| 43 |         # in-memory OutStream, so it does not have a file descriptor.
 | 
|---|
| 44 |         # We need to replace sys.stdout so that interleaved Python
 | 
|---|
| 45 |         # and C output gets captured in the correct order.
 | 
|---|
| 46 |         #
 | 
|---|
| 47 |         # We enable line_buffering to force a flush after each line.
 | 
|---|
| 48 |         # And write_through to force all data to be passed through the
 | 
|---|
| 49 |         # wrapper directly into the binary temporary file.
 | 
|---|
| 50 |         temp_wrapper = io.TextIOWrapper(
 | 
|---|
| 51 |             temp, encoding=encoding, line_buffering=True, write_through=True)
 | 
|---|
| 52 |         sys.stdout = temp_wrapper
 | 
|---|
| 53 |         sys.stderr = temp_wrapper
 | 
|---|
| 54 | 
 | 
|---|
| 55 |         # Disabling buffering of C stdout.
 | 
|---|
| 56 |         disable_stdout_buffering()
 | 
|---|
| 57 |         disable_stderr_buffering()
 | 
|---|
| 58 | 
 | 
|---|
| 59 |         yield
 | 
|---|
| 60 | 
 | 
|---|
| 61 |         # Must flush to clear the C library buffer.
 | 
|---|
| 62 |         fflush_stdout()
 | 
|---|
| 63 |         fflush_stderr()
 | 
|---|
| 64 | 
 | 
|---|
| 65 |         # Restoring stdout.
 | 
|---|
| 66 |         os.dup2(prev_stdout_fd, 1)
 | 
|---|
| 67 |         os.close(prev_stdout_fd)
 | 
|---|
| 68 |         sys.stdout = prev_sys_stdout
 | 
|---|
| 69 | 
 | 
|---|
| 70 |         os.dup2(prev_stderr_fd, 2)
 | 
|---|
| 71 |         os.close(prev_stderr_fd)
 | 
|---|
| 72 |         sys.stderr = prev_sys_stderr
 | 
|---|
| 73 |         temp.fileno(1)
 | 
|---|
| 74 | 
 | 
|---|
| 75 |         # Printing the captured output.
 | 
|---|
| 76 |         temp_wrapper.seek(0)
 | 
|---|
| 77 |         print(temp_wrapper.read(), end='')
 | 
|---|