Mercurial > public > mercurial-scm > hg
comparison mercurial/sshpeer.py @ 36533:1a36ef7df70a
sshpeer: support not reading and forwarding stderr
The "doublepipe" primitive as used by sshpeer will automatically read
from stderr and forward output to the local ui.
This poses problems for deterministic testing because reads may not
be consistent. For example, the server may not be done sending all
output to stderr and the client will perform different numbers of
read operations or will read from stderr and stdout at different times.
To make tests deterministic, we'll need to disable the "doublepipe"
primitive and perform stderr I/O explicitly. We add an argument to the
sshpeer constructor to disable the use of the doublepipe.
Differential Revision: https://phab.mercurial-scm.org/D2467
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Mon, 26 Feb 2018 13:12:03 -0800 |
parents | dabf86721200 |
children | 3cd245945ef3 |
comparison
equal
deleted
inserted
replaced
36532:1138e5c0fbc9 | 36533:1a36ef7df70a |
---|---|
335 _forwardoutput(ui, stderr) | 335 _forwardoutput(ui, stderr) |
336 | 336 |
337 return protoname, caps | 337 return protoname, caps |
338 | 338 |
339 class sshv1peer(wireproto.wirepeer): | 339 class sshv1peer(wireproto.wirepeer): |
340 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps): | 340 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps, |
341 autoreadstderr=True): | |
341 """Create a peer from an existing SSH connection. | 342 """Create a peer from an existing SSH connection. |
342 | 343 |
343 ``proc`` is a handle on the underlying SSH process. | 344 ``proc`` is a handle on the underlying SSH process. |
344 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio | 345 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio |
345 pipes for that process. | 346 pipes for that process. |
346 ``caps`` is a set of capabilities supported by the remote. | 347 ``caps`` is a set of capabilities supported by the remote. |
348 ``autoreadstderr`` denotes whether to automatically read from | |
349 stderr and to forward its output. | |
347 """ | 350 """ |
348 self._url = url | 351 self._url = url |
349 self._ui = ui | 352 self._ui = ui |
350 # self._subprocess is unused. Keeping a handle on the process | 353 # self._subprocess is unused. Keeping a handle on the process |
351 # holds a reference and prevents it from being garbage collected. | 354 # holds a reference and prevents it from being garbage collected. |
352 self._subprocess = proc | 355 self._subprocess = proc |
353 | 356 |
354 # And we hook up our "doublepipe" wrapper to allow querying | 357 # And we hook up our "doublepipe" wrapper to allow querying |
355 # stderr any time we perform I/O. | 358 # stderr any time we perform I/O. |
356 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr) | 359 if autoreadstderr: |
357 stdin = doublepipe(ui, stdin, stderr) | 360 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr) |
361 stdin = doublepipe(ui, stdin, stderr) | |
358 | 362 |
359 self._pipeo = stdin | 363 self._pipeo = stdin |
360 self._pipei = stdout | 364 self._pipei = stdout |
361 self._pipee = stderr | 365 self._pipee = stderr |
362 self._caps = caps | 366 self._caps = caps |
529 """A peer that speakers version 2 of the transport protocol.""" | 533 """A peer that speakers version 2 of the transport protocol.""" |
530 # Currently version 2 is identical to version 1 post handshake. | 534 # Currently version 2 is identical to version 1 post handshake. |
531 # And handshake is performed before the peer is instantiated. So | 535 # And handshake is performed before the peer is instantiated. So |
532 # we need no custom code. | 536 # we need no custom code. |
533 | 537 |
534 def makepeer(ui, path, proc, stdin, stdout, stderr): | 538 def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True): |
535 """Make a peer instance from existing pipes. | 539 """Make a peer instance from existing pipes. |
536 | 540 |
537 ``path`` and ``proc`` are stored on the eventual peer instance and may | 541 ``path`` and ``proc`` are stored on the eventual peer instance and may |
538 not be used for anything meaningful. | 542 not be used for anything meaningful. |
539 | 543 |
550 except Exception: | 554 except Exception: |
551 _cleanuppipes(ui, stdout, stdin, stderr) | 555 _cleanuppipes(ui, stdout, stdin, stderr) |
552 raise | 556 raise |
553 | 557 |
554 if protoname == wireprotoserver.SSHV1: | 558 if protoname == wireprotoserver.SSHV1: |
555 return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps) | 559 return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps, |
560 autoreadstderr=autoreadstderr) | |
556 elif protoname == wireprotoserver.SSHV2: | 561 elif protoname == wireprotoserver.SSHV2: |
557 return sshv2peer(ui, path, proc, stdin, stdout, stderr, caps) | 562 return sshv2peer(ui, path, proc, stdin, stdout, stderr, caps, |
563 autoreadstderr=autoreadstderr) | |
558 else: | 564 else: |
559 _cleanuppipes(ui, stdout, stdin, stderr) | 565 _cleanuppipes(ui, stdout, stdin, stderr) |
560 raise error.RepoError(_('unknown version of SSH protocol: %s') % | 566 raise error.RepoError(_('unknown version of SSH protocol: %s') % |
561 protoname) | 567 protoname) |
562 | 568 |