Skip to content

Commit

Permalink
feat: fallback to a synchronous main loop on WASM platforms
Browse files Browse the repository at this point in the history
This should be all we need to add basic support for both pyodide and
wasi WASM runtimes
  • Loading branch information
alcarney committed Nov 3, 2024
1 parent 7332a0b commit 410ff35
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 3 deletions.
2 changes: 2 additions & 0 deletions pygls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@

IS_WIN = os.name == "nt"
IS_PYODIDE = "pyodide" in sys.modules
IS_WASI = sys.platform == "wasi"
IS_WASM = IS_PYODIDE or IS_WASI

pygls = "pygls"
44 changes: 41 additions & 3 deletions pygls/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@

import cattrs

from pygls import IS_WASM
from pygls.exceptions import JsonRpcException, PyglsError
from pygls.io_ import StdinAsyncReader, StdoutWriter, run_async, run_websocket
from pygls.io_ import StdinAsyncReader, StdoutWriter, run, run_async, run_websocket
from pygls.protocol import JsonRPCProtocol

if typing.TYPE_CHECKING:
Expand Down Expand Up @@ -105,8 +106,18 @@ def report_server_error(self, error: Exception, source: ServerErrors):
def start_io(
self, stdin: Optional[BinaryIO] = None, stdout: Optional[BinaryIO] = None
):
"""Starts IO server."""
logger.info("Starting IO server")
"""Starts an IO server."""

if IS_WASM:
self._start_io_sync(stdin, stdout)
else:
self._start_io_async(stdin, stdout)

def _start_io_async(
self, stdin: Optional[BinaryIO] = None, stdout: Optional[BinaryIO] = None
):
"""Starts an asynchronous IO server."""
logger.info("Starting async IO server")

self._stop_event = Event()
reader = StdinAsyncReader(stdin or sys.stdin.buffer, self.thread_pool)
Expand All @@ -130,6 +141,33 @@ def start_io(
finally:
self.shutdown()

def _start_io_sync(
self, stdin: Optional[BinaryIO] = None, stdout: Optional[BinaryIO] = None
):
"""Starts an synchronous IO server."""
logger.info("Starting sync IO server")

self._stop_event = Event()
writer = StdoutWriter(stdout or sys.stdout.buffer)
self.protocol.set_writer(writer)

try:
asyncio.run(
run(
stop_event=self._stop_event,
reader=stdin or sys.stdin.buffer,
protocol=self.protocol,
logger=logger,
error_handler=self.report_server_error,
)
)
except BrokenPipeError:
logger.error("Connection to the client is lost! Shutting down the server.")
except (KeyboardInterrupt, SystemExit):
pass
finally:
self.shutdown()

def start_tcp(self, host: str, port: int) -> None:
"""Starts TCP server."""
logger.info("Starting TCP server on %s:%s", host, port)
Expand Down

0 comments on commit 410ff35

Please sign in to comment.