Building on macOS
On macOS, pyzed build makes an app bundle, dist/Shop.app: a folder that Finder shows as one app, with
Python, Pyzed and your code inside.
Build the .app
Section titled “Build the .app”-
Install uv if you do not have it:
Terminal window curl -LsSf https://astral.sh/uv/install.sh | sh -
In the project folder, build:
Terminal window uv run pyzed buildTerminal window source .venv/bin/activatepyzed build # or: python -m pyzed build -
Try it:
Terminal window open dist/Shop.app
Icon and bundle identifier
Section titled “Icon and bundle identifier”macOS uses an .icns icon. Every Mac app also has a bundle identifier, a reverse domain name such as
com.acme.shop that macOS uses to keep its settings and permissions. Set both in pyproject.toml; the identifier
goes to PyInstaller through args:
[tool.pyzed.build]icon = "assets/icon.icns"args = ["--osx-bundle-identifier", "com.acme.shop"]See Icons and metadata to make an .icns from a PNG.
Sign and notarize
Section titled “Sign and notarize”Apps downloaded from the internet are checked by Gatekeeper. An app that is not signed and notarized is blocked (“Shop cannot be opened because Apple cannot check it for malicious software”). People can still open it with right-click → Open, or in System Settings → Privacy & Security → Open Anyway, but for a real release, sign and notarize it.
You need an Apple Developer Program membership, a Developer ID Application certificate in your keychain and an app-specific password for your Apple ID.
-
Sign the bundle with the hardened runtime and a timestamp:
Terminal window codesign --force --deep --options runtime --timestamp \--sign "Developer ID Application: Your Name (TEAMID)" dist/Shop.app -
Zip it with
ditto(it keeps the bundle’s symlinks and signature) and send it to Apple’s notary service:Terminal window ditto -c -k --keepParent dist/Shop.app Shop.zipxcrun notarytool submit Shop.zip --wait \--apple-id you@example.com --team-id TEAMID --password "$APPLE_APP_PASSWORD" -
Staple the ticket to the app, so it opens even offline:
Terminal window xcrun stapler staple dist/Shop.app
Check the result with spctl --assess --verbose dist/Shop.app (“accepted, source=Notarized Developer ID”).
A disk image (.dmg)
Section titled “A disk image (.dmg)”Mac users expect a .dmg with the app and a shortcut to Applications to drag it into.
mkdir -p build/dmgditto dist/Shop.app build/dmg/Shop.appln -s /Applications build/dmg/Applicationshdiutil create -volname Shop -srcfolder build/dmg -fs HFS+ -format UDZO -ov Shop-macos-arm64.dmgcodesign --force --timestamp --sign "Developer ID Application: Your Name (TEAMID)" Shop-macos-arm64.dmgNotarize and staple the .dmg the same way as the zip above. This is how Pyzed Design’s own .dmg is made (see
macos_dmg in scripts/build_designer.py).
Things to know
Section titled “Things to know”- Where it lives: people drag the app to
/Applications, which is writable by admin users, so the app can update itself there. - Quarantine: a downloaded app carries a quarantine flag until the first launch passes Gatekeeper. For your own
testing you can clear it with
xattr -dr com.apple.quarantine dist/Shop.app; do not ask your users to do this. - Permissions: if the app needs the camera, microphone or other protected resources, macOS asks the user. Add the
usage descriptions to the bundle’s
Info.plistwith PyInstaller’s spec file options;pyzed builddoes not set them for you.