You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 91f015e
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: PROJECT.md
+9-7Lines changed: 9 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,22 +27,24 @@ Compose(`androidx.compose.*`)를 파이썬에서 쓰기 위한 pip 패키지 **`
27
27
28
28
| 상태 | 항목 |
29
29
|---|---|
30
-
| 구현 | 매핑 매니페스트 `pythonx-map.toml`, `@Composable` 항등 데코레이터, 실제 디스크 패키지 `pythonx` 와 모듈 수준 규칙 기반 재노출(snake_case 이름·키워드·시그니처, 오버로드 디스패치, 값 클래스 허용 목록), 실제 Compose 1.11.1 에서 생성한 `.pyi` 스텁과 `py.typed`(wheel 포함, mypy 로 검사) |
31
-
| 부분 | 배포 설정, proxy 메서드의 키워드 인자는 아직 Kotlin 이름(`m.padding(paddingValues=...)`), Material 3 위젯(렌더 증거는 python-multiplatform 에만), `Icon`·색 스킴(import 만 됨), `Alignment`/`Arrangement` 상수(`pythonx` 에서 읽힘; 묶음 표기는 python-multiplatform #36 대기)|
30
+
| 구현 | 매핑 매니페스트 `pythonx-map.toml`, `@Composable` 항등 데코레이터, 실제 디스크 패키지 `pythonx` 와 모듈 수준 규칙 기반 재노출(snake_case 이름·키워드·시그니처, 오버로드 디스패치, 값 클래스 허용 목록), 실제 Compose 1.11.1 에서 생성한 `.pyi` 스텁과 `py.typed`(wheel 포함, mypy 로 검사), `Alignment`/`Arrangement` 상수(평면 `Alignment.End` 와 묶음 `Alignment.Horizontal.End` 모두, #9)|
31
+
| 부분 | 배포 설정, proxy 메서드의 키워드 인자는 아직 Kotlin 이름(`m.padding(paddingValues=...)`), Material 3 위젯(렌더 증거는 python-multiplatform 에만), `Icon`·색 스킴(import 만 됨) |
32
32
| 계획 | 선언형 앱 루트(#11), `remember_saveable`·`DefaultIcons`·코루틴 스코프 |
|`Alignment` / `Arrangement`|✅ implemented and tested — `Alignment.Center`, `Arrangement.spaced_by(8)`, and grouped `Alignment.Horizontal.End`beside `Alignment.End`|
154
156
| Material 3 widgets (`Text`, `Button`, `Card`, `TextField`, …) | 🟡 partial — re-exported by rule; render proofs per widget pending (#9) |
155
157
| Type stubs (`.pyi`) from real Compose 1.11.1, `py.typed`| ✅ implemented and checked with mypy; many types are still `Any`, and method keywords are Kotlin's (#12) |
156
158
| Distribution (`pythonx-compose`) | 🟡 partial — the wheel carries the manifest, the re-export rule, the stubs and `py.typed`|
Copy file name to clipboardExpand all lines: docs/SPEC.md
+62-16Lines changed: 62 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,24 +14,26 @@ Each item carries a status:
14
14
15
15
A behaviour change starts here, then becomes a failing test, then code (`AGENTS.md` §5).
16
16
17
-
## 0. Test baseline (2026-10-03, after issue #12)
17
+
## 0. Test baseline (2026-10-03, after issue #9's grouped constants)
18
18
19
19
Run from a worktree with `python -m pytest tests -q -rs` (pytest 8, mypy 2.4, CPython 3.13), where
20
20
`UI.ipynb` is absent and its one test skips:
21
21
22
22
| Environment | Result |
23
23
|---|---|
24
-
| No `PythonMultiplatform` checkout found |**84 passed, 60 skipped**, 115 subtests passed |
25
-
| python-multiplatform `develop` at `ba4c6f49` or later (has `add_member_resolver`) |**143 passed, 1 skipped**, 139 subtests passed |
26
-
| an older checkout, without `add_member_resolver`| the same, with 5 more skipped |
27
-
28
-
Without a checkout, the 59 tests that install the binder's layers through `tests/adapter.py` skip.
29
-
Against a binder older than `ba4c6f49`, the 5 that call a snake_case method on a proxy the binder
30
-
returned skip, because that needs its member resolver (python-multiplatform #17). The 1 skip in both
24
+
| No `PythonMultiplatform` checkout found |**91 passed, 73 skipped**, 122 subtests passed |
25
+
| python-multiplatform `develop` at `d6d39787` or later (has `add_member_resolver` and `describe(module, name)`) |**163 passed, 1 skipped**, 149 subtests passed |
26
+
| an older checkout, without `describe(module, name)` (python-multiplatform #36) | the same, with 13 more skipped |
27
+
| an older one still, without `add_member_resolver`| with 5 more skipped again |
28
+
29
+
Without a checkout, the 72 tests that install the binder's layers through `tests/adapter.py` skip.
30
+
Against a binder without `describe(module, name)`, the 13 that check grouped constants (§7, S7.1)
31
+
skip. Against a binder older than `ba4c6f49`, the 5 that call a snake_case method on a proxy the
32
+
binder returned skip as well, because that needs its member resolver (python-multiplatform #17). The 1 skip in both
31
33
rows is `UI.ipynb`'s (§2). `tests/test_typing.py` skips where mypy is not installed. A skip is not a
32
34
pass.
33
35
34
-
Of the 84 that pass without a checkout, 30 check the type stubs and the wheel (S1.2); most of the
36
+
Of the 91 that pass without a checkout, 37 check the type stubs and the wheel (S1.2); most of the
35
37
rest assert **absence** (a retired token, a deleted file, a docstring that exists). Those are listed
36
38
in §9 and are not counted as features.
37
39
@@ -261,7 +263,7 @@ a name is adapted once and then lives in the module dict; `dir()` reports the Py
261
263
without the binding layer a name read says the host never installed it; dropping a proxy releases
262
264
its Kotlin handle. Evidence: `tests/test_chain.py::Laziness` (5 tests), `::Handles` (1 test).
263
265
264
-
## 7. Layout constants — `Alignment` and `Arrangement` — `partial`
266
+
## 7. Layout constants — `Alignment` and `Arrangement` — `implemented`
265
267
266
268
The bound constant names are documented in `pythonx/compose/ui/alignment.py` (15 `Alignment`
267
269
names) and `pythonx/compose/layout/arrangement.py` (8 `Arrangement` names), checked by
@@ -277,11 +279,55 @@ the Kotlin module does not list is tried as a Kotlin object sub-package and serv
277
279
`KotlinObject` namespace by the §3 rule. Constants keep their Kotlin spelling and are read again on
278
280
every access; a function inside the object is snake_case (`Arrangement.spaced_by`). Evidence:
279
281
`tests/test_chain.py::ObjectNamespaces` (6 tests); without the sub-package step 6 fail. The binder
280
-
will list these objects itself (python-multiplatform #35).
281
-
282
-
Decided (INTENT §5.3) and not built: the notebook's grouping by type, `Alignment.Horizontal.End`.
283
-
It needs a constant's declared type without reading it, `python_multiplatform.describe(module,
284
-
name)` (python-multiplatform #36; issue #9).
282
+
now lists these objects itself (python-multiplatform #35), and either way reaches them.
<pdata-lang="en">The notebook's grouping by type, <code>Alignment.Horizontal.End</code>, is decided but not built yet; write <code>Alignment.End</code>.</p><pdata-lang="ko">노트북의 타입별 묶음 표기 <code>Alignment.Horizontal.End</code> 는 결정되었지만 아직 만들어지지 않았습니다. <code>Alignment.End</code> 로 쓰세요.</p>
83
+
Arrangement.spaced_by(8)
84
+
Alignment.Horizontal.End # the same constant as Alignment.End</code></pre>
85
+
<pdata-lang="en">A constant can also be read grouped by its declared type, as the notebook writes it: <code>Alignment.Horizontal.End</code> is <code>Alignment.End</code>, read the same way. A group holds only the constants declared as exactly that type, so <code>Alignment.Center</code> (declared <code>Alignment</code>) is in no group, and <code>Arrangement.SpaceBetween</code> is under <code>Arrangement.HorizontalOrVertical</code>, not <code>Arrangement.Horizontal</code>.</p><pdata-lang="ko">노트북처럼 상수를 선언된 타입별로 묶어서 읽을 수도 있습니다. <code>Alignment.Horizontal.End</code> 는 <code>Alignment.End</code> 이며 같은 방식으로 읽힙니다. 묶음에는 정확히 그 타입으로 선언된 상수만 들어가므로 <code>Alignment.Center</code>(선언 타입 <code>Alignment</code>)는 어느 묶음에도 없고, <code>Arrangement.SpaceBetween</code> 은 <code>Arrangement.Horizontal</code> 이 아니라 <code>Arrangement.HorizontalOrVertical</code> 아래에 있습니다.</p>
<pdata-lang="en">Expect <strong>84 passed, 60 skipped</strong>. One skipped test reads the maintainer's <code>UI.ipynb</code>, which a clone does not have; mypy type-checks the stubs. The other skipped tests drive the binder's adaptation layer and need a <ahref="https://github.com/thisisthepy/python-multiplatform">python-multiplatform</a> checkout beside this one, or:</p><pdata-lang="ko"><strong>84 passed, 60 skipped</strong> 가 정상입니다. 건너뛴 테스트 하나는 클론에는 없는 메인테이너의 <code>UI.ipynb</code> 를 읽으며, mypy 는 스텁을 타입 검사합니다. 나머지 건너뛴 테스트는 바인더의 적응 계층을 구동하며, 옆에 <ahref="https://github.com/thisisthepy/python-multiplatform">python-multiplatform</a> 체크아웃이 있거나 다음이 필요합니다:</p>
46
+
<pdata-lang="en">Expect <strong>91 passed, 73 skipped</strong>. One skipped test reads the maintainer's <code>UI.ipynb</code>, which a clone does not have; mypy type-checks the stubs. The other skipped tests drive the binder's adaptation layer and need a <ahref="https://github.com/thisisthepy/python-multiplatform">python-multiplatform</a> checkout beside this one, or:</p><pdata-lang="ko"><strong>91 passed, 73 skipped</strong> 가 정상입니다. 건너뛴 테스트 하나는 클론에는 없는 메인테이너의 <code>UI.ipynb</code> 를 읽으며, mypy 는 스텁을 타입 검사합니다. 나머지 건너뛴 테스트는 바인더의 적응 계층을 구동하며, 옆에 <ahref="https://github.com/thisisthepy/python-multiplatform">python-multiplatform</a> 체크아웃이 있거나 다음이 필요합니다:</p>
<pdata-lang="en">With a current one (<code>ba4c6f49</code> or later) <strong>143 pass</strong> and only the <code>UI.ipynb</code> test skips; an older one skips 5 that call a snake_case method on a proxy the binder returned. That is expected and recorded.</p><pdata-lang="ko">최신 체크아웃(<code>ba4c6f49</code> 이후)이면 <strong>143 개가 통과</strong>하고 <code>UI.ipynb</code> 테스트만 건너뛰며, 오래된 체크아웃은 바인더가 돌려준 proxy 의 snake_case 메서드를 부르는 5 개를 <strong>건너뜁니다</strong>. 예상된 상태이며 기록되어 있습니다.</p>
0 commit comments