diff --git a/.appveyor.yml b/.appveyor.yml index f7e5c8a04..6b7454118 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -398,7 +398,6 @@ for: install: - python --version - cd sdk/python - - pip install certifi==2022.9.24 - pip install pdm - pdm install @@ -417,7 +416,6 @@ for: install: - python --version - cd sdk/python - - pip install certifi==2022.9.24 - pip install --upgrade setuptools wheel twine pdm - pdm install diff --git a/sdk/python/flet/__pyinstaller/config.py b/sdk/python/flet/__pyinstaller/config.py new file mode 100644 index 000000000..9f07382dc --- /dev/null +++ b/sdk/python/flet/__pyinstaller/config.py @@ -0,0 +1 @@ +temp_bin_dir = None diff --git a/sdk/python/flet/__pyinstaller/hook-flet.py b/sdk/python/flet/__pyinstaller/hook-flet.py index cbb2276e7..d5d8961d9 100644 --- a/sdk/python/flet/__pyinstaller/hook-flet.py +++ b/sdk/python/flet/__pyinstaller/hook-flet.py @@ -1,10 +1,15 @@ import os -# package entire "bin" folder -bin_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, "bin")) +import flet.__pyinstaller.config as hook_config +from flet.__pyinstaller.utils import get_flet_bin_path -# package "bin/fletd" only -if os.getenv("PACKAGE_FLETD_ONLY"): - bin_path = os.path.join(bin_path, "fletd*") +bin_path = hook_config.temp_bin_dir +if not bin_path: + bin_path = get_flet_bin_path() -datas = [(bin_path, "flet/bin")] +if bin_path: + # package "bin/fletd" only + if os.getenv("PACKAGE_FLETD_ONLY"): + bin_path = os.path.join(bin_path, "fletd*") + + datas = [(bin_path, "flet/bin")] diff --git a/sdk/python/flet/__pyinstaller/macos_utils.py b/sdk/python/flet/__pyinstaller/macos_utils.py new file mode 100644 index 000000000..57d7fce27 --- /dev/null +++ b/sdk/python/flet/__pyinstaller/macos_utils.py @@ -0,0 +1,122 @@ +import os +import plistlib +import shutil +import subprocess +import tarfile +from pathlib import Path + +from PyInstaller.building.icon import normalize_icon_type + +from flet.utils import safe_tar_extractall + + +def unpack_app_bundle(tar_path): + bin_dir = str(Path(tar_path).parent) + + with tarfile.open(tar_path, "r:gz") as tar_arch: + safe_tar_extractall(tar_arch, bin_dir) + os.remove(tar_path) + + return os.path.join(bin_dir, "Flet.app") + + +def update_flet_view_icon(app_path, icon_path): + print("Updating Flet View icon", app_path, icon_path) + + icon_file = "AppIcon.icns" + + # normalize icon + normalized_icon_path = normalize_icon_type( + icon_path, ("icns",), "icns", os.getcwd() + ) + + # patch icon + print("Copying icons from", normalized_icon_path) + shutil.copy( + normalized_icon_path, + os.path.join(app_path, "Contents", "Resources", icon_file), + ) + + # update icon file name + pl = __load_info_plist(app_path) + pl["CFBundleIconFile"] = icon_file + del pl["CFBundleIconName"] + __save_info_plist(app_path, pl) + + +def update_flet_view_version_info( + app_path, + bundle_id, + product_name, + product_version, + copyright, +): + print("Updating Flet View plist", app_path) + + pl = __load_info_plist(app_path) + + if bundle_id: + pl["CFBundleIdentifier"] = bundle_id + if product_name: + pl["CFBundleName"] = product_name + pl["CFBundleDisplayName"] = product_name + + # rename app bundle + new_app_path = os.path.join(Path(app_path).parent, f"{product_name}.app") + os.rename(app_path, new_app_path) + app_path = new_app_path + if product_version: + pl["CFBundleShortVersionString"] = product_version + if copyright: + pl["NSHumanReadableCopyright"] = copyright + + __save_info_plist(app_path, pl) + + return app_path + + +def assemble_app_bundle(app_path, tar_path): + + # sign app bundle + print(f"Signing file {app_path}") + cmd_args = [ + "codesign", + "-s", + "-", + "--force", + "--all-architectures", + "--timestamp", + "--deep", + app_path, + ] + p = subprocess.run( + cmd_args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) + if p.returncode: + raise SystemError( + f"codesign command ({cmd_args}) failed with error code {p.returncode}!\noutput: {p.stdout}" + ) + + # pack tar + with tarfile.open(tar_path, "w:gz") as tar: + tar.add(app_path, arcname=os.path.basename(app_path)) + + # cleanup + shutil.rmtree(app_path, ignore_errors=True) + + +def __load_info_plist(app_path): + with open(__get_plist_path(app_path), "rb") as fp: + return plistlib.load(fp) + + +def __save_info_plist(app_path, pl): + with open(__get_plist_path(app_path), "wb") as fp: + plistlib.dump(pl, fp) + + +def __get_plist_path(app_path): + return os.path.join(app_path, "Contents", "Info.plist") diff --git a/sdk/python/flet/__pyinstaller/utils.py b/sdk/python/flet/__pyinstaller/utils.py new file mode 100644 index 000000000..8041e515c --- /dev/null +++ b/sdk/python/flet/__pyinstaller/utils.py @@ -0,0 +1,25 @@ +import os +import shutil +import tempfile +import uuid +from pathlib import Path + + +def get_flet_bin_path(): + bin_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir, "bin") + ) + if not os.path.exists(bin_path): + return None + return bin_path + + +def copy_flet_bin(): + bin_path = get_flet_bin_path() + if not bin_path: + return None + + # create temp bin dir + temp_bin_dir = Path(tempfile.gettempdir()).joinpath(str(uuid.uuid4())) + shutil.copytree(bin_path, str(temp_bin_dir)) + return str(temp_bin_dir) diff --git a/sdk/python/flet/__pyinstaller/win_utils.py b/sdk/python/flet/__pyinstaller/win_utils.py new file mode 100644 index 000000000..8157fb505 --- /dev/null +++ b/sdk/python/flet/__pyinstaller/win_utils.py @@ -0,0 +1,106 @@ +import os +import tempfile +import uuid +from pathlib import Path + +import packaging.version as version +import pefile +from PyInstaller.building.icon import normalize_icon_type +from PyInstaller.compat import win32api +from PyInstaller.utils.win32.icon import IconFile, normalize_icon_type +from PyInstaller.utils.win32.versioninfo import decode + + +def update_flet_view_icon(exe_path, icon_path): + print("Updating Flet View icon", exe_path, icon_path) + + RT_ICON = 3 + RT_GROUP_ICON = 14 + + normalized_icon_path = normalize_icon_type( + icon_path, ("exe", "ico"), "ico", os.getcwd() + ) + icon = IconFile(normalized_icon_path) + print("Copying icons from", normalized_icon_path) + + hdst = win32api.BeginUpdateResource(exe_path, 0) + + iconid = 1 + # Each step in the following enumerate() will instantiate an IconFile object, as a result of deferred execution + # of the map() above. + i = 101 + data = icon.grp_icon_dir() + data = data + icon.grp_icondir_entries(iconid) + win32api.UpdateResource(hdst, RT_GROUP_ICON, i, data, 1033) + print("Writing RT_GROUP_ICON %d resource with %d bytes", i, len(data)) + for data in icon.images: + win32api.UpdateResource(hdst, RT_ICON, iconid, data, 1033) + print("Writing RT_ICON %d resource with %d bytes", iconid, len(data)) + iconid = iconid + 1 + + win32api.EndUpdateResource(hdst, 0) + + +def update_flet_view_version_info( + exe_path, + product_name, + file_description, + product_version, + file_version, + company_name, + copyright, +): + print("Updating Flet View version info", exe_path) + + # load versioninfo from exe + vs = decode(exe_path) + + # update file version + if file_version: + pv = version.parse(file_version) + filevers = (pv.major, pv.minor, pv.micro, 0) + vs.ffi.fileVersionMS = (filevers[0] << 16) | (filevers[1] & 0xFFFF) + vs.ffi.fileVersionLS = (filevers[2] << 16) | (filevers[3] & 0xFFFF) + + # update string props + for k in vs.kids[0].kids[0].kids: + if k.name == "ProductName": + k.val = product_name if product_name else "" + elif k.name == "FileDescription": + k.val = file_description if file_description else "" + if k.name == "ProductVersion": + k.val = product_version if product_version else "" + if k.name == "FileVersion" and file_version: + k.val = file_version if file_version else "" + if k.name == "CompanyName": + k.val = company_name if company_name else "" + if k.name == "LegalCopyright": + k.val = copyright if copyright else "" + + version_info_path = str(Path(tempfile.gettempdir()).joinpath(str(uuid.uuid4()))) + with open(version_info_path, "w") as f: + f.write(str(vs)) + + # Remember overlay + pe = pefile.PE(exe_path, fast_load=True) + overlay_before = pe.get_overlay() + pe.close() + + hdst = win32api.BeginUpdateResource(exe_path, 0) + win32api.UpdateResource( + hdst, pefile.RESOURCE_TYPE["RT_VERSION"], 1, vs.toRaw(), 1033 + ) + win32api.EndUpdateResource(hdst, 0) + + if overlay_before: + # Check if the overlay is still present + pe = pefile.PE(exe_path, fast_load=True) + overlay_after = pe.get_overlay() + pe.close() + + # If the update removed the overlay data, re-append it + if not overlay_after: + with open(exe_path, "ab") as exef: + exef.write(overlay_before) + + return version_info_path diff --git a/sdk/python/flet/cli/cli.py b/sdk/python/flet/cli/cli.py index c1cfa8c15..d1dbd63ef 100644 --- a/sdk/python/flet/cli/cli.py +++ b/sdk/python/flet/cli/cli.py @@ -1,8 +1,10 @@ import argparse import sys -import flet.version + +import flet.cli.commands.pack import flet.cli.commands.run -import flet.cli.commands.build +import flet.version + # Source https://stackoverflow.com/a/26379693 def set_default_subparser(self, name, args=None, positional_args=0): @@ -60,7 +62,7 @@ def main(): # sp.default = "run" flet.cli.commands.run.Command.register_to(sp, "run") - flet.cli.commands.build.Command.register_to(sp, "build") + flet.cli.commands.pack.Command.register_to(sp, "pack") parser.set_default_subparser("run", positional_args=1) # print usage if called without args diff --git a/sdk/python/flet/cli/commands/build.py b/sdk/python/flet/cli/commands/build.py deleted file mode 100644 index 7d896d353..000000000 --- a/sdk/python/flet/cli/commands/build.py +++ /dev/null @@ -1,23 +0,0 @@ -import argparse -from flet.cli.commands.base import BaseCommand - - -class Command(BaseCommand): - """ - Package Flet app to a standalone bundle - """ - - def add_arguments(self, parser: argparse.ArgumentParser) -> None: - parser.add_argument("script", type=str, help="path to a Python script") - parser.add_argument( - "-F", - "--onefile", - dest="onefile", - action="store_true", - default=False, - help="create a one-file bundled executable", - ) - - def handle(self, options: argparse.Namespace) -> None: - # print("BUILD COMMAND", options) - raise NotImplementedError("Build command is not implemented yet.") diff --git a/sdk/python/flet/cli/commands/pack.py b/sdk/python/flet/cli/commands/pack.py new file mode 100644 index 000000000..5e36a2d4d --- /dev/null +++ b/sdk/python/flet/cli/commands/pack.py @@ -0,0 +1,179 @@ +import argparse +import os +import shutil +from pathlib import Path + +import flet.__pyinstaller.config as hook_config +from flet.cli.commands.base import BaseCommand +from flet.utils import is_macos, is_windows + + +class Command(BaseCommand): + """ + Package Flet app to a standalone bundle + """ + + def add_arguments(self, parser: argparse.ArgumentParser) -> None: + parser.add_argument("script", type=str, help="path to a Python script") + parser.add_argument( + "-i", + "--icon", + dest="icon", + help="path to an icon file (.ico, .png, .icns)", + ) + parser.add_argument( + "-n", + "--name", + dest="name", + help="name for the generated executable (Windows) or app bundle (macOS)", + ) + parser.add_argument( + "--add-data", + dest="add_data", + help="additional non-binary files or folders to be added to the executable", + ) + parser.add_argument( + "--product-name", + dest="product_name", + help="executable product name (Windows) or bundle name (macOS)", + ) + parser.add_argument( + "--file-description", + dest="file_description", + help="executable file description (Windows)", + ) + parser.add_argument( + "--product-version", + dest="product_version", + help="executable product version (Windows) or bundle version (macOS)", + ) + parser.add_argument( + "--file-version", + dest="file_version", + help="executable file version, n.n.n.n (Windows)", + ) + parser.add_argument( + "--company-name", + dest="company_name", + help="executable company name (Windows)", + ) + parser.add_argument( + "--copyright", + dest="copyright", + help="executable (Windows) or bundle (macOS) copyright", + ) + parser.add_argument( + "--bundle-id", + dest="bundle_id", + help="bundle identifier (macOS)", + ) + + def handle(self, options: argparse.Namespace) -> None: + + # delete "build" directory + build_dir = Path(os.getcwd()).joinpath("build") + if build_dir.exists(): + shutil.rmtree(str(build_dir), ignore_errors=True) + + # delete "dist" directory + dist_dir = Path(os.getcwd()).joinpath("dist") + if dist_dir.exists(): + shutil.rmtree(str(dist_dir), ignore_errors=True) + + try: + import PyInstaller.__main__ + + from flet.__pyinstaller.utils import copy_flet_bin + + pyi_args = [options.script, "--noconsole", "--noconfirm", "--onefile"] + if options.icon: + pyi_args.extend(["--icon", options.icon]) + if options.name: + pyi_args.extend(["--name", options.name]) + if options.add_data: + pyi_args.extend(["--add-data", options.add_data]) + if options.bundle_id: + pyi_args.extend(["--osx-bundle-identifier", options.bundle_id]) + + # copy "bin" + hook_config.temp_bin_dir = copy_flet_bin() + + if hook_config.temp_bin_dir is not None: + if is_windows(): + + from flet.__pyinstaller.win_utils import ( + update_flet_view_icon, + update_flet_view_version_info, + ) + + exe_path = Path(hook_config.temp_bin_dir).joinpath( + "flet", "flet.exe" + ) + if os.path.exists(exe_path): + # icon + if options.icon: + icon_path = options.icon + if not Path(icon_path).is_absolute(): + icon_path = Path(os.getcwd()).joinpath(icon_path) + update_flet_view_icon(str(exe_path), icon_path) + + # version info + version_info_path = update_flet_view_version_info( + exe_path=exe_path, + product_name=options.product_name, + file_description=options.file_description, + product_version=options.product_version, + file_version=options.file_version, + company_name=options.company_name, + copyright=options.copyright, + ) + + pyi_args.extend(["--version-file", version_info_path]) + + elif is_macos(): + from flet.__pyinstaller.macos_utils import ( + assemble_app_bundle, + unpack_app_bundle, + update_flet_view_icon, + update_flet_view_version_info, + ) + + tar_path = Path(hook_config.temp_bin_dir).joinpath( + "flet-macos-amd64.tar.gz" + ) + if tar_path.exists(): + + # unpack + app_path = unpack_app_bundle(tar_path) + + # icon + if options.icon: + icon_path = options.icon + if not Path(icon_path).is_absolute(): + icon_path = Path(os.getcwd()).joinpath(icon_path) + update_flet_view_icon(app_path, icon_path) + + # version info + app_path = update_flet_view_version_info( + app_path=app_path, + bundle_id=options.bundle_id, + product_name=options.product_name, + product_version=options.product_version, + copyright=options.copyright, + ) + + # assemble + assemble_app_bundle(app_path, tar_path) + + # run PyInstaller! + PyInstaller.__main__.run(pyi_args) + + # cleanup + if hook_config.temp_bin_dir is not None and os.path.exists( + hook_config.temp_bin_dir + ): + print("Deleting temp directory:", hook_config.temp_bin_dir) + shutil.rmtree(hook_config.temp_bin_dir, ignore_errors=True) + except ImportError: + print("Please install PyInstaller module to use flet package command.") + exit(1) diff --git a/sdk/python/flet/flet.py b/sdk/python/flet/flet.py index 65f93e64f..dd1b92748 100644 --- a/sdk/python/flet/flet.py +++ b/sdk/python/flet/flet.py @@ -422,7 +422,12 @@ def open_flet_view(page_url, hidden): else: logging.info(f"Flet View found in: {temp_flet_dir}") - app_path = temp_flet_dir.joinpath("Flet.app") + app_name = None + for f in os.listdir(temp_flet_dir): + if f.endswith(".app"): + app_name = f + assert app_name is not None, f"Application bundle not found in {temp_flet_dir}" + app_path = temp_flet_dir.joinpath(app_name) args = ["open", str(app_path), "-n", "-W", "--args", page_url] elif is_linux(): # build version-specific path to flet folder diff --git a/sdk/python/pdm.lock b/sdk/python/pdm.lock index 1602ada5d..e8b2ca1c3 100644 --- a/sdk/python/pdm.lock +++ b/sdk/python/pdm.lock @@ -12,7 +12,7 @@ summary = "Unbearably fast runtime type checking in pure Python." [[package]] name = "certifi" -version = "2022.9.24" +version = "2022.12.7" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." @@ -41,19 +41,19 @@ summary = "Distribution utilities" [[package]] name = "exceptiongroup" -version = "1.0.0" +version = "1.0.4" requires_python = ">=3.7" summary = "Backport of PEP 654 (exception groups)" [[package]] name = "filelock" -version = "3.8.0" +version = "3.8.2" requires_python = ">=3.7" summary = "A platform independent file lock." [[package]] name = "identify" -version = "2.5.8" +version = "2.5.10" requires_python = ">=3.7" summary = "File identification library for Python" @@ -65,7 +65,7 @@ summary = "Internationalized Domain Names in Applications (IDNA)" [[package]] name = "importlib-metadata" -version = "5.0.0" +version = "5.1.0" requires_python = ">=3.7" summary = "Read metadata from Python packages" dependencies = [ @@ -95,18 +95,15 @@ summary = "A generic, spec-compliant, thorough implementation of the OAuth reque [[package]] name = "packaging" -version = "21.3" -requires_python = ">=3.6" +version = "22.0" +requires_python = ">=3.7" summary = "Core utilities for Python packages" -dependencies = [ - "pyparsing!=3.0.5,>=2.0.2", -] [[package]] name = "platformdirs" -version = "2.5.2" +version = "2.6.0" requires_python = ">=3.7" -summary = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." [[package]] name = "pluggy" @@ -132,12 +129,6 @@ dependencies = [ "virtualenv>=20.0.8", ] -[[package]] -name = "pyparsing" -version = "3.0.9" -requires_python = ">=3.6.8" -summary = "pyparsing module - Classes and methods to define and execute parsing grammars" - [[package]] name = "pytest" version = "7.2.0" @@ -182,7 +173,7 @@ dependencies = [ [[package]] name = "setuptools" -version = "65.5.0" +version = "65.6.3" requires_python = ">=3.7" summary = "Easily download, build, install, upgrade, and uninstall Python packages" @@ -212,13 +203,13 @@ summary = "Backported and Experimental Type Hints for Python 3.7+" [[package]] name = "urllib3" -version = "1.26.12" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +version = "1.26.13" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" summary = "HTTP library with thread-safe connection pooling, file post, and more." [[package]] name = "virtualenv" -version = "20.16.6" +version = "20.17.1" requires_python = ">=3.6" summary = "Virtual Python Environment builder" dependencies = [ @@ -242,7 +233,7 @@ summary = "WebSocket client for Python with low level API options" [[package]] name = "zipp" -version = "3.10.0" +version = "3.11.0" requires_python = ">=3.7" summary = "Backport of pathlib-compatible object wrapper for zip files" @@ -259,9 +250,9 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {url = "https://files.pythonhosted.org/packages/b3/7c/0c6451ff5477cf6f3ac5101cafd084e43e3a29242d28f2056cad76ff03e9/beartype-0.11.0.tar.gz", hash = "sha256:3854b50eaaa98bb89490be57e73c69c777a0f304574e7043ac7da98ac6a735a6"}, {url = "https://files.pythonhosted.org/packages/ca/e7/bd684b9b0d5f8db8bedb7e031940df872aa79b42825e26469bc36013536f/beartype-0.11.0-py3-none-any.whl", hash = "sha256:d1ed5f97edebe909385190fac95ef9af3daf233eb1a5cd08b88b0d8260708ad7"}, ] -"certifi 2022.9.24" = [ - {url = "https://files.pythonhosted.org/packages/1d/38/fa96a426e0c0e68aabc68e896584b83ad1eec779265a028e156ce509630e/certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, - {url = "https://files.pythonhosted.org/packages/cb/a4/7de7cd59e429bd0ee6521ba58a75adaec136d32f91a761b28a11d8088d44/certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, +"certifi 2022.12.7" = [ + {url = "https://files.pythonhosted.org/packages/37/f7/2b1b0ec44fdc30a3d31dfebe52226be9ddc40cd6c0f34ffc8923ba423b69/certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, + {url = "https://files.pythonhosted.org/packages/71/4c/3db2b8021bd6f2f0ceb0e088d6b2d49147671f25832fb17970e9b583d742/certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, ] "cfgv 3.3.1" = [ {url = "https://files.pythonhosted.org/packages/6d/82/0a0ebd35bae9981dea55c06f8e6aaf44a49171ad798795c72c6f64cba4c2/cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, @@ -279,25 +270,25 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {url = "https://files.pythonhosted.org/packages/58/07/815476ae605bcc5f95c87a62b95e74a1bce0878bc7a3119bc2bf4178f175/distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, {url = "https://files.pythonhosted.org/packages/76/cb/6bbd2b10170ed991cf64e8c8b85e01f2fb38f95d1bc77617569e0b0b26ac/distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, ] -"exceptiongroup 1.0.0" = [ - {url = "https://files.pythonhosted.org/packages/4f/7e/eca80dbb5c10ec12ffb50884f52ecbff653cb5f443b0529244a748f114da/exceptiongroup-1.0.0.tar.gz", hash = "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad"}, - {url = "https://files.pythonhosted.org/packages/de/4c/2854d9e3dcce78108126aa3634186a64ddbfd06d4d69af74954665529e52/exceptiongroup-1.0.0-py3-none-any.whl", hash = "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41"}, +"exceptiongroup 1.0.4" = [ + {url = "https://files.pythonhosted.org/packages/ce/2e/9a327cc0d2d674ee2d570ee30119755af772094edba86d721dda94404d1a/exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, + {url = "https://files.pythonhosted.org/packages/fa/e1/4f89a2608978a78876a76e69e82fa1fc5ce3fb346297eed2a51dd3df2dcf/exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, ] -"filelock 3.8.0" = [ - {url = "https://files.pythonhosted.org/packages/94/b3/ff2845971788613e646e667043fdb5f128e2e540aefa09a3c55be8290d6d/filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, - {url = "https://files.pythonhosted.org/packages/95/55/b897882bffb8213456363e646bf9e9fa704ffda5a7d140edf935a9e02c7b/filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, +"filelock 3.8.2" = [ + {url = "https://files.pythonhosted.org/packages/7c/37/4fed6f28d8010c791437b808a94f37c4ae58ee3998b653d2c0286a8cc190/filelock-3.8.2-py3-none-any.whl", hash = "sha256:8df285554452285f79c035efb0c861eb33a4bcfa5b7a137016e32e6a90f9792c"}, + {url = "https://files.pythonhosted.org/packages/d8/73/292d9ea2370840a163e6dd2d2816a571244e9335e2f6ad957bf0527c492f/filelock-3.8.2.tar.gz", hash = "sha256:7565f628ea56bfcd8e54e42bdc55da899c85c1abfe1b5bcfd147e9188cebb3b2"}, ] -"identify 2.5.8" = [ - {url = "https://files.pythonhosted.org/packages/1b/66/c826b47a47d805f09092b8f5a4dd241aebc50bb3b190b9e2e3d44e3f6f55/identify-2.5.8-py2.py3-none-any.whl", hash = "sha256:48b7925fe122720088aeb7a6c34f17b27e706b72c61070f27fe3789094233440"}, - {url = "https://files.pythonhosted.org/packages/67/e1/869d7b8df41a3ac2a3c74a2a4ba401df468044dccc489b8937aad40d148e/identify-2.5.8.tar.gz", hash = "sha256:7a214a10313b9489a0d61467db2856ae8d0b8306fc923e03a9effa53d8aedc58"}, +"identify 2.5.10" = [ + {url = "https://files.pythonhosted.org/packages/24/7f/6812dcfa4ff7f8bd425252eb2bd4ef394477eea1424d3e09bbbfdac94e87/identify-2.5.10.tar.gz", hash = "sha256:dce9e31fee7dbc45fea36a9e855c316b8fbf807e65a862f160840bb5a2bf5dfd"}, + {url = "https://files.pythonhosted.org/packages/3e/63/88e087dc7fbe81d16225bdff91fd781bf7cb03101ffc1adfa6b909e36274/identify-2.5.10-py2.py3-none-any.whl", hash = "sha256:fb7c2feaeca6976a3ffa31ec3236a6911fbc51aec9acc111de2aed99f244ade2"}, ] "idna 3.4" = [ {url = "https://files.pythonhosted.org/packages/8b/e1/43beb3d38dba6cb420cefa297822eac205a277ab43e5ba5d5c46faf96438/idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, {url = "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, ] -"importlib-metadata 5.0.0" = [ - {url = "https://files.pythonhosted.org/packages/7e/ec/97f2ce958b62961fddd7258e0ceede844953606ad09b672fa03b86c453d3/importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"}, - {url = "https://files.pythonhosted.org/packages/b5/64/ef29a63cf08f047bb7fb22ab0f1f774b87eed0bb46d067a5a524798a4af8/importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"}, +"importlib-metadata 5.1.0" = [ + {url = "https://files.pythonhosted.org/packages/32/5a/e0d75c8010295ae6746f379f5324bc726076dfc426548bfa6f0763fce870/importlib_metadata-5.1.0.tar.gz", hash = "sha256:d5059f9f1e8e41f80e9c56c2ee58811450c31984dfa625329ffd7c0dad88a73b"}, + {url = "https://files.pythonhosted.org/packages/e1/16/1f59f5d87d256012e9cdf0e8af8810965fa253e835cfecce64f4b11d4f2d/importlib_metadata-5.1.0-py3-none-any.whl", hash = "sha256:d84d17e21670ec07990e1044a99efe8d615d860fd176fc29ef5c306068fda313"}, ] "iniconfig 1.1.1" = [ {url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, @@ -311,13 +302,13 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, {url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, ] -"packaging 21.3" = [ - {url = "https://files.pythonhosted.org/packages/05/8e/8de486cbd03baba4deef4142bd643a3e7bbe954a784dc1bb17142572d127/packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {url = "https://files.pythonhosted.org/packages/df/9e/d1a7217f69310c1db8fdf8ab396229f55a699ce34a203691794c5d1cad0c/packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +"packaging 22.0" = [ + {url = "https://files.pythonhosted.org/packages/6b/f7/c240d7654ddd2d2f3f328d8468d4f1f876865f6b9038b146bec0a6737c65/packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"}, + {url = "https://files.pythonhosted.org/packages/8f/7b/42582927d281d7cb035609cd3a543ffac89b74f3f4ee8e1c50914bcb57eb/packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"}, ] -"platformdirs 2.5.2" = [ - {url = "https://files.pythonhosted.org/packages/ed/22/967181c94c3a4063fe64e15331b4cb366bdd7dfbf46fcb8ad89650026fec/platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {url = "https://files.pythonhosted.org/packages/ff/7b/3613df51e6afbf2306fc2465671c03390229b55e3ef3ab9dd3f846a53be6/platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +"platformdirs 2.6.0" = [ + {url = "https://files.pythonhosted.org/packages/87/69/cd019a9473bcdfb38983e2d550ccb239264fc4c2fc32c42ac1b1cc2506b6/platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"}, + {url = "https://files.pythonhosted.org/packages/ec/4c/9af851448e55c57b30a13a72580306e628c3b431d97fdae9e0b8d4fa3685/platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"}, ] "pluggy 1.0.0" = [ {url = "https://files.pythonhosted.org/packages/9e/01/f38e2ff29715251cf25532b9082a1589ab7e4f571ced434f98d0139336dc/pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, @@ -327,10 +318,6 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {url = "https://files.pythonhosted.org/packages/1e/ba/8cf8b88d0e07588818de46877effc9971305541d9421bc6377b06639d135/pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, {url = "https://files.pythonhosted.org/packages/b2/6c/9ccb5213a3d9fd3f8c0fd69d207951901eaef86b7a1a69bcc478364d3072/pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, ] -"pyparsing 3.0.9" = [ - {url = "https://files.pythonhosted.org/packages/6c/10/a7d0fa5baea8fe7b50f448ab742f26f52b80bfca85ac2be9d35cdd9a3246/pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {url = "https://files.pythonhosted.org/packages/71/22/207523d16464c40a0310d2d4d8926daffa00ac1f5b1576170a32db749636/pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] "pytest 7.2.0" = [ {url = "https://files.pythonhosted.org/packages/0b/21/055f39bf8861580b43f845f9e8270c7786fe629b2f8562ff09007132e2e7/pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, {url = "https://files.pythonhosted.org/packages/67/68/a5eb36c3a8540594b6035e6cdae40c1ef1b6a2bfacbecc3d1a544583c078/pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, @@ -385,9 +372,9 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {url = "https://files.pythonhosted.org/packages/a5/61/a867851fd5ab77277495a8709ddda0861b28163c4613b011bc00228cc724/requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, {url = "https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c0441b5cda699c90f618b6f8a1b42/requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, ] -"setuptools 65.5.0" = [ - {url = "https://files.pythonhosted.org/packages/41/82/7f54bbfe5c247a8c9f78d8d1d7c051847bcb78843c397b866dba335c1e88/setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, - {url = "https://files.pythonhosted.org/packages/c5/41/247814d8b7a044717164c74080725a6c8f3d2b5fc82b34bd825b617df663/setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, +"setuptools 65.6.3" = [ + {url = "https://files.pythonhosted.org/packages/b6/21/cb9a8d0b2c8597c83fce8e9c02884bce3d4951e41e807fc35791c6b23d9a/setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, + {url = "https://files.pythonhosted.org/packages/ef/e3/29d6e1a07e8d90ace4a522d9689d03e833b67b50d1588e693eec15f26251/setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, ] "six 1.16.0" = [ {url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -405,13 +392,13 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {url = "https://files.pythonhosted.org/packages/0b/8e/f1a0a5a76cfef77e1eb6004cb49e5f8d72634da638420b9ea492ce8305e8/typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {url = "https://files.pythonhosted.org/packages/e3/a7/8f4e456ef0adac43f452efc2d0e4b242ab831297f1bac60ac815d37eb9cf/typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] -"urllib3 1.26.12" = [ - {url = "https://files.pythonhosted.org/packages/6f/de/5be2e3eed8426f871b170663333a0f627fc2924cc386cd41be065e7ea870/urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, - {url = "https://files.pythonhosted.org/packages/b2/56/d87d6d3c4121c0bcec116919350ca05dc3afd2eeb7dc88d07e8083f8ea94/urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, +"urllib3 1.26.13" = [ + {url = "https://files.pythonhosted.org/packages/65/0c/cc6644eaa594585e5875f46f3c83ee8762b647b51fc5b0fb253a242df2dc/urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, + {url = "https://files.pythonhosted.org/packages/c2/51/32da03cf19d17d46cce5c731967bf58de9bd71db3a379932f53b094deda4/urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, ] -"virtualenv 20.16.6" = [ - {url = "https://files.pythonhosted.org/packages/b4/27/b71df0a723d879baa0af1ad897b2498ad78f284ae668b4420092e44c05fa/virtualenv-20.16.6.tar.gz", hash = "sha256:530b850b523c6449406dfba859d6345e48ef19b8439606c5d74d7d3c9e14d76e"}, - {url = "https://files.pythonhosted.org/packages/ef/e0/1295d8a0b34f71a81fdf0f09c1ef658ae6d611240829c3c39fb2b6b80967/virtualenv-20.16.6-py3-none-any.whl", hash = "sha256:186ca84254abcbde98180fd17092f9628c5fe742273c02724972a1d8a2035108"}, +"virtualenv 20.17.1" = [ + {url = "https://files.pythonhosted.org/packages/18/a2/7931d40ecb02b5236a34ac53770f2f6931e3082b7a7dafe915d892d749d6/virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, + {url = "https://files.pythonhosted.org/packages/7b/19/65f13cff26c8cc11fdfcb0499cd8f13388dd7b35a79a376755f152b42d86/virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, ] "watchdog 2.2.0" = [ {url = "https://files.pythonhosted.org/packages/0b/0f/f7f8adbc21791e07a2fd720cc691eea5fbae77b72f924a25d09d32a2da32/watchdog-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e99c1713e4436d2563f5828c8910e5ff25abd6ce999e75f15c15d81d41980b6"}, @@ -447,7 +434,7 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {url = "https://files.pythonhosted.org/packages/75/af/1d13b93e7a21aca7f8ab8645fcfcfad21fc39716dc9dce5dc2a97f73ff78/websocket-client-1.4.2.tar.gz", hash = "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59"}, {url = "https://files.pythonhosted.org/packages/78/d5/2b5719b738791cd798e8f097eba4bb093ff5efca5cef2f3d37a72daa111f/websocket_client-1.4.2-py3-none-any.whl", hash = "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574"}, ] -"zipp 3.10.0" = [ - {url = "https://files.pythonhosted.org/packages/40/8a/d63273ed0fa4a3d06f77e7b043f6577d8894e95515b0c187c52e2c0efabb/zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"}, - {url = "https://files.pythonhosted.org/packages/8d/d7/1bd1e0a5bc95a27a6f5c4ee8066ddfc5b69a9ec8d39ab11a41a804ec8f0d/zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"}, +"zipp 3.11.0" = [ + {url = "https://files.pythonhosted.org/packages/8e/b3/8b16a007184714f71157b1a71bbe632c5d66dd43bc8152b3c799b13881e1/zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, + {url = "https://files.pythonhosted.org/packages/d8/20/256eb3f3f437c575fb1a2efdce5e801a5ce3162ea8117da96c43e6ee97d8/zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, ] diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml index c6f8adef6..0bd3bef30 100644 --- a/sdk/python/pyproject.toml +++ b/sdk/python/pyproject.toml @@ -35,7 +35,8 @@ tests = [ "pytest>=6.1.2", ] dev = [ - "pre-commit>=2.17.0"] + "pre-commit>=2.17.0", +] [project.scripts] flet = "flet.cli.cli:main"