MobDev.PythonAndroidSupport (mob_dev v0.5.2)

Copy Markdown View Source

Downloads and caches a Chaquopy CPython distribution so Android builds can embed Python.

Why Chaquopy

Chaquopy is currently the only actively-maintained source of pre-built CPython binaries for Android. BeeWare's Python-Android-support (the iOS sibling we already use) hasn't shipped a Python 3.11+ release. Chaquopy is Apache 2.0 (since 2025), publishes to Maven Central, and ships exactly the .so files we need:

target-VSN-arm64-v8a.zip:
  jniLibs/arm64-v8a/libpython3.13.so           interpreter
  jniLibs/arm64-v8a/libcrypto_python.so        OpenSSL crypto
  jniLibs/arm64-v8a/libssl_python.so           OpenSSL SSL
  jniLibs/arm64-v8a/libsqlite3_python.so       bundled SQLite
  lib-dynload/arm64-v8a/*.so                   C extensions
  include/python3.13/                          headers (NIF compile)

target-VSN-stdlib.zip:
  os.py, urllib/, email/,                     shared pure-Python

target-VSN-x86_64.zip:
  Same as arm64-v8a but for x86_64 emulator slice.

We only use these binaries — Chaquopy's Java<->Python bridge is bypassed. Pythonx's NIF dlopens libpython3.13.so directly via the same path contract as iOS, just with Android paths.

Architectures

  • arm64-v8a — modern Android phones. Required.
  • x86_64 — Android emulators on Intel/AMD development machines. Required for sim development.
  • armeabi-v7a (32-bit) — NOT supported. Chaquopy dropped 32-bit Android Python a few releases back. iOS-era 32-bit Android phones (~2017 and earlier) cannot run Pythonx-enabled Mob apps.

Mirrors PythonAppleSupport

Same caching pattern: ~/.mob/cache/python-android-support-<version>/, valid_dir?/1 for layout validation, downloads on-demand via MobDev.NativeBuild when Pythonx is in the user's project.

Summary

Functions

Supported ABIs (the per-arch artifacts we extract).

URL the per-variant artifact is fetched from.

Ensures Chaquopy's Python distribution is cached and extracted. Returns {:ok, extracted_dir} or {:error, reason}.

Returns the cached extraction directory path.

Per-abi C headers for cross-compiling NIFs (pythonx, etc.) against the bundled libpython.

Per-abi jniLibs/<abi> subtree containing libpython.so + its bundled OpenSSL/SQLite dependencies.

Per-abi lib-dynload/<abi> subtree with arch-specific Python C extensions (_ssl, _ctypes, _hashlib, …).

Path to libpython3.13.so for a given ABI.

Pinned Python version (3.13).

Pinned Chaquopy target version (3.13.9-0, …).

Shared (slice-independent) Python standard library directory.

Tarball file name for a given variant (arm64-v8a / x86_64 / stdlib).

Validates the extracted bundle has the expected layout: per-abi jniLibs/<abi>/libpython3.13.so, lib-dynload/<abi>/, headers, and the shared stdlib.

Functions

abis()

@spec abis() :: [String.t()]

Supported ABIs (the per-arch artifacts we extract).

download_url(variant)

@spec download_url(String.t()) :: String.t()

URL the per-variant artifact is fetched from.

ensure()

@spec ensure() :: {:ok, String.t()} | {:error, term()}

Ensures Chaquopy's Python distribution is cached and extracted. Returns {:ok, extracted_dir} or {:error, reason}.

Three artifacts get downloaded: per-abi binary zips for arm64-v8a and x86_64, plus the shared stdlib zip.

extracted_dir()

@spec extracted_dir() :: String.t()

Returns the cached extraction directory path.

headers_dir(dir, abi)

@spec headers_dir(String.t(), String.t()) :: String.t()

Per-abi C headers for cross-compiling NIFs (pythonx, etc.) against the bundled libpython.

jni_libs_dir(dir, abi)

@spec jni_libs_dir(String.t(), String.t()) :: String.t()

Per-abi jniLibs/<abi> subtree containing libpython.so + its bundled OpenSSL/SQLite dependencies.

lib_dynload_dir(dir, abi)

@spec lib_dynload_dir(String.t(), String.t()) :: String.t()

Per-abi lib-dynload/<abi> subtree with arch-specific Python C extensions (_ssl, _ctypes, _hashlib, …).

libpython_path(dir, abi)

@spec libpython_path(String.t(), String.t()) :: String.t()

Path to libpython3.13.so for a given ABI.

python_version()

@spec python_version() :: String.t()

Pinned Python version (3.13).

release_tag()

@spec release_tag() :: String.t()

Pinned Chaquopy target version (3.13.9-0, …).

stdlib_dir(dir)

@spec stdlib_dir(String.t()) :: String.t()

Shared (slice-independent) Python standard library directory.

tarball_name(variant)

@spec tarball_name(String.t()) :: String.t()

Tarball file name for a given variant (arm64-v8a / x86_64 / stdlib).

valid_dir?(dir)

@spec valid_dir?(String.t()) :: boolean()

Validates the extracted bundle has the expected layout: per-abi jniLibs/<abi>/libpython3.13.so, lib-dynload/<abi>/, headers, and the shared stdlib.

Public for testing (per AGENTS.md convention).