""" AUTHOR: Khushal P Soonderji DATE: Create: Monday, 29th Sept., 2025 OBJECTIVE: To have a centralized Socket.IO app from where several namespaces can be registered. This is kind of like how you can have one Quart app and register several blueprints. REFERENCES: N/A DOWNLOADS: N/A """ # ***************************************************************************************************************** # ***** **** # *** IMPORT *** # ***** **** # ***************************************************************************************************************** # To make sibling directories accessible for imports: import sys sys.path.append(".") sys.path.append("..") # System-level activities: import os # To make HTTP calls: import httpx # To work with SocketIO: import socket import socketio # To maintain the app's state: from wsio_v2.app_state import AppState from wsio_v2.test.echo import EchoNamespace from wsio_v2.finstitutions.trading.tick_out import TicksNamespace # Debugging: from icecream import IceCreamDebugger # ***************************************************************************************************************** # ***** **** # *** MACROS / ONE-TIME INIT *** # ***** **** # ***************************************************************************************************************** # A custom class to maintain the app's state: app_state = AppState() # Debugging: app_state.printer = IceCreamDebugger(prefix = "WSIO | ", includeContext = True) app_state.no_context_printer = IceCreamDebugger(prefix = "WSIO | ", includeContext = False) # To make API calls: app_state.http_client = httpx.AsyncClient( limits = httpx.Limits( max_connections = 100, # ............ Maximum number of connections allowed in the pool. max_keepalive_connections = 50, # ... Maximum number of connections that can be kept alive. ), timeout = httpx.Timeout( pool = 120.0, # .... Time to wait for a free connection from the pool. connect = 2.5, # ... Time to wait for establishing a connection to the server. write = 10.0, # .... Time to wait for sending data. read = 9.9 # ....... Time to wait for receiving data. ) ) # General: app_state.SCRIPT_ID = None app_state.debug = True app_state.SERVER_HOSTNAME = str(socket.gethostname()) app_state.ALLOWED_ORIGINS = [] app_state.connected_clients = {} # Namespaces: app_state.NAMESPACE_DEFAULT = None app_state.NAMESPACE_ECHO = "/echo" app_state.NAMESPACE_TICKS = "/ticks" app_state.NAMESPACE_ORDER_UPDATES = "/order-updates" # Events: app_state.EVENT_CONNECT = "connect" app_state.EVENT_DISCONNECT = "disconnect" app_state.EVENT_ECHO = "echo" app_state.EVENT_TICKS = "ticks" app_state.EVENT_ORDER_UPDATE = "order_update" # ***************************************************************************************************************** # ***** **** # *** VARIABLES *** # ***** **** # ***************************************************************************************************************** # For SocketIO: sio = socketio.AsyncServer( cors_allowed_origins = "*", async_mode = "asgi" ) app = socketio.ASGIApp(sio) # Register the namespaces: sio.register_namespace(EchoNamespace(namespace = app_state.NAMESPACE_ECHO, app_state = app_state)) sio.register_namespace(TicksNamespace(namespace = app_state.NAMESPACE_TICKS, app_state = app_state)) # ***************************************************************************************************************** # ***** **** # *** FUNCTIONS *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** MAIN PROGRAM *** # ***** **** # ***************************************************************************************************************** if __name__ == "__main__": app_state.printer("Main.") # To get args from the terminal: import argparse # To run the ASGI: import uvicorn from multiprocessing import freeze_support # Get the config from the command-line: parser = argparse.ArgumentParser(description = f"SocketIO to serve live market data (and a general passthrough).") parser.add_argument( "-w", "--workers", type = int, help = "The no. of threads to spin up for this instance!", default = 2 ) parser.add_argument( "-a", "--host", type = str, help = "The host for the app. e.g.: '0.0.0.0' or '127.0.0.1'.", default = "0.0.0.0" ) parser.add_argument( "-p", "--port", type = int, help = "The port no. to bind the app to.", default = 8080 ) parser.add_argument( "-s", "--script-id", type = str, help = "The id of this script (will affect the loaded config)." ) parser.add_argument( "-d", "--debug", action = "store_true", help = "Whether, or not, you want to see debugging messages in the terminal.", default = False ) args = parser.parse_args() # Note down the config; os.environ["SCRIPT_ID"] = args.script_id os.environ["DEBUG"] = str(args.debug) # Startup message: app_state.printer.enable() if args.debug != "True": app_state.printer("Disabling debug print.") app_state.printer.disable() # Run the gateway: freeze_support() uvicorn.run( app = "app:app", workers = args.workers, host = args.host, port = args.port )