app-model: Generic Application Schema
raw JSON → 0.5.1 verified Fri Apr 17 auth: no python
app-model is a Python library that provides a generic application schema, inspired by frameworks like VS Code and Qt. It allows defining commands, menus, keybindings, and application state in a structured, framework-agnostic way. Built on Pydantic, it offers strong typing and data validation. The current version is 0.5.1, with releases typically tied to feature development and bug fixes.
pip install app-model Common errors
error pydantic.v1.error_wrappers.ValidationError: ... ↓
cause You are likely running `app-model` with Pydantic v1.x installed, which is incompatible with recent versions of `app-model` (v0.4.0+).
fix
Upgrade Pydantic to version 2.0 or newer:
pip install 'pydantic>=2'. error AttributeError: 'Future' object has no attribute 'my_expected_attribute' ↓
cause You forgot to call `.result()` on the `Future` object returned by `app.commands.execute()`, attempting to access attributes directly on the Future itself.
fix
Modify your command execution to retrieve the actual result:
result = app.commands.execute('my-app.command_id').result(). error app_model.types.CommandNotFoundError: Command 'unknown_id' not found ↓
cause The command ID provided to `app.commands.execute()` or `app.register_menu_item()` does not correspond to any command that has been registered with the `Application` instance.
fix
Ensure the command ID matches exactly one of your registered commands. Verify the ID in
app.register_command(Command('YOUR_ID', ...)) and app.commands.execute('YOUR_ID', ...). Warnings
breaking app-model v0.4.0+ requires Pydantic v2.0 or newer. Using Pydantic v1.x will lead to `ValidationError` or `AttributeError` issues, particularly when models are instantiated or validated. ↓
fix Ensure `pydantic>=2.0` is installed in your environment: `pip install 'pydantic>=2'`.
gotcha The `app.commands.execute()` method returns a `Future` object, not the direct result of the command. If you need the result immediately in a synchronous context, you must call `.result()` on the returned Future. In an `async` context, `await` the Future. ↓
fix Always append `.result()` for synchronous calls: `app.commands.execute(...).result()`. For async functions, use `await app.commands.execute(...)`.
gotcha The `Menu` enums were refactored in v0.5.0. If you were previously using patterns like `MenuType.APP`, these have been simplified to `Menu.APP`. Direct `Menu` enum members are now used for registering menu items. ↓
fix Update your menu registration code to use the direct `Menu` enum members, e.g., `Menu.APP` instead of `MenuType.APP` or string literal names.
Imports
- Application
from app_model import Application - Command
from app_model import Command - Menu
from app_model import Menu - SubMenu
from app_model import SubMenu - Action wrong
from app_model import Actioncorrectfrom app_model.types import Action
Quickstart
from app_model import Application, Command, Menu
def say_hello(name: str = "World") -> str:
"A command that greets the provided name."
return f"Hello, {name}!"
def say_goodbye() -> str:
"A simple command to say goodbye."
return "Goodbye!"
# 1. Initialize the application
app = Application("my-first-app")
# 2. Register commands
app.register_command(Command("my-app.hello", say_hello, title="Say Hello"))
app.register_command(Command("my-app.goodbye", say_goodbye, title="Say Goodbye"))
# 3. Register a menu item for the 'hello' command
app.register_menu_item(
Menu.APP, # or Menu.FILE, Menu.EDIT, etc.
Command("my-app.hello", title="Say Hello from Menu", menu_path="File > Hello"),
)
# 4. Execute a command and get its result
hello_result = app.commands.execute("my-app.hello", {"name": "Registry"}).result()
print(f"Hello Command Result: {hello_result}")
goodbye_result = app.commands.execute("my-app.goodbye").result()
print(f"Goodbye Command Result: {goodbye_result}")