diff --git a/PROJECT.md b/PROJECT.md index 637a310..58793af 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -27,16 +27,16 @@ Compose(`androidx.compose.*`)를 파이썬에서 쓰기 위한 pip 패키지 **` | 상태 | 항목 | |---|---| -| 구현 | 매핑 매니페스트 `pythonx-map.toml`, `@Composable` 항등 데코레이터, 실제 디스크 패키지 `pythonx` 와 모듈 수준 규칙 기반 재노출(snake_case 이름·키워드·시그니처, 오버로드 디스패치, 값 클래스 허용 목록), 실제 Compose 1.11.1-ext.1(`org.thisisthepy.compose.*:1.11.1-ext.1`, `material-icons-core:1.7.3` 은 upstream 유지; #127)에서 생성한 `.pyi` 스텁과 `py.typed`(wheel 포함, mypy 로 검사), `Alignment`/`Arrangement` 상수(평면 `Alignment.End` 와 묶음 `Alignment.Horizontal.End` 모두, #9), proxy 메서드의 snake_case 이름과 키워드 인자(`m.padding(padding_values=...)`; python-multiplatform #131 이 바인더 쪽에서 제공하므로 #131 이상 필요; Kotlin 이름도 실행 시 그대로 통하고 스텁은 snake_case 만), 함수이기도 한 Kotlin object 의 호출(`Color(0xFFFFFFFF)` 과 `Color.Red` 둘 다; #88, 설치한 wheel 에서도 테스트됨. Int/Long 오버로드 선택은 python-multiplatform #146), TypedPython 게이트 통과(`pythonx/` 에 오류 0, 경고 5: `_reexport.py` 의 재노출 자체인 `dynamic-attr`; #118, CI 의 with-binder 에서 검사) | +| 구현 | 매핑 매니페스트 `pythonx-map.toml`, `@Composable` 항등 데코레이터, `DefaultCoroutineScope`·`MainCoroutineScope`(`pythonx.concurrent.CoroutineScope` 기반; #94), 실제 디스크 패키지 `pythonx` 와 모듈 수준 규칙 기반 재노출(snake_case 이름·키워드·시그니처, 오버로드 디스패치, 값 클래스 허용 목록), 실제 Compose 1.11.1-ext.1(`org.thisisthepy.compose.*:1.11.1-ext.1`, `material-icons-core:1.7.3` 은 upstream 유지; #127)에서 생성한 `.pyi` 스텁과 `py.typed`(wheel 포함, mypy 로 검사), `Alignment`/`Arrangement` 상수(평면 `Alignment.End` 와 묶음 `Alignment.Horizontal.End` 모두, #9), proxy 메서드의 snake_case 이름과 키워드 인자(`m.padding(padding_values=...)`; python-multiplatform #131 이 바인더 쪽에서 제공하므로 #131 이상 필요; Kotlin 이름도 실행 시 그대로 통하고 스텁은 snake_case 만), 함수이기도 한 Kotlin object 의 호출(`Color(0xFFFFFFFF)` 과 `Color.Red` 둘 다; #88, 설치한 wheel 에서도 테스트됨. Int/Long 오버로드 선택은 python-multiplatform #146), TypedPython 게이트 통과(`pythonx/` 에 오류 0, 경고 5: `_reexport.py` 의 재노출 자체인 `dynamic-attr`; #118, CI 의 with-binder 에서 검사) | | 부분 | 노트북 표기 그대로의 `remember_saveable`(`getValue()`/`setValue()`, `.value` 별칭; #102, 가짜 호스트로 테스트됨, 저장·복원 증거는 python-multiplatform #174), 선언형 앱 루트 `@app`·`app_root`(#11; 로직은 테스트됨, 바인더 경로는 #38 모양의 가짜 호스트로 테스트됨, 실제 Compose 증거는 python-multiplatform 노트북 E2E #26 에서 통과; 숫자·문자열 상태는 python-multiplatform #69 부터 왕복됨), 배포 설정, Material 3 위젯(렌더 증거는 python-multiplatform 노트북 E2E 에만, 설치한 wheel 에서 Kotlin 과 픽셀 일치), `Icon`(import 만 됨)·`DefaultIcons`(`Icons.Default` alias, 가짜 호스트로만 테스트됨, 스텁에서는 `DefaultIcons.Add` 가 `ImageVector`; `DefaultIcons.Add` 로 쓴다; 노트북의 `DefaultIcons.Add()` 가 아님), `TextField(state=...)`·`TextFieldState`(#10; `pythonx.compose.foundation.text.input`, 가짜 호스트로 테스트됨, 노트북의 `text_state=`/`padding=` 는 `state=`/`modifier=Modifier.padding(8)`; IME 조합 증거는 python-multiplatform 노트북 E2E #26 에서 통과) | -| 계획 | 색 스킴, 코루틴 스코프(pythonx-concurrent 이후) | +| 계획 | 색 스킴 | ### 테스트 기준선 ``` -GitHub Actions, develop (2026-10-24, #127; with-binder 는 pyrefly==1.3.2 포함) - PythonMultiplatform 체크아웃 없음 156 passed, 129 skipped - python-multiplatform develop 31c092f0+ 283 passed, 2 skipped (UI.ipynb, .tmp/kotlin-stubs.zip 없음) +GitHub Actions, develop (2026-10-24, #94; with-binder 는 pyrefly==1.3.2 포함) + PythonMultiplatform 체크아웃 없음 160 passed, 129 skipped + python-multiplatform develop 31c092f0+ 287 passed, 2 skipped (UI.ipynb, .tmp/kotlin-stubs.zip 없음) describe_member 없는 체크아웃 같은 수, 그중 7 개 건너뜀 (python-multiplatform #54 이전) describe(module, name) 없는 체크아웃 같은 수, 그중 13 개 건너뜀 (python-multiplatform #36 이전) 그보다 오래된 체크아웃 거기에 5 개 더 건너뜀 (#131 이전, snake_case 이름 없음) diff --git a/README.md b/README.md index 820ee9d..076bf58 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ standalone desktop toolkit. | `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 | +| Coroutine scopes (`DefaultCoroutineScope`, `MainCoroutineScope`) | ✅ implemented and tested on top of `pythonx.concurrent` (#94) | The full list is on the guide's [Status page](https://thisisthepy.github.io/pythonx-compose/status.html). diff --git a/docs/SPEC.md b/docs/SPEC.md index b390a1d..9eeae5a 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -502,14 +502,23 @@ Stubs: `tests/test_stubs.py::TheConversion::test_a_class_the_package_defines_kee Typing: `tests/test_typing.py::test_remember_saveable_types_the_notebook_spelling` and `test_what_remember_saveable_returns_has_no_other_spelling`. -### S5.7 `DefaultCoroutineScope`, `MainCoroutineScope`: `planned` (after pythonx-concurrent) - -INTENT §5.10. In 2024 `DefaultCoroutineScope()` was Kotlin's `CoroutineScope(Dispatchers.Default)` -and `MainCoroutineScope()` was `CoroutineScope(Dispatchers.Main)`, each with `.launch(block)` -running a Python callable (PyREPL `abc6952`, `Runtime.kt`). Here they are names in -`pythonx.compose.runtime` on top of `pythonx.concurrent`: a `pythonx.concurrent` scope on -`Dispatchers.Default` and one on `Dispatchers.Main`, with `launch`. Nothing is implemented before -pythonx-concurrent provides scopes and dispatchers. +### S5.7 `DefaultCoroutineScope`, `MainCoroutineScope`: `implemented` + +INTENT §5.10 (issue #94). In 2024 `DefaultCoroutineScope()` was Kotlin's +`CoroutineScope(Dispatchers.Default)` and `MainCoroutineScope()` was +`CoroutineScope(Dispatchers.Main)`, each with `.launch(block)` running a Python callable (PyREPL +`abc6952`, `Runtime.kt`). Here they are functions in `pythonx.compose.runtime` built on +`pythonx.concurrent` (Milestone 1, `thisisthepy/pythonx-concurrent#21`): + +- `DefaultCoroutineScope()` imports `pythonx.concurrent` and returns + `CoroutineScope(Dispatchers.Default)`. +- `MainCoroutineScope()` imports `pythonx.concurrent` and returns + `CoroutineScope(Dispatchers.Main)`. +- When `pythonx.concurrent` is not installed, calling either function raises `RuntimeError` naming + `pythonx-concurrent`. + +Tests: `tests/test_coroutine_scopes.py` (`CoroutineScopesSurface`, `CoroutineScopesWithConcurrent`) +and `tests/test_typing.py::test_default_and_main_coroutine_scopes_type_check`. ## 6. Modifiers: extension functions as methods diff --git a/docs/guide/status.html b/docs/guide/status.html index 539d8d7..0ccb079 100644 --- a/docs/guide/status.html +++ b/docs/guide/status.html @@ -34,9 +34,9 @@
Implemented means code in this repository and a passing test that exercises it. Tests that only assert an absence are not counted.
구현은 이 저장소의 코드와 그것을 실행하는 통과 테스트가 함께 있다는 뜻입니다. 부재만 확인하는 테스트는 세지 않습니다.
| State상태 | Item항목 |
|---|---|
| implemented구현 | Mapping manifest pythonx/compose/pythonx-map.toml: inside the package, ships in the wheel매핑 매니페스트 pythonx/compose/pythonx-map.toml: 패키지 안에 있으며 wheel 에 포함 |
| implemented구현 | @Composable identity decorator@Composable 항등 데코레이터 |
| implemented구현 | pythonx as a real on-disk package re-exporting androidx.compose.* by one rule: names, keywords, signatures, Dp allowlistandroidx.compose.* 를 규칙 하나로 재노출하는 실제 디스크 패키지 pythonx: 이름, 키워드, 시그니처, Dp 허용 목록 |
| implemented구현 | Modifier chains with snake_case methods, overload dispatch, Dp as a numbersnake_case 메서드를 쓰는 Modifier 체인, 오버로드 디스패치, 숫자 Dp |
| implemented구현 | Column, Row, Spacer importable from material3 as well as layout (manifest [aliases])layout 뿐 아니라 material3 에서도 import 되는 Column, Row, Spacer (매니페스트 [aliases]) |
| implemented구현 | Type stubs (.pyi) generated from real Compose 1.11.1-ext.1 by the runtime's naming rule, with py.typed, checked with mypy (#12); many types are still Any런타임과 같은 이름 규칙으로 실제 Compose 1.11.1-ext.1 에서 생성한 타입 스텁(.pyi)과 py.typed, mypy 로 검사(#12); 아직 많은 타입이 Any 입니다 |
| implemented구현 | Snake-case names, keywords and signatures: modules and methods; the names and keywords on a proxy are the binder's since python-multiplatform #131 (an older binder spells them in Kotlin), and Kotlin's own spelling still works at run timesnake_case 이름·키워드·시그니처: 모듈과 메서드; 프록시의 이름과 키워드는 python-multiplatform #131 부터 바인더가 제공하고(오래된 바인더는 Kotlin 이름으로 씁니다), Kotlin 이름도 실행 시에는 그대로 통합니다 |
| partial부분 | The empty Modifier: Modifier.padding(16) from the class needs an app-supplied factory against real Compose빈 Modifier: 클래스에서 시작하는 Modifier.padding(16) 은 실제 Compose 에서 앱이 제공하는 팩토리가 필요 |
| implemented구현 | Alignment / Arrangement: Alignment.Center, Arrangement.End, Arrangement.spaced_by(8), and the grouped Alignment.Horizontal.End beside Alignment.EndAlignment / Arrangement: Alignment.Center, Arrangement.End, Arrangement.spaced_by(8), 그리고 Alignment.End 와 나란히 묶음 표기 Alignment.Horizontal.End |
| partial부분 | Material 3 widgets: re-exported by rule; render proofs per widget pending (#9)Material 3 위젯: 규칙으로 재노출됨; 위젯별 렌더 증거 대기(#9) |
| partial부분 | Distribution as pythonx-compose: the wheel carries the manifest, the re-export rule, the type stubs and py.typedpythonx-compose 배포: wheel 에 매니페스트, 재노출 규칙, 타입 스텁, py.typed 포함 |
| partial부분 | A declared app root (@app) and Pythonic state (state), with no update call: binder path tested against a fake host shaped after python-multiplatform #38; real Compose is the E2E module (#11, #19)갱신 호출 없는 선언형 앱 루트(@app)와 파이썬다운 상태(state): 바인더 경로는 python-multiplatform #38 모양의 가짜 호스트로 테스트됨, 실제 Compose 는 E2E 모듈(#11, #19) |
| partial부분 | DefaultIcons is Icons.Default, written DefaultIcons.Add, tested against a fake host shaped after python-multiplatform #37/#38; the real draw is in python-multiplatform's render testDefaultIcons 는 Icons.Default 이며 DefaultIcons.Add 로 씁니다. python-multiplatform #37/#38 모양의 가짜 호스트로 테스트됨; 실제 그리기는 python-multiplatform 의 렌더 테스트에 있음 |
| partial부분 | TextField(state=…) with TextFieldState from pythonx.compose.foundation.text.input: tested against a fake host shaped after python-multiplatform #73; the input-method (IME) composing proof is python-multiplatform E2E #26 (#10)pythonx.compose.foundation.text.input 의 TextFieldState 를 쓰는 TextField(state=…): python-multiplatform #73 모양의 가짜 호스트로 테스트됨; 입력기(IME) 조합 증거는 python-multiplatform E2E #26 (#10) |
| planned계획 | Colour schemes (36 colour parameters)색 스킴 (색 매개변수 36 개) |
| partial부분 | remember_saveable with the notebook's getValue() / setValue(): tested here against a fake host; saving and restoring is python-multiplatform's test (#174)노트북의 getValue() / setValue() 를 쓰는 remember_saveable: 여기서는 가짜 호스트로 테스트, 저장과 복원은 python-multiplatform 의 테스트(#174) |
| planned계획 | Coroutine scopes (after pythonx-concurrent)코루틴 스코프 (pythonx-concurrent 이후) |
| implemented구현 | Mapping manifest pythonx/compose/pythonx-map.toml: inside the package, ships in the wheel매핑 매니페스트 pythonx/compose/pythonx-map.toml: 패키지 안에 있으며 wheel 에 포함 |
| implemented구현 | @Composable identity decorator@Composable 항등 데코레이터 |
| implemented구현 | pythonx as a real on-disk package re-exporting androidx.compose.* by one rule: names, keywords, signatures, Dp allowlistandroidx.compose.* 를 규칙 하나로 재노출하는 실제 디스크 패키지 pythonx: 이름, 키워드, 시그니처, Dp 허용 목록 |
| implemented구현 | Modifier chains with snake_case methods, overload dispatch, Dp as a numbersnake_case 메서드를 쓰는 Modifier 체인, 오버로드 디스패치, 숫자 Dp |
| implemented구현 | Column, Row, Spacer importable from material3 as well as layout (manifest [aliases])layout 뿐 아니라 material3 에서도 import 되는 Column, Row, Spacer (매니페스트 [aliases]) |
| implemented구현 | Type stubs (.pyi) generated from real Compose 1.11.1-ext.1 by the runtime's naming rule, with py.typed, checked with mypy (#12); many types are still Any런타임과 같은 이름 규칙으로 실제 Compose 1.11.1-ext.1 에서 생성한 타입 스텁(.pyi)과 py.typed, mypy 로 검사(#12); 아직 많은 타입이 Any 입니다 |
| implemented구현 | Snake-case names, keywords and signatures: modules and methods; the names and keywords on a proxy are the binder's since python-multiplatform #131 (an older binder spells them in Kotlin), and Kotlin's own spelling still works at run timesnake_case 이름·키워드·시그니처: 모듈과 메서드; 프록시의 이름과 키워드는 python-multiplatform #131 부터 바인더가 제공하고(오래된 바인더는 Kotlin 이름으로 씁니다), Kotlin 이름도 실행 시에는 그대로 통합니다 |
| partial부분 | The empty Modifier: Modifier.padding(16) from the class needs an app-supplied factory against real Compose빈 Modifier: 클래스에서 시작하는 Modifier.padding(16) 은 실제 Compose 에서 앱이 제공하는 팩토리가 필요 |
| implemented구현 | Alignment / Arrangement: Alignment.Center, Arrangement.End, Arrangement.spaced_by(8), and the grouped Alignment.Horizontal.End beside Alignment.EndAlignment / Arrangement: Alignment.Center, Arrangement.End, Arrangement.spaced_by(8), 그리고 Alignment.End 와 나란히 묶음 표기 Alignment.Horizontal.End |
| partial부분 | Material 3 widgets: re-exported by rule; render proofs per widget pending (#9)Material 3 위젯: 규칙으로 재노출됨; 위젯별 렌더 증거 대기(#9) |
| partial부분 | Distribution as pythonx-compose: the wheel carries the manifest, the re-export rule, the type stubs and py.typedpythonx-compose 배포: wheel 에 매니페스트, 재노출 규칙, 타입 스텁, py.typed 포함 |
| partial부분 | A declared app root (@app) and Pythonic state (state), with no update call: binder path tested against a fake host shaped after python-multiplatform #38; real Compose is the E2E module (#11, #19)갱신 호출 없는 선언형 앱 루트(@app)와 파이썬다운 상태(state): 바인더 경로는 python-multiplatform #38 모양의 가짜 호스트로 테스트됨, 실제 Compose 는 E2E 모듈(#11, #19) |
| partial부분 | DefaultIcons is Icons.Default, written DefaultIcons.Add, tested against a fake host shaped after python-multiplatform #37/#38; the real draw is in python-multiplatform's render testDefaultIcons 는 Icons.Default 이며 DefaultIcons.Add 로 씁니다. python-multiplatform #37/#38 모양의 가짜 호스트로 테스트됨; 실제 그리기는 python-multiplatform 의 렌더 테스트에 있음 |
| partial부분 | TextField(state=…) with TextFieldState from pythonx.compose.foundation.text.input: tested against a fake host shaped after python-multiplatform #73; the input-method (IME) composing proof is python-multiplatform E2E #26 (#10)pythonx.compose.foundation.text.input 의 TextFieldState 를 쓰는 TextField(state=…): python-multiplatform #73 모양의 가짜 호스트로 테스트됨; 입력기(IME) 조합 증거는 python-multiplatform E2E #26 (#10) |
| planned계획 | Colour schemes (36 colour parameters)색 스킴 (색 매개변수 36 개) |
| partial부분 | remember_saveable with the notebook's getValue() / setValue(): tested here against a fake host; saving and restoring is python-multiplatform's test (#174)노트북의 getValue() / setValue() 를 쓰는 remember_saveable: 여기서는 가짜 호스트로 테스트, 저장과 복원은 python-multiplatform 의 테스트(#174) |
| implemented구현 | Coroutine scopes (DefaultCoroutineScope, MainCoroutineScope) built on pythonx.concurrent (#94)pythonx.concurrent 위에 구현된 코루틴 스코프 (DefaultCoroutineScope, MainCoroutineScope, #94) |
Test baseline on CI: 156 passed, 129 skipped without a python-multiplatform checkout; 283 passed, 2 skipped with a current one. The two skips with a current checkout read the maintainer's UI.ipynb and a downloaded stubs artefact, which CI does not have.
CI 의 테스트 기준선: python-multiplatform 체크아웃이 없으면 156 passed, 129 skipped, 최신 체크아웃이면 283 passed, 2 skipped. 최신 체크아웃에서 건너뛰는 2 개는 메인테이너의 UI.ipynb 와 내려받은 스텁 아티팩트를 읽는 테스트이고, CI 에는 둘 다 없습니다.
Test baseline on CI: 160 passed, 129 skipped without a python-multiplatform checkout; 287 passed, 2 skipped with a current one. The two skips with a current checkout read the maintainer's UI.ipynb and a downloaded stubs artefact, which CI does not have.
CI 의 테스트 기준선: python-multiplatform 체크아웃이 없으면 160 passed, 129 skipped, 최신 체크아웃이면 287 passed, 2 skipped. 최신 체크아웃에서 건너뛰는 2 개는 메인테이너의 UI.ipynb 와 내려받은 스텁 아티팩트를 읽는 테스트이고, CI 에는 둘 다 없습니다.