Skip to content

Commit b76968b

Browse files
author
Yury Selivanov
committed
Merge 3.5 (asyncio, issue #26909)
2 parents 62b81c3 + 4c5bf3b commit b76968b

3 files changed

Lines changed: 37 additions & 42 deletions

File tree

Lib/asyncio/unix_events.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
435435
self._pipe = pipe
436436
self._fileno = pipe.fileno()
437437
self._protocol = protocol
438-
self._buffer = []
438+
self._buffer = bytearray()
439439
self._conn_lost = 0
440440
self._closing = False # Set when close() or write_eof() called.
441441

@@ -451,7 +451,6 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
451451
"pipes, sockets and character devices")
452452

453453
_set_nonblocking(self._fileno)
454-
455454
self._loop.call_soon(self._protocol.connection_made, self)
456455

457456
# On AIX, the reader trick (to be notified when the read end of the
@@ -493,7 +492,7 @@ def __repr__(self):
493492
return '<%s>' % ' '.join(info)
494493

495494
def get_write_buffer_size(self):
496-
return sum(len(data) for data in self._buffer)
495+
return len(self._buffer)
497496

498497
def _read_ready(self):
499498
# Pipe was closed by peer.
@@ -531,39 +530,37 @@ def write(self, data):
531530
if n == len(data):
532531
return
533532
elif n > 0:
534-
data = data[n:]
533+
data = memoryview(data)[n:]
535534
self._loop.add_writer(self._fileno, self._write_ready)
536535

537-
self._buffer.append(data)
536+
self._buffer += data
538537
self._maybe_pause_protocol()
539538

540539
def _write_ready(self):
541-
data = b''.join(self._buffer)
542-
assert data, 'Data should not be empty'
540+
assert self._buffer, 'Data should not be empty'
543541

544-
self._buffer.clear()
545542
try:
546-
n = os.write(self._fileno, data)
543+
n = os.write(self._fileno, self._buffer)
547544
except (BlockingIOError, InterruptedError):
548-
self._buffer.append(data)
545+
pass
549546
except Exception as exc:
547+
self._buffer.clear()
550548
self._conn_lost += 1
551549
# Remove writer here, _fatal_error() doesn't it
552550
# because _buffer is empty.
553551
self._loop.remove_writer(self._fileno)
554552
self._fatal_error(exc, 'Fatal write error on pipe transport')
555553
else:
556-
if n == len(data):
554+
if n == len(self._buffer):
555+
self._buffer.clear()
557556
self._loop.remove_writer(self._fileno)
558557
self._maybe_resume_protocol() # May append to buffer.
559-
if not self._buffer and self._closing:
558+
if self._closing:
560559
self._loop.remove_reader(self._fileno)
561560
self._call_connection_lost(None)
562561
return
563562
elif n > 0:
564-
data = data[n:]
565-
566-
self._buffer.append(data) # Try again later.
563+
del self._buffer[:n]
567564

568565
def can_write_eof(self):
569566
return True

Lib/test/test_asyncio/test_unix_events.py

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -518,43 +518,42 @@ def test_write(self, m_write):
518518
tr.write(b'data')
519519
m_write.assert_called_with(5, b'data')
520520
self.assertFalse(self.loop.writers)
521-
self.assertEqual([], tr._buffer)
521+
self.assertEqual(bytearray(), tr._buffer)
522522

523523
@mock.patch('os.write')
524524
def test_write_no_data(self, m_write):
525525
tr = self.write_pipe_transport()
526526
tr.write(b'')
527527
self.assertFalse(m_write.called)
528528
self.assertFalse(self.loop.writers)
529-
self.assertEqual([], tr._buffer)
529+
self.assertEqual(bytearray(b''), tr._buffer)
530530

531531
@mock.patch('os.write')
532532
def test_write_partial(self, m_write):
533533
tr = self.write_pipe_transport()
534534
m_write.return_value = 2
535535
tr.write(b'data')
536-
m_write.assert_called_with(5, b'data')
537536
self.loop.assert_writer(5, tr._write_ready)
538-
self.assertEqual([b'ta'], tr._buffer)
537+
self.assertEqual(bytearray(b'ta'), tr._buffer)
539538

540539
@mock.patch('os.write')
541540
def test_write_buffer(self, m_write):
542541
tr = self.write_pipe_transport()
543542
self.loop.add_writer(5, tr._write_ready)
544-
tr._buffer = [b'previous']
543+
tr._buffer = bytearray(b'previous')
545544
tr.write(b'data')
546545
self.assertFalse(m_write.called)
547546
self.loop.assert_writer(5, tr._write_ready)
548-
self.assertEqual([b'previous', b'data'], tr._buffer)
547+
self.assertEqual(bytearray(b'previousdata'), tr._buffer)
549548

550549
@mock.patch('os.write')
551550
def test_write_again(self, m_write):
552551
tr = self.write_pipe_transport()
553552
m_write.side_effect = BlockingIOError()
554553
tr.write(b'data')
555-
m_write.assert_called_with(5, b'data')
554+
m_write.assert_called_with(5, bytearray(b'data'))
556555
self.loop.assert_writer(5, tr._write_ready)
557-
self.assertEqual([b'data'], tr._buffer)
556+
self.assertEqual(bytearray(b'data'), tr._buffer)
558557

559558
@mock.patch('asyncio.unix_events.logger')
560559
@mock.patch('os.write')
@@ -566,7 +565,7 @@ def test_write_err(self, m_write, m_log):
566565
tr.write(b'data')
567566
m_write.assert_called_with(5, b'data')
568567
self.assertFalse(self.loop.writers)
569-
self.assertEqual([], tr._buffer)
568+
self.assertEqual(bytearray(), tr._buffer)
570569
tr._fatal_error.assert_called_with(
571570
err,
572571
'Fatal write error on pipe transport')
@@ -606,58 +605,55 @@ def test__read_ready(self):
606605
def test__write_ready(self, m_write):
607606
tr = self.write_pipe_transport()
608607
self.loop.add_writer(5, tr._write_ready)
609-
tr._buffer = [b'da', b'ta']
608+
tr._buffer = bytearray(b'data')
610609
m_write.return_value = 4
611610
tr._write_ready()
612-
m_write.assert_called_with(5, b'data')
613611
self.assertFalse(self.loop.writers)
614-
self.assertEqual([], tr._buffer)
612+
self.assertEqual(bytearray(), tr._buffer)
615613

616614
@mock.patch('os.write')
617615
def test__write_ready_partial(self, m_write):
618616
tr = self.write_pipe_transport()
619617
self.loop.add_writer(5, tr._write_ready)
620-
tr._buffer = [b'da', b'ta']
618+
tr._buffer = bytearray(b'data')
621619
m_write.return_value = 3
622620
tr._write_ready()
623-
m_write.assert_called_with(5, b'data')
624621
self.loop.assert_writer(5, tr._write_ready)
625-
self.assertEqual([b'a'], tr._buffer)
622+
self.assertEqual(bytearray(b'a'), tr._buffer)
626623

627624
@mock.patch('os.write')
628625
def test__write_ready_again(self, m_write):
629626
tr = self.write_pipe_transport()
630627
self.loop.add_writer(5, tr._write_ready)
631-
tr._buffer = [b'da', b'ta']
628+
tr._buffer = bytearray(b'data')
632629
m_write.side_effect = BlockingIOError()
633630
tr._write_ready()
634-
m_write.assert_called_with(5, b'data')
631+
m_write.assert_called_with(5, bytearray(b'data'))
635632
self.loop.assert_writer(5, tr._write_ready)
636-
self.assertEqual([b'data'], tr._buffer)
633+
self.assertEqual(bytearray(b'data'), tr._buffer)
637634

638635
@mock.patch('os.write')
639636
def test__write_ready_empty(self, m_write):
640637
tr = self.write_pipe_transport()
641638
self.loop.add_writer(5, tr._write_ready)
642-
tr._buffer = [b'da', b'ta']
639+
tr._buffer = bytearray(b'data')
643640
m_write.return_value = 0
644641
tr._write_ready()
645-
m_write.assert_called_with(5, b'data')
642+
m_write.assert_called_with(5, bytearray(b'data'))
646643
self.loop.assert_writer(5, tr._write_ready)
647-
self.assertEqual([b'data'], tr._buffer)
644+
self.assertEqual(bytearray(b'data'), tr._buffer)
648645

649646
@mock.patch('asyncio.log.logger.error')
650647
@mock.patch('os.write')
651648
def test__write_ready_err(self, m_write, m_logexc):
652649
tr = self.write_pipe_transport()
653650
self.loop.add_writer(5, tr._write_ready)
654-
tr._buffer = [b'da', b'ta']
651+
tr._buffer = bytearray(b'data')
655652
m_write.side_effect = err = OSError()
656653
tr._write_ready()
657-
m_write.assert_called_with(5, b'data')
658654
self.assertFalse(self.loop.writers)
659655
self.assertFalse(self.loop.readers)
660-
self.assertEqual([], tr._buffer)
656+
self.assertEqual(bytearray(), tr._buffer)
661657
self.assertTrue(tr.is_closing())
662658
m_logexc.assert_called_with(
663659
test_utils.MockPattern(
@@ -673,13 +669,12 @@ def test__write_ready_closing(self, m_write):
673669
tr = self.write_pipe_transport()
674670
self.loop.add_writer(5, tr._write_ready)
675671
tr._closing = True
676-
tr._buffer = [b'da', b'ta']
672+
tr._buffer = bytearray(b'data')
677673
m_write.return_value = 4
678674
tr._write_ready()
679-
m_write.assert_called_with(5, b'data')
680675
self.assertFalse(self.loop.writers)
681676
self.assertFalse(self.loop.readers)
682-
self.assertEqual([], tr._buffer)
677+
self.assertEqual(bytearray(), tr._buffer)
683678
self.protocol.connection_lost.assert_called_with(None)
684679
self.pipe.close.assert_called_with()
685680

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,9 @@ Library
427427
- Issue #26654: Inspect functools.partial in asyncio.Handle.__repr__.
428428
Patch by iceboy.
429429

430+
- Issue #26909: Fix slow pipes IO in asyncio.
431+
Patch by INADA Naoki.
432+
430433
IDLE
431434
----
432435

0 commit comments

Comments
 (0)