From 4441984a42f1c3e4ff9af7477acb2149c0f41e16 Mon Sep 17 00:00:00 2001 From: Wei Ouyang Date: Sun, 3 Mar 2024 22:46:30 +0100 Subject: [PATCH] Upgrade lifespan api (#599) * Upgrade lifespan api * bump imjoy-rpc 0.5.48 * post1 * Add requirements --- hypha/VERSION | 2 +- hypha/built-in/echo-service.html | 2 +- hypha/built-in/imjoy-plugin-parser.html | 2 +- hypha/built-in/playground.html | 2 +- hypha/built-in/test.html | 2 +- hypha/core/__init__.py | 4 ++-- hypha/core/store.py | 23 +++++++++++++++++------ hypha/core/workspace.py | 15 +++++++++++---- hypha/server.py | 23 ++++++++++++----------- hypha/templates/web-python-plugin.html | 2 +- hypha/templates/web-worker-plugin.html | 2 +- hypha/templates/window-plugin.html | 2 +- requirements.txt | 2 +- setup.py | 2 +- 14 files changed, 52 insertions(+), 33 deletions(-) diff --git a/hypha/VERSION b/hypha/VERSION index 446f8ffb..652778b0 100644 --- a/hypha/VERSION +++ b/hypha/VERSION @@ -1,3 +1,3 @@ { - "version": "0.15.46" + "version": "0.15.47" } diff --git a/hypha/built-in/echo-service.html b/hypha/built-in/echo-service.html index 83a0ab55..c8a5b362 100644 --- a/hypha/built-in/echo-service.html +++ b/hypha/built-in/echo-service.html @@ -6,7 +6,7 @@ ImJoy Plugin Template - + diff --git a/hypha/built-in/imjoy-plugin-parser.html b/hypha/built-in/imjoy-plugin-parser.html index 3e1f9c09..9e5a0c85 100644 --- a/hypha/built-in/imjoy-plugin-parser.html +++ b/hypha/built-in/imjoy-plugin-parser.html @@ -6,7 +6,7 @@ ImJoy Plugin Template - + diff --git a/hypha/built-in/playground.html b/hypha/built-in/playground.html index 5490ba38..1f9c067f 100644 --- a/hypha/built-in/playground.html +++ b/hypha/built-in/playground.html @@ -7,7 +7,7 @@ - + + diff --git a/hypha/core/__init__.py b/hypha/core/__init__.py index 5a48ce90..edd8ed77 100644 --- a/hypha/core/__init__.py +++ b/hypha/core/__init__.py @@ -70,7 +70,7 @@ class ServiceInfo(BaseModel): class Config: """Set the config for pydantic.""" - extra='allow' + extra = "allow" def is_singleton(self): """Check if the service is singleton.""" @@ -137,7 +137,7 @@ class RDF(BaseModel): class Config: """Set the config for pydantic.""" - extra='allow' + extra = "allow" class ApplicationInfo(RDF): diff --git a/hypha/core/store.py b/hypha/core/store.py index adfc3780..203432c4 100644 --- a/hypha/core/store.py +++ b/hypha/core/store.py @@ -140,7 +140,9 @@ async def init(self, reset_redis, startup_functions=None): await self.cleanup_disconnected_clients() for service in self._public_services: try: - await self._public_workspace_interface.register_service(service.model_dump()) + await self._public_workspace_interface.register_service( + service.model_dump() + ) except Exception: # pylint: disable=broad-except logger.exception("Failed to register public service: %s", service) raise @@ -187,7 +189,9 @@ async def add_disconnected_client(self, client_info: ClientInfo): await self._redis.hset( "clients:disconnected", f"{client_info.workspace}/{client_info.id}", - json.dumps({"client": client_info.model_dump_json(), "timestamp": time.time()}), + json.dumps( + {"client": client_info.model_dump_json(), "timestamp": time.time()} + ), ) async def remove_disconnected_client( @@ -239,14 +243,17 @@ async def get_user_workspace(self, user_id: str): workspace_info = await self._redis.hget("workspaces", user_id) if workspace_info is None: return None - workspace_info = WorkspaceInfo.model_validate(json.loads(workspace_info.decode())) + workspace_info = WorkspaceInfo.model_validate( + json.loads(workspace_info.decode()) + ) return workspace_info async def get_all_users(self): """Get all users.""" users = await self._redis.hgetall("users") return [ - UserInfo.model_validate(json.loads(user.decode())) for user in users.values() + UserInfo.model_validate(json.loads(user.decode())) + for user in users.values() ] async def get_all_workspace(self): @@ -278,14 +285,18 @@ async def register_workspace(self, workspace: dict, overwrite=False): raise KeyError( f"Client does not exist: {workspace.name}/{client_id}" ) - client_info = ClientInfo.model_validate(json.loads(client_info.decode())) + client_info = ClientInfo.model_validate( + json.loads(client_info.decode()) + ) await self._redis.srem( f"user:{client_info.user_info.id}:clients", client_info.id ) # assert ret >= 1, f"Client not found in user({client_info.user_info.id})'s clients list: {client_info.id}" await self._redis.hdel(f"{workspace.name}:clients", client_id) await self._redis.delete(f"{workspace}:clients") - await self._redis.hset("workspaces", workspace.name, workspace.model_dump_json()) + await self._redis.hset( + "workspaces", workspace.name, workspace.model_dump_json() + ) await self.get_workspace_manager(workspace.name, setup=True) self._event_bus.emit("workspace_registered", workspace.model_dump()) diff --git a/hypha/core/workspace.py b/hypha/core/workspace.py index dac19b10..5084de87 100644 --- a/hypha/core/workspace.py +++ b/hypha/core/workspace.py @@ -166,7 +166,9 @@ async def create_workspace( if not overwrite and await self._redis.hexists("workspaces", workspace.name): raise Exception(f"Workspace {workspace.name} already exists.") - await self._redis.hset("workspaces", workspace.name, workspace.model_dump_json()) + await self._redis.hset( + "workspaces", workspace.name, workspace.model_dump_json() + ) # Clear the workspace await self.remove_clients(workspace.name) workspace_info = await self.get_workspace_info(workspace.name) @@ -630,7 +632,9 @@ async def get_workspace_info(self, workspace: str = None) -> WorkspaceInfo: workspace_info = await self._redis.hget("workspaces", workspace) if workspace_info is None: raise KeyError(f"Workspace not found: {workspace}") - workspace_info = WorkspaceInfo.model_validate(json.loads(workspace_info.decode())) + workspace_info = WorkspaceInfo.model_validate( + json.loads(workspace_info.decode()) + ) return workspace_info async def _get_workspace_info_dict( @@ -819,7 +823,8 @@ async def _get_all_workspace(self): """Get all workspaces.""" workspaces = await self._redis.hgetall("workspaces") return [ - WorkspaceInfo.model_validate(json.loads(v.decode())) for v in workspaces.values() + WorkspaceInfo.model_validate(json.loads(v.decode())) + for v in workspaces.values() ] async def check_permission( @@ -938,7 +943,9 @@ async def _update_workspace(self, config: dict, context=None): if _id not in workspace.owners: workspace.owners.append(_id) workspace.owners = [o.strip() for o in workspace.owners if o.strip()] - await self._redis.hset("workspaces", workspace.name, workspace.model_dump_json()) + await self._redis.hset( + "workspaces", workspace.name, workspace.model_dump_json() + ) self._event_bus.emit("workspace_changed", workspace.model_dump()) async def delete_if_empty(self): diff --git a/hypha/server.py b/hypha/server.py index c1d3d290..9561cbc2 100644 --- a/hypha/server.py +++ b/hypha/server.py @@ -20,6 +20,7 @@ from hypha.triton import TritonProxy from hypha.utils import GZipMiddleware, GzipRoute, PatchedCORSMiddleware from hypha.websocket import WebsocketServer +from contextlib import asynccontextmanager try: # For pyodide, we need to patch http @@ -152,17 +153,6 @@ async def liveness(req: Request) -> JSONResponse: return JSONResponse({"status": "DOWN"}, status_code=503) - @app.on_event("startup") - async def startup_event(): - # Here we can register all the startup functions - args.startup_functions = args.startup_functions or [] - args.startup_functions.append("hypha.core.auth:register_login_service") - await store.init(args.reset_redis, startup_functions=args.startup_functions) - - @app.on_event("shutdown") - def shutdown_event(): - store.get_event_bus().emit("shutdown", target="local") - def mount_static_files(app, new_route, directory, name="static"): # Get top level route paths @@ -200,8 +190,19 @@ def create_application(args): else: args.allow_origins = env.get("ALLOW_ORIGINS", "*").split(",") + @asynccontextmanager + async def lifespan(app: FastAPI): + # Here we can register all the startup functions + args.startup_functions = args.startup_functions or [] + args.startup_functions.append("hypha.core.auth:register_login_service") + await store.init(args.reset_redis, startup_functions=args.startup_functions) + yield + # Emit the shutdown event + store.get_event_bus().emit("shutdown", target="local") + application = FastAPI( title="Hypha", + lifespan=lifespan, docs_url="/api-docs", redoc_url="/api-redoc", description=( diff --git a/hypha/templates/web-python-plugin.html b/hypha/templates/web-python-plugin.html index af48d5aa..ee87422a 100644 --- a/hypha/templates/web-python-plugin.html +++ b/hypha/templates/web-python-plugin.html @@ -93,7 +93,7 @@ async def run(): try: - await micropip.install(["imjoy-rpc==0.5.44", {% for req in requirements %}"{{req}}", {% endfor %}]) + await micropip.install(["imjoy-rpc==0.5.48.post1", {% for req in requirements %}"{{req}}", {% endfor %}]) js.__resolve() except Exception as e: js.__reject(traceback.format_exc()) diff --git a/hypha/templates/web-worker-plugin.html b/hypha/templates/web-worker-plugin.html index 03b91654..73df5a27 100644 --- a/hypha/templates/web-worker-plugin.html +++ b/hypha/templates/web-worker-plugin.html @@ -15,7 +15,7 @@ self.onmessage = function(e) { const config = e.data -importScripts("https://cdn.jsdelivr.net/npm/imjoy-rpc@0.5.44/dist/hypha-rpc-websocket.min.js") +importScripts("https://cdn.jsdelivr.net/npm/imjoy-rpc@0.5.48/dist/hypha-rpc-websocket.min.js") hyphaWebsocketClient.connectToServer(config).then(async (api)=>{ await hyphaWebsocketClient.loadRequirements([{% for req in requirements %}"{{req}}", {% endfor %}]) diff --git a/hypha/templates/window-plugin.html b/hypha/templates/window-plugin.html index 030a9a8e..62a30447 100644 --- a/hypha/templates/window-plugin.html +++ b/hypha/templates/window-plugin.html @@ -6,7 +6,7 @@ ImJoy Plugin (window) - + diff --git a/requirements.txt b/requirements.txt index 0cd8f140..0ab01396 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ aioboto3==11.2.0 aiofiles==23.2.1 base58==2.1.1 fastapi==0.104.1 -imjoy-rpc==0.5.44 +imjoy-rpc==0.5.48.post1 jinja2==3.1.2 lxml==4.9.3 msgpack==1.0.5 diff --git a/setup.py b/setup.py index 12f06ccd..6569ae79 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ REQUIREMENTS = [ "aiofiles", "fastapi>=0.70.0", - "imjoy-rpc>=0.5.44", + "imjoy-rpc>=0.5.48.post1", "msgpack>=1.0.2", "numpy", "pydantic[email]>=2.6.1",