Skip to content

Manage dependencies and re-lock

Your app's dependencies live in [project].dependencies. kivyforge resolves them separately for each target and pins the result in that target's lockfile. This page shows how to add a dependency, hold a package on one version, re-lock, and keep the lock in sync with pyproject.toml.

Before you begin

  • A project with an overlay for each target you build. See Project configuration.
  • A host that can lock the target. lock runs on any host for every target except iOS, which needs macOS. See Which host builds which target.
  • For an Android project that declares Maven dependencies in [tool.kivy.android.gradle], a JDK. lock runs Gradle to resolve them.

Add or change a dependency

  1. Edit [project].dependencies in pyproject.toml:

    [project]
    dependencies = [
        "kivy>=2.3.1",
        "pillow>=10",
    ]
    
  2. Re-lock each target you build:

    kivyforge lock -p macos
    kivyforge lock -p android
    

Each target gets its own pylock.<platform>.toml, because the wheels differ by platform.

Pin a package version

The version written into the lock is the newest one your specifier allows. kivyforge lock resolves [project].dependencies from scratch when the lock is stale, and when you pass --update. While the lock is in sync, lock leaves those versions where they are.

To hold a package on one version, require that version exactly:

[project]
dependencies = [
    "kivy==2.3.1",
    "pillow==10.4.0",
]

A range such as kivy>=2.3.1 or pillow>=10 moves to the newest match the next time you re-lock. A Kivy 3 development snapshot is a pre-release, so the specifier has to name one. See the kivy_generation setting in Configure your Android app. A range such as kivy>=3.0.0.dev0,<4 still moves to the newest snapshot. Pin the snapshot you mean:

[project]
dependencies = [
    "kivy==3.0.0.dev202607301604",
]

A package you did not list moves as well. On Android, lock adds pyjnius when you have not declared it, using a range, so a re-lock picks up newer pyjnius releases. Declare it yourself to choose the version. See Call Android APIs with pyjnius. A package pulled in by another package moves the same way. List it in [project].dependencies with == and that requirement is the one lock resolves.

lock --check resolves again and compares the result with the lock on disk. An exact pin stays stable. A range fails the check when a newer match is published, even though pyproject.toml has not changed. build, run, and package still accept that lock. They refuse it when the file hash no longer matches.

The next lock overwrites a hand-edited pylock.<platform>.toml. Change the specifier and re-lock.

When a step edits pyproject.toml after the lock was resolved, and this build should keep the pins on disk, pass --no-verify-lock to build, run, or package. That skips the drift check. See Drive kivyforge from CI or an agent.

Make sure every dependency has a wheel

kivyforge installs prebuilt wheels only and never builds a package from source. A pure-Python package works on every target. A package with compiled code works only on targets that have a matching wheel: macosx_*, manylinux_*, win_amd64, ios_*, or android_*. If a target has no matching wheel, lock fails for that target. See Prebuilt runtimes and wheels.

To add an index or a local directory of wheels, use extra_index_urls or find_links in the target's overlay.

Prune unused Kivy dependencies

Kivy's wheels declare dependencies that many apps don't use. To leave them out of the lock and the bundle, list them in the overlay's exclude key:

[tool.kivy.macos]
exclude = ["kivy-garden", "docutils", "pygments"]

Keep the lock in sync

The lock records a hash of the whole pyproject.toml, so any edit to the file makes the lock stale, including edits outside [project].dependencies. After you edit pyproject.toml, re-lock the targets you build. That re-lock resolves again. Pin a package version so the packages you care about stay put.

If the lock is in sync, lock leaves it unchanged. To resolve again anyway and take the newest version each specifier allows, pass --update:

kivyforge lock -p linux --update

Refresh downloaded artifacts

To download the pinned runtime and native artifacts again without resolving again, run upgrade:

kivyforge upgrade -p linux

To remove the generated project files and also clear the download cache, run:

kivyforge clean --cache

This clears kivyforge's cache of runtimes and wheels. pip's own cache, which lock uses while resolving, is separate; clear it with pip cache purge.

Verify

Check that the lock matches pyproject.toml:

kivyforge lock -p linux --check

The command exits 0 and prints that the lock is up to date. If the lock is stale, or a specifier allows a newer release than the lock records, it lists what changed, exits 4 with KF-LOCK-DRIFT, and writes nothing. build, run, and package fail the same way when the pyproject.toml hash no longer matches the lock. A lock whose hash still matches is accepted, even when --check would now resolve something newer. See Pin a package version and Drive kivyforge from CI or an agent.

What's next