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 │ │ ваш код │ │
│ └────────────┘ └──────────────────── │
└─────────────────────────────────────────────────────────────┘
📑 Содержание
- Возможности
- Установка
- Требования
- Быстрый старт
- CLI: команда
paf - Структура проекта
- Конфигурация
enpaf.json - Python API
- JavaScript SDK (
enpaf.js) - Панель настроек ⚙
- Разрешения и фичи устройства
- Deep links (диплинки)
- Уведомления и нативные возможности
- Сборка APK
- Что можно импортировать
- Решение проблем
- Публикация в PyPI (для мейнтейнеров)
- Лицензия
✨ Возможности
- 🐍 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/aaalllexxx/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)
Из JavaScript:
enpaf.device.notify("Заголовок", "Текст уведомления", 1);
Rich-уведомления (Python)
Из Python можно отправлять мощные системные уведомления с картинками и кнопками:
import base64
with open("app/img/logo.png", "rb") as f:
img_b64 = base64.b64encode(f.read()).decode("utf-8")
app.api.notify(
title="Новое сообщение",
text="Привет!",
notification_id=1,
image_base64=img_b64, # Показывает большую картинку
action="open_chat", # Передается при клике на уведомление
payload="user_123",
buttons=[
{"text": "Ответить", "action": "reply"},
{"text": "Закрыть", "action": "close"}
]
)
Нажатие на само уведомление или любую его кнопку автоматически разбудит/откроет приложение и сгенерирует событие в Python:
@app.on("notification_click")
def on_notif_click(data):
print(f"Action: {data.get('action')}, Payload: {data.get('payload')}")
Важно: На Android 13+ приложение запрашивает разрешение
POST_NOTIFICATIONS(добавьте его вpermissionsв вашемenpaf.json).
Доступные нативные методы моста (вызываются через enpaf.device.* в JS или app.api.* в Python): toast, vibrate, notify, share, setOrientation, clipboard, openUrl.
📦 Сборка APK
paf build apk # debug
paf build apk --release # release
paf build aab # release bundle (.aab)
Что происходит:
- Проверяется окружение (Python/JDK/Android SDK).
- Подбирается совместимая JDK 17–21 (учитывается
JAVA_HOME; иначе ищется в стандартных местах, включая JBR из Android Studio). - Генерируется Gradle-проект (Chaquopy), скачивается официальный Gradle wrapper.
- 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file enpaf-1.0.5.tar.gz.
File metadata
- Download URL: enpaf-1.0.5.tar.gz
- Upload date:
- Size: 83.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
527b556f61a1d5832acd4dd754b75e694618779244335b11c397e8daa8ba4d33
|
|
| MD5 |
599450471c58d7094c20b59540651150
|
|
| BLAKE2b-256 |
894fa0818bf41882f21904936e3a97e61da3504cd8d7a19bbfa24b863d3347d4
|
File details
Details for the file enpaf-1.0.5-py3-none-any.whl.
File metadata
- Download URL: enpaf-1.0.5-py3-none-any.whl
- Upload date:
- Size: 83.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4c9736cdc5b68f71d227ebfb0d7279e6b3c7939d8f4ecc9164752f72cf32889
|
|
| MD5 |
f3746649c1c19b11132394778b7b4a21
|
|
| BLAKE2b-256 |
e801996618c535ceea87fa283d9bcb2d52be9894700c79a9531b1281e61e6bdc
|