Workaround for Windows Unicode console handling. Python 3.6 implemented Unicode console handling for Windows. This works by reading/writing to the raw console handle using ``{Read,Write}ConsoleW``. The problem is that we are going to ``dup2`` over the stdio file descriptors whe
(stream: TextIO)
| 98 | |
| 99 | |
| 100 | def _windowsconsoleio_workaround(stream: TextIO) -> None: |
| 101 | """Workaround for Windows Unicode console handling. |
| 102 | |
| 103 | Python 3.6 implemented Unicode console handling for Windows. This works |
| 104 | by reading/writing to the raw console handle using |
| 105 | ``{Read,Write}ConsoleW``. |
| 106 | |
| 107 | The problem is that we are going to ``dup2`` over the stdio file |
| 108 | descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the |
| 109 | handles used by Python to write to the console. Though there is still some |
| 110 | weirdness and the console handle seems to only be closed randomly and not |
| 111 | on the first call to ``CloseHandle``, or maybe it gets reopened with the |
| 112 | same handle value when we suspend capturing. |
| 113 | |
| 114 | The workaround in this case will reopen stdio with a different fd which |
| 115 | also means a different handle by replicating the logic in |
| 116 | "Py_lifecycle.c:initstdio/create_stdio". |
| 117 | |
| 118 | :param stream: |
| 119 | In practice ``sys.stdout`` or ``sys.stderr``, but given |
| 120 | here as parameter for unittesting purposes. |
| 121 | |
| 122 | See https://github.com/pytest-dev/py/issues/103. |
| 123 | """ |
| 124 | if not sys.platform.startswith("win32") or hasattr(sys, "pypy_version_info"): |
| 125 | return |
| 126 | |
| 127 | # Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666). |
| 128 | if not hasattr(stream, "buffer"): # type: ignore[unreachable,unused-ignore] |
| 129 | return |
| 130 | |
| 131 | raw_stdout = stream.buffer.raw if hasattr(stream.buffer, "raw") else stream.buffer |
| 132 | |
| 133 | if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined,unused-ignore] |
| 134 | return |
| 135 | |
| 136 | def _reopen_stdio(f, mode): |
| 137 | if not hasattr(stream.buffer, "raw") and mode[0] == "w": |
| 138 | buffering = 0 |
| 139 | else: |
| 140 | buffering = -1 |
| 141 | |
| 142 | return io.TextIOWrapper( |
| 143 | open(os.dup(f.fileno()), mode, buffering), |
| 144 | f.encoding, |
| 145 | f.errors, |
| 146 | f.newlines, |
| 147 | f.line_buffering, |
| 148 | ) |
| 149 | |
| 150 | sys.stdin = _reopen_stdio(sys.stdin, "rb") |
| 151 | sys.stdout = _reopen_stdio(sys.stdout, "wb") |
| 152 | sys.stderr = _reopen_stdio(sys.stderr, "wb") |
| 153 | |
| 154 | |
| 155 | @hookimpl(wrapper=True) |