PyInstaller on macOS frustration

I’ve experienced several issues trying to generate an installation on macOS:

  1. I was unable located two csv files that I put in the same directory containing main script, using the datas array. The main python script is run as an executable, on a mac after installation and the normal methods of getting location of the current script don’t work! Is this why there is the nudge to use the qt resources system in the tutorial?

  2. The size of installation was huge, in excess of 800Mb. I had assumed that pyinstaller just included imported modules, however in my case I found it had included modules relating to Jupyter notebook, QtNetwork, IPython, QtWebSockets none of which are referenced in my script. I am sure that none of my imports relied on these modules. After zipping 800Mb was reduced to a more manageable 277Mb.

  3. Is there a Packaging tool for macOS. The tutorial targets windows nicely. What is the solution for mac users?

Unfortunately I am forced to question whether PyInstaller is fit for purpose on macOS.

Hey, thanks for posting this. Unfortunately packaging Python applications remains a bit of a painful experience when your applications get more complicated/have more dependencies. There are however things you can do do to improve things. I’ll go through the questions one by one –

Paths in bundled apps

The path issue you mention is probably due to the “freezing process” for the app. See this in the PyInstaller documentation about how to get runtime information in a packaged app. See the note about file paths. If you use that approach for the paths, they should work identically bundled and unbundled.

How are you bundling your Mac app? To a single file, or a .app bundle? If you use an .app bundle, this is just a folder, so you can cd into it and have a look around.

This is why I hint towards resources, but you can definitely get it working without them.

Installation size

This is a consequence of how Python works – imports are dynamic, meaning you can import and access anywhere you want, conditionally, and from anywhere in your application. For example, if you import PyQt5 you can then access PyQt5.QtNetwork in your application without explicitly importing it. The bundlers avoid dealing with this by importing everything.

If you know there are modules you don’t not need, you can use PyInstallers --exclude-module (or excludes=[]) to exclude these from the bundle. Make sure you’re not bundling tests.

macOS specific packaging tools

I’ve had the most luck with PyInstaller on macOS, but have also used py2app and cx_freeze with success. That said, they use the same bundling/freezing approach, and also produce the same size binaries.