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-updatekitfrom 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
webbrowserfor 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
Install from a local checkout (recommended while developing)
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
- Create a UTF-8 plain-text
latest.txt, for example containing exactly1.4.2(a trailing newline is fine). - Create a UTF-8 plain-text
download.txtcontaining one complete HTTP(S) URL, for examplehttps://downloads.example.com/my-app. - Host both files at stable HTTPS URLs with publicly readable responses.
- Check that the version file is numeric and dot-separated; avoid labels such
as
latest: 1.4.2,v1.4.2-beta, or JSON. - Check that the download file contains the final page URL, not another text file URL. The checker follows this explicit two-file contract.
- Use the developer sandbox (
python sandbox.pyfrom 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)
| File | Size | Uploaded | |
|---|---|---|---|
| g_updatekit-0.1.0.tar.gz | 11.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|