Skip to main content

UpdateKit

UpdateKit is a small, importable update checker for Python desktop applications. It compares numeric version strings served as plain text, asks the user whether to open an update page, and opens that page in the system browser when accepted. It does not download, install, replace, or restart your application.

The project is named UpdateKit and is distributed as the g-updatekit Python distribution. Import it in code as updatekit; the name used by pip does not have to match the Python import name.

Publishing status: this repository is ready to build as a Python package, but pip install g-updatekit from PyPI will only work after a release has been published there. Until then, install it from this checkout using the local-install commands below.

How the pieces fit together

flowchart LR
    A["Your Python app"] -->|"import updatekit"| B["check_for_updates()"]
    B --> C["latest.txt<br/>2.4.0"]
    B --> D{"Is latest newer?"}
    D -->|"No"| E["Continue app"]
    D -->|"Yes"| F["Ask the user"]
    F -->|"Accept"| G["Read download.txt"]
    G --> H["Open HTTP(S) page in browser"]
    F -->|"Decline"| E

The endpoint files are ordinary UTF-8 text files. For example:

URL file Exact file contents Purpose
latest.txt 2.4.0 Version available to install
download.txt https://example.com/downloads/my-app Page opened after user acceptance

Host both over HTTP or HTTPS. Prefer HTTPS. The download target is deliberately a human-facing page; your application remains in control of the actual installation.

Requirements and behavior

  • Python 3.10 or newer.
  • No third-party runtime dependencies; the implementation uses the standard library, including Tkinter for the default prompt and webbrowser for opening the download page.
  • Importing the module requires the Python Tkinter bindings because the default prompt and splash use them. GUI prompts additionally require a working desktop/Tk session. On many Linux systems, Tkinter is an optional OS package (often named python3-tk); install the OS package even if you plan to use a custom prompt callback.
  • Network requests have a 10-second timeout and read at most 4 KiB from each text endpoint.
  • Versions are numeric dot-separated components: 1.9 < 1.10, v2.0 == 2, and trailing zeroes do not affect equality (1.2 == 1.2.0).
  • Pre-release labels such as 2.0-rc1, calendar versions containing letters, and arbitrary semantic-version syntax are not supported.
  • If the server cannot be reached, the exact message Servers unreachable. Unable to check for updates. is printed, and the user is asked whether to continue without updating. The result records the choice.
  • Invalid URLs, invalid version strings, malformed endpoint contents, and a browser-open failure are reported as exceptions rather than silently treated as a successful check.

Install

Open a terminal in the directory containing pyproject.toml and run:

python -m pip install -e .

-e means editable: Python imports the source in this checkout, so code edits take effect without reinstalling. For a normal local installation that copies the built module into the environment, omit -e:

python -m pip install .

Run these commands in the same virtual environment that runs your application. For example, create and activate one first:

python -m venv .venv
# Windows PowerShell
.\.venv\Scripts\Activate.ps1
# macOS/Linux
source .venv/bin/activate
python -m pip install -e .

Install a published release

After the package has been uploaded to PyPI, users will install it with:

python -m pip install g-updatekit

Until publication, the local-checkout command is the usable installation path.

Import it in your application

The canonical import is:

from updatekit import check_for_updates

Or import all supported public API names:

from updatekit import (
    SERVERS_UNREACHABLE_MESSAGE,
    ServerUnreachableError,
    UpdateCheckResult,
    check_for_updates,
    compare_versions,
)

UpdateCheckResult, check_for_updates, compare_versions, SERVERS_UNREACHABLE_MESSAGE, and ServerUnreachableError are exported names. The high-level check_for_updates function handles endpoint reachability and returns the outcome in its result, so applications normally do not need to catch ServerUnreachableError themselves.

Do not copy updatekit.py into your application or import sandbox.py. Install the distribution in the application's environment and import the module by its module name. The root main.py is a compatibility shim for old source-checkout examples and a launcher for this repository's developer sandbox; it is not the package import name.

Minimal integration

Publish latest.txt and download.txt as described above, then call the checker near the start of your app's normal startup path:

from updatekit import check_for_updates

VERSION = "1.0.0"
LATEST_VERSION_URL = "https://example.com/my-app/latest.txt"
DOWNLOAD_PAGE_FILE_URL = "https://example.com/my-app/download.txt"

result = check_for_updates(
    current_version=VERSION,
    latest_version_url=LATEST_VERSION_URL,
    download_url_file=DOWNLOAD_PAGE_FILE_URL,
    app_name="My application",
)

if not result.continue_application:
    raise SystemExit(0)

# Continue creating/showing your application here.

The default mode does not show a splash window. The update dialog is displayed when one is needed; the connectivity-failure decision is also prompted. If an update is accepted, the configured page opens in the browser and the function returns. The function does not block your application until the user downloads or installs anything.

For a command-line program, the default prompt still uses Tkinter. If the application has no desktop session, supply prompt_user to adapt the decision to your own interface or policy (see the callback example below).

API reference

check_for_updates(...)

check_for_updates(
    current_version,
    latest_version_url,
    download_url_file,
    app_name="Application",
    prompt_user=None,
    open_download=None,
    show_splash=False,
)
Parameter Type Meaning
current_version str Version of the running application, such as "1.4.2".
latest_version_url str HTTP(S) URL whose response body is the latest numeric version.
download_url_file str HTTP(S) URL whose response body is the final HTTP(S) page URL. It is fetched only if an update is available and accepted.
app_name str Friendly name used in default dialog titles and messages.
prompt_user Callable[[str, str], bool] | None Optional decision callback receiving (title, message) and returning True to accept/continue or False to decline/stop.
open_download Callable[[str], bool] \\| None Optional callback for opening a validated download URL. Return True on success; a false result raises RuntimeError.
show_splash bool Show the built-in checking window and in-window decision UI. Defaults to False.

The function returns an immutable UpdateCheckResult with these fields:

Field Meaning
current_version Version passed into the check.
latest_version Version fetched from the server, or "" if that first request could not be reached.
update_available Whether the published version compares greater than the current version.
opened_download Whether the update was accepted and the browser callback succeeded.
servers_reachable Whether the necessary endpoint(s) could be reached.
continue_application Whether the user's response to an unreachable-server prompt permits the app to continue. It is True for ordinary update/no-update results.

Examples of interpreting the result:

if not result.servers_reachable:
    log_warning("Update check could not reach its server")

if result.update_available and result.opened_download:
    log_info(f"Opened the page for version {result.latest_version}")

if not result.continue_application:
    close_application()

compare_versions(current_version, latest_version)

Returns -1 when the first version is older, 0 when they compare equal, and 1 when it is newer. Numeric components are compared as integers, not strings. Invalid values raise ValueError.

from updatekit import compare_versions

assert compare_versions("1.9", "1.10") == -1
assert compare_versions("v2.0", "2") == 0
assert compare_versions("3.0.1", "3.0") == 1

UpdateCheckResult

This frozen dataclass is returned by the main function. Read its fields as shown above; it is not intended to be modified.

Common integration patterns

Start an existing Tkinter application

Call before mainloop() so the update prompt can be answered before the main window appears. The checker owns and closes its temporary prompt/splash windows.

import tkinter as tk
from updatekit import check_for_updates

result = check_for_updates(
    "1.4.2",
    "https://example.com/app/latest.txt",
    "https://example.com/app/download.txt",
    app_name="My Tk app",
    show_splash=True,
)
if not result.continue_application:
    raise SystemExit(0)

root = tk.Tk()
root.title("My Tk app")
root.mainloop()

Use your own prompt UI

Provide a callback that returns a boolean. This is useful for applications with an existing dialog framework or for tests. The callback is used both for an available update and an unreachable-server decision. Inspect its title and message to choose the appropriate UI:

from tkinter import messagebox
from updatekit import check_for_updates

def ask_user(title: str, message: str) -> bool:
    return messagebox.askyesno(title, message)

result = check_for_updates(
    "1.0.0",
    LATEST_VERSION_URL,
    DOWNLOAD_PAGE_FILE_URL,
    app_name="My application",
    prompt_user=ask_user,
)

When a custom prompt_user is supplied with show_splash=True, the splash continues to display the checking status while the custom callback handles the decision. If you want the built-in in-window buttons, omit prompt_user.

Inject a browser opener (tests or custom shell)

def open_page(url: str) -> bool:
    # Replace with your app's browser integration.
    return my_shell_open(url)

result = check_for_updates(
    "1.0.0",
    LATEST_VERSION_URL,
    DOWNLOAD_PAGE_FILE_URL,
    open_download=open_page,
)

The target URL is validated as HTTP(S) before the callback runs. The callback must return a truthy success value; returning False raises RuntimeError.

Schedule around your application lifecycle

The checker is synchronous from the caller's perspective. It performs network requests and waits for the user's decision before returning. Call it before showing your main window or schedule it using the application's existing background-task/event-loop pattern if startup must remain non-blocking. Do not call Tkinter UI methods from a worker thread; Tkinter is generally expected to be used on its owning UI thread.

Endpoint publishing and validation

  1. Create a UTF-8 plain-text latest.txt, for example containing exactly 1.4.2 (a trailing newline is fine).
  2. Create a UTF-8 plain-text download.txt containing one complete HTTP(S) URL, for example https://downloads.example.com/my-app.
  3. Host both files at stable HTTPS URLs with publicly readable responses.
  4. Check that the version file is numeric and dot-separated; avoid labels such as latest: 1.4.2, v1.4.2-beta, or JSON.
  5. Check that the download file contains the final page URL, not another text file URL. The checker follows this explicit two-file contract.
  6. Use the developer sandbox (python sandbox.py from the checkout) to test reachable, unreachable, latest-version, and update-available cases before wiring it into a release.

The request implementation accepts HTTP and HTTPS endpoint URLs, caps response size at 4096 bytes, decodes UTF-8 (including a UTF-8 BOM), strips surrounding whitespace, and times out after 10 seconds. It does not currently support authentication headers, JSON feeds, proxies configured by the library, or background auto-installation.

Local development and tests

python -m pip install -e .
python -m unittest discover -v
python sandbox.py

Tests replace network and UI functions with fakes; they do not require a real update server or actual user interaction.

Build and publish

The package configuration is in pyproject.toml. The importable source is the single top-level module updatekit.py; setuptools includes that module in the wheel. The sandbox, tests, profile data, and the compatibility launcher are development files and are not part of the installed distribution.

To create distribution archives:

python -m pip install build twine
python -m build
python -m twine check dist/*

Before publishing, confirm that g-updatekit is available on PyPI, update the version, project URLs, license choice, and maintainer information in pyproject.toml. Uploading to PyPI makes the package public; use a trusted publishing workflow or a securely configured token, never commit credentials. Then publish the validated archives:

python -m twine upload dist/*

After a release is published, users can install it with python -m pip install g-updatekit and import it with from updatekit import check_for_updates.

Metadata

Release files for g-updatekit 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for g-updatekit 0.1.0
File Size Uploaded
g_updatekit-0.1.0.tar.gz 11.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for g-updatekit 0.1.0
File Interpreter ABI Platform
g_updatekit-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.6 kB

Release files / g_updatekit-0.1.0.tar.gz

Download URL g_updatekit-0.1.0.tar.gz
Size 11.4 kB
Tags Source
SHA-256 checksum
How to use checksums
69edc8f9418a5fcbe7f9100ea00d23d969c8c88b22eef0870019928d5a69aadf
BLAKE2b-256 checksum
How to use checksums
9b610693fd675da92a4fed6b07669239466cb146a7e747a1afe65c1a08f60623
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / g_updatekit-0.1.0-py3-none-any.whl

Download URL g_updatekit-0.1.0-py3-none-any.whl
Size 11.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3508109571ab62c9dac1de43aeec3e0691cda1227809fec857d4a77594a329e
BLAKE2b-256 checksum
How to use checksums
868ced79df594bdded83881dc934cad65f056381f7113d6fbb8b0bc564a4f1b9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page