Can I redirect the stdout in python into some sort of string buffer?

前端 未结 9 1550
野趣味
野趣味 2020-11-22 06:23

I\'m using python\'s ftplib to write a small FTP client, but some of the functions in the package don\'t return string output, but print to stdout.

相关标签:
9条回答
  • 2020-11-22 06:36

    A context manager for python3:

    import sys
    from io import StringIO
    
    
    class RedirectedStdout:
        def __init__(self):
            self._stdout = None
            self._string_io = None
    
        def __enter__(self):
            self._stdout = sys.stdout
            sys.stdout = self._string_io = StringIO()
            return self
    
        def __exit__(self, type, value, traceback):
            sys.stdout = self._stdout
    
        def __str__(self):
            return self._string_io.getvalue()
    

    use like this:

    >>> with RedirectedStdout() as out:
    >>>     print('asdf')
    >>>     s = str(out)
    >>>     print('bsdf')
    >>> print(s, out)
    'asdf\n' 'asdf\nbsdf\n'
    
    0 讨论(0)
  • 2020-11-22 06:43

    In Python3.6, the StringIO and cStringIO modules are gone, you should use io.StringIO instead.So you should do this like the first answer:

    import sys
    from io import StringIO
    
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    my_stdout = sys.stdout = StringIO()
    my_stderr = sys.stderr = StringIO()
    
    # blah blah lots of code ...
    
    sys.stdout = self.old_stdout
    sys.stderr = self.old_stderr
    
    // if you want to see the value of redirect output, be sure the std output is turn back
    print(my_stdout.getvalue())
    print(my_stderr.getvalue())
    
    my_stdout.close()
    my_stderr.close()
    
    0 讨论(0)
  • 2020-11-22 06:45

    This method restores sys.stdout even if there's an exception. It also gets any output before the exception.

    import io
    import sys
    
    real_stdout = sys.stdout
    fake_stdout = io.BytesIO()   # or perhaps io.StringIO()
    try:
        sys.stdout = fake_stdout
        # do what you have to do to create some output
    finally:
        sys.stdout = real_stdout
        output_string = fake_stdout.getvalue()
        fake_stdout.close()
        # do what you want with the output_string
    

    Tested in Python 2.7.10 using io.BytesIO()

    Tested in Python 3.6.4 using io.StringIO()


    Bob, added for a case if you feel anything from the modified / extended code experimentation might get interesting in any sense, otherwise feel free to delete it

    Ad informandum ... a few remarks from extended experimentation during finding some viable mechanics to "grab" outputs, directed by numexpr.print_versions() directly to the <stdout> ( upon a need to clean GUI and collecting details into debugging-report )

    # THIS WORKS AS HELL: as Bob Stein proposed years ago:
    #  py2 SURPRISEDaBIT:
    #
    import io
    import sys
    #
    real_stdout = sys.stdout                        #           PUSH <stdout> ( store to REAL_ )
    fake_stdout = io.BytesIO()                      #           .DEF FAKE_
    try:                                            # FUSED .TRY:
        sys.stdout.flush()                          #           .flush() before
        sys.stdout = fake_stdout                    #           .SET <stdout> to use FAKE_
        # ----------------------------------------- #           +    do what you gotta do to create some output
        print 123456789                             #           + 
        import  numexpr                             #           + 
        QuantFX.numexpr.__version__                 #           + [3] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
        QuantFX.numexpr.print_versions()            #           + [4] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
        _ = os.system( 'echo os.system() redir-ed' )#           + [1] via real_stdout                                 + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
        _ = os.write(  sys.stderr.fileno(),         #           + [2] via      stderr                                 + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
                           b'os.write()  redir-ed' )#  *OTHERWISE, if via fake_stdout, EXC <_io.BytesIO object at 0x02C0BB10> Traceback (most recent call last):
        # ----------------------------------------- #           ?                              io.UnsupportedOperation: fileno
        #'''                                                    ? YET:        <_io.BytesIO object at 0x02C0BB10> has a .fileno() method listed
        #>>> 'fileno' in dir( sys.stdout )       -> True        ? HAS IT ADVERTISED,
        #>>> pass;            sys.stdout.fileno  -> <built-in method fileno of _io.BytesIO object at 0x02C0BB10>
        #>>> pass;            sys.stdout.fileno()-> Traceback (most recent call last):
        #                                             File "<stdin>", line 1, in <module>
        #                                           io.UnsupportedOperation: fileno
        #                                                       ? BUT REFUSES TO USE IT
        #'''
    finally:                                        # == FINALLY:
        sys.stdout.flush()                          #           .flush() before ret'd back REAL_
        sys.stdout = real_stdout                    #           .SET <stdout> to use POP'd REAL_
        sys.stdout.flush()                          #           .flush() after  ret'd back REAL_
        out_string = fake_stdout.getvalue()         #           .GET string           from FAKE_
        fake_stdout.close()                         #                <FD>.close()
        # +++++++++++++++++++++++++++++++++++++     # do what you want with the out_string
        #
        print "\n{0:}\n{1:}{0:}".format( 60 * "/\\",# "LATE" deferred print the out_string at the very end reached -> real_stdout
                                         out_string #                   
                                         )
    '''
    PASS'd:::::
    ...
    os.system() redir-ed
    os.write()  redir-ed
    /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
    123456789
    '2.5'
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    Numexpr version:   2.5
    NumPy version:     1.10.4
    Python version:    2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
    AMD/Intel CPU?     True
    VML available?     True
    VML/MKL version:   Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
    Number of threads used by default: 4 (out of 4 detected cores)
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
    >>>
    
    EXC'd :::::
    ...
    os.system() redir-ed
    /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
    123456789
    '2.5'
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    Numexpr version:   2.5
    NumPy version:     1.10.4
    Python version:    2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
    AMD/Intel CPU?     True
    VML available?     True
    VML/MKL version:   Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
    Number of threads used by default: 4 (out of 4 detected cores)
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
    
    Traceback (most recent call last):
      File "<stdin>", line 9, in <module>
    io.UnsupportedOperation: fileno
    '''
    
    0 讨论(0)
  • 2020-11-22 06:48

    Use pipe() and write to the appropriate file descriptor.

    https://docs.python.org/library/os.html#file-descriptor-operations

    0 讨论(0)
  • 2020-11-22 06:50

    There is contextlib.redirect_stdout() function in Python 3.4:

    import io
    from contextlib import redirect_stdout
    
    with io.StringIO() as buf, redirect_stdout(buf):
        print('redirected')
        output = buf.getvalue()
    

    Here's code example that shows how to implement it on older Python versions.

    0 讨论(0)
  • 2020-11-22 06:50

    Starting with Python 2.6 you can use anything implementing the TextIOBase API from the io module as a replacement. This solution also enables you to use sys.stdout.buffer.write() in Python 3 to write (already) encoded byte strings to stdout (see stdout in Python 3). Using StringIO wouldn't work then, because neither sys.stdout.encoding nor sys.stdout.buffer would be available.

    A solution using TextIOWrapper:

    import sys
    from io import TextIOWrapper, BytesIO
    
    # setup the environment
    old_stdout = sys.stdout
    sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)
    
    # do something that writes to stdout or stdout.buffer
    
    # get output
    sys.stdout.seek(0)      # jump to the start
    out = sys.stdout.read() # read output
    
    # restore stdout
    sys.stdout.close()
    sys.stdout = old_stdout
    

    This solution works for Python 2 >= 2.6 and Python 3.

    Please note that our new sys.stdout.write() only accepts unicode strings and sys.stdout.buffer.write() only accepts byte strings. This might not be the case for old code, but is often the case for code that is built to run on Python 2 and 3 without changes, which again often makes use of sys.stdout.buffer.

    You can build a slight variation that accepts unicode and byte strings for write():

    class StdoutBuffer(TextIOWrapper):
        def write(self, string):
            try:
                return super(StdoutBuffer, self).write(string)
            except TypeError:
                # redirect encoded byte strings directly to buffer
                return super(StdoutBuffer, self).buffer.write(string)
    

    You don't have to set the encoding of the buffer the sys.stdout.encoding, but this helps when using this method for testing/comparing script output.

    0 讨论(0)
提交回复
热议问题