Freezes Python scripts into Windows apps
pip install kelvin==2.0.1
Freezes Python 3.6+ code into a Windows executable.
There are other projects that do this, but none were an exact fit for something simple.
Kelvin contains two executables which are built from C++ source, so use a binary wheel when possible:
pip install kelvin
If you want to build it yourself, install Visual Studio Build Tools 2017.
Note: Be sure to choose the Windows 8 SDK in the installer, not the Windows 10 SDK. The Windows 10 SDK does not include rc.exe (or it is not in the path), so the linking will fail with:
LINK : fatal error LNK1158: cannot run 'rc.exe'
Import the kelvin Builder class and pass the appropriate options to its constructor. Then call build():
from kelvin.builder import Builder b = Builder(script='src\\prog.py', dist='dist') b.build()
This example creates the executable dist\prog.exe
by analyzing the source for prog.py.
Below are the Builder constructor parameters. Only script
, dist
, and version
are
required.
A string that determines the file version like "1.2.3.4". Since this is put into the executable's version resource, it can only contain numbers and periods. It cannot contain things like "-rc1". This is a Windows limitation.
Windows versions always have 4 parts. If you specify fewer, ".0" will be appended for each missing part.
version_strings
Strings that are also included in the version resource and show up on the "Details" tab of the executable's property sheet. These are provided a dictionary mapping a language id, such as 0x0409 for U.S. English, to another dictionary mapping from key to value:
version_strings = { 0x0409: { 'ProductName': 'product', 'ProductVersion' : '1.2', 'FileVersion': '1.2.3', 'FileDescription': 'Descriptiion' } }Note: The FileVersion entry is a string, but it must only contain 1-4 numbers. If you put other things there, it causes the version information to be silently ignored.
script
parameter, but a different
filename can be supplied here. This should not contain a path - the resulting executable
will be put into the dist
directory.sys.path
, so I recommend using virtual
environments to ensure you don't include global items.If you have namespace packages that are split across multiple directories, you'll need to pass them here. Kelvin does not actually run your code, so utilities like pkgutils.extend_path won't take effect.
Provide a dictionary mapping from package name to a sequence of directories:
package_paths={'mylib': ('lib\\mylib1', 'lib\\mylib2')}
A sequence of non-code files to be put into the executable. Each entry can be a relative filename, which will be copied into the executable with the same relative name, or a tuple pair containing the source path and the path to use in the archive:
extra = [ 'data\\schema.json', ('..\\docs\\README.rst', 'data\\README.rst) ]
This would include the put both files in the executable in a data directory.
To retrieve these files at runtime, open the executable (sys.executable
) as a zip file
using the zipfile package.
A logging.Logger instance for Kelvin to output to. If not provided, a logger named "kelvin" is used.
Kelvin outputs very little at the INFO level. It outputs more at the DEBUG level which may
be useful for troubleshooting. It also very detailed information at level 1, though this is
most likely of interest for Kelvin development. (There is no constant like "TRACE" for this,
so use logger.setLevel(1)
.)
Python's built-in ModuleFinder class is used to analyze your source to find all modules it uses.
A precompiled executable is copied into the distribution directory. All needed Python modules
are compiled into a zip file which is appended to the executable. On startup, the executable
puts itself into sys.path
and Python will load modules from it normally like any other zip
file. (Interestingly, zip files are processed starting from the end, so we have a zip file
with "garbage" (the executable) at the beginning which is ignored.)
Extension modules are actually DLLs, so they are copied, along with any dependencies, into the
distribution directory, which is also added to sys.path
.
This project used to support Python 2.7+, but I am now only supporting Python 3.6+ since it no longer requires messing with Windows manifest files. (It is possible that change was made in Python in 3.5.) If you need a manifest file, you can either add it after the executable is complete or you can put it in the same directory as the executable.