Run python from a single exe (without needing to extract anything to disk).
This project uses reflective dll loading and either nim's staticRead
to load the python runtime from the executable itself, or optionally downloads the embedded zip on launch ("staged" mode).
Custom (python) import hooks are installed to support loading modules (both native python (.pyc) and extension modules (.pyd)) from the embedded standard library.
Or just download from from the releases page.
- Set up nim
- Download
python-3.10.1-embed-amd64.zip
to the project directory nimble build
- Run
onefile_python.exe
Potentially useful if you want a smaller exe.
nimble build -d:staged
- Run
onefile_python.exe
onefile_python
Usage:
onefile_python [options] [file] [arg ...]
Arguments:
[file] Program; read from script file/URL ('-' or empty for interactive) (default: )
[arg ...] Arguments passed to program in sys.argv[1:]
Options:
-h, --help
-V, --version
-c, --command=COMMAND Program; passed in as string
file
can be a http or https URL.
For staged version:
-d, --download=DOWNLOAD Download `python-3.10.1-embed-amd64.zip` from this url (default: https://www.python.org/ftp/python/3.10.1)
Alternatively specify the download URL in the app filename e.g. rename onefile_python.exe
to blabla(10.0.0.1)foobar.exe
to download python from https://10.0.0.1/python-3.10.1-embed-amd64.zip
. blabla
and foobar
can be any string.
- Build option for embedding a python file/module and running that on launch (instead of accepting file/interactive loop)
- Support other versions of python than
3.10.1
(autodetect?)
Both projects are better suited for bundling an application (and all its dependencies) to end users. They both support some form of dependency resolution so modules not required by the bundled don't get installed, while this project includes the entire standard library.
PyInstaller supports single exe mode, but this just extracts the runtime to a temporary directory. py2exe supports a diskless/"bundle" mode.
Both these projects are much more complex than this one and support lots of extra features, but sometimes you don't need that...
This project is (maybe) better if you want a single exe that can run any python script, or just want an exe that gives a python REPL. Being simpler, this project should be easier to hack on or learn from.
There's not much to it...
- Use nim's staticRead to include
python-*-embedded.zip
andbootstrap.py
inside compiled exe itself OR download the zip from a URL. - Use zippy to access the contents of the archive at runtime.
- Use memlib to perform reflective dll loading of the embedded
python*.dll
. Reflective dll loading allows for loading the dll from memory rather than from disk. HookLdrLoadDll
andK32EnumProcessModules
so other code using the dll can find it. n.b. currently using a fork until khchen/memlib#3 is merged. - Call various functions in the (reflectively) loaded dll to partially initialize python. Configure python to not try to load anything from disk (not absolutely required, but prevents conflicts and means the exe doesn't run any code in the current directory)
- Use nimpy to initialize a python extension exporting some nim functions that can read data out of the
python*.zip
standard library (contained within the...-embedded.zip
). - Run the embedded
bootsrap.py
code to install an import hook. This import hook uses the functions from (4) to support importing python modules. If a.pyc
can be found that matches an import, a loader that returns the unmarshalled.pyc
is provided. If a.pyd
can be found, the returned loader reflectively loads the.pyd
and calls the module's initialization routine. - Now that python's standard library can be imported, finish initializing python.
- Reflectively load other
.dlls
inside the...-embedded.zip
. This is required so extension modules that depend on these dlls work e.g.ctypes
needs_ctypes.pyd
which requireslibffi.dll
. - Run python code / REPL
No.
It uses reflective DLL loading, which is a technique some malware uses so that might upset particularly sensitive AVs. Like python itself, it could be used to run a malicious script.