You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			88 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			88 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
from __future__ import annotations
 | 
						|
 | 
						|
import errno
 | 
						|
import os
 | 
						|
import sys
 | 
						|
from contextlib import contextmanager
 | 
						|
from typing import IO, Iterator, TextIO
 | 
						|
 | 
						|
__all__ = ["flush_stdout"]
 | 
						|
 | 
						|
 | 
						|
def flush_stdout(stdout: TextIO, data: str) -> None:
 | 
						|
    # If the IO object has an `encoding` and `buffer` attribute, it means that
 | 
						|
    # we can access the underlying BinaryIO object and write into it in binary
 | 
						|
    # mode. This is preferred if possible.
 | 
						|
    # NOTE: When used in a Jupyter notebook, don't write binary.
 | 
						|
    #       `ipykernel.iostream.OutStream` has an `encoding` attribute, but not
 | 
						|
    #       a `buffer` attribute, so we can't write binary in it.
 | 
						|
    has_binary_io = hasattr(stdout, "encoding") and hasattr(stdout, "buffer")
 | 
						|
 | 
						|
    try:
 | 
						|
        # Ensure that `stdout` is made blocking when writing into it.
 | 
						|
        # Otherwise, when uvloop is activated (which makes stdout
 | 
						|
        # non-blocking), and we write big amounts of text, then we get a
 | 
						|
        # `BlockingIOError` here.
 | 
						|
        with _blocking_io(stdout):
 | 
						|
            # (We try to encode ourself, because that way we can replace
 | 
						|
            # characters that don't exist in the character set, avoiding
 | 
						|
            # UnicodeEncodeError crashes. E.g. u'\xb7' does not appear in 'ascii'.)
 | 
						|
            # My Arch Linux installation of july 2015 reported 'ANSI_X3.4-1968'
 | 
						|
            # for sys.stdout.encoding in xterm.
 | 
						|
            if has_binary_io:
 | 
						|
                stdout.buffer.write(data.encode(stdout.encoding or "utf-8", "replace"))
 | 
						|
            else:
 | 
						|
                stdout.write(data)
 | 
						|
 | 
						|
            stdout.flush()
 | 
						|
    except OSError as e:
 | 
						|
        if e.args and e.args[0] == errno.EINTR:
 | 
						|
            # Interrupted system call. Can happen in case of a window
 | 
						|
            # resize signal. (Just ignore. The resize handler will render
 | 
						|
            # again anyway.)
 | 
						|
            pass
 | 
						|
        elif e.args and e.args[0] == 0:
 | 
						|
            # This can happen when there is a lot of output and the user
 | 
						|
            # sends a KeyboardInterrupt by pressing Control-C. E.g. in
 | 
						|
            # a Python REPL when we execute "while True: print('test')".
 | 
						|
            # (The `ptpython` REPL uses this `Output` class instead of
 | 
						|
            # `stdout` directly -- in order to be network transparent.)
 | 
						|
            # So, just ignore.
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            raise
 | 
						|
 | 
						|
 | 
						|
@contextmanager
 | 
						|
def _blocking_io(io: IO[str]) -> Iterator[None]:
 | 
						|
    """
 | 
						|
    Ensure that the FD for `io` is set to blocking in here.
 | 
						|
    """
 | 
						|
    if sys.platform == "win32":
 | 
						|
        # On Windows, the `os` module doesn't have a `get/set_blocking`
 | 
						|
        # function.
 | 
						|
        yield
 | 
						|
        return
 | 
						|
 | 
						|
    try:
 | 
						|
        fd = io.fileno()
 | 
						|
        blocking = os.get_blocking(fd)
 | 
						|
    except:  # noqa
 | 
						|
        # Failed somewhere.
 | 
						|
        # `get_blocking` can raise `OSError`.
 | 
						|
        # The io object can raise `AttributeError` when no `fileno()` method is
 | 
						|
        # present if we're not a real file object.
 | 
						|
        blocking = True  # Assume we're good, and don't do anything.
 | 
						|
 | 
						|
    try:
 | 
						|
        # Make blocking if we weren't blocking yet.
 | 
						|
        if not blocking:
 | 
						|
            os.set_blocking(fd, True)
 | 
						|
 | 
						|
        yield
 | 
						|
 | 
						|
    finally:
 | 
						|
        # Restore original blocking mode.
 | 
						|
        if not blocking:
 | 
						|
            os.set_blocking(fd, blocking)
 |