I am in environment my_project
My folder structure looks something like this
.
`-- my_project/
|-- my_app/
| |-- __init__.py
| `-- __main__.py
|-- setup.py
|-- README.md
`-- build.sh
where __init__.py
is empty and
__main__.py
def main():
print('Hello World')
if __name__ == '__main__':
main()
and
setup.py
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="zipapp_demo",
version="0.0.1",
author="user1291",
author_email="yeah,not posting that here",
description="Some demo app",
long_description=long_description,
long_description_content_type="text/markdown",
packages=setuptools.find_packages(),
install_requires=[
],
classifiers=[
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
],
python_requires='>=3.10',
)
because I have dependencies that rely on extension modules (cf. How do you install a generated openapi client to a zipapp? ) , I want to use shiv
to build my app.
So build.sh
contains
python setup.py sdist bdist_wheel
SETUP_EXIT_CODE=$?
if [ $SETUP_EXIT_CODE -ne 0 ]; then
exit $SETUP_EXIT_CODE
fi
shiv
.
-o shiv_app.pyz
--root out
-e zipapp_demo.__main__:main
--site-packages ~/anaconda3/envs/zipapp_demo/lib/python3.10/site-packages/
so far, this works. And the resulting shiv_app.pyz
correctly outputs
Hello World
regardless of whether I run it in the my_project
environment or activate a base
environment before I run it.
Let’s slightly tweak __main__.py
from openapi_client import ApiClient, Configuration
def main():
print('Hello world')
if __name__ == '__main__':
main()
What is open_api_client
? Why it’s the generated client.
More specifically, I ran openapitools/openapi-generator-cli with a python
generator and then installed the output to the environment:
OUT_ABSOLUTE="<path to generated openapi client>"
sudo chown -R "${USER}" "${OUT_ABSOLUTE}"
pip install "${OUT_ABSOLUTE}" --force-reinstall
NOW, if I run ./shiv_app.pyz
in the my_project
environment (where I installed the generated openapi client), it outputs
Hello World
as it should.
BUT if I run it in a different (e.g. base
environment) – which, you know, is the whole point why I’m bothering with all this zipapp stuff … I instead get
Traceback (most recent call last):
File "/home/user1291/code/my_project/./shiv_app.pyz/_bootstrap/__init__.py", line 76, in import_string
File "/home/user1291/code/my_project/out/shiv_app.pyz_407216d2bc06ad19a6a476be4b28081b1673d69730db8ea575cc93921d0ab451/site-packages/zip_app_demo/__main__.py", line 1, in <module>
from openapi_client import ApiClient, Configuration
File "/home/user1291/code/my_project/out/shiv_app.pyz_407216d2bc06ad19a6a476be4b28081b1673d69730db8ea575cc93921d0ab451/site-packages/openapi_client/__init__.py", line 20, in <module>
from openapi_client.api.aa_sequences_api import AASequencesApi
File "/home/user1291/code/my_project/out/shiv_app.pyz_407216d2bc06ad19a6a476be4b28081b1673d69730db8ea575cc93921d0ab451/site-packages/openapi_client/api/__init__.py", line 4, in <module>
from openapi_client.api.aa_sequences_api import AASequencesApi
File "/home/user1291/code/my_project/out/shiv_app.pyz_407216d2bc06ad19a6a476be4b28081b1673d69730db8ea575cc93921d0ab451/site-packages/openapi_client/api/aa_sequences_api.py", line 15, in <module>
from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt
File "/home/user1291/code/my_project/out/shiv_app.pyz_407216d2bc06ad19a6a476be4b28081b1673d69730db8ea575cc93921d0ab451/site-packages/pydantic/__init__.py", line 404, in __getattr__
module = import_module(module_name, package=package)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user1291/anaconda3/lib/python3.12/importlib/__init__.py", line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user1291/code/my_project/out/shiv_app.pyz_407216d2bc06ad19a6a476be4b28081b1673d69730db8ea575cc93921d0ab451/site-packages/pydantic/validate_call_decorator.py", line 8, in <module>
from ._internal import _typing_extra, _validate_call
File "/home/user1291/code/my_project/out/shiv_app.pyz_407216d2bc06ad19a6a476be4b28081b1673d69730db8ea575cc93921d0ab451/site-packages/pydantic/_internal/_validate_call.py", line 7, in <module>
import pydantic_core
File "/home/user1291/code/my_project/out/shiv_app.pyz_407216d2bc06ad19a6a476be4b28081b1673d69730db8ea575cc93921d0ab451/site-packages/pydantic_core/__init__.py", line 6, in <module>
from ._pydantic_core import (
ModuleNotFoundError: No module named 'pydantic_core._pydantic_core'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/user1291/code/my_project/./shiv_app.pyz/_bootstrap/__init__.py", line 84, in import_string
AttributeError: module 'zip_app_demo' has no attribute '__main__'. Did you mean: '__name__'?
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/home/user1291/code/my_project/./shiv_app.pyz/__main__.py", line 3, in <module>
File "/home/user1291/code/my_project/./shiv_app.pyz/_bootstrap/__init__.py", line 253, in bootstrap
File "/home/user1291/code/my_project/./shiv_app.pyz/_bootstrap/__init__.py", line 81, in import_string
File "/home/user1291/code/my_project/./shiv_app.pyz/_bootstrap/__init__.py", line 87, in import_string
ImportError: module 'zip_app_demo' has no attribute '__main__'
so yeah, even switching from zipapp
to shiv
to bundle my application, I still get the
ModuleNotFoundError: No module named 'pydantic_core._pydantic_core'
Which as far as I can tell is actually included in the shiv app. At least once extracted for execution, it shows up:
So my question about this is twofold:
- why does the behaviour differ depending on which environment is active?
- how do I resolve this and actually build a zipapp that I can execute on my server without having to install additional dependencies to that server?