def test_prepate(): data = "test data" event_id = "1" event = "2" retry = 3 result = Sse._prepare(data, event_id=event_id, event=event, retry=retry) assert (result == "id: {}\r\nevent: {}\r\ndata: {}\r\nretry: {}\r\n\r\n".format( event_id, event, data, retry).encode()) with pytest.raises(TypeError): Sse._prepare(data, retry="3")
async def test_listeners(): sanic_app = Sanic() sse = Sse() sse.init_app(sanic_app) sanic_app.listeners["after_server_start"][0](sanic_app, asyncio.get_event_loop()) assert sse._ping_task is not None await sanic_app.listeners["before_server_stop"][0]( sanic_app, asyncio.get_event_loop()) assert sse._ping_task.cancelled()
def test_create(): sanic_app = Sanic() sse = Sse(sanic_app) assert sse._url == Sse._DEFAULT_URL assert sse._ping_task is None assert sse._ping_interval == Sse._DEFAULT_PING_INTERVAL
async def test_streaming_fn(): sanic_app = Sanic() sse = Sse(sanic_app) class Request: # pylint: disable=too-few-public-methods args = {"channel_id": "1"} counter = 0 class Response: # pylint: disable=too-few-public-methods @staticmethod async def write(data): nonlocal counter counter += 1 assert data == b"data: test\r\n\r\n" str_response = await sanic_app.router.routes_all["/sse"].handler(Request()) fut = asyncio.ensure_future(str_response.streaming_fn(Response())) await sanic_app.sse_send("test") # pylint: disable=no-member await sse._pubsub.close() fut.cancel() with contextlib.suppress(asyncio.CancelledError): await fut assert counter == 1
async def test_before_request_callback_bad(): sanic_app = Sanic() def test_func1(_): assert True with pytest.raises(TypeError): Sse(sanic_app, before_request_func=test_func1) async def test_func2(_, __): assert True with pytest.raises(ValueError): Sse(sanic_app, before_request_func=test_func2) test_func3 = "" with pytest.raises(TypeError): Sse(sanic_app, before_request_func=test_func3)
async def test_before_request_callback(): sanic_app = Sanic() async def test_func(request): assert "channel_id" in request.args Sse(sanic_app, before_request_func=test_func) class Request: # pylint: disable=too-few-public-methods args = {"channel_id": "1"} await sanic_app.router.routes_all["/sse"].handler(Request())
async def test_register_two_subscribers(): sanic_app = Sanic() Sse(sanic_app) class Request: # pylint: disable=too-few-public-methods args = {"channel_id": "1"} await sanic_app.router.routes_all["/sse"].handler(Request()) with pytest.raises(InvalidUsage): await sanic_app.router.routes_all["/sse"].handler(Request())
async def test_ping(): sanic_app = Sanic() sse = Sse(sanic_app, ping_interval=0.1) channel_id = sse._pubsub.register() sanic_app.listeners["after_server_start"][0](sanic_app, asyncio.get_event_loop()) await asyncio.sleep(0) data = await sse._pubsub.get(channel_id) assert data == b": ping\r\n\r\n" await sanic_app.listeners["before_server_stop"][0]( sanic_app, asyncio.get_event_loop())
async def test_send_nowait(): sanic_app = Sanic() sse = Sse(sanic_app) channel_id = sse._pubsub.register() data = "test data" event_id = "1" event = "2" retry = 3 sanic_app.sse_send( # pylint: disable=no-member data, event_id=event_id, event=event, retry=retry) await asyncio.sleep(0) result = await sse._pubsub.get(channel_id) assert (result == "id: {}\r\nevent: {}\r\ndata: {}\r\nretry: {}\r\n\r\n".format( event_id, event, data, retry).encode())
async def test_transport_closed(): sanic_app = Sanic() sse = Sse(sanic_app) class Request: # pylint: disable=too-few-public-methods args = {"channel_id": "1"} class Response: # pylint: disable=too-few-public-methods @staticmethod def write(data): raise Exception str_response = await sanic_app.router.routes_all["/sse"].handler(Request()) fut = asyncio.ensure_future(str_response.streaming_fn(Response())) await sanic_app.sse_send("test") # pylint: disable=no-member fut.cancel() with contextlib.suppress(Exception): await fut assert len(sse._pubsub._channels[None]) == 0
SOCKETS_BP = Blueprint("sockets", url_prefix="") start_timer = None async def before_sse_request(request): if request.headers.get("Auth", "") != "some_token": pass #abort(HTTPStatus.UNAUTHORIZED, "Bad auth token") #sanic_app = Sanic() # The default sse url is /sse but you can set it via init argument url. Sse(app, url="/events", before_request_func=before_sse_request) # or you can use init_app method @SOCKETS_BP.post('send') async def send_event(request): # if channel_id is None than event will be send to all subscribers channel_id = request.json.get("channel_id") logger.success(f"Message {request.json} on {channel_id}") # optional arguments: event_id - str, event - str, retry - int # data should always be str # also you can use sse_send_nowait for send event without waiting try: await request.app.sse_send(json_dumps(request.json), channel_id=channel_id)
from sanic import Sanic from sanic_session import Session, InMemorySessionInterface from jinja2 import Environment, PackageLoader from sanic_sse import Sse app = Sanic('OPhO') Sse(app, url="/events") from app.listeners import listeners from app.root import root from app.opho import opho from app.config import Config def create_app(config=Config): app.update_config(config) app.ctx.env = Environment(loader=PackageLoader('app', 'templates'), enable_async=True) Session(app, interface=InMemorySessionInterface()) app.static('/static', './app/static') app.static('/favicon.ico', './app/static/files/favicon.ico?', name='favicon') app.blueprint(root) app.blueprint(opho) app.blueprint(listeners)