From 5f0546f32ee497ddbe0a102eee54537fbd81a1fb Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 15 Nov 2024 14:53:51 -0800 Subject: [PATCH] what am i doing anymore --- poetry.lock | 359 ++-- pyproject.toml | 3 +- reflex/.templates/web/utils/state.js | 27 + reflex/components/core/cond.py | 22 +- reflex/vars/base.py | 285 ++- reflex/vars/function.py | 245 ++- reflex/vars/number.py | 180 +- reflex/vars/object.py | 37 +- reflex/vars/sequence.py | 2074 +++++++------------ tests/units/components/core/test_foreach.py | 8 +- 10 files changed, 1498 insertions(+), 1742 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8199c975a..a954cc627 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "alembic" @@ -386,73 +386,73 @@ files = [ [[package]] name = "coverage" -version = "7.6.4" +version = "7.6.5" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, - {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, - {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, - {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, - {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, - {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, - {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, - {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, - {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, - {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, - {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, - {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, - {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, - {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, - {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, - {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, + {file = "coverage-7.6.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5fc459f1b62aa328b5c6943b4fa060fa63e7749e41c974929c503dc01d0527b"}, + {file = "coverage-7.6.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:197fc6b5e6271c4f822486cabbd91f32e73f784076b69c91179c5a9fec2d1442"}, + {file = "coverage-7.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7cab0762dfbf0b0cd6eb22f7bceade31bda0f0647f9420cbb45571de4493a3"}, + {file = "coverage-7.6.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee4559597f53455d70b9935e25c21fd05aebbb8d540af04097f7cf6dc7562754"}, + {file = "coverage-7.6.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e68b894ee1a170da94b7da381527f277ec00c67f6141e79aa1ce8eebbb5561"}, + {file = "coverage-7.6.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe4ea637711f1f1895895578972e3d0ed5efb6ef970ba0e2e26d9fad1e3c820e"}, + {file = "coverage-7.6.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1d5f036235a747cd30be433ef7ba6dab5ac41d8dc69d54094d5438c34fe8d565"}, + {file = "coverage-7.6.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a6ab7b88b1a614bc1db015e68048eb29b0c30ffa01be3d7d04da1f320db0f01"}, + {file = "coverage-7.6.5-cp310-cp310-win32.whl", hash = "sha256:ad712a72cd734fb4265041005011bbf61f8d6cba74e12c91f14a9cda63a80a64"}, + {file = "coverage-7.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:61e03bb66c087b74aea6c28d10a49f72eca98b95438a8db1ae6dfcdd060f9039"}, + {file = "coverage-7.6.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dffec9f67f4eb8bc9c5df720833f1f1ca36b73d86e6f95b422ca5210e264cc26"}, + {file = "coverage-7.6.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2fde790ac0024af19fc5327fd50890dad0c31b653f6d2ed91ab2810c046bfe22"}, + {file = "coverage-7.6.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3250186381ec8e9b71234fb92ef77da87d81cbf20df3364f8f5ebf7180ec030d"}, + {file = "coverage-7.6.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ecfa205ce1fab6d8e94fe011eec04f6035a6069f70c331efd7cd1cd2d33d897"}, + {file = "coverage-7.6.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15af7bfbc37de33e7df3f740cc735057606c63bbe44aee8b07339a3e7bb8ecf6"}, + {file = "coverage-7.6.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:caf4d6af23af0e0df4e40e9985f6063d7f5434f225ee4d4ed7001f1428302403"}, + {file = "coverage-7.6.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5dcf2da597fe616a41c59e29fd8d390ac2149aeed421172eef14470c7e9dcd06"}, + {file = "coverage-7.6.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebc76107d896a53116e5ef21998f321b630b574a65b78b01176ca64e8978b43e"}, + {file = "coverage-7.6.5-cp311-cp311-win32.whl", hash = "sha256:0e9e4cd48dca252d99bb97b14f13b5940813937cc7ec568418c1a195dec9cbcc"}, + {file = "coverage-7.6.5-cp311-cp311-win_amd64.whl", hash = "sha256:a6eb14739a20c5a46073c8ad066ada17d91d14599ed98d724614db46fbae867b"}, + {file = "coverage-7.6.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9ae01c434cb0d445008257bb42dcd38112190e5bfc3a4480fde49572b16bc2ae"}, + {file = "coverage-7.6.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c72ef3be899f389c9f0934a9d06a28fa097ade096760102c732583c04cc31d75"}, + {file = "coverage-7.6.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2fc574b4fb082a0141d4df00079c4877d46cb98e8ec979cbd9a92426f5abd8a"}, + {file = "coverage-7.6.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1bc0eba158ad9d1883efb4f1bf08f88a999e091daf30454fd5f136322e700c72"}, + {file = "coverage-7.6.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a360b282c0acbf3541cc67e8d8a2a65589ea6cfa10c7e8a48e318bf28ca90f94"}, + {file = "coverage-7.6.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b22f96d3f2425942a649d786f57ae431425c9a970afae784cd865c1ffee34bad"}, + {file = "coverage-7.6.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:70eca9c6bf742feaf3ee453c1aaa932c2ab88ca420f411d90aa43ae831127b22"}, + {file = "coverage-7.6.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c4bafec5da3498d498a4ca3136f5a01fded487c6a54f18aea0bcd673feedf1b"}, + {file = "coverage-7.6.5-cp312-cp312-win32.whl", hash = "sha256:edecf498cabb335e8a683eb672558355bb9536d4397c54f1e135d9b8910512a3"}, + {file = "coverage-7.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:e7c40ae56761d3c08f916019b2f8579a147f93be8e12f0f2bf4edc4ea9e1c0ab"}, + {file = "coverage-7.6.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:49ea4a739dc14856d7c5f935da90db123b77a850cfddcfacb490a28de8f87257"}, + {file = "coverage-7.6.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0c51339a28aa43d0f2b1211e57ceeeeed5e09f4deb6fc543d939de68069e81e"}, + {file = "coverage-7.6.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:040c3d5cf4db24e7cb890bf4b547a25bd3a3516c58c9f2a22f822199ee2ad8ed"}, + {file = "coverage-7.6.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0b7e67f9d3b156ab93fce71485fadd043ab04b45d5d88623c6d94f7d16ced5b"}, + {file = "coverage-7.6.5-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e078bfb114025c55fdbaa802f4c13e20e6ce4e10a96918d7234656b41f69e649"}, + {file = "coverage-7.6.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:559cdb21aca30810e648ac08270535c1d2e17226ebbdf90860a060d3680cb05f"}, + {file = "coverage-7.6.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:23e2dd956277061f24d9eda7539113a9c35a9409a9935647a34ced79b8aacb75"}, + {file = "coverage-7.6.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e7c4ccb41dc9830b2ca8592e401045a81740f627c7c0348bdc3b7373ce52f8e"}, + {file = "coverage-7.6.5-cp313-cp313-win32.whl", hash = "sha256:9d3565bb7deaa12d634426f113e6b106028c535667ba7756af65f00464981ba5"}, + {file = "coverage-7.6.5-cp313-cp313-win_amd64.whl", hash = "sha256:5039410420d9ddcd5b8566d3afbb28b89d70c4481dbb283ea543263cbefa2b67"}, + {file = "coverage-7.6.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:77b640aa78d4d9f620fb2e1b2a41b0d196120c188d0a7f678761d668d6251fcc"}, + {file = "coverage-7.6.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bb3799f6279df37e369027128926de4c159e6399000316ebd7a69e55b84dc97f"}, + {file = "coverage-7.6.5-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55aba7ab64e8af37a18064f23f399dff10041fa3aaf201528f12004968638b9f"}, + {file = "coverage-7.6.5-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6065a988d724dd3328cb21e97378bef0549b2f8b7ac0a3376785d9f7f05dc736"}, + {file = "coverage-7.6.5-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f092d222e4286cdd1ab9707da36944c11ba6294d8c9b18534057f03e6866367"}, + {file = "coverage-7.6.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1dc99aece5f899955eece053a798e279f7fe7059dd5e2a95af82878cfe4a44e1"}, + {file = "coverage-7.6.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1b14515f83ffa7a6787e725d804c6b11dd317a6bd0373d8519a61e4a587fe534"}, + {file = "coverage-7.6.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9fa6d90130165346935541f3762933dae07e237ff7d6d780fae556039f08a470"}, + {file = "coverage-7.6.5-cp313-cp313t-win32.whl", hash = "sha256:1be9ec4c49becb35955b9d69c27e6385aedd40d233f1cf065e8430c59924b30e"}, + {file = "coverage-7.6.5-cp313-cp313t-win_amd64.whl", hash = "sha256:7ff4fd7679df56e36fc838ef227e95e3aa1b0ca0548daede7f8ae6e54479c115"}, + {file = "coverage-7.6.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:23abf0846290aa57d629c4f4181d0d56cbaa45d3999e60cb0df1d2bab7bc6bfe"}, + {file = "coverage-7.6.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4903685e8059e170182ac4681ee72d2dfbb92692225023c1e325a9d85c1be31"}, + {file = "coverage-7.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ad9621fd9773b1461f8942da4130fbb16ee0a877eb58bc57532ea41cce20d3e"}, + {file = "coverage-7.6.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7324358a77f37ffd8ba94d3c8326eb316c972ec72264f36fc3be04cff8542465"}, + {file = "coverage-7.6.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf182001229411cd6a90d180973b345bd6fe255dbbac362100e6a625dfb107f5"}, + {file = "coverage-7.6.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4601dacd88556c94c9fb5063b9354b1fe971af9a5b25b2575faefd12bf8170a5"}, + {file = "coverage-7.6.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e5aa3d62285ef1b16f655e1ae298c6fa919209637d317934e382e9b99c28c118"}, + {file = "coverage-7.6.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8cb5601620c3d98d2c98847272acc2406333d43c9d7d49386d879bd451677429"}, + {file = "coverage-7.6.5-cp39-cp39-win32.whl", hash = "sha256:c32428f6285344caedd945236f31c46645bb10faae8702d1409bb49df218e55a"}, + {file = "coverage-7.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:809e868eee27d056bc72590c69940c119775d218681b1a8ef9ba0ef8d7693e53"}, + {file = "coverage-7.6.5-pp39.pp310-none-any.whl", hash = "sha256:49145276f39f940b18a539e1e4a378e06c64a127922450ffd2fb82b9fe1ad3d9"}, + {file = "coverage-7.6.5.tar.gz", hash = "sha256:6069188329fbe0a63876719099076261ce7a1adeea95bf236cff4353a8451b0d"}, ] [package.dependencies] @@ -585,13 +585,13 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.115.4" +version = "0.115.5" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.4-py3-none-any.whl", hash = "sha256:0b504a063ffb3cf96a5e27dc1bc32c80ca743a2528574f9cdc77daa2d31b4742"}, - {file = "fastapi-0.115.4.tar.gz", hash = "sha256:db653475586b091cb8b2fec2ac54a680ac6a158e07406e1abae31679e8826349"}, + {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, + {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, ] [package.dependencies] @@ -785,13 +785,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "identify" -version = "2.6.1" +version = "2.6.2" description = "File identification library for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, - {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, + {file = "identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3"}, + {file = "identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd"}, ] [package.extras] @@ -1288,13 +1288,13 @@ attrs = ">=19.2.0" [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -1350,8 +1350,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, ] python-dateutil = ">=2.8.2" @@ -1669,8 +1669,8 @@ files = [ annotated-types = ">=0.6.0" pydantic-core = "2.23.4" typing-extensions = [ - {version = ">=4.6.1", markers = "python_version < \"3.13\""}, {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, ] [package.extras] @@ -1822,21 +1822,23 @@ files = [ [[package]] name = "pyright" -version = "1.1.334" +version = "1.1.389" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.334-py3-none-any.whl", hash = "sha256:dcb13e8358e021189672c4d6ebcad192ab061e4c7225036973ec493183c6da68"}, - {file = "pyright-1.1.334.tar.gz", hash = "sha256:3adaf10f1f4209575dc022f9c897f7ef024639b7ea5b3cbe49302147e6949cd4"}, + {file = "pyright-1.1.389-py3-none-any.whl", hash = "sha256:41e9620bba9254406dc1f621a88ceab5a88af4c826feb4f614d95691ed243a60"}, + {file = "pyright-1.1.389.tar.gz", hash = "sha256:716bf8cc174ab8b4dcf6828c3298cac05c5ed775dda9910106a5dcfe4c7fe220"}, ] [package.dependencies] nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" [package.extras] -all = ["twine (>=3.4.1)"] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] [[package]] name = "pysocks" @@ -2350,23 +2352,23 @@ websocket-client = ">=1.8,<2.0" [[package]] name = "setuptools" -version = "75.3.0" +version = "75.5.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, - {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, + {file = "setuptools-75.5.0-py3-none-any.whl", hash = "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829"}, + {file = "setuptools-75.5.0.tar.gz", hash = "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib-metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] 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)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] [[package]] name = "shellingham" @@ -2634,13 +2636,13 @@ files = [ [[package]] name = "tomli" -version = "2.0.2" +version = "2.1.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, ] [[package]] @@ -2828,108 +2830,91 @@ test = ["websockets"] [[package]] name = "websockets" -version = "13.1" +version = "14.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, - {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, - {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, - {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, - {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, - {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, - {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, - {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, - {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, - {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, - {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, - {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, - {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, - {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, - {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, - {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, - {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, - {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, - {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, - {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, - {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, - {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, - {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, - {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, - {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, + {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"}, + {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"}, + {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"}, + {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"}, + {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"}, + {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"}, + {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"}, + {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"}, + {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"}, + {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"}, + {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"}, + {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"}, + {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"}, + {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"}, + {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"}, + {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"}, + {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"}, + {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"}, + {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"}, ] [[package]] name = "wheel" -version = "0.44.0" +version = "0.45.0" description = "A built-package format for Python" optional = false python-versions = ">=3.8" files = [ - {file = "wheel-0.44.0-py3-none-any.whl", hash = "sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f"}, - {file = "wheel-0.44.0.tar.gz", hash = "sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49"}, + {file = "wheel-0.45.0-py3-none-any.whl", hash = "sha256:52f0baa5e6522155090a09c6bd95718cc46956d1b51d537ea5454249edb671c7"}, + {file = "wheel-0.45.0.tar.gz", hash = "sha256:a57353941a3183b3d5365346b567a260a0602a0f8a635926a7dede41b94c674a"}, ] [package.extras] @@ -3030,13 +3015,13 @@ h11 = ">=0.9.0,<1" [[package]] name = "zipp" -version = "3.20.2" +version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] @@ -3050,4 +3035,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "937f0cadb1a4566117dad8d0be6018ad1a8fe9aeb19c499d2a010d36ef391ee1" +content-hash = "8efad13f953b1865334876fa726e321ea73c0ca6591553ecf5d562425a2c52ee" diff --git a/pyproject.toml b/pyproject.toml index 20bf81d92..e49dcae9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ reflex-chakra = ">=0.6.0" [tool.poetry.group.dev.dependencies] pytest = ">=7.1.2,<9.0" pytest-mock = ">=3.10.0,<4.0" -pyright = ">=1.1.229,<1.1.335" +pyright = ">=1.1.229,<=1.1.389" darglint = ">=1.8.1,<2.0" dill = ">=0.3.8" toml = ">=0.10.2,<1.0" @@ -88,6 +88,7 @@ requires = ["poetry-core>=1.5.1"] build-backend = "poetry.core.masonry.api" [tool.pyright] +reportIncompatibleMethodOverride = false [tool.ruff] target-version = "py39" diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 3899ddc89..5a39f59e1 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -859,6 +859,33 @@ export const isTrue = (val) => { return Boolean(val); }; +/** + * Returns a copy of a section of an array. + * @param {Array | string} arrayLike The array to slice. + * @param {[number, number, number]} slice The slice to apply. + * @returns The sliced array. + */ +export const atSlice = (arrayLike, slice) => { + const array = [...arrayLike]; + const [startSlice, endSlice, stepSlice] = slice; + if (stepSlice ?? null === null) { + return array.slice(startSlice ?? undefined, endSlice ?? undefined); + } + const step = stepSlice ?? 1; + if (step > 0) { + return array + .slice(startSlice ?? undefined, endSlice ?? undefined) + .filter((_, i) => i % step === 0); + } + const actualStart = (endSlice ?? null) === null ? 0 : endSlice + 1; + const actualEnd = + (startSlice ?? null) === null ? array.length : startSlice + 1; + return array + .slice(actualStart, actualEnd) + .reverse() + .filter((_, i) => i % step === 0); +}; + /** * Get the value from a ref. * @param ref The ref to get the value from. diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 5b6ee2a7f..19b585a47 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -138,34 +138,18 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var: """ # Convert the condition to a Var. cond_var = LiteralVar.create(condition) - if cond_var is None: - raise ValueError("The condition must be set.") - # If the first component is a component, create a Cond component. + # If the first component is a component, create a Fragment if the second component is not set. if isinstance(c1, BaseComponent): - if c2 is not None and not isinstance(c2, BaseComponent): - raise ValueError("Both arguments must be components.") - return Cond.create(cond_var, c1, c2) + c2 = c2 if c2 is not None else Fragment.create() - # Otherwise, create a conditional Var. # Check that the second argument is valid. - if isinstance(c2, BaseComponent): - raise ValueError("Both arguments must be props.") if c2 is None: raise ValueError("For conditional vars, the second argument must be set.") - def create_var(cond_part): - return LiteralVar.create(cond_part) - - # convert the truth and false cond parts into vars so the _var_data can be obtained. - c1 = create_var(c1) - c2 = create_var(c2) - # Create the conditional var. return ternary_operation( - cond_var.bool()._replace( # type: ignore - merge_var_data=VarData(imports=_IS_TRUE_IMPORT), - ), # type: ignore + cond_var.bool(), c1, c2, ) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index a3f8357b2..8945d130b 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -26,7 +26,9 @@ from typing import ( Iterable, List, Literal, + NoReturn, Optional, + Sequence, Set, Tuple, Type, @@ -69,6 +71,7 @@ from reflex.utils.types import ( _isinstance, get_origin, has_args, + typehint_issubclass, unionize, ) @@ -82,6 +85,9 @@ if TYPE_CHECKING: VAR_TYPE = TypeVar("VAR_TYPE", covariant=True) +VALUE = TypeVar("VALUE") +INT_OR_FLOAT = TypeVar("INT_OR_FLOAT", int, float) +FAKE_VAR_TYPE = TypeVar("FAKE_VAR_TYPE") OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE") warnings.filterwarnings("ignore", message="fields may not start with an underscore") @@ -545,11 +551,11 @@ class Var(Generic[VAR_TYPE]): @classmethod def create( cls, - value: Any, + value: FAKE_VAR_TYPE, _var_is_local: bool | None = None, _var_is_string: bool | None = None, _var_data: VarData | None = None, - ) -> Var: + ) -> Var[FAKE_VAR_TYPE]: """Create a var from a value. Args: @@ -587,7 +593,7 @@ class Var(Generic[VAR_TYPE]): return LiteralVar.create(value) if _var_is_string is False or _var_is_local is True: - return cls( + return Var( _js_expr=value, _var_data=_var_data, ) @@ -629,19 +635,22 @@ class Var(Generic[VAR_TYPE]): return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}" @overload - def to(self, output: Type[str]) -> StringVar: ... + def to(self, output: Type[bool]) -> BooleanVar: ... # pyright: ignore [reportOverlappingOverload] @overload - def to(self, output: Type[bool]) -> BooleanVar: ... + def to(self, output: Type[int]) -> NumberVar[int]: ... @overload - def to(self, output: type[int] | type[float]) -> NumberVar: ... + def to(self, output: type[float]) -> NumberVar[float]: ... + + @overload + def to(self, output: Type[str]) -> StringVar: ... # pyright: ignore [reportOverlappingOverload] @overload def to( self, - output: type[list] | type[tuple] | type[set], - ) -> ArrayVar: ... + output: type[Sequence[VALUE]] | type[set[VALUE]], + ) -> ArrayVar[Sequence[VALUE]]: ... @overload def to( @@ -728,20 +737,29 @@ class Var(Generic[VAR_TYPE]): return self + # We use `NoReturn` here to catch `Var[Any]` and `Var[Unknown]` cases first. @overload - def guess_type(self: Var[str]) -> StringVar: ... + def guess_type(self: Var[NoReturn]) -> Var: ... # pyright: ignore [reportOverlappingOverload] @overload def guess_type(self: Var[bool]) -> BooleanVar: ... @overload - def guess_type(self: Var[int] | Var[float] | Var[int | float]) -> NumberVar: ... + def guess_type(self: Var[INT_OR_FLOAT]) -> NumberVar[INT_OR_FLOAT]: ... @overload - def guess_type(self: Var[list] | Var[tuple] | Var[set]) -> ArrayVar: ... + def guess_type(self: Var[str]) -> StringVar: ... # pyright: ignore [reportOverlappingOverload] @overload - def guess_type(self: Var[dict]) -> ObjectVar[dict]: ... + def guess_type(self: Var[Sequence[VALUE]]) -> ArrayVar[Sequence[VALUE]]: ... + + @overload + def guess_type(self: Var[Set[VALUE]]) -> ArrayVar[Set[VALUE]]: ... + + @overload + def guess_type( + self: Var[Dict[VALUE, OTHER_VAR_TYPE]], + ) -> ObjectVar[Dict[VALUE, OTHER_VAR_TYPE]]: ... @overload def guess_type(self) -> Self: ... @@ -925,7 +943,7 @@ class Var(Generic[VAR_TYPE]): """ from .number import equal_operation - return equal_operation(self, other) + return equal_operation(self, other).guess_type() def __ne__(self, other: Var | Any) -> BooleanVar: """Check if the current object is not equal to the given object. @@ -948,7 +966,7 @@ class Var(Generic[VAR_TYPE]): """ from .number import boolify - return boolify(self).guess_type() + return boolify(self) # pyright: ignore [reportReturnType] def __and__(self, other: Var | Any) -> Var: """Perform a logical AND operation on the current instance and another variable. @@ -1150,7 +1168,7 @@ class Var(Generic[VAR_TYPE]): @overload @classmethod - def range(cls, stop: int | NumberVar, /) -> ArrayVar[List[int]]: ... + def range(cls, stop: int | NumberVar, /) -> ArrayVar[Sequence[int]]: ... @overload @classmethod @@ -1160,15 +1178,16 @@ class Var(Generic[VAR_TYPE]): end: int | NumberVar, step: int | NumberVar = 1, /, - ) -> ArrayVar[List[int]]: ... + ) -> ArrayVar[Sequence[int]]: ... @classmethod def range( cls, - first_endpoint: int | NumberVar, - second_endpoint: int | NumberVar | None = None, - step: int | NumberVar | None = None, - ) -> ArrayVar[List[int]]: + first_endpoint: int | Var[int], + second_endpoint: int | Var[int] | None = None, + step: int | Var[int] | None = None, + /, + ) -> ArrayVar[Sequence[int]]: """Create a range of numbers. Args: @@ -1181,7 +1200,11 @@ class Var(Generic[VAR_TYPE]): """ from .sequence import ArrayVar - return ArrayVar.range(first_endpoint, second_endpoint, step) + if second_endpoint is None: + return ArrayVar.range.call(first_endpoint).guess_type() + if step is None: + return ArrayVar.range.call(first_endpoint, second_endpoint).guess_type() + return ArrayVar.range.call(first_endpoint, second_endpoint, step).guess_type() def __bool__(self) -> bool: """Raise exception if using Var in a boolean context. @@ -1221,6 +1244,27 @@ VAR_SUBCLASS = TypeVar("VAR_SUBCLASS", bound=Var) VAR_INSIDE = TypeVar("VAR_INSIDE") +class VarWithDefault(Var[VAR_TYPE]): + """Annotate an optional argument.""" + + def __init__(self, default_value: VAR_TYPE): + """Initialize the default value. + + Args: + default_value: The default value. + """ + self._default = default_value + + @property + def default(self) -> Var[VAR_TYPE]: + """Get the default value. + + Returns: + The default value. + """ + return Var.create(self._default) + + class ToOperation: """A var operation that converts a var to another type.""" @@ -1362,9 +1406,6 @@ class LiteralVar(Var): Raises: TypeError: If the value is not a supported type for LiteralVar. """ - from .object import LiteralObjectVar - from .sequence import LiteralStringVar - if isinstance(value, Var): if _var_data is None: return value @@ -1377,6 +1418,9 @@ class LiteralVar(Var): from reflex.event import EventHandler from reflex.utils.format import get_event_handler_parts + from .object import LiteralObjectVar + from .sequence import LiteralStringVar + if isinstance(value, EventHandler): return Var(_js_expr=".".join(filter(None, get_event_handler_parts(value)))) @@ -1466,7 +1510,7 @@ def get_python_literal(value: Union[LiteralVar, Any]) -> Any | None: return value -def validate_arg(type_hint: GenericType) -> Callable[[Any], bool]: +def validate_arg(type_hint: GenericType) -> Callable[[Any], str | None]: """Create a validator for an argument. Args: @@ -1477,7 +1521,15 @@ def validate_arg(type_hint: GenericType) -> Callable[[Any], bool]: """ def validate(value: Any): - return True + if isinstance(value, LiteralVar): + if not _isinstance(value._var_value, type_hint): + return f"Expected {type_hint} but got {value._var_value} of type {type(value._var_value)}." + elif isinstance(value, Var): + if not typehint_issubclass(value._var_type, type_hint): + return f"Expected {type_hint} but got {value._var_type}." + else: + if not _isinstance(value, type_hint): + return f"Expected {type_hint} but got {value} of type {type(value)}." return validate @@ -1505,14 +1557,58 @@ class TypeComputer(Protocol): @overload def var_operation( - func: Callable[[], CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation[ReflexCallable[[], T]]: ... + func: Callable[[Var[V1], Var[V2], Var[V3]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3], T]]: ... @overload def var_operation( - func: Callable[[Var[V1]], CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation[ReflexCallable[[V1], T]]: ... + func: Callable[[Var[V1], Var[V2], VarWithDefault[V3]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, VarWithDefault[V3]], T]]: ... + + +@overload +def var_operation( + func: Callable[ + [ + Var[V1], + VarWithDefault[V2], + VarWithDefault[V3], + ], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ + ReflexCallable[ + [ + V1, + VarWithDefault[V2], + VarWithDefault[V3], + ], + T, + ] +]: ... + + +@overload +def var_operation( + func: Callable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + VarWithDefault[V3], + ], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V1], + VarWithDefault[V1], + ], + T, + ] +]: ... @overload @@ -1523,23 +1619,68 @@ def var_operation( @overload def var_operation( - func: Callable[[Var[V1], Var[V2], Var[V3]], CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3], T]]: ... - - -@overload -def var_operation( - func: Callable[[Var[V1], Var[V2], Var[V3], Var[V4]], CustomVarOperationReturn[T]], -) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3, V4], T]]: ... + func: Callable[ + [ + Var[V1], + VarWithDefault[V2], + ], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ + ReflexCallable[ + [ + V1, + VarWithDefault[V2], + ], + T, + ] +]: ... @overload def var_operation( func: Callable[ - [Var[V1], Var[V2], Var[V3], Var[V4], Var[V5]], + [ + VarWithDefault[V1], + VarWithDefault[V2], + ], CustomVarOperationReturn[T], ], -) -> ArgsFunctionOperation[ReflexCallable[[V1, V2, V3, V4, V5], T]]: ... +) -> ArgsFunctionOperation[ + ReflexCallable[ + [ + VarWithDefault[V1], + VarWithDefault[V2], + ], + T, + ] +]: ... + + +@overload +def var_operation( + func: Callable[[Var[V1]], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[V1], T]]: ... + + +@overload +def var_operation( + func: Callable[ + [VarWithDefault[V1]], + CustomVarOperationReturn[T], + ], +) -> ArgsFunctionOperation[ + ReflexCallable[ + [VarWithDefault[V1]], + T, + ] +]: ... + + +@overload +def var_operation( + func: Callable[[], CustomVarOperationReturn[T]], +) -> ArgsFunctionOperation[ReflexCallable[[], T]]: ... def var_operation( @@ -1568,6 +1709,7 @@ def var_operation( func_name = func.__name__ func_arg_spec = inspect.getfullargspec(func) + func_signature = inspect.signature(func) if func_arg_spec.kwonlyargs: raise TypeError(f"Function {func_name} cannot have keyword-only arguments.") @@ -1576,10 +1718,23 @@ def var_operation( arg_names = func_arg_spec.args + arg_default_values: Sequence[inspect.Parameter.empty | VarWithDefault] = tuple( + ( + default_value + if isinstance( + (default_value := func_signature.parameters[arg_name].default), + VarWithDefault, + ) + else inspect.Parameter.empty() + ) + for arg_name in arg_names + ) + type_hints = get_type_hints(func) if not all( - (get_origin((type_hint := type_hints.get(arg_name, Any))) or type_hint) is Var + (get_origin((type_hint := type_hints.get(arg_name, Any))) or type_hint) + in (Var, VarWithDefault) and len(get_args(type_hint)) <= 1 for arg_name in arg_names ): @@ -1606,13 +1761,22 @@ def var_operation( args_operation = ArgsFunctionOperation.create( tuple(map(str, arg_vars)), custom_operation_return, + default_values=arg_default_values, validators=tuple( - validate_arg(type_hints.get(arg_name, Any)) for arg_name in arg_names + validate_arg(arg_type) + if not isinstance(arg_type, TypeVar) + else validate_arg(arg_type.__bound__ or Any) + for _, arg_type in args_with_type_hints ), function_name=func_name, type_computer=custom_operation_return._type_computer, _var_type=ReflexCallable[ - tuple(arg_python_type for _, arg_python_type in args_with_type_hints), # type: ignore + tuple( + arg_python_type + if isinstance(arg_default_values[i], inspect.Parameter) + else VarWithDefault[arg_python_type] + for i, (_, arg_python_type) in enumerate(args_with_type_hints) + ), # type: ignore custom_operation_return._var_type, ], ) @@ -2018,10 +2182,10 @@ class ComputedVar(Var[RETURN_TYPE]): @overload def __get__( - self: ComputedVar[list[LIST_INSIDE]], + self: ComputedVar[Sequence[LIST_INSIDE]], instance: None, owner: Type, - ) -> ArrayVar[list[LIST_INSIDE]]: ... + ) -> ArrayVar[Sequence[LIST_INSIDE]]: ... @overload def __get__( @@ -2030,13 +2194,6 @@ class ComputedVar(Var[RETURN_TYPE]): owner: Type, ) -> ArrayVar[set[LIST_INSIDE]]: ... - @overload - def __get__( - self: ComputedVar[tuple[LIST_INSIDE, ...]], - instance: None, - owner: Type, - ) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ... - @overload def __get__(self, instance: None, owner: Type) -> ComputedVar[RETURN_TYPE]: ... @@ -2175,7 +2332,7 @@ class ComputedVar(Var[RETURN_TYPE]): d.update( self._deps( objclass=objclass, - obj=ref_obj, + obj=ref_obj, # pyright: ignore [reportArgumentType] ) ) # recurse into property fget functions @@ -2218,7 +2375,7 @@ class ComputedVar(Var[RETURN_TYPE]): with contextlib.suppress(AttributeError): delattr(instance, self._cache_attr) - def _determine_var_type(self) -> Type: + def _determine_var_type(self) -> GenericType: """Get the type of the var. Returns: @@ -2672,8 +2829,12 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]: var_datas.append(sub._var_data) elif not isinstance(sub, str): # Recurse into dict values. - if hasattr(sub, "values") and callable(sub.values): - var_datas.extend(_extract_var_data(sub.values())) + if ( + (values_fn := getattr(sub, "values", None)) is not None + and callable(values_fn) + and isinstance((values := values_fn()), Iterable) + ): + var_datas.extend(_extract_var_data(values)) # Recurse into iterable values (or dict keys). var_datas.extend(_extract_var_data(sub)) @@ -2682,9 +2843,9 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]: var_datas.append(value._var_data) else: # Recurse when value is a dict itself. - values = getattr(value, "values", None) - if callable(values): - var_datas.extend(_extract_var_data(values())) + values_fn = getattr(value, "values", None) + if callable(values_fn) and isinstance((values := values_fn()), Iterable): + var_datas.extend(_extract_var_data(values)) return var_datas @@ -2966,10 +3127,10 @@ class Field(Generic[T]): @overload def __get__( - self: Field[List[V]] | Field[Set[V]] | Field[Tuple[V, ...]], + self: Field[Sequence[V]] | Field[Set[V]], instance: None, owner, - ) -> ArrayVar[List[V]]: ... + ) -> ArrayVar[Sequence[V]]: ... @overload def __get__( @@ -3151,7 +3312,7 @@ def nary_type_computer( def type_computer(*args: Var): if len(args) != len(types): return ( - ReflexCallable[[], types[len(args)]], # type: ignore + types[len(args)], functools.partial(type_computer, *args), ) return ( diff --git a/reflex/vars/function.py b/reflex/vars/function.py index 371276ca1..c79a44ce0 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -3,14 +3,25 @@ from __future__ import annotations import dataclasses +import inspect import sys -from typing import Any, Callable, Optional, Sequence, Tuple, Type, Union, overload +from typing import ( + Any, + Callable, + NoReturn, + Optional, + Sequence, + Tuple, + Type, + Union, + overload, +) from typing_extensions import Concatenate, Generic, ParamSpec, TypeVar from reflex.utils import format from reflex.utils.exceptions import VarTypeError -from reflex.utils.types import GenericType +from reflex.utils.types import GenericType, Unset, get_origin from .base import ( CachedVarOperation, @@ -19,12 +30,14 @@ from .base import ( TypeComputer, Var, VarData, + VarWithDefault, cached_property_no_lock, unwrap_reflex_callalbe, ) P = ParamSpec("P") R = TypeVar("R") +R2 = TypeVar("R2") V1 = TypeVar("V1") V2 = TypeVar("V2") V3 = TypeVar("V3") @@ -47,20 +60,39 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): @overload def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, P], R]], + self: FunctionVar[ReflexCallable[Concatenate[VarWithDefault[V1], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, P], R]], arg1: Union[V1, Var[V1]], ) -> FunctionVar[ReflexCallable[P, R]]: ... @overload def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, V2, P], R]], + self: FunctionVar[ + ReflexCallable[Concatenate[VarWithDefault[V1], VarWithDefault[V2], P], R] + ] + | FunctionVar[ReflexCallable[Concatenate[V1, VarWithDefault[V2], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, P], R]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], ) -> FunctionVar[ReflexCallable[P, R]]: ... @overload def partial( - self: FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, P], R]], + self: FunctionVar[ + ReflexCallable[ + Concatenate[ + VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3], P + ], + R, + ] + ] + | FunctionVar[ + ReflexCallable[ + Concatenate[V1, VarWithDefault[V2], VarWithDefault[V3], P], R + ] + ] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, VarWithDefault[V3], P], R]] + | FunctionVar[ReflexCallable[Concatenate[V1, V2, V3, P], R]], arg1: Union[V1, Var[V1]], arg2: Union[V2, Var[V2]], arg3: Union[V3, Var[V3]], @@ -148,11 +180,58 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): _var_type=partial_types, ) + @overload + def call(self: FunctionVar[ReflexCallable[[], R]]) -> VarOperationCall[[], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1]], R]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + ) -> VarOperationCall[[VarWithDefault[V1]], R]: ... + + @overload + def call( + self: FunctionVar[ReflexCallable[[VarWithDefault[V1], VarWithDefault[V2]], R]], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> VarOperationCall[[VarWithDefault[V1], VarWithDefault[V2]], R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], R + ] + ], + arg1: Union[V1, Var[V1], Unset] = Unset(), + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> VarOperationCall[ + [VarWithDefault[V1], VarWithDefault[V2], VarWithDefault[V3]], R + ]: ... + @overload def call( self: FunctionVar[ReflexCallable[[V1], R]], arg1: Union[V1, Var[V1]] ) -> VarOperationCall[[V1], R]: ... + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, VarWithDefault[V2]], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + ) -> VarOperationCall[[V1, VarWithDefault[V2]], R]: ... + + @overload + def call( + self: FunctionVar[ + ReflexCallable[[V1, VarWithDefault[V2], VarWithDefault[V3]], R] + ], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2], Unset] = Unset(), + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> VarOperationCall[[V1, VarWithDefault[V2], VarWithDefault[V3]], R]: ... + @overload def call( self: FunctionVar[ReflexCallable[[V1, V2], R]], @@ -160,6 +239,14 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): arg2: Union[V2, Var[V2]], ) -> VarOperationCall[[V1, V2], R]: ... + @overload + def call( + self: FunctionVar[ReflexCallable[[V1, V2, VarWithDefault[V3]], R]], + arg1: Union[V1, Var[V1]], + arg2: Union[V2, Var[V2]], + arg3: Union[V3, Var[V3], Unset] = Unset(), + ) -> VarOperationCall[[V1, V2, VarWithDefault[V3]], R]: ... + @overload def call( self: FunctionVar[ReflexCallable[[V1, V2, V3], R]], @@ -198,15 +285,11 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): arg6: Union[V6, Var[V6]], ) -> VarOperationCall[[V1, V2, V3, V4, V5, V6], R]: ... + # Capture Any to allow for arbitrary number of arguments @overload - def call( - self: FunctionVar[ReflexCallable[P, R]], *args: Var | Any - ) -> VarOperationCall[P, R]: ... + def call(self: FunctionVar[NoReturn], *args: Var | Any) -> VarOperationCall: ... - @overload - def call(self, *args: Var | Any) -> Var: ... - - def call(self, *args: Var | Any) -> Var: # type: ignore + def call(self, *args: Var | Any) -> VarOperationCall: # type: ignore """Call the function with the given arguments. Args: @@ -218,13 +301,53 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): Raises: VarTypeError: If the number of arguments is invalid """ + required_arg_len = self._required_arg_len() arg_len = self._arg_len() - if arg_len is not None and len(args) != arg_len: - raise VarTypeError(f"Invalid number of arguments provided to {str(self)}") + if arg_len is not None: + if len(args) < required_arg_len: + raise VarTypeError( + f"Passed {len(args)} arguments, expected at least {required_arg_len} for {str(self)}" + ) + if len(args) > arg_len: + raise VarTypeError( + f"Passed {len(args)} arguments, expected at most {arg_len} for {str(self)}" + ) args = tuple(map(LiteralVar.create, args)) self._pre_check(*args) return_type = self._return_type(*args) - return VarOperationCall.create(self, *args, _var_type=return_type).guess_type() + return VarOperationCall.create(self, *args, _var_type=return_type) + + def chain( + self: FunctionVar[ReflexCallable[P, R]], + other: FunctionVar[ReflexCallable[[R], R2]] + | FunctionVar[ReflexCallable[[R, VarWithDefault[Any]], R2]] + | FunctionVar[ + ReflexCallable[[R, VarWithDefault[Any], VarWithDefault[Any]], R2] + ], + ) -> FunctionVar[ReflexCallable[P, R2]]: + """Chain two functions together. + + Args: + other: The other function to chain. + + Returns: + The chained function. + """ + self_arg_type, self_return_type = unwrap_reflex_callalbe(self._var_type) + _, other_return_type = unwrap_reflex_callalbe(other._var_type) + + return ArgsFunctionOperationBuilder.create( + (), + VarOperationCall.create( + other, + VarOperationCall.create( + self, Var(_js_expr="...args"), _var_type=self_return_type + ), + _var_type=other_return_type, + ), + rest="arg", + _var_type=ReflexCallable[self_arg_type, other_return_type], # pyright: ignore [reportInvalidTypeArguments] + ) def _partial_type( self, *args: Var | Any @@ -253,6 +376,21 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): return len(args_types) return None + def _required_arg_len(self) -> int: + """Get the number of required arguments the function takes. + + Returns: + The number of required arguments the function takes. + """ + args_types, _ = unwrap_reflex_callalbe(self._var_type) + if isinstance(args_types, tuple): + return sum( + 1 + for arg_type in args_types + if get_origin(arg_type) is not VarWithDefault + ) + return 0 + def _return_type(self, *args: Var | Any) -> GenericType: """Override the type of the function call with the given arguments. @@ -265,7 +403,9 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): partial_types, _ = self._partial_type(*args) return unwrap_reflex_callalbe(partial_types)[1] - def _pre_check(self, *args: Var | Any) -> Tuple[Callable[[Any], bool], ...]: + def _pre_check( + self, *args: Var | Any + ) -> Tuple[Callable[[Any], Optional[str]], ...]: """Check if the function can be called with the given arguments. Args: @@ -276,6 +416,30 @@ class FunctionVar(Var[CALLABLE_TYPE], default_type=ReflexCallable[Any, Any]): """ return tuple() + @overload + def __get__(self, instance: None, owner: Any) -> FunctionVar[CALLABLE_TYPE]: ... + + @overload + def __get__( + self: FunctionVar[ReflexCallable[Concatenate[V1, P], R]], + instance: Var[V1], + owner: Any, + ) -> FunctionVar[ReflexCallable[P, R]]: ... + + def __get__(self, instance: Any, owner: Any): + """Get the function var. + + Args: + instance: The instance of the class. + owner: The owner of the class. + + Returns: + The function var. + """ + if instance is None: + return self + return self.partial(instance) + __call__ = call @@ -321,7 +485,9 @@ class FunctionStringVar(FunctionVar[CALLABLE_TYPE]): class VarOperationCall(Generic[P, R], CachedVarOperation, Var[R]): """Base class for immutable vars that are the result of a function call.""" - _func: Optional[FunctionVar[ReflexCallable[P, R]]] = dataclasses.field(default=None) + _func: Optional[FunctionVar[ReflexCallable[..., R]]] = dataclasses.field( + default=None + ) _args: Tuple[Union[Var, Any], ...] = dataclasses.field(default_factory=tuple) @cached_property_no_lock @@ -331,7 +497,8 @@ class VarOperationCall(Generic[P, R], CachedVarOperation, Var[R]): Returns: The name of the var. """ - return f"({str(self._func)}({', '.join([str(LiteralVar.create(arg)) for arg in self._args])}))" + func_str = str(self._func) + return f"({func_str}({', '.join([str(LiteralVar.create(arg)) for arg in self._args])}))" @cached_property_no_lock def _cached_get_all_var_data(self) -> VarData | None: @@ -422,8 +589,16 @@ def format_args_function_operation( """ arg_names_str = ", ".join( [ - arg if isinstance(arg, str) else arg.to_javascript() - for arg in self._args.args + (arg if isinstance(arg, str) else arg.to_javascript()) + + ( + f" = {str(default_value.default)}" + if i < len(self._default_values) + and not isinstance( + (default_value := self._default_values[i]), inspect.Parameter.empty + ) + else "" + ) + for i, arg in enumerate(self._args.args) ] + ([f"...{self._args.rest}"] if self._args.rest else []) ) @@ -442,7 +617,7 @@ def format_args_function_operation( def pre_check_args( self: ArgsFunctionOperation | ArgsFunctionOperationBuilder, *args: Var | Any -) -> Tuple[Callable[[Any], bool], ...]: +) -> Tuple[Callable[[Any], Optional[str]], ...]: """Check if the function can be called with the given arguments. Args: @@ -456,14 +631,14 @@ def pre_check_args( VarTypeError: If the arguments are invalid. """ for i, (validator, arg) in enumerate(zip(self._validators, args)): - if not validator(arg): + if (validation_message := validator(arg)) is not None: arg_name = self._args.args[i] if i < len(self._args.args) else None if arg_name is not None: raise VarTypeError( - f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}" + f"Invalid argument {str(arg)} provided to {arg_name} in {self._function_name or 'var operation'}. {validation_message}" ) raise VarTypeError( - f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}" + f"Invalid argument {str(arg)} provided to argument {i} in {self._function_name or 'var operation'}. {validation_message}" ) return self._validators[len(args) :] @@ -497,7 +672,10 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): """Base class for immutable function defined via arguments and return expression.""" _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) - _validators: Tuple[Callable[[Any], bool], ...] = dataclasses.field( + _default_values: Tuple[VarWithDefault | inspect.Parameter.empty, ...] = ( + dataclasses.field(default_factory=tuple) + ) + _validators: Tuple[Callable[[Any], Optional[str]], ...] = dataclasses.field( default_factory=tuple ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) @@ -516,8 +694,10 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): cls, args_names: Sequence[Union[str, DestructuredArg]], return_expr: Var | Any, + /, + default_values: Sequence[VarWithDefault | inspect.Parameter.empty] = (), rest: str | None = None, - validators: Sequence[Callable[[Any], bool]] = (), + validators: Sequence[Callable[[Any], Optional[str]]] = (), function_name: str = "", explicit_return: bool = False, type_computer: Optional[TypeComputer] = None, @@ -529,6 +709,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): Args: args_names: The names of the arguments. return_expr: The return expression of the function. + default_values: The default values of the arguments. rest: The name of the rest argument. validators: The validators for the arguments. function_name: The name of the function. @@ -545,6 +726,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar[CALLABLE_TYPE]): _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _default_values=tuple(default_values), _function_name=function_name, _validators=tuple(validators), _return_expr=return_expr, @@ -564,7 +746,10 @@ class ArgsFunctionOperationBuilder( """Base class for immutable function defined via arguments and return expression with the builder pattern.""" _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs) - _validators: Tuple[Callable[[Any], bool], ...] = dataclasses.field( + _default_values: Tuple[VarWithDefault | inspect.Parameter.empty, ...] = ( + dataclasses.field(default_factory=tuple) + ) + _validators: Tuple[Callable[[Any], Optional[str]], ...] = dataclasses.field( default_factory=tuple ) _return_expr: Union[Var, Any] = dataclasses.field(default=None) @@ -583,8 +768,10 @@ class ArgsFunctionOperationBuilder( cls, args_names: Sequence[Union[str, DestructuredArg]], return_expr: Var | Any, + /, + default_values: Sequence[VarWithDefault | inspect.Parameter.empty] = (), rest: str | None = None, - validators: Sequence[Callable[[Any], bool]] = (), + validators: Sequence[Callable[[Any], Optional[str]]] = (), function_name: str = "", explicit_return: bool = False, type_computer: Optional[TypeComputer] = None, @@ -596,6 +783,7 @@ class ArgsFunctionOperationBuilder( Args: args_names: The names of the arguments. return_expr: The return expression of the function. + default_values: The default values of the arguments. rest: The name of the rest argument. validators: The validators for the arguments. function_name: The name of the function. @@ -612,6 +800,7 @@ class ArgsFunctionOperationBuilder( _var_type=_var_type, _var_data=_var_data, _args=FunctionArgs(args=tuple(args_names), rest=rest), + _default_values=tuple(default_values), _function_name=function_name, _validators=tuple(validators), _return_expr=return_expr, diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 3eb59e427..014cbd16b 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -30,6 +30,7 @@ from .base import ( NUMBER_T = TypeVar("NUMBER_T", int, float, Union[int, float], bool) if TYPE_CHECKING: + from .function import FunctionVar from .sequence import ArrayVar @@ -53,13 +54,7 @@ def raise_unsupported_operand_types( class NumberVar(Var[NUMBER_T], python_types=(int, float)): """Base class for immutable number vars.""" - @overload - def __add__(self, other: number_types) -> NumberVar: ... - - @overload - def __add__(self, other: NoReturn) -> NoReturn: ... - - def __add__(self, other: Any): + def __add__(self, other: number_types) -> NumberVar: """Add two numbers. Args: @@ -72,13 +67,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): raise_unsupported_operand_types("+", (type(self), type(other))) return number_add_operation(self, +other).guess_type() - @overload - def __radd__(self, other: number_types) -> NumberVar: ... - - @overload - def __radd__(self, other: NoReturn) -> NoReturn: ... - - def __radd__(self, other: Any): + def __radd__(self, other: number_types) -> NumberVar: """Add two numbers. Args: @@ -91,13 +80,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): raise_unsupported_operand_types("+", (type(other), type(self))) return number_add_operation(+other, self).guess_type() - @overload - def __sub__(self, other: number_types) -> NumberVar: ... - - @overload - def __sub__(self, other: NoReturn) -> NoReturn: ... - - def __sub__(self, other: Any): + def __sub__(self, other: number_types) -> NumberVar: """Subtract two numbers. Args: @@ -111,13 +94,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_subtract_operation(self, +other).guess_type() - @overload - def __rsub__(self, other: number_types) -> NumberVar: ... - - @overload - def __rsub__(self, other: NoReturn) -> NoReturn: ... - - def __rsub__(self, other: Any): + def __rsub__(self, other: number_types) -> NumberVar: """Subtract two numbers. Args: @@ -193,13 +170,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_multiply_operation(+other, self).guess_type() - @overload - def __truediv__(self, other: number_types) -> NumberVar: ... - - @overload - def __truediv__(self, other: NoReturn) -> NoReturn: ... - - def __truediv__(self, other: Any): + def __truediv__(self, other: number_types) -> NumberVar: """Divide two numbers. Args: @@ -213,13 +184,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_true_division_operation(self, +other).guess_type() - @overload - def __rtruediv__(self, other: number_types) -> NumberVar: ... - - @overload - def __rtruediv__(self, other: NoReturn) -> NoReturn: ... - - def __rtruediv__(self, other: Any): + def __rtruediv__(self, other: number_types) -> NumberVar: """Divide two numbers. Args: @@ -233,13 +198,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_true_division_operation(+other, self).guess_type() - @overload - def __floordiv__(self, other: number_types) -> NumberVar: ... - - @overload - def __floordiv__(self, other: NoReturn) -> NoReturn: ... - - def __floordiv__(self, other: Any): + def __floordiv__(self, other: number_types) -> NumberVar: """Floor divide two numbers. Args: @@ -253,13 +212,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_floor_division_operation(self, +other).guess_type() - @overload - def __rfloordiv__(self, other: number_types) -> NumberVar: ... - - @overload - def __rfloordiv__(self, other: NoReturn) -> NoReturn: ... - - def __rfloordiv__(self, other: Any): + def __rfloordiv__(self, other: number_types) -> NumberVar: """Floor divide two numbers. Args: @@ -273,13 +226,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_floor_division_operation(+other, self).guess_type() - @overload - def __mod__(self, other: number_types) -> NumberVar: ... - - @overload - def __mod__(self, other: NoReturn) -> NoReturn: ... - - def __mod__(self, other: Any): + def __mod__(self, other: number_types) -> NumberVar: """Modulo two numbers. Args: @@ -293,13 +240,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_modulo_operation(self, +other).guess_type() - @overload - def __rmod__(self, other: number_types) -> NumberVar: ... - - @overload - def __rmod__(self, other: NoReturn) -> NoReturn: ... - - def __rmod__(self, other: Any): + def __rmod__(self, other: number_types) -> NumberVar: """Modulo two numbers. Args: @@ -313,13 +254,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_modulo_operation(+other, self).guess_type() - @overload - def __pow__(self, other: number_types) -> NumberVar: ... - - @overload - def __pow__(self, other: NoReturn) -> NoReturn: ... - - def __pow__(self, other: Any): + def __pow__(self, other: number_types) -> NumberVar: """Exponentiate two numbers. Args: @@ -333,13 +268,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): return number_exponent_operation(self, +other).guess_type() - @overload - def __rpow__(self, other: number_types) -> NumberVar: ... - - @overload - def __rpow__(self, other: NoReturn) -> NoReturn: ... - - def __rpow__(self, other: Any): + def __rpow__(self, other: number_types) -> NumberVar: """Exponentiate two numbers. Args: @@ -409,13 +338,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): """ return number_trunc_operation(self).guess_type() - @overload - def __lt__(self, other: number_types) -> BooleanVar: ... - - @overload - def __lt__(self, other: NoReturn) -> NoReturn: ... - - def __lt__(self, other: Any): + def __lt__(self, other: number_types) -> BooleanVar: """Less than comparison. Args: @@ -426,15 +349,9 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("<", (type(self), type(other))) - return less_than_operation(self, +other) + return less_than_operation(self, +other).guess_type() - @overload - def __le__(self, other: number_types) -> BooleanVar: ... - - @overload - def __le__(self, other: NoReturn) -> NoReturn: ... - - def __le__(self, other: Any): + def __le__(self, other: number_types) -> BooleanVar: """Less than or equal comparison. Args: @@ -445,9 +362,9 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types("<=", (type(self), type(other))) - return less_than_or_equal_operation(self, +other) + return less_than_or_equal_operation(self, +other).guess_type() - def __eq__(self, other: Any): + def __eq__(self, other: Any) -> BooleanVar: """Equal comparison. Args: @@ -457,10 +374,10 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): The result of the comparison. """ if isinstance(other, NUMBER_TYPES): - return equal_operation(self, +other) - return equal_operation(self, other) + return equal_operation(self, +other).guess_type() + return equal_operation(self, other).guess_type() - def __ne__(self, other: Any): + def __ne__(self, other: Any) -> BooleanVar: """Not equal comparison. Args: @@ -470,16 +387,10 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): The result of the comparison. """ if isinstance(other, NUMBER_TYPES): - return not_equal_operation(self, +other) - return not_equal_operation(self, other) + return not_equal_operation(self, +other).guess_type() + return not_equal_operation(self, other).guess_type() - @overload - def __gt__(self, other: number_types) -> BooleanVar: ... - - @overload - def __gt__(self, other: NoReturn) -> NoReturn: ... - - def __gt__(self, other: Any): + def __gt__(self, other: number_types) -> BooleanVar: """Greater than comparison. Args: @@ -490,15 +401,9 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types(">", (type(self), type(other))) - return greater_than_operation(self, +other) + return greater_than_operation(self, +other).guess_type() - @overload - def __ge__(self, other: number_types) -> BooleanVar: ... - - @overload - def __ge__(self, other: NoReturn) -> NoReturn: ... - - def __ge__(self, other: Any): + def __ge__(self, other: number_types) -> BooleanVar: """Greater than or equal comparison. Args: @@ -509,9 +414,9 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)): """ if not isinstance(other, NUMBER_TYPES): raise_unsupported_operand_types(">=", (type(self), type(other))) - return greater_than_or_equal_operation(self, +other) + return greater_than_or_equal_operation(self, +other).guess_type() - def bool(self): + def bool(self) -> BooleanVar: """Boolean conversion. Returns: @@ -863,7 +768,7 @@ def boolean_to_number_operation(value: Var[bool]): def comparison_operator( func: Callable[[Var, Var], str], -) -> Callable[[Var | Any, Var | Any], BooleanVar]: +) -> FunctionVar[ReflexCallable[[Any, Any], bool]]: """Decorator to create a comparison operation. Args: @@ -873,26 +778,15 @@ def comparison_operator( The comparison operation. """ - @var_operation def operation(lhs: Var[Any], rhs: Var[Any]): return var_operation_return( js_expression=func(lhs, rhs), var_type=bool, ) - def wrapper(lhs: Var | Any, rhs: Var | Any) -> BooleanVar: - """Create the comparison operation. + operation.__name__ = func.__name__ - Args: - lhs: The first value. - rhs: The second value. - - Returns: - The comparison operation. - """ - return operation(lhs, rhs).guess_type() - - return wrapper + return var_operation(operation) @comparison_operator @@ -1104,6 +998,14 @@ _IS_TRUE_IMPORT: ImportDict = { f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], } +_AT_SLICE_IMPORT: ImportDict = { + f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSlice")], +} + +_RANGE_IMPORT: ImportDict = { + f"$/{Dirs.UTILS}/helpers/range": [ImportVar(tag="range", is_default=True)], +} + @var_operation def boolify(value: Var): @@ -1122,8 +1024,8 @@ def boolify(value: Var): ) -T = TypeVar("T") -U = TypeVar("U") +T = TypeVar("T", bound=Any) +U = TypeVar("U", bound=Any) @var_operation diff --git a/reflex/vars/object.py b/reflex/vars/object.py index 8607e7b9f..fb47984c3 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -11,6 +11,7 @@ from typing import ( Dict, List, NoReturn, + Sequence, Tuple, Type, TypeVar, @@ -71,9 +72,9 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): ) -> Type[VALUE_TYPE]: ... @overload - def _value_type(self) -> Type: ... + def _value_type(self) -> GenericType: ... - def _value_type(self) -> Type: + def _value_type(self) -> GenericType: """Get the type of the values of the object. Returns: @@ -85,7 +86,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): args = get_args(self._var_type) if issubclass(fixed_type, dict) else () return args[1] if args else Any - def keys(self) -> ArrayVar[List[str]]: + def keys(self) -> ArrayVar[Sequence[str]]: """Get the keys of the object. Returns: @@ -96,7 +97,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): @overload def values( self: ObjectVar[Dict[Any, VALUE_TYPE]], - ) -> ArrayVar[List[VALUE_TYPE]]: ... + ) -> ArrayVar[Sequence[VALUE_TYPE]]: ... @overload def values(self) -> ArrayVar: ... @@ -112,7 +113,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): @overload def entries( self: ObjectVar[Dict[Any, VALUE_TYPE]], - ) -> ArrayVar[List[Tuple[str, VALUE_TYPE]]]: ... + ) -> ArrayVar[Sequence[Tuple[str, VALUE_TYPE]]]: ... @overload def entries(self) -> ArrayVar: ... @@ -163,9 +164,9 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): @overload def __getitem__( - self: ObjectVar[Dict[Any, list[ARRAY_INNER_TYPE]]], + self: ObjectVar[Dict[Any, Sequence[ARRAY_INNER_TYPE]]], key: Var | Any, - ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ... + ) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ... @overload def __getitem__( @@ -173,12 +174,6 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): key: Var | Any, ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ... - @overload - def __getitem__( - self: ObjectVar[Dict[Any, tuple[ARRAY_INNER_TYPE, ...]]], - key: Var | Any, - ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ... - @overload def __getitem__( self: ObjectVar[Dict[Any, dict[OTHER_KEY_TYPE, VALUE_TYPE]]], @@ -202,7 +197,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): # NoReturn is used here to catch when key value is Any @overload - def __getattr__( + def __getattr__( # pyright: ignore [reportOverlappingOverload] self: ObjectVar[Dict[Any, NoReturn]], name: str, ) -> Var: ... @@ -225,9 +220,9 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): @overload def __getattr__( - self: ObjectVar[Dict[Any, list[ARRAY_INNER_TYPE]]], + self: ObjectVar[Dict[Any, Sequence[ARRAY_INNER_TYPE]]], name: str, - ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ... + ) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ... @overload def __getattr__( @@ -235,12 +230,6 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict): name: str, ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ... - @overload - def __getattr__( - self: ObjectVar[Dict[Any, tuple[ARRAY_INNER_TYPE, ...]]], - name: str, - ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ... - @overload def __getattr__( self: ObjectVar[Dict[Any, dict[OTHER_KEY_TYPE, VALUE_TYPE]]], @@ -311,7 +300,7 @@ class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar): default_factory=dict ) - def _key_type(self) -> Type: + def _key_type(self) -> GenericType: """Get the type of the keys of the object. Returns: @@ -320,7 +309,7 @@ class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar): args_list = typing.get_args(self._var_type) return args_list[0] if args_list else Any - def _value_type(self) -> Type: + def _value_type(self) -> GenericType: """Get the type of the values of the object. Returns: diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 759f9ac5d..7b71a8208 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -13,16 +13,14 @@ from typing import ( TYPE_CHECKING, Any, Callable, - Dict, + ClassVar, List, - Literal, - NoReturn, + Sequence, Set, Tuple, Type, Union, cast, - overload, ) from typing_extensions import TypeAliasType, TypeVar @@ -40,6 +38,7 @@ from .base import ( ReflexCallable, Var, VarData, + VarWithDefault, _global_vars, cached_property_no_lock, figure_out_type, @@ -53,7 +52,9 @@ from .base import ( var_operation_return, ) from .number import ( - BooleanVar, + _AT_SLICE_IMPORT, + _IS_TRUE_IMPORT, + _RANGE_IMPORT, LiteralNumberVar, NumberVar, raise_unsupported_operand_types, @@ -62,302 +63,18 @@ from .number import ( if TYPE_CHECKING: from .function import FunctionVar - from .object import ObjectVar STRING_TYPE = TypeVar("STRING_TYPE", default=str) +ARRAY_VAR_TYPE = TypeVar("ARRAY_VAR_TYPE", bound=Union[Set, Tuple, Sequence]) +OTHER_ARRAY_VAR_TYPE = TypeVar( + "OTHER_ARRAY_VAR_TYPE", bound=Union[Set, Tuple, Sequence] +) +INNER_ARRAY_VAR = TypeVar("INNER_ARRAY_VAR", covariant=True) +ANOTHER_ARRAY_VAR = TypeVar("ANOTHER_ARRAY_VAR", covariant=True) -class StringVar(Var[STRING_TYPE], python_types=str): - """Base class for immutable string vars.""" - - @overload - def __add__(self, other: StringVar | str) -> ConcatVarOperation: ... - - @overload - def __add__(self, other: NoReturn) -> NoReturn: ... - - def __add__(self, other: Any) -> ConcatVarOperation: - """Concatenate two strings. - - Args: - other: The other string. - - Returns: - The string concatenation operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("+", (type(self), type(other))) - - return ConcatVarOperation.create(self, other) - - @overload - def __radd__(self, other: StringVar | str) -> ConcatVarOperation: ... - - @overload - def __radd__(self, other: NoReturn) -> NoReturn: ... - - def __radd__(self, other: Any) -> ConcatVarOperation: - """Concatenate two strings. - - Args: - other: The other string. - - Returns: - The string concatenation operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("+", (type(other), type(self))) - - return ConcatVarOperation.create(other, self) - - @overload - def __mul__(self, other: NumberVar | int) -> StringVar: ... - - @overload - def __mul__(self, other: NoReturn) -> NoReturn: ... - - def __mul__(self, other: Any) -> StringVar: - """Multiply the sequence by a number or an integer. - - Args: - other: The number or integer to multiply the sequence by. - - Returns: - StringVar: The resulting sequence after multiplication. - """ - if not isinstance(other, (NumberVar, int)): - raise_unsupported_operand_types("*", (type(self), type(other))) - - return (self.split() * other).join() - - @overload - def __rmul__(self, other: NumberVar | int) -> StringVar: ... - - @overload - def __rmul__(self, other: NoReturn) -> NoReturn: ... - - def __rmul__(self, other: Any) -> StringVar: - """Multiply the sequence by a number or an integer. - - Args: - other: The number or integer to multiply the sequence by. - - Returns: - StringVar: The resulting sequence after multiplication. - """ - if not isinstance(other, (NumberVar, int)): - raise_unsupported_operand_types("*", (type(other), type(self))) - - return (self.split() * other).join() - - @overload - def __getitem__(self, i: slice) -> StringVar: ... - - @overload - def __getitem__(self, i: int | NumberVar) -> StringVar: ... - - def __getitem__(self, i: Any) -> StringVar: - """Get a slice of the string. - - Args: - i: The slice. - - Returns: - The string slice operation. - """ - if isinstance(i, slice): - return self.split()[i].join() - if not isinstance(i, (int, NumberVar)) or ( - isinstance(i, NumberVar) and i._is_strict_float() - ): - raise_unsupported_operand_types("[]", (type(self), type(i))) - return string_item_operation(self, i).guess_type() - - def length(self) -> NumberVar: - """Get the length of the string. - - Returns: - The string length operation. - """ - return self.split().length() - - def lower(self) -> StringVar: - """Convert the string to lowercase. - - Returns: - The string lower operation. - """ - return string_lower_operation(self).guess_type() - - def upper(self) -> StringVar: - """Convert the string to uppercase. - - Returns: - The string upper operation. - """ - return string_upper_operation(self).guess_type() - - def strip(self) -> StringVar: - """Strip the string. - - Returns: - The string strip operation. - """ - return string_strip_operation(self).guess_type() - - def reversed(self) -> StringVar: - """Reverse the string. - - Returns: - The string reverse operation. - """ - return self.split().reverse().join() - - @overload - def contains( - self, other: StringVar | str, field: StringVar | str | None = None - ) -> BooleanVar: ... - - @overload - def contains( - self, other: NoReturn, field: StringVar | str | None = None - ) -> NoReturn: ... - - def contains(self, other: Any, field: Any = None) -> BooleanVar: - """Check if the string contains another string. - - Args: - other: The other string. - field: The field to check. - - Returns: - The string contains operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("contains", (type(self), type(other))) - if field is not None: - if not isinstance(field, (StringVar, str)): - raise_unsupported_operand_types("contains", (type(self), type(field))) - return string_contains_field_operation(self, other, field).guess_type() - return string_contains_operation(self, other).guess_type() - - @overload - def split(self, separator: StringVar | str = "") -> ArrayVar[List[str]]: ... - - @overload - def split(self, separator: NoReturn) -> NoReturn: ... - - def split(self, separator: Any = "") -> ArrayVar[List[str]]: - """Split the string. - - Args: - separator: The separator. - - Returns: - The string split operation. - """ - if not isinstance(separator, (StringVar, str)): - raise_unsupported_operand_types("split", (type(self), type(separator))) - return string_split_operation(self, separator).guess_type() - - @overload - def startswith(self, prefix: StringVar | str) -> BooleanVar: ... - - @overload - def startswith(self, prefix: NoReturn) -> NoReturn: ... - - def startswith(self, prefix: Any) -> BooleanVar: - """Check if the string starts with a prefix. - - Args: - prefix: The prefix. - - Returns: - The string starts with operation. - """ - if not isinstance(prefix, (StringVar, str)): - raise_unsupported_operand_types("startswith", (type(self), type(prefix))) - return string_starts_with_operation(self, prefix).guess_type() - - @overload - def __lt__(self, other: StringVar | str) -> BooleanVar: ... - - @overload - def __lt__(self, other: NoReturn) -> NoReturn: ... - - def __lt__(self, other: Any): - """Check if the string is less than another string. - - Args: - other: The other string. - - Returns: - The string less than operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("<", (type(self), type(other))) - - return string_lt_operation(self, other).guess_type() - - @overload - def __gt__(self, other: StringVar | str) -> BooleanVar: ... - - @overload - def __gt__(self, other: NoReturn) -> NoReturn: ... - - def __gt__(self, other: Any): - """Check if the string is greater than another string. - - Args: - other: The other string. - - Returns: - The string greater than operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types(">", (type(self), type(other))) - - return string_gt_operation(self, other).guess_type() - - @overload - def __le__(self, other: StringVar | str) -> BooleanVar: ... - - @overload - def __le__(self, other: NoReturn) -> NoReturn: ... - - def __le__(self, other: Any): - """Check if the string is less than or equal to another string. - - Args: - other: The other string. - - Returns: - The string less than or equal operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types("<=", (type(self), type(other))) - - return string_le_operation(self, other).guess_type() - - @overload - def __ge__(self, other: StringVar | str) -> BooleanVar: ... - - @overload - def __ge__(self, other: NoReturn) -> NoReturn: ... - - def __ge__(self, other: Any): - """Check if the string is greater than or equal to another string. - - Args: - other: The other string. - - Returns: - The string greater than or equal operation. - """ - if not isinstance(other, (StringVar, str)): - raise_unsupported_operand_types(">=", (type(self), type(other))) - - return string_ge_operation(self, other).guess_type() +KEY_TYPE = TypeVar("KEY_TYPE") +VALUE_TYPE = TypeVar("VALUE_TYPE") @var_operation @@ -457,7 +174,9 @@ def string_strip_operation(string: Var[str]): @var_operation def string_contains_field_operation( - haystack: Var[str], needle: Var[str], field: Var[str] + haystack: Var[str], + needle: Var[str], + field: VarWithDefault[str] = VarWithDefault(""), ): """Check if a string contains another string. @@ -470,7 +189,7 @@ def string_contains_field_operation( The string contains operation. """ return var_operation_return( - js_expression=f"{haystack}.some(obj => obj[{field}] === {needle})", + js_expression=f"{field.bool()} ? {haystack}.some(obj => obj[{field}] === {needle}) : {haystack}.some(obj => obj === {needle})", var_type=bool, ) @@ -541,6 +260,782 @@ def string_replace_operation( ) +@var_operation +def array_pluck_operation( + array: Var[Sequence[Any]], + field: Var[str], +) -> CustomVarOperationReturn[Sequence[Any]]: + """Pluck a field from an array of objects. + + Args: + array: The array to pluck from. + field: The field to pluck from the objects in the array. + + Returns: + The reversed array. + """ + return var_operation_return( + js_expression=f"{array}.map(e=>e?.[{field}])", + var_type=List[Any], + ) + + +@var_operation +def array_join_operation( + array: Var[Sequence[Any]], sep: VarWithDefault[str] = VarWithDefault("") +): + """Join the elements of an array. + + Args: + array: The array. + sep: The separator. + + Returns: + The joined elements. + """ + return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) + + +@var_operation +def array_reverse_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], +) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR]]: + """Reverse an array. + + Args: + array: The array to reverse. + + Returns: + The reversed array. + """ + return var_operation_return( + js_expression=f"{array}.slice().reverse()", + type_computer=passthrough_unary_type_computer(ReflexCallable[[List], List]), + ) + + +@var_operation +def array_lt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): + """Check if an array is less than another array. + + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. + + Returns: + The array less than operation. + """ + return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool) + + +@var_operation +def array_gt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): + """Check if an array is greater than another array. + + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. + + Returns: + The array greater than operation. + """ + return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool) + + +@var_operation +def array_le_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): + """Check if an array is less than or equal to another array. + + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. + + Returns: + The array less than or equal operation. + """ + return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool) + + +@var_operation +def array_ge_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): + """Check if an array is greater than or equal to another array. + + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. + + Returns: + The array greater than or equal operation. + """ + return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool) + + +@var_operation +def array_length_operation(array: Var[ARRAY_VAR_TYPE]): + """Get the length of an array. + + Args: + array: The array. + + Returns: + The length of the array. + """ + return var_operation_return( + js_expression=f"{array}.length", + var_type=int, + ) + + +@var_operation +def string_split_operation( + string: Var[str], sep: VarWithDefault[str] = VarWithDefault("") +): + """Split a string. + + Args: + string: The string to split. + sep: The separator. + + Returns: + The split string. + """ + return var_operation_return( + js_expression=f"isTrue({sep}) ? {string}.split({sep}) : [...{string}]", + var_type=Sequence[str], + var_data=VarData(imports=_IS_TRUE_IMPORT), + ) + + +@var_operation +def array_slice_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], + slice: Var[slice], +) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR]]: + """Get a slice from an array. + + Args: + array: The array. + slice: The slice. + + Returns: + The item or slice from the array. + """ + return var_operation_return( + js_expression=f"at_slice({array}, {slice})", + type_computer=nary_type_computer( + ReflexCallable[[List, slice], Any], + ReflexCallable[[slice], Any], + computer=lambda args: args[0]._var_type, + ), + var_data=VarData( + imports=_AT_SLICE_IMPORT, + ), + ) + + +@var_operation +def array_item_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], index: Var[int] +) -> CustomVarOperationReturn[INNER_ARRAY_VAR]: + """Get an item from an array. + + Args: + array: The array. + index: The index of the item. + + Returns: + The item from the array. + """ + + def type_computer(*args): + if len(args) == 0: + return ( + ReflexCallable[[List[Any], int], Any], + functools.partial(type_computer, *args), + ) + + array = args[0] + array_args = typing.get_args(array._var_type) + + if len(args) == 1: + return ( + ReflexCallable[[int], unionize(*array_args)], + functools.partial(type_computer, *args), + ) + + index = args[1] + + if ( + array_args + and isinstance(index, LiteralNumberVar) + and is_tuple_type(array._var_type) + ): + index_value = int(index._var_value) + element_type = array_args[index_value % len(array_args)] + else: + element_type = unionize(*array_args) + + return (ReflexCallable[[], element_type], None) + + return var_operation_return( + js_expression=f"{array}.at({index})", + type_computer=type_computer, + ) + + +@var_operation +def array_range_operation( + e1: Var[int], + e2: VarWithDefault[int | None] = VarWithDefault(None), + step: VarWithDefault[int] = VarWithDefault(1), +) -> CustomVarOperationReturn[Sequence[int]]: + """Create a range of numbers. + + Args: + e1: The end of the range if e2 is not provided, otherwise the start of the range. + e2: The end of the range. + step: The step of the range. + + Returns: + The range of numbers. + """ + return var_operation_return( + js_expression=f"range({e1}, {e2}, {step})", + var_type=List[int], + var_data=VarData( + imports=_RANGE_IMPORT, + ), + ) + + +@var_operation +def array_contains_field_operation( + haystack: Var[ARRAY_VAR_TYPE], + needle: Var[Any], + field: VarWithDefault[str] = VarWithDefault(""), +): + """Check if an array contains an element. + + Args: + haystack: The array to check. + needle: The element to check for. + field: The field to check. + + Returns: + The array contains operation. + """ + return var_operation_return( + js_expression=f"isTrue({field}) ? {haystack}.some(obj => obj[{field}] === {needle}) : {haystack}.some(obj => obj === {needle})", + var_type=bool, + var_data=VarData( + imports=_IS_TRUE_IMPORT, + ), + ) + + +@var_operation +def array_contains_operation(haystack: Var[ARRAY_VAR_TYPE], needle: Var): + """Check if an array contains an element. + + Args: + haystack: The array to check. + needle: The element to check for. + + Returns: + The array contains operation. + """ + return var_operation_return( + js_expression=f"{haystack}.includes({needle})", + var_type=bool, + ) + + +@var_operation +def repeat_array_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], count: Var[int] +) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR]]: + """Repeat an array a number of times. + + Args: + array: The array to repeat. + count: The number of times to repeat the array. + + Returns: + The repeated array. + """ + + def type_computer(*args: Var): + if not args: + return ( + ReflexCallable[[List[Any], int], List[Any]], + type_computer, + ) + if len(args) == 1: + return ( + ReflexCallable[[int], args[0]._var_type], + functools.partial(type_computer, *args), + ) + return (ReflexCallable[[], args[0]._var_type], None) + + return var_operation_return( + js_expression=f"Array.from({{ length: {count} }}).flatMap(() => {array})", + type_computer=type_computer, + ) + + +@var_operation +def repeat_string_operation( + string: Var[str], count: Var[int] +) -> CustomVarOperationReturn[str]: + """Repeat a string a number of times. + + Args: + string: The string to repeat. + count: The number of times to repeat the string. + + Returns: + The repeated string. + """ + return var_operation_return( + js_expression=f"{string}.repeat({count})", + var_type=str, + ) + + +if TYPE_CHECKING: + pass + + +@var_operation +def map_array_operation( + array: Var[Sequence[INNER_ARRAY_VAR]], + function: Var[ + ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR] + | ReflexCallable[[], ANOTHER_ARRAY_VAR] + ], +) -> CustomVarOperationReturn[Sequence[ANOTHER_ARRAY_VAR]]: + """Map a function over an array. + + Args: + array: The array. + function: The function to map. + + Returns: + The mapped array. + """ + + def type_computer(*args: Var): + if not args: + return ( + ReflexCallable[[List[Any], ReflexCallable], List[Any]], + type_computer, + ) + if len(args) == 1: + return ( + ReflexCallable[[ReflexCallable], List[Any]], + functools.partial(type_computer, *args), + ) + return (ReflexCallable[[], List[args[0]._var_type]], None) + + return var_operation_return( + js_expression=f"{array}.map({function})", + type_computer=nary_type_computer( + ReflexCallable[[List[Any], ReflexCallable], List[Any]], + ReflexCallable[[ReflexCallable], List[Any]], + computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], # type: ignore + ), + ) + + +@var_operation +def array_concat_operation( + lhs: Var[Sequence[INNER_ARRAY_VAR]], rhs: Var[Sequence[ANOTHER_ARRAY_VAR]] +) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR | ANOTHER_ARRAY_VAR]]: + """Concatenate two arrays. + + Args: + lhs: The left-hand side array. + rhs: The right-hand side array. + + Returns: + The concatenated array. + """ + return var_operation_return( + js_expression=f"[...{lhs}, ...{rhs}]", + type_computer=nary_type_computer( + ReflexCallable[[List[Any], List[Any]], List[Any]], + ReflexCallable[[List[Any]], List[Any]], + computer=lambda args: unionize(args[0]._var_type, args[1]._var_type), + ), + ) + + +@var_operation +def string_concat_operation( + lhs: Var[str], rhs: Var[str] +) -> CustomVarOperationReturn[str]: + """Concatenate two strings. + + Args: + lhs: The left-hand side string. + rhs: The right-hand side string. + + Returns: + The concatenated string. + """ + return var_operation_return( + js_expression=f"{lhs} + {rhs}", + var_type=str, + ) + + +@var_operation +def reverse_string_concat_operation( + lhs: Var[str], rhs: Var[str] +) -> CustomVarOperationReturn[str]: + """Concatenate two strings in reverse order. + + Args: + lhs: The left-hand side string. + rhs: The right-hand side string. + + Returns: + The concatenated string. + """ + return var_operation_return( + js_expression=f"{rhs} + {lhs}", + var_type=str, + ) + + +class SliceVar(Var[slice], python_types=slice): + """Base class for immutable slice vars.""" + + +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) +class LiteralSliceVar(CachedVarOperation, LiteralVar, SliceVar): + """Base class for immutable literal slice vars.""" + + _var_value: slice = dataclasses.field(default=slice(None)) + + @cached_property_no_lock + def _cached_var_name(self) -> str: + """The name of the var. + + Returns: + The name of the var. + """ + return f"[{str(LiteralVar.create(self._var_value.start))}, {str(LiteralVar.create(self._var_value.stop))}, {str(LiteralVar.create(self._var_value.step))}]" + + @cached_property_no_lock + def _cached_get_all_var_data(self) -> VarData | None: + """Get all the VarData asVarDatae Var. + + Returns: + The VarData associated with the Var. + """ + return VarData.merge( + *[ + var._get_all_var_data() + for var in [ + self._var_value.start, + self._var_value.stop, + self._var_value.step, + ] + if isinstance(var, Var) + ], + self._var_data, + ) + + @classmethod + def create( + cls, + value: slice, + _var_type: Type[slice] | None = None, + _var_data: VarData | None = None, + ) -> SliceVar: + """Create a var from a slice value. + + Args: + value: The value to create the var from. + _var_type: The type of the var. + _var_data: Additional hooks and imports associated with the Var. + + Returns: + The var. + """ + return cls( + _js_expr="", + _var_type=_var_type, + _var_data=_var_data, + _var_value=value, + ) + + def __hash__(self) -> int: + """Get the hash of the var. + + Returns: + The hash of the var. + """ + return hash( + ( + self.__class__.__name__, + self._var_value.start, + self._var_value.stop, + self._var_value.step, + ) + ) + + def json(self) -> str: + """Get the JSON representation of the var. + + Returns: + The JSON representation of the var. + """ + return json.dumps( + [self._var_value.start, self._var_value.stop, self._var_value.step] + ) + + +class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(Sequence, set)): + """Base class for immutable array vars.""" + + join = array_join_operation + + reverse = array_reverse_operation + + __add__ = array_concat_operation + + __getitem__ = array_item_operation + + slice = array_slice_operation + + length = array_length_operation + + range: ClassVar[ + FunctionVar[ + ReflexCallable[ + [int, VarWithDefault[int | None], VarWithDefault[int]], Sequence[int] + ] + ] + ] = array_range_operation + + contains = array_contains_field_operation + + pluck = array_pluck_operation + + __rmul__ = __mul__ = repeat_array_operation + + __lt__ = array_lt_operation + + __gt__ = array_gt_operation + + __le__ = array_le_operation + + __ge__ = array_ge_operation + + def foreach( + self: ArrayVar[Sequence[INNER_ARRAY_VAR]], + fn: Callable[[Var[INNER_ARRAY_VAR]], ANOTHER_ARRAY_VAR] + | Callable[[], ANOTHER_ARRAY_VAR], + ) -> ArrayVar[Sequence[ANOTHER_ARRAY_VAR]]: + """Apply a function to each element of the array. + + Args: + fn: The function to apply. + + Returns: + The array after applying the function. + + Raises: + VarTypeError: If the function takes more than one argument. + """ + from .function import ArgsFunctionOperation + + if not callable(fn): + raise_unsupported_operand_types("foreach", (type(self), type(fn))) + # get the number of arguments of the function + num_args = len(inspect.signature(fn).parameters) + if num_args > 1: + raise VarTypeError( + "The function passed to foreach should take at most one argument." + ) + + if num_args == 0: + return_value = fn() # type: ignore + simple_function_var: FunctionVar[ReflexCallable[[], ANOTHER_ARRAY_VAR]] = ( + ArgsFunctionOperation.create(tuple(), return_value) + ) + return map_array_operation(self, simple_function_var).guess_type() + + # generic number var + number_var = Var("").to(NumberVar, int) + + first_arg_type = self.__getitem__(number_var)._var_type + + arg_name = get_unique_variable_name() + + # get first argument type + first_arg = cast( + Var[Any], + Var( + _js_expr=arg_name, + _var_type=first_arg_type, + ).guess_type(), + ) + + function_var = cast( + Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]], + ArgsFunctionOperation.create( + (arg_name,), + Var.create(fn(first_arg)), # type: ignore + ), + ) + + return map_array_operation.call(self, function_var).guess_type() + + +LIST_ELEMENT = TypeVar("LIST_ELEMENT", covariant=True) + +ARRAY_VAR_OF_LIST_ELEMENT = TypeAliasType( + "ARRAY_VAR_OF_LIST_ELEMENT", + Union[ + ArrayVar[Sequence[LIST_ELEMENT]], + ArrayVar[Set[LIST_ELEMENT]], + ], + type_params=(LIST_ELEMENT,), +) + + +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) +class LiteralArrayVar(CachedVarOperation, LiteralVar, ArrayVar[ARRAY_VAR_TYPE]): + """Base class for immutable literal array vars.""" + + _var_value: Union[ + Sequence[Union[Var, Any]], + Set[Union[Var, Any]], + ] = dataclasses.field(default_factory=list) + + @cached_property_no_lock + def _cached_var_name(self) -> str: + """The name of the var. + + Returns: + The name of the var. + """ + return ( + "[" + + ", ".join( + [str(LiteralVar.create(element)) for element in self._var_value] + ) + + "]" + ) + + @cached_property_no_lock + def _cached_get_all_var_data(self) -> VarData | None: + """Get all the VarData associated with the Var. + + Returns: + The VarData associated with the Var. + """ + return VarData.merge( + *[ + LiteralVar.create(element)._get_all_var_data() + for element in self._var_value + ], + self._var_data, + ) + + def __hash__(self) -> int: + """Get the hash of the var. + + Returns: + The hash of the var. + """ + return hash((self.__class__.__name__, self._js_expr)) + + def json(self) -> str: + """Get the JSON representation of the var. + + Returns: + The JSON representation of the var. + """ + return ( + "[" + + ", ".join( + [LiteralVar.create(element).json() for element in self._var_value] + ) + + "]" + ) + + @classmethod + def create( + cls, + value: ARRAY_VAR_TYPE, + _var_type: Type[ARRAY_VAR_TYPE] | None = None, + _var_data: VarData | None = None, + ) -> LiteralArrayVar[ARRAY_VAR_TYPE]: + """Create a var from a string value. + + Args: + value: The value to create the var from. + _var_data: Additional hooks and imports associated with the Var. + + Returns: + The var. + """ + return cls( + _js_expr="", + _var_type=figure_out_type(value) if _var_type is None else _var_type, + _var_data=_var_data, + _var_value=value, + ) + + +class StringVar(Var[STRING_TYPE], python_types=str): + """Base class for immutable string vars.""" + + __add__ = string_concat_operation + + __radd__ = reverse_string_concat_operation + + __getitem__ = string_item_operation + + lower = string_lower_operation + + upper = string_upper_operation + + strip = string_strip_operation + + contains = string_contains_field_operation + + split = string_split_operation + + length = split.chain(array_length_operation) + + reversed = split.chain(array_reverse_operation).chain(array_join_operation) + + startswith = string_starts_with_operation + + __rmul__ = __mul__ = repeat_string_operation + + __lt__ = string_lt_operation + + __gt__ = string_gt_operation + + __le__ = string_le_operation + + __ge__ = string_ge_operation + + # Compile regex for finding reflex var tags. _decode_var_pattern_re = ( rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}" @@ -749,784 +1244,6 @@ class ConcatVarOperation(CachedVarOperation, StringVar[str]): ) -ARRAY_VAR_TYPE = TypeVar("ARRAY_VAR_TYPE", bound=Union[List, Tuple, Set]) - -OTHER_TUPLE = TypeVar("OTHER_TUPLE") - -INNER_ARRAY_VAR = TypeVar("INNER_ARRAY_VAR") -ANOTHER_ARRAY_VAR = TypeVar("ANOTHER_ARRAY_VAR") - -KEY_TYPE = TypeVar("KEY_TYPE") -VALUE_TYPE = TypeVar("VALUE_TYPE") - - -class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(list, tuple, set)): - """Base class for immutable array vars.""" - - @overload - def join(self, sep: StringVar | str = "") -> StringVar: ... - - @overload - def join(self, sep: NoReturn) -> NoReturn: ... - - def join(self, sep: Any = "") -> StringVar: - """Join the elements of the array. - - Args: - sep: The separator between elements. - - Returns: - The joined elements. - """ - if not isinstance(sep, (StringVar, str)): - raise_unsupported_operand_types("join", (type(self), type(sep))) - if ( - isinstance(self, LiteralArrayVar) - and ( - len( - args := [ - x - for x in self._var_value - if isinstance(x, (LiteralStringVar, str)) - ] - ) - == len(self._var_value) - ) - and isinstance(sep, (LiteralStringVar, str)) - ): - sep_str = sep._var_value if isinstance(sep, LiteralStringVar) else sep - return LiteralStringVar.create( - sep_str.join( - i._var_value if isinstance(i, LiteralStringVar) else i for i in args - ) - ) - return array_join_operation(self, sep).guess_type() - - def reverse(self) -> ArrayVar[ARRAY_VAR_TYPE]: - """Reverse the array. - - Returns: - The reversed array. - """ - return array_reverse_operation(self).to(ArrayVar, self._var_type) - - @overload - def __add__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> ArrayVar[ARRAY_VAR_TYPE]: ... - - @overload - def __add__(self, other: NoReturn) -> NoReturn: ... - - def __add__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]: - """Concatenate two arrays. - - Parameters: - other: The other array to concatenate. - - Returns: - ArrayConcatOperation: The concatenation of the two arrays. - """ - if not isinstance(other, ArrayVar): - raise_unsupported_operand_types("+", (type(self), type(other))) - - return array_concat_operation(self, other).to( - ArrayVar, unionize(self._var_type, other._var_type) - ) - - @overload - def __getitem__(self, i: slice) -> ArrayVar[ARRAY_VAR_TYPE]: ... - - @overload - def __getitem__( - self: ( - ArrayVar[Tuple[int, OTHER_TUPLE]] - | ArrayVar[Tuple[float, OTHER_TUPLE]] - | ArrayVar[Tuple[int | float, OTHER_TUPLE]] - ), - i: Literal[0, -2], - ) -> NumberVar: ... - - @overload - def __getitem__( - self: ( - ArrayVar[Tuple[Any, int]] - | ArrayVar[Tuple[Any, float]] - | ArrayVar[Tuple[Any, int | float]] - ), - i: Literal[1, -1], - ) -> NumberVar: ... - - @overload - def __getitem__( - self: ArrayVar[Tuple[str, Any]], i: Literal[0, -2] - ) -> StringVar: ... - - @overload - def __getitem__( - self: ArrayVar[Tuple[Any, str]], i: Literal[1, -1] - ) -> StringVar: ... - - @overload - def __getitem__( - self: ArrayVar[Tuple[bool, Any]], i: Literal[0, -2] - ) -> BooleanVar: ... - - @overload - def __getitem__( - self: ArrayVar[Tuple[Any, bool]], i: Literal[1, -1] - ) -> BooleanVar: ... - - @overload - def __getitem__( - self: ( - ARRAY_VAR_OF_LIST_ELEMENT[int] - | ARRAY_VAR_OF_LIST_ELEMENT[float] - | ARRAY_VAR_OF_LIST_ELEMENT[int | float] - ), - i: int | NumberVar, - ) -> NumberVar: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[str], i: int | NumberVar - ) -> StringVar: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[bool], i: int | NumberVar - ) -> BooleanVar: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[List[INNER_ARRAY_VAR]], - i: int | NumberVar, - ) -> ArrayVar[List[INNER_ARRAY_VAR]]: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[Set[INNER_ARRAY_VAR]], - i: int | NumberVar, - ) -> ArrayVar[Set[INNER_ARRAY_VAR]]: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[Tuple[KEY_TYPE, VALUE_TYPE]], - i: int | NumberVar, - ) -> ArrayVar[Tuple[KEY_TYPE, VALUE_TYPE]]: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[Tuple[INNER_ARRAY_VAR, ...]], - i: int | NumberVar, - ) -> ArrayVar[Tuple[INNER_ARRAY_VAR, ...]]: ... - - @overload - def __getitem__( - self: ARRAY_VAR_OF_LIST_ELEMENT[Dict[KEY_TYPE, VALUE_TYPE]], - i: int | NumberVar, - ) -> ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]: ... - - @overload - def __getitem__(self, i: int | NumberVar) -> Var: ... - - def __getitem__(self, i: Any) -> ArrayVar[ARRAY_VAR_TYPE] | Var: - """Get a slice of the array. - - Args: - i: The slice. - - Returns: - The array slice operation. - """ - if isinstance(i, slice): - return ArraySliceOperation.create(self, i) - if not isinstance(i, (int, NumberVar)) or ( - isinstance(i, NumberVar) and i._is_strict_float() - ): - raise_unsupported_operand_types("[]", (type(self), type(i))) - return array_item_operation(self, i).guess_type() - - def length(self) -> NumberVar: - """Get the length of the array. - - Returns: - The length of the array. - """ - return array_length_operation(self).guess_type() - - @overload - @classmethod - def range(cls, stop: int | NumberVar, /) -> ArrayVar[List[int]]: ... - - @overload - @classmethod - def range( - cls, - start: int | NumberVar, - end: int | NumberVar, - step: int | NumberVar = 1, - /, - ) -> ArrayVar[List[int]]: ... - - @overload - @classmethod - def range( - cls, - first_endpoint: int | NumberVar, - second_endpoint: int | NumberVar | None = None, - step: int | NumberVar | None = None, - ) -> ArrayVar[List[int]]: ... - - @classmethod - def range( - cls, - first_endpoint: int | NumberVar, - second_endpoint: int | NumberVar | None = None, - step: int | NumberVar | None = None, - ) -> ArrayVar[List[int]]: - """Create a range of numbers. - - Args: - first_endpoint: The end of the range if second_endpoint is not provided, otherwise the start of the range. - second_endpoint: The end of the range. - step: The step of the range. - - Returns: - The range of numbers. - """ - if any( - not isinstance(i, (int, NumberVar)) - for i in (first_endpoint, second_endpoint, step) - if i is not None - ): - raise_unsupported_operand_types( - "range", (type(first_endpoint), type(second_endpoint), type(step)) - ) - if second_endpoint is None: - start = 0 - end = first_endpoint - else: - start = first_endpoint - end = second_endpoint - - return array_range_operation(start, end, step or 1).guess_type() - - @overload - def contains(self, other: Any) -> BooleanVar: ... - - @overload - def contains(self, other: Any, field: StringVar | str) -> BooleanVar: ... - - def contains(self, other: Any, field: Any = None) -> BooleanVar: - """Check if the array contains an element. - - Args: - other: The element to check for. - field: The field to check. - - Returns: - The array contains operation. - """ - if field is not None: - if not isinstance(field, (StringVar, str)): - raise_unsupported_operand_types("contains", (type(self), type(field))) - return array_contains_field_operation(self, other, field).guess_type() - return array_contains_operation(self, other).guess_type() - - def pluck(self, field: StringVar | str) -> ArrayVar: - """Pluck a field from the array. - - Args: - field: The field to pluck from the array. - - Returns: - The array pluck operation. - """ - return array_pluck_operation(self, field).guess_type() - - @overload - def __mul__(self, other: NumberVar | int) -> ArrayVar[ARRAY_VAR_TYPE]: ... - - @overload - def __mul__(self, other: NoReturn) -> NoReturn: ... - - def __mul__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]: - """Multiply the sequence by a number or integer. - - Parameters: - other: The number or integer to multiply the sequence by. - - Returns: - ArrayVar[ARRAY_VAR_TYPE]: The result of multiplying the sequence by the given number or integer. - """ - if not isinstance(other, (NumberVar, int)) or ( - isinstance(other, NumberVar) and other._is_strict_float() - ): - raise_unsupported_operand_types("*", (type(self), type(other))) - - return repeat_array_operation(self, other).to(ArrayVar, self._var_type) - - __rmul__ = __mul__ # type: ignore - - @overload - def __lt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... - - @overload - def __lt__(self, other: list | tuple) -> BooleanVar: ... - - def __lt__(self, other: Any): - """Check if the array is less than another array. - - Args: - other: The other array. - - Returns: - The array less than operation. - """ - if not isinstance(other, (ArrayVar, list, tuple)): - raise_unsupported_operand_types("<", (type(self), type(other))) - - return array_lt_operation(self, other).guess_type() - - @overload - def __gt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... - - @overload - def __gt__(self, other: list | tuple) -> BooleanVar: ... - - def __gt__(self, other: Any): - """Check if the array is greater than another array. - - Args: - other: The other array. - - Returns: - The array greater than operation. - """ - if not isinstance(other, (ArrayVar, list, tuple)): - raise_unsupported_operand_types(">", (type(self), type(other))) - - return array_gt_operation(self, other).guess_type() - - @overload - def __le__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... - - @overload - def __le__(self, other: list | tuple) -> BooleanVar: ... - - def __le__(self, other: Any): - """Check if the array is less than or equal to another array. - - Args: - other: The other array. - - Returns: - The array less than or equal operation. - """ - if not isinstance(other, (ArrayVar, list, tuple)): - raise_unsupported_operand_types("<=", (type(self), type(other))) - - return array_le_operation(self, other).guess_type() - - @overload - def __ge__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ... - - @overload - def __ge__(self, other: list | tuple) -> BooleanVar: ... - - def __ge__(self, other: Any): - """Check if the array is greater than or equal to another array. - - Args: - other: The other array. - - Returns: - The array greater than or equal operation. - """ - if not isinstance(other, (ArrayVar, list, tuple)): - raise_unsupported_operand_types(">=", (type(self), type(other))) - - return array_ge_operation(self, other).guess_type() - - def foreach( - self: ARRAY_VAR_OF_LIST_ELEMENT[INNER_ARRAY_VAR], - fn: Callable[[Var[INNER_ARRAY_VAR]], ANOTHER_ARRAY_VAR] - | Callable[[], ANOTHER_ARRAY_VAR], - ) -> ArrayVar[List[ANOTHER_ARRAY_VAR]]: - """Apply a function to each element of the array. - - Args: - fn: The function to apply. - - Returns: - The array after applying the function. - - Raises: - VarTypeError: If the function takes more than one argument. - """ - from .function import ArgsFunctionOperation - - if not callable(fn): - raise_unsupported_operand_types("foreach", (type(self), type(fn))) - # get the number of arguments of the function - num_args = len(inspect.signature(fn).parameters) - if num_args > 1: - raise VarTypeError( - "The function passed to foreach should take at most one argument." - ) - - if num_args == 0: - return_value = fn() # type: ignore - simple_function_var: FunctionVar[ReflexCallable[[], ANOTHER_ARRAY_VAR]] = ( - ArgsFunctionOperation.create(tuple(), return_value) - ) - return map_array_operation(self, simple_function_var).guess_type() - - # generic number var - number_var = Var("").to(NumberVar, int) - - first_arg_type = self[number_var]._var_type - - arg_name = get_unique_variable_name() - - # get first argument type - first_arg = cast( - Var[Any], - Var( - _js_expr=arg_name, - _var_type=first_arg_type, - ).guess_type(), - ) - - function_var: FunctionVar[ - ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR] - ] = ArgsFunctionOperation.create( - (arg_name,), - Var.create(fn(first_arg)), # type: ignore - ) - - return map_array_operation(self, function_var).guess_type() - - -LIST_ELEMENT = TypeVar("LIST_ELEMENT") - -ARRAY_VAR_OF_LIST_ELEMENT = TypeAliasType( - "ARRAY_VAR_OF_LIST_ELEMENT", - Union[ - ArrayVar[List[LIST_ELEMENT]], - ArrayVar[Tuple[LIST_ELEMENT, ...]], - ArrayVar[Set[LIST_ELEMENT]], - ], - type_params=(LIST_ELEMENT,), -) - - -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class LiteralArrayVar(CachedVarOperation, LiteralVar, ArrayVar[ARRAY_VAR_TYPE]): - """Base class for immutable literal array vars.""" - - _var_value: Union[ - List[Union[Var, Any]], - Set[Union[Var, Any]], - Tuple[Union[Var, Any], ...], - ] = dataclasses.field(default_factory=list) - - @cached_property_no_lock - def _cached_var_name(self) -> str: - """The name of the var. - - Returns: - The name of the var. - """ - return ( - "[" - + ", ".join( - [str(LiteralVar.create(element)) for element in self._var_value] - ) - + "]" - ) - - @cached_property_no_lock - def _cached_get_all_var_data(self) -> VarData | None: - """Get all the VarData associated with the Var. - - Returns: - The VarData associated with the Var. - """ - return VarData.merge( - *[ - LiteralVar.create(element)._get_all_var_data() - for element in self._var_value - ], - self._var_data, - ) - - def __hash__(self) -> int: - """Get the hash of the var. - - Returns: - The hash of the var. - """ - return hash((self.__class__.__name__, self._js_expr)) - - def json(self) -> str: - """Get the JSON representation of the var. - - Returns: - The JSON representation of the var. - """ - return ( - "[" - + ", ".join( - [LiteralVar.create(element).json() for element in self._var_value] - ) - + "]" - ) - - @classmethod - def create( - cls, - value: ARRAY_VAR_TYPE, - _var_type: Type[ARRAY_VAR_TYPE] | None = None, - _var_data: VarData | None = None, - ) -> LiteralArrayVar[ARRAY_VAR_TYPE]: - """Create a var from a string value. - - Args: - value: The value to create the var from. - _var_data: Additional hooks and imports associated with the Var. - - Returns: - The var. - """ - return cls( - _js_expr="", - _var_type=figure_out_type(value) if _var_type is None else _var_type, - _var_data=_var_data, - _var_value=value, - ) - - -@var_operation -def string_split_operation(string: Var[str], sep: Var[str]): - """Split a string. - - Args: - string: The string to split. - sep: The separator. - - Returns: - The split string. - """ - return var_operation_return( - js_expression=f"{string}.split({sep})", var_type=List[str] - ) - - -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ArraySliceOperation(CachedVarOperation, ArrayVar): - """Base class for immutable string vars that are the result of a string slice operation.""" - - _array: ArrayVar = dataclasses.field( - default_factory=lambda: LiteralArrayVar.create([]) - ) - _start: NumberVar | int = dataclasses.field(default_factory=lambda: 0) - _stop: NumberVar | int = dataclasses.field(default_factory=lambda: 0) - _step: NumberVar | int = dataclasses.field(default_factory=lambda: 1) - - @cached_property_no_lock - def _cached_var_name(self) -> str: - """The name of the var. - - Returns: - The name of the var. - - Raises: - ValueError: If the slice step is zero. - """ - start, end, step = self._start, self._stop, self._step - - normalized_start = ( - LiteralVar.create(start) if start is not None else Var(_js_expr="undefined") - ) - normalized_end = ( - LiteralVar.create(end) if end is not None else Var(_js_expr="undefined") - ) - if step is None: - return f"{str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)})" - if not isinstance(step, Var): - if step < 0: - actual_start = end + 1 if end is not None else 0 - actual_end = start + 1 if start is not None else self._array.length() - return str(self._array[actual_start:actual_end].reverse()[::-step]) - if step == 0: - raise ValueError("slice step cannot be zero") - return f"{str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)}).filter((_, i) => i % {str(step)} === 0)" - - actual_start_reverse = end + 1 if end is not None else 0 - actual_end_reverse = start + 1 if start is not None else self._array.length() - - return f"{str(self.step)} > 0 ? {str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)}).filter((_, i) => i % {str(step)} === 0) : {str(self._array)}.slice({str(actual_start_reverse)}, {str(actual_end_reverse)}).reverse().filter((_, i) => i % {str(-step)} === 0)" - - @classmethod - def create( - cls, - array: ArrayVar, - slice: slice, - _var_data: VarData | None = None, - ) -> ArraySliceOperation: - """Create a var from a string value. - - Args: - array: The array. - slice: The slice. - _var_data: Additional hooks and imports associated with the Var. - - Returns: - The var. - """ - return cls( - _js_expr="", - _var_type=array._var_type, - _var_data=_var_data, - _array=array, - _start=slice.start, - _stop=slice.stop, - _step=slice.step, - ) - - -@var_operation -def array_pluck_operation( - array: Var[ARRAY_VAR_TYPE], - field: Var[str], -) -> CustomVarOperationReturn[List]: - """Pluck a field from an array of objects. - - Args: - array: The array to pluck from. - field: The field to pluck from the objects in the array. - - Returns: - The reversed array. - """ - return var_operation_return( - js_expression=f"{array}.map(e=>e?.[{field}])", - var_type=List[Any], - ) - - -@var_operation -def array_join_operation(array: Var[ARRAY_VAR_TYPE], sep: Var[str]): - """Join the elements of an array. - - Args: - array: The array. - sep: The separator. - - Returns: - The joined elements. - """ - return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) - - -@var_operation -def array_reverse_operation( - array: Var[ARRAY_VAR_TYPE], -) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: - """Reverse an array. - - Args: - array: The array to reverse. - - Returns: - The reversed array. - """ - return var_operation_return( - js_expression=f"{array}.slice().reverse()", - type_computer=passthrough_unary_type_computer(ReflexCallable[[List], List]), - ) - - -@var_operation -def array_lt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): - """Check if an array is less than another array. - - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. - - Returns: - The array less than operation. - """ - return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool) - - -@var_operation -def array_gt_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): - """Check if an array is greater than another array. - - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. - - Returns: - The array greater than operation. - """ - return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool) - - -@var_operation -def array_le_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): - """Check if an array is less than or equal to another array. - - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. - - Returns: - The array less than or equal operation. - """ - return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool) - - -@var_operation -def array_ge_operation(lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE]): - """Check if an array is greater than or equal to another array. - - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. - - Returns: - The array greater than or equal operation. - """ - return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool) - - -@var_operation -def array_length_operation(array: Var[ARRAY_VAR_TYPE]): - """Get the length of an array. - - Args: - array: The array. - - Returns: - The length of the array. - """ - return var_operation_return( - js_expression=f"{array}.length", - var_type=int, - ) - - def is_tuple_type(t: GenericType) -> bool: """Check if a type is a tuple type. @@ -1541,207 +1258,6 @@ def is_tuple_type(t: GenericType) -> bool: return get_origin(t) is tuple -@var_operation -def array_item_operation(array: Var[ARRAY_VAR_TYPE], index: Var[int]): - """Get an item from an array. - - Args: - array: The array. - index: The index of the item. - - Returns: - The item from the array. - """ - - def type_computer(*args): - if len(args) == 0: - return ( - ReflexCallable[[List[Any], int], Any], - functools.partial(type_computer, *args), - ) - - array = args[0] - array_args = typing.get_args(array._var_type) - - if len(args) == 1: - return ( - ReflexCallable[[int], unionize(*array_args)], - functools.partial(type_computer, *args), - ) - - index = args[1] - - if ( - array_args - and isinstance(index, LiteralNumberVar) - and is_tuple_type(array._var_type) - ): - index_value = int(index._var_value) - element_type = array_args[index_value % len(array_args)] - else: - element_type = unionize(*array_args) - - return (ReflexCallable[[], element_type], None) - - return var_operation_return( - js_expression=f"{str(array)}.at({str(index)})", - type_computer=type_computer, - ) - - -@var_operation -def array_range_operation(start: Var[int], stop: Var[int], step: Var[int]): - """Create a range of numbers. - - Args: - start: The start of the range. - stop: The end of the range. - step: The step of the range. - - Returns: - The range of numbers. - """ - return var_operation_return( - js_expression=f"Array.from({{ length: ({str(stop)} - {str(start)}) / {str(step)} }}, (_, i) => {str(start)} + i * {str(step)})", - var_type=List[int], - ) - - -@var_operation -def array_contains_field_operation( - haystack: Var[ARRAY_VAR_TYPE], needle: Var, field: Var[str] -): - """Check if an array contains an element. - - Args: - haystack: The array to check. - needle: The element to check for. - field: The field to check. - - Returns: - The array contains operation. - """ - return var_operation_return( - js_expression=f"{haystack}.some(obj => obj[{field}] === {needle})", - var_type=bool, - ) - - -@var_operation -def array_contains_operation(haystack: Var[ARRAY_VAR_TYPE], needle: Var): - """Check if an array contains an element. - - Args: - haystack: The array to check. - needle: The element to check for. - - Returns: - The array contains operation. - """ - return var_operation_return( - js_expression=f"{haystack}.includes({needle})", - var_type=bool, - ) - - -@var_operation -def repeat_array_operation( - array: Var[ARRAY_VAR_TYPE], count: Var[int] -) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: - """Repeat an array a number of times. - - Args: - array: The array to repeat. - count: The number of times to repeat the array. - - Returns: - The repeated array. - """ - - def type_computer(*args: Var): - if not args: - return ( - ReflexCallable[[List[Any], int], List[Any]], - type_computer, - ) - if len(args) == 1: - return ( - ReflexCallable[[int], args[0]._var_type], - functools.partial(type_computer, *args), - ) - return (ReflexCallable[[], args[0]._var_type], None) - - return var_operation_return( - js_expression=f"Array.from({{ length: {count} }}).flatMap(() => {array})", - type_computer=type_computer, - ) - - -if TYPE_CHECKING: - pass - - -@var_operation -def map_array_operation( - array: Var[ARRAY_VAR_OF_LIST_ELEMENT[INNER_ARRAY_VAR]], - function: Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]], -) -> CustomVarOperationReturn[List[ANOTHER_ARRAY_VAR]]: - """Map a function over an array. - - Args: - array: The array. - function: The function to map. - - Returns: - The mapped array. - """ - - def type_computer(*args: Var): - if not args: - return ( - ReflexCallable[[List[Any], ReflexCallable], List[Any]], - type_computer, - ) - if len(args) == 1: - return ( - ReflexCallable[[ReflexCallable], List[Any]], - functools.partial(type_computer, *args), - ) - return (ReflexCallable[[], List[args[0]._var_type]], None) - - return var_operation_return( - js_expression=f"{array}.map({function})", - type_computer=nary_type_computer( - ReflexCallable[[List[Any], ReflexCallable], List[Any]], - ReflexCallable[[ReflexCallable], List[Any]], - computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], # type: ignore - ), - ) - - -@var_operation -def array_concat_operation( - lhs: Var[ARRAY_VAR_TYPE], rhs: Var[ARRAY_VAR_TYPE] -) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]: - """Concatenate two arrays. - - Args: - lhs: The left-hand side array. - rhs: The right-hand side array. - - Returns: - The concatenated array. - """ - return var_operation_return( - js_expression=f"[...{lhs}, ...{rhs}]", - type_computer=nary_type_computer( - ReflexCallable[[List[Any], List[Any]], List[Any]], - ReflexCallable[[List[Any]], List[Any]], - computer=lambda args: unionize(args[0]._var_type, args[1]._var_type), - ), - ) - - class ColorVar(StringVar[Color], python_types=Color): """Base class for immutable color vars.""" @@ -1802,7 +1318,7 @@ class LiteralColorVar(CachedVarOperation, LiteralVar, ColorVar): Returns: The name of the var. """ - alpha = self._var_value.alpha + alpha = cast(Union[Var[bool], bool], self._var_value.alpha) alpha = ( ternary_operation( alpha, diff --git a/tests/units/components/core/test_foreach.py b/tests/units/components/core/test_foreach.py index 228165d3e..f781a9254 100644 --- a/tests/units/components/core/test_foreach.py +++ b/tests/units/components/core/test_foreach.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Set, Tuple, Union +from typing import Dict, List, Sequence, Set, Tuple, Union import pytest @@ -115,8 +115,10 @@ def display_colors_set(color): return box(text(color)) -def display_nested_list_element(element: ArrayVar[List[str]], index: NumberVar[int]): - assert element._var_type == List[str] +def display_nested_list_element( + element: ArrayVar[Sequence[str]], index: NumberVar[int] +): + assert element._var_type == Sequence[str] assert index._var_type is int return box(text(element[index]))