Hierarchical, lifecycle-aware MVVM viewmodel framework.
Project description
vmx — Python
Hierarchical lifecycle-aware MVVM viewmodel framework for Python, spec-compatible with the C#, TypeScript, and Swift flavors.
1. Status
v3.1.0 — implements spec-v3.1.0 end-to-end. 281/281 library conformance IDs
pass. Supports Python 3.10–3.13.
mypy --strict clean. Opt-in vmx.notifications subpackage ships an
INotificationHub for async confirmations. The Swift flavor is at total
parity; see ../swift/README.md §5 for the current conformance matrix.
2. Install
The source tree currently implements v3.1.0. The latest public PyPI package may lag this source tree; pin a version when reproducing released behavior.
pip install vmx
# or
uv add vmx
3. Quick start
The minimum-viable shape is imports → services → builder (name + model + services + optional modeled_hinter) → construct() → read status:
from dataclasses import dataclass
from vmx import (
ComponentVMOf,
CompositeVM,
MessageHub,
RxDispatcher,
)
@dataclass
class TabModel:
title: str
# 1. Services (a hub + a dispatcher).
hub = MessageHub()
dispatcher = RxDispatcher.immediate()
# 2. Build leaves: name, model, services, optional modeled_hinter.
home: ComponentVMOf[TabModel] = (
ComponentVMOf.builder()
.name("home")
.model(TabModel("Home"))
.modeled_hinter(lambda m: m.title) # optional — defaults to lambda _m: ""
.services(hub, dispatcher)
.build()
)
settings: ComponentVMOf[TabModel] = (
ComponentVMOf.builder()
.name("settings")
.model(TabModel("Settings"))
.services(hub, dispatcher)
.build()
)
# 3. Build a composite over the leaves.
tabs = (
CompositeVM[ComponentVMOf[TabModel]]
.builder()
.name("tab-bar")
.services(hub, dispatcher)
.children(lambda: [home, settings])
.build()
)
# 4. Transition the lifecycle from DESTRUCTED → CONSTRUCTED before use.
tabs.construct()
print(tabs.status) # ConstructionStatus.CONSTRUCTED
tabs.current = settings
print(tabs.current.model.title) # "Settings"
tabs.dispose()
hub.dispose()
Tips:
.modeled_hinter(...)is optional on every modeled builder; the default islambda _m: "". Pass a callable when you want to derive a display hint from the model.- For tests, samples, and headless code,
NULL_MESSAGE_HUBandNULL_DISPATCHERare safe no-op singletons. Annotate variables asMessageHubProto[Message](the structuralProtocol) to keepmypy --stricthappy, or use the genericnull_message_hub_of(MyMessage)factory (imported fromvmx.services) for a narrower message type.
The C# and TypeScript flavors mirror this shape: see C# Quick start and TypeScript Quick start — only the identifier casing differs.
See docs/getting-started/python.md for the full walkthrough.
3.1 Cross-language naming
The conceptual surface is identical across the four flavors; identifier casing follows the per-language idiom (see ADR-0006).
| Concept | C# | Python | TypeScript | Swift |
|---|---|---|---|---|
| Unmodeled VM | ComponentVM |
ComponentVM |
ComponentVM |
ComponentVM |
| Modeled VM | ComponentVM<M> |
ComponentVMOf[M] |
ComponentVMOf<M> |
ComponentVMOf<M> |
| Status property | Status |
status |
status |
status |
| Builder entrypoint | Builder() |
builder() |
builder() |
builder() |
| Null hub singleton | NullMessageHub.Instance |
NULL_MESSAGE_HUB |
NullMessageHub.INSTANCE |
NullMessageHub.INSTANCE |
C# uses PascalCase, Python uses snake_case, TypeScript and Swift use
camelCase. The single substantive divergence is that C# names the modeled
variant with a generic-parameter suffix (ComponentVM<M>), while Python,
TypeScript, and Swift use a separate ComponentVMOf type because their
generics syntax cannot overload an unparameterised name.
4. API surface
The public API is re-exported from a single entry point:
from vmx import ... # see vmx/__init__.py for the full list
| Export | Description |
|---|---|
ComponentVM |
Leaf viewmodel (no model) |
ComponentVMOf[M] |
Leaf viewmodel with a typed model |
ReadonlyComponentVMOf[M] |
Leaf VM with read-only model |
CompositeVM[VM] / CompositeVMOf[M,VM] |
Ordered collection + current slot |
GroupVM[VM] |
Collection without current selection |
AggregateVM1..6[…] |
Fixed-arity named component slots (arity 6 added in spec v2.2.0 — see ADR-0034) |
ForwardingComponentVM |
Decorator for ComponentVMOfProto |
ForwardingCompositeVM |
Decorator for composites |
RelayCommand / RelayCommandOf[T] |
Executable command with can_execute predicate |
CompositeCommand |
Aggregate N inner commands (spec v2.0) |
DecoratorCommand |
Wrap a command with pre/post + can-execute gate |
ConfirmationDecoratorCommand |
Wrap a command with an async confirm coroutine |
ModeledCrudCommands[M,VM] |
Create / UpdateCurrent / DeleteCurrent helper |
MessageHub |
Pub/sub hub backed by reactivex Subject |
NullMessageHub / NULL_MESSAGE_HUB |
Null-object variant per ADR-0017 |
RxDispatcher |
Foreground/background scheduler pair |
NullDispatcher / NULL_DISPATCHER |
Null-object variant per ADR-0017 |
ConstructionStatus |
5-state lifecycle enum |
StatusTransitionError |
Raised on illegal lifecycle operations |
BuilderValidationError |
Raised when a builder is missing required fields |
walk(root) |
DFS pre-order tree traversal generator |
walk_expanded(root) |
DFS walk gated on IExpandable.is_expanded (v2.0) |
find(root, predicate) |
Short-circuit tree search |
DerivedProperty[TValue] / from_sources(...) |
N-source computed value (spec v2.0) |
ExpandableState |
IExpandable+ICollapsible helper (spec v2.0) |
SearchableState[T] |
Debounced filter helper (spec v2.0) |
ILocalizer / NullLocalizer / NULL_LOCALIZER |
i18n hook + null-default (v2.0) |
| 22× capability ABCs | vmx.capabilities.* — opt-in (spec v2.0+) |
HierarchicalVM[TModel, TVM] |
Recursive tree-structured VM (spec v2.1) |
TreeStructureChangedMessage |
Tree-structural-change notification (spec v2.1) |
FormVM[TM] |
Snapshot/revert form lifecycle (spec v2.1) |
DialogService / NullDialogService |
File/confirm/notify dialogs + null (spec v2.1) |
ServicedObservableCollection[T] |
Hub-aware observable collection (spec v2.1) |
ObservableList[T] |
Granular per-mutation events (spec v2.1) |
ObservableDictionary[K1, K2, V] |
Multi-key observable dictionary (spec v2.1) |
PagedComposition[TVM] |
Pageable iterable decorator (spec v2.1) |
| Fluent command helpers | confirm / precede_with / succeed_with / wrap_with over commands (spec v2.1) |
property_value_changed_messages_for |
Hub helper yielding an observable of property-value snapshots (spec v2.1) |
The opt-in vmx.notifications subpackage (spec v2.0+) adds:
| Export | Description |
|---|---|
Notification / NotificationType / NotificationReaction |
Notification primitives |
INotificationHub / NotificationHub / NullNotificationHub / NULL_NOTIFICATION_HUB |
Async notification hub + null variant |
make_confirm(hub, prompt) |
Bridge to ConfirmationDecoratorCommand |
NotificationVM |
Render-side VM for Notification (spec v2.1) |
ConfirmationVM |
Render-side VM with Approve/Reject (spec v2.1) |
5. Conformance
All 281 library conformance IDs from spec/12-conformance.md are covered (the 5 THEME scenario IDs live in the flagship example apps — see CONTRIBUTING §2.5). Test-layout conventions for the conformance tree are documented in tests/conformance/README.md.
v1.x LIFE-001..013 HUB-001..007 PROP-001..004 CMD-001..007
CVM-001..006 COMP-001..013 GRP-001..006 AGG-001..005
FWD-001..003 BLD-001..004 THR-001..004 UTIL-001..003
v2.0 CAP-001..020 NULL-001..003 DPROP-001..012 CMDD-001..009
NOTIF-001..010 COMP-014..024 GRP-007..010 EXP-001..005
LOC-001..003
v2.1 HIER-001..014 DIA-001..008 FORM-001..010 NOTIF-011..016
COL-001..023 CMD-008..011 CAP-021..022
v2.2 AGG-006
v2.3 BLD-005 FORM-011..013 HIER-015..017
v2.4 THEME-001..005
v2.5 HIER-018 NOTIF-017 FORM-014
v2.6 COMP-025..026
v3.0 LIFE-014 FORM-015 CMDD-010 COMP-027 CMD-012
v3.1 CMD-013 COL-024..031 COMP-028..037 FORM-016..023
DIA-009..013 HIER-019..022 DISC-001..006 BLD-006 GRP-011
Run the suite:
uv run pytest
6. Development
# From this directory
uv sync --all-extras
uv run pytest
uv run ruff check
uv run ruff format --check
uv run mypy --strict src/vmx
The lifecycle-transitions.json fixture from spec/fixtures/ is tracked under
src/vmx/lifecycle/_data/ and shipped inside the wheel. The
vmx.lifecycle.transition_validator module loads it via importlib.resources
with a repo-relative fallback for diagnostics. tools/check-python-fixture-sync.py
keeps the package copy byte-identical to the spec fixture.
7. Releasing
See RELEASING.md for the PyPI release pipeline runbook.
8. 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 vmx-3.1.0.tar.gz.
File metadata
- Download URL: vmx-3.1.0.tar.gz
- Upload date:
- Size: 206.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
788f6a8fd7b769a2425ca66713804eda4447ce51ce3e57a27825c2d74bc9a97d
|
|
| MD5 |
0aa1a765dfacdf414a0a86f788213bc7
|
|
| BLAKE2b-256 |
cefde1ddac43bda036ca9adc3c609c2db3eb2387f3a3a3cb17580d79fb0fc473
|
Provenance
The following attestation bundles were made for vmx-3.1.0.tar.gz:
Publisher:
release.yml on thekaveh/VMx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vmx-3.1.0.tar.gz -
Subject digest:
788f6a8fd7b769a2425ca66713804eda4447ce51ce3e57a27825c2d74bc9a97d - Sigstore transparency entry: 2043175756
- Sigstore integration time:
-
Permalink:
thekaveh/VMx@a77b25aa28078eb3d56fba011d4fe32e9a2a4d12 -
Branch / Tag:
refs/tags/python-v3.1.0 - Owner: https://github.com/thekaveh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a77b25aa28078eb3d56fba011d4fe32e9a2a4d12 -
Trigger Event:
push
-
Statement type:
File details
Details for the file vmx-3.1.0-py3-none-any.whl.
File metadata
- Download URL: vmx-3.1.0-py3-none-any.whl
- Upload date:
- Size: 133.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aab6343d2d0fe41e44ee417bed738819ce92fb713b7496279a6835641a38430c
|
|
| MD5 |
4ddf3cc1052dd64641ae9c9134e6a7fb
|
|
| BLAKE2b-256 |
5b9be13febc03ed9eba1829b48bc3240c2b6f430c7c920ce16e7880b1215629d
|
Provenance
The following attestation bundles were made for vmx-3.1.0-py3-none-any.whl:
Publisher:
release.yml on thekaveh/VMx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vmx-3.1.0-py3-none-any.whl -
Subject digest:
aab6343d2d0fe41e44ee417bed738819ce92fb713b7496279a6835641a38430c - Sigstore transparency entry: 2043175807
- Sigstore integration time:
-
Permalink:
thekaveh/VMx@a77b25aa28078eb3d56fba011d4fe32e9a2a4d12 -
Branch / Tag:
refs/tags/python-v3.1.0 - Owner: https://github.com/thekaveh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a77b25aa28078eb3d56fba011d4fe32e9a2a4d12 -
Trigger Event:
push
-
Statement type: