From 4a617469b81daaf0d537bb266aecff9a8ebe1990 Mon Sep 17 00:00:00 2001 From: AI_Assistant Date: Sat, 22 Aug 2026 13:22:59 +0000 Subject: [PATCH] Document dist-cache gotcha for python_depends changes, Settings label bug CLAUDE.md was out of date after this session's two on-device fixes (commit d276f88): still described the kivy recipe as python_depends=[] (filetype is back), and didn't mention the p4a dist-caching trap that made the filetype fix silently ineffective until the stale dist directory was deleted, or the Settings popup label wrapping bug. Cross-referenced with android-app-template's CLAUDE.md, which hit the same label bug in more places. --- CLAUDE.md | 90 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 17 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 962153f..6b0717d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,11 +9,13 @@ Context for future Claude sessions working on this repo. relocatable data path, taskbar icon, "Time Since" column. - Repo is pushed to `repo.tas-tech.net/tas-tech.net/event-tracker` (private — will go public once there's a tagged release). -- **Android build (`android/`) works end-to-end** — `./build.sh android - debug` produces `android/bin/eventtracker-0.1-arm64-v8a-debug.apk`. - Confirmed via a full `BUILD SUCCESSFUL` Gradle run, not just "recipes - compiled." Three real, distinct bugs had to be fixed to get here (all - now baked into `build.sh`/`buildozer.spec`, nothing left to redo): +- **Android build (`android/`) works end-to-end AND is confirmed running + on-device** — `./build.sh android debug` produces + `android/bin/eventtracker-0.1-arm64-v8a-debug.apk`, installs, and + launches clean (`adb logcat` shows `Start application main loop`, no + crash). Five real, distinct bugs had to be fixed to get here (all now + baked into `build.sh`/`buildozer.spec`/`main.py`, nothing left to + redo): 1. p4a's `kivy` recipe pulling in unused `requests`/`charset-normalizer` that couldn't actually install — `android/p4a-recipes/kivy`. 2. p4a's own internal pymodules venv corrupting itself on every @@ -21,9 +23,19 @@ Context for future Claude sessions working on this repo. 3. Gradle's build-script compiler rejecting this machine's default JDK (java-26) — `build.sh` pins `JAVA_HOME` to `java-17-openjdk` for just this build. + 4. Fix #1 above was applied too aggressively and dropped `filetype` + too, which `kivy/core/image` genuinely needs — on-device startup + crash (`ModuleNotFoundError: No module named 'filetype'`). Restored + just `filetype`. + 5. p4a caches a built dist by recipe-name match and never re-derives + a recipe's `python_depends` for an existing dist, so fix #4 above + silently had zero effect until the stale dist directory + (`.buildozer/android/platform/build-/dists/eventtracker`) was + deleted to force a re-derive. Traced end-to-end through + `pythonforandroid/distribution.py`/`graph.py`, not guessed — see + the dedicated entry below. See "Android companion — design decisions" below for the full - root-cause writeup on each. Not yet installed/tested on an actual - phone — that's the next real checkpoint, not another build fix. + root-cause writeup on each. - **Outstanding, agreed but not implemented**: split `config.json` into two files — event list (safe to sync between machines) vs. data file path (must stay local per-device, same principle as the Android app's @@ -158,6 +170,18 @@ android/ - **No matplotlib on Android** — unreliable through Kivy's garden packaging. `BarRow` in `main.py` hand-draws a proportional bar with Kivy's own `Color`/`Rectangle` canvas primitives instead. +- **Wrapping `Label`s bind `text_size` to their own `width`, never set + it once from `self.width` in a `Popup.__init__`** — `SettingsPopup`'s + info label originally did `text_size=(self.width, None)` at + construction time, but the Popup hasn't been laid out yet at that + point, so `self.width` is Kivy's ~100px pre-layout default rather + than the popup's real size. That locked the label into wrapping at + ~100px forever — rendered on-device as a 1-character-wide vertical + column of text. Fixed by binding instead: + `label.bind(width=lambda inst, w: setattr(inst, "text_size", (w, None)))` + after construction. Same bug, same fix, also applied to + `android-app-template`'s `SettingsPopup`/`LegalPopup` (that repo had + it in four places) — see that repo's `CLAUDE.md`. - **Config path is local-only, same principle as desktop's `CONFIG_PATH`** — each device's app-private storage holds only that device's CSV path; it is never itself synced. Only the CSV — the actual data — goes @@ -192,11 +216,11 @@ android/ - **`p4a-recipes/kivy` — local override, not a workaround** — p4a's own built-in `kivy` recipe declares `python_depends = ['certifi', 'chardet', 'idna', 'requests', 'urllib3', 'filetype']` - (for `kivy.network.UrlRequest`, which this app never calls). - `requests` transitively needs `charset-normalizer`, whose newest - release is the first to ship a prebuilt Android-tagged wheel (new, - tied to CPython 3.14's official Android build target). p4a's own - dependency resolver (`process_python_modules` in + (most of that list only for `kivy.network.UrlRequest`, which this app + never calls). `requests` transitively needs `charset-normalizer`, + whose newest release is the first to ship a prebuilt Android-tagged + wheel (new, tied to CPython 3.14's official Android build target). + p4a's own dependency resolver (`process_python_modules` in `pythonforandroid/build.py`) finds that wheel correctly during its `pip --dry-run --platform=android_24_arm64_v8a` pass and pins the exact URL into a generated `requirements.txt` — but the *next* stage @@ -206,11 +230,43 @@ android/ p4a bug, confirmed by reading `pythonforandroid/build.py` directly (not guessed) — and `python_depends` isn't something `buildozer.spec` can override, so `p4a-recipes/kivy/__init__.py` is a full copy of the - upstream recipe (patches included) with just `python_depends = []`. - `buildozer.spec` points at it via `p4a.local_recipes = p4a-recipes`. - If a future Kivy release actually needs one of those network extras, - re-diff against p4a's current `pythonforandroid/recipes/kivy/__init__.py` - before blindly re-adding them. + upstream recipe (patches included) keeping only `filetype` — + `kivy/core/image/__init__.py` imports it directly for image format + sniffing, confirmed by an on-device startup crash + (`ModuleNotFoundError: No module named 'filetype'`) the one time it + got dropped along with the rest. `filetype` is pure Python with zero + dependencies of its own, so it doesn't reintroduce the + requests/charset-normalizer chain. `buildozer.spec` points at the + override via `p4a.local_recipes = p4a-recipes`. If a future Kivy + release actually needs one of the other four back, re-diff against + p4a's current `pythonforandroid/recipes/kivy/__init__.py` before + blindly re-adding them. +- **Editing `p4a-recipes/kivy/__init__.py` alone is not enough — p4a + caches the built dist and may never re-read it.** `Distribution.get_ + distribution()` (`pythonforandroid/distribution.py`) matches an + existing dist purely by name (`eventtracker`) and recipe-name list + (`python3`, `kivy`, etc. — from `buildozer.spec`'s `requirements =` + line). `python_depends` isn't a recipe name, so it's never consulted + during that match; if a dist with the same name+recipes already + exists, it's reused wholesale with `needs_build = False`, and + `build_dist_from_args()` — the only function that reads + `recipe.python_depends` and pip-installs pymodules + (`graph.py:334` → `run_pymodules_install`) — never runs at all. This + is exactly what happened restoring `filetype`: the recipe file was + correct on disk, but the next build reused the stale dist and the + APK still crashed identically. Confirmed two ways: unpacked the built + APK's `libpybundle.so` and found no `filetype` anywhere in it, then + traced the caching logic in `distribution.py`/`graph.py` line by + line. Fix: delete the stale dist directory so + `Distribution.get_distributions()`'s glob finds nothing and + `needs_build` falls back to `True` — + `rm -rf android/.buildozer/android/platform/build-/dists/` + — then rebuild. This only redoes packaging (pymodule install + + Gradle assembly), not the expensive native recipe compilation, so + it's fast (single-digit seconds of Gradle work in practice). Anytime + a *recipe's* `python_depends` changes (not `buildozer.spec`'s + `requirements =` line, which p4a does check), delete the dist before + the next build or the change silently won't take effect. - **Previously-tried and reverted**: `--system-site-packages` on the build venv. It fixed the `--user`/pip-in-venv error above, but leaked the real system Python's installed packages into what p4a decided the