Path: blob/main/singlestoredb/apps/_python_udfs.py
469 views
import asyncio1import os2import typing34from ..functions.ext.asgi import Application5from ._config import AppConfig6from ._connection_info import UdfConnectionInfo7from ._process import kill_process_by_port89if typing.TYPE_CHECKING:10from ._uvicorn_util import AwaitableUvicornServer1112# Keep track of currently running server13_running_server: 'typing.Optional[AwaitableUvicornServer]' = None1415# Maximum number of UDFs allowed16MAX_UDFS_LIMIT = 10171819async def run_udf_app(20log_level: str = 'error',21kill_existing_app_server: bool = True,22) -> UdfConnectionInfo:23global _running_server24from ._uvicorn_util import AwaitableUvicornServer2526try:27import uvicorn28except ImportError:29raise ImportError('package uvicorn is required to run python udfs')3031app_config = AppConfig.from_env()3233if kill_existing_app_server:34# Shutdown the server gracefully if it was started by us.35# Since the uvicorn server doesn't start a new subprocess36# killing the process would result in kernel dying.37if _running_server is not None:38await _running_server.shutdown()39_running_server = None4041# Kill if any other process is occupying the port42kill_process_by_port(app_config.listen_port)4344base_url = generate_base_url(app_config)4546udf_suffix = ''47if app_config.running_interactively:48udf_suffix = '_test'49app = Application(50url=base_url,51app_mode='managed',52name_suffix=udf_suffix,53log_level=log_level,54)5556if not app.endpoints:57raise ValueError('You must define at least one function.')58if len(app.endpoints) > MAX_UDFS_LIMIT:59raise ValueError(60f'You can only define a maximum of {MAX_UDFS_LIMIT} functions.',61)6263config = uvicorn.Config(64app,65host='0.0.0.0',66port=app_config.listen_port,67log_config=app.get_uvicorn_log_config(),68)6970# Register the functions only if the app is running interactively.71if app_config.running_interactively:72app.register_functions(replace=True)7374_running_server = AwaitableUvicornServer(config)75asyncio.create_task(_running_server.serve())76await _running_server.wait_for_startup()7778print(f'Python UDF registered at {base_url}')7980return UdfConnectionInfo(base_url, app.get_function_info())818283def generate_base_url(app_config: AppConfig) -> str:84if not app_config.is_gateway_enabled:85raise RuntimeError('Python UDFs are not available if Nova Gateway is not enabled')8687if not app_config.running_interactively:88return app_config.base_url8990# generate python udf endpoint for interactive notebooks91gateway_url = os.environ.get('SINGLESTOREDB_NOVA_GATEWAY_ENDPOINT')92if app_config.is_local_dev:93gateway_url = os.environ.get('SINGLESTOREDB_NOVA_GATEWAY_DEV_ENDPOINT')94if gateway_url is None:95raise RuntimeError(96'Missing SINGLESTOREDB_NOVA_GATEWAY_DEV_ENDPOINT environment variable.',97)9899return f'{gateway_url}/pythonudfs/{app_config.notebook_server_id}/interactive/'100101102