From 3c800bf08f9464e7911f98c83b0470609b0fc0f5 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 14 Dec 2022 20:57:17 -0800 Subject: [PATCH 01/17] Trying to wrap PyInstaller --- sdk/python/flet/__pyinstaller/config.py | 1 + sdk/python/flet/__pyinstaller/hook-flet.py | 4 + sdk/python/flet/cli/cli.py | 8 +- sdk/python/flet/cli/commands/build.py | 23 ------ sdk/python/flet/cli/commands/package.py | 49 ++++++++++++ sdk/python/pdm.lock | 87 +++++++++++++++++++++- sdk/python/pyproject.toml | 4 +- 7 files changed, 148 insertions(+), 28 deletions(-) create mode 100644 sdk/python/flet/__pyinstaller/config.py delete mode 100644 sdk/python/flet/cli/commands/build.py create mode 100644 sdk/python/flet/cli/commands/package.py diff --git a/sdk/python/flet/__pyinstaller/config.py b/sdk/python/flet/__pyinstaller/config.py new file mode 100644 index 0000000000..57f798c777 --- /dev/null +++ b/sdk/python/flet/__pyinstaller/config.py @@ -0,0 +1 @@ +icon_file = "" diff --git a/sdk/python/flet/__pyinstaller/hook-flet.py b/sdk/python/flet/__pyinstaller/hook-flet.py index cbb2276e72..67cdaacab6 100644 --- a/sdk/python/flet/__pyinstaller/hook-flet.py +++ b/sdk/python/flet/__pyinstaller/hook-flet.py @@ -1,5 +1,9 @@ import os +import flet.__pyinstaller.config as hook_config + +# raise Exception(f"icon_file: {hook_config.icon_file}") + # package entire "bin" folder bin_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, "bin")) diff --git a/sdk/python/flet/cli/cli.py b/sdk/python/flet/cli/cli.py index c1cfa8c153..9ab618743e 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.package 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.package.Command.register_to(sp, "package") 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 7d896d353a..0000000000 --- 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/package.py b/sdk/python/flet/cli/commands/package.py new file mode 100644 index 0000000000..cb77222629 --- /dev/null +++ b/sdk/python/flet/cli/commands/package.py @@ -0,0 +1,49 @@ +import argparse + +import flet.__pyinstaller.config as hook_config +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", + ) + 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", + ) + + def handle(self, options: argparse.Namespace) -> None: + + try: + import PyInstaller.__main__ + + pyi_args = [options.script, "--noconsole", "--noconfirm"] + if options.icon: + pyi_args.extend(["--icon", options.icon]) + hook_config.icon_file = options.icon + if options.name: + pyi_args.extend(["--name", options.name]) + PyInstaller.__main__.run(pyi_args) + except ImportError: + print("Please install PyInstaller module to use flet package command.") + exit(1) diff --git a/sdk/python/pdm.lock b/sdk/python/pdm.lock index 1602ada5d8..6ef39582b2 100644 --- a/sdk/python/pdm.lock +++ b/sdk/python/pdm.lock @@ -1,3 +1,8 @@ +[[package]] +name = "altgraph" +version = "0.17.3" +summary = "Python graph (network) package" + [[package]] name = "attrs" version = "22.1.0" @@ -51,6 +56,12 @@ version = "3.8.0" requires_python = ">=3.7" summary = "A platform independent file lock." +[[package]] +name = "future" +version = "0.18.2" +requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Clean single-source support for Python 3 and 2" + [[package]] name = "identify" version = "2.5.8" @@ -78,6 +89,14 @@ name = "iniconfig" version = "1.1.1" summary = "iniconfig: brain-dead simple config-ini parsing" +[[package]] +name = "macholib" +version = "1.16.2" +summary = "Mach-O header analysis and editing" +dependencies = [ + "altgraph>=0.17", +] + [[package]] name = "nodeenv" version = "1.7.0" @@ -102,6 +121,15 @@ dependencies = [ "pyparsing!=3.0.5,>=2.0.2", ] +[[package]] +name = "pefile" +version = "2022.5.30" +requires_python = ">=3.6.0" +summary = "Python PE parsing module" +dependencies = [ + "future", +] + [[package]] name = "platformdirs" version = "2.5.2" @@ -132,6 +160,27 @@ dependencies = [ "virtualenv>=20.0.8", ] +[[package]] +name = "pyinstaller" +version = "4.5.1" +requires_python = ">=3.6" +summary = "PyInstaller bundles a Python application and all its dependencies into a single package." +dependencies = [ + "altgraph", + "importlib-metadata; python_version < \"3.8\"", + "macholib>=1.8; sys_platform == \"darwin\"", + "pefile>=2017.8.1; sys_platform == \"win32\"", + "pyinstaller-hooks-contrib>=2020.6", + "pywin32-ctypes>=0.2.0; sys_platform == \"win32\"", + "setuptools", +] + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2022.14" +requires_python = ">=3.7" +summary = "Community maintained hooks for PyInstaller" + [[package]] name = "pyparsing" version = "3.0.9" @@ -154,6 +203,11 @@ dependencies = [ "tomli>=1.0.0; python_version < \"3.11\"", ] +[[package]] +name = "pywin32-ctypes" +version = "0.2.0" +summary = "UNKNOWN" + [[package]] name = "pyyaml" version = "6.0" @@ -248,9 +302,13 @@ summary = "Backport of pathlib-compatible object wrapper for zip files" [metadata] lock_version = "4.0" -content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019feb419eef" +content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780ccb39f9b" [metadata.files] +"altgraph 0.17.3" = [ + {url = "https://files.pythonhosted.org/packages/5a/13/a7cfa43856a7b8e4894848ec8f71cd9e1ac461e51802391a3e2101c60ed6/altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"}, + {url = "https://files.pythonhosted.org/packages/cc/ff/88d277ba936d226b0f6dbd6711145f90fcfeed3aa9455c1c2f62c8ffec5b/altgraph-0.17.3-py2.py3-none-any.whl", hash = "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe"}, +] "attrs 22.1.0" = [ {url = "https://files.pythonhosted.org/packages/1a/cb/c4ffeb41e7137b23755a45e1bfec9cbb76ecf51874c6f1d113984ecaa32c/attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, {url = "https://files.pythonhosted.org/packages/f2/bc/d817287d1aa01878af07c19505fafd1165cd6a119e9d0821ca1d1c20312d/attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, @@ -287,6 +345,9 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {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"}, ] +"future 0.18.2" = [ + {url = "https://files.pythonhosted.org/packages/45/0b/38b06fd9b92dc2b68d58b75f900e97884c45bedd2ff83203d933cf5851c9/future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +] "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"}, @@ -303,6 +364,10 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, {url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, ] +"macholib 1.16.2" = [ + {url = "https://files.pythonhosted.org/packages/46/92/bffe4576b383f20995ffb15edccf1c97d2e39f9a8c72136836407f099277/macholib-1.16.2.tar.gz", hash = "sha256:557bbfa1bb255c20e9abafe7ed6cd8046b48d9525db2f9b77d3122a63a2a8bf8"}, + {url = "https://files.pythonhosted.org/packages/5f/8c/8d7c7437f2799f570f9f4cb1fc974f671eff6fecd659f5e4097858a014ef/macholib-1.16.2-py2.py3-none-any.whl", hash = "sha256:44c40f2cd7d6726af8fa6fe22549178d3a4dfecc35a9cd15ea916d9c83a688e0"}, +] "nodeenv 1.7.0" = [ {url = "https://files.pythonhosted.org/packages/96/a8/d3b5baead78adadacb99e7281b3e842126da825cf53df61688cfc8b8ff91/nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, {url = "https://files.pythonhosted.org/packages/f3/9d/a28ecbd1721cd6c0ea65da6bfb2771d31c5d7e32d916a8f643b062530af3/nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, @@ -315,6 +380,9 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {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"}, ] +"pefile 2022.5.30" = [ + {url = "https://files.pythonhosted.org/packages/48/30/4559d06bad5bb627733dac1ef28c34f5e35f1461247ba63e5f6366901277/pefile-2022.5.30.tar.gz", hash = "sha256:a5488a3dd1fd021ce33f969780b88fe0f7eebb76eb20996d7318f307612a045b"}, +] "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"}, @@ -327,6 +395,19 @@ 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"}, ] +"pyinstaller 4.5.1" = [ + {url = "https://files.pythonhosted.org/packages/12/a9/91a182d5b2541d8b89396f4371151e412246f8e8d6e65954871548a2a14b/pyinstaller-4.5.1-py3-none-win_amd64.whl", hash = "sha256:aae456205c68355f9597411090576bb31b614e53976b4c102d072bbe5db8392a"}, + {url = "https://files.pythonhosted.org/packages/28/0d/89e847020fe6514dec7d18d1ca78d9b9cbbabfa17f1d38b8122bc9be8d14/pyinstaller-4.5.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4d848cd782ee0893d7ad9fe2bfe535206a79f0b6760cecc5f2add831258b9322"}, + {url = "https://files.pythonhosted.org/packages/36/eb/e30b66fd38d80a4d45f2dbfea3f0985002c66aa08b855b61f08bce50891c/pyinstaller-4.5.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c587da8f521a7ce1b9efb4e3d0117cd63c92dc6cedff24590aeef89372f53012"}, + {url = "https://files.pythonhosted.org/packages/79/ba/fbd0f192e1d711bf391e44a42875fc3ecffaf4529d7d7d9604fc79027327/pyinstaller-4.5.1-py3-none-win32.whl", hash = "sha256:fed9f5e4802769a416a8f2ca171c6be961d1861cc05a0b71d20dfe05423137e9"}, + {url = "https://files.pythonhosted.org/packages/a9/d9/9fdfb0ac2354d059e466d562689dbe53a23c4062019da2057f0eaed635e0/pyinstaller-4.5.1.tar.gz", hash = "sha256:30733baaf8971902286a0ddf77e5499ac5f7bf8e7c39163e83d4f8c696ef265e"}, + {url = "https://files.pythonhosted.org/packages/ba/d5/331cbbee7aa8f3b62051d8be714e2e8c208536abfe558e61e9c4b09a6ae4/pyinstaller-4.5.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:ecc2baadeeefd2b6fbf39d13c65d4aa603afdda1c6aaaebc4577ba72893fee9e"}, + {url = "https://files.pythonhosted.org/packages/e8/60/cbfcd60f99c8814bb314910953b4cd56c4bf3a5d8099516066cd839443e8/pyinstaller-4.5.1-py3-none-manylinux2014_i686.whl", hash = "sha256:8f747b190e6ad30e2d2fd5da9a64636f61aac8c038c0b7f685efa92c782ea14f"}, +] +"pyinstaller-hooks-contrib 2022.14" = [ + {url = "https://files.pythonhosted.org/packages/32/4b/4a87a98392e0bae03a40932dcfb79896e1cd85af29461ebf82d77ea6778c/pyinstaller_hooks_contrib-2022.14-py2.py3-none-any.whl", hash = "sha256:1a125838a22d7b35a18993c6e56d3c5cc3ad7da00954f95bc5606523939203f2"}, + {url = "https://files.pythonhosted.org/packages/f4/e1/1b4f8394be02574479167b0f09a9fdb3ca4f184b45bd89d5878e44ba72ed/pyinstaller-hooks-contrib-2022.14.tar.gz", hash = "sha256:5ae8da3a92cf20e37b3e00604d0c3468896e7d746e5c1449473597a724331b0b"}, +] "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"}, @@ -335,6 +416,10 @@ content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019fe {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"}, ] +"pywin32-ctypes 0.2.0" = [ + {url = "https://files.pythonhosted.org/packages/7a/7d/0dbc4c99379452a819b0fb075a0ffbb98611df6b6d59f54db67367af5bc0/pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, + {url = "https://files.pythonhosted.org/packages/9e/4b/3ab2720f1fa4b4bc924ef1932b842edf10007e4547ea8157b0b9fc78599a/pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, +] "pyyaml 6.0" = [ {url = "https://files.pythonhosted.org/packages/02/25/6ba9f6bb50a3d4fbe22c1a02554dc670682a07c8701d1716d19ddea2c940/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"}, {url = "https://files.pythonhosted.org/packages/08/f4/ffa743f860f34a5e8c60abaaa686f82c9ac7a2b50e5a1c3b1eb564d59159/PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml index c6f8adef6a..d76c5d8da0 100644 --- a/sdk/python/pyproject.toml +++ b/sdk/python/pyproject.toml @@ -35,7 +35,9 @@ tests = [ "pytest>=6.1.2", ] dev = [ - "pre-commit>=2.17.0"] + "pre-commit>=2.17.0", + "pyinstaller>=4.5.1", +] [project.scripts] flet = "flet.cli.cli:main" From 8d7f6ca8b4b5a9576a1a2a616e7aee9b3c072a55 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 15 Dec 2022 19:51:08 -0800 Subject: [PATCH 02/17] Version package args --- sdk/python/flet/cli/commands/package.py | 49 +++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/sdk/python/flet/cli/commands/package.py b/sdk/python/flet/cli/commands/package.py index cb77222629..955080ea9e 100644 --- a/sdk/python/flet/cli/commands/package.py +++ b/sdk/python/flet/cli/commands/package.py @@ -31,11 +31,58 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: dest="name", help="name for the generated executable", ) + 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", + ) + parser.add_argument( + "--file-description", + dest="file_description", + help="executable file description", + ) + parser.add_argument( + "--product-version", + dest="product_version", + help="executable product version (any string)", + ) + parser.add_argument( + "--file-version", + dest="file_version", + help="executable file version (n.n.n.n)", + ) + parser.add_argument( + "--company-name", + dest="company_name", + help="executable companyname", + ) + parser.add_argument( + "--copyright", + dest="copyright", + help="executable copyright", + ) def handle(self, options: argparse.Namespace) -> None: try: + import pefile import PyInstaller.__main__ + from PyInstaller.compat import win32api + from PyInstaller.utils.win32.versioninfo import ( + FixedFileInfo, + StringFileInfo, + StringStruct, + StringTable, + VarFileInfo, + VarStruct, + VSVersionInfo, + decode, + ) pyi_args = [options.script, "--noconsole", "--noconfirm"] if options.icon: @@ -43,6 +90,8 @@ def handle(self, options: argparse.Namespace) -> None: hook_config.icon_file = options.icon if options.name: pyi_args.extend(["--name", options.name]) + if options.add_data: + pyi_args.extend(["--add-data", options.add_data]) PyInstaller.__main__.run(pyi_args) except ImportError: print("Please install PyInstaller module to use flet package command.") From e76eee4edfcbd42922e9d1f6bf47f96a85255c71 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 15 Dec 2022 21:00:48 -0800 Subject: [PATCH 03/17] Copy bin dir --- sdk/python/flet/__pyinstaller/config.py | 3 +- sdk/python/flet/__pyinstaller/hook-flet.py | 44 +++++++++++++++++++--- sdk/python/flet/cli/commands/package.py | 13 +------ 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/sdk/python/flet/__pyinstaller/config.py b/sdk/python/flet/__pyinstaller/config.py index 57f798c777..917e012869 100644 --- a/sdk/python/flet/__pyinstaller/config.py +++ b/sdk/python/flet/__pyinstaller/config.py @@ -1 +1,2 @@ -icon_file = "" +icon_file = None +temp_bin_dir = None diff --git a/sdk/python/flet/__pyinstaller/hook-flet.py b/sdk/python/flet/__pyinstaller/hook-flet.py index 67cdaacab6..10fa563e70 100644 --- a/sdk/python/flet/__pyinstaller/hook-flet.py +++ b/sdk/python/flet/__pyinstaller/hook-flet.py @@ -1,14 +1,46 @@ import os +import shutil +import tempfile +import uuid +from pathlib import Path + +import pefile +from PyInstaller.compat import win32api +from PyInstaller.utils.win32.versioninfo import ( + FixedFileInfo, + StringFileInfo, + StringStruct, + StringTable, + VarFileInfo, + VarStruct, + VSVersionInfo, + decode, +) import flet.__pyinstaller.config as hook_config # raise Exception(f"icon_file: {hook_config.icon_file}") -# package entire "bin" folder -bin_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, "bin")) -# package "bin/fletd" only -if os.getenv("PACKAGE_FLETD_ONLY"): - bin_path = os.path.join(bin_path, "fletd*") +def copy_bin(): + bin_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir, "bin") + ) + if not os.path.exists(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) + + +bin_path = copy_bin() +hook_config.temp_bin_dir = bin_path + +if bin_path != None: + # package "bin/fletd" only + if os.getenv("PACKAGE_FLETD_ONLY"): + bin_path = os.path.join(bin_path, "fletd*") -datas = [(bin_path, "flet/bin")] + datas = [(bin_path, "flet/bin")] diff --git a/sdk/python/flet/cli/commands/package.py b/sdk/python/flet/cli/commands/package.py index 955080ea9e..f758ac89ea 100644 --- a/sdk/python/flet/cli/commands/package.py +++ b/sdk/python/flet/cli/commands/package.py @@ -70,19 +70,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: def handle(self, options: argparse.Namespace) -> None: try: - import pefile import PyInstaller.__main__ - from PyInstaller.compat import win32api - from PyInstaller.utils.win32.versioninfo import ( - FixedFileInfo, - StringFileInfo, - StringStruct, - StringTable, - VarFileInfo, - VarStruct, - VSVersionInfo, - decode, - ) pyi_args = [options.script, "--noconsole", "--noconfirm"] if options.icon: @@ -93,6 +81,7 @@ def handle(self, options: argparse.Namespace) -> None: if options.add_data: pyi_args.extend(["--add-data", options.add_data]) PyInstaller.__main__.run(pyi_args) + print(hook_config.temp_bin_dir) except ImportError: print("Please install PyInstaller module to use flet package command.") exit(1) From a51885810696f89f9d2cc71d2554c9c8fb810052 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 17 Dec 2022 12:36:13 -0800 Subject: [PATCH 04/17] Patch icon and version info --- sdk/python/flet/__pyinstaller/config.py | 1 - sdk/python/flet/__pyinstaller/hook-flet.py | 41 +--- sdk/python/flet/__pyinstaller/utils.py | 133 ++++++++++++ sdk/python/flet/cli/cli.py | 4 +- .../flet/cli/commands/{package.py => pack.py} | 60 +++++- sdk/python/pdm.lock | 194 +++++------------- sdk/python/pyproject.toml | 1 - 7 files changed, 246 insertions(+), 188 deletions(-) create mode 100644 sdk/python/flet/__pyinstaller/utils.py rename sdk/python/flet/cli/commands/{package.py => pack.py} (52%) diff --git a/sdk/python/flet/__pyinstaller/config.py b/sdk/python/flet/__pyinstaller/config.py index 917e012869..9f07382dc6 100644 --- a/sdk/python/flet/__pyinstaller/config.py +++ b/sdk/python/flet/__pyinstaller/config.py @@ -1,2 +1 @@ -icon_file = None temp_bin_dir = None diff --git a/sdk/python/flet/__pyinstaller/hook-flet.py b/sdk/python/flet/__pyinstaller/hook-flet.py index 10fa563e70..d5d8961d93 100644 --- a/sdk/python/flet/__pyinstaller/hook-flet.py +++ b/sdk/python/flet/__pyinstaller/hook-flet.py @@ -1,44 +1,13 @@ import os -import shutil -import tempfile -import uuid -from pathlib import Path - -import pefile -from PyInstaller.compat import win32api -from PyInstaller.utils.win32.versioninfo import ( - FixedFileInfo, - StringFileInfo, - StringStruct, - StringTable, - VarFileInfo, - VarStruct, - VSVersionInfo, - decode, -) import flet.__pyinstaller.config as hook_config +from flet.__pyinstaller.utils import get_flet_bin_path -# raise Exception(f"icon_file: {hook_config.icon_file}") - - -def copy_bin(): - bin_path = os.path.abspath( - os.path.join(os.path.dirname(__file__), os.pardir, "bin") - ) - if not os.path.exists(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) - - -bin_path = copy_bin() -hook_config.temp_bin_dir = bin_path +bin_path = hook_config.temp_bin_dir +if not bin_path: + bin_path = get_flet_bin_path() -if bin_path != None: +if bin_path: # package "bin/fletd" only if os.getenv("PACKAGE_FLETD_ONLY"): bin_path = os.path.join(bin_path, "fletd*") diff --git a/sdk/python/flet/__pyinstaller/utils.py b/sdk/python/flet/__pyinstaller/utils.py new file mode 100644 index 0000000000..aa6f89d3d7 --- /dev/null +++ b/sdk/python/flet/__pyinstaller/utils.py @@ -0,0 +1,133 @@ +import os +import shutil +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.misc import decode +from PyInstaller.utils.win32.icon import IconFile, normalize_icon_type +from PyInstaller.utils.win32.versioninfo import ( + FixedFileInfo, + StringFileInfo, + StringStruct, + StringTable, + VarFileInfo, + VarStruct, + VSVersionInfo, + decode, +) + + +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) + + +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 %s", 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 "" + + print(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) diff --git a/sdk/python/flet/cli/cli.py b/sdk/python/flet/cli/cli.py index 9ab618743e..d1dbd63eff 100644 --- a/sdk/python/flet/cli/cli.py +++ b/sdk/python/flet/cli/cli.py @@ -1,7 +1,7 @@ import argparse import sys -import flet.cli.commands.package +import flet.cli.commands.pack import flet.cli.commands.run import flet.version @@ -62,7 +62,7 @@ def main(): # sp.default = "run" flet.cli.commands.run.Command.register_to(sp, "run") - flet.cli.commands.package.Command.register_to(sp, "package") + 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/package.py b/sdk/python/flet/cli/commands/pack.py similarity index 52% rename from sdk/python/flet/cli/commands/package.py rename to sdk/python/flet/cli/commands/pack.py index f758ac89ea..7aad9b4404 100644 --- a/sdk/python/flet/cli/commands/package.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -1,7 +1,16 @@ import argparse +import os +import shutil +from pathlib import Path import flet.__pyinstaller.config as hook_config +from flet.__pyinstaller.utils import ( + copy_flet_bin, + update_flet_view_icon, + update_flet_view_version_info, +) from flet.cli.commands.base import BaseCommand +from flet.utils import is_windows class Command(BaseCommand): @@ -69,19 +78,66 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: 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__ pyi_args = [options.script, "--noconsole", "--noconfirm"] + if options.onefile: + pyi_args.extend(["--onefile"]) if options.icon: pyi_args.extend(["--icon", options.icon]) - hook_config.icon_file = options.icon if options.name: pyi_args.extend(["--name", options.name]) if options.add_data: pyi_args.extend(["--add-data", options.add_data]) + + # copy "bin" + hook_config.temp_bin_dir = copy_flet_bin() + + if hook_config.temp_bin_dir is not None: + if is_windows(): + 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 + if options.company_name or options.file_description: + 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, + ) + + # run PyInstaller! PyInstaller.__main__.run(pyi_args) - print(hook_config.temp_bin_dir) + + # 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/pdm.lock b/sdk/python/pdm.lock index 6ef39582b2..e8b2ca1c39 100644 --- a/sdk/python/pdm.lock +++ b/sdk/python/pdm.lock @@ -1,8 +1,3 @@ -[[package]] -name = "altgraph" -version = "0.17.3" -summary = "Python graph (network) package" - [[package]] name = "attrs" version = "22.1.0" @@ -17,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." @@ -46,25 +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 = "future" -version = "0.18.2" -requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Clean single-source support for Python 3 and 2" - [[package]] name = "identify" -version = "2.5.8" +version = "2.5.10" requires_python = ">=3.7" summary = "File identification library for Python" @@ -76,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 = [ @@ -89,14 +78,6 @@ name = "iniconfig" version = "1.1.1" summary = "iniconfig: brain-dead simple config-ini parsing" -[[package]] -name = "macholib" -version = "1.16.2" -summary = "Mach-O header analysis and editing" -dependencies = [ - "altgraph>=0.17", -] - [[package]] name = "nodeenv" version = "1.7.0" @@ -114,27 +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 = "pefile" -version = "2022.5.30" -requires_python = ">=3.6.0" -summary = "Python PE parsing module" -dependencies = [ - "future", -] [[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" @@ -160,33 +129,6 @@ dependencies = [ "virtualenv>=20.0.8", ] -[[package]] -name = "pyinstaller" -version = "4.5.1" -requires_python = ">=3.6" -summary = "PyInstaller bundles a Python application and all its dependencies into a single package." -dependencies = [ - "altgraph", - "importlib-metadata; python_version < \"3.8\"", - "macholib>=1.8; sys_platform == \"darwin\"", - "pefile>=2017.8.1; sys_platform == \"win32\"", - "pyinstaller-hooks-contrib>=2020.6", - "pywin32-ctypes>=0.2.0; sys_platform == \"win32\"", - "setuptools", -] - -[[package]] -name = "pyinstaller-hooks-contrib" -version = "2022.14" -requires_python = ">=3.7" -summary = "Community maintained hooks for PyInstaller" - -[[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" @@ -203,11 +145,6 @@ dependencies = [ "tomli>=1.0.0; python_version < \"3.11\"", ] -[[package]] -name = "pywin32-ctypes" -version = "0.2.0" -summary = "UNKNOWN" - [[package]] name = "pyyaml" version = "6.0" @@ -236,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" @@ -266,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 = [ @@ -296,19 +233,15 @@ 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" [metadata] lock_version = "4.0" -content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780ccb39f9b" +content_hash = "sha256:9bd1e399fac3187f9ca477a6d26e125417df67a1172333ed5232019feb419eef" [metadata.files] -"altgraph 0.17.3" = [ - {url = "https://files.pythonhosted.org/packages/5a/13/a7cfa43856a7b8e4894848ec8f71cd9e1ac461e51802391a3e2101c60ed6/altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"}, - {url = "https://files.pythonhosted.org/packages/cc/ff/88d277ba936d226b0f6dbd6711145f90fcfeed3aa9455c1c2f62c8ffec5b/altgraph-0.17.3-py2.py3-none-any.whl", hash = "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe"}, -] "attrs 22.1.0" = [ {url = "https://files.pythonhosted.org/packages/1a/cb/c4ffeb41e7137b23755a45e1bfec9cbb76ecf51874c6f1d113984ecaa32c/attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, {url = "https://files.pythonhosted.org/packages/f2/bc/d817287d1aa01878af07c19505fafd1165cd6a119e9d0821ca1d1c20312d/attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, @@ -317,9 +250,9 @@ content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780c {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"}, @@ -337,37 +270,30 @@ content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780c {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"}, ] -"future 0.18.2" = [ - {url = "https://files.pythonhosted.org/packages/45/0b/38b06fd9b92dc2b68d58b75f900e97884c45bedd2ff83203d933cf5851c9/future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, -] -"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"}, {url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, ] -"macholib 1.16.2" = [ - {url = "https://files.pythonhosted.org/packages/46/92/bffe4576b383f20995ffb15edccf1c97d2e39f9a8c72136836407f099277/macholib-1.16.2.tar.gz", hash = "sha256:557bbfa1bb255c20e9abafe7ed6cd8046b48d9525db2f9b77d3122a63a2a8bf8"}, - {url = "https://files.pythonhosted.org/packages/5f/8c/8d7c7437f2799f570f9f4cb1fc974f671eff6fecd659f5e4097858a014ef/macholib-1.16.2-py2.py3-none-any.whl", hash = "sha256:44c40f2cd7d6726af8fa6fe22549178d3a4dfecc35a9cd15ea916d9c83a688e0"}, -] "nodeenv 1.7.0" = [ {url = "https://files.pythonhosted.org/packages/96/a8/d3b5baead78adadacb99e7281b3e842126da825cf53df61688cfc8b8ff91/nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, {url = "https://files.pythonhosted.org/packages/f3/9d/a28ecbd1721cd6c0ea65da6bfb2771d31c5d7e32d916a8f643b062530af3/nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, @@ -376,16 +302,13 @@ content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780c {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"}, ] -"pefile 2022.5.30" = [ - {url = "https://files.pythonhosted.org/packages/48/30/4559d06bad5bb627733dac1ef28c34f5e35f1461247ba63e5f6366901277/pefile-2022.5.30.tar.gz", hash = "sha256:a5488a3dd1fd021ce33f969780b88fe0f7eebb76eb20996d7318f307612a045b"}, -] -"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"}, @@ -395,31 +318,10 @@ content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780c {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"}, ] -"pyinstaller 4.5.1" = [ - {url = "https://files.pythonhosted.org/packages/12/a9/91a182d5b2541d8b89396f4371151e412246f8e8d6e65954871548a2a14b/pyinstaller-4.5.1-py3-none-win_amd64.whl", hash = "sha256:aae456205c68355f9597411090576bb31b614e53976b4c102d072bbe5db8392a"}, - {url = "https://files.pythonhosted.org/packages/28/0d/89e847020fe6514dec7d18d1ca78d9b9cbbabfa17f1d38b8122bc9be8d14/pyinstaller-4.5.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4d848cd782ee0893d7ad9fe2bfe535206a79f0b6760cecc5f2add831258b9322"}, - {url = "https://files.pythonhosted.org/packages/36/eb/e30b66fd38d80a4d45f2dbfea3f0985002c66aa08b855b61f08bce50891c/pyinstaller-4.5.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c587da8f521a7ce1b9efb4e3d0117cd63c92dc6cedff24590aeef89372f53012"}, - {url = "https://files.pythonhosted.org/packages/79/ba/fbd0f192e1d711bf391e44a42875fc3ecffaf4529d7d7d9604fc79027327/pyinstaller-4.5.1-py3-none-win32.whl", hash = "sha256:fed9f5e4802769a416a8f2ca171c6be961d1861cc05a0b71d20dfe05423137e9"}, - {url = "https://files.pythonhosted.org/packages/a9/d9/9fdfb0ac2354d059e466d562689dbe53a23c4062019da2057f0eaed635e0/pyinstaller-4.5.1.tar.gz", hash = "sha256:30733baaf8971902286a0ddf77e5499ac5f7bf8e7c39163e83d4f8c696ef265e"}, - {url = "https://files.pythonhosted.org/packages/ba/d5/331cbbee7aa8f3b62051d8be714e2e8c208536abfe558e61e9c4b09a6ae4/pyinstaller-4.5.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:ecc2baadeeefd2b6fbf39d13c65d4aa603afdda1c6aaaebc4577ba72893fee9e"}, - {url = "https://files.pythonhosted.org/packages/e8/60/cbfcd60f99c8814bb314910953b4cd56c4bf3a5d8099516066cd839443e8/pyinstaller-4.5.1-py3-none-manylinux2014_i686.whl", hash = "sha256:8f747b190e6ad30e2d2fd5da9a64636f61aac8c038c0b7f685efa92c782ea14f"}, -] -"pyinstaller-hooks-contrib 2022.14" = [ - {url = "https://files.pythonhosted.org/packages/32/4b/4a87a98392e0bae03a40932dcfb79896e1cd85af29461ebf82d77ea6778c/pyinstaller_hooks_contrib-2022.14-py2.py3-none-any.whl", hash = "sha256:1a125838a22d7b35a18993c6e56d3c5cc3ad7da00954f95bc5606523939203f2"}, - {url = "https://files.pythonhosted.org/packages/f4/e1/1b4f8394be02574479167b0f09a9fdb3ca4f184b45bd89d5878e44ba72ed/pyinstaller-hooks-contrib-2022.14.tar.gz", hash = "sha256:5ae8da3a92cf20e37b3e00604d0c3468896e7d746e5c1449473597a724331b0b"}, -] -"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"}, ] -"pywin32-ctypes 0.2.0" = [ - {url = "https://files.pythonhosted.org/packages/7a/7d/0dbc4c99379452a819b0fb075a0ffbb98611df6b6d59f54db67367af5bc0/pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, - {url = "https://files.pythonhosted.org/packages/9e/4b/3ab2720f1fa4b4bc924ef1932b842edf10007e4547ea8157b0b9fc78599a/pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, -] "pyyaml 6.0" = [ {url = "https://files.pythonhosted.org/packages/02/25/6ba9f6bb50a3d4fbe22c1a02554dc670682a07c8701d1716d19ddea2c940/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"}, {url = "https://files.pythonhosted.org/packages/08/f4/ffa743f860f34a5e8c60abaaa686f82c9ac7a2b50e5a1c3b1eb564d59159/PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, @@ -470,9 +372,9 @@ content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780c {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"}, @@ -490,13 +392,13 @@ content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780c {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"}, @@ -532,7 +434,7 @@ content_hash = "sha256:b31a24c9b8317ff5cfec4bbe291ae2dbb25774d370eb1fd820702780c {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 d76c5d8da0..0bd3bef30d 100644 --- a/sdk/python/pyproject.toml +++ b/sdk/python/pyproject.toml @@ -36,7 +36,6 @@ tests = [ ] dev = [ "pre-commit>=2.17.0", - "pyinstaller>=4.5.1", ] [project.scripts] From 3232b3d2d5ee8b418eaf1879fa57963edab11ed9 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 17 Dec 2022 12:50:01 -0800 Subject: [PATCH 05/17] certifi==2022.12.7 --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index f7e5c8a04a..7f3a629db3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -398,7 +398,7 @@ for: install: - python --version - cd sdk/python - - pip install certifi==2022.9.24 + - pip install certifi==2022.12.7 - pip install pdm - pdm install From 296da3099e5d2befff7ea51794aae3ef38639b96 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 17 Dec 2022 12:52:03 -0800 Subject: [PATCH 06/17] Try removing certifi pin --- .appveyor.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7f3a629db3..6b7454118f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -398,7 +398,6 @@ for: install: - python --version - cd sdk/python - - pip install certifi==2022.12.7 - 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 From 58e9b6aa75d3a4c244541f23ae8eeb51970c68a7 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 17 Dec 2022 21:44:39 -0800 Subject: [PATCH 07/17] Update main exec version info --- sdk/python/flet/__pyinstaller/utils.py | 6 +++++- sdk/python/flet/cli/commands/pack.py | 21 +++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sdk/python/flet/__pyinstaller/utils.py b/sdk/python/flet/__pyinstaller/utils.py index aa6f89d3d7..61d705b673 100644 --- a/sdk/python/flet/__pyinstaller/utils.py +++ b/sdk/python/flet/__pyinstaller/utils.py @@ -108,7 +108,9 @@ def update_flet_view_version_info( if k.name == "LegalCopyright": k.val = copyright if copyright else "" - print(vs) + 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) @@ -131,3 +133,5 @@ def update_flet_view_version_info( 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/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index 7aad9b4404..4727dfa21d 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -118,16 +118,17 @@ def handle(self, options: argparse.Namespace) -> None: update_flet_view_icon(str(exe_path), icon_path) # version info - if options.company_name or options.file_description: - 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, - ) + 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]) # run PyInstaller! PyInstaller.__main__.run(pyi_args) From f6d54835425525b9dd58473f74fed1da71cd2096 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 18 Dec 2022 16:19:38 -0800 Subject: [PATCH 08/17] Move Windows utils into a separate module --- sdk/python/flet/__pyinstaller/utils.py | 112 --------------------- sdk/python/flet/__pyinstaller/win_utils.py | 106 +++++++++++++++++++ sdk/python/flet/cli/commands/pack.py | 13 ++- 3 files changed, 114 insertions(+), 117 deletions(-) create mode 100644 sdk/python/flet/__pyinstaller/win_utils.py diff --git a/sdk/python/flet/__pyinstaller/utils.py b/sdk/python/flet/__pyinstaller/utils.py index 61d705b673..8041e515c5 100644 --- a/sdk/python/flet/__pyinstaller/utils.py +++ b/sdk/python/flet/__pyinstaller/utils.py @@ -4,23 +4,6 @@ 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.misc import decode -from PyInstaller.utils.win32.icon import IconFile, normalize_icon_type -from PyInstaller.utils.win32.versioninfo import ( - FixedFileInfo, - StringFileInfo, - StringStruct, - StringTable, - VarFileInfo, - VarStruct, - VSVersionInfo, - decode, -) - def get_flet_bin_path(): bin_path = os.path.abspath( @@ -40,98 +23,3 @@ def copy_flet_bin(): temp_bin_dir = Path(tempfile.gettempdir()).joinpath(str(uuid.uuid4())) shutil.copytree(bin_path, str(temp_bin_dir)) return str(temp_bin_dir) - - -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 %s", 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/__pyinstaller/win_utils.py b/sdk/python/flet/__pyinstaller/win_utils.py new file mode 100644 index 0000000000..e7f896b270 --- /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 %s", 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/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index 4727dfa21d..57b85ce7d7 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -4,11 +4,6 @@ from pathlib import Path import flet.__pyinstaller.config as hook_config -from flet.__pyinstaller.utils import ( - copy_flet_bin, - update_flet_view_icon, - update_flet_view_version_info, -) from flet.cli.commands.base import BaseCommand from flet.utils import is_windows @@ -91,6 +86,8 @@ def handle(self, options: argparse.Namespace) -> None: try: import PyInstaller.__main__ + from flet.__pyinstaller.utils import copy_flet_bin + pyi_args = [options.script, "--noconsole", "--noconfirm"] if options.onefile: pyi_args.extend(["--onefile"]) @@ -106,6 +103,12 @@ def handle(self, options: argparse.Namespace) -> None: 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" ) From 22a27820930f8bc2e6093276813086631711e4ac Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 25 Dec 2022 17:47:57 -0800 Subject: [PATCH 09/17] Update icon on macOS --- sdk/python/flet/__pyinstaller/macos_utils.py | 39 ++++++++++++++++++++ sdk/python/flet/cli/commands/pack.py | 16 +++++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 sdk/python/flet/__pyinstaller/macos_utils.py diff --git a/sdk/python/flet/__pyinstaller/macos_utils.py b/sdk/python/flet/__pyinstaller/macos_utils.py new file mode 100644 index 0000000000..427650dc91 --- /dev/null +++ b/sdk/python/flet/__pyinstaller/macos_utils.py @@ -0,0 +1,39 @@ +import os +import shutil +import tarfile +from pathlib import Path + +from PyInstaller.building.icon import normalize_icon_type + +from flet.utils import safe_tar_extractall + + +def update_flet_view_icon(tar_path, icon_path): + print("Updating Flet View icon", tar_path, icon_path) + + bin_dir = str(Path(tar_path).parent) + + # normalize icon + normalized_icon_path = normalize_icon_type( + icon_path, ("icns",), "icns", os.getcwd() + ) + + with tarfile.open(tar_path, "r:gz") as tar_arch: + safe_tar_extractall(tar_arch, bin_dir) + os.remove(tar_path) + + app_path = os.path.join(bin_dir, "Flet.app") + + # patch icon + print("Copying icons from %s", normalized_icon_path) + shutil.copy( + normalized_icon_path, + os.path.join(app_path, "Contents", "Resources", "AppIcon.icns"), + ) + + # 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) diff --git a/sdk/python/flet/cli/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index 57b85ce7d7..472c15e436 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -5,7 +5,7 @@ import flet.__pyinstaller.config as hook_config from flet.cli.commands.base import BaseCommand -from flet.utils import is_windows +from flet.utils import is_macos, is_windows class Command(BaseCommand): @@ -133,6 +133,20 @@ def handle(self, options: argparse.Namespace) -> None: pyi_args.extend(["--version-file", version_info_path]) + elif is_macos(): + from flet.__pyinstaller.macos_utils import update_flet_view_icon + + tar_path = Path(hook_config.temp_bin_dir).joinpath( + "flet-macos-amd64.tar.gz" + ) + if tar_path.exists(): + # 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(tar_path), icon_path) + # run PyInstaller! PyInstaller.__main__.run(pyi_args) From d7c9e3459d0c45979499b97c67f0e29c00cbab4b Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 26 Dec 2022 16:47:37 -0800 Subject: [PATCH 10/17] Patching plist and signing app bundle --- sdk/python/flet/__pyinstaller/macos_utils.py | 92 +++++++++++++++++--- sdk/python/flet/cli/commands/pack.py | 26 +++++- 2 files changed, 105 insertions(+), 13 deletions(-) diff --git a/sdk/python/flet/__pyinstaller/macos_utils.py b/sdk/python/flet/__pyinstaller/macos_utils.py index 427650dc91..e9318bc497 100644 --- a/sdk/python/flet/__pyinstaller/macos_utils.py +++ b/sdk/python/flet/__pyinstaller/macos_utils.py @@ -1,5 +1,7 @@ import os +import plistlib import shutil +import subprocess import tarfile from pathlib import Path @@ -8,28 +10,84 @@ from flet.utils import safe_tar_extractall -def update_flet_view_icon(tar_path, icon_path): - print("Updating Flet View icon", tar_path, icon_path) - +def unpack_app_bundle(tar_path): bin_dir = str(Path(tar_path).parent) - # normalize icon - normalized_icon_path = normalize_icon_type( - icon_path, ("icns",), "icns", os.getcwd() - ) - with tarfile.open(tar_path, "r:gz") as tar_arch: safe_tar_extractall(tar_arch, bin_dir) os.remove(tar_path) - app_path = os.path.join(bin_dir, "Flet.app") + 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 %s", normalized_icon_path) shutil.copy( normalized_icon_path, - os.path.join(app_path, "Contents", "Resources", "AppIcon.icns"), + os.path.join(app_path, "Contents", "Resources", icon_file), + ) + + # update icon file name + pl = __load_info_plist(app_path) + pl["CFBundleIconFile"] = icon_file + __save_info_plist(app_path, pl) + + +def update_flet_view_version_info( + app_path, + product_name, + product_version, + copyright, +): + print("Updating Flet View plist", app_path) + + pl = __load_info_plist(app_path) + + if product_name: + pl["CFBundleName"] = product_name + pl["CFBundleDisplayName"] = product_name + if product_version: + pl["CFBundleShortVersionString"] = product_version + if copyright: + pl["NSHumanReadableCopyright"] = copyright + + __save_info_plist(app_path, pl) + + +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: @@ -37,3 +95,17 @@ def update_flet_view_icon(tar_path, icon_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/cli/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index 472c15e436..0d3a654913 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -134,18 +134,38 @@ def handle(self, options: argparse.Namespace) -> None: pyi_args.extend(["--version-file", version_info_path]) elif is_macos(): - from flet.__pyinstaller.macos_utils import update_flet_view_icon + 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(str(tar_path), icon_path) + update_flet_view_icon(app_path, icon_path) + + # version info + version_info_path = update_flet_view_version_info( + app_path=app_path, + 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) @@ -155,7 +175,7 @@ def handle(self, options: argparse.Namespace) -> None: hook_config.temp_bin_dir ): print("Deleting temp directory:", hook_config.temp_bin_dir) - shutil.rmtree(hook_config.temp_bin_dir, ignore_errors=True) + # shutil.rmtree(hook_config.temp_bin_dir, ignore_errors=True) except ImportError: print("Please install PyInstaller module to use flet package command.") exit(1) From ae2a73cb51c9cb89478605621a5e770e06172e9c Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 26 Dec 2022 17:04:35 -0800 Subject: [PATCH 11/17] Remove icon name field --- sdk/python/flet/__pyinstaller/macos_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/python/flet/__pyinstaller/macos_utils.py b/sdk/python/flet/__pyinstaller/macos_utils.py index e9318bc497..0b61e930d1 100644 --- a/sdk/python/flet/__pyinstaller/macos_utils.py +++ b/sdk/python/flet/__pyinstaller/macos_utils.py @@ -40,6 +40,7 @@ def update_flet_view_icon(app_path, icon_path): # update icon file name pl = __load_info_plist(app_path) pl["CFBundleIconFile"] = icon_file + del pl["CFBundleIconName"] __save_info_plist(app_path, pl) From c651cf2ca05f43a33d341b9dd5ae205cca33f150 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 26 Dec 2022 17:11:04 -0800 Subject: [PATCH 12/17] Rename Flet.app to a custom bundle name --- sdk/python/flet/__pyinstaller/macos_utils.py | 7 +++++++ sdk/python/flet/cli/commands/pack.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/sdk/python/flet/__pyinstaller/macos_utils.py b/sdk/python/flet/__pyinstaller/macos_utils.py index 0b61e930d1..8530ef3336 100644 --- a/sdk/python/flet/__pyinstaller/macos_utils.py +++ b/sdk/python/flet/__pyinstaller/macos_utils.py @@ -57,6 +57,11 @@ def update_flet_view_version_info( 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: @@ -64,6 +69,8 @@ def update_flet_view_version_info( __save_info_plist(app_path, pl) + return app_path + def assemble_app_bundle(app_path, tar_path): diff --git a/sdk/python/flet/cli/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index 0d3a654913..f0128a0596 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -157,7 +157,7 @@ def handle(self, options: argparse.Namespace) -> None: update_flet_view_icon(app_path, icon_path) # version info - version_info_path = update_flet_view_version_info( + app_path = update_flet_view_version_info( app_path=app_path, product_name=options.product_name, product_version=options.product_version, From ebdc199f944344e47f94addaf3a16d21f86d7bb2 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 26 Dec 2022 17:30:03 -0800 Subject: [PATCH 13/17] Update CFBundleIdentifier --- sdk/python/flet/__pyinstaller/macos_utils.py | 5 ++++- sdk/python/flet/cli/commands/pack.py | 20 +++++++++++++------- sdk/python/flet/flet.py | 7 ++++++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/sdk/python/flet/__pyinstaller/macos_utils.py b/sdk/python/flet/__pyinstaller/macos_utils.py index 8530ef3336..57d7fce277 100644 --- a/sdk/python/flet/__pyinstaller/macos_utils.py +++ b/sdk/python/flet/__pyinstaller/macos_utils.py @@ -31,7 +31,7 @@ def update_flet_view_icon(app_path, icon_path): ) # patch icon - print("Copying icons from %s", normalized_icon_path) + print("Copying icons from", normalized_icon_path) shutil.copy( normalized_icon_path, os.path.join(app_path, "Contents", "Resources", icon_file), @@ -46,6 +46,7 @@ def update_flet_view_icon(app_path, icon_path): def update_flet_view_version_info( app_path, + bundle_id, product_name, product_version, copyright, @@ -54,6 +55,8 @@ def update_flet_view_version_info( pl = __load_info_plist(app_path) + if bundle_id: + pl["CFBundleIdentifier"] = bundle_id if product_name: pl["CFBundleName"] = product_name pl["CFBundleDisplayName"] = product_name diff --git a/sdk/python/flet/cli/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index f0128a0596..fd93e9d059 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -43,32 +43,37 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--product-name", dest="product_name", - help="executable product name", + help="executable product name (Windows, macOS)", ) parser.add_argument( "--file-description", dest="file_description", - help="executable file description", + help="executable file description (Windows)", ) parser.add_argument( "--product-version", dest="product_version", - help="executable product version (any string)", + help="executable product version, any string (Windows, macOS)", ) parser.add_argument( "--file-version", dest="file_version", - help="executable file version (n.n.n.n)", + help="executable file version, n.n.n.n (Windows)", ) parser.add_argument( "--company-name", dest="company_name", - help="executable companyname", + help="executable companyname (Windows)", ) parser.add_argument( "--copyright", dest="copyright", - help="executable copyright", + help="executable copyright (Windows, macOS)", + ) + parser.add_argument( + "--bundle-id", + dest="bundle_id", + help="bundle identifier (macOS)", ) def handle(self, options: argparse.Namespace) -> None: @@ -159,6 +164,7 @@ def handle(self, options: argparse.Namespace) -> None: # 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, @@ -175,7 +181,7 @@ def handle(self, options: argparse.Namespace) -> None: hook_config.temp_bin_dir ): print("Deleting temp directory:", hook_config.temp_bin_dir) - # shutil.rmtree(hook_config.temp_bin_dir, ignore_errors=True) + 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 65f93e64f8..dd1b92748c 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 From a631f9344eda071e1e8db893871c2dee70e5cf01 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 26 Dec 2022 17:43:57 -0800 Subject: [PATCH 14/17] --onefile is always forced --- sdk/python/flet/cli/commands/pack.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/sdk/python/flet/cli/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index fd93e9d059..9557109f34 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -15,14 +15,6 @@ class Command(BaseCommand): 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", - ) parser.add_argument( "-i", "--icon", @@ -43,7 +35,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--product-name", dest="product_name", - help="executable product name (Windows, macOS)", + help="executable product name (Windows) or bundle name (macOS)", ) parser.add_argument( "--file-description", @@ -53,7 +45,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--product-version", dest="product_version", - help="executable product version, any string (Windows, macOS)", + help="executable product version (Windows) or bundle version (macOS)", ) parser.add_argument( "--file-version", @@ -63,12 +55,12 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--company-name", dest="company_name", - help="executable companyname (Windows)", + help="executable company name (Windows)", ) parser.add_argument( "--copyright", dest="copyright", - help="executable copyright (Windows, macOS)", + help="executable (Windows) or bundle (macOS) copyright", ) parser.add_argument( "--bundle-id", @@ -93,9 +85,7 @@ def handle(self, options: argparse.Namespace) -> None: from flet.__pyinstaller.utils import copy_flet_bin - pyi_args = [options.script, "--noconsole", "--noconfirm"] - if options.onefile: - pyi_args.extend(["--onefile"]) + pyi_args = [options.script, "--noconsole", "--noconfirm", "--onefile"] if options.icon: pyi_args.extend(["--icon", options.icon]) if options.name: From 2e56b9bcf145d371119f499f277c62eb17a81494 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 26 Dec 2022 17:47:54 -0800 Subject: [PATCH 15/17] Update pack.py --- sdk/python/flet/cli/commands/pack.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/python/flet/cli/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index 9557109f34..a4aed1a423 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -92,6 +92,8 @@ def handle(self, options: argparse.Namespace) -> None: 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() From 80738c5235c91b0e850e6e63f65768b4058d0fad Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 26 Dec 2022 17:54:59 -0800 Subject: [PATCH 16/17] Update win_utils.py --- sdk/python/flet/__pyinstaller/win_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/flet/__pyinstaller/win_utils.py b/sdk/python/flet/__pyinstaller/win_utils.py index e7f896b270..8157fb5059 100644 --- a/sdk/python/flet/__pyinstaller/win_utils.py +++ b/sdk/python/flet/__pyinstaller/win_utils.py @@ -21,7 +21,7 @@ def update_flet_view_icon(exe_path, icon_path): icon_path, ("exe", "ico"), "ico", os.getcwd() ) icon = IconFile(normalized_icon_path) - print("Copying icons from %s", normalized_icon_path) + print("Copying icons from", normalized_icon_path) hdst = win32api.BeginUpdateResource(exe_path, 0) From 15a315b67da0f93933e30477b374473b9756bcc4 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Mon, 26 Dec 2022 18:11:40 -0800 Subject: [PATCH 17/17] Fix --name arg description --- sdk/python/flet/cli/commands/pack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/flet/cli/commands/pack.py b/sdk/python/flet/cli/commands/pack.py index a4aed1a423..5e36a2d4d5 100644 --- a/sdk/python/flet/cli/commands/pack.py +++ b/sdk/python/flet/cli/commands/pack.py @@ -25,7 +25,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: "-n", "--name", dest="name", - help="name for the generated executable", + help="name for the generated executable (Windows) or app bundle (macOS)", ) parser.add_argument( "--add-data",