summaryrefslogtreecommitdiffstats
path: root/scripts/redirect.py
blob: 498fe150ab46baf42428fae2ee02ac75f35689af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
"""

A simple context manager for redirecting streams in Python. 
The streams are redirected at the the C runtime level so that the output of C extensions
that use stdio will also be redirected.

null_file : a stream representing the null device (e.g. /dev/null on Unix)
redirect: a context manager for redirecting streams

Author: Baruch Sterin (sterin@berkeley.edu)

"""

import os
import sys

from contextlib import contextmanager

null_file = open( os.devnull, "w" )

@contextmanager
def _dup( f ):
    fd = os.dup( f.fileno() )
    yield fd
    os.close(fd)
    
@contextmanager
def redirect(dst = null_file, src = sys.stdout):
    
    """
    Redirect the src stream into dst.
    
    Example:
        with redirect( open("somefile.txt", sys.stdout ) ):
            do some stuff ...        
    """

    if src.fileno() == dst.fileno():
        yield 
        return
        
    with _dup( src ) as fd_dup_src:

        dst.flush()
        
        src.flush()
        os.close( src.fileno() )
        os.dup2( dst.fileno(), src.fileno() )

        yield

        src.flush()
        os.close( src.fileno() )
        os.dup2( fd_dup_src, src.fileno() ) 

def start_redirect(dst = null_file, src = sys.stdout):
    
    """
    Start redirection of src stream into dst. Return the duplicated file handle of the source.
    
    Example:
        fd = start_redirect( open("somefile.txt"), sys.stdout )
            ... do some stuff ...        
        end_redirect(sys.stdout, fd)
    """

    if src.fileno() == dst.fileno():
        return None
        
    fd_dup_src = os.dup( src.fileno() )
    
    dst.flush()
    src.flush()

    os.close( src.fileno() )
    os.dup2( dst.fileno(), src.fileno() )
    
    return fd_dup_src

def end_redirect(src, fd_dup_src):
    
    """
    End redirection of stream src.Redirect the src stream into dst. src is the source stream and fd_dup_src is the value returned by
    start_redirect()
    """

    if fd_dup_src is None:
        return

    src.flush()
    os.close( src.fileno() )
    os.dup2( fd_dup_src, src.fileno() ) 
    
    os.close(fd_dup_src)