Skip to content

Commit 6ee621e

Browse files
committed
get basic tests working
1 parent 5f8788c commit 6ee621e

29 files changed

+369
-335
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
run: pip install -r requirements/nox-deps.txt
2929
- name: Run Tests
3030
env: { "CI": "true" }
31-
run: nox -s test_python_suite -- --headless --maxfail=3
31+
run: nox -s test_python_suite -- --maxfail=3
3232
test-python-environments:
3333
runs-on: ${{ matrix.os }}
3434
strategy:
@@ -50,7 +50,7 @@ jobs:
5050
run: pip install -r requirements/nox-deps.txt
5151
- name: Run Tests
5252
env: { "CI": "true" }
53-
run: nox -s test_python --stop-on-first-error -- --headless --maxfail=3 --no-cov
53+
run: nox -s test_python --stop-on-first-error -- --maxfail=3 --no-cov
5454
test-docs:
5555
runs-on: ubuntu-latest
5656
steps:

requirements/pkg-extras.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ flask<2.0
1515
markupsafe<2.1
1616
flask-cors
1717
flask-sockets
18+
a2wsgi
1819

1920
# tornado
2021
tornado
2122

2223
# extra=testing
23-
selenium
24+
playwright
2425

2526
# extra=matplotlib
2627
matplotlib

src/idom/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from .core.serve import Stop
1717
from .core.vdom import vdom
1818
from .sample import run_sample_app
19-
from .server.any import run
19+
from .server.utils import run
2020
from .utils import Ref, html_to_vdom
2121
from .widgets import hotswap, multiview
2222

src/idom/sample.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
from __future__ import annotations
22

3-
import webbrowser
4-
from typing import Any
5-
63
from . import html
74
from .core.component import component
85
from .core.types import VdomDict
9-
from .server.any import run
6+
from .server.utils import run
107

118

129
@component

src/idom/server/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
from .any import all_implementations

src/idom/server/_asgi.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
from asyncio import FIRST_EXCEPTION, CancelledError
5+
6+
from asgiref.typing import ASGIApplication
7+
from uvicorn.config import Config as UvicornConfig
8+
from uvicorn.server import Server as UvicornServer
9+
10+
11+
async def serve_development_asgi(
12+
app: ASGIApplication,
13+
host: str,
14+
port: int,
15+
started: asyncio.Event,
16+
) -> None:
17+
"""Run a development server for starlette"""
18+
server = UvicornServer(UvicornConfig(app, host=host, port=port, loop="asyncio"))
19+
20+
async def check_if_started():
21+
while not server.started:
22+
await asyncio.sleep(0.2)
23+
started.set()
24+
25+
coros = [server.serve(), check_if_started()]
26+
_, pending = await asyncio.wait(
27+
list(map(asyncio.create_task, coros)), return_when=FIRST_EXCEPTION
28+
)
29+
30+
for task in pending:
31+
task.cancel()
32+
33+
try:
34+
await asyncio.gather(*list(pending))
35+
except CancelledError:
36+
pass

src/idom/server/any.py

Lines changed: 0 additions & 113 deletions
This file was deleted.

src/idom/server/default.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
from typing import Any
5+
6+
from idom.types import RootComponentConstructor
7+
8+
from .utils import default_implementation
9+
10+
11+
def configure(app: Any, component: RootComponentConstructor) -> None:
12+
"""Configure the given app instance to display the given component"""
13+
return default_implementation().configure(app, component)
14+
15+
16+
def create_development_app() -> Any:
17+
"""Create an application instance for development purposes"""
18+
return default_implementation().create_development_app()
19+
20+
21+
async def serve_development_app(
22+
app: Any, host: str, port: int, started: asyncio.Event
23+
) -> None:
24+
"""Run an application using a development server"""
25+
return await default_implementation().serve_development_app(
26+
app, host, port, started
27+
)

src/idom/server/flask.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
from flask import Blueprint, Flask, redirect, request, send_from_directory, url_for
1414
from flask_cors import CORS
1515
from flask_sockets import Sockets
16+
from gevent import pywsgi
17+
from geventwebsocket.handler import WebSocketHandler
1618
from geventwebsocket.websocket import WebSocket
1719
from typing_extensions import TypedDict
18-
from werkzeug.serving import ThreadedWSGIServer
1920

2021
import idom
21-
from idom.config import IDOM_DEBUG_MODE, IDOM_WEB_MODULES_DIR
22+
from idom.config import IDOM_WEB_MODULES_DIR
2223
from idom.core.layout import LayoutEvent, LayoutUpdate
2324
from idom.core.serve import serve_json_patch
2425
from idom.core.types import ComponentType, RootComponentConstructor
@@ -58,28 +59,34 @@ async def serve_development_app(
5859
) -> None:
5960
"""Run an application using a development server"""
6061
loop = asyncio.get_event_loop()
61-
62-
@app.before_first_request
63-
def set_started():
64-
loop.call_soon_threadsafe(started.set)
65-
66-
server = ThreadedWSGIServer(host, port, app)
67-
6862
stopped = asyncio.Event()
6963

64+
server: pywsgi.WSGIServer
65+
7066
def run_server():
67+
nonlocal server
68+
server = pywsgi.WSGIServer(
69+
(host, port),
70+
app,
71+
handler_class=WebSocketHandler,
72+
)
73+
server.start()
74+
loop.call_soon_threadsafe(started.set)
7175
try:
7276
server.serve_forever()
7377
finally:
7478
loop.call_soon_threadsafe(stopped.set)
7579

7680
thread = Thread(target=run_server, daemon=True)
81+
thread.start()
82+
83+
await started.wait()
7784

7885
try:
7986
await stopped.wait()
8087
finally:
8188
# we may have exitted because this task was cancelled
82-
server.shutdown()
89+
server.stop(3)
8390
# the thread should eventually join
8491
thread.join(timeout=3)
8592
# just double check it happened

src/idom/server/sanic.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
import asyncio
44
import json
55
import logging
6+
import os
7+
import socket
68
from typing import Any, Dict, Tuple, Union
79

810
from mypy_extensions import TypedDict
911
from sanic import Blueprint, Sanic, request, response
1012
from sanic_cors import CORS
11-
from websockets import WebSocketCommonProtocol
13+
from websockets.legacy.protocol import WebSocketCommonProtocol
1214

1315
from idom.config import IDOM_WEB_MODULES_DIR
1416
from idom.core.layout import Layout, LayoutEvent
@@ -48,12 +50,17 @@ async def serve_development_app(
4850
app: Sanic, host: str, port: int, started: asyncio.Event
4951
) -> None:
5052
"""Run a development server for :mod:`sanic`"""
51-
52-
@app.listener("after_server_start")
53-
async def after_started():
53+
try:
54+
server = await app.create_server(
55+
host, port, return_asyncio_server=True, debug=True
56+
)
57+
await server.startup()
58+
await server.start_serving()
5459
started.set()
55-
56-
await app.create_server(host, port, debug=True)
60+
await server.serve_forever()
61+
except KeyboardInterrupt:
62+
app.shutdown_tasks(3)
63+
app.stop()
5764

5865

5966
class Options(TypedDict, total=False):

src/idom/server/starlette.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
)
2727
from idom.core.types import RootComponentConstructor
2828

29+
from ._asgi import serve_development_asgi
2930
from .utils import CLIENT_BUILD_DIR
3031

3132

@@ -63,25 +64,7 @@ async def serve_development_app(
6364
started: asyncio.Event,
6465
) -> None:
6566
"""Run a development server for starlette"""
66-
server = UvicornServer(UvicornConfig(app, host=host, port=port, loop="asyncio"))
67-
68-
async def check_if_started():
69-
while not server.started:
70-
await asyncio.sleep(0.2)
71-
started.set()
72-
73-
_, pending = await asyncio.wait(
74-
[server.serve(), check_if_started()],
75-
return_when=FIRST_EXCEPTION,
76-
)
77-
78-
for task in pending:
79-
task.cancel()
80-
81-
try:
82-
await asyncio.gather(*list(pending))
83-
except CancelledError:
84-
pass
67+
await serve_development_asgi(app, host, port, started)
8568

8669

8770
class Options(TypedDict, total=False):

0 commit comments

Comments
 (0)