{"id":28452904,"url":"https://github.com/marketsquare/compact_testprogram_distribution","last_synced_at":"2025-06-27T23:31:19.680Z","repository":{"id":152319105,"uuid":"617998328","full_name":"MarketSquare/compact_testprogram_distribution","owner":"MarketSquare","description":"Zipapps ","archived":false,"fork":false,"pushed_at":"2025-05-21T20:39:53.000Z","size":64,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-06T18:13:08.282Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MarketSquare.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-03-23T14:45:31.000Z","updated_at":"2025-05-21T20:39:57.000Z","dependencies_parsed_at":"2025-05-20T16:51:38.277Z","dependency_job_id":null,"html_url":"https://github.com/MarketSquare/compact_testprogram_distribution","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/MarketSquare/compact_testprogram_distribution","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarketSquare%2Fcompact_testprogram_distribution","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarketSquare%2Fcompact_testprogram_distribution/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarketSquare%2Fcompact_testprogram_distribution/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarketSquare%2Fcompact_testprogram_distribution/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MarketSquare","download_url":"https://codeload.github.com/MarketSquare/compact_testprogram_distribution/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarketSquare%2Fcompact_testprogram_distribution/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262351468,"owners_count":23297627,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-06T18:13:07.813Z","updated_at":"2025-06-27T23:31:19.668Z","avatar_url":"https://github.com/MarketSquare.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Compact testprogram distribution\n\nZipapps are a sensible way to be able to distribute test programs with reasonable dependencies.\n\nFor executing Robot Framework in order to perform production facility / test floor automated tasks\nthe dependency list becomes.:\n\n - The operating system\n - The non python drivers for the used equipment\n - python itself\n\n## Zipapps with no native code\n\nThe only dependency is a python interpreter with a minimum version number, and the limitation \nthat only zipapp compatible dependencies can be used.\n\nFor python versions 3.10 and 3.11 it is recommended to disable zipimport.zipimporter.invalidate_caches \nas it has a large performance impact. Disabling it, loses the feature to modify the zipapp during\nruntime...\n\n### Using pdm with pdm-packer\n\nA python only zipapp without any native code.\n\n``` bash\n    $ cd examples\\basic\n    $ pdm install \n    $ pdm pack -m runway:main\n    $ py zipapprobot.pyz .\n```\nAt this point you are presented with robot output... \n\n### Minimal zipapp build (a.k.a. Limbo Mode)\n\nlet’s reduce the size of the zipapp.:\n\n``` bash\n    $ cd examples\\basic\n    $ pdm install \n    $ pdm pack -m runway:main -c --compile\n    $ py  zipapprobot.pyz .\n```\n\nThis results in a zipapp of less than 2MB in size, making it highly portable.\n\n## Performance impact\n\nThe limbo zipapp is faster than the native version on my setup for this example.\n\n``` powershell\n    $sw = [Diagnostics.Stopwatch]::StartNew()\n    pdm.exe run robot .\n    $sw.Stop()\n    $sw.Elapsed\n    $sw = [Diagnostics.Stopwatch]::StartNew()\n    py zipapprobot.pyz .\n    $sw.Stop()\n    $sw.Elapsed\n```\n\n## Zipapps with native code\n\nThis is not supported, and generally a bad idea. \n\nHowever this is technically possible by using importlib.util, and either https://github.com/SeaHOH/memimport or importlib.resources. Thus said, this is hacky\nand a bit off topic. There is an example of how this _can_ be achieved. In the example directory, it is a proof that this can generally be achieved, but\nnothing more than that.\n\nThe feasibility of this approach heavily depends on the specific native code. For instance, polars works relatively well, whereas numpy presents significant challenges.\n\n## cx_freeze\n\ncx_freeze can create many targets, the one I personally find most interesting is bdist_appimage (currently linux only). The resulting file with 34MBytes is reasonably\nclose to the size of a python installer (28MBytes), and is a self contained, single file executable. Being dependent only of the operating system and drivers.\n\ncx_freeze does use script entry points.\n\nRobot Framework does come with a script, which is documented [here](https://robot-framework.readthedocs.io/en/latest/autodoc/robot.html#module-robot.run).\n\nWhat is not documented (and as far as I understand from this [discussion](https://github.com/robotframework/robotframework/issues/5384) will not be, this\n[pull request](https://github.com/robotframework/robotframework/pull/5390) was provided to remove pythonpathsetter), is that this script needs to live in \nthe source tree, and can not use an installed Robot Framework, like in the zipapp/frozenapp/etc use case.\n\n### background\nIt is possible to use Robot Framework straight from the source tree, without installation or configuring PYTHONPATH. This feature is not documented in the \nend user documentation, and not explained in the CONTRIBUTING.rst.\n\nThis is implemented using the `pythonpathsetter` module which can be loaded by `import pythonpathsetter` when the Robot Framework is run from the script,\nadditionally by `import robot.pythonpathsetter` when the script runs inside an environment where Robot Framework was installed into, and only using \n`import robot.pythonpathsetter`. This module changes the the `sys.path` at runtime, which can cause severe confusion when debugging dependency issues.\n\nThese are the symptoms to look out for.:\n 1 is that Robot Framework fails, as pythonpathsetter can not be imported. \n 2 you can use ```import robot; robot.run()``` when debugging from a REPL.\n\n In total at the time of writing there are 34 instances of ```sys.path``` and 89 ```__file__```in the source, all of them bring the risk of causing issues \n with zipapp/frozenapp usage. When I went over the source the first time, I missed how the ```pythonpathsetter``` can cause issues.\n\n### solutions\n#### make sure pythonpathsetter exits\nInstall a  ```pythonpathsetter.py``` into your environment. Be aware future releases of Robot Framework will call a ```pythonpathsetter.set_pythonpath``` fucntion.\nThis function is in the source tree where you would expect ```robot.pythonpathsetter.set_pythonpath```. It doesn’t need to do anything but if it is missing the code \nwill not work. This change is introduced in response to linter messages.\n\n#### do not use Robot Framework distributed scripts\nProvide your own start script.\n\n#### [merge](https://github.com/robotframework/robotframework/pull/5390) (would need to come from upstream)\nApart of improving the situation for frozenapp/zipapp use cases this brings these advantages.:\n\n 1) Reduction of total complexity (cyclomatic complexity goes from 18329 to 18307 a delta of 22, number of lines from 58033 to 57967, a delta of 134)\n 2) The ```pythonpathsetter``` feature is not advertised. It is likely that its most common use is in Robot Framework quality control.\n    - if that would make testing easier/faster for multiple python installations that might be worth the risk.\n    - solutions like [uvx](https://docs.astral.sh/uv/) ```uvx --python 3.14 --from . --reinstall robot``` are available, taking care of warehousing the python versions.\n    - Virutal environments are cheap, get two.\n    - Have you heard of ziapps? You can have the Robot Framework version you like installed into a single file, and then use the python of choice to execute it!\n    - Have you heard of frozen apps? You can have Robot Framework with python frozen together ahead of time!\n 3) The fact that Robot Framework \"script edition\" ignores installed Robot Framework but requires to be part of the Robot Framework source tree\n    are two unusual properties, which can cause confusion.\n      \n It is hard to get this point across, but the \"unpack into a directory\" distribution method is very much outdated by now.\n\n#### do not modify the ```sys.path``` (would need to come from upstream)\nPlacing this code into the src directory, next to the robot directory of the source distribution. Using this code allows the robot code base to reduce \nthe modifications of the ```sys.path```, while keeping the feature to use Robot Framework straight from the source directory.\n\n```python\nimport pathlib\n\nif __name__ == \"__main__\":\n    try:\n        source = (pathlib.Path(__file__).parent / \"robot\" / \"run.py\")\n        with source.open() as run:\n            source_code = run.read()\n    except Exception as e:\n        print(f\"\"\"run_rf is a tool allowing you to start Robot Framework from a source tree without installing anything. Exception {e} occurred.\n              \n              This script should not be used outside of Robot Framework development, and is not part of Robot Framework itself.\"\"\")\n    exec(source_code)\n```\n\n### conclusion\nThere are alternatives available.\n\n - docker (way more heavy weight, user rights need to be managed)\n - astral [uv](https://docs.astral.sh/uv/) ```uvx --from robotframework==7.2.2 --with numpy==2.2.6 robot``` (not a single file, needs access to wheels...)\n - ...\n\nI see value in frozen/zipapps and encourage everyone who is interested to tinker with them in an effort to learn. They are often faster than regular\nenvironments, easier to deploy and manage. However I would not recommend using them in a productive environment.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarketsquare%2Fcompact_testprogram_distribution","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarketsquare%2Fcompact_testprogram_distribution","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarketsquare%2Fcompact_testprogram_distribution/lists"}