@@ -58,6 +58,14 @@ def pytask_extend_command_line_interface(cli):
58
58
help = "Per task capturing method. [default: fd]" ,
59
59
),
60
60
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
+ ),
61
69
]
62
70
cli .commands ["build" ].params .extend (additional_parameters )
63
71
@@ -80,6 +88,14 @@ def pytask_parse_config(config, config_from_cli, config_from_file):
80
88
callback = _capture_callback ,
81
89
)
82
90
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
+
83
99
84
100
@hookimpl
85
101
def pytask_post_parse (config ):
@@ -106,6 +122,22 @@ def _capture_callback(x):
106
122
else :
107
123
raise ValueError ("'capture' can only be one of ['fd', 'no', 'sys', 'tee-sys']." )
108
124
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
+
109
141
110
142
# Copied from pytest.
111
143
@@ -126,21 +158,22 @@ def _colorama_workaround() -> None:
126
158
127
159
128
160
def _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.
131
163
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.
136
168
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.
142
174
143
175
See https://github.com/pytest-dev/pytest/pull/1281.
176
+
144
177
"""
145
178
if sys .platform .startswith ("win32" ):
146
179
try :
@@ -152,19 +185,17 @@ def _readline_workaround() -> None:
152
185
def _py36_windowsconsoleio_workaround (stream : TextIO ) -> None :
153
186
"""Workaround for Windows Unicode console handling on Python>=3.6.
154
187
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``.
158
190
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.
165
196
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
168
199
"Py_lifecycle.c:initstdio/create_stdio".
169
200
170
201
:param stream:
@@ -384,6 +415,7 @@ class FDCaptureBinary:
384
415
"""Capture IO to/from a given OS-level file descriptor.
385
416
386
417
snap() produces `bytes`.
418
+
387
419
"""
388
420
389
421
EMPTY_BUFFER = b""
@@ -394,17 +426,17 @@ def __init__(self, targetfd: int) -> None:
394
426
try :
395
427
os .fstat (targetfd )
396
428
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
+
405
437
# 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.
408
440
self .targetfd_invalid : Optional [int ] = os .open (os .devnull , os .O_RDWR )
409
441
os .dup2 (self .targetfd_invalid , targetfd )
410
442
else :
@@ -524,11 +556,10 @@ def writeorg(self, data):
524
556
# MultiCapture
525
557
526
558
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
532
563
@final
533
564
@functools .total_ordering
534
565
class CaptureResult (Generic [AnyStr ]):
0 commit comments