Skip to content

Add trio implementation #1628

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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
("py:meth", "protocol.WebSocketCommonProtocol.connection_lost"),
("py:meth", "protocol.WebSocketCommonProtocol.read_message"),
("py:meth", "protocol.WebSocketCommonProtocol.write_frame"),
# Caused by https://github.com/sphinx-doc/sphinx/issues/13838
("py:class", "ssl_module.SSLContext"),
]

# Add any Sphinx extension module names here, as strings. They can be
Expand Down Expand Up @@ -85,6 +87,7 @@
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"sesame": ("https://django-sesame.readthedocs.io/en/stable/", None),
"trio": ("https://trio.readthedocs.io/en/stable/", None),
"werkzeug": ("https://werkzeug.palletsprojects.com/en/stable/", None),
}

Expand Down
9 changes: 9 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ Here's an echo server and corresponding client.

.. literalinclude:: ../example/sync/echo.py

.. tab:: trio

.. literalinclude:: ../example/trio/echo.py

.. tab:: asyncio
:new-set:

Expand All @@ -79,6 +83,11 @@ Here's an echo server and corresponding client.

.. literalinclude:: ../example/sync/hello.py

.. tab:: trio

.. literalinclude:: ../example/trio/hello.py


Don't worry about the opening and closing handshakes, pings and pongs, or any
other behavior described in the WebSocket specification. websockets takes care
of this under the hood so you can focus on your application!
Expand Down
8 changes: 8 additions & 0 deletions docs/project/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ Backwards-incompatible changes
New features
............

.. admonition:: websockets 16.0 introduces a :mod:`trio` implementation.
:class: important

It is an alternative to the :mod:`asyncio` implementation.

See :func:`websockets.trio.client.connect` and
:func:`websockets.trio.server.serve` for details.

* Validated compatibility with Python 3.14.

Improvements
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/asyncio/server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Running a server

.. automethod:: serve_forever

.. autoattribute:: sockets
.. autoproperty:: sockets

Using a connection
------------------
Expand Down
257 changes: 129 additions & 128 deletions docs/reference/features.rst

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions docs/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ This alternative implementation can be a good choice for clients.
sync/server
sync/client

:mod:`trio`
------------

This is another option for servers that handle many clients concurrently.

.. toctree::
:titlesonly:

trio/server
trio/client

`Sans-I/O`_
-----------

Expand Down
63 changes: 63 additions & 0 deletions docs/reference/trio/client.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Client (:mod:`trio`)
=======================

.. automodule:: websockets.trio.client

Opening a connection
--------------------

.. autofunction:: connect
:async:

.. autofunction:: process_exception

Using a connection
------------------

.. autoclass:: ClientConnection

.. automethod:: __aiter__

.. automethod:: recv

.. automethod:: recv_streaming

.. automethod:: send

.. automethod:: aclose

.. automethod:: wait_closed

.. automethod:: ping

.. automethod:: pong

WebSocket connection objects also provide these attributes:

.. autoattribute:: id

.. autoattribute:: logger

.. autoproperty:: local_address

.. autoproperty:: remote_address

.. autoattribute:: latency

.. autoproperty:: state

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

.. autoattribute:: request

.. 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
54 changes: 54 additions & 0 deletions docs/reference/trio/common.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
:orphan:

Both sides (:mod:`trio`)
===========================

.. automodule:: websockets.trio.connection

.. autoclass:: Connection

.. automethod:: __aiter__

.. automethod:: recv

.. automethod:: recv_streaming

.. automethod:: send

.. automethod:: aclose

.. automethod:: wait_closed

.. automethod:: ping

.. automethod:: pong

WebSocket connection objects also provide these attributes:

.. autoattribute:: id

.. autoattribute:: logger

.. autoproperty:: local_address

.. autoproperty:: remote_address

.. autoattribute:: latency

.. autoproperty:: state

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

.. autoattribute:: request

.. 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
84 changes: 84 additions & 0 deletions docs/reference/trio/server.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
Server (:mod:`trio`)
=======================

.. automodule:: websockets.trio.server

Creating a server
-----------------

.. autofunction:: serve
:async:

.. currentmodule:: websockets.trio.server

Running a server
----------------

.. autoclass:: Server

.. autoattribute:: connections

.. automethod:: aclose

.. autoattribute:: listeners

Using a connection
------------------

.. autoclass:: ServerConnection

.. automethod:: __aiter__

.. automethod:: recv

.. automethod:: recv_streaming

.. automethod:: send

.. automethod:: aclose

.. automethod:: wait_closed

.. automethod:: ping

.. automethod:: pong

.. automethod:: respond

WebSocket connection objects also provide these attributes:

.. autoattribute:: id

.. autoattribute:: logger

.. autoproperty:: local_address

.. autoproperty:: remote_address

.. autoattribute:: latency

.. autoproperty:: state

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

.. autoattribute:: request

.. 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

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

websockets supports HTTP Basic Authentication according to
:rfc:`7235` and :rfc:`7617`.

.. autofunction:: basic_auth
4 changes: 2 additions & 2 deletions docs/topics/keepalive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ measured during the last exchange of Ping and Pong frames::
Alternatively, you can measure the latency at any time by calling
:attr:`~asyncio.connection.Connection.ping` and awaiting its result::

pong_waiter = await websocket.ping()
latency = await pong_waiter
pong_received = await websocket.ping()
latency = await pong_received

Latency between a client and a server may increase for two reasons:

Expand Down
1 change: 0 additions & 1 deletion example/asyncio/client.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""Client example using the asyncio API."""

import asyncio

from websockets.asyncio.client import connect


Expand Down
Empty file modified example/asyncio/server.py
100644 → 100755
Empty file.
Empty file modified example/sync/client.py
100644 → 100755
Empty file.
Empty file modified example/sync/server.py
100644 → 100755
Empty file.
21 changes: 21 additions & 0 deletions example/trio/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python

"""Client example using the trio API."""

import trio
from websockets.trio.client import connect


async def hello():
async with connect("ws://localhost:8765") as websocket:
name = input("What's your name? ")

await websocket.send(name)
print(f">>> {name}")

greeting = await websocket.recv()
print(f"<<< {greeting}")


if __name__ == "__main__":
trio.run(hello)
15 changes: 15 additions & 0 deletions example/trio/echo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python

"""Echo server using the trio API."""

import trio
from websockets.trio.server import serve


async def echo(websocket):
async for message in websocket:
await websocket.send(message)


if __name__ == "__main__":
trio.run(serve, echo, 8765)
17 changes: 17 additions & 0 deletions example/trio/hello.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python

"""Client using the trio API."""

import trio
from websockets.trio.client import connect


async def hello():
async with connect("ws://localhost:8765") as websocket:
await websocket.send("Hello world!")
message = await websocket.recv()
print(message)


if __name__ == "__main__":
trio.run(hello)
20 changes: 20 additions & 0 deletions example/trio/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env python

"""Server example using the trio API."""

import trio
from websockets.trio.server import serve


async def hello(websocket):
name = await websocket.recv()
print(f"<<< {name}")

greeting = f"Hello {name}!"

await websocket.send(greeting)
print(f">>> {greeting}")


if __name__ == "__main__":
trio.run(serve, hello, 8765)
Loading
Loading