Workaround for Windows Unicode console handling on Python>=3.6. 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
(stream: TextIO)
| 69 | |
| 70 | |
| 71 | def _py36_windowsconsoleio_workaround(stream: TextIO) -> None: |
| 72 | """Workaround for Windows Unicode console handling on Python>=3.6. |
| 73 | |
| 74 | Python 3.6 implemented Unicode console handling for Windows. This works |
| 75 | by reading/writing to the raw console handle using |
| 76 | ``{Read,Write}ConsoleW``. |
| 77 | |
| 78 | The problem is that we are going to ``dup2`` over the stdio file |
| 79 | descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the |
| 80 | handles used by Python to write to the console. Though there is still some |
| 81 | weirdness and the console handle seems to only be closed randomly and not |
| 82 | on the first call to ``CloseHandle``, or maybe it gets reopened with the |
| 83 | same handle value when we suspend capturing. |
| 84 | |
| 85 | The workaround in this case will reopen stdio with a different fd which |
| 86 | also means a different handle by replicating the logic in |
| 87 | "Py_lifecycle.c:initstdio/create_stdio". |
| 88 | |
| 89 | :param stream: |
| 90 | In practice ``sys.stdout`` or ``sys.stderr``, but given |
| 91 | here as parameter for unittesting purposes. |
| 92 | |
| 93 | See https://github.com/pytest-dev/py/issues/103. |
| 94 | """ |
| 95 | if not sys.platform.startswith("win32") or hasattr(sys, "pypy_version_info"): |
| 96 | return |
| 97 | |
| 98 | # Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666). |
| 99 | if not hasattr(stream, "buffer"): # type: ignore[unreachable] |
| 100 | return |
| 101 | |
| 102 | buffered = hasattr(stream.buffer, "raw") |
| 103 | raw_stdout = stream.buffer.raw if buffered else stream.buffer # type: ignore[attr-defined] |
| 104 | |
| 105 | if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined] |
| 106 | return |
| 107 | |
| 108 | def _reopen_stdio(f, mode): |
| 109 | if not buffered and mode[0] == "w": |
| 110 | buffering = 0 |
| 111 | else: |
| 112 | buffering = -1 |
| 113 | |
| 114 | return io.TextIOWrapper( |
| 115 | open(os.dup(f.fileno()), mode, buffering), # type: ignore[arg-type] |
| 116 | f.encoding, |
| 117 | f.errors, |
| 118 | f.newlines, |
| 119 | f.line_buffering, |
| 120 | ) |
| 121 | |
| 122 | sys.stdin = _reopen_stdio(sys.stdin, "rb") |
| 123 | sys.stdout = _reopen_stdio(sys.stdout, "wb") |
| 124 | sys.stderr = _reopen_stdio(sys.stderr, "wb") |
| 125 | |
| 126 | |
| 127 | @hookimpl(hookwrapper=True) |