Skip to content
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

Add close_code and close_reason to new implementations. #1543

Merged
merged 1 commit into from
Nov 11, 2024
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
7 changes: 7 additions & 0 deletions docs/reference/asyncio/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,10 @@ Using a connection
.. autoattribute:: response

.. autoproperty:: subprotocol

The following attributes are available after the closing handshake,
once the WebSocket connection is closed:

.. autoproperty:: close_code

.. autoproperty:: close_reason
7 changes: 7 additions & 0 deletions docs/reference/asyncio/common.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,10 @@ Both sides (new :mod:`asyncio`)
.. autoattribute:: response

.. autoproperty:: subprotocol

The following attributes are available after the closing handshake,
once the WebSocket connection is closed:

.. autoproperty:: close_code

.. autoproperty:: close_reason
7 changes: 7 additions & 0 deletions docs/reference/asyncio/server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ Using a connection

.. autoproperty:: subprotocol

The following attributes are available after the closing handshake,
once the WebSocket connection is closed:

.. autoproperty:: close_code

.. autoproperty:: close_reason

Broadcast
---------

Expand Down
9 changes: 9 additions & 0 deletions docs/reference/sync/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Using a connection

.. autoproperty:: remote_address

.. autoproperty:: state

The following attributes are available after the opening handshake,
once the WebSocket connection is open:

Expand All @@ -47,3 +49,10 @@ Using a connection
.. autoattribute:: response

.. autoproperty:: subprotocol

The following attributes are available after the closing handshake,
once the WebSocket connection is closed:

.. autoproperty:: close_code

.. autoproperty:: close_reason
9 changes: 9 additions & 0 deletions docs/reference/sync/common.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Both sides (:mod:`threading`)

.. autoproperty:: remote_address

.. autoproperty:: state

The following attributes are available after the opening handshake,
once the WebSocket connection is open:

Expand All @@ -39,3 +41,10 @@ Both sides (:mod:`threading`)
.. autoattribute:: response

.. autoproperty:: subprotocol

The following attributes are available after the closing handshake,
once the WebSocket connection is closed:

.. autoproperty:: close_code

.. autoproperty:: close_reason
9 changes: 9 additions & 0 deletions docs/reference/sync/server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Using a connection

.. autoproperty:: remote_address

.. autoproperty:: state

The following attributes are available after the opening handshake,
once the WebSocket connection is open:

Expand All @@ -61,6 +63,13 @@ Using a connection

.. autoproperty:: subprotocol

The following attributes are available after the closing handshake,
once the WebSocket connection is closed:

.. autoproperty:: close_code

.. autoproperty:: close_reason

HTTP Basic Authentication
-------------------------

Expand Down
24 changes: 24 additions & 0 deletions src/websockets/asyncio/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,30 @@ def subprotocol(self) -> Subprotocol | None:
"""
return self.protocol.subprotocol

@property
def close_code(self) -> int | None:
"""
State of the WebSocket connection, defined in :rfc:`6455`.

This attribute is provided for completeness. Typical applications
shouldn't check its value. Instead, they should inspect attributes
of :exc:`~websockets.exceptions.ConnectionClosed` exceptions.

"""
return self.protocol.close_code

@property
def close_reason(self) -> str | None:
"""
State of the WebSocket connection, defined in :rfc:`6455`.

This attribute is provided for completeness. Typical applications
shouldn't check its value. Instead, they should inspect attributes
of :exc:`~websockets.exceptions.ConnectionClosed` exceptions.

"""
return self.protocol.close_reason

# Public methods

async def __aenter__(self) -> Connection:
Expand Down
21 changes: 14 additions & 7 deletions src/websockets/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,12 @@ def state(self) -> State:
"""
State of the WebSocket connection.

Defined in 4.1, 4.2, 7.1.3, and 7.1.4 of :rfc:`6455`.
Defined in 4.1_, 4.2_, 7.1.3_, and 7.1.4_ of :rfc:`6455`.

.. _4.1: https://datatracker.ietf.org/doc/html/rfc6455#section-4.1
.. _4.2: https://datatracker.ietf.org/doc/html/rfc6455#section-4.2
.. _7.1.3: https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.3
.. _7.1.4: https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4

"""
return self._state
Expand All @@ -173,10 +178,11 @@ def state(self, state: State) -> None:
@property
def close_code(self) -> int | None:
"""
`WebSocket close code`_.
WebSocket close code received from the remote endpoint.

Defined in 7.1.5_ of :rfc:`6455`.

.. _WebSocket close code:
https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5
.. _7.1.5: https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5

:obj:`None` if the connection isn't closed yet.

Expand All @@ -191,10 +197,11 @@ def close_code(self) -> int | None:
@property
def close_reason(self) -> str | None:
"""
`WebSocket close reason`_.
WebSocket close reason received from the remote endpoint.

Defined in 7.1.6_ of :rfc:`6455`.

.. _WebSocket close reason:
https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6
.. _7.1.6: https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6

:obj:`None` if the connection isn't closed yet.

Expand Down
37 changes: 37 additions & 0 deletions src/websockets/sync/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,19 @@ def remote_address(self) -> Any:
"""
return self.socket.getpeername()

@property
def state(self) -> State:
"""
State of the WebSocket connection, defined in :rfc:`6455`.

This attribute is provided for completeness. Typical applications
shouldn't check its value. Instead, they should call :meth:`~recv` or
:meth:`send` and handle :exc:`~websockets.exceptions.ConnectionClosed`
exceptions.

"""
return self.protocol.state

@property
def subprotocol(self) -> Subprotocol | None:
"""
Expand All @@ -150,6 +163,30 @@ def subprotocol(self) -> Subprotocol | None:
"""
return self.protocol.subprotocol

@property
def close_code(self) -> int | None:
"""
State of the WebSocket connection, defined in :rfc:`6455`.

This attribute is provided for completeness. Typical applications
shouldn't check its value. Instead, they should inspect attributes
of :exc:`~websockets.exceptions.ConnectionClosed` exceptions.

"""
return self.protocol.close_code

@property
def close_reason(self) -> str | None:
"""
State of the WebSocket connection, defined in :rfc:`6455`.

This attribute is provided for completeness. Typical applications
shouldn't check its value. Instead, they should inspect attributes
of :exc:`~websockets.exceptions.ConnectionClosed` exceptions.

"""
return self.protocol.close_reason

# Public methods

def __enter__(self) -> Connection:
Expand Down
10 changes: 9 additions & 1 deletion tests/asyncio/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1139,7 +1139,7 @@ async def test_remote_address(self, get_extra_info):

async def test_state(self):
"""Connection has a state attribute."""
self.assertEqual(self.connection.state, State.OPEN)
self.assertIs(self.connection.state, State.OPEN)

async def test_request(self):
"""Connection has a request attribute."""
Expand All @@ -1153,6 +1153,14 @@ async def test_subprotocol(self):
"""Connection has a subprotocol attribute."""
self.assertIsNone(self.connection.subprotocol)

async def test_close_code(self):
"""Connection has a close_code attribute."""
self.assertIsNone(self.connection.close_code)

async def test_close_reason(self):
"""Connection has a close_reason attribute."""
self.assertIsNone(self.connection.close_reason)

# Test reporting of network errors.

async def test_writing_in_data_received_fails(self):
Expand Down
14 changes: 13 additions & 1 deletion tests/sync/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
ConnectionClosedOK,
)
from websockets.frames import CloseCode, Frame, Opcode
from websockets.protocol import CLIENT, SERVER, Protocol
from websockets.protocol import CLIENT, SERVER, Protocol, State
from websockets.sync.connection import *

from ..protocol import RecordingProtocol
Expand Down Expand Up @@ -808,6 +808,10 @@ def test_remote_address(self, getpeername):
self.assertEqual(self.connection.remote_address, ("peer", 1234))
getpeername.assert_called_with()

def test_state(self):
"""Connection has a state attribute."""
self.assertIs(self.connection.state, State.OPEN)

def test_request(self):
"""Connection has a request attribute."""
self.assertIsNone(self.connection.request)
Expand All @@ -820,6 +824,14 @@ def test_subprotocol(self):
"""Connection has a subprotocol attribute."""
self.assertIsNone(self.connection.subprotocol)

def test_close_code(self):
"""Connection has a close_code attribute."""
self.assertIsNone(self.connection.close_code)

def test_close_reason(self):
"""Connection has a close_reason attribute."""
self.assertIsNone(self.connection.close_reason)

# Test reporting of network errors.

@unittest.skipUnless(sys.platform == "darwin", "works only on BSD")
Expand Down