From 73d6275802f53a98fedfd20e11ce7657445dd83a Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Tue, 20 Aug 2024 07:19:46 +0000 Subject: [PATCH] fix: set realtime as not implemented in the sync client (#193) --- Makefile | 3 - poetry.lock | 259 +++++++------------ pyproject.toml | 1 - realtime/_sync/channel.py | 504 +------------------------------------ realtime/_sync/client.py | 248 +----------------- realtime/_sync/presence.py | 229 +---------------- realtime/_sync/push.py | 120 --------- realtime/_sync/timer.py | 26 -- 8 files changed, 107 insertions(+), 1283 deletions(-) delete mode 100644 realtime/_sync/push.py delete mode 100644 realtime/_sync/timer.py diff --git a/Makefile b/Makefile index 00674b1..074aa3b 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,3 @@ run_tests: tests tests_only: poetry run pytest -vv - -build_sync: - poetry run unasync realtime tests diff --git a/poetry.lock b/poetry.lock index b23660b..f58f4ee 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,98 +2,113 @@ [[package]] name = "aiohappyeyeballs" -version = "2.3.6" +version = "2.4.0" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.3.6-py3-none-any.whl", hash = "sha256:15dca2611fa78442f1cb54cf07ffb998573f2b4fbeab45ca8554c045665c896b"}, - {file = "aiohappyeyeballs-2.3.6.tar.gz", hash = "sha256:88211068d2a40e0436033956d7de3926ff36d54776f8b1022d6b21320cadae79"}, + {file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"}, + {file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"}, ] [[package]] name = "aiohttp" -version = "3.10.3" +version = "3.10.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc36cbdedf6f259371dbbbcaae5bb0e95b879bc501668ab6306af867577eb5db"}, - {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85466b5a695c2a7db13eb2c200af552d13e6a9313d7fa92e4ffe04a2c0ea74c1"}, - {file = "aiohttp-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71bb1d97bfe7e6726267cea169fdf5df7658831bb68ec02c9c6b9f3511e108bb"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baec1eb274f78b2de54471fc4c69ecbea4275965eab4b556ef7a7698dee18bf2"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13031e7ec1188274bad243255c328cc3019e36a5a907978501256000d57a7201"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2bbc55a964b8eecb341e492ae91c3bd0848324d313e1e71a27e3d96e6ee7e8e8"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8cc0564b286b625e673a2615ede60a1704d0cbbf1b24604e28c31ed37dc62aa"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f817a54059a4cfbc385a7f51696359c642088710e731e8df80d0607193ed2b73"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8542c9e5bcb2bd3115acdf5adc41cda394e7360916197805e7e32b93d821ef93"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:671efce3a4a0281060edf9a07a2f7e6230dca3a1cbc61d110eee7753d28405f7"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0974f3b5b0132edcec92c3306f858ad4356a63d26b18021d859c9927616ebf27"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:44bb159b55926b57812dca1b21c34528e800963ffe130d08b049b2d6b994ada7"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6ae9ae382d1c9617a91647575255ad55a48bfdde34cc2185dd558ce476bf16e9"}, - {file = "aiohttp-3.10.3-cp310-cp310-win32.whl", hash = "sha256:aed12a54d4e1ee647376fa541e1b7621505001f9f939debf51397b9329fd88b9"}, - {file = "aiohttp-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:b51aef59370baf7444de1572f7830f59ddbabd04e5292fa4218d02f085f8d299"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9"}, - {file = "aiohttp-3.10.3-cp311-cp311-win32.whl", hash = "sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b"}, - {file = "aiohttp-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3"}, - {file = "aiohttp-3.10.3-cp312-cp312-win32.whl", hash = "sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a"}, - {file = "aiohttp-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d1100e68e70eb72eadba2b932b185ebf0f28fd2f0dbfe576cfa9d9894ef49752"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a541414578ff47c0a9b0b8b77381ea86b0c8531ab37fc587572cb662ccd80b88"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d5548444ef60bf4c7b19ace21f032fa42d822e516a6940d36579f7bfa8513f9c"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba2e838b5e6a8755ac8297275c9460e729dc1522b6454aee1766c6de6d56e5e"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48665433bb59144aaf502c324694bec25867eb6630fcd831f7a893ca473fcde4"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bac352fceed158620ce2d701ad39d4c1c76d114255a7c530e057e2b9f55bdf9f"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0f670502100cdc567188c49415bebba947eb3edaa2028e1a50dd81bd13363f"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b09f38a67679e32d380fe512189ccb0b25e15afc79b23fbd5b5e48e4fc8fd9"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:cd788602e239ace64f257d1c9d39898ca65525583f0fbf0988bcba19418fe93f"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:214277dcb07ab3875f17ee1c777d446dcce75bea85846849cc9d139ab8f5081f"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:32007fdcaab789689c2ecaaf4b71f8e37bf012a15cd02c0a9db8c4d0e7989fa8"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:123e5819bfe1b87204575515cf448ab3bf1489cdeb3b61012bde716cda5853e7"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:812121a201f0c02491a5db335a737b4113151926a79ae9ed1a9f41ea225c0e3f"}, - {file = "aiohttp-3.10.3-cp38-cp38-win32.whl", hash = "sha256:b97dc9a17a59f350c0caa453a3cb35671a2ffa3a29a6ef3568b523b9113d84e5"}, - {file = "aiohttp-3.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:3731a73ddc26969d65f90471c635abd4e1546a25299b687e654ea6d2fc052394"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38d91b98b4320ffe66efa56cb0f614a05af53b675ce1b8607cdb2ac826a8d58e"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9743fa34a10a36ddd448bba8a3adc2a66a1c575c3c2940301bacd6cc896c6bf1"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7c126f532caf238031c19d169cfae3c6a59129452c990a6e84d6e7b198a001dc"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:926e68438f05703e500b06fe7148ef3013dd6f276de65c68558fa9974eeb59ad"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434b3ab75833accd0b931d11874e206e816f6e6626fd69f643d6a8269cd9166a"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d35235a44ec38109b811c3600d15d8383297a8fab8e3dec6147477ec8636712a"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59c489661edbd863edb30a8bd69ecb044bd381d1818022bc698ba1b6f80e5dd1"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50544fe498c81cb98912afabfc4e4d9d85e89f86238348e3712f7ca6a2f01dab"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:09bc79275737d4dc066e0ae2951866bb36d9c6b460cb7564f111cc0427f14844"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:af4dbec58e37f5afff4f91cdf235e8e4b0bd0127a2a4fd1040e2cad3369d2f06"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b22cae3c9dd55a6b4c48c63081d31c00fc11fa9db1a20c8a50ee38c1a29539d2"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ba562736d3fbfe9241dad46c1a8994478d4a0e50796d80e29d50cabe8fbfcc3f"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f25d6c4e82d7489be84f2b1c8212fafc021b3731abdb61a563c90e37cced3a21"}, - {file = "aiohttp-3.10.3-cp39-cp39-win32.whl", hash = "sha256:b69d832e5f5fa15b1b6b2c8eb6a9fd2c0ec1fd7729cb4322ed27771afc9fc2ac"}, - {file = "aiohttp-3.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:673bb6e3249dc8825df1105f6ef74e2eab779b7ff78e96c15cadb78b04a83752"}, - {file = "aiohttp-3.10.3.tar.gz", hash = "sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696"}, + {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, + {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, + {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, + {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, + {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, + {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, + {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, + {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, + {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, + {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, + {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, + {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"}, + {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"}, + {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"}, + {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"}, + {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, + {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, + {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, + {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, ] [package.dependencies] @@ -838,22 +853,6 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] -[[package]] -name = "setuptools" -version = "72.2.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, - {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, -] - -[package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] - [[package]] name = "six" version = "1.16.0" @@ -865,17 +864,6 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[package]] -name = "tokenize-rt" -version = "6.0.0" -description = "A wrapper around the stdlib `tokenize` which roundtrips." -optional = false -python-versions = ">=3.8" -files = [ - {file = "tokenize_rt-6.0.0-py2.py3-none-any.whl", hash = "sha256:d4ff7ded2873512938b4f8cbb98c9b07118f01d30ac585a30d7a88353ca36d22"}, - {file = "tokenize_rt-6.0.0.tar.gz", hash = "sha256:b9711bdfc51210211137499b5e355d3de5ec88a85d2025c520cbb921b5194367"}, -] - [[package]] name = "tomli" version = "2.0.1" @@ -887,26 +875,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "typer" -version = "0.4.2" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.6" -files = [ - {file = "typer-0.4.2-py3-none-any.whl", hash = "sha256:023bae00d1baf358a6cc7cea45851639360bb716de687b42b0a4641cd99173f1"}, - {file = "typer-0.4.2.tar.gz", hash = "sha256:b8261c6c0152dd73478b5ba96ba677e5d6948c715c310f7c91079f311f62ec03"}, -] - -[package.dependencies] -click = ">=7.1.1,<9.0.0" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "shellingham (>=1.3.0,<2.0.0)"] - [[package]] name = "typing-extensions" version = "4.12.2" @@ -918,41 +886,6 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -[[package]] -name = "unasync" -version = "0.6.0" -description = "The async transformation code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "unasync-0.6.0-py3-none-any.whl", hash = "sha256:9cf7aaaea9737e417d8949bf9be55dc25fdb4ef1f4edc21b58f76ff0d2b9d73f"}, - {file = "unasync-0.6.0.tar.gz", hash = "sha256:a9d01ace3e1068b20550ab15b7f9723b15b8bcde728bc1770bcb578374c7ee58"}, -] - -[package.dependencies] -setuptools = "*" -tokenize-rt = "*" - -[[package]] -name = "unasync-cli" -version = "0.0.1" -description = "Command line interface for unasync. Fork of https://github.com/leynier/unasync-cli/" -optional = false -python-versions = "^3.8.18" -files = [] -develop = false - -[package.dependencies] -setuptools = "^72.1.0" -typer = "^0.4.0" -unasync = "^0.6.0" - -[package.source] -type = "git" -url = "https://github.com/supabase-community/unasync-cli.git" -reference = "main" -resolved_reference = "6a082ee36d5e8941622b70f6cbcaf8e7a5be339d" - [[package]] name = "virtualenv" version = "20.26.3" @@ -1160,4 +1093,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "92cfe646324425e659c2b3793cc30947b8c270813379485c72f11c70e252cd75" +content-hash = "a1a048458997f7fffc4e65900650bb856ef2cc28ee54f75b64eef2088fa7ced0" diff --git a/pyproject.toml b/pyproject.toml index 96023de..f1b72ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,6 @@ black = ">=23.11,<25.0" isort = "^5.12.0" pre-commit = "^3.5.0" pytest-cov = "^5.0.0" -unasync-cli = { git = "https://github.com/supabase-community/unasync-cli.git", branch = "main" } [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/realtime/_sync/channel.py b/realtime/_sync/channel.py index 745f1f7..4c42a3f 100644 --- a/realtime/_sync/channel.py +++ b/realtime/_sync/channel.py @@ -1,29 +1,8 @@ from __future__ import annotations -import asyncio -import json -import logging -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional +from typing import TYPE_CHECKING -from realtime.types import ( - Binding, - Callback, - ChannelEvents, - ChannelStates, - RealtimeChannelOptions, - RealtimePostgresChangesListenEvent, - RealtimePresenceState, - RealtimeSubscribeStates, -) - -from ..transformers import http_endpoint_url -from .presence import ( - PresenceOnJoinCallback, - PresenceOnLeaveCallback, - SyncRealtimePresence, -) -from .push import SyncPush -from .timer import SyncTimer +from realtime.types import RealtimeChannelOptions if TYPE_CHECKING: from .client import SyncRealtimeClient @@ -49,482 +28,3 @@ def __init__( :param topic: Topic that it subscribes to on the realtime server :param params: Optional parameters for connection. """ - self.socket = socket - self.params = params - self.topic = topic - self._joined_once = False - self.bindings: Dict[str, List[Binding]] = {} - self.presence = SyncRealtimePresence(self) - self.state = ChannelStates.CLOSED - self._push_buffer: List[SyncPush] = [] - self.timeout = self.socket.timeout - self.params["config"] = { - "broadcast": {"ack": False, "self": False}, - "presence": {"key": ""}, - "private": False, - **params.get("config", {}), - } - - self.join_push = SyncPush(self, ChannelEvents.join, self.params) - self.rejoin_timer = SyncTimer( - self._rejoin_until_connected, lambda tries: 2**tries - ) - - self.broadcast_endpoint_url = self._broadcast_endpoint_url() - - def on_join_push_ok(payload: Dict[str, Any], *args): - self.state = ChannelStates.JOINED - self.rejoin_timer.reset() - for push in self._push_buffer: - asyncio.create_task(push.send()) - self._push_buffer = [] - - def on_join_push_timeout(*args): - if not self.is_joining: - return - - logging.error(f"join push timeout for channel {self.topic}") - self.state = ChannelStates.ERRORED - self.rejoin_timer.schedule_timeout() - - self.join_push.receive("ok", on_join_push_ok).receive( - "timeout", on_join_push_timeout - ) - - def on_close(*args): - logging.info(f"channel {self.topic} closed") - self.rejoin_timer.reset() - self.state = ChannelStates.CLOSED - self.socket._remove(self) - - def on_error(payload, *args): - if self.is_leaving or self.is_closed: - return - - logging.info(f"channel {self.topic} error: {payload}") - self.state = ChannelStates.ERRORED - self.rejoin_timer.schedule_timeout() - - self._on("close", on_close) - self._on("error", on_error) - - def on_reply(payload, ref): - self._trigger(self._reply_event_name(ref), payload) - - self._on(ChannelEvents.reply, on_reply) - - # Properties - @property - def is_closed(self): - return self.state == ChannelStates.CLOSED - - @property - def is_joining(self): - return self.state == ChannelStates.JOINING - - @property - def is_leaving(self): - return self.state == ChannelStates.LEAVING - - @property - def is_errored(self): - return self.state == ChannelStates.ERRORED - - @property - def is_joined(self): - return self.state == ChannelStates.JOINED - - @property - def join_ref(self): - return self.join_push.ref - - # Core channel methods - def subscribe( - self, - callback: Optional[ - Callable[[RealtimeSubscribeStates, Optional[Exception]], None] - ] = None, - ) -> SyncRealtimeChannel: - """ - Subscribe to the channel. - - :return: The Channel instance for method chaining. - """ - if not self.socket.is_connected: - self.socket.connect() - if self._joined_once: - raise Exception( - "Tried to subscribe multiple times. 'subscribe' can only be called a single time per channel instance" - ) - else: - config = self.params.get("config", {}) - broadcast = config.get("broadcast", {}) - presence = config.get("presence", {}) - private = config.get("private", False) - - access_token_payload = {} - config = { - "broadcast": broadcast, - "presence": presence, - "private": private, - "postgres_changes": list( - map(lambda x: x.filter, self.bindings.get("postgres_changes", [])) - ), - } - - if self.socket.access_token: - access_token_payload["access_token"] = self.socket.access_token - - self.join_push.update_payload( - {**{"config": config}, **access_token_payload} - ) - self._joined_once = True - - def on_join_push_ok(payload: Dict[str, Any]): - server_postgres_changes: List[Dict[str, Any]] = payload.get( - "postgres_changes", [] - ) - - if len(server_postgres_changes) == 0: - callback and callback(RealtimeSubscribeStates.SUBSCRIBED, None) - return - - client_postgres_changes = self.bindings.get("postgres_changes", []) - new_postgres_bindings = [] - - bindings_len = len(client_postgres_changes) - - for i in range(bindings_len): - client_binding = client_postgres_changes[i] - event = client_binding.filter.get("event") - schema = client_binding.filter.get("schema") - table = client_binding.filter.get("table") - filter = client_binding.filter.get("filter") - - server_binding = ( - server_postgres_changes[i] - if i < len(server_postgres_changes) - else None - ) - - if ( - server_binding - and server_binding.get("event") == event - and server_binding.get("schema") == schema - and server_binding.get("table") == table - and server_binding.get("filter") == filter - ): - client_binding.id = server_binding.get("id") - new_postgres_bindings.append(client_binding) - else: - asyncio.create_task(self.unsubscribe()) - callback and callback( - RealtimeSubscribeStates.CHANNEL_ERROR, - Exception( - "mismatch between server and client bindings for postgres changes" - ), - ) - return - - self.bindings["postgres_changes"] = new_postgres_bindings - callback and callback(RealtimeSubscribeStates.SUBSCRIBED, None) - - def on_join_push_error(payload: Dict[str, Any]): - callback and callback( - RealtimeSubscribeStates.CHANNEL_ERROR, - Exception(json.dumps(payload)), - ) - - def on_join_push_timeout(): - callback and callback(RealtimeSubscribeStates.TIMED_OUT, None) - - self.join_push.receive("ok", on_join_push_ok).receive( - "error", on_join_push_error - ).receive("timeout", on_join_push_timeout) - - self._rejoin() - - return self - - def unsubscribe(self): - self.state = ChannelStates.LEAVING - - self.rejoin_timer.reset() - self.join_push.destroy() - - def _close(*args): - logging.info(f"channel {self.topic} leave") - self._trigger(ChannelEvents.close, "leave") - - leave_push = SyncPush(self, ChannelEvents.leave, {}) - leave_push.receive("ok", _close).receive("timeout", _close) - - leave_push.send() - - if not self._can_push(): - leave_push.trigger("ok", {}) - - def push( - self, event: str, payload: Dict[str, Any], timeout: Optional[int] = None - ) -> SyncPush: - if not self._joined_once: - raise Exception( - f"tried to push '{event}' to '{self.topic}' before joining. Use channel.subscribe() before pushing events" - ) - - timeout = timeout or self.timeout - - push = SyncPush(self, event, payload, timeout) - if self._can_push(): - push.send() - else: - push.start_timeout() - self._push_buffer.append(push) - - return push - - def join(self) -> SyncRealtimeChannel: - """ - Coroutine that attempts to join Phoenix Realtime server via a certain topic. - - :return: Channel - """ - try: - self.socket._send( - { - "topic": self.topic, - "event": "phx_join", - "payload": {"config": self.channel_params}, - "ref": None, - } - ) - except Exception as e: - print(e) - return self - - # Event handling methods - def _on( - self, type: str, callback: Callback, filter: Dict[str, Any] = {} - ) -> SyncRealtimeChannel: - """ - Set up a listener for a specific event. - - :param type: The type of the event to listen for. - :param filter: Additional parameters for the event. - :param callback: The callback function to execute when the event is received. - :return: The Channel instance for method chaining. - """ - - type_lowercase = type.lower() - binding = Binding(type=type_lowercase, filter=filter, callback=callback) - self.bindings.setdefault(type_lowercase, []).append(binding) - - return self - - def _off(self, type: str, filter: Dict[str, Any]) -> SyncRealtimeChannel: - """ - Remove a listener for a specific event type and filter. - - :param type: The type of the event to remove the listener for. - :param filter: The filter associated with the listener to remove. - :return: The Channel instance for method chaining. - - This method removes all bindings for the specified event type that match - the given filter. If no matching bindings are found, the method does nothing. - """ - type_lowercase = type.lower() - - if type_lowercase in self.bindings: - self.bindings[type_lowercase] = [ - binding - for binding in self.bindings[type_lowercase] - if binding.filter != filter - ] - return self - - def on_broadcast( - self, event: str, callback: Callable[[Dict[str, Any]], None] - ) -> SyncRealtimeChannel: - """ - Set up a listener for a specific broadcast event. - - :param event: The name of the broadcast event to listen for. - :param callback: The callback function to execute when the event is received. - :return: The Channel instance for method chaining. - """ - return self._on( - "broadcast", - filter={"event": event}, - callback=lambda payload, _: callback(payload), - ) - - def on_postgres_changes( - self, - event: RealtimePostgresChangesListenEvent, - callback: Callable[[Dict[str, Any]], None], - table: str = "*", - schema: str = "public", - filter: Optional[str] = None, - ) -> SyncRealtimeChannel: - """ - Set up a listener for a specific Postgres changes event. - - :param event: The name of the Postgres changes event to listen for. - :param table: The table name for which changes should be monitored. - :param callback: The callback function to execute when the event is received. - :param schema: The database schema where the table exists. Default is 'public'. - :return: The Channel instance for method chaining. - """ - - binding_filter = {"event": event, "schema": schema, "table": table} - if filter: - binding_filter["filter"] = filter - - return self._on( - "postgres_changes", - filter=binding_filter, - callback=lambda payload, _: callback(payload), - ) - - # Presence methods - def track(self, user_status: Dict[str, Any]) -> None: - """ - Track a user's presence. - - :param user_status: User's presence status. - :return: None - """ - self.send_presence("track", user_status) - - def untrack(self) -> None: - """ - Untrack a user's presence. - - :return: None - """ - self.send_presence("untrack", {}) - - def presence_state(self) -> RealtimePresenceState: - return self.presence.state - - def on_presence_sync(self, callback: Callable[[], None]) -> SyncRealtimeChannel: - """ - Register a callback for presence sync events. - - :param callback: The callback function to execute when a presence sync event occurs. - :return: The Channel instance for method chaining. - """ - self.presence.on_sync(callback) - return self - - def on_presence_join(self, callback: PresenceOnJoinCallback) -> SyncRealtimeChannel: - """ - Register a callback for presence join events. - - :param callback: The callback function to execute when a presence join event occurs. - :return: The Channel instance for method chaining. - """ - self.presence.on_join(callback) - return self - - def on_presence_leave( - self, callback: PresenceOnLeaveCallback - ) -> SyncRealtimeChannel: - """ - Register a callback for presence leave events. - - :param callback: The callback function to execute when a presence leave event occurs. - :return: The Channel instance for method chaining. - """ - self.presence.on_leave(callback) - return self - - # Broadcast methods - def send_broadcast(self, event: str, data: Any) -> None: - """ - Sends a broadcast message to the current channel. - - :param event: The name of the broadcast event. - :param data: The data to be sent with the message. - :return: An asyncio.Future object representing the send operation. - """ - self.push( - ChannelEvents.broadcast, - {"type": "broadcast", "event": event, "payload": data}, - ) - - # Internal methods - def _broadcast_endpoint_url(self): - return f"{http_endpoint_url(self.socket.http_endpoint)}/api/broadcast" - - def _rejoin(self) -> None: - if self.is_leaving: - return - self.socket._leave_open_topic(self.topic) - self.state = ChannelStates.JOINING - self.join_push.resend() - - def _can_push(self): - return self.socket.is_connected and self._joined_once - - def send_presence(self, event: str, data: Any) -> None: - self.push(ChannelEvents.presence, {"event": event, "payload": data}) - - def _trigger(self, type: str, payload: Optional[Any], ref: Optional[str] = None): - type_lowercase = type.lower() - events = [ - ChannelEvents.close, - ChannelEvents.error, - ChannelEvents.leave, - ChannelEvents.join, - ] - - if ref is not None and type_lowercase in events and ref != self.join_push.ref: - return - - if type_lowercase in ["insert", "update", "delete"]: - postgres_changes = filter( - lambda binding: binding.filter.get("event", "").lower() - in [type_lowercase, "*"], - self.bindings.get("postgres_changes", []), - ) - for binding in postgres_changes: - binding.callback(payload, ref) - else: - bindings = self.bindings.get(type_lowercase, []) - for binding in bindings: - if type_lowercase in ["broadcast", "postgres_changes", "presence"]: - bind_id = binding.id - bind_event = ( - binding.filter.get("event", "").lower() - if binding.filter.get("event") - else None - ) - payload_event = ( - payload.get("event", "").lower() - if payload.get("event") - else None - ) - data_type = ( - payload.get("data", {}).get("type", "").lower() - if payload.get("data", {}).get("type") - else None - ) - if ( - bind_id - and bind_id in payload.get("ids", []) - and (bind_event == data_type or bind_event == "*") - ): - binding.callback(payload, ref) - elif bind_event in [payload_event, "*"]: - binding.callback(payload, ref) - elif binding.type == type_lowercase: - binding.callback(payload, ref) - - def _reply_event_name(self, ref: str): - return f"chan_reply_{ref}" - - def _rejoin_until_connected(self): - self.rejoin_timer.schedule_timeout() - if self.socket.is_connected: - self._rejoin() diff --git a/realtime/_sync/client.py b/realtime/_sync/client.py index 19af66f..79995af 100644 --- a/realtime/_sync/client.py +++ b/realtime/_sync/client.py @@ -1,37 +1,8 @@ -import asyncio -import json -import logging -import re -from functools import wraps -from typing import Any, Callable, Dict, List, Optional, Union +from typing import Any, Dict, List, Union -import websockets - -from ..exceptions import NotConnectedError -from ..message import Message -from ..transformers import http_endpoint_url -from ..types import ( - DEFAULT_TIMEOUT, - PHOENIX_CHANNEL, - Callback, - ChannelEvents, - T_ParamSpec, - T_Retval, -) from .channel import RealtimeChannelOptions, SyncRealtimeChannel -logger = logging.getLogger(__name__) - - -def ensure_connection(func: Callback): - @wraps(func) - def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: - if not args[0].is_connected: - raise NotConnectedError(func.__name__) - - return func(*args, **kwargs) - - return wrapper +NOT_IMPLEMENTED_MESSAGE = "This feature isn't available in the sync client. You can use the realtime feature in the async client only." class SyncRealtimeClient: @@ -57,145 +28,7 @@ def __init__( :param max_retries: Maximum number of reconnection attempts. Defaults to 5. :param initial_backoff: Initial backoff time (in seconds) for reconnection attempts. Defaults to 1.0. """ - self.url = f"{re.sub(r'https://', 'wss://', re.sub(r'http://', 'ws://', url, flags=re.IGNORECASE), flags=re.IGNORECASE)}/websocket?apikey={token}" - self.http_endpoint = http_endpoint_url(url) - self.is_connected = False - self.params = params - self.apikey = token - self.access_token = token - self.send_buffer: List[Callable] = [] - self.hb_interval = hb_interval - self.ws_connection: Optional[websockets.client.WebSocketClientProtocol] = None - self.ref = 0 - self.auto_reconnect = auto_reconnect - self.channels: Dict[str, SyncRealtimeChannel] = {} - self.max_retries = max_retries - self.initial_backoff = initial_backoff - self.timeout = DEFAULT_TIMEOUT - - def _listen(self) -> None: - """ - An infinite loop that keeps listening. - :return: None - """ - while True: - try: - msg = self.ws_connection.recv() - logger.info(f"receive: {msg}") - - msg = Message(**json.loads(msg)) - channel = self.channels.get(msg.topic) - - if channel: - channel._trigger(msg.event, msg.payload, msg.ref) - else: - logger.info(f"Channel {msg.topic} not found") - - except websockets.exceptions.ConnectionClosed: - if self.auto_reconnect: - logger.info("Connection with server closed, trying to reconnect...") - self._connect() - for topic, channel in self.channels.items(): - channel.join() - else: - logger.exception("Connection with the server closed.") - break - - def connect(self) -> None: - """ - Establishes a WebSocket connection with exponential backoff retry mechanism. - - This method attempts to connect to the WebSocket server. If the connection fails, - it will retry with an exponential backoff strategy up to a maximum number of retries. - - Returns: - None - - Raises: - Exception: If unable to establish a connection after max_retries attempts. - - Note: - - The initial backoff time and maximum retries are set during RealtimeClient initialization. - - The backoff time doubles after each failed attempt, up to a maximum of 60 seconds. - """ - retries = 0 - backoff = self.initial_backoff - - while retries < self.max_retries: - try: - self.ws_connection = websockets.connect(self.url) - if self.ws_connection.open: - logger.info("Connection was successful") - return self._on_connect() - else: - raise Exception("Failed to open WebSocket connection") - except Exception as e: - retries += 1 - if retries >= self.max_retries or not self.auto_reconnect: - logger.error( - f"Failed to establish WebSocket connection after {retries} attempts: {e}" - ) - raise - else: - wait_time = backoff * (2 ** (retries - 1)) # Exponential backoff - logger.info( - f"Connection attempt {retries} failed. Retrying in {wait_time:.2f} seconds..." - ) - asyncio.sleep(wait_time) - backoff = min(backoff * 2, 60) # Cap the backoff at 60 seconds - - raise Exception( - f"Failed to establish WebSocket connection after {self.max_retries} attempts" - ) - def listen(self): - asyncio.gather(self._listen(), self._heartbeat()) - - def _on_connect(self): - self.is_connected = True - self._flush_send_buffer() - - def _flush_send_buffer(self): - if self.is_connected and len(self.send_buffer) > 0: - for callback in self.send_buffer: - callback() - self.send_buffer = [] - - @ensure_connection - def close(self) -> None: - """ - Close the WebSocket connection. - - Returns: - None - - Raises: - NotConnectedError: If the connection is not established when this method is called. - """ - - self.ws_connection.close() - self.is_connected = False - - def _heartbeat(self) -> None: - while self.is_connected: - try: - data = dict( - topic=PHOENIX_CHANNEL, - event=ChannelEvents.heartbeat, - payload={}, - ref=None, - ) - self.send(data) - asyncio.sleep(self.hb_interval) - except websockets.exceptions.ConnectionClosed: - if self.auto_reconnect: - logger.info("Connection with server closed, trying to reconnect...") - self._connect() - else: - logger.exception("Connection with the server closed.") - break - - @ensure_connection def channel( self, topic: str, params: RealtimeChannelOptions = {} ) -> SyncRealtimeChannel: @@ -203,14 +36,10 @@ def channel( :param topic: Initializes a channel and creates a two-way association with the socket :return: Channel """ - topic = f"realtime:{topic}" - chan = SyncRealtimeChannel(self, topic, params) - self.channels[topic] = chan - - return chan + raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE) def get_channels(self) -> List[SyncRealtimeChannel]: - return list(self.channels.values()) + raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE) def remove_channel(self, channel: SyncRealtimeChannel) -> None: """ @@ -218,30 +47,14 @@ def remove_channel(self, channel: SyncRealtimeChannel) -> None: :param channel: Channel to remove :return: None """ - if channel.topic in self.channels: - self.channels[channel.topic].unsubscribe() - del self.channels[channel.topic] - - if len(self.channels) == 0: - self.close() + raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE) def remove_all_channels(self) -> None: """ Unsubscribes and removes all channels from the socket :return: None """ - for _, channel in self.channels.items(): - channel.unsubscribe() - - self.close() - - def summary(self) -> None: - """ - Prints a list of topics and event the socket is listening to - :return: None - """ - for topic, channel in self.channels.items(): - print(f"Topic: {topic} | Events: {[e for e, _ in channel.listeners]}]") + raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE) def set_auth(self, token: Union[str, None]) -> None: """ @@ -256,52 +69,3 @@ def set_auth(self, token: Union[str, None]) -> None: Returns: None """ - self.access_token = token - - for _, channel in self.channels.items(): - if channel._joined_once and channel.is_joined: - channel.push(ChannelEvents.access_token, {"access_token": token}) - - def _make_ref(self) -> str: - self.ref += 1 - return f"{self.ref}" - - def send(self, message: Dict[str, Any]) -> None: - """ - Send a message through the WebSocket connection. - - This method serializes the given message dictionary to JSON, - and sends it through the WebSocket connection. If the connection - is not currently established, the message will be buffered and sent - once the connection is re-established. - - Args: - message (Dict[str, Any]): The message to be sent, as a dictionary. - - Returns: - None - - Raises: - websockets.exceptions.WebSocketException: If there's an error sending the message. - """ - - message = json.dumps(message) - logging.info(f"send: {message}") - - def send_message(): - self.ws_connection.send(message) - - if self.is_connected: - send_message() - else: - self.send_buffer.append(send_message) - - def _leave_open_topic(self, topic: str): - dup_channels = [ - ch - for ch in self.channels.values() - if ch.topic == topic and (ch.is_joined or ch.is_joining) - ] - - for ch in dup_channels: - ch.unsubscribe() diff --git a/realtime/_sync/presence.py b/realtime/_sync/presence.py index 37fe0d0..db02a1c 100644 --- a/realtime/_sync/presence.py +++ b/realtime/_sync/presence.py @@ -2,234 +2,11 @@ Defines the RealtimePresence class and its dependencies. """ -from typing import Any, Callable, Dict, List, Optional, Union +from typing import Optional -from ..types import ( - PresenceDiff, - PresenceEvents, - PresenceOnJoinCallback, - PresenceOnLeaveCallback, - PresenceOpts, - RawPresenceDiff, - RawPresenceState, - RealtimePresenceState, -) +from ..types import PresenceOpts class SyncRealtimePresence: def __init__(self, channel, opts: Optional[PresenceOpts] = None): - self.channel = channel - self.state: RealtimePresenceState = {} - self.pending_diffs: List[RawPresenceDiff] = [] - self.join_ref: Optional[str] = None - self.caller = { - "onJoin": lambda *args: None, - "onLeave": lambda *args: None, - "onSync": lambda: None, - "onAuthSuccess": lambda: None, - "onAuthFailure": lambda: None, - } - # Initialize with default events if not provided - events = ( - opts.events - if opts - else PresenceEvents(state="presence_state", diff="presence_diff") - ) - # Set up event listeners for presence state and diff - self.channel._on(events.state, callback=self._on_state_event) - self.channel._on(events.diff, callback=self._on_diff_event) - self.channel._on("phx_auth", callback=self._on_auth_event) - - def on_join(self, callback: PresenceOnJoinCallback): - self.caller["onJoin"] = callback - - def on_leave(self, callback: PresenceOnLeaveCallback): - self.caller["onLeave"] = callback - - def on_sync(self, callback: Callable[[], None]): - self.caller["onSync"] = callback - - def on_auth_success(self, callback: Callable[[], None]): - self.caller["onAuthSuccess"] = callback - - def on_auth_failure(self, callback: Callable[[], None]): - self.caller["onAuthFailure"] = callback - - def _on_state_event(self, payload: RawPresenceState, *args): - onJoin = self.caller["onJoin"] - onLeave = self.caller["onLeave"] - onSync = self.caller["onSync"] - - self.join_ref = self.channel.join_ref - self.state = self._sync_state(self.state, payload, onJoin, onLeave) - - for diff in self.pending_diffs: - self.state = self._sync_diff(self.state, diff, onJoin, onLeave) - self.pending_diffs = [] - onSync() - - def _on_diff_event(self, payload: Dict[str, Any], *args): - onJoin = self.caller["onJoin"] - onLeave = self.caller["onLeave"] - onSync = self.caller["onSync"] - - if self.in_pending_sync_state(): - self.pending_diffs.append(payload) - else: - self.state = self._sync_diff(self.state, payload, onJoin, onLeave) - onSync() - - def _on_auth_event(self, payload: Dict[str, Any], *args): - if payload.get("status") == "ok": - self.caller["onAuthSuccess"]() - else: - self.caller["onAuthFailure"]() - - def _sync_state( - self, - current_state: RealtimePresenceState, - new_state: Union[RawPresenceState, RealtimePresenceState], - onJoin: PresenceOnJoinCallback, - onLeave: PresenceOnLeaveCallback, - ) -> RealtimePresenceState: - state = {key: list(value) for key, value in current_state.items()} - transformed_state = SyncRealtimePresence._transform_state(new_state) - - joins: Dict[str, Any] = {} - leaves: Dict[str, Any] = { - k: v for k, v in state.items() if k not in transformed_state - } - - for key, value in transformed_state.items(): - current_presences = state.get(key, []) - - if len(current_presences) > 0: - new_presence_refs = {presence.get("presence_ref") for presence in value} - cur_presence_refs = { - presence.get("presence_ref") for presence in current_presences - } - - joined_presences = [ - p for p in value if p.get("presence_ref") not in cur_presence_refs - ] - left_presences = [ - p - for p in current_presences - if p.get("presence_ref") not in new_presence_refs - ] - - if joined_presences: - joins[key] = joined_presences - if left_presences: - leaves[key] = left_presences - else: - joins[key] = value - - return self._sync_diff( - state, {"joins": joins, "leaves": leaves}, onJoin, onLeave - ) - - def _sync_diff( - self, - state: RealtimePresenceState, - diff: Union[RawPresenceDiff, PresenceDiff], - onJoin: PresenceOnJoinCallback, - onLeave: PresenceOnLeaveCallback, - ) -> RealtimePresenceState: - joins = SyncRealtimePresence._transform_state(diff.get("joins", {})) - leaves = SyncRealtimePresence._transform_state(diff.get("leaves", {})) - - for key, new_presences in joins.items(): - current_presences = state.get(key, []) - state[key] = new_presences - - if len(current_presences) > 0: - joined_presence_refs = { - presence.get("presence_ref") for presence in state.get(key) - } - cur_presences = list( - presence - for presence in current_presences - if presence.get("presence_ref") not in joined_presence_refs - ) - state[key] = cur_presences + state[key] - - onJoin(key, current_presences, new_presences) - - for key, left_presences in leaves.items(): - current_presences = state.get(key, []) - - if len(current_presences) == 0: - break - - presence_refs_to_remove = { - presence.get("presence_ref") for presence in left_presences - } - current_presences = [ - presence - for presence in current_presences - if presence.get("presence_ref") not in presence_refs_to_remove - ] - state[key] = current_presences - - onLeave(key, current_presences, left_presences) - - if len(current_presences) == 0: - del state[key] - - return state - - def in_pending_sync_state(self) -> bool: - return self.join_ref is None or self.join_ref != self.channel.join_ref - - @staticmethod - def _transform_state( - state: Union[RawPresenceState, RealtimePresenceState] - ) -> RealtimePresenceState: - """ - Transform the raw presence state into a standardized RealtimePresenceState format. - - This method processes the input state, which can be either a RawPresenceState or - an already transformed RealtimePresenceState. It handles the conversion of the - Phoenix channel's presence format to our internal representation. - - Args: - state (Union[RawPresenceState, RealtimePresenceState[T]]): The presence state to transform. - - Returns: - RealtimePresenceState[T]: The transformed presence state. - - Example: - Input (RawPresenceState): - { - "user1": { - "metas": [ - {"phx_ref": "ABC123", "user_id": "user1", "status": "online"}, - {"phx_ref": "DEF456", "phx_ref_prev": "ABC123", "user_id": "user1", "status": "away"} - ] - }, - "user2": [{"user_id": "user2", "status": "offline"}] - } - - Output (RealtimePresenceState): - { - "user1": [ - {"presence_ref": "ABC123", "user_id": "user1", "status": "online"}, - {"presence_ref": "DEF456", "user_id": "user1", "status": "away"} - ], - "user2": [{"user_id": "user2", "status": "offline"}] - } - """ - new_state: RealtimePresenceState = {} - for key, presences in state.items(): - if isinstance(presences, dict) and "metas" in presences: - new_state[key] = [] - - for presence in presences["metas"]: - presence["presence_ref"] = presence.pop("phx_ref", None) - presence.pop("phx_ref_prev", None) - new_state[key].append(presence) - - else: - new_state[key] = presences - return new_state + pass diff --git a/realtime/_sync/push.py b/realtime/_sync/push.py deleted file mode 100644 index 174c920..0000000 --- a/realtime/_sync/push.py +++ /dev/null @@ -1,120 +0,0 @@ -import asyncio -import logging -from typing import TYPE_CHECKING, Any, Dict, List, Optional - -from ..types import DEFAULT_TIMEOUT, Callback, _Hook - -if TYPE_CHECKING: - from .channel import SyncRealtimeChannel - - -class SyncPush: - def __init__( - self, - channel: "SyncRealtimeChannel", - event: str, - payload: Dict[str, Any] = {}, - timeout: int = DEFAULT_TIMEOUT, - ): - self.channel = channel - self.event = event - self.payload = payload - self.timeout = timeout - self.rec_hooks: List[_Hook] = [] - self.ref: Optional[str] = None - self.ref_event: Optional[str] = None - self.received_resp: Optional[Dict[str, Any]] = None - self.sent = False - self.timeout_task: Optional[asyncio.Task] = None - - def resend(self): - self._cancel_ref_event() - self.ref = "" - self.ref_event = None - self.received_resp = None - self.sent = False - self.send() - - def send(self): - if self._has_received("timeout"): - return - - self.start_timeout() - self.sent = True - - try: - self.channel.socket.send( - { - "topic": self.channel.topic, - "event": self.event, - "payload": self.payload, - "ref": self.ref, - "join_ref": self.channel.join_push.ref, - } - ) - except Exception as e: - logging.error(f"send push failed: {e}") - - def update_payload(self, payload: Dict[str, Any]): - self.payload = {**self.payload, **payload} - - def receive(self, status: str, callback: Callback) -> "SyncPush": - if self._has_received(status): - callback(self.received_resp.get("response", {})) - - self.rec_hooks.append(_Hook(status, callback)) - return self - - def start_timeout(self): - if self.timeout_task: - return - - self.ref = self.channel.socket._make_ref() - self.ref_event = self.channel._reply_event_name(self.ref) - - def on_reply(payload, *args): - self._cancel_ref_event() - self._cancel_timeout() - self.received_resp = payload - self._match_receive(**self.received_resp) - - self.channel._on(self.ref_event, on_reply) - - def timeout(self): - asyncio.sleep(self.timeout) - self.trigger("timeout", {}) - - self.timeout_task = asyncio.create_task(timeout(self)) - - def trigger(self, status: str, response: Any): - if self.ref_event: - payload = { - "status": status, - "response": response, - } - self.channel._trigger(self.ref_event, payload) - - def destroy(self): - self._cancel_ref_event() - self._cancel_timeout() - - def _cancel_ref_event(self): - if not self.ref_event: - return - - self.channel._off(self.ref_event, {}) - - def _cancel_timeout(self): - if not self.timeout_task: - return - - self.timeout_task.cancel() - self.timeout_task = None - - def _match_receive(self, status: str, response: Any): - for hook in self.rec_hooks: - if hook.status == status: - hook.callback(response) - - def _has_received(self, status: str): - return self.received_resp and self.received_resp.get("status") == status diff --git a/realtime/_sync/timer.py b/realtime/_sync/timer.py deleted file mode 100644 index f32773b..0000000 --- a/realtime/_sync/timer.py +++ /dev/null @@ -1,26 +0,0 @@ -import asyncio -from typing import Callable - - -class SyncTimer: - def __init__(self, callback: Callable, timer_calc: Callable[[int], int]): - self.callback = callback - self.timer_calc = timer_calc - self.timer = None - self.tries = 0 - - def reset(self): - self.tries = 0 - if self.timer: - self.timer.cancel() - - def schedule_timeout(self): - if self.timer: - self.timer.cancel() - - self.timer = asyncio.create_task(self._run_timer()) - - def _run_timer(self): - asyncio.sleep(self.timer_calc(self.tries + 1)) - self.tries += 1 - self.callback()