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:
|
