Scaffold data-driven apps from declarative YAML DocTypes - Frappe-inspired, MySQL-backed, with a cute TUI.
Project description
qalib
قالب — Arabic for "mold / template"
qalib scaffolds data-driven applications from declarative YAML files.
You describe your entities — DocTypes, in the Frappe Framework sense — as plain YAML files in your project. qalib turns them into typed SQLAlchemy models, Pydantic schemas, Alembic migrations and a live MySQL schema. The YAML is the single source of truth; everything else is a derived artifact you never hand-edit.
Type qalib and a web desk opens in your browser — Streamlit-style. It ships inside
the Python package (no Node, no npm, no build step): a calm, Frappe-Desk-inspired UI with
list views, filters, metadata-driven forms, inline child tables and English/Arabic
switching, all rendered live from your YAML. Edit a YAML file, refresh the browser, and
the desk follows.
The terminal side stays minimal on purpose — like the best modern CLIs: qalib serve
prints a quiet status banner (project, URL, what it's watching) and gets out of your
way, with a small ASCII cat that celebrates clean syncs ([cli] mascot = false turns it
off). VS Code is a first-class citizen: qalib init wires JSON-Schema autocomplete and
inline validation for every DocType YAML file.
The core loop
desk / CLI ──creates/edits──▶ modules/*/doctypes/*.yaml ──edited in VS Code──┐
▲ │ │
│ ▼ │
└────────reads back─── qalib sync ──▶ generated Python models │
│ ──▶ Alembic migration │
│ ──▶ MySQL schema │
└───────────────watch mode ◀──────────┘
qalib sync is idempotent: running it twice on an unchanged tree produces zero diffs and
zero migrations.
Install
uv add qalib-cli # as a project dependency
uvx --from qalib-cli qalib # run without installing
pip install qalib-cli # classic
The package installs as
qalib-cli(the bareqalibname is squatted on PyPI), but the command and the import are plainqalib.
Requires Python 3.11+ and a MySQL 8 server for syncing (validation and codegen work offline).
60-second quickstart
qalib init myapp && cd myapp
# .env, git, pre-commit and VS Code wiring are ready; modules are folders:
code modules/crm/doctypes/customer.yaml # schema autocomplete included
qalib check # validate the YAML: every error with file, line, and a suggested fix
qalib sync # generate models + migration, apply to MySQL
qalib # opens the web desk in your browser (list views, forms, child tables)
qalib watch # re-sync automatically whenever a YAML file changes
No npm install, ever: the web desk is part of the qalib package itself. While the
server runs, YAML edits show up on the next browser refresh.
What a DocType looks like
One DocType per file, one folder per module (Frappe-app style):
modules/<module>/doctypes/<doctype>.yaml — the module: key is optional and derived
from the folder. Labels are {en, ar} maps — qalib is Arabic-first and every table
is utf8mb4.
# modules/crm/doctypes/customer.yaml
doctype: Customer
module: CRM
label:
en: Customer
ar: زبون
naming:
strategy: series
series: "CUST-.YYYY.-.#####"
title_field: customer_name
fields:
- fieldname: customer_name
label: {en: Customer Name, ar: اسم الزبون}
fieldtype: Data
reqd: true
in_list_view: true
- fieldname: customer_type
fieldtype: Select
options: [Individual, Company]
default: Company
- fieldname: territory
label: {en: Territory, ar: المنطقة}
fieldtype: Link
options: Territory # foreign key to tabTerritory.name
on_delete: restrict
- fieldname: credit_limit
fieldtype: Currency
default: 0
- fieldname: addresses
fieldtype: Table
options: Customer Address # child table rows, edited inline
permissions:
- role: Sales User
read: true
write: true
create: true
From this, qalib sync derives:
- a
tabCustomerMySQL table with standard fields (nameprimary key,creation,modified,owner, …) plus your columns, indexes and foreign keys, - a typed SQLAlchemy 2.0 model and Pydantic schemas under
generated/(banner-marked, never hand-edited — escape hatches live inhooks/andoverrides/), - a reviewed Alembic migration. Destructive changes (dropped columns or tables) are
refused unless you pass
--allow-destructive.
See examples/crm for a complete committed project: a territory tree, a
customer with an embedded address child table, and a submittable sales order with line
items.
CLI reference
qalib new [app|module|doctype] create anything — guided, commented files
qalib init <name> [--db-url URL] scaffold a new project (= qalib new app)
qalib doctype new [NAME] interactive or flag-driven (= qalib new doctype)
qalib doctype list [--module M]
qalib doctype show <NAME> rendered field table
qalib doctype rename <OLD> <NEW>
qalib doctype delete <NAME> --allow-destructive
qalib check [--strict] validate YAML only, exit 1 on error
qalib sync [--plan-only] [--yes] [--allow-destructive]
qalib migrate [--revision REV] alembic passthrough
qalib watch re-sync on YAML change (debounce 400ms)
qalib db shell | reset | import-db (import-db lands in Milestone 5)
qalib fixtures load|dump
qalib serve [--host H] [--port P] web desk + REST API (also: bare `qalib`)
Global flags: --project PATH, --verbose, --no-color, and --json for
machine-readable output from check, sync --plan-only and doctype list.
The web desk
Bare qalib (or qalib serve) starts a local server and opens the desk: a sidebar of
your DocTypes grouped by module, list views with the columns you marked in_list_view,
filters from in_standard_filter, and create/edit forms generated from the field
metadata — sections and columns from your layout fields, inline grids for child tables,
Submit/Cancel for submittable DocTypes, and an EN/AR toggle with real RTL. It talks to a
REST API (/api/...) you can also use directly; DocType permissions are enforced per
role, and each module's hooks/<doctype>.py functions (validate, before_insert, …)
run on every write. Password fields are write-only and never returned.
The look is modern Frappe: white surfaces, quiet gray borders, and a single near-black
accent — dark mode inverts it. The desk is three static files inside the wheel —
hand-written JS/CSS, no framework, no build step, no external requests, no npm install.
CLI charm
The terminal stays calm and friendly — gentle wording, no noise, and a small ASCII cat that keeps you company:
$ qalib sync
Loaded 5 DocType(s).
Generated code: 0 written, 12 unchanged.
✓ Everything is in sync
/\_/\
( ^.^ ) ~ nice!
> ^ <
Not a cat person? [cli] mascot = false in qalib.toml.
Dev container
The repository ships a dev container with Python, uv and a MySQL 8 service (utf8mb4) preconfigured:
- Open the folder in VS Code and choose Reopen in Container.
uv syncruns automatically on create;QALIB_TEST_DB_URLalready points at the bundled MySQL service.
Contributing / dev setup
uv sync # install everything
uv run ruff format --check src tests # formatting
uv run ruff check src tests # lint
uv run mypy --strict src/qalib # types
uv run pytest -q # unit + snapshot tests
docker compose -f docker-compose.test.yml up -d # MySQL 8 on port 3307
uv run pytest -q -m db # integration tests
Commits follow Conventional Commits
(feat:, fix:, refactor:, test:, docs:).
Roadmap
| Milestone | Scope | Status |
|---|---|---|
| M1 | Schema & YAML core: DocType/Field models, loader, round-trip writer, qalib check |
shipping |
| M2 | Codegen & MySQL: DDL mapping, SQLAlchemy models, idempotent qalib sync, naming series |
shipping |
| M3 | Developer UX: VS Code round-trip, watch mode, CLI charm (superseded the TUI) | shipping |
| M4 | App runtime: web desk + REST API (qalib serve), hooks, docstatus, permissions |
shipping |
| M5 | Polish: qalib import-db reverse engineering, fixtures, end-to-end CRM example in CI |
next |
Prefer a custom frontend? qalib init --frontend also drops in a minimal React + Vite
template wired to the same REST API — entirely optional; the built-in desk needs nothing.
Arabic-first
qalib assumes bilingual applications from day one: MySQL charset is utf8mb4 with
utf8mb4_unicode_ci collation, every label is an {en, ar} map, and the desk is tested
under LANG=ar_IQ.UTF-8. Latin-only and LTR-only assumptions are treated as bugs.
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 qalib_cli-0.1.0.tar.gz.
File metadata
- Download URL: qalib_cli-0.1.0.tar.gz
- Upload date:
- Size: 190.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39a7a392159867b26fd54af853db80a26b862d4d73bb359708345e1e0ff5f48d
|
|
| MD5 |
ea1c3bdfacdf4b02326160d0654918f9
|
|
| BLAKE2b-256 |
8f34e10857eb0e8eaa99b81113fdcce2a94c46d6c4a1302b8eae650df6a20212
|
File details
Details for the file qalib_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: qalib_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 127.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a90a1cf41b2ceed36b43718608a2f055440c0a57e0f99bfff2d4f51428e0ffb
|
|
| MD5 |
dc6626d73500e64bfe6c0370f80813ad
|
|
| BLAKE2b-256 |
b4aa3e5b84a893df6a678be9d1517f524c5d127cec68811931eeb3650e599828
|