Benjamin Schubert pushed to branch bschubert/fix-binary-output-capture at BuildStream / buildstream
Commits:
-
f069d82f
by Daniel Silverstone at 2018-10-26T12:19:58Z
-
283887a5
by James Ennis at 2018-10-26T13:12:12Z
-
2c6cb230
by Valentin David at 2018-10-26T14:21:18Z
-
a8250ca4
by Valentin David at 2018-10-26T15:04:25Z
-
1b308fe3
by Daniel Silverstone at 2018-10-26T15:50:42Z
-
65d90c31
by Daniel Silverstone at 2018-10-26T15:50:54Z
-
ed733f3e
by Daniel Silverstone at 2018-10-26T15:51:40Z
8 changed files:
- buildstream/_stream.py
- buildstream/_yaml.py
- buildstream/element.py
- buildstream/plugin.py
- buildstream/utils.py
- tests/frontend/buildcheckout.py
- tests/frontend/yamlcache.py
- tests/testutils/runcli.py
Changes:
... | ... | @@ -423,9 +423,16 @@ class Stream(): |
423 | 423 |
else:
|
424 | 424 |
if location == '-':
|
425 | 425 |
with target.timed_activity("Creating tarball"):
|
426 |
- with os.fdopen(sys.stdout.fileno(), 'wb') as fo:
|
|
427 |
- with tarfile.open(fileobj=fo, mode="w|") as tf:
|
|
428 |
- sandbox_vroot.export_to_tar(tf, '.')
|
|
426 |
+ # Save the stdout FD to restore later
|
|
427 |
+ saved_fd = os.dup(sys.stdout.fileno())
|
|
428 |
+ try:
|
|
429 |
+ with os.fdopen(sys.stdout.fileno(), 'wb') as fo:
|
|
430 |
+ with tarfile.open(fileobj=fo, mode="w|") as tf:
|
|
431 |
+ sandbox_vroot.export_to_tar(tf, '.')
|
|
432 |
+ finally:
|
|
433 |
+ # No matter what, restore stdout for further use
|
|
434 |
+ os.dup2(saved_fd, sys.stdout.fileno())
|
|
435 |
+ os.close(saved_fd)
|
|
429 | 436 |
else:
|
430 | 437 |
with target.timed_activity("Creating tarball '{}'"
|
431 | 438 |
.format(location)):
|
... | ... | @@ -335,16 +335,9 @@ def node_get_provenance(node, key=None, indices=None): |
335 | 335 |
return provenance
|
336 | 336 |
|
337 | 337 |
|
338 |
-# Helper to use utils.sentinel without unconditional utils import,
|
|
339 |
-# which causes issues for completion.
|
|
340 |
-#
|
|
341 |
-# Local private, but defined here because sphinx appears to break if
|
|
342 |
-# it's not defined before any functions calling it in default kwarg
|
|
343 |
-# values.
|
|
344 |
-#
|
|
345 |
-def _get_sentinel():
|
|
346 |
- from .utils import _sentinel
|
|
347 |
- return _sentinel
|
|
338 |
+# A sentinel to be used as a default argument for functions that need
|
|
339 |
+# to distinguish between a kwarg set to None and an unset kwarg.
|
|
340 |
+_sentinel = object()
|
|
348 | 341 |
|
349 | 342 |
|
350 | 343 |
# node_get()
|
... | ... | @@ -368,10 +361,10 @@ def _get_sentinel(): |
368 | 361 |
# Note:
|
369 | 362 |
# Returned strings are stripped of leading and trailing whitespace
|
370 | 363 |
#
|
371 |
-def node_get(node, expected_type, key, indices=None, default_value=_get_sentinel()):
|
|
364 |
+def node_get(node, expected_type, key, indices=None, default_value=_sentinel):
|
|
372 | 365 |
value = node.get(key, default_value)
|
373 | 366 |
provenance = node_get_provenance(node)
|
374 |
- if value is _get_sentinel():
|
|
367 |
+ if value is _sentinel:
|
|
375 | 368 |
raise LoadError(LoadErrorReason.INVALID_DATA,
|
376 | 369 |
"{}: Dictionary did not contain expected key '{}'".format(provenance, key))
|
377 | 370 |
|
... | ... | @@ -451,7 +451,7 @@ class Element(Plugin): |
451 | 451 |
|
452 | 452 |
return None
|
453 | 453 |
|
454 |
- def node_subst_member(self, node, member_name, default=utils._sentinel):
|
|
454 |
+ def node_subst_member(self, node, member_name, default=_yaml._sentinel):
|
|
455 | 455 |
"""Fetch the value of a string node member, substituting any variables
|
456 | 456 |
in the loaded value with the element contextual variables.
|
457 | 457 |
|
... | ... | @@ -321,7 +321,7 @@ class Plugin(): |
321 | 321 |
provenance = _yaml.node_get_provenance(node, key=member_name)
|
322 | 322 |
return str(provenance)
|
323 | 323 |
|
324 |
- def node_get_member(self, node, expected_type, member_name, default=utils._sentinel):
|
|
324 |
+ def node_get_member(self, node, expected_type, member_name, default=_yaml._sentinel):
|
|
325 | 325 |
"""Fetch the value of a node member, raising an error if the value is
|
326 | 326 |
missing or incorrectly typed.
|
327 | 327 |
|
... | ... | @@ -654,10 +654,6 @@ def _pretty_size(size, dec_places=0): |
654 | 654 |
return "{size:g}{unit}".format(size=round(psize, dec_places), unit=unit)
|
655 | 655 |
|
656 | 656 |
|
657 |
-# A sentinel to be used as a default argument for functions that need
|
|
658 |
-# to distinguish between a kwarg set to None and an unset kwarg.
|
|
659 |
-_sentinel = object()
|
|
660 |
- |
|
661 | 657 |
# Main process pid
|
662 | 658 |
_main_pid = os.getpid()
|
663 | 659 |
|
... | ... | @@ -128,7 +128,6 @@ def test_build_checkout_tarball(datafiles, cli): |
128 | 128 |
assert os.path.join('.', 'usr', 'include', 'pony.h') in tar.getnames()
|
129 | 129 |
|
130 | 130 |
|
131 |
-@pytest.mark.skip(reason="Capturing the binary output is causing a stacktrace")
|
|
132 | 131 |
@pytest.mark.datafiles(DATA_DIR)
|
133 | 132 |
def test_build_checkout_tarball_stdout(datafiles, cli):
|
134 | 133 |
project = os.path.join(datafiles.dirname, datafiles.basename)
|
... | ... | @@ -143,7 +142,7 @@ def test_build_checkout_tarball_stdout(datafiles, cli): |
143 | 142 |
|
144 | 143 |
checkout_args = ['checkout', '--tar', 'target.bst', '-']
|
145 | 144 |
|
146 |
- result = cli.run(project=project, args=checkout_args)
|
|
145 |
+ result = cli.run(project=project, args=checkout_args, binary_capture=True)
|
|
147 | 146 |
result.assert_success()
|
148 | 147 |
|
149 | 148 |
with open(tarball, 'wb') as f:
|
... | ... | @@ -103,7 +103,7 @@ def test_yamlcache_used(cli, tmpdir, ref_storage, with_junction, move_project): |
103 | 103 |
yc.put_from_key(prj, element_path, key, contents)
|
104 | 104 |
|
105 | 105 |
# Show that a variable has been added
|
106 |
- result = cli.run(project=project, args=['show', '--format', '%{vars}', 'test.bst'])
|
|
106 |
+ result = cli.run(project=project, args=['show', '--deps', 'none', '--format', '%{vars}', 'test.bst'])
|
|
107 | 107 |
result.assert_success()
|
108 | 108 |
data = yaml.safe_load(result.output)
|
109 | 109 |
assert 'modified' in data
|
... | ... | @@ -135,7 +135,7 @@ def test_yamlcache_changed_file(cli, tmpdir, ref_storage, with_junction): |
135 | 135 |
_yaml.load(element_path, copy_tree=False, project=prj, yaml_cache=yc)
|
136 | 136 |
|
137 | 137 |
# Show that a variable has been added
|
138 |
- result = cli.run(project=project, args=['show', '--format', '%{vars}', 'test.bst'])
|
|
138 |
+ result = cli.run(project=project, args=['show', '--deps', 'none', '--format', '%{vars}', 'test.bst'])
|
|
139 | 139 |
result.assert_success()
|
140 | 140 |
data = yaml.safe_load(result.output)
|
141 | 141 |
assert 'modified' in data
|
... | ... | @@ -17,7 +17,7 @@ import pytest |
17 | 17 |
# CliRunner convenience API (click.testing module) does not support
|
18 | 18 |
# separation of stdout/stderr.
|
19 | 19 |
#
|
20 |
-from _pytest.capture import MultiCapture, FDCapture
|
|
20 |
+from _pytest.capture import MultiCapture, FDCapture, FDCaptureBinary
|
|
21 | 21 |
|
22 | 22 |
# Import the main cli entrypoint
|
23 | 23 |
from buildstream._frontend import cli as bst_cli
|
... | ... | @@ -234,9 +234,10 @@ class Cli(): |
234 | 234 |
# silent (bool): Whether to pass --no-verbose
|
235 | 235 |
# env (dict): Environment variables to temporarily set during the test
|
236 | 236 |
# args (list): A list of arguments to pass buildstream
|
237 |
+ # binary_capture (bool): Whether to capture the stdout/stderr as binary
|
|
237 | 238 |
#
|
238 | 239 |
def run(self, configure=True, project=None, silent=False, env=None,
|
239 |
- cwd=None, options=None, args=None):
|
|
240 |
+ cwd=None, options=None, args=None, binary_capture=False):
|
|
240 | 241 |
if args is None:
|
241 | 242 |
args = []
|
242 | 243 |
if options is None:
|
... | ... | @@ -278,7 +279,7 @@ class Cli(): |
278 | 279 |
except ValueError:
|
279 | 280 |
sys.__stdout__ = open('/dev/stdout', 'w')
|
280 | 281 |
|
281 |
- result = self.invoke(bst_cli, bst_args)
|
|
282 |
+ result = self.invoke(bst_cli, bst_args, binary_capture=binary_capture)
|
|
282 | 283 |
|
283 | 284 |
# Some informative stdout we can observe when anything fails
|
284 | 285 |
if self.verbose:
|
... | ... | @@ -295,7 +296,7 @@ class Cli(): |
295 | 296 |
|
296 | 297 |
return result
|
297 | 298 |
|
298 |
- def invoke(self, cli, args=None, color=False, **extra):
|
|
299 |
+ def invoke(self, cli, args=None, color=False, binary_capture=False, **extra):
|
|
299 | 300 |
exc_info = None
|
300 | 301 |
exception = None
|
301 | 302 |
exit_code = 0
|
... | ... | @@ -305,8 +306,8 @@ class Cli(): |
305 | 306 |
old_stdin = sys.stdin
|
306 | 307 |
with open(os.devnull) as devnull:
|
307 | 308 |
sys.stdin = devnull
|
308 |
- |
|
309 |
- capture = MultiCapture(out=True, err=True, in_=False, Capture=FDCapture)
|
|
309 |
+ capture_kind = FDCaptureBinary if binary_capture else FDCapture
|
|
310 |
+ capture = MultiCapture(out=True, err=True, in_=False, Capture=capture_kind)
|
|
310 | 311 |
capture.start_capturing()
|
311 | 312 |
|
312 | 313 |
try:
|