pythonx-compose 0.1.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. pythonx_compose-0.1.0a1/LICENSE +21 -0
  2. pythonx_compose-0.1.0a1/PKG-INFO +228 -0
  3. pythonx_compose-0.1.0a1/README.md +203 -0
  4. pythonx_compose-0.1.0a1/pyproject.toml +49 -0
  5. pythonx_compose-0.1.0a1/pythonx/compose/__init__.py +4 -0
  6. pythonx_compose-0.1.0a1/pythonx/compose/__init__.pyi +1 -0
  7. pythonx_compose-0.1.0a1/pythonx/compose/_reexport.py +512 -0
  8. pythonx_compose-0.1.0a1/pythonx/compose/foundation/__init__.py +5 -0
  9. pythonx_compose-0.1.0a1/pythonx/compose/foundation/__init__.pyi +17 -0
  10. pythonx_compose-0.1.0a1/pythonx/compose/foundation/layout/__init__.py +5 -0
  11. pythonx_compose-0.1.0a1/pythonx/compose/foundation/layout/__init__.pyi +2 -0
  12. pythonx_compose-0.1.0a1/pythonx/compose/foundation/text/__init__.py +5 -0
  13. pythonx_compose-0.1.0a1/pythonx/compose/foundation/text/input/__init__.py +5 -0
  14. pythonx_compose-0.1.0a1/pythonx/compose/foundation/text/input/__init__.pyi +78 -0
  15. pythonx_compose-0.1.0a1/pythonx/compose/layout/__init__.py +25 -0
  16. pythonx_compose-0.1.0a1/pythonx/compose/layout/__init__.pyi +878 -0
  17. pythonx_compose-0.1.0a1/pythonx/compose/layout/arrangement.py +34 -0
  18. pythonx_compose-0.1.0a1/pythonx/compose/material/__init__.py +8 -0
  19. pythonx_compose-0.1.0a1/pythonx/compose/material/__init__.pyi +1 -0
  20. pythonx_compose-0.1.0a1/pythonx/compose/material/icons/__init__.py +9 -0
  21. pythonx_compose-0.1.0a1/pythonx/compose/material/icons/__init__.pyi +1159 -0
  22. pythonx_compose-0.1.0a1/pythonx/compose/material3/__init__.py +24 -0
  23. pythonx_compose-0.1.0a1/pythonx/compose/material3/__init__.pyi +3305 -0
  24. pythonx_compose-0.1.0a1/pythonx/compose/py.typed +0 -0
  25. pythonx_compose-0.1.0a1/pythonx/compose/pythonx-map.toml +73 -0
  26. pythonx_compose-0.1.0a1/pythonx/compose/runtime/__init__.py +128 -0
  27. pythonx_compose-0.1.0a1/pythonx/compose/runtime/__init__.pyi +180 -0
  28. pythonx_compose-0.1.0a1/pythonx/compose/ui/__init__.py +21 -0
  29. pythonx_compose-0.1.0a1/pythonx/compose/ui/__init__.pyi +975 -0
  30. pythonx_compose-0.1.0a1/pythonx/compose/ui/alignment.py +47 -0
  31. pythonx_compose-0.1.0a1/pythonx/compose/ui/graphics/__init__.py +5 -0
  32. pythonx_compose-0.1.0a1/pythonx/compose/ui/graphics/__init__.pyi +795 -0
  33. pythonx_compose-0.1.0a1/pythonx/compose/ui/graphics/vector/__init__.py +5 -0
  34. pythonx_compose-0.1.0a1/pythonx/compose/ui/graphics/vector/__init__.pyi +221 -0
  35. pythonx_compose-0.1.0a1/pythonx/compose/ui/modifier.py +110 -0
  36. pythonx_compose-0.1.0a1/pythonx/compose/ui/text/__init__.py +5 -0
  37. pythonx_compose-0.1.0a1/pythonx/compose/ui/text/__init__.pyi +703 -0
  38. pythonx_compose-0.1.0a1/pythonx/compose/ui/unit/__init__.py +19 -0
  39. pythonx_compose-0.1.0a1/pythonx/compose/ui/unit/__init__.pyi +348 -0
  40. pythonx_compose-0.1.0a1/pythonx_compose.egg-info/PKG-INFO +228 -0
  41. pythonx_compose-0.1.0a1/pythonx_compose.egg-info/SOURCES.txt +57 -0
  42. pythonx_compose-0.1.0a1/pythonx_compose.egg-info/dependency_links.txt +1 -0
  43. pythonx_compose-0.1.0a1/pythonx_compose.egg-info/top_level.txt +1 -0
  44. pythonx_compose-0.1.0a1/setup.cfg +4 -0
  45. pythonx_compose-0.1.0a1/tests/test_alignment_modules.py +128 -0
  46. pythonx_compose-0.1.0a1/tests/test_app_root.py +287 -0
  47. pythonx_compose-0.1.0a1/tests/test_chain.py +768 -0
  48. pythonx_compose-0.1.0a1/tests/test_guide.py +25 -0
  49. pythonx_compose-0.1.0a1/tests/test_legacy_modules.py +111 -0
  50. pythonx_compose-0.1.0a1/tests/test_material3_module.py +244 -0
  51. pythonx_compose-0.1.0a1/tests/test_modifier_module.py +120 -0
  52. pythonx_compose-0.1.0a1/tests/test_pythonx_map.py +152 -0
  53. pythonx_compose-0.1.0a1/tests/test_release_version.py +47 -0
  54. pythonx_compose-0.1.0a1/tests/test_runtime_module.py +155 -0
  55. pythonx_compose-0.1.0a1/tests/test_stubs.py +647 -0
  56. pythonx_compose-0.1.0a1/tests/test_text_field.py +116 -0
  57. pythonx_compose-0.1.0a1/tests/test_typing.py +265 -0
  58. pythonx_compose-0.1.0a1/tests/test_ui_init_module.py +77 -0
  59. pythonx_compose-0.1.0a1/tests/test_wheel.py +91 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2024 BREW (b-re-w), Jong-uk Lee (rnoro5122)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: pythonx-compose
3
+ Version: 0.1.0a1
4
+ Summary: Pythonic Compose UI for Python on Kotlin Multiplatform, through python-multiplatform
5
+ Author: BREW (b-re-w), Jong-uk Lee (rnoro5122)
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/thisisthepy/pythonx-compose
8
+ Project-URL: Source, https://github.com/thisisthepy/pythonx-compose
9
+ Project-URL: Issues, https://github.com/thisisthepy/pythonx-compose/issues
10
+ Project-URL: Documentation, https://thisisthepy.github.io/pythonx-compose/
11
+ Keywords: compose,jetpack-compose,compose-multiplatform,kotlin-multiplatform,ui,python-multiplatform
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: User Interfaces
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: license-file
25
+
26
+ English | [ํ•œ๊ตญ์–ด](https://github.com/thisisthepy/pythonx-compose/blob/main/docs/locale/README_ko.md)
27
+
28
+ <div align="center">
29
+
30
+ # pythonx-compose
31
+
32
+ **Write Compose user interfaces in Python โ€” with Compose's own widgets, spelled the Python way.**
33
+
34
+ [![License: MIT](https://img.shields.io/badge/license-MIT-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE)
35
+ [![Python](https://img.shields.io/badge/python-3.11%2B-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/pyproject.toml)
36
+ [![pip](https://img.shields.io/badge/pip-pythonx--compose-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/pyproject.toml)
37
+ [![Status](https://img.shields.io/badge/status-pre--alpha-lightgrey.svg)](#-status)
38
+
39
+ [Guide](https://thisisthepy.github.io/pythonx-compose/) ยท [Getting started](https://thisisthepy.github.io/pythonx-compose/getting-started.html) ยท [Concepts](https://thisisthepy.github.io/pythonx-compose/concepts.html) ยท [Status](https://thisisthepy.github.io/pythonx-compose/status.html)
40
+
41
+ </div>
42
+
43
+ ---
44
+
45
+ ## Why
46
+
47
+ Jetpack Compose and Compose Multiplatform are a superb way to build UI โ€” if you write Kotlin.
48
+ `pythonx-compose` is for people who write Python. It does not reimplement Compose and it does not
49
+ invent a new widget set: it takes the real `androidx.compose.*` API, which
50
+ [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) exposes to Python under
51
+ its Kotlin names, and restructures it into a Pythonic package, `pythonx.compose`.
52
+
53
+ ```python
54
+ from pythonx.compose.runtime import Composable
55
+ from pythonx.compose.material3 import Text, Button
56
+
57
+ @Composable
58
+ def Greeting():
59
+ Button(on_click=lambda: print("hi"), content=lambda: Text("Hello from Python"))
60
+ ```
61
+
62
+ <sub>These names resolve today, by one rule, inside an app that embeds the binder; per-widget render
63
+ proofs are still pending. See [Status](#-status).</sub>
64
+
65
+ ## โœจ Principles
66
+
67
+ - **The original API, Pythonic names.** Every parameter is Compose's own parameter, in
68
+ `snake_case`: `onClick` โ†’ `on_click`, `horizontalAlignment` โ†’ `horizontal_alignment`. Upper-case
69
+ names (types, objects, composables) keep their Kotlin spelling; every other name is `snake_case`
70
+ only (`fillMaxWidth` โ†’ `fill_max_width`, `toURLString` โ†’ `to_url_string`), and an explicit
71
+ overload keeps its suffix (`padding__Dp`).
72
+ - **Extension functions are methods.** `Modifier.padding(16).size(24).fill_max_width()` chains
73
+ exactly as it does in Kotlin. Keyword arguments to such a method are `snake_case`
74
+ too (`m.padding(padding_values=...)`), as they are on the module function
75
+ `padding(m, padding_values=...)`; Kotlin's own spelling still works at run time, and the type
76
+ stubs offer only the Pythonic one.
77
+ - **Kotlin objects are namespaces.** `Alignment.Center` and `Arrangement.End` are read without
78
+ parentheses; functions inside them are `snake_case`: `Arrangement.spaced_by(8)`. Constants can
79
+ also be reached grouped by their declared type, as the notebook writes them:
80
+ `Alignment.Horizontal.End` is `Alignment.End`.
81
+ - **`@Composable` stays.** Screens are decorated Python functions.
82
+ - **A real package.** `pythonx/` is ordinary Python source that imports `androidx.compose.*` and
83
+ reshapes it. The binder never renames anything โ€” renaming is this package's job.
84
+ - **One manifest.** [`pythonx-map.toml`](https://github.com/thisisthepy/pythonx-compose/blob/main/pythonx/compose/pythonx-map.toml) says which `pythonx.compose.*` module
85
+ stands for which Kotlin package. It lives inside the package and ships in the wheel. The runtime
86
+ and the `.pyi` generator read the same file, so what your editor completes is what the interpreter
87
+ resolves.
88
+
89
+ ## ๐Ÿงฉ Architecture at a glance
90
+
91
+ ```mermaid
92
+ flowchart LR
93
+ app["Your Python UI<br/>@Composable def Screen()"] --> px["pythonx.compose<br/>(this package, real Python)"]
94
+ px --> ax["androidx.compose.*<br/>Kotlin names, exposed by python-multiplatform"]
95
+ ax --> compose["Jetpack / Compose Multiplatform"]
96
+ map["pythonx-map.toml"] -.-> px
97
+ map -.-> pyi[".pyi stubs<br/>(in the wheel)"]
98
+ ```
99
+
100
+ | Python module | Kotlin package |
101
+ |---|---|
102
+ | `pythonx.compose.runtime` | `androidx.compose.runtime` |
103
+ | `pythonx.compose.ui` | `androidx.compose.ui` |
104
+ | `pythonx.compose.layout` | `androidx.compose.foundation.layout` |
105
+ | `pythonx.compose.material3` | `androidx.compose.material3` |
106
+
107
+ Each module is a real file whose `__init__.py` calls one re-export rule; names resolve on first use.
108
+ `Column`, `Row` and `Spacer` are importable from `pythonx.compose.material3` as well as
109
+ `pythonx.compose.layout`, as the notebook writes them (the manifest's `[aliases]`).
110
+
111
+ ## ๐Ÿš€ Quick start
112
+
113
+ > [!NOTE]
114
+ > `pythonx-compose` is **pre-alpha**. The first alpha, `0.1.0a1`, is released to PyPI when the
115
+ > maintainer tags it; a pre-release needs `pip install --pre pythonx-compose`.
116
+
117
+ From a clone, to run the tests:
118
+
119
+ ```bash
120
+ git clone https://github.com/thisisthepy/pythonx-compose
121
+ cd pythonx-compose
122
+ python3 -m pip install pytest # inside a virtual environment
123
+ python3 -m pytest tests -q
124
+ ```
125
+
126
+ What runs today, from the repository root:
127
+
128
+ ```python
129
+ from pythonx.compose.runtime import Composable
130
+
131
+ @Composable # an identity decorator: the function stays a plain function
132
+ def Screen():
133
+ """A screen."""
134
+ return "drawn"
135
+
136
+ assert Screen() == "drawn" and Screen.__name__ == "Screen"
137
+ ```
138
+
139
+ ```python
140
+ import tomllib # the manifest: the one place the mapping is written down
141
+
142
+ with open("pythonx/compose/pythonx-map.toml", "rb") as f:
143
+ manifest = tomllib.load(f)
144
+
145
+ manifest["modules"]["pythonx.compose.layout"]
146
+ # 'androidx.compose.foundation.layout'
147
+ manifest["value-classes"]["raw-primitive-allowed"]
148
+ # ['androidx.compose.ui.unit.Dp'] -> padding(16) means padding(16.dp)
149
+ ```
150
+
151
+ ```python
152
+ from pythonx.compose._reexport import python_name # the naming rule, no binder needed
153
+
154
+ python_name("fillMaxWidth") # 'fill_max_width'
155
+ python_name("toURLString") # 'to_url_string'
156
+ python_name("Modifier") # 'Modifier'
157
+ ```
158
+
159
+ Reading a Compose name such as `pythonx.compose.material3.Text` needs the binder, which the Kotlin
160
+ host installs; outside an app it raises a `RuntimeError` saying so.
161
+
162
+ The tests that drive a `Modifier` chain and overload dispatch read the binder's adaptation layer
163
+ from a sibling [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) checkout
164
+ (or `PYTHONMULTIPLATFORM_HOME`); without one they are skipped.
165
+
166
+ ## ๐Ÿ“ฆ Installation
167
+
168
+ Distributed as the pip package **`pythonx-compose`** (Python 3.11 or later), providing the import
169
+ package `pythonx.compose`, with the manifest inside the wheel, and typed: `.pyi` stubs generated from
170
+ real Compose 1.11.1 and `py.typed` ship with it. It runs inside an app that embeds CPython
171
+ through [python-multiplatform](https://github.com/thisisthepy/python-multiplatform); it is not a
172
+ standalone desktop toolkit.
173
+
174
+ ## ๐Ÿงช Status
175
+
176
+ | Area | State |
177
+ |---|---|
178
+ | Mapping manifest `pythonx-map.toml` | โœ… implemented and tested |
179
+ | `@Composable` decorator | โœ… implemented and tested |
180
+ | `pythonx` as a real on-disk package re-exporting `androidx.compose.*` by one rule | โœ… implemented and tested |
181
+ | `Modifier` chains with snake_case methods, overload dispatch, `Dp` as a number | โœ… implemented and tested against the binder's layer |
182
+ | `Column`, `Row`, `Spacer` from `material3` as well as `layout` | โœ… implemented and tested |
183
+ | Method keyword arguments in `snake_case` | โœ… implemented and tested โ€” module functions and methods (methods need python-multiplatform's `describe_member`) |
184
+ | The empty `Modifier` | ๐ŸŸก partial โ€” `Modifier.padding(16)` from the class needs an app-supplied factory against real Compose |
185
+ | `Alignment` / `Arrangement` | โœ… implemented and tested โ€” `Alignment.Center`, `Arrangement.spaced_by(8)`, and grouped `Alignment.Horizontal.End` beside `Alignment.End` |
186
+ | Material 3 widgets (`Text`, `Button`, `Card`, `TextField`, โ€ฆ) | ๐ŸŸก partial โ€” re-exported by rule; render proofs per widget pending (#9) |
187
+ | Type stubs (`.pyi`) from real Compose 1.11.1, `py.typed` | โœ… implemented and checked with mypy; many types are still `Any` (#12) |
188
+ | Distribution (`pythonx-compose`) | ๐ŸŸก partial โ€” the wheel carries the manifest, the re-export rule, the stubs and `py.typed` |
189
+ | 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` |
190
+ | `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) |
191
+ | `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 |
192
+ | Colour schemes | โณ planned |
193
+ | `remember_saveable`, coroutine scopes | โณ planned |
194
+
195
+ The full list is on the guide's [Status page](https://thisisthepy.github.io/pythonx-compose/status.html).
196
+
197
+ ## ๐Ÿ“– Documentation
198
+
199
+ - **Guide** โ€” [`docs/guide/`](https://thisisthepy.github.io/pythonx-compose/), bilingual (English / ํ•œ๊ตญ์–ด)
200
+ - **Korean README** โ€” [`docs/locale/README_ko.md`](https://github.com/thisisthepy/pythonx-compose/blob/main/docs/locale/README_ko.md)
201
+
202
+ ## ๐Ÿ”Œ Ecosystem
203
+
204
+ | Repository | Role |
205
+ |---|---|
206
+ | [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) | The binder: CPython embedded in Kotlin Multiplatform, exposing Kotlin to Python under Kotlin names |
207
+ | **pythonx-compose** | Compose, restructured for Python โ€” this repository |
208
+ | [toolchain](https://github.com/thisisthepy/toolchain) | Gradle build plugin for Python Multiplatform apps |
209
+ | [pypackpack](https://github.com/thisisthepy/pypackpack) | Distributing Python projects across platforms |
210
+ | [torchnative](https://github.com/thisisthepy/torchnative) | Run the real PyTorch ecosystem on device |
211
+ | [Gemstone](https://github.com/LogitAI/Gemstone) | A Kotlin Multiplatform AI app built alongside the stack |
212
+
213
+ ## ๐Ÿค Contributing
214
+
215
+ Development is intent-first and test-first: a change starts as a specification change, then a
216
+ failing test, then code. The [guide](https://thisisthepy.github.io/pythonx-compose/status.html) lists what is open and where help is
217
+ useful. Please open an issue before large changes.
218
+
219
+ ## Maintainers
220
+
221
+ | Name | Area | Since |
222
+ |---|---|---|
223
+ | [@b-re-w](https://github.com/b-re-w) | Composable runtime | 2023 |
224
+ | [@rnoro5122](https://github.com/rnoro5122) | Material 3 | 2024 |
225
+
226
+ ## License
227
+
228
+ [MIT](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) ยฉ 2023โ€“2024 BREW (b-re-w), Jong-uk Lee (rnoro5122)
@@ -0,0 +1,203 @@
1
+ English | [ํ•œ๊ตญ์–ด](https://github.com/thisisthepy/pythonx-compose/blob/main/docs/locale/README_ko.md)
2
+
3
+ <div align="center">
4
+
5
+ # pythonx-compose
6
+
7
+ **Write Compose user interfaces in Python โ€” with Compose's own widgets, spelled the Python way.**
8
+
9
+ [![License: MIT](https://img.shields.io/badge/license-MIT-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE)
10
+ [![Python](https://img.shields.io/badge/python-3.11%2B-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/pyproject.toml)
11
+ [![pip](https://img.shields.io/badge/pip-pythonx--compose-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/pyproject.toml)
12
+ [![Status](https://img.shields.io/badge/status-pre--alpha-lightgrey.svg)](#-status)
13
+
14
+ [Guide](https://thisisthepy.github.io/pythonx-compose/) ยท [Getting started](https://thisisthepy.github.io/pythonx-compose/getting-started.html) ยท [Concepts](https://thisisthepy.github.io/pythonx-compose/concepts.html) ยท [Status](https://thisisthepy.github.io/pythonx-compose/status.html)
15
+
16
+ </div>
17
+
18
+ ---
19
+
20
+ ## Why
21
+
22
+ Jetpack Compose and Compose Multiplatform are a superb way to build UI โ€” if you write Kotlin.
23
+ `pythonx-compose` is for people who write Python. It does not reimplement Compose and it does not
24
+ invent a new widget set: it takes the real `androidx.compose.*` API, which
25
+ [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) exposes to Python under
26
+ its Kotlin names, and restructures it into a Pythonic package, `pythonx.compose`.
27
+
28
+ ```python
29
+ from pythonx.compose.runtime import Composable
30
+ from pythonx.compose.material3 import Text, Button
31
+
32
+ @Composable
33
+ def Greeting():
34
+ Button(on_click=lambda: print("hi"), content=lambda: Text("Hello from Python"))
35
+ ```
36
+
37
+ <sub>These names resolve today, by one rule, inside an app that embeds the binder; per-widget render
38
+ proofs are still pending. See [Status](#-status).</sub>
39
+
40
+ ## โœจ Principles
41
+
42
+ - **The original API, Pythonic names.** Every parameter is Compose's own parameter, in
43
+ `snake_case`: `onClick` โ†’ `on_click`, `horizontalAlignment` โ†’ `horizontal_alignment`. Upper-case
44
+ names (types, objects, composables) keep their Kotlin spelling; every other name is `snake_case`
45
+ only (`fillMaxWidth` โ†’ `fill_max_width`, `toURLString` โ†’ `to_url_string`), and an explicit
46
+ overload keeps its suffix (`padding__Dp`).
47
+ - **Extension functions are methods.** `Modifier.padding(16).size(24).fill_max_width()` chains
48
+ exactly as it does in Kotlin. Keyword arguments to such a method are `snake_case`
49
+ too (`m.padding(padding_values=...)`), as they are on the module function
50
+ `padding(m, padding_values=...)`; Kotlin's own spelling still works at run time, and the type
51
+ stubs offer only the Pythonic one.
52
+ - **Kotlin objects are namespaces.** `Alignment.Center` and `Arrangement.End` are read without
53
+ parentheses; functions inside them are `snake_case`: `Arrangement.spaced_by(8)`. Constants can
54
+ also be reached grouped by their declared type, as the notebook writes them:
55
+ `Alignment.Horizontal.End` is `Alignment.End`.
56
+ - **`@Composable` stays.** Screens are decorated Python functions.
57
+ - **A real package.** `pythonx/` is ordinary Python source that imports `androidx.compose.*` and
58
+ reshapes it. The binder never renames anything โ€” renaming is this package's job.
59
+ - **One manifest.** [`pythonx-map.toml`](https://github.com/thisisthepy/pythonx-compose/blob/main/pythonx/compose/pythonx-map.toml) says which `pythonx.compose.*` module
60
+ stands for which Kotlin package. It lives inside the package and ships in the wheel. The runtime
61
+ and the `.pyi` generator read the same file, so what your editor completes is what the interpreter
62
+ resolves.
63
+
64
+ ## ๐Ÿงฉ Architecture at a glance
65
+
66
+ ```mermaid
67
+ flowchart LR
68
+ app["Your Python UI<br/>@Composable def Screen()"] --> px["pythonx.compose<br/>(this package, real Python)"]
69
+ px --> ax["androidx.compose.*<br/>Kotlin names, exposed by python-multiplatform"]
70
+ ax --> compose["Jetpack / Compose Multiplatform"]
71
+ map["pythonx-map.toml"] -.-> px
72
+ map -.-> pyi[".pyi stubs<br/>(in the wheel)"]
73
+ ```
74
+
75
+ | Python module | Kotlin package |
76
+ |---|---|
77
+ | `pythonx.compose.runtime` | `androidx.compose.runtime` |
78
+ | `pythonx.compose.ui` | `androidx.compose.ui` |
79
+ | `pythonx.compose.layout` | `androidx.compose.foundation.layout` |
80
+ | `pythonx.compose.material3` | `androidx.compose.material3` |
81
+
82
+ Each module is a real file whose `__init__.py` calls one re-export rule; names resolve on first use.
83
+ `Column`, `Row` and `Spacer` are importable from `pythonx.compose.material3` as well as
84
+ `pythonx.compose.layout`, as the notebook writes them (the manifest's `[aliases]`).
85
+
86
+ ## ๐Ÿš€ Quick start
87
+
88
+ > [!NOTE]
89
+ > `pythonx-compose` is **pre-alpha**. The first alpha, `0.1.0a1`, is released to PyPI when the
90
+ > maintainer tags it; a pre-release needs `pip install --pre pythonx-compose`.
91
+
92
+ From a clone, to run the tests:
93
+
94
+ ```bash
95
+ git clone https://github.com/thisisthepy/pythonx-compose
96
+ cd pythonx-compose
97
+ python3 -m pip install pytest # inside a virtual environment
98
+ python3 -m pytest tests -q
99
+ ```
100
+
101
+ What runs today, from the repository root:
102
+
103
+ ```python
104
+ from pythonx.compose.runtime import Composable
105
+
106
+ @Composable # an identity decorator: the function stays a plain function
107
+ def Screen():
108
+ """A screen."""
109
+ return "drawn"
110
+
111
+ assert Screen() == "drawn" and Screen.__name__ == "Screen"
112
+ ```
113
+
114
+ ```python
115
+ import tomllib # the manifest: the one place the mapping is written down
116
+
117
+ with open("pythonx/compose/pythonx-map.toml", "rb") as f:
118
+ manifest = tomllib.load(f)
119
+
120
+ manifest["modules"]["pythonx.compose.layout"]
121
+ # 'androidx.compose.foundation.layout'
122
+ manifest["value-classes"]["raw-primitive-allowed"]
123
+ # ['androidx.compose.ui.unit.Dp'] -> padding(16) means padding(16.dp)
124
+ ```
125
+
126
+ ```python
127
+ from pythonx.compose._reexport import python_name # the naming rule, no binder needed
128
+
129
+ python_name("fillMaxWidth") # 'fill_max_width'
130
+ python_name("toURLString") # 'to_url_string'
131
+ python_name("Modifier") # 'Modifier'
132
+ ```
133
+
134
+ Reading a Compose name such as `pythonx.compose.material3.Text` needs the binder, which the Kotlin
135
+ host installs; outside an app it raises a `RuntimeError` saying so.
136
+
137
+ The tests that drive a `Modifier` chain and overload dispatch read the binder's adaptation layer
138
+ from a sibling [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) checkout
139
+ (or `PYTHONMULTIPLATFORM_HOME`); without one they are skipped.
140
+
141
+ ## ๐Ÿ“ฆ Installation
142
+
143
+ Distributed as the pip package **`pythonx-compose`** (Python 3.11 or later), providing the import
144
+ package `pythonx.compose`, with the manifest inside the wheel, and typed: `.pyi` stubs generated from
145
+ real Compose 1.11.1 and `py.typed` ship with it. It runs inside an app that embeds CPython
146
+ through [python-multiplatform](https://github.com/thisisthepy/python-multiplatform); it is not a
147
+ standalone desktop toolkit.
148
+
149
+ ## ๐Ÿงช Status
150
+
151
+ | Area | State |
152
+ |---|---|
153
+ | Mapping manifest `pythonx-map.toml` | โœ… implemented and tested |
154
+ | `@Composable` decorator | โœ… implemented and tested |
155
+ | `pythonx` as a real on-disk package re-exporting `androidx.compose.*` by one rule | โœ… implemented and tested |
156
+ | `Modifier` chains with snake_case methods, overload dispatch, `Dp` as a number | โœ… implemented and tested against the binder's layer |
157
+ | `Column`, `Row`, `Spacer` from `material3` as well as `layout` | โœ… implemented and tested |
158
+ | Method keyword arguments in `snake_case` | โœ… implemented and tested โ€” module functions and methods (methods need python-multiplatform's `describe_member`) |
159
+ | The empty `Modifier` | ๐ŸŸก partial โ€” `Modifier.padding(16)` from the class needs an app-supplied factory against real Compose |
160
+ | `Alignment` / `Arrangement` | โœ… implemented and tested โ€” `Alignment.Center`, `Arrangement.spaced_by(8)`, and grouped `Alignment.Horizontal.End` beside `Alignment.End` |
161
+ | Material 3 widgets (`Text`, `Button`, `Card`, `TextField`, โ€ฆ) | ๐ŸŸก partial โ€” re-exported by rule; render proofs per widget pending (#9) |
162
+ | Type stubs (`.pyi`) from real Compose 1.11.1, `py.typed` | โœ… implemented and checked with mypy; many types are still `Any` (#12) |
163
+ | Distribution (`pythonx-compose`) | ๐ŸŸก partial โ€” the wheel carries the manifest, the re-export rule, the stubs and `py.typed` |
164
+ | 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` |
165
+ | `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) |
166
+ | `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 |
167
+ | Colour schemes | โณ planned |
168
+ | `remember_saveable`, coroutine scopes | โณ planned |
169
+
170
+ The full list is on the guide's [Status page](https://thisisthepy.github.io/pythonx-compose/status.html).
171
+
172
+ ## ๐Ÿ“– Documentation
173
+
174
+ - **Guide** โ€” [`docs/guide/`](https://thisisthepy.github.io/pythonx-compose/), bilingual (English / ํ•œ๊ตญ์–ด)
175
+ - **Korean README** โ€” [`docs/locale/README_ko.md`](https://github.com/thisisthepy/pythonx-compose/blob/main/docs/locale/README_ko.md)
176
+
177
+ ## ๐Ÿ”Œ Ecosystem
178
+
179
+ | Repository | Role |
180
+ |---|---|
181
+ | [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) | The binder: CPython embedded in Kotlin Multiplatform, exposing Kotlin to Python under Kotlin names |
182
+ | **pythonx-compose** | Compose, restructured for Python โ€” this repository |
183
+ | [toolchain](https://github.com/thisisthepy/toolchain) | Gradle build plugin for Python Multiplatform apps |
184
+ | [pypackpack](https://github.com/thisisthepy/pypackpack) | Distributing Python projects across platforms |
185
+ | [torchnative](https://github.com/thisisthepy/torchnative) | Run the real PyTorch ecosystem on device |
186
+ | [Gemstone](https://github.com/LogitAI/Gemstone) | A Kotlin Multiplatform AI app built alongside the stack |
187
+
188
+ ## ๐Ÿค Contributing
189
+
190
+ Development is intent-first and test-first: a change starts as a specification change, then a
191
+ failing test, then code. The [guide](https://thisisthepy.github.io/pythonx-compose/status.html) lists what is open and where help is
192
+ useful. Please open an issue before large changes.
193
+
194
+ ## Maintainers
195
+
196
+ | Name | Area | Since |
197
+ |---|---|---|
198
+ | [@b-re-w](https://github.com/b-re-w) | Composable runtime | 2023 |
199
+ | [@rnoro5122](https://github.com/rnoro5122) | Material 3 | 2024 |
200
+
201
+ ## License
202
+
203
+ [MIT](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) ยฉ 2023โ€“2024 BREW (b-re-w), Jong-uk Lee (rnoro5122)
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pythonx-compose"
7
+ version = "0.1.0a1"
8
+ description = "Pythonic Compose UI for Python on Kotlin Multiplatform, through python-multiplatform"
9
+ readme = {file = "README.md", content-type = "text/markdown"}
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [
13
+ {name = "BREW (b-re-w)"},
14
+ {name = "Jong-uk Lee (rnoro5122)"},
15
+ ]
16
+ keywords = ["compose", "jetpack-compose", "compose-multiplatform", "kotlin-multiplatform", "ui", "python-multiplatform"]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Developers",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Software Development :: User Interfaces",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ ]
30
+ #dependencies-kotlin = [
31
+ # "org.jetbrains.compose:compose-ui:0.4.0",
32
+ #]
33
+ requires-python = ">= 3.11" # tomllib, which reads the manifest at run time
34
+ #requires-kotlin = ">= 1.4.32"
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/thisisthepy/pythonx-compose"
38
+ Source = "https://github.com/thisisthepy/pythonx-compose"
39
+ Issues = "https://github.com/thisisthepy/pythonx-compose/issues"
40
+ Documentation = "https://thisisthepy.github.io/pythonx-compose/"
41
+
42
+ [tool.setuptools.packages.find]
43
+ include = ["pythonx.compose*"]
44
+
45
+ [tool.setuptools.package-data]
46
+ # Every package, not just `pythonx.compose` itself: a package-data key names one package and does
47
+ # not descend, so `"pythonx.compose"` alone would ship `pythonx/compose/*.pyi` and drop
48
+ # `pythonx/compose/ui/*.pyi` -- which is where most of the generated stubs land.
49
+ "*" = ["*.pyi", "py.typed", "pythonx-map.toml"]
@@ -0,0 +1,4 @@
1
+
2
+ from pythonx.compose._reexport import reexport
3
+
4
+ __getattr__, __dir__ = reexport(__name__)
@@ -0,0 +1 @@
1
+ # GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37103737430, commit a5028618, Compose 1.11.1). Do not edit.