# `MobDev.PythonAndroidSupport`
[🔗](https://github.com/genericjam/mob_dev/blob/main/lib/mob_dev/python_android_support.ex#L1)

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.

# `abis`

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

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

# `download_url`

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

URL the per-variant artifact is fetched from.

# `ensure`

```elixir
@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`

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

Returns the cached extraction directory path.

# `headers_dir`

```elixir
@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`

```elixir
@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`

```elixir
@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`

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

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

# `python_version`

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

Pinned Python version (`3.13`).

# `release_tag`

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

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

# `stdlib_dir`

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

Shared (slice-independent) Python standard library directory.

# `tarball_name`

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

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

# `valid_dir?`

```elixir
@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).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
