From 448b1ba39edbbbb8ea9b748a40bbbf2fe5f89906 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 16 Jan 2023 14:15:53 -0800 Subject: [PATCH 01/55] A working skeleton prototype of Pyodide and Flet --- client/lib/main.dart | 10 +- client/web/index.html | 91 ++++++++++++++++--- package/lib/src/flet_server_protocol.dart | 10 +- .../src/flet_server_protocol_javascript.dart | 58 ++++++++++++ .../lib/src/utils/platform_utils_non_web.dart | 4 + package/lib/src/utils/platform_utils_web.dart | 7 ++ 6 files changed, 162 insertions(+), 18 deletions(-) create mode 100644 package/lib/src/flet_server_protocol_javascript.dart diff --git a/client/lib/main.dart b/client/lib/main.dart index d84468f91..752c85ce6 100644 --- a/client/lib/main.dart +++ b/client/lib/main.dart @@ -1,17 +1,17 @@ import 'dart:io'; import 'package:flet/flet.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:url_strategy/url_strategy.dart'; const bool isProduction = bool.fromEnvironment('dart.vm.product'); void main([List? args]) async { - if (isProduction) { - // ignore: avoid_returning_null_for_void - debugPrint = (String? message, {int? wrapWidth}) => null; - } + // if (isProduction) { + // // ignore: avoid_returning_null_for_void + // debugPrint = (String? message, {int? wrapWidth}) => null; + // } await setupDesktop(); diff --git a/client/web/index.html b/client/web/index.html index 37a945c8e..7e17e695e 100644 --- a/client/web/index.html +++ b/client/web/index.html @@ -1,5 +1,6 @@ + @@ -14,10 +15,11 @@ - + + Flet @@ -30,15 +32,20 @@ + +
+ + @keyframes breathe { + from { + transform: scale(1) + } + + to { + transform: scale(0.95) + } + } + + @keyframes zooooom { + from { + transform: scale(1) + } + + to { + transform: scale(10) + } + } + Loading indicator...
+ - + + \ No newline at end of file diff --git a/package/lib/src/flet_server_protocol.dart b/package/lib/src/flet_server_protocol.dart index 17467ff71..574a334ec 100644 --- a/package/lib/src/flet_server_protocol.dart +++ b/package/lib/src/flet_server_protocol.dart @@ -1,5 +1,8 @@ +import 'flet_server_protocol_javascript.dart'; import 'flet_server_protocol_tcp_socket.dart'; import 'flet_server_protocol_web_socket.dart'; +import 'utils/platform_utils_non_web.dart' + if (dart.library.js) "utils/platform_utils_web.dart"; typedef FletServerProtocolOnDisconnectCallback = void Function(); typedef FletServerProtocolOnMessageCallback = void Function(String message); @@ -9,7 +12,12 @@ abstract class FletServerProtocol { {required String address, required FletServerProtocolOnDisconnectCallback onDisconnect, required FletServerProtocolOnMessageCallback onMessage}) { - if (address.startsWith("http://") || address.startsWith("https://")) { + if (isFletWebPyodideMode()) { + // JavaScript + return FletJavaScriptServerProtocol( + address: address, onDisconnect: onDisconnect, onMessage: onMessage); + } else if (address.startsWith("http://") || + address.startsWith("https://")) { // WebSocket return FletWebSocketServerProtocol( address: address, onDisconnect: onDisconnect, onMessage: onMessage); diff --git a/package/lib/src/flet_server_protocol_javascript.dart b/package/lib/src/flet_server_protocol_javascript.dart new file mode 100644 index 000000000..a2beaf61e --- /dev/null +++ b/package/lib/src/flet_server_protocol_javascript.dart @@ -0,0 +1,58 @@ +@JS() +library script.js; + +import 'dart:js_util'; + +import 'package:flutter/foundation.dart'; +import 'package:js/js.dart'; + +import 'flet_server_protocol.dart'; +import 'utils/uri.dart'; + +@JS() +external dynamic sleep(int ms); + +@JS() +external dynamic jsConnect(); + +@JS() +external dynamic jsSend(String data); + +@JS() +external dynamic jsReceive(); + +class FletJavaScriptServerProtocol implements FletServerProtocol { + late final String _wsUrl; + FletServerProtocolOnMessageCallback onMessage; + FletServerProtocolOnDisconnectCallback onDisconnect; + + FletJavaScriptServerProtocol( + {required String address, + required this.onDisconnect, + required this.onMessage}) { + _wsUrl = getWebSocketEndpoint(Uri.parse(address)); + } + + @override + connect() async { + debugPrint("Connecting to JavaScript server $_wsUrl..."); + await promiseToFuture(jsConnect()); + receiveLoop(); + } + + Future receiveLoop() async { + debugPrint("Starting receive loop..."); + while (true) { + var message = await promiseToFuture(jsReceive()); + onMessage(message); + } + } + + @override + void send(String message) { + jsSend(message); + } + + @override + void disconnect() {} +} diff --git a/package/lib/src/utils/platform_utils_non_web.dart b/package/lib/src/utils/platform_utils_non_web.dart index 569a2266d..13dbaf94c 100644 --- a/package/lib/src/utils/platform_utils_non_web.dart +++ b/package/lib/src/utils/platform_utils_non_web.dart @@ -6,5 +6,9 @@ String getFletRouteUrlStrategy() { return ""; } +bool isFletWebPyodideMode() { + return false; +} + void openPopupBrowserWindow( String url, String windowName, int minWidth, int minHeight) {} diff --git a/package/lib/src/utils/platform_utils_web.dart b/package/lib/src/utils/platform_utils_web.dart index 43e58340b..01e2a7969 100644 --- a/package/lib/src/utils/platform_utils_web.dart +++ b/package/lib/src/utils/platform_utils_web.dart @@ -13,6 +13,13 @@ String getFletRouteUrlStrategy() { return meta != null ? meta.attributes["content"]! : ""; } +bool isFletWebPyodideMode() { + var meta = html.document.head?.querySelector("meta[name='flet-web-pyodide']"); + return meta != null + ? meta.attributes["content"]?.toLowerCase() == "true" + : false; +} + void openPopupBrowserWindow( String url, String windowName, int width, int height) { int screenWidth = html.window.screen!.width!; From 93eff31d28668db5fd6e6a1a291aa7c22c7ab89f Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 16 Jan 2023 16:11:09 -0800 Subject: [PATCH 02/55] Fix pubsubhub --- client/web/index.html | 6 ++++++ sdk/python/packages/flet-core/src/flet_core/connection.py | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/client/web/index.html b/client/web/index.html index 7e17e695e..f5cc4b3e4 100644 --- a/client/web/index.html +++ b/client/web/index.html @@ -116,6 +116,12 @@ diff --git a/sdk/python/packages/flet-pyodide/README.md b/sdk/python/packages/flet-pyodide/README.md new file mode 100644 index 000000000..a3b53a2af --- /dev/null +++ b/sdk/python/packages/flet-pyodide/README.md @@ -0,0 +1,3 @@ +# Flet for Pyodide + +Run standalone Flet apps in a browser! \ No newline at end of file diff --git a/sdk/python/packages/flet-pyodide/poetry.lock b/sdk/python/packages/flet-pyodide/poetry.lock new file mode 100644 index 000000000..0ef5321d0 --- /dev/null +++ b/sdk/python/packages/flet-pyodide/poetry.lock @@ -0,0 +1,817 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "anyio" +version = "3.6.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16,<0.22)"] + +[[package]] +name = "attrs" +version = "22.2.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] + +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "cryptography" +version = "39.0.0" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288"}, + {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717"}, + {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df"}, + {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1"}, + {file = "cryptography-39.0.0-cp36-abi3-win32.whl", hash = "sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de"}, + {file = "cryptography-39.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39"}, + {file = "cryptography-39.0.0.tar.gz", hash = "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf"}, +] + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "ruff"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.0" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, + {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.9.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, + {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, +] + +[package.extras] +docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "flet-core" +version = "0.1.0" +description = "Flet core library" +category = "main" +optional = false +python-versions = "*" +files = [] +develop = true + +[package.source] +type = "directory" +url = "../flet-core" + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[package.dependencies] +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "httpcore" +version = "0.16.3" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "httpx" +version = "0.23.3" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "identify" +version = "2.5.13" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "identify-2.5.13-py2.py3-none-any.whl", hash = "sha256:8aa48ce56e38c28b6faa9f261075dea0a942dfbb42b341b4e711896cbb40f3f7"}, + {file = "identify-2.5.13.tar.gz", hash = "sha256:abb546bca6f470228785338a01b539de8a85bbf46491250ae03363956d8ebb10"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.0.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, + {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "nodeenv" +version = "1.7.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "packaging" +version = "23.0" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, +] + +[[package]] +name = "platformdirs" +version = "2.6.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, + {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.21.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pytest" +version = "7.2.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, + {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "setuptools" +version = "66.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-66.0.0-py3-none-any.whl", hash = "sha256:a78d01d1e2c175c474884671dde039962c9d74c7223db7369771fcf6e29ceeab"}, + {file = "setuptools-66.0.0.tar.gz", hash = "sha256:bd6eb2d6722568de6d14b87c44a96fac54b2a45ff5e940e639979a3d1792adb6"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "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-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "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 = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, +] + +[[package]] +name = "virtualenv" +version = "20.17.1" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, + {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.4.1,<4" +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} +platformdirs = ">=2.4,<3" + +[package.extras] +docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "watchdog" +version = "2.2.1" +description = "Filesystem events monitoring" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "watchdog-2.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a09483249d25cbdb4c268e020cb861c51baab2d1affd9a6affc68ffe6a231260"}, + {file = "watchdog-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5100eae58133355d3ca6c1083a33b81355c4f452afa474c2633bd2fbbba398b3"}, + {file = "watchdog-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e618a4863726bc7a3c64f95c218437f3349fb9d909eb9ea3a1ed3b567417c661"}, + {file = "watchdog-2.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:102a60093090fc3ff76c983367b19849b7cc24ec414a43c0333680106e62aae1"}, + {file = "watchdog-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:748ca797ff59962e83cc8e4b233f87113f3cf247c23e6be58b8a2885c7337aa3"}, + {file = "watchdog-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ccd8d84b9490a82b51b230740468116b8205822ea5fdc700a553d92661253a3"}, + {file = "watchdog-2.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6e01d699cd260d59b84da6bda019dce0a3353e3fcc774408ae767fe88ee096b7"}, + {file = "watchdog-2.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8586d98c494690482c963ffb24c49bf9c8c2fe0589cec4dc2f753b78d1ec301d"}, + {file = "watchdog-2.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:adaf2ece15f3afa33a6b45f76b333a7da9256e1360003032524d61bdb4c422ae"}, + {file = "watchdog-2.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83a7cead445008e880dbde833cb9e5cc7b9a0958edb697a96b936621975f15b9"}, + {file = "watchdog-2.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8ac23ff2c2df4471a61af6490f847633024e5aa120567e08d07af5718c9d092"}, + {file = "watchdog-2.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d0f29fd9f3f149a5277929de33b4f121a04cf84bb494634707cfa8ea8ae106a8"}, + {file = "watchdog-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:967636031fa4c4955f0f3f22da3c5c418aa65d50908d31b73b3b3ffd66d60640"}, + {file = "watchdog-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96cbeb494e6cbe3ae6aacc430e678ce4b4dd3ae5125035f72b6eb4e5e9eb4f4e"}, + {file = "watchdog-2.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61fdb8e9c57baf625e27e1420e7ca17f7d2023929cd0065eb79c83da1dfbeacd"}, + {file = "watchdog-2.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4cb5ecc332112017fbdb19ede78d92e29a8165c46b68a0b8ccbd0a154f196d5e"}, + {file = "watchdog-2.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a480d122740debf0afac4ddd583c6c0bb519c24f817b42ed6f850e2f6f9d64a8"}, + {file = "watchdog-2.2.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:978a1aed55de0b807913b7482d09943b23a2d634040b112bdf31811a422f6344"}, + {file = "watchdog-2.2.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:8c28c23972ec9c524967895ccb1954bc6f6d4a557d36e681a36e84368660c4ce"}, + {file = "watchdog-2.2.1-py3-none-manylinux2014_i686.whl", hash = "sha256:c27d8c1535fd4474e40a4b5e01f4ba6720bac58e6751c667895cbc5c8a7af33c"}, + {file = "watchdog-2.2.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d6b87477752bd86ac5392ecb9eeed92b416898c30bd40c7e2dd03c3146105646"}, + {file = "watchdog-2.2.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:cece1aa596027ff56369f0b50a9de209920e1df9ac6d02c7f9e5d8162eb4f02b"}, + {file = "watchdog-2.2.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:8b5cde14e5c72b2df5d074774bdff69e9b55da77e102a91f36ef26ca35f9819c"}, + {file = "watchdog-2.2.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e038be858425c4f621900b8ff1a3a1330d9edcfeaa1c0468aeb7e330fb87693e"}, + {file = "watchdog-2.2.1-py3-none-win32.whl", hash = "sha256:bc43c1b24d2f86b6e1cc15f68635a959388219426109233e606517ff7d0a5a73"}, + {file = "watchdog-2.2.1-py3-none-win_amd64.whl", hash = "sha256:17f1708f7410af92ddf591e94ae71a27a13974559e72f7e9fde3ec174b26ba2e"}, + {file = "watchdog-2.2.1-py3-none-win_ia64.whl", hash = "sha256:195ab1d9d611a4c1e5311cbf42273bc541e18ea8c32712f2fb703cfc6ff006f9"}, + {file = "watchdog-2.2.1.tar.gz", hash = "sha256:cdcc23c9528601a8a293eb4369cbd14f6b4f34f07ae8769421252e9c22718b6f"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "websocket-client" +version = "1.4.2" +description = "WebSocket client for Python with low level API options" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websocket-client-1.4.2.tar.gz", hash = "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59"}, + {file = "websocket_client-1.4.2-py3-none-any.whl", hash = "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574"}, +] + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "websockets" +version = "10.4" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, + {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, + {file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"}, + {file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"}, + {file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"}, + {file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"}, + {file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"}, + {file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"}, + {file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"}, + {file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"}, + {file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"}, + {file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"}, + {file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, + {file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"}, + {file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"}, + {file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"}, + {file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"}, + {file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"}, + {file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"}, + {file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"}, + {file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"}, + {file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"}, + {file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, + {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, +] + +[[package]] +name = "zipp" +version = "3.11.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, + {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.7" +content-hash = "0ef2c6beaacc246910536c6a1b988c34bea4274b274cef8142cfd397e6e9d8bc" diff --git a/sdk/python/packages/flet-pyodide/pyproject.toml b/sdk/python/packages/flet-pyodide/pyproject.toml new file mode 100644 index 000000000..0b8a23816 --- /dev/null +++ b/sdk/python/packages/flet-pyodide/pyproject.toml @@ -0,0 +1,28 @@ +[tool.poetry] +name = "flet-pyodide" +version = "0.1.0" +description = "Flet for Pyodide" +authors = ["Appveyor Systems Inc. "] +license = "MIT" +readme = "README.md" + +packages = [ + { include = "flet_pyodide", from = "src" }, +] + +[tool.poetry.urls] +homepage = "https://flet.dev" +repository = "https://github.com/flet-dev/flet" +documentation = "https://flet.dev/docs" + +[tool.poetry.dependencies] +flet-core = "0.1.0" +python = "^3.10" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.isort] +profile = "black" +float_to_top = true \ No newline at end of file diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/__init__.py b/sdk/python/packages/flet-pyodide/src/flet_pyodide/__init__.py new file mode 100644 index 000000000..556aeb791 --- /dev/null +++ b/sdk/python/packages/flet-pyodide/src/flet_pyodide/__init__.py @@ -0,0 +1,2 @@ +from flet_core import * +from flet_pyodide.flet import FLET_APP, FLET_APP_HIDDEN, WEB_BROWSER, app, app_async diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/flet.py b/sdk/python/packages/flet-pyodide/src/flet_pyodide/flet.py new file mode 100644 index 000000000..89ba529cb --- /dev/null +++ b/sdk/python/packages/flet-pyodide/src/flet_pyodide/flet.py @@ -0,0 +1,90 @@ +import inspect +import logging +import traceback + +import flet_js +from flet_core.event import Event +from flet_core.page import Page +from flet_pyodide.pyodide_connection import PyodideConnection + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal + +WEB_BROWSER = "web_browser" +FLET_APP = "flet_app" +FLET_APP_HIDDEN = "flet_app_hidden" + + +def app( + target, + name="", + host=None, + port=0, + view=None, + assets_dir=None, + upload_dir=None, + web_renderer=None, + route_url_strategy=None, + auth_token=None, +): + if not inspect.iscoroutinefunction(target): + raise Exception("Sync apps are not supported in Pyodide.") + + app_async( + target=target, + name=name, + host=host, + port=port, + view=view, + assets_dir=assets_dir, + upload_dir=upload_dir, + web_renderer=web_renderer, + route_url_strategy=route_url_strategy, + auth_token=auth_token, + ) + + +def app_async( + target, + name="", + host=None, + port=0, + view=None, + assets_dir=None, + upload_dir=None, + web_renderer=None, + route_url_strategy=None, + auth_token=None, +): + async def on_event(e): + if e.sessionID in conn.sessions: + await conn.sessions[e.sessionID].on_event_async( + Event(e.eventTarget, e.eventName, e.eventData) + ) + if e.eventTarget == "page" and e.eventName == "close": + logging.info(f"Session closed: {e.sessionID}") + del conn.sessions[e.sessionID] + + async def on_session_created(session_data): + page = Page(conn, session_data.sessionID) + await page.fetch_page_details_async() + conn.sessions[session_data.sessionID] = page + logging.info(f"Session started: {session_data.sessionID}") + try: + assert target is not None + await target(page) + except Exception as e: + print( + f"Unhandled error processing page session {page.session_id}:", + traceback.format_exc(), + ) + await page.error_async( + f"There was an error while processing your request: {e}" + ) + + conn = PyodideConnection( + on_event=on_event, + on_session_created=on_session_created, + ) diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/matplotlib_chart.py b/sdk/python/packages/flet-pyodide/src/flet_pyodide/matplotlib_chart.py new file mode 100644 index 000000000..c6c5690dc --- /dev/null +++ b/sdk/python/packages/flet-pyodide/src/flet_pyodide/matplotlib_chart.py @@ -0,0 +1 @@ +from flet_core.matplotlib_chart import MatplotlibChart diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/plotly_chart.py b/sdk/python/packages/flet-pyodide/src/flet_pyodide/plotly_chart.py new file mode 100644 index 000000000..0c7fa0a10 --- /dev/null +++ b/sdk/python/packages/flet-pyodide/src/flet_pyodide/plotly_chart.py @@ -0,0 +1 @@ +from flet_core.plotly_chart import PlotlyChart diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/pyodide_connection.py b/sdk/python/packages/flet-pyodide/src/flet_pyodide/pyodide_connection.py new file mode 100644 index 000000000..f25ed3884 --- /dev/null +++ b/sdk/python/packages/flet-pyodide/src/flet_pyodide/pyodide_connection.py @@ -0,0 +1,105 @@ +import asyncio +import json +import logging +from typing import List + +import flet_js +from flet_core.local_connection import LocalConnection +from flet_core.protocol import ( + ClientActions, + ClientMessage, + Command, + CommandEncoder, + PageCommandResponsePayload, + PageCommandsBatchResponsePayload, + RegisterWebClientRequestPayload, +) + + +class PyodideConnection(LocalConnection): + def __init__( + self, + on_event, + on_session_created, + ): + super().__init__() + self.__send_queue = asyncio.Queue(1) + self.__receive_queue = asyncio.Queue() + self.__on_event = on_event + self.__on_session_created = on_session_created + flet_js.start_connection = self.connect + + async def connect(self): + print("Starting Pyodide connection...") + asyncio.create_task(self.receive_loop()) + flet_js.send = self.send_from_js + flet_js.receive_async = self.receive_from_js_async + + async def receive_loop(self): + while True: + message = await self.__receive_queue.get() + await self.__on_message(message) + + def send_from_js(self, message: str): + print("Sending data from JavaScript to Python:", message) + self.__receive_queue.put_nowait(message) + + async def receive_from_js_async(self): + return await self.__send_queue.get() + + async def __on_message(self, data: str): + logging.debug(f"_on_message: {data}") + msg_dict = json.loads(data) + msg = ClientMessage(**msg_dict) + if msg.action == ClientActions.REGISTER_WEB_CLIENT: + self._client_details = RegisterWebClientRequestPayload(**msg.payload) + + # register response + await self.__send(self._create_register_web_client_response()) + + # start session + if self.__on_session_created is not None: + asyncio.create_task( + self.__on_session_created(self._create_session_handler_arg()) + ) + + elif msg.action == ClientActions.PAGE_EVENT_FROM_WEB: + if self.__on_event is not None: + asyncio.create_task( + self.__on_event(self._create_page_event_handler_arg(msg)) + ) + + elif msg.action == ClientActions.UPDATE_CONTROL_PROPS: + if self.__on_event is not None: + asyncio.create_task( + self.__on_event(self._create_update_control_props_handler_arg(msg)) + ) + else: + # it's something else + raise Exception('Unknown message "{}": {}'.format(msg.action, msg.payload)) + + async def send_command_async(self, session_id: str, command: Command): + result, message = self._process_command(command) + if message: + await self.__send(message) + return PageCommandResponsePayload(result=result, error="") + + async def send_commands_async(self, session_id: str, commands: List[Command]): + results = [] + messages = [] + for command in commands: + result, message = self._process_command(command) + if command.name in ["add", "get"]: + results.append(result) + if message: + messages.append(message) + if len(messages) > 0: + await self.__send( + ClientMessage(ClientActions.PAGE_CONTROLS_BATCH, messages) + ) + return PageCommandsBatchResponsePayload(results=results, error="") + + async def __send(self, message: ClientMessage): + j = json.dumps(message, cls=CommandEncoder, separators=(",", ":")) + logging.debug(f"__send: {j}") + await self.__send_queue.put(j) diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/version.py b/sdk/python/packages/flet-pyodide/src/flet_pyodide/version.py new file mode 100644 index 000000000..f76b2ca9a --- /dev/null +++ b/sdk/python/packages/flet-pyodide/src/flet_pyodide/version.py @@ -0,0 +1,4 @@ +"""Provide the current Flet version.""" + +# this value will be replaced by CI +version = "" diff --git a/sdk/python/poetry.lock b/sdk/python/poetry.lock index 41f2fd431..eb0f93a13 100644 --- a/sdk/python/poetry.lock +++ b/sdk/python/poetry.lock @@ -592,6 +592,13 @@ files = [ {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, + {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"}, + {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"}, + {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"}, + {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"}, + {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"}, + {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"}, + {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"}, {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, @@ -780,14 +787,14 @@ files = [ [[package]] name = "pytest" -version = "7.2.0" +version = "7.2.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, - {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, + {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, + {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, ] [package.dependencies] @@ -900,14 +907,14 @@ idna2008 = ["idna"] [[package]] name = "setuptools" -version = "65.7.0" +version = "66.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-65.7.0-py3-none-any.whl", hash = "sha256:8ab4f1dbf2b4a65f7eec5ad0c620e84c34111a68d3349833494b9088212214dd"}, - {file = "setuptools-65.7.0.tar.gz", hash = "sha256:4d3c92fac8f1118bb77a22181355e29c239cabfe2b9effdaa665c66b711136d7"}, + {file = "setuptools-66.0.0-py3-none-any.whl", hash = "sha256:a78d01d1e2c175c474884671dde039962c9d74c7223db7369771fcf6e29ceeab"}, + {file = "setuptools-66.0.0.tar.gz", hash = "sha256:bd6eb2d6722568de6d14b87c44a96fac54b2a45ff5e940e639979a3d1792adb6"}, ] [package.extras] From 992978a228d83974a2f67374f7f17ddcd3936676 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 18 Jan 2023 09:17:03 -0800 Subject: [PATCH 07/55] renamed flet_pyodide to flet --- sdk/python/packages/flet-pyodide/poetry.lock | 817 ------------------ .../packages/flet-pyodide/pyproject.toml | 2 +- .../flet-pyodide/src/flet/__init__.py | 2 + .../src/{flet_pyodide => flet}/flet.py | 2 +- .../matplotlib_chart.py | 0 .../{flet_pyodide => flet}/plotly_chart.py | 0 .../pyodide_connection.py | 0 .../src/{flet_pyodide => flet}/version.py | 0 .../flet-pyodide/src/flet_pyodide/__init__.py | 2 - 9 files changed, 4 insertions(+), 821 deletions(-) delete mode 100644 sdk/python/packages/flet-pyodide/poetry.lock create mode 100644 sdk/python/packages/flet-pyodide/src/flet/__init__.py rename sdk/python/packages/flet-pyodide/src/{flet_pyodide => flet}/flet.py (97%) rename sdk/python/packages/flet-pyodide/src/{flet_pyodide => flet}/matplotlib_chart.py (100%) rename sdk/python/packages/flet-pyodide/src/{flet_pyodide => flet}/plotly_chart.py (100%) rename sdk/python/packages/flet-pyodide/src/{flet_pyodide => flet}/pyodide_connection.py (100%) rename sdk/python/packages/flet-pyodide/src/{flet_pyodide => flet}/version.py (100%) delete mode 100644 sdk/python/packages/flet-pyodide/src/flet_pyodide/__init__.py diff --git a/sdk/python/packages/flet-pyodide/poetry.lock b/sdk/python/packages/flet-pyodide/poetry.lock deleted file mode 100644 index 0ef5321d0..000000000 --- a/sdk/python/packages/flet-pyodide/poetry.lock +++ /dev/null @@ -1,817 +0,0 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. - -[[package]] -name = "anyio" -version = "3.6.2" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" -optional = false -python-versions = ">=3.6.2" -files = [ - {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, - {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16,<0.22)"] - -[[package]] -name = "attrs" -version = "22.2.0" -description = "Classes Without Boilerplate" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, - {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] -tests = ["attrs[tests-no-zope]", "zope.interface"] -tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] - -[[package]] -name = "certifi" -version = "2022.12.7" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] - -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "cfgv" -version = "3.3.1" -description = "Validate configuration and produce human readable error messages." -category = "dev" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "cryptography" -version = "39.0.0" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288"}, - {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72"}, - {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9"}, - {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f"}, - {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b"}, - {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c"}, - {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96"}, - {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717"}, - {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df"}, - {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1"}, - {file = "cryptography-39.0.0-cp36-abi3-win32.whl", hash = "sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de"}, - {file = "cryptography-39.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e"}, - {file = "cryptography-39.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb"}, - {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f"}, - {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458"}, - {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190"}, - {file = "cryptography-39.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773"}, - {file = "cryptography-39.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856"}, - {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f"}, - {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce"}, - {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8"}, - {file = "cryptography-39.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39"}, - {file = "cryptography-39.0.0.tar.gz", hash = "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf"}, -] - -[package.dependencies] -cffi = ">=1.12" - -[package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] -pep8test = ["black", "ruff"] -sdist = ["setuptools-rust (>=0.11.4)"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] - -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.1.0" -description = "Backport of PEP 654 (exception groups)" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, - {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "filelock" -version = "3.9.0" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, - {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, -] - -[package.extras] -docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] -testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "flet-core" -version = "0.1.0" -description = "Flet core library" -category = "main" -optional = false -python-versions = "*" -files = [] -develop = true - -[package.source] -type = "directory" -url = "../flet-core" - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[package.dependencies] -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "httpcore" -version = "0.16.3" -description = "A minimal low-level HTTP client." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, - {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, -] - -[package.dependencies] -anyio = ">=3.0,<5.0" -certifi = "*" -h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" - -[package.extras] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] - -[[package]] -name = "httpx" -version = "0.23.3" -description = "The next generation HTTP client." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, - {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, -] - -[package.dependencies] -certifi = "*" -httpcore = ">=0.15.0,<0.17.0" -rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] - -[[package]] -name = "identify" -version = "2.5.13" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "identify-2.5.13-py2.py3-none-any.whl", hash = "sha256:8aa48ce56e38c28b6faa9f261075dea0a942dfbb42b341b4e711896cbb40f3f7"}, - {file = "identify-2.5.13.tar.gz", hash = "sha256:abb546bca6f470228785338a01b539de8a85bbf46491250ae03363956d8ebb10"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "importlib-metadata" -version = "6.0.0" -description = "Read metadata from Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, - {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, -] - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "nodeenv" -version = "1.7.0" -description = "Node.js virtual environment builder" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, -] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "oauthlib" -version = "3.2.2" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, -] - -[package.extras] -rsa = ["cryptography (>=3.0.0)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "packaging" -version = "23.0" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, -] - -[[package]] -name = "platformdirs" -version = "2.6.2" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, - {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} - -[package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "2.21.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, - {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] - -[[package]] -name = "pytest" -version = "7.2.1" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, - {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] - -[[package]] -name = "rfc3986" -version = "1.5.0" -description = "Validating URI References per RFC 3986" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, -] - -[package.dependencies] -idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} - -[package.extras] -idna2008 = ["idna"] - -[[package]] -name = "setuptools" -version = "66.0.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-66.0.0-py3-none-any.whl", hash = "sha256:a78d01d1e2c175c474884671dde039962c9d74c7223db7369771fcf6e29ceeab"}, - {file = "setuptools-66.0.0.tar.gz", hash = "sha256:bd6eb2d6722568de6d14b87c44a96fac54b2a45ff5e940e639979a3d1792adb6"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "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-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "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 = "sniffio" -version = "1.3.0" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "typing-extensions" -version = "4.4.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, -] - -[[package]] -name = "virtualenv" -version = "20.17.1" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, - {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, -] - -[package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.4.1,<4" -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} -platformdirs = ">=2.4,<3" - -[package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "watchdog" -version = "2.2.1" -description = "Filesystem events monitoring" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "watchdog-2.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a09483249d25cbdb4c268e020cb861c51baab2d1affd9a6affc68ffe6a231260"}, - {file = "watchdog-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5100eae58133355d3ca6c1083a33b81355c4f452afa474c2633bd2fbbba398b3"}, - {file = "watchdog-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e618a4863726bc7a3c64f95c218437f3349fb9d909eb9ea3a1ed3b567417c661"}, - {file = "watchdog-2.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:102a60093090fc3ff76c983367b19849b7cc24ec414a43c0333680106e62aae1"}, - {file = "watchdog-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:748ca797ff59962e83cc8e4b233f87113f3cf247c23e6be58b8a2885c7337aa3"}, - {file = "watchdog-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ccd8d84b9490a82b51b230740468116b8205822ea5fdc700a553d92661253a3"}, - {file = "watchdog-2.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6e01d699cd260d59b84da6bda019dce0a3353e3fcc774408ae767fe88ee096b7"}, - {file = "watchdog-2.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8586d98c494690482c963ffb24c49bf9c8c2fe0589cec4dc2f753b78d1ec301d"}, - {file = "watchdog-2.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:adaf2ece15f3afa33a6b45f76b333a7da9256e1360003032524d61bdb4c422ae"}, - {file = "watchdog-2.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83a7cead445008e880dbde833cb9e5cc7b9a0958edb697a96b936621975f15b9"}, - {file = "watchdog-2.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8ac23ff2c2df4471a61af6490f847633024e5aa120567e08d07af5718c9d092"}, - {file = "watchdog-2.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d0f29fd9f3f149a5277929de33b4f121a04cf84bb494634707cfa8ea8ae106a8"}, - {file = "watchdog-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:967636031fa4c4955f0f3f22da3c5c418aa65d50908d31b73b3b3ffd66d60640"}, - {file = "watchdog-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96cbeb494e6cbe3ae6aacc430e678ce4b4dd3ae5125035f72b6eb4e5e9eb4f4e"}, - {file = "watchdog-2.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61fdb8e9c57baf625e27e1420e7ca17f7d2023929cd0065eb79c83da1dfbeacd"}, - {file = "watchdog-2.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4cb5ecc332112017fbdb19ede78d92e29a8165c46b68a0b8ccbd0a154f196d5e"}, - {file = "watchdog-2.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a480d122740debf0afac4ddd583c6c0bb519c24f817b42ed6f850e2f6f9d64a8"}, - {file = "watchdog-2.2.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:978a1aed55de0b807913b7482d09943b23a2d634040b112bdf31811a422f6344"}, - {file = "watchdog-2.2.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:8c28c23972ec9c524967895ccb1954bc6f6d4a557d36e681a36e84368660c4ce"}, - {file = "watchdog-2.2.1-py3-none-manylinux2014_i686.whl", hash = "sha256:c27d8c1535fd4474e40a4b5e01f4ba6720bac58e6751c667895cbc5c8a7af33c"}, - {file = "watchdog-2.2.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d6b87477752bd86ac5392ecb9eeed92b416898c30bd40c7e2dd03c3146105646"}, - {file = "watchdog-2.2.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:cece1aa596027ff56369f0b50a9de209920e1df9ac6d02c7f9e5d8162eb4f02b"}, - {file = "watchdog-2.2.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:8b5cde14e5c72b2df5d074774bdff69e9b55da77e102a91f36ef26ca35f9819c"}, - {file = "watchdog-2.2.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e038be858425c4f621900b8ff1a3a1330d9edcfeaa1c0468aeb7e330fb87693e"}, - {file = "watchdog-2.2.1-py3-none-win32.whl", hash = "sha256:bc43c1b24d2f86b6e1cc15f68635a959388219426109233e606517ff7d0a5a73"}, - {file = "watchdog-2.2.1-py3-none-win_amd64.whl", hash = "sha256:17f1708f7410af92ddf591e94ae71a27a13974559e72f7e9fde3ec174b26ba2e"}, - {file = "watchdog-2.2.1-py3-none-win_ia64.whl", hash = "sha256:195ab1d9d611a4c1e5311cbf42273bc541e18ea8c32712f2fb703cfc6ff006f9"}, - {file = "watchdog-2.2.1.tar.gz", hash = "sha256:cdcc23c9528601a8a293eb4369cbd14f6b4f34f07ae8769421252e9c22718b6f"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "websocket-client" -version = "1.4.2" -description = "WebSocket client for Python with low level API options" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "websocket-client-1.4.2.tar.gz", hash = "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59"}, - {file = "websocket_client-1.4.2-py3-none-any.whl", hash = "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574"}, -] - -[package.extras] -docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] -optional = ["python-socks", "wsaccel"] -test = ["websockets"] - -[[package]] -name = "websockets" -version = "10.4" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, - {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, - {file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"}, - {file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"}, - {file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"}, - {file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"}, - {file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"}, - {file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"}, - {file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"}, - {file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"}, - {file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"}, - {file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"}, - {file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"}, - {file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"}, - {file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"}, - {file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"}, - {file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"}, - {file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"}, - {file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"}, - {file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"}, - {file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"}, - {file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"}, - {file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"}, - {file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"}, - {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"}, - {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"}, - {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"}, - {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"}, - {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"}, - {file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"}, - {file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"}, - {file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"}, - {file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"}, - {file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"}, - {file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"}, - {file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"}, - {file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"}, - {file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"}, - {file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"}, - {file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"}, - {file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, - {file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"}, - {file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"}, - {file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"}, - {file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"}, - {file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"}, - {file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"}, - {file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"}, - {file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"}, - {file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"}, - {file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"}, - {file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"}, - {file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"}, - {file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"}, - {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"}, - {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"}, - {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"}, - {file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"}, - {file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"}, - {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"}, - {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"}, - {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"}, - {file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"}, - {file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"}, - {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"}, - {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"}, - {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"}, - {file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, - {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, -] - -[[package]] -name = "zipp" -version = "3.11.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, - {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.7" -content-hash = "0ef2c6beaacc246910536c6a1b988c34bea4274b274cef8142cfd397e6e9d8bc" diff --git a/sdk/python/packages/flet-pyodide/pyproject.toml b/sdk/python/packages/flet-pyodide/pyproject.toml index 0b8a23816..917bfb4c5 100644 --- a/sdk/python/packages/flet-pyodide/pyproject.toml +++ b/sdk/python/packages/flet-pyodide/pyproject.toml @@ -7,7 +7,7 @@ license = "MIT" readme = "README.md" packages = [ - { include = "flet_pyodide", from = "src" }, + { include = "flet", from = "src" }, ] [tool.poetry.urls] diff --git a/sdk/python/packages/flet-pyodide/src/flet/__init__.py b/sdk/python/packages/flet-pyodide/src/flet/__init__.py new file mode 100644 index 000000000..194705f9a --- /dev/null +++ b/sdk/python/packages/flet-pyodide/src/flet/__init__.py @@ -0,0 +1,2 @@ +from flet.flet import FLET_APP, FLET_APP_HIDDEN, WEB_BROWSER, app, app_async +from flet_core import * diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/flet.py b/sdk/python/packages/flet-pyodide/src/flet/flet.py similarity index 97% rename from sdk/python/packages/flet-pyodide/src/flet_pyodide/flet.py rename to sdk/python/packages/flet-pyodide/src/flet/flet.py index 89ba529cb..439870ccb 100644 --- a/sdk/python/packages/flet-pyodide/src/flet_pyodide/flet.py +++ b/sdk/python/packages/flet-pyodide/src/flet/flet.py @@ -3,9 +3,9 @@ import traceback import flet_js +from flet.pyodide_connection import PyodideConnection from flet_core.event import Event from flet_core.page import Page -from flet_pyodide.pyodide_connection import PyodideConnection try: from typing import Literal diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/matplotlib_chart.py b/sdk/python/packages/flet-pyodide/src/flet/matplotlib_chart.py similarity index 100% rename from sdk/python/packages/flet-pyodide/src/flet_pyodide/matplotlib_chart.py rename to sdk/python/packages/flet-pyodide/src/flet/matplotlib_chart.py diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/plotly_chart.py b/sdk/python/packages/flet-pyodide/src/flet/plotly_chart.py similarity index 100% rename from sdk/python/packages/flet-pyodide/src/flet_pyodide/plotly_chart.py rename to sdk/python/packages/flet-pyodide/src/flet/plotly_chart.py diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/pyodide_connection.py b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py similarity index 100% rename from sdk/python/packages/flet-pyodide/src/flet_pyodide/pyodide_connection.py rename to sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/version.py b/sdk/python/packages/flet-pyodide/src/flet/version.py similarity index 100% rename from sdk/python/packages/flet-pyodide/src/flet_pyodide/version.py rename to sdk/python/packages/flet-pyodide/src/flet/version.py diff --git a/sdk/python/packages/flet-pyodide/src/flet_pyodide/__init__.py b/sdk/python/packages/flet-pyodide/src/flet_pyodide/__init__.py deleted file mode 100644 index 556aeb791..000000000 --- a/sdk/python/packages/flet-pyodide/src/flet_pyodide/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from flet_core import * -from flet_pyodide.flet import FLET_APP, FLET_APP_HIDDEN, WEB_BROWSER, app, app_async From 918fc6b53014b9f73ba1ced887fc12ef12004a6b Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 18 Jan 2023 09:22:48 -0800 Subject: [PATCH 08/55] Package flet-pyodide --- .appveyor.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index cc93bcefa..5c6a15dff 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -393,9 +393,13 @@ for: fi - cd .. + # package web build + - tar -czvf flet-web.tar.gz -C client/build/web * + artifacts: - path: server/dist/fletd-* - path: server/dist/fletd_*/* + - path: flet-web.tar.gz ###################### # Python Tests # @@ -450,13 +454,19 @@ for: - python3 build-wheels.py - popd + # build "flet-pyodide" package + - pushd packages/flet-pyodide + - poetry build + - popd + # publish package - sh: | if [[ ("$APPVEYOR_REPO_BRANCH" == "main" || "$APPVEYOR_REPO_TAG_NAME" != "") && "$APPVEYOR_PULL_REQUEST_NUMBER" == "" ]]; then - twine upload packages/flet-core/dist/* packages/flet/dist/* + twine upload packages/flet-core/dist/* packages/flet/dist/* packages/flet-pyodide/dist/* fi artifacts: - path: sdk/python/packages/flet-core/dist/* - path: sdk/python/packages/flet/dist/* + - path: sdk/python/packages/flet-pyodide/dist/* From bfec2d2f621c3ca18ff3412b9182735740393448 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 18 Jan 2023 09:41:41 -0800 Subject: [PATCH 09/55] Fix archive creation --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 5c6a15dff..927f12d43 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -394,7 +394,7 @@ for: - cd .. # package web build - - tar -czvf flet-web.tar.gz -C client/build/web * + - tar -czvf flet-web.tar.gz -C client/build/web . artifacts: - path: server/dist/fletd-* From fac6b61c149923fd79c326fda1704207adce78fb Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 18 Jan 2023 10:10:54 -0800 Subject: [PATCH 10/55] patch flet-pyodide --- .appveyor.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index 927f12d43..452bd1092 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -400,6 +400,15 @@ for: - path: server/dist/fletd-* - path: server/dist/fletd_*/* - path: flet-web.tar.gz + name: flet_web + + deploy: + provider: GitHub + auth_token: $(GITHUB_TOKEN) + release: $(APPVEYOR_REPO_TAG_NAME) + artifact: flet_web + on: + APPVEYOR_REPO_TAG: true ###################### # Python Tests # @@ -442,6 +451,7 @@ for: $vp = "packages/flet/src/flet/version.py"; (Get-Content $vp).replace("version = `"`"", "version = `"$env:PYPI_VER`"") | Set-Content $vp - python3 patch_toml.py packages/flet/pyproject.toml $PYPI_VER - python3 patch_toml.py packages/flet-core/pyproject.toml $PYPI_VER + - python3 patch_toml.py packages/flet-pyodide/pyproject.toml $PYPI_VER # build "flet-core" package - pushd packages/flet-core From 6c5006fec60735bf2db202e0ccf5b922c51f2b7b Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 18 Jan 2023 11:01:13 -0800 Subject: [PATCH 11/55] patch flet-pyodide/src/flet/version.py --- .appveyor.yml | 1 + client/web/index.html | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 452bd1092..2e208339d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -449,6 +449,7 @@ for: $ErrorActionPreference = "Stop" $env:PYPI_VER = $env:APPVEYOR_BUILD_VERSION.replace("+", ".dev") $vp = "packages/flet/src/flet/version.py"; (Get-Content $vp).replace("version = `"`"", "version = `"$env:PYPI_VER`"") | Set-Content $vp + $vp = "packages/flet-pyodide/src/flet/version.py"; (Get-Content $vp).replace("version = `"`"", "version = `"$env:PYPI_VER`"") | Set-Content $vp - python3 patch_toml.py packages/flet/pyproject.toml $PYPI_VER - python3 patch_toml.py packages/flet-core/pyproject.toml $PYPI_VER - python3 patch_toml.py packages/flet-pyodide/pyproject.toml $PYPI_VER diff --git a/client/web/index.html b/client/web/index.html index 3ba35693a..416ca4e44 100644 --- a/client/web/index.html +++ b/client/web/index.html @@ -121,9 +121,9 @@ await pyodide.loadPackage("micropip"); await pyodide.runPythonAsync(` import micropip - await micropip.install('/python/flet_core-0.1.0-py3-none-any.whl', pre=True) + await micropip.install('/python/flet_core-0.4.0.dev1069-py3-none-any.whl', pre=True) print("Imported flet-core") - await micropip.install('/python/flet_pyodide-0.1.0-py3-none-any.whl', pre=True) + await micropip.install('/python/flet_pyodide-0.4.0.dev1069-py3-none-any.whl', pre=True) print("Imported flet-pyodide") `); pyodide.registerJsModule("flet_js", flet_js); From 3ea6948e98be3d3fdeddaef56e0782c01cfebbb1 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 18 Jan 2023 17:00:41 -0800 Subject: [PATCH 12/55] Added python.js --- client/web/index.html | 46 +++----------------------------------- client/web/python.js | 52 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 43 deletions(-) create mode 100644 client/web/python.js diff --git a/client/web/index.html b/client/web/index.html index 416ca4e44..ad41a4c37 100644 --- a/client/web/index.html +++ b/client/web/index.html @@ -26,13 +26,15 @@ + + + - @@ -113,48 +115,6 @@ }); }); - \ No newline at end of file diff --git a/client/web/python.js b/client/web/python.js new file mode 100644 index 000000000..f77c222df --- /dev/null +++ b/client/web/python.js @@ -0,0 +1,52 @@ +const pyodideUrl = "https://cdn.jsdelivr.net/pyodide/v0.22.0/full/pyodide.js"; +let flet_js = {}; // namespace for Python global functions + +function loadScript(url, callback) { + // Adding the script tag to the head as suggested before + var head = document.head; + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = url; + + // Then bind the event to the callback function. + // There are several events for cross browser compatibility. + script.onreadystatechange = callback; + script.onload = callback; + + // Fire the loading + head.appendChild(script); +} + +let pyodideReady = new Promise((resolve) => { + loadScript(pyodideUrl, async () => { + let pyodide = await loadPyodide(); + await pyodide.loadPackage("micropip"); + await pyodide.runPythonAsync(` + import micropip + await micropip.install('/python/flet_core-0.4.0.dev1069-py3-none-any.whl', pre=True) + print("Imported flet-core") + await micropip.install('/python/flet_pyodide-0.4.0.dev1069-py3-none-any.whl', pre=True) + print("Imported flet-pyodide") + `); + pyodide.registerJsModule("flet_js", flet_js); + console.log("Before fetching main script"); + let main_script = await (await fetch("/python/main.py")).text(); + console.log("After fetching main script"); + await pyodide.runPythonAsync(main_script); + console.log("Loaded main program") + resolve(pyodide); + }); +}); + +async function jsConnect() { + await pyodideReady; + await flet_js.start_connection(); +} + +async function jsSend(data) { + flet_js.send(data) +} + +async function jsReceive() { + return await flet_js.receive_async(); +} \ No newline at end of file From bfee95b8bfbf046ae6edd1661c5e45525c5ca259 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 19 Jan 2023 09:45:34 -0800 Subject: [PATCH 13/55] Route strategy changed from "hash" to "path" --- sdk/python/packages/flet/src/flet/flet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/python/packages/flet/src/flet/flet.py b/sdk/python/packages/flet/src/flet/flet.py index 1418ed899..9d168f1d0 100644 --- a/sdk/python/packages/flet/src/flet/flet.py +++ b/sdk/python/packages/flet/src/flet/flet.py @@ -60,7 +60,7 @@ def app( assets_dir=None, upload_dir=None, web_renderer="canvaskit", - route_url_strategy="hash", + route_url_strategy="path", auth_token=None, ): if inspect.iscoroutinefunction(target): @@ -102,7 +102,7 @@ def __app_sync( assets_dir=None, upload_dir=None, web_renderer="canvaskit", - route_url_strategy="hash", + route_url_strategy="path", auth_token=None, ): force_web_view = os.environ.get("FLET_FORCE_WEB_VIEW") From 2fb28119e8e73dee5c77803b8f405f3c32c51ffc Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 19 Jan 2023 09:45:51 -0800 Subject: [PATCH 14/55] Load python app from app.tar.gz --- client/web/index.html | 9 ++++++--- client/web/python.js | 22 ++++++++++++---------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/client/web/index.html b/client/web/index.html index ad41a4c37..7a00d8b3b 100644 --- a/client/web/index.html +++ b/client/web/index.html @@ -19,15 +19,18 @@ - + Flet - - + + + + """ + index = index.replace("%FLET_WEB_PYODIDE%", "true") + index = index.replace("", pyodideCode) + index = index.replace( + "", + f'', + ) + index = index.replace("%FLET_ROUTE_URL_STRATEGY%", options.route_url_strategy) + + with open(index_path, "w") as f: + f.write(index) def __download_flet_web(self, file_name): ver = flet.version.version From de9d4640e27ebe318d52a97612ea80b33293cf67 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 19 Jan 2023 16:58:33 -0800 Subject: [PATCH 17/55] Host "web" dir inside module --- .../src/flet/pyodide_connection.py | 4 +-- sdk/python/packages/flet/.gitignore | 1 + sdk/python/packages/flet/build-wheels.py | 18 ++++++++++ .../flet/src/flet/cli/commands/publish.py | 35 ++++++------------- 4 files changed, 31 insertions(+), 27 deletions(-) create mode 100644 sdk/python/packages/flet/.gitignore diff --git a/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py index f25ed3884..72c8a3898 100644 --- a/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py +++ b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py @@ -30,7 +30,7 @@ def __init__( flet_js.start_connection = self.connect async def connect(self): - print("Starting Pyodide connection...") + logging.info("Starting Pyodide connection...") asyncio.create_task(self.receive_loop()) flet_js.send = self.send_from_js flet_js.receive_async = self.receive_from_js_async @@ -41,7 +41,7 @@ async def receive_loop(self): await self.__on_message(message) def send_from_js(self, message: str): - print("Sending data from JavaScript to Python:", message) + logging.debug(f"Sending data from JavaScript to Python: {message}") self.__receive_queue.put_nowait(message) async def receive_from_js_async(self): diff --git a/sdk/python/packages/flet/.gitignore b/sdk/python/packages/flet/.gitignore new file mode 100644 index 000000000..ef9119282 --- /dev/null +++ b/sdk/python/packages/flet/.gitignore @@ -0,0 +1 @@ +src/flet/web/ \ No newline at end of file diff --git a/sdk/python/packages/flet/build-wheels.py b/sdk/python/packages/flet/build-wheels.py index aba517201..a6ee7c435 100644 --- a/sdk/python/packages/flet/build-wheels.py +++ b/sdk/python/packages/flet/build-wheels.py @@ -13,6 +13,7 @@ from base64 import urlsafe_b64encode fletd_job_name = "Build Fletd" +flet_web_job_name = "Build Fletd" build_jobs = {} @@ -108,6 +109,14 @@ def download_flet_server(jobId, asset, exec_filename, dest_file): os.chmod(dest_file, st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH) +def download_flet_web(jobId, dest_file): + flet_web_url = ( + f"https://ci.appveyor.com/api/buildjobs/{jobId}/artifacts/flet-web.tar.gz" + ) + print(f"Downloading {flet_web_url}...") + urllib.request.urlretrieve(flet_web_url, dest_file) + + def download_artifact_by_name(jobId, artifact_name, dest_file): url = f"https://ci.appveyor.com/api/buildjobs/{jobId}/artifacts" print(f"Fetching build job artifacts at {url}") @@ -235,6 +244,15 @@ def rehash_record_lines(root_dir): zip_arch.extractall(bin_path) os.remove(client_arch_path) + # create "web" directory + web_path = unpacked_whl.joinpath("flet", "web") + web_path.mkdir(exist_ok=True) + web_tar_path = unpacked_whl.joinpath("flet-web.tar.gz") + download_flet_web(build_jobs[flet_web_job_name], web_tar_path) + with tarfile.open(web_tar_path, "r:gz") as tar: + tar.extractall(str(web_path)) + os.remove(web_tar_path) + # update WHEEL file for tag in package["wheel_tags"]: wheel_lines.append(f"Tag: {tag}\n") diff --git a/sdk/python/packages/flet/src/flet/cli/commands/publish.py b/sdk/python/packages/flet/src/flet/cli/commands/publish.py index 0bf2407c4..92db7bbdb 100644 --- a/sdk/python/packages/flet/src/flet/cli/commands/publish.py +++ b/sdk/python/packages/flet/src/flet/cli/commands/publish.py @@ -1,17 +1,11 @@ import argparse -import logging import os import shutil import tarfile -import tempfile -import urllib.request from pathlib import Path -import flet.__pyinstaller.config as hook_config -import flet.version from flet.cli.commands.base import BaseCommand -from flet.utils import is_macos, is_windows, is_within_directory, safe_tar_extractall -from flet_core.utils import random_string +from flet.utils import is_within_directory class Command(BaseCommand): @@ -77,13 +71,12 @@ def handle(self, options: argparse.Namespace) -> None: shutil.rmtree(dist_dir, ignore_errors=True) Path(dist_dir).mkdir(parents=True, exist_ok=True) - # download flet-web.tar.gz - print(f"Downloading Flet Web to {dist_dir}") - temp_arch = self.__download_flet_web(flet_web_filename) - - # unpack to dist - with tarfile.open(temp_arch, "r:gz") as tar_arch: - safe_tar_extractall(tar_arch, dist_dir) + # copy "web" + web_path = Path(__file__).parent.parent.parent.joinpath("web") + if not web_path.exists(): + print("Flet module does not contain 'web' directory.") + exit(1) + shutil.copytree(str(web_path), dist_dir, dirs_exist_ok=True) # copy assets assets_dir = options.assets_dir @@ -92,6 +85,9 @@ def handle(self, options: argparse.Namespace) -> None: Path(os.path.dirname(script_path)).joinpath(assets_dir).resolve() ) if assets_dir: + if not os.path.exists(assets_dir): + print("Assets dir not found:", assets_dir) + exit(1) shutil.copytree(assets_dir, dist_dir, dirs_exist_ok=True) # create "./dist/requirements.txt" if not exist @@ -164,14 +160,3 @@ def filter_tar(tarinfo: tarfile.TarInfo): with open(index_path, "w") as f: f.write(index) - - def __download_flet_web(self, file_name): - ver = flet.version.version - temp_arch = str(Path(tempfile.gettempdir()).joinpath(f"{ver}-{file_name}")) - logging.info(f"Downloading Flet Web v{ver} to {temp_arch}") - flet_url = ( - # f"https://github.com/flet-dev/flet/releases/download/v{ver}/{file_name}" - "https://ci.appveyor.com/api/buildjobs/5k01xli75kelopba/artifacts/flet-web.tar.gz" - ) - urllib.request.urlretrieve(flet_url, temp_arch) - return temp_arch From 9e8ffc8652994707b6a2b40118ce4479b808c889 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Fri, 20 Jan 2023 12:13:48 -0800 Subject: [PATCH 18/55] Run Python in web worker --- client/web/python-worker.js | 45 ++++++++++++ client/web/python.js | 73 ++++++------------- .../flet_server_protocol_javascript_web.dart | 19 +---- .../src/flet/pyodide_connection.py | 10 +-- 4 files changed, 72 insertions(+), 75 deletions(-) create mode 100644 client/web/python-worker.js diff --git a/client/web/python-worker.js b/client/web/python-worker.js new file mode 100644 index 000000000..0dddadcd9 --- /dev/null +++ b/client/web/python-worker.js @@ -0,0 +1,45 @@ +importScripts("https://cdn.jsdelivr.net/pyodide/v0.22.0/full/pyodide.js"); + +self.micropipIncludePre = false; +self.pythonModuleName = null; +self.initialized = false; +self.flet_js = {}; // namespace for Python global functions + +self.initPyodide = async function () { + self.pyodide = await loadPyodide(); + self.pyodide.registerJsModule("flet_js", flet_js); + await self.pyodide.loadPackage("micropip"); + let pre = self.micropipIncludePre ? "True" : "False"; + await self.pyodide.runPythonAsync(` + import micropip + import os + from pyodide.http import pyfetch + response = await pyfetch("/app.tar.gz") + await response.unpack_archive() + if os.path.exists("requirements.txt"): + with open("requirements.txt", "r") as f: + deps = [line.rstrip() for line in f] + print("Loading requirements.txt:", deps) + await micropip.install(deps, pre=${pre}) + `); + pyodide.pyimport(self.pythonModuleName); + await self.flet_js.start_connection(self.receiveCallback); + self.postMessage("initialized"); +}; + +self.receiveCallback = (message) => { + self.postMessage(message); +} + +self.onmessage = async (event) => { + // run only once + if (!self.initialized) { + self.initialized = true; + self.micropipIncludePre = event.data.micropipIncludePre; + self.pythonModuleName = event.data.pythonModuleName; + await self.initPyodide(); + } else { + // message + flet_js.send(event.data); + } +}; \ No newline at end of file diff --git a/client/web/python.js b/client/web/python.js index df3c1ce03..2c9f59be8 100644 --- a/client/web/python.js +++ b/client/web/python.js @@ -1,55 +1,26 @@ -const pyodideUrl = "https://cdn.jsdelivr.net/pyodide/v0.22.0/full/pyodide.js"; -let flet_js = {}; // namespace for Python global functions - -function loadScript(url, callback) { - // Adding the script tag to the head as suggested before - var head = document.head; - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = url; - - // Then bind the event to the callback function. - // There are several events for cross browser compatibility. - script.onreadystatechange = callback; - script.onload = callback; - - // Fire the loading - head.appendChild(script); -} - -let pyodideReady = new Promise((resolve) => { - loadScript(pyodideUrl, async () => { - let pyodide = await loadPyodide(); - pyodide.registerJsModule("flet_js", flet_js); - await pyodide.loadPackage("micropip"); - let pre = micropipIncludePre ? "True" : "False"; - await pyodide.runPythonAsync(` - import micropip - import os - - from pyodide.http import pyfetch - response = await pyfetch("/app.tar.gz") - await response.unpack_archive() - if os.path.exists("requirements.txt"): - with open("requirements.txt", "r") as f: - deps = [line.rstrip() for line in f] - print("Loading requirements.txt:", deps) - await micropip.install(deps, pre=${pre}) - `); - pyodide.pyimport(pythonModuleName); - resolve(pyodide); - }); -}); - -async function jsConnect() { - await pyodideReady; - await flet_js.start_connection(); +const pythonWorker = new Worker("/python-worker.js"); + +let _onPythonInitialized = null; +let pythonInitialized = new Promise((onSuccess) => _onPythonInitialized = onSuccess); +let _onReceivedCallback = null; + +pythonWorker.onmessage = (event) => { + if (event.data == "initialized") { + _onPythonInitialized(); + } else { + _onReceivedCallback(event.data); + } +}; + +// initialize worker +pythonWorker.postMessage({ micropipIncludePre, pythonModuleName }); + +async function jsConnect(receiveCallback) { + _onReceivedCallback = receiveCallback; + await pythonInitialized; + console.log("Python engine initialized!"); } async function jsSend(data) { - flet_js.send(data) -} - -async function jsReceive() { - return await flet_js.receive_async(); + pythonWorker.postMessage(data); } \ No newline at end of file diff --git a/package/lib/src/flet_server_protocol_javascript_web.dart b/package/lib/src/flet_server_protocol_javascript_web.dart index 2d5037034..37aa21eaf 100644 --- a/package/lib/src/flet_server_protocol_javascript_web.dart +++ b/package/lib/src/flet_server_protocol_javascript_web.dart @@ -9,17 +9,11 @@ import 'package:js/js.dart'; import 'flet_server_protocol.dart'; @JS() -external dynamic sleep(int ms); - -@JS() -external dynamic jsConnect(); +external dynamic jsConnect(FletServerProtocolOnMessageCallback onMessage); @JS() external dynamic jsSend(String data); -@JS() -external dynamic jsReceive(); - class FletJavaScriptServerProtocol implements FletServerProtocol { final String address; final FletServerProtocolOnMessageCallback onMessage; @@ -33,16 +27,7 @@ class FletJavaScriptServerProtocol implements FletServerProtocol { @override connect() async { debugPrint("Connecting to JavaScript server $address..."); - await promiseToFuture(jsConnect()); - receiveLoop(); - } - - Future receiveLoop() async { - debugPrint("Starting receive loop..."); - while (true) { - var message = await promiseToFuture(jsReceive()); - onMessage(message); - } + await promiseToFuture(jsConnect(allowInterop(onMessage))); } @override diff --git a/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py index 72c8a3898..9e1473a05 100644 --- a/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py +++ b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py @@ -23,17 +23,16 @@ def __init__( on_session_created, ): super().__init__() - self.__send_queue = asyncio.Queue(1) self.__receive_queue = asyncio.Queue() self.__on_event = on_event self.__on_session_created = on_session_created flet_js.start_connection = self.connect - async def connect(self): + async def connect(self, send_callback): logging.info("Starting Pyodide connection...") + self.send_callback = send_callback asyncio.create_task(self.receive_loop()) flet_js.send = self.send_from_js - flet_js.receive_async = self.receive_from_js_async async def receive_loop(self): while True: @@ -44,9 +43,6 @@ def send_from_js(self, message: str): logging.debug(f"Sending data from JavaScript to Python: {message}") self.__receive_queue.put_nowait(message) - async def receive_from_js_async(self): - return await self.__send_queue.get() - async def __on_message(self, data: str): logging.debug(f"_on_message: {data}") msg_dict = json.loads(data) @@ -102,4 +98,4 @@ async def send_commands_async(self, session_id: str, commands: List[Command]): async def __send(self, message: ClientMessage): j = json.dumps(message, cls=CommandEncoder, separators=(",", ":")) logging.debug(f"__send: {j}") - await self.__send_queue.put(j) + self.send_callback(j) From 461e73e67a449d208e65293fe26997038e4af658 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Fri, 20 Jan 2023 14:01:55 -0800 Subject: [PATCH 19/55] Fix writing dependencies --- sdk/python/packages/flet/src/flet/cli/commands/publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/packages/flet/src/flet/cli/commands/publish.py b/sdk/python/packages/flet/src/flet/cli/commands/publish.py index 92db7bbdb..63d93bcd1 100644 --- a/sdk/python/packages/flet/src/flet/cli/commands/publish.py +++ b/sdk/python/packages/flet/src/flet/cli/commands/publish.py @@ -108,7 +108,7 @@ def handle(self, options: argparse.Namespace) -> None: if not pyodide_dep_found: deps.append(f"flet-pyodide") with open(reqs_path, "w") as f: - f.writelines(deps) + f.writelines(dep + "\n" for dep in deps) # pack all files in script's directory to dist/app.tar.gz app_tar_gz_path = os.path.join(dist_dir, app_tar_gz_filename) From 1c50d6800a6653b55188e1f121198a0ea212f09b Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 12:26:41 -0800 Subject: [PATCH 20/55] Fix hanging async connection on Windows --- .../flet/src/flet/async_local_socket_connection.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sdk/python/packages/flet/src/flet/async_local_socket_connection.py b/sdk/python/packages/flet/src/flet/async_local_socket_connection.py index e591d398a..4aa018256 100644 --- a/sdk/python/packages/flet/src/flet/async_local_socket_connection.py +++ b/sdk/python/packages/flet/src/flet/async_local_socket_connection.py @@ -37,6 +37,8 @@ def __init__( self.__on_session_created = on_session_created async def connect(self): + self.__connected = False + self.__uds_path = None if is_windows() or self.__port > 0: # TCP host = "localhost" @@ -60,8 +62,11 @@ async def connect(self): async def handle_connection( self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter ): - asyncio.create_task(self.__receive_loop(reader)) - asyncio.create_task(self.__send_loop(writer)) + if not self.__connected: + self.__connected = True + logging.debug("Connected new TCP client") + asyncio.create_task(self.__receive_loop(reader)) + asyncio.create_task(self.__send_loop(writer)) async def __receive_loop(self, reader: asyncio.StreamReader): while True: @@ -84,7 +89,8 @@ async def __send_loop(self, writer: asyncio.StreamWriter): data = message.encode("utf-8") msg = struct.pack(">I", len(data)) + data writer.write(msg) - await writer.drain() + # await writer.drain() + logging.debug("sent to TCP: {}".format(len(msg))) except: # re-enqueue the message to repeat it when re-connected self.__send_queue.put_nowait(message) From 2a542869c273ddd8350970f351ccf25898739c83 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 12:44:44 -0800 Subject: [PATCH 21/55] Fix cryptic error in async local connection on Windows --- client/lib/main.dart | 2 +- .../flet/src/flet/async_local_socket_connection.py | 2 +- sdk/python/packages/flet/src/flet/flet.py | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/client/lib/main.dart b/client/lib/main.dart index ac1283d5e..9753f750d 100644 --- a/client/lib/main.dart +++ b/client/lib/main.dart @@ -20,7 +20,7 @@ void main([List? args]) async { //debugPrint("Uri.base: ${Uri.base}"); if (kDebugMode) { - pageUrl = "http://localhost:8550"; + pageUrl = "tcp://localhost:8550"; } if (kIsWeb) { diff --git a/sdk/python/packages/flet/src/flet/async_local_socket_connection.py b/sdk/python/packages/flet/src/flet/async_local_socket_connection.py index 4aa018256..8e721c8a2 100644 --- a/sdk/python/packages/flet/src/flet/async_local_socket_connection.py +++ b/sdk/python/packages/flet/src/flet/async_local_socket_connection.py @@ -72,7 +72,7 @@ async def __receive_loop(self, reader: asyncio.StreamReader): while True: try: raw_msglen = await reader.readexactly(4) - except asyncio.IncompleteReadError: + except: return None if not raw_msglen: diff --git a/sdk/python/packages/flet/src/flet/flet.py b/sdk/python/packages/flet/src/flet/flet.py index 9d168f1d0..9b3c96b09 100644 --- a/sdk/python/packages/flet/src/flet/flet.py +++ b/sdk/python/packages/flet/src/flet/flet.py @@ -1,10 +1,8 @@ import asyncio -import inspect import logging import os import signal import subprocess -import sys import tarfile import tempfile import threading @@ -34,7 +32,7 @@ ) from flet_core.event import Event from flet_core.page import Page -from flet_core.utils import random_string +from flet_core.utils import is_coroutine, random_string try: from typing import Literal @@ -63,8 +61,8 @@ def app( route_url_strategy="path", auth_token=None, ): - if inspect.iscoroutinefunction(target): - asyncio.run( + if is_coroutine(target): + asyncio.get_event_loop().run_until_complete( app_async( target=target, name=name, From 38c7313a57acac269e2ae29e8b1c9189301e0dd4 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 13:01:22 -0800 Subject: [PATCH 22/55] Support sync apps in Pyodide --- sdk/python/build-pyodide.cmd | 10 ++++ .../flet-core/src/flet_core/event_handler.py | 12 +++-- .../packages/flet-core/src/flet_core/page.py | 49 +++++++------------ .../packages/flet-core/src/flet_core/utils.py | 3 +- .../packages/flet-pyodide/pyproject.toml | 2 +- .../packages/flet-pyodide/src/flet/flet.py | 10 ++-- .../src/flet/pyodide_connection.py | 16 +++--- .../flet/src/flet/auth/authorization.py | 1 - sdk/python/packages/flet/src/flet/pubsub.py | 2 - 9 files changed, 55 insertions(+), 50 deletions(-) create mode 100644 sdk/python/build-pyodide.cmd diff --git a/sdk/python/build-pyodide.cmd b/sdk/python/build-pyodide.cmd new file mode 100644 index 000000000..40dcdeea4 --- /dev/null +++ b/sdk/python/build-pyodide.cmd @@ -0,0 +1,10 @@ +cd packages\flet-core +poetry build +cd ..\.. + +cd packages\flet-pyodide +poetry build +cd ..\.. + +copy packages\flet-core\dist\*.whl ..\..\client\build\web\python +copy packages\flet-pyodide\dist\*.whl ..\..\client\build\web\python \ No newline at end of file diff --git a/sdk/python/packages/flet-core/src/flet_core/event_handler.py b/sdk/python/packages/flet-core/src/flet_core/event_handler.py index 76d0e8c0e..c5cd6618f 100644 --- a/sdk/python/packages/flet-core/src/flet_core/event_handler.py +++ b/sdk/python/packages/flet-core/src/flet_core/event_handler.py @@ -1,4 +1,4 @@ -from flet_core.utils import is_asyncio +from flet_core.utils import is_asyncio, is_coroutine class EventHandler: @@ -36,9 +36,15 @@ async def __async_handler(self, e): r.data = e.data r.control = e.control r.page = e.page - await h(r) + if is_coroutine(h): + await h(r) + else: + h(r) else: - await h(e) + if is_coroutine(h): + await h(e) + else: + h(e) def subscribe(self, handler): if handler is not None: diff --git a/sdk/python/packages/flet-core/src/flet_core/page.py b/sdk/python/packages/flet-core/src/flet_core/page.py index b12cf31a1..55c9e021b 100644 --- a/sdk/python/packages/flet-core/src/flet_core/page.py +++ b/sdk/python/packages/flet-core/src/flet_core/page.py @@ -60,6 +60,22 @@ def __init__(self, pubsubhub, session_id) -> None: from typing_extensions import Literal +class NopeLock(object): + def __enter__(self): + pass + + def __exit__(self, *args): + pass + + +class AsyncNopeLock(object): + async def __aenter__(self): + pass + + async def __aexit__(self, *args): + pass + + class Page(Control): """ Page is a container for `View` (https://flet.dev/docs/controls/view) controls. @@ -95,8 +111,8 @@ def __init__(self, conn: Connection, session_id): self._session_id = session_id self._index = {self._Control__uid: self} # index with all page controls - self.__lock = threading.Lock() if not is_asyncio() else None - self.__async_lock = asyncio.Lock() if is_asyncio() else None + self.__lock = threading.Lock() if not is_asyncio() else NopeLock() + self.__async_lock = asyncio.Lock() if is_asyncio() else AsyncNopeLock() self.__views = [View()] self.__default_view = self.__views[0] @@ -235,7 +251,6 @@ def __set_page_details(self, values): self._set_attr("windowLeft", values[9], False) def update(self, *controls): - assert self.__lock, "Sync method calls are not supported in async app." with self.__lock: if len(controls) == 0: r = self.__update(self) @@ -244,9 +259,6 @@ def update(self, *controls): self.__handle_mount_unmount(*r) async def update_async(self, *controls): - assert ( - self.__async_lock - ), "Async method calls are not supported in a regular app." async with self.__async_lock: if len(controls) == 0: r = await self.__update_async(self) @@ -255,23 +267,18 @@ async def update_async(self, *controls): await self.__handle_mount_unmount_async(*r) def add(self, *controls): - assert self.__lock, "Sync method calls are not supported in async app." with self.__lock: self._controls.extend(controls) r = self.__update(self) self.__handle_mount_unmount(*r) async def add_async(self, *controls): - assert ( - self.__async_lock - ), "Async method calls are not supported in a regular app." async with self.__async_lock: self._controls.extend(controls) r = await self.__update_async(self) await self.__handle_mount_unmount_async(*r) def insert(self, at, *controls): - assert self.__lock, "Sync method calls are not supported in async app." with self.__lock: n = at for control in controls: @@ -281,9 +288,6 @@ def insert(self, at, *controls): self.__handle_mount_unmount(*r) async def insert_async(self, at, *controls): - assert ( - self.__async_lock - ), "Async method calls are not supported in a regular app." async with self.__async_lock: n = at for control in controls: @@ -293,7 +297,6 @@ async def insert_async(self, at, *controls): await self.__handle_mount_unmount_async(*r) def remove(self, *controls): - assert self.__lock, "Sync method calls are not supported in async app." with self.__lock: for control in controls: self._controls.remove(control) @@ -301,9 +304,6 @@ def remove(self, *controls): self.__handle_mount_unmount(*r) async def remove_async(self, *controls): - assert ( - self.__async_lock - ), "Async method calls are not supported in a regular app." async with self.__async_lock: for control in controls: self._controls.remove(control) @@ -311,16 +311,12 @@ async def remove_async(self, *controls): await self.__handle_mount_unmount_async(*r) def remove_at(self, index): - assert self.__lock, "Sync method calls are not supported in async app." with self.__lock: self._controls.pop(index) r = self.__update(self) self.__handle_mount_unmount(*r) async def remove_at_async(self, index): - assert ( - self.__async_lock - ), "Async method calls are not supported in a regular app." async with self.__async_lock: self._controls.pop(index) r = await self.__update_async(self) @@ -335,7 +331,6 @@ async def clean_async(self): self._controls.clear() def _clean(self, control: Control): - assert self.__lock, "Sync method calls are not supported in async app." with self.__lock: control._previous_children.clear() assert control.uid is not None @@ -349,9 +344,6 @@ def _clean(self, control: Control): c.will_unmount() async def _clean_async(self, control: Control): - assert ( - self.__async_lock - ), "Async method calls are not supported in a regular app." async with self.__async_lock: control._previous_children.clear() assert control.uid is not None @@ -420,20 +412,15 @@ async def __handle_mount_unmount_async(self, added_controls, removed_controls): await ctrl.did_mount_async() def error(self, message=""): - assert self.__lock, "Sync method calls are not supported in async app." with self.__lock: self._send_command("error", [message]) async def error_async(self, message=""): - assert ( - self.__async_lock - ), "Async method calls are not supported in a regular app." async with self.__async_lock: await self._send_command_async("error", [message]) def on_event(self, e: Event): logging.info(f"page.on_event: {e.target} {e.name} {e.data}") - assert self.__lock, "Sync method calls are not supported in async app." with self.__lock: if e.target == "page" and e.name == "change": self.__on_page_change_event(e.data) diff --git a/sdk/python/packages/flet-core/src/flet_core/utils.py b/sdk/python/packages/flet-core/src/flet_core/utils.py index 3adcb5c4b..9c70d3717 100644 --- a/sdk/python/packages/flet-core/src/flet_core/utils.py +++ b/sdk/python/packages/flet-core/src/flet_core/utils.py @@ -2,6 +2,7 @@ import inspect import random import string +import sys def random_string(length): @@ -10,7 +11,7 @@ def random_string(length): def is_asyncio(): try: - return asyncio.current_task() is not None + return asyncio.current_task() is not None or sys.platform == "emscripten" except RuntimeError: return False diff --git a/sdk/python/packages/flet-pyodide/pyproject.toml b/sdk/python/packages/flet-pyodide/pyproject.toml index 917bfb4c5..fbe11944a 100644 --- a/sdk/python/packages/flet-pyodide/pyproject.toml +++ b/sdk/python/packages/flet-pyodide/pyproject.toml @@ -17,7 +17,7 @@ documentation = "https://flet.dev/docs" [tool.poetry.dependencies] flet-core = "0.1.0" -python = "^3.10" +python = "^3.9" [build-system] requires = ["poetry-core"] diff --git a/sdk/python/packages/flet-pyodide/src/flet/flet.py b/sdk/python/packages/flet-pyodide/src/flet/flet.py index 439870ccb..10b02fecf 100644 --- a/sdk/python/packages/flet-pyodide/src/flet/flet.py +++ b/sdk/python/packages/flet-pyodide/src/flet/flet.py @@ -1,4 +1,3 @@ -import inspect import logging import traceback @@ -6,6 +5,7 @@ from flet.pyodide_connection import PyodideConnection from flet_core.event import Event from flet_core.page import Page +from flet_core.utils import is_coroutine try: from typing import Literal @@ -29,9 +29,6 @@ def app( route_url_strategy=None, auth_token=None, ): - if not inspect.iscoroutinefunction(target): - raise Exception("Sync apps are not supported in Pyodide.") - app_async( target=target, name=name, @@ -74,7 +71,10 @@ async def on_session_created(session_data): logging.info(f"Session started: {session_data.sessionID}") try: assert target is not None - await target(page) + if is_coroutine(target): + await target(page) + else: + target(page) except Exception as e: print( f"Unhandled error processing page session {page.session_id}:", diff --git a/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py index 9e1473a05..a7ca8dd59 100644 --- a/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py +++ b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py @@ -51,7 +51,7 @@ async def __on_message(self, data: str): self._client_details = RegisterWebClientRequestPayload(**msg.payload) # register response - await self.__send(self._create_register_web_client_response()) + self.__send(self._create_register_web_client_response()) # start session if self.__on_session_created is not None: @@ -75,12 +75,18 @@ async def __on_message(self, data: str): raise Exception('Unknown message "{}": {}'.format(msg.action, msg.payload)) async def send_command_async(self, session_id: str, command: Command): + return self.send_command(session_id, command) + + def send_command(self, session_id: str, command: Command): result, message = self._process_command(command) if message: - await self.__send(message) + self.__send(message) return PageCommandResponsePayload(result=result, error="") async def send_commands_async(self, session_id: str, commands: List[Command]): + return self.send_commands(session_id, commands) + + def send_commands(self, session_id: str, commands: List[Command]): results = [] messages = [] for command in commands: @@ -90,12 +96,10 @@ async def send_commands_async(self, session_id: str, commands: List[Command]): if message: messages.append(message) if len(messages) > 0: - await self.__send( - ClientMessage(ClientActions.PAGE_CONTROLS_BATCH, messages) - ) + self.__send(ClientMessage(ClientActions.PAGE_CONTROLS_BATCH, messages)) return PageCommandsBatchResponsePayload(results=results, error="") - async def __send(self, message: ClientMessage): + def __send(self, message: ClientMessage): j = json.dumps(message, cls=CommandEncoder, separators=(",", ":")) logging.debug(f"__send: {j}") self.send_callback(j) diff --git a/sdk/python/packages/flet/src/flet/auth/authorization.py b/sdk/python/packages/flet/src/flet/auth/authorization.py index cb0f1e8fa..f161ce0bb 100644 --- a/sdk/python/packages/flet/src/flet/auth/authorization.py +++ b/sdk/python/packages/flet/src/flet/auth/authorization.py @@ -10,7 +10,6 @@ from flet.auth.oauth_token import OAuthToken from flet.auth.user import User from flet.version import version -from flet_core.utils import is_asyncio from oauthlib.oauth2 import WebApplicationClient from oauthlib.oauth2.rfc6749.tokens import OAuth2Token diff --git a/sdk/python/packages/flet/src/flet/pubsub.py b/sdk/python/packages/flet/src/flet/pubsub.py index 79f4a5c8f..2218c9471 100644 --- a/sdk/python/packages/flet/src/flet/pubsub.py +++ b/sdk/python/packages/flet/src/flet/pubsub.py @@ -3,8 +3,6 @@ import threading from typing import Any, Callable, Dict, Iterable -from flet_core.utils import is_asyncio - class PubSubHub: def __init__(self): From 5c2f1c56c1921a095020a70e123984f55968e9a3 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 13:40:19 -0800 Subject: [PATCH 23/55] Set page.url for pyodide --- client/web/python.js | 4 +++- .../packages/flet-core/src/flet_core/event_handler.py | 3 +++ sdk/python/packages/flet-core/src/flet_core/page.py | 10 ++++++---- .../packages/flet-core/src/flet_core/querystring.py | 2 +- .../flet-pyodide/src/flet/pyodide_connection.py | 2 ++ .../packages/flet/src/flet/cli/commands/publish.py | 2 +- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/client/web/python.js b/client/web/python.js index 2c9f59be8..2d158d65b 100644 --- a/client/web/python.js +++ b/client/web/python.js @@ -12,8 +12,10 @@ pythonWorker.onmessage = (event) => { } }; +documentUrl = document.URL; + // initialize worker -pythonWorker.postMessage({ micropipIncludePre, pythonModuleName }); +pythonWorker.postMessage({ documentUrl, micropipIncludePre, pythonModuleName }); async function jsConnect(receiveCallback) { _onReceivedCallback = receiveCallback; diff --git a/sdk/python/packages/flet-core/src/flet_core/event_handler.py b/sdk/python/packages/flet-core/src/flet_core/event_handler.py index c5cd6618f..8a1c4cccc 100644 --- a/sdk/python/packages/flet-core/src/flet_core/event_handler.py +++ b/sdk/python/packages/flet-core/src/flet_core/event_handler.py @@ -6,6 +6,9 @@ def __init__(self, result_converter=None) -> None: self.__handlers = {} self.__result_converter = result_converter + def get_sync_handler(self): + return self.__sync_handler + def get_handler(self): if is_asyncio(): return self.__async_handler diff --git a/sdk/python/packages/flet-core/src/flet_core/page.py b/sdk/python/packages/flet-core/src/flet_core/page.py index 55c9e021b..d3e6fb1ba 100644 --- a/sdk/python/packages/flet-core/src/flet_core/page.py +++ b/sdk/python/packages/flet-core/src/flet_core/page.py @@ -459,7 +459,7 @@ def __on_page_change_event(self, data): def go(self, route, **kwargs): self.route = route if kwargs == {} else route + self.query.post(kwargs) - self.__on_route_change.get_handler()( + self.__on_route_change.get_sync_handler()( ControlEvent( target="page", name="route_change", @@ -541,7 +541,9 @@ def login( ) else: self.__authorization.dehydrate_token(saved_token) - self.__on_login.get_handler()(LoginEvent(error="", error_description="")) + self.__on_login.get_sync_handler()( + LoginEvent(error="", error_description="") + ) return self.__authorization async def login_async( @@ -613,7 +615,7 @@ def __on_authorize(self, e): self.__authorization.request_token(code) except Exception as ex: login_evt.error = str(ex) - self.__on_login.get_handler()(login_evt) + self.__on_login.get_sync_handler()(login_evt) async def __on_authorize_async(self, e): assert self.__authorization is not None @@ -644,7 +646,7 @@ async def __on_authorize_async(self, e): def logout(self): self.__authorization = None - self.__on_logout.get_handler()( + self.__on_logout.get_sync_handler()( ControlEvent(target="page", name="logout", data="", control=self, page=self) ) diff --git a/sdk/python/packages/flet-core/src/flet_core/querystring.py b/sdk/python/packages/flet-core/src/flet_core/querystring.py index e72de43b1..53d67d571 100644 --- a/sdk/python/packages/flet-core/src/flet_core/querystring.py +++ b/sdk/python/packages/flet-core/src/flet_core/querystring.py @@ -1,5 +1,5 @@ -import urllib.parse import re +import urllib.parse class UrlComponents: diff --git a/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py index a7ca8dd59..df03b55b6 100644 --- a/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py +++ b/sdk/python/packages/flet-pyodide/src/flet/pyodide_connection.py @@ -4,6 +4,7 @@ from typing import List import flet_js +import js from flet_core.local_connection import LocalConnection from flet_core.protocol import ( ClientActions, @@ -30,6 +31,7 @@ def __init__( async def connect(self, send_callback): logging.info("Starting Pyodide connection...") + self.page_url = flet_js.documentUrl self.send_callback = send_callback asyncio.create_task(self.receive_loop()) flet_js.send = self.send_from_js diff --git a/sdk/python/packages/flet/src/flet/cli/commands/publish.py b/sdk/python/packages/flet/src/flet/cli/commands/publish.py index 63d93bcd1..090a5889a 100644 --- a/sdk/python/packages/flet/src/flet/cli/commands/publish.py +++ b/sdk/python/packages/flet/src/flet/cli/commands/publish.py @@ -115,7 +115,7 @@ def handle(self, options: argparse.Namespace) -> None: def filter_tar(tarinfo: tarfile.TarInfo): full_path = os.path.join(script_dir, tarinfo.name) - if tarinfo.name.startswith("."): + if tarinfo.name.startswith(".") or tarinfo.name.startswith("__pycache__"): return None elif assets_dir and is_within_directory(assets_dir, full_path): return None From 5ee72f5db7bbea5488749b31a7703b3840f6e0e9 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 13:43:57 -0800 Subject: [PATCH 24/55] Pass documentUrl to web worker --- client/web/python-worker.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/web/python-worker.js b/client/web/python-worker.js index 0dddadcd9..599d1facf 100644 --- a/client/web/python-worker.js +++ b/client/web/python-worker.js @@ -8,6 +8,7 @@ self.flet_js = {}; // namespace for Python global functions self.initPyodide = async function () { self.pyodide = await loadPyodide(); self.pyodide.registerJsModule("flet_js", flet_js); + flet_js.documentUrl = documentUrl; await self.pyodide.loadPackage("micropip"); let pre = self.micropipIncludePre ? "True" : "False"; await self.pyodide.runPythonAsync(` @@ -35,6 +36,7 @@ self.onmessage = async (event) => { // run only once if (!self.initialized) { self.initialized = true; + self.documentUrl = event.data.documentUrl; self.micropipIncludePre = event.data.micropipIncludePre; self.pythonModuleName = event.data.pythonModuleName; await self.initPyodide(); From 1f9326c0845ccf9fdcefbb0443fec1b4a59bed85 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 17:47:30 -0800 Subject: [PATCH 25/55] Centered error message --- client/lib/main.dart | 6 ++++- package/lib/src/flet_app_main.dart | 24 ++++++++++++------- .../flet-core/src/flet_core/control.py | 6 +++++ .../flet-core/src/flet_core/protocol.py | 3 +++ 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/client/lib/main.dart b/client/lib/main.dart index 9753f750d..a67b92b14 100644 --- a/client/lib/main.dart +++ b/client/lib/main.dart @@ -20,7 +20,11 @@ void main([List? args]) async { //debugPrint("Uri.base: ${Uri.base}"); if (kDebugMode) { - pageUrl = "tcp://localhost:8550"; + if (kIsWeb) { + pageUrl = "http://localhost:8550"; + } else { + pageUrl = "tcp://localhost:8550"; + } } if (kIsWeb) { diff --git a/package/lib/src/flet_app_main.dart b/package/lib/src/flet_app_main.dart index ace7ded19..99a4a9aef 100644 --- a/package/lib/src/flet_app_main.dart +++ b/package/lib/src/flet_app_main.dart @@ -26,15 +26,21 @@ class FletAppMain extends StatelessWidget { return MaterialApp( title: title, home: Scaffold( - body: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.error_outline, - color: Colors.red, size: 25), - Text(viewModel.error, - style: const TextStyle(color: Colors.red)) - ], - ), + body: Container( + padding: const EdgeInsets.all(30), + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.error, + color: Colors.redAccent, size: 30), + const SizedBox(height: 8), + Flexible( + child: Text(viewModel.error, + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.red))) + ], + )), )); } else { return createControl(null, "page", false); diff --git a/sdk/python/packages/flet-core/src/flet_core/control.py b/sdk/python/packages/flet-core/src/flet_core/control.py index fe517eaa8..bea6870b7 100644 --- a/sdk/python/packages/flet-core/src/flet_core/control.py +++ b/sdk/python/packages/flet-core/src/flet_core/control.py @@ -399,6 +399,12 @@ def _build_add_commands(self, indent=0, index=None, added_controls=None): command = self._build_command(False) command.indent = indent command.values.append(self._get_control_name()) + + if self.page: + raise Exception( + "Control has already been added to a page: {}".format(command) + ) + commands.append(command) if added_controls is not None: diff --git a/sdk/python/packages/flet-core/src/flet_core/protocol.py b/sdk/python/packages/flet-core/src/flet_core/protocol.py index 012867001..aac958f2c 100644 --- a/sdk/python/packages/flet-core/src/flet_core/protocol.py +++ b/sdk/python/packages/flet-core/src/flet_core/protocol.py @@ -43,6 +43,9 @@ class Command: attrs: Dict[str, str] = field(default_factory=dict) commands: List[Any] = field(default_factory=list) + def __str__(self): + return "{} {}".format(self.values, self.attrs) + @dataclass class Message: From 51ad0e244ce24b47458f8fce731470c41e4e21e9 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 18:21:05 -0800 Subject: [PATCH 26/55] `flet publish --base-url` --- .../flet/src/flet/cli/commands/publish.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sdk/python/packages/flet/src/flet/cli/commands/publish.py b/sdk/python/packages/flet/src/flet/cli/commands/publish.py index 090a5889a..39828ca3b 100644 --- a/sdk/python/packages/flet/src/flet/cli/commands/publish.py +++ b/sdk/python/packages/flet/src/flet/cli/commands/publish.py @@ -30,6 +30,13 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: default=None, help="path to an assets directory", ) + parser.add_argument( + "--base-url", + dest="base_url", + type=str, + default=None, + help="base URL for the app", + ) parser.add_argument( "--web-renderer", dest="web_renderer", @@ -158,5 +165,14 @@ def filter_tar(tarinfo: tarfile.TarInfo): ) index = index.replace("%FLET_ROUTE_URL_STRATEGY%", options.route_url_strategy) + if options.base_url: + base_url = options.base_url.strip("/").strip() + index = index.replace( + '', + ''.format( + "/" if base_url == "" else "/{}/".format(base_url) + ), + ) + with open(index_path, "w") as f: f.write(index) From e1a32bf7dced3f839127be719784a3d16ae00c0c Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 18:53:50 -0800 Subject: [PATCH 27/55] Replace "flet" req with "flet-pyodide" --- .../flet/src/flet/cli/commands/publish.py | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/sdk/python/packages/flet/src/flet/cli/commands/publish.py b/sdk/python/packages/flet/src/flet/cli/commands/publish.py index 39828ca3b..7df28bd16 100644 --- a/sdk/python/packages/flet/src/flet/cli/commands/publish.py +++ b/sdk/python/packages/flet/src/flet/cli/commands/publish.py @@ -1,11 +1,14 @@ import argparse import os +import re import shutil import tarfile +import tempfile from pathlib import Path from flet.cli.commands.base import BaseCommand from flet.utils import is_within_directory +from flet_core.utils import random_string class Command(BaseCommand): @@ -107,22 +110,32 @@ def handle(self, options: argparse.Namespace) -> None: with open(reqs_path, "r") as f: deps = [line.rstrip() for line in f] + deps = list( + filter(lambda dep: not re.search("(^flet$)|(^flet[^a-z0-9-]+)", dep), deps) + ) + pyodide_dep_found = False for dep in deps: - if dep.startswith("flet-pyodide"): + if re.search("(^flet-pyodide$)|(^flet-pyodide[^a-z0-9-]+)", dep): pyodide_dep_found = True break if not pyodide_dep_found: deps.append(f"flet-pyodide") - with open(reqs_path, "w") as f: - f.writelines(dep + "\n" for dep in deps) + + temp_reqs_txt = Path(tempfile.gettempdir()).joinpath(random_string(10)) + with open(temp_reqs_txt, "w") as f: + f.writelines(dep + "\n" for dep in deps) # pack all files in script's directory to dist/app.tar.gz app_tar_gz_path = os.path.join(dist_dir, app_tar_gz_filename) def filter_tar(tarinfo: tarfile.TarInfo): full_path = os.path.join(script_dir, tarinfo.name) - if tarinfo.name.startswith(".") or tarinfo.name.startswith("__pycache__"): + if ( + tarinfo.name.startswith(".") + or tarinfo.name.startswith("__pycache__") + or tarinfo.name == "requirements.txt" + ): return None elif assets_dir and is_within_directory(assets_dir, full_path): return None @@ -134,8 +147,12 @@ def filter_tar(tarinfo: tarfile.TarInfo): return tarinfo print(f"Packaging application to {app_tar_gz_filename}") - with tarfile.open(app_tar_gz_path, "w:gz") as tar: + with tarfile.open(app_tar_gz_path, "w:gz", format=tarfile.GNU_FORMAT) as tar: tar.add(script_dir, arcname="/", filter=filter_tar) + print("Adding requirements.txt") + tar.add(temp_reqs_txt, arcname="requirements.txt") + + os.remove(temp_reqs_txt) # patch ./dist/index.html # - From f306bc221afac3d33b194ca8613e4c8bf6cbdce0 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 21 Jan 2023 19:05:51 -0800 Subject: [PATCH 28/55] Patching app title and description --- .../flet/src/flet/cli/commands/publish.py | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/sdk/python/packages/flet/src/flet/cli/commands/publish.py b/sdk/python/packages/flet/src/flet/cli/commands/publish.py index 7df28bd16..374357c54 100644 --- a/sdk/python/packages/flet/src/flet/cli/commands/publish.py +++ b/sdk/python/packages/flet/src/flet/cli/commands/publish.py @@ -33,6 +33,20 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: default=None, help="path to an assets directory", ) + parser.add_argument( + "--app-title", + dest="app_title", + type=str, + default=None, + help="application title", + ) + parser.add_argument( + "--app-description", + dest="app_description", + type=str, + default=None, + help="application description", + ) parser.add_argument( "--base-url", dest="base_url", @@ -143,13 +157,14 @@ def filter_tar(tarinfo: tarfile.TarInfo): return None # tarinfo.uid = tarinfo.gid = 0 # tarinfo.uname = tarinfo.gname = "root" - print("Adding", tarinfo.name) + if tarinfo.name != "": + print(" Adding", tarinfo.name) return tarinfo print(f"Packaging application to {app_tar_gz_filename}") with tarfile.open(app_tar_gz_path, "w:gz", format=tarfile.GNU_FORMAT) as tar: tar.add(script_dir, arcname="/", filter=filter_tar) - print("Adding requirements.txt") + print(" Adding requirements.txt") tar.add(temp_reqs_txt, arcname="requirements.txt") os.remove(temp_reqs_txt) @@ -190,6 +205,15 @@ def filter_tar(tarinfo: tarfile.TarInfo): "/" if base_url == "" else "/{}/".format(base_url) ), ) + if options.app_title: + index = index.replace( + 'content="Flet"', 'content="{}"'.format(options.app_title) + ) + if options.app_description: + index = index.replace( + 'content="Flet application."', + 'content="{}"'.format(options.app_description), + ) with open(index_path, "w") as f: f.write(index) From 6e7829dc687101bc29127fd6eef2ae59b1df1e9a Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 23 Jan 2023 09:10:48 -0800 Subject: [PATCH 29/55] Introduce get_package_bin_dir() --- .../flet/src/flet/__pyinstaller/utils.py | 6 +++--- .../flet/src/flet/cli/commands/publish.py | 8 ++++---- sdk/python/packages/flet/src/flet/flet.py | 19 +++++++++---------- sdk/python/packages/flet/src/flet/utils.py | 13 +++++++++++++ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/sdk/python/packages/flet/src/flet/__pyinstaller/utils.py b/sdk/python/packages/flet/src/flet/__pyinstaller/utils.py index 8041e515c..ed9f788d0 100644 --- a/sdk/python/packages/flet/src/flet/__pyinstaller/utils.py +++ b/sdk/python/packages/flet/src/flet/__pyinstaller/utils.py @@ -4,11 +4,11 @@ import uuid from pathlib import Path +from flet.utils import get_package_bin_dir + def get_flet_bin_path(): - bin_path = os.path.abspath( - os.path.join(os.path.dirname(__file__), os.pardir, "bin") - ) + bin_path = get_package_bin_dir() if not os.path.exists(bin_path): return None return bin_path diff --git a/sdk/python/packages/flet/src/flet/cli/commands/publish.py b/sdk/python/packages/flet/src/flet/cli/commands/publish.py index 374357c54..a6b284ecf 100644 --- a/sdk/python/packages/flet/src/flet/cli/commands/publish.py +++ b/sdk/python/packages/flet/src/flet/cli/commands/publish.py @@ -7,7 +7,7 @@ from pathlib import Path from flet.cli.commands.base import BaseCommand -from flet.utils import is_within_directory +from flet.utils import get_package_web_dir, is_within_directory from flet_core.utils import random_string @@ -96,11 +96,11 @@ def handle(self, options: argparse.Namespace) -> None: Path(dist_dir).mkdir(parents=True, exist_ok=True) # copy "web" - web_path = Path(__file__).parent.parent.parent.joinpath("web") - if not web_path.exists(): + web_path = get_package_web_dir() + if not os.path.exists(web_path): print("Flet module does not contain 'web' directory.") exit(1) - shutil.copytree(str(web_path), dist_dir, dirs_exist_ok=True) + shutil.copytree(web_path, dist_dir, dirs_exist_ok=True) # copy assets assets_dir = options.assets_dir diff --git a/sdk/python/packages/flet/src/flet/flet.py b/sdk/python/packages/flet/src/flet/flet.py index 9b3c96b09..2e202f34a 100644 --- a/sdk/python/packages/flet/src/flet/flet.py +++ b/sdk/python/packages/flet/src/flet/flet.py @@ -21,6 +21,7 @@ get_arch, get_current_script_dir, get_free_tcp_port, + get_package_bin_dir, get_platform, is_linux, is_linux_server, @@ -404,9 +405,8 @@ def __start_flet_server( fletd_exe = "fletd.exe" if is_windows() else "fletd" # check if flet.exe exists in "bin" directory (user mode) - p = Path(__file__).parent.joinpath("bin", fletd_exe) - if p.exists(): - fletd_path = str(p) + fletd_path = os.path.join(get_package_bin_dir(), fletd_exe) + if os.path.exists(fletd_path): logging.info(f"Flet Server found in: {fletd_path}") else: # check if flet.exe is in PATH (flet developer mode) @@ -524,9 +524,8 @@ def __locate_and_unpack_flet_view(page_url, assets_dir, hidden): temp_flet_dir = Path.home().joinpath(".flet", "bin", f"flet-{version.version}") # check if flet_view.exe exists in "bin" directory (user mode) - p = Path(__file__).parent.joinpath("bin", "flet", flet_exe) - if p.exists(): - flet_path = str(p) + flet_path = os.path.join(get_package_bin_dir(), "flet", flet_exe) + if os.path.exists(flet_path): logging.info(f"Flet View found in: {flet_path}") else: # check if flet.exe is in FLET_VIEW_PATH (flet developer mode) @@ -558,8 +557,8 @@ def __locate_and_unpack_flet_view(page_url, assets_dir, hidden): if not temp_flet_dir.exists(): # check if flet.tar.gz exists gz_filename = "flet-macos-amd64.tar.gz" - tar_file = Path(__file__).parent.joinpath("bin", gz_filename) - if not tar_file.exists(): + tar_file = os.path.join(get_package_bin_dir(), gz_filename) + if not os.path.exists(tar_file): tar_file = __download_flet_client(gz_filename) logging.info(f"Extracting Flet.app from archive to {temp_flet_dir}") @@ -584,8 +583,8 @@ def __locate_and_unpack_flet_view(page_url, assets_dir, hidden): if not temp_flet_dir.exists(): # check if flet.tar.gz exists gz_filename = f"flet-linux-{get_arch()}.tar.gz" - tar_file = Path(__file__).parent.joinpath("bin", gz_filename) - if not tar_file.exists(): + tar_file = os.path.join(get_package_bin_dir(), gz_filename) + if not os.path.exists(tar_file): tar_file = __download_flet_client(gz_filename) logging.info(f"Extracting Flet from archive to {temp_flet_dir}") diff --git a/sdk/python/packages/flet/src/flet/utils.py b/sdk/python/packages/flet/src/flet/utils.py index 0138f6de9..c4486b71f 100644 --- a/sdk/python/packages/flet/src/flet/utils.py +++ b/sdk/python/packages/flet/src/flet/utils.py @@ -5,6 +5,7 @@ import sys import unicodedata import webbrowser +from pathlib import Path def is_windows(): @@ -106,6 +107,18 @@ def is_localhost_url(url): ) +def get_package_root_dir(): + return str(Path(__file__).parent) + + +def get_package_bin_dir(): + return os.path.join(get_package_root_dir(), "bin") + + +def get_package_web_dir(): + return os.path.join(get_package_root_dir(), "web") + + def get_free_tcp_port(): sock = socket.socket() sock.bind(("", 0)) From 2b1d005a13e684074c9ea28e3d49884cef026f14 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 23 Jan 2023 10:48:35 -0800 Subject: [PATCH 30/55] Remove embedded resources from fletd --- .appveyor.yml | 9 +-- .../flet-core/src/flet_core/control.py | 12 ++-- .../packages/flet-core/src/flet_core/page.py | 9 +++ sdk/python/packages/flet/build-wheels.py | 18 ------ sdk/python/packages/flet/download-web.py | 45 +++++++++++++ sdk/python/packages/flet/src/flet/flet.py | 16 +++-- server/commands/server.go | 44 ++----------- server/config/config.go | 5 -- server/server/server.go | 8 ++- server/server/static_assets.go | 22 +++---- server/server/static_assets_embed.go | 64 ------------------- server/server/static_assets_fs.go | 16 +++-- 12 files changed, 106 insertions(+), 162 deletions(-) create mode 100644 sdk/python/packages/flet/download-web.py delete mode 100644 server/server/static_assets_embed.go diff --git a/.appveyor.yml b/.appveyor.yml index 2e208339d..1acd0be68 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -377,13 +377,12 @@ for: # Flutter Web client - cd client - flutter build web --release - - rm -rf build/web/canvaskit + - rm -rf build/web/canvaskit/profiling - ls -alR build/web - cd .. + - tar -czvf flet-web.tar.gz -C client/build/web . # Flet Server in Go - - mkdir server/server/content - - cp -rf client/build/web/* server/server/content - cd server - sh: | if [[ "$APPVEYOR_REPO_TAG" == "true" ]]; then @@ -393,9 +392,6 @@ for: fi - cd .. - # package web build - - tar -czvf flet-web.tar.gz -C client/build/web . - artifacts: - path: server/dist/fletd-* - path: server/dist/fletd_*/* @@ -461,6 +457,7 @@ for: # build "flet" package - pushd packages/flet + - python3 download-web.py - poetry build - python3 build-wheels.py - popd diff --git a/sdk/python/packages/flet-core/src/flet_core/control.py b/sdk/python/packages/flet-core/src/flet_core/control.py index bea6870b7..0cf882a50 100644 --- a/sdk/python/packages/flet-core/src/flet_core/control.py +++ b/sdk/python/packages/flet-core/src/flet_core/control.py @@ -150,6 +150,12 @@ def _wrap_attr_dict(self, value): return value return {"": value} + def __str__(self): + attrs = {} + for k, v in self.__attrs.items(): + attrs[k] = v[0] + return "{} {}".format(self._get_control_name(), attrs) + # event_handlers @property def event_handlers(self): @@ -399,12 +405,6 @@ def _build_add_commands(self, indent=0, index=None, added_controls=None): command = self._build_command(False) command.indent = indent command.values.append(self._get_control_name()) - - if self.page: - raise Exception( - "Control has already been added to a page: {}".format(command) - ) - commands.append(command) if added_controls is not None: diff --git a/sdk/python/packages/flet-core/src/flet_core/page.py b/sdk/python/packages/flet-core/src/flet_core/page.py index d3e6fb1ba..1158c5dfa 100644 --- a/sdk/python/packages/flet-core/src/flet_core/page.py +++ b/sdk/python/packages/flet-core/src/flet_core/page.py @@ -358,12 +358,14 @@ async def _clean_async(self, control: Control): def __update(self, *controls) -> Tuple[List[Control], List[Control]]: commands, added_controls, removed_controls = self.__prepare_update(*controls) + self.__validate_controls_page(added_controls) results = self.__conn.send_commands(self._session_id, commands).results self.__update_control_ids(added_controls, results) return added_controls, removed_controls async def __update_async(self, *controls) -> Tuple[List[Control], List[Control]]: commands, added_controls, removed_controls = self.__prepare_update(*controls) + self.__validate_controls_page(added_controls) results = ( await self.__conn.send_commands_async(self._session_id, commands) ).results @@ -386,6 +388,13 @@ def __prepare_update(self, *controls): return commands, added_controls, removed_controls + def __validate_controls_page(self, added_controls): + for ctrl in added_controls: + if ctrl.page and ctrl.page != self: + raise Exception( + "Control has already been added to another page: {}".format(ctrl) + ) + def __update_control_ids(self, added_controls, results): if len(results) > 0: n = 0 diff --git a/sdk/python/packages/flet/build-wheels.py b/sdk/python/packages/flet/build-wheels.py index a6ee7c435..aba517201 100644 --- a/sdk/python/packages/flet/build-wheels.py +++ b/sdk/python/packages/flet/build-wheels.py @@ -13,7 +13,6 @@ from base64 import urlsafe_b64encode fletd_job_name = "Build Fletd" -flet_web_job_name = "Build Fletd" build_jobs = {} @@ -109,14 +108,6 @@ def download_flet_server(jobId, asset, exec_filename, dest_file): os.chmod(dest_file, st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH) -def download_flet_web(jobId, dest_file): - flet_web_url = ( - f"https://ci.appveyor.com/api/buildjobs/{jobId}/artifacts/flet-web.tar.gz" - ) - print(f"Downloading {flet_web_url}...") - urllib.request.urlretrieve(flet_web_url, dest_file) - - def download_artifact_by_name(jobId, artifact_name, dest_file): url = f"https://ci.appveyor.com/api/buildjobs/{jobId}/artifacts" print(f"Fetching build job artifacts at {url}") @@ -244,15 +235,6 @@ def rehash_record_lines(root_dir): zip_arch.extractall(bin_path) os.remove(client_arch_path) - # create "web" directory - web_path = unpacked_whl.joinpath("flet", "web") - web_path.mkdir(exist_ok=True) - web_tar_path = unpacked_whl.joinpath("flet-web.tar.gz") - download_flet_web(build_jobs[flet_web_job_name], web_tar_path) - with tarfile.open(web_tar_path, "r:gz") as tar: - tar.extractall(str(web_path)) - os.remove(web_tar_path) - # update WHEEL file for tag in package["wheel_tags"]: wheel_lines.append(f"Tag: {tag}\n") diff --git a/sdk/python/packages/flet/download-web.py b/sdk/python/packages/flet/download-web.py new file mode 100644 index 000000000..aee022b8a --- /dev/null +++ b/sdk/python/packages/flet/download-web.py @@ -0,0 +1,45 @@ +import json +import os +import pathlib +import tarfile +import urllib.request + +flet_web_job_name = "Build Fletd" + +build_jobs = {} + + +def download_flet_web(jobId, dest_file): + flet_web_url = ( + f"https://ci.appveyor.com/api/buildjobs/{jobId}/artifacts/flet-web.tar.gz" + ) + print(f"Downloading {flet_web_url}...") + urllib.request.urlretrieve(flet_web_url, dest_file) + + +def get_flet_server_job_ids(): + account_name = os.environ.get("APPVEYOR_ACCOUNT_NAME") + project_slug = os.environ.get("APPVEYOR_PROJECT_SLUG") + build_id = os.environ.get("APPVEYOR_BUILD_ID") + url = f"https://ci.appveyor.com/api/projects/{account_name}/{project_slug}/builds/{build_id}" + print(f"Fetching build details at {url}") + req = urllib.request.Request(url) + req.add_header("Content-type", "application/json") + project = json.loads(urllib.request.urlopen(req).read().decode()) + for job in project["build"]["jobs"]: + build_jobs[job["name"]] = job["jobId"] + + +current_dir = pathlib.Path(os.getcwd()) +print("current_dir", current_dir) + +get_flet_server_job_ids() + +# create "web" directory +web_path = current_dir.joinpath("src", "flet", "web") +web_path.mkdir(exist_ok=True) +web_tar_path = current_dir.joinpath("flet-web.tar.gz") +download_flet_web(build_jobs[flet_web_job_name], web_tar_path) +with tarfile.open(web_tar_path, "r:gz") as tar: + tar.extractall(str(web_path)) +os.remove(web_tar_path) diff --git a/sdk/python/packages/flet/src/flet/flet.py b/sdk/python/packages/flet/src/flet/flet.py index 2e202f34a..6d77d9005 100644 --- a/sdk/python/packages/flet/src/flet/flet.py +++ b/sdk/python/packages/flet/src/flet/flet.py @@ -22,6 +22,7 @@ get_current_script_dir, get_free_tcp_port, get_package_bin_dir, + get_package_web_dir, get_platform, is_linux, is_linux_server, @@ -419,9 +420,6 @@ def __start_flet_server( fletd_env = {**os.environ} - if assets_dir: - fletd_env["FLET_STATIC_ROOT_DIR"] = assets_dir - if upload_dir: if not Path(upload_dir).is_absolute(): upload_dir = str( @@ -445,7 +443,17 @@ def __start_flet_server( logging.info(f"Route URL strategy configured: {route_url_strategy}") fletd_env["FLET_ROUTE_URL_STRATEGY"] = route_url_strategy - args = [fletd_path, "--port", str(port)] + web_root_dir = os.environ.get("FLET_WEB_PATH") + if not web_root_dir: + web_root_dir = get_package_web_dir() + + if not os.path.exists(web_root_dir): + raise Exception("Web root path not found: {}".format(web_root_dir)) + + args = [fletd_path, "--content-dir", web_root_dir, "--port", str(port)] + + if assets_dir: + args.extend(["--assets-dir", assets_dir]) creationflags = 0 start_new_session = False diff --git a/server/commands/server.go b/server/commands/server.go index e95ec2e0a..feddbb9ef 100644 --- a/server/commands/server.go +++ b/server/commands/server.go @@ -2,19 +2,15 @@ package commands import ( "context" - "fmt" "net" "os" - "os/exec" "runtime" - "strconv" "sync" "time" "github.com/flet-dev/flet/server/cache" "github.com/flet-dev/flet/server/config" "github.com/flet-dev/flet/server/server" - "github.com/flet-dev/flet/server/utils" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -27,7 +23,8 @@ var ( func NewServerCommand(cancel context.CancelFunc) *cobra.Command { var serverPort int - var background bool + var contentDir string + var assetsDir string var attachedProcess bool var cmd = &cobra.Command{ @@ -47,11 +44,6 @@ func NewServerCommand(cancel context.CancelFunc) *cobra.Command { } } - if background { - startServerService(serverPort, attachedProcess) - return - } - if attachedProcess { go monitorParentProcess(cancel) } @@ -61,7 +53,7 @@ func NewServerCommand(cancel context.CancelFunc) *cobra.Command { waitGroup := sync.WaitGroup{} waitGroup.Add(1) - go server.Start(cmd.Context(), &waitGroup, serverPort) + go server.Start(cmd.Context(), &waitGroup, serverPort, contentDir, assetsDir) waitGroup.Wait() }, } @@ -71,7 +63,9 @@ func NewServerCommand(cancel context.CancelFunc) *cobra.Command { cmd.PersistentFlags().StringVarP(&LogLevel, "log-level", "l", "info", "verbosity level for logs") cmd.Flags().IntVarP(&serverPort, "port", "p", config.ServerPort(), "port on which the server will listen") - //cmd.Flags().BoolVarP(&background, "background", "b", false, "run server in background") + cmd.Flags().StringVarP(&contentDir, "content-dir", "", "", "path to web content directory") + cmd.MarkFlagRequired("content-dir") + cmd.Flags().StringVarP(&assetsDir, "assets-dir", "", "", "path to user assets directory") cmd.Flags().BoolVarP(&attachedProcess, "attached", "a", false, "attach background server process to the parent one") return cmd @@ -116,32 +110,6 @@ func monitorParentProcessWindows(cancel context.CancelFunc) { cancel() } -func startServerService(serverPort int, attached bool) { - log.Debugln("Starting Flet Server") - - // run server - execPath, _ := os.Executable() - - var cmd *exec.Cmd - args := []string{"server", "--port", strconv.Itoa(serverPort)} - if attached { - cmd = exec.Command(execPath, args...) - } else { - cmd = utils.GetDetachedCmd(execPath, args...) - } - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=true", config.LogToFileFlag)) - - err := cmd.Start() - - if err != nil { - log.Fatalln(err) - } - - log.Debugln("Server process started with PID:", cmd.Process.Pid) - fmt.Println(serverPort) -} - func getFreePort() (int, error) { addr, err := net.ResolveTCPAddr("tcp", "localhost:0") if err != nil { diff --git a/server/config/config.go b/server/config/config.go index f9e4bcbe7..41f073bf4 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -71,7 +71,6 @@ const ( defaultMasterSecretKey = "master_secret_key" // development - staticRootDir = "STATIC_ROOT_DIR" uploadRootDir = "UPLOAD_ROOT_DIR" webRenderer = "WEB_RENDERER" routeUrlStrategy = "ROUTE_URL_STRATEGY" @@ -287,10 +286,6 @@ func MasterSecretKey() string { // Development -func StaticRootDir() string { - return viper.GetString(staticRootDir) -} - func UploadRootDir() string { return viper.GetString(uploadRootDir) } diff --git a/server/server/server.go b/server/server/server.go index a12f40baf..072da0f84 100644 --- a/server/server/server.go +++ b/server/server/server.go @@ -37,9 +37,13 @@ var upgrader = websocket.Upgrader{ WriteBufferSize: 1024, } -func Start(ctx context.Context, wg *sync.WaitGroup, serverPort int) { +func Start(ctx context.Context, wg *sync.WaitGroup, serverPort int, contentDir string, assetsDir string) { defer wg.Done() + if contentDir == "" { + log.Fatalf("contentDir is not set") + } + Port = serverPort // Set the router as the default one shipped with Gin @@ -69,7 +73,7 @@ func Start(ctx context.Context, wg *sync.WaitGroup, serverPort int) { mime.AddExtensionType(".js", "application/javascript") // Serve frontend static files - assetsFS := newAssetsFS() + assetsFS := newAssetsFS(contentDir, assetsDir) router.Use(static.Serve("/", assetsFS)) // WebSockets diff --git a/server/server/static_assets.go b/server/server/static_assets.go index 0ea1bf1f3..8a2c99c25 100644 --- a/server/server/static_assets.go +++ b/server/server/static_assets.go @@ -7,36 +7,34 @@ import ( ) type AssetsFS struct { - embedAssets *EmbedAssetsSFS - fsAssets *FileSystemAssetsSFS + staticContent *FileSystemAssetsSFS + userAssets *FileSystemAssetsSFS } -func newAssetsFS() AssetsFS { - embedFs := newEmbedAssetsSFS() - fileSystemFs := newFileSystemAssetsSFS() +func newAssetsFS(contentDir string, assetsDir string) AssetsFS { return AssetsFS{ - embedAssets: embedFs, - fsAssets: fileSystemFs, + staticContent: newFileSystemAssetsSFS(contentDir), + userAssets: newFileSystemAssetsSFS(assetsDir), } } func (fs AssetsFS) Exists(prefix string, path string) bool { //log.Debugln("AssetsSFS Exists:", prefix, path) - if fs.fsAssets != nil && fs.fsAssets.Exists(prefix, path) { + if fs.userAssets != nil && fs.userAssets.Exists(prefix, path) { return true } - return fs.embedAssets.Exists(prefix, path) + return fs.staticContent.Exists(prefix, path) } func (fs AssetsFS) Open(name string) (file http.File, err error) { //log.Debugln("AssetsSFS Open:", name, fs.fsAssets) - if fs.fsAssets != nil { - file, err = fs.fsAssets.Open(name) + if fs.userAssets != nil { + file, err = fs.userAssets.Open(name) if err == nil { return } log.Debugln("FileSystemSFS error:", err) } - file, err = fs.embedAssets.Open(name) + file, err = fs.staticContent.Open(name) return } diff --git a/server/server/static_assets_embed.go b/server/server/static_assets_embed.go deleted file mode 100644 index 30bffbdd8..000000000 --- a/server/server/static_assets_embed.go +++ /dev/null @@ -1,64 +0,0 @@ -package server - -import ( - "embed" - "io/fs" - "net/http" - "strings" - - log "github.com/sirupsen/logrus" -) - -const ( - contentRootFolder string = "content/" -) - -//go:embed content/* -var f embed.FS - -type EmbedAssetsSFS struct { - files map[string]bool - prefix string - httpFS http.FileSystem -} - -func newEmbedAssetsSFS() *EmbedAssetsSFS { - files := make(map[string]bool) - fs.WalkDir(f, ".", func(path string, d fs.DirEntry, err error) error { - if !d.IsDir() { - files[strings.TrimPrefix(path, contentRootFolder)] = true - } - return nil - }) - - for k := range files { - log.Debugln("EmbedAssetsFS item:", k) - } - - return &EmbedAssetsSFS{ - files: files, - prefix: contentRootFolder, - httpFS: http.FS(f), - } -} - -func (fs *EmbedAssetsSFS) Exists(prefix string, path string) bool { - //log.Debugln("EmbedAssetsSFS Exists: ", prefix, path) - return fs.findCachedFileName(fs.files, path) != "" -} - -func (fs *EmbedAssetsSFS) Open(name string) (http.File, error) { - //log.Debugln("EmbedAssetsFS Open: ", name) - return fs.httpFS.Open(fs.prefix + fs.findCachedFileName(fs.files, name)) -} - -func (fs *EmbedAssetsSFS) findCachedFileName(files map[string]bool, path string) string { - pathParts := strings.Split(strings.TrimPrefix(path, "/"), "/") - for i := 0; i < len(pathParts); i++ { - partialPath := strings.Join(pathParts[i:], "/") - if _, exists := files[partialPath]; exists { - return partialPath - } - } - return "" -} diff --git a/server/server/static_assets_fs.go b/server/server/static_assets_fs.go index 9683cd5b4..f77eba8bb 100644 --- a/server/server/static_assets_fs.go +++ b/server/server/static_assets_fs.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - "github.com/flet-dev/flet/server/config" + "github.com/flet-dev/flet/server/utils" log "github.com/sirupsen/logrus" ) @@ -15,14 +15,12 @@ type FileSystemAssetsSFS struct { httpFS http.FileSystem } -func newFileSystemAssetsSFS() *FileSystemAssetsSFS { - rootWebDir := config.StaticRootDir() - +func newFileSystemAssetsSFS(rootWebDir string) *FileSystemAssetsSFS { if rootWebDir == "" { - log.Debugln("Variable FLET_STATIC_ROOT_DIR with path to web static content is not set.") + log.Debugln("Directory with web content is not set.") return nil } else if _, err := os.Stat(rootWebDir); os.IsNotExist(err) { - log.Warnf("Directory %s with web static content does not exist.", rootWebDir) + log.Warnf("Directory %s with web content does not exist.", rootWebDir) return nil } @@ -48,7 +46,11 @@ func (fs *FileSystemAssetsSFS) findFullPath(path string) string { pathParts := strings.Split(strings.TrimPrefix(path, "/"), "/") for i := 0; i < len(pathParts); i++ { partialPath := strings.Join(pathParts[i:], "/") - if _, err := os.Stat(filepath.Join(fs.rootWebDir, partialPath)); err == nil { + fullPath := filepath.Clean(filepath.Join(fs.rootWebDir, partialPath)) + if err := utils.InTrustedRoot(fullPath, fs.rootWebDir); err != nil { + return "" + } + if _, err := os.Stat(fullPath); err == nil { return partialPath } } From 4ab196bb02fe9f7222dbae6c7fc8a0f52ed8463f Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 23 Jan 2023 11:42:46 -0800 Subject: [PATCH 31/55] Include "web" into source package --- sdk/python/packages/flet/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 sdk/python/packages/flet/.gitignore diff --git a/sdk/python/packages/flet/.gitignore b/sdk/python/packages/flet/.gitignore deleted file mode 100644 index ef9119282..000000000 --- a/sdk/python/packages/flet/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src/flet/web/ \ No newline at end of file From d67ac4aaa20837586dc2383f474d8e4847623bfd Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 23 Jan 2023 12:57:44 -0800 Subject: [PATCH 32/55] Fix paths to web resources --- client/web/python-worker.js | 2 +- client/web/python.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/web/python-worker.js b/client/web/python-worker.js index 599d1facf..5025bcd5c 100644 --- a/client/web/python-worker.js +++ b/client/web/python-worker.js @@ -15,7 +15,7 @@ self.initPyodide = async function () { import micropip import os from pyodide.http import pyfetch - response = await pyfetch("/app.tar.gz") + response = await pyfetch("app.tar.gz") await response.unpack_archive() if os.path.exists("requirements.txt"): with open("requirements.txt", "r") as f: diff --git a/client/web/python.js b/client/web/python.js index 2d158d65b..21814aa8e 100644 --- a/client/web/python.js +++ b/client/web/python.js @@ -1,4 +1,4 @@ -const pythonWorker = new Worker("/python-worker.js"); +const pythonWorker = new Worker("python-worker.js"); let _onPythonInitialized = null; let pythonInitialized = new Promise((onSuccess) => _onPythonInitialized = onSuccess); From 64dfc7f5d7748f8bf3a0d272d6e950c9ede939ff Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 23 Jan 2023 21:04:39 -0800 Subject: [PATCH 33/55] Remove entire web/canvaskit --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 1acd0be68..456ac3ea7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -377,7 +377,7 @@ for: # Flutter Web client - cd client - flutter build web --release - - rm -rf build/web/canvaskit/profiling + - rm -rf build/web/canvaskit - ls -alR build/web - cd .. - tar -czvf flet-web.tar.gz -C client/build/web . From 0e3ebd882dadbbdc5fe70b1204505d0c53352fa7 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Tue, 24 Jan 2023 12:46:17 -0800 Subject: [PATCH 34/55] Loading animation changed to SVG --- client/web/icons/loading-animation.png | Bin 17040 -> 0 bytes client/web/icons/loading-animation.svg | 5 +++++ client/web/index.html | 8 +++++--- media/logo/flet-logo-no-text.svg | 5 +++++ 4 files changed, 15 insertions(+), 3 deletions(-) delete mode 100644 client/web/icons/loading-animation.png create mode 100644 client/web/icons/loading-animation.svg create mode 100644 media/logo/flet-logo-no-text.svg diff --git a/client/web/icons/loading-animation.png b/client/web/icons/loading-animation.png deleted file mode 100644 index e97cd02bc0d401f4b467c0bd51acd74a2559e445..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17040 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 V%ix<2Jp}%HsH~w>de1E6e*gkW073u& diff --git a/client/web/icons/loading-animation.svg b/client/web/icons/loading-animation.svg new file mode 100644 index 000000000..e9c0bb905 --- /dev/null +++ b/client/web/icons/loading-animation.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/client/web/index.html b/client/web/index.html index 28688134f..ea51002fb 100644 --- a/client/web/index.html +++ b/client/web/index.html @@ -72,11 +72,13 @@ @keyframes breathe { from { - transform: scale(1) + transform: scale(1); + opacity: 1.0; } to { - transform: scale(0.95) + transform: scale(0.95); + opacity: .7; } } @@ -90,7 +92,7 @@ } } - Loading indicator... + Loading indicator...