7
7
import contextlib
8
8
import sys
9
9
import os
10
+ import io
10
11
from io import UnsupportedOperation
11
12
from tempfile import TemporaryFile
12
13
@@ -33,8 +34,10 @@ def pytest_addoption(parser):
33
34
34
35
@pytest .hookimpl (hookwrapper = True )
35
36
def pytest_load_initial_conftests (early_config , parser , args ):
36
- _readline_workaround ()
37
37
ns = early_config .known_args_namespace
38
+ if ns .capture == "fd" :
39
+ _py36_windowsconsoleio_workaround ()
40
+ _readline_workaround ()
38
41
pluginmanager = early_config .pluginmanager
39
42
capman = CaptureManager (ns .capture )
40
43
pluginmanager .register (capman , "capturemanager" )
@@ -491,3 +494,49 @@ def _readline_workaround():
491
494
import readline # noqa
492
495
except ImportError :
493
496
pass
497
+
498
+
499
+ def _py36_windowsconsoleio_workaround ():
500
+ """
501
+ Python 3.6 implemented unicode console handling for Windows. This works
502
+ by reading/writing to the raw console handle using
503
+ ``{Read,Write}ConsoleW``.
504
+
505
+ The problem is that we are going to ``dup2`` over the stdio file
506
+ descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the
507
+ handles used by Python to write to the console. Though there is still some
508
+ weirdness and the console handle seems to only be closed randomly and not
509
+ on the first call to ``CloseHandle``, or maybe it gets reopened with the
510
+ same handle value when we suspend capturing.
511
+
512
+ The workaround in this case will reopen stdio with a different fd which
513
+ also means a different handle by replicating the logic in
514
+ "Py_lifecycle.c:initstdio/create_stdio".
515
+
516
+ See https://github.com/pytest-dev/py/issues/103
517
+ """
518
+ if not sys .platform .startswith ('win32' ) or sys .version_info [:2 ] < (3 , 6 ):
519
+ return
520
+
521
+ buffered = hasattr (sys .stdout .buffer , 'raw' )
522
+ raw_stdout = sys .stdout .buffer .raw if buffered else sys .stdout .buffer
523
+
524
+ if not isinstance (raw_stdout , io ._WindowsConsoleIO ):
525
+ return
526
+
527
+ def _reopen_stdio (f , mode ):
528
+ if not buffered and mode [0 ] == 'w' :
529
+ buffering = 0
530
+ else :
531
+ buffering = - 1
532
+
533
+ return io .TextIOWrapper (
534
+ open (os .dup (f .fileno ()), mode , buffering ),
535
+ f .encoding ,
536
+ f .errors ,
537
+ f .newlines ,
538
+ f .line_buffering )
539
+
540
+ sys .__stdin__ = sys .stdin = _reopen_stdio (sys .stdin , 'rb' )
541
+ sys .__stdout__ = sys .stdout = _reopen_stdio (sys .stdout , 'wb' )
542
+ sys .__stderr__ = sys .stderr = _reopen_stdio (sys .stderr , 'wb' )
0 commit comments