Skip to main content
Pre-release

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

English | 한국어

pythonx-compose

Write Compose user interfaces in Python — with Compose's own widgets, spelled the Python way.

License: MIT 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 anything — renaming is this package's job.
  • 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

From a clone, to run the tests:

git clone https://github.com/thisisthepy/pythonx-compose
cd pythonx-compose
python3 -m pip install pytest      # inside a virtual environment
python3 -m 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) and Pythonic state (state), no update call 🟡 partial — binder path tested against a fake host shaped after python-multiplatform #38; real Compose is the E2E module (#11, #19); numbers and strings round-trip through state
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, coroutine scopes ⏳ planned

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

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

Metadata

Release files for pythonx-compose 0.1.0a1

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.0a1
File Size Uploaded
pythonx_compose-0.1.0a1.tar.gz 126.4 kB Details

Built distribution (wheel)

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

Total release size: 220.5 kB

Release files / pythonx_compose-0.1.0a1.tar.gz

Download URL pythonx_compose-0.1.0a1.tar.gz
Size 126.4 kB
Tags Source
SHA-256 checksum
How to use checksums
518698f0c1ea335d7a0c93fd080e3ad6417d532c96f66aac72ca9924b3044496
BLAKE2b-256 checksum
How to use checksums
90f828d06db53ae847b451d866b1054ddc02c4eb72c8614808ebca17c1aaf576
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.0a1-py3-none-any.whl

Download URL pythonx_compose-0.1.0a1-py3-none-any.whl
Size 94.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7a075c23c361ef8df6ba3786ce3b8d55891cb475f70b6078d5e75dac5116df62
BLAKE2b-256 checksum
How to use checksums
4ab70975ee8503cb8cffe1140c8900c305435ba43deda0ea2cc1160417e4524b
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.0a1 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