Merge remote-tracking branch 'upstream/main' into minify-state-names-v2
This commit is contained in:
commit
d8def13530
230
poetry.lock
generated
230
poetry.lock
generated
@ -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.6.1", markers = "python_version < \"3.13\""},
|
||||
{version = ">=4.12.2", 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]
|
||||
@ -2198,13 +2206,13 @@ reflex = ">=0.6.0a"
|
||||
|
||||
[[package]]
|
||||
name = "reflex-hosting-cli"
|
||||
version = "0.1.16"
|
||||
version = "0.1.17"
|
||||
description = "Reflex Hosting CLI"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8"
|
||||
files = [
|
||||
{file = "reflex_hosting_cli-0.1.16-py3-none-any.whl", hash = "sha256:1b12c2a76a27571102cacf5ec3028fb09816a51568bff793735e66aaad886605"},
|
||||
{file = "reflex_hosting_cli-0.1.16.tar.gz", hash = "sha256:9c9826318fd3d388b706be70f1f500a35e355f8f86f54138e7789fd6096a4ab9"},
|
||||
{file = "reflex_hosting_cli-0.1.17-py3-none-any.whl", hash = "sha256:cf1accec70745557a40125ffa2a8929e6ef9834808afe78e4f4a01933ac0cb67"},
|
||||
{file = "reflex_hosting_cli-0.1.17.tar.gz", hash = "sha256:263d8dc217eb24d4198ac0bcfd710980bd7795d9818a5e522027657f94752710"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -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 = "a610d4c4bfd852f30e69ad2fbb288c2d9cbdf49e05b9d4936fe5e9b2a7cdefdb"
|
||||
content-hash = "8000601d48cfc1b10d0ae18c6046cc59a50cb6c45e6d3ef4775a3203769f2154"
|
||||
|
@ -49,7 +49,7 @@ wrapt = [
|
||||
{version = ">=1.11.0,<2.0", python = "<3.11"},
|
||||
]
|
||||
packaging = ">=23.1,<25.0"
|
||||
reflex-hosting-cli = ">=0.1.16,<2.0"
|
||||
reflex-hosting-cli = ">=0.1.17,<2.0"
|
||||
charset-normalizer = ">=3.3.2,<4.0"
|
||||
wheel = ">=0.42.0,<1.0"
|
||||
build = ">=1.0.3,<2.0"
|
||||
|
@ -707,6 +707,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)) {
|
||||
@ -764,7 +769,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: "",
|
||||
}),
|
||||
]);
|
||||
@ -839,11 +844,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]);
|
||||
|
||||
|
@ -264,6 +264,7 @@ _MAPPING: dict = {
|
||||
"experimental": ["_x"],
|
||||
"admin": ["AdminDash"],
|
||||
"app": ["App", "UploadFile"],
|
||||
"assets": ["asset"],
|
||||
"base": ["Base"],
|
||||
"components.component": [
|
||||
"Component",
|
||||
|
@ -19,6 +19,7 @@ from . import vars as vars
|
||||
from .admin import AdminDash as AdminDash
|
||||
from .app import App as App
|
||||
from .app import UploadFile as UploadFile
|
||||
from .assets import asset as asset
|
||||
from .base import Base as Base
|
||||
from .components import el as el
|
||||
from .components import lucide as lucide
|
||||
|
95
reflex/assets.py
Normal file
95
reflex/assets.py
Normal file
@ -0,0 +1,95 @@
|
||||
"""Helper functions for adding assets to the app."""
|
||||
|
||||
import inspect
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from reflex import constants
|
||||
from reflex.utils.exec import is_backend_only
|
||||
|
||||
|
||||
def asset(
|
||||
path: str,
|
||||
shared: bool = False,
|
||||
subfolder: Optional[str] = None,
|
||||
_stack_level: int = 1,
|
||||
) -> str:
|
||||
"""Add an asset to the app, either shared as a symlink or local.
|
||||
|
||||
Shared/External/Library assets:
|
||||
Place the file next to your including python file.
|
||||
Links the file to the app's external assets directory.
|
||||
|
||||
Example:
|
||||
```python
|
||||
# my_custom_javascript.js is a shared asset located next to the including python file.
|
||||
rx.script(src=rx.asset(path="my_custom_javascript.js", shared=True))
|
||||
rx.image(src=rx.asset(path="test_image.png", shared=True, subfolder="subfolder"))
|
||||
```
|
||||
|
||||
Local/Internal assets:
|
||||
Place the file in the app's assets/ directory.
|
||||
|
||||
Example:
|
||||
```python
|
||||
# local_image.png is an asset located in the app's assets/ directory. It cannot be shared when developing a library.
|
||||
rx.image(src=rx.asset(path="local_image.png"))
|
||||
```
|
||||
|
||||
Args:
|
||||
path: The relative path of the asset.
|
||||
subfolder: The directory to place the shared asset in.
|
||||
shared: Whether to expose the asset to other apps.
|
||||
_stack_level: The stack level to determine the calling file, defaults to
|
||||
the immediate caller 1. When using rx.asset via a helper function,
|
||||
increase this number for each helper function in the stack.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the file does not exist.
|
||||
ValueError: If subfolder is provided for local assets.
|
||||
|
||||
Returns:
|
||||
The relative URL to the asset.
|
||||
"""
|
||||
assets = constants.Dirs.APP_ASSETS
|
||||
backend_only = is_backend_only()
|
||||
|
||||
# Local asset handling
|
||||
if not shared:
|
||||
cwd = Path.cwd()
|
||||
src_file_local = cwd / assets / path
|
||||
if subfolder is not None:
|
||||
raise ValueError("Subfolder is not supported for local assets.")
|
||||
if not backend_only and not src_file_local.exists():
|
||||
raise FileNotFoundError(f"File not found: {src_file_local}")
|
||||
return f"/{path}"
|
||||
|
||||
# Shared asset handling
|
||||
# Determine the file by which the asset is exposed.
|
||||
frame = inspect.stack()[_stack_level]
|
||||
calling_file = frame.filename
|
||||
module = inspect.getmodule(frame[0])
|
||||
assert module is not None
|
||||
|
||||
external = constants.Dirs.EXTERNAL_APP_ASSETS
|
||||
src_file_shared = Path(calling_file).parent / path
|
||||
if not src_file_shared.exists():
|
||||
raise FileNotFoundError(f"File not found: {src_file_shared}")
|
||||
|
||||
caller_module_path = module.__name__.replace(".", "/")
|
||||
subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path
|
||||
|
||||
# Symlink the asset to the app's external assets directory if running frontend.
|
||||
if not backend_only:
|
||||
# Create the asset folder in the currently compiling app.
|
||||
asset_folder = Path.cwd() / assets / external / subfolder
|
||||
asset_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
dst_file = asset_folder / path
|
||||
|
||||
if not dst_file.exists() and (
|
||||
not dst_file.is_symlink() or dst_file.resolve() != src_file_shared.resolve()
|
||||
):
|
||||
dst_file.symlink_to(src_file_shared)
|
||||
|
||||
return f"/{external}/{subfolder}/{path}"
|
@ -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"
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
||||
|
@ -7,6 +7,7 @@ import importlib
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import urllib.parse
|
||||
from importlib.util import find_spec
|
||||
from pathlib import Path
|
||||
@ -468,6 +469,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."""
|
||||
|
||||
@ -571,6 +580,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)
|
||||
|
||||
# Whether to minify state names. Default to true in prod mode and false otherwise.
|
||||
REFLEX_MINIFY_STATES: EnvVar[Optional[bool]] = env_var(
|
||||
default_factory=lambda: EnvironmentVariables.REFLEX_ENV_MODE.get()
|
||||
@ -844,6 +862,10 @@ def _get_config() -> Config:
|
||||
return rxconfig.config
|
||||
|
||||
|
||||
# Protect sys.path from concurrent modification
|
||||
_config_lock = threading.RLock()
|
||||
|
||||
|
||||
def get_config(reload: bool = False) -> Config:
|
||||
"""Get the app config.
|
||||
|
||||
@ -853,21 +875,26 @@ def get_config(reload: bool = False) -> Config:
|
||||
Returns:
|
||||
The app config.
|
||||
"""
|
||||
# Remove any cached module when `reload` is requested.
|
||||
if reload and constants.Config.MODULE in sys.modules:
|
||||
del sys.modules[constants.Config.MODULE]
|
||||
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
|
||||
|
||||
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.
|
||||
with _config_lock:
|
||||
sys_path = sys.path.copy()
|
||||
sys.path.clear()
|
||||
sys.path.extend(sys_path)
|
||||
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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,14 +1,15 @@
|
||||
"""Helper functions for adding assets to the app."""
|
||||
|
||||
import inspect
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from reflex import constants
|
||||
from reflex import assets
|
||||
from reflex.utils import console
|
||||
|
||||
|
||||
def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
|
||||
"""Add an asset to the app.
|
||||
"""DEPRECATED: use `rx.asset` with `shared=True` instead.
|
||||
|
||||
Add an asset to the app.
|
||||
Place the file next to your including python file.
|
||||
Copies the file to the app's external assets directory.
|
||||
|
||||
@ -22,38 +23,15 @@ def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
|
||||
relative_filename: The relative filename of the asset.
|
||||
subfolder: The directory to place the asset in.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the file does not exist.
|
||||
ValueError: If the module is None.
|
||||
|
||||
Returns:
|
||||
The relative URL to the copied asset.
|
||||
"""
|
||||
# Determine the file by which the asset is exposed.
|
||||
calling_file = inspect.stack()[1].filename
|
||||
module = inspect.getmodule(inspect.stack()[1][0])
|
||||
if module is None:
|
||||
raise ValueError("Module is None")
|
||||
caller_module_path = module.__name__.replace(".", "/")
|
||||
|
||||
subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path
|
||||
|
||||
src_file = Path(calling_file).parent / relative_filename
|
||||
|
||||
assets = constants.Dirs.APP_ASSETS
|
||||
external = constants.Dirs.EXTERNAL_APP_ASSETS
|
||||
|
||||
if not src_file.exists():
|
||||
raise FileNotFoundError(f"File not found: {src_file}")
|
||||
|
||||
# Create the asset folder in the currently compiling app.
|
||||
asset_folder = Path.cwd() / assets / external / subfolder
|
||||
asset_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
dst_file = asset_folder / relative_filename
|
||||
|
||||
if not dst_file.exists():
|
||||
dst_file.symlink_to(src_file)
|
||||
|
||||
asset_url = f"/{external}/{subfolder}/{relative_filename}"
|
||||
return asset_url
|
||||
console.deprecate(
|
||||
feature_name="rx._x.asset",
|
||||
reason="Use `rx.asset` with `shared=True` instead of `rx._x.asset`.",
|
||||
deprecation_version="0.6.6",
|
||||
removal_version="0.7.0",
|
||||
)
|
||||
return assets.asset(
|
||||
relative_filename, shared=True, subfolder=subfolder, _stack_level=2
|
||||
)
|
||||
|
@ -17,10 +17,10 @@ from reflex_cli.v2.deployments import check_version, hosting_cli
|
||||
from reflex import constants
|
||||
from reflex.config import EnvironmentVariables, get_config
|
||||
from reflex.custom_components.custom_components import custom_components_cli
|
||||
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 not 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}")
|
||||
|
||||
@ -675,8 +653,7 @@ def deployv2(
|
||||
project: Optional[str] = typer.Option(
|
||||
None,
|
||||
"--project",
|
||||
help="project to deploy to",
|
||||
hidden=True,
|
||||
help="project id to deploy to",
|
||||
),
|
||||
token: Optional[str] = typer.Option(
|
||||
None,
|
||||
|
@ -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
|
||||
@ -90,6 +97,7 @@ from reflex.utils.exceptions import (
|
||||
ReflexRuntimeError,
|
||||
SetUndefinedStateVarError,
|
||||
StateSchemaMismatchError,
|
||||
StateTooLargeError,
|
||||
)
|
||||
from reflex.utils.exec import is_testing_env
|
||||
from reflex.utils.serializers import serializer
|
||||
@ -110,10 +118,13 @@ 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 EnvironmentVariables.REFLEX_PERF_MODE.get() != PerformanceMode.OFF:
|
||||
# If the state is this large, it's considered a performance issue.
|
||||
TOO_LARGE_SERIALIZED_STATE = (
|
||||
EnvironmentVariables.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 = (
|
||||
@ -1306,7 +1317,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)
|
||||
@ -2155,7 +2166,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,
|
||||
):
|
||||
@ -2163,6 +2174,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 (
|
||||
@ -2170,10 +2184,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 EnvironmentVariables.REFLEX_PERF_MODE.get() == PerformanceMode.WARN:
|
||||
console.warn(msg)
|
||||
elif EnvironmentVariables.REFLEX_PERF_MODE.get() == PerformanceMode.RAISE:
|
||||
raise StateTooLargeError(msg)
|
||||
_WARNED_ABOUT_STATE_SIZE.add(state_full_name)
|
||||
|
||||
@classmethod
|
||||
@ -2215,7 +2233,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 EnvironmentVariables.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 = (
|
||||
@ -3606,7 +3625,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.
|
||||
@ -3646,6 +3674,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.
|
||||
|
||||
@ -3656,9 +3696,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,
|
||||
@ -3716,7 +3754,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, EnvironmentVariables, 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 EnvironmentVariables.REFLEX_CHECK_LATEST_VERSION.get() is False:
|
||||
return
|
||||
try:
|
||||
# Get the latest version from PyPI
|
||||
current_version = importlib.metadata.version(package_name)
|
||||
@ -1212,7 +1214,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:
|
||||
@ -1224,9 +1226,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)):
|
||||
@ -1381,15 +1388,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.
|
||||
@ -1402,54 +1513,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:
|
||||
|
@ -270,6 +270,53 @@ def serialize_base(value: Base) -> dict:
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
def serialize_set(value: Set) -> list:
|
||||
"""Serialize a set to a JSON serializable list.
|
||||
|
@ -576,6 +576,10 @@ def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
|
||||
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)
|
||||
|
||||
|
94
tests/units/assets/test_assets.py
Normal file
94
tests/units/assets/test_assets.py
Normal file
@ -0,0 +1,94 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Generator
|
||||
|
||||
import pytest
|
||||
|
||||
import reflex as rx
|
||||
import reflex.constants as constants
|
||||
|
||||
|
||||
def test_shared_asset() -> None:
|
||||
"""Test shared assets."""
|
||||
# The asset function copies a file to the app's external assets directory.
|
||||
asset = rx.asset(path="custom_script.js", shared=True, subfolder="subfolder")
|
||||
assert asset == "/external/test_assets/subfolder/custom_script.js"
|
||||
result_file = Path(
|
||||
Path.cwd(), "assets/external/test_assets/subfolder/custom_script.js"
|
||||
)
|
||||
assert result_file.exists()
|
||||
|
||||
# Running a second time should not raise an error.
|
||||
asset = rx.asset(path="custom_script.js", shared=True, subfolder="subfolder")
|
||||
|
||||
# Test the asset function without a subfolder.
|
||||
asset = rx.asset(path="custom_script.js", shared=True)
|
||||
assert asset == "/external/test_assets/custom_script.js"
|
||||
result_file = Path(Path.cwd(), "assets/external/test_assets/custom_script.js")
|
||||
assert result_file.exists()
|
||||
|
||||
# clean up
|
||||
shutil.rmtree(Path.cwd() / "assets/external")
|
||||
|
||||
with pytest.raises(FileNotFoundError):
|
||||
asset = rx.asset("non_existent_file.js")
|
||||
|
||||
# Nothing is done to assets when file does not exist.
|
||||
assert not Path(Path.cwd() / "assets/external").exists()
|
||||
|
||||
|
||||
def test_deprecated_x_asset(capsys) -> None:
|
||||
"""Test that the deprecated asset function raises a warning.
|
||||
|
||||
Args:
|
||||
capsys: Pytest fixture that captures stdout and stderr.
|
||||
"""
|
||||
assert rx.asset("custom_script.js", shared=True) == rx._x.asset("custom_script.js")
|
||||
assert (
|
||||
"DeprecationWarning: rx._x.asset has been deprecated in version 0.6.6"
|
||||
in capsys.readouterr().out
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path,shared",
|
||||
[
|
||||
pytest.param("non_existing_file", True),
|
||||
pytest.param("non_existing_file", False),
|
||||
],
|
||||
)
|
||||
def test_invalid_assets(path: str, shared: bool) -> None:
|
||||
"""Test that asset raises an error when the file does not exist.
|
||||
|
||||
Args:
|
||||
path: The path to the asset.
|
||||
shared: Whether the asset should be shared.
|
||||
"""
|
||||
with pytest.raises(FileNotFoundError):
|
||||
_ = rx.asset(path, shared=shared)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def custom_script_in_asset_dir() -> Generator[Path, None, None]:
|
||||
"""Create a custom_script.js file in the app's assets directory.
|
||||
|
||||
Yields:
|
||||
The path to the custom_script.js file.
|
||||
"""
|
||||
asset_dir = Path.cwd() / constants.Dirs.APP_ASSETS
|
||||
asset_dir.mkdir(exist_ok=True)
|
||||
path = asset_dir / "custom_script.js"
|
||||
path.touch()
|
||||
yield path
|
||||
path.unlink()
|
||||
|
||||
|
||||
def test_local_asset(custom_script_in_asset_dir: Path) -> None:
|
||||
"""Test that no error is raised if shared is set and both files exist.
|
||||
|
||||
Args:
|
||||
custom_script_in_asset_dir: Fixture that creates a custom_script.js file in the app's assets directory.
|
||||
|
||||
"""
|
||||
asset = rx.asset("custom_script.js", shared=False)
|
||||
assert asset == "/custom_script.js"
|
@ -1,36 +0,0 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import reflex as rx
|
||||
|
||||
|
||||
def test_asset():
|
||||
# Test the asset function.
|
||||
|
||||
# The asset function copies a file to the app's external assets directory.
|
||||
asset = rx._x.asset("custom_script.js", "subfolder")
|
||||
assert asset == "/external/test_assets/subfolder/custom_script.js"
|
||||
result_file = Path(
|
||||
Path.cwd(), "assets/external/test_assets/subfolder/custom_script.js"
|
||||
)
|
||||
assert result_file.exists()
|
||||
|
||||
# Running a second time should not raise an error.
|
||||
asset = rx._x.asset("custom_script.js", "subfolder")
|
||||
|
||||
# Test the asset function without a subfolder.
|
||||
asset = rx._x.asset("custom_script.js")
|
||||
assert asset == "/external/test_assets/custom_script.js"
|
||||
result_file = Path(Path.cwd(), "assets/external/test_assets/custom_script.js")
|
||||
assert result_file.exists()
|
||||
|
||||
# clean up
|
||||
shutil.rmtree(Path.cwd() / "assets/external")
|
||||
|
||||
with pytest.raises(FileNotFoundError):
|
||||
asset = rx._x.asset("non_existent_file.js")
|
||||
|
||||
# Nothing is done to assets when file does not exist.
|
||||
assert not Path(Path.cwd() / "assets/external").exists()
|
@ -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
|
||||
@ -3414,6 +3416,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"
|
||||
|
Loading…
Reference in New Issue
Block a user