{"id":26483364,"url":"https://github.com/mandrewcito/signalrcore","last_synced_at":"2025-05-15T17:06:16.678Z","repository":{"id":40396024,"uuid":"172356279","full_name":"mandrewcito/signalrcore","owner":"mandrewcito","description":"SignalR Core python client","archived":false,"fork":false,"pushed_at":"2024-10-18T21:23:26.000Z","size":494,"stargazers_count":122,"open_issues_count":34,"forks_count":58,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-11T23:05:07.691Z","etag":null,"topics":["aspnet-core","python","signalr","signalr-client","signalr-core"],"latest_commit_sha":null,"homepage":"https://mandrewcito.github.io/signalrcore/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mandrewcito.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["mandrewcito"],"patreon":"mandrewcito","ko_fi":"mandrewcito"}},"created_at":"2019-02-24T15:44:14.000Z","updated_at":"2025-04-11T08:08:00.000Z","dependencies_parsed_at":"2024-06-18T16:45:55.524Z","dependency_job_id":"ed42d642-8c2f-456d-96b4-09955c1da950","html_url":"https://github.com/mandrewcito/signalrcore","commit_stats":{"total_commits":139,"total_committers":17,"mean_commits":8.176470588235293,"dds":0.5323741007194245,"last_synced_commit":"bc4ade21705469fb390581afe32ebd754a7e1173"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandrewcito%2Fsignalrcore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandrewcito%2Fsignalrcore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandrewcito%2Fsignalrcore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandrewcito%2Fsignalrcore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mandrewcito","download_url":"https://codeload.github.com/mandrewcito/signalrcore/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253678963,"owners_count":21946325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnet-core","python","signalr","signalr-client","signalr-core"],"created_at":"2025-03-20T04:54:48.132Z","updated_at":"2025-05-15T17:06:11.667Z","avatar_url":"https://github.com/mandrewcito.png","language":"Python","funding_links":["https://github.com/sponsors/mandrewcito","https://patreon.com/mandrewcito","https://ko-fi.com/mandrewcito","https://www.paypal.me/mandrewcito/1"],"categories":[],"sub_categories":[],"readme":"# SignalR core client\r\n[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg?logo=paypal\u0026style=flat-square)](https://www.paypal.me/mandrewcito/1)\r\n![Pypi](https://img.shields.io/pypi/v/signalrcore.svg)\r\n[![Downloads](https://pepy.tech/badge/signalrcore/month)](https://pepy.tech/project/signalrcore/month)\r\n[![Downloads](https://pepy.tech/badge/signalrcore)](https://pepy.tech/project/signalrcore)\r\n![Issues](https://img.shields.io/github/issues/mandrewcito/signalrcore.svg)\r\n![Open issues](https://img.shields.io/github/issues-raw/mandrewcito/signalrcore.svg)\r\n![codecov.io](https://codecov.io/github/mandrewcito/signalrcore/coverage.svg?branch=master)\r\n\r\n![logo alt](https://raw.githubusercontent.com/mandrewcito/signalrcore/master/docs/img/logo_temp.128.svg.png)\r\n\r\n\r\n# Links \r\n\r\n* [Dev to posts with library examples and implementation](https://dev.to/mandrewcito/singlar-core-python-client-58e7)\r\n\r\n* [Pypi](https://pypi.org/project/signalrcore/)\r\n\r\n* [Wiki - This Doc](https://mandrewcito.github.io/signalrcore/)\r\n\r\n# Develop\r\n\r\nTest server will be avaiable in [here](https://github.com/mandrewcito/signalrcore-containertestservers) and docker compose is required.\r\n\r\n```bash\r\ngit clone https://github.com/mandrewcito/signalrcore-containertestservers\r\ncd signalrcore-containertestservers\r\ndocker-compose up\r\ncd ../signalrcore\r\nmake tests\r\n```\r\n\r\n## Known Issues\r\n\r\nIssues related with closing sockets are inherited from the websocket-client library. Due to these problems i can't update the library to versions higher than websocket-client 0.54.0. \r\nI'm working to solve it but for now its patched (Error number 1. Raises an exception, and then exception is treated for prevent errors). \r\nIf I update the websocket library I fall into error number 2, on local machine I can't reproduce it but travis builds fail (sometimes and randomly :()\r\n* [1. Closing socket error](https://github.com/slackapi/python-slackclient/issues/171)\r\n* [2. Random errors closing socket](https://github.com/websocket-client/websocket-client/issues/449)\r\n\r\n# A Tiny How To\r\n\r\n## Connect to a server without auth\r\n\r\n```python\r\nhub_connection = HubConnectionBuilder()\\\r\n    .with_url(server_url)\\\r\n    .configure_logging(logging.DEBUG)\\\r\n    .with_automatic_reconnect({\r\n        \"type\": \"raw\",\r\n        \"keep_alive_interval\": 10,\r\n        \"reconnect_interval\": 5,\r\n        \"max_attempts\": 5\r\n    }).build()\r\n```\r\n## Connect to a server with auth\r\n\r\nlogin_function must provide auth token\r\n\r\n```python\r\nhub_connection = HubConnectionBuilder()\\\r\n            .with_url(server_url,\r\n            options={\r\n                \"access_token_factory\": login_function,\r\n                \"headers\": {\r\n                    \"mycustomheader\": \"mycustomheadervalue\"\r\n                }\r\n            })\\\r\n            .configure_logging(logging.DEBUG)\\\r\n            .with_automatic_reconnect({\r\n                \"type\": \"raw\",\r\n                \"keep_alive_interval\": 10,\r\n                \"reconnect_interval\": 5,\r\n                \"max_attempts\": 5\r\n            }).build()\r\n```\r\n### Unauthorized errors\r\nA login function must provide an error controller if authorization fails. When connection starts, if authorization fails exception will be propagated.\r\n\r\n```python\r\n    def login(self):\r\n        response = requests.post(\r\n            self.login_url,\r\n            json={\r\n                \"username\": self.email,\r\n                \"password\": self.password\r\n                },verify=False)\r\n        if response.status_code == 200:\r\n            return response.json()[\"token\"]\r\n        raise requests.exceptions.ConnectionError()\r\n\r\n    hub_connection.start()   # this code will raise  requests.exceptions.ConnectionError() if auth fails\r\n```\r\n## Configure logging\r\n\r\n```python\r\nHubConnectionBuilder()\\\r\n    .with_url(server_url,\r\n    .configure_logging(logging.DEBUG)\r\n    ...\r\n```\r\n## Configure socket trace\r\n```python \r\nHubConnectionBuilder()\\\r\n    .with_url(server_url,\r\n    .configure_logging(logging.DEBUG, socket_trace=True) \r\n    ... \r\n ```\r\n## Configure your own handler\r\n```python\r\n import logging\r\nhandler = logging.StreamHandler()\r\nhandler.setLevel(logging.DEBUG)\r\nhub_connection = HubConnectionBuilder()\\\r\n    .with_url(server_url, options={\"verify_ssl\": False}) \\\r\n    .configure_logging(logging.DEBUG, socket_trace=True, handler=handler)\r\n    ...\r\n ```\r\n## Configuring reconnection\r\nAfter reaching max_attempts an exeption will be thrown and on_disconnect event will be fired.\r\n```python\r\nhub_connection = HubConnectionBuilder()\\\r\n    .with_url(server_url)\\\r\n    ...\r\n    .build()\r\n```\r\n## Configuring additional headers\r\n```python\r\nhub_connection = HubConnectionBuilder()\\\r\n            .with_url(server_url,\r\n            options={\r\n                \"headers\": {\r\n                    \"mycustomheader\": \"mycustomheadervalue\"\r\n                }\r\n            })\r\n            ...\r\n            .build()\r\n```\r\n## Configuring additional querystring parameters\r\n```python\r\nserver_url =\"http.... /?myquerystringparam=134\u0026foo=bar\"\r\nconnection = HubConnectionBuilder()\\\r\n            .with_url(server_url,\r\n            options={\r\n            })\\\r\n            .build()\r\n```\r\n## Congfiguring skip negotiation\r\n```python\r\nhub_connection = HubConnectionBuilder() \\\r\n        .with_url(\"ws://\"+server_url, options={\r\n            \"verify_ssl\": False,\r\n            \"skip_negotiation\": False,\r\n            \"headers\": {\r\n            }\r\n        }) \\\r\n        .configure_logging(logging.DEBUG, socket_trace=True, handler=handler) \\\r\n        .build()\r\n\r\n```\r\n## Configuring ping(keep alive)\r\n\r\nkeep_alive_interval sets the seconds of ping message\r\n\r\n```python\r\nhub_connection = HubConnectionBuilder()\\\r\n    .with_url(server_url)\\\r\n    .configure_logging(logging.DEBUG)\\\r\n    .with_automatic_reconnect({\r\n        \"type\": \"raw\",\r\n        \"keep_alive_interval\": 10,\r\n        \"reconnect_interval\": 5,\r\n        \"max_attempts\": 5\r\n    }).build()\r\n```\r\n## Configuring logging\r\n```python\r\nhub_connection = HubConnectionBuilder()\\\r\n    .with_url(server_url)\\\r\n    .configure_logging(logging.DEBUG)\\\r\n    .with_automatic_reconnect({\r\n        \"type\": \"raw\",\r\n        \"keep_alive_interval\": 10,\r\n        \"reconnect_interval\": 5,\r\n        \"max_attempts\": 5\r\n    }).build()\r\n```\r\n\r\n## Configure messagepack\r\n\r\n```python\r\nfrom signalrcore.protocol.messagepack_protocol import MessagePackHubProtocol\r\n\r\nHubConnectionBuilder()\\\r\n            .with_url(self.server_url, options={\"verify_ssl\":False})\\\r\n                ... \r\n            .with_hub_protocol(MessagePackHubProtocol())\\\r\n                ...\r\n            .build()\r\n```\r\n## Events\r\n\r\n### On Connect / On Disconnect\r\non_open - fires when connection is opened and ready to send messages\r\non_close - fires when connection is closed\r\n```python\r\nhub_connection.on_open(lambda: print(\"connection opened and handshake received ready to send messages\"))\r\nhub_connection.on_close(lambda: print(\"connection closed\"))\r\n\r\n```\r\n### On Hub Error (Hub Exceptions ...)\r\n```\r\nhub_connection.on_error(lambda data: print(f\"An exception was thrown closed{data.error}\"))\r\n```\r\n### Register an operation \r\nReceiveMessage - signalr method\r\nprint - function that has as parameters args of signalr method\r\n```python\r\nhub_connection.on(\"ReceiveMessage\", print)\r\n```\r\n## Sending messages\r\nSendMessage - signalr method\r\nusername, message - parameters of signalrmethod\r\n```python\r\n    hub_connection.send(\"SendMessage\", [username, message])\r\n```\r\n\r\n## Sending messages with callback\r\nSendMessage - signalr method\r\nusername, message - parameters of signalrmethod\r\n```python\r\n    send_callback_received = threading.Lock()\r\n    send_callback_received.acquire()\r\n    self.connection.send(\r\n        \"SendMessage\", # Method\r\n        [self.username, self.message], # Params\r\n        lambda m: send_callback_received.release()) # Callback\r\n    if not send_callback_received.acquire(timeout=1):\r\n        raise ValueError(\"CALLBACK NOT RECEIVED\")\r\n```\r\n\r\n## Requesting streaming (Server to client)\r\n```python\r\nhub_connection.stream(\r\n            \"Counter\",\r\n            [len(self.items), 500]).subscribe({\r\n                \"next\": self.on_next,\r\n                \"complete\": self.on_complete,\r\n                \"error\": self.on_error\r\n            })\r\n```\r\n## Client side Streaming\r\n```python\r\nfrom signalrcore.subject import  Subject\r\n\r\nsubject = Subject()\r\n\r\n# Start Streaming\r\nhub_connection.send(\"UploadStream\", subject)\r\n\r\n# Each iteration\r\nsubject.next(str(iteration))\r\n\r\n# End streaming\r\nsubject.complete()\r\n```\r\n\r\n# Full Examples\r\n\r\nExamples will be avaiable [here](https://github.com/mandrewcito/signalrcore/tree/master/test/examples)\r\nIt were developed using package from [aspnet core - SignalRChat](https://codeload.github.com/aspnet/Docs/zip/master) \r\n\r\n## Chat example\r\nA mini example could be something like this:\r\n\r\n```python\r\nimport logging\r\nimport sys\r\nfrom signalrcore.hub_connection_builder import HubConnectionBuilder\r\n\r\n\r\ndef input_with_default(input_text, default_value):\r\n    value = input(input_text.format(default_value))\r\n    return default_value if value is None or value.strip() == \"\" else value\r\n\r\n\r\nserver_url = input_with_default('Enter your server url(default: {0}): ', \"wss://localhost:44376/chatHub\")\r\nusername = input_with_default('Enter your username (default: {0}): ', \"mandrewcito\")\r\nhandler = logging.StreamHandler()\r\nhandler.setLevel(logging.DEBUG)\r\nhub_connection = HubConnectionBuilder()\\\r\n    .with_url(server_url, options={\"verify_ssl\": False}) \\\r\n    .configure_logging(logging.DEBUG, socket_trace=True, handler=handler) \\\r\n    .with_automatic_reconnect({\r\n            \"type\": \"interval\",\r\n            \"keep_alive_interval\": 10,\r\n            \"intervals\": [1, 3, 5, 6, 7, 87, 3]\r\n        }).build()\r\n\r\nhub_connection.on_open(lambda: print(\"connection opened and handshake received ready to send messages\"))\r\nhub_connection.on_close(lambda: print(\"connection closed\"))\r\n\r\nhub_connection.on(\"ReceiveMessage\", print)\r\nhub_connection.start()\r\nmessage = None\r\n\r\n# Do login\r\n\r\nwhile message != \"exit()\":\r\n    message = input(\"\u003e\u003e \")\r\n    if message is not None and message != \"\" and message != \"exit()\":\r\n        hub_connection.send(\"SendMessage\", [username, message])\r\n\r\nhub_connection.stop()\r\n\r\nsys.exit(0)\r\n\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmandrewcito%2Fsignalrcore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmandrewcito%2Fsignalrcore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmandrewcito%2Fsignalrcore/lists"}