Skip to main content

ENPAF — Python + Web framework for building Android APK applications

Project description

🚀 ENPAF — Engine for Native Python App Framework

Создавайте Android-приложения на Python + HTML/CSS/JS и собирайте их в APK.

ENPAF — это фреймворк, который позволяет писать мобильные приложения, используя привычный веб-стек (HTML/CSS/JS) для интерфейса и Python для логики. В режиме разработки приложение работает как обычный сайт (Flask + WebSocket + hot-reload), а готовый продукт собирается в настоящий APK на базе WebView + встроенного Python (Chaquopy).

┌──────────────────────── Android APK ────────────────────────┐
│   ┌────────────┐   Bridge (JSON)   ┌────────────────────┐    │
│   │  WebView   │ ◄───────────────► │   Python (main.py) │    │
│   │ HTML/CSS/JS│                   │   enpaf core       │    │
│   │ + enpaf.js │                   │   ваш код          │    │
│   └────────────┘                   └────────────────────┘    │
└──────────────────────────────────────────────────────────────┘

📑 Содержание


✨ Возможности

  • 🐍 Python для логики, HTML/CSS/JS для интерфейса.
  • 🔗 Двусторонний мост Python ↔ JavaScript (вызовы и события).
  • 💾 Встроенное хранилище — key-value и коллекции на SQLite.
  • Hot-reload в режиме разработки.
  • ⚙️ Веб-панель настроек — иконка, имя, ориентация, цвета, разрешения, фичи и диплинки настраиваются прямо в браузере и пишутся в enpaf.json.
  • 📱 Нативные API — toast, вибрация, уведомления, буфер обмена, шаринг, ориентация.
  • 🔐 Разрешения и uses-feature (камера, NFC, датчики, Bluetooth, GPS…).
  • 🔗 Deep links (кастомные схемы и App Links).
  • 📦 Сборка в APK/AAB на Windows/macOS/Linux через Gradle + Chaquopy.

📥 Установка

Из PyPI

pip install enpaf

После установки доступна команда paf и пакет enpaf для импорта.

Из исходников (для разработки фреймворка)

git clone https://github.com/enpaf/enpaf
cd enpaf
pip install -e .

📋 Требования

Что Зачем Версия
Python фреймворк и CLI 3.9+
Java JDK сборка APK 17–21 (рекомендуется 17)
Android SDK сборка APK Android Studio или command-line tools

Для разработки (paf run) нужен только Python. JDK и Android SDK нужны только для сборки APK (paf build). Проверить окружение: paf doctor.


⚡ Быстрый старт

paf create myapp          # создать проект
cd myapp
paf run                   # запустить dev-сервер → http://127.0.0.1:8080
# ... разрабатываете, правите app/ и main.py, страница перезагружается сама ...
paf build apk             # собрать APK → dist/myapp-1.0.0.apk

Установить APK на телефон:

adb install dist/myapp-1.0.0.apk
# либо просто перекиньте .apk на телефон и откройте

🛠 CLI: команда paf

Команда Описание
paf create <name> Создать новый проект из шаблона
paf run Запустить dev-сервер (Flask + hot-reload)
paf build apk Собрать debug APK
paf build apk --release Собрать release APK
paf build aab Собрать release Android App Bundle (.aab)
paf doctor Проверить окружение (Python, JDK, Android SDK)
paf info Показать информацию о текущем проекте

Флаги create:

  • --package, -p — Android package id (по умолчанию com.enpaf.<name>)
  • --template, -t — шаблон проекта (по умолчанию default)

Флаги run:

  • --host (по умолчанию 127.0.0.1)
  • --port (по умолчанию 8080)
  • --no-browser — не открывать браузер автоматически
  • --debug — режим отладки

Флаги build:

  • --release — релизная сборка
  • --keystore <path> — keystore для подписи
  • --clean — чистая сборка (удалить кэш сборки)

🏗 Структура проекта

myapp/
├── app/                  # Интерфейс (HTML/CSS/JS) → попадёт в assets APK
│   ├── index.html        # Главная страница
│   ├── css/style.css
│   ├── js/app.js
│   ├── pages/            # Доп. страницы
│   └── img/
├── main.py               # Python-логика (точка входа)
├── enpaf.json            # Конфигурация проекта
├── icon.png              # (опционально) иконка приложения
├── data/                 # Локальная БД (SQLite) — создаётся автоматически
└── dist/                 # Готовые APK после сборки

⚙️ Конфигурация enpaf.json

Полный пример со всеми полями:

{
    "name": "My App",
    "package": "com.example.myapp",
    "version": "1.0.0",
    "description": "My ENPAF Application",
    "author": "Developer",
    "orientation": "portrait",
    "icon": "icon.png",
    "permissions": ["INTERNET", "CAMERA", "VIBRATE"],
    "features": [
        { "key": "CAMERA", "required": true },
        { "key": "NFC", "required": false }
    ],
    "deeplinks": [
        { "label": "Open profile", "scheme": "myapp", "host": "open",
          "path": "/profile", "pathType": "prefix", "autoVerify": false }
    ],
    "python_requirements": ["requests"],
    "min_sdk": 24,
    "target_sdk": 34,
    "theme": {
        "primary_color": "#6C5CE7",
        "status_bar_color": "#5A4BD1"
    }
}
Поле Тип Описание
name string Название приложения (ярлык на устройстве)
package string Android application id, напр. com.example.myapp
version string Версия (major.minor.patch)
description, author string Метаданные
orientation string portrait / landscape / auto / sensor / unspecified
icon string Путь к иконке (PNG/JPG/WebP) относительно проекта
permissions string[] Список ключей разрешений (см. таблицу)
features object[] `{ "key": "...", "required": true
deeplinks object[] Диплинки (см. раздел)
python_requirements string[] pip-зависимости, встраиваемые в APK через Chaquopy
min_sdk / target_sdk int Android API levels (по умолчанию 24 / 34)
theme.primary_color string Основной цвет (HEX)
theme.status_bar_color string Цвет статус-бара (HEX)
log_level string Уровень логирования (INFO, DEBUG, …)

💡 Эти поля удобнее всего редактировать через панель настроек ⚙, не открывая JSON вручную.


🐍 Python API

main.py — точка входа

from enpaf import EnpafApp

app = EnpafApp(__name__)

# ─── Маршруты страниц (Jinja2-шаблоны, режим разработки) ───
@app.route("/")
def index():
    return app.render("index.html", title=app.name)

# ─── Bridge-функции (вызываются из JavaScript) ───
@app.bridge_handler("get_user")
def get_user(params):
    user_id = params.get("id")
    return {"id": user_id, "name": "Alex"}

# ─── События жизненного цикла ───
@app.on("app_start")
def on_start():
    print("Приложение запущено!")

if __name__ == "__main__":
    app.run()

Класс EnpafApp

EnpafApp(import_name, app_dir="app", config_file="enpaf.json")

Декораторы / методы:

Метод Назначение
@app.route(path, methods=None) Зарегистрировать страницу-маршрут
@app.bridge_handler(name) / @app.bridge_func(name) Зарегистрировать функцию, вызываемую из JS
@app.on(event) Подписаться на событие
app.emit(event, data=None) Отправить событие (в Python и в JS)
app.render(template, **context) Отрендерить Jinja2-шаблон из app/
app.run(host, port, debug, open_browser) Запустить (dev-сервер или Android runtime)

Свойства / компоненты:

Свойство Тип Описание
app.config dict Содержимое enpaf.json
app.name str Имя приложения
app.storage Storage Локальное хранилище
app.events EventEmitter Система событий
app.bridge Bridge Мост Python↔JS
app.api DeviceAPI Доступ к функциям устройства
app.router Router Роутер/шаблонизатор

Хранилище — app.storage

Key-value:

app.storage.set("theme", "dark")          # значение (str/int/float/bool/dict/list)
theme = app.storage.get("theme", "light") # с дефолтом
app.storage.delete("theme")
app.storage.exists("theme")               # -> bool
app.storage.keys("user_%")                # LIKE-паттерн
app.storage.all()                         # -> dict всех пар
app.storage.clear()

Коллекции (мини документ-стор):

notes = app.storage.collection("notes")
note_id = notes.add({"text": "Привет"})   # -> int (id)
notes.all()                                # -> list[dict] (+ _id, _created_at)
notes.find({"text": "Привет"})            # -> list[dict]
notes.find_one({"text": "Привет"})        # -> dict | None
notes.update(note_id, {"text": "Пока"})
notes.delete(note_id)
notes.count()                              # -> int
notes.clear()

На Android БД автоматически пишется в записываемый каталог приложения (getFilesDir()), а не рядом с исходниками.

События — app.events

@app.on("app_start")
def _(): ...

app.events.once("page_load", handler)   # сработает один раз
app.events.off("app_start", handler)    # отписаться
app.events.emit("my_event", payload)    # вызвать

Встроенные lifecycle-события: app_start, app_stop, app_pause, app_resume, app_error, page_load, page_unload, bridge_connect, bridge_disconnect.

Функции устройства — app.api (DeviceAPI)

app.api.toast("Сохранено!", duration="short")  # "short" | "long"
app.api.vibrate(200)                            # мс
app.api.get_device_info()                       # -> dict
app.api.set_status_bar_color("#000000")
app.api.set_orientation("portrait")             # "portrait"|"landscape"|"auto"
app.api.open_url("https://example.com")
app.api.clipboard_set("текст")
app.api.clipboard_get()
app.api.share("Посмотри!", title="Моё приложение")

🌐 JavaScript SDK (enpaf.js)

Мост enpaf.js подключается автоматически: в режиме разработки его внедряет сервер, а при сборке APK — билдер вставляет <script src="js/enpaf.js"> в ваши HTML-страницы. Глобальный объект — window.enpaf.

Вызовы и события

// Вызвать Python-функцию (-> Promise)
const user = await enpaf.call("get_user", { id: 42 });

// События из Python
enpaf.on("data_updated", (payload) => console.log(payload));
enpaf.off("data_updated");

// Отправить событие в Python
enpaf.emit("button_clicked", { id: "save" });

// Готовность моста
enpaf.ready(() => console.log("bridge ready"));

// Навигация между страницами
enpaf.navigate("/pages/about.html");

enpaf.version;    // "1.0.0"
enpaf.isAndroid;  // true в APK, false в браузере

Хранилище из JS

await enpaf.storage.set("theme", "dark");
const theme = await enpaf.storage.get("theme");
await enpaf.storage.delete("theme");

Функции устройства — enpaf.device

Метод Описание
enpaf.device.toast(msg, dur) Toast-уведомление ("short"/"long")
enpaf.device.vibrate(ms) Вибрация
enpaf.device.notify(title, text, id) Системное уведомление
enpaf.device.share(text, title) Системный «Поделиться»
enpaf.device.setOrientation(mode) "portrait"/"landscape"/"auto"
enpaf.device.clipboard(text) Скопировать в буфер обмена
enpaf.device.openUrl(url) Открыть ссылку во внешнем браузере
enpaf.device.getInfo() Информация об окружении (Promise)

В браузере (dev) методы используют веб-аналоги (Web Notifications, navigator.share, navigator.clipboard и т.д.), на Android — нативные вызовы.

Утилиты

enpaf.utils.formatDate(Date.now(), "ru-RU");
enpaf.utils.uid();   // случайный id

⚙️ Панель настроек ⚙

Запустите paf run и откройте http://127.0.0.1:8080/enpaf-settings (или нажмите плавающую кнопку в правом нижнем углу страницы). Панель позволяет настроить и сохранить в enpaf.json без ручного редактирования:

  • General — имя приложения, иконка (загрузка с превью), ориентация, основной цвет и цвет статус-бара;
  • Permissions — разрешения (<uses-permission>);
  • Hardware features — фичи устройства (<uses-feature>) с флагом «required»;
  • Deep Links — диплинки и App Links;
  • Manifest preview — живой предпросмотр того, что попадёт в AndroidManifest.xml.

После «Save» изменения применятся при следующей paf build.


🔐 Разрешения и фичи устройства

Разрешения

Указываются по коротким ключам в permissions. Доступные ключи:

INTERNET, VIBRATE, CAMERA, READ_STORAGE, WRITE_STORAGE, FINE_LOCATION, COARSE_LOCATION, RECORD_AUDIO, READ_CONTACTS, CALL_PHONE, SEND_SMS, BLUETOOTH, BLUETOOTH_ADMIN, NFC, WAKE_LOCK, FOREGROUND_SERVICE, POST_NOTIFICATIONS.

Можно указать и полное имя, напр. android.permission.CAMERA.

Фичи (<uses-feature>)

Формат: { "key": "<KEY>", "required": true|false }. Доступные ключи:

CAMERA, CAMERA_FRONT, CAMERA_AUTOFOCUS, NFC, BLUETOOTH, BLUETOOTH_LE, GPS, LOCATION, MICROPHONE, WIFI, TELEPHONY, TOUCHSCREEN, FINGERPRINT, ACCELEROMETER, GYROSCOPE, COMPASS, PROXIMITY, LIGHT, BAROMETER, STEP_COUNTER, HEART_RATE.

required: false оставляет приложение устанавливаемым на устройствах без соответствующего железа.


🔗 Deep links (диплинки)

Каждый диплинк превращается в <intent-filter> на главной активности.

"deeplinks": [
    { "label": "Профиль", "scheme": "myapp", "host": "open",
      "path": "/profile", "pathType": "prefix", "autoVerify": false }
]
Поле Описание
scheme Обязательно. Напр. myapp или https
host Опционально. Напр. example.com
path Опционально. Напр. /profile
pathType path (точно) / prefix / pattern
autoVerify true для App Links (проверяемые https-ссылки)
label Только для UI-панели

Проверить диплинк на устройстве:

adb shell am start -a android.intent.action.VIEW -d "myapp://open/profile"

🔔 Уведомления и нативные возможности

Из JavaScript:

enpaf.device.notify("Заголовок", "Текст уведомления", 1);

На Android уведомления используют канал по умолчанию; на Android 13+ приложение запрашивает разрешение POST_NOTIFICATIONS (добавьте его в permissions).

Доступные нативные методы моста (вызываются через enpaf.device.*): toast, vibrate, notify, share, setOrientation, clipboard, openUrl.


📦 Сборка APK

paf build apk            # debug
paf build apk --release  # release
paf build aab            # release bundle (.aab)

Что происходит:

  1. Проверяется окружение (Python/JDK/Android SDK).
  2. Подбирается совместимая JDK 17–21 (учитывается JAVA_HOME; иначе ищется в стандартных местах, включая JBR из Android Studio).
  3. Генерируется Gradle-проект (Chaquopy), скачивается официальный Gradle wrapper.
  4. Gradle собирает APK; результат копируется в dist/<name>-<version>.apk.

Первая сборка долгая — Gradle докачивает Android platform/build-tools и NDK (~1 ГБ) для Chaquopy. Дальнейшие сборки быстрее (всё кэшируется в ~/.gradle и Android SDK).

OneDrive / облачные папки

Если проект лежит в OneDrive (или другой синхронизируемой папке), ENPAF автоматически выносит каталог сборки в %LOCALAPPDATA%\enpaf\builds\…, потому что синхронизация ломает удаление файлов Gradle. Путь можно переопределить переменной окружения ENPAF_BUILD_DIR. Итоговый APK всё равно копируется в dist/.


📥 Что можно импортировать

Основное (рекомендуется):

from enpaf import EnpafApp, __version__

Продвинутое (обычно используется через app.*, но доступно напрямую):

from enpaf.core.storage import Storage, Collection
from enpaf.core.events import EventEmitter
from enpaf.core.bridge import Bridge
from enpaf.core.router import Router
from enpaf.core.api import DeviceAPI

# Справочники для манифеста / панели настроек
from enpaf.android.permissions import PERMISSIONS, get_permission_catalog
from enpaf.android.features import FEATURES, get_feature_catalog
from enpaf.android.deeplinks import get_deeplink_xml

# Программная сборка APK
from enpaf.builder.apk_builder import APKBuilder

# Точка входа CLI
from enpaf.cli.main import main

Клиентский SDK для подключения вручную (если не используете авто-инъекцию):

<script src="js/enpaf.js"></script>

🩺 Решение проблем

Симптом Причина и решение
Приложение сразу закрывается Пересоберите APK — фиксы применяются только в новой сборке. Если повторяется — снимите лог: adb logcat (теги AndroidRuntime, python.stderr, chaquopy).
enpaf is not defined Мост не подключён. Сборка вставляет js/enpaf.js автоматически; убедитесь, что собираете свежую версию.
Синий/неожиданный цвет статус-бара Это theme.status_bar_color. Поменяйте его в панели ⚙ → General или в enpaf.json.
Unable to delete directory … python\sources OneDrive блокирует файлы. ENPAF собирает вне OneDrive автоматически; при желании задайте ENPAF_BUILD_DIR.
Incompatible Java version Нужна JDK 17–21. Установите JDK 17 и задайте JAVA_HOME, либо дайте ENPAF найти её автоматически.
'""' is not recognized при Gradle Старый сломанный wrapper. Удалите каталог сборки и соберите заново (paf build apk --clean).

Полная диагностика окружения: paf doctor.


📤 Публикация в PyPI (для мейнтейнеров)

Пакет уже подготовлен (pyproject.toml, MANIFEST.in). Подробная пошаговая инструкция — в PUBLISHING.md. Кратко:

pip install build twine
python -m build                 # создаст dist/*.whl и dist/*.tar.gz
twine check dist/*
twine upload --repository testpypi dist/*   # сначала на TestPyPI
twine upload dist/*                          # затем на PyPI

📄 Лицензия

MIT License — см. LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

enpaf-1.0.0.tar.gz (80.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

enpaf-1.0.0-py3-none-any.whl (80.6 kB view details)

Uploaded Python 3

File details

Details for the file enpaf-1.0.0.tar.gz.

File metadata

  • Download URL: enpaf-1.0.0.tar.gz
  • Upload date:
  • Size: 80.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for enpaf-1.0.0.tar.gz
Algorithm Hash digest
SHA256 1c44f98d37acce201168697cf49c32bbe7122971132412b57c865a1b126e0740
MD5 052d5a2e5c4e608f6b14c2ec18584081
BLAKE2b-256 dae94cdac42869468aa836162ce9ed02734230bf6fc8d55a301515b04fab1066

See more details on using hashes here.

File details

Details for the file enpaf-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: enpaf-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 80.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for enpaf-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 18e1a07b57fc0dcd47db2952a960e6193f5b47723dd270aae937b9ac5314c7eb
MD5 4a08f5e517434873173439c70d814c36
BLAKE2b-256 7513a8e59f34e9683e9afde668d6ac7d252c75427bca1e31fbf65a3c4c855c97

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page