Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Doc/library/asyncio-queue.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ Queue
Return an item if one is immediately available, else raise
:exc:`QueueEmpty`.

Raises :exc:`QueueShutDown` if the queue has been shut down and is empty.

.. method:: join()
:async:

Expand All @@ -96,6 +98,8 @@ Queue

If no free slot is immediately available, raise :exc:`QueueFull`.

Raises :exc:`QueueShutDown` if the queue has been shut down.

.. method:: qsize()

Return the number of items in the queue.
Expand Down Expand Up @@ -188,8 +192,9 @@ Exceptions

.. exception:: QueueShutDown

Exception raised when :meth:`~Queue.put` or :meth:`~Queue.get` is
called on a queue which has been shut down.
Exception raised when :meth:`~Queue.put`, :meth:`~Queue.put_nowait`,
:meth:`~Queue.get` or :meth:`~Queue.get_nowait` is called
on a queue which has been shut down.

.. versionadded:: 3.13

Expand Down
11 changes: 7 additions & 4 deletions Doc/tutorial/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -532,10 +532,13 @@ This example introduces several new features.
and ``!=`` (not equal to).

* The *body* of the loop is *indented*: indentation is Python's way of grouping
statements. At the interactive prompt, you have to type a tab or space(s) for
each indented line. In practice you will prepare more complicated input
for Python with a text editor; all decent text editors have an auto-indent
facility. When a compound statement is entered interactively, it must be
statements. At the interactive prompt, the default REPL automatically
indents continuation lines after compound statement headers like ``if`` or
``while``. In the basic REPL (invoked with :envvar:`PYTHON_BASIC_REPL`)
or in older Python versions, you have to type a tab or space(s) for each
indented line manually. In practice you will prepare more complicated
input for Python with a text editor; all decent text editors have an
auto-indent facility. When a compound statement is entered interactively, it must be
followed by a blank line to indicate completion (since the parser cannot
guess when you have typed the last line). Note that each line within a basic
block must be indented by the same amount.
Expand Down
16 changes: 4 additions & 12 deletions Lib/_pyrepl/base_eventqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ def empty(self) -> bool:
"""
return not self.events

def flush_buf(self) -> bytearray:
"""
Flushes the buffer and returns its contents.
"""
old = self.buf
self.buf = bytearray()
return old

def insert(self, event: Event) -> None:
"""
Inserts an event into the queue.
Expand All @@ -87,7 +79,7 @@ def push(self, char: int | bytes) -> None:
if isinstance(k, dict):
self.keymap = k
else:
self.insert(Event('key', k, bytes(self.flush_buf())))
self.insert(Event('key', k, self.buf.take_bytes())) # type: ignore[attr-defined]
self.keymap = self.compiled_keymap

elif self.buf and self.buf[0] == 27: # escape
Expand All @@ -97,14 +89,14 @@ def push(self, char: int | bytes) -> None:
trace('unrecognized escape sequence, propagating...')
self.keymap = self.compiled_keymap
self.insert(Event('key', '\033', b'\033'))
for _c in self.flush_buf()[1:]:
for _c in self.buf.take_bytes()[1:]: # type: ignore[attr-defined]
self.push(_c)

else:
try:
decoded = bytes(self.buf).decode(self.encoding)
decoded = self.buf.decode(self.encoding)
except UnicodeError:
return
else:
self.insert(Event('key', decoded, bytes(self.flush_buf())))
self.insert(Event('key', decoded, self.buf.take_bytes())) # type: ignore[attr-defined]
self.keymap = self.compiled_keymap
7 changes: 7 additions & 0 deletions Lib/asyncio/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ def _stream_reader(self):
def _replace_transport(self, transport):
self._transport = transport
self._over_ssl = transport.get_extra_info('sslcontext') is not None
reader = self._stream_reader
if reader is not None:
reader._replace_transport(transport)

def connection_made(self, transport):
if self._reject_connection:
Expand Down Expand Up @@ -473,6 +476,10 @@ def set_transport(self, transport):
assert self._transport is None, 'Transport already set'
self._transport = transport

def _replace_transport(self, transport):
assert self._transport is not None, 'Transport not set'
self._transport = transport

def _maybe_resume_transport(self):
if self._paused and len(self._buffer) <= self._limit:
self._paused = False
Expand Down
28 changes: 28 additions & 0 deletions Lib/test/test_asyncio/test_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,34 @@ async def run_test():
self.loop.run_until_complete(run_test())
self.assertEqual(messages, [])

def test_streamwriter_start_tls_updates_reader_transport(self):
reader = asyncio.StreamReader(loop=self.loop)
protocol = asyncio.StreamReaderProtocol(reader, loop=self.loop)
old_transport = mock.Mock()
old_transport.get_extra_info.return_value = None
old_transport.is_closing.return_value = False
protocol.connection_made(old_transport)

writer = asyncio.StreamWriter(old_transport, protocol, reader, self.loop)

ssl_context = mock.sentinel.ssl_context
new_transport = mock.Mock()
new_transport.get_extra_info.return_value = ssl_context
self.loop.start_tls = mock.AsyncMock(return_value=new_transport)

self.loop.run_until_complete(writer.start_tls(ssl_context))

self.loop.start_tls.assert_awaited_once_with(
old_transport, protocol, ssl_context,
server_side=False, server_hostname=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
)
self.assertIs(writer.transport, new_transport)
self.assertIs(protocol._transport, new_transport)
self.assertIs(reader._transport, new_transport)
self.assertTrue(protocol._over_ssl)

def test_streamreader_constructor_without_loop(self):
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
asyncio.StreamReader()
Expand Down
8 changes: 1 addition & 7 deletions Lib/test/test_pyrepl/test_eventqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ def test_empty(self):
eq.insert(Event("key", "a", b"a"))
self.assertFalse(eq.empty())

def test_flush_buf(self):
eq = self.make_eventqueue()
eq.buf.extend(b"test")
self.assertEqual(eq.flush_buf(), b"test")
self.assertEqual(eq.buf, bytearray())

def test_insert(self):
eq = self.make_eventqueue()
event = Event("key", "a", b"a")
Expand Down Expand Up @@ -93,7 +87,7 @@ def test_push_with_keymap_in_keymap_and_escape(self, mock_keymap):
eq.push(b"a")
mock_keymap.compile_keymap.assert_called()
self.assertTrue(eq.empty())
eq.flush_buf()
eq.buf.resize(0)
eq.push(b"\033")
self.assertEqual(eq.events[0].evt, "key")
self.assertEqual(eq.events[0].data, "\033")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix ``asyncio.StreamWriter.start_tls()`` to keep the linked
``StreamReader`` transport in sync with the upgraded transport.
Loading