@@ -58,6 +58,14 @@ def pytask_extend_command_line_interface(cli):
5858 help = "Per task capturing method. [default: fd]" ,
5959 ),
6060 click .Option (["-s" ], is_flag = True , help = "Shortcut for --capture=no." ),
61+ click .Option (
62+ ["--show-capture" ],
63+ type = click .Choice (["no" , "stdout" , "stderr" , "all" ]),
64+ help = (
65+ "Choose which captured output should be shown for failed tasks. "
66+ "[default: all]"
67+ ),
68+ ),
6169 ]
6270 cli .commands ["build" ].params .extend (additional_parameters )
6371
@@ -80,6 +88,14 @@ def pytask_parse_config(config, config_from_cli, config_from_file):
8088 callback = _capture_callback ,
8189 )
8290
91+ config ["show_capture" ] = get_first_non_none_value (
92+ config_from_cli ,
93+ config_from_file ,
94+ key = "show_capture" ,
95+ default = "all" ,
96+ callback = _show_capture_callback ,
97+ )
98+
8399
84100@hookimpl
85101def pytask_post_parse (config ):
@@ -106,6 +122,22 @@ def _capture_callback(x):
106122 else :
107123 raise ValueError ("'capture' can only be one of ['fd', 'no', 'sys', 'tee-sys']." )
108124
125+ return x
126+
127+
128+ def _show_capture_callback (x ):
129+ """Validate the passed options for showing captured output."""
130+ if x in [None , "None" , "none" ]:
131+ x = None
132+ elif x in ["no" , "stdout" , "stderr" , "all" ]:
133+ pass
134+ else :
135+ raise ValueError (
136+ "'show_capture' must be one of ['no', 'stdout', 'stderr', 'all']."
137+ )
138+
139+ return x
140+
109141
110142# Copied from pytest.
111143
@@ -126,21 +158,22 @@ def _colorama_workaround() -> None:
126158
127159
128160def _readline_workaround () -> None :
129- """Ensure readline is imported so that it attaches to the correct stdio
130- handles on Windows.
161+ """Ensure readline is imported so that it attaches to the correct stdio handles on
162+ Windows.
131163
132- Pdb uses readline support where available--when not running from the Python
133- prompt, the readline module is not imported until running the pdb REPL. If
134- running pytest with the --pdb option this means the readline module is not
135- imported until after I/O capture has been started.
164+ Pdb uses readline support where available--when not running from the Python prompt,
165+ the readline module is not imported until running the pdb REPL. If running pytest
166+ with the --pdb option this means the readline module is not imported until after I/O
167+ capture has been started.
136168
137- This is a problem for pyreadline, which is often used to implement readline
138- support on Windows, as it does not attach to the correct handles for stdout
139- and/or stdin if they have been redirected by the FDCapture mechanism. This
140- workaround ensures that readline is imported before I/O capture is setup so
141- that it can attach to the actual stdin/out for the console.
169+ This is a problem for pyreadline, which is often used to implement readline support
170+ on Windows, as it does not attach to the correct handles for stdout and/or stdin if
171+ they have been redirected by the FDCapture mechanism. This workaround ensures that
172+ readline is imported before I/O capture is setup so that it can attach to the actual
173+ stdin/out for the console.
142174
143175 See https://github.com/pytest-dev/pytest/pull/1281.
176+
144177 """
145178 if sys .platform .startswith ("win32" ):
146179 try :
@@ -152,19 +185,17 @@ def _readline_workaround() -> None:
152185def _py36_windowsconsoleio_workaround (stream : TextIO ) -> None :
153186 """Workaround for Windows Unicode console handling on Python>=3.6.
154187
155- Python 3.6 implemented Unicode console handling for Windows. This works
156- by reading/writing to the raw console handle using
157- ``{Read,Write}ConsoleW``.
188+ Python 3.6 implemented Unicode console handling for Windows. This works by
189+ reading/writing to the raw console handle using ``{Read,Write}ConsoleW``.
158190
159- The problem is that we are going to ``dup2`` over the stdio file
160- descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the
161- handles used by Python to write to the console. Though there is still some
162- weirdness and the console handle seems to only be closed randomly and not
163- on the first call to ``CloseHandle``, or maybe it gets reopened with the
164- same handle value when we suspend capturing.
191+ The problem is that we are going to ``dup2`` over the stdio file descriptors when
192+ doing ``FDCapture`` and this will ``CloseHandle`` the handles used by Python to
193+ write to the console. Though there is still some weirdness and the console handle
194+ seems to only be closed randomly and not on the first call to ``CloseHandle``, or
195+ maybe it gets reopened with the same handle value when we suspend capturing.
165196
166- The workaround in this case will reopen stdio with a different fd which
167- also means a different handle by replicating the logic in
197+ The workaround in this case will reopen stdio with a different fd which also means a
198+ different handle by replicating the logic in
168199 "Py_lifecycle.c:initstdio/create_stdio".
169200
170201 :param stream:
@@ -384,6 +415,7 @@ class FDCaptureBinary:
384415 """Capture IO to/from a given OS-level file descriptor.
385416
386417 snap() produces `bytes`.
418+
387419 """
388420
389421 EMPTY_BUFFER = b""
@@ -394,17 +426,17 @@ def __init__(self, targetfd: int) -> None:
394426 try :
395427 os .fstat (targetfd )
396428 except OSError :
397- # FD capturing is conceptually simple -- create a temporary file,
398- # redirect the FD to it, redirect back when done. But when the
399- # target FD is invalid it throws a wrench into this loveley scheme.
400- #
401- # Tests themselves shouldn't care if the FD is valid, FD capturing
402- # should work regardless of external circumstances. So falling back
403- # to just sys capturing is not a good option.
404- #
429+ # FD capturing is conceptually simple -- create a temporary file, redirect
430+ # the FD to it, redirect back when done. But when the target FD is invalid
431+ # it throws a wrench into this loveley scheme.
432+
433+ # Tests themselves shouldn't care if the FD is valid, FD capturing should
434+ # work regardless of external circumstances. So falling back to just sys
435+ # capturing is not a good option.
436+
405437 # Further complications are the need to support suspend() and the
406- # possibility of FD reuse (e.g. the tmpfile getting the very same
407- # target FD). The following approach is robust, I believe.
438+ # possibility of FD reuse (e.g. the tmpfile getting the very same target
439+ # FD). The following approach is robust, I believe.
408440 self .targetfd_invalid : Optional [int ] = os .open (os .devnull , os .O_RDWR )
409441 os .dup2 (self .targetfd_invalid , targetfd )
410442 else :
@@ -524,11 +556,10 @@ def writeorg(self, data):
524556# MultiCapture
525557
526558
527- # This class was a namedtuple, but due to mypy limitation[0] it could not be
528- # made generic, so was replaced by a regular class which tries to emulate the
529- # pertinent parts of a namedtuple. If the mypy limitation is ever lifted, can
530- # make it a namedtuple again.
531- # [0]: https://github.com/python/mypy/issues/685
559+ # This class was a namedtuple, but due to mypy limitation[0] it could not be made
560+ # generic, so was replaced by a regular class which tries to emulate the pertinent parts
561+ # of a namedtuple. If the mypy limitation is ever lifted, can make it a namedtuple
562+ # again. [0]: https://github.com/python/mypy/issues/685
532563@final
533564@functools .total_ordering
534565class CaptureResult (Generic [AnyStr ]):
0 commit comments