I write a lot of small Python tools, and the moment one of them is useful, someone without Python asks for it. Sending a .py file to that person starts an afternoon of "what is PATH?" over chat.
Here is what I have learned about turning a Python script into something a non-programmer can double-click on Windows, the traps that bite every time, and the free tool I built to make the common case one click.
Your options, honestly
- PyInstaller. The standard.
pyinstaller --onefile --windowed --icon app.ico app.pygets you far. The cost is learning its flags, spec files and hidden-import fixes when a package is not detected. - Nuitka. Compiles your code through C. It can produce faster and harder-to-unpack programs, at the price of a C compiler and much longer builds.
- Ship Python with the script. The embeddable Python zip plus a .bat launcher works and is easy to debug, but it is a folder of files, not one program.
- A GUI wrapper. Point, click, get an .exe. That is the gap Py2Win FREE fills for me.
No option is wrong. The right one depends on how often you build and how much you want to touch configuration.
The traps that bite every time
1. It works on your machine because your machine has everything. Test the .exe on a clean Windows install or a fresh virtual machine, never on the PC you built it on. Missing DLLs and data files only show up there.
2. One file or one folder. A single .exe is easier to send, but it unpacks itself to a temp folder on every launch, so it starts slower. For a tool people open once a day, nobody notices. For something launched constantly, a folder build feels snappier.
3. Console or windowed. A Tkinter app built in console mode opens a black window next to it. A command-line script built in windowed mode shows nothing at all, and its errors vanish. Pick on purpose.
4. Paths to your own files. open("data.json") looks in the current working directory, which is not where your .exe lives. Build paths from the location of the program instead, and handle the bundled case where the files are unpacked somewhere else.
5. Antivirus and SmartScreen. Packed Python programs are a common false positive, and any unsigned .exe from an independent developer can trigger Windows SmartScreen ("More info", then "Run anyway"). Code signing is the real fix; until then, tell your users before they see the warning.
6. Size. A tiny script can still produce a large .exe, because the interpreter and every imported package come along. Import only what you use, and build from a clean virtual environment so stray packages are not bundled.
The one-click route
I built Py2Win FREE for the everyday case: a script I want to hand to a co-worker, a client or a student today.
- Drag and drop your .py file, or pick it.
- Automatic dependency detection.
- Your own .ico, or a generated icon.
- Console mode or windowed mode.
- One portable .exe as the output, usually 10 to 30 MB.
- Windows 10 and 11. No license key, no account, unlimited conversions, free.
It does not replace PyInstaller when you need fine control. It removes the configuration when you do not.
If what you built is a web page rather than a script (an HTML, CSS and JavaScript tool), I wrote about that case too: you probably do not need Electron to ship your HTML app as a desktop program. HTML2Win Pro is the tool I sell for that one.
What is the worst packaging bug you have hit? I would like to add it to the list.
I make Py2Win FREE and HTML2Win Pro. An AI assistant wrote this post on my behalf.