fix merge conflicts
Merge remote-tracking branch 'upstream/main' into hybrid-properties
This commit is contained in:
commit
dbec527475
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
|||||||
assets/external/*
|
assets/external/*
|
||||||
dist/*
|
dist/*
|
||||||
examples/
|
examples/
|
||||||
|
.web
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
.coverage
|
.coverage
|
||||||
|
486
poetry.lock
generated
486
poetry.lock
generated
@ -151,13 +151,13 @@ virtualenv = ["virtualenv (>=20.0.35)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2024.7.4"
|
version = "2024.8.30"
|
||||||
description = "Python package for providing Mozilla's CA Bundle."
|
description = "Python package for providing Mozilla's CA Bundle."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
{file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
|
{file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
|
||||||
{file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
|
{file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -582,22 +582,23 @@ test = ["pytest (>=6)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastapi"
|
name = "fastapi"
|
||||||
version = "0.110.3"
|
version = "0.112.2"
|
||||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"},
|
{file = "fastapi-0.112.2-py3-none-any.whl", hash = "sha256:db84b470bd0e2b1075942231e90e3577e12a903c4dc8696f0d206a7904a7af1c"},
|
||||||
{file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"},
|
{file = "fastapi-0.112.2.tar.gz", hash = "sha256:3d4729c038414d5193840706907a41839d839523da6ed0c2811f1168cac1798c"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
|
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
|
||||||
starlette = ">=0.37.2,<0.38.0"
|
starlette = ">=0.37.2,<0.39.0"
|
||||||
typing-extensions = ">=4.8.0"
|
typing-extensions = ">=4.8.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
||||||
|
standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
@ -688,13 +689,13 @@ test = ["objgraph", "psutil"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gunicorn"
|
name = "gunicorn"
|
||||||
version = "22.0.0"
|
version = "23.0.0"
|
||||||
description = "WSGI HTTP Server for UNIX"
|
description = "WSGI HTTP Server for UNIX"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"},
|
{file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"},
|
||||||
{file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"},
|
{file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -741,13 +742,13 @@ trio = ["trio (>=0.22.0,<0.26.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpx"
|
name = "httpx"
|
||||||
version = "0.27.0"
|
version = "0.27.2"
|
||||||
description = "The next generation HTTP client."
|
description = "The next generation HTTP client."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
|
{file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"},
|
||||||
{file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
|
{file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -762,6 +763,7 @@ brotli = ["brotli", "brotlicffi"]
|
|||||||
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
|
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
|
||||||
http2 = ["h2 (>=3,<5)"]
|
http2 = ["h2 (>=3,<5)"]
|
||||||
socks = ["socksio (==1.*)"]
|
socks = ["socksio (==1.*)"]
|
||||||
|
zstd = ["zstandard (>=0.18.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "identify"
|
name = "identify"
|
||||||
@ -1180,56 +1182,56 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "numpy"
|
name = "numpy"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
description = "Fundamental package for array computing in Python"
|
description = "Fundamental package for array computing in Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "numpy-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fbb536eac80e27a2793ffd787895242b7f18ef792563d742c2d673bfcb75134"},
|
{file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69ff563d43c69b1baba77af455dd0a839df8d25e8590e79c90fcbe1499ebde42"},
|
{file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1b902ce0e0a5bb7704556a217c4f63a7974f8f43e090aff03fcf262e0b135e02"},
|
{file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:f1659887361a7151f89e79b276ed8dff3d75877df906328f14d8bb40bb4f5101"},
|
{file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4658c398d65d1b25e1760de3157011a80375da861709abd7cef3bad65d6543f9"},
|
{file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4127d4303b9ac9f94ca0441138acead39928938660ca58329fe156f84b9f3015"},
|
{file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e5eeca8067ad04bc8a2a8731183d51d7cbaac66d86085d5f4766ee6bf19c7f87"},
|
{file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9adbd9bb520c866e1bfd7e10e1880a1f7749f1f6e5017686a5fbb9b72cf69f82"},
|
{file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-win32.whl", hash = "sha256:7b9853803278db3bdcc6cd5beca37815b133e9e77ff3d4733c247414e78eb8d1"},
|
{file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"},
|
||||||
{file = "numpy-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81b0893a39bc5b865b8bf89e9ad7807e16717f19868e9d234bdaf9b1f1393868"},
|
{file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75b4e316c5902d8163ef9d423b1c3f2f6252226d1aa5cd8a0a03a7d01ffc6268"},
|
{file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e4eeb6eb2fced786e32e6d8df9e755ce5be920d17f7ce00bc38fcde8ccdbf9e"},
|
{file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1e01dcaab205fbece13c1410253a9eea1b1c9b61d237b6fa59bcc46e8e89343"},
|
{file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8fc2de81ad835d999113ddf87d1ea2b0f4704cbd947c948d2f5513deafe5a7b"},
|
{file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a3d94942c331dd4e0e1147f7a8699a4aa47dffc11bf8a1523c12af8b2e91bbe"},
|
{file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15eb4eca47d36ec3f78cde0a3a2ee24cf05ca7396ef808dda2c0ddad7c2bde67"},
|
{file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b83e16a5511d1b1f8a88cbabb1a6f6a499f82c062a4251892d9ad5d609863fb7"},
|
{file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f87fec1f9bc1efd23f4227becff04bd0e979e23ca50cc92ec88b38489db3b55"},
|
{file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-win32.whl", hash = "sha256:36d3a9405fd7c511804dc56fc32974fa5533bdeb3cd1604d6b8ff1d292b819c4"},
|
{file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"},
|
||||||
{file = "numpy-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:08458fbf403bff5e2b45f08eda195d4b0c9b35682311da5a5a0a0925b11b9bd8"},
|
{file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bf4e6f4a2a2e26655717a1983ef6324f2664d7011f6ef7482e8c0b3d51e82ac"},
|
{file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6fddc5fe258d3328cd8e3d7d3e02234c5d70e01ebe377a6ab92adb14039cb4"},
|
{file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5daab361be6ddeb299a918a7c0864fa8618af66019138263247af405018b04e1"},
|
{file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:ea2326a4dca88e4a274ba3a4405eb6c6467d3ffbd8c7d38632502eaae3820587"},
|
{file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529af13c5f4b7a932fb0e1911d3a75da204eff023ee5e0e79c1751564221a5c8"},
|
{file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6790654cb13eab303d8402354fabd47472b24635700f631f041bd0b65e37298a"},
|
{file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbab9fc9c391700e3e1287666dfd82d8666d10e69a6c4a09ab97574c0b7ee0a7"},
|
{file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99d0d92a5e3613c33a5f01db206a33f8fdf3d71f2912b0de1739894668b7a93b"},
|
{file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-win32.whl", hash = "sha256:173a00b9995f73b79eb0191129f2455f1e34c203f559dd118636858cc452a1bf"},
|
{file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"},
|
||||||
{file = "numpy-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:bb2124fdc6e62baae159ebcfa368708867eb56806804d005860b6007388df171"},
|
{file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc085b28d62ff4009364e7ca34b80a9a080cbd97c2c0630bb5f7f770dae9414"},
|
{file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fae4ebbf95a179c1156fab0b142b74e4ba4204c87bde8d3d8b6f9c34c5825ef"},
|
{file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:72dc22e9ec8f6eaa206deb1b1355eb2e253899d7347f5e2fae5f0af613741d06"},
|
{file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:ec87f5f8aca726117a1c9b7083e7656a9d0d606eec7299cc067bb83d26f16e0c"},
|
{file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f682ea61a88479d9498bf2091fdcd722b090724b08b31d63e022adc063bad59"},
|
{file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8efc84f01c1cd7e34b3fb310183e72fcdf55293ee736d679b6d35b35d80bba26"},
|
{file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3fdabe3e2a52bc4eff8dc7a5044342f8bd9f11ef0934fcd3289a788c0eb10018"},
|
{file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:24a0e1befbfa14615b49ba9659d3d8818a0f4d8a1c5822af8696706fbda7310c"},
|
{file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-win32.whl", hash = "sha256:f9cf5ea551aec449206954b075db819f52adc1638d46a6738253a712d553c7b4"},
|
{file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"},
|
||||||
{file = "numpy-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:e9e81fa9017eaa416c056e5d9e71be93d05e2c3c2ab308d23307a8bc4443c368"},
|
{file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"},
|
||||||
{file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61728fba1e464f789b11deb78a57805c70b2ed02343560456190d0501ba37b0f"},
|
{file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"},
|
||||||
{file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:12f5d865d60fb9734e60a60f1d5afa6d962d8d4467c120a1c0cda6eb2964437d"},
|
{file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"},
|
||||||
{file = "numpy-2.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eacf3291e263d5a67d8c1a581a8ebbcfd6447204ef58828caf69a5e3e8c75990"},
|
{file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"},
|
||||||
{file = "numpy-2.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c3a346ae20cfd80b6cfd3e60dc179963ef2ea58da5ec074fd3d9e7a1e7ba97f"},
|
{file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"},
|
||||||
{file = "numpy-2.0.1.tar.gz", hash = "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3"},
|
{file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1593,13 +1595,13 @@ type = ["mypy (>=1.8)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotly"
|
name = "plotly"
|
||||||
version = "5.23.0"
|
version = "5.24.0"
|
||||||
description = "An open-source, interactive data visualization library for Python"
|
description = "An open-source, interactive data visualization library for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "plotly-5.23.0-py3-none-any.whl", hash = "sha256:76cbe78f75eddc10c56f5a4ee3e7ccaade7c0a57465546f02098c0caed6c2d1a"},
|
{file = "plotly-5.24.0-py3-none-any.whl", hash = "sha256:0e54efe52c8cef899f7daa41be9ed97dfb6be622613a2a8f56a86a0634b2b67e"},
|
||||||
{file = "plotly-5.23.0.tar.gz", hash = "sha256:89e57d003a116303a34de6700862391367dd564222ab71f8531df70279fc0193"},
|
{file = "plotly-5.24.0.tar.gz", hash = "sha256:eae9f4f54448682442c92c1e97148e3ad0c52f0cf86306e1b76daba24add554a"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -2239,13 +2241,13 @@ idna2008 = ["idna"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rich"
|
name = "rich"
|
||||||
version = "13.7.1"
|
version = "13.8.0"
|
||||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7.0"
|
python-versions = ">=3.7.0"
|
||||||
files = [
|
files = [
|
||||||
{file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"},
|
{file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"},
|
||||||
{file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"},
|
{file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -2299,13 +2301,13 @@ jeepney = ">=0.6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selenium"
|
name = "selenium"
|
||||||
version = "4.23.1"
|
version = "4.24.0"
|
||||||
description = "Official Python bindings for Selenium WebDriver"
|
description = "Official Python bindings for Selenium WebDriver"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "selenium-4.23.1-py3-none-any.whl", hash = "sha256:3a8d9f23dc636bd3840dd56f00c2739e32ec0c1e34a821dd553e15babef24477"},
|
{file = "selenium-4.24.0-py3-none-any.whl", hash = "sha256:42c23f60753d5415b261b236cecbd69bd4eb5271e1563915f546b443cb6b71c6"},
|
||||||
{file = "selenium-4.23.1.tar.gz", hash = "sha256:128d099e66284437e7128d2279176ec7a06e6ec7426e167f5d34987166bd8f46"},
|
{file = "selenium-4.24.0.tar.gz", hash = "sha256:88281e5b5b90fe231868905d5ea745b9ee5e30db280b33498cc73fb0fa06d571"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -2496,13 +2498,13 @@ SQLAlchemy = ">=2.0.14,<2.1.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "starlette"
|
name = "starlette"
|
||||||
version = "0.37.2"
|
version = "0.38.2"
|
||||||
description = "The little ASGI library that shines."
|
description = "The little ASGI library that shines."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"},
|
{file = "starlette-0.38.2-py3-none-any.whl", hash = "sha256:4ec6a59df6bbafdab5f567754481657f7ed90dc9d69b0c9ff017907dd54faeff"},
|
||||||
{file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"},
|
{file = "starlette-0.38.2.tar.gz", hash = "sha256:c7c0441065252160993a1a37cf2a73bb64d271b17303e0b0c1eb7191cfb12d75"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -2657,13 +2659,13 @@ urllib3 = ">=1.26.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typer"
|
name = "typer"
|
||||||
version = "0.12.4"
|
version = "0.12.5"
|
||||||
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
|
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "typer-0.12.4-py3-none-any.whl", hash = "sha256:819aa03699f438397e876aa12b0d63766864ecba1b579092cc9fe35d886e34b6"},
|
{file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"},
|
||||||
{file = "typer-0.12.4.tar.gz", hash = "sha256:c9c1613ed6a166162705b3347b8d10b661ccc5d95692654d0fb628118f2c34e6"},
|
{file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -2753,152 +2755,6 @@ platformdirs = ">=3.9.1,<5"
|
|||||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||||
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "watchdog"
|
|
||||||
version = "4.0.2"
|
|
||||||
description = "Filesystem events monitoring"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"},
|
|
||||||
{file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"},
|
|
||||||
{file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"},
|
|
||||||
{file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"},
|
|
||||||
{file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"},
|
|
||||||
{file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"},
|
|
||||||
{file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"},
|
|
||||||
{file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"},
|
|
||||||
{file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"},
|
|
||||||
{file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"},
|
|
||||||
{file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"},
|
|
||||||
{file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"},
|
|
||||||
{file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"},
|
|
||||||
{file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"},
|
|
||||||
{file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"},
|
|
||||||
{file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"},
|
|
||||||
{file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"},
|
|
||||||
{file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"},
|
|
||||||
{file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"},
|
|
||||||
{file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"},
|
|
||||||
{file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"},
|
|
||||||
{file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"},
|
|
||||||
{file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"},
|
|
||||||
{file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"},
|
|
||||||
{file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"},
|
|
||||||
{file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
watchmedo = ["PyYAML (>=3.10)"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "watchfiles"
|
|
||||||
version = "0.23.0"
|
|
||||||
description = "Simple, modern and high performance file watching and code reload in python."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-none-win32.whl", hash = "sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42"},
|
|
||||||
{file = "watchfiles-0.23.0-cp310-none-win_amd64.whl", hash = "sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-none-win32.whl", hash = "sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-none-win_amd64.whl", hash = "sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e"},
|
|
||||||
{file = "watchfiles-0.23.0-cp311-none-win_arm64.whl", hash = "sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-none-win32.whl", hash = "sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-none-win_amd64.whl", hash = "sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb"},
|
|
||||||
{file = "watchfiles-0.23.0-cp312-none-win_arm64.whl", hash = "sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-none-win32.whl", hash = "sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b"},
|
|
||||||
{file = "watchfiles-0.23.0-cp313-none-win_amd64.whl", hash = "sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:2dddc2487d33e92f8b6222b5fb74ae2cfde5e8e6c44e0248d24ec23befdc5366"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e75695cc952e825fa3e0684a7f4a302f9128721f13eedd8dbd3af2ba450932b8"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2537ef60596511df79b91613a5bb499b63f46f01a11a81b0a2b0dedf645d0a9c"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20b423b58f5fdde704a226b598a2d78165fe29eb5621358fe57ea63f16f165c4"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b98732ec893975455708d6fc9a6daab527fc8bbe65be354a3861f8c450a632a4"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee1f5fcbf5bc33acc0be9dd31130bcba35d6d2302e4eceafafd7d9018c7755ab"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f195338a5a7b50a058522b39517c50238358d9ad8284fd92943643144c0c03"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524fcb8d59b0dbee2c9b32207084b67b2420f6431ed02c18bd191e6c575f5c48"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0eff099a4df36afaa0eea7a913aa64dcf2cbd4e7a4f319a73012210af4d23810"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a8323daae27ea290ba3350c70c836c0d2b0fb47897fa3b0ca6a5375b952b90d3"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-none-win32.whl", hash = "sha256:aafea64a3ae698695975251f4254df2225e2624185a69534e7fe70581066bc1b"},
|
|
||||||
{file = "watchfiles-0.23.0-cp38-none-win_amd64.whl", hash = "sha256:c846884b2e690ba62a51048a097acb6b5cd263d8bd91062cd6137e2880578472"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a753993635eccf1ecb185dedcc69d220dab41804272f45e4aef0a67e790c3eb3"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6bb91fa4d0b392f0f7e27c40981e46dda9eb0fbc84162c7fb478fe115944f491"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1f67312efa3902a8e8496bfa9824d3bec096ff83c4669ea555c6bdd213aa516"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ca6b71dcc50d320c88fb2d88ecd63924934a8abc1673683a242a7ca7d39e781"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aec5c29915caf08771d2507da3ac08e8de24a50f746eb1ed295584ba1820330"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1733b9bc2c8098c6bdb0ff7a3d7cb211753fecb7bd99bdd6df995621ee1a574b"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02ff5d7bd066c6a7673b17c8879cd8ee903078d184802a7ee851449c43521bdd"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e2de19801b0eaa4c5292a223effb7cfb43904cb742c5317a0ac686ed604765"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8ada449e22198c31fb013ae7e9add887e8d2bd2335401abd3cbc55f8c5083647"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3af1b05361e1cc497bf1be654a664750ae61f5739e4bb094a2be86ec8c6db9b6"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-none-win32.whl", hash = "sha256:486bda18be5d25ab5d932699ceed918f68eb91f45d018b0343e3502e52866e5e"},
|
|
||||||
{file = "watchfiles-0.23.0-cp39-none-win_amd64.whl", hash = "sha256:d2d42254b189a346249424fb9bb39182a19289a2409051ee432fb2926bad966a"},
|
|
||||||
{file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe"},
|
|
||||||
{file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b"},
|
|
||||||
{file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed"},
|
|
||||||
{file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee"},
|
|
||||||
{file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aba037c1310dd108411d27b3d5815998ef0e83573e47d4219f45753c710f969f"},
|
|
||||||
{file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:a96ac14e184aa86dc43b8a22bb53854760a58b2966c2b41580de938e9bf26ed0"},
|
|
||||||
{file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11698bb2ea5e991d10f1f4f83a39a02f91e44e4bd05f01b5c1ec04c9342bf63c"},
|
|
||||||
{file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efadd40fca3a04063d40c4448c9303ce24dd6151dc162cfae4a2a060232ebdcb"},
|
|
||||||
{file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:556347b0abb4224c5ec688fc58214162e92a500323f50182f994f3ad33385dcb"},
|
|
||||||
{file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1cf7f486169986c4b9d34087f08ce56a35126600b6fef3028f19ca16d5889071"},
|
|
||||||
{file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18de0f82c62c4197bea5ecf4389288ac755896aac734bd2cc44004c56e4ac47"},
|
|
||||||
{file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:532e1f2c491274d1333a814e4c5c2e8b92345d41b12dc806cf07aaff786beb66"},
|
|
||||||
{file = "watchfiles-0.23.0.tar.gz", hash = "sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
anyio = ">=3.0.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "websocket-client"
|
name = "websocket-client"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
@ -2917,97 +2773,97 @@ test = ["websockets"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "websockets"
|
name = "websockets"
|
||||||
version = "13.0"
|
version = "13.0.1"
|
||||||
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "websockets-13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ad4fa707ff9e2ffee019e946257b5300a45137a58f41fbd9a4db8e684ab61528"},
|
{file = "websockets-13.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1841c9082a3ba4a05ea824cf6d99570a6a2d8849ef0db16e9c826acb28089e8f"},
|
||||||
{file = "websockets-13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6fd757f313c13c34dae9f126d3ba4cf97175859c719e57c6a614b781c86b617e"},
|
{file = "websockets-13.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c5870b4a11b77e4caa3937142b650fbbc0914a3e07a0cf3131f35c0587489c1c"},
|
||||||
{file = "websockets-13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cbac2eb7ce0fac755fb983c9247c4a60c4019bcde4c0e4d167aeb17520cc7ef1"},
|
{file = "websockets-13.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f1d3d1f2eb79fe7b0fb02e599b2bf76a7619c79300fc55f0b5e2d382881d4f7f"},
|
||||||
{file = "websockets-13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4b83cf7354cbbc058e97b3e545dceb75b8d9cf17fd5a19db419c319ddbaaf7a"},
|
{file = "websockets-13.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15c7d62ee071fa94a2fc52c2b472fed4af258d43f9030479d9c4a2de885fd543"},
|
||||||
{file = "websockets-13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9202c0010c78fad1041e1c5285232b6508d3633f92825687549540a70e9e5901"},
|
{file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6724b554b70d6195ba19650fef5759ef11346f946c07dbbe390e039bcaa7cc3d"},
|
||||||
{file = "websockets-13.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6566e79c8c7cbea75ec450f6e1828945fc5c9a4769ceb1c7b6e22470539712"},
|
{file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a952fa2ae57a42ba7951e6b2605e08a24801a4931b5644dfc68939e041bc7f"},
|
||||||
{file = "websockets-13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e7fcad070dcd9ad37a09d89a4cbc2a5e3e45080b88977c0da87b3090f9f55ead"},
|
{file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17118647c0ea14796364299e942c330d72acc4b248e07e639d34b75067b3cdd8"},
|
||||||
{file = "websockets-13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a8f7d65358a25172db00c69bcc7df834155ee24229f560d035758fd6613111a"},
|
{file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64a11aae1de4c178fa653b07d90f2fb1a2ed31919a5ea2361a38760192e1858b"},
|
||||||
{file = "websockets-13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:63b702fb31e3f058f946ccdfa551f4d57a06f7729c369e8815eb18643099db37"},
|
{file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0617fd0b1d14309c7eab6ba5deae8a7179959861846cbc5cb528a7531c249448"},
|
||||||
{file = "websockets-13.0-cp310-cp310-win32.whl", hash = "sha256:3a20cf14ba7b482c4a1924b5e061729afb89c890ca9ed44ac4127c6c5986e424"},
|
{file = "websockets-13.0.1-cp310-cp310-win32.whl", hash = "sha256:11f9976ecbc530248cf162e359a92f37b7b282de88d1d194f2167b5e7ad80ce3"},
|
||||||
{file = "websockets-13.0-cp310-cp310-win_amd64.whl", hash = "sha256:587245f0704d0bb675f919898d7473e8827a6d578e5a122a21756ca44b811ec8"},
|
{file = "websockets-13.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c3c493d0e5141ec055a7d6809a28ac2b88d5b878bb22df8c621ebe79a61123d0"},
|
||||||
{file = "websockets-13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:06df8306c241c235075d2ae77367038e701e53bc8c1bb4f6644f4f53aa6dedd0"},
|
{file = "websockets-13.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:699ba9dd6a926f82a277063603fc8d586b89f4cb128efc353b749b641fcddda7"},
|
||||||
{file = "websockets-13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85a1f92a02f0b8c1bf02699731a70a8a74402bb3f82bee36e7768b19a8ed9709"},
|
{file = "websockets-13.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf2fae6d85e5dc384bf846f8243ddaa9197f3a1a70044f59399af001fd1f51d4"},
|
||||||
{file = "websockets-13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9ed02c604349068d46d87ef4c2012c112c791f2bec08671903a6bb2bd9c06784"},
|
{file = "websockets-13.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52aed6ef21a0f1a2a5e310fb5c42d7555e9c5855476bbd7173c3aa3d8a0302f2"},
|
||||||
{file = "websockets-13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b89849171b590107f6724a7b0790736daead40926ddf47eadf998b4ff51d6414"},
|
{file = "websockets-13.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb2b9a318542153674c6e377eb8cb9ca0fc011c04475110d3477862f15d29f0"},
|
||||||
{file = "websockets-13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:939a16849d71203628157a5e4a495da63967c744e1e32018e9b9e2689aca64d4"},
|
{file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5df891c86fe68b2c38da55b7aea7095beca105933c697d719f3f45f4220a5e0e"},
|
||||||
{file = "websockets-13.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad818cdac37c0ad4c58e51cb4964eae4f18b43c4a83cb37170b0d90c31bd80cf"},
|
{file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac2d146ff30d9dd2fcf917e5d147db037a5c573f0446c564f16f1f94cf87462"},
|
||||||
{file = "websockets-13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cbfe82a07596a044de78bb7a62519e71690c5812c26c5f1d4b877e64e4f46309"},
|
{file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b8ac5b46fd798bbbf2ac6620e0437c36a202b08e1f827832c4bf050da081b501"},
|
||||||
{file = "websockets-13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e07e76c49f39c5b45cbd7362b94f001ae209a3ea4905ae9a09cfd53b3c76373d"},
|
{file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:46af561eba6f9b0848b2c9d2427086cabadf14e0abdd9fde9d72d447df268418"},
|
||||||
{file = "websockets-13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:372f46a0096cfda23c88f7e42349a33f8375e10912f712e6b496d3a9a557290f"},
|
{file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b5a06d7f60bc2fc378a333978470dfc4e1415ee52f5f0fce4f7853eb10c1e9df"},
|
||||||
{file = "websockets-13.0-cp311-cp311-win32.whl", hash = "sha256:376a43a4fd96725f13450d3d2e98f4f36c3525c562ab53d9a98dd2950dca9a8a"},
|
{file = "websockets-13.0.1-cp311-cp311-win32.whl", hash = "sha256:556e70e4f69be1082e6ef26dcb70efcd08d1850f5d6c5f4f2bcb4e397e68f01f"},
|
||||||
{file = "websockets-13.0-cp311-cp311-win_amd64.whl", hash = "sha256:2be1382a4daa61e2f3e2be3b3c86932a8db9d1f85297feb6e9df22f391f94452"},
|
{file = "websockets-13.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:67494e95d6565bf395476e9d040037ff69c8b3fa356a886b21d8422ad86ae075"},
|
||||||
{file = "websockets-13.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5407c34776b9b77bd89a5f95eb0a34aaf91889e3f911c63f13035220eb50107"},
|
{file = "websockets-13.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f9c9e258e3d5efe199ec23903f5da0eeaad58cf6fccb3547b74fd4750e5ac47a"},
|
||||||
{file = "websockets-13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4782ec789f059f888c1e8fdf94383d0e64b531cffebbf26dd55afd53ab487ca4"},
|
{file = "websockets-13.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6b41a1b3b561f1cba8321fb32987552a024a8f67f0d05f06fcf29f0090a1b956"},
|
||||||
{file = "websockets-13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8feb8e19ef65c9994e652c5b0324abd657bedd0abeb946fb4f5163012c1e730"},
|
{file = "websockets-13.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f73e676a46b0fe9426612ce8caeca54c9073191a77c3e9d5c94697aef99296af"},
|
||||||
{file = "websockets-13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f3d2e20c442b58dbac593cb1e02bc02d149a86056cc4126d977ad902472e3b"},
|
{file = "websockets-13.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f613289f4a94142f914aafad6c6c87903de78eae1e140fa769a7385fb232fdf"},
|
||||||
{file = "websockets-13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e39d393e0ab5b8bd01717cc26f2922026050188947ff54fe6a49dc489f7750b7"},
|
{file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f52504023b1480d458adf496dc1c9e9811df4ba4752f0bc1f89ae92f4f07d0c"},
|
||||||
{file = "websockets-13.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f661a4205741bdc88ac9c2b2ec003c72cee97e4acd156eb733662ff004ba429"},
|
{file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:139add0f98206cb74109faf3611b7783ceafc928529c62b389917a037d4cfdf4"},
|
||||||
{file = "websockets-13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:384129ad0490e06bab2b98c1da9b488acb35bb11e2464c728376c6f55f0d45f3"},
|
{file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47236c13be337ef36546004ce8c5580f4b1150d9538b27bf8a5ad8edf23ccfab"},
|
||||||
{file = "websockets-13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:df5c0eff91f61b8205a6c9f7b255ff390cdb77b61c7b41f79ca10afcbb22b6cb"},
|
{file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c44ca9ade59b2e376612df34e837013e2b273e6c92d7ed6636d0556b6f4db93d"},
|
||||||
{file = "websockets-13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:02cc9bb1a887dac0e08bf657c5d00aa3fac0d03215d35a599130c2034ae6663a"},
|
{file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9bbc525f4be3e51b89b2a700f5746c2a6907d2e2ef4513a8daafc98198b92237"},
|
||||||
{file = "websockets-13.0-cp312-cp312-win32.whl", hash = "sha256:d9726d2c9bd6aed8cb994d89b3910ca0079406edce3670886ec828a73e7bdd53"},
|
{file = "websockets-13.0.1-cp312-cp312-win32.whl", hash = "sha256:3624fd8664f2577cf8de996db3250662e259bfbc870dd8ebdcf5d7c6ac0b5185"},
|
||||||
{file = "websockets-13.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0839f35322f7b038d8adcf679e2698c3a483688cc92e3bd15ee4fb06669e9a"},
|
{file = "websockets-13.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0513c727fb8adffa6d9bf4a4463b2bade0186cbd8c3604ae5540fae18a90cb99"},
|
||||||
{file = "websockets-13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:da7e501e59857e8e3e9d10586139dc196b80445a591451ca9998aafba1af5278"},
|
{file = "websockets-13.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1ee4cc030a4bdab482a37462dbf3ffb7e09334d01dd37d1063be1136a0d825fa"},
|
||||||
{file = "websockets-13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a00e1e587c655749afb5b135d8d3edcfe84ec6db864201e40a882e64168610b3"},
|
{file = "websockets-13.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbb0b697cc0655719522406c059eae233abaa3243821cfdfab1215d02ac10231"},
|
||||||
{file = "websockets-13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a7fbf2a8fe7556a8f4e68cb3e736884af7bf93653e79f6219f17ebb75e97d8f0"},
|
{file = "websockets-13.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:acbebec8cb3d4df6e2488fbf34702cbc37fc39ac7abf9449392cefb3305562e9"},
|
||||||
{file = "websockets-13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ea9c9c7443a97ea4d84d3e4d42d0e8c4235834edae652993abcd2aff94affd7"},
|
{file = "websockets-13.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63848cdb6fcc0bf09d4a155464c46c64ffdb5807ede4fb251da2c2692559ce75"},
|
||||||
{file = "websockets-13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35c2221b539b360203f3f9ad168e527bf16d903e385068ae842c186efb13d0ea"},
|
{file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:872afa52a9f4c414d6955c365b6588bc4401272c629ff8321a55f44e3f62b553"},
|
||||||
{file = "websockets-13.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:358d37c5c431dd050ffb06b4b075505aae3f4f795d7fff9794e5ed96ce99b998"},
|
{file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e70fec7c54aad4d71eae8e8cab50525e899791fc389ec6f77b95312e4e9920"},
|
||||||
{file = "websockets-13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:038e7a0f1bfafc7bf52915ab3506b7a03d1e06381e9f60440c856e8918138151"},
|
{file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e82db3756ccb66266504f5a3de05ac6b32f287faacff72462612120074103329"},
|
||||||
{file = "websockets-13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fd038bc9e2c134847f1e0ce3191797fad110756e690c2fdd9702ed34e7a43abb"},
|
{file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4e85f46ce287f5c52438bb3703d86162263afccf034a5ef13dbe4318e98d86e7"},
|
||||||
{file = "websockets-13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93b8c2008f372379fb6e5d2b3f7c9ec32f7b80316543fd3a5ace6610c5cde1b0"},
|
{file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3fea72e4e6edb983908f0db373ae0732b275628901d909c382aae3b592589f2"},
|
||||||
{file = "websockets-13.0-cp313-cp313-win32.whl", hash = "sha256:851fd0afb3bc0b73f7c5b5858975d42769a5fdde5314f4ef2c106aec63100687"},
|
{file = "websockets-13.0.1-cp313-cp313-win32.whl", hash = "sha256:254ecf35572fca01a9f789a1d0f543898e222f7b69ecd7d5381d8d8047627bdb"},
|
||||||
{file = "websockets-13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7d14901fdcf212804970c30ab9ee8f3f0212e620c7ea93079d6534863444fb4e"},
|
{file = "websockets-13.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca48914cdd9f2ccd94deab5bcb5ac98025a5ddce98881e5cce762854a5de330b"},
|
||||||
{file = "websockets-13.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae7a519a56a714f64c3445cabde9fc2fc927e7eae44f413eae187cddd9e54178"},
|
{file = "websockets-13.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b74593e9acf18ea5469c3edaa6b27fa7ecf97b30e9dabd5a94c4c940637ab96e"},
|
||||||
{file = "websockets-13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5575031472ca87302aeb2ce2c2349f4c6ea978c86a9d1289bc5d16058ad4c10a"},
|
{file = "websockets-13.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:132511bfd42e77d152c919147078460c88a795af16b50e42a0bd14f0ad71ddd2"},
|
||||||
{file = "websockets-13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9895df6cd0bfe79d09bcd1dbdc03862846f26fbd93797153de954306620c1d00"},
|
{file = "websockets-13.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:165bedf13556f985a2aa064309baa01462aa79bf6112fbd068ae38993a0e1f1b"},
|
||||||
{file = "websockets-13.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4de299c947a54fca9ce1c5fd4a08eb92ffce91961becb13bd9195f7c6e71b47"},
|
{file = "websockets-13.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e801ca2f448850685417d723ec70298feff3ce4ff687c6f20922c7474b4746ae"},
|
||||||
{file = "websockets-13.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05c25f7b849702950b6fd0e233989bb73a0d2bc83faa3b7233313ca395205f6d"},
|
{file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30d3a1f041360f029765d8704eae606781e673e8918e6b2c792e0775de51352f"},
|
||||||
{file = "websockets-13.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede95125a30602b1691a4b1da88946bf27dae283cf30f22cd2cb8ca4b2e0d119"},
|
{file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67648f5e50231b5a7f6d83b32f9c525e319f0ddc841be0de64f24928cd75a603"},
|
||||||
{file = "websockets-13.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:addf0a16e4983280efed272d8cb3b2e05f0051755372461e7d966b80a6554e16"},
|
{file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4f0426d51c8f0926a4879390f53c7f5a855e42d68df95fff6032c82c888b5f36"},
|
||||||
{file = "websockets-13.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:06b3186e97bf9a33921fa60734d5ed90f2a9b407cce8d23c7333a0984049ef61"},
|
{file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ef48e4137e8799998a343706531e656fdec6797b80efd029117edacb74b0a10a"},
|
||||||
{file = "websockets-13.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:eae368cac85adc4c7dc3b0d5f84ffcca609d658db6447387300478e44db70796"},
|
{file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:249aab278810bee585cd0d4de2f08cfd67eed4fc75bde623be163798ed4db2eb"},
|
||||||
{file = "websockets-13.0-cp38-cp38-win32.whl", hash = "sha256:337837ac788d955728b1ab01876d72b73da59819a3388e1c5e8e05c3999f1afa"},
|
{file = "websockets-13.0.1-cp38-cp38-win32.whl", hash = "sha256:06c0a667e466fcb56a0886d924b5f29a7f0886199102f0a0e1c60a02a3751cb4"},
|
||||||
{file = "websockets-13.0-cp38-cp38-win_amd64.whl", hash = "sha256:f66e00e42f25ca7e91076366303e11c82572ca87cc5aae51e6e9c094f315ab41"},
|
{file = "websockets-13.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1f3cf6d6ec1142412d4535adabc6bd72a63f5f148c43fe559f06298bc21953c9"},
|
||||||
{file = "websockets-13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:94c1c02721139fe9940b38d28fb15b4b782981d800d5f40f9966264fbf23dcc8"},
|
{file = "websockets-13.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1fa082ea38d5de51dd409434edc27c0dcbd5fed2b09b9be982deb6f0508d25bc"},
|
||||||
{file = "websockets-13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd4ba86513430513e2aa25a441bb538f6f83734dc368a2c5d18afdd39097aa33"},
|
{file = "websockets-13.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a365bcb7be554e6e1f9f3ed64016e67e2fa03d7b027a33e436aecf194febb63"},
|
||||||
{file = "websockets-13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a1ab8f0e0cadc5be5f3f9fa11a663957fecbf483d434762c8dfb8aa44948944a"},
|
{file = "websockets-13.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10a0dc7242215d794fb1918f69c6bb235f1f627aaf19e77f05336d147fce7c37"},
|
||||||
{file = "websockets-13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3670def5d3dfd5af6f6e2b3b243ea8f1f72d8da1ef927322f0703f85c90d9603"},
|
{file = "websockets-13.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59197afd478545b1f73367620407b0083303569c5f2d043afe5363676f2697c9"},
|
||||||
{file = "websockets-13.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6058b6be92743358885ad6dcdecb378fde4a4c74d4dd16a089d07580c75a0e80"},
|
{file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d20516990d8ad557b5abeb48127b8b779b0b7e6771a265fa3e91767596d7d97"},
|
||||||
{file = "websockets-13.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516062a0a8ef5ecbfa4acbaec14b199fc070577834f9fe3d40800a99f92523ca"},
|
{file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1a2e272d067030048e1fe41aa1ec8cfbbaabce733b3d634304fa2b19e5c897f"},
|
||||||
{file = "websockets-13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:da7e918d82e7bdfc6f66d31febe1b2e28a1ca3387315f918de26f5e367f61572"},
|
{file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad327ac80ba7ee61da85383ca8822ff808ab5ada0e4a030d66703cc025b021c4"},
|
||||||
{file = "websockets-13.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9cc7f35dcb49a4e32db82a849fcc0714c4d4acc9d2273aded2d61f87d7f660b7"},
|
{file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:518f90e6dd089d34eaade01101fd8a990921c3ba18ebbe9b0165b46ebff947f0"},
|
||||||
{file = "websockets-13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f5737c53eb2c8ed8f64b50d3dafd3c1dae739f78aa495a288421ac1b3de82717"},
|
{file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68264802399aed6fe9652e89761031acc734fc4c653137a5911c2bfa995d6d6d"},
|
||||||
{file = "websockets-13.0-cp39-cp39-win32.whl", hash = "sha256:265e1f0d3f788ce8ef99dca591a1aec5263b26083ca0934467ad9a1d1181067c"},
|
{file = "websockets-13.0.1-cp39-cp39-win32.whl", hash = "sha256:a5dc0c42ded1557cc7c3f0240b24129aefbad88af4f09346164349391dea8e58"},
|
||||||
{file = "websockets-13.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d70c89e3d3b347a7c4d3c33f8d323f0584c9ceb69b82c2ef8a174ca84ea3d4a"},
|
{file = "websockets-13.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b448a0690ef43db5ef31b3a0d9aea79043882b4632cfc3eaab20105edecf6097"},
|
||||||
{file = "websockets-13.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:602cbd010d8c21c8475f1798b705bb18567eb189c533ab5ef568bc3033fdf417"},
|
{file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:faef9ec6354fe4f9a2c0bbb52fb1ff852effc897e2a4501e25eb3a47cb0a4f89"},
|
||||||
{file = "websockets-13.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:bf8eb5dca4f484a60f5327b044e842e0d7f7cdbf02ea6dc4a4f811259f1f1f0b"},
|
{file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:03d3f9ba172e0a53e37fa4e636b86cc60c3ab2cfee4935e66ed1d7acaa4625ad"},
|
||||||
{file = "websockets-13.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d795c1802d99a643bf689b277e8604c14b5af1bc0a31dade2cd7a678087212"},
|
{file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d450f5a7a35662a9b91a64aefa852f0c0308ee256122f5218a42f1d13577d71e"},
|
||||||
{file = "websockets-13.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:788bc841d250beccff67a20a5a53a15657a60111ef9c0c0a97fbdd614fae0fe2"},
|
{file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f55b36d17ac50aa8a171b771e15fbe1561217510c8768af3d546f56c7576cdc"},
|
||||||
{file = "websockets-13.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7334752052532c156d28b8eaf3558137e115c7871ea82adff69b6d94a7bee273"},
|
{file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14b9c006cac63772b31abbcd3e3abb6228233eec966bf062e89e7fa7ae0b7333"},
|
||||||
{file = "websockets-13.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7a1963302947332c3039e3f66209ec73b1626f8a0191649e0713c391e9f5b0d"},
|
{file = "websockets-13.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b79915a1179a91f6c5f04ece1e592e2e8a6bd245a0e45d12fd56b2b59e559a32"},
|
||||||
{file = "websockets-13.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2e1cf4e1eb84b4fd74a47688e8b0940c89a04ad9f6937afa43d468e71128cd68"},
|
{file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f40de079779acbcdbb6ed4c65af9f018f8b77c5ec4e17a4b737c05c2db554491"},
|
||||||
{file = "websockets-13.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:c026ee729c4ce55708a14b839ba35086dfae265fc12813b62d34ce33f4980c1c"},
|
{file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e4ba642fc87fa532bac07e5ed7e19d56940b6af6a8c61d4429be48718a380f"},
|
||||||
{file = "websockets-13.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5f9d23fbbf96eefde836d9692670bfc89e2d159f456d499c5efcf6a6281c1af"},
|
{file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a02b0161c43cc9e0232711eff846569fad6ec836a7acab16b3cf97b2344c060"},
|
||||||
{file = "websockets-13.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ad684cb7efce227d756bae3e8484f2e56aa128398753b54245efdfbd1108f2c"},
|
{file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6aa74a45d4cdc028561a7d6ab3272c8b3018e23723100b12e58be9dfa5a24491"},
|
||||||
{file = "websockets-13.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1e10b3fbed7be4a59831d3a939900e50fcd34d93716e433d4193a4d0d1d335d"},
|
{file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00fd961943b6c10ee6f0b1130753e50ac5dcd906130dcd77b0003c3ab797d026"},
|
||||||
{file = "websockets-13.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d42a818e634f789350cd8fb413a3f5eec1cf0400a53d02062534c41519f5125c"},
|
{file = "websockets-13.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d93572720d781331fb10d3da9ca1067817d84ad1e7c31466e9f5e59965618096"},
|
||||||
{file = "websockets-13.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5ba5e9b332267d0f2c33ede390061850f1ac3ee6cd1bdcf4c5ea33ead971966"},
|
{file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:71e6e5a3a3728886caee9ab8752e8113670936a193284be9d6ad2176a137f376"},
|
||||||
{file = "websockets-13.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f9af457ed593e35f467140d8b61d425495b127744a9d65d45a366f8678449a23"},
|
{file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c4a6343e3b0714e80da0b0893543bf9a5b5fa71b846ae640e56e9abc6fbc4c83"},
|
||||||
{file = "websockets-13.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcea3eb58c09c3a31cc83b45c06d5907f02ddaf10920aaa6443975310f699b95"},
|
{file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a678532018e435396e37422a95e3ab87f75028ac79570ad11f5bf23cd2a7d8c"},
|
||||||
{file = "websockets-13.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c210d1460dc8d326ffdef9703c2f83269b7539a1690ad11ae04162bc1878d33d"},
|
{file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6716c087e4aa0b9260c4e579bb82e068f84faddb9bfba9906cb87726fa2e870"},
|
||||||
{file = "websockets-13.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b32f38bc81170fd56d0482d505b556e52bf9078b36819a8ba52624bd6667e39e"},
|
{file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e33505534f3f673270dd67f81e73550b11de5b538c56fe04435d63c02c3f26b5"},
|
||||||
{file = "websockets-13.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:81a11a1ddd5320429db47c04d35119c3e674d215173d87aaeb06ae80f6e9031f"},
|
{file = "websockets-13.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acab3539a027a85d568c2573291e864333ec9d912675107d6efceb7e2be5d980"},
|
||||||
{file = "websockets-13.0-py3-none-any.whl", hash = "sha256:dbbac01e80aee253d44c4f098ab3cc17c822518519e869b284cfbb8cd16cc9de"},
|
{file = "websockets-13.0.1-py3-none-any.whl", hash = "sha256:b80f0c51681c517604152eb6a572f5a9378f877763231fddb883ba2f968e8817"},
|
||||||
{file = "websockets-13.0.tar.gz", hash = "sha256:b7bf950234a482b7461afdb2ec99eee3548ec4d53f418c7990bb79c620476602"},
|
{file = "websockets-13.0.1.tar.gz", hash = "sha256:4d6ece65099411cfd9a48d13701d7438d9c34f479046b34c50ff60bb8834e43e"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3119,20 +2975,24 @@ h11 = ">=0.9.0,<1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zipp"
|
name = "zipp"
|
||||||
version = "3.20.0"
|
version = "3.20.1"
|
||||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"},
|
{file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"},
|
||||||
{file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"},
|
{file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
|
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
|
||||||
|
cover = ["pytest-cov"]
|
||||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||||
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
enabler = ["pytest-enabler (>=2.2)"]
|
||||||
|
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
|
||||||
|
type = ["pytest-mypy"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
content-hash = "4b609c46858e2a6766b9c9b759833a1366dbe7bb64b3332f339a9c9f713a870f"
|
content-hash = "0bd98b6baa48d41ec50bfa0903d89005129211f6e429d7b62504ee880f8ba523"
|
||||||
|
@ -28,8 +28,8 @@ packages = [
|
|||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.8"
|
python = "^3.8"
|
||||||
dill = ">=0.3.8,<0.4"
|
dill = ">=0.3.8,<0.4"
|
||||||
fastapi = ">=0.96.0,<0.111.0"
|
fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
|
||||||
gunicorn = ">=20.1.0,<23.0"
|
gunicorn = ">=20.1.0,<24.0"
|
||||||
jinja2 = ">=3.1.2,<4.0"
|
jinja2 = ">=3.1.2,<4.0"
|
||||||
psutil = ">=5.9.4,<7.0"
|
psutil = ">=5.9.4,<7.0"
|
||||||
pydantic = ">=1.10.2,<3.0"
|
pydantic = ">=1.10.2,<3.0"
|
||||||
@ -40,8 +40,6 @@ rich = ">=13.0.0,<14.0"
|
|||||||
sqlmodel = ">=0.0.14,<0.1"
|
sqlmodel = ">=0.0.14,<0.1"
|
||||||
typer = ">=0.4.2,<1.0"
|
typer = ">=0.4.2,<1.0"
|
||||||
uvicorn = ">=0.20.0"
|
uvicorn = ">=0.20.0"
|
||||||
watchdog = ">=2.3.1,<5.0"
|
|
||||||
watchfiles = ">=0.19.0,<1.0"
|
|
||||||
starlette-admin = ">=0.11.0,<1.0"
|
starlette-admin = ">=0.11.0,<1.0"
|
||||||
alembic = ">=1.11.1,<2.0"
|
alembic = ">=1.11.1,<2.0"
|
||||||
platformdirs = ">=3.10.0,<5.0"
|
platformdirs = ">=3.10.0,<5.0"
|
||||||
|
4
reflex/.templates/apps/demo/.gitignore
vendored
4
reflex/.templates/apps/demo/.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
*.db
|
|
||||||
*.py[cod]
|
|
||||||
.web
|
|
||||||
__pycache__/
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
@ -1,10 +0,0 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g id="Github" clip-path="url(#clip0_469_1929)">
|
|
||||||
<path id="Vector" d="M8.0004 0.587524C3.80139 0.587524 0.400391 3.98851 0.400391 8.1875C0.400391 11.5505 2.57589 14.391 5.59689 15.398C5.97689 15.4645 6.11939 15.2365 6.11939 15.037C6.11939 14.8565 6.10989 14.258 6.10989 13.6215C4.20039 13.973 3.70639 13.156 3.55439 12.7285C3.46889 12.51 3.09839 11.8355 2.77539 11.655C2.50939 11.5125 2.12939 11.161 2.76589 11.1515C3.36439 11.142 3.79189 11.7025 3.93439 11.9305C4.61839 13.08 5.71089 12.757 6.14789 12.5575C6.21439 12.0635 6.41388 11.731 6.6324 11.541C4.94139 11.351 3.17439 10.6955 3.17439 7.7885C3.17439 6.962 3.46889 6.27801 3.95339 5.74601C3.87739 5.55601 3.61139 4.77701 4.02939 3.73201C4.02939 3.73201 4.66589 3.53251 6.11939 4.51101C6.7274 4.34001 7.3734 4.25451 8.0194 4.25451C8.6654 4.25451 9.3114 4.34001 9.9194 4.51101C11.3729 3.52301 12.0094 3.73201 12.0094 3.73201C12.4274 4.77701 12.1614 5.55601 12.0854 5.74601C12.5699 6.27801 12.8644 6.9525 12.8644 7.7885C12.8644 10.705 11.0879 11.351 9.3969 11.541C9.6724 11.7785 9.9099 12.2345 9.9099 12.947C9.9099 13.9635 9.9004 14.7805 9.9004 15.037C9.9004 15.2365 10.0429 15.474 10.4229 15.398C13.5165 14.3536 15.5996 11.4527 15.6004 8.1875C15.6004 3.98851 12.1994 0.587524 8.0004 0.587524Z" fill="#494369"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_469_1929">
|
|
||||||
<rect width="16" height="16" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1,37 +0,0 @@
|
|||||||
<svg width="67" height="14" viewBox="0 0 67 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect width="67" height="14" fill="#1E1E1E"/>
|
|
||||||
<g id="Nav Template > Initial" clip-path="url(#clip0_0_1)">
|
|
||||||
<rect width="1440" height="1024" transform="translate(-16 -17)" fill="white"/>
|
|
||||||
<g id="Sidebar">
|
|
||||||
<g clip-path="url(#clip1_0_1)">
|
|
||||||
<path d="M-16 -17H264V1007H-16V-17Z" fill="white"/>
|
|
||||||
<g id="Header">
|
|
||||||
<path d="M-16 -17H264V31H-16V-17Z" fill="white"/>
|
|
||||||
<g id="Button">
|
|
||||||
<rect x="-4" y="-3" width="74.316" height="20" rx="6" fill="white"/>
|
|
||||||
<g id="Logo">
|
|
||||||
<g id="Reflex">
|
|
||||||
<path d="M0 13.6316V0.368408H10.6106V5.67369H7.95792V3.02105H2.65264V5.67369H7.95792V8.32633H2.65264V13.6316H0ZM7.95792 13.6316V8.32633H10.6106V13.6316H7.95792Z" fill="#110F1F"/>
|
|
||||||
<path d="M13.2632 13.6316V0.368408H21.2211V3.02105H15.9158V5.67369H21.2211V8.32633H15.9158V10.979H21.2211V13.6316H13.2632Z" fill="#110F1F"/>
|
|
||||||
<path d="M23.8738 13.6316V0.368408H31.8317V3.02105H26.5264V5.67369H31.8317V8.32633H26.5264V13.6316H23.8738Z" fill="#110F1F"/>
|
|
||||||
<path d="M34.4843 13.6316V0.368408H37.137V10.979H42.4422V13.6316H34.4843Z" fill="#110F1F"/>
|
|
||||||
<path d="M45.0949 13.6316V0.368408H53.0528V3.02105H47.7475V5.67369H53.0528V8.32633H47.7475V10.979H53.0528V13.6316H45.0949Z" fill="#110F1F"/>
|
|
||||||
<path d="M55.7054 5.67369V0.368408H58.3581V5.67369H55.7054ZM63.6634 5.67369V0.368408H66.316V5.67369H63.6634ZM58.3581 8.32633V5.67369H63.6634V8.32633H58.3581ZM55.7054 13.6316V8.32633H58.3581V13.6316H55.7054ZM63.6634 13.6316V8.32633H66.316V13.6316H63.6634Z" fill="#110F1F"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path d="M264 30.5H-16V31.5H264V30.5Z" fill="#F4F3F6"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path d="M263.5 -17V1007H264.5V-17H263.5Z" fill="#F4F3F6"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_0_1">
|
|
||||||
<rect width="1440" height="1024" fill="white" transform="translate(-16 -17)"/>
|
|
||||||
</clipPath>
|
|
||||||
<clipPath id="clip1_0_1">
|
|
||||||
<path d="M-16 -17H264V1007H-16V-17Z" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.9 KiB |
@ -1,68 +0,0 @@
|
|||||||
<svg width="80" height="78" viewBox="0 0 80 78" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g filter="url(#filter0_ddddi_449_2821)">
|
|
||||||
<path d="M13 11C13 6.58172 16.5817 3 21 3H59C63.4183 3 67 6.58172 67 11V49C67 52.3137 64.3137 55 61 55H19C15.6863 55 13 52.3137 13 49V11Z" fill="url(#paint0_radial_449_2821)"/>
|
|
||||||
<path d="M13 11C13 6.58172 16.5817 3 21 3H59C63.4183 3 67 6.58172 67 11V49C67 52.3137 64.3137 55 61 55H19C15.6863 55 13 52.3137 13 49V11Z" fill="url(#paint1_radial_449_2821)"/>
|
|
||||||
<g filter="url(#filter1_i_449_2821)">
|
|
||||||
<path d="M31 37.5C30.4477 37.5 30 37.0523 30 36.5V13.5001C30 12.9478 30.4477 12.5001 31 12.5001H49C49.5523 12.5001 50 12.9478 50 13.5001V21.5001C50 22.0524 49.5523 22.5001 49 22.5001H45V18.5001C45 17.9478 44.5523 17.5001 44 17.5001H36C35.4477 17.5001 35 17.9478 35 18.5001V21.5001C35 22.0524 35.4477 22.5001 36 22.5001H45V27.5001H36C35.4477 27.5001 35 27.9478 35 28.5001V36.5C35 37.0523 34.5523 37.5 34 37.5H31ZM46 37.5C45.4477 37.5 45 37.0523 45 36.5V27.5001H49C49.5523 27.5001 50 27.9478 50 28.5001V36.5C50 37.0523 49.5523 37.5 49 37.5H46Z" fill="url(#paint2_radial_449_2821)"/>
|
|
||||||
</g>
|
|
||||||
<path d="M13 11C13 6.58172 16.5817 3 21 3H59C63.4183 3 67 6.58172 67 11V49C67 52.3137 64.3137 55 61 55H19C15.6863 55 13 52.3137 13 49V11Z" stroke="#20117E" stroke-opacity="0.04"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<filter id="filter0_ddddi_449_2821" x="0.5" y="0.5" width="79" height="77" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feMorphology radius="4" operator="erode" in="SourceAlpha" result="effect1_dropShadow_449_2821"/>
|
|
||||||
<feOffset dy="10"/>
|
|
||||||
<feGaussianBlur stdDeviation="8"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0784314 0 0 0 0 0.0705882 0 0 0 0 0.231373 0 0 0 0.06 0"/>
|
|
||||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_449_2821"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feMorphology radius="6" operator="erode" in="SourceAlpha" result="effect2_dropShadow_449_2821"/>
|
|
||||||
<feOffset dy="12"/>
|
|
||||||
<feGaussianBlur stdDeviation="3"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0784314 0 0 0 0 0.0705882 0 0 0 0 0.231373 0 0 0 0.1 0"/>
|
|
||||||
<feBlend mode="normal" in2="effect1_dropShadow_449_2821" result="effect2_dropShadow_449_2821"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feMorphology radius="4" operator="erode" in="SourceAlpha" result="effect3_dropShadow_449_2821"/>
|
|
||||||
<feOffset dy="10"/>
|
|
||||||
<feGaussianBlur stdDeviation="3"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.12549 0 0 0 0 0.0666667 0 0 0 0 0.494118 0 0 0 0.16 0"/>
|
|
||||||
<feBlend mode="normal" in2="effect2_dropShadow_449_2821" result="effect3_dropShadow_449_2821"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feMorphology radius="1" operator="dilate" in="SourceAlpha" result="effect4_dropShadow_449_2821"/>
|
|
||||||
<feOffset dy="2"/>
|
|
||||||
<feGaussianBlur stdDeviation="1"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.12549 0 0 0 0 0.0666667 0 0 0 0 0.494118 0 0 0 0.05 0"/>
|
|
||||||
<feBlend mode="normal" in2="effect3_dropShadow_449_2821" result="effect4_dropShadow_449_2821"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_449_2821" result="shape"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feOffset dy="-8"/>
|
|
||||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.678431 0 0 0 0 0.607843 0 0 0 0 0.972549 0 0 0 0.2 0"/>
|
|
||||||
<feBlend mode="normal" in2="shape" result="effect5_innerShadow_449_2821"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter1_i_449_2821" x="30" y="12.5001" width="20" height="26.9999" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feOffset dy="2"/>
|
|
||||||
<feGaussianBlur stdDeviation="1.5"/>
|
|
||||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.12549 0 0 0 0 0.0666667 0 0 0 0 0.494118 0 0 0 0.32 0"/>
|
|
||||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_449_2821"/>
|
|
||||||
</filter>
|
|
||||||
<radialGradient id="paint0_radial_449_2821" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(40 3) rotate(90) scale(52 54)">
|
|
||||||
<stop stop-color="white" stop-opacity="0.9"/>
|
|
||||||
<stop offset="1" stop-color="#4E3DB9" stop-opacity="0.24"/>
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient id="paint1_radial_449_2821" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(40 3) rotate(90) scale(52 54)">
|
|
||||||
<stop stop-color="white"/>
|
|
||||||
<stop offset="1" stop-color="#F7F7F7"/>
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient id="paint2_radial_449_2821" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(40 12.5001) rotate(90) scale(24.9999 20)">
|
|
||||||
<stop stop-color="#F5F3FF"/>
|
|
||||||
<stop stop-color="white"/>
|
|
||||||
<stop offset="1" stop-color="#E1DDF4"/>
|
|
||||||
</radialGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.3 KiB |
@ -1,13 +0,0 @@
|
|||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g id="PaneLeft" clip-path="url(#clip0_469_1942)">
|
|
||||||
<g id="Vector">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.80217 0.525009C7.34654 0.525009 6.97717 0.894373 6.97717 1.35001V10.65C6.97717 11.1056 7.34654 11.475 7.80217 11.475H10.6522C11.1078 11.475 11.4772 11.1056 11.4772 10.65V1.35001C11.4772 0.894373 11.1078 0.525009 10.6522 0.525009H7.80217ZM8.02717 10.425V1.57501H10.4272V10.425H8.02717Z" fill="#494369"/>
|
|
||||||
<path d="M3.78215 8.14502L2.16213 6.525H5.92717V5.475H2.16213L3.78215 3.85498L3.03969 3.11252L0.523438 5.62877V6.37123L3.03969 8.88748L3.78215 8.14502Z" fill="#494369"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_469_1942">
|
|
||||||
<rect width="12" height="12" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 807 B |
@ -1 +0,0 @@
|
|||||||
"""Base template for Reflex."""
|
|
@ -1,127 +0,0 @@
|
|||||||
"""Welcome to Reflex! This file outlines the steps to create a basic app."""
|
|
||||||
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from .pages import chatapp_page, datatable_page, forms_page, graphing_page, home_page
|
|
||||||
from .sidebar import sidebar
|
|
||||||
from .state import State
|
|
||||||
from .styles import *
|
|
||||||
|
|
||||||
meta = [
|
|
||||||
{
|
|
||||||
"name": "viewport",
|
|
||||||
"content": "width=device-width, shrink-to-fit=no, initial-scale=1",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def template(main_content: Callable[[], rx.Component]) -> rx.Component:
|
|
||||||
"""The template for each page of the app.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
main_content (Callable[[], rx.Component]): The main content of the page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The template for each page of the app.
|
|
||||||
"""
|
|
||||||
menu_button = rx.chakra.box(
|
|
||||||
rx.chakra.menu(
|
|
||||||
rx.chakra.menu_button(
|
|
||||||
rx.chakra.icon(
|
|
||||||
tag="hamburger",
|
|
||||||
size="4em",
|
|
||||||
color=text_color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.menu_list(
|
|
||||||
rx.chakra.menu_item(rx.chakra.link("Home", href="/", width="100%")),
|
|
||||||
rx.chakra.menu_divider(),
|
|
||||||
rx.chakra.menu_item(
|
|
||||||
rx.chakra.link(
|
|
||||||
"About", href="https://github.com/reflex-dev", width="100%"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
rx.chakra.menu_item(
|
|
||||||
rx.chakra.link(
|
|
||||||
"Contact", href="mailto:founders@reflex.dev", width="100%"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
position="fixed",
|
|
||||||
right="1.5em",
|
|
||||||
top="1.5em",
|
|
||||||
z_index="500",
|
|
||||||
)
|
|
||||||
|
|
||||||
return rx.chakra.hstack(
|
|
||||||
sidebar(),
|
|
||||||
main_content(),
|
|
||||||
rx.chakra.spacer(),
|
|
||||||
menu_button,
|
|
||||||
align_items="flex-start",
|
|
||||||
transition="left 0.5s, width 0.5s",
|
|
||||||
position="relative",
|
|
||||||
left=rx.cond(State.sidebar_displayed, "0px", f"-{sidebar_width}"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@rx.page("/", meta=meta)
|
|
||||||
@template
|
|
||||||
def home() -> rx.Component:
|
|
||||||
"""Home page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The home page.
|
|
||||||
"""
|
|
||||||
return home_page()
|
|
||||||
|
|
||||||
|
|
||||||
@rx.page("/forms", meta=meta)
|
|
||||||
@template
|
|
||||||
def forms() -> rx.Component:
|
|
||||||
"""Forms page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The settings page.
|
|
||||||
"""
|
|
||||||
return forms_page()
|
|
||||||
|
|
||||||
|
|
||||||
@rx.page("/graphing", meta=meta)
|
|
||||||
@template
|
|
||||||
def graphing() -> rx.Component:
|
|
||||||
"""Graphing page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The graphing page.
|
|
||||||
"""
|
|
||||||
return graphing_page()
|
|
||||||
|
|
||||||
|
|
||||||
@rx.page("/datatable", meta=meta)
|
|
||||||
@template
|
|
||||||
def datatable() -> rx.Component:
|
|
||||||
"""Data Table page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The chatapp page.
|
|
||||||
"""
|
|
||||||
return datatable_page()
|
|
||||||
|
|
||||||
|
|
||||||
@rx.page("/chatapp", meta=meta)
|
|
||||||
@template
|
|
||||||
def chatapp() -> rx.Component:
|
|
||||||
"""Chatapp page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The chatapp page.
|
|
||||||
"""
|
|
||||||
return chatapp_page()
|
|
||||||
|
|
||||||
|
|
||||||
# Create the app.
|
|
||||||
app = rx.App(style=base_style)
|
|
@ -1,7 +0,0 @@
|
|||||||
"""The pages of the app."""
|
|
||||||
|
|
||||||
from .chatapp import chatapp_page
|
|
||||||
from .datatable import datatable_page
|
|
||||||
from .forms import forms_page
|
|
||||||
from .graphing import graphing_page
|
|
||||||
from .home import home_page
|
|
@ -1,31 +0,0 @@
|
|||||||
"""The main Chat app."""
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ..styles import *
|
|
||||||
from ..webui import styles
|
|
||||||
from ..webui.components import chat, modal, navbar, sidebar
|
|
||||||
|
|
||||||
|
|
||||||
def chatapp_page() -> rx.Component:
|
|
||||||
"""The main app.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The UI for the main app.
|
|
||||||
"""
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
navbar(),
|
|
||||||
chat.chat(),
|
|
||||||
chat.action_bar(),
|
|
||||||
sidebar(),
|
|
||||||
modal(),
|
|
||||||
bg=styles.bg_dark_color,
|
|
||||||
color=styles.text_light_color,
|
|
||||||
min_h="100vh",
|
|
||||||
align_items="stretch",
|
|
||||||
spacing="0",
|
|
||||||
style=template_content_style,
|
|
||||||
),
|
|
||||||
style=template_page_style,
|
|
||||||
)
|
|
@ -1,360 +0,0 @@
|
|||||||
"""The settings page for the template."""
|
|
||||||
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
from reflex.components.datadisplay.dataeditor import DataEditorTheme
|
|
||||||
|
|
||||||
from ..styles import *
|
|
||||||
from ..webui.state import State
|
|
||||||
|
|
||||||
|
|
||||||
class DataTableState(State):
|
|
||||||
"""Datatable state."""
|
|
||||||
|
|
||||||
cols: list[Any] = [
|
|
||||||
{"title": "Title", "type": "str"},
|
|
||||||
{
|
|
||||||
"title": "Name",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 300,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Birth",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Human",
|
|
||||||
"type": "bool",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "House",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Wand",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 250,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Patronus",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Blood status",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 200,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
data = [
|
|
||||||
[
|
|
||||||
"1",
|
|
||||||
"Harry James Potter",
|
|
||||||
"31 July 1980",
|
|
||||||
True,
|
|
||||||
"Gryffindor",
|
|
||||||
"11' Holly phoenix feather",
|
|
||||||
"Stag",
|
|
||||||
"Half-blood",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"2",
|
|
||||||
"Ronald Bilius Weasley",
|
|
||||||
"1 March 1980",
|
|
||||||
True,
|
|
||||||
"Gryffindor",
|
|
||||||
"12' Ash unicorn tail hair",
|
|
||||||
"Jack Russell terrier",
|
|
||||||
"Pure-blood",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"3",
|
|
||||||
"Hermione Jean Granger",
|
|
||||||
"19 September, 1979",
|
|
||||||
True,
|
|
||||||
"Gryffindor",
|
|
||||||
"10¾' vine wood dragon heartstring",
|
|
||||||
"Otter",
|
|
||||||
"Muggle-born",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"4",
|
|
||||||
"Albus Percival Wulfric Brian Dumbledore",
|
|
||||||
"Late August 1881",
|
|
||||||
True,
|
|
||||||
"Gryffindor",
|
|
||||||
"15' Elder Thestral tail hair core",
|
|
||||||
"Phoenix",
|
|
||||||
"Half-blood",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"5",
|
|
||||||
"Rubeus Hagrid",
|
|
||||||
"6 December 1928",
|
|
||||||
False,
|
|
||||||
"Gryffindor",
|
|
||||||
"16' Oak unknown core",
|
|
||||||
"None",
|
|
||||||
"Part-Human (Half-giant)",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"6",
|
|
||||||
"Fred Weasley",
|
|
||||||
"1 April, 1978",
|
|
||||||
True,
|
|
||||||
"Gryffindor",
|
|
||||||
"Unknown",
|
|
||||||
"Unknown",
|
|
||||||
"Pure-blood",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"7",
|
|
||||||
"George Weasley",
|
|
||||||
"1 April, 1978",
|
|
||||||
True,
|
|
||||||
"Gryffindor",
|
|
||||||
"Unknown",
|
|
||||||
"Unknown",
|
|
||||||
"Pure-blood",
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
code_show = """rx.chakra.hstack(
|
|
||||||
rx.chakra.divider(orientation="vertical", height="100vh", border="solid black 1px"),
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.box(
|
|
||||||
rx.data_editor(
|
|
||||||
columns=DataTableState.cols,
|
|
||||||
data=DataTableState.data,
|
|
||||||
draw_focus_ring=True,
|
|
||||||
row_height=50,
|
|
||||||
smooth_scroll_x=True,
|
|
||||||
smooth_scroll_y=True,
|
|
||||||
column_select="single",
|
|
||||||
# style
|
|
||||||
theme=DataEditorTheme(**darkTheme),
|
|
||||||
width="80vw",
|
|
||||||
height="80vh",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.spacer(),
|
|
||||||
height="100vh",
|
|
||||||
spacing="25",
|
|
||||||
),
|
|
||||||
)"""
|
|
||||||
|
|
||||||
state_show = """class DataTableState(State):
|
|
||||||
cols: list[Any] = [
|
|
||||||
{"title": "Title", "type": "str"},
|
|
||||||
{
|
|
||||||
"title": "Name",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 300,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Birth",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Human",
|
|
||||||
"type": "bool",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "House",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Wand",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 250,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Patronus",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Blood status",
|
|
||||||
"type": "str",
|
|
||||||
"group": "Data",
|
|
||||||
"width": 200,
|
|
||||||
},
|
|
||||||
]"""
|
|
||||||
|
|
||||||
data_show = """[
|
|
||||||
["1", "Harry James Potter", "31 July 1980", True, "Gryffindor", "11' Holly phoenix feather", "Stag", "Half-blood"],
|
|
||||||
["2", "Ronald Bilius Weasley", "1 March 1980", True,"Gryffindor", "12' Ash unicorn tail hair", "Jack Russell terrier", "Pure-blood"],
|
|
||||||
["3", "Hermione Jean Granger", "19 September, 1979", True, "Gryffindor", "10¾' vine wood dragon heartstring", "Otter", "Muggle-born"],
|
|
||||||
["4", "Albus Percival Wulfric Brian Dumbledore", "Late August 1881", True, "Gryffindor", "15' Elder Thestral tail hair core", "Phoenix", "Half-blood"],
|
|
||||||
["5", "Rubeus Hagrid", "6 December 1928", False, "Gryffindor", "16' Oak unknown core", "None", "Part-Human (Half-giant)"],
|
|
||||||
["6", "Fred Weasley", "1 April, 1978", True, "Gryffindor", "Unknown", "Unknown", "Pure-blood"],
|
|
||||||
["7", "George Weasley", "1 April, 1978", True, "Gryffindor", "Unknown", "Unknown", "Pure-blood"],
|
|
||||||
]"""
|
|
||||||
|
|
||||||
|
|
||||||
darkTheme = {
|
|
||||||
"accent_color": "#8c96ff",
|
|
||||||
"accent_light": "rgba(202, 206, 255, 0.253)",
|
|
||||||
"text_dark": "#ffffff",
|
|
||||||
"text_medium": "#b8b8b8",
|
|
||||||
"text_light": "#a0a0a0",
|
|
||||||
"text_bubble": "#ffffff",
|
|
||||||
"bg_icon_header": "#b8b8b8",
|
|
||||||
"fg_icon_header": "#000000",
|
|
||||||
"text_header": "#a1a1a1",
|
|
||||||
"text_header_selected": "#000000",
|
|
||||||
"bg_cell": "#16161b",
|
|
||||||
"bg_cell_medium": "#202027",
|
|
||||||
"bg_header": "#212121",
|
|
||||||
"bg_header_has_focus": "#474747",
|
|
||||||
"bg_header_hovered": "#404040",
|
|
||||||
"bg_bubble": "#212121",
|
|
||||||
"bg_bubble_selected": "#000000",
|
|
||||||
"bg_search_result": "#423c24",
|
|
||||||
"border_color": "rgba(225,225,225,0.2)",
|
|
||||||
"drilldown_border": "rgba(225,225,225,0.4)",
|
|
||||||
"link_color": "#4F5DFF",
|
|
||||||
"header_font_style": "bold 14px",
|
|
||||||
"base_font_style": "13px",
|
|
||||||
"font_family": "Inter, Roboto, -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Ubuntu, noto, arial, sans-serif",
|
|
||||||
}
|
|
||||||
|
|
||||||
darkTheme_show = """darkTheme={
|
|
||||||
"accent_color": "#8c96ff",
|
|
||||||
"accent_light": "rgba(202, 206, 255, 0.253)",
|
|
||||||
"text_dark": "#ffffff",
|
|
||||||
"text_medium": "#b8b8b8",
|
|
||||||
"text_light": "#a0a0a0",
|
|
||||||
"text_bubble": "#ffffff",
|
|
||||||
"bg_icon_header": "#b8b8b8",
|
|
||||||
"fg_icon_header": "#000000",
|
|
||||||
"text_header": "#a1a1a1",
|
|
||||||
"text_header_selected": "#000000",
|
|
||||||
"bg_cell": "#16161b",
|
|
||||||
"bg_cell_medium": "#202027",
|
|
||||||
"bg_header": "#212121",
|
|
||||||
"bg_header_has_focus": "#474747",
|
|
||||||
"bg_header_hovered": "#404040",
|
|
||||||
"bg_bubble": "#212121",
|
|
||||||
"bg_bubble_selected": "#000000",
|
|
||||||
"bg_search_result": "#423c24",
|
|
||||||
"border_color": "rgba(225,225,225,0.2)",
|
|
||||||
"drilldown_border": "rgba(225,225,225,0.4)",
|
|
||||||
"link_color": "#4F5DFF",
|
|
||||||
"header_font_style": "bold 14px",
|
|
||||||
"base_font_style": "13px",
|
|
||||||
"font_family": "Inter, Roboto, -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Ubuntu, noto, arial, sans-serif",
|
|
||||||
}"""
|
|
||||||
|
|
||||||
|
|
||||||
def datatable_page() -> rx.Component:
|
|
||||||
"""The UI for the settings page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The UI for the settings page.
|
|
||||||
"""
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.heading(
|
|
||||||
"Data Table Demo",
|
|
||||||
font_size="3em",
|
|
||||||
),
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.box(
|
|
||||||
rx.data_editor(
|
|
||||||
columns=DataTableState.cols,
|
|
||||||
data=DataTableState.data,
|
|
||||||
draw_focus_ring=True,
|
|
||||||
row_height=50,
|
|
||||||
smooth_scroll_x=True,
|
|
||||||
smooth_scroll_y=True,
|
|
||||||
column_select="single",
|
|
||||||
# style
|
|
||||||
theme=DataEditorTheme(**darkTheme),
|
|
||||||
width="80vw",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.spacer(),
|
|
||||||
spacing="25",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.tabs(
|
|
||||||
rx.chakra.tab_list(
|
|
||||||
rx.chakra.tab("Code", style=tab_style),
|
|
||||||
rx.chakra.tab("Data", style=tab_style),
|
|
||||||
rx.chakra.tab("State", style=tab_style),
|
|
||||||
rx.chakra.tab("Styling", style=tab_style),
|
|
||||||
padding_x=0,
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panels(
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
code_show,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
data_show,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
state_show,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
darkTheme_show,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
),
|
|
||||||
variant="unstyled",
|
|
||||||
color_scheme="purple",
|
|
||||||
align="end",
|
|
||||||
width="100%",
|
|
||||||
padding_top=".5em",
|
|
||||||
),
|
|
||||||
style=template_content_style,
|
|
||||||
),
|
|
||||||
style=template_page_style,
|
|
||||||
)
|
|
@ -1,257 +0,0 @@
|
|||||||
"""The settings page for the template."""
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ..states.form_state import FormState, UploadState
|
|
||||||
from ..styles import *
|
|
||||||
|
|
||||||
forms_1_code = """rx.chakra.vstack(
|
|
||||||
rx.chakra.form(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.input(
|
|
||||||
placeholder="First Name",
|
|
||||||
id="first_name",
|
|
||||||
),
|
|
||||||
rx.chakra.input(
|
|
||||||
placeholder="Last Name", id="last_name"
|
|
||||||
),
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.checkbox("Checked", id="check"),
|
|
||||||
rx.chakra.switch("Switched", id="switch"),
|
|
||||||
),
|
|
||||||
rx.chakra.button("Submit",
|
|
||||||
type_="submit",
|
|
||||||
bg="#ecfdf5",
|
|
||||||
color="#047857",
|
|
||||||
border_radius="lg",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
on_submit=FormState.handle_submit,
|
|
||||||
),
|
|
||||||
rx.chakra.divider(),
|
|
||||||
rx.chakra.heading("Results"),
|
|
||||||
rx.chakra.text(FormState.form_data.to_string()),
|
|
||||||
width="100%",
|
|
||||||
)"""
|
|
||||||
|
|
||||||
color = "rgb(107,99,246)"
|
|
||||||
|
|
||||||
forms_1_state = """class FormState(rx.State):
|
|
||||||
|
|
||||||
form_data: dict = {}
|
|
||||||
|
|
||||||
def handle_submit(self, form_data: dict):
|
|
||||||
"Handle the form submit."
|
|
||||||
self.form_data = form_data"""
|
|
||||||
|
|
||||||
|
|
||||||
forms_2_code = """rx.chakra.vstack(
|
|
||||||
rx.upload(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.button(
|
|
||||||
"Select File",
|
|
||||||
color=color,
|
|
||||||
bg="white",
|
|
||||||
border=f"1px solid {color}",
|
|
||||||
),
|
|
||||||
rx.chakra.text(
|
|
||||||
"Drag and drop files here or click to select files"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
border=f"1px dotted {color}",
|
|
||||||
padding="5em",
|
|
||||||
),
|
|
||||||
rx.chakra.hstack(rx.foreach(rx.selected_files, rx.chakra.text)),
|
|
||||||
rx.chakra.button(
|
|
||||||
"Upload",
|
|
||||||
on_click=lambda: UploadState.handle_upload(
|
|
||||||
rx.upload_files()
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.button(
|
|
||||||
"Clear",
|
|
||||||
on_click=rx.clear_selected_files,
|
|
||||||
),
|
|
||||||
rx.foreach(
|
|
||||||
UploadState.img, lambda img: rx.chakra.image(src=img, width="20%", height="auto",)
|
|
||||||
),
|
|
||||||
padding="5em",
|
|
||||||
width="100%",
|
|
||||||
)"""
|
|
||||||
|
|
||||||
forms_2_state = """class UploadState(State):
|
|
||||||
"The app state."
|
|
||||||
|
|
||||||
# The images to show.
|
|
||||||
img: list[str]
|
|
||||||
|
|
||||||
async def handle_upload(
|
|
||||||
self, files: list[rx.UploadFile]
|
|
||||||
):
|
|
||||||
"Handle the upload of file(s).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
files: The uploaded files.
|
|
||||||
"
|
|
||||||
for file in files:
|
|
||||||
upload_data = await file.read()
|
|
||||||
outfile = rx.get_asset_path(file.filename)
|
|
||||||
# Save the file.
|
|
||||||
with open(outfile, "wb") as file_object:
|
|
||||||
file_object.write(upload_data)
|
|
||||||
|
|
||||||
# Update the img var.
|
|
||||||
self.img.append(f"/{file.filename}")"""
|
|
||||||
|
|
||||||
|
|
||||||
def forms_page() -> rx.Component:
|
|
||||||
"""The UI for the settings page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The UI for the settings page.
|
|
||||||
"""
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.heading(
|
|
||||||
"Forms Demo",
|
|
||||||
font_size="3em",
|
|
||||||
),
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.form(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.input(
|
|
||||||
placeholder="First Name",
|
|
||||||
id="first_name",
|
|
||||||
),
|
|
||||||
rx.chakra.input(placeholder="Last Name", id="last_name"),
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.checkbox("Checked", id="check"),
|
|
||||||
rx.chakra.switch("Switched", id="switch"),
|
|
||||||
),
|
|
||||||
rx.chakra.button(
|
|
||||||
"Submit",
|
|
||||||
type_="submit",
|
|
||||||
bg="#ecfdf5",
|
|
||||||
color="#047857",
|
|
||||||
border_radius="lg",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
on_submit=FormState.handle_submit,
|
|
||||||
),
|
|
||||||
rx.chakra.divider(),
|
|
||||||
rx.chakra.heading("Results"),
|
|
||||||
rx.chakra.text(FormState.form_data.to_string()),
|
|
||||||
width="100%",
|
|
||||||
),
|
|
||||||
rx.chakra.tabs(
|
|
||||||
rx.chakra.tab_list(
|
|
||||||
rx.chakra.tab("Code", style=tab_style),
|
|
||||||
rx.chakra.tab("State", style=tab_style),
|
|
||||||
padding_x=0,
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panels(
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
forms_1_code,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
forms_1_state,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
),
|
|
||||||
variant="unstyled",
|
|
||||||
color_scheme="purple",
|
|
||||||
align="end",
|
|
||||||
width="100%",
|
|
||||||
padding_top=".5em",
|
|
||||||
),
|
|
||||||
rx.chakra.heading("Upload Example", font_size="3em"),
|
|
||||||
rx.chakra.text("Try uploading some images and see how they look."),
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.upload(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.button(
|
|
||||||
"Select File",
|
|
||||||
color=color,
|
|
||||||
bg="white",
|
|
||||||
border=f"1px solid {color}",
|
|
||||||
),
|
|
||||||
rx.chakra.text(
|
|
||||||
"Drag and drop files here or click to select files"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
border=f"1px dotted {color}",
|
|
||||||
padding="5em",
|
|
||||||
),
|
|
||||||
rx.chakra.hstack(rx.foreach(rx.selected_files, rx.chakra.text)),
|
|
||||||
rx.chakra.button(
|
|
||||||
"Upload",
|
|
||||||
on_click=lambda: UploadState.handle_upload(rx.upload_files()),
|
|
||||||
),
|
|
||||||
rx.chakra.button(
|
|
||||||
"Clear",
|
|
||||||
on_click=rx.clear_selected_files,
|
|
||||||
),
|
|
||||||
rx.foreach(
|
|
||||||
UploadState.img,
|
|
||||||
lambda img: rx.chakra.image(
|
|
||||||
src=img,
|
|
||||||
width="20%",
|
|
||||||
height="auto",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
padding="5em",
|
|
||||||
width="100%",
|
|
||||||
),
|
|
||||||
rx.chakra.tabs(
|
|
||||||
rx.chakra.tab_list(
|
|
||||||
rx.chakra.tab("Code", style=tab_style),
|
|
||||||
rx.chakra.tab("State", style=tab_style),
|
|
||||||
padding_x=0,
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panels(
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
forms_2_code,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
forms_2_state,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
),
|
|
||||||
variant="unstyled",
|
|
||||||
color_scheme="purple",
|
|
||||||
align="end",
|
|
||||||
width="100%",
|
|
||||||
padding_top=".5em",
|
|
||||||
),
|
|
||||||
style=template_content_style,
|
|
||||||
),
|
|
||||||
style=template_page_style,
|
|
||||||
)
|
|
@ -1,253 +0,0 @@
|
|||||||
"""The dashboard page for the template."""
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ..states.pie_state import PieChartState
|
|
||||||
from ..styles import *
|
|
||||||
|
|
||||||
data_1 = [
|
|
||||||
{"name": "Page A", "uv": 4000, "pv": 2400, "amt": 2400},
|
|
||||||
{"name": "Page B", "uv": 3000, "pv": 1398, "amt": 2210},
|
|
||||||
{"name": "Page C", "uv": 2000, "pv": 9800, "amt": 2290},
|
|
||||||
{"name": "Page D", "uv": 2780, "pv": 3908, "amt": 2000},
|
|
||||||
{"name": "Page E", "uv": 1890, "pv": 4800, "amt": 2181},
|
|
||||||
{"name": "Page F", "uv": 2390, "pv": 3800, "amt": 2500},
|
|
||||||
{"name": "Page G", "uv": 3490, "pv": 4300, "amt": 2100},
|
|
||||||
]
|
|
||||||
data_1_show = """[
|
|
||||||
{"name": "Page A", "uv": 4000, "pv": 2400, "amt": 2400},
|
|
||||||
{"name": "Page B", "uv": 3000, "pv": 1398, "amt": 2210},
|
|
||||||
{"name": "Page C", "uv": 2000, "pv": 9800, "amt": 2290},
|
|
||||||
{"name": "Page D", "uv": 2780, "pv": 3908, "amt": 2000},
|
|
||||||
{"name": "Page E", "uv": 1890, "pv": 4800, "amt": 2181},
|
|
||||||
{"name": "Page F", "uv": 2390, "pv": 3800, "amt": 2500},
|
|
||||||
{"name": "Page G", "uv": 3490, "pv": 4300, "amt": 2100},
|
|
||||||
]"""
|
|
||||||
|
|
||||||
|
|
||||||
graph_1_code = """rx.recharts.composed_chart(
|
|
||||||
rx.recharts.area(
|
|
||||||
data_key="uv", stroke="#8884d8", fill="#8884d8"
|
|
||||||
),
|
|
||||||
rx.recharts.bar(
|
|
||||||
data_key="amt", bar_size=20, fill="#413ea0"
|
|
||||||
),
|
|
||||||
rx.recharts.line(
|
|
||||||
data_key="pv", type_="monotone", stroke="#ff7300"
|
|
||||||
),
|
|
||||||
rx.recharts.x_axis(data_key="name"),
|
|
||||||
rx.recharts.y_axis(),
|
|
||||||
rx.recharts.cartesian_grid(stroke_dasharray="3 3"),
|
|
||||||
rx.recharts.graphing_tooltip(),
|
|
||||||
data=data,
|
|
||||||
)"""
|
|
||||||
|
|
||||||
|
|
||||||
graph_2_code = """rx.recharts.pie_chart(
|
|
||||||
rx.recharts.pie(
|
|
||||||
data=PieChartState.resources,
|
|
||||||
data_key="count",
|
|
||||||
name_key="type_",
|
|
||||||
cx="50%",
|
|
||||||
cy="50%",
|
|
||||||
start_angle=180,
|
|
||||||
end_angle=0,
|
|
||||||
fill="#8884d8",
|
|
||||||
label=True,
|
|
||||||
),
|
|
||||||
rx.recharts.graphing_tooltip(),
|
|
||||||
),
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.foreach(
|
|
||||||
PieChartState.resource_types,
|
|
||||||
lambda type_, i: rx.chakra.hstack(
|
|
||||||
rx.chakra.button(
|
|
||||||
"-",
|
|
||||||
on_click=PieChartState.decrement(type_),
|
|
||||||
),
|
|
||||||
rx.chakra.text(
|
|
||||||
type_,
|
|
||||||
PieChartState.resources[i]["count"],
|
|
||||||
),
|
|
||||||
rx.chakra.button(
|
|
||||||
"+",
|
|
||||||
on_click=PieChartState.increment(type_),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)"""
|
|
||||||
|
|
||||||
graph_2_state = """class PieChartState(rx.State):
|
|
||||||
resources: list[dict[str, Any]] = [
|
|
||||||
dict(type_="🏆", count=1),
|
|
||||||
dict(type_="🪵", count=1),
|
|
||||||
dict(type_="🥑", count=1),
|
|
||||||
dict(type_="🧱", count=1),
|
|
||||||
]
|
|
||||||
|
|
||||||
@rx.cached_var
|
|
||||||
def resource_types(self) -> list[str]:
|
|
||||||
return [r["type_"] for r in self.resources]
|
|
||||||
|
|
||||||
def increment(self, type_: str):
|
|
||||||
for resource in self.resources:
|
|
||||||
if resource["type_"] == type_:
|
|
||||||
resource["count"] += 1
|
|
||||||
break
|
|
||||||
|
|
||||||
def decrement(self, type_: str):
|
|
||||||
for resource in self.resources:
|
|
||||||
if (
|
|
||||||
resource["type_"] == type_
|
|
||||||
and resource["count"] > 0
|
|
||||||
):
|
|
||||||
resource["count"] -= 1
|
|
||||||
break
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def graphing_page() -> rx.Component:
|
|
||||||
"""The UI for the dashboard page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The UI for the dashboard page.
|
|
||||||
"""
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.heading(
|
|
||||||
"Graphing Demo",
|
|
||||||
font_size="3em",
|
|
||||||
),
|
|
||||||
rx.chakra.heading(
|
|
||||||
"Composed Chart",
|
|
||||||
font_size="2em",
|
|
||||||
),
|
|
||||||
rx.chakra.stack(
|
|
||||||
rx.recharts.composed_chart(
|
|
||||||
rx.recharts.area(data_key="uv", stroke="#8884d8", fill="#8884d8"),
|
|
||||||
rx.recharts.bar(data_key="amt", bar_size=20, fill="#413ea0"),
|
|
||||||
rx.recharts.line(data_key="pv", type_="monotone", stroke="#ff7300"),
|
|
||||||
rx.recharts.x_axis(data_key="name"),
|
|
||||||
rx.recharts.y_axis(),
|
|
||||||
rx.recharts.cartesian_grid(stroke_dasharray="3 3"),
|
|
||||||
rx.recharts.graphing_tooltip(),
|
|
||||||
data=data_1,
|
|
||||||
# height="15em",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
height="20em",
|
|
||||||
),
|
|
||||||
rx.chakra.tabs(
|
|
||||||
rx.chakra.tab_list(
|
|
||||||
rx.chakra.tab("Code", style=tab_style),
|
|
||||||
rx.chakra.tab("Data", style=tab_style),
|
|
||||||
padding_x=0,
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panels(
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
graph_1_code,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
data_1_show,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
),
|
|
||||||
variant="unstyled",
|
|
||||||
color_scheme="purple",
|
|
||||||
align="end",
|
|
||||||
width="100%",
|
|
||||||
padding_top=".5em",
|
|
||||||
),
|
|
||||||
rx.chakra.heading("Interactive Example", font_size="2em"),
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.recharts.pie_chart(
|
|
||||||
rx.recharts.pie(
|
|
||||||
data=PieChartState.resources,
|
|
||||||
data_key="count",
|
|
||||||
name_key="type_",
|
|
||||||
cx="50%",
|
|
||||||
cy="50%",
|
|
||||||
start_angle=180,
|
|
||||||
end_angle=0,
|
|
||||||
fill="#8884d8",
|
|
||||||
label=True,
|
|
||||||
),
|
|
||||||
rx.recharts.graphing_tooltip(),
|
|
||||||
),
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.foreach(
|
|
||||||
PieChartState.resource_types,
|
|
||||||
lambda type_, i: rx.chakra.hstack(
|
|
||||||
rx.chakra.button(
|
|
||||||
"-",
|
|
||||||
on_click=PieChartState.decrement(type_),
|
|
||||||
),
|
|
||||||
rx.chakra.text(
|
|
||||||
type_,
|
|
||||||
PieChartState.resources[i]["count"],
|
|
||||||
),
|
|
||||||
rx.chakra.button(
|
|
||||||
"+",
|
|
||||||
on_click=PieChartState.increment(type_),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
height="15em",
|
|
||||||
),
|
|
||||||
rx.chakra.tabs(
|
|
||||||
rx.chakra.tab_list(
|
|
||||||
rx.chakra.tab("Code", style=tab_style),
|
|
||||||
rx.chakra.tab("State", style=tab_style),
|
|
||||||
padding_x=0,
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panels(
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
graph_2_code,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
rx.chakra.tab_panel(
|
|
||||||
rx.code_block(
|
|
||||||
graph_2_state,
|
|
||||||
language="python",
|
|
||||||
show_line_numbers=True,
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
padding_x=0,
|
|
||||||
padding_y=".25em",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
),
|
|
||||||
variant="unstyled",
|
|
||||||
color_scheme="purple",
|
|
||||||
align="end",
|
|
||||||
width="100%",
|
|
||||||
padding_top=".5em",
|
|
||||||
),
|
|
||||||
style=template_content_style,
|
|
||||||
min_h="100vh",
|
|
||||||
),
|
|
||||||
style=template_page_style,
|
|
||||||
min_h="100vh",
|
|
||||||
)
|
|
@ -1,56 +0,0 @@
|
|||||||
"""The home page of the app."""
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ..styles import *
|
|
||||||
|
|
||||||
|
|
||||||
def home_page() -> rx.Component:
|
|
||||||
"""The UI for the home page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The UI for the home page.
|
|
||||||
"""
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.heading(
|
|
||||||
"Welcome to Reflex! 👋",
|
|
||||||
font_size="3em",
|
|
||||||
),
|
|
||||||
rx.chakra.text(
|
|
||||||
"Reflex is an open-source app framework built specifically to allow you to build web apps in pure python. 👈 Select a demo from the sidebar to see some examples of what Reflex can do!",
|
|
||||||
),
|
|
||||||
rx.chakra.heading(
|
|
||||||
"Things to check out:",
|
|
||||||
font_size="2em",
|
|
||||||
),
|
|
||||||
rx.chakra.unordered_list(
|
|
||||||
rx.chakra.list_item(
|
|
||||||
"Take a look at ",
|
|
||||||
rx.chakra.link(
|
|
||||||
"reflex.dev",
|
|
||||||
href="https://reflex.dev",
|
|
||||||
color="rgb(107,99,246)",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.list_item(
|
|
||||||
"Check out our ",
|
|
||||||
rx.chakra.link(
|
|
||||||
"docs",
|
|
||||||
href="https://reflex.dev/docs/getting-started/introduction/",
|
|
||||||
color="rgb(107,99,246)",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.list_item(
|
|
||||||
"Ask a question in our ",
|
|
||||||
rx.chakra.link(
|
|
||||||
"community",
|
|
||||||
href="https://discord.gg/T5WSbC2YtQ",
|
|
||||||
color="rgb(107,99,246)",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
style=template_content_style,
|
|
||||||
),
|
|
||||||
style=template_page_style,
|
|
||||||
)
|
|
@ -1,178 +0,0 @@
|
|||||||
"""Sidebar component for the app."""
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from .state import State
|
|
||||||
from .styles import *
|
|
||||||
|
|
||||||
|
|
||||||
def sidebar_header() -> rx.Component:
|
|
||||||
"""Sidebar header.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The sidebar header component.
|
|
||||||
"""
|
|
||||||
return rx.chakra.hstack(
|
|
||||||
rx.chakra.image(
|
|
||||||
src="/icon.svg",
|
|
||||||
height="2em",
|
|
||||||
),
|
|
||||||
rx.chakra.spacer(),
|
|
||||||
rx.chakra.link(
|
|
||||||
rx.chakra.center(
|
|
||||||
rx.chakra.image(
|
|
||||||
src="/github.svg",
|
|
||||||
height="3em",
|
|
||||||
padding="0.5em",
|
|
||||||
),
|
|
||||||
box_shadow=box_shadow,
|
|
||||||
bg="transparent",
|
|
||||||
border_radius=border_radius,
|
|
||||||
_hover={
|
|
||||||
"bg": accent_color,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
href="https://github.com/reflex-dev/reflex",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
border_bottom=border,
|
|
||||||
padding="1em",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def sidebar_footer() -> rx.Component:
|
|
||||||
"""Sidebar footer.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The sidebar footer component.
|
|
||||||
"""
|
|
||||||
return rx.chakra.hstack(
|
|
||||||
rx.chakra.link(
|
|
||||||
rx.chakra.center(
|
|
||||||
rx.chakra.image(
|
|
||||||
src="/paneleft.svg",
|
|
||||||
height="2em",
|
|
||||||
padding="0.5em",
|
|
||||||
),
|
|
||||||
bg="transparent",
|
|
||||||
border_radius=border_radius,
|
|
||||||
**hover_accent_bg,
|
|
||||||
),
|
|
||||||
on_click=State.toggle_sidebar_displayed,
|
|
||||||
transform=rx.cond(~State.sidebar_displayed, "rotate(180deg)", ""),
|
|
||||||
transition="transform 0.5s, left 0.5s",
|
|
||||||
position="relative",
|
|
||||||
left=rx.cond(State.sidebar_displayed, "0px", "20.5em"),
|
|
||||||
**overlapping_button_style,
|
|
||||||
),
|
|
||||||
rx.chakra.spacer(),
|
|
||||||
rx.chakra.link(
|
|
||||||
rx.chakra.text(
|
|
||||||
"Docs",
|
|
||||||
),
|
|
||||||
href="https://reflex.dev/docs/getting-started/introduction/",
|
|
||||||
),
|
|
||||||
rx.chakra.link(
|
|
||||||
rx.chakra.text(
|
|
||||||
"Blog",
|
|
||||||
),
|
|
||||||
href="https://reflex.dev/blog/",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
border_top=border,
|
|
||||||
padding="1em",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def sidebar_item(text: str, icon: str, url: str) -> rx.Component:
|
|
||||||
"""Sidebar item.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text (str): The text of the item.
|
|
||||||
icon (str): The icon of the item.
|
|
||||||
url (str): The URL of the item.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The sidebar item component.
|
|
||||||
"""
|
|
||||||
return rx.chakra.link(
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.image(
|
|
||||||
src=icon,
|
|
||||||
height="2.5em",
|
|
||||||
padding="0.5em",
|
|
||||||
),
|
|
||||||
rx.chakra.text(
|
|
||||||
text,
|
|
||||||
),
|
|
||||||
bg=rx.cond(
|
|
||||||
State.origin_url == f"/{text.lower()}/",
|
|
||||||
accent_color,
|
|
||||||
"transparent",
|
|
||||||
),
|
|
||||||
color=rx.cond(
|
|
||||||
State.origin_url == f"/{text.lower()}/",
|
|
||||||
accent_text_color,
|
|
||||||
text_color,
|
|
||||||
),
|
|
||||||
border_radius=border_radius,
|
|
||||||
box_shadow=box_shadow,
|
|
||||||
width="100%",
|
|
||||||
padding_x="1em",
|
|
||||||
),
|
|
||||||
href=url,
|
|
||||||
width="100%",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def sidebar() -> rx.Component:
|
|
||||||
"""Sidebar.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
rx.Component: The sidebar component.
|
|
||||||
"""
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
sidebar_header(),
|
|
||||||
rx.chakra.vstack(
|
|
||||||
sidebar_item(
|
|
||||||
"Welcome",
|
|
||||||
"/github.svg",
|
|
||||||
"/",
|
|
||||||
),
|
|
||||||
sidebar_item(
|
|
||||||
"Graphing Demo",
|
|
||||||
"/github.svg",
|
|
||||||
"/graphing",
|
|
||||||
),
|
|
||||||
sidebar_item(
|
|
||||||
"Data Table Demo",
|
|
||||||
"/github.svg",
|
|
||||||
"/datatable",
|
|
||||||
),
|
|
||||||
sidebar_item(
|
|
||||||
"Forms Demo",
|
|
||||||
"/github.svg",
|
|
||||||
"/forms",
|
|
||||||
),
|
|
||||||
sidebar_item(
|
|
||||||
"Chat App Demo",
|
|
||||||
"/github.svg",
|
|
||||||
"/chatapp",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
overflow_y="auto",
|
|
||||||
align_items="flex-start",
|
|
||||||
padding="1em",
|
|
||||||
),
|
|
||||||
rx.chakra.spacer(),
|
|
||||||
sidebar_footer(),
|
|
||||||
height="100dvh",
|
|
||||||
),
|
|
||||||
display=["none", "none", "block"],
|
|
||||||
min_width=sidebar_width,
|
|
||||||
height="100%",
|
|
||||||
position="sticky",
|
|
||||||
top="0px",
|
|
||||||
border_right=border,
|
|
||||||
)
|
|
@ -1,22 +0,0 @@
|
|||||||
"""Base state for the app."""
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
|
|
||||||
class State(rx.State):
|
|
||||||
"""State for the app."""
|
|
||||||
|
|
||||||
sidebar_displayed: bool = True
|
|
||||||
|
|
||||||
@rx.var
|
|
||||||
def origin_url(self) -> str:
|
|
||||||
"""Get the url of the current page.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: The url of the current page.
|
|
||||||
"""
|
|
||||||
return self.router_data.get("asPath", "")
|
|
||||||
|
|
||||||
def toggle_sidebar_displayed(self) -> None:
|
|
||||||
"""Toggle the sidebar displayed."""
|
|
||||||
self.sidebar_displayed = not self.sidebar_displayed
|
|
@ -1,40 +0,0 @@
|
|||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ..state import State
|
|
||||||
|
|
||||||
|
|
||||||
class FormState(State):
|
|
||||||
"""Form state."""
|
|
||||||
|
|
||||||
form_data: dict = {}
|
|
||||||
|
|
||||||
def handle_submit(self, form_data: dict):
|
|
||||||
"""Handle the form submit.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
form_data: The form data.
|
|
||||||
"""
|
|
||||||
self.form_data = form_data
|
|
||||||
|
|
||||||
|
|
||||||
class UploadState(State):
|
|
||||||
"""The app state."""
|
|
||||||
|
|
||||||
# The images to show.
|
|
||||||
img: list[str]
|
|
||||||
|
|
||||||
async def handle_upload(self, files: list[rx.UploadFile]):
|
|
||||||
"""Handle the upload of file(s).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
files: The uploaded files.
|
|
||||||
"""
|
|
||||||
for file in files:
|
|
||||||
upload_data = await file.read()
|
|
||||||
outfile = rx.get_asset_path(file.filename)
|
|
||||||
# Save the file.
|
|
||||||
with open(outfile, "wb") as file_object:
|
|
||||||
file_object.write(upload_data)
|
|
||||||
|
|
||||||
# Update the img var.
|
|
||||||
self.img.append(f"/{file.filename}")
|
|
@ -1,47 +0,0 @@
|
|||||||
from typing import Any
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ..state import State
|
|
||||||
|
|
||||||
|
|
||||||
class PieChartState(State):
|
|
||||||
"""Pie Chart State."""
|
|
||||||
|
|
||||||
resources: list[dict[str, Any]] = [
|
|
||||||
dict(type_="🏆", count=1),
|
|
||||||
dict(type_="🪵", count=1),
|
|
||||||
dict(type_="🥑", count=1),
|
|
||||||
dict(type_="🧱", count=1),
|
|
||||||
]
|
|
||||||
|
|
||||||
@rx.cached_var
|
|
||||||
def resource_types(self) -> list[str]:
|
|
||||||
"""Get the resource types.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The resource types.
|
|
||||||
"""
|
|
||||||
return [r["type_"] for r in self.resources]
|
|
||||||
|
|
||||||
def increment(self, type_: str):
|
|
||||||
"""Increment the count of a resource type.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
type_: The type of resource to increment.
|
|
||||||
"""
|
|
||||||
for resource in self.resources:
|
|
||||||
if resource["type_"] == type_:
|
|
||||||
resource["count"] += 1
|
|
||||||
break
|
|
||||||
|
|
||||||
def decrement(self, type_: str):
|
|
||||||
"""Decrement the count of a resource type.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
type_: The type of resource to decrement.
|
|
||||||
"""
|
|
||||||
for resource in self.resources:
|
|
||||||
if resource["type_"] == type_ and resource["count"] > 0:
|
|
||||||
resource["count"] -= 1
|
|
||||||
break
|
|
@ -1,68 +0,0 @@
|
|||||||
"""Styles for the app."""
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from .state import State
|
|
||||||
|
|
||||||
border_radius = "0.375rem"
|
|
||||||
box_shadow = "0px 0px 0px 1px rgba(84, 82, 95, 0.14)"
|
|
||||||
border = "1px solid #F4F3F6"
|
|
||||||
text_color = "black"
|
|
||||||
accent_text_color = "#1A1060"
|
|
||||||
accent_color = "#F5EFFE"
|
|
||||||
hover_accent_color = {"_hover": {"color": accent_color}}
|
|
||||||
hover_accent_bg = {"_hover": {"bg": accent_color}}
|
|
||||||
content_width_vw = "90vw"
|
|
||||||
sidebar_width = "20em"
|
|
||||||
|
|
||||||
template_page_style = {
|
|
||||||
"padding_top": "5em",
|
|
||||||
"padding_x": "2em",
|
|
||||||
}
|
|
||||||
|
|
||||||
template_content_style = {
|
|
||||||
"width": rx.cond(
|
|
||||||
State.sidebar_displayed,
|
|
||||||
f"calc({content_width_vw} - {sidebar_width})",
|
|
||||||
content_width_vw,
|
|
||||||
),
|
|
||||||
"min-width": sidebar_width,
|
|
||||||
"align_items": "flex-start",
|
|
||||||
"box_shadow": box_shadow,
|
|
||||||
"border_radius": border_radius,
|
|
||||||
"padding": "1em",
|
|
||||||
"margin_bottom": "2em",
|
|
||||||
}
|
|
||||||
|
|
||||||
link_style = {
|
|
||||||
"color": text_color,
|
|
||||||
"text_decoration": "none",
|
|
||||||
**hover_accent_color,
|
|
||||||
}
|
|
||||||
|
|
||||||
overlapping_button_style = {
|
|
||||||
"background_color": "white",
|
|
||||||
"border": border,
|
|
||||||
"border_radius": border_radius,
|
|
||||||
}
|
|
||||||
|
|
||||||
base_style = {
|
|
||||||
rx.chakra.MenuButton: {
|
|
||||||
"width": "3em",
|
|
||||||
"height": "3em",
|
|
||||||
**overlapping_button_style,
|
|
||||||
},
|
|
||||||
rx.chakra.MenuItem: hover_accent_bg,
|
|
||||||
}
|
|
||||||
|
|
||||||
tab_style = {
|
|
||||||
"color": "#494369",
|
|
||||||
"font_weight": 600,
|
|
||||||
"_selected": {
|
|
||||||
"color": "#5646ED",
|
|
||||||
"bg": "#F5EFFE",
|
|
||||||
"padding_x": "0.5em",
|
|
||||||
"padding_y": "0.25em",
|
|
||||||
"border_radius": "8px",
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
from .loading_icon import loading_icon
|
|
||||||
from .modal import modal
|
|
||||||
from .navbar import navbar
|
|
||||||
from .sidebar import sidebar
|
|
@ -1,118 +0,0 @@
|
|||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ...webui import styles
|
|
||||||
from ...webui.components import loading_icon
|
|
||||||
from ...webui.state import QA, State
|
|
||||||
|
|
||||||
|
|
||||||
def message(qa: QA) -> rx.Component:
|
|
||||||
"""A single question/answer message.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
qa: The question/answer pair.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A component displaying the question/answer pair.
|
|
||||||
"""
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.box(
|
|
||||||
rx.chakra.text(
|
|
||||||
qa.question,
|
|
||||||
bg=styles.border_color,
|
|
||||||
shadow=styles.shadow_light,
|
|
||||||
**styles.message_style,
|
|
||||||
),
|
|
||||||
text_align="right",
|
|
||||||
margin_top="1em",
|
|
||||||
),
|
|
||||||
rx.chakra.box(
|
|
||||||
rx.chakra.text(
|
|
||||||
qa.answer,
|
|
||||||
bg=styles.accent_color,
|
|
||||||
shadow=styles.shadow_light,
|
|
||||||
**styles.message_style,
|
|
||||||
),
|
|
||||||
text_align="left",
|
|
||||||
padding_top="1em",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def chat() -> rx.Component:
|
|
||||||
"""List all the messages in a single conversation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A component displaying all the messages in a single conversation.
|
|
||||||
"""
|
|
||||||
return rx.chakra.vstack(
|
|
||||||
rx.chakra.box(rx.foreach(State.chats[State.current_chat], message)),
|
|
||||||
py="8",
|
|
||||||
flex="1",
|
|
||||||
width="100%",
|
|
||||||
max_w="3xl",
|
|
||||||
padding_x="4",
|
|
||||||
align_self="center",
|
|
||||||
overflow="hidden",
|
|
||||||
padding_bottom="5em",
|
|
||||||
**styles.base_style[rx.chakra.Vstack],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def action_bar() -> rx.Component:
|
|
||||||
"""The action bar to send a new message.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The action bar to send a new message.
|
|
||||||
"""
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.chakra.form(
|
|
||||||
rx.chakra.form_control(
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.input(
|
|
||||||
placeholder="Type something...",
|
|
||||||
value=State.question,
|
|
||||||
on_change=State.set_question,
|
|
||||||
_placeholder={"color": "#fffa"},
|
|
||||||
_hover={"border_color": styles.accent_color},
|
|
||||||
style=styles.input_style,
|
|
||||||
),
|
|
||||||
rx.chakra.button(
|
|
||||||
rx.cond(
|
|
||||||
State.processing,
|
|
||||||
loading_icon(height="1em"),
|
|
||||||
rx.chakra.text("Send"),
|
|
||||||
),
|
|
||||||
type_="submit",
|
|
||||||
_hover={"bg": styles.accent_color},
|
|
||||||
style=styles.input_style,
|
|
||||||
),
|
|
||||||
**styles.base_style[rx.chakra.Hstack],
|
|
||||||
),
|
|
||||||
is_disabled=State.processing,
|
|
||||||
),
|
|
||||||
on_submit=State.process_question,
|
|
||||||
width="100%",
|
|
||||||
),
|
|
||||||
rx.chakra.text(
|
|
||||||
"ReflexGPT may return factually incorrect or misleading responses. Use discretion.",
|
|
||||||
font_size="xs",
|
|
||||||
color="#fff6",
|
|
||||||
text_align="center",
|
|
||||||
),
|
|
||||||
width="100%",
|
|
||||||
max_w="3xl",
|
|
||||||
mx="auto",
|
|
||||||
**styles.base_style[rx.chakra.Vstack],
|
|
||||||
),
|
|
||||||
position="sticky",
|
|
||||||
bottom="0",
|
|
||||||
left="0",
|
|
||||||
py="4",
|
|
||||||
backdrop_filter="auto",
|
|
||||||
backdrop_blur="lg",
|
|
||||||
border_top=f"1px solid {styles.border_color}",
|
|
||||||
align_items="stretch",
|
|
||||||
width="100%",
|
|
||||||
)
|
|
@ -1,19 +0,0 @@
|
|||||||
import reflex as rx
|
|
||||||
|
|
||||||
|
|
||||||
class LoadingIcon(rx.Component):
|
|
||||||
"""A custom loading icon component."""
|
|
||||||
|
|
||||||
library = "react-loading-icons"
|
|
||||||
tag = "SpinningCircles"
|
|
||||||
stroke: rx.Var[str]
|
|
||||||
stroke_opacity: rx.Var[str]
|
|
||||||
fill: rx.Var[str]
|
|
||||||
fill_opacity: rx.Var[str]
|
|
||||||
stroke_width: rx.Var[str]
|
|
||||||
speed: rx.Var[str]
|
|
||||||
height: rx.Var[str]
|
|
||||||
on_change: rx.EventHandler[lambda status: [status]]
|
|
||||||
|
|
||||||
|
|
||||||
loading_icon = LoadingIcon.create
|
|
@ -1,56 +0,0 @@
|
|||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ...webui.state import State
|
|
||||||
|
|
||||||
|
|
||||||
def modal() -> rx.Component:
|
|
||||||
"""A modal to create a new chat.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The modal component.
|
|
||||||
"""
|
|
||||||
return rx.chakra.modal(
|
|
||||||
rx.chakra.modal_overlay(
|
|
||||||
rx.chakra.modal_content(
|
|
||||||
rx.chakra.modal_header(
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.text("Create new chat"),
|
|
||||||
rx.chakra.icon(
|
|
||||||
tag="close",
|
|
||||||
font_size="sm",
|
|
||||||
on_click=State.toggle_modal,
|
|
||||||
color="#fff8",
|
|
||||||
_hover={"color": "#fff"},
|
|
||||||
cursor="pointer",
|
|
||||||
),
|
|
||||||
align_items="center",
|
|
||||||
justify_content="space-between",
|
|
||||||
)
|
|
||||||
),
|
|
||||||
rx.chakra.modal_body(
|
|
||||||
rx.chakra.input(
|
|
||||||
placeholder="Type something...",
|
|
||||||
on_blur=State.set_new_chat_name,
|
|
||||||
bg="#222",
|
|
||||||
border_color="#fff3",
|
|
||||||
_placeholder={"color": "#fffa"},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.modal_footer(
|
|
||||||
rx.chakra.button(
|
|
||||||
"Create",
|
|
||||||
bg="#5535d4",
|
|
||||||
box_shadow="md",
|
|
||||||
px="4",
|
|
||||||
py="2",
|
|
||||||
h="auto",
|
|
||||||
_hover={"bg": "#4c2db3"},
|
|
||||||
on_click=State.create_chat,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
bg="#222",
|
|
||||||
color="#fff",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
is_open=State.modal_open,
|
|
||||||
)
|
|
@ -1,70 +0,0 @@
|
|||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ...webui import styles
|
|
||||||
from ...webui.state import State
|
|
||||||
|
|
||||||
|
|
||||||
def navbar():
|
|
||||||
return rx.chakra.box(
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.icon(
|
|
||||||
tag="hamburger",
|
|
||||||
mr=4,
|
|
||||||
on_click=State.toggle_drawer,
|
|
||||||
cursor="pointer",
|
|
||||||
),
|
|
||||||
rx.chakra.link(
|
|
||||||
rx.chakra.box(
|
|
||||||
rx.chakra.image(src="favicon.ico", width=30, height="auto"),
|
|
||||||
p="1",
|
|
||||||
border_radius="6",
|
|
||||||
bg="#F0F0F0",
|
|
||||||
mr="2",
|
|
||||||
),
|
|
||||||
href="/",
|
|
||||||
),
|
|
||||||
rx.chakra.breadcrumb(
|
|
||||||
rx.chakra.breadcrumb_item(
|
|
||||||
rx.chakra.heading("ReflexGPT", size="sm"),
|
|
||||||
),
|
|
||||||
rx.chakra.breadcrumb_item(
|
|
||||||
rx.chakra.text(
|
|
||||||
State.current_chat, size="sm", font_weight="normal"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.button(
|
|
||||||
"+ New chat",
|
|
||||||
bg=styles.accent_color,
|
|
||||||
px="4",
|
|
||||||
py="2",
|
|
||||||
h="auto",
|
|
||||||
on_click=State.toggle_modal,
|
|
||||||
),
|
|
||||||
rx.chakra.menu(
|
|
||||||
rx.chakra.menu_button(
|
|
||||||
rx.chakra.avatar(name="User", size="md"),
|
|
||||||
rx.chakra.box(),
|
|
||||||
),
|
|
||||||
rx.chakra.menu_list(
|
|
||||||
rx.chakra.menu_item("Help"),
|
|
||||||
rx.chakra.menu_divider(),
|
|
||||||
rx.chakra.menu_item("Settings"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
spacing="8",
|
|
||||||
),
|
|
||||||
justify="space-between",
|
|
||||||
),
|
|
||||||
bg=styles.bg_dark_color,
|
|
||||||
backdrop_filter="auto",
|
|
||||||
backdrop_blur="lg",
|
|
||||||
p="4",
|
|
||||||
border_bottom=f"1px solid {styles.border_color}",
|
|
||||||
position="sticky",
|
|
||||||
top="0",
|
|
||||||
z_index="100",
|
|
||||||
)
|
|
@ -1,66 +0,0 @@
|
|||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ...webui import styles
|
|
||||||
from ...webui.state import State
|
|
||||||
|
|
||||||
|
|
||||||
def sidebar_chat(chat: str) -> rx.Component:
|
|
||||||
"""A sidebar chat item.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
chat: The chat item.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The sidebar chat item.
|
|
||||||
"""
|
|
||||||
return rx.chakra.hstack(
|
|
||||||
rx.chakra.box(
|
|
||||||
chat,
|
|
||||||
on_click=lambda: State.set_chat(chat),
|
|
||||||
style=styles.sidebar_style,
|
|
||||||
color=styles.icon_color,
|
|
||||||
flex="1",
|
|
||||||
),
|
|
||||||
rx.chakra.box(
|
|
||||||
rx.chakra.icon(
|
|
||||||
tag="delete",
|
|
||||||
style=styles.icon_style,
|
|
||||||
on_click=State.delete_chat,
|
|
||||||
),
|
|
||||||
style=styles.sidebar_style,
|
|
||||||
),
|
|
||||||
color=styles.text_light_color,
|
|
||||||
cursor="pointer",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def sidebar() -> rx.Component:
|
|
||||||
"""The sidebar component.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The sidebar component.
|
|
||||||
"""
|
|
||||||
return rx.chakra.drawer(
|
|
||||||
rx.chakra.drawer_overlay(
|
|
||||||
rx.chakra.drawer_content(
|
|
||||||
rx.chakra.drawer_header(
|
|
||||||
rx.chakra.hstack(
|
|
||||||
rx.chakra.text("Chats"),
|
|
||||||
rx.chakra.icon(
|
|
||||||
tag="close",
|
|
||||||
on_click=State.toggle_drawer,
|
|
||||||
style=styles.icon_style,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
rx.chakra.drawer_body(
|
|
||||||
rx.chakra.vstack(
|
|
||||||
rx.foreach(State.chat_titles, lambda chat: sidebar_chat(chat)),
|
|
||||||
align_items="stretch",
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
placement="left",
|
|
||||||
is_open=State.drawer_open,
|
|
||||||
)
|
|
@ -1,146 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
import reflex as rx
|
|
||||||
|
|
||||||
from ..state import State
|
|
||||||
|
|
||||||
# openai.api_key = os.environ["OPENAI_API_KEY"]
|
|
||||||
# openai.api_base = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
|
|
||||||
|
|
||||||
|
|
||||||
class QA(rx.Base):
|
|
||||||
"""A question and answer pair."""
|
|
||||||
|
|
||||||
question: str
|
|
||||||
answer: str
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_CHATS = {
|
|
||||||
"Intros": [],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class State(State):
|
|
||||||
"""The app state."""
|
|
||||||
|
|
||||||
# A dict from the chat name to the list of questions and answers.
|
|
||||||
chats: dict[str, list[QA]] = DEFAULT_CHATS
|
|
||||||
|
|
||||||
# The current chat name.
|
|
||||||
current_chat = "Intros"
|
|
||||||
|
|
||||||
# The current question.
|
|
||||||
question: str
|
|
||||||
|
|
||||||
# Whether we are processing the question.
|
|
||||||
processing: bool = False
|
|
||||||
|
|
||||||
# The name of the new chat.
|
|
||||||
new_chat_name: str = ""
|
|
||||||
|
|
||||||
# Whether the drawer is open.
|
|
||||||
drawer_open: bool = False
|
|
||||||
|
|
||||||
# Whether the modal is open.
|
|
||||||
modal_open: bool = False
|
|
||||||
|
|
||||||
def create_chat(self):
|
|
||||||
"""Create a new chat."""
|
|
||||||
# Add the new chat to the list of chats.
|
|
||||||
self.current_chat = self.new_chat_name
|
|
||||||
self.chats[self.new_chat_name] = []
|
|
||||||
|
|
||||||
# Toggle the modal.
|
|
||||||
self.modal_open = False
|
|
||||||
|
|
||||||
def toggle_modal(self):
|
|
||||||
"""Toggle the new chat modal."""
|
|
||||||
self.modal_open = not self.modal_open
|
|
||||||
|
|
||||||
def toggle_drawer(self):
|
|
||||||
"""Toggle the drawer."""
|
|
||||||
self.drawer_open = not self.drawer_open
|
|
||||||
|
|
||||||
def delete_chat(self):
|
|
||||||
"""Delete the current chat."""
|
|
||||||
del self.chats[self.current_chat]
|
|
||||||
if len(self.chats) == 0:
|
|
||||||
self.chats = DEFAULT_CHATS
|
|
||||||
# set self.current_chat to the first chat.
|
|
||||||
self.current_chat = next(iter(self.chats))
|
|
||||||
self.toggle_drawer()
|
|
||||||
|
|
||||||
def set_chat(self, chat_name: str):
|
|
||||||
"""Set the name of the current chat.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
chat_name: The name of the chat.
|
|
||||||
"""
|
|
||||||
self.current_chat = chat_name
|
|
||||||
self.toggle_drawer()
|
|
||||||
|
|
||||||
@rx.var
|
|
||||||
def chat_titles(self) -> list[str]:
|
|
||||||
"""Get the list of chat titles.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The list of chat names.
|
|
||||||
"""
|
|
||||||
return [*self.chats]
|
|
||||||
|
|
||||||
async def process_question(self, form_data: dict[str, str]):
|
|
||||||
"""Get the response from the API.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
form_data: A dict with the current question.
|
|
||||||
|
|
||||||
Yields:
|
|
||||||
The current question and the response.
|
|
||||||
"""
|
|
||||||
# Check if the question is empty
|
|
||||||
if self.question == "":
|
|
||||||
return
|
|
||||||
|
|
||||||
# Add the question to the list of questions.
|
|
||||||
qa = QA(question=self.question, answer="")
|
|
||||||
self.chats[self.current_chat].append(qa)
|
|
||||||
|
|
||||||
# Clear the input and start the processing.
|
|
||||||
self.processing = True
|
|
||||||
self.question = ""
|
|
||||||
yield
|
|
||||||
|
|
||||||
# # Build the messages.
|
|
||||||
# messages = [
|
|
||||||
# {"role": "system", "content": "You are a friendly chatbot named Reflex."}
|
|
||||||
# ]
|
|
||||||
# for qa in self.chats[self.current_chat]:
|
|
||||||
# messages.append({"role": "user", "content": qa.question})
|
|
||||||
# messages.append({"role": "assistant", "content": qa.answer})
|
|
||||||
|
|
||||||
# # Remove the last mock answer.
|
|
||||||
# messages = messages[:-1]
|
|
||||||
|
|
||||||
# Start a new session to answer the question.
|
|
||||||
# session = openai.ChatCompletion.create(
|
|
||||||
# model=os.getenv("OPENAI_MODEL", "gpt-3.5-turbo"),
|
|
||||||
# messages=messages,
|
|
||||||
# stream=True,
|
|
||||||
# )
|
|
||||||
|
|
||||||
# Stream the results, yielding after every word.
|
|
||||||
# for item in session:
|
|
||||||
answer = "I don't know! This Chatbot still needs to add in AI API keys!"
|
|
||||||
for i in range(len(answer)):
|
|
||||||
# Pause to show the streaming effect.
|
|
||||||
await asyncio.sleep(0.1)
|
|
||||||
# Add one letter at a time to the output.
|
|
||||||
|
|
||||||
# if hasattr(item.choices[0].delta, "content"):
|
|
||||||
# answer_text = item.choices[0].delta.content
|
|
||||||
self.chats[self.current_chat][-1].answer += answer[i]
|
|
||||||
self.chats = self.chats
|
|
||||||
yield
|
|
||||||
|
|
||||||
# Toggle the processing flag.
|
|
||||||
self.processing = False
|
|
@ -1,88 +0,0 @@
|
|||||||
import reflex as rx
|
|
||||||
|
|
||||||
bg_dark_color = "#111"
|
|
||||||
bg_medium_color = "#222"
|
|
||||||
|
|
||||||
border_color = "#fff3"
|
|
||||||
|
|
||||||
accennt_light = "#6649D8"
|
|
||||||
accent_color = "#5535d4"
|
|
||||||
accent_dark = "#4c2db3"
|
|
||||||
|
|
||||||
icon_color = "#fff8"
|
|
||||||
|
|
||||||
text_light_color = "#fff"
|
|
||||||
shadow_light = "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px;"
|
|
||||||
shadow = "rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px, rgba(10, 37, 64, 0.35) 0px -2px 6px 0px inset;"
|
|
||||||
|
|
||||||
message_style = dict(display="inline-block", p="4", border_radius="xl", max_w="30em")
|
|
||||||
|
|
||||||
input_style = dict(
|
|
||||||
bg=bg_medium_color,
|
|
||||||
border_color=border_color,
|
|
||||||
border_width="1px",
|
|
||||||
p="4",
|
|
||||||
)
|
|
||||||
|
|
||||||
icon_style = dict(
|
|
||||||
font_size="md",
|
|
||||||
color=icon_color,
|
|
||||||
_hover=dict(color=text_light_color),
|
|
||||||
cursor="pointer",
|
|
||||||
w="8",
|
|
||||||
)
|
|
||||||
|
|
||||||
sidebar_style = dict(
|
|
||||||
border="double 1px transparent;",
|
|
||||||
border_radius="10px;",
|
|
||||||
background_image=f"linear-gradient({bg_dark_color}, {bg_dark_color}), radial-gradient(circle at top left, {accent_color},{accent_dark});",
|
|
||||||
background_origin="border-box;",
|
|
||||||
background_clip="padding-box, border-box;",
|
|
||||||
p="2",
|
|
||||||
_hover=dict(
|
|
||||||
background_image=f"linear-gradient({bg_dark_color}, {bg_dark_color}), radial-gradient(circle at top left, {accent_color},{accennt_light});",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
base_style = {
|
|
||||||
rx.chakra.Avatar: {
|
|
||||||
"shadow": shadow,
|
|
||||||
"color": text_light_color,
|
|
||||||
# "bg": border_color,
|
|
||||||
},
|
|
||||||
rx.chakra.Button: {
|
|
||||||
"shadow": shadow,
|
|
||||||
"color": text_light_color,
|
|
||||||
"_hover": {
|
|
||||||
"bg": accent_dark,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
rx.chakra.Menu: {
|
|
||||||
"bg": bg_dark_color,
|
|
||||||
"border": f"red",
|
|
||||||
},
|
|
||||||
rx.chakra.MenuList: {
|
|
||||||
"bg": bg_dark_color,
|
|
||||||
"border": f"1.5px solid {bg_medium_color}",
|
|
||||||
},
|
|
||||||
rx.chakra.MenuDivider: {
|
|
||||||
"border": f"1px solid {bg_medium_color}",
|
|
||||||
},
|
|
||||||
rx.chakra.MenuItem: {
|
|
||||||
"bg": bg_dark_color,
|
|
||||||
"color": text_light_color,
|
|
||||||
},
|
|
||||||
rx.chakra.DrawerContent: {
|
|
||||||
"bg": bg_dark_color,
|
|
||||||
"color": text_light_color,
|
|
||||||
"opacity": "0.9",
|
|
||||||
},
|
|
||||||
rx.chakra.Hstack: {
|
|
||||||
"align_items": "center",
|
|
||||||
"justify_content": "space-between",
|
|
||||||
},
|
|
||||||
rx.chakra.Vstack: {
|
|
||||||
"align_items": "stretch",
|
|
||||||
"justify_content": "space-between",
|
|
||||||
},
|
|
||||||
}
|
|
@ -140,6 +140,7 @@ RADIX_THEMES_COMPONENTS_MAPPING: dict = {
|
|||||||
"components.radix.themes.components.radio_group": ["radio", "radio_group"],
|
"components.radix.themes.components.radio_group": ["radio", "radio_group"],
|
||||||
"components.radix.themes.components.dropdown_menu": ["menu", "dropdown_menu"],
|
"components.radix.themes.components.dropdown_menu": ["menu", "dropdown_menu"],
|
||||||
"components.radix.themes.components.separator": ["divider", "separator"],
|
"components.radix.themes.components.separator": ["divider", "separator"],
|
||||||
|
"components.radix.themes.components.progress": ["progress"],
|
||||||
}
|
}
|
||||||
|
|
||||||
RADIX_THEMES_LAYOUT_MAPPING: dict = {
|
RADIX_THEMES_LAYOUT_MAPPING: dict = {
|
||||||
@ -205,7 +206,6 @@ RADIX_PRIMITIVES_MAPPING: dict = {
|
|||||||
"components.radix.primitives.form": [
|
"components.radix.primitives.form": [
|
||||||
"form",
|
"form",
|
||||||
],
|
],
|
||||||
"components.radix.primitives.progress": ["progress"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
COMPONENTS_CORE_MAPPING: dict = {
|
COMPONENTS_CORE_MAPPING: dict = {
|
||||||
|
@ -71,7 +71,6 @@ from .components.plotly import plotly as plotly
|
|||||||
from .components.radix.primitives.accordion import accordion as accordion
|
from .components.radix.primitives.accordion import accordion as accordion
|
||||||
from .components.radix.primitives.drawer import drawer as drawer
|
from .components.radix.primitives.drawer import drawer as drawer
|
||||||
from .components.radix.primitives.form import form as form
|
from .components.radix.primitives.form import form as form
|
||||||
from .components.radix.primitives.progress import progress as progress
|
|
||||||
from .components.radix.themes.base import theme as theme
|
from .components.radix.themes.base import theme as theme
|
||||||
from .components.radix.themes.base import theme_panel as theme_panel
|
from .components.radix.themes.base import theme_panel as theme_panel
|
||||||
from .components.radix.themes.color_mode import color_mode as color_mode
|
from .components.radix.themes.color_mode import color_mode as color_mode
|
||||||
@ -106,6 +105,7 @@ from .components.radix.themes.components.hover_card import hover_card as hover_c
|
|||||||
from .components.radix.themes.components.icon_button import icon_button as icon_button
|
from .components.radix.themes.components.icon_button import icon_button as icon_button
|
||||||
from .components.radix.themes.components.inset import inset as inset
|
from .components.radix.themes.components.inset import inset as inset
|
||||||
from .components.radix.themes.components.popover import popover as popover
|
from .components.radix.themes.components.popover import popover as popover
|
||||||
|
from .components.radix.themes.components.progress import progress as progress
|
||||||
from .components.radix.themes.components.radio_cards import radio_cards as radio_cards
|
from .components.radix.themes.components.radio_cards import radio_cards as radio_cards
|
||||||
from .components.radix.themes.components.radio_group import radio as radio
|
from .components.radix.themes.components.radio_group import radio as radio
|
||||||
from .components.radix.themes.components.radio_group import radio_group as radio_group
|
from .components.radix.themes.components.radio_group import radio_group as radio_group
|
||||||
|
@ -33,7 +33,7 @@ from typing import (
|
|||||||
|
|
||||||
from fastapi import FastAPI, HTTPException, Request, UploadFile
|
from fastapi import FastAPI, HTTPException, Request, UploadFile
|
||||||
from fastapi.middleware import cors
|
from fastapi.middleware import cors
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import JSONResponse, StreamingResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
||||||
from socketio import ASGIApp, AsyncNamespace, AsyncServer
|
from socketio import ASGIApp, AsyncNamespace, AsyncServer
|
||||||
@ -65,7 +65,7 @@ from reflex.components.core.upload import Upload, get_upload_dir
|
|||||||
from reflex.components.radix import themes
|
from reflex.components.radix import themes
|
||||||
from reflex.config import get_config
|
from reflex.config import get_config
|
||||||
from reflex.event import Event, EventHandler, EventSpec, window_alert
|
from reflex.event import Event, EventHandler, EventSpec, window_alert
|
||||||
from reflex.model import Model
|
from reflex.model import Model, get_db_status
|
||||||
from reflex.page import (
|
from reflex.page import (
|
||||||
DECORATED_PAGES,
|
DECORATED_PAGES,
|
||||||
)
|
)
|
||||||
@ -377,6 +377,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|||||||
"""Add default api endpoints (ping)."""
|
"""Add default api endpoints (ping)."""
|
||||||
# To test the server.
|
# To test the server.
|
||||||
self.api.get(str(constants.Endpoint.PING))(ping)
|
self.api.get(str(constants.Endpoint.PING))(ping)
|
||||||
|
self.api.get(str(constants.Endpoint.HEALTH))(health)
|
||||||
|
|
||||||
def _add_optional_endpoints(self):
|
def _add_optional_endpoints(self):
|
||||||
"""Add optional api endpoints (_upload)."""
|
"""Add optional api endpoints (_upload)."""
|
||||||
@ -1319,6 +1320,38 @@ async def ping() -> str:
|
|||||||
return "pong"
|
return "pong"
|
||||||
|
|
||||||
|
|
||||||
|
async def health() -> JSONResponse:
|
||||||
|
"""Health check endpoint to assess the status of the database and Redis services.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSONResponse: A JSON object with the health status:
|
||||||
|
- "status" (bool): Overall health, True if all checks pass.
|
||||||
|
- "db" (bool or str): Database status - True, False, or "NA".
|
||||||
|
- "redis" (bool or str): Redis status - True, False, or "NA".
|
||||||
|
"""
|
||||||
|
health_status = {"status": True}
|
||||||
|
status_code = 200
|
||||||
|
|
||||||
|
db_status, redis_status = await asyncio.gather(
|
||||||
|
get_db_status(), prerequisites.get_redis_status()
|
||||||
|
)
|
||||||
|
|
||||||
|
health_status["db"] = db_status
|
||||||
|
|
||||||
|
if redis_status is None:
|
||||||
|
health_status["redis"] = False
|
||||||
|
else:
|
||||||
|
health_status["redis"] = redis_status
|
||||||
|
|
||||||
|
if not health_status["db"] or (
|
||||||
|
not health_status["redis"] and redis_status is not None
|
||||||
|
):
|
||||||
|
health_status["status"] = False
|
||||||
|
status_code = 503
|
||||||
|
|
||||||
|
return JSONResponse(content=health_status, status_code=status_code)
|
||||||
|
|
||||||
|
|
||||||
def upload(app: App):
|
def upload(app: App):
|
||||||
"""Upload a file.
|
"""Upload a file.
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class Bare(Component):
|
|||||||
"""
|
"""
|
||||||
if isinstance(contents, ImmutableVar):
|
if isinstance(contents, ImmutableVar):
|
||||||
return cls(contents=contents)
|
return cls(contents=contents)
|
||||||
if isinstance(contents, Var) and contents._get_all_var_data():
|
if isinstance(contents, Var):
|
||||||
contents = contents.to(str)
|
contents = contents.to(str)
|
||||||
else:
|
else:
|
||||||
contents = str(contents) if contents is not None else ""
|
contents = str(contents) if contents is not None else ""
|
||||||
|
@ -45,7 +45,7 @@ from reflex.event import (
|
|||||||
)
|
)
|
||||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||||
from reflex.style import Style, format_as_emotion
|
from reflex.style import Style, format_as_emotion
|
||||||
from reflex.utils import console, format, imports, types
|
from reflex.utils import format, imports, types
|
||||||
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
||||||
from reflex.utils.serializers import serializer
|
from reflex.utils.serializers import serializer
|
||||||
from reflex.vars import BaseVar, ImmutableVarData, Var, VarData
|
from reflex.vars import BaseVar, ImmutableVarData, Var, VarData
|
||||||
@ -527,15 +527,7 @@ class Component(BaseComponent, ABC):
|
|||||||
for v in value:
|
for v in value:
|
||||||
if isinstance(v, (EventHandler, EventSpec)):
|
if isinstance(v, (EventHandler, EventSpec)):
|
||||||
# Call the event handler to get the event.
|
# Call the event handler to get the event.
|
||||||
try:
|
events.append(call_event_handler(v, args_spec))
|
||||||
event = call_event_handler(v, args_spec)
|
|
||||||
except ValueError as err:
|
|
||||||
raise ValueError(
|
|
||||||
f" {err} defined in the `{type(self).__name__}` component"
|
|
||||||
) from err
|
|
||||||
|
|
||||||
# Add the event to the chain.
|
|
||||||
events.append(event)
|
|
||||||
elif isinstance(v, Callable):
|
elif isinstance(v, Callable):
|
||||||
# Call the lambda to get the event chain.
|
# Call the lambda to get the event chain.
|
||||||
result = call_event_fn(v, args_spec)
|
result = call_event_fn(v, args_spec)
|
||||||
@ -636,27 +628,6 @@ class Component(BaseComponent, ABC):
|
|||||||
|
|
||||||
return _compile_component(self)
|
return _compile_component(self)
|
||||||
|
|
||||||
def _apply_theme(self, theme: Optional[Component]):
|
|
||||||
"""Apply the theme to this component.
|
|
||||||
|
|
||||||
Deprecated. Use add_style instead.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
theme: The theme to apply.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def apply_theme(self, theme: Optional[Component]):
|
|
||||||
"""Apply a theme to the component and its children.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
theme: The theme to apply.
|
|
||||||
"""
|
|
||||||
self._apply_theme(theme)
|
|
||||||
for child in self.children:
|
|
||||||
if isinstance(child, Component):
|
|
||||||
child.apply_theme(theme)
|
|
||||||
|
|
||||||
def _exclude_props(self) -> list[str]:
|
def _exclude_props(self) -> list[str]:
|
||||||
"""Props to exclude when adding the component props to the Tag.
|
"""Props to exclude when adding the component props to the Tag.
|
||||||
|
|
||||||
@ -764,22 +735,6 @@ class Component(BaseComponent, ABC):
|
|||||||
from reflex.components.base.fragment import Fragment
|
from reflex.components.base.fragment import Fragment
|
||||||
from reflex.utils.exceptions import ComponentTypeError
|
from reflex.utils.exceptions import ComponentTypeError
|
||||||
|
|
||||||
# Translate deprecated props to new names.
|
|
||||||
new_prop_names = [
|
|
||||||
prop for prop in cls.get_props() if prop in ["type", "min", "max"]
|
|
||||||
]
|
|
||||||
for prop in new_prop_names:
|
|
||||||
under_prop = f"{prop}_"
|
|
||||||
if under_prop in props:
|
|
||||||
console.deprecate(
|
|
||||||
f"Underscore suffix for prop `{under_prop}`",
|
|
||||||
reason=f"for consistency. Use `{prop}` instead.",
|
|
||||||
deprecation_version="0.4.0",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
dedupe=False,
|
|
||||||
)
|
|
||||||
props[prop] = props.pop(under_prop)
|
|
||||||
|
|
||||||
# Filter out None props
|
# Filter out None props
|
||||||
props = {key: value for key, value in props.items() if value is not None}
|
props = {key: value for key, value in props.items() if value is not None}
|
||||||
|
|
||||||
@ -896,17 +851,6 @@ class Component(BaseComponent, ABC):
|
|||||||
new_style.update(component_style)
|
new_style.update(component_style)
|
||||||
style_vars.append(component_style._var_data)
|
style_vars.append(component_style._var_data)
|
||||||
|
|
||||||
# 3. User-defined style from `Component.style`.
|
|
||||||
# Apply theme for retro-compatibility with deprecated _apply_theme API
|
|
||||||
if type(self)._apply_theme != Component._apply_theme:
|
|
||||||
console.deprecate(
|
|
||||||
f"{self.__class__.__name__}._apply_theme",
|
|
||||||
reason="use add_style instead",
|
|
||||||
deprecation_version="0.5.0",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
)
|
|
||||||
self._apply_theme(theme)
|
|
||||||
|
|
||||||
# 4. style dict and css props passed to the component instance.
|
# 4. style dict and css props passed to the component instance.
|
||||||
new_style.update(self.style)
|
new_style.update(self.style)
|
||||||
style_vars.append(self.style._var_data)
|
style_vars.append(self.style._var_data)
|
||||||
|
@ -9,7 +9,7 @@ from reflex.components.component import BaseComponent, Component, MemoizationLea
|
|||||||
from reflex.components.tags import CondTag, Tag
|
from reflex.components.tags import CondTag, Tag
|
||||||
from reflex.constants import Dirs
|
from reflex.constants import Dirs
|
||||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||||
from reflex.ivars.number import TernaryOperator
|
from reflex.ivars.number import ternary_operation
|
||||||
from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode
|
from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode
|
||||||
from reflex.utils.imports import ImportDict, ImportVar
|
from reflex.utils.imports import ImportDict, ImportVar
|
||||||
from reflex.vars import Var, VarData
|
from reflex.vars import Var, VarData
|
||||||
@ -163,11 +163,12 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | ImmutableVar:
|
|||||||
c2 = create_var(c2)
|
c2 = create_var(c2)
|
||||||
|
|
||||||
# Create the conditional var.
|
# Create the conditional var.
|
||||||
return TernaryOperator.create(
|
return ternary_operation(
|
||||||
condition=cond_var.to(bool), # type: ignore
|
cond_var.bool()._replace( # type: ignore
|
||||||
if_true=c1,
|
merge_var_data=VarData(imports=_IS_TRUE_IMPORT),
|
||||||
if_false=c2,
|
), # type: ignore
|
||||||
_var_data=VarData(imports=_IS_TRUE_IMPORT),
|
c1,
|
||||||
|
c2,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ from reflex.components.tags import IterTag
|
|||||||
from reflex.constants import MemoizationMode
|
from reflex.constants import MemoizationMode
|
||||||
from reflex.ivars.base import ImmutableVar
|
from reflex.ivars.base import ImmutableVar
|
||||||
from reflex.state import ComponentState
|
from reflex.state import ComponentState
|
||||||
from reflex.utils import console
|
|
||||||
from reflex.vars import Var
|
from reflex.vars import Var
|
||||||
|
|
||||||
|
|
||||||
@ -39,14 +38,12 @@ class Foreach(Component):
|
|||||||
cls,
|
cls,
|
||||||
iterable: Var[Iterable] | Iterable,
|
iterable: Var[Iterable] | Iterable,
|
||||||
render_fn: Callable,
|
render_fn: Callable,
|
||||||
**props,
|
|
||||||
) -> Foreach:
|
) -> Foreach:
|
||||||
"""Create a foreach component.
|
"""Create a foreach component.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
iterable: The iterable to create components from.
|
iterable: The iterable to create components from.
|
||||||
render_fn: A function from the render args to the component.
|
render_fn: A function from the render args to the component.
|
||||||
**props: The attributes to pass to each child component (deprecated).
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The foreach component.
|
The foreach component.
|
||||||
@ -55,13 +52,6 @@ class Foreach(Component):
|
|||||||
ForeachVarError: If the iterable is of type Any.
|
ForeachVarError: If the iterable is of type Any.
|
||||||
TypeError: If the render function is a ComponentState.
|
TypeError: If the render function is a ComponentState.
|
||||||
"""
|
"""
|
||||||
if props:
|
|
||||||
console.deprecate(
|
|
||||||
feature_name="Passing props to rx.foreach",
|
|
||||||
reason="it does not have the intended effect and may be confusing",
|
|
||||||
deprecation_version="0.5.0",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
)
|
|
||||||
iterable = ImmutableVar.create_safe(iterable)
|
iterable = ImmutableVar.create_safe(iterable)
|
||||||
if iterable._var_type == Any:
|
if iterable._var_type == Any:
|
||||||
raise ForeachVarError(
|
raise ForeachVarError(
|
||||||
|
@ -10,7 +10,6 @@ _SUBMODULES: set[str] = {"elements"}
|
|||||||
_SUBMOD_ATTRS: dict[str, list[str]] = {
|
_SUBMOD_ATTRS: dict[str, list[str]] = {
|
||||||
f"elements.{k}": v for k, v in elements._MAPPING.items()
|
f"elements.{k}": v for k, v in elements._MAPPING.items()
|
||||||
}
|
}
|
||||||
_PYRIGHT_IGNORE_IMPORTS = elements._PYRIGHT_IGNORE_IMPORTS
|
|
||||||
|
|
||||||
__getattr__, __dir__, __all__ = lazy_loader.attach(
|
__getattr__, __dir__, __all__ = lazy_loader.attach(
|
||||||
__name__,
|
__name__,
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
from . import elements
|
|
||||||
from .elements.forms import Button as Button
|
from .elements.forms import Button as Button
|
||||||
from .elements.forms import Fieldset as Fieldset
|
from .elements.forms import Fieldset as Fieldset
|
||||||
from .elements.forms import Form as Form
|
from .elements.forms import Form as Form
|
||||||
@ -88,36 +87,28 @@ from .elements.inline import u as u
|
|||||||
from .elements.inline import wbr as wbr
|
from .elements.inline import wbr as wbr
|
||||||
from .elements.media import Area as Area
|
from .elements.media import Area as Area
|
||||||
from .elements.media import Audio as Audio
|
from .elements.media import Audio as Audio
|
||||||
from .elements.media import Defs as Defs
|
|
||||||
from .elements.media import Embed as Embed
|
from .elements.media import Embed as Embed
|
||||||
from .elements.media import Iframe as Iframe
|
from .elements.media import Iframe as Iframe
|
||||||
from .elements.media import Img as Img
|
from .elements.media import Img as Img
|
||||||
from .elements.media import LinearGradient as LinearGradient
|
|
||||||
from .elements.media import Map as Map
|
from .elements.media import Map as Map
|
||||||
from .elements.media import Object as Object
|
from .elements.media import Object as Object
|
||||||
from .elements.media import Path as Path
|
|
||||||
from .elements.media import Picture as Picture
|
from .elements.media import Picture as Picture
|
||||||
from .elements.media import Portal as Portal
|
from .elements.media import Portal as Portal
|
||||||
from .elements.media import Source as Source
|
from .elements.media import Source as Source
|
||||||
from .elements.media import Stop as Stop
|
|
||||||
from .elements.media import Svg as Svg
|
from .elements.media import Svg as Svg
|
||||||
from .elements.media import Track as Track
|
from .elements.media import Track as Track
|
||||||
from .elements.media import Video as Video
|
from .elements.media import Video as Video
|
||||||
from .elements.media import area as area
|
from .elements.media import area as area
|
||||||
from .elements.media import audio as audio
|
from .elements.media import audio as audio
|
||||||
from .elements.media import defs as defs # type: ignore
|
|
||||||
from .elements.media import embed as embed
|
from .elements.media import embed as embed
|
||||||
from .elements.media import iframe as iframe
|
from .elements.media import iframe as iframe
|
||||||
from .elements.media import image as image
|
from .elements.media import image as image
|
||||||
from .elements.media import img as img
|
from .elements.media import img as img
|
||||||
from .elements.media import lineargradient as lineargradient # type: ignore
|
|
||||||
from .elements.media import map as map
|
from .elements.media import map as map
|
||||||
from .elements.media import object as object
|
from .elements.media import object as object
|
||||||
from .elements.media import path as path # type: ignore
|
|
||||||
from .elements.media import picture as picture
|
from .elements.media import picture as picture
|
||||||
from .elements.media import portal as portal
|
from .elements.media import portal as portal
|
||||||
from .elements.media import source as source
|
from .elements.media import source as source
|
||||||
from .elements.media import stop as stop # type: ignore
|
|
||||||
from .elements.media import svg as svg
|
from .elements.media import svg as svg
|
||||||
from .elements.media import track as track
|
from .elements.media import track as track
|
||||||
from .elements.media import video as video
|
from .elements.media import video as video
|
||||||
@ -231,5 +222,3 @@ from .elements.typography import ol as ol
|
|||||||
from .elements.typography import p as p
|
from .elements.typography import p as p
|
||||||
from .elements.typography import pre as pre
|
from .elements.typography import pre as pre
|
||||||
from .elements.typography import ul as ul
|
from .elements.typography import ul as ul
|
||||||
|
|
||||||
_PYRIGHT_IGNORE_IMPORTS = elements._PYRIGHT_IGNORE_IMPORTS
|
|
||||||
|
@ -65,11 +65,6 @@ _MAPPING = {
|
|||||||
"portal",
|
"portal",
|
||||||
"source",
|
"source",
|
||||||
"svg",
|
"svg",
|
||||||
"defs",
|
|
||||||
"lineargradient",
|
|
||||||
"LinearGradient",
|
|
||||||
"stop",
|
|
||||||
"path",
|
|
||||||
],
|
],
|
||||||
"metadata": [
|
"metadata": [
|
||||||
"base",
|
"base",
|
||||||
@ -130,13 +125,12 @@ _MAPPING = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EXCLUDE = ["del_", "Del", "image", "lineargradient", "LinearGradient"]
|
EXCLUDE = ["del_", "Del", "image"]
|
||||||
for _, v in _MAPPING.items():
|
for _, v in _MAPPING.items():
|
||||||
v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE])
|
v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE])
|
||||||
|
|
||||||
_SUBMOD_ATTRS: dict[str, list[str]] = _MAPPING
|
_SUBMOD_ATTRS: dict[str, list[str]] = _MAPPING
|
||||||
|
|
||||||
_PYRIGHT_IGNORE_IMPORTS = ["stop", "lineargradient", "path", "defs"]
|
|
||||||
__getattr__, __dir__, __all__ = lazy_loader.attach(
|
__getattr__, __dir__, __all__ = lazy_loader.attach(
|
||||||
__name__,
|
__name__,
|
||||||
submod_attrs=_SUBMOD_ATTRS,
|
submod_attrs=_SUBMOD_ATTRS,
|
||||||
|
@ -87,36 +87,28 @@ from .inline import u as u
|
|||||||
from .inline import wbr as wbr
|
from .inline import wbr as wbr
|
||||||
from .media import Area as Area
|
from .media import Area as Area
|
||||||
from .media import Audio as Audio
|
from .media import Audio as Audio
|
||||||
from .media import Defs as Defs
|
|
||||||
from .media import Embed as Embed
|
from .media import Embed as Embed
|
||||||
from .media import Iframe as Iframe
|
from .media import Iframe as Iframe
|
||||||
from .media import Img as Img
|
from .media import Img as Img
|
||||||
from .media import LinearGradient as LinearGradient
|
|
||||||
from .media import Map as Map
|
from .media import Map as Map
|
||||||
from .media import Object as Object
|
from .media import Object as Object
|
||||||
from .media import Path as Path
|
|
||||||
from .media import Picture as Picture
|
from .media import Picture as Picture
|
||||||
from .media import Portal as Portal
|
from .media import Portal as Portal
|
||||||
from .media import Source as Source
|
from .media import Source as Source
|
||||||
from .media import Stop as Stop
|
|
||||||
from .media import Svg as Svg
|
from .media import Svg as Svg
|
||||||
from .media import Track as Track
|
from .media import Track as Track
|
||||||
from .media import Video as Video
|
from .media import Video as Video
|
||||||
from .media import area as area
|
from .media import area as area
|
||||||
from .media import audio as audio
|
from .media import audio as audio
|
||||||
from .media import defs as defs # type: ignore
|
|
||||||
from .media import embed as embed
|
from .media import embed as embed
|
||||||
from .media import iframe as iframe
|
from .media import iframe as iframe
|
||||||
from .media import image as image
|
from .media import image as image
|
||||||
from .media import img as img
|
from .media import img as img
|
||||||
from .media import lineargradient as lineargradient # type: ignore
|
|
||||||
from .media import map as map
|
from .media import map as map
|
||||||
from .media import object as object
|
from .media import object as object
|
||||||
from .media import path as path # type: ignore
|
|
||||||
from .media import picture as picture
|
from .media import picture as picture
|
||||||
from .media import portal as portal
|
from .media import portal as portal
|
||||||
from .media import source as source
|
from .media import source as source
|
||||||
from .media import stop as stop # type: ignore
|
|
||||||
from .media import svg as svg
|
from .media import svg as svg
|
||||||
from .media import track as track
|
from .media import track as track
|
||||||
from .media import video as video
|
from .media import video as video
|
||||||
@ -292,11 +284,6 @@ _MAPPING = {
|
|||||||
"portal",
|
"portal",
|
||||||
"source",
|
"source",
|
||||||
"svg",
|
"svg",
|
||||||
"defs",
|
|
||||||
"lineargradient",
|
|
||||||
"LinearGradient",
|
|
||||||
"stop",
|
|
||||||
"path",
|
|
||||||
],
|
],
|
||||||
"metadata": ["base", "head", "link", "meta", "title", "style"],
|
"metadata": ["base", "head", "link", "meta", "title", "style"],
|
||||||
"other": ["details", "dialog", "summary", "slot", "template", "math", "html"],
|
"other": ["details", "dialog", "summary", "slot", "template", "math", "html"],
|
||||||
@ -348,7 +335,6 @@ _MAPPING = {
|
|||||||
"Del",
|
"Del",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
EXCLUDE = ["del_", "Del", "image", "lineargradient", "LinearGradient"]
|
EXCLUDE = ["del_", "Del", "image"]
|
||||||
for _, v in _MAPPING.items():
|
for _, v in _MAPPING.items():
|
||||||
v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE])
|
v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE])
|
||||||
_PYRIGHT_IGNORE_IMPORTS = ["stop", "lineargradient", "path", "defs"]
|
|
||||||
|
@ -501,7 +501,7 @@ AUTO_HEIGHT_JS = """
|
|||||||
const autoHeightOnInput = (e, is_enabled) => {
|
const autoHeightOnInput = (e, is_enabled) => {
|
||||||
if (is_enabled) {
|
if (is_enabled) {
|
||||||
const el = e.target;
|
const el = e.target;
|
||||||
el.style.overflowY = "hidden";
|
el.style.overflowY = "scroll";
|
||||||
el.style.height = "auto";
|
el.style.height = "auto";
|
||||||
el.style.height = (e.target.scrollHeight) + "px";
|
el.style.height = (e.target.scrollHeight) + "px";
|
||||||
if (el.form && !el.form.data_resize_on_reset) {
|
if (el.form && !el.form.data_resize_on_reset) {
|
||||||
|
@ -1559,7 +1559,7 @@ class Select(BaseHTML):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
AUTO_HEIGHT_JS = '\nconst autoHeightOnInput = (e, is_enabled) => {\n if (is_enabled) {\n const el = e.target;\n el.style.overflowY = "hidden";\n el.style.height = "auto";\n el.style.height = (e.target.scrollHeight) + "px";\n if (el.form && !el.form.data_resize_on_reset) {\n el.form.addEventListener("reset", () => window.setTimeout(() => autoHeightOnInput(e, is_enabled), 0))\n el.form.data_resize_on_reset = true;\n }\n }\n}\n'
|
AUTO_HEIGHT_JS = '\nconst autoHeightOnInput = (e, is_enabled) => {\n if (is_enabled) {\n const el = e.target;\n el.style.overflowY = "scroll";\n el.style.height = "auto";\n el.style.height = (e.target.scrollHeight) + "px";\n if (el.form && !el.form.data_resize_on_reset) {\n el.form.addEventListener("reset", () => window.setTimeout(() => autoHeightOnInput(e, is_enabled), 0))\n el.form.data_resize_on_reset = true;\n }\n }\n}\n'
|
||||||
ENTER_KEY_SUBMIT_JS = "\nconst enterKeySubmitOnKeyDown = (e, is_enabled) => {\n if (is_enabled && e.which === 13 && !e.shiftKey) {\n e.preventDefault();\n if (!e.repeat) {\n if (e.target.form) {\n e.target.form.requestSubmit();\n }\n }\n }\n}\n"
|
ENTER_KEY_SUBMIT_JS = "\nconst enterKeySubmitOnKeyDown = (e, is_enabled) => {\n if (is_enabled && e.which === 13 && !e.shiftKey) {\n e.preventDefault();\n if (!e.repeat) {\n if (e.target.form) {\n e.target.form.requestSubmit();\n }\n }\n }\n}\n"
|
||||||
|
|
||||||
class Textarea(BaseHTML):
|
class Textarea(BaseHTML):
|
||||||
|
@ -4,7 +4,6 @@ from typing import Any, Union
|
|||||||
|
|
||||||
from reflex import Component, ComponentNamespace
|
from reflex import Component, ComponentNamespace
|
||||||
from reflex.constants.colors import Color
|
from reflex.constants.colors import Color
|
||||||
from reflex.utils import console
|
|
||||||
from reflex.vars import Var as Var
|
from reflex.vars import Var as Var
|
||||||
|
|
||||||
from .base import BaseHTML
|
from .base import BaseHTML
|
||||||
@ -445,23 +444,3 @@ picture = Picture.create
|
|||||||
portal = Portal.create
|
portal = Portal.create
|
||||||
source = Source.create
|
source = Source.create
|
||||||
svg = SVG()
|
svg = SVG()
|
||||||
|
|
||||||
|
|
||||||
def __getattr__(name: str):
|
|
||||||
if name in ("defs", "lineargradient", "stop", "path"):
|
|
||||||
console.deprecate(
|
|
||||||
f"`rx.el.{name}`",
|
|
||||||
reason=f"use `rx.el.svg.{'linear_gradient' if name =='lineargradient' else name}`",
|
|
||||||
deprecation_version="0.5.8",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
LinearGradient.create
|
|
||||||
if name == "lineargradient"
|
|
||||||
else globals()[name.capitalize()].create
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return globals()[name]
|
|
||||||
except KeyError:
|
|
||||||
raise AttributeError(f"module '{__name__} has no attribute '{name}'") from None
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Lucide Icon component."""
|
"""Lucide Icon component."""
|
||||||
|
|
||||||
from reflex.components.component import Component
|
from reflex.components.component import Component
|
||||||
from reflex.utils import console, format
|
from reflex.utils import format
|
||||||
from reflex.vars import Var
|
from reflex.vars import Var
|
||||||
|
|
||||||
|
|
||||||
@ -36,19 +36,6 @@ class Icon(LucideIconComponent):
|
|||||||
Returns:
|
Returns:
|
||||||
The created component.
|
The created component.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def map_deprecated_icon_names_05(tag: str) -> str:
|
|
||||||
new_tag = RENAMED_ICONS_05.get(tag)
|
|
||||||
if new_tag is not None:
|
|
||||||
console.deprecate(
|
|
||||||
feature_name=f"icon {tag}",
|
|
||||||
reason=f"it was renamed upstream. Use {new_tag} instead.",
|
|
||||||
deprecation_version="0.4.6",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
)
|
|
||||||
return new_tag
|
|
||||||
return tag
|
|
||||||
|
|
||||||
if children:
|
if children:
|
||||||
if len(children) == 1 and isinstance(children[0], str):
|
if len(children) == 1 and isinstance(children[0], str):
|
||||||
props["tag"] = children[0]
|
props["tag"] = children[0]
|
||||||
@ -62,8 +49,7 @@ class Icon(LucideIconComponent):
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
not isinstance(props["tag"], str)
|
not isinstance(props["tag"], str)
|
||||||
or map_deprecated_icon_names_05(format.to_snake_case(props["tag"]))
|
or format.to_snake_case(props["tag"]) not in LUCIDE_ICON_LIST
|
||||||
not in LUCIDE_ICON_LIST
|
|
||||||
):
|
):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Invalid icon tag: {props['tag']}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
|
f"Invalid icon tag: {props['tag']}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
|
||||||
@ -76,116 +62,6 @@ class Icon(LucideIconComponent):
|
|||||||
return super().create(*children, **props)
|
return super().create(*children, **props)
|
||||||
|
|
||||||
|
|
||||||
RENAMED_ICONS_05 = {
|
|
||||||
"activity_square": "square_activity",
|
|
||||||
"alert_circle": "circle_alert",
|
|
||||||
"alert_octagon": "octagon_alert",
|
|
||||||
"alert_triangle": "triangle_alert",
|
|
||||||
"arrow_down_circle": "circle_arrow_down",
|
|
||||||
"arrow_down_left_from_circle": "circle_arrow_out_down_left",
|
|
||||||
"arrow_down_left_from_square": "square_arrow_out_down_left",
|
|
||||||
"arrow_down_left_square": "square_arrow_down_left",
|
|
||||||
"arrow_down_right_from_circle": "circle_arrow_out_down_right",
|
|
||||||
"arrow_down_right_from_square": "square_arrow_out_down_right",
|
|
||||||
"arrow_down_right_square": "square_arrow_down_right",
|
|
||||||
"arrow_down_square": "square_arrow_down",
|
|
||||||
"arrow_left_circle": "circle_arrow_left",
|
|
||||||
"arrow_left_square": "square_arrow_left",
|
|
||||||
"arrow_right_circle": "circle_arrow_right",
|
|
||||||
"arrow_right_square": "square_arrow_right",
|
|
||||||
"arrow_up_circle": "circle_arrow_up",
|
|
||||||
"arrow_up_left_from_circle": "circle_arrow_out_up_left",
|
|
||||||
"arrow_up_left_from_square": "square_arrow_out_up_left",
|
|
||||||
"arrow_up_left_square": "square_arrow_up_left",
|
|
||||||
"arrow_up_right_from_circle": "circle_arrow_out_up_right",
|
|
||||||
"arrow_up_right_from_square": "square_arrow_out_up_right",
|
|
||||||
"arrow_up_right_square": "square_arrow_up_right",
|
|
||||||
"arrow_up_square": "square_arrow_up",
|
|
||||||
"asterisk_square": "square_asterisk",
|
|
||||||
"check_circle": "circle_check_big",
|
|
||||||
"check_circle_2": "circle_check",
|
|
||||||
"check_square": "square_check_big",
|
|
||||||
"check_square_2": "square_check",
|
|
||||||
"chevron_down_circle": "circle_chevron_down",
|
|
||||||
"chevron_down_square": "square_chevron_down",
|
|
||||||
"chevron_left_circle": "circle_chevron_left",
|
|
||||||
"chevron_left_square": "square_chevron_left",
|
|
||||||
"chevron_right_circle": "circle_chevron_right",
|
|
||||||
"chevron_right_square": "square_chevron_right",
|
|
||||||
"chevron_up_circle": "circle_chevron_up",
|
|
||||||
"chevron_up_square": "square_chevron_up",
|
|
||||||
"code_2": "code_xml",
|
|
||||||
"code_square": "square_code",
|
|
||||||
"contact_2": "contact_round",
|
|
||||||
"divide_circle": "circle_divide",
|
|
||||||
"divide_square": "square_divide",
|
|
||||||
"dot_square": "square_dot",
|
|
||||||
"download_cloud": "cloud_download",
|
|
||||||
"equal_square": "square_equal",
|
|
||||||
"form_input": "rectangle_ellipsis",
|
|
||||||
"function_square": "square_function",
|
|
||||||
"gantt_chart_square": "square_gantt_chart",
|
|
||||||
"gauge_circle": "circle_gauge",
|
|
||||||
"globe_2": "earth",
|
|
||||||
"help_circle": "circle_help",
|
|
||||||
"helping_hand": "hand_helping",
|
|
||||||
"ice_cream": "ice_cream_cone",
|
|
||||||
"ice_cream_2": "ice_cream_bowl",
|
|
||||||
"indent": "indent_increase",
|
|
||||||
"kanban_square": "square_kanban",
|
|
||||||
"kanban_square_dashed": "square_dashed_kanban",
|
|
||||||
"laptop_2": "laptop_minimal",
|
|
||||||
"library_square": "square_library",
|
|
||||||
"loader_2": "loader_circle",
|
|
||||||
"m_square": "square_m",
|
|
||||||
"menu_square": "square_menu",
|
|
||||||
"mic_2": "mic_vocal",
|
|
||||||
"minus_circle": "circle_minus",
|
|
||||||
"minus_square": "square_minus",
|
|
||||||
"more_horizontal": "ellipsis",
|
|
||||||
"more_vertical": "ellipsis_vertical",
|
|
||||||
"mouse_pointer_square": "square_mouse_pointer",
|
|
||||||
"mouse_pointer_square_dashed": "square_dashed_mouse_pointer",
|
|
||||||
"outdent": "indent_decrease",
|
|
||||||
"palm_tree": "tree_palm",
|
|
||||||
"parking_circle": "circle_parking",
|
|
||||||
"parking_circle_off": "circle_parking_off",
|
|
||||||
"parking_square": "square_parking",
|
|
||||||
"parking_square_off": "square_parking_off",
|
|
||||||
"pause_circle": "circle_pause",
|
|
||||||
"pause_octagon": "octagon_pause",
|
|
||||||
"percent_circle": "circle_percent",
|
|
||||||
"percent_diamond": "diamond_percent",
|
|
||||||
"percent_square": "square_percent",
|
|
||||||
"pi_square": "square_pi",
|
|
||||||
"pilcrow_square": "square_pilcrow",
|
|
||||||
"play_circle": "circle_play",
|
|
||||||
"play_square": "square_play",
|
|
||||||
"plus_circle": "circle_plus",
|
|
||||||
"plus_square": "square_plus",
|
|
||||||
"power_circle": "circle_power",
|
|
||||||
"power_square": "square_power",
|
|
||||||
"school_2": "university",
|
|
||||||
"scissors_square": "square_scissors",
|
|
||||||
"scissors_square_dashed_bottom": "square_bottom_dashed_scissors",
|
|
||||||
"sigma_square": "square_sigma",
|
|
||||||
"slash_circle": "circle_slash",
|
|
||||||
"sliders": "sliders_vertical",
|
|
||||||
"split_square_horizontal": "square_split_horizontal",
|
|
||||||
"split_square_vertical": "square_split_vertical",
|
|
||||||
"stop_circle": "circle_stop",
|
|
||||||
"subtitles": "captions",
|
|
||||||
"test_tube_2": "test_tube_diagonal",
|
|
||||||
"unlock": "lock_open",
|
|
||||||
"unlock_keyhole": "lock_keyhole_open",
|
|
||||||
"upload_cloud": "cloud_upload",
|
|
||||||
"wallet_2": "wallet_minimal",
|
|
||||||
"wand_2": "wand_sparkles",
|
|
||||||
"x_circle": "circle_x",
|
|
||||||
"x_octagon": "octagon_x",
|
|
||||||
"x_square": "square_x",
|
|
||||||
}
|
|
||||||
|
|
||||||
LUCIDE_ICON_LIST = [
|
LUCIDE_ICON_LIST = [
|
||||||
"a_arrow_down",
|
"a_arrow_down",
|
||||||
"a_arrow_up",
|
"a_arrow_up",
|
||||||
|
@ -150,115 +150,6 @@ class Icon(LucideIconComponent):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
RENAMED_ICONS_05 = {
|
|
||||||
"activity_square": "square_activity",
|
|
||||||
"alert_circle": "circle_alert",
|
|
||||||
"alert_octagon": "octagon_alert",
|
|
||||||
"alert_triangle": "triangle_alert",
|
|
||||||
"arrow_down_circle": "circle_arrow_down",
|
|
||||||
"arrow_down_left_from_circle": "circle_arrow_out_down_left",
|
|
||||||
"arrow_down_left_from_square": "square_arrow_out_down_left",
|
|
||||||
"arrow_down_left_square": "square_arrow_down_left",
|
|
||||||
"arrow_down_right_from_circle": "circle_arrow_out_down_right",
|
|
||||||
"arrow_down_right_from_square": "square_arrow_out_down_right",
|
|
||||||
"arrow_down_right_square": "square_arrow_down_right",
|
|
||||||
"arrow_down_square": "square_arrow_down",
|
|
||||||
"arrow_left_circle": "circle_arrow_left",
|
|
||||||
"arrow_left_square": "square_arrow_left",
|
|
||||||
"arrow_right_circle": "circle_arrow_right",
|
|
||||||
"arrow_right_square": "square_arrow_right",
|
|
||||||
"arrow_up_circle": "circle_arrow_up",
|
|
||||||
"arrow_up_left_from_circle": "circle_arrow_out_up_left",
|
|
||||||
"arrow_up_left_from_square": "square_arrow_out_up_left",
|
|
||||||
"arrow_up_left_square": "square_arrow_up_left",
|
|
||||||
"arrow_up_right_from_circle": "circle_arrow_out_up_right",
|
|
||||||
"arrow_up_right_from_square": "square_arrow_out_up_right",
|
|
||||||
"arrow_up_right_square": "square_arrow_up_right",
|
|
||||||
"arrow_up_square": "square_arrow_up",
|
|
||||||
"asterisk_square": "square_asterisk",
|
|
||||||
"check_circle": "circle_check_big",
|
|
||||||
"check_circle_2": "circle_check",
|
|
||||||
"check_square": "square_check_big",
|
|
||||||
"check_square_2": "square_check",
|
|
||||||
"chevron_down_circle": "circle_chevron_down",
|
|
||||||
"chevron_down_square": "square_chevron_down",
|
|
||||||
"chevron_left_circle": "circle_chevron_left",
|
|
||||||
"chevron_left_square": "square_chevron_left",
|
|
||||||
"chevron_right_circle": "circle_chevron_right",
|
|
||||||
"chevron_right_square": "square_chevron_right",
|
|
||||||
"chevron_up_circle": "circle_chevron_up",
|
|
||||||
"chevron_up_square": "square_chevron_up",
|
|
||||||
"code_2": "code_xml",
|
|
||||||
"code_square": "square_code",
|
|
||||||
"contact_2": "contact_round",
|
|
||||||
"divide_circle": "circle_divide",
|
|
||||||
"divide_square": "square_divide",
|
|
||||||
"dot_square": "square_dot",
|
|
||||||
"download_cloud": "cloud_download",
|
|
||||||
"equal_square": "square_equal",
|
|
||||||
"form_input": "rectangle_ellipsis",
|
|
||||||
"function_square": "square_function",
|
|
||||||
"gantt_chart_square": "square_gantt_chart",
|
|
||||||
"gauge_circle": "circle_gauge",
|
|
||||||
"globe_2": "earth",
|
|
||||||
"help_circle": "circle_help",
|
|
||||||
"helping_hand": "hand_helping",
|
|
||||||
"ice_cream": "ice_cream_cone",
|
|
||||||
"ice_cream_2": "ice_cream_bowl",
|
|
||||||
"indent": "indent_increase",
|
|
||||||
"kanban_square": "square_kanban",
|
|
||||||
"kanban_square_dashed": "square_dashed_kanban",
|
|
||||||
"laptop_2": "laptop_minimal",
|
|
||||||
"library_square": "square_library",
|
|
||||||
"loader_2": "loader_circle",
|
|
||||||
"m_square": "square_m",
|
|
||||||
"menu_square": "square_menu",
|
|
||||||
"mic_2": "mic_vocal",
|
|
||||||
"minus_circle": "circle_minus",
|
|
||||||
"minus_square": "square_minus",
|
|
||||||
"more_horizontal": "ellipsis",
|
|
||||||
"more_vertical": "ellipsis_vertical",
|
|
||||||
"mouse_pointer_square": "square_mouse_pointer",
|
|
||||||
"mouse_pointer_square_dashed": "square_dashed_mouse_pointer",
|
|
||||||
"outdent": "indent_decrease",
|
|
||||||
"palm_tree": "tree_palm",
|
|
||||||
"parking_circle": "circle_parking",
|
|
||||||
"parking_circle_off": "circle_parking_off",
|
|
||||||
"parking_square": "square_parking",
|
|
||||||
"parking_square_off": "square_parking_off",
|
|
||||||
"pause_circle": "circle_pause",
|
|
||||||
"pause_octagon": "octagon_pause",
|
|
||||||
"percent_circle": "circle_percent",
|
|
||||||
"percent_diamond": "diamond_percent",
|
|
||||||
"percent_square": "square_percent",
|
|
||||||
"pi_square": "square_pi",
|
|
||||||
"pilcrow_square": "square_pilcrow",
|
|
||||||
"play_circle": "circle_play",
|
|
||||||
"play_square": "square_play",
|
|
||||||
"plus_circle": "circle_plus",
|
|
||||||
"plus_square": "square_plus",
|
|
||||||
"power_circle": "circle_power",
|
|
||||||
"power_square": "square_power",
|
|
||||||
"school_2": "university",
|
|
||||||
"scissors_square": "square_scissors",
|
|
||||||
"scissors_square_dashed_bottom": "square_bottom_dashed_scissors",
|
|
||||||
"sigma_square": "square_sigma",
|
|
||||||
"slash_circle": "circle_slash",
|
|
||||||
"sliders": "sliders_vertical",
|
|
||||||
"split_square_horizontal": "square_split_horizontal",
|
|
||||||
"split_square_vertical": "square_split_vertical",
|
|
||||||
"stop_circle": "circle_stop",
|
|
||||||
"subtitles": "captions",
|
|
||||||
"test_tube_2": "test_tube_diagonal",
|
|
||||||
"unlock": "lock_open",
|
|
||||||
"unlock_keyhole": "lock_keyhole_open",
|
|
||||||
"upload_cloud": "cloud_upload",
|
|
||||||
"wallet_2": "wallet_minimal",
|
|
||||||
"wand_2": "wand_sparkles",
|
|
||||||
"x_circle": "circle_x",
|
|
||||||
"x_octagon": "octagon_x",
|
|
||||||
"x_square": "square_x",
|
|
||||||
}
|
|
||||||
LUCIDE_ICON_LIST = [
|
LUCIDE_ICON_LIST = [
|
||||||
"a_arrow_down",
|
"a_arrow_down",
|
||||||
"a_arrow_up",
|
"a_arrow_up",
|
||||||
|
@ -8,7 +8,6 @@ from . import themes as themes
|
|||||||
from .primitives.accordion import accordion as accordion
|
from .primitives.accordion import accordion as accordion
|
||||||
from .primitives.drawer import drawer as drawer
|
from .primitives.drawer import drawer as drawer
|
||||||
from .primitives.form import form as form
|
from .primitives.form import form as form
|
||||||
from .primitives.progress import progress as progress
|
|
||||||
from .themes.base import theme as theme
|
from .themes.base import theme as theme
|
||||||
from .themes.base import theme_panel as theme_panel
|
from .themes.base import theme_panel as theme_panel
|
||||||
from .themes.color_mode import color_mode as color_mode
|
from .themes.color_mode import color_mode as color_mode
|
||||||
@ -31,6 +30,7 @@ from .themes.components.hover_card import hover_card as hover_card
|
|||||||
from .themes.components.icon_button import icon_button as icon_button
|
from .themes.components.icon_button import icon_button as icon_button
|
||||||
from .themes.components.inset import inset as inset
|
from .themes.components.inset import inset as inset
|
||||||
from .themes.components.popover import popover as popover
|
from .themes.components.popover import popover as popover
|
||||||
|
from .themes.components.progress import progress as progress
|
||||||
from .themes.components.radio_cards import radio_cards as radio_cards
|
from .themes.components.radio_cards import radio_cards as radio_cards
|
||||||
from .themes.components.radio_group import radio as radio
|
from .themes.components.radio_group import radio as radio
|
||||||
from .themes.components.radio_group import radio_group as radio_group
|
from .themes.components.radio_group import radio_group as radio_group
|
||||||
|
@ -6,4 +6,3 @@
|
|||||||
from .accordion import accordion as accordion
|
from .accordion import accordion as accordion
|
||||||
from .drawer import drawer as drawer
|
from .drawer import drawer as drawer
|
||||||
from .form import form as form
|
from .form import form as form
|
||||||
from .progress import progress as progress
|
|
||||||
|
@ -32,7 +32,6 @@ from reflex.style import (
|
|||||||
set_color_mode,
|
set_color_mode,
|
||||||
toggle_color_mode,
|
toggle_color_mode,
|
||||||
)
|
)
|
||||||
from reflex.utils import console
|
|
||||||
from reflex.vars import Var
|
from reflex.vars import Var
|
||||||
|
|
||||||
from .components.icon_button import IconButton
|
from .components.icon_button import IconButton
|
||||||
@ -100,7 +99,6 @@ class ColorModeIconButton(IconButton):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
cls,
|
cls,
|
||||||
*children,
|
|
||||||
position: LiteralPosition | None = None,
|
position: LiteralPosition | None = None,
|
||||||
allow_system: bool = False,
|
allow_system: bool = False,
|
||||||
**props,
|
**props,
|
||||||
@ -108,7 +106,6 @@ class ColorModeIconButton(IconButton):
|
|||||||
"""Create a icon button component that calls toggle_color_mode on click.
|
"""Create a icon button component that calls toggle_color_mode on click.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*children: The children of the component.
|
|
||||||
position: The position of the icon button. Follow document flow if None.
|
position: The position of the icon button. Follow document flow if None.
|
||||||
allow_system: Allow picking the "system" value for the color mode.
|
allow_system: Allow picking the "system" value for the color mode.
|
||||||
**props: The props to pass to the component.
|
**props: The props to pass to the component.
|
||||||
@ -116,14 +113,6 @@ class ColorModeIconButton(IconButton):
|
|||||||
Returns:
|
Returns:
|
||||||
The button component.
|
The button component.
|
||||||
"""
|
"""
|
||||||
if children:
|
|
||||||
console.deprecate(
|
|
||||||
feature_name="passing children to color_mode.button",
|
|
||||||
reason=", use color_mode_cond and toggle_color_mode instead to build a custom color_mode component",
|
|
||||||
deprecation_version="0.5.0",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
)
|
|
||||||
|
|
||||||
# position is used to set nice defaults for positioning the icon button
|
# position is used to set nice defaults for positioning the icon button
|
||||||
if isinstance(position, Var):
|
if isinstance(position, Var):
|
||||||
_set_var_default(props, position, "position", "fixed", position)
|
_set_var_default(props, position, "position", "fixed", position)
|
||||||
|
@ -102,10 +102,6 @@ class ColorModeIconButton(IconButton):
|
|||||||
def create( # type: ignore
|
def create( # type: ignore
|
||||||
cls,
|
cls,
|
||||||
*children,
|
*children,
|
||||||
position: Optional[
|
|
||||||
Literal["top-left", "top-right", "bottom-left", "bottom-right"]
|
|
||||||
] = None,
|
|
||||||
allow_system: Optional[bool] = False,
|
|
||||||
as_child: Optional[Union[Var[bool], bool]] = None,
|
as_child: Optional[Union[Var[bool], bool]] = None,
|
||||||
size: Optional[
|
size: Optional[
|
||||||
Union[
|
Union[
|
||||||
@ -280,7 +276,6 @@ class ColorModeIconButton(IconButton):
|
|||||||
"""Create a icon button component that calls toggle_color_mode on click.
|
"""Create a icon button component that calls toggle_color_mode on click.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*children: The children of the component.
|
|
||||||
position: The position of the icon button. Follow document flow if None.
|
position: The position of the icon button. Follow document flow if None.
|
||||||
allow_system: Allow picking the "system" value for the color mode.
|
allow_system: Allow picking the "system" value for the color mode.
|
||||||
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
|
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
|
||||||
|
@ -22,6 +22,7 @@ from .hover_card import hover_card as hover_card
|
|||||||
from .icon_button import icon_button as icon_button
|
from .icon_button import icon_button as icon_button
|
||||||
from .inset import inset as inset
|
from .inset import inset as inset
|
||||||
from .popover import popover as popover
|
from .popover import popover as popover
|
||||||
|
from .progress import progress as progress
|
||||||
from .radio_cards import radio_cards as radio_cards
|
from .radio_cards import radio_cards as radio_cards
|
||||||
from .radio_group import radio as radio
|
from .radio_group import radio as radio
|
||||||
from .radio_group import radio_group as radio_group
|
from .radio_group import radio_group as radio_group
|
||||||
|
@ -12,6 +12,7 @@ from reflex.components.radix.themes.typography.text import Text
|
|||||||
from reflex.event import EventHandler
|
from reflex.event import EventHandler
|
||||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||||
from reflex.ivars.sequence import StringVar
|
from reflex.ivars.sequence import StringVar
|
||||||
|
from reflex.utils import types
|
||||||
from reflex.vars import Var
|
from reflex.vars import Var
|
||||||
|
|
||||||
from ..base import (
|
from ..base import (
|
||||||
@ -133,6 +134,9 @@ class HighLevelRadioGroup(RadixThemesComponent):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The created radio group component.
|
The created radio group component.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If the type of items is invalid.
|
||||||
"""
|
"""
|
||||||
direction = props.pop("direction", "column")
|
direction = props.pop("direction", "column")
|
||||||
spacing = props.pop("spacing", "2")
|
spacing = props.pop("spacing", "2")
|
||||||
@ -141,6 +145,16 @@ class HighLevelRadioGroup(RadixThemesComponent):
|
|||||||
color_scheme = props.pop("color_scheme", None)
|
color_scheme = props.pop("color_scheme", None)
|
||||||
default_value = props.pop("default_value", "")
|
default_value = props.pop("default_value", "")
|
||||||
|
|
||||||
|
if (
|
||||||
|
not isinstance(items, (list, Var))
|
||||||
|
or isinstance(items, Var)
|
||||||
|
and not types._issubclass(items._var_type, list)
|
||||||
|
):
|
||||||
|
items_type = type(items) if not isinstance(items, Var) else items._var_type
|
||||||
|
raise TypeError(
|
||||||
|
f"The radio group component takes in a list, got {items_type} instead"
|
||||||
|
)
|
||||||
|
|
||||||
default_value = LiteralVar.create(default_value)
|
default_value = LiteralVar.create(default_value)
|
||||||
|
|
||||||
# convert only non-strings to json(JSON.stringify) so quotes are not rendered
|
# convert only non-strings to json(JSON.stringify) so quotes are not rendered
|
||||||
|
@ -419,6 +419,9 @@ class HighLevelRadioGroup(RadixThemesComponent):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The created radio group component.
|
The created radio group component.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If the type of items is invalid.
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -588,6 +591,9 @@ class RadioGroup(ComponentNamespace):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The created radio group component.
|
The created radio group component.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If the type of items is invalid.
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -4,15 +4,11 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Literal, Union
|
from typing import Literal, Union
|
||||||
|
|
||||||
from reflex.components.base.fragment import Fragment
|
|
||||||
from reflex.components.component import Component, ComponentNamespace
|
from reflex.components.component import Component, ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
from reflex.components.core.debounce import DebounceInput
|
from reflex.components.core.debounce import DebounceInput
|
||||||
from reflex.components.el import elements
|
from reflex.components.el import elements
|
||||||
from reflex.event import EventHandler
|
from reflex.event import EventHandler
|
||||||
from reflex.ivars.base import LiteralVar
|
|
||||||
from reflex.style import Style, format_as_emotion
|
|
||||||
from reflex.utils import console
|
|
||||||
from reflex.vars import Var
|
from reflex.vars import Var
|
||||||
|
|
||||||
from ..base import (
|
from ..base import (
|
||||||
@ -107,80 +103,6 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
|
|||||||
return DebounceInput.create(component)
|
return DebounceInput.create(component)
|
||||||
return component
|
return component
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_root_deprecated(cls, *children, **props) -> Component:
|
|
||||||
"""Create a Fragment component (wrapper for deprecated name).
|
|
||||||
|
|
||||||
Copy the attributes that were previously defined on TextFieldRoot in 0.4.9 to
|
|
||||||
any child input elements (via custom_attrs).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
*children: The children of the component.
|
|
||||||
**props: The properties of the component.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The component.
|
|
||||||
"""
|
|
||||||
console.deprecate(
|
|
||||||
feature_name="rx.input.root",
|
|
||||||
reason="use rx.input without the .root suffix",
|
|
||||||
deprecation_version="0.5.0",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
)
|
|
||||||
inputs = [
|
|
||||||
child
|
|
||||||
for child in children
|
|
||||||
if isinstance(child, (TextFieldRoot, DebounceInput))
|
|
||||||
]
|
|
||||||
if not inputs:
|
|
||||||
# Old-style where no explicit child input was provided
|
|
||||||
return cls.create(*children, **props)
|
|
||||||
slots = [child for child in children if isinstance(child, TextFieldSlot)]
|
|
||||||
carry_props = {
|
|
||||||
prop: props.pop(prop)
|
|
||||||
for prop in ["size", "variant", "color_scheme", "radius"]
|
|
||||||
if prop in props
|
|
||||||
}
|
|
||||||
template = cls.create(**props)
|
|
||||||
for child in inputs:
|
|
||||||
child.children.extend(slots)
|
|
||||||
custom_attrs = child.custom_attrs
|
|
||||||
custom_attrs.update(
|
|
||||||
{
|
|
||||||
prop: value
|
|
||||||
for prop, value in carry_props.items()
|
|
||||||
if prop not in custom_attrs and getattr(child, prop) is None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
style = Style(template.style)
|
|
||||||
style.update(child.style)
|
|
||||||
child._get_style = lambda style=style: {
|
|
||||||
"css": LiteralVar.create(format_as_emotion(style))
|
|
||||||
}
|
|
||||||
for trigger in template.event_triggers:
|
|
||||||
if trigger not in child.event_triggers:
|
|
||||||
child.event_triggers[trigger] = template.event_triggers[trigger]
|
|
||||||
return Fragment.create(*inputs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_input_deprecated(cls, *children, **props) -> Component:
|
|
||||||
"""Create a TextFieldRoot component (wrapper for deprecated name).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
*children: The children of the component.
|
|
||||||
**props: The properties of the component.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The component.
|
|
||||||
"""
|
|
||||||
console.deprecate(
|
|
||||||
feature_name="rx.input.input",
|
|
||||||
reason="use rx.input without the .input suffix",
|
|
||||||
deprecation_version="0.5.0",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
)
|
|
||||||
return cls.create(*children, **props)
|
|
||||||
|
|
||||||
|
|
||||||
class TextFieldSlot(RadixThemesComponent):
|
class TextFieldSlot(RadixThemesComponent):
|
||||||
"""Contains icons or buttons associated with an Input."""
|
"""Contains icons or buttons associated with an Input."""
|
||||||
@ -194,8 +116,6 @@ class TextFieldSlot(RadixThemesComponent):
|
|||||||
class TextField(ComponentNamespace):
|
class TextField(ComponentNamespace):
|
||||||
"""TextField components namespace."""
|
"""TextField components namespace."""
|
||||||
|
|
||||||
root = staticmethod(TextFieldRoot.create_root_deprecated)
|
|
||||||
input = staticmethod(TextFieldRoot.create_input_deprecated)
|
|
||||||
slot = staticmethod(TextFieldSlot.create)
|
slot = staticmethod(TextFieldSlot.create)
|
||||||
__call__ = staticmethod(TextFieldRoot.create)
|
__call__ = staticmethod(TextFieldRoot.create)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
from typing import Any, Callable, Dict, Literal, Optional, Union, overload
|
from typing import Any, Callable, Dict, Literal, Optional, Union, overload
|
||||||
|
|
||||||
from reflex.components.component import Component, ComponentNamespace
|
from reflex.components.component import ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Breakpoints
|
from reflex.components.core.breakpoints import Breakpoints
|
||||||
from reflex.components.el import elements
|
from reflex.components.el import elements
|
||||||
from reflex.event import EventHandler, EventSpec
|
from reflex.event import EventHandler, EventSpec
|
||||||
@ -239,11 +239,6 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_root_deprecated(cls, *children, **props) -> Component: ...
|
|
||||||
@classmethod
|
|
||||||
def create_input_deprecated(cls, *children, **props) -> Component: ...
|
|
||||||
|
|
||||||
class TextFieldSlot(RadixThemesComponent):
|
class TextFieldSlot(RadixThemesComponent):
|
||||||
@overload
|
@overload
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -377,8 +372,6 @@ class TextFieldSlot(RadixThemesComponent):
|
|||||||
...
|
...
|
||||||
|
|
||||||
class TextField(ComponentNamespace):
|
class TextField(ComponentNamespace):
|
||||||
root = staticmethod(TextFieldRoot.create_root_deprecated)
|
|
||||||
input = staticmethod(TextFieldRoot.create_input_deprecated)
|
|
||||||
slot = staticmethod(TextFieldSlot.create)
|
slot = staticmethod(TextFieldSlot.create)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -76,6 +76,9 @@ def _toast_callback_signature(toast: Var) -> list[Var]:
|
|||||||
class ToastProps(PropsBase):
|
class ToastProps(PropsBase):
|
||||||
"""Props for the toast component."""
|
"""Props for the toast component."""
|
||||||
|
|
||||||
|
# Toast's title, renders above the description.
|
||||||
|
title: Optional[Union[str, Var]]
|
||||||
|
|
||||||
# Toast's description, renders underneath the title.
|
# Toast's description, renders underneath the title.
|
||||||
description: Optional[Union[str, Var]]
|
description: Optional[Union[str, Var]]
|
||||||
|
|
||||||
@ -290,7 +293,7 @@ class Toaster(Component):
|
|||||||
return call_script(toast_action)
|
return call_script(toast_action)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_info(message: str, **kwargs):
|
def toast_info(message: str = "", **kwargs):
|
||||||
"""Display an info toast message.
|
"""Display an info toast message.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -303,7 +306,7 @@ class Toaster(Component):
|
|||||||
return Toaster.send_toast(message, level="info", **kwargs)
|
return Toaster.send_toast(message, level="info", **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_warning(message: str, **kwargs):
|
def toast_warning(message: str = "", **kwargs):
|
||||||
"""Display a warning toast message.
|
"""Display a warning toast message.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -316,7 +319,7 @@ class Toaster(Component):
|
|||||||
return Toaster.send_toast(message, level="warning", **kwargs)
|
return Toaster.send_toast(message, level="warning", **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_error(message: str, **kwargs):
|
def toast_error(message: str = "", **kwargs):
|
||||||
"""Display an error toast message.
|
"""Display an error toast message.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -329,7 +332,7 @@ class Toaster(Component):
|
|||||||
return Toaster.send_toast(message, level="error", **kwargs)
|
return Toaster.send_toast(message, level="error", **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_success(message: str, **kwargs):
|
def toast_success(message: str = "", **kwargs):
|
||||||
"""Display a success toast message.
|
"""Display a success toast message.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -32,6 +32,7 @@ class ToastAction(Base):
|
|||||||
def serialize_action(action: ToastAction) -> dict: ...
|
def serialize_action(action: ToastAction) -> dict: ...
|
||||||
|
|
||||||
class ToastProps(PropsBase):
|
class ToastProps(PropsBase):
|
||||||
|
title: Optional[Union[str, Var]]
|
||||||
description: Optional[Union[str, Var]]
|
description: Optional[Union[str, Var]]
|
||||||
close_button: Optional[bool]
|
close_button: Optional[bool]
|
||||||
invert: Optional[bool]
|
invert: Optional[bool]
|
||||||
@ -65,13 +66,13 @@ class Toaster(Component):
|
|||||||
message: str = "", level: str | None = None, **props
|
message: str = "", level: str | None = None, **props
|
||||||
) -> EventSpec: ...
|
) -> EventSpec: ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_info(message: str, **kwargs): ...
|
def toast_info(message: str = "", **kwargs): ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_warning(message: str, **kwargs): ...
|
def toast_warning(message: str = "", **kwargs): ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_error(message: str, **kwargs): ...
|
def toast_error(message: str = "", **kwargs): ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_success(message: str, **kwargs): ...
|
def toast_success(message: str = "", **kwargs): ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toast_dismiss(id: Var | str | None = None): ...
|
def toast_dismiss(id: Var | str | None = None): ...
|
||||||
@overload
|
@overload
|
||||||
|
@ -45,6 +45,8 @@ class Dirs(SimpleNamespace):
|
|||||||
REFLEX_JSON = "reflex.json"
|
REFLEX_JSON = "reflex.json"
|
||||||
# The name of the postcss config file.
|
# The name of the postcss config file.
|
||||||
POSTCSS_JS = "postcss.config.js"
|
POSTCSS_JS = "postcss.config.js"
|
||||||
|
# The name of the states directory.
|
||||||
|
STATES = "states"
|
||||||
|
|
||||||
|
|
||||||
class Reflex(SimpleNamespace):
|
class Reflex(SimpleNamespace):
|
||||||
@ -103,7 +105,7 @@ class Templates(SimpleNamespace):
|
|||||||
|
|
||||||
# The reflex.build backend host
|
# The reflex.build backend host
|
||||||
REFLEX_BUILD_BACKEND = os.environ.get(
|
REFLEX_BUILD_BACKEND = os.environ.get(
|
||||||
"REFLEX_BUILD_BACKEND", "https://rxh-prod-flexgen.fly.dev"
|
"REFLEX_BUILD_BACKEND", "https://flexgen-prod-flexgen.fly.dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
# The URL to redirect to reflex.build
|
# The URL to redirect to reflex.build
|
||||||
|
@ -11,6 +11,7 @@ class Endpoint(Enum):
|
|||||||
EVENT = "_event"
|
EVENT = "_event"
|
||||||
UPLOAD = "_upload"
|
UPLOAD = "_upload"
|
||||||
AUTH_CODESPACE = "auth-codespace"
|
AUTH_CODESPACE = "auth-codespace"
|
||||||
|
HEALTH = "_health"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
"""Get the string representation of the endpoint.
|
"""Get the string representation of the endpoint.
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
import types
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from typing import (
|
from typing import (
|
||||||
@ -22,6 +23,7 @@ from reflex.ivars.base import ImmutableVar, LiteralVar
|
|||||||
from reflex.ivars.function import FunctionStringVar, FunctionVar
|
from reflex.ivars.function import FunctionStringVar, FunctionVar
|
||||||
from reflex.ivars.object import ObjectVar
|
from reflex.ivars.object import ObjectVar
|
||||||
from reflex.utils import format
|
from reflex.utils import format
|
||||||
|
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch
|
||||||
from reflex.utils.types import ArgsSpec
|
from reflex.utils.types import ArgsSpec
|
||||||
from reflex.vars import ImmutableVarData, Var
|
from reflex.vars import ImmutableVarData, Var
|
||||||
|
|
||||||
@ -831,7 +833,7 @@ def call_event_handler(
|
|||||||
arg_spec: The lambda that define the argument(s) to pass to the event handler.
|
arg_spec: The lambda that define the argument(s) to pass to the event handler.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: if number of arguments expected by event_handler doesn't match the spec.
|
EventHandlerArgMismatch: if number of arguments expected by event_handler doesn't match the spec.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The event spec from calling the event handler.
|
The event spec from calling the event handler.
|
||||||
@ -843,13 +845,16 @@ def call_event_handler(
|
|||||||
return event_handler.add_args(*parsed_args)
|
return event_handler.add_args(*parsed_args)
|
||||||
|
|
||||||
args = inspect.getfullargspec(event_handler.fn).args
|
args = inspect.getfullargspec(event_handler.fn).args
|
||||||
if len(args) == len(["self", *parsed_args]):
|
n_args = len(args) - 1 # subtract 1 for bound self arg
|
||||||
|
if n_args == len(parsed_args):
|
||||||
return event_handler(*parsed_args) # type: ignore
|
return event_handler(*parsed_args) # type: ignore
|
||||||
else:
|
else:
|
||||||
source = inspect.getsource(arg_spec) # type: ignore
|
raise EventHandlerArgMismatch(
|
||||||
raise ValueError(
|
"The number of arguments accepted by "
|
||||||
f"number of arguments in {event_handler.fn.__qualname__} "
|
f"{event_handler.fn.__qualname__} ({n_args}) "
|
||||||
f"doesn't match the definition of the event trigger '{source.strip().strip(',')}'"
|
"does not match the arguments passed by the event trigger: "
|
||||||
|
f"{[str(v) for v in parsed_args]}\n"
|
||||||
|
"See https://reflex.dev/docs/events/event-arguments/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -874,58 +879,60 @@ def parse_args_spec(arg_spec: ArgsSpec):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec] | Var:
|
def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
|
||||||
"""Call a function to a list of event specs.
|
"""Call a function to a list of event specs.
|
||||||
|
|
||||||
The function should return a single EventSpec, a list of EventSpecs, or a
|
The function should return a single EventSpec, a list of EventSpecs, or a
|
||||||
single Var. If the function takes in an arg, the arg will be passed to the
|
single Var. The function signature must match the passed arg_spec or
|
||||||
function. Otherwise, the function will be called with no args.
|
EventFnArgsMismatch will be raised.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
fn: The function to call.
|
fn: The function to call.
|
||||||
arg: The argument to pass to the function.
|
arg_spec: The argument spec for the event trigger.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The event specs from calling the function or a Var.
|
The event specs from calling the function or a Var.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
EventHandlerValueError: If the lambda has an invalid signature.
|
EventFnArgMismatch: If the function signature doesn't match the arg spec.
|
||||||
|
EventHandlerValueError: If the lambda returns an unusable value.
|
||||||
"""
|
"""
|
||||||
# Import here to avoid circular imports.
|
# Import here to avoid circular imports.
|
||||||
from reflex.event import EventHandler, EventSpec
|
from reflex.event import EventHandler, EventSpec
|
||||||
from reflex.utils.exceptions import EventHandlerValueError
|
from reflex.utils.exceptions import EventHandlerValueError
|
||||||
|
|
||||||
# Get the args of the lambda.
|
# Check that fn signature matches arg_spec
|
||||||
args = inspect.getfullargspec(fn).args
|
fn_args = inspect.getfullargspec(fn).args
|
||||||
|
n_fn_args = len(fn_args)
|
||||||
|
if isinstance(fn, types.MethodType):
|
||||||
|
n_fn_args -= 1 # subtract 1 for bound self arg
|
||||||
|
parsed_args = parse_args_spec(arg_spec)
|
||||||
|
if len(parsed_args) != n_fn_args:
|
||||||
|
raise EventFnArgMismatch(
|
||||||
|
"The number of arguments accepted by "
|
||||||
|
f"{fn} ({n_fn_args}) "
|
||||||
|
"does not match the arguments passed by the event trigger: "
|
||||||
|
f"{[str(v) for v in parsed_args]}\n"
|
||||||
|
"See https://reflex.dev/docs/events/event-arguments/"
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(arg, ArgsSpec):
|
# Call the function with the parsed args.
|
||||||
out = fn(*parse_args_spec(arg)) # type: ignore
|
out = fn(*parsed_args)
|
||||||
else:
|
|
||||||
# Call the lambda.
|
|
||||||
if len(args) == 0:
|
|
||||||
out = fn()
|
|
||||||
elif len(args) == 1:
|
|
||||||
out = fn(arg)
|
|
||||||
else:
|
|
||||||
raise EventHandlerValueError(f"Lambda {fn} must have 0 or 1 arguments.")
|
|
||||||
|
|
||||||
# If the function returns a Var, assume it's an EventChain and render it directly.
|
# If the function returns a Var, assume it's an EventChain and render it directly.
|
||||||
if isinstance(out, Var):
|
if isinstance(out, Var):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
# Convert the output to a list.
|
# Convert the output to a list.
|
||||||
if not isinstance(out, List):
|
if not isinstance(out, list):
|
||||||
out = [out]
|
out = [out]
|
||||||
|
|
||||||
# Convert any event specs to event specs.
|
# Convert any event specs to event specs.
|
||||||
events = []
|
events = []
|
||||||
for e in out:
|
for e in out:
|
||||||
# Convert handlers to event specs.
|
|
||||||
if isinstance(e, EventHandler):
|
if isinstance(e, EventHandler):
|
||||||
if len(args) == 0:
|
# An un-called EventHandler gets all of the args of the event trigger.
|
||||||
e = e()
|
e = call_event_handler(e, arg_spec)
|
||||||
elif len(args) == 1:
|
|
||||||
e = e(arg) # type: ignore
|
|
||||||
|
|
||||||
# Make sure the event spec is valid.
|
# Make sure the event spec is valid.
|
||||||
if not isinstance(e, EventSpec):
|
if not isinstance(e, EventSpec):
|
||||||
|
@ -32,18 +32,39 @@ class ExperimentalNamespace(SimpleNamespace):
|
|||||||
Returns:
|
Returns:
|
||||||
The toast namespace.
|
The toast namespace.
|
||||||
"""
|
"""
|
||||||
if "toast" not in _EMITTED_PROMOTION_WARNINGS:
|
self.register_component_warning("toast")
|
||||||
_EMITTED_PROMOTION_WARNINGS.add("toast")
|
|
||||||
warn(f"`rx._x.toast` was promoted to `rx.toast`.")
|
|
||||||
return toast
|
return toast
|
||||||
|
|
||||||
|
@property
|
||||||
|
def progress(self):
|
||||||
|
"""Temporary property returning the toast namespace.
|
||||||
|
|
||||||
|
Remove this property when toast is fully promoted.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The toast namespace.
|
||||||
|
"""
|
||||||
|
self.register_component_warning("progress")
|
||||||
|
return progress
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def register_component_warning(component_name: str):
|
||||||
|
"""Add component to emitted warnings and throw a warning if it
|
||||||
|
doesn't exist.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
component_name: name of the component.
|
||||||
|
"""
|
||||||
|
if component_name not in _EMITTED_PROMOTION_WARNINGS:
|
||||||
|
_EMITTED_PROMOTION_WARNINGS.add(component_name)
|
||||||
|
warn(f"`rx._x.{component_name}` was promoted to `rx.{component_name}`.")
|
||||||
|
|
||||||
|
|
||||||
_x = ExperimentalNamespace(
|
_x = ExperimentalNamespace(
|
||||||
asset=asset,
|
asset=asset,
|
||||||
client_state=ClientStateVar.create,
|
client_state=ClientStateVar.create,
|
||||||
hooks=hooks,
|
hooks=hooks,
|
||||||
layout=layout,
|
layout=layout,
|
||||||
progress=progress,
|
|
||||||
PropsBase=PropsBase,
|
PropsBase=PropsBase,
|
||||||
run_in_thread=run_in_thread,
|
run_in_thread=run_in_thread,
|
||||||
)
|
)
|
||||||
|
@ -12,7 +12,6 @@ from .number import LiteralNumberVar as LiteralNumberVar
|
|||||||
from .number import NumberVar as NumberVar
|
from .number import NumberVar as NumberVar
|
||||||
from .object import LiteralObjectVar as LiteralObjectVar
|
from .object import LiteralObjectVar as LiteralObjectVar
|
||||||
from .object import ObjectVar as ObjectVar
|
from .object import ObjectVar as ObjectVar
|
||||||
from .sequence import ArrayJoinOperation as ArrayJoinOperation
|
|
||||||
from .sequence import ArrayVar as ArrayVar
|
from .sequence import ArrayVar as ArrayVar
|
||||||
from .sequence import ConcatVarOperation as ConcatVarOperation
|
from .sequence import ConcatVarOperation as ConcatVarOperation
|
||||||
from .sequence import LiteralArrayVar as LiteralArrayVar
|
from .sequence import LiteralArrayVar as LiteralArrayVar
|
||||||
|
@ -20,6 +20,7 @@ from typing import (
|
|||||||
Generic,
|
Generic,
|
||||||
List,
|
List,
|
||||||
Literal,
|
Literal,
|
||||||
|
NoReturn,
|
||||||
Optional,
|
Optional,
|
||||||
Sequence,
|
Sequence,
|
||||||
Set,
|
Set,
|
||||||
@ -384,10 +385,18 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
return self.to(BooleanVar, output)
|
return self.to(BooleanVar, output)
|
||||||
|
|
||||||
if issubclass(output, NumberVar):
|
if issubclass(output, NumberVar):
|
||||||
if fixed_type is not None and not issubclass(fixed_type, (int, float)):
|
if fixed_type is not None:
|
||||||
raise TypeError(
|
if fixed_type is Union:
|
||||||
f"Unsupported type {var_type} for NumberVar. Must be int or float."
|
inner_types = get_args(base_type)
|
||||||
)
|
if not all(issubclass(t, (int, float)) for t in inner_types):
|
||||||
|
raise TypeError(
|
||||||
|
f"Unsupported type {var_type} for NumberVar. Must be int or float."
|
||||||
|
)
|
||||||
|
|
||||||
|
elif not issubclass(fixed_type, (int, float)):
|
||||||
|
raise TypeError(
|
||||||
|
f"Unsupported type {var_type} for NumberVar. Must be int or float."
|
||||||
|
)
|
||||||
return ToNumberVarOperation.create(self, var_type or float)
|
return ToNumberVarOperation.create(self, var_type or float)
|
||||||
|
|
||||||
if issubclass(output, BooleanVar):
|
if issubclass(output, BooleanVar):
|
||||||
@ -440,7 +449,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Raises:
|
Raises:
|
||||||
TypeError: If the type is not supported for guessing.
|
TypeError: If the type is not supported for guessing.
|
||||||
"""
|
"""
|
||||||
from .number import NumberVar
|
from .number import BooleanVar, NumberVar
|
||||||
from .object import ObjectVar
|
from .object import ObjectVar
|
||||||
from .sequence import ArrayVar, StringVar
|
from .sequence import ArrayVar, StringVar
|
||||||
|
|
||||||
@ -454,11 +463,16 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
fixed_type = get_origin(var_type) or var_type
|
fixed_type = get_origin(var_type) or var_type
|
||||||
|
|
||||||
if fixed_type is Union:
|
if fixed_type is Union:
|
||||||
|
inner_types = get_args(var_type)
|
||||||
|
if int in inner_types and float in inner_types:
|
||||||
|
return self.to(NumberVar, self._var_type)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if not inspect.isclass(fixed_type):
|
if not inspect.isclass(fixed_type):
|
||||||
raise TypeError(f"Unsupported type {var_type} for guess_type.")
|
raise TypeError(f"Unsupported type {var_type} for guess_type.")
|
||||||
|
|
||||||
|
if issubclass(fixed_type, bool):
|
||||||
|
return self.to(BooleanVar, self._var_type)
|
||||||
if issubclass(fixed_type, (int, float)):
|
if issubclass(fixed_type, (int, float)):
|
||||||
return self.to(NumberVar, self._var_type)
|
return self.to(NumberVar, self._var_type)
|
||||||
if issubclass(fixed_type, dict):
|
if issubclass(fixed_type, dict):
|
||||||
@ -570,9 +584,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
BooleanVar: A BooleanVar object representing the result of the equality check.
|
BooleanVar: A BooleanVar object representing the result of the equality check.
|
||||||
"""
|
"""
|
||||||
from .number import EqualOperation
|
from .number import equal_operation
|
||||||
|
|
||||||
return EqualOperation.create(self, other)
|
return equal_operation(self, other)
|
||||||
|
|
||||||
def __ne__(self, other: Var | Any) -> BooleanVar:
|
def __ne__(self, other: Var | Any) -> BooleanVar:
|
||||||
"""Check if the current object is not equal to the given object.
|
"""Check if the current object is not equal to the given object.
|
||||||
@ -583,9 +597,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
BooleanVar: A BooleanVar object representing the result of the comparison.
|
BooleanVar: A BooleanVar object representing the result of the comparison.
|
||||||
"""
|
"""
|
||||||
from .number import EqualOperation
|
from .number import equal_operation
|
||||||
|
|
||||||
return ~EqualOperation.create(self, other)
|
return ~equal_operation(self, other)
|
||||||
|
|
||||||
def __gt__(self, other: Var | Any) -> BooleanVar:
|
def __gt__(self, other: Var | Any) -> BooleanVar:
|
||||||
"""Compare the current instance with another variable and return a BooleanVar representing the result of the greater than operation.
|
"""Compare the current instance with another variable and return a BooleanVar representing the result of the greater than operation.
|
||||||
@ -596,9 +610,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
BooleanVar: A BooleanVar representing the result of the greater than operation.
|
BooleanVar: A BooleanVar representing the result of the greater than operation.
|
||||||
"""
|
"""
|
||||||
from .number import GreaterThanOperation
|
from .number import greater_than_operation
|
||||||
|
|
||||||
return GreaterThanOperation.create(self, other)
|
return greater_than_operation(self, other)
|
||||||
|
|
||||||
def __ge__(self, other: Var | Any) -> BooleanVar:
|
def __ge__(self, other: Var | Any) -> BooleanVar:
|
||||||
"""Check if the value of this variable is greater than or equal to the value of another variable or object.
|
"""Check if the value of this variable is greater than or equal to the value of another variable or object.
|
||||||
@ -609,9 +623,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
BooleanVar: A BooleanVar object representing the result of the comparison.
|
BooleanVar: A BooleanVar object representing the result of the comparison.
|
||||||
"""
|
"""
|
||||||
from .number import GreaterThanOrEqualOperation
|
from .number import greater_than_or_equal_operation
|
||||||
|
|
||||||
return GreaterThanOrEqualOperation.create(self, other)
|
return greater_than_or_equal_operation(self, other)
|
||||||
|
|
||||||
def __lt__(self, other: Var | Any) -> BooleanVar:
|
def __lt__(self, other: Var | Any) -> BooleanVar:
|
||||||
"""Compare the current instance with another variable using the less than (<) operator.
|
"""Compare the current instance with another variable using the less than (<) operator.
|
||||||
@ -622,9 +636,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
A `BooleanVar` object representing the result of the comparison.
|
A `BooleanVar` object representing the result of the comparison.
|
||||||
"""
|
"""
|
||||||
from .number import LessThanOperation
|
from .number import less_than_operation
|
||||||
|
|
||||||
return LessThanOperation.create(self, other)
|
return less_than_operation(self, other)
|
||||||
|
|
||||||
def __le__(self, other: Var | Any) -> BooleanVar:
|
def __le__(self, other: Var | Any) -> BooleanVar:
|
||||||
"""Compare if the current instance is less than or equal to the given value.
|
"""Compare if the current instance is less than or equal to the given value.
|
||||||
@ -635,9 +649,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
A BooleanVar object representing the result of the comparison.
|
A BooleanVar object representing the result of the comparison.
|
||||||
"""
|
"""
|
||||||
from .number import LessThanOrEqualOperation
|
from .number import less_than_or_equal_operation
|
||||||
|
|
||||||
return LessThanOrEqualOperation.create(self, other)
|
return less_than_or_equal_operation(self, other)
|
||||||
|
|
||||||
def bool(self) -> BooleanVar:
|
def bool(self) -> BooleanVar:
|
||||||
"""Convert the var to a boolean.
|
"""Convert the var to a boolean.
|
||||||
@ -645,9 +659,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
The boolean var.
|
The boolean var.
|
||||||
"""
|
"""
|
||||||
from .number import ToBooleanVarOperation
|
from .number import boolify
|
||||||
|
|
||||||
return ToBooleanVarOperation.create(self)
|
return boolify(self)
|
||||||
|
|
||||||
def __and__(self, other: Var | Any) -> ImmutableVar:
|
def __and__(self, other: Var | Any) -> ImmutableVar:
|
||||||
"""Perform a logical AND operation on the current instance and another variable.
|
"""Perform a logical AND operation on the current instance and another variable.
|
||||||
@ -658,7 +672,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
A `BooleanVar` object representing the result of the logical AND operation.
|
A `BooleanVar` object representing the result of the logical AND operation.
|
||||||
"""
|
"""
|
||||||
return AndOperation.create(self, other)
|
return and_operation(self, other)
|
||||||
|
|
||||||
def __rand__(self, other: Var | Any) -> ImmutableVar:
|
def __rand__(self, other: Var | Any) -> ImmutableVar:
|
||||||
"""Perform a logical AND operation on the current instance and another variable.
|
"""Perform a logical AND operation on the current instance and another variable.
|
||||||
@ -669,7 +683,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
A `BooleanVar` object representing the result of the logical AND operation.
|
A `BooleanVar` object representing the result of the logical AND operation.
|
||||||
"""
|
"""
|
||||||
return AndOperation.create(other, self)
|
return and_operation(other, self)
|
||||||
|
|
||||||
def __or__(self, other: Var | Any) -> ImmutableVar:
|
def __or__(self, other: Var | Any) -> ImmutableVar:
|
||||||
"""Perform a logical OR operation on the current instance and another variable.
|
"""Perform a logical OR operation on the current instance and another variable.
|
||||||
@ -680,7 +694,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
A `BooleanVar` object representing the result of the logical OR operation.
|
A `BooleanVar` object representing the result of the logical OR operation.
|
||||||
"""
|
"""
|
||||||
return OrOperation.create(self, other)
|
return or_operation(self, other)
|
||||||
|
|
||||||
def __ror__(self, other: Var | Any) -> ImmutableVar:
|
def __ror__(self, other: Var | Any) -> ImmutableVar:
|
||||||
"""Perform a logical OR operation on the current instance and another variable.
|
"""Perform a logical OR operation on the current instance and another variable.
|
||||||
@ -691,7 +705,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
A `BooleanVar` object representing the result of the logical OR operation.
|
A `BooleanVar` object representing the result of the logical OR operation.
|
||||||
"""
|
"""
|
||||||
return OrOperation.create(other, self)
|
return or_operation(other, self)
|
||||||
|
|
||||||
def __invert__(self) -> BooleanVar:
|
def __invert__(self) -> BooleanVar:
|
||||||
"""Perform a logical NOT operation on the current instance.
|
"""Perform a logical NOT operation on the current instance.
|
||||||
@ -699,9 +713,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
A `BooleanVar` object representing the result of the logical NOT operation.
|
A `BooleanVar` object representing the result of the logical NOT operation.
|
||||||
"""
|
"""
|
||||||
from .number import BooleanNotOperation
|
return ~self.bool()
|
||||||
|
|
||||||
return BooleanNotOperation.create(self.bool())
|
|
||||||
|
|
||||||
def to_string(self) -> ImmutableVar:
|
def to_string(self) -> ImmutableVar:
|
||||||
"""Convert the var to a string.
|
"""Convert the var to a string.
|
||||||
@ -926,52 +938,92 @@ class LiteralVar(ImmutableVar):
|
|||||||
|
|
||||||
|
|
||||||
P = ParamSpec("P")
|
P = ParamSpec("P")
|
||||||
T = TypeVar("T", bound=ImmutableVar)
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
def var_operation(*, output: Type[T]) -> Callable[[Callable[P, str]], Callable[P, T]]:
|
# NoReturn is used to match CustomVarOperationReturn with no type hint.
|
||||||
|
@overload
|
||||||
|
def var_operation(
|
||||||
|
func: Callable[P, CustomVarOperationReturn[NoReturn]],
|
||||||
|
) -> Callable[P, ImmutableVar]: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def var_operation(
|
||||||
|
func: Callable[P, CustomVarOperationReturn[bool]],
|
||||||
|
) -> Callable[P, BooleanVar]: ...
|
||||||
|
|
||||||
|
|
||||||
|
NUMBER_T = TypeVar("NUMBER_T", int, float, Union[int, float])
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def var_operation(
|
||||||
|
func: Callable[P, CustomVarOperationReturn[NUMBER_T]],
|
||||||
|
) -> Callable[P, NumberVar[NUMBER_T]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def var_operation(
|
||||||
|
func: Callable[P, CustomVarOperationReturn[str]],
|
||||||
|
) -> Callable[P, StringVar]: ...
|
||||||
|
|
||||||
|
|
||||||
|
LIST_T = TypeVar("LIST_T", bound=Union[List[Any], Tuple, Set])
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def var_operation(
|
||||||
|
func: Callable[P, CustomVarOperationReturn[LIST_T]],
|
||||||
|
) -> Callable[P, ArrayVar[LIST_T]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
OBJECT_TYPE = TypeVar("OBJECT_TYPE", bound=Dict)
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def var_operation(
|
||||||
|
func: Callable[P, CustomVarOperationReturn[OBJECT_TYPE]],
|
||||||
|
) -> Callable[P, ObjectVar[OBJECT_TYPE]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
def var_operation(
|
||||||
|
func: Callable[P, CustomVarOperationReturn[T]],
|
||||||
|
) -> Callable[P, ImmutableVar[T]]:
|
||||||
"""Decorator for creating a var operation.
|
"""Decorator for creating a var operation.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```python
|
```python
|
||||||
@var_operation(output=NumberVar)
|
@var_operation
|
||||||
def add(a: NumberVar, b: NumberVar):
|
def add(a: NumberVar, b: NumberVar):
|
||||||
return f"({a} + {b})"
|
return custom_var_operation(f"{a} + {b}")
|
||||||
```
|
```
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
output: The output type of the operation.
|
func: The function to decorate.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The decorator.
|
The decorated function.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func: Callable[P, str], output=output):
|
@functools.wraps(func)
|
||||||
@functools.wraps(func)
|
def wrapper(*args: P.args, **kwargs: P.kwargs) -> ImmutableVar[T]:
|
||||||
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
func_args = list(inspect.signature(func).parameters)
|
||||||
args_vars = [
|
args_vars = {
|
||||||
LiteralVar.create(arg) if not isinstance(arg, Var) else arg
|
func_args[i]: (LiteralVar.create(arg) if not isinstance(arg, Var) else arg)
|
||||||
for arg in args
|
for i, arg in enumerate(args)
|
||||||
]
|
}
|
||||||
kwargs_vars = {
|
kwargs_vars = {
|
||||||
key: LiteralVar.create(value) if not isinstance(value, Var) else value
|
key: LiteralVar.create(value) if not isinstance(value, Var) else value
|
||||||
for key, value in kwargs.items()
|
for key, value in kwargs.items()
|
||||||
}
|
}
|
||||||
return output(
|
|
||||||
_var_name=func(*args_vars, **kwargs_vars), # type: ignore
|
|
||||||
_var_data=VarData.merge(
|
|
||||||
*[arg._get_all_var_data() for arg in args if isinstance(arg, Var)],
|
|
||||||
*[
|
|
||||||
arg._get_all_var_data()
|
|
||||||
for arg in kwargs.values()
|
|
||||||
if isinstance(arg, Var)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return wrapper
|
return CustomVarOperation.create(
|
||||||
|
args=tuple(list(args_vars.items()) + list(kwargs_vars.items())),
|
||||||
|
return_var=func(*args_vars.values(), **kwargs_vars), # type: ignore
|
||||||
|
).guess_type()
|
||||||
|
|
||||||
return decorator
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def unionize(*args: Type) -> Type:
|
def unionize(*args: Type) -> Type:
|
||||||
@ -1100,114 +1152,64 @@ class CachedVarOperation:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
def and_operation(a: Var | Any, b: Var | Any) -> ImmutableVar:
|
||||||
eq=False,
|
"""Perform a logical AND operation on two variables.
|
||||||
frozen=True,
|
|
||||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
||||||
)
|
|
||||||
class AndOperation(CachedVarOperation, ImmutableVar):
|
|
||||||
"""Class for the logical AND operation."""
|
|
||||||
|
|
||||||
# The first var.
|
Args:
|
||||||
_var1: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
a: The first variable.
|
||||||
|
b: The second variable.
|
||||||
|
|
||||||
# The second var.
|
Returns:
|
||||||
_var2: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
The result of the logical AND operation.
|
||||||
|
"""
|
||||||
@cached_property_no_lock
|
return _and_operation(a, b) # type: ignore
|
||||||
def _cached_var_name(self) -> str:
|
|
||||||
"""Get the cached var name.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The cached var name.
|
|
||||||
"""
|
|
||||||
return f"({str(self._var1)} && {str(self._var2)})"
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
"""Calculates the hash value of the object.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: The hash value of the object.
|
|
||||||
"""
|
|
||||||
return hash((self.__class__.__name__, self._var1, self._var2))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create(
|
|
||||||
cls, var1: Var | Any, var2: Var | Any, _var_data: VarData | None = None
|
|
||||||
) -> AndOperation:
|
|
||||||
"""Create an AndOperation.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
var1: The first var.
|
|
||||||
var2: The second var.
|
|
||||||
_var_data: Additional hooks and imports associated with the Var.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The AndOperation.
|
|
||||||
"""
|
|
||||||
var1, var2 = map(LiteralVar.create, (var1, var2))
|
|
||||||
return AndOperation(
|
|
||||||
_var_name="",
|
|
||||||
_var_type=unionize(var1._var_type, var2._var_type),
|
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
|
||||||
_var1=var1,
|
|
||||||
_var2=var2,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@var_operation
|
||||||
eq=False,
|
def _and_operation(a: ImmutableVar, b: ImmutableVar):
|
||||||
frozen=True,
|
"""Perform a logical AND operation on two variables.
|
||||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
||||||
)
|
|
||||||
class OrOperation(CachedVarOperation, ImmutableVar):
|
|
||||||
"""Class for the logical OR operation."""
|
|
||||||
|
|
||||||
# The first var.
|
Args:
|
||||||
_var1: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
a: The first variable.
|
||||||
|
b: The second variable.
|
||||||
|
|
||||||
# The second var.
|
Returns:
|
||||||
_var2: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
The result of the logical AND operation.
|
||||||
|
"""
|
||||||
|
return var_operation_return(
|
||||||
|
js_expression=f"({a} && {b})",
|
||||||
|
var_type=unionize(a._var_type, b._var_type),
|
||||||
|
)
|
||||||
|
|
||||||
@cached_property_no_lock
|
|
||||||
def _cached_var_name(self) -> str:
|
|
||||||
"""Get the cached var name.
|
|
||||||
|
|
||||||
Returns:
|
def or_operation(a: Var | Any, b: Var | Any) -> ImmutableVar:
|
||||||
The cached var name.
|
"""Perform a logical OR operation on two variables.
|
||||||
"""
|
|
||||||
return f"({str(self._var1)} || {str(self._var2)})"
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
Args:
|
||||||
"""Calculates the hash value for the object.
|
a: The first variable.
|
||||||
|
b: The second variable.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: The hash value of the object.
|
The result of the logical OR operation.
|
||||||
"""
|
"""
|
||||||
return hash((self.__class__.__name__, self._var1, self._var2))
|
return _or_operation(a, b) # type: ignore
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create(
|
|
||||||
cls, var1: Var | Any, var2: Var | Any, _var_data: VarData | None = None
|
|
||||||
) -> OrOperation:
|
|
||||||
"""Create an OrOperation.
|
|
||||||
|
|
||||||
Args:
|
@var_operation
|
||||||
var1: The first var.
|
def _or_operation(a: ImmutableVar, b: ImmutableVar):
|
||||||
var2: The second var.
|
"""Perform a logical OR operation on two variables.
|
||||||
_var_data: Additional hooks and imports associated with the Var.
|
|
||||||
|
|
||||||
Returns:
|
Args:
|
||||||
The OrOperation.
|
a: The first variable.
|
||||||
"""
|
b: The second variable.
|
||||||
var1, var2 = map(LiteralVar.create, (var1, var2))
|
|
||||||
return OrOperation(
|
Returns:
|
||||||
_var_name="",
|
The result of the logical OR operation.
|
||||||
_var_type=unionize(var1._var_type, var2._var_type),
|
"""
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
return var_operation_return(
|
||||||
_var1=var1,
|
js_expression=f"({a} || {b})",
|
||||||
_var2=var2,
|
var_type=unionize(a._var_type, b._var_type),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@dataclasses.dataclass(
|
||||||
@ -1797,3 +1799,114 @@ def immutable_computed_var(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
RETURN = TypeVar("RETURN")
|
||||||
|
|
||||||
|
|
||||||
|
class CustomVarOperationReturn(ImmutableVar[RETURN]):
|
||||||
|
"""Base class for custom var operations."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
js_expression: str,
|
||||||
|
_var_type: Type[RETURN] | None = None,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> CustomVarOperationReturn[RETURN]:
|
||||||
|
"""Create a CustomVarOperation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
js_expression: The JavaScript expression to evaluate.
|
||||||
|
_var_type: The type of the var.
|
||||||
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The CustomVarOperation.
|
||||||
|
"""
|
||||||
|
return CustomVarOperationReturn(
|
||||||
|
_var_name=js_expression,
|
||||||
|
_var_type=_var_type or Any,
|
||||||
|
_var_data=ImmutableVarData.merge(_var_data),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def var_operation_return(
|
||||||
|
js_expression: str,
|
||||||
|
var_type: Type[RETURN] | None = None,
|
||||||
|
) -> CustomVarOperationReturn[RETURN]:
|
||||||
|
"""Shortcut for creating a CustomVarOperationReturn.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
js_expression: The JavaScript expression to evaluate.
|
||||||
|
var_type: The type of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The CustomVarOperationReturn.
|
||||||
|
"""
|
||||||
|
return CustomVarOperationReturn.create(js_expression, var_type)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
frozen=True,
|
||||||
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||||
|
)
|
||||||
|
class CustomVarOperation(CachedVarOperation, ImmutableVar[T]):
|
||||||
|
"""Base class for custom var operations."""
|
||||||
|
|
||||||
|
_args: Tuple[Tuple[str, Var], ...] = dataclasses.field(default_factory=tuple)
|
||||||
|
|
||||||
|
_return: CustomVarOperationReturn[T] = dataclasses.field(
|
||||||
|
default_factory=lambda: CustomVarOperationReturn.create("")
|
||||||
|
)
|
||||||
|
|
||||||
|
@cached_property_no_lock
|
||||||
|
def _cached_var_name(self) -> str:
|
||||||
|
"""Get the cached var name.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The cached var name.
|
||||||
|
"""
|
||||||
|
return str(self._return)
|
||||||
|
|
||||||
|
@cached_property_no_lock
|
||||||
|
def _cached_get_all_var_data(self) -> ImmutableVarData | None:
|
||||||
|
"""Get the cached VarData.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The cached VarData.
|
||||||
|
"""
|
||||||
|
return ImmutableVarData.merge(
|
||||||
|
*map(
|
||||||
|
lambda arg: arg[1]._get_all_var_data(),
|
||||||
|
self._args,
|
||||||
|
),
|
||||||
|
self._return._get_all_var_data(),
|
||||||
|
self._var_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
args: Tuple[Tuple[str, Var], ...],
|
||||||
|
return_var: CustomVarOperationReturn[T],
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> CustomVarOperation[T]:
|
||||||
|
"""Create a CustomVarOperation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args: The arguments to the operation.
|
||||||
|
return_var: The return var.
|
||||||
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The CustomVarOperation.
|
||||||
|
"""
|
||||||
|
return CustomVarOperation(
|
||||||
|
_var_name="",
|
||||||
|
_var_type=return_var._var_type,
|
||||||
|
_var_data=ImmutableVarData.merge(_var_data),
|
||||||
|
_args=args,
|
||||||
|
_return=return_var,
|
||||||
|
)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,8 @@ from .base import (
|
|||||||
LiteralVar,
|
LiteralVar,
|
||||||
cached_property_no_lock,
|
cached_property_no_lock,
|
||||||
figure_out_type,
|
figure_out_type,
|
||||||
|
var_operation,
|
||||||
|
var_operation_return,
|
||||||
)
|
)
|
||||||
from .number import BooleanVar, NumberVar
|
from .number import BooleanVar, NumberVar
|
||||||
from .sequence import ArrayVar, StringVar
|
from .sequence import ArrayVar, StringVar
|
||||||
@ -56,7 +58,9 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
|||||||
return str
|
return str
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def _value_type(self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]) -> VALUE_TYPE: ...
|
def _value_type(
|
||||||
|
self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]],
|
||||||
|
) -> Type[VALUE_TYPE]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def _value_type(self) -> Type: ...
|
def _value_type(self) -> Type: ...
|
||||||
@ -79,7 +83,7 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
The keys of the object.
|
The keys of the object.
|
||||||
"""
|
"""
|
||||||
return ObjectKeysOperation.create(self)
|
return object_keys_operation(self)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def values(
|
def values(
|
||||||
@ -95,7 +99,7 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
The values of the object.
|
The values of the object.
|
||||||
"""
|
"""
|
||||||
return ObjectValuesOperation.create(self)
|
return object_values_operation(self)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def entries(
|
def entries(
|
||||||
@ -111,9 +115,9 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
The entries of the object.
|
The entries of the object.
|
||||||
"""
|
"""
|
||||||
return ObjectEntriesOperation.create(self)
|
return object_entries_operation(self)
|
||||||
|
|
||||||
def merge(self, other: ObjectVar) -> ObjectMergeOperation:
|
def merge(self, other: ObjectVar):
|
||||||
"""Merge two objects.
|
"""Merge two objects.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -122,7 +126,7 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
The merged object.
|
The merged object.
|
||||||
"""
|
"""
|
||||||
return ObjectMergeOperation.create(self, other)
|
return object_merge_operation(self, other)
|
||||||
|
|
||||||
# NoReturn is used here to catch when key value is Any
|
# NoReturn is used here to catch when key value is Any
|
||||||
@overload
|
@overload
|
||||||
@ -270,7 +274,7 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
The result of the check.
|
The result of the check.
|
||||||
"""
|
"""
|
||||||
return ObjectHasOwnProperty.create(self, key)
|
return object_has_own_property_operation(self, key)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@dataclasses.dataclass(
|
||||||
@ -387,207 +391,72 @@ class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@var_operation
|
||||||
eq=False,
|
def object_keys_operation(value: ObjectVar):
|
||||||
frozen=True,
|
"""Get the keys of an object.
|
||||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
||||||
)
|
|
||||||
class ObjectToArrayOperation(CachedVarOperation, ArrayVar):
|
|
||||||
"""Base class for object to array operations."""
|
|
||||||
|
|
||||||
_value: ObjectVar = dataclasses.field(
|
Args:
|
||||||
default_factory=lambda: LiteralObjectVar.create({})
|
value: The object to get the keys from.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The keys of the object.
|
||||||
|
"""
|
||||||
|
return var_operation_return(
|
||||||
|
js_expression=f"Object.keys({value})",
|
||||||
|
var_type=List[str],
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property_no_lock
|
|
||||||
def _cached_var_name(self) -> str:
|
|
||||||
"""The name of the operation.
|
|
||||||
|
|
||||||
Raises:
|
@var_operation
|
||||||
NotImplementedError: Must implement _cached_var_name.
|
def object_values_operation(value: ObjectVar):
|
||||||
"""
|
"""Get the values of an object.
|
||||||
raise NotImplementedError(
|
|
||||||
"ObjectToArrayOperation must implement _cached_var_name"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
Args:
|
||||||
def create(
|
value: The object to get the values from.
|
||||||
cls,
|
|
||||||
value: ObjectVar,
|
|
||||||
_var_type: GenericType | None = None,
|
|
||||||
_var_data: VarData | None = None,
|
|
||||||
) -> ObjectToArrayOperation:
|
|
||||||
"""Create the object to array operation.
|
|
||||||
|
|
||||||
Args:
|
Returns:
|
||||||
value: The value of the operation.
|
The values of the object.
|
||||||
_var_data: Additional hooks and imports associated with the operation.
|
"""
|
||||||
|
return var_operation_return(
|
||||||
Returns:
|
js_expression=f"Object.values({value})",
|
||||||
The object to array operation.
|
var_type=List[value._value_type()],
|
||||||
"""
|
|
||||||
return cls(
|
|
||||||
_var_name="",
|
|
||||||
_var_type=list if _var_type is None else _var_type,
|
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
|
||||||
_value=value,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectKeysOperation(ObjectToArrayOperation):
|
|
||||||
"""Operation to get the keys of an object."""
|
|
||||||
|
|
||||||
@cached_property_no_lock
|
|
||||||
def _cached_var_name(self) -> str:
|
|
||||||
"""The name of the operation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The name of the operation.
|
|
||||||
"""
|
|
||||||
return f"Object.keys({str(self._value)})"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create(
|
|
||||||
cls,
|
|
||||||
value: ObjectVar,
|
|
||||||
_var_data: VarData | None = None,
|
|
||||||
) -> ObjectKeysOperation:
|
|
||||||
"""Create the object keys operation.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value: The value of the operation.
|
|
||||||
_var_data: Additional hooks and imports associated with the operation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The object keys operation.
|
|
||||||
"""
|
|
||||||
return cls(
|
|
||||||
_var_name="",
|
|
||||||
_var_type=List[str],
|
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
|
||||||
_value=value,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectValuesOperation(ObjectToArrayOperation):
|
|
||||||
"""Operation to get the values of an object."""
|
|
||||||
|
|
||||||
@cached_property_no_lock
|
|
||||||
def _cached_var_name(self) -> str:
|
|
||||||
"""The name of the operation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The name of the operation.
|
|
||||||
"""
|
|
||||||
return f"Object.values({str(self._value)})"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create(
|
|
||||||
cls,
|
|
||||||
value: ObjectVar,
|
|
||||||
_var_data: VarData | None = None,
|
|
||||||
) -> ObjectValuesOperation:
|
|
||||||
"""Create the object values operation.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value: The value of the operation.
|
|
||||||
_var_data: Additional hooks and imports associated with the operation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The object values operation.
|
|
||||||
"""
|
|
||||||
return cls(
|
|
||||||
_var_name="",
|
|
||||||
_var_type=List[value._value_type()],
|
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
|
||||||
_value=value,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectEntriesOperation(ObjectToArrayOperation):
|
|
||||||
"""Operation to get the entries of an object."""
|
|
||||||
|
|
||||||
@cached_property_no_lock
|
|
||||||
def _cached_var_name(self) -> str:
|
|
||||||
"""The name of the operation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The name of the operation.
|
|
||||||
"""
|
|
||||||
return f"Object.entries({str(self._value)})"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create(
|
|
||||||
cls,
|
|
||||||
value: ObjectVar,
|
|
||||||
_var_data: VarData | None = None,
|
|
||||||
) -> ObjectEntriesOperation:
|
|
||||||
"""Create the object entries operation.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value: The value of the operation.
|
|
||||||
_var_data: Additional hooks and imports associated with the operation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The object entries operation.
|
|
||||||
"""
|
|
||||||
return cls(
|
|
||||||
_var_name="",
|
|
||||||
_var_type=List[Tuple[str, value._value_type()]],
|
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
|
||||||
_value=value,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
|
||||||
eq=False,
|
|
||||||
frozen=True,
|
|
||||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
||||||
)
|
|
||||||
class ObjectMergeOperation(CachedVarOperation, ObjectVar):
|
|
||||||
"""Operation to merge two objects."""
|
|
||||||
|
|
||||||
_lhs: ObjectVar = dataclasses.field(
|
|
||||||
default_factory=lambda: LiteralObjectVar.create({})
|
|
||||||
)
|
|
||||||
_rhs: ObjectVar = dataclasses.field(
|
|
||||||
default_factory=lambda: LiteralObjectVar.create({})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property_no_lock
|
|
||||||
def _cached_var_name(self) -> str:
|
|
||||||
"""The name of the operation.
|
|
||||||
|
|
||||||
Returns:
|
@var_operation
|
||||||
The name of the operation.
|
def object_entries_operation(value: ObjectVar):
|
||||||
"""
|
"""Get the entries of an object.
|
||||||
return f"({{...{str(self._lhs)}, ...{str(self._rhs)}}})"
|
|
||||||
|
|
||||||
@classmethod
|
Args:
|
||||||
def create(
|
value: The object to get the entries from.
|
||||||
cls,
|
|
||||||
lhs: ObjectVar,
|
|
||||||
rhs: ObjectVar,
|
|
||||||
_var_data: VarData | None = None,
|
|
||||||
) -> ObjectMergeOperation:
|
|
||||||
"""Create the object merge operation.
|
|
||||||
|
|
||||||
Args:
|
Returns:
|
||||||
lhs: The left object to merge.
|
The entries of the object.
|
||||||
rhs: The right object to merge.
|
"""
|
||||||
_var_data: Additional hooks and imports associated with the operation.
|
return var_operation_return(
|
||||||
|
js_expression=f"Object.entries({value})",
|
||||||
|
var_type=List[Tuple[str, value._value_type()]],
|
||||||
|
)
|
||||||
|
|
||||||
Returns:
|
|
||||||
The object merge operation.
|
@var_operation
|
||||||
"""
|
def object_merge_operation(lhs: ObjectVar, rhs: ObjectVar):
|
||||||
# TODO: Figure out how to merge the types
|
"""Merge two objects.
|
||||||
return cls(
|
|
||||||
_var_name="",
|
Args:
|
||||||
_var_type=lhs._var_type,
|
lhs: The first object to merge.
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
rhs: The second object to merge.
|
||||||
_lhs=lhs,
|
|
||||||
_rhs=rhs,
|
Returns:
|
||||||
)
|
The merged object.
|
||||||
|
"""
|
||||||
|
return var_operation_return(
|
||||||
|
js_expression=f"({{...{lhs}, ...{rhs}}})",
|
||||||
|
var_type=Dict[
|
||||||
|
Union[lhs._key_type(), rhs._key_type()],
|
||||||
|
Union[lhs._value_type(), rhs._value_type()],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@dataclasses.dataclass(
|
||||||
@ -688,49 +557,18 @@ class ToObjectOperation(CachedVarOperation, ObjectVar):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@var_operation
|
||||||
eq=False,
|
def object_has_own_property_operation(object: ObjectVar, key: Var):
|
||||||
frozen=True,
|
"""Check if an object has a key.
|
||||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
||||||
)
|
|
||||||
class ObjectHasOwnProperty(CachedVarOperation, BooleanVar):
|
|
||||||
"""Operation to check if an object has a property."""
|
|
||||||
|
|
||||||
_object: ObjectVar = dataclasses.field(
|
Args:
|
||||||
default_factory=lambda: LiteralObjectVar.create({})
|
object: The object to check.
|
||||||
|
key: The key to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The result of the check.
|
||||||
|
"""
|
||||||
|
return var_operation_return(
|
||||||
|
js_expression=f"{object}.hasOwnProperty({key})",
|
||||||
|
var_type=bool,
|
||||||
)
|
)
|
||||||
_key: Var | Any = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
|
||||||
|
|
||||||
@cached_property_no_lock
|
|
||||||
def _cached_var_name(self) -> str:
|
|
||||||
"""The name of the operation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The name of the operation.
|
|
||||||
"""
|
|
||||||
return f"{str(self._object)}.hasOwnProperty({str(self._key)})"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create(
|
|
||||||
cls,
|
|
||||||
object: ObjectVar,
|
|
||||||
key: Var | Any,
|
|
||||||
_var_data: VarData | None = None,
|
|
||||||
) -> ObjectHasOwnProperty:
|
|
||||||
"""Create the object has own property operation.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
object: The object to check.
|
|
||||||
key: The key to check.
|
|
||||||
_var_data: Additional hooks and imports associated with the operation.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The object has own property operation.
|
|
||||||
"""
|
|
||||||
return cls(
|
|
||||||
_var_name="",
|
|
||||||
_var_type=bool,
|
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
|
||||||
_object=object,
|
|
||||||
_key=key if isinstance(key, Var) else LiteralVar.create(key),
|
|
||||||
)
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@ import alembic.runtime.environment
|
|||||||
import alembic.script
|
import alembic.script
|
||||||
import alembic.util
|
import alembic.util
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
import sqlalchemy.exc
|
||||||
import sqlalchemy.orm
|
import sqlalchemy.orm
|
||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
@ -51,6 +52,27 @@ def get_engine(url: str | None = None) -> sqlalchemy.engine.Engine:
|
|||||||
return sqlmodel.create_engine(url, echo=echo_db_query, connect_args=connect_args)
|
return sqlmodel.create_engine(url, echo=echo_db_query, connect_args=connect_args)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_db_status() -> bool:
|
||||||
|
"""Checks the status of the database connection.
|
||||||
|
|
||||||
|
Attempts to connect to the database and execute a simple query to verify connectivity.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: The status of the database connection:
|
||||||
|
- True: The database is accessible.
|
||||||
|
- False: The database is not accessible.
|
||||||
|
"""
|
||||||
|
status = True
|
||||||
|
try:
|
||||||
|
engine = get_engine()
|
||||||
|
with engine.connect() as connection:
|
||||||
|
connection.execute(sqlalchemy.text("SELECT 1"))
|
||||||
|
except sqlalchemy.exc.OperationalError:
|
||||||
|
status = False
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
SQLModelOrSqlAlchemy = Union[
|
SQLModelOrSqlAlchemy = Union[
|
||||||
Type[sqlmodel.SQLModel], Type[sqlalchemy.orm.DeclarativeBase]
|
Type[sqlmodel.SQLModel], Type[sqlalchemy.orm.DeclarativeBase]
|
||||||
]
|
]
|
||||||
|
267
reflex/state.py
267
reflex/state.py
@ -11,6 +11,7 @@ import os
|
|||||||
import uuid
|
import uuid
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from pathlib import Path
|
||||||
from types import FunctionType, MethodType
|
from types import FunctionType, MethodType
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
@ -23,6 +24,7 @@ from typing import (
|
|||||||
Optional,
|
Optional,
|
||||||
Sequence,
|
Sequence,
|
||||||
Set,
|
Set,
|
||||||
|
Tuple,
|
||||||
Type,
|
Type,
|
||||||
Union,
|
Union,
|
||||||
cast,
|
cast,
|
||||||
@ -52,7 +54,7 @@ from reflex.event import (
|
|||||||
EventSpec,
|
EventSpec,
|
||||||
fix_events,
|
fix_events,
|
||||||
)
|
)
|
||||||
from reflex.utils import console, format, prerequisites, types
|
from reflex.utils import console, format, path_ops, prerequisites, types
|
||||||
from reflex.utils.exceptions import ImmutableStateError, LockExpiredError
|
from reflex.utils.exceptions import ImmutableStateError, LockExpiredError
|
||||||
from reflex.utils.exec import is_testing_env
|
from reflex.utils.exec import is_testing_env
|
||||||
from reflex.utils.serializers import SerializedType, serialize, serializer
|
from reflex.utils.serializers import SerializedType, serialize, serializer
|
||||||
@ -2339,7 +2341,7 @@ class StateManager(Base, ABC):
|
|||||||
token_expiration=config.redis_token_expiration,
|
token_expiration=config.redis_token_expiration,
|
||||||
lock_expiration=config.redis_lock_expiration,
|
lock_expiration=config.redis_lock_expiration,
|
||||||
)
|
)
|
||||||
return StateManagerMemory(state=state)
|
return StateManagerDisk(state=state)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def get_state(self, token: str) -> BaseState:
|
async def get_state(self, token: str) -> BaseState:
|
||||||
@ -2446,6 +2448,258 @@ class StateManagerMemory(StateManager):
|
|||||||
await self.set_state(token, state)
|
await self.set_state(token, state)
|
||||||
|
|
||||||
|
|
||||||
|
def _default_token_expiration() -> int:
|
||||||
|
"""Get the default token expiration time.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The default token expiration time.
|
||||||
|
"""
|
||||||
|
return get_config().redis_token_expiration
|
||||||
|
|
||||||
|
|
||||||
|
def _serialize_type(type_: Any) -> str:
|
||||||
|
"""Serialize a type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
type_: The type to serialize.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The serialized type.
|
||||||
|
"""
|
||||||
|
if not inspect.isclass(type_):
|
||||||
|
return f"{type_}"
|
||||||
|
return f"{type_.__module__}.{type_.__qualname__}"
|
||||||
|
|
||||||
|
|
||||||
|
def state_to_schema(
|
||||||
|
state: BaseState,
|
||||||
|
) -> List[
|
||||||
|
Tuple[
|
||||||
|
str,
|
||||||
|
str,
|
||||||
|
Any,
|
||||||
|
Union[bool, None],
|
||||||
|
]
|
||||||
|
]:
|
||||||
|
"""Convert a state to a schema.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
state: The state to convert to a schema.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The schema.
|
||||||
|
"""
|
||||||
|
return list(
|
||||||
|
sorted(
|
||||||
|
(
|
||||||
|
field_name,
|
||||||
|
model_field.name,
|
||||||
|
_serialize_type(model_field.type_),
|
||||||
|
(
|
||||||
|
model_field.required
|
||||||
|
if isinstance(model_field.required, bool)
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for field_name, model_field in state.__fields__.items()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StateManagerDisk(StateManager):
|
||||||
|
"""A state manager that stores states in memory."""
|
||||||
|
|
||||||
|
# The mapping of client ids to states.
|
||||||
|
states: Dict[str, BaseState] = {}
|
||||||
|
|
||||||
|
# The mutex ensures the dict of mutexes is updated exclusively
|
||||||
|
_state_manager_lock = asyncio.Lock()
|
||||||
|
|
||||||
|
# The dict of mutexes for each client
|
||||||
|
_states_locks: Dict[str, asyncio.Lock] = pydantic.PrivateAttr({})
|
||||||
|
|
||||||
|
# The token expiration time (s).
|
||||||
|
token_expiration: int = pydantic.Field(default_factory=_default_token_expiration)
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""The Pydantic config."""
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
"_states_locks": {"exclude": True},
|
||||||
|
}
|
||||||
|
keep_untouched = (functools.cached_property,)
|
||||||
|
|
||||||
|
def __init__(self, state: Type[BaseState]):
|
||||||
|
"""Create a new state manager.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
state: The state class to use.
|
||||||
|
"""
|
||||||
|
super().__init__(state=state)
|
||||||
|
|
||||||
|
path_ops.mkdir(self.states_directory)
|
||||||
|
|
||||||
|
self._purge_expired_states()
|
||||||
|
|
||||||
|
@functools.cached_property
|
||||||
|
def states_directory(self) -> Path:
|
||||||
|
"""Get the states directory.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The states directory.
|
||||||
|
"""
|
||||||
|
return prerequisites.get_web_dir() / constants.Dirs.STATES
|
||||||
|
|
||||||
|
def _purge_expired_states(self):
|
||||||
|
"""Purge expired states from the disk."""
|
||||||
|
import time
|
||||||
|
|
||||||
|
for path in path_ops.ls(self.states_directory):
|
||||||
|
# check path is a pickle file
|
||||||
|
if path.suffix != ".pkl":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# load last edited field from file
|
||||||
|
last_edited = path.stat().st_mtime
|
||||||
|
|
||||||
|
# check if the file is older than the token expiration time
|
||||||
|
if time.time() - last_edited > self.token_expiration:
|
||||||
|
# remove the file
|
||||||
|
path.unlink()
|
||||||
|
|
||||||
|
def token_path(self, token: str) -> Path:
|
||||||
|
"""Get the path for a token.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
token: The token to get the path for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The path for the token.
|
||||||
|
"""
|
||||||
|
return (self.states_directory / f"{token}.pkl").absolute()
|
||||||
|
|
||||||
|
async def load_state(self, token: str, root_state: BaseState) -> BaseState:
|
||||||
|
"""Load a state object based on the provided token.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
token: The token used to identify the state object.
|
||||||
|
root_state: The root state object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The loaded state object.
|
||||||
|
"""
|
||||||
|
if token in self.states:
|
||||||
|
return self.states[token]
|
||||||
|
|
||||||
|
client_token, substate_address = _split_substate_key(token)
|
||||||
|
|
||||||
|
token_path = self.token_path(token)
|
||||||
|
|
||||||
|
if token_path.exists():
|
||||||
|
try:
|
||||||
|
with token_path.open(mode="rb") as file:
|
||||||
|
(substate_schema, substate) = dill.load(file)
|
||||||
|
if substate_schema == state_to_schema(substate):
|
||||||
|
await self.populate_substates(client_token, substate, root_state)
|
||||||
|
return substate
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return root_state.get_substate(substate_address.split(".")[1:])
|
||||||
|
|
||||||
|
async def populate_substates(
|
||||||
|
self, client_token: str, state: BaseState, root_state: BaseState
|
||||||
|
):
|
||||||
|
"""Populate the substates of a state object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
client_token: The client token.
|
||||||
|
state: The state object to populate.
|
||||||
|
root_state: The root state object.
|
||||||
|
"""
|
||||||
|
for substate in state.get_substates():
|
||||||
|
substate_token = _substate_key(client_token, substate)
|
||||||
|
|
||||||
|
substate = await self.load_state(substate_token, root_state)
|
||||||
|
|
||||||
|
state.substates[substate.get_name()] = substate
|
||||||
|
substate.parent_state = state
|
||||||
|
|
||||||
|
@override
|
||||||
|
async def get_state(
|
||||||
|
self,
|
||||||
|
token: str,
|
||||||
|
) -> BaseState:
|
||||||
|
"""Get the state for a token.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
token: The token to get the state for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The state for the token.
|
||||||
|
"""
|
||||||
|
client_token, substate_address = _split_substate_key(token)
|
||||||
|
|
||||||
|
root_state_token = _substate_key(client_token, substate_address.split(".")[0])
|
||||||
|
|
||||||
|
return await self.load_state(
|
||||||
|
root_state_token, self.state(_reflex_internal_init=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def set_state_for_substate(self, client_token: str, substate: BaseState):
|
||||||
|
"""Set the state for a substate.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
client_token: The client token.
|
||||||
|
substate: The substate to set.
|
||||||
|
"""
|
||||||
|
substate_token = _substate_key(client_token, substate)
|
||||||
|
|
||||||
|
self.states[substate_token] = substate
|
||||||
|
|
||||||
|
state_dilled = dill.dumps((state_to_schema(substate), substate))
|
||||||
|
if not self.states_directory.exists():
|
||||||
|
self.states_directory.mkdir(parents=True, exist_ok=True)
|
||||||
|
self.token_path(substate_token).write_bytes(state_dilled)
|
||||||
|
|
||||||
|
for substate_substate in substate.substates.values():
|
||||||
|
await self.set_state_for_substate(client_token, substate_substate)
|
||||||
|
|
||||||
|
@override
|
||||||
|
async def set_state(self, token: str, state: BaseState):
|
||||||
|
"""Set the state for a token.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
token: The token to set the state for.
|
||||||
|
state: The state to set.
|
||||||
|
"""
|
||||||
|
client_token, substate = _split_substate_key(token)
|
||||||
|
await self.set_state_for_substate(client_token, state)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@contextlib.asynccontextmanager
|
||||||
|
async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
|
||||||
|
"""Modify the state for a token while holding exclusive lock.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
token: The token to modify the state for.
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
The state for the token.
|
||||||
|
"""
|
||||||
|
# Memory state manager ignores the substate suffix and always returns the top-level state.
|
||||||
|
client_token, substate = _split_substate_key(token)
|
||||||
|
if client_token not in self._states_locks:
|
||||||
|
async with self._state_manager_lock:
|
||||||
|
if client_token not in self._states_locks:
|
||||||
|
self._states_locks[client_token] = asyncio.Lock()
|
||||||
|
|
||||||
|
async with self._states_locks[client_token]:
|
||||||
|
state = await self.get_state(token)
|
||||||
|
yield state
|
||||||
|
await self.set_state(token, state)
|
||||||
|
|
||||||
|
|
||||||
# Workaround https://github.com/cloudpipe/cloudpickle/issues/408 for dynamic pydantic classes
|
# Workaround https://github.com/cloudpipe/cloudpickle/issues/408 for dynamic pydantic classes
|
||||||
if not isinstance(State.validate.__func__, FunctionType):
|
if not isinstance(State.validate.__func__, FunctionType):
|
||||||
cython_function_or_method = type(State.validate.__func__)
|
cython_function_or_method = type(State.validate.__func__)
|
||||||
@ -2474,15 +2728,6 @@ def _default_lock_expiration() -> int:
|
|||||||
return get_config().redis_lock_expiration
|
return get_config().redis_lock_expiration
|
||||||
|
|
||||||
|
|
||||||
def _default_token_expiration() -> int:
|
|
||||||
"""Get the default token expiration time.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The default token expiration time.
|
|
||||||
"""
|
|
||||||
return get_config().redis_token_expiration
|
|
||||||
|
|
||||||
|
|
||||||
class StateManagerRedis(StateManager):
|
class StateManagerRedis(StateManager):
|
||||||
"""A state manager that stores states in redis."""
|
"""A state manager that stores states in redis."""
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ import reflex.utils.prerequisites
|
|||||||
import reflex.utils.processes
|
import reflex.utils.processes
|
||||||
from reflex.state import (
|
from reflex.state import (
|
||||||
BaseState,
|
BaseState,
|
||||||
|
StateManager,
|
||||||
|
StateManagerDisk,
|
||||||
StateManagerMemory,
|
StateManagerMemory,
|
||||||
StateManagerRedis,
|
StateManagerRedis,
|
||||||
reload_state_module,
|
reload_state_module,
|
||||||
@ -126,7 +128,7 @@ class AppHarness:
|
|||||||
frontend_output_thread: Optional[threading.Thread] = None
|
frontend_output_thread: Optional[threading.Thread] = None
|
||||||
backend_thread: Optional[threading.Thread] = None
|
backend_thread: Optional[threading.Thread] = None
|
||||||
backend: Optional[uvicorn.Server] = None
|
backend: Optional[uvicorn.Server] = None
|
||||||
state_manager: Optional[StateManagerMemory | StateManagerRedis] = None
|
state_manager: Optional[StateManager] = None
|
||||||
_frontends: list["WebDriver"] = dataclasses.field(default_factory=list)
|
_frontends: list["WebDriver"] = dataclasses.field(default_factory=list)
|
||||||
_decorated_pages: list = dataclasses.field(default_factory=list)
|
_decorated_pages: list = dataclasses.field(default_factory=list)
|
||||||
|
|
||||||
@ -290,6 +292,8 @@ class AppHarness:
|
|||||||
if isinstance(self.app_instance._state_manager, StateManagerRedis):
|
if isinstance(self.app_instance._state_manager, StateManagerRedis):
|
||||||
# Create our own redis connection for testing.
|
# Create our own redis connection for testing.
|
||||||
self.state_manager = StateManagerRedis.create(self.app_instance.state)
|
self.state_manager = StateManagerRedis.create(self.app_instance.state)
|
||||||
|
elif isinstance(self.app_instance._state_manager, StateManagerDisk):
|
||||||
|
self.state_manager = StateManagerDisk.create(self.app_instance.state)
|
||||||
else:
|
else:
|
||||||
self.state_manager = self.app_instance._state_manager
|
self.state_manager = self.app_instance._state_manager
|
||||||
|
|
||||||
@ -327,7 +331,8 @@ class AppHarness:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.backend.shutdown = self._get_backend_shutdown_handler()
|
self.backend.shutdown = self._get_backend_shutdown_handler()
|
||||||
self.backend_thread = threading.Thread(target=self.backend.run)
|
with chdir(self.app_path):
|
||||||
|
self.backend_thread = threading.Thread(target=self.backend.run)
|
||||||
self.backend_thread.start()
|
self.backend_thread.start()
|
||||||
|
|
||||||
async def _reset_backend_state_manager(self):
|
async def _reset_backend_state_manager(self):
|
||||||
@ -787,7 +792,7 @@ class AppHarness:
|
|||||||
raise RuntimeError("App is not running.")
|
raise RuntimeError("App is not running.")
|
||||||
state_manager = self.app_instance.state_manager
|
state_manager = self.app_instance.state_manager
|
||||||
assert isinstance(
|
assert isinstance(
|
||||||
state_manager, StateManagerMemory
|
state_manager, (StateManagerMemory, StateManagerDisk)
|
||||||
), "Only works with memory state manager"
|
), "Only works with memory state manager"
|
||||||
if not self._poll_for(
|
if not self._poll_for(
|
||||||
target=lambda: state_manager.states,
|
target=lambda: state_manager.states,
|
||||||
|
@ -79,3 +79,11 @@ class LockExpiredError(ReflexError):
|
|||||||
|
|
||||||
class MatchTypeError(ReflexError, TypeError):
|
class MatchTypeError(ReflexError, TypeError):
|
||||||
"""Raised when the return types of match cases are different."""
|
"""Raised when the return types of match cases are different."""
|
||||||
|
|
||||||
|
|
||||||
|
class EventHandlerArgMismatch(ReflexError, TypeError):
|
||||||
|
"""Raised when the number of args accepted by an EventHandler is differs from that provided by the event trigger."""
|
||||||
|
|
||||||
|
|
||||||
|
class EventFnArgMismatch(ReflexError, TypeError):
|
||||||
|
"""Raised when the number of args accepted by a lambda differs from that provided by the event trigger."""
|
||||||
|
@ -18,22 +18,11 @@ from reflex import constants
|
|||||||
from reflex.config import get_config
|
from reflex.config import get_config
|
||||||
from reflex.utils import console, path_ops
|
from reflex.utils import console, path_ops
|
||||||
from reflex.utils.prerequisites import get_web_dir
|
from reflex.utils.prerequisites import get_web_dir
|
||||||
from reflex.utils.watch import AssetFolderWatch
|
|
||||||
|
|
||||||
# For uvicorn windows bug fix (#2335)
|
# For uvicorn windows bug fix (#2335)
|
||||||
frontend_process = None
|
frontend_process = None
|
||||||
|
|
||||||
|
|
||||||
def start_watching_assets_folder(root):
|
|
||||||
"""Start watching assets folder.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
root: root path of the project.
|
|
||||||
"""
|
|
||||||
asset_watch = AssetFolderWatch(root)
|
|
||||||
asset_watch.start()
|
|
||||||
|
|
||||||
|
|
||||||
def detect_package_change(json_file_path: str) -> str:
|
def detect_package_change(json_file_path: str) -> str:
|
||||||
"""Calculates the SHA-256 hash of a JSON file and returns it as a hexadecimal string.
|
"""Calculates the SHA-256 hash of a JSON file and returns it as a hexadecimal string.
|
||||||
|
|
||||||
@ -149,8 +138,6 @@ def run_frontend(root: Path, port: str, backend_present=True):
|
|||||||
"""
|
"""
|
||||||
from reflex.utils import prerequisites
|
from reflex.utils import prerequisites
|
||||||
|
|
||||||
# Start watching asset folder.
|
|
||||||
start_watching_assets_folder(root)
|
|
||||||
# validate dependencies before run
|
# validate dependencies before run
|
||||||
prerequisites.validate_frontend_dependencies(init=False)
|
prerequisites.validate_frontend_dependencies(init=False)
|
||||||
|
|
||||||
@ -215,7 +202,6 @@ def run_backend(
|
|||||||
log_level=loglevel.value,
|
log_level=loglevel.value,
|
||||||
reload=True,
|
reload=True,
|
||||||
reload_dirs=[config.app_name],
|
reload_dirs=[config.app_name],
|
||||||
reload_excludes=[str(web_dir)],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,6 +81,18 @@ def mkdir(path: str | Path):
|
|||||||
Path(path).mkdir(parents=True, exist_ok=True)
|
Path(path).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def ls(path: str | Path) -> list[Path]:
|
||||||
|
"""List the contents of a directory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: The path to the directory.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of paths to the contents of the directory.
|
||||||
|
"""
|
||||||
|
return list(Path(path).iterdir())
|
||||||
|
|
||||||
|
|
||||||
def ln(src: str | Path, dest: str | Path, overwrite: bool = False) -> bool:
|
def ln(src: str | Path, dest: str | Path, overwrite: bool = False) -> bool:
|
||||||
"""Create a symbolic link.
|
"""Create a symbolic link.
|
||||||
|
|
||||||
@ -197,4 +209,4 @@ def find_replace(directory: str | Path, find: str, replace: str):
|
|||||||
filepath = Path(root, file)
|
filepath = Path(root, file)
|
||||||
text = filepath.read_text(encoding="utf-8")
|
text = filepath.read_text(encoding="utf-8")
|
||||||
text = re.sub(find, replace, text)
|
text = re.sub(find, replace, text)
|
||||||
filepath.write_text(text)
|
filepath.write_text(text, encoding="utf-8")
|
||||||
|
@ -28,6 +28,7 @@ import typer
|
|||||||
from alembic.util.exc import CommandError
|
from alembic.util.exc import CommandError
|
||||||
from packaging import version
|
from packaging import version
|
||||||
from redis import Redis as RedisSync
|
from redis import Redis as RedisSync
|
||||||
|
from redis import exceptions
|
||||||
from redis.asyncio import Redis
|
from redis.asyncio import Redis
|
||||||
|
|
||||||
from reflex import constants, model
|
from reflex import constants, model
|
||||||
@ -344,6 +345,30 @@ def parse_redis_url() -> str | dict | None:
|
|||||||
return dict(host=redis_url, port=int(redis_port), db=0)
|
return dict(host=redis_url, port=int(redis_port), db=0)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_redis_status() -> bool | None:
|
||||||
|
"""Checks the status of the Redis connection.
|
||||||
|
|
||||||
|
Attempts to connect to Redis and send a ping command to verify connectivity.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool or None: The status of the Redis connection:
|
||||||
|
- True: Redis is accessible and responding.
|
||||||
|
- False: Redis is not accessible due to a connection error.
|
||||||
|
- None: Redis not used i.e redis_url is not set in rxconfig.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
status = True
|
||||||
|
redis_client = get_redis_sync()
|
||||||
|
if redis_client is not None:
|
||||||
|
redis_client.ping()
|
||||||
|
else:
|
||||||
|
status = None
|
||||||
|
except exceptions.RedisError:
|
||||||
|
status = False
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
def validate_app_name(app_name: str | None = None) -> str:
|
def validate_app_name(app_name: str | None = None) -> str:
|
||||||
"""Validate the app name.
|
"""Validate the app name.
|
||||||
|
|
||||||
|
@ -245,9 +245,14 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
|
|||||||
"""
|
"""
|
||||||
from reflex.model import Model
|
from reflex.model import Model
|
||||||
|
|
||||||
attr = getattr(cls, name, None)
|
try:
|
||||||
|
attr = getattr(cls, name, None)
|
||||||
|
except NotImplementedError:
|
||||||
|
attr = None
|
||||||
|
|
||||||
if hint := get_property_hint(attr):
|
if hint := get_property_hint(attr):
|
||||||
return hint
|
return hint
|
||||||
|
|
||||||
if (
|
if (
|
||||||
hasattr(cls, "__fields__")
|
hasattr(cls, "__fields__")
|
||||||
and name in cls.__fields__
|
and name in cls.__fields__
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
"""General utility functions."""
|
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import time
|
|
||||||
|
|
||||||
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
|
||||||
from watchdog.observers import Observer
|
|
||||||
|
|
||||||
from reflex.constants import Dirs
|
|
||||||
from reflex.utils.prerequisites import get_web_dir
|
|
||||||
|
|
||||||
|
|
||||||
class AssetFolderWatch:
|
|
||||||
"""Asset folder watch class."""
|
|
||||||
|
|
||||||
def __init__(self, root):
|
|
||||||
"""Initialize the Watch Class.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
root: root path of the public.
|
|
||||||
"""
|
|
||||||
self.path = str(root / Dirs.APP_ASSETS)
|
|
||||||
self.event_handler = AssetFolderHandler(root)
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
"""Start watching asset folder."""
|
|
||||||
self.observer = Observer()
|
|
||||||
self.observer.schedule(self.event_handler, self.path, recursive=True)
|
|
||||||
self.observer.start()
|
|
||||||
|
|
||||||
|
|
||||||
class AssetFolderHandler(FileSystemEventHandler):
|
|
||||||
"""Asset folder event handler."""
|
|
||||||
|
|
||||||
def __init__(self, root):
|
|
||||||
"""Initialize the AssetFolderHandler Class.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
root: root path of the public.
|
|
||||||
"""
|
|
||||||
super().__init__()
|
|
||||||
self.root = root
|
|
||||||
|
|
||||||
def on_modified(self, event: FileSystemEvent):
|
|
||||||
"""Event handler when a file or folder was modified.
|
|
||||||
|
|
||||||
This is called every time after a file is created, modified and deleted.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
event: Event information.
|
|
||||||
"""
|
|
||||||
dest_path = self.get_dest_path(event.src_path)
|
|
||||||
|
|
||||||
# wait 1 sec for fully saved
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
if os.path.isfile(event.src_path):
|
|
||||||
with contextlib.suppress(PermissionError):
|
|
||||||
shutil.copyfile(event.src_path, dest_path)
|
|
||||||
if os.path.isdir(event.src_path):
|
|
||||||
if os.path.exists(dest_path):
|
|
||||||
shutil.rmtree(dest_path)
|
|
||||||
with contextlib.suppress(PermissionError):
|
|
||||||
shutil.copytree(event.src_path, dest_path)
|
|
||||||
|
|
||||||
def on_deleted(self, event: FileSystemEvent):
|
|
||||||
"""Event hander when a file or folder was deleted.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
event: Event infomation.
|
|
||||||
"""
|
|
||||||
dest_path = self.get_dest_path(event.src_path)
|
|
||||||
|
|
||||||
if os.path.isfile(dest_path):
|
|
||||||
# when event is about a file, pass
|
|
||||||
# this will be deleted at on_modified function
|
|
||||||
return
|
|
||||||
|
|
||||||
if os.path.exists(dest_path):
|
|
||||||
shutil.rmtree(dest_path)
|
|
||||||
|
|
||||||
def get_dest_path(self, src_path: str) -> str:
|
|
||||||
"""Get public file path.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
src_path: The asset file path.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The public file path.
|
|
||||||
"""
|
|
||||||
return src_path.replace(
|
|
||||||
str(self.root / Dirs.APP_ASSETS),
|
|
||||||
str(self.root / get_web_dir() / Dirs.PUBLIC),
|
|
||||||
)
|
|
@ -6,7 +6,6 @@ import contextlib
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
import datetime
|
import datetime
|
||||||
import dis
|
import dis
|
||||||
import functools
|
|
||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
@ -2532,7 +2531,6 @@ def computed_var(
|
|||||||
auto_deps: bool = True,
|
auto_deps: bool = True,
|
||||||
interval: Optional[Union[datetime.timedelta, int]] = None,
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
||||||
backend: bool | None = None,
|
backend: bool | None = None,
|
||||||
_deprecated_cached_var: bool = False,
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> ComputedVar | Callable[[Callable[[BaseState], Any]], ComputedVar]:
|
) -> ComputedVar | Callable[[Callable[[BaseState], Any]], ComputedVar]:
|
||||||
"""A ComputedVar decorator with or without kwargs.
|
"""A ComputedVar decorator with or without kwargs.
|
||||||
@ -2545,7 +2543,6 @@ def computed_var(
|
|||||||
auto_deps: Whether var dependencies should be auto-determined.
|
auto_deps: Whether var dependencies should be auto-determined.
|
||||||
interval: Interval at which the computed var should be updated.
|
interval: Interval at which the computed var should be updated.
|
||||||
backend: Whether the computed var is a backend var.
|
backend: Whether the computed var is a backend var.
|
||||||
_deprecated_cached_var: Indicate usage of deprecated cached_var partial function.
|
|
||||||
**kwargs: additional attributes to set on the instance
|
**kwargs: additional attributes to set on the instance
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -2555,14 +2552,6 @@ def computed_var(
|
|||||||
ValueError: If caching is disabled and an update interval is set.
|
ValueError: If caching is disabled and an update interval is set.
|
||||||
VarDependencyError: If user supplies dependencies without caching.
|
VarDependencyError: If user supplies dependencies without caching.
|
||||||
"""
|
"""
|
||||||
if _deprecated_cached_var:
|
|
||||||
console.deprecate(
|
|
||||||
feature_name="cached_var",
|
|
||||||
reason=("Use @rx.var(cache=True) instead of @rx.cached_var."),
|
|
||||||
deprecation_version="0.5.6",
|
|
||||||
removal_version="0.6.0",
|
|
||||||
)
|
|
||||||
|
|
||||||
if cache is False and interval is not None:
|
if cache is False and interval is not None:
|
||||||
raise ValueError("Cannot set update interval without caching.")
|
raise ValueError("Cannot set update interval without caching.")
|
||||||
|
|
||||||
@ -2587,9 +2576,6 @@ def computed_var(
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
# Partial function of computed_var with cache=True
|
|
||||||
cached_var = functools.partial(computed_var, cache=True, _deprecated_cached_var=True)
|
|
||||||
|
|
||||||
VAR_CALLABLE = Callable[[Any], Var]
|
VAR_CALLABLE = Callable[[Any], Var]
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,11 +67,11 @@ def test_color(color, expected):
|
|||||||
[
|
[
|
||||||
(
|
(
|
||||||
rx.cond(True, rx.color("mint"), rx.color("tomato", 5)),
|
rx.cond(True, rx.color("mint"), rx.color("tomato", 5)),
|
||||||
'(Boolean(true) ? "var(--mint-7)" : "var(--tomato-5)")',
|
'(true ? "var(--mint-7)" : "var(--tomato-5)")',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
rx.cond(True, rx.color(ColorState.color), rx.color(ColorState.color, 5)), # type: ignore
|
rx.cond(True, rx.color(ColorState.color), rx.color(ColorState.color, 5)), # type: ignore
|
||||||
f'(Boolean(true) ? ("var(--"+{str(color_state_name)}.color+"-7)") : ("var(--"+{str(color_state_name)}.color+"-5)"))',
|
f'(true ? ("var(--"+{str(color_state_name)}.color+"-7)") : ("var(--"+{str(color_state_name)}.color+"-5)"))',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
rx.match(
|
rx.match(
|
||||||
|
@ -23,7 +23,7 @@ def cond_state(request):
|
|||||||
def test_f_string_cond_interpolation():
|
def test_f_string_cond_interpolation():
|
||||||
# make sure backticks inside interpolation don't get escaped
|
# make sure backticks inside interpolation don't get escaped
|
||||||
var = LiteralVar.create(f"x {cond(True, 'a', 'b')}")
|
var = LiteralVar.create(f"x {cond(True, 'a', 'b')}")
|
||||||
assert str(var) == '("x "+(Boolean(true) ? "a" : "b"))'
|
assert str(var) == '("x "+(true ? "a" : "b"))'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -97,7 +97,7 @@ def test_prop_cond(c1: Any, c2: Any):
|
|||||||
c1 = json.dumps(c1)
|
c1 = json.dumps(c1)
|
||||||
if not isinstance(c2, Var):
|
if not isinstance(c2, Var):
|
||||||
c2 = json.dumps(c2)
|
c2 = json.dumps(c2)
|
||||||
assert str(prop_cond) == f"(Boolean(true) ? {c1} : {c2})"
|
assert str(prop_cond) == f"(true ? {c1} : {c2})"
|
||||||
|
|
||||||
|
|
||||||
def test_cond_no_mix():
|
def test_cond_no_mix():
|
||||||
@ -141,8 +141,7 @@ def test_cond_computed_var():
|
|||||||
|
|
||||||
state_name = format_state_name(CondStateComputed.get_full_name())
|
state_name = format_state_name(CondStateComputed.get_full_name())
|
||||||
assert (
|
assert (
|
||||||
str(comp)
|
str(comp) == f"(true ? {state_name}.computed_int : {state_name}.computed_str)"
|
||||||
== f"(Boolean(true) ? {state_name}.computed_int : {state_name}.computed_str)"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert comp._var_type == Union[int, str]
|
assert comp._var_type == Union[int, str]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from reflex.components.lucide.icon import LUCIDE_ICON_LIST, RENAMED_ICONS_05, Icon
|
from reflex.components.lucide.icon import LUCIDE_ICON_LIST, Icon
|
||||||
from reflex.utils import format
|
from reflex.utils import format
|
||||||
|
|
||||||
|
|
||||||
@ -10,16 +10,6 @@ def test_icon(tag):
|
|||||||
assert icon.alias == f"Lucide{format.to_title_case(tag)}Icon"
|
assert icon.alias == f"Lucide{format.to_title_case(tag)}Icon"
|
||||||
|
|
||||||
|
|
||||||
RENAMED_TAGS = [tag for tag in RENAMED_ICONS_05.items()]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("tag, new_tag", RENAMED_TAGS)
|
|
||||||
def test_icon_renamed_tags(tag, new_tag):
|
|
||||||
Icon.create(tag)
|
|
||||||
# TODO: need a PR so we can pass the following test. Currently it fails and uses the old tag as the import.
|
|
||||||
# assert icon.alias == f"Lucide{format.to_title_case(new_tag)}Icon"
|
|
||||||
|
|
||||||
|
|
||||||
def test_icon_missing_tag():
|
def test_icon_missing_tag():
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
_ = Icon.create()
|
_ = Icon.create()
|
||||||
|
@ -22,6 +22,7 @@ from reflex.ivars.base import LiteralVar
|
|||||||
from reflex.state import BaseState
|
from reflex.state import BaseState
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from reflex.utils import imports
|
from reflex.utils import imports
|
||||||
|
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch
|
||||||
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
||||||
from reflex.vars import BaseVar, Var, VarData
|
from reflex.vars import BaseVar, Var, VarData
|
||||||
|
|
||||||
@ -79,6 +80,8 @@ def component2() -> Type[Component]:
|
|||||||
# A test list prop.
|
# A test list prop.
|
||||||
arr: Var[List[str]]
|
arr: Var[List[str]]
|
||||||
|
|
||||||
|
on_prop_event: EventHandler[lambda e0: [e0]]
|
||||||
|
|
||||||
def get_event_triggers(self) -> Dict[str, Any]:
|
def get_event_triggers(self) -> Dict[str, Any]:
|
||||||
"""Test controlled triggers.
|
"""Test controlled triggers.
|
||||||
|
|
||||||
@ -496,7 +499,7 @@ def test_get_props(component1, component2):
|
|||||||
component2: A test component.
|
component2: A test component.
|
||||||
"""
|
"""
|
||||||
assert component1.get_props() == {"text", "number", "text_or_number"}
|
assert component1.get_props() == {"text", "number", "text_or_number"}
|
||||||
assert component2.get_props() == {"arr"}
|
assert component2.get_props() == {"arr", "on_prop_event"}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -574,7 +577,7 @@ def test_get_event_triggers(component1, component2):
|
|||||||
assert component1().get_event_triggers().keys() == default_triggers
|
assert component1().get_event_triggers().keys() == default_triggers
|
||||||
assert (
|
assert (
|
||||||
component2().get_event_triggers().keys()
|
component2().get_event_triggers().keys()
|
||||||
== {"on_open", "on_close"} | default_triggers
|
== {"on_open", "on_close", "on_prop_event"} | default_triggers
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -888,18 +891,105 @@ def test_invalid_event_handler_args(component2, test_state):
|
|||||||
component2: A test component.
|
component2: A test component.
|
||||||
test_state: A test state.
|
test_state: A test state.
|
||||||
"""
|
"""
|
||||||
# Uncontrolled event handlers should not take args.
|
# EventHandler args must match
|
||||||
# This is okay.
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
component2.create(on_click=test_state.do_something)
|
|
||||||
# This is not okay.
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
component2.create(on_click=test_state.do_something_arg)
|
component2.create(on_click=test_state.do_something_arg)
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
component2.create(on_open=test_state.do_something)
|
component2.create(on_open=test_state.do_something)
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(on_prop_event=test_state.do_something)
|
||||||
|
|
||||||
|
# Multiple EventHandler args: all must match
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(
|
||||||
|
on_click=[test_state.do_something_arg, test_state.do_something]
|
||||||
|
)
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
component2.create(
|
component2.create(
|
||||||
on_open=[test_state.do_something_arg, test_state.do_something]
|
on_open=[test_state.do_something_arg, test_state.do_something]
|
||||||
)
|
)
|
||||||
# However lambdas are okay.
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(
|
||||||
|
on_prop_event=[test_state.do_something_arg, test_state.do_something]
|
||||||
|
)
|
||||||
|
|
||||||
|
# lambda cannot return weird values.
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
component2.create(on_click=lambda: 1)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
component2.create(on_click=lambda: [1])
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
component2.create(
|
||||||
|
on_click=lambda: (test_state.do_something_arg(1), test_state.do_something)
|
||||||
|
)
|
||||||
|
|
||||||
|
# lambda signature must match event trigger.
|
||||||
|
with pytest.raises(EventFnArgMismatch):
|
||||||
|
component2.create(on_click=lambda _: test_state.do_something_arg(1))
|
||||||
|
with pytest.raises(EventFnArgMismatch):
|
||||||
|
component2.create(on_open=lambda: test_state.do_something)
|
||||||
|
with pytest.raises(EventFnArgMismatch):
|
||||||
|
component2.create(on_prop_event=lambda: test_state.do_something)
|
||||||
|
|
||||||
|
# lambda returning EventHandler must match spec
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(on_click=lambda: test_state.do_something_arg)
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(on_open=lambda _: test_state.do_something)
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(on_prop_event=lambda _: test_state.do_something)
|
||||||
|
|
||||||
|
# Mixed EventSpec and EventHandler must match spec.
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(
|
||||||
|
on_click=lambda: [
|
||||||
|
test_state.do_something_arg(1),
|
||||||
|
test_state.do_something_arg,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(
|
||||||
|
on_open=lambda _: [test_state.do_something_arg(1), test_state.do_something]
|
||||||
|
)
|
||||||
|
with pytest.raises(EventHandlerArgMismatch):
|
||||||
|
component2.create(
|
||||||
|
on_prop_event=lambda _: [
|
||||||
|
test_state.do_something_arg(1),
|
||||||
|
test_state.do_something,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_event_handler_args(component2, test_state):
|
||||||
|
"""Test that an valid event handler args do not raise exception.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
component2: A test component.
|
||||||
|
test_state: A test state.
|
||||||
|
"""
|
||||||
|
# Uncontrolled event handlers should not take args.
|
||||||
|
component2.create(on_click=test_state.do_something)
|
||||||
|
component2.create(on_click=test_state.do_something_arg(1))
|
||||||
|
|
||||||
|
# Controlled event handlers should take args.
|
||||||
|
component2.create(on_open=test_state.do_something_arg)
|
||||||
|
component2.create(on_prop_event=test_state.do_something_arg)
|
||||||
|
|
||||||
|
# Using a partial event spec bypasses arg validation (ignoring the args).
|
||||||
|
component2.create(on_open=test_state.do_something())
|
||||||
|
component2.create(on_prop_event=test_state.do_something())
|
||||||
|
|
||||||
|
# lambda returning EventHandler is okay if the spec matches.
|
||||||
|
component2.create(on_click=lambda: test_state.do_something)
|
||||||
|
component2.create(on_open=lambda _: test_state.do_something_arg)
|
||||||
|
component2.create(on_prop_event=lambda _: test_state.do_something_arg)
|
||||||
|
|
||||||
|
# lambda can always return an EventSpec.
|
||||||
component2.create(on_click=lambda: test_state.do_something_arg(1))
|
component2.create(on_click=lambda: test_state.do_something_arg(1))
|
||||||
|
component2.create(on_open=lambda _: test_state.do_something_arg(1))
|
||||||
|
component2.create(on_prop_event=lambda _: test_state.do_something_arg(1))
|
||||||
|
|
||||||
|
# Return EventSpec and EventHandler (no arg).
|
||||||
component2.create(
|
component2.create(
|
||||||
on_click=lambda: [test_state.do_something_arg(1), test_state.do_something]
|
on_click=lambda: [test_state.do_something_arg(1), test_state.do_something]
|
||||||
)
|
)
|
||||||
@ -907,9 +997,24 @@ def test_invalid_event_handler_args(component2, test_state):
|
|||||||
on_click=lambda: [test_state.do_something_arg(1), test_state.do_something()]
|
on_click=lambda: [test_state.do_something_arg(1), test_state.do_something()]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Controlled event handlers should take args.
|
# Return 2 EventSpec.
|
||||||
# This is okay.
|
component2.create(
|
||||||
component2.create(on_open=test_state.do_something_arg)
|
on_open=lambda _: [test_state.do_something_arg(1), test_state.do_something()]
|
||||||
|
)
|
||||||
|
component2.create(
|
||||||
|
on_prop_event=lambda _: [
|
||||||
|
test_state.do_something_arg(1),
|
||||||
|
test_state.do_something(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return EventHandler (1 arg) and EventSpec.
|
||||||
|
component2.create(
|
||||||
|
on_open=lambda _: [test_state.do_something_arg, test_state.do_something()]
|
||||||
|
)
|
||||||
|
component2.create(
|
||||||
|
on_prop_event=lambda _: [test_state.do_something_arg, test_state.do_something()]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_get_hooks_nested(component1, component2, component3):
|
def test_get_hooks_nested(component1, component2, component3):
|
||||||
@ -1613,62 +1718,6 @@ def test_rename_props():
|
|||||||
assert 'renamed_prop3={"prop3_2"}' in rendered_c2["props"]
|
assert 'renamed_prop3={"prop3_2"}' in rendered_c2["props"]
|
||||||
|
|
||||||
|
|
||||||
def test_deprecated_props(capsys):
|
|
||||||
"""Assert that deprecated underscore suffix props are translated.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
capsys: Pytest fixture for capturing stdout and stderr.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class C1(Component):
|
|
||||||
tag = "C1"
|
|
||||||
|
|
||||||
type: Var[str]
|
|
||||||
min: Var[str]
|
|
||||||
max: Var[str]
|
|
||||||
|
|
||||||
# No warnings are emitted when using the new prop names.
|
|
||||||
c1_1 = C1.create(type="type1", min="min1", max="max1")
|
|
||||||
out_err = capsys.readouterr()
|
|
||||||
assert not out_err.err
|
|
||||||
assert not out_err.out
|
|
||||||
|
|
||||||
c1_1_render = c1_1.render()
|
|
||||||
assert 'type={"type1"}' in c1_1_render["props"]
|
|
||||||
assert 'min={"min1"}' in c1_1_render["props"]
|
|
||||||
assert 'max={"max1"}' in c1_1_render["props"]
|
|
||||||
|
|
||||||
# Deprecation warning is emitted with underscore suffix,
|
|
||||||
# but the component still works.
|
|
||||||
c1_2 = C1.create(type_="type2", min_="min2", max_="max2")
|
|
||||||
out_err = capsys.readouterr()
|
|
||||||
assert out_err.out.count("DeprecationWarning:") == 3
|
|
||||||
assert not out_err.err
|
|
||||||
|
|
||||||
c1_2_render = c1_2.render()
|
|
||||||
assert 'type={"type2"}' in c1_2_render["props"]
|
|
||||||
assert 'min={"min2"}' in c1_2_render["props"]
|
|
||||||
assert 'max={"max2"}' in c1_2_render["props"]
|
|
||||||
|
|
||||||
class C2(Component):
|
|
||||||
tag = "C2"
|
|
||||||
|
|
||||||
type_: Var[str]
|
|
||||||
min_: Var[str]
|
|
||||||
max_: Var[str]
|
|
||||||
|
|
||||||
# No warnings are emitted if the actual prop has an underscore suffix
|
|
||||||
c2_1 = C2.create(type_="type1", min_="min1", max_="max1")
|
|
||||||
out_err = capsys.readouterr()
|
|
||||||
assert not out_err.err
|
|
||||||
assert not out_err.out
|
|
||||||
|
|
||||||
c2_1_render = c2_1.render()
|
|
||||||
assert 'type={"type1"}' in c2_1_render["props"]
|
|
||||||
assert 'min={"min1"}' in c2_1_render["props"]
|
|
||||||
assert 'max={"max1"}' in c2_1_render["props"]
|
|
||||||
|
|
||||||
|
|
||||||
def test_custom_component_get_imports():
|
def test_custom_component_get_imports():
|
||||||
class Inner(Component):
|
class Inner(Component):
|
||||||
tag = "Inner"
|
tag = "Inner"
|
||||||
|
@ -42,6 +42,7 @@ from reflex.state import (
|
|||||||
OnLoadInternalState,
|
OnLoadInternalState,
|
||||||
RouterData,
|
RouterData,
|
||||||
State,
|
State,
|
||||||
|
StateManagerDisk,
|
||||||
StateManagerMemory,
|
StateManagerMemory,
|
||||||
StateManagerRedis,
|
StateManagerRedis,
|
||||||
StateUpdate,
|
StateUpdate,
|
||||||
@ -1395,7 +1396,9 @@ def test_app_state_manager():
|
|||||||
app.state_manager
|
app.state_manager
|
||||||
app._enable_state()
|
app._enable_state()
|
||||||
assert app.state_manager is not None
|
assert app.state_manager is not None
|
||||||
assert isinstance(app.state_manager, (StateManagerMemory, StateManagerRedis))
|
assert isinstance(
|
||||||
|
app.state_manager, (StateManagerMemory, StateManagerDisk, StateManagerRedis)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_generate_component():
|
def test_generate_component():
|
||||||
|
@ -94,6 +94,15 @@ class SQLAClass(SQLABase):
|
|||||||
"""
|
"""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@hybrid_property
|
||||||
|
def first_label(self) -> Optional[SQLALabel]:
|
||||||
|
"""First label property.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
First label
|
||||||
|
"""
|
||||||
|
return self.labels[0] if self.labels else None
|
||||||
|
|
||||||
|
|
||||||
class ModelClass(rx.Model):
|
class ModelClass(rx.Model):
|
||||||
"""Test reflex model."""
|
"""Test reflex model."""
|
||||||
@ -125,6 +134,15 @@ class ModelClass(rx.Model):
|
|||||||
"""
|
"""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def first_label(self) -> Optional[SQLALabel]:
|
||||||
|
"""First label property.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
First label
|
||||||
|
"""
|
||||||
|
return self.labels[0] if self.labels else None
|
||||||
|
|
||||||
|
|
||||||
class BaseClass(rx.Base):
|
class BaseClass(rx.Base):
|
||||||
"""Test rx.Base class."""
|
"""Test rx.Base class."""
|
||||||
@ -156,6 +174,15 @@ class BaseClass(rx.Base):
|
|||||||
"""
|
"""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def first_label(self) -> Optional[SQLALabel]:
|
||||||
|
"""First label property.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
First label
|
||||||
|
"""
|
||||||
|
return self.labels[0] if self.labels else None
|
||||||
|
|
||||||
|
|
||||||
class BareClass:
|
class BareClass:
|
||||||
"""Bare python class."""
|
"""Bare python class."""
|
||||||
@ -187,6 +214,15 @@ class BareClass:
|
|||||||
"""
|
"""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def first_label(self) -> Optional[SQLALabel]:
|
||||||
|
"""First label property.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
First label
|
||||||
|
"""
|
||||||
|
return self.labels[0] if self.labels else None
|
||||||
|
|
||||||
|
|
||||||
@attrs.define
|
@attrs.define
|
||||||
class AttrClass:
|
class AttrClass:
|
||||||
@ -219,6 +255,15 @@ class AttrClass:
|
|||||||
"""
|
"""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def first_label(self) -> Optional[SQLALabel]:
|
||||||
|
"""First label property.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
First label
|
||||||
|
"""
|
||||||
|
return self.labels[0] if self.labels else None
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
params=[
|
params=[
|
||||||
@ -254,6 +299,7 @@ def cls(request: pytest.FixtureRequest) -> type:
|
|||||||
pytest.param("dict_str_str", Dict[str, str], id="Dict[str, str]"),
|
pytest.param("dict_str_str", Dict[str, str], id="Dict[str, str]"),
|
||||||
pytest.param("str_property", str, id="str_property"),
|
pytest.param("str_property", str, id="str_property"),
|
||||||
pytest.param("str_or_int_property", Union[str, int], id="str_or_int_property"),
|
pytest.param("str_or_int_property", Union[str, int], id="str_or_int_property"),
|
||||||
|
pytest.param("first_label", Optional[SQLALabel], id="first_label"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_get_attribute_access_type(cls: type, attr: str, expected: GenericType) -> None:
|
def test_get_attribute_access_type(cls: type, attr: str, expected: GenericType) -> None:
|
||||||
|
106
tests/test_health_endpoint.py
Normal file
106
tests/test_health_endpoint.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import json
|
||||||
|
from unittest.mock import MagicMock, Mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import sqlalchemy
|
||||||
|
from redis.exceptions import RedisError
|
||||||
|
|
||||||
|
from reflex.app import health
|
||||||
|
from reflex.model import get_db_status
|
||||||
|
from reflex.utils.prerequisites import get_redis_status
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"mock_redis_client, expected_status",
|
||||||
|
[
|
||||||
|
# Case 1: Redis client is available and responds to ping
|
||||||
|
(Mock(ping=lambda: None), True),
|
||||||
|
# Case 2: Redis client raises RedisError
|
||||||
|
(Mock(ping=lambda: (_ for _ in ()).throw(RedisError)), False),
|
||||||
|
# Case 3: Redis client is not used
|
||||||
|
(None, None),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_get_redis_status(mock_redis_client, expected_status, mocker):
|
||||||
|
# Mock the `get_redis_sync` function to return the mock Redis client
|
||||||
|
mock_get_redis_sync = mocker.patch(
|
||||||
|
"reflex.utils.prerequisites.get_redis_sync", return_value=mock_redis_client
|
||||||
|
)
|
||||||
|
|
||||||
|
# Call the function
|
||||||
|
status = await get_redis_status()
|
||||||
|
|
||||||
|
# Verify the result
|
||||||
|
assert status == expected_status
|
||||||
|
mock_get_redis_sync.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"mock_engine, execute_side_effect, expected_status",
|
||||||
|
[
|
||||||
|
# Case 1: Database is accessible
|
||||||
|
(MagicMock(), None, True),
|
||||||
|
# Case 2: Database connection error (OperationalError)
|
||||||
|
(
|
||||||
|
MagicMock(),
|
||||||
|
sqlalchemy.exc.OperationalError("error", "error", "error"),
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_get_db_status(mock_engine, execute_side_effect, expected_status, mocker):
|
||||||
|
# Mock get_engine to return the mock_engine
|
||||||
|
mock_get_engine = mocker.patch("reflex.model.get_engine", return_value=mock_engine)
|
||||||
|
|
||||||
|
# Mock the connection and its execute method
|
||||||
|
if mock_engine:
|
||||||
|
mock_connection = mock_engine.connect.return_value.__enter__.return_value
|
||||||
|
if execute_side_effect:
|
||||||
|
# Simulate execute method raising an exception
|
||||||
|
mock_connection.execute.side_effect = execute_side_effect
|
||||||
|
else:
|
||||||
|
# Simulate successful execute call
|
||||||
|
mock_connection.execute.return_value = None
|
||||||
|
|
||||||
|
# Call the function
|
||||||
|
status = await get_db_status()
|
||||||
|
|
||||||
|
# Verify the result
|
||||||
|
assert status == expected_status
|
||||||
|
mock_get_engine.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"db_status, redis_status, expected_status, expected_code",
|
||||||
|
[
|
||||||
|
# Case 1: Both services are connected
|
||||||
|
(True, True, {"status": True, "db": True, "redis": True}, 200),
|
||||||
|
# Case 2: Database not connected, Redis connected
|
||||||
|
(False, True, {"status": False, "db": False, "redis": True}, 503),
|
||||||
|
# Case 3: Database connected, Redis not connected
|
||||||
|
(True, False, {"status": False, "db": True, "redis": False}, 503),
|
||||||
|
# Case 4: Both services not connected
|
||||||
|
(False, False, {"status": False, "db": False, "redis": False}, 503),
|
||||||
|
# Case 5: Database Connected, Redis not used
|
||||||
|
(True, None, {"status": True, "db": True, "redis": False}, 200),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_health(db_status, redis_status, expected_status, expected_code, mocker):
|
||||||
|
# Mock get_db_status and get_redis_status
|
||||||
|
mocker.patch("reflex.app.get_db_status", return_value=db_status)
|
||||||
|
mocker.patch(
|
||||||
|
"reflex.utils.prerequisites.get_redis_status", return_value=redis_status
|
||||||
|
)
|
||||||
|
|
||||||
|
# Call the async health function
|
||||||
|
response = await health()
|
||||||
|
|
||||||
|
print(json.loads(response.body))
|
||||||
|
print(expected_status)
|
||||||
|
|
||||||
|
# Verify the response content and status code
|
||||||
|
assert response.status_code == expected_code
|
||||||
|
assert json.loads(response.body) == expected_status
|
@ -31,6 +31,7 @@ from reflex.state import (
|
|||||||
RouterData,
|
RouterData,
|
||||||
State,
|
State,
|
||||||
StateManager,
|
StateManager,
|
||||||
|
StateManagerDisk,
|
||||||
StateManagerMemory,
|
StateManagerMemory,
|
||||||
StateManagerRedis,
|
StateManagerRedis,
|
||||||
StateProxy,
|
StateProxy,
|
||||||
@ -1586,7 +1587,7 @@ async def test_state_with_invalid_yield(capsys, mock_app):
|
|||||||
assert "must only return/yield: None, Events or other EventHandlers" in captured.out
|
assert "must only return/yield: None, Events or other EventHandlers" in captured.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function", params=["in_process", "redis"])
|
@pytest.fixture(scope="function", params=["in_process", "disk", "redis"])
|
||||||
def state_manager(request) -> Generator[StateManager, None, None]:
|
def state_manager(request) -> Generator[StateManager, None, None]:
|
||||||
"""Instance of state manager parametrized for redis and in-process.
|
"""Instance of state manager parametrized for redis and in-process.
|
||||||
|
|
||||||
@ -1600,8 +1601,11 @@ def state_manager(request) -> Generator[StateManager, None, None]:
|
|||||||
if request.param == "redis":
|
if request.param == "redis":
|
||||||
if not isinstance(state_manager, StateManagerRedis):
|
if not isinstance(state_manager, StateManagerRedis):
|
||||||
pytest.skip("Test requires redis")
|
pytest.skip("Test requires redis")
|
||||||
else:
|
elif request.param == "disk":
|
||||||
# explicitly NOT using redis
|
# explicitly NOT using redis
|
||||||
|
state_manager = StateManagerDisk(state=TestState)
|
||||||
|
assert not state_manager._states_locks
|
||||||
|
else:
|
||||||
state_manager = StateManagerMemory(state=TestState)
|
state_manager = StateManagerMemory(state=TestState)
|
||||||
assert not state_manager._states_locks
|
assert not state_manager._states_locks
|
||||||
|
|
||||||
@ -1639,7 +1643,7 @@ async def test_state_manager_modify_state(
|
|||||||
async with state_manager.modify_state(substate_token) as state:
|
async with state_manager.modify_state(substate_token) as state:
|
||||||
if isinstance(state_manager, StateManagerRedis):
|
if isinstance(state_manager, StateManagerRedis):
|
||||||
assert await state_manager.redis.get(f"{token}_lock")
|
assert await state_manager.redis.get(f"{token}_lock")
|
||||||
elif isinstance(state_manager, StateManagerMemory):
|
elif isinstance(state_manager, (StateManagerMemory, StateManagerDisk)):
|
||||||
assert token in state_manager._states_locks
|
assert token in state_manager._states_locks
|
||||||
assert state_manager._states_locks[token].locked()
|
assert state_manager._states_locks[token].locked()
|
||||||
# Should be able to write proxy objects inside mutables
|
# Should be able to write proxy objects inside mutables
|
||||||
@ -1649,11 +1653,11 @@ async def test_state_manager_modify_state(
|
|||||||
# lock should be dropped after exiting the context
|
# lock should be dropped after exiting the context
|
||||||
if isinstance(state_manager, StateManagerRedis):
|
if isinstance(state_manager, StateManagerRedis):
|
||||||
assert (await state_manager.redis.get(f"{token}_lock")) is None
|
assert (await state_manager.redis.get(f"{token}_lock")) is None
|
||||||
elif isinstance(state_manager, StateManagerMemory):
|
elif isinstance(state_manager, (StateManagerMemory, StateManagerDisk)):
|
||||||
assert not state_manager._states_locks[token].locked()
|
assert not state_manager._states_locks[token].locked()
|
||||||
|
|
||||||
# separate instances should NOT share locks
|
# separate instances should NOT share locks
|
||||||
sm2 = StateManagerMemory(state=TestState)
|
sm2 = state_manager.__class__(state=TestState)
|
||||||
assert sm2._state_manager_lock is state_manager._state_manager_lock
|
assert sm2._state_manager_lock is state_manager._state_manager_lock
|
||||||
assert not sm2._states_locks
|
assert not sm2._states_locks
|
||||||
if state_manager._states_locks:
|
if state_manager._states_locks:
|
||||||
@ -1691,7 +1695,7 @@ async def test_state_manager_contend(
|
|||||||
|
|
||||||
if isinstance(state_manager, StateManagerRedis):
|
if isinstance(state_manager, StateManagerRedis):
|
||||||
assert (await state_manager.redis.get(f"{token}_lock")) is None
|
assert (await state_manager.redis.get(f"{token}_lock")) is None
|
||||||
elif isinstance(state_manager, StateManagerMemory):
|
elif isinstance(state_manager, (StateManagerMemory, StateManagerDisk)):
|
||||||
assert token in state_manager._states_locks
|
assert token in state_manager._states_locks
|
||||||
assert not state_manager._states_locks[token].locked()
|
assert not state_manager._states_locks[token].locked()
|
||||||
|
|
||||||
@ -1831,7 +1835,7 @@ async def test_state_proxy(grandchild_state: GrandchildState, mock_app: rx.App):
|
|||||||
assert child_state is not None
|
assert child_state is not None
|
||||||
parent_state = child_state.parent_state
|
parent_state = child_state.parent_state
|
||||||
assert parent_state is not None
|
assert parent_state is not None
|
||||||
if isinstance(mock_app.state_manager, StateManagerMemory):
|
if isinstance(mock_app.state_manager, (StateManagerMemory, StateManagerDisk)):
|
||||||
mock_app.state_manager.states[parent_state.router.session.client_token] = (
|
mock_app.state_manager.states[parent_state.router.session.client_token] = (
|
||||||
parent_state
|
parent_state
|
||||||
)
|
)
|
||||||
@ -1874,7 +1878,7 @@ async def test_state_proxy(grandchild_state: GrandchildState, mock_app: rx.App):
|
|||||||
# For in-process store, only one instance of the state exists
|
# For in-process store, only one instance of the state exists
|
||||||
assert sp.__wrapped__ is grandchild_state
|
assert sp.__wrapped__ is grandchild_state
|
||||||
else:
|
else:
|
||||||
# When redis is used, a new+updated instance is assigned to the proxy
|
# When redis or disk is used, a new+updated instance is assigned to the proxy
|
||||||
assert sp.__wrapped__ is not grandchild_state
|
assert sp.__wrapped__ is not grandchild_state
|
||||||
sp.value2 = "42"
|
sp.value2 = "42"
|
||||||
assert not sp._self_mutable # proxy is not mutable after exiting context
|
assert not sp._self_mutable # proxy is not mutable after exiting context
|
||||||
@ -2837,7 +2841,7 @@ async def test_get_state(mock_app: rx.App, token: str):
|
|||||||
_substate_key(token, ChildState2)
|
_substate_key(token, ChildState2)
|
||||||
)
|
)
|
||||||
assert isinstance(test_state, TestState)
|
assert isinstance(test_state, TestState)
|
||||||
if isinstance(mock_app.state_manager, StateManagerMemory):
|
if isinstance(mock_app.state_manager, (StateManagerMemory, StateManagerDisk)):
|
||||||
# All substates are available
|
# All substates are available
|
||||||
assert tuple(sorted(test_state.substates)) == (
|
assert tuple(sorted(test_state.substates)) == (
|
||||||
ChildState.get_name(),
|
ChildState.get_name(),
|
||||||
@ -2916,6 +2920,15 @@ async def test_get_state(mock_app: rx.App, token: str):
|
|||||||
ChildState2.get_name(),
|
ChildState2.get_name(),
|
||||||
ChildState3.get_name(),
|
ChildState3.get_name(),
|
||||||
)
|
)
|
||||||
|
elif isinstance(mock_app.state_manager, StateManagerDisk):
|
||||||
|
# On disk, it's a new instance
|
||||||
|
assert new_test_state is not test_state
|
||||||
|
# All substates are available
|
||||||
|
assert tuple(sorted(new_test_state.substates)) == (
|
||||||
|
ChildState.get_name(),
|
||||||
|
ChildState2.get_name(),
|
||||||
|
ChildState3.get_name(),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# With redis, we get a whole new instance
|
# With redis, we get a whole new instance
|
||||||
assert new_test_state is not test_state
|
assert new_test_state is not test_state
|
||||||
|
@ -12,6 +12,7 @@ from reflex.ivars.base import (
|
|||||||
ImmutableVar,
|
ImmutableVar,
|
||||||
LiteralVar,
|
LiteralVar,
|
||||||
var_operation,
|
var_operation,
|
||||||
|
var_operation_return,
|
||||||
)
|
)
|
||||||
from reflex.ivars.function import ArgsFunctionOperation, FunctionStringVar
|
from reflex.ivars.function import ArgsFunctionOperation, FunctionStringVar
|
||||||
from reflex.ivars.number import (
|
from reflex.ivars.number import (
|
||||||
@ -925,9 +926,9 @@ def test_function_var():
|
|||||||
|
|
||||||
|
|
||||||
def test_var_operation():
|
def test_var_operation():
|
||||||
@var_operation(output=NumberVar)
|
@var_operation
|
||||||
def add(a: Union[NumberVar, int], b: Union[NumberVar, int]) -> str:
|
def add(a: Union[NumberVar, int], b: Union[NumberVar, int]):
|
||||||
return f"({a} + {b})"
|
return var_operation_return(js_expression=f"({a} + {b})", var_type=int)
|
||||||
|
|
||||||
assert str(add(1, 2)) == "(1 + 2)"
|
assert str(add(1, 2)) == "(1 + 2)"
|
||||||
assert str(add(a=4, b=-9)) == "(4 + -9)"
|
assert str(add(a=4, b=-9)) == "(4 + -9)"
|
||||||
@ -967,14 +968,14 @@ def test_all_number_operations():
|
|||||||
|
|
||||||
assert (
|
assert (
|
||||||
str(even_more_complicated_number)
|
str(even_more_complicated_number)
|
||||||
== "!(Boolean((Math.abs(Math.floor(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2))) || (2 && Math.round(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2))))))"
|
== "!(((Math.abs(Math.floor(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2))) || (2 && Math.round(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2)))) !== 0))"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert str(LiteralNumberVar.create(5) > False) == "(5 > 0)"
|
assert str(LiteralNumberVar.create(5) > False) == "(5 > 0)"
|
||||||
assert str(LiteralBooleanVar.create(False) < 5) == "((false ? 1 : 0) < 5)"
|
assert str(LiteralBooleanVar.create(False) < 5) == "(Number(false) < 5)"
|
||||||
assert (
|
assert (
|
||||||
str(LiteralBooleanVar.create(False) < LiteralBooleanVar.create(True))
|
str(LiteralBooleanVar.create(False) < LiteralBooleanVar.create(True))
|
||||||
== "((false ? 1 : 0) < (true ? 1 : 0))"
|
== "(Number(false) < Number(true))"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user