Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
bpo-41279: Add StreamReaderBufferedProtocol#21446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
9a65cfe6db642c71d6a90cdc14d89e81f1a3dc4d18File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -112,7 +112,7 @@ def factory(): | ||
| return await loop.create_unix_server(factory, path, **kwds) | ||
| class FlowControlMixin(protocols.Protocol): | ||
| class FlowControlMixin(protocols.BaseProtocol): | ||
| """Reusable flow control logic for StreamWriter.drain(). | ||
| This implements the protocol methods pause_writing(), | ||
| @@ -180,7 +180,7 @@ def _get_close_waiter(self, stream): | ||
| raise NotImplementedError | ||
| class StreamReaderProtocol(FlowControlMixin, protocols.Protocol): | ||
| class BaseStreamReaderProtocol(FlowControlMixin): | ||
| """Helper class to adapt between Protocol and StreamReader. | ||
| (This is a helper class instead of making StreamReader itself a | ||
| @@ -267,11 +267,6 @@ def connection_lost(self, exc): | ||
| self._stream_writer = None | ||
| self._transport = None | ||
| def data_received(self, data): | ||
| reader = self._stream_reader | ||
| if reader is not None: | ||
| reader.feed_data(data) | ||
| def eof_received(self): | ||
| reader = self._stream_reader | ||
| if reader is not None: | ||
| @@ -298,6 +293,30 @@ def __del__(self): | ||
| closed.exception() | ||
| class StreamReaderProtocol(BaseStreamReaderProtocol, protocols.Protocol): | ||
| def data_received(self, data): | ||
| reader = self._stream_reader | ||
| if reader is not None: | ||
| reader.feed_data(data) | ||
| class StreamReaderBufferedProtocol(BaseStreamReaderProtocol, protocols.BufferedProtocol): | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don't follow. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It isn't used right now, as there is no way to include this class without changing or adding to the API. Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please elaborate. I like this PR, it makes a value if the buffered protocol is used. But if this protocol is not used -- why we need the PR at all? I can imagine a little different approach: don't extract What do you think?
| ||
| def __init__(self, stream_reader, client_connected_cb=None, loop=None, | ||
| buffer_size=65536): | ||
| super().__init__(stream_reader, | ||
| client_connected_cb=client_connected_cb, | ||
| loop=loop) | ||
| self._buffer = memoryview(bytearray(buffer_size)) | ||
| def get_buffer(self, sizehint): | ||
| return self._buffer | ||
| def buffer_updated(self, nbytes): | ||
| reader = self._stream_reader | ||
| if reader is not None: | ||
| reader.feed_data(self._buffer[:nbytes]) | ||
| class StreamWriter: | ||
| """Wraps a Transport. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -20,6 +20,7 @@ | ||
| from . import events | ||
| from . import exceptions | ||
| from . import futures | ||
| from . import protocols | ||
| from . import selector_events | ||
| from . import tasks | ||
| from . import transports | ||
| @@ -480,9 +481,13 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None): | ||
| os.set_blocking(self._fileno, False) | ||
| self._loop.call_soon(self._protocol.connection_made, self) | ||
| # only start reading when connection_made() has been called | ||
| self._loop.call_soon(self._loop._add_reader, | ||
| self._fileno, self._read_ready) | ||
| if isinstance(protocol, protocols.BufferedProtocol): | ||
| self._read_ready = self._readinto_buffer_ready | ||
| else: | ||
| self._read_ready = self._read_buffer_ready | ||
| self._loop.call_soon(self._loop._add_reader, self._fileno, self._read_ready) | ||
| if waiter is not None: | ||
| # only wake up the waiter when connection_made() has been called | ||
| self._loop.call_soon(futures._set_result_unless_cancelled, | ||
| @@ -509,7 +514,37 @@ def __repr__(self): | ||
| info.append('closed') | ||
| return '<{}>'.format(' '.join(info)) | ||
| def _read_ready(self): | ||
| def _readinto_buffer_ready(self): | ||
| try: | ||
| buf = self._protocol.get_buffer(-1) | ||
| if not len(buf): | ||
| raise RuntimeError('get_buffer() returned an empty buffer') | ||
| except (SystemExit, KeyboardInterrupt): | ||
| raise | ||
| except BaseException as exc: | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Never mind, this is how we typically invoke callback methods. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 | ||
| self._fatal_error( | ||
| exc, 'Fatal error: protocol.get_buffer() call failed.') | ||
| return | ||
| nbytes = 0 | ||
| try: | ||
| nbytes = self._pipe.readinto(buf) | ||
| except (BlockingIOError, InterruptedError): | ||
| pass | ||
| except OSError as exc: | ||
| self._fatal_error(exc, 'Fatal read error on pipe transport') | ||
| else: | ||
| if nbytes: | ||
| self._protocol.buffer_updated(nbytes) | ||
| else: | ||
| if self._loop.get_debug(): | ||
| logger.info("%r was closed by peer", self) | ||
| self._closing = True | ||
| self._loop._remove_reader(self._fileno) | ||
| self._loop.call_soon(self._protocol.eof_received) | ||
| self._loop.call_soon(self._call_connection_lost, None) | ||
| def _read_buffer_ready(self): | ||
| try: | ||
| data = os.read(self._fileno, self.max_size) | ||
| except (BlockingIOError, InterruptedError): | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the corresponding changes in selector_events.py. Does it handle these use cases already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it has the following bit of code: