22
33import asyncio
44import logging
5- from contextlib import AsyncExitStack , suppress
5+ from collections .abc import Coroutine
6+ from contextlib import AsyncExitStack
7+ from threading import Thread
68from types import TracebackType
79from typing import Any , Callable
810from urllib .parse import urlencode , urlunparse
911
10- from reactpy .asgi import default as default_server
11- from reactpy .asgi .utils import find_available_port
12+ import uvicorn
13+
14+ from reactpy .asgi .standalone import ReactPy
1215from reactpy .config import REACTPY_TESTS_DEFAULT_TIMEOUT
1316from reactpy .core .component import component
1417from reactpy .core .hooks import use_callback , use_effect , use_state
1720 capture_reactpy_logs ,
1821 list_logged_exceptions ,
1922)
20- from reactpy .types import BackendType , ComponentConstructor
23+ from reactpy .testing .utils import find_available_port
24+ from reactpy .types import ComponentConstructor
2125from reactpy .utils import Ref
2226
2327
@@ -39,11 +43,9 @@ class BackendFixture:
3943
4044 def __init__ (
4145 self ,
46+ app : Callable [..., Coroutine ] | None = None ,
4247 host : str = "127.0.0.1" ,
4348 port : int | None = None ,
44- app : Any | None = None ,
45- implementation : BackendType [Any ] | None = None ,
46- options : Any | None = None ,
4749 timeout : float | None = None ,
4850 ) -> None :
4951 self .host = host
@@ -52,14 +54,12 @@ def __init__(
5254 self .timeout = (
5355 REACTPY_TESTS_DEFAULT_TIMEOUT .current if timeout is None else timeout
5456 )
55-
56- if app is not None and implementation is None :
57- msg = "If an application instance its corresponding server implementation must be provided too."
58- raise ValueError (msg )
59-
60- self ._app = app
61- self .implementation = implementation or default_server
62- self ._options = options
57+ self ._app = app or ReactPy (self ._root_component )
58+ self .webserver = uvicorn .Server (
59+ uvicorn .Config (
60+ app = self ._app , host = self .host , port = self .port , loop = "asyncio"
61+ )
62+ )
6363
6464 @property
6565 def log_records (self ) -> list [logging .LogRecord ]:
@@ -109,30 +109,7 @@ def list_logged_exceptions(
109109 async def __aenter__ (self ) -> BackendFixture :
110110 self ._exit_stack = AsyncExitStack ()
111111 self ._records = self ._exit_stack .enter_context (capture_reactpy_logs ())
112-
113- app = self ._app or self .implementation .create_development_app ()
114- self .implementation .configure (app , self ._root_component , self ._options )
115-
116- started = asyncio .Event ()
117- server_future = asyncio .create_task (
118- self .implementation .serve_development_app (
119- app , self .host , self .port , started
120- )
121- )
122-
123- async def stop_server () -> None :
124- server_future .cancel ()
125- with suppress (asyncio .CancelledError ):
126- await asyncio .wait_for (server_future , timeout = self .timeout )
127-
128- self ._exit_stack .push_async_callback (stop_server )
129-
130- try :
131- await asyncio .wait_for (started .wait (), timeout = self .timeout )
132- except Exception : # nocov
133- # see if we can await the future for a more helpful error
134- await asyncio .wait_for (server_future , timeout = self .timeout )
135- raise
112+ Thread (target = self .webserver .run , daemon = True ).start ()
136113
137114 return self
138115
@@ -151,6 +128,8 @@ async def __aexit__(
151128 msg = "Unexpected logged exception"
152129 raise LogAssertionError (msg ) from logged_errors [0 ]
153130
131+ await asyncio .wait_for (self .webserver .shutdown (), timeout = 5 )
132+
154133
155134_MountFunc = Callable [["Callable[[], Any] | None" ], None ]
156135
0 commit comments