├── .github └── workflows │ ├── publish.yml │ └── test.yml ├── .gitignore ├── CONTRIBUTORS.md ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── examples └── app.py ├── fastapi_socketio ├── __init__.py └── socket_manager.py ├── setup.py └── tests └── test_fastapi_socketio.py /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Python 13 | uses: actions/setup-python@v2 14 | with: 15 | python-version: '3.8' 16 | - uses: actions/cache@v2 17 | name: Configure pip caching 18 | with: 19 | path: ~/.cache/pip 20 | key: ${{ runner.os }}-publish-pip-${{ hashFiles('**/setup.py') }} 21 | restore-keys: | 22 | ${{ runner.os }}-publish-pip- 23 | - name: Install dependencies 24 | run: | 25 | pip install -e '.[test]' 26 | pip install setuptools wheel twine 27 | - name: Run tests 28 | run: pytest 29 | - name: Publish 30 | if: success() 31 | env: 32 | TWINE_USERNAME: __token__ 33 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 34 | run: | 35 | python setup.py sdist bdist_wheel 36 | twine upload --skip-existing dist/* 37 | 38 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: [3.6, 3.7, 3.8] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python ${{ matrix.python-version }} 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: ${{ matrix.python-version }} 17 | - uses: actions/cache@v2 18 | name: Configure pip caching 19 | with: 20 | path: ~/.cache/pip 21 | key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} 22 | restore-keys: | 23 | ${{ runner.os }}-pip- 24 | - name: Install dependencies 25 | run: | 26 | pip install -e '.[test]' 27 | - name: Run tests 28 | run: | 29 | pytest 30 | 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | venv 6 | .eggs 7 | .pytest_cache 8 | *.egg-info 9 | .DS_Store 10 | .vscode/ -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | * [Matthew D. Scholefield](https://github.com/MatthewScholefield) - Added cors allowed origins for AsyncServer [#7](https://github.com/pyropy/fastapi-socketio/pull/7) 4 | * [Aaron Tolman](https://github.com/tolmanam) - Reported issue with broken pip download [#5](https://github.com/pyropy/fastapi-socketio/issues/5) 5 | * [Khiem Doan](https://github.com/khiemdoan) - Helped with cleaning up old unused dependencies[#5](https://github.com/pyropy/fastapi-socketio/issues/15) 6 | * [Artem Kolomatskiy](https://github.com/Roxe322) - Fixed invalid usage of enter_room and leave_room[#24](https://github.com/pyropy/fastapi-socketio/issues/24) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | jedi = "*" 8 | neovim = "*" 9 | 10 | [packages] 11 | fastapi = ">=0.61.1" 12 | netifaces = ">=0.10.6" 13 | pydantic = ">=1.6.1" 14 | starlette = ">=0.13.6" 15 | python-socketio = ">=4.6.0" 16 | python-engineio = "*" 17 | 18 | [test] 19 | pytest = ">=6.0.1" 20 | fastapi = ">=0.61.1" 21 | netifaces = ">=0.10.6" 22 | pydantic = ">=1.6.1" 23 | starlette = ">=0.13.6" 24 | python-socketio = ">=4.6.0" 25 | python-engineio = "*" 26 | 27 | 28 | [requires] 29 | python_version = "3.8" 30 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "66ea5b34a9c92727c054844c2115d439f8d99e26b1e0e81b75030d919194c0a7" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.8" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "fastapi": { 20 | "hashes": [ 21 | "sha256:62074dd38541d9d7245f3aacbbd0d44340c53d56186c9b249d261a18dad4874b", 22 | "sha256:8f4c64cd9cea67fb7dd175ca5015961efa572b9f43a8731014dac8929d86225f" 23 | ], 24 | "index": "pypi", 25 | "version": "==0.62.0" 26 | }, 27 | "netifaces": { 28 | "hashes": [ 29 | "sha256:078986caf4d6a602a4257d3686afe4544ea74362b8928e9f4389b5cd262bc215", 30 | "sha256:0c4304c6d5b33fbd9b20fdc369f3a2fef1a8bbacfb6fd05b9708db01333e9e7b", 31 | "sha256:2dee9ffdd16292878336a58d04a20f0ffe95555465fee7c9bd23b3490ef2abf3", 32 | "sha256:3095218b66d359092b82f07c5422293c2f6559cf8d36b96b379cc4cdc26eeffa", 33 | "sha256:30ed89ab8aff715caf9a9d827aa69cd02ad9f6b1896fd3fb4beb998466ed9a3c", 34 | "sha256:4921ed406386246b84465950d15a4f63480c1458b0979c272364054b29d73084", 35 | "sha256:563a1a366ee0fb3d96caab79b7ac7abd2c0a0577b157cc5a40301373a0501f89", 36 | "sha256:5b3167f923f67924b356c1338eb9ba275b2ba8d64c7c2c47cf5b5db49d574994", 37 | "sha256:6d84e50ec28e5d766c9911dce945412dc5b1ce760757c224c71e1a9759fa80c2", 38 | "sha256:755050799b5d5aedb1396046f270abfc4befca9ccba3074f3dbbb3cb34f13aae", 39 | "sha256:75d3a4ec5035db7478520ac547f7c176e9fd438269e795819b67223c486e5cbe", 40 | "sha256:7a25a8e28281504f0e23e181d7a9ed699c72f061ca6bdfcd96c423c2a89e75fc", 41 | "sha256:7cc6fd1eca65be588f001005446a47981cbe0b2909f5be8feafef3bf351a4e24", 42 | "sha256:86b8a140e891bb23c8b9cb1804f1475eb13eea3dbbebef01fcbbf10fbafbee42", 43 | "sha256:ad10acab2ef691eb29a1cc52c3be5ad1423700e993cc035066049fa72999d0dc", 44 | "sha256:b2ff3a0a4f991d2da5376efd3365064a43909877e9fabfa801df970771161d29", 45 | "sha256:b47e8f9ff6846756be3dc3fb242ca8e86752cd35a08e06d54ffc2e2a2aca70ea", 46 | "sha256:da298241d87bcf468aa0f0705ba14572ad296f24c4fda5055d6988701d6fd8e1", 47 | "sha256:db881478f1170c6dd524175ba1c83b99d3a6f992a35eca756de0ddc4690a1940", 48 | "sha256:f0427755c68571df37dc58835e53a4307884a48dec76f3c01e33eb0d4a3a81d7", 49 | "sha256:f8885cc48c8c7ad51f36c175e462840f163cb4687eeb6c6d7dfaf7197308e36b", 50 | "sha256:f911b7f0083d445c8d24cfa5b42ad4996e33250400492080f5018a28c026db2b" 51 | ], 52 | "index": "pypi", 53 | "version": "==0.10.9" 54 | }, 55 | "pydantic": { 56 | "hashes": [ 57 | "sha256:025bf13ce27990acc059d0c5be46f416fc9b293f45363b3d19855165fee1874f", 58 | "sha256:185e18134bec5ef43351149fe34fda4758e53d05bb8ea4d5928f0720997b79ef", 59 | "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9", 60 | "sha256:24ca47365be2a5a3cc3f4a26dcc755bcdc9f0036f55dcedbd55663662ba145ec", 61 | "sha256:38be427ea01a78206bcaf9a56f835784afcba9e5b88fbdce33bbbfbcd7841229", 62 | "sha256:475f2fa134cf272d6631072554f845d0630907fce053926ff634cc6bc45bf1af", 63 | "sha256:514b473d264671a5c672dfb28bdfe1bf1afd390f6b206aa2ec9fed7fc592c48e", 64 | "sha256:59e45f3b694b05a69032a0d603c32d453a23f0de80844fb14d55ab0c6c78ff2f", 65 | "sha256:5b24e8a572e4b4c18f614004dda8c9f2c07328cb5b6e314d6e1bbd536cb1a6c1", 66 | "sha256:6e3874aa7e8babd37b40c4504e3a94cc2023696ced5a0500949f3347664ff8e2", 67 | "sha256:8d72e814c7821125b16f1553124d12faba88e85405b0864328899aceaad7282b", 68 | "sha256:a4143c8d0c456a093387b96e0f5ee941a950992904d88bc816b4f0e72c9a0009", 69 | "sha256:b2b054d095b6431cdda2f852a6d2f0fdec77686b305c57961b4c5dd6d863bf3c", 70 | "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd", 71 | "sha256:d1fe3f0df8ac0f3a9792666c69a7cd70530f329036426d06b4f899c025aca74e", 72 | "sha256:d8df4b9090b595511906fa48deda47af04e7d092318bfb291f4d45dfb6bb2127", 73 | "sha256:dba5c1f0a3aeea5083e75db9660935da90216f8a81b6d68e67f54e135ed5eb23", 74 | "sha256:e682f6442ebe4e50cb5e1cfde7dda6766fb586631c3e5569f6aa1951fd1a76ef", 75 | "sha256:ecb54491f98544c12c66ff3d15e701612fc388161fd455242447083350904730", 76 | "sha256:f5b06f5099e163295b8ff5b1b71132ecf5866cc6e7f586d78d7d3fd6e8084608", 77 | "sha256:f6864844b039805add62ebe8a8c676286340ba0c6d043ae5dea24114b82a319e", 78 | "sha256:ffd180ebd5dd2a9ac0da4e8b995c9c99e7c74c31f985ba090ee01d681b1c4b95" 79 | ], 80 | "index": "pypi", 81 | "version": "==1.7.3" 82 | }, 83 | "python-engineio": { 84 | "hashes": [ 85 | "sha256:5a9e6086d192463b04a1428ff1f85b6ba631bbb19d453b144ffc04f530542b84", 86 | "sha256:eab4553f2804c1ce97054c8b22cf0d5a9ab23128075248b97e1a5b2f29553085" 87 | ], 88 | "index": "pypi", 89 | "version": "==3.14.2" 90 | }, 91 | "python-socketio": { 92 | "hashes": [ 93 | "sha256:5a21da53fdbdc6bb6c8071f40e13d100e0b279ad997681c2492478e06f370523", 94 | "sha256:cd1f5aa492c1eb2be77838e837a495f117e17f686029ebc03d62c09e33f4fa10" 95 | ], 96 | "index": "pypi", 97 | "version": "==4.6.1" 98 | }, 99 | "six": { 100 | "hashes": [ 101 | "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", 102 | "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" 103 | ], 104 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 105 | "version": "==1.15.0" 106 | }, 107 | "starlette": { 108 | "hashes": [ 109 | "sha256:bd2ffe5e37fb75d014728511f8e68ebf2c80b0fa3d04ca1479f4dc752ae31ac9", 110 | "sha256:ebe8ee08d9be96a3c9f31b2cb2a24dbdf845247b745664bd8a3f9bd0c977fdbc" 111 | ], 112 | "index": "pypi", 113 | "version": "==0.13.6" 114 | } 115 | }, 116 | "develop": { 117 | "greenlet": { 118 | "hashes": [ 119 | "sha256:1023d7b43ca11264ab7052cb09f5635d4afdb43df55e0854498fc63070a0b206", 120 | "sha256:124a3ae41215f71dc91d1a3d45cbf2f84e46b543e5d60b99ecc20e24b4c8f272", 121 | "sha256:13037e2d7ab2145300676852fa069235512fdeba4ed1e3bb4b0677a04223c525", 122 | "sha256:3af587e9813f9bd8be9212722321a5e7be23b2bc37e6323a90e592ab0c2ef117", 123 | "sha256:41d8835c69a78de718e466dd0e6bfd4b46125f21a67c3ff6d76d8d8059868d6b", 124 | "sha256:4481002118b2f1588fa3d821936ffdc03db80ef21186b62b90c18db4ba5e743b", 125 | "sha256:47825c3a109f0331b1e54c1173d4e57fa000aa6c96756b62852bfa1af91cd652", 126 | "sha256:5494e3baeacc371d988345fbf8aa4bd15555b3077c40afcf1994776bb6d77eaf", 127 | "sha256:75e4c27188f28149b74e7685809f9227410fd15432a4438fc48627f518577fa5", 128 | "sha256:97f2b01ab622a4aa4b3724a3e1fba66f47f054c434fbaa551833fa2b41e3db51", 129 | "sha256:a34023b9eabb3525ee059f3bf33a417d2e437f7f17e341d334987d4091ae6072", 130 | "sha256:ac85db59aa43d78547f95fc7b6fd2913e02b9e9b09e2490dfb7bbdf47b2a4914", 131 | "sha256:be7a79988b8fdc5bbbeaed69e79cfb373da9759242f1565668be4fb7f3f37552", 132 | "sha256:bee111161420f341a346731279dd976be161b465c1286f82cc0779baf7b729e8", 133 | "sha256:ccd62f09f90b2730150d82f2f2ffc34d73c6ce7eac234aed04d15dc8a3023994", 134 | "sha256:d3436110ca66fe3981031cc6aff8cc7a40d8411d173dde73ddaa5b8445385e2d", 135 | "sha256:e495096e3e2e8f7192afb6aaeba19babc4fb2bdf543d7b7fed59e00c1df7f170", 136 | "sha256:e66a824f44892bc4ec66c58601a413419cafa9cec895e63d8da889c8a1a4fa4a" 137 | ], 138 | "version": "==0.4.17" 139 | }, 140 | "jedi": { 141 | "hashes": [ 142 | "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20", 143 | "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5" 144 | ], 145 | "index": "pypi", 146 | "version": "==0.17.2" 147 | }, 148 | "msgpack": { 149 | "hashes": [ 150 | "sha256:002a0d813e1f7b60da599bdf969e632074f9eec1b96cbed8fb0973a63160a408", 151 | "sha256:25b3bc3190f3d9d965b818123b7752c5dfb953f0d774b454fd206c18fe384fb8", 152 | "sha256:271b489499a43af001a2e42f42d876bb98ccaa7e20512ff37ca78c8e12e68f84", 153 | "sha256:39c54fdebf5fa4dda733369012c59e7d085ebdfe35b6cf648f09d16708f1be5d", 154 | "sha256:4233b7f86c1208190c78a525cd3828ca1623359ef48f78a6fea4b91bb995775a", 155 | "sha256:5bea44181fc8e18eed1d0cd76e355073f00ce232ff9653a0ae88cb7d9e643322", 156 | "sha256:5dba6d074fac9b24f29aaf1d2d032306c27f04187651511257e7831733293ec2", 157 | "sha256:7a22c965588baeb07242cb561b63f309db27a07382825fc98aecaf0827c1538e", 158 | "sha256:908944e3f038bca67fcfedb7845c4a257c7749bf9818632586b53bcf06ba4b97", 159 | "sha256:9534d5cc480d4aff720233411a1f765be90885750b07df772380b34c10ecb5c0", 160 | "sha256:aa5c057eab4f40ec47ea6f5a9825846be2ff6bf34102c560bad5cad5a677c5be", 161 | "sha256:b3758dfd3423e358bbb18a7cccd1c74228dffa7a697e5be6cb9535de625c0dbf", 162 | "sha256:c901e8058dd6653307906c5f157f26ed09eb94a850dddd989621098d347926ab", 163 | "sha256:cec8bf10981ed70998d98431cd814db0ecf3384e6b113366e7f36af71a0fca08", 164 | "sha256:db685187a415f51d6b937257474ca72199f393dad89534ebbdd7d7a3b000080e", 165 | "sha256:e35b051077fc2f3ce12e7c6a34cf309680c63a842db3a0616ea6ed25ad20d272", 166 | "sha256:e7bbdd8e2b277b77782f3ce34734b0dfde6cbe94ddb74de8d733d603c7f9e2b1", 167 | "sha256:ea41c9219c597f1d2bf6b374d951d310d58684b5de9dc4bd2976db9e1e22c140" 168 | ], 169 | "version": "==1.0.0" 170 | }, 171 | "neovim": { 172 | "hashes": [ 173 | "sha256:a6a0e7a5b4433bf4e6ddcbc5c5ff44170be7d84259d002b8e8d8fb4ee78af60f" 174 | ], 175 | "index": "pypi", 176 | "version": "==0.3.1" 177 | }, 178 | "parso": { 179 | "hashes": [ 180 | "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea", 181 | "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9" 182 | ], 183 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 184 | "version": "==0.7.1" 185 | }, 186 | "pynvim": { 187 | "hashes": [ 188 | "sha256:6bc6204d465de5888a0c5e3e783fe01988b032e22ae87875912280bef0e40f8f" 189 | ], 190 | "version": "==0.4.2" 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastapi-socketio 2 | 3 | [![PyPI](https://img.shields.io/pypi/v/fastapi-socketio.svg)](https://pypi.org/project/fastapi-socketio/) 4 | [![Changelog](https://img.shields.io/github/v/release/pyropy/fastapi-socketio?label=changelog)](https://github.com/pyropy/fastapi-socketio/releases) 5 | [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/pyropy/fastapi-socketio/blob/main/LICENSE) 6 | 7 | Easly integrate socket.io with your FastAPI app. 8 | 9 | ## Installation 10 | 11 | Install this plugin using `pip`: 12 | 13 | $ pip install fastapi-socketio 14 | 15 | ## Usage 16 | 17 | To add SocketIO support to FastAPI all you need to do is import `SocketManager` and pass it `FastAPI` object. 18 | 19 | ```python 20 | # app.py 21 | from fastapi import FastAPI 22 | from fastapi_socketio import SocketManager 23 | 24 | app = FastAPI() 25 | socket_manager = SocketManager(app=app) 26 | ``` 27 | 28 | 29 | Now you can use SocketIO directly from your `FastAPI` app object. 30 | ```python 31 | # socket_handlers.py 32 | from .app import app 33 | 34 | @app.sio.on('join') 35 | async def handle_join(sid, *args, **kwargs): 36 | await app.sio.emit('lobby', 'User joined') 37 | 38 | ``` 39 | 40 | Or you can import `SocketManager` object that exposes most of the SocketIO functionality. 41 | 42 | ```python 43 | # socket_handlers2.py 44 | from .app import socket_manager as sm 45 | 46 | @sm.on('leave') 47 | async def handle_leave(sid, *args, **kwargs): 48 | await sm.emit('lobby', 'User left') 49 | 50 | ``` 51 | 52 | ## Development 53 | 54 | To contribute to this library, first checkout the code. Then create a new virtual environment: 55 | 56 | cd fastapi-socketio 57 | python -mvenv venv 58 | source venv/bin/activate 59 | 60 | Or if you are using `pipenv`: 61 | 62 | pipenv shell 63 | 64 | Now install the dependencies and tests: 65 | 66 | pip install -e '.[test]' 67 | 68 | To run the tests: 69 | 70 | pytest 71 | 72 | 73 | ## Run example 74 | 75 | To run the examples simply run: 76 | 77 | ```bash 78 | PYTHONPATH=. python examples/app.py 79 | ``` 80 | 81 | Before running example make sure you have all dependencies installed. 82 | 83 | ## Contributors 84 | 85 | For list of contributors please reefer to CONTRIBUTORS.md file in this repository. -------------------------------------------------------------------------------- /examples/app.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from fastapi_socketio import SocketManager 3 | 4 | app = FastAPI() 5 | sio = SocketManager(app=app) 6 | 7 | 8 | @app.sio.on('join') 9 | async def handle_join(sid, *args, **kwargs): 10 | await sio.emit('lobby', 'User joined') 11 | 12 | 13 | @sio.on('test') 14 | async def test(sid, *args, **kwargs): 15 | await sio.emit('hey', 'joe') 16 | 17 | 18 | 19 | if __name__ == '__main__': 20 | import logging 21 | import sys 22 | 23 | logging.basicConfig(level=logging.DEBUG, 24 | stream=sys.stdout) 25 | 26 | import uvicorn 27 | 28 | uvicorn.run("examples.app:app", host='0.0.0.0', port=8000, reload=True, debug=False) 29 | -------------------------------------------------------------------------------- /fastapi_socketio/__init__.py: -------------------------------------------------------------------------------- 1 | from .socket_manager import SocketManager -------------------------------------------------------------------------------- /fastapi_socketio/socket_manager.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | import socketio 4 | from fastapi import FastAPI 5 | 6 | class SocketManager: 7 | """ 8 | Integrates SocketIO with FastAPI app. 9 | Adds `sio` property to FastAPI object (app). 10 | 11 | Default mount location for SocketIO app is at `/ws` 12 | and defautl SocketIO path is `socket.io`. 13 | (e.g. full path: `ws://www.example.com/ws/socket.io/) 14 | 15 | SocketManager exposes basic underlying SocketIO functionality 16 | 17 | e.g. emit, on, send, call, etc. 18 | """ 19 | 20 | def __init__( 21 | self, 22 | app: FastAPI, 23 | mount_location: str = "/ws", 24 | socketio_path: str = "socket.io", 25 | cors_allowed_origins: Union[str, list] = '*', 26 | async_mode: str = "asgi", 27 | **kwargs 28 | ) -> None: 29 | # TODO: Change Cors policy based on fastapi cors Middleware 30 | self._sio = socketio.AsyncServer(async_mode=async_mode, cors_allowed_origins=cors_allowed_origins, **kwargs) 31 | self._app = socketio.ASGIApp( 32 | socketio_server=self._sio, socketio_path=socketio_path 33 | ) 34 | 35 | app.mount(mount_location, self._app) 36 | app.sio = self._sio 37 | 38 | def is_asyncio_based(self) -> bool: 39 | return True 40 | 41 | @property 42 | def on(self): 43 | return self._sio.on 44 | 45 | @property 46 | def attach(self): 47 | return self._sio.attach 48 | 49 | @property 50 | def emit(self): 51 | return self._sio.emit 52 | 53 | @property 54 | def send(self): 55 | return self._sio.send 56 | 57 | @property 58 | def call(self): 59 | return self._sio.call 60 | 61 | @property 62 | def close_room(self): 63 | return self._sio.close_room 64 | 65 | @property 66 | def get_session(self): 67 | return self._sio.get_session 68 | 69 | @property 70 | def save_session(self): 71 | return self._sio.save_session 72 | 73 | @property 74 | def session(self): 75 | return self._sio.session 76 | 77 | @property 78 | def disconnect(self): 79 | return self._sio.disconnect 80 | 81 | @property 82 | def handle_request(self): 83 | return self._sio.handle_request 84 | 85 | @property 86 | def start_background_task(self): 87 | return self._sio.start_background_task 88 | 89 | @property 90 | def sleep(self): 91 | return self._sio.sleep 92 | 93 | @property 94 | def enter_room(self): 95 | return self._sio.enter_room 96 | 97 | @property 98 | def leave_room(self): 99 | return self._sio.leave_room 100 | 101 | @property 102 | def register_namespace(self): 103 | return self._sio.register_namespace 104 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import os 3 | 4 | VERSION = "0.0.10" 5 | 6 | 7 | def get_long_description(): 8 | with open( 9 | os.path.join(os.path.dirname(os.path.abspath(__file__)), "README.md"), 10 | encoding="utf8", 11 | ) as fp: 12 | return fp.read() 13 | 14 | 15 | setup( 16 | name="fastapi-socketio", 17 | description="Easily integrate socket.io with your FastAPI app.", 18 | long_description=get_long_description(), 19 | long_description_content_type="text/markdown", 20 | author="Srdjan Stankovic", 21 | author_email="stankovic.srdjo@gmail.com", 22 | url="https://github.com/pyropy/fastapi-socketio", 23 | project_urls={ 24 | "Issues": "https://github.com/pyropy/fastapi-socketio/issues", 25 | "CI": "https://github.com/pyropy/fastapi-socketio/actions", 26 | "Changelog": "https://github.com/pyropy/fastapi-socketio/releases", 27 | }, 28 | license="Apache License, Version 2.0", 29 | version=VERSION, 30 | packages=["fastapi_socketio"], 31 | install_requires=[ 32 | "fastapi>=0.61.1", 33 | "python-socketio>=4.6.0", 34 | ], 35 | extras_require={"test": ["pytest"]}, 36 | tests_require=["fastapi-socketio[test]"], 37 | ) 38 | -------------------------------------------------------------------------------- /tests/test_fastapi_socketio.py: -------------------------------------------------------------------------------- 1 | def test_dummy(): 2 | assert True --------------------------------------------------------------------------------