Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

English | 한국어

pythonx-compose

Build Compose Multiplatform UIs in Python: Python Declarative UI Framework

License: Apache-2.0 Python pip Status

Guide · Getting started · Concepts · Status


Why

Jetpack Compose and Compose Multiplatform are a superb way to build UI, if you write Kotlin. pythonx-compose is for people who write Python. It does not reimplement Compose and it does not invent a new widget set: it takes the real androidx.compose.* API, which python-multiplatform exposes to Python under its Kotlin names, and restructures it into a Pythonic package, pythonx.compose.

from pythonx.compose.runtime import Composable
from pythonx.compose.material3 import Text, Button

@Composable
def Greeting():
    Button(on_click=lambda: print("hi"), content=lambda: Text("Hello from Python"))

These names resolve today, by one rule, inside an app that embeds the binder; per-widget render proofs are still pending. See Status.

✨ Principles

  • The original API, Pythonic names. Every parameter is Compose's own parameter, in snake_case: onClick → on_click, horizontalAlignment → horizontal_alignment. Upper-case names (types, objects, composables) keep their Kotlin spelling; every other name is snake_case only (fillMaxWidth → fill_max_width, toURLString → to_url_string), and an explicit overload keeps its suffix (padding__Dp).
  • Extension functions are methods. Modifier.padding(16).size(24).fill_max_width() chains exactly as it does in Kotlin. Keyword arguments to such a method are snake_case too (m.padding(padding_values=...)), as they are on the module function padding(m, padding_values=...); Kotlin's own spelling still works at run time, and the type stubs offer only the Pythonic one.
  • Kotlin objects are namespaces. Alignment.Center and Arrangement.End are read without parentheses; functions inside them are snake_case: Arrangement.spaced_by(8). Constants can also be reached grouped by their declared type, as the notebook writes them: Alignment.Horizontal.End is Alignment.End.
  • @Composable stays. Screens are decorated Python functions.
  • A real package. pythonx/ is ordinary Python source that imports androidx.compose.* and reshapes it. The binder never renames a namespace. Since python-multiplatform #131 it serves snake_case names and keywords itself, by this package's rule; this package keeps the module grouping and the snake_case-only surface.
  • One manifest. pythonx-map.toml says which pythonx.compose.* module stands for which Kotlin package. It lives inside the package and ships in the wheel. The runtime and the .pyi generator read the same file, so what your editor completes is what the interpreter resolves.

🧩 Architecture at a glance

flowchart LR
    app["Your Python UI<br/>@Composable def Screen()"] --> px["pythonx.compose<br/>(this package, real Python)"]
    px --> ax["androidx.compose.*<br/>Kotlin names, exposed by python-multiplatform"]
    ax --> compose["Jetpack / Compose Multiplatform"]
    map["pythonx-map.toml"] -.-> px
    map -.-> pyi[".pyi stubs<br/>(in the wheel)"]
Python module Kotlin package
pythonx.compose.runtime androidx.compose.runtime
pythonx.compose.ui androidx.compose.ui
pythonx.compose.layout androidx.compose.foundation.layout
pythonx.compose.material3 androidx.compose.material3

Each module is a real file whose __init__.py calls one re-export rule; names resolve on first use. Column, Row and Spacer are importable from pythonx.compose.material3 as well as pythonx.compose.layout, as the notebook writes them (the manifest's [aliases]).

🚀 Quick start

uv add --prerelease allow pythonx-compose
# or, with pypackpack, into a package of a ppp workspace:
ppp core add "pythonx-compose==0.1.0a2"
# or, with toolchain-lite:
tcl install pythonx-compose

From a clone, to run the tests:

git clone https://github.com/thisisthepy/pythonx-compose
cd pythonx-compose
uv run --with pytest --with mypy pytest tests -q

What runs today, from the repository root:

from pythonx.compose.runtime import Composable

@Composable                      # an identity decorator: the function stays a plain function
def Screen():
    """A screen."""
    return "drawn"

assert Screen() == "drawn" and Screen.__name__ == "Screen"
import tomllib                   # the manifest: the one place the mapping is written down

with open("pythonx/compose/pythonx-map.toml", "rb") as f:
    manifest = tomllib.load(f)

manifest["modules"]["pythonx.compose.layout"]
# 'androidx.compose.foundation.layout'
manifest["value-classes"]["raw-primitive-allowed"]
# ['androidx.compose.ui.unit.Dp']   ->  padding(16) means padding(16.dp)
from pythonx.compose._reexport import python_name   # the naming rule, no binder needed

python_name("fillMaxWidth")   # 'fill_max_width'
python_name("toURLString")    # 'to_url_string'
python_name("Modifier")       # 'Modifier'

Reading a Compose name such as pythonx.compose.material3.Text needs the binder, which the Kotlin host installs; outside an app it raises a RuntimeError saying so.

The tests that drive a Modifier chain and overload dispatch read the binder's adaptation layer from a sibling python-multiplatform checkout (or PYTHONMULTIPLATFORM_HOME); without one they are skipped.

📦 Installation

Distributed as the pip package pythonx-compose (Python 3.11 or later), providing the import package pythonx.compose, with the manifest inside the wheel, and typed: .pyi stubs generated from real Compose 1.11.1 and py.typed ship with it. It runs inside an app that embeds CPython through python-multiplatform; it is not a standalone desktop toolkit.

🧪 Status

Area State
Mapping manifest pythonx-map.toml ✅ implemented and tested
@Composable decorator ✅ implemented and tested
pythonx as a real on-disk package re-exporting androidx.compose.* by one rule ✅ implemented and tested
Modifier chains with snake_case methods, overload dispatch, Dp as a number ✅ implemented and tested against the binder's layer
Column, Row, Spacer from material3 as well as layout ✅ implemented and tested
Method keyword arguments in snake_case ✅ implemented and tested: module functions and methods (methods need python-multiplatform's describe_member)
The empty Modifier 🟡 partial: Modifier.padding(16) from the class needs an app-supplied factory against real Compose
Alignment / Arrangement ✅ implemented and tested: Alignment.Center, Arrangement.spaced_by(8), and grouped Alignment.Horizontal.End beside Alignment.End
Material 3 widgets (Text, Button, Card, TextField, …) 🟡 partial: re-exported by rule; render proofs per widget pending (#9)
Type stubs (.pyi) from real Compose 1.11.1, py.typed ✅ implemented and checked with mypy; many types are still Any (#12)
Distribution (pythonx-compose) 🟡 partial: the wheel carries the manifest, the re-export rule, the stubs and py.typed
Declared app root (@app), no update call; state through Kotlin's mutable_state_of 🟡 partial: binder path tested against a fake host shaped after python-multiplatform #38; against real Compose, python-multiplatform's notebook E2E passes (#11, #19); numbers and strings round-trip
TextField(state=...) with TextFieldState (pythonx.compose.foundation.text.input) 🟡 partial: tested against a fake host shaped after python-multiplatform #73; the input-method (IME) composing proof is python-multiplatform E2E #26 (#10)
DefaultIcons (Icons.Default), written DefaultIcons.Add 🟡 partial: tested against a fake host shaped after python-multiplatform #37/#38; Icon(DefaultIcons.Add, …) is drawn in python-multiplatform's render test
Colour schemes ⏳ planned
remember_saveable with the notebook's getValue() / setValue() 🟡 partial: tested against a fake host; save and restore is python-multiplatform's test (#174)
Coroutine scopes (DefaultCoroutineScope, MainCoroutineScope) ⏳ planned, after pythonx-concurrent

The full list is on the guide's Status page.

📖 Documentation

🔌 Ecosystem

Repository Role
python-multiplatform The binder: CPython embedded in Kotlin Multiplatform, exposing Kotlin to Python under Kotlin names
pythonx-compose Compose, restructured for Python (this repository)
toolchain Gradle build plugin for Python Multiplatform apps
pypackpack Distributing Python projects across platforms
torchnative Run the real PyTorch ecosystem on device
Gemstone A Kotlin Multiplatform AI app built alongside the stack

🤝 Contributing

Development is intent-first and test-first: a change starts as a specification change, then a failing test, then code. The guide lists what is open and where help is useful. Please open an issue before large changes.

Maintainers

Name Area Since
@b-re-w Composable runtime 2023
@rnoro5122 Material 3 2024

License

Apache License 2.0 © 2023–2024 BREW (b-re-w), Jong-uk Lee (rnoro5122)

Metadata

Release files for pythonx-compose 0.1.0a2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pythonx-compose 0.1.0a2
File Size Uploaded
pythonx_compose-0.1.0a2.tar.gz 134.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pythonx-compose 0.1.0a2
File Interpreter ABI Platform
pythonx_compose-0.1.0a2-py3-none-any.whl Python 3 none any Details

Total release size: 231.9 kB

Release files / pythonx_compose-0.1.0a2.tar.gz

Download URL pythonx_compose-0.1.0a2.tar.gz
Size 134.1 kB
Tags Source
SHA-256 checksum
How to use checksums
513dbc1431ae88283f199c7507ac37c7da7c8b71df6aa37dc39698dea132f4c0
BLAKE2b-256 checksum
How to use checksums
d53b393aaf57817ae53f740e4ed65a94a241b4609c9e265340e1364fd1d73ab3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.

Transparency log

Release files / pythonx_compose-0.1.0a2-py3-none-any.whl

Download URL pythonx_compose-0.1.0a2-py3-none-any.whl
Size 97.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2117f6350d41b4fd2772385138cdb22cb8a1e49e532818e01a18d757d0d50cee
BLAKE2b-256 checksum
How to use checksums
7d8029cc36a37d3059013e38a2fa672b5cb94c4f1326718b9bae9d94fc0e8224
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0a2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page