""" @package pmsco.helpers helper classes a collection of small and generic code bits mostly collected from the www. """ import contextlib import ctypes import io import os import sys from typing import BinaryIO class BraceMessage(object): """ a string formatting proxy class useful for logging and exceptions. use BraceMessage("{0} {1}", "formatted", "message") in place of "{0} {1}".format("formatted", "message"). the advantage is that the format method is called only if the string is actually used. """ def __init__(self, fmt, *args, **kwargs): self.fmt = fmt self.args = args self.kwargs = kwargs def __str__(self): return self.fmt.format(*self.args, **self.kwargs) libc = ctypes.CDLL(None) c_stdout = ctypes.c_void_p.in_dll(libc, 'stdout') @contextlib.contextmanager def stdout_redirected(dest_file: BinaryIO): """ A context manager to temporarily redirect stdout to a file. Redirects all standard output from Python and the C library to the specified file. This can be used, e.g., to capture output from Fortran code. credit: https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/ @param dest_file: binary file open for writing ('wb' mode). This function requires just the fileno function. @return: None """ original_stdout_fd = sys.stdout.fileno() def _redirect_stdout(to_fd): """Redirect stdout to the given file descriptor.""" libc.fflush(c_stdout) sys.stdout.close() os.dup2(to_fd, original_stdout_fd) sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd, 'wb')) saved_stdout_fd = os.dup(original_stdout_fd) try: _redirect_stdout(dest_file.fileno()) yield _redirect_stdout(saved_stdout_fd) finally: os.close(saved_stdout_fd)