Add serializers for different var types (#1816)

This commit is contained in:
Nikhil Rao 2023-09-15 17:19:26 -07:00 committed by GitHub
parent 74d227d2fd
commit 1938a6cc58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 735 additions and 314 deletions

234
poetry.lock generated
View File

@ -1,14 +1,14 @@
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
[[package]]
name = "alembic"
version = "1.11.1"
version = "1.12.0"
description = "A database migration tool for SQLAlchemy."
optional = false
python-versions = ">=3.7"
files = [
{file = "alembic-1.11.1-py3-none-any.whl", hash = "sha256:dc871798a601fab38332e38d6ddb38d5e734f60034baeb8e2db5b642fccd8ab8"},
{file = "alembic-1.11.1.tar.gz", hash = "sha256:6a810a6b012c88b33458fceb869aef09ac75d6ace5291915ba7fae44de372c01"},
{file = "alembic-1.12.0-py3-none-any.whl", hash = "sha256:03226222f1cf943deee6c85d9464261a6c710cd19b4fe867a3ad1f25afda610f"},
{file = "alembic-1.12.0.tar.gz", hash = "sha256:8e7645c32e4f200675e69f0745415335eb59a3663f5feb487abfa0b30c45888b"},
]
[package.dependencies]
@ -45,13 +45,13 @@ trio = ["trio (<0.22)"]
[[package]]
name = "async-timeout"
version = "4.0.2"
version = "4.0.3"
description = "Timeout context manager for asyncio programs"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
files = [
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
]
[package.dependencies]
@ -230,24 +230,24 @@ pycparser = "*"
[[package]]
name = "cfgv"
version = "3.3.1"
version = "3.4.0"
description = "Validate configuration and produce human readable error messages."
optional = false
python-versions = ">=3.6.1"
python-versions = ">=3.8"
files = [
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
]
[[package]]
name = "click"
version = "8.1.6"
version = "8.1.7"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.7"
files = [
{file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"},
{file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"},
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
]
[package.dependencies]
@ -386,13 +386,13 @@ files = [
[[package]]
name = "exceptiongroup"
version = "1.1.2"
version = "1.1.3"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"},
{file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"},
{file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"},
{file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"},
]
[package.extras]
@ -421,18 +421,19 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6
[[package]]
name = "filelock"
version = "3.12.2"
version = "3.12.4"
description = "A platform independent file lock."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"},
{file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"},
{file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"},
{file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"},
]
[package.extras]
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"]
typing = ["typing-extensions (>=4.7.1)"]
[[package]]
name = "greenlet"
@ -587,13 +588,13 @@ socks = ["socksio (==1.*)"]
[[package]]
name = "identify"
version = "2.5.26"
version = "2.5.29"
description = "File identification library for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "identify-2.5.26-py2.py3-none-any.whl", hash = "sha256:c22a8ead0d4ca11f1edd6c9418c3220669b3b7533ada0a0ffa6cc0ef85cf9b54"},
{file = "identify-2.5.26.tar.gz", hash = "sha256:7243800bce2f58404ed41b7c002e53d4d22bcf3ae1b7900c2d7aefd95394bf7f"},
{file = "identify-2.5.29-py2.py3-none-any.whl", hash = "sha256:24437fbf6f4d3fe6efd0eb9d67e24dd9106db99af5ceb27996a5f7895f24bf1b"},
{file = "identify-2.5.29.tar.gz", hash = "sha256:d43d52b86b15918c137e3a74fff5224f60385cd0e9c38e99d07c257f02f151a5"},
]
[package.extras]
@ -1032,8 +1033,8 @@ files = [
[package.dependencies]
numpy = [
{version = ">=1.20.3", markers = "python_version < \"3.10\""},
{version = ">=1.21.0", markers = "python_version >= \"3.10\""},
{version = ">=1.23.2", markers = "python_version >= \"3.11\""},
{version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""},
]
python-dateutil = ">=2.8.1"
pytz = ">=2020.1"
@ -1052,6 +1053,73 @@ files = [
{file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
]
[[package]]
name = "pillow"
version = "10.0.1"
description = "Python Imaging Library (Fork)"
optional = false
python-versions = ">=3.8"
files = [
{file = "Pillow-10.0.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:8f06be50669087250f319b706decf69ca71fdecd829091a37cc89398ca4dc17a"},
{file = "Pillow-10.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50bd5f1ebafe9362ad622072a1d2f5850ecfa44303531ff14353a4059113b12d"},
{file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6a90167bcca1216606223a05e2cf991bb25b14695c518bc65639463d7db722d"},
{file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f11c9102c56ffb9ca87134bd025a43d2aba3f1155f508eff88f694b33a9c6d19"},
{file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:186f7e04248103482ea6354af6d5bcedb62941ee08f7f788a1c7707bc720c66f"},
{file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0462b1496505a3462d0f35dc1c4d7b54069747d65d00ef48e736acda2c8cbdff"},
{file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d889b53ae2f030f756e61a7bff13684dcd77e9af8b10c6048fb2c559d6ed6eaf"},
{file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:552912dbca585b74d75279a7570dd29fa43b6d93594abb494ebb31ac19ace6bd"},
{file = "Pillow-10.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:787bb0169d2385a798888e1122c980c6eff26bf941a8ea79747d35d8f9210ca0"},
{file = "Pillow-10.0.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fd2a5403a75b54661182b75ec6132437a181209b901446ee5724b589af8edef1"},
{file = "Pillow-10.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d7e91b4379f7a76b31c2dda84ab9e20c6220488e50f7822e59dac36b0cd92b1"},
{file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e9adb3f22d4c416e7cd79b01375b17159d6990003633ff1d8377e21b7f1b21"},
{file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93139acd8109edcdeffd85e3af8ae7d88b258b3a1e13a038f542b79b6d255c54"},
{file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:92a23b0431941a33242b1f0ce6c88a952e09feeea9af4e8be48236a68ffe2205"},
{file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cbe68deb8580462ca0d9eb56a81912f59eb4542e1ef8f987405e35a0179f4ea2"},
{file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:522ff4ac3aaf839242c6f4e5b406634bfea002469656ae8358644fc6c4856a3b"},
{file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:84efb46e8d881bb06b35d1d541aa87f574b58e87f781cbba8d200daa835b42e1"},
{file = "Pillow-10.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:898f1d306298ff40dc1b9ca24824f0488f6f039bc0e25cfb549d3195ffa17088"},
{file = "Pillow-10.0.1-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:bcf1207e2f2385a576832af02702de104be71301c2696d0012b1b93fe34aaa5b"},
{file = "Pillow-10.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d6c9049c6274c1bb565021367431ad04481ebb54872edecfcd6088d27edd6ed"},
{file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28444cb6ad49726127d6b340217f0627abc8732f1194fd5352dec5e6a0105635"},
{file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de596695a75496deb3b499c8c4f8e60376e0516e1a774e7bc046f0f48cd620ad"},
{file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2872f2d7846cf39b3dbff64bc1104cc48c76145854256451d33c5faa55c04d1a"},
{file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4ce90f8a24e1c15465048959f1e94309dfef93af272633e8f37361b824532e91"},
{file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ee7810cf7c83fa227ba9125de6084e5e8b08c59038a7b2c9045ef4dde61663b4"},
{file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1be1c872b9b5fcc229adeadbeb51422a9633abd847c0ff87dc4ef9bb184ae08"},
{file = "Pillow-10.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:98533fd7fa764e5f85eebe56c8e4094db912ccbe6fbf3a58778d543cadd0db08"},
{file = "Pillow-10.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:764d2c0daf9c4d40ad12fbc0abd5da3af7f8aa11daf87e4fa1b834000f4b6b0a"},
{file = "Pillow-10.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fcb59711009b0168d6ee0bd8fb5eb259c4ab1717b2f538bbf36bacf207ef7a68"},
{file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:697a06bdcedd473b35e50a7e7506b1d8ceb832dc238a336bd6f4f5aa91a4b500"},
{file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f665d1e6474af9f9da5e86c2a3a2d2d6204e04d5af9c06b9d42afa6ebde3f21"},
{file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:2fa6dd2661838c66f1a5473f3b49ab610c98a128fc08afbe81b91a1f0bf8c51d"},
{file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:3a04359f308ebee571a3127fdb1bd01f88ba6f6fb6d087f8dd2e0d9bff43f2a7"},
{file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:723bd25051454cea9990203405fa6b74e043ea76d4968166dfd2569b0210886a"},
{file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:71671503e3015da1b50bd18951e2f9daf5b6ffe36d16f1eb2c45711a301521a7"},
{file = "Pillow-10.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:44e7e4587392953e5e251190a964675f61e4dae88d1e6edbe9f36d6243547ff3"},
{file = "Pillow-10.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:3855447d98cced8670aaa63683808df905e956f00348732448b5a6df67ee5849"},
{file = "Pillow-10.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed2d9c0704f2dc4fa980b99d565c0c9a543fe5101c25b3d60488b8ba80f0cce1"},
{file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5bb289bb835f9fe1a1e9300d011eef4d69661bb9b34d5e196e5e82c4cb09b37"},
{file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0d3e54ab1df9df51b914b2233cf779a5a10dfd1ce339d0421748232cea9876"},
{file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:2cc6b86ece42a11f16f55fe8903595eff2b25e0358dec635d0a701ac9586588f"},
{file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ca26ba5767888c84bf5a0c1a32f069e8204ce8c21d00a49c90dabeba00ce0145"},
{file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f0b4b06da13275bc02adfeb82643c4a6385bd08d26f03068c2796f60d125f6f2"},
{file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bc2e3069569ea9dbe88d6b8ea38f439a6aad8f6e7a6283a38edf61ddefb3a9bf"},
{file = "Pillow-10.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:8b451d6ead6e3500b6ce5c7916a43d8d8d25ad74b9102a629baccc0808c54971"},
{file = "Pillow-10.0.1-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:32bec7423cdf25c9038fef614a853c9d25c07590e1a870ed471f47fb80b244db"},
{file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cf63d2c6928b51d35dfdbda6f2c1fddbe51a6bc4a9d4ee6ea0e11670dd981e"},
{file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f6d3d4c905e26354e8f9d82548475c46d8e0889538cb0657aa9c6f0872a37aa4"},
{file = "Pillow-10.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:847e8d1017c741c735d3cd1883fa7b03ded4f825a6e5fcb9378fd813edee995f"},
{file = "Pillow-10.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7f771e7219ff04b79e231d099c0a28ed83aa82af91fd5fa9fdb28f5b8d5addaf"},
{file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459307cacdd4138edee3875bbe22a2492519e060660eaf378ba3b405d1c66317"},
{file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b059ac2c4c7a97daafa7dc850b43b2d3667def858a4f112d1aa082e5c3d6cf7d"},
{file = "Pillow-10.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6caf3cd38449ec3cd8a68b375e0c6fe4b6fd04edb6c9766b55ef84a6e8ddf2d"},
{file = "Pillow-10.0.1.tar.gz", hash = "sha256:d72967b06be9300fed5cfbc8b5bafceec48bf7cdc7dab66b1d2549035287191d"},
]
[package.extras]
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
[[package]]
name = "platformdirs"
version = "3.10.0"
@ -1072,13 +1140,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co
[[package]]
name = "plotly"
version = "5.15.0"
version = "5.17.0"
description = "An open-source, interactive data visualization library for Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "plotly-5.15.0-py2.py3-none-any.whl", hash = "sha256:3508876bbd6aefb8a692c21a7128ca87ce42498dd041efa5c933ee44b55aab24"},
{file = "plotly-5.15.0.tar.gz", hash = "sha256:822eabe53997d5ebf23c77e1d1fcbf3bb6aa745eb05d532afd4b6f9a2e2ab02f"},
{file = "plotly-5.17.0-py2.py3-none-any.whl", hash = "sha256:7c84cdf11da162423da957bb093287134f2d6f170eb9a74f1459f825892247c3"},
{file = "plotly-5.17.0.tar.gz", hash = "sha256:290d796bf7bab87aad184fe24b86096234c4c95dcca6ecbca02d02bdf17d3d97"},
]
[package.dependencies]
@ -1105,13 +1173,13 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pre-commit"
version = "3.3.3"
version = "3.4.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
python-versions = ">=3.8"
files = [
{file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"},
{file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"},
{file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"},
{file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"},
]
[package.dependencies]
@ -1212,13 +1280,13 @@ email = ["email-validator (>=1.0.3)"]
[[package]]
name = "pygments"
version = "2.15.1"
version = "2.16.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.7"
files = [
{file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"},
{file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"},
{file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"},
{file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"},
]
[package.extras]
@ -1226,13 +1294,13 @@ plugins = ["importlib-metadata"]
[[package]]
name = "pyright"
version = "1.1.318"
version = "1.1.327"
description = "Command line wrapper for pyright"
optional = false
python-versions = ">=3.7"
files = [
{file = "pyright-1.1.318-py3-none-any.whl", hash = "sha256:056c1b2e711c3526e32919de1684ae599d34b7ec27e94398858a43f56ac9ba9b"},
{file = "pyright-1.1.318.tar.gz", hash = "sha256:69dcf9c32d5be27d531750de627e76a7cadc741d333b547c09044278b508db7b"},
{file = "pyright-1.1.327-py3-none-any.whl", hash = "sha256:3462cda239e9140276238bbdbd0b59d77406f1c2e14d8cb8c20c8e25639c6b3c"},
{file = "pyright-1.1.327.tar.gz", hash = "sha256:ba74148ad64f22020dbbed6781c4bdb38ecb8a7ca90dc3c87a4f08d1c0e11592"},
]
[package.dependencies]
@ -1257,13 +1325,13 @@ files = [
[[package]]
name = "pytest"
version = "7.4.0"
version = "7.4.2"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"},
{file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"},
{file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"},
{file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"},
]
[package.dependencies]
@ -1348,15 +1416,18 @@ six = ">=1.5"
[[package]]
name = "python-engineio"
version = "4.5.1"
version = "4.7.1"
description = "Engine.IO server and client for Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "python-engineio-4.5.1.tar.gz", hash = "sha256:b167a1b208fcdce5dbe96a61a6ca22391cfa6715d796c22de93e3adf9c07ae0c"},
{file = "python_engineio-4.5.1-py3-none-any.whl", hash = "sha256:67a675569f3e9bb274a8077f3c2068a8fe79cbfcb111cf31ca27b968484fe6c7"},
{file = "python-engineio-4.7.1.tar.gz", hash = "sha256:a8422e345cd9a21451303380b160742ff02197975b1c3a02cef115febe2b1b20"},
{file = "python_engineio-4.7.1-py3-none-any.whl", hash = "sha256:52499e8ab94fea1a6525ffe872fe7028d04b575799c5fa8e2cf7880e032de42e"},
]
[package.dependencies]
simple-websocket = ">=0.10.0"
[package.extras]
asyncio-client = ["aiohttp (>=3.4)"]
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
@ -1377,32 +1448,33 @@ six = ">=1.4.0"
[[package]]
name = "python-socketio"
version = "5.8.0"
version = "5.9.0"
description = "Socket.IO server and client for Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "python-socketio-5.8.0.tar.gz", hash = "sha256:e714f4dddfaaa0cb0e37a1e2deef2bb60590a5b9fea9c343dd8ca5e688416fd9"},
{file = "python_socketio-5.8.0-py3-none-any.whl", hash = "sha256:7adb8867aac1c2929b9c1429f1c02e12ca4c36b67c807967393e367dfbb01441"},
{file = "python-socketio-5.9.0.tar.gz", hash = "sha256:dc42735f65534187f381fde291ebf620216a4960001370f32de940229b2e7f8f"},
{file = "python_socketio-5.9.0-py3-none-any.whl", hash = "sha256:c20f12e4ed0cba57581af26bbeea9998bc2eeebb3b952fa92493a1e051cfe9dc"},
]
[package.dependencies]
bidict = ">=0.21.0"
python-engineio = ">=4.3.0"
python-engineio = ">=4.7.0"
[package.extras]
asyncio-client = ["aiohttp (>=3.4)"]
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
docs = ["sphinx"]
[[package]]
name = "pytz"
version = "2023.3"
version = "2023.3.post1"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
files = [
{file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"},
{file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"},
{file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
{file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
]
[[package]]
@ -1417,6 +1489,7 @@ files = [
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
@ -1424,8 +1497,15 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
@ -1442,6 +1522,7 @@ files = [
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
@ -1449,6 +1530,7 @@ files = [
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
@ -1476,13 +1558,13 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
[[package]]
name = "rich"
version = "13.5.1"
version = "13.5.2"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "rich-13.5.1-py3-none-any.whl", hash = "sha256:b97381b204a206e1be618f5e1215a57174a1a7732490b3bf6668cf41d30bc72d"},
{file = "rich-13.5.1.tar.gz", hash = "sha256:881653ee7037803559d8eae98f145e0a4c4b0ec3ff0300d2cc8d479c71fc6819"},
{file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"},
{file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"},
]
[package.dependencies]
@ -1551,6 +1633,20 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-g
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "simple-websocket"
version = "0.10.1"
description = "Simple WebSocket server and client for Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "simple-websocket-0.10.1.tar.gz", hash = "sha256:0ab46c8ffa51a46dc95eed94608b3b722841c0bf849def71d465c5c356679c82"},
{file = "simple_websocket-0.10.1-py3-none-any.whl", hash = "sha256:62c36bacfd75cc867927bb39d91951342a7234bdfe20f41dd969a3b8bb1413b7"},
]
[package.dependencies]
wsproto = "*"
[[package]]
name = "six"
version = "1.16.0"
@ -1635,7 +1731,7 @@ files = [
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\")"}
greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
@ -1731,13 +1827,13 @@ test = ["aiomysql (>=0.1.1,<0.2.0)", "aiosqlite (>=0.17.0,<0.20.0)", "arrow (>=1
[[package]]
name = "tenacity"
version = "8.2.2"
version = "8.2.3"
description = "Retry code until it succeeds"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
files = [
{file = "tenacity-8.2.2-py3-none-any.whl", hash = "sha256:2f277afb21b851637e8f52e6a613ff08734c347dc19ade928e519d7d2d8569b0"},
{file = "tenacity-8.2.2.tar.gz", hash = "sha256:43af037822bd0029025877f3b2d97cc4d7bb0c2991000a3d59d71517c5c969e0"},
{file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"},
{file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"},
]
[package.extras]
@ -1787,13 +1883,13 @@ sortedcontainers = "*"
[[package]]
name = "trio-websocket"
version = "0.10.3"
version = "0.10.4"
description = "WebSocket library for Trio"
optional = false
python-versions = ">=3.7"
files = [
{file = "trio-websocket-0.10.3.tar.gz", hash = "sha256:1a748604ad906a7dcab9a43c6eb5681e37de4793ba0847ef0bc9486933ed027b"},
{file = "trio_websocket-0.10.3-py3-none-any.whl", hash = "sha256:a9937d48e8132ebf833019efde2a52ca82d223a30a7ea3e8d60a7d28f75a4e3a"},
{file = "trio-websocket-0.10.4.tar.gz", hash = "sha256:e66b3db3e2453017431dfbd352081006654e1241c2a6800dc2f43d7df54d55c5"},
{file = "trio_websocket-0.10.4-py3-none-any.whl", hash = "sha256:c7a620c4013c34b7e4477d89fe76695da1e455e4510a8d7ae13f81c632bdce1d"},
]
[package.dependencies]
@ -1923,13 +2019,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
[[package]]
name = "virtualenv"
version = "20.24.2"
version = "20.24.5"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.7"
files = [
{file = "virtualenv-20.24.2-py3-none-any.whl", hash = "sha256:43a3052be36080548bdee0b42919c88072037d50d56c28bd3f853cbe92b953ff"},
{file = "virtualenv-20.24.2.tar.gz", hash = "sha256:fd8a78f46f6b99a67b7ec5cf73f92357891a7b3a40fd97637c27f854aae3b9e0"},
{file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"},
{file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"},
]
[package.dependencies]
@ -1938,7 +2034,7 @@ filelock = ">=3.12.2,<4"
platformdirs = ">=3.9.1,<4"
[package.extras]
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
[[package]]
@ -2125,4 +2221,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
[metadata]
lock-version = "2.0"
python-versions = "^3.7"
content-hash = "44cce3d4423be203bf6b1ddc046cbdd9061924523b86baea8a42cd954dc86b36"
content-hash = "0dd6230851cc4f43e192e45431d1c1dcb451b7946ae7cd169e220e7f7a072aa2"

View File

@ -29,7 +29,6 @@ fastapi = "^0.96.0"
gunicorn = "^20.1.0"
httpx = "^0.24.0"
jinja2 = "^3.1.2"
plotly = "^5.13.0"
psutil = "^5.9.4"
pydantic = "^1.10.2"
python-multipart = "^0.0.5"
@ -63,6 +62,10 @@ pandas = [
{version = "^1.5.3", python = ">=3.8,<4.0"},
{version = "^1.1", python = ">=3.7, <3.8"}
]
pillow = [
{version = "^10.0.0", python = ">=3.8,<4.0"}
]
plotly = "^5.13.0"
asynctest = "^0.13.0"
pre-commit = {version = "^3.2.1", python = ">=3.8,<4.0"}
selenium = "^4.11.0"

View File

@ -1,10 +1,13 @@
"""Table components."""
from __future__ import annotations
from typing import Any, Dict, List, Union
from reflex.components.component import Component
from reflex.components.tags import Tag
from reflex.utils import format, imports, types
from reflex.utils import imports, types
from reflex.utils.serializers import serialize, serializer
from reflex.vars import BaseVar, ComputedVar, ImportVar, Var
@ -106,23 +109,59 @@ class DataTable(Gridjs):
)
def _render(self) -> Tag:
if isinstance(self.data, Var):
if types.is_dataframe(self.data.type_):
self.columns = BaseVar(
name=f"{self.data.name}.columns",
type_=List[Any],
state=self.data.state,
)
self.data = BaseVar(
name=f"{self.data.name}.data",
type_=List[List[Any]],
state=self.data.state,
)
else:
if isinstance(self.data, Var) and types.is_dataframe(self.data.type_):
self.columns = BaseVar(
name=f"{self.data.name}.columns",
type_=List[Any],
state=self.data.state,
)
self.data = BaseVar(
name=f"{self.data.name}.data",
type_=List[List[Any]],
state=self.data.state,
)
if types.is_dataframe(type(self.data)):
# If given a pandas df break up the data and columns
if types.is_dataframe(type(self.data)):
self.columns = Var.create(list(self.data.columns.values.tolist())) # type: ignore
self.data = Var.create(format.format_dataframe_values(self.data)) # type: ignore
data = serialize(self.data)
assert isinstance(data, dict), "Serialized dataframe should be a dict."
self.columns = Var.create_safe(data["columns"])
self.data = Var.create_safe(data["data"])
# Render the table.
return super()._render()
try:
from pandas import DataFrame
def format_dataframe_values(df: DataFrame) -> List[List[Any]]:
"""Format dataframe values to a list of lists.
Args:
df: The dataframe to format.
Returns:
The dataframe as a list of lists.
"""
return [
[str(d) if isinstance(d, (list, tuple)) else d for d in data]
for data in list(df.values.tolist())
]
@serializer
def serialize_dataframe(df: DataFrame) -> dict:
"""Serialize a pandas dataframe.
Args:
df: The dataframe to serialize.
Returns:
The serialized dataframe.
"""
return {
"columns": df.columns.tolist(),
"data": format_dataframe_values(df),
}
except ImportError:
pass

View File

@ -25,6 +25,7 @@ class Tabs(ChakraComponent):
is_manual: If true, the tabs will be manually activated and display its panel by pressing Space or Enter. If false, the tabs will be automatically activated and their panel is displayed when they receive focus.
orientation: The orientation of the tab list.
variant: "line" | "enclosed" | "enclosed-colored" | "soft-rounded" | "solid-rounded" | "unstyled"
color_scheme: The color scheme of the tabs.
items: The items for the tabs component, a list of tuple (label, panel)
**props: The properties of the component.

View File

@ -13,7 +13,7 @@ from reflex.event import EventHandler, EventChain, EventSpec
class Select(ChakraComponent):
@overload
@classmethod
def create(cls, *children, value: Optional[Union[Var[str], str]] = None, default_value: Optional[Union[Var[str], str]] = None, placeholder: Optional[Union[Var[str], str]] = None, error_border_color: Optional[Union[Var[str], str]] = None, focus_border_color: Optional[Union[Var[str], str]] = None, is_disabled: Optional[Union[Var[bool], bool]] = None, is_invalid: Optional[Union[Var[bool], bool]] = None, is_read_only: Optional[Union[Var[bool], bool]] = None, is_required: Optional[Union[Var[bool], bool]] = None, variant: Optional[Union[Var[str], str]] = None, size: Optional[Union[Var[str], str]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_change: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Select": # type: ignore
def create(cls, *children, value: Optional[Union[Var[str], str]] = None, default_value: Optional[Union[Var[str], str]] = None, placeholder: Optional[Union[Var[str], str]] = None, error_border_color: Optional[Union[Var[str], str]] = None, focus_border_color: Optional[Union[Var[str], str]] = None, is_disabled: Optional[Union[Var[bool], bool]] = None, is_invalid: Optional[Union[Var[bool], bool]] = None, is_required: Optional[Union[Var[bool], bool]] = None, variant: Optional[Union[Var[str], str]] = None, size: Optional[Union[Var[str], str]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_change: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Select": # type: ignore
"""Create a select component.
If a list is provided as the first children, a default component
@ -28,7 +28,6 @@ class Select(ChakraComponent):
focus_border_color: The border color when the select is focused.
is_disabled: If true, the select will be disabled.
is_invalid: If true, the form control will be invalid. This has 2 side effects: - The FormLabel and FormErrorIcon will have `data-invalid` set to true - The form element (e.g, Input) will have `aria-invalid` set to true
is_read_only: If true, the form control will be readonly
is_required: If true, the form control will be required. This has 2 side effects: - The FormLabel will show a required indicator - The form element (e.g, Input) will have `aria-required` set to true
variant: "outline" | "filled" | "flushed" | "unstyled"
size: The size of the select.

View File

@ -1,13 +1,17 @@
"""Component for displaying a plotly graph."""
from typing import Dict, List
from plotly.graph_objects import Figure
import json
from typing import Any, Dict, List
from reflex.components.component import NoSSRComponent
from reflex.components.tags import Tag
from reflex.utils.serializers import serializer
from reflex.vars import Var
try:
from plotly.graph_objects import Figure
except ImportError:
Figure = Any
class PlotlyLib(NoSSRComponent):
"""A component that wraps a plotly lib."""
@ -39,14 +43,22 @@ class Plotly(PlotlyLib):
# If true, the graph will resize when the window is resized.
use_resize_handler: Var[bool]
def _render(self) -> Tag:
if (
isinstance(self.data, Figure)
and self.layout is None
and self.width is not None
):
layout = Var.create({"width": self.width, "height": self.height})
assert layout is not None
self.layout = layout
return super()._render()
try:
from plotly.graph_objects import Figure
from plotly.io import to_json
@serializer
def serialize_figure(figure: Figure) -> list:
"""Serialize a plotly figure.
Args:
figure: The figure to serialize.
Returns:
The serialized figure.
"""
return json.loads(str(to_json(figure)))["data"]
except ImportError:
pass

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, List, Optional, Union, overload
from typing import Any, Dict, List, Optional, Union, overload
from reflex.components.component import Component
from reflex.components.component import NoSSRComponent
from reflex.vars import Var, BaseVar, ComputedVar
@ -31,7 +31,7 @@ class PlotlyLib(NoSSRComponent):
class Plotly(PlotlyLib):
@overload
@classmethod
def create(cls, *children, data: Optional[Union[Var[Figure], Figure]] = None, layout: Optional[Union[Var[Dict], Dict]] = None, width: Optional[Union[Var[str], str]] = None, height: Optional[Union[Var[str], str]] = None, use_resize_handler: Optional[Union[Var[bool], bool]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Plotly": # type: ignore
def create(cls, *children, data: Optional[Union[Var[Any], Any]] = None, layout: Optional[Union[Var[Dict], Dict]] = None, width: Optional[Union[Var[str], str]] = None, height: Optional[Union[Var[str], str]] = None, use_resize_handler: Optional[Union[Var[bool], bool]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Plotly": # type: ignore
"""Create the component.
Args:

View File

@ -1,12 +1,14 @@
"""An image component."""
from __future__ import annotations
from typing import Any, Optional, Set
import base64
import io
from typing import Any, Optional
from reflex.components.component import Component
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.tags import Tag
from reflex.utils import format, types
from reflex.utils.serializers import serializer
from reflex.vars import Var
@ -51,7 +53,7 @@ class Image(ChakraComponent):
# Learn more _[here](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)_
src_set: Var[str]
def get_triggers(self) -> Set[str]:
def get_triggers(self) -> set[str]:
"""Get the event triggers for the component.
Returns:
@ -60,9 +62,30 @@ class Image(ChakraComponent):
return super().get_triggers() | {"on_error", "on_load"}
def _render(self) -> Tag:
# If the src is an image, convert it to a base64 string.
if types.is_image(type(self.src)):
self.src = Var.create(format.format_image_data(self.src)) # type: ignore
self.src.is_string = True
# Render the table.
return super()._render()
try:
from PIL.Image import Image as Img
@serializer
def serialize_image(image: Img) -> str:
"""Serialize a plotly figure.
Args:
image: The image to serialize.
Returns:
The serialized image.
"""
buff = io.BytesIO()
image.save(buff, format="PNG")
image_bytes = buff.getvalue()
base64_image = base64.b64encode(image_bytes).decode("utf-8")
return f"data:image/png;base64,{base64_image}"
except ImportError:
pass

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Any, Optional, Set, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -197,6 +197,7 @@ class Model(Base, sqlmodel.SQLModel):
target_metadata=sqlmodel.SQLModel.metadata,
render_item=cls._alembic_render_item,
process_revision_directives=writer, # type: ignore
compare_type=False,
)
env.run_migrations()
changes_detected = False

View File

@ -2,22 +2,16 @@
from __future__ import annotations
import base64
import io
import json
import os
import os.path as op
import re
import sys
import types as builtin_types
from typing import TYPE_CHECKING, Any, Callable, Type, Union
import plotly.graph_objects as go
from plotly.graph_objects import Figure
from plotly.io import to_json
from typing import TYPE_CHECKING, Any, Union
from reflex import constants
from reflex.utils import exceptions, types
from reflex.utils import exceptions, serializers, types
from reflex.utils.serializers import serialize
from reflex.vars import Var
if TYPE_CHECKING:
@ -316,12 +310,9 @@ def format_prop(
return prop
return json_dumps(prop)
elif isinstance(prop, Figure):
prop = json.loads(to_json(prop))["data"] # type: ignore
# For dictionaries, convert any properties to strings.
elif isinstance(prop, dict):
prop = format_dict(prop)
prop = serializers.serialize_dict(prop) # type: ignore
else:
# Dump the prop as JSON.
@ -461,44 +452,6 @@ def format_query_params(router_data: dict[str, Any]) -> dict[str, str]:
return {k.replace("-", "_"): v for k, v in params.items()}
def format_dataframe_values(value: Type) -> list[Any]:
"""Format dataframe values.
Args:
value: The value to format.
Returns:
Format data
"""
if not types.is_dataframe(type(value)):
return value
format_data = []
for data in list(value.values.tolist()):
element = []
for d in data:
element.append(str(d) if isinstance(d, (list, tuple)) else d)
format_data.append(element)
return format_data
def format_image_data(value: Type) -> str:
"""Format image data.
Args:
value: The value to format.
Returns:
Format data
"""
buff = io.BytesIO()
value.save(buff, format="PNG")
image_bytes = buff.getvalue()
base64_image = base64.b64encode(image_bytes).decode("utf-8")
return f"data:image/png;base64,{base64_image}"
def format_state(value: Any) -> Any:
"""Recursively format values in the given state.
@ -523,30 +476,12 @@ def format_state(value: Any) -> Any:
if isinstance(value, types.StateBases):
return value
# Convert plotly figures to JSON.
if isinstance(value, go.Figure):
return json.loads(to_json(value))["data"] # type: ignore
# Serialize the value.
serialized = serialize(value)
if serialized is not None:
return serialized
# Convert pandas dataframes to JSON.
if types.is_dataframe(type(value)):
return {
"columns": value.columns.tolist(),
"data": format_dataframe_values(value),
}
# Convert datetime objects to str.
if types.is_datetime(type(value)):
return str(value)
# Convert Image objects to base64.
if types.is_image(type(value)):
return format_image_data(value) # type: ignore
raise TypeError(
"State vars must be primitive Python types, "
"or subclasses of rx.Base. "
f"Got var of type {type(value)}."
)
raise TypeError(f"No JSON serializer found for var {value} of type {type(value)}.")
def format_ref(ref: str) -> str:
@ -580,58 +515,6 @@ def format_array_ref(refs: str, idx: Var | None) -> str:
return f"refs_{clean_ref}"
def format_dict(prop: ComponentStyle) -> str:
"""Format a dict with vars potentially as values.
Args:
prop: The dict to format.
Returns:
The formatted dict.
Raises:
InvalidStylePropError: If a style prop has a callable value
"""
# Import here to avoid circular imports.
from reflex.event import EventHandler
from reflex.vars import Var
prop_dict = {}
# Convert any var keys to strings.
for key, value in prop.items():
if issubclass(type(value), Callable):
raise exceptions.InvalidStylePropError(
f"The style prop `{to_snake_case(key)}` cannot have " # type: ignore
f"`{value.fn.__qualname__ if isinstance(value, EventHandler) else value.__qualname__ if isinstance(value, builtin_types.FunctionType) else value}`, "
f"an event handler or callable as its value"
)
prop_dict[key] = str(value) if isinstance(value, Var) else value
# Dump the dict to a string.
fprop = json_dumps(prop_dict)
def unescape_double_quotes_in_var(m: re.Match) -> str:
# Since the outer quotes are removed, the inner escaped quotes must be unescaped.
return re.sub('\\\\"', '"', m.group(1))
# This substitution is necessary to unwrap var values.
fprop = re.sub(
pattern=r"""
(?<!\\) # must NOT start with a backslash
" # match opening double quote of JSON value
{(.*?)} # extract the value between curly braces (non-greedy)
" # match must end with an unescaped double quote
""",
repl=unescape_double_quotes_in_var,
string=fprop,
flags=re.VERBOSE,
)
# Return the formatted dict.
return fprop
def format_breadcrumbs(route: str) -> list[tuple[str, str]]:
"""Take a route and return a list of tuple for use in breadcrumb.

193
reflex/utils/serializers.py Normal file
View File

@ -0,0 +1,193 @@
"""Serializers used to convert Var types to JSON strings."""
from __future__ import annotations
import re
import types as builtin_types
from datetime import date, datetime, time, timedelta
from typing import Any, Callable, Dict, Type, Union, get_type_hints
from reflex.utils import exceptions, types
# Mapping from type to a serializer.
# The serializer should convert the type to a JSON object.
SerializedType = Union[str, bool, int, float, list, dict]
Serializer = Callable[[Type], SerializedType]
SERIALIZERS: dict[Type, Serializer] = {}
def serializer(fn: Serializer) -> Serializer:
"""Decorator to add a serializer for a given type.
Args:
fn: The function to decorate.
Returns:
The decorated function.
Raises:
ValueError: If the function does not take a single argument.
"""
# Get the global serializers.
global SERIALIZERS
# Check the type hints to get the type of the argument.
type_hints = get_type_hints(fn)
args = [arg for arg in type_hints if arg != "return"]
# Make sure the function takes a single argument.
if len(args) != 1:
raise ValueError("Serializer must take a single argument.")
# Get the type of the argument.
type_ = type_hints[args[0]]
# Make sure the type is not already registered.
registered_fn = SERIALIZERS.get(type_)
if registered_fn is not None and registered_fn != fn:
raise ValueError(
f"Serializer for type {type_} is already registered as {registered_fn.__qualname__}."
)
# Register the serializer.
SERIALIZERS[type_] = fn
# Return the function.
return fn
def serialize(value: Any) -> SerializedType | None:
"""Serialize the value to a JSON string.
Args:
value: The value to serialize.
Returns:
The serialized value, or None if a serializer is not found.
"""
# Get the serializer for the type.
serializer = get_serializer(type(value))
# If there is no serializer, return None.
if serializer is None:
return None
# Serialize the value.
return serializer(value)
def get_serializer(type_: Type) -> Serializer | None:
"""Get the serializer for the type.
Args:
type_: The type to get the serializer for.
Returns:
The serializer for the type, or None if there is no serializer.
"""
global SERIALIZERS
# First, check if the type is registered.
serializer = SERIALIZERS.get(type_)
if serializer is not None:
return serializer
# If the type is not registered, check if it is a subclass of a registered type.
for registered_type, serializer in SERIALIZERS.items():
if types._issubclass(type_, registered_type):
return serializer
# If there is no serializer, return None.
return None
def has_serializer(type_: Type) -> bool:
"""Check if there is a serializer for the type.
Args:
type_: The type to check.
Returns:
Whether there is a serializer for the type.
"""
return get_serializer(type_) is not None
@serializer
def serialize_str(value: str) -> str:
"""Serialize a string.
Args:
value: The string to serialize.
Returns:
The serialized string.
"""
return value
@serializer
def serialize_dict(prop: Dict[str, Any]) -> str:
"""Serialize a dictionary to a JSON string.
Args:
prop: The dictionary to serialize.
Returns:
The serialized dictionary.
Raises:
InvalidStylePropError: If the style prop is invalid.
"""
# Import here to avoid circular imports.
from reflex.event import EventHandler
from reflex.utils.format import json_dumps, to_snake_case
from reflex.vars import Var
prop_dict = {}
# Convert any var keys to strings.
for key, value in prop.items():
if types._issubclass(type(value), Callable):
raise exceptions.InvalidStylePropError(
f"The style prop `{to_snake_case(key)}` cannot have " # type: ignore
f"`{value.fn.__qualname__ if isinstance(value, EventHandler) else value.__qualname__ if isinstance(value, builtin_types.FunctionType) else value}`, "
f"an event handler or callable as its value"
)
prop_dict[key] = str(value) if isinstance(value, Var) else value
# Dump the dict to a string.
fprop = json_dumps(prop_dict)
def unescape_double_quotes_in_var(m: re.Match) -> str:
# Since the outer quotes are removed, the inner escaped quotes must be unescaped.
return re.sub('\\\\"', '"', m.group(1))
# This substitution is necessary to unwrap var values.
fprop = re.sub(
pattern=r"""
(?<!\\) # must NOT start with a backslash
" # match opening double quote of JSON value
{(.*?)} # extract the value between curly braces (non-greedy)
" # match must end with an unescaped double quote
""",
repl=unescape_double_quotes_in_var,
string=fprop,
flags=re.VERBOSE,
)
# Return the formatted dict.
return fprop
@serializer
def serialize_datetime(dt: Union[date, datetime, time, timedelta]) -> str:
"""Serialize a datetime to a JSON string.
Args:
dt: The datetime to serialize.
Returns:
The serialized datetime.
"""
return str(dt)

View File

@ -4,10 +4,10 @@ from __future__ import annotations
import contextlib
import typing
from datetime import date, datetime, time, timedelta
from typing import Any, Callable, Type, Union, _GenericAlias # type: ignore
from reflex.base import Base
from reflex.utils import serializers
# Union of generic types.
GenericType = Union[Type, _GenericAlias]
@ -143,60 +143,16 @@ def is_dataframe(value: Type) -> bool:
return value.__name__ == "DataFrame"
def is_image(value: Type) -> bool:
"""Check if the given value is a pillow image. By checking if the value subclasses PIL.
def is_valid_var_type(type_: Type) -> bool:
"""Check if the given type is a valid prop type.
Args:
value: The value to check.
type_: The type to check.
Returns:
Whether the value is a pillow image.
Whether the type is a valid prop type.
"""
if is_generic_alias(value) or value == typing.Any:
return False
return "PIL" in value.__module__
def is_figure(value: Type) -> bool:
"""Check if the given value is a figure.
Args:
value: The value to check.
Returns:
Whether the value is a figure.
"""
return value.__name__ == "Figure"
def is_datetime(value: Type) -> bool:
"""Check if the given value is a datetime object.
Args:
value: The value to check.
Returns:
Whether the value is a date, datetime, time, or timedelta.
"""
return issubclass(value, (date, datetime, time, timedelta))
def is_valid_var_type(var: Type) -> bool:
"""Check if the given value is a valid prop type.
Args:
var: The value to check.
Returns:
Whether the value is a valid prop type.
"""
return (
_issubclass(var, StateVar)
or is_dataframe(var)
or is_figure(var)
or is_image(var)
or is_datetime(var)
)
return _issubclass(type_, StateVar) or serializers.has_serializer(type_)
def is_backend_variable(name: str) -> bool:

View File

@ -24,13 +24,12 @@ from typing import (
get_type_hints,
)
from plotly.graph_objects import Figure
from plotly.io import to_json
from pydantic.fields import ModelField
from reflex import constants
from reflex.base import Base
from reflex.utils import console, format, types
from reflex.utils.serializers import serialize
if TYPE_CHECKING:
from reflex.state import State
@ -126,19 +125,16 @@ class Var(ABC):
type_ = type(value)
# Special case for plotly figures.
if isinstance(value, Figure):
value = json.loads(to_json(value))["data"] # type: ignore
type_ = Figure
if isinstance(value, dict):
value = format.format_dict(value)
# Try to serialize the value.
serialized = serialize(value)
if serialized is not None:
value = serialized
try:
name = value if isinstance(value, str) else json.dumps(value)
except TypeError as e:
raise TypeError(
f"To create a Var must be Var or JSON-serializable. Got {value} of type {type(value)}."
f"No JSON serializer found for var {value} of type {type_}."
) from e
return BaseVar(name=name, type_=type_, is_local=is_local, is_string=is_string)
@ -184,7 +180,7 @@ class Var(ABC):
"""
if self.state:
return self.full_name
if self.is_string or self.type_ is Figure:
if self.is_string:
return self.name
try:
return json.loads(self.name)

View File

@ -7,7 +7,7 @@ import re
import sys
from inspect import getfullargspec
from pathlib import Path
from typing import Any, Dict, List, Optional, get_args
from typing import Any, Dict, List, Optional, Union, get_args # NOQA
import black
@ -181,7 +181,14 @@ class PyiGenerator:
return _get_var_definition(self.current_module, _name)
def _generate_function(self, _name, _func):
definition = "".join(inspect.getsource(_func).split(":\n")[0].split("\n"))
import textwrap
# Don't generate indented functions.
source = inspect.getsource(_func)
if textwrap.dedent(source) != source:
return []
definition = "".join([line for line in source.split(":\n")[0].split("\n")])
return [f"{definition}:", " ..."]
def _write_pyi_file(self, variables, functions, classes):

View File

@ -2,8 +2,12 @@ import pandas as pd
import pytest
import reflex as rx
from reflex.components import data_table
from reflex.components.datadisplay.datatable import (
DataTable,
serialize_dataframe, # type: ignore
)
from reflex.utils import types
from reflex.utils.serializers import serialize
@pytest.mark.parametrize(
@ -31,11 +35,11 @@ def test_validate_data_table(data_table_state: rx.Var, expected):
"""
if not types.is_dataframe(data_table_state.data.type_):
data_table_component = data_table(
data_table_component = DataTable.create(
data=data_table_state.data, columns=data_table_state.columns
)
else:
data_table_component = data_table(data=data_table_state.data)
data_table_component = DataTable.create(data=data_table_state.data)
data_table_dict = data_table_component.render()
@ -62,7 +66,7 @@ def test_invalid_props(props):
props: props to pass in component.
"""
with pytest.raises(ValueError):
data_table(**props)
DataTable.create(**props)
@pytest.mark.parametrize(
@ -96,10 +100,21 @@ def test_computed_var_without_annotation(fixture, request, err_msg, is_data_fram
"""
with pytest.raises(ValueError) as err:
if is_data_frame:
data_table(data=request.getfixturevalue(fixture).data)
DataTable.create(data=request.getfixturevalue(fixture).data)
else:
data_table(
DataTable.create(
data=request.getfixturevalue(fixture).data,
columns=request.getfixturevalue(fixture).columns,
)
assert err.value.args[0] == err_msg
def test_serialize_dataframe():
"""Test if dataframe is serialized correctly."""
df = pd.DataFrame(
[["foo", "bar"], ["foo1", "bar1"]], columns=["column1", "column2"]
)
value = serialize(df)
assert value == serialize_dataframe(df)
assert isinstance(value, dict)
assert list(value.keys()) == ["columns", "data"]

View File

@ -0,0 +1,34 @@
import numpy as np
import plotly.graph_objects as go
import pytest
from reflex.components.graphing.plotly import serialize_figure # type: ignore
from reflex.utils.serializers import serialize
@pytest.fixture
def plotly_fig() -> go.Figure:
"""Get a plotly figure.
Returns:
A random plotly figure.
"""
# Generate random data.
data = np.random.randint(0, 10, size=(10, 4))
trace = go.Scatter(
x=list(range(len(data))), y=data[:, 0], mode="lines", name="Trace 1"
)
# Create a graph.
return go.Figure(data=[trace])
def test_serialize_plotly(plotly_fig: go.Figure):
"""Test that serializing a plotly figure works.
Args:
plotly_fig: The figure to serialize.
"""
value = serialize(plotly_fig)
assert isinstance(value, list)
assert value == serialize_figure(plotly_fig)

View File

@ -0,0 +1,65 @@
import pytest
try:
# PIL is only available in python 3.8+
import numpy as np
import PIL
from PIL.Image import Image as Img
import reflex as rx
from reflex.components.media.image import Image, serialize_image # type: ignore
from reflex.utils.serializers import serialize
@pytest.fixture
def pil_image() -> Img:
"""Get an image.
Returns:
A random PIL image.
"""
imarray = np.random.rand(100, 100, 3) * 255
return PIL.Image.fromarray(imarray.astype("uint8")).convert("RGBA") # type: ignore
def test_serialize_image(pil_image: Img):
"""Test that serializing an image works.
Args:
pil_image: The image to serialize.
"""
data = serialize(pil_image)
assert isinstance(data, str)
assert data == serialize_image(pil_image)
assert data.startswith("data:image/png;base64,")
def test_set_src_str():
"""Test that setting the src works."""
image = rx.image(src="pic2.jpeg")
assert str(image.src) == "pic2.jpeg" # type: ignore
def test_set_src_img(pil_image: Img):
"""Test that setting the src works.
Args:
pil_image: The image to serialize.
"""
image = Image.create(src=pil_image)
assert str(image.src) == serialize_image(pil_image) # type: ignore
def test_render(pil_image: Img):
"""Test that rendering an image works.
Args:
pil_image: The image to serialize.
"""
image = Image.create(src=pil_image)
assert not image.src.is_string # type: ignore
image._render()
assert image.src.is_string # type: ignore
except ImportError:
def test_pillow_import():
"""Make sure the Python version is less than 3.8."""
import sys
assert sys.version_info < (3, 8)

View File

@ -230,14 +230,9 @@ def test_create_type_error():
value = ErrorType()
with pytest.raises(TypeError) as exception:
with pytest.raises(TypeError):
Var.create(value)
assert (
exception.value.args[0]
== f"To create a Var must be Var or JSON-serializable. Got {value} of type {type(value)}."
)
def v(value) -> Var:
val = (

0
tests/utils/__init__.py Normal file
View File

View File

@ -0,0 +1,102 @@
import datetime
from typing import Any, Dict, Type
import pytest
from reflex.utils import serializers
@pytest.mark.parametrize(
"type_,expected",
[
(str, True),
(dict, True),
(Dict[int, int], True),
],
)
def test_has_serializer(type_: Type, expected: bool):
"""Test that has_serializer returns the correct value.
Args:
type_: The type to check.
expected: The expected result.
"""
assert serializers.has_serializer(type_) == expected
@pytest.mark.parametrize(
"type_,expected",
[
(str, serializers.serialize_str),
(dict, serializers.serialize_dict),
(Dict[int, int], serializers.serialize_dict),
(datetime.datetime, serializers.serialize_datetime),
(datetime.date, serializers.serialize_datetime),
(datetime.time, serializers.serialize_datetime),
(datetime.timedelta, serializers.serialize_datetime),
],
)
def test_get_serializer(type_: Type, expected: serializers.Serializer):
"""Test that get_serializer returns the correct value.
Args:
type_: The type to check.
expected: The expected result.
"""
assert serializers.get_serializer(type_) == expected
def test_add_serializer():
"""Test that adding a serializer works."""
def serialize_test(value: int) -> str:
"""Serialize an int to a string.
Args:
value: The value to serialize.
Returns:
The serialized value.
"""
return str(value)
# Initially there should be no serializer for int.
assert not serializers.has_serializer(int)
assert serializers.serialize(5) is None
# Register the serializer.
assert serializers.serializer(serialize_test) == serialize_test
# There should now be a serializer for int.
assert serializers.has_serializer(int)
assert serializers.get_serializer(int) == serialize_test
assert serializers.serialize(5) == "5"
# Remove the serializer.
serializers.SERIALIZERS.pop(int)
@pytest.mark.parametrize(
"value,expected",
[
("test", "test"),
(datetime.datetime(2021, 1, 1, 1, 1, 1, 1), "2021-01-01 01:01:01.000001"),
(datetime.date(2021, 1, 1), "2021-01-01"),
(datetime.time(1, 1, 1, 1), "01:01:01.000001"),
(datetime.timedelta(1, 1, 1), "1 day, 0:00:01.000001"),
(5, None),
(None, None),
([], None),
],
)
def test_serialize(value: Any, expected: str):
"""Test that serialize returns the correct value.
Args:
value: The value to serialize.
expected: The expected result.
"""
assert serializers.serialize(value) == expected

View File

@ -21,6 +21,7 @@ from reflex.utils import (
types,
)
from reflex.utils import exec as utils_exec
from reflex.utils.serializers import serialize
from reflex.vars import BaseVar, Var
@ -777,4 +778,4 @@ def test_style_prop_with_event_handler_value(callable):
}
with pytest.raises(TypeError):
format.format_dict(style) # type: ignore
serialize(style) # type: ignore