Mercurial > hg
comparison mercurial/chgserver.py @ 45852:b56feaa9b520
chgserver: backport py3 buffered I/O workarounds from procutil
I've recently switched to new machine and I found chg's stdout is fully
buffered.
Even though chg server is a daemon process, it inherits the environment
where the chg client originally forked the server. This means the server's
stdout might have been wrapped by LineBufferedWrapper. That's why we need
to do wrap/unwrap in both ways.
The "if" condition in _restoreio() looks weird, but I'm not willing to
clean things up because stdio behavior is fundamentally different between
py2 and py3, and py2 support will be dropped anyway.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Tue, 17 Nov 2020 19:29:08 +0900 |
parents | d2e1dcd4490d |
children | 4b4160a83303 |
comparison
equal
deleted
inserted
replaced
45847:d68618954ade | 45852:b56feaa9b520 |
---|---|
407 # to see output immediately on pager, the mode stays unchanged | 407 # to see output immediately on pager, the mode stays unchanged |
408 # when client re-attached. ferr is unchanged because it should | 408 # when client re-attached. ferr is unchanged because it should |
409 # be unbuffered no matter if it is a tty or not. | 409 # be unbuffered no matter if it is a tty or not. |
410 if fn == b'ferr': | 410 if fn == b'ferr': |
411 newfp = fp | 411 newfp = fp |
412 elif pycompat.ispy3: | |
413 # On Python 3, the standard library doesn't offer line-buffered | |
414 # binary streams, so wrap/unwrap it. | |
415 if fp.isatty(): | |
416 newfp = procutil.make_line_buffered(fp) | |
417 else: | |
418 newfp = procutil.unwrap_line_buffered(fp) | |
412 else: | 419 else: |
413 # make it line buffered explicitly because the default is | 420 # Python 2 uses the I/O streams provided by the C library, so |
414 # decided on first write(), where fout could be a pager. | 421 # make it line-buffered explicitly. Otherwise the default would |
422 # be decided on first write(), where fout could be a pager. | |
415 if fp.isatty(): | 423 if fp.isatty(): |
416 bufsize = 1 # line buffered | 424 bufsize = 1 # line buffered |
417 else: | 425 else: |
418 bufsize = -1 # system default | 426 bufsize = -1 # system default |
419 newfp = os.fdopen(fp.fileno(), mode, bufsize) | 427 newfp = os.fdopen(fp.fileno(), mode, bufsize) |
428 if newfp is not fp: | |
420 setattr(ui, fn, newfp) | 429 setattr(ui, fn, newfp) |
421 setattr(self, cn, newfp) | 430 setattr(self, cn, newfp) |
422 | 431 |
423 self._ioattached = True | 432 self._ioattached = True |
424 self.cresult.write(struct.pack(b'>i', len(clientfds))) | 433 self.cresult.write(struct.pack(b'>i', len(clientfds))) |
438 return | 447 return |
439 nullfd = os.open(os.devnull, os.O_WRONLY) | 448 nullfd = os.open(os.devnull, os.O_WRONLY) |
440 ui = self.ui | 449 ui = self.ui |
441 for (ch, fp, fd), (cn, fn, mode) in zip(self._oldios, _iochannels): | 450 for (ch, fp, fd), (cn, fn, mode) in zip(self._oldios, _iochannels): |
442 newfp = getattr(ui, fn) | 451 newfp = getattr(ui, fn) |
443 # close newfp while it's associated with client; otherwise it | 452 # On Python 2, newfp and fp may be separate file objects associated |
444 # would be closed when newfp is deleted | 453 # with the same fd, so we must close newfp while it's associated |
445 if newfp is not fp: | 454 # with the client. Otherwise the new associated fd would be closed |
455 # when newfp gets deleted. On Python 3, newfp is just a wrapper | |
456 # around fp even if newfp is not fp, so deleting newfp is safe. | |
457 if not (pycompat.ispy3 or newfp is fp): | |
446 newfp.close() | 458 newfp.close() |
447 # restore original fd: fp is open again | 459 # restore original fd: fp is open again |
448 try: | 460 try: |
449 if newfp is fp and 'w' in mode: | 461 if (pycompat.ispy3 or newfp is fp) and 'w' in mode: |
450 # Discard buffered data which couldn't be flushed because | 462 # Discard buffered data which couldn't be flushed because |
451 # of EPIPE. The data should belong to the current session | 463 # of EPIPE. The data should belong to the current session |
452 # and should never persist. | 464 # and should never persist. |
453 os.dup2(nullfd, fp.fileno()) | 465 os.dup2(nullfd, fp.fileno()) |
454 fp.flush() | 466 fp.flush() |