Metadata-Version: 2.5
Name: kavacha
Version: 0.0.6
Summary: run and package web apps as native desktop applications
Project-URL: Repository, https://github.com/vedicreader/kavacha
Project-URL: Documentation, https://vedicreader.github.io/kavacha
Author-email: Karthik Rajgopal <karthik.rajgopal@hotmail.com>
License: Apache-2.0
License-File: LICENSE
Keywords: desktop,packaging,py2app,py2exe,pywebview
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.10
Requires-Dist: fastcore>=1.8.17
Provides-Extra: dev
Requires-Dist: nbdev>=2.4.6; extra == 'dev'
Requires-Dist: pillow>=10.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Provides-Extra: icons
Requires-Dist: pillow>=10.0; extra == 'icons'
Provides-Extra: macapp
Requires-Dist: pillow>=10.0; extra == 'macapp'
Requires-Dist: py2app>=0.28.8; (sys_platform == 'darwin') and extra == 'macapp'
Requires-Dist: pyobjc-framework-cocoa>=10.1; (sys_platform == 'darwin') and extra == 'macapp'
Requires-Dist: pyobjc-framework-webkit>=10.1; (sys_platform == 'darwin') and extra == 'macapp'
Requires-Dist: pythonnet>=3.0.3; (sys_platform == 'win32') and extra == 'macapp'
Requires-Dist: pywebview>=5.3; extra == 'macapp'
Provides-Extra: window
Requires-Dist: pyobjc-framework-cocoa>=10.1; (sys_platform == 'darwin') and extra == 'window'
Requires-Dist: pyobjc-framework-webkit>=10.1; (sys_platform == 'darwin') and extra == 'window'
Requires-Dist: pythonnet>=3.0.3; (sys_platform == 'win32') and extra == 'window'
Requires-Dist: pywebview>=5.3; extra == 'window'
Provides-Extra: windows
Requires-Dist: pillow>=10.0; extra == 'windows'
Requires-Dist: py2exe>=0.14.2; (sys_platform == 'win32') and extra == 'windows'
Requires-Dist: pyobjc-framework-cocoa>=10.1; (sys_platform == 'darwin') and extra == 'windows'
Requires-Dist: pyobjc-framework-webkit>=10.1; (sys_platform == 'darwin') and extra == 'windows'
Requires-Dist: pythonnet>=3.0.3; (sys_platform == 'win32') and extra == 'windows'
Requires-Dist: pywebview>=5.3; extra == 'windows'
Description-Content-Type: text/markdown

# kavacha


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

kavacha (कवच, “armour”) is for Python web apps that run on the user’s own machine, such as a FastHTML app that uvicorn serves on 127.0.0.1. It does two jobs. While the app runs, it opens a native desktop window that shows the app, so the user gets an application with its own Dock icon and menu bar instead of a browser tab. At build time, it turns the project into a standalone application that carries its own copy of Python, so the user does not have to install Python to run it.

The build side relies on a freezer. A freezer is a tool that collects a Python program, the interpreter and every module the program imports into one folder that runs by itself. kavacha uses py2app on macOS, where that folder is a `.app` bundle, and py2exe on Windows. Each freezer has to be told about imports it cannot find by reading the code, and each leaves problems behind in the bundle it writes. kavacha keeps the description of your app in one place, turns it into the options each freezer expects, and repairs the bundle afterwards.

## Install

``` sh
pip install kavacha
```

The base package depends only on fastcore, so you can describe an app and check a build on any machine, Linux included. Extras add what each job needs:

| extra | adds | needed for |
|----|----|----|
| `window` | pywebview, with pyobjc on macOS or pythonnet on Windows | opening the native window |
| `icons` | Pillow | making icons from one image |
| `macapp` | `window`, `icons` and py2app | building a macOS app |
| `windows` | `window`, `icons` and py2exe | building a Windows app |

``` sh
pip install "kavacha[macapp]"
```

## Describe the app

An [`App`](https://vedicreader.github.io/kavacha/spec.html#app) describes the application once, and both freezers take their options from it.

``` python
from kavacha import App, tree, doc_types, mypyc_modules

app = App(
    name='Demo',
    entry='demo_app.py',
    version='1.2.3',
    icon='assets/Demo.icns',
    packages=['demo', 'fasthtml', 'uvicorn', 'numpy'],
    includes=['demo.cli', *mypyc_modules()],
    grafted=['apsw', 'playwright'],
    data=tree('demo/static', 'demo/static'),
    extras=['desktop'],
    doc_types=doc_types(['.py', '.rs', '.md']),
)
app.py2app_options()
```

`entry` is the script the app runs. `packages` are copied into the bundle whole. `includes` names single modules the freezer would miss because no `import` statement names them, such as the compiled helper modules [`mypyc_modules`](https://vedicreader.github.io/kavacha/spec.html#mypyc_modules) finds. `grafted` names packages that do not work from inside the freezer’s zip archive, so kavacha copies them in after the freezer has run. `data` lists files the app reads that are not Python, and [`tree`](https://vedicreader.github.io/kavacha/spec.html#tree) builds that list from a folder. [`doc_types`](https://vedicreader.github.io/kavacha/spec.html#doc_types) declares which files Finder may open with the app. `extras` are the optional dependency groups of your project that the build environment installs. `py2app_options()` and `py2exe_options()` return the options for each freezer. The [spec](spec.html) page covers every field.

## Build

``` python
from kavacha import build, check

check(app, root)
build(app, root, setup_py='packaging/macos/setup.py')
```

[`check`](https://vedicreader.github.io/kavacha/build.html#check) reports what a build would do on this machine without changing anything: which freezer it would use, whether the Python it runs on can be used, whether pywebview is installed, and whether a copy of the app is running. [`build`](https://vedicreader.github.io/kavacha/build.html#build) then does the work. `root` is your project folder and `setup_py` is the script that calls the freezer.

On macOS, py2app can only embed a framework build of Python, the kind python.org and Homebrew install. The interpreters uv and pyenv download are not framework builds. When the current Python is not one, [`build`](https://vedicreader.github.io/kavacha/build.html#build) finds one, creates a virtual environment from it with the package versions pinned in `uv.lock`, and runs your build script again inside it. It refuses to overwrite an app that is running unless you pass `force=True`, and it writes the git commit the app was built from into the bundle. The [build](build.html) page has the details.

## After freezing

``` python
from kavacha import finish, sign

finish(bundle, app)
sign(bundle, 'Developer ID Application: ...', 'entitlements.plist')
```

py2app’s output needs some repairs before it runs well. [`finish`](https://vedicreader.github.io/kavacha/bundle.html#finish) makes them in order: it copies in the `grafted` packages, deletes the copies of them and any other unused code from the bundle’s zip archive, replaces identical large files with hardlinks, repairs libraries in `Contents/Frameworks` that py2app mixed up because two had the same file name, and installs a macOS 26 Icon Composer icon if the app has one. [`build`](https://vedicreader.github.io/kavacha/build.html#build) calls [`finish`](https://vedicreader.github.io/kavacha/bundle.html#finish) for you on macOS, so you only call it yourself when you run the freezer some other way. [`sign`](https://vedicreader.github.io/kavacha/bundle.html#sign) code-signs the finished bundle. The [bundle](bundle.html) page explains each step.

## Icons

``` python
from kavacha.icons import icons_for

icons_for('assets/logo.png', 'assets', 'Demo')
```

[`icons_for`](https://vedicreader.github.io/kavacha/icons.html#icons_for) takes one square image at least 512 pixels wide and writes a PNG favicon for the web page, a Windows `.ico` and, on macOS, an `.icns` for the bundle. It refuses a smaller or non-square image instead of stretching it.

## Window

``` python
from kavacha.window import run_shell, shell_ready, use_app

use_app('Demo')
ok, why = shell_ready()
run_shell(['http://127.0.0.1:8000/'], titles=['Demo'], size=(1440, 900))
```

[`shell_ready`](https://vedicreader.github.io/kavacha/probe.html#shell_ready) says whether this machine can open a native window and, if it cannot, what is missing. [`use_app`](https://vedicreader.github.io/kavacha/window.html#use_app) sets the name kavacha uses for the window title and the settings folder. [`run_shell`](https://vedicreader.github.io/kavacha/window.html#run_shell) opens one window for each URL in the list, shows a holding page until the server at that URL answers, and then runs the platform’s GUI loop until the app quits. Start your server first, on another thread or process, and call [`run_shell`](https://vedicreader.github.io/kavacha/window.html#run_shell) on the main thread. The host, the application using kavacha, can also pass callbacks for opening folders and a menu bar built from the classes in `kavacha.menus`. The [window](window.html) page describes them.

## Status

The spec, build, bundle and icon modules have tests that run on any platform. The window and menu code was moved here from an application where it is in daily use, but CI cannot open a macOS window, so it is not exercised here. Try it on a Mac before you depend on it.
