freezer, installer and updater for Python applications
Project description
PyShip
Enables shipping a python application to end users.
PyShip's Major Features
- Freeze practically any Python application.
- Optional Code Signing (avoid Windows "Unknown Publisher" warning). This is not required, and you can ship your application for free, but code signing is supported if you have a Code Signing certificate (typically an additional cost).
- Installs via Microsoft Windows App Store or self-hosted installers entirely outside the app store.
- Automatic application updating in the background (no user intervention).
- OS native application (e.g., .exe for Windows).
- Run on OS startup option.
Currently only for Windows. May be extended to other Operating Systems in the future.
Configuration
pyship settings are configured in your project's pyproject.toml under [tool.pyship].
pyproject.toml
[tool.pyship]
is_gui = false # true if the app is a GUI application (default: false)
run_on_startup = false # true to run the app on OS startup (default: false)
AWS Keys in CLI Arguments
The user may optionally provide their AWS keys as CLI arguments. They may also be provided in the ~/.aws/credentials
file (typical for AWS CLI and boto3). These are not in pyproject.toml since it is usually checked into the code repo.
| Argument | Description |
|---|---|
-i, --id |
AWS Access Key ID |
-s, --secret |
AWS Secret Access Key |
Microsoft Windows Code Signing (Optional)
Signing your executables suppresses the Windows SmartScreen "Unknown Publisher" warning. pyship signs two files: the
launcher stub ({app_name}.exe) and the NSIS installer ({app_name}_installer_*.exe). The launcher is signed before
NSIS packages it, so the signed binary ends up inside the installer.
You need an Authenticode code-signing certificate in PFX format, a password for that PFX file, and signtool.exe from the Windows SDK.
Getting a code-signing certificate
An Authenticode certificate identifies you (or your organisation) as the publisher of the software. Windows uses it to suppress SmartScreen warnings and to display your name in UAC prompts and Add/Remove Programs.
Certificate types
| Type | SmartScreen behaviour | Typical cost | Notes |
|---|---|---|---|
| OV (Organisation Validation) | Builds reputation over time; new certs still trigger SmartScreen until enough installs accumulate | ~$200–400/yr | Issued to a verified company or individual. Most common for open-source and small commercial projects. |
| EV (Extended Validation) | Immediate SmartScreen reputation from the first install | ~$400–600/yr | Only for registered organizations (Corporations). Requires a hardware token (USB) or cloud HSM. Strongest trust signal. |
Where to buy
tldr:
Individuals, ~$300-400/year: Sectigo Code Signing Certificate .
Existing organizations (registered LLCs, Corporations, etc.), ~$400-500/year: Sectigo EV Code Signing Certificate .
Other options
Certificates are issued by Certificate Authorities (CAs). Common options include:
- SSL.com — offers OV and EV code-signing certificates; EV certificates can be stored on their cloud-based eSigner HSM, making them usable in CI without a physical USB token.
- DigiCert — popular for EV certificates; offers KeyLocker (cloud HSM) for CI integration.
- Sectigo (formerly Comodo) — widely used OV and EV code-signing certificates.
- SignPath — free certificates for open-source projects via their Foundation programme.
The purchase process typically involves:
- Choose a CA and certificate type (OV or EV).
- Complete identity verification (business registration documents for OV; additional legal vetting for EV).
- The CA issues a
.pfx(PKCS #12) file containing your private key and certificate chain, or provides access via a hardware token / cloud HSM.
Verification usually takes 1–5 business days for OV, and 1–2 weeks for EV.
The PFX file and password
The .pfx file (also called .p12) is an encrypted archive that bundles your private signing key with the certificate.
The PFX password (also called the export password) protects this file — anyone with both the file and the password
can sign software as you.
Creating a PFX from separate files
If your CA provided a .crt certificate and a .key private key separately (common with some CAs), combine them into
a PFX:
openssl pkcs12 -export -out certificate.pfx -inkey private.key -in certificate.crt -certfile ca_chain.crt
OpenSSL will prompt you to set a password — this becomes your PFX password.
Extracting certificate info from a PFX
To view the certificate subject (needed for msix_publisher):
certutil -dump certificate.pfx
Security best practices
- Never commit the
.pfxfile to your source repository. Add*.pfxto.gitignore. - Store the PFX password in a secrets manager or CI secrets — not in source code, environment files, or scripts.
- For CI, base64-encode the PFX file and store it as a CI secret, then decode it at build time:
- name: Decode signing certificate run: echo "${{ secrets.PFX_BASE64 }}" | base64 --decode > certificate.pfx - name: Ship env: PFX_PASSWORD: ${{ secrets.PFX_PASSWORD }} run: python ship.py
- Rotate certificates before expiry. Most code-signing certificates are valid for 1–3 years.
Getting signtool.exe
Install the Windows SDK and select Windows SDK
Signing Tools for Desktop Apps. pyship auto-discovers the newest version under
C:\Program Files (x86)\Windows Kits\10\bin\.
Basic usage
from pathlib import Path
from pyship import PyShip
ps = PyShip(
project_dir=Path("path/to/your/app"),
pfx_path=Path("path/to/certificate.pfx"),
certificate_password="your-pfx-password",
)
ps.ship()
CI / environment variable password
Avoid storing the PFX password in source code. Pass it via an environment variable instead:
ps = PyShip(
project_dir=Path("path/to/your/app"),
pfx_path=Path("path/to/certificate.pfx"),
certificate_password_env_var="PFX_PASSWORD", # reads os.environ["PFX_PASSWORD"] at ship() time
)
ps.ship()
Set the secret in your CI system (e.g. GitHub Actions → Settings → Secrets) and expose it in your workflow:
- name: Ship
env:
PFX_PASSWORD: ${{ secrets.PFX_PASSWORD }}
run: python ship.py
Explicit signtool path
If signtool.exe is not in the default Windows SDK location, point pyship directly at it:
ps = PyShip(
...
signtool_path=Path(r"C:\custom\path\signtool.exe"),
)
Timestamp server
By default pyship uses DigiCert's RFC 3161 server (http://timestamp.digicert.com). Override it with the
timestamp_url field:
ps = PyShip(
...
timestamp_url="http://timestamp.sectigo.com",
)
Verifying a signed executable
signtool verify /pa /v YourApp.exe
Skipping signing
Leave pfx_path and certificate_password / certificate_password_env_var unset (the defaults). pyship will build and
package the executables without signing them.
Microsoft Store Distribution (MSIX) (Optional)
The Microsoft Store is an alternative distribution channel that gives users a trusted, one-click install experience and
eliminates SmartScreen warnings entirely. pyship can produce both a traditional NSIS installer and an MSIX package
in a single ship() call.
Prerequisites
| Requirement | Notes |
|---|---|
| Windows SDK | Provides makeappx.exe and signtool.exe. Install from developer.microsoft.com/windows/downloads/windows-sdk and select Windows SDK Signing Tools for Desktop Apps. pyship auto-discovers both tools. |
| Authenticode certificate | Same PFX used for code signing. The Publisher field in the MSIX manifest must exactly match the certificate subject. Self-signed certificates work for sideloading and testing but not for Store submission. |
| Microsoft Partner Center account | Required for Store submission only. Register at partner.microsoft.com. One-time $19 fee for individuals. |
Basic usage
Set msix=True and supply the certificate subject DN as msix_publisher:
from pathlib import Path
from pyship import PyShip
ps = PyShip(
project_dir=Path("path/to/your/app"),
pfx_path=Path("certificate.pfx"),
certificate_password_env_var="PFX_PASSWORD",
msix=True,
msix_publisher="CN=My Company, O=My Company LLC, C=US", # must match cert subject exactly
)
ps.ship()
This produces two files in installers/:
{app_name}_installer_win.exe— the NSIS installer (direct distribution, auto-updates via pyshipupdate){app_name}_installer_win.msix— the MSIX package (Store or sideloading, updates managed by the Store)
Both are signed with the same certificate. The NSIS installer is built first; the MSIX is packed from the same app directory afterwards, so they do not interfere with each other.
Finding the msix_publisher string
msix_publisher must be the exact Distinguished Name of your signing certificate's subject. To read it from your PFX:
certutil -dump certificate.pfx
Look for the Subject: line, e.g. CN=My Company, O=My Company LLC, C=US. Copy it verbatim — any difference will cause
installation to fail.
Store logo assets
MSIX packages require three PNG logo files. pyship generates 1×1 white placeholder PNGs automatically, which is sufficient for testing and sideloading. For Store submission, provide real assets at the correct sizes:
| File | Size |
|---|---|
StoreLogo.png |
50×50 |
Square44x44Logo.png |
44×44 |
Square150x150Logo.png |
150×150 |
Point pyship at a directory containing these files with store_assets_dir:
ps = PyShip(
...
msix=True,
msix_publisher="CN=My Company, O=My Company LLC, C=US",
store_assets_dir=Path("store_assets"),
)
Any asset not found in that directory falls back to the placeholder PNG automatically.
Explicit makeappx path
If makeappx.exe is not in the default Windows SDK location:
ps = PyShip(
...
makeappx_path=Path(r"C:\custom\path\makeappx.exe"),
)
Sideloading (without the Store)
Signed MSIX packages can be installed directly without going through the Store — double-click the .msix file or use:
Add-AppxPackage -Path "{app_name}_installer_win.msix"
This is useful for enterprise deployments and beta distribution.
Submitting to the Microsoft Store
- Log in to Partner Center.
- Go to Windows & Xbox → Overview → Create a new app and reserve your app name.
- Under Submissions → New submission, complete the store listing (description, screenshots, age rating, pricing).
- Upload your signed
.msixfrom theinstallers/directory under Packages. - Submit for certification. Review typically takes 1–3 business days.
Limitations
run_on_startup = trueis not supported in MSIX builds. The MSIX runtime requires aStartupTaskextension in the manifest; pyship logs a warning and continues, but the app will not auto-start. Configure startup behaviour manually in a custom manifest if needed.- MSIX packages installed via the Store are updated by the Store, not by pyshipupdate. The self-update logic in pyshipupdate is silently inactive when running inside an MSIX container.
NSIS vs. MSIX comparison
| NSIS installer | MSIX | |
|---|---|---|
| Distribution | Your own URL / S3 | Microsoft Store or sideload |
| SmartScreen | Suppressed with EV cert | Not shown (implicitly trusted) |
| Auto-update | pyshipupdate | Store manages updates |
| Review required | No | Yes (Store only; ~1–3 days) |
| Revenue share | None | 15% (>$1M/yr: 12%) |
| Startup support | Yes | Requires manifest extension |
Testing
Run tests with:
venv\Scripts\python.exe -m pytest test_pyship/ -v
Environment Variables
| Variable | Description | Default |
|---|---|---|
AWSIMPLE_USE_MOTO_MOCK |
Set to 0 to use real AWS instead of moto mock. Required for test_f_update which tests cross-process S3 updates. Requires valid AWS credentials configured. |
1 (use moto) |
MAKE_NSIS_PATH |
Path to the NSIS makensis.exe executable. |
C:\Program Files (x86)\NSIS\makensis.exe |
Test Modes
- Default (moto mock): All tests run with mocked AWS S3. No credentials needed.
test_f_updateis skipped. - Real AWS (
AWSIMPLE_USE_MOTO_MOCK=0): All tests run against real AWS S3.test_f_updateruns and tests cross-process updates.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyship-0.6.1-py3-none-any.whl.
File metadata
- Download URL: pyship-0.6.1-py3-none-any.whl
- Upload date:
- Size: 55.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78ef746644b69fb45ea5c68f970df47085d73355014f61e38e261eeac9f65ba5
|
|
| MD5 |
db9f4629b97a250e4aef85c5a2dfd26b
|
|
| BLAKE2b-256 |
a079d5e990b7b81273d18e548030f6ae8129ab8438d1530f6e54277a880608e1
|