{"id":6434,"library":"pysignalr","title":"PySignalR Client","description":"pysignalr is a modern, reliable, and async-ready client for the SignalR protocol, designed to connect Python applications to SignalR hubs. It is currently at version 1.3.1 and maintains an active release cadence with regular updates and new feature additions, ensuring compatibility with the latest Python versions and SignalR protocol specifications.","status":"active","version":"1.3.1","language":"python","source_language":"en","source_url":"https://github.com/baking-bad/pysignalr","tags":["SignalR","websocket","async","client","real-time","asyncio"],"install":[{"cmd":"pip install pysignalr","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core dependency for WebSocket communication, frequently updated.","package":"websockets","optional":false},{"reason":"Used for faster JSON deserialization, automatically detected if installed.","package":"orjson","optional":true}],"imports":[{"symbol":"SignalRClient","correct":"from pysignalr.client import SignalRClient"},{"symbol":"CompletionMessage","correct":"from pysignalr.messages import CompletionMessage"}],"quickstart":{"code":"import asyncio\nfrom contextlib import suppress\nfrom typing import Any, Dict, List\nfrom pysignalr.client import SignalRClient\nfrom pysignalr.messages import CompletionMessage\nimport os\n\nasync def on_open() -> None:\n    print('Connected to the server')\n\nasync def on_close() -> None:\n    print('Disconnected from the server')\n\nasync def on_message(message: List[Dict[str, Any]]) -> None:\n    print(f'Received message: {message}')\n\nasync def on_client_result(message: list[dict[str, Any]]) -> str:\n    print(f'Received message requesting result: {message}')\n    return 'reply_from_client'\n\nasync def on_error(message: CompletionMessage) -> None:\n    print(f'Received error: {message.error}')\n\nasync def main() -> None:\n    # Replace with your SignalR hub URL\n    # For example, a public API like TzKT.io or your own local/remote hub\n    signalr_url = os.environ.get('SIGNALR_HUB_URL', 'https://api.tzkt.io/v1/ws')\n    access_token = os.environ.get('SIGNALR_ACCESS_TOKEN', '') # Optional: for authenticated hubs\n\n    client_args = {'url': signalr_url}\n    if access_token:\n        # For authenticated hubs, provide an access token factory\n        client_args['access_token_factory'] = lambda: access_token\n\n    client = SignalRClient(**client_args)\n    client.on_open(on_open)\n    client.on_close(on_close)\n    client.on_error(on_error)\n\n    # Register handlers for specific events from the server\n    client.on('operations', on_message) # Example: subscribing to 'operations' event\n    client.on('client_result', on_client_result) # Example: handling a server request for a client result\n\n    await asyncio.gather(\n        client.run(),\n        # Example: Sending a message to the server (e.g., to subscribe to a topic)\n        client.send('SubscribeToOperations', [{}]), \n    )\n\nif __name__ == '__main__':\n    with suppress(KeyboardInterrupt, asyncio.CancelledError):\n        asyncio.run(main())\n","lang":"python","description":"This quickstart demonstrates how to establish a connection to a SignalR hub, register event handlers for incoming messages, and send messages to the server. It includes examples for connection lifecycle events (open, close, error) and handling specific server events, including those requesting a client result."},"warnings":[{"fix":"Upgrade Python to version 3.10 or newer. If unable to upgrade, pin `pysignalr` to a version prior to 1.3.0 (e.g., `pysignalr<1.3.0`).","message":"Python 3.9 support was officially dropped in `pysignalr` version 1.3.0. Users on Python 3.9 or older must upgrade their Python environment to at least 3.10 to use versions 1.3.0 and newer.","severity":"breaking","affected_versions":">=1.3.0"},{"fix":"Upgrade to `pysignalr` version 1.3.1 or newer to ensure full protocol compliance and stability.","message":"Version 1.3.1 introduced several fixes for SignalR Hub Protocol spec compliance in JSON and MessagePack codecs (e.g., `streamIds`, `invocationId`, `ResultKind`, headers, varint framing). Older versions might have exhibited non-compliant behavior that could lead to subtle issues or unexpected interactions with some SignalR servers.","severity":"gotcha","affected_versions":"<1.3.1"},{"fix":"Upgrade to `pysignalr` version 1.1.0 or newer to benefit from improved reconnection stability.","message":"Prior to version 1.1.0, the reconnection logic in `pysignalr` was prone to issues. Applications relying on stable and automatic reconnections in the face of network interruptions might experience unreliability in older versions.","severity":"gotcha","affected_versions":"<1.1.0"},{"fix":"Ensure exact matching of method names and parameter types/counts between server invocations and client `on()` handlers. Enable client-side logging (`logging.DEBUG`) to diagnose unhandled messages or errors.","message":"SignalR itself can silently fail to invoke client methods if the method name or signature sent from the server does not exactly match a registered client-side handler. The server will not receive an error.","severity":"gotcha","affected_versions":"All versions (SignalR protocol behavior)"},{"fix":"Adopt the `access_token_factory` for dynamic token management, especially with expiring JWTs, to ensure continuous authentication without manual re-connection.","message":"The `access_token_factory` argument was added in 1.1.0, allowing dynamic token generation for authentication. If you were using older, less flexible authentication methods or hardcoding tokens, you might need to refactor your authentication logic when upgrading to leverage this feature or if your token needs refreshing.","severity":"gotcha","affected_versions":"<1.1.0"}],"env_vars":null,"search_vec":"'1.3.1':30 'activ':34 'addit':43 'applic':21 'async':10,57 'async-readi':9 'asyncio':62 'cadenc':36 'client':2,12,58 'compat':45 'connect':19 'current':27 'design':17 'ensur':44 'featur':42 'hub':24 'latest':48 'maintain':32 'modern':6 'new':41 'protocol':16,53 'pysignalr':1,3 'python':20,49 'readi':11 'real':60 'real-tim':59 'regular':38 'releas':35 'reliabl':7 'signalr':15,23,52,55 'specif':54 'time':61 'updat':39 'version':29,50 'websocket':56","created_at":"2026-04-15T05:37:08.753094+00:00","updated_at":"2026-04-16T19:50:11.479755+00:00","problems":[{"fix":"Ensure `await hub_connection.start()` completes successfully and implement robust error handling with reconnection logic to maintain an active connection.","cause":"The `HubConnection` object's internal WebSocket client is `None` because the connection failed to establish or was closed unexpectedly, leading to `recv()` being called on a non-existent object.","error":"AttributeError: 'NoneType' object has no attribute 'recv'"},{"fix":"Verify the SignalR hub URL (scheme, host, port) is absolutely correct and confirm that the SignalR server is running and network-accessible from the client's environment.","cause":"The pysignalr client failed to establish a basic network connection to the specified SignalR hub URL, typically due to an incorrect URL, an inaccessible host, or the server not running.","error":"aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host"},{"fix":"Ensure the method name provided to `hub_connection.invoke()` precisely matches an existing, accessible method on the SignalR hub server, including correct casing, and check server logs for execution errors.","cause":"This usually occurs when `hub_connection.invoke()` returns `None`, often because the specified server method does not exist or failed to execute, and the subsequent code attempts to call this `None` result.","error":"TypeError: 'NoneType' object is not callable"},{"fix":"Implement automatic reconnection logic for the `hub_connection` within your application and consult server logs to understand the reason for the server-initiated connection closure.","cause":"The WebSocket connection was actively closed by the SignalR server, which can happen due to inactivity, a server-side error, explicit disconnection, or adherence to the protocol.","error":"websockets.exceptions.ConnectionClosedOK"}],"ecosystem":"pypi","meta_description":null,"install_score":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":"1.3.2","cli_name":"","cli_version":null,"type":"library","homepage":null,"github":"https://github.com/baking-bad/pysignalr","docs":null,"changelog":null,"pypi":"https://pypi.org/project/pysignalr/","npm":null,"openapi_spec":null,"status_page":null,"smithery":null,"categories":["http-networking","communication"],"base_url":null,"auth_type":null,"provenance":{"verified_status":"passing","verified_at":"2026-06-28","last_verified":"2026-06-28","next_check":"2026-07-28","install_tag":null}}