Merge remote-tracking branch 'origin/main' into simon/hosting-cli-upgrades
This commit is contained in:
commit
f2c904ee72
@ -3,7 +3,7 @@ fail_fast: true
|
||||
repos:
|
||||
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.7.2
|
||||
rev: v0.7.4
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
args: [reflex, tests]
|
||||
|
264
poetry.lock
generated
264
poetry.lock
generated
@ -1350,8 +1350,8 @@ files = [
|
||||
|
||||
[package.dependencies]
|
||||
numpy = [
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
{version = ">=1.22.4", markers = "python_version < \"3.11\""},
|
||||
]
|
||||
python-dateutil = ">=2.8.2"
|
||||
@ -1656,22 +1656,19 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.9.2"
|
||||
version = "2.10.1"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"},
|
||||
{file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"},
|
||||
{file = "pydantic-2.10.1-py3-none-any.whl", hash = "sha256:a8d20db84de64cf4a7d59e899c2caf0fe9d660c7cfc482528e7020d7dd189a7e"},
|
||||
{file = "pydantic-2.10.1.tar.gz", hash = "sha256:a4daca2dc0aa429555e0656d6bf94873a7dc5f54ee42b1f5873d666fb3f35560"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.6.0"
|
||||
pydantic-core = "2.23.4"
|
||||
typing-extensions = [
|
||||
{version = ">=4.12.2", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=4.6.1", markers = "python_version < \"3.13\""},
|
||||
]
|
||||
pydantic-core = "2.27.1"
|
||||
typing-extensions = ">=4.12.2"
|
||||
|
||||
[package.extras]
|
||||
email = ["email-validator (>=2.0.0)"]
|
||||
@ -1679,100 +1676,111 @@ timezone = ["tzdata"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.23.4"
|
||||
version = "2.27.1"
|
||||
description = "Core functionality for Pydantic validation and serialization"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"},
|
||||
{file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"},
|
||||
{file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"},
|
||||
{file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"},
|
||||
{file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"},
|
||||
{file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"},
|
||||
{file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"},
|
||||
{file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"},
|
||||
{file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"},
|
||||
{file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"},
|
||||
{file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"},
|
||||
{file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"},
|
||||
{file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"},
|
||||
{file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"},
|
||||
{file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"},
|
||||
{file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"},
|
||||
{file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"},
|
||||
{file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"},
|
||||
{file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"},
|
||||
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"},
|
||||
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"},
|
||||
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"},
|
||||
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"},
|
||||
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"},
|
||||
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"},
|
||||
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"},
|
||||
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"},
|
||||
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"},
|
||||
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"},
|
||||
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"},
|
||||
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"},
|
||||
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"},
|
||||
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"},
|
||||
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"},
|
||||
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"},
|
||||
{file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"},
|
||||
{file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"},
|
||||
{file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"},
|
||||
{file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"},
|
||||
{file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"},
|
||||
{file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"},
|
||||
{file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"},
|
||||
{file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"},
|
||||
{file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"},
|
||||
{file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"},
|
||||
{file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"},
|
||||
{file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"},
|
||||
{file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"},
|
||||
{file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"},
|
||||
{file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"},
|
||||
{file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"},
|
||||
{file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"},
|
||||
{file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"},
|
||||
{file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"},
|
||||
{file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"},
|
||||
{file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"},
|
||||
{file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"},
|
||||
{file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"},
|
||||
{file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"},
|
||||
{file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2289,29 +2297,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.7.2"
|
||||
version = "0.7.4"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.7.2-py3-none-linux_armv6l.whl", hash = "sha256:b73f873b5f52092e63ed540adefc3c36f1f803790ecf2590e1df8bf0a9f72cb8"},
|
||||
{file = "ruff-0.7.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5b813ef26db1015953daf476202585512afd6a6862a02cde63f3bafb53d0b2d4"},
|
||||
{file = "ruff-0.7.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:853277dbd9675810c6826dad7a428d52a11760744508340e66bf46f8be9701d9"},
|
||||
{file = "ruff-0.7.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21aae53ab1490a52bf4e3bf520c10ce120987b047c494cacf4edad0ba0888da2"},
|
||||
{file = "ruff-0.7.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc7e0fc6e0cb3168443eeadb6445285abaae75142ee22b2b72c27d790ab60ba"},
|
||||
{file = "ruff-0.7.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd77877a4e43b3a98e5ef4715ba3862105e299af0c48942cc6d51ba3d97dc859"},
|
||||
{file = "ruff-0.7.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e00163fb897d35523c70d71a46fbaa43bf7bf9af0f4534c53ea5b96b2e03397b"},
|
||||
{file = "ruff-0.7.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3c54b538633482dc342e9b634d91168fe8cc56b30a4b4f99287f4e339103e88"},
|
||||
{file = "ruff-0.7.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b792468e9804a204be221b14257566669d1db5c00d6bb335996e5cd7004ba80"},
|
||||
{file = "ruff-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba53ed84ac19ae4bfb4ea4bf0172550a2285fa27fbb13e3746f04c80f7fa088"},
|
||||
{file = "ruff-0.7.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b19fafe261bf741bca2764c14cbb4ee1819b67adb63ebc2db6401dcd652e3748"},
|
||||
{file = "ruff-0.7.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28bd8220f4d8f79d590db9e2f6a0674f75ddbc3847277dd44ac1f8d30684b828"},
|
||||
{file = "ruff-0.7.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9fd67094e77efbea932e62b5d2483006154794040abb3a5072e659096415ae1e"},
|
||||
{file = "ruff-0.7.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:576305393998b7bd6c46018f8104ea3a9cb3fa7908c21d8580e3274a3b04b691"},
|
||||
{file = "ruff-0.7.2-py3-none-win32.whl", hash = "sha256:fa993cfc9f0ff11187e82de874dfc3611df80852540331bc85c75809c93253a8"},
|
||||
{file = "ruff-0.7.2-py3-none-win_amd64.whl", hash = "sha256:dd8800cbe0254e06b8fec585e97554047fb82c894973f7ff18558eee33d1cb88"},
|
||||
{file = "ruff-0.7.2-py3-none-win_arm64.whl", hash = "sha256:bb8368cd45bba3f57bb29cbb8d64b4a33f8415d0149d2655c5c8539452ce7760"},
|
||||
{file = "ruff-0.7.2.tar.gz", hash = "sha256:2b14e77293380e475b4e3a7a368e14549288ed2931fce259a6f99978669e844f"},
|
||||
{file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"},
|
||||
{file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"},
|
||||
{file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"},
|
||||
{file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"},
|
||||
{file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"},
|
||||
{file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"},
|
||||
{file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"},
|
||||
{file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"},
|
||||
{file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"},
|
||||
{file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"},
|
||||
{file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"},
|
||||
{file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"},
|
||||
{file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"},
|
||||
{file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"},
|
||||
{file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"},
|
||||
{file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"},
|
||||
{file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"},
|
||||
{file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2350,23 +2358,23 @@ websocket-client = ">=1.8,<2.0"
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "75.5.0"
|
||||
version = "75.6.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "setuptools-75.5.0-py3-none-any.whl", hash = "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829"},
|
||||
{file = "setuptools-75.5.0.tar.gz", hash = "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef"},
|
||||
{file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"},
|
||||
{file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"]
|
||||
core = ["importlib-metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
|
||||
core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
|
||||
cover = ["pytest-cov"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
|
||||
enabler = ["pytest-enabler (>=2.2)"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
|
||||
type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"]
|
||||
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "shellingham"
|
||||
@ -2773,13 +2781,13 @@ zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.32.0"
|
||||
version = "0.32.1"
|
||||
description = "The lightning-fast ASGI server."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"},
|
||||
{file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"},
|
||||
{file = "uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e"},
|
||||
{file = "uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2788,7 +2796,7 @@ h11 = ">=0.8"
|
||||
typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
||||
standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
@ -3033,4 +3041,4 @@ type = ["pytest-mypy"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "600773827321e7a0fdb2fe7d6af650a039dff759ce6df9c51ca22f0d7df4c290"
|
||||
content-hash = "a610d4c4bfd852f30e69ad2fbb288c2d9cbdf49e05b9d4936fe5e9b2a7cdefdb"
|
||||
|
@ -70,7 +70,7 @@ dill = ">=0.3.8"
|
||||
toml = ">=0.10.2,<1.0"
|
||||
pytest-asyncio = ">=0.24.0"
|
||||
pytest-cov = ">=4.0.0,<7.0"
|
||||
ruff = "0.7.2"
|
||||
ruff = "0.7.4"
|
||||
pandas = ">=2.1.1,<3.0"
|
||||
pillow = ">=10.0.0,<12.0"
|
||||
plotly = ">=5.13.0,<6.0"
|
||||
|
@ -705,6 +705,11 @@ export const useEventLoop = (
|
||||
_e.stopPropagation();
|
||||
}
|
||||
const combined_name = events.map((e) => e.name).join("+++");
|
||||
if (event_actions?.temporal) {
|
||||
if (!socket.current || !socket.current.connected) {
|
||||
return; // don't queue when the backend is not connected
|
||||
}
|
||||
}
|
||||
if (event_actions?.throttle) {
|
||||
// If throttle returns false, the events are not added to the queue.
|
||||
if (!throttle(combined_name, event_actions.throttle)) {
|
||||
@ -762,7 +767,7 @@ export const useEventLoop = (
|
||||
window.onunhandledrejection = function (event) {
|
||||
addEvents([
|
||||
Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: event.reason.stack,
|
||||
stack: event.reason?.stack,
|
||||
component_stack: "",
|
||||
}),
|
||||
]);
|
||||
@ -837,11 +842,20 @@ export const useEventLoop = (
|
||||
}
|
||||
};
|
||||
const change_complete = () => addEvents(onLoadInternalEvent());
|
||||
const change_error = () => {
|
||||
// Remove cached error state from router for this page, otherwise the
|
||||
// page will never send on_load events again.
|
||||
if (router.components[router.pathname].error) {
|
||||
delete router.components[router.pathname].error;
|
||||
}
|
||||
}
|
||||
router.events.on("routeChangeStart", change_start);
|
||||
router.events.on("routeChangeComplete", change_complete);
|
||||
router.events.on("routeChangeError", change_error);
|
||||
return () => {
|
||||
router.events.off("routeChangeStart", change_start);
|
||||
router.events.off("routeChangeComplete", change_complete);
|
||||
router.events.off("routeChangeError", change_error);
|
||||
};
|
||||
}, [router]);
|
||||
|
||||
|
@ -850,10 +850,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
if self.theme is not None:
|
||||
# If a theme component was provided, wrap the app with it
|
||||
app_wrappers[(20, "Theme")] = self.theme
|
||||
# Fix #2992 by removing the top-level appearance prop
|
||||
self.theme.appearance = None
|
||||
|
||||
for route in self.unevaluated_pages:
|
||||
console.debug(f"Evaluating page: {route}")
|
||||
self._compile_page(route)
|
||||
|
||||
# Add the optional endpoints (_upload)
|
||||
@ -1006,6 +1005,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
compile_results.append(
|
||||
compiler.compile_contexts(self.state, self.theme),
|
||||
)
|
||||
if self.theme is not None:
|
||||
# Fix #2992 by removing the top-level appearance prop
|
||||
self.theme.appearance = None
|
||||
progress.advance(task)
|
||||
|
||||
# Compile the app root.
|
||||
|
@ -186,6 +186,23 @@ ComponentStyle = Dict[
|
||||
ComponentChild = Union[types.PrimitiveType, Var, BaseComponent]
|
||||
|
||||
|
||||
def satisfies_type_hint(obj: Any, type_hint: Any) -> bool:
|
||||
"""Check if an object satisfies a type hint.
|
||||
|
||||
Args:
|
||||
obj: The object to check.
|
||||
type_hint: The type hint to check against.
|
||||
|
||||
Returns:
|
||||
Whether the object satisfies the type hint.
|
||||
"""
|
||||
if isinstance(obj, LiteralVar):
|
||||
return types._isinstance(obj._var_value, type_hint)
|
||||
if isinstance(obj, Var):
|
||||
return types._issubclass(obj._var_type, type_hint)
|
||||
return types._isinstance(obj, type_hint)
|
||||
|
||||
|
||||
class Component(BaseComponent, ABC):
|
||||
"""A component with style, event trigger and other props."""
|
||||
|
||||
@ -460,8 +477,7 @@ class Component(BaseComponent, ABC):
|
||||
)
|
||||
) or (
|
||||
# Else just check if the passed var type is valid.
|
||||
not passed_types
|
||||
and not types._issubclass(passed_type, expected_type, value)
|
||||
not passed_types and not satisfies_type_hint(value, expected_type)
|
||||
):
|
||||
value_name = value._js_expr if isinstance(value, Var) else value
|
||||
|
||||
|
@ -382,7 +382,7 @@ for theme_name in dir(Theme):
|
||||
class CodeBlock(Component, MarkdownComponentMap):
|
||||
"""A code block."""
|
||||
|
||||
library = "react-syntax-highlighter@15.6.1"
|
||||
library = "react-syntax-highlighter@15.6.0"
|
||||
|
||||
tag = "PrismAsyncLight"
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
from . import elements as elements
|
||||
from .elements.forms import Button as Button
|
||||
from .elements.forms import Datalist as Datalist
|
||||
from .elements.forms import Fieldset as Fieldset
|
||||
from .elements.forms import Form as Form
|
||||
from .elements.forms import Input as Input
|
||||
@ -18,6 +19,7 @@ from .elements.forms import Progress as Progress
|
||||
from .elements.forms import Select as Select
|
||||
from .elements.forms import Textarea as Textarea
|
||||
from .elements.forms import button as button
|
||||
from .elements.forms import datalist as datalist
|
||||
from .elements.forms import fieldset as fieldset
|
||||
from .elements.forms import form as form
|
||||
from .elements.forms import input as input
|
||||
|
@ -7,6 +7,7 @@ from reflex.utils import lazy_loader
|
||||
_MAPPING = {
|
||||
"forms": [
|
||||
"button",
|
||||
"datalist",
|
||||
"fieldset",
|
||||
"form",
|
||||
"input",
|
||||
|
@ -4,6 +4,7 @@
|
||||
# ------------------------------------------------------
|
||||
|
||||
from .forms import Button as Button
|
||||
from .forms import Datalist as Datalist
|
||||
from .forms import Fieldset as Fieldset
|
||||
from .forms import Form as Form
|
||||
from .forms import Input as Input
|
||||
@ -17,6 +18,7 @@ from .forms import Progress as Progress
|
||||
from .forms import Select as Select
|
||||
from .forms import Textarea as Textarea
|
||||
from .forms import button as button
|
||||
from .forms import datalist as datalist
|
||||
from .forms import fieldset as fieldset
|
||||
from .forms import form as form
|
||||
from .forms import input as input
|
||||
@ -226,6 +228,7 @@ from .typography import ul as ul
|
||||
_MAPPING = {
|
||||
"forms": [
|
||||
"button",
|
||||
"datalist",
|
||||
"fieldset",
|
||||
"form",
|
||||
"input",
|
||||
|
@ -681,6 +681,7 @@ class Textarea(BaseHTML):
|
||||
|
||||
|
||||
button = Button.create
|
||||
datalist = Datalist.create
|
||||
fieldset = Fieldset.create
|
||||
form = Form.create
|
||||
input = Input.create
|
||||
|
@ -1490,6 +1490,7 @@ class Textarea(BaseHTML):
|
||||
...
|
||||
|
||||
button = Button.create
|
||||
datalist = Datalist.create
|
||||
fieldset = Fieldset.create
|
||||
form = Form.create
|
||||
input = Input.create
|
||||
|
@ -1,7 +1,8 @@
|
||||
"""Moment component for humanized date rendering."""
|
||||
|
||||
import dataclasses
|
||||
from typing import List, Optional
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from reflex.components.component import NoSSRComponent
|
||||
from reflex.event import EventHandler, passthrough_event_spec
|
||||
@ -19,7 +20,7 @@ class MomentDelta:
|
||||
weeks: Optional[int] = dataclasses.field(default=None)
|
||||
days: Optional[int] = dataclasses.field(default=None)
|
||||
hours: Optional[int] = dataclasses.field(default=None)
|
||||
minutess: Optional[int] = dataclasses.field(default=None)
|
||||
minutes: Optional[int] = dataclasses.field(default=None)
|
||||
seconds: Optional[int] = dataclasses.field(default=None)
|
||||
milliseconds: Optional[int] = dataclasses.field(default=None)
|
||||
|
||||
@ -78,7 +79,7 @@ class Moment(NoSSRComponent):
|
||||
duration: Var[str]
|
||||
|
||||
# The date to display (also work if passed as children).
|
||||
date: Var[str]
|
||||
date: Var[Union[str, datetime, date, time, timedelta]]
|
||||
|
||||
# Shows the duration (elapsed time) between now and the provided datetime.
|
||||
duration_from_now: Var[bool]
|
||||
|
@ -4,6 +4,7 @@
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
import dataclasses
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import NoSSRComponent
|
||||
@ -20,7 +21,7 @@ class MomentDelta:
|
||||
weeks: Optional[int]
|
||||
days: Optional[int]
|
||||
hours: Optional[int]
|
||||
minutess: Optional[int]
|
||||
minutes: Optional[int]
|
||||
seconds: Optional[int]
|
||||
milliseconds: Optional[int]
|
||||
|
||||
@ -46,7 +47,16 @@ class Moment(NoSSRComponent):
|
||||
decimal: Optional[Union[Var[bool], bool]] = None,
|
||||
unit: Optional[Union[Var[str], str]] = None,
|
||||
duration: Optional[Union[Var[str], str]] = None,
|
||||
date: Optional[Union[Var[str], str]] = None,
|
||||
date: Optional[
|
||||
Union[
|
||||
Var[Union[date, datetime, str, time, timedelta]],
|
||||
date,
|
||||
datetime,
|
||||
str,
|
||||
time,
|
||||
timedelta,
|
||||
]
|
||||
] = None,
|
||||
duration_from_now: Optional[Union[Var[bool], bool]] = None,
|
||||
unix: Optional[Union[Var[bool], bool]] = None,
|
||||
local: Optional[Union[Var[bool], bool]] = None,
|
||||
|
@ -11,7 +11,6 @@ from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||
from reflex.components.radix.themes.base import Theme
|
||||
from reflex.components.radix.themes.layout.flex import Flex
|
||||
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
||||
from reflex.utils import console
|
||||
from reflex.vars.base import Var
|
||||
|
||||
|
||||
@ -140,19 +139,19 @@ class DrawerContent(DrawerComponent):
|
||||
base_style.update(style)
|
||||
return {"css": base_style}
|
||||
|
||||
# Fired when the drawer content is opened. Deprecated.
|
||||
# Fired when the drawer content is opened.
|
||||
on_open_auto_focus: EventHandler[no_args_event_spec]
|
||||
|
||||
# Fired when the drawer content is closed. Deprecated.
|
||||
# Fired when the drawer content is closed.
|
||||
on_close_auto_focus: EventHandler[no_args_event_spec]
|
||||
|
||||
# Fired when the escape key is pressed. Deprecated.
|
||||
# Fired when the escape key is pressed.
|
||||
on_escape_key_down: EventHandler[no_args_event_spec]
|
||||
|
||||
# Fired when the pointer is down outside the drawer content. Deprecated.
|
||||
# Fired when the pointer is down outside the drawer content.
|
||||
on_pointer_down_outside: EventHandler[no_args_event_spec]
|
||||
|
||||
# Fired when interacting outside the drawer content. Deprecated.
|
||||
# Fired when interacting outside the drawer content.
|
||||
on_interact_outside: EventHandler[no_args_event_spec]
|
||||
|
||||
@classmethod
|
||||
@ -170,23 +169,6 @@ class DrawerContent(DrawerComponent):
|
||||
Returns:
|
||||
The drawer content.
|
||||
"""
|
||||
deprecated_properties = [
|
||||
"on_open_auto_focus",
|
||||
"on_close_auto_focus",
|
||||
"on_escape_key_down",
|
||||
"on_pointer_down_outside",
|
||||
"on_interact_outside",
|
||||
]
|
||||
|
||||
for prop in deprecated_properties:
|
||||
if prop in props:
|
||||
console.deprecate(
|
||||
feature_name="drawer content events",
|
||||
reason=f"The `{prop}` event is deprecated and will be removed in 0.7.0.",
|
||||
deprecation_version="0.6.3",
|
||||
removal_version="0.7.0",
|
||||
)
|
||||
|
||||
comp = super().create(*children, **props)
|
||||
|
||||
return Theme.create(comp)
|
||||
|
@ -12,7 +12,9 @@ from reflex.vars.base import Var
|
||||
from ..base import LiteralAccentColor, RadixThemesComponent
|
||||
|
||||
|
||||
def on_value_change(value: Var[str | List[str]]) -> Tuple[Var[str | List[str]]]:
|
||||
def on_value_change(
|
||||
value: Var[Union[str, List[str]]],
|
||||
) -> Tuple[Var[Union[str, List[str]]]]:
|
||||
"""Handle the on_value_change event.
|
||||
|
||||
Args:
|
||||
|
@ -13,7 +13,9 @@ from reflex.vars.base import Var
|
||||
|
||||
from ..base import RadixThemesComponent
|
||||
|
||||
def on_value_change(value: Var[str | List[str]]) -> Tuple[Var[str | List[str]]]: ...
|
||||
def on_value_change(
|
||||
value: Var[Union[str, List[str]]],
|
||||
) -> Tuple[Var[Union[str, List[str]]]]: ...
|
||||
|
||||
class SegmentedControlRoot(RadixThemesComponent):
|
||||
@overload
|
||||
@ -118,7 +120,10 @@ class SegmentedControlRoot(RadixThemesComponent):
|
||||
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
|
||||
on_blur: Optional[EventType[[], BASE_STATE]] = None,
|
||||
on_change: Optional[
|
||||
Union[EventType[[], BASE_STATE], EventType[[str | List[str]], BASE_STATE]]
|
||||
Union[
|
||||
EventType[[], BASE_STATE],
|
||||
EventType[[Union[str, List[str]]], BASE_STATE],
|
||||
]
|
||||
] = None,
|
||||
on_click: Optional[EventType[[], BASE_STATE]] = None,
|
||||
on_context_menu: Optional[EventType[[], BASE_STATE]] = None,
|
||||
|
@ -67,6 +67,9 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
|
||||
# Value of the input
|
||||
value: Var[Union[str, int, float]]
|
||||
|
||||
# References a datalist for suggested options
|
||||
list: Var[Union[str, int, bool]]
|
||||
|
||||
# Fired when the value of the textarea changes.
|
||||
on_change: EventHandler[input_event]
|
||||
|
||||
|
@ -119,6 +119,7 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
|
||||
required: Optional[Union[Var[bool], bool]] = None,
|
||||
type: Optional[Union[Var[str], str]] = None,
|
||||
value: Optional[Union[Var[Union[float, int, str]], float, int, str]] = None,
|
||||
list: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
auto_capitalize: Optional[
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
@ -206,6 +207,7 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
|
||||
required: Indicates that the input is required
|
||||
type: Specifies the type of input
|
||||
value: Value of the input
|
||||
list: References a datalist for suggested options
|
||||
on_change: Fired when the value of the textarea changes.
|
||||
on_focus: Fired when the textarea is focused.
|
||||
on_blur: Fired when the textarea is blurred.
|
||||
@ -454,6 +456,7 @@ class TextField(ComponentNamespace):
|
||||
required: Optional[Union[Var[bool], bool]] = None,
|
||||
type: Optional[Union[Var[str], str]] = None,
|
||||
value: Optional[Union[Var[Union[float, int, str]], float, int, str]] = None,
|
||||
list: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
auto_capitalize: Optional[
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
@ -541,6 +544,7 @@ class TextField(ComponentNamespace):
|
||||
required: Indicates that the input is required
|
||||
type: Specifies the type of input
|
||||
value: Value of the input
|
||||
list: References a datalist for suggested options
|
||||
on_change: Fired when the value of the textarea changes.
|
||||
on_focus: Fired when the textarea is focused.
|
||||
on_blur: Fired when the textarea is blurred.
|
||||
|
@ -15,6 +15,8 @@ from reflex.utils.imports import ImportVar
|
||||
from reflex.utils.serializers import serializer
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
from reflex.vars.function import FunctionVar
|
||||
from reflex.vars.object import ObjectVar
|
||||
|
||||
LiteralPosition = Literal[
|
||||
"top-left",
|
||||
@ -232,7 +234,9 @@ class Toaster(Component):
|
||||
return [hook]
|
||||
|
||||
@staticmethod
|
||||
def send_toast(message: str = "", level: str | None = None, **props) -> EventSpec:
|
||||
def send_toast(
|
||||
message: str | Var = "", level: str | None = None, **props
|
||||
) -> EventSpec:
|
||||
"""Send a toast message.
|
||||
|
||||
Args:
|
||||
@ -250,20 +254,27 @@ class Toaster(Component):
|
||||
raise ValueError(
|
||||
"Toaster component must be created before sending a toast. (use `rx.toast.provider()`)"
|
||||
)
|
||||
toast_command = f"{toast_ref}.{level}" if level is not None else toast_ref
|
||||
if message == "" and ("title" not in props or "description" not in props):
|
||||
raise ValueError("Toast message or title or description must be provided.")
|
||||
if props:
|
||||
args = LiteralVar.create(ToastProps(component_name="rx.toast", **props)) # type: ignore
|
||||
toast = f"{toast_command}(`{message}`, {str(args)})"
|
||||
else:
|
||||
toast = f"{toast_command}(`{message}`)"
|
||||
|
||||
toast_action = Var(_js_expr=toast)
|
||||
return run_script(toast_action)
|
||||
toast_command = (
|
||||
ObjectVar.__getattr__(toast_ref.to(dict), level) if level else toast_ref
|
||||
).to(FunctionVar)
|
||||
|
||||
if isinstance(message, Var):
|
||||
props.setdefault("title", message)
|
||||
message = ""
|
||||
elif message == "" and "title" not in props and "description" not in props:
|
||||
raise ValueError("Toast message or title or description must be provided.")
|
||||
|
||||
if props:
|
||||
args = LiteralVar.create(ToastProps(component_name="rx.toast", **props)) # pyright: ignore [reportCallIssue, reportGeneralTypeIssues]
|
||||
toast = toast_command.call(message, args)
|
||||
else:
|
||||
toast = toast_command.call(message)
|
||||
|
||||
return run_script(toast)
|
||||
|
||||
@staticmethod
|
||||
def toast_info(message: str = "", **kwargs):
|
||||
def toast_info(message: str | Var = "", **kwargs):
|
||||
"""Display an info toast message.
|
||||
|
||||
Args:
|
||||
@ -276,7 +287,7 @@ class Toaster(Component):
|
||||
return Toaster.send_toast(message, level="info", **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def toast_warning(message: str = "", **kwargs):
|
||||
def toast_warning(message: str | Var = "", **kwargs):
|
||||
"""Display a warning toast message.
|
||||
|
||||
Args:
|
||||
@ -289,7 +300,7 @@ class Toaster(Component):
|
||||
return Toaster.send_toast(message, level="warning", **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def toast_error(message: str = "", **kwargs):
|
||||
def toast_error(message: str | Var = "", **kwargs):
|
||||
"""Display an error toast message.
|
||||
|
||||
Args:
|
||||
@ -302,7 +313,7 @@ class Toaster(Component):
|
||||
return Toaster.send_toast(message, level="error", **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def toast_success(message: str = "", **kwargs):
|
||||
def toast_success(message: str | Var = "", **kwargs):
|
||||
"""Display a success toast message.
|
||||
|
||||
Args:
|
||||
|
@ -59,16 +59,16 @@ class Toaster(Component):
|
||||
def add_hooks(self) -> list[Var | str]: ...
|
||||
@staticmethod
|
||||
def send_toast(
|
||||
message: str = "", level: str | None = None, **props
|
||||
message: str | Var = "", level: str | None = None, **props
|
||||
) -> EventSpec: ...
|
||||
@staticmethod
|
||||
def toast_info(message: str = "", **kwargs): ...
|
||||
def toast_info(message: str | Var = "", **kwargs): ...
|
||||
@staticmethod
|
||||
def toast_warning(message: str = "", **kwargs): ...
|
||||
def toast_warning(message: str | Var = "", **kwargs): ...
|
||||
@staticmethod
|
||||
def toast_error(message: str = "", **kwargs): ...
|
||||
def toast_error(message: str | Var = "", **kwargs): ...
|
||||
@staticmethod
|
||||
def toast_success(message: str = "", **kwargs): ...
|
||||
def toast_success(message: str | Var = "", **kwargs): ...
|
||||
@staticmethod
|
||||
def toast_dismiss(id: Var | str | None = None): ...
|
||||
@overload
|
||||
@ -176,7 +176,7 @@ class ToastNamespace(ComponentNamespace):
|
||||
|
||||
@staticmethod
|
||||
def __call__(
|
||||
message: str = "", level: Optional[str] = None, **props
|
||||
message: Union[str, Var] = "", level: Optional[str] = None, **props
|
||||
) -> "Optional[EventSpec]":
|
||||
"""Send a toast message.
|
||||
|
||||
|
@ -8,7 +8,9 @@ import importlib
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import urllib.parse
|
||||
from importlib.util import find_spec
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
@ -452,6 +454,14 @@ class PathExistsFlag:
|
||||
ExistingPath = Annotated[Path, PathExistsFlag]
|
||||
|
||||
|
||||
class PerformanceMode(enum.Enum):
|
||||
"""Performance mode for the app."""
|
||||
|
||||
WARN = "warn"
|
||||
RAISE = "raise"
|
||||
OFF = "off"
|
||||
|
||||
|
||||
class EnvironmentVariables:
|
||||
"""Environment variables class to instantiate environment variables."""
|
||||
|
||||
@ -545,6 +555,15 @@ class EnvironmentVariables:
|
||||
# Where to save screenshots when tests fail.
|
||||
SCREENSHOT_DIR: EnvVar[Optional[Path]] = env_var(None)
|
||||
|
||||
# Whether to check for outdated package versions.
|
||||
REFLEX_CHECK_LATEST_VERSION: EnvVar[bool] = env_var(True)
|
||||
|
||||
# In which performance mode to run the app.
|
||||
REFLEX_PERF_MODE: EnvVar[Optional[PerformanceMode]] = env_var(PerformanceMode.WARN)
|
||||
|
||||
# The maximum size of the reflex state in kilobytes.
|
||||
REFLEX_STATE_SIZE_LIMIT: EnvVar[int] = env_var(1000)
|
||||
|
||||
|
||||
environment = EnvironmentVariables()
|
||||
|
||||
@ -798,6 +817,27 @@ class Config(Base):
|
||||
self._replace_defaults(**kwargs)
|
||||
|
||||
|
||||
def _get_config() -> Config:
|
||||
"""Get the app config.
|
||||
|
||||
Returns:
|
||||
The app config.
|
||||
"""
|
||||
# only import the module if it exists. If a module spec exists then
|
||||
# the module exists.
|
||||
spec = find_spec(constants.Config.MODULE)
|
||||
if not spec:
|
||||
# we need this condition to ensure that a ModuleNotFound error is not thrown when
|
||||
# running unit/integration tests or during `reflex init`.
|
||||
return Config(app_name="")
|
||||
rxconfig = importlib.import_module(constants.Config.MODULE)
|
||||
return rxconfig.config
|
||||
|
||||
|
||||
# Protect sys.path from concurrent modification
|
||||
_config_lock = threading.RLock()
|
||||
|
||||
|
||||
def get_config(reload: bool = False) -> Config:
|
||||
"""Get the app config.
|
||||
|
||||
@ -807,15 +847,26 @@ def get_config(reload: bool = False) -> Config:
|
||||
Returns:
|
||||
The app config.
|
||||
"""
|
||||
sys.path.insert(0, os.getcwd())
|
||||
# only import the module if it exists. If a module spec exists then
|
||||
# the module exists.
|
||||
spec = importlib.util.find_spec(constants.Config.MODULE) # type: ignore
|
||||
if not spec:
|
||||
# we need this condition to ensure that a ModuleNotFound error is not thrown when
|
||||
# running unit/integration tests.
|
||||
return Config(app_name="")
|
||||
rxconfig = importlib.import_module(constants.Config.MODULE)
|
||||
if reload:
|
||||
importlib.reload(rxconfig)
|
||||
return rxconfig.config
|
||||
cached_rxconfig = sys.modules.get(constants.Config.MODULE, None)
|
||||
if cached_rxconfig is not None:
|
||||
if reload:
|
||||
# Remove any cached module when `reload` is requested.
|
||||
del sys.modules[constants.Config.MODULE]
|
||||
else:
|
||||
return cached_rxconfig.config
|
||||
|
||||
with _config_lock:
|
||||
sys_path = sys.path.copy()
|
||||
sys.path.clear()
|
||||
sys.path.append(os.getcwd())
|
||||
try:
|
||||
# Try to import the module with only the current directory in the path.
|
||||
return _get_config()
|
||||
except Exception:
|
||||
# If the module import fails, try to import with the original sys.path.
|
||||
sys.path.extend(sys_path)
|
||||
return _get_config()
|
||||
finally:
|
||||
# Restore the original sys.path.
|
||||
sys.path.clear()
|
||||
sys.path.extend(sys_path)
|
||||
|
@ -97,6 +97,18 @@ class Templates(SimpleNamespace):
|
||||
# The default template
|
||||
DEFAULT = "blank"
|
||||
|
||||
# The AI template
|
||||
AI = "ai"
|
||||
|
||||
# The option for the user to choose a remote template.
|
||||
CHOOSE_TEMPLATES = "choose-templates"
|
||||
|
||||
# The URL to find reflex templates.
|
||||
REFLEX_TEMPLATES_URL = "https://reflex.dev/templates"
|
||||
|
||||
# Demo url for the default template.
|
||||
DEFAULT_TEMPLATE_URL = "https://blank-template.reflex.run"
|
||||
|
||||
# The reflex.build frontend host
|
||||
REFLEX_BUILD_FRONTEND = "https://flexgen.reflex.run"
|
||||
|
||||
|
@ -184,15 +184,15 @@ class PackageJson(SimpleNamespace):
|
||||
"json5": "2.2.3",
|
||||
"next": "14.2.16",
|
||||
"next-sitemap": "4.2.3",
|
||||
"next-themes": "0.3.0",
|
||||
"next-themes": "0.4.3",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-focus-lock": "2.13.2",
|
||||
"socket.io-client": "4.8.1",
|
||||
"universal-cookie": "7.2.1",
|
||||
"universal-cookie": "7.2.2",
|
||||
}
|
||||
DEV_DEPENDENCIES = {
|
||||
"autoprefixer": "10.4.20",
|
||||
"postcss": "8.4.47",
|
||||
"postcss": "8.4.49",
|
||||
"postcss-import": "16.1.0",
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ class Tailwind(SimpleNamespace):
|
||||
"""Tailwind constants."""
|
||||
|
||||
# The Tailwindcss version
|
||||
VERSION = "tailwindcss@3.4.14"
|
||||
VERSION = "tailwindcss@3.4.15"
|
||||
# The Tailwind config.
|
||||
CONFIG = "tailwind.config.js"
|
||||
# Default Tailwind content paths
|
||||
|
@ -181,6 +181,18 @@ class EventActionsMixin:
|
||||
event_actions={"debounce": delay_ms, **self.event_actions},
|
||||
)
|
||||
|
||||
@property
|
||||
def temporal(self):
|
||||
"""Do not queue the event if the backend is down.
|
||||
|
||||
Returns:
|
||||
New EventHandler-like with temporal set to True.
|
||||
"""
|
||||
return dataclasses.replace(
|
||||
self,
|
||||
event_actions={"temporal": True, **self.event_actions},
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
init=True,
|
||||
@ -1346,6 +1358,10 @@ def check_fn_match_arg_spec(
|
||||
EventFnArgMismatch: Raised if the number of mandatory arguments do not match
|
||||
"""
|
||||
user_args = inspect.getfullargspec(user_func).args
|
||||
# Drop the first argument if it's a bound method
|
||||
if inspect.ismethod(user_func) and user_func.__self__ is not None:
|
||||
user_args = user_args[1:]
|
||||
|
||||
user_default_args = inspect.getfullargspec(user_func).defaults
|
||||
number_of_user_args = len(user_args) - number_of_bound_args
|
||||
number_of_user_default_args = len(user_default_args) if user_default_args else 0
|
||||
|
@ -17,10 +17,10 @@ from reflex import constants
|
||||
from reflex.config import environment, get_config
|
||||
from reflex.custom_components.custom_components import custom_components_cli
|
||||
from reflex.state import reset_disk_state_manager
|
||||
from reflex.utils import console, redir, telemetry
|
||||
from reflex.utils import console, telemetry
|
||||
|
||||
# Disable typer+rich integration for help panels
|
||||
typer.core.rich = False # type: ignore
|
||||
typer.core.rich = None # type: ignore
|
||||
|
||||
# Create the app.
|
||||
try:
|
||||
@ -89,30 +89,8 @@ def _init(
|
||||
# Set up the web project.
|
||||
prerequisites.initialize_frontend_dependencies()
|
||||
|
||||
# Integrate with reflex.build.
|
||||
generation_hash = None
|
||||
if ai:
|
||||
if template is None:
|
||||
# If AI is requested and no template specified, redirect the user to reflex.build.
|
||||
generation_hash = redir.reflex_build_redirect()
|
||||
elif prerequisites.is_generation_hash(template):
|
||||
# Otherwise treat the template as a generation hash.
|
||||
generation_hash = template
|
||||
else:
|
||||
console.error(
|
||||
"Cannot use `--template` option with `--ai` option. Please remove `--template` option."
|
||||
)
|
||||
raise typer.Exit(2)
|
||||
template = constants.Templates.DEFAULT
|
||||
|
||||
# Initialize the app.
|
||||
template = prerequisites.initialize_app(app_name, template)
|
||||
|
||||
# If a reflex.build generation hash is available, download the code and apply it to the main module.
|
||||
if generation_hash:
|
||||
prerequisites.initialize_main_module_index_from_generation(
|
||||
app_name, generation_hash=generation_hash
|
||||
)
|
||||
template = prerequisites.initialize_app(app_name, template, ai)
|
||||
|
||||
# Initialize the .gitignore.
|
||||
prerequisites.initialize_gitignore()
|
||||
@ -120,7 +98,7 @@ def _init(
|
||||
# Initialize the requirements.txt.
|
||||
prerequisites.initialize_requirements_txt()
|
||||
|
||||
template_msg = "" if template else f" using the {template} template"
|
||||
template_msg = f" using the {template} template" if template else ""
|
||||
# Finish initializing the app.
|
||||
console.success(f"Initialized {app_name}{template_msg}")
|
||||
|
||||
|
@ -43,7 +43,7 @@ from sqlalchemy.orm import DeclarativeBase
|
||||
from typing_extensions import Self
|
||||
|
||||
from reflex import event
|
||||
from reflex.config import get_config
|
||||
from reflex.config import PerformanceMode, get_config
|
||||
from reflex.istate.data import RouterData
|
||||
from reflex.istate.storage import ClientStorageBase
|
||||
from reflex.model import Model
|
||||
@ -62,6 +62,13 @@ try:
|
||||
except ModuleNotFoundError:
|
||||
import pydantic
|
||||
|
||||
from pydantic import BaseModel as BaseModelV2
|
||||
|
||||
try:
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
except ModuleNotFoundError:
|
||||
BaseModelV1 = BaseModelV2
|
||||
|
||||
import wrapt
|
||||
from redis.asyncio import Redis
|
||||
from redis.exceptions import ResponseError
|
||||
@ -87,8 +94,10 @@ from reflex.utils.exceptions import (
|
||||
ImmutableStateError,
|
||||
InvalidStateManagerMode,
|
||||
LockExpiredError,
|
||||
ReflexRuntimeError,
|
||||
SetUndefinedStateVarError,
|
||||
StateSchemaMismatchError,
|
||||
StateTooLargeError,
|
||||
)
|
||||
from reflex.utils.exec import is_testing_env
|
||||
from reflex.utils.serializers import serializer
|
||||
@ -109,10 +118,11 @@ Delta = Dict[str, Any]
|
||||
var = computed_var
|
||||
|
||||
|
||||
# If the state is this large, it's considered a performance issue.
|
||||
TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb
|
||||
# Only warn about each state class size once.
|
||||
_WARNED_ABOUT_STATE_SIZE: Set[str] = set()
|
||||
if environment.REFLEX_PERF_MODE.get() != PerformanceMode.OFF:
|
||||
# If the state is this large, it's considered a performance issue.
|
||||
TOO_LARGE_SERIALIZED_STATE = environment.REFLEX_STATE_SIZE_LIMIT.get() * 1024
|
||||
# Only warn about each state class size once.
|
||||
_WARNED_ABOUT_STATE_SIZE: Set[str] = set()
|
||||
|
||||
# Errors caught during pickling of state
|
||||
HANDLED_PICKLE_ERRORS = (
|
||||
@ -387,6 +397,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
"State classes should not be instantiated directly in a Reflex app. "
|
||||
"See https://reflex.dev/docs/state/ for further information."
|
||||
)
|
||||
if type(self)._mixin:
|
||||
raise ReflexRuntimeError(
|
||||
f"{type(self).__name__} is a state mixin and cannot be instantiated directly."
|
||||
)
|
||||
kwargs["parent_state"] = parent_state
|
||||
super().__init__()
|
||||
for name, value in kwargs.items():
|
||||
@ -1243,7 +1257,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
if parent_state is not None:
|
||||
return getattr(parent_state, name)
|
||||
|
||||
if isinstance(value, MutableProxy.__mutable_types__) and (
|
||||
if MutableProxy._is_mutable_type(value) and (
|
||||
name in super().__getattribute__("base_vars") or name in backend_vars
|
||||
):
|
||||
# track changes in mutable containers (list, dict, set, etc)
|
||||
@ -2092,7 +2106,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
state["__dict__"].pop(inherited_var_name, None)
|
||||
return state
|
||||
|
||||
def _warn_if_too_large(
|
||||
def _check_state_size(
|
||||
self,
|
||||
pickle_state_size: int,
|
||||
):
|
||||
@ -2100,6 +2114,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
|
||||
Args:
|
||||
pickle_state_size: The size of the pickled state.
|
||||
|
||||
Raises:
|
||||
StateTooLargeError: If the state is too large.
|
||||
"""
|
||||
state_full_name = self.get_full_name()
|
||||
if (
|
||||
@ -2107,10 +2124,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
and pickle_state_size > TOO_LARGE_SERIALIZED_STATE
|
||||
and self.substates
|
||||
):
|
||||
console.warn(
|
||||
msg = (
|
||||
f"State {state_full_name} serializes to {pickle_state_size} bytes "
|
||||
"which may present performance issues. Consider reducing the size of this state."
|
||||
+ "which may present performance issues. Consider reducing the size of this state."
|
||||
)
|
||||
if environment.REFLEX_PERF_MODE.get() == PerformanceMode.WARN:
|
||||
console.warn(msg)
|
||||
elif environment.REFLEX_PERF_MODE.get() == PerformanceMode.RAISE:
|
||||
raise StateTooLargeError(msg)
|
||||
_WARNED_ABOUT_STATE_SIZE.add(state_full_name)
|
||||
|
||||
@classmethod
|
||||
@ -2152,7 +2173,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
"""
|
||||
try:
|
||||
pickle_state = pickle.dumps((self._to_schema(), self))
|
||||
self._warn_if_too_large(len(pickle_state))
|
||||
if environment.REFLEX_PERF_MODE.get() != PerformanceMode.OFF:
|
||||
self._check_state_size(len(pickle_state))
|
||||
return pickle_state
|
||||
except HANDLED_PICKLE_ERRORS as og_pickle_error:
|
||||
error = (
|
||||
@ -2367,6 +2389,23 @@ class ComponentState(State, mixin=True):
|
||||
# The number of components created from this class.
|
||||
_per_component_state_instance_count: ClassVar[int] = 0
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Do not allow direct initialization of the ComponentState.
|
||||
|
||||
Args:
|
||||
*args: The args to pass to the State init method.
|
||||
**kwargs: The kwargs to pass to the State init method.
|
||||
|
||||
Raises:
|
||||
ReflexRuntimeError: If the ComponentState is initialized directly.
|
||||
"""
|
||||
if type(self)._mixin:
|
||||
raise ReflexRuntimeError(
|
||||
f"{ComponentState.__name__} {type(self).__name__} is not meant to be initialized directly. "
|
||||
+ "Use the `create` method to create a new instance and access the state via the `State` attribute."
|
||||
)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def __init_subclass__(cls, mixin: bool = True, **kwargs):
|
||||
"""Overwrite mixin default to True.
|
||||
@ -3526,7 +3565,16 @@ class MutableProxy(wrapt.ObjectProxy):
|
||||
pydantic.BaseModel.__dict__
|
||||
)
|
||||
|
||||
__mutable_types__ = (list, dict, set, Base, DeclarativeBase)
|
||||
# These types will be wrapped in MutableProxy
|
||||
__mutable_types__ = (
|
||||
list,
|
||||
dict,
|
||||
set,
|
||||
Base,
|
||||
DeclarativeBase,
|
||||
BaseModelV2,
|
||||
BaseModelV1,
|
||||
)
|
||||
|
||||
def __init__(self, wrapped: Any, state: BaseState, field_name: str):
|
||||
"""Create a proxy for a mutable object that tracks changes.
|
||||
@ -3566,6 +3614,18 @@ class MutableProxy(wrapt.ObjectProxy):
|
||||
if wrapped is not None:
|
||||
return wrapped(*args, **(kwargs or {}))
|
||||
|
||||
@classmethod
|
||||
def _is_mutable_type(cls, value: Any) -> bool:
|
||||
"""Check if a value is of a mutable type and should be wrapped.
|
||||
|
||||
Args:
|
||||
value: The value to check.
|
||||
|
||||
Returns:
|
||||
Whether the value is of a mutable type.
|
||||
"""
|
||||
return isinstance(value, cls.__mutable_types__)
|
||||
|
||||
def _wrap_recursive(self, value: Any) -> Any:
|
||||
"""Wrap a value recursively if it is mutable.
|
||||
|
||||
@ -3576,9 +3636,7 @@ class MutableProxy(wrapt.ObjectProxy):
|
||||
The wrapped value.
|
||||
"""
|
||||
# Recursively wrap mutable types, but do not re-wrap MutableProxy instances.
|
||||
if isinstance(value, self.__mutable_types__) and not isinstance(
|
||||
value, MutableProxy
|
||||
):
|
||||
if self._is_mutable_type(value) and not isinstance(value, MutableProxy):
|
||||
return type(self)(
|
||||
wrapped=value,
|
||||
state=self._self_state,
|
||||
@ -3636,7 +3694,7 @@ class MutableProxy(wrapt.ObjectProxy):
|
||||
self._wrap_recursive_decorator,
|
||||
)
|
||||
|
||||
if isinstance(value, self.__mutable_types__) and __name not in (
|
||||
if self._is_mutable_type(value) and __name not in (
|
||||
"__wrapped__",
|
||||
"_self_state",
|
||||
):
|
||||
|
@ -151,6 +151,10 @@ class InvalidPropValueError(ReflexError):
|
||||
"""Raised when a prop value is invalid."""
|
||||
|
||||
|
||||
class StateTooLargeError(ReflexError):
|
||||
"""Raised when the state is too large to be serialized."""
|
||||
|
||||
|
||||
class SystemPackageMissingError(ReflexError):
|
||||
"""Raised when a system package is missing."""
|
||||
|
||||
|
@ -34,7 +34,7 @@ from redis.asyncio import Redis
|
||||
from reflex import constants, model
|
||||
from reflex.compiler import templates
|
||||
from reflex.config import Config, environment, get_config
|
||||
from reflex.utils import console, net, path_ops, processes
|
||||
from reflex.utils import console, net, path_ops, processes, redir
|
||||
from reflex.utils.exceptions import (
|
||||
GeneratedCodeHasNoFunctionDefs,
|
||||
raise_system_package_missing_error,
|
||||
@ -93,6 +93,8 @@ def check_latest_package_version(package_name: str):
|
||||
Args:
|
||||
package_name: The name of the package.
|
||||
"""
|
||||
if environment.REFLEX_CHECK_LATEST_VERSION.get() is False:
|
||||
return
|
||||
try:
|
||||
# Get the latest version from PyPI
|
||||
current_version = importlib.metadata.version(package_name)
|
||||
@ -1209,7 +1211,7 @@ def check_schema_up_to_date():
|
||||
)
|
||||
|
||||
|
||||
def prompt_for_template(templates: list[Template]) -> str:
|
||||
def prompt_for_template_options(templates: list[Template]) -> str:
|
||||
"""Prompt the user to specify a template.
|
||||
|
||||
Args:
|
||||
@ -1221,9 +1223,14 @@ def prompt_for_template(templates: list[Template]) -> str:
|
||||
# Show the user the URLs of each template to preview.
|
||||
console.print("\nGet started with a template:")
|
||||
|
||||
def format_demo_url_str(url: str) -> str:
|
||||
return f" ({url})" if url else ""
|
||||
|
||||
# Prompt the user to select a template.
|
||||
id_to_name = {
|
||||
str(idx): f"{template.name} ({template.demo_url}) - {template.description}"
|
||||
str(
|
||||
idx
|
||||
): f"{template.name.replace('_', ' ').replace('-', ' ')}{format_demo_url_str(template.demo_url)} - {template.description}"
|
||||
for idx, template in enumerate(templates)
|
||||
}
|
||||
for id in range(len(id_to_name)):
|
||||
@ -1378,15 +1385,119 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
|
||||
shutil.rmtree(unzip_dir)
|
||||
|
||||
|
||||
def initialize_app(app_name: str, template: str | None = None) -> str | None:
|
||||
def initialize_default_app(app_name: str):
|
||||
"""Initialize the default app.
|
||||
|
||||
Args:
|
||||
app_name: The name of the app.
|
||||
"""
|
||||
create_config(app_name)
|
||||
initialize_app_directory(app_name)
|
||||
|
||||
|
||||
def validate_and_create_app_using_remote_template(app_name, template, templates):
|
||||
"""Validate and create an app using a remote template.
|
||||
|
||||
Args:
|
||||
app_name: The name of the app.
|
||||
template: The name of the template.
|
||||
templates: The available templates.
|
||||
|
||||
Raises:
|
||||
Exit: If the template is not found.
|
||||
"""
|
||||
# If user selects a template, it needs to exist.
|
||||
if template in templates:
|
||||
template_url = templates[template].code_url
|
||||
else:
|
||||
# Check if the template is a github repo.
|
||||
if template.startswith("https://github.com"):
|
||||
template_url = f"{template.strip('/').replace('.git', '')}/archive/main.zip"
|
||||
else:
|
||||
console.error(f"Template `{template}` not found.")
|
||||
raise typer.Exit(1)
|
||||
|
||||
if template_url is None:
|
||||
return
|
||||
|
||||
create_config_init_app_from_remote_template(
|
||||
app_name=app_name, template_url=template_url
|
||||
)
|
||||
|
||||
|
||||
def generate_template_using_ai(template: str | None = None) -> str:
|
||||
"""Generate a template using AI(Flexgen).
|
||||
|
||||
Args:
|
||||
template: The name of the template.
|
||||
|
||||
Returns:
|
||||
The generation hash.
|
||||
|
||||
Raises:
|
||||
Exit: If the template and ai flags are used.
|
||||
"""
|
||||
if template is None:
|
||||
# If AI is requested and no template specified, redirect the user to reflex.build.
|
||||
return redir.reflex_build_redirect()
|
||||
elif is_generation_hash(template):
|
||||
# Otherwise treat the template as a generation hash.
|
||||
return template
|
||||
else:
|
||||
console.error(
|
||||
"Cannot use `--template` option with `--ai` option. Please remove `--template` option."
|
||||
)
|
||||
raise typer.Exit(2)
|
||||
|
||||
|
||||
def fetch_remote_templates(
|
||||
template: Optional[str] = None,
|
||||
) -> tuple[str, dict[str, Template]]:
|
||||
"""Fetch the available remote templates.
|
||||
|
||||
Args:
|
||||
template: The name of the template.
|
||||
|
||||
Returns:
|
||||
The selected template and the available templates.
|
||||
|
||||
Raises:
|
||||
Exit: If the template is not valid or if the template is not specified.
|
||||
"""
|
||||
available_templates = {}
|
||||
|
||||
try:
|
||||
# Get the available templates
|
||||
available_templates = fetch_app_templates(constants.Reflex.VERSION)
|
||||
except Exception as e:
|
||||
console.warn("Failed to fetch templates. Falling back to default template.")
|
||||
console.debug(f"Error while fetching templates: {e}")
|
||||
template = constants.Templates.DEFAULT
|
||||
|
||||
if template == constants.Templates.DEFAULT:
|
||||
return template, available_templates
|
||||
|
||||
if template in available_templates:
|
||||
return template, available_templates
|
||||
|
||||
else:
|
||||
if template is not None:
|
||||
console.error(f"{template!r} is not a valid template name.")
|
||||
console.print(
|
||||
f"Go to the templates page ({constants.Templates.REFLEX_TEMPLATES_URL}) and copy the command to init with a template."
|
||||
)
|
||||
raise typer.Exit(0)
|
||||
|
||||
|
||||
def initialize_app(
|
||||
app_name: str, template: str | None = None, ai: bool = False
|
||||
) -> str | None:
|
||||
"""Initialize the app either from a remote template or a blank app. If the config file exists, it is considered as reinit.
|
||||
|
||||
Args:
|
||||
app_name: The name of the app.
|
||||
template: The name of the template to use.
|
||||
|
||||
Raises:
|
||||
Exit: If template is directly provided in the command flag and is invalid.
|
||||
ai: Whether to use AI to generate the template.
|
||||
|
||||
Returns:
|
||||
The name of the template.
|
||||
@ -1399,54 +1510,73 @@ def initialize_app(app_name: str, template: str | None = None) -> str | None:
|
||||
telemetry.send("reinit")
|
||||
return
|
||||
|
||||
generation_hash = None
|
||||
if ai:
|
||||
generation_hash = generate_template_using_ai(template)
|
||||
template = constants.Templates.DEFAULT
|
||||
|
||||
templates: dict[str, Template] = {}
|
||||
|
||||
# Don't fetch app templates if the user directly asked for DEFAULT.
|
||||
if template is None or (template != constants.Templates.DEFAULT):
|
||||
try:
|
||||
# Get the available templates
|
||||
templates = fetch_app_templates(constants.Reflex.VERSION)
|
||||
if template is None and len(templates) > 0:
|
||||
template = prompt_for_template(list(templates.values()))
|
||||
except Exception as e:
|
||||
console.warn("Failed to fetch templates. Falling back to default template.")
|
||||
console.debug(f"Error while fetching templates: {e}")
|
||||
finally:
|
||||
template = template or constants.Templates.DEFAULT
|
||||
if template is not None and (template not in (constants.Templates.DEFAULT,)):
|
||||
template, templates = fetch_remote_templates(template)
|
||||
|
||||
if template is None:
|
||||
template = prompt_for_template_options(get_init_cli_prompt_options())
|
||||
if template == constants.Templates.AI:
|
||||
generation_hash = generate_template_using_ai()
|
||||
# change to the default to allow creation of default app
|
||||
template = constants.Templates.DEFAULT
|
||||
elif template == constants.Templates.CHOOSE_TEMPLATES:
|
||||
template, templates = fetch_remote_templates()
|
||||
|
||||
# If the blank template is selected, create a blank app.
|
||||
if template == constants.Templates.DEFAULT:
|
||||
if template in (constants.Templates.DEFAULT,):
|
||||
# Default app creation behavior: a blank app.
|
||||
create_config(app_name)
|
||||
initialize_app_directory(app_name)
|
||||
initialize_default_app(app_name)
|
||||
else:
|
||||
# Fetch App templates from the backend server.
|
||||
console.debug(f"Available templates: {templates}")
|
||||
|
||||
# If user selects a template, it needs to exist.
|
||||
if template in templates:
|
||||
template_url = templates[template].code_url
|
||||
else:
|
||||
# Check if the template is a github repo.
|
||||
if template.startswith("https://github.com"):
|
||||
template_url = (
|
||||
f"{template.strip('/').replace('.git', '')}/archive/main.zip"
|
||||
)
|
||||
else:
|
||||
console.error(f"Template `{template}` not found.")
|
||||
raise typer.Exit(1)
|
||||
|
||||
if template_url is None:
|
||||
return
|
||||
|
||||
create_config_init_app_from_remote_template(
|
||||
app_name=app_name, template_url=template_url
|
||||
validate_and_create_app_using_remote_template(
|
||||
app_name=app_name, template=template, templates=templates
|
||||
)
|
||||
|
||||
# If a reflex.build generation hash is available, download the code and apply it to the main module.
|
||||
if generation_hash:
|
||||
initialize_main_module_index_from_generation(
|
||||
app_name, generation_hash=generation_hash
|
||||
)
|
||||
telemetry.send("init", template=template)
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def get_init_cli_prompt_options() -> list[Template]:
|
||||
"""Get the CLI options for initializing a Reflex app.
|
||||
|
||||
Returns:
|
||||
The CLI options.
|
||||
"""
|
||||
return [
|
||||
Template(
|
||||
name=constants.Templates.DEFAULT,
|
||||
description="A blank Reflex app.",
|
||||
demo_url=constants.Templates.DEFAULT_TEMPLATE_URL,
|
||||
code_url="",
|
||||
),
|
||||
Template(
|
||||
name=constants.Templates.AI,
|
||||
description="Generate a template using AI [Experimental]",
|
||||
demo_url="",
|
||||
code_url="",
|
||||
),
|
||||
Template(
|
||||
name=constants.Templates.CHOOSE_TEMPLATES,
|
||||
description="Choose an existing template.",
|
||||
demo_url="",
|
||||
code_url="",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def initialize_main_module_index_from_generation(app_name: str, generation_hash: str):
|
||||
"""Overwrite the `index` function in the main module with reflex.build generated code.
|
||||
|
||||
|
@ -10,6 +10,18 @@ from .. import constants
|
||||
from . import console
|
||||
|
||||
|
||||
def open_browser(target_url: str) -> None:
|
||||
"""Open a browser window to target_url.
|
||||
|
||||
Args:
|
||||
target_url: The URL to open in the browser.
|
||||
"""
|
||||
if not webbrowser.open(target_url):
|
||||
console.warn(
|
||||
f"Unable to automatically open the browser. Please navigate to {target_url} in your browser."
|
||||
)
|
||||
|
||||
|
||||
def open_browser_and_wait(
|
||||
target_url: str, poll_url: str, interval: int = 2
|
||||
) -> httpx.Response:
|
||||
@ -23,10 +35,7 @@ def open_browser_and_wait(
|
||||
Returns:
|
||||
The response from the poll_url.
|
||||
"""
|
||||
if not webbrowser.open(target_url):
|
||||
console.warn(
|
||||
f"Unable to automatically open the browser. Please navigate to {target_url} in your browser."
|
||||
)
|
||||
open_browser(target_url)
|
||||
console.info("[b]Complete the workflow in the browser to continue.[/b]")
|
||||
while True:
|
||||
try:
|
||||
|
@ -263,7 +263,58 @@ def serialize_base(value: Base) -> dict:
|
||||
Returns:
|
||||
The serialized Base.
|
||||
"""
|
||||
return {k: v for k, v in value.dict().items() if not callable(v)}
|
||||
from reflex.vars.base import Var
|
||||
|
||||
return {
|
||||
k: v for k, v in value.dict().items() if isinstance(v, Var) or not callable(v)
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
|
||||
@serializer(to=dict)
|
||||
def serialize_base_model_v1(model: BaseModelV1) -> dict:
|
||||
"""Serialize a pydantic v1 BaseModel instance.
|
||||
|
||||
Args:
|
||||
model: The BaseModel to serialize.
|
||||
|
||||
Returns:
|
||||
The serialized BaseModel.
|
||||
"""
|
||||
return model.dict()
|
||||
|
||||
from pydantic import BaseModel as BaseModelV2
|
||||
|
||||
if BaseModelV1 is not BaseModelV2:
|
||||
|
||||
@serializer(to=dict)
|
||||
def serialize_base_model_v2(model: BaseModelV2) -> dict:
|
||||
"""Serialize a pydantic v2 BaseModel instance.
|
||||
|
||||
Args:
|
||||
model: The BaseModel to serialize.
|
||||
|
||||
Returns:
|
||||
The serialized BaseModel.
|
||||
"""
|
||||
return model.model_dump()
|
||||
except ImportError:
|
||||
# Older pydantic v1 import
|
||||
from pydantic import BaseModel as BaseModelV1
|
||||
|
||||
@serializer(to=dict)
|
||||
def serialize_base_model_v1(model: BaseModelV1) -> dict:
|
||||
"""Serialize a pydantic v1 BaseModel instance.
|
||||
|
||||
Args:
|
||||
model: The BaseModel to serialize.
|
||||
|
||||
Returns:
|
||||
The serialized BaseModel.
|
||||
"""
|
||||
return model.dict()
|
||||
|
||||
|
||||
@serializer
|
||||
|
@ -14,9 +14,11 @@ from typing import (
|
||||
Callable,
|
||||
ClassVar,
|
||||
Dict,
|
||||
FrozenSet,
|
||||
Iterable,
|
||||
List,
|
||||
Literal,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
@ -29,6 +31,7 @@ from typing import (
|
||||
from typing import get_origin as get_origin_og
|
||||
|
||||
import sqlalchemy
|
||||
from typing_extensions import is_typeddict
|
||||
|
||||
import reflex
|
||||
from reflex.components.core.breakpoints import Breakpoints
|
||||
@ -494,6 +497,14 @@ def _issubclass(cls: GenericType, cls_check: GenericType, instance: Any = None)
|
||||
if isinstance(instance, Breakpoints):
|
||||
return _breakpoints_satisfies_typing(cls_check, instance)
|
||||
|
||||
if isinstance(cls_check_base, tuple):
|
||||
cls_check_base = tuple(
|
||||
cls_check_one if not is_typeddict(cls_check_one) else dict
|
||||
for cls_check_one in cls_check_base
|
||||
)
|
||||
if is_typeddict(cls_check_base):
|
||||
cls_check_base = dict
|
||||
|
||||
# Check if the types match.
|
||||
try:
|
||||
return cls_check_base == Any or issubclass(cls_base, cls_check_base)
|
||||
@ -503,6 +514,36 @@ def _issubclass(cls: GenericType, cls_check: GenericType, instance: Any = None)
|
||||
raise TypeError(f"Invalid type for issubclass: {cls_base}") from te
|
||||
|
||||
|
||||
def does_obj_satisfy_typed_dict(obj: Any, cls: GenericType) -> bool:
|
||||
"""Check if an object satisfies a typed dict.
|
||||
|
||||
Args:
|
||||
obj: The object to check.
|
||||
cls: The typed dict to check against.
|
||||
|
||||
Returns:
|
||||
Whether the object satisfies the typed dict.
|
||||
"""
|
||||
if not isinstance(obj, Mapping):
|
||||
return False
|
||||
|
||||
key_names_to_values = get_type_hints(cls)
|
||||
required_keys: FrozenSet[str] = getattr(cls, "__required_keys__", frozenset())
|
||||
|
||||
if not all(
|
||||
isinstance(key, str)
|
||||
and key in key_names_to_values
|
||||
and _isinstance(value, key_names_to_values[key])
|
||||
for key, value in obj.items()
|
||||
):
|
||||
return False
|
||||
|
||||
# TODO in 3.14: Implement https://peps.python.org/pep-0728/ if it's approved
|
||||
|
||||
# required keys are all present
|
||||
return required_keys.issubset(required_keys)
|
||||
|
||||
|
||||
def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
|
||||
"""Check if an object is an instance of a class.
|
||||
|
||||
@ -529,6 +570,16 @@ def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
|
||||
origin = get_origin(cls)
|
||||
|
||||
if origin is None:
|
||||
# cls is a typed dict
|
||||
if is_typeddict(cls):
|
||||
if nested:
|
||||
return does_obj_satisfy_typed_dict(obj, cls)
|
||||
return isinstance(obj, dict)
|
||||
|
||||
# cls is a float
|
||||
if cls is float:
|
||||
return isinstance(obj, (float, int))
|
||||
|
||||
# cls is a simple class
|
||||
return isinstance(obj, cls)
|
||||
|
||||
@ -553,7 +604,7 @@ def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
|
||||
and len(obj) == len(args)
|
||||
and all(_isinstance(item, arg) for item, arg in zip(obj, args))
|
||||
)
|
||||
if origin is dict:
|
||||
if origin in (dict, Breakpoints):
|
||||
return isinstance(obj, dict) and all(
|
||||
_isinstance(key, args[0]) and _isinstance(value, args[1])
|
||||
for key, value in obj.items()
|
||||
|
@ -1,7 +1,10 @@
|
||||
"""Ensure that Components returned by ComponentState.create have independent State classes."""
|
||||
|
||||
import pytest
|
||||
|
||||
import reflex as rx
|
||||
from reflex.components.base.bare import Bare
|
||||
from reflex.utils.exceptions import ReflexRuntimeError
|
||||
|
||||
|
||||
def test_component_state():
|
||||
@ -40,3 +43,21 @@ def test_component_state():
|
||||
assert len(cs2.children) == 1
|
||||
assert cs2.children[0].render() == Bare.create("b").render()
|
||||
assert cs2.id == "b"
|
||||
|
||||
|
||||
def test_init_component_state() -> None:
|
||||
"""Ensure that ComponentState subclasses cannot be instantiated directly."""
|
||||
|
||||
class CS(rx.ComponentState):
|
||||
@classmethod
|
||||
def get_component(cls, *children, **props):
|
||||
return rx.el.div()
|
||||
|
||||
with pytest.raises(ReflexRuntimeError):
|
||||
CS()
|
||||
|
||||
class SubCS(CS):
|
||||
pass
|
||||
|
||||
with pytest.raises(ReflexRuntimeError):
|
||||
SubCS()
|
||||
|
@ -2,6 +2,7 @@ from typing import Callable, List
|
||||
|
||||
import pytest
|
||||
|
||||
import reflex as rx
|
||||
from reflex.event import (
|
||||
Event,
|
||||
EventChain,
|
||||
@ -439,3 +440,17 @@ def test_event_var_data():
|
||||
# Ensure chain carries _var_data
|
||||
chain_var = Var.create(EventChain(events=[S.s(S.x)], args_spec=_args_spec))
|
||||
assert chain_var._get_all_var_data() == S.x._get_all_var_data()
|
||||
|
||||
|
||||
def test_event_bound_method() -> None:
|
||||
class S(BaseState):
|
||||
@event
|
||||
def e(self, arg: str):
|
||||
print(arg)
|
||||
|
||||
class Wrapper:
|
||||
def get_handler(self, arg: str):
|
||||
return S.e(arg)
|
||||
|
||||
w = Wrapper()
|
||||
_ = rx.input(on_change=w.get_handler)
|
||||
|
@ -16,6 +16,8 @@ from unittest.mock import AsyncMock, Mock
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from plotly.graph_objects import Figure
|
||||
from pydantic import BaseModel as BaseModelV2
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
|
||||
import reflex as rx
|
||||
import reflex.config
|
||||
@ -43,7 +45,7 @@ from reflex.state import (
|
||||
)
|
||||
from reflex.testing import chdir
|
||||
from reflex.utils import format, prerequisites, types
|
||||
from reflex.utils.exceptions import SetUndefinedStateVarError
|
||||
from reflex.utils.exceptions import ReflexRuntimeError, SetUndefinedStateVarError
|
||||
from reflex.utils.format import json_dumps
|
||||
from reflex.vars.base import Var, computed_var
|
||||
from tests.units.states.mutation import MutableSQLAModel, MutableTestState
|
||||
@ -3413,6 +3415,53 @@ def test_typed_state() -> None:
|
||||
_ = TypedState(field="str")
|
||||
|
||||
|
||||
class ModelV1(BaseModelV1):
|
||||
"""A pydantic BaseModel v1."""
|
||||
|
||||
foo: str = "bar"
|
||||
|
||||
|
||||
class ModelV2(BaseModelV2):
|
||||
"""A pydantic BaseModel v2."""
|
||||
|
||||
foo: str = "bar"
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class ModelDC:
|
||||
"""A dataclass."""
|
||||
|
||||
foo: str = "bar"
|
||||
|
||||
|
||||
class PydanticState(rx.State):
|
||||
"""A state with pydantic BaseModel vars."""
|
||||
|
||||
v1: ModelV1 = ModelV1()
|
||||
v2: ModelV2 = ModelV2()
|
||||
dc: ModelDC = ModelDC()
|
||||
|
||||
|
||||
def test_mutable_models():
|
||||
"""Test that dataclass and pydantic BaseModel v1 and v2 use dep tracking."""
|
||||
state = PydanticState()
|
||||
assert isinstance(state.v1, MutableProxy)
|
||||
state.v1.foo = "baz"
|
||||
assert state.dirty_vars == {"v1"}
|
||||
state.dirty_vars.clear()
|
||||
|
||||
assert isinstance(state.v2, MutableProxy)
|
||||
state.v2.foo = "baz"
|
||||
assert state.dirty_vars == {"v2"}
|
||||
state.dirty_vars.clear()
|
||||
|
||||
# Not yet supported ENG-4083
|
||||
# assert isinstance(state.dc, MutableProxy)
|
||||
# state.dc.foo = "baz"
|
||||
# assert state.dirty_vars == {"dc"}
|
||||
# state.dirty_vars.clear()
|
||||
|
||||
|
||||
def test_get_value():
|
||||
class GetValueState(rx.State):
|
||||
foo: str = "FOO"
|
||||
@ -3441,3 +3490,19 @@ def test_get_value():
|
||||
"bar": "foo",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_init_mixin() -> None:
|
||||
"""Ensure that State mixins can not be instantiated directly."""
|
||||
|
||||
class Mixin(BaseState, mixin=True):
|
||||
pass
|
||||
|
||||
with pytest.raises(ReflexRuntimeError):
|
||||
Mixin()
|
||||
|
||||
class SubMixin(Mixin, mixin=True):
|
||||
pass
|
||||
|
||||
with pytest.raises(ReflexRuntimeError):
|
||||
SubMixin()
|
||||
|
Loading…
Reference in New Issue
Block a user