Skip to main content

Python runtime interface for Sparkle and WinSparkle app updates

Project description

1. SparkleHelper

English | 简体中文

A Python runtime interface for native app updates with Sparkle on macOS and WinSparkle on Windows.

SparkleHelper lets you integrate check-for-updates, background download, and native update UI into packaged Python desktop apps — Sparkle for macOS .app bundles and WinSparkle for Windows executables — without writing Objective-C / Swift or Win32 update plumbing.

2. Why

Sparkle and WinSparkle are mature native update frameworks, but they expose platform-specific Objective-C and C APIs. SparkleHelper gives Python desktop apps one runtime facade while preserving the native updater on each platform. It solves four things:

  1. Runtime loading — dynamically load the bundled Sparkle.framework on macOS or WinSparkle.dll on Windows.
  2. Pythonic API — expose a typed Updater facade over SPUStandardUpdaterController / SPUUpdater and the WinSparkle C API.
  3. Offline packaging — ship platform-native update runtimes inside wheels and let supported bundlers collect them without network access.
  4. Release authoring — bundle the matching upstream tools for generating keys, signing update archives, and producing macOS appcasts.

3. Quick start

3.1. Install

# uv project
uv add sparklehelper

# or plain pip
pip install sparklehelper

3.2. Use in an app

On macOS, Sparkle must run inside a packaged .app bundle because it depends on the bundle structure and Info.plist. On Windows, WinSparkle is configured from Updater(...) before its native runtime is started.

macOS minimal usage:

from sparklehelper import Updater

updater = Updater()              # reads SUFeedURL from Info.plist
updater.check_for_updates()      # pops the native Sparkle update window

Windows minimal usage:

from sparklehelper import Updater

updater = Updater(
    feed_url="https://example.com/appcast.xml",
    public_key="...",
    company="Example",
    app_name="ExampleApp",
    version="0.1.0",
)
updater.check_for_updates()      # pops the native WinSparkle update window

On macOS, bind it to a GUI menu item and drive its enabled state via KVO:

updater = Updater()

with updater.observe_can_check_for_updates(menu_item.setEnabled):
    # the menu item's state refreshes automatically while inside the block
    ...

Other macOS KVO properties can be subscribed to through the unified entry point:

subscription = updater.observe(
    "automatically_downloads_updates",
    settings_view.set_auto_download_enabled,
)

macOS custom feed source and channel filtering (implement any subset of UpdaterDelegate). On Windows, pass feed_url directly to Updater(...):

class MyDelegate:
    def feed_url_string_for_updater(self):
        return "https://example.com/appcast.xml"
    def allowed_channels_for_updater(self):
        return ("beta",) if is_beta_user() else ()

updater = Updater(delegate=MyDelegate())

4. Platform configuration

4.1. macOS

Sparkle's core macOS configuration lives in the .app Info.plist:

Key Required Description
SUFeedURL Yes Public URL of appcast.xml
SUPublicEDKey Yes EdDSA public key used to verify update signatures
SUEnableAutomaticChecks No Enable automatic checks (on by default)
SUScheduledCheckInterval No Check interval in seconds (default 86400)

The Nuitka wrapper supports Sparkle's full host-app plist key set exposed by Sparkle 2.x SUConstants.h; pass any of them with --sparkle-key KEY=VALUE or the generated kebab-case option such as --su-automatically-update true.

4.2. Windows

WinSparkle must receive its init-time settings from Python before win_sparkle_init():

Updater(...) argument Required Description
feed_url Yes Public URL of appcast.xml
public_key Recommended EdDSA public key used to verify update signatures
company / app_name / version Recommended App identity shown by WinSparkle and used for its settings
build No Build version used for update comparison

5. Packaging

SparkleHelper ships first-class support for the two mainstream Python bundlers. Wheels include the native runtime for their target platform: Sparkle.framework for macOS and WinSparkle.dll for Windows, so packaging never downloads native assets.

5.1. PyInstaller

SparkleHelper registers a built-in PyInstaller hook (via the pyinstaller40 entry point) that collects the platform-native updater at build time. It uses the wheel copy by default; SPARKLEHELPER_FRAMEWORK_PATH on macOS and SPARKLEHELPER_WINSPARKLE_PATH on Windows can override the source for development or compatibility testing.

pyinstaller my_app.spec

On macOS, inject the Sparkle keys via info_plist={} in your .spec; see examples/demo_app/build.spec for a reference. On Windows, pass WinSparkle settings to Updater(...).

For macOS onefile .app specs shaped like BUNDLE(exe, ...), use the wrapper command instead:

uv run sparklehelper pyinstaller my_app.spec

The wrapper creates a temporary patched spec that moves Sparkle.framework from Analysis into BUNDLE, so it lands in Contents/Frameworks instead of the onefile _MEI... extraction directory. Onedir specs shaped like BUNDLE(coll, ...) do not need this wrapper and should keep using plain pyinstaller. If a onefile .app spec is built with plain pyinstaller, the hook stops the build and points to this wrapper command.

5.2. Nuitka

SparkleHelper ships a Nuitka package config and user plugin as package data. Nuitka does not auto-discover either third-party resource, so the sparklehelper nuitka wrapper injects both before forwarding all remaining arguments to Nuitka.

Requirements:

  • Nuitka 4.1.2 or newer.
  • macOS 11 or newer for Sparkle wheels, or Windows for WinSparkle wheels.
uv run sparklehelper nuitka \
  --version 0.1.0 \
  --build-version 1 \
  --feed-url https://example.com/appcast.xml \
  --public-ed-key YOUR_EDDSA_PUBLIC_KEY_BASE64 \
  --mode=app \
  my_app.py

The config collects the wheel's bundled copy and places it at Contents/Frameworks/Sparkle.framework on macOS and at WinSparkle.dll in the Windows Nuitka dist directory; the runtime locates this directory through Nuitka's __compiled__.containing_dir.

On macOS, the plugin restores the framework's top-level Autoupdate symlink before Nuitka signs the app. The wrapper's --version option writes CFBundleShortVersionString and defaults to 0.1.0 when neither --version nor Nuitka's native --macos-app-version is passed. --build-version optionally writes the Sparkle-comparable CFBundleVersion; when it is omitted, the wrapper derives an Apple-compatible build version from --version, for example 0.1.0 becomes build version 1. The wrapper also accepts Sparkle Info.plist keys via aliases such as --feed-url, --public-ed-key, --automatic-checks, and --scheduled-check-interval, or via repeated --sparkle-key KEY=VALUE entries for the full Sparkle key set. If Nuitka's native --macos-app-version is forwarded without wrapper version options, the plugin reads Nuitka's generated CFBundleShortVersionString from Info.plist and only fills in a missing CFBundleVersion. If the application also uses pypylon, Nuitka intentionally keeps its loader-relative framework at Contents/Frameworks/pypylon/pylon.framework.

To build the demo:

cd examples/demo_app
uv run sparklehelper nuitka \
  --version 0.1.0 \
  --build-version 1 \
  --feed-url https://example.com/appcast.xml \
  --public-ed-key YOUR_EDDSA_PUBLIC_KEY_BASE64 \
  --mode=app \
  demo.py

5.3. Release authoring CLI

Platform wheels include the matching upstream release authoring tools. The wrapper forwards all arguments and preserves the native tool's exit code.

Generate or manage an EdDSA signing key:

uv run sparklehelper release keys --help

Sign an update archive:

uv run sparklehelper release sign --help

Generate or update an appcast and its delta updates on macOS:

uv run sparklehelper release appcast --help
uv run sparklehelper release appcast ./releases

On macOS these commands invoke Sparkle's bundled universal2 generate_keys, sign_update, generate_appcast, and BinaryDelta tools. On Windows x64 and ARM64, keys and sign invoke the official x64 winsparkle-tool.exe; Windows ARM64 relies on the operating system's x64 translation. Windows x86 release authoring and Windows appcast generation are not supported. These restrictions only affect release authoring—the WinSparkle runtime remains available in all three Windows wheel architectures.

6. API overview

Updater                       main entry (wraps the native platform backend)
├─ check_for_updates()        pops the native update window
├─ check_for_updates_in_background()
├─ start() / reset_update_cycle() / reset_update_cycle_after_short_delay()
├─ can_check_for_updates      macOS-only KVO property
├─ feed_url / host_bundle_path / last_update_check_date / system_profile
├─ automatically_checks_for_updates / update_check_interval
├─ automatically_downloads_updates / allows_automatic_updates
├─ user_agent_string / http_headers / sends_system_profile
└─ observe(property_name, cb) -> Subscription  macOS-only KVO subscription

UpdaterDelegate              macOS-only optional callback Protocol
├─ updater_may_perform_update_check()
├─ feed_url_string_for_updater()
├─ allowed_channels_for_updater()
├─ feed_parameters_for_updater() / allowed_system_profile_keys_for_updater()
├─ updater_did_find_valid_update()
├─ updater_did_not_find_update()
├─ updater_should_proceed_with_update() / updater_user_did_make_choice()
├─ updater_will_download_update() / updater_did_download_update()
├─ updater_failed_to_download_update() / user_did_cancel_download()
├─ updater_will_extract_update() / updater_did_extract_update()
├─ updater_will_install_update() / updater_should_relaunch_application()
├─ updater_will_schedule_update_check() / updater_will_not_schedule_update_check()
└─ updater_did_abort() / updater_did_finish_cycle()

UpdateInfo / SystemProfileEntry / UpdateCheckResult / UserUpdateState   dataclass
UpdateCheckKind / UserUpdateChoice / UserUpdateStage                    enums

ensure_runnable()            aggregate check: platform/native runtime/config
errors                       SparkleError exception hierarchy

7. Platform constraints

  • macOS main thread: Sparkle/Cocoa APIs must be called on the main thread; SparkleHelper asserts this at every entry point.
  • macOS bundle: Sparkle must be packaged as a .app; ensure_runnable() checks the bundle and plist.
  • Sparkle ships its own UI: check_for_updates() pops the native window with zero config (built-in SPUStandardUserDriver + the in-framework nib).
  • macOS persistence: dynamic settings still land in Sparkle's own NSUserDefaults, following the official advice to "not add another layer on top of user preferences".
  • macOS GUI run loop: Sparkle's startUpdater is asynchronous and depends on the NSApp run loop to complete; canCheckForUpdates only becomes True once the run loop is spinning. So the host GUI must drive the NSApp run loop — wxPython, PyQt/PySide, PyObjC/AppKit native all work; tkinter does not (it uses a separate Tcl event loop that does not interoperate with NSApp, leaving menu items permanently greyed out). See examples/demo_app/README.md.
  • macOS delay start(): Updater() does not start automatic checks by default. In GUI apps, call start() after entering the mainloop (the demo does this via wx.CallAfter), or pass start=True only when the NSApp run loop is already ready.
  • Windows cleanup: WinSparkle starts background work in win_sparkle_init(). Call cleanup() before application exit, or use Updater(...) as a context manager.

8. Development

uv venv && uv pip install -e ".[dev]"
uv run pytest                # unit tests (mock the ObjC layer; run on any platform)

8.1. Building wheels (maintainers)

This is a packaging repo: the main package source tree no longer ships the large native runtime binaries (Sparkle.framework, WinSparkle.dll). They are fetched at wheel build time.

  • Normal install — published wheels already embed the native assets, so end users need no network access or compiler.
  • Upstream source refs — Sparkle/ and winsparkle/ are Git submodules pinned to upstream commits for source browsing, matching the opencv-python packaging-repo style where GitHub shows entries such as Sparkle @ <commit>.
  • Fresh clone — use git clone --recursive, or run git submodule update --init --recursive after cloning.
  • Building a wheel — uv build --wheel resolves the latest upstream Sparkle / WinSparkle release assets, verifies their SHA256, syncs the matching upstream license files, and unpacks them into the wheel. The archive is cached under build/native-cache/ and reused on subsequent builds.
  • Offline build — either keep the previously generated native files in place, or set SPARKLEHELPER_SKIP_NATIVE_SYNC=1, which forbids network access and only validates the local native/license assets (the build fails with a clear error if they are missing).
  • Tracked vs. generated — upstream source references are tracked as gitlinks; package resources only commit src/sparklehelper/winsparkle/winsparkle.h. Sparkle.framework/, winsparkle/*/WinSparkle.dll, and src/sparklehelper/licenses/*.txt are git-ignored and regenerated by scripts/sync_native_deps.py.

9. License

SparkleHelper is MIT licensed. Bundled native dependencies are redistributed under their upstream licenses:

  • Sparkle.framework: sparklehelper/licenses/Sparkle-LICENSE.txt
  • WinSparkle.dll: sparklehelper/licenses/WinSparkle-LICENSE.txt

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sparklehelper-0.1.2.tar.gz (93.2 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

sparklehelper-0.1.2-py3-none-win_arm64.whl (3.7 MB view details)

Uploaded Python 3Windows ARM64

sparklehelper-0.1.2-py3-none-win_amd64.whl (3.7 MB view details)

Uploaded Python 3Windows x86-64

sparklehelper-0.1.2-py3-none-win32.whl (3.5 MB view details)

Uploaded Python 3Windows x86

sparklehelper-0.1.2-py3-none-macosx_11_0_universal2.whl (4.3 MB view details)

Uploaded Python 3macOS 11.0+ universal2 (ARM64, x86-64)

File details

Details for the file sparklehelper-0.1.2.tar.gz.

File metadata

  • Download URL: sparklehelper-0.1.2.tar.gz
  • Upload date:
  • Size: 93.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sparklehelper-0.1.2.tar.gz
Algorithm Hash digest
SHA256 bc6f8f51162c9863b96e22f370c1db326b99341cfa41c63d0097e685ce431d5e
MD5 f7689aa73e6e9afac7d5708ea5fa67db
BLAKE2b-256 6d484b31cef9bb0bc597f451ec2eb7b169a067c3be6284d153f95853c15c2602

See more details on using hashes here.

File details

Details for the file sparklehelper-0.1.2-py3-none-win_arm64.whl.

File metadata

  • Download URL: sparklehelper-0.1.2-py3-none-win_arm64.whl
  • Upload date:
  • Size: 3.7 MB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sparklehelper-0.1.2-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 3a23993b6fc393649a16f59a7137480fab258164da8e9c6aef806f004caa9245
MD5 062d075327ab9122d8ba04f6ef336db5
BLAKE2b-256 67f0530a4a149ca5d80c74fa7fcd6c002a1ff33c353339e9ca6b199de802c377

See more details on using hashes here.

File details

Details for the file sparklehelper-0.1.2-py3-none-win_amd64.whl.

File metadata

  • Download URL: sparklehelper-0.1.2-py3-none-win_amd64.whl
  • Upload date:
  • Size: 3.7 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sparklehelper-0.1.2-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 361ea24fbeb41de8e98ee8abab04d80bc12617ef3526ca7313b859fbf8c25f21
MD5 c99be24209be32543322b4c093c34adf
BLAKE2b-256 4c61e53a4465ec1058031b10ac8535e507ac014b2271e339fbd359fb56f6cb5a

See more details on using hashes here.

File details

Details for the file sparklehelper-0.1.2-py3-none-win32.whl.

File metadata

  • Download URL: sparklehelper-0.1.2-py3-none-win32.whl
  • Upload date:
  • Size: 3.5 MB
  • Tags: Python 3, Windows x86
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sparklehelper-0.1.2-py3-none-win32.whl
Algorithm Hash digest
SHA256 579f748d99a2f63a831a4050d347075f25cf150b6073c6073466f9e14b18651c
MD5 429cd18f8f8d2ac457f8cf33779bcacc
BLAKE2b-256 9e3b9aa06d83ead347b7eedcdd5606b41c47efb07b8bcda63c6739db283830d4

See more details on using hashes here.

File details

Details for the file sparklehelper-0.1.2-py3-none-macosx_11_0_universal2.whl.

File metadata

  • Download URL: sparklehelper-0.1.2-py3-none-macosx_11_0_universal2.whl
  • Upload date:
  • Size: 4.3 MB
  • Tags: Python 3, macOS 11.0+ universal2 (ARM64, x86-64)
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sparklehelper-0.1.2-py3-none-macosx_11_0_universal2.whl
Algorithm Hash digest
SHA256 e6f0ec99d8f314a404e901a84506a48565d283832664dcaf7fa51144a32dddba
MD5 f40d4610512923ecb4a216c6661e2bd1
BLAKE2b-256 7d90225054a9c2e1d785ea068bead8b66372edaf072eb7170d21f91d4b26c17f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page