diff --git a/.github/scripts/release/README.md b/.github/scripts/release/README.md index 9288dc3..4d5a1ea 100644 --- a/.github/scripts/release/README.md +++ b/.github/scripts/release/README.md @@ -16,8 +16,7 @@ ignored and overwritten. `GITHUB_TOKEN`. This needs the repository setting *Actions -> General -> Allow GitHub Actions to create and approve pull requests*. No PAT is needed; an optional `RELEASE_PR_TOKEN` secret is used for the PR step when present. -3. Merging that PR is the only way to change `main`. The maintainer merges it after checking - that its source branch is `release`; repository settings alone protect `main` (below). +3. Merging that PR is the only way to change `main`, and the maintainer does it (below). 4. On push to `main`, `pages.yml` deploys `docs/guide/` to GitHub Pages. ## What is dropped on release @@ -40,10 +39,5 @@ It refuses to run while `release` is checked out. ## Protecting main - .github/scripts/release/protect-main.sh # print what would be applied - .github/scripts/release/protect-main.sh --apply # apply via gh api (needs admin) - -The settings (the maintainer's decision, 2026-10-03): a pull request is required with no -approving review, direct push, force-push and deletion are blocked, the branch is not locked, and -only administrators may merge. No workflow checks the source branch; the maintainer does, when -merging the release PR. +The maintainer manages `main`'s protection in the repository settings, by hand; `main` is locked. +No script or agent creates or changes those settings. The maintainer merges the release PR. diff --git a/.github/scripts/release/protect-main.sh b/.github/scripts/release/protect-main.sh deleted file mode 100755 index 4bc6514..0000000 --- a/.github/scripts/release/protect-main.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# Print (default) or apply (--apply) branch protection for main. -# Usage: protect-main.sh [--apply] [--repo OWNER/NAME] [--branch main] -set -eu -APPLY=0; REPO=""; BRANCH=main -while [ $# -gt 0 ]; do - case "$1" in - --apply) APPLY=1; shift ;; - --repo) REPO="$2"; shift 2 ;; - --branch) BRANCH="$2"; shift 2 ;; - *) echo "unknown argument: $1" >&2; exit 2 ;; - esac -done -[ -n "$REPO" ] || REPO="{owner}/{repo}" # gh expands these placeholders from the current repo - -BODY='{ - "required_status_checks": null, - "enforce_admins": false, - "required_pull_request_reviews": {"required_approving_review_count": 0}, - "restrictions": {"users": [], "teams": [], "apps": []}, - "allow_force_pushes": false, - "allow_deletions": false, - "lock_branch": false -}' -CMD="gh api --method PUT repos/$REPO/branches/$BRANCH/protection --input -" - -if [ "$APPLY" -eq 1 ]; then - printf '%s' "$BODY" | gh api --method PUT "repos/$REPO/branches/$BRANCH/protection" --input - - echo "applied protection to $BRANCH" -else - echo "[dry run] would run: $CMD" - echo "$BODY" - echo "Re-run with --apply to apply (needs admin). 'restrictions' with empty lists means only" - echo "administrators may merge; it is accepted for organisation repositories only." -fi diff --git a/LICENSE b/LICENSE index 6a14d04..57bc88a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,202 @@ -MIT License - -Copyright (c) 2023-2024 BREW (b-re-w), Jong-uk Lee (rnoro5122) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/README.md b/README.md index a178167..c6d7b81 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ English | [한국어](https://github.com/thisisthepy/pythonx-compose/blob/main/d # pythonx-compose -**Write Compose user interfaces in Python — with Compose's own widgets, spelled the Python way.** +**Build Compose Multiplatform UIs in Python: Python Declarative UI Framework** -[![License: MIT](https://img.shields.io/badge/license-MIT-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) +[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) [![Python](https://img.shields.io/badge/python-3.11%2B-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/pyproject.toml) [![pip](https://img.shields.io/badge/pip-pythonx--compose-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/pyproject.toml) [![Status](https://img.shields.io/badge/status-pre--alpha-lightgrey.svg)](#-status) @@ -19,7 +19,7 @@ English | [한국어](https://github.com/thisisthepy/pythonx-compose/blob/main/d ## Why -Jetpack Compose and Compose Multiplatform are a superb way to build UI — if you write Kotlin. +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](https://github.com/thisisthepy/python-multiplatform) exposes to Python under @@ -55,7 +55,9 @@ proofs are still pending. See [Status](#-status). `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. + 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`](https://github.com/thisisthepy/pythonx-compose/blob/main/pythonx/compose/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 @@ -86,16 +88,23 @@ Each module is a real file whose `__init__.py` calls one re-export rule; names r ## 🚀 Quick start > [!NOTE] -> `pythonx-compose` is **pre-alpha**. The first alpha, `0.1.0a1`, is released to PyPI when the -> maintainer tags it; a pre-release needs `pip install --pre pythonx-compose`. +> `pythonx-compose` is an **alpha**. `0.1.0a1` is on [PyPI](https://pypi.org/project/pythonx-compose/); +> as a pre-release it installs with uv (or ppp, or tcl): + +```bash +uv add --prerelease allow pythonx-compose +# or, with pypackpack, into a package of a ppp workspace: +ppp core add "pythonx-compose==0.1.0a1" +# or, with toolchain-lite: +tcl install pythonx-compose +``` From a clone, to run the tests: ```bash 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 +uv run --with pytest --with mypy pytest tests -q ``` What runs today, from the repository root: @@ -155,15 +164,15 @@ standalone desktop toolkit. | `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) | +| 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 | +| 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 | @@ -171,15 +180,15 @@ The full list is on the guide's [Status page](https://thisisthepy.github.io/pyth ## 📖 Documentation -- **Guide** — [`docs/guide/`](https://thisisthepy.github.io/pythonx-compose/), bilingual (English / 한국어) -- **Korean README** — [`docs/locale/README_ko.md`](https://github.com/thisisthepy/pythonx-compose/blob/main/docs/locale/README_ko.md) +- **Guide**: [`docs/guide/`](https://thisisthepy.github.io/pythonx-compose/), bilingual (English / 한국어) +- **Korean README**: [`docs/locale/README_ko.md`](https://github.com/thisisthepy/pythonx-compose/blob/main/docs/locale/README_ko.md) ## 🔌 Ecosystem | Repository | Role | |---|---| | [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) | The binder: CPython embedded in Kotlin Multiplatform, exposing Kotlin to Python under Kotlin names | -| **pythonx-compose** | Compose, restructured for Python — this repository | +| **pythonx-compose** | Compose, restructured for Python (this repository) | | [toolchain](https://github.com/thisisthepy/toolchain) | Gradle build plugin for Python Multiplatform apps | | [pypackpack](https://github.com/thisisthepy/pypackpack) | Distributing Python projects across platforms | | [torchnative](https://github.com/thisisthepy/torchnative) | Run the real PyTorch ecosystem on device | @@ -200,4 +209,4 @@ useful. Please open an issue before large changes. ## License -[MIT](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) © 2023–2024 BREW (b-re-w), Jong-uk Lee (rnoro5122) +[Apache License 2.0](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) © 2023–2024 BREW (b-re-w), Jong-uk Lee (rnoro5122) diff --git a/docs/guide/assets/style.css b/docs/guide/assets/style.css index 932d708..3d0beb1 100644 --- a/docs/guide/assets/style.css +++ b/docs/guide/assets/style.css @@ -1,4 +1,4 @@ -/* pythonx-compose guide — shared thisisthepy family tokens, accent #7c4dff. */ +/* pythonx-compose guide: shared thisisthepy family tokens, accent #7c4dff. */ :root { --bg: #fbfbfa; --fg: #16181d; --muted: #5b6270; --line: #e6e6e3; --code-bg: #f3f3f1; --radius: 10px; --maxw: 72rem; @@ -146,7 +146,7 @@ a.card:hover { text-decoration: none; border-color: var(--accent); } ul.clean { padding-left: 1.1rem; margin: 0 0 1rem; } ul.clean li { margin: 0.3rem 0; } -.not li::marker { content: "— "; color: var(--muted); } +.not li::marker { content: "– "; color: var(--muted); } /* Tables */ .table-wrap { overflow-x: auto; margin: 1rem 0 1.5rem; border: 1px solid var(--line); border-radius: var(--radius); } diff --git a/docs/guide/concepts.html b/docs/guide/concepts.html index 3619414..be1e719 100644 --- a/docs/guide/concepts.html +++ b/docs/guide/concepts.html @@ -32,14 +32,14 @@

The two layers두 계층

-

python-multiplatform — the binder — embeds CPython in a Kotlin Multiplatform app and exposes Kotlin declarations to Python under their Kotlin names: androidx.compose.material3 is androidx.compose.material3. It never renames a namespace.

python-multiplatform — 바인더 — 는 Kotlin Multiplatform 앱에 CPython 을 임베딩하고, Kotlin 선언을 Kotlin 이름 그대로 파이썬에 노출합니다: androidx.compose.material3 는 androidx.compose.material3 입니다. 네임스페이스 이름을 절대 바꾸지 않습니다.

-

pythonx-compose — this package — is real Python on disk. Its modules import those androidx.compose.* modules and restructure them into pythonx.compose.*.

pythonx-compose — 이 패키지 — 는 디스크에 있는 실제 파이썬입니다. 이 패키지의 모듈이 androidx.compose.* 를 import 해 pythonx.compose.* 로 재구성합니다.

+

python-multiplatform, the binder, embeds CPython in a Kotlin Multiplatform app and exposes Kotlin declarations to Python under their Kotlin names: androidx.compose.material3 is androidx.compose.material3. It never renames a namespace. Since python-multiplatform #131 it does serve snake_case names and keywords on its own callables and proxies, by the rule this package wrote.

python-multiplatform(바인더)는 Kotlin Multiplatform 앱에 CPython 을 임베딩하고, Kotlin 선언을 Kotlin 이름 그대로 파이썬에 노출합니다: androidx.compose.material3 는 androidx.compose.material3 입니다. 네임스페이스 이름을 절대 바꾸지 않습니다. 다만 python-multiplatform #131 부터는 이 패키지가 정한 규칙대로 자신의 함수와 프록시에서 snake_case 이름과 키워드를 제공합니다.

+

pythonx-compose, this package, is real Python on disk. Its modules import those androidx.compose.* modules and restructure them into pythonx.compose.*.

pythonx-compose(이 패키지)는 디스크에 있는 실제 파이썬입니다. 이 패키지의 모듈이 androidx.compose.* 를 import 해 pythonx.compose.* 로 재구성합니다.

@Composable def Screen()your Pythonpythonx.composethis package · real .pyandroidx.compose.*Kotlin names · binderComposeJetpack / Multiplatformpythonx-map.tomlthe one mapping.pyi stubsin the wheel@Composable def Screen()your Pythonpythonx.composethis package · real .pyandroidx.compose.*Kotlin names · binderComposeJetpack / Multiplatformpythonx-map.toml→ pythonx.compose, .pyi

The naming rule이름 규칙

-

Expose the original Kotlin declarations and parameters with Pythonic names. A name that starts upper-case — a type, an object, a composable — keeps its Kotlin spelling. Every other name is reached by its snake_case spelling only; the camelCase spelling is not a second name. An explicit overload keeps its type suffix.

Kotlin 의 원래 선언과 매개변수를 파이썬다운 이름으로 노출합니다. 대문자로 시작하는 이름 — 타입, 객체, 컴포저블 — 은 Kotlin 표기를 유지합니다. 그 밖의 이름은 snake_case 표기로만 접근하며, camelCase 표기가 또 하나의 이름이 되지는 않습니다. 명시적 오버로드는 타입 접미사를 유지합니다.

+

Expose the original Kotlin declarations and parameters with Pythonic names. A name that starts upper-case (a type, an object, a composable) keeps its Kotlin spelling. Every other name is reached by its snake_case spelling only; the camelCase spelling is not a second name. An explicit overload keeps its type suffix. Turning a Kotlin name or keyword into snake_case is the binder's work since python-multiplatform #131 (its python_name is this package's rule, and a test pins that the two agree). This package decides what the pythonx surface lists: snake_case only, with no camelCase second name.

Kotlin 의 원래 선언과 매개변수를 파이썬다운 이름으로 노출합니다. 대문자로 시작하는 이름(타입, 객체, 컴포저블)은 Kotlin 표기를 유지합니다. 그 밖의 이름은 snake_case 표기로만 접근하며, camelCase 표기가 또 하나의 이름이 되지는 않습니다. 명시적 오버로드는 타입 접미사를 유지합니다. Kotlin 이름과 키워드를 snake_case 로 바꾸는 일은 python-multiplatform #131 부터 바인더가 합니다(바인더의 python_name 은 이 패키지의 규칙이며, 둘이 일치함을 테스트가 확인합니다). pythonx 표면에 무엇을 나열할지는 이 패키지가 정하며, snake_case 만 나열하고 camelCase 두 번째 이름은 두지 않습니다.

@@ -58,7 +58,7 @@

The naming rule이름 규

One re-export rule재노출 규칙 하나

-

Every module in pythonx-map.toml is a real file on disk whose __init__.py is one line. Nothing is resolved at import; a name is looked up in the mapped Kotlin package the first time it is read, and then lives in the module like any attribute. There is no per-widget wrapper.

pythonx-map.toml 의 모든 모듈은 디스크에 있는 실제 파일이며, __init__.py 는 한 줄입니다. import 할 때는 아무것도 해석하지 않고, 이름을 처음 읽을 때 대응된 Kotlin 패키지에서 찾은 뒤 보통의 속성처럼 모듈에 남습니다. 위젯별 래퍼는 없습니다.

+

Every module in pythonx-map.toml is a real file on disk whose __init__.py is one line. Nothing is resolved at import; a name is looked up in the mapped Kotlin package the first time it is read, and then lives in the module like any attribute. There is no per-widget wrapper, and no keyword wrapper: the callables are the binder's own. What this package keeps is the module grouping, the aliases, the objects and their grouped constants, the value-class allowlist and the app root. It needs python-multiplatform #131 or later for snake_case on proxies.

pythonx-map.toml 의 모든 모듈은 디스크에 있는 실제 파일이며, __init__.py 는 한 줄입니다. import 할 때는 아무것도 해석하지 않고, 이름을 처음 읽을 때 대응된 Kotlin 패키지에서 찾은 뒤 보통의 속성처럼 모듈에 남습니다. 위젯별 래퍼도, 키워드 래퍼도 없습니다. 호출 대상은 바인더 자신의 함수입니다. 이 패키지가 맡는 것은 모듈 묶음, 별칭, 객체와 묶음 상수, 값 클래스 허용 목록, 앱 루트입니다. 프록시에서 snake_case 를 쓰려면 python-multiplatform #131 이상이 필요합니다.

# pythonx/compose/material3/__init__.py
 from pythonx.compose._reexport import reexport
 
@@ -67,9 +67,9 @@ 

One re-export rule재노출

Extension functions are methods확장 함수는 메서드

-

Most of Modifier is Kotlin extension functions. In Python each one is a method on the receiver's proxy, so a chain reads as it does in Kotlin. The binder's proxy class keeps Kotlin names; the same naming rule is registered as the binder's member resolver, so the snake_case spelling reaches the Kotlin member.

Modifier 의 대부분은 Kotlin 확장 함수입니다. 파이썬에서는 각각이 수신 객체 프록시의 메서드가 되어, 체인이 Kotlin 에서처럼 읽힙니다. 바인더의 프록시 클래스는 Kotlin 이름을 유지하며, 같은 이름 규칙이 바인더의 멤버 리졸버로 등록되어 snake_case 표기가 Kotlin 멤버에 닿습니다.

+

Most of Modifier is Kotlin extension functions. In Python each one is a method on the receiver's proxy, so a chain reads as it does in Kotlin. The binder serves the snake_case spelling on its proxies since python-multiplatform #131 (this package registers nothing), and its class lists Kotlin names only in dir().

Modifier 의 대부분은 Kotlin 확장 함수입니다. 파이썬에서는 각각이 수신 객체 프록시의 메서드가 되어, 체인이 Kotlin 에서처럼 읽힙니다. python-multiplatform #131 부터 바인더가 프록시에서 snake_case 표기를 제공하며(이 패키지는 아무것도 등록하지 않습니다), 바인더의 클래스는 dir() 에만 Kotlin 이름을 나열합니다.

Modifier.padding(16).size(24).fill_max_width()
-

Keyword arguments to such a method are snake_case too — m.padding(padding_values=…), as the module function takes padding(m, padding_values=…). The resolver also hands the binder a keyword map read from the declaration's own parameter names. Kotlin's spelling (paddingValues=) still works at run time; the type stubs offer only the Pythonic one.

이런 메서드의 키워드 인자도 snake_case 입니다 — m.padding(padding_values=…) 이며, 모듈 함수는 padding(m, padding_values=…) 를 받습니다. 리졸버는 선언의 실제 파라미터 이름에서 읽은 키워드 맵도 바인더에 넘깁니다. Kotlin 이름(paddingValues=)도 실행 시에는 그대로 통하지만, 타입 스텁은 Pythonic 이름만 제시합니다.

+

Keyword arguments to such a method are snake_case too: m.padding(padding_values=…), as the module function takes padding(m, padding_values=…). The resolver also hands the binder a keyword map read from the declaration's own parameter names. Kotlin's spelling (paddingValues=) still works at run time; the type stubs offer only the Pythonic one.

이런 메서드의 키워드 인자도 snake_case 입니다: m.padding(padding_values=…) 이며, 모듈 함수는 padding(m, padding_values=…) 를 받습니다. 리졸버는 선언의 실제 파라미터 이름에서 읽은 키워드 맵도 바인더에 넘깁니다. Kotlin 이름(paddingValues=)도 실행 시에는 그대로 통하지만, 타입 스텁은 Pythonic 이름만 제시합니다.

@@ -87,7 +87,7 @@

Kotlin objectsKotlin 객체

Value classes값 클래스

-

Dp wraps a single float, so a raw number may stand for it: padding(16) means padding(16.dp). That permission is the manifest's allowlist, handed to the binder when a pythonx.compose module first resolves a name. Color and TextUnit pack several fields into one value, so a raw number would decode as something else entirely — they are refused, with a message saying why. A colour is written through Kotlin's own factory: Color(0xFFFF0000).

Dp 는 float 하나를 감싸므로 숫자로 쓸 수 있습니다: padding(16) 은 padding(16.dp) 입니다. 이 허용은 매니페스트의 허용 목록이며, pythonx.compose 모듈이 처음 이름을 해석할 때 바인더에 전달됩니다. Color 와 TextUnit 은 여러 필드를 한 값에 담으므로 숫자를 넣으면 전혀 다른 값으로 해석됩니다 — 그래서 이유를 밝히며 거절합니다. 색은 Kotlin 자신의 팩토리로 씁니다: Color(0xFFFF0000).

+

Dp wraps a single float, so a raw number may stand for it: padding(16) means padding(16.dp). That permission is the manifest's allowlist, handed to the binder when a pythonx.compose module first resolves a name. Color and TextUnit pack several fields into one value, so a raw number would decode as something else entirely, so they are refused, with a message saying why. A colour is written through Kotlin's own factory: Color(0xFFFF0000).

Dp 는 float 하나를 감싸므로 숫자로 쓸 수 있습니다: padding(16) 은 padding(16.dp) 입니다. 이 허용은 매니페스트의 허용 목록이며, pythonx.compose 모듈이 처음 이름을 해석할 때 바인더에 전달됩니다. Color 와 TextUnit 은 여러 필드를 한 값에 담으므로 숫자를 넣으면 전혀 다른 값으로 해석됩니다. 그래서 이유를 밝히며 거절합니다. 색은 Kotlin 자신의 팩토리로 씁니다: Color(0xFFFF0000).

@@ -105,8 +105,8 @@

@Composable@Composable
-pythonx-compose — Compose, restructured for Python.pythonx-compose — 파이썬을 위해 재구성한 Compose. -GitHub · MIT LicenseMIT 라이선스 +pythonx-compose: Compose, restructured for Python.pythonx-compose: 파이썬을 위해 재구성한 Compose. +GitHub · Apache License 2.0Apache 라이선스 2.0
diff --git a/docs/guide/ecosystem.html b/docs/guide/ecosystem.html index 671590e..adc4adb 100644 --- a/docs/guide/ecosystem.html +++ b/docs/guide/ecosystem.html @@ -43,8 +43,8 @@

Who owns what누가 무엇
-pythonx-compose — Compose, restructured for Python.pythonx-compose — 파이썬을 위해 재구성한 Compose. -GitHub · MIT LicenseMIT 라이선스 +pythonx-compose: Compose, restructured for Python.pythonx-compose: 파이썬을 위해 재구성한 Compose. +GitHub · Apache License 2.0Apache 라이선스 2.0
diff --git a/docs/guide/getting-started.html b/docs/guide/getting-started.html index cb403e6..2e63831 100644 --- a/docs/guide/getting-started.html +++ b/docs/guide/getting-started.html @@ -30,22 +30,20 @@

Getting started시작하기

From clone to first call클론부터 첫 호출까지

Five minutes, honest about what runs.5 분, 동작하는 범위를 솔직하게.

-

pythonx-compose is pre-alpha. The first alpha, 0.1.0a1, is released to PyPI when the maintainer tags it; install a pre-release with pip install --pre pythonx-compose. This page shows how to get the source, run the tests, and what you can call today.

pythonx-compose 는 pre-alpha 입니다. 첫 알파 0.1.0a1 은 메인테이너가 태그를 달면 PyPI 에 배포되며, 프리릴리스는 pip install --pre pythonx-compose 로 설치합니다. 이 페이지는 소스를 받고, 테스트를 돌리고, 지금 호출할 수 있는 것을 보여줍니다.

+

pythonx-compose is an alpha: 0.1.0a1 is on PyPI. As a pre-release it installs with uv add --prerelease allow pythonx-compose; with ppp it is ppp core add "pythonx-compose==0.1.0a1", with tcl tcl install pythonx-compose. It needs Python 3.11 or later. This page shows how to get the source, run the tests, and what you can call today.

pythonx-compose 는 알파 단계이며 0.1.0a1 이 PyPI 에 있습니다. 프리릴리스이므로 uv add --prerelease allow pythonx-compose 로 설치하며, ppp 로는 ppp core add "pythonx-compose==0.1.0a1", tcl 로는 tcl install pythonx-compose 입니다. 파이썬 3.11 이상이 필요합니다. 이 페이지는 소스를 받고, 테스트를 돌리고, 지금 호출할 수 있는 것을 보여줍니다.

1. Get the source1. 소스 받기

git clone https://github.com/thisisthepy/pythonx-compose
-cd pythonx-compose
-python3 -m venv .venv && . .venv/bin/activate
-python3 -m pip install pytest mypy      # Python 3.11 or later
+cd pythonx-compose

2. Run the tests2. 테스트 실행

-
python3 -m pytest tests -q
-

Expect 139 passed, 112 skipped. One skipped test reads the maintainer's UI.ipynb, which a clone does not have; mypy type-checks the stubs. The other skipped tests drive the binder's adaptation layer and need a python-multiplatform checkout beside this one, or:

139 passed, 112 skipped 가 정상입니다. 건너뛴 테스트 하나는 클론에는 없는 메인테이너의 UI.ipynb 를 읽으며, mypy 는 스텁을 타입 검사합니다. 나머지 건너뛴 테스트는 바인더의 적응 계층을 구동하며, 옆에 python-multiplatform 체크아웃이 있거나 다음이 필요합니다:

-
PYTHONMULTIPLATFORM_HOME=/path/to/python-multiplatform python3 -m pytest tests -q
-

With a current one (31c092f0 or later) 250 pass and 1 skips (the UI.ipynb test); an older one skips 5 that call a snake_case method on a proxy the binder returned. That is expected and recorded.

최신 체크아웃(31c092f0 이후)이면 230 개가 통과하고 1 개(UI.ipynb 테스트)를 건너뛰며, 오래된 체크아웃은 바인더가 돌려준 proxy 의 snake_case 메서드를 부르는 5 개를 건너뜁니다. 예상된 상태이며 기록되어 있습니다.

+
uv run --with pytest --with mypy pytest tests -q
+

Expect 148 passed, 109 skipped. One skipped test reads the maintainer's UI.ipynb, which a clone does not have; mypy type-checks the stubs. The other skipped tests drive the binder's adaptation layer and need a python-multiplatform checkout beside this one, or:

148 passed, 109 skipped 가 정상입니다. 건너뛴 테스트 하나는 클론에는 없는 메인테이너의 UI.ipynb 를 읽으며, mypy 는 스텁을 타입 검사합니다. 나머지 건너뛴 테스트는 바인더의 적응 계층을 구동하며, 옆에 python-multiplatform 체크아웃이 있거나 다음이 필요합니다:

+
PYTHONMULTIPLATFORM_HOME=/path/to/python-multiplatform uv run --with pytest --with mypy pytest tests -q
+

With a current one (31c092f0 or later) 256 pass and 1 skips (the UI.ipynb test); an older one skips 5 that call a snake_case method on a proxy the binder returned. That is expected and recorded.

최신 체크아웃(31c092f0 이후)이면 256 개가 통과하고 1 개(UI.ipynb 테스트)를 건너뛰며, 오래된 체크아웃은 바인더가 돌려준 proxy 의 snake_case 메서드를 부르는 5 개를 건너뜁니다. 예상된 상태이며 기록되어 있습니다.

@@ -84,7 +82,7 @@

3. What you can call today3

4. What you write in an app4. 앱 안에서 쓰는 코드

-

Inside an app whose Kotlin host has installed the binder, a screen looks like this — every name and parameter is Compose's own, spelled by the one rule:

Kotlin 호스트가 바인더를 설치한 앱 안에서는, 화면이 이렇게 됩니다 — 모든 이름과 매개변수는 Compose 자신의 것이며, 규칙 하나로 표기됩니다:

+

Inside an app whose Kotlin host has installed the binder, a screen looks like this: every name and parameter is Compose's own, spelled by the one rule:

Kotlin 호스트가 바인더를 설치한 앱 안에서는, 화면이 이렇게 됩니다. 모든 이름과 매개변수는 Compose 자신의 것이며, 규칙 하나로 표기됩니다:

from pythonx.compose.runtime import Composable
 from pythonx.compose.material3 import Text, Button, Column   # Column also from layout
 from pythonx.compose.layout import Arrangement
@@ -101,7 +99,7 @@ 

4. What you write in an app Button(on_click=lambda: None, content=lambda: Text("Press")), ), )

-

Two things to know. A chain that starts from the class, Modifier.padding(16), needs the app to register an empty-Modifier factory against real Compose — see Modifiers. And the screen above runs inside the app; a declared app root that redefining a screen replaces (issue #11) is planned, not built.

알아 둘 것이 두 가지 있습니다. 클래스에서 시작하는 체인 Modifier.padding(16) 은 실제 Compose 에서 앱이 빈 Modifier 팩토리를 등록해야 합니다 — 모디파이어를 보세요. 그리고 위 화면은 앱 안에서 실행됩니다. 화면을 다시 정의하면 교체되는 선언형 앱 루트(이슈 #11)는 계획 단계이며 아직 만들어지지 않았습니다.

+

Two things to know. A chain that starts from the class, Modifier.padding(16), needs the app to register an empty-Modifier factory against real Compose; see Modifiers. And the screen above runs inside the app; a declared app root that redefining a screen replaces (issue #11) is planned, not built.

알아 둘 것이 두 가지 있습니다. 클래스에서 시작하는 체인 Modifier.padding(16) 은 실제 Compose 에서 앱이 빈 Modifier 팩토리를 등록해야 합니다. 모디파이어를 보세요. 그리고 위 화면은 앱 안에서 실행됩니다. 화면을 다시 정의하면 교체되는 선언형 앱 루트(이슈 #11)는 계획 단계이며 아직 만들어지지 않았습니다.

@@ -109,8 +107,8 @@

4. What you write in an app
-pythonx-compose — Compose, restructured for Python.pythonx-compose — 파이썬을 위해 재구성한 Compose. -GitHub · MIT LicenseMIT 라이선스 +pythonx-compose: Compose, restructured for Python.pythonx-compose: 파이썬을 위해 재구성한 Compose. +GitHub · Apache License 2.0Apache 라이선스 2.0
diff --git a/docs/guide/guide-composables.html b/docs/guide/guide-composables.html index 9eea091..a9346ea 100644 --- a/docs/guide/guide-composables.html +++ b/docs/guide/guide-composables.html @@ -44,7 +44,7 @@

Write a screen화면 하나 def Profile(name: str): Card(content=lambda: Text(f"Hello, {name}"))

A screen is a plain function. @Composable marks it; it does not wrap it. Content slots take a lambda.

화면은 평범한 함수입니다. @Composable 은 표시만 하고 감싸지 않습니다. content 슬롯은 람다를 받습니다.

-

Column, Row and Spacer are declared in Kotlin's layout package, and the notebook imports them from material3. Both work, and both give the same object — the manifest's [aliases] section lends them to material3:

Column, Row, Spacer 는 Kotlin 의 layout 패키지에 선언되어 있고, 노트북은 이들을 material3 에서 import 합니다. 둘 다 되며 같은 객체를 줍니다 — 매니페스트의 [aliases] 섹션이 이들을 material3 에 빌려줍니다:

+

Column, Row and Spacer are declared in Kotlin's layout package, and the notebook imports them from material3. Both work, and both give the same object: the manifest's [aliases] section lends them to material3:

Column, Row, Spacer 는 Kotlin 의 layout 패키지에 선언되어 있고, 노트북은 이들을 material3 에서 import 합니다. 둘 다 되며 같은 객체를 줍니다. 매니페스트의 [aliases] 섹션이 이들을 material3 에 빌려줍니다:

from pythonx.compose.material3 import Column, Row, Spacer   # as the notebook writes
 from pythonx.compose.layout import Column                   # Kotlin's home; same object

@@ -60,7 +60,7 @@

Declare the app and keep state@app declares the screen the host draws and returns the function unchanged. Declaring it again, for example in a notebook cell, replaces the screen; there is no update call. state(initial) is a Compose state, read and written through .value. The host is configured once with PythonContent("pythonx.compose.runtime", "app_root").

@app 은 호스트가 그리는 화면을 선언하고 함수를 그대로 돌려줍니다. 노트북 셀 등에서 다시 선언하면 화면이 바뀌며, 갱신 호출은 없습니다. state(initial) 는 Compose 상태이며 .value 로 읽고 씁니다. 호스트는 PythonContent("pythonx.compose.runtime", "app_root") 로 한 번만 설정합니다.

+

@app declares the screen the host draws and returns the function unchanged. Declaring it again, for example in a notebook cell, replaces the screen; there is no update call. state(initial) is a Compose state, read and written through .value. The host is configured once with PythonAppView(module = "pythonx.compose.runtime", attribute = "app_root").

@app 은 호스트가 그리는 화면을 선언하고 함수를 그대로 돌려줍니다. 노트북 셀 등에서 다시 선언하면 화면이 바뀌며, 갱신 호출은 없습니다. state(initial) 는 Compose 상태이며 .value 로 읽고 씁니다. 호스트는 PythonAppView(module = "pythonx.compose.runtime", attribute = "app_root") 로 한 번만 설정합니다.

Status: partial. The binder path, which creates the state through Compose's mutableStateOf, is tested against a fake host shaped after python-multiplatform #38; proof against real Compose is still the E2E module. Numbers and strings round-trip: state(0) and count.value += 1 work (python-multiplatform #69).

상태: 부분. Compose 의 mutableStateOf 로 상태를 만드는 바인더 경로는 python-multiplatform #38 모양의 가짜 호스트로 테스트되었으며, 실제 Compose 증거는 아직 E2E 모듈입니다. 숫자와 문자열은 왕복됩니다: state(0) 과 count.value += 1 이 동작합니다(python-multiplatform #69).

@@ -78,7 +78,7 @@

A text field텍스트 필 draft = TextFieldState("hi") draft.set_text_and_place_cursor_at_end("hello") draft.clear_text() -

Compose keeps the text and the input method's composing region, so nothing is sent to Python per keystroke; you read field.text on demand. Keep this state apart from state you stream output into, such as messages = state(""). The notebook's TextField(text_state=…, padding=8) has no Kotlin counterpart and is written TextField(state=…, modifier=Modifier.padding(8)). Status: partial — tested against a fake host; the input-method proof is python-multiplatform's end-to-end test (#26).

Compose 가 텍스트와 입력기의 조합 영역을 보관하므로 키 입력마다 파이썬으로 보내지는 것이 없고, 필요할 때 field.text 를 읽습니다. 이 상태는 출력을 흘려 넣는 상태(예: messages = state(""))와 분리해 두세요. 노트북의 TextField(text_state=…, padding=8) 은 Kotlin 에 대응이 없으며 TextField(state=…, modifier=Modifier.padding(8)) 로 씁니다. 상태: 부분 — 가짜 호스트로 테스트되었으며, 입력기 증거는 python-multiplatform 의 종단 간 테스트(#26)입니다.

+

Compose keeps the text and the input method's composing region, so nothing is sent to Python per keystroke; you read field.text on demand. Keep this state apart from state you stream output into, such as messages = state(""). The notebook's TextField(text_state=…, padding=8) has no Kotlin counterpart and is written TextField(state=…, modifier=Modifier.padding(8)). Status: partial. Tested against a fake host; the input-method proof is python-multiplatform's end-to-end test (#26).

Compose 가 텍스트와 입력기의 조합 영역을 보관하므로 키 입력마다 파이썬으로 보내지는 것이 없고, 필요할 때 field.text 를 읽습니다. 이 상태는 출력을 흘려 넣는 상태(예: messages = state(""))와 분리해 두세요. 노트북의 TextField(text_state=…, padding=8) 은 Kotlin 에 대응이 없으며 TextField(state=…, modifier=Modifier.padding(8)) 로 씁니다. 상태: 부분. 가짜 호스트로 테스트되었으며, 입력기 증거는 python-multiplatform 의 종단 간 테스트(#26)입니다.

@@ -90,7 +90,7 @@

From the notebook's spelling to the real parameters

- +
KotlinPython
Button(onclick=…)onClickon_click
Text(font_size=30)fontSizefont_size
Column(horizontal_alignment=…)horizontalAlignmenthorizontal_alignment
Button(corner_radius=50)none — Compose takes a shape없음 — Compose 는 shape 를 받음shape
Button(corner_radius=50)none: Compose takes a shape없음: Compose 는 shape 를 받음shape
TextField(text_state=…, padding=8)state, modifierstate=…, modifier=Modifier.padding(8)

fontSize is a TextUnit, which packs several fields into one value, so font_size refuses a raw number such as 30.

fontSize 는 여러 필드를 한 값에 담는 TextUnit 이므로, font_size 는 30 같은 숫자를 거절합니다.

@@ -111,8 +111,8 @@

Spellings that changed바

What does not work yet아직 안 되는 것

    -
  • The colour-scheme factories cannot be called yet: they take 36 colour parameters. DefaultIcons is Icons.Default, an icon is a value (an ImageVector to a type checker) read without parentheses — Icon(DefaultIcons.Add, content_description=None), not the notebook's DefaultIcons.Add() — and it is tested here against a fake host only; the real draw is in python-multiplatform's render test.색 스킴 팩토리는 색 매개변수를 36 개 받아 아직 호출할 수 없습니다. DefaultIcons 는 Icons.Default 이고, 아이콘은 괄호 없이 읽는 값(타입 검사기에는 ImageVector)입니다 — 노트북의 DefaultIcons.Add() 가 아니라 Icon(DefaultIcons.Add, content_description=None). 여기서는 가짜 호스트로만 테스트되며, 실제 그리기는 python-multiplatform 의 렌더 테스트에 있습니다.
  • -
  • A declared app root and Pythonic state — redefining a screen replaces it, with no update call — are planned (issue #11).선언형 앱 루트와 파이썬다운 상태 — 화면을 다시 정의하면 교체되고, 갱신 호출은 없음 — 는 계획 단계입니다(이슈 #11).
  • +
  • The colour-scheme factories cannot be called yet: they take 36 colour parameters. DefaultIcons is Icons.Default, an icon is a value (an ImageVector to a type checker) read without parentheses: Icon(DefaultIcons.Add, content_description=None), not the notebook's DefaultIcons.Add(). It is tested here against a fake host only; the real draw is in python-multiplatform's render test.색 스킴 팩토리는 색 매개변수를 36 개 받아 아직 호출할 수 없습니다. DefaultIcons 는 Icons.Default 이고, 아이콘은 괄호 없이 읽는 값(타입 검사기에는 ImageVector)입니다. 노트북의 DefaultIcons.Add() 가 아니라 Icon(DefaultIcons.Add, content_description=None). 여기서는 가짜 호스트로만 테스트되며, 실제 그리기는 python-multiplatform 의 렌더 테스트에 있습니다.
  • +
  • A declared app root and Pythonic state (redefining a screen replaces it, with no update call) are planned (issue #11).선언형 앱 루트와 파이썬다운 상태(화면을 다시 정의하면 교체되고, 갱신 호출은 없음)는 계획 단계입니다(이슈 #11).
  • TextField(state=…) is wired and tested against a fake host only; that the input method's composing sequence survives, with no Python callback per keystroke, is proved in python-multiplatform's end-to-end test (#26).TextField(state=…) 는 가짜 호스트로만 테스트되었습니다. 입력기의 조합 입력이 키 입력마다 파이썬 콜백 없이 유지되는지는 python-multiplatform 의 종단 간 테스트(#26)에서 증명합니다.
  • remember_saveable and the coroutine scopes the notebook imports are planned.노트북이 import 하는 remember_saveable, 코루틴 스코프는 계획 단계입니다.
@@ -123,8 +123,8 @@

What does not work yet아
-pythonx-compose — Compose, restructured for Python.pythonx-compose — 파이썬을 위해 재구성한 Compose. -GitHub · MIT LicenseMIT 라이선스 +pythonx-compose: Compose, restructured for Python.pythonx-compose: 파이썬을 위해 재구성한 Compose. +GitHub · Apache License 2.0Apache 라이선스 2.0
diff --git a/docs/guide/guide-manifest.html b/docs/guide/guide-manifest.html index 5e556f5..d4fb26f 100644 --- a/docs/guide/guide-manifest.html +++ b/docs/guide/guide-manifest.html @@ -62,7 +62,7 @@

Lending a name to another module

Keeping it in step with the notebook노트북과 맞춰 두기

-

A test reads every pythonx import in the user's notebook and fails if the manifest does not map it. The notebook lives only in the maintainer's checkout, so elsewhere that test has nothing to check — run it there before claiming coverage.

테스트 하나가 사용자 노트북의 모든 pythonx import 를 읽고, 매니페스트가 대응시키지 않으면 실패합니다. 노트북은 메인테이너의 체크아웃에만 있으므로 다른 곳에서는 그 테스트가 확인할 것이 없습니다 — 커버리지를 주장하기 전에 그곳에서 돌리세요.

+

A test reads every pythonx import in the user's notebook and fails if the manifest does not map it. The notebook lives only in the maintainer's checkout, so elsewhere that test has nothing to check, so run it there before claiming coverage.

테스트 하나가 사용자 노트북의 모든 pythonx import 를 읽고, 매니페스트가 대응시키지 않으면 실패합니다. 노트북은 메인테이너의 체크아웃에만 있으므로 다른 곳에서는 그 테스트가 확인할 것이 없습니다. 커버리지를 주장하기 전에 그곳에서 돌리세요.

@@ -70,8 +70,8 @@

Keeping it in step with the notebook
-pythonx-compose — Compose, restructured for Python.pythonx-compose — 파이썬을 위해 재구성한 Compose. -GitHub · MIT LicenseMIT 라이선스 +pythonx-compose: Compose, restructured for Python.pythonx-compose: 파이썬을 위해 재구성한 Compose. +GitHub · Apache License 2.0Apache 라이선스 2.0
diff --git a/docs/guide/guide-modifiers.html b/docs/guide/guide-modifiers.html index 5a25434..6fcf57a 100644 --- a/docs/guide/guide-modifiers.html +++ b/docs/guide/guide-modifiers.html @@ -32,7 +32,7 @@

partial부분

-

Chains, snake_case method names, overload dispatch and Dp as a number are implemented and pass against the binder's adaptation layer (python-multiplatform ba4c6f49 or later). The partial piece is where a chain starts against real Compose — see the empty Modifier below.

체인, snake_case 메서드 이름, 오버로드 디스패치, 숫자로 쓰는 Dp 는 구현되어 있고 바인더의 적응 계층(python-multiplatform ba4c6f49 이후)에서 통과합니다. 부분적인 것은 실제 Compose 에서 체인이 시작되는 지점입니다 — 아래 빈 Modifier 를 보세요.

+

Chains, snake_case method names, overload dispatch and Dp as a number are implemented and pass against the binder's adaptation layer (python-multiplatform ba4c6f49 or later). The partial piece is where a chain starts against real Compose. See the empty Modifier below.

체인, snake_case 메서드 이름, 오버로드 디스패치, 숫자로 쓰는 Dp 는 구현되어 있고 바인더의 적응 계층(python-multiplatform ba4c6f49 이후)에서 통과합니다. 부분적인 것은 실제 Compose 에서 체인이 시작되는 지점입니다. 아래 빈 Modifier 를 보세요.

@@ -40,7 +40,7 @@

Chaining체이닝from pythonx.compose.ui import Modifier m = Modifier.padding(16).size(24).fill_max_width() -

Each link returns a new receiver; nothing is mutated. Method names are snake_case (fill_max_width reaches fillMaxWidth): the naming rule is registered as the binder's member resolver, while the binder's proxy class itself keeps Kotlin names in dir(). A name that is not bound raises AttributeError naming the type it looked on.

각 단계는 새 수신 객체를 돌려주며, 아무것도 변경하지 않습니다. 메서드 이름은 snake_case 입니다(fill_max_width 가 fillMaxWidth 에 닿음): 이름 규칙이 바인더의 멤버 리졸버로 등록되어 있고, 바인더의 프록시 클래스 자체는 dir() 에 Kotlin 이름을 유지합니다. 바인딩되지 않은 이름은 어느 타입에서 찾았는지 밝히는 AttributeError 를 냅니다.

+

Each link returns a new receiver; nothing is mutated. Method names are snake_case (fill_max_width reaches fillMaxWidth): the binder serves the snake_case spelling on its proxies since python-multiplatform #131, while its proxy class keeps Kotlin names in dir(). A name that is not bound raises AttributeError naming the type it looked on.

각 단계는 새 수신 객체를 돌려주며, 아무것도 변경하지 않습니다. 메서드 이름은 snake_case 입니다(fill_max_width 가 fillMaxWidth 에 닿음): python-multiplatform #131 부터 바인더가 프록시에서 snake_case 표기를 제공하고, 바인더의 프록시 클래스 자체는 dir() 에 Kotlin 이름을 유지합니다. 바인딩되지 않은 이름은 어느 타입에서 찾았는지 밝히는 AttributeError 를 냅니다.

@@ -54,7 +54,7 @@

Overloads오버로드

Keywords: method or module function키워드: 메서드와 모듈 함수

-

The member resolver maps keywords as well as names, so a method takes snake_case keywords exactly as the same extension does when called as a module function; inspect.signature shows them. Kotlin's own spelling still works at run time, the type stubs offer only the Pythonic one, and the mapping needs python-multiplatform's describe_member (on an older binder a method's keywords are Kotlin's):

멤버 리졸버는 이름뿐 아니라 키워드도 대응시키므로, 메서드는 같은 확장 함수를 모듈 함수로 부를 때와 똑같이 snake_case 키워드를 받고, inspect.signature 도 그 이름을 보여 줍니다. Kotlin 이름도 실행 시에는 그대로 통하고 타입 스텁은 Pythonic 이름만 제시하며, 이 대응에는 python-multiplatform 의 describe_member 가 필요합니다(오래된 바인더에서는 메서드의 키워드가 Kotlin 이름입니다):

+

The binder maps keywords as well as names (python-multiplatform #131), so a method takes snake_case keywords exactly as the same extension does when called as a module function; inspect.signature shows them. Kotlin's own spelling still works at run time, the type stubs offer only the Pythonic one, and the mapping needs python-multiplatform #131 or later (on an older binder a method's keywords are Kotlin's):

바인더(python-multiplatform #131)는 이름뿐 아니라 키워드도 대응시키므로, 메서드는 같은 확장 함수를 모듈 함수로 부를 때와 똑같이 snake_case 키워드를 받고, inspect.signature 도 그 이름을 보여 줍니다. Kotlin 이름도 실행 시에는 그대로 통하고 타입 스텁은 Pythonic 이름만 제시하며, 이 대응에는 python-multiplatform #131 이상이 필요합니다(오래된 바인더에서는 메서드의 키워드가 Kotlin 이름입니다):

import inspect
 import pythonx.compose.layout as layout
 
@@ -77,7 +77,7 @@ 

Numbers and value classes

The empty Modifier빈 Modifier

-

In Kotlin, Modifier as an expression is an object, and bindings are functions — so there is no bound name for the empty modifier a chain starts from. pythonx/compose/ui/modifier.py is the seam: an application registers a one-line Kotlin factory.

Kotlin 에서 식으로 쓰인 Modifier 는 객체이고 바인딩은 함수이므로, 체인이 시작하는 빈 모디파이어에는 바인딩된 이름이 없습니다. pythonx/compose/ui/modifier.py 가 그 접점이며, 앱이 한 줄짜리 Kotlin 팩토리를 등록합니다.

+

In Kotlin, Modifier as an expression is an object, and bindings are functions, so there is no bound name for the empty modifier a chain starts from. pythonx/compose/ui/modifier.py is the seam: an application registers a one-line Kotlin factory.

Kotlin 에서 식으로 쓰인 Modifier 는 객체이고 바인딩은 함수이므로, 체인이 시작하는 빈 모디파이어에는 바인딩된 이름이 없습니다. pythonx/compose/ui/modifier.py 가 그 접점이며, 앱이 한 줄짜리 Kotlin 팩토리를 등록합니다.

// in the application's Kotlin
 fun emptyModifier(): Modifier = Modifier
import pythonx.compose.ui.modifier as seam
@@ -91,8 +91,8 @@ 

The empty Modifier빈 Modif
-pythonx-compose — Compose, restructured for Python.pythonx-compose — 파이썬을 위해 재구성한 Compose. -GitHub · MIT LicenseMIT 라이선스 +pythonx-compose: Compose, restructured for Python.pythonx-compose: 파이썬을 위해 재구성한 Compose. +GitHub · Apache License 2.0Apache 라이선스 2.0
diff --git a/docs/guide/index.html b/docs/guide/index.html index bddf0b7..f039ac8 100644 --- a/docs/guide/index.html +++ b/docs/guide/index.html @@ -31,9 +31,9 @@
-pre-alpha · pip install --pre pythonx-composepre-alpha · pip install --pre pythonx-compose +pre-alpha · uv add --prerelease allow pythonx-composepre-alpha · uv add --prerelease allow pythonx-compose

Compose, in Python.Compose 를, 파이썬으로.

-

The real Compose widgets with their real parameters — restructured into a Python package, spelled the way Python spells things.진짜 Compose 위젯과 진짜 매개변수를 — 파이썬 패키지로 재구성해, 파이썬이 쓰는 방식대로 씁니다.

+

Build Compose Multiplatform UIs in Python. A Python declarative UI framework: Compose's own widgets with their own parameters, spelled the way Python spells things.파이썬으로 Compose Multiplatform UI 를 만듭니다. 파이썬 선언형 UI 프레임워크로, Compose 의 위젯과 매개변수를 그대로 쓰되 파이썬다운 이름으로 씁니다.

Get started시작하기 → How it works동작 방식 @@ -68,7 +68,7 @@

Same widgets. Python names.

Architecture구조

Two layers, one rule about names.두 계층, 이름에 관한 규칙 하나.

@Composable def Screen()your Pythonpythonx.composethis package · real .pyandroidx.compose.*Kotlin names · binderComposeJetpack / Multiplatformpythonx-map.tomlthe one mapping.pyi stubsin the wheel@Composable def Screen()your Pythonpythonx.composethis package · real .pyandroidx.compose.*Kotlin names · binderComposeJetpack / Multiplatformpythonx-map.toml→ pythonx.compose, .pyi -
The binder exposes Kotlin under its Kotlin names and never renames. This package is real Python that imports those modules and reshapes them. One manifest says which module maps to which.바인더는 Kotlin 을 Kotlin 이름 그대로 노출하고 절대 이름을 바꾸지 않습니다. 이 패키지는 그 모듈을 import 해 재구성하는 실제 파이썬입니다. 어떤 모듈이 무엇에 대응하는지는 매니페스트 하나가 말합니다.
+
The binder exposes Kotlin under its Kotlin names, never renames a namespace, and serves snake_case names and keywords (python-multiplatform #131). This package is real Python that imports those modules and reshapes them. One manifest says which module maps to which.바인더는 Kotlin 을 Kotlin 이름 그대로 노출하고, 네임스페이스 이름은 절대 바꾸지 않으며, snake_case 이름과 키워드를 제공합니다(python-multiplatform #131). 이 패키지는 그 모듈을 import 해 재구성하는 실제 파이썬입니다. 어떤 모듈이 무엇에 대응하는지는 매니페스트 하나가 말합니다.

@@ -77,15 +77,15 @@

Two layers, one rule about names.What it holds to지키는 것

onClick → on_click

The original API원래 API 그대로

Compose's own parameters, in snake_case. No convenience parameters Kotlin does not have.

Compose 자신의 매개변수를 snake_case 로. Kotlin 에 없는 편의 매개변수는 만들지 않습니다.

-
Modifier.padding(16).size(24)

Extensions are methods확장 함수는 메서드

A Kotlin extension function is a method on its receiver, so chains read as they do in Kotlin — fill_max_width() included.

Kotlin 확장 함수는 수신 객체의 메서드가 되어, 체인이 Kotlin 에서처럼 읽힙니다 — fill_max_width() 도 마찬가지입니다.

+
Modifier.padding(16).size(24)

Extensions are methods확장 함수는 메서드

A Kotlin extension function is a method on its receiver, so chains read as they do in Kotlin, fill_max_width() included.

Kotlin 확장 함수는 수신 객체의 메서드가 되어, 체인이 Kotlin 에서처럼 읽힙니다. fill_max_width() 도 마찬가지입니다.

import androidx.compose…

A real package실제 패키지

pythonx/ is ordinary Python on disk. Renaming happens here, never in the binder.

pythonx/ 는 디스크에 있는 평범한 파이썬입니다. 이름 바꾸기는 여기서 하고, 바인더에서는 하지 않습니다.

@Composable

Composable stays@Composable 은 그대로

Screens are decorated Python functions, the way the notebook writes them.

화면은 노트북이 쓰는 그대로, 데코레이터가 붙은 파이썬 함수입니다.

What it is not이것이 아닌 것

    -
  • Not the binder — embedding CPython and the FFI belong to python-multiplatform.바인더가 아닙니다 — CPython 임베딩과 FFI 는 python-multiplatform 의 몫입니다.
  • +
  • Not the binder: embedding CPython and the FFI belong to python-multiplatform.바인더가 아닙니다: CPython 임베딩과 FFI 는 python-multiplatform 의 몫입니다.
  • Not a reimplementation of Compose, and not a new widget set.Compose 의 재구현도, 새 위젯 세트도 아닙니다.
  • -
  • Not runtime reflection — bindings are produced at build time, because native images and Kotlin/Native have none.런타임 리플렉션이 아닙니다 — 네이티브 이미지와 Kotlin/Native 에는 리플렉션이 없으므로 바인딩은 빌드 타임에 만듭니다.
  • +
  • Not runtime reflection: bindings are produced at build time, because native images and Kotlin/Native have none.런타임 리플렉션이 아닙니다: 네이티브 이미지와 Kotlin/Native 에는 리플렉션이 없으므로 바인딩은 빌드 타임에 만듭니다.

@@ -93,8 +93,8 @@

What it is not이것이 아
-pythonx-compose — Compose, restructured for Python.pythonx-compose — 파이썬을 위해 재구성한 Compose. -GitHub · MIT LicenseMIT 라이선스 +pythonx-compose: Compose, restructured for Python.pythonx-compose: 파이썬을 위해 재구성한 Compose. +GitHub · Apache License 2.0Apache 라이선스 2.0
diff --git a/docs/guide/status.html b/docs/guide/status.html index aec2835..05055b8 100644 --- a/docs/guide/status.html +++ b/docs/guide/status.html @@ -34,13 +34,13 @@

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 by the runtime's naming rule, with py.typed, checked with mypy (#12); many types are still Any런타임과 같은 이름 규칙으로 실제 Compose 1.11.1 에서 생성한 타입 스텁(.pyi)과 py.typed, mypy 로 검사(#12); 아직 많은 타입이 Any 입니다
implemented구현Snake-case names, keywords and signatures — modules and methods; a method's keywords need python-multiplatform's describe_member, and Kotlin's own spelling still works at run timesnake_case 이름·키워드·시그니처 — 모듈과 메서드; 메서드 키워드에는 python-multiplatform 의 describe_member 가 필요하고, 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 개)
planned계획remember_saveable, coroutine scopesremember_saveable, 코루틴 스코프
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 by the runtime's naming rule, with py.typed, checked with mypy (#12); many types are still Any런타임과 같은 이름 규칙으로 실제 Compose 1.11.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 개)
planned계획remember_saveable, coroutine scopesremember_saveable, 코루틴 스코프
-

Test baseline: 139 passed, 112 skipped without a python-multiplatform checkout; 250 passed, 1 skipped with a current one. The one skip with a current checkout reads the maintainer's UI.ipynb.

테스트 기준선: python-multiplatform 체크아웃이 없으면 132 passed, 99 skipped, 최신 체크아웃이면 250 passed, 1 skipped. 최신 체크아웃에서 건너뛰는 1 개는 메인테이너의 UI.ipynb 를 읽는 테스트입니다.

+

Test baseline: 148 passed, 109 skipped without a python-multiplatform checkout; 256 passed, 1 skipped with a current one. The one skip with a current checkout reads the maintainer's UI.ipynb.

테스트 기준선: python-multiplatform 체크아웃이 없으면 132 passed, 99 skipped, 최신 체크아웃이면 256 passed, 1 skipped. 최신 체크아웃에서 건너뛰는 1 개는 메인테이너의 UI.ipynb 를 읽는 테스트입니다.

FAQ

-
Can I pip install it?pip 으로 설치할 수 있나요?

Yes, once the maintainer tags the first alpha: pip install --pre pythonx-compose (version 0.1.0a1). Until it is out, clone the repository.

메인테이너가 첫 알파를 태그하면 가능합니다: pip install --pre pythonx-compose (버전 0.1.0a1). 배포 전에는 저장소를 클론하세요.

Do I need to know Kotlin?Kotlin 을 알아야 하나요?

No. The widgets and parameters are Compose's, so Compose documentation applies — read onClick as on_click.

아니요. 위젯과 매개변수가 Compose 의 것이므로 Compose 문서가 그대로 적용됩니다 — onClick 을 on_click 으로 읽으면 됩니다.

Does it run on its own?단독으로 실행되나요?

No. It runs inside an app that embeds CPython through python-multiplatform.

아니요. python-multiplatform 으로 CPython 을 임베딩한 앱 안에서 동작합니다.

Why not look classes up at run time?왜 런타임에 클래스를 찾지 않나요?

GraalVM native images and Kotlin/Native have no reflection, so bindings are generated at build time.

GraalVM 네이티브 이미지와 Kotlin/Native 에는 리플렉션이 없으므로, 바인딩을 빌드 타임에 생성합니다.

What happened to the test/ directory?test/ 디렉터리는 어떻게 되었나요?

It no longer exists. It was a 2023–2024 Kotlin Multiplatform sample from the project's first generation, and it was deleted; the tag archive/pre-restructure keeps it.

더 이상 없습니다. 프로젝트 첫 세대의 2023–2024 Kotlin Multiplatform 샘플이었고 삭제되었으며, 태그 archive/pre-restructure 로 되찾을 수 있습니다.

+
Can I install it from PyPI?PyPI 에서 설치할 수 있나요?

Yes: uv add --prerelease allow pythonx-compose installs 0.1.0a1, the first alpha; with ppp use ppp core add "pythonx-compose==0.1.0a1", with tcl tcl install pythonx-compose. It needs a host app that embeds python-multiplatform to resolve Compose names.

네: uv add --prerelease allow pythonx-compose 로 첫 알파 0.1.0a1 을 설치합니다. ppp 는 ppp core add "pythonx-compose==0.1.0a1", tcl 은 tcl install pythonx-compose 입니다. Compose 이름을 쓰려면 python-multiplatform 을 품은 호스트 앱이 필요합니다.

Do I need to know Kotlin?Kotlin 을 알아야 하나요?

No. The widgets and parameters are Compose's, so Compose documentation applies; read onClick as on_click.

아니요. 위젯과 매개변수가 Compose 의 것이므로 Compose 문서가 그대로 적용됩니다. onClick 을 on_click 으로 읽으면 됩니다.

Does it run on its own?단독으로 실행되나요?

No. It runs inside an app that embeds CPython through python-multiplatform.

아니요. python-multiplatform 으로 CPython 을 임베딩한 앱 안에서 동작합니다.

Why not look classes up at run time?왜 런타임에 클래스를 찾지 않나요?

GraalVM native images and Kotlin/Native have no reflection, so bindings are generated at build time.

GraalVM 네이티브 이미지와 Kotlin/Native 에는 리플렉션이 없으므로, 바인딩을 빌드 타임에 생성합니다.

What happened to the test/ directory?test/ 디렉터리는 어떻게 되었나요?

It no longer exists. It was a 2023–2024 Kotlin Multiplatform sample from the project's first generation, and it was deleted; the tag archive/pre-restructure keeps it.

더 이상 없습니다. 프로젝트 첫 세대의 2023–2024 Kotlin Multiplatform 샘플이었고 삭제되었으며, 태그 archive/pre-restructure 로 되찾을 수 있습니다.

@@ -48,8 +48,8 @@

FAQ

-pythonx-compose — Compose, restructured for Python.pythonx-compose — 파이썬을 위해 재구성한 Compose. -GitHub · MIT LicenseMIT 라이선스 +pythonx-compose: Compose, restructured for Python.pythonx-compose: 파이썬을 위해 재구성한 Compose. +GitHub · Apache License 2.0Apache 라이선스 2.0
diff --git a/docs/locale/README_ko.md b/docs/locale/README_ko.md index 5a6715a..b4f1c9c 100644 --- a/docs/locale/README_ko.md +++ b/docs/locale/README_ko.md @@ -4,9 +4,9 @@ # pythonx-compose -**Compose UI 를 파이썬으로 — Compose 의 위젯 그대로, 파이썬다운 이름으로.** +**파이썬으로 Compose Multiplatform UI 를 만듭니다: 파이썬 선언형 UI 프레임워크** -[![License: MIT](https://img.shields.io/badge/license-MIT-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) +[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) [![Python](https://img.shields.io/badge/python-3.11%2B-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/pyproject.toml) [![pip](https://img.shields.io/badge/pip-pythonx--compose-7c4dff.svg)](https://github.com/thisisthepy/pythonx-compose/blob/main/pyproject.toml) [![Status](https://img.shields.io/badge/status-pre--alpha-lightgrey.svg)](#-현황) @@ -19,7 +19,7 @@ ## 왜 만드는가 -Jetpack Compose 와 Compose Multiplatform 은 UI 를 만드는 훌륭한 방법입니다 — Kotlin 을 쓴다면요. +Jetpack Compose 와 Compose Multiplatform 은 UI 를 만드는 훌륭한 방법입니다. Kotlin 을 쓴다면요. `pythonx-compose` 는 파이썬을 쓰는 사람을 위한 것입니다. Compose 를 다시 구현하지도, 새 위젯 세트를 만들지도 않습니다. [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) 이 Kotlin 이름 그대로 파이썬에 노출한 실제 `androidx.compose.*` API 를 가져와, 파이썬다운 패키지 @@ -53,7 +53,9 @@ def Greeting(): 수도 있습니다: `Alignment.Horizontal.End` 는 `Alignment.End` 입니다. - **`@Composable` 은 그대로.** 화면은 데코레이터가 붙은 파이썬 함수입니다. - **실제 패키지.** `pythonx/` 는 `androidx.compose.*` 를 import 해 재구성하는 평범한 파이썬 - 소스입니다. 바인더는 아무 이름도 바꾸지 않습니다 — 이름을 바꾸는 것은 이 패키지의 일입니다. + 소스입니다. 바인더는 네임스페이스 이름을 바꾸지 않습니다. python-multiplatform #131 부터는 + `snake_case` 이름과 키워드를 이 패키지의 규칙대로 바인더가 직접 제공하며, 이 패키지는 모듈 묶음과 + `snake_case` 만 나열하는 표면을 맡습니다. - **하나의 매니페스트.** [`pythonx-map.toml`](https://github.com/thisisthepy/pythonx-compose/blob/main/pythonx/compose/pythonx-map.toml) 이 어떤 `pythonx.compose.*` 모듈이 어떤 Kotlin 패키지에 대응하는지 적습니다. 이 파일은 패키지 안에 있으며 wheel 에 함께 담깁니다. 런타임과 `.pyi` 생성기가 같은 파일을 읽으므로, 편집기가 자동완성하는 이름과 인터프리터가 @@ -84,16 +86,23 @@ flowchart LR ## 🚀 빠른 시작 > [!NOTE] -> `pythonx-compose` 는 **pre-alpha** 단계입니다. 첫 알파 `0.1.0a1` 은 메인테이너가 태그를 달면 PyPI 에 -> 배포되며, 프리릴리스이므로 `pip install --pre pythonx-compose` 로 설치합니다. +> `pythonx-compose` 는 **알파** 단계입니다. `0.1.0a1` 이 [PyPI](https://pypi.org/project/pythonx-compose/) 에 +> 있으며, 프리릴리스이므로 uv(또는 ppp, tcl)로 설치합니다. + +```bash +uv add --prerelease allow pythonx-compose +# 또는 pypackpack 으로, ppp 워크스페이스의 패키지에 추가: +ppp core add "pythonx-compose==0.1.0a1" +# 또는 toolchain-lite 로: +tcl install pythonx-compose +``` 클론에서 테스트를 돌리려면: ```bash git clone https://github.com/thisisthepy/pythonx-compose cd pythonx-compose -python3 -m pip install pytest # 가상환경 안에서 -python3 -m pytest tests -q +uv run --with pytest --with mypy pytest tests -q ``` 저장소 루트에서 지금 동작하는 것: @@ -152,15 +161,15 @@ pip 패키지 **`pythonx-compose`**(파이썬 3.11 이상)로 배포되며, impo | `androidx.compose.*` 를 규칙 하나로 재노출하는 실제 디스크 패키지 `pythonx` | ✅ 구현, 테스트됨 | | snake_case 메서드를 쓰는 `Modifier` 체인, 오버로드 디스패치, 숫자로 쓰는 `Dp` | ✅ 구현, 바인더 계층으로 테스트됨 | | `layout` 뿐 아니라 `material3` 에서도 쓰는 `Column`, `Row`, `Spacer` | ✅ 구현, 테스트됨 | -| `snake_case` 메서드 키워드 인자 | ✅ 구현, 테스트됨 — 모듈 함수와 메서드(메서드는 python-multiplatform 의 `describe_member` 필요) | -| 빈 `Modifier` | 🟡 부분 — 클래스에서 시작하는 `Modifier.padding(16)` 은 실제 Compose 에서 앱이 제공하는 팩토리가 필요 | -| `Alignment` / `Arrangement` | ✅ 구현 및 테스트 — `Alignment.Center`, `Arrangement.spaced_by(8)`, 그리고 `Alignment.End` 와 나란히 묶음 표기 `Alignment.Horizontal.End` | -| Material 3 위젯 (`Text`, `Button`, `Card`, `TextField`, …) | 🟡 부분 — 규칙으로 재노출됨; 위젯별 렌더 증거 대기(#9) | +| `snake_case` 메서드 키워드 인자 | ✅ 구현, 테스트됨: 모듈 함수와 메서드(메서드는 python-multiplatform 의 `describe_member` 필요) | +| 빈 `Modifier` | 🟡 부분: 클래스에서 시작하는 `Modifier.padding(16)` 은 실제 Compose 에서 앱이 제공하는 팩토리가 필요 | +| `Alignment` / `Arrangement` | ✅ 구현 및 테스트: `Alignment.Center`, `Arrangement.spaced_by(8)`, 그리고 `Alignment.End` 와 나란히 묶음 표기 `Alignment.Horizontal.End` | +| Material 3 위젯 (`Text`, `Button`, `Card`, `TextField`, …) | 🟡 부분: 규칙으로 재노출됨; 위젯별 렌더 증거 대기(#9) | | 실제 Compose 1.11.1 에서 생성한 타입 스텁(`.pyi`), `py.typed` | ✅ 구현, mypy 로 검사; 아직 많은 타입이 `Any` 입니다(#12) | -| 배포 (`pythonx-compose`) | 🟡 부분 — wheel 에 매니페스트, 재노출 규칙, 스텁, `py.typed` 포함 | -| 갱신 호출 없는 선언형 앱 루트(`@app`)와 파이썬다운 상태(`state`) | 🟡 부분 — 바인더 경로는 python-multiplatform #38 모양의 가짜 호스트로 테스트됨, 실제 Compose 는 E2E 모듈(#11, #19), 숫자와 문자열은 `state` 로 왕복됨 | -| `TextField(state=...)` 와 `TextFieldState` (`pythonx.compose.foundation.text.input`) | 🟡 부분 — python-multiplatform #73 모양의 가짜 호스트로 테스트됨; 입력기(IME) 조합 증거는 python-multiplatform E2E #26 (#10) | -| `DefaultIcons`(`Icons.Default`), `DefaultIcons.Add` 로 씀 | 🟡 부분 — python-multiplatform #37/#38 모양의 가짜 호스트로 테스트됨; `Icon(DefaultIcons.Add, …)` 의 실제 그리기는 python-multiplatform 의 렌더 테스트에 있음 | +| 배포 (`pythonx-compose`) | 🟡 부분: wheel 에 매니페스트, 재노출 규칙, 스텁, `py.typed` 포함 | +| 갱신 호출 없는 선언형 앱 루트(`@app`)와 파이썬다운 상태(`state`) | 🟡 부분: 바인더 경로는 python-multiplatform #38 모양의 가짜 호스트로 테스트됨, 실제 Compose 는 E2E 모듈(#11, #19), 숫자와 문자열은 `state` 로 왕복됨 | +| `TextField(state=...)` 와 `TextFieldState` (`pythonx.compose.foundation.text.input`) | 🟡 부분: python-multiplatform #73 모양의 가짜 호스트로 테스트됨; 입력기(IME) 조합 증거는 python-multiplatform E2E #26 (#10) | +| `DefaultIcons`(`Icons.Default`), `DefaultIcons.Add` 로 씀 | 🟡 부분: python-multiplatform #37/#38 모양의 가짜 호스트로 테스트됨; `Icon(DefaultIcons.Add, …)` 의 실제 그리기는 python-multiplatform 의 렌더 테스트에 있음 | | 색 스킴 | ⏳ 계획 | | `remember_saveable`, 코루틴 스코프 | ⏳ 계획 | @@ -168,15 +177,15 @@ pip 패키지 **`pythonx-compose`**(파이썬 3.11 이상)로 배포되며, impo ## 📖 문서 -- **가이드** — [`docs/guide/`](https://thisisthepy.github.io/pythonx-compose/), 영어 / 한국어 -- **English README** — [`README.md`](https://github.com/thisisthepy/pythonx-compose/blob/main/README.md) +- **가이드**: [`docs/guide/`](https://thisisthepy.github.io/pythonx-compose/), 영어 / 한국어 +- **English README**: [`README.md`](https://github.com/thisisthepy/pythonx-compose/blob/main/README.md) ## 🔌 생태계 | 저장소 | 역할 | |---|---| | [python-multiplatform](https://github.com/thisisthepy/python-multiplatform) | 바인더: Kotlin Multiplatform 에 임베딩한 CPython, Kotlin 을 Kotlin 이름 그대로 파이썬에 노출 | -| **pythonx-compose** | 파이썬을 위해 재구성한 Compose — 이 저장소 | +| **pythonx-compose** | 파이썬을 위해 재구성한 Compose (이 저장소) | | [toolchain](https://github.com/thisisthepy/toolchain) | Python Multiplatform 앱을 위한 Gradle 빌드 플러그인 | | [pypackpack](https://github.com/thisisthepy/pypackpack) | 파이썬 프로젝트의 멀티플랫폼 배포 | | [torchnative](https://github.com/thisisthepy/torchnative) | 실제 PyTorch 생태계를 기기에서 실행 | @@ -197,4 +206,4 @@ pip 패키지 **`pythonx-compose`**(파이썬 3.11 이상)로 배포되며, impo ## 라이선스 -[MIT](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) © 2023–2024 BREW (b-re-w), Jong-uk Lee (rnoro5122) +[Apache License 2.0](https://github.com/thisisthepy/pythonx-compose/blob/main/LICENSE) © 2023–2024 BREW (b-re-w), Jong-uk Lee (rnoro5122) diff --git a/docs/style/writing.md b/docs/style/writing.md new file mode 100644 index 0000000..b6c0093 --- /dev/null +++ b/docs/style/writing.md @@ -0,0 +1,51 @@ +# Writing style + +The house style for thisisthepy documentation. The maintainer named this repository's guide +(`docs/guide/`) as the reference for every repository, so other projects align their READMEs, guides +and docs with what is written here. When this file and the guide disagree, the guide's practice wins +and this file gets corrected. + +## Voice + +- **Say what is true now.** Describe what the code does today, in the present tense. Something not + built is marked as such, with its status (`implemented`, `partial`, `planned`) and the issue that + tracks it. Never describe a plan as if it shipped. +- **Plain and concrete.** No marketing words and no hedging. Name the actual thing: a module, a + function, a test, an issue number, a commit. +- **Short declarative sentences.** One idea per sentence, about 12 to 18 words on average in English. + A long sentence is split, not joined with a dash. +- **Reasons beside rules.** When a rule or a limitation is stated, the next clause or sentence gives + the reason ("because ...", "so that ..."). A reader should never have to guess why. +- **Examples over abstraction.** Show the spelling a user writes (`Modifier.padding(16).fill_max_width()`) + rather than describing it. + +## English and Korean + +Every user-facing page exists in both languages (README.md and docs/locale/README_ko.md; each +visible string of the guide in `data-lang="en"` and `data-lang="ko"`). + +- **Same content, same order, not word for word.** Each English paragraph has one Korean paragraph + saying the same thing in natural Korean. A sentence may be split or merged differently, but no + fact is added or dropped in one language only. +- **Korean register.** User-facing text (README_ko, the guide) uses the polite declarative, + 합니다체: "설치합니다", "동작합니다". Internal records (PROJECT.md, which is written in Korean) + use the plain declarative, 한다체: "한다", "두었다". +- **Terms.** Code identifiers, Kotlin names, commands and file names stay in their original spelling + inside code formatting, in both languages. A particle follows code with a space: "`UI.ipynb` 를", + "`state()` 로". Common technical words are written as Korean loanwords when that is how Korean + developers say them ("바인더", "스텁", "컴포저블", "모듈"); otherwise the English term stays + ("snake_case", "wheel", "proxy"). +- **Status words** pair one to one: implemented / 구현, partial / 부분, planned / 계획. +- **Headings** are short noun phrases in both languages ("The naming rule" / "이름 규칙"). + +## Mechanics + +- **No em-dash (U+2014)**, anywhere in documents or code. Split the sentence, or use a comma, a colon + or parentheses. The en-dash in ranges (2023–2024) is fine. +- **Install and run examples use uv, ppp (pypackpack) and tcl (toolchain-lite) only.** No `pip` + examples. Show uv first: `uv add --prerelease allow pythonx-compose`, then + `ppp core add "pythonx-compose==0.1.0a1"` and `tcl install pythonx-compose`; run with `uv run ...`. +- **Numbers carry their source.** A test count names the environment it was measured in; a claim + about upstream names the commit, PR or issue. +- **Links** in README files are absolute (they are read on pypi.org too), and README files never + link the internal documents (AGENTS.md, PROJECT.md, docs/INTENT.md, docs/SPEC.md). diff --git a/pyproject.toml b/pyproject.toml index b6797f2..6b25fd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,9 +5,9 @@ build-backend = "setuptools.build_meta" [project] name = "pythonx-compose" version = "0.1.0a1" -description = "Pythonic Compose UI for Python on Kotlin Multiplatform, through python-multiplatform" +description = "Build Compose Multiplatform UIs in Python: Python Declarative UI Framework" readme = {file = "README.md", content-type = "text/markdown"} -license = "MIT" +license = "Apache-2.0" license-files = ["LICENSE"] authors = [ {name = "BREW (b-re-w)"}, diff --git a/pythonx/compose/__init__.pyi b/pythonx/compose/__init__.pyi index b912439..311d35c 100644 --- a/pythonx/compose/__init__.pyi +++ b/pythonx/compose/__init__.pyi @@ -1 +1 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. diff --git a/pythonx/compose/_reexport.py b/pythonx/compose/_reexport.py index a38afb7..f627da4 100644 --- a/pythonx/compose/_reexport.py +++ b/pythonx/compose/_reexport.py @@ -1,33 +1,29 @@ """The one rule that makes a `pythonx.compose.*` module a Pythonic view of a Kotlin package. -The binder (`python-multiplatform`) serves every bound Kotlin package under its **Kotlin** name -- -`androidx.compose.foundation.layout` -- with Kotlin's own surface: declaration names, keyword -arguments by Kotlin parameter name, Kotlin defaults, overload dispatch. It renames nothing, and it -publishes what a Pythonic layer needs to rename by rule: `inspect.signature(fn)` with the Kotlin -parameter names, and `python_multiplatform.describe(fn)` with one dict per overload. +The binder (`python-multiplatform`) serves every bound Kotlin package under its **Kotlin** name +(`androidx.compose.foundation.layout`), and since its #131 it also serves the Pythonic spellings +itself: snake_case names and keyword arguments, by this module's rule (`python_name`), with the +Kotlin spelling still working and `inspect.signature` reporting the snake_case keywords. It never +renames a namespace: `pythonx` is this package. -This module is that layer. A mapped package's `__init__.py` is one line:: +This module groups those Kotlin packages into `pythonx.compose.*`. A mapped package's `__init__.py` +is one line:: __getattr__, __dir__ = reexport(__name__) and every name in it then resolves against the Kotlin package `pythonx-map.toml` maps it to: -- **Names.** A name that starts upper-case -- a type, an object, a `@Composable` -- keeps its Kotlin - spelling (`Modifier`, `Text`). Any other name is reached by its snake_case spelling only +- **Names.** A name that starts upper-case (a type, an object, a `@Composable`) keeps its Kotlin + spelling (`Modifier`, `Text`). Any other name is listed and reached by its snake_case spelling (`fillMaxWidth` -> `fill_max_width`, `toURLString` -> `to_url_string`), and an explicit overload - keeps its suffix (`padding__Dp`). The inverse is never computed: the module's real Kotlin names - are converted forward and looked up, so a name the reverse rule could not invert still resolves. -- **Keywords.** `on_click=` reaches the parameter Kotlin calls `onClick`, matched against the - declaration's own parameter names (every overload's), never derived. -- **Signatures.** A re-exported function answers `inspect.signature` with the snake_case names; - defaults stay `python_multiplatform.KOTLIN_DEFAULT`. + keeps its suffix (`padding__Dp`). The rule is applied forward to the module's real Kotlin names, + so a name the reverse rule could not invert still resolves. +- **Functions** are the binder's own callables; their keywords and signatures are the binder's. - **Value classes.** The manifest's `raw-primitive-allowed` list is handed to the binder once per installed binding layer, so `padding(16)` reaches a `Dp` parameter. -- **Methods on a proxy.** The same name rule is registered as the binder's member resolver, so - `Modifier.padding(16).fill_max_width()` reaches `fillMaxWidth` on a proxy Kotlin returned, and - `m.padding(padding_values=...)` reaches `paddingValues`: the resolver also answers a keyword map, - read from `python_multiplatform.describe_member`. The Kotlin spelling still works. The binder - caches the alias in its own registry; its proxy classes keep Kotlin names in `dir()`. +- **Methods on a proxy** are the binder's: since python-multiplatform #131 it serves + `Modifier.padding(16).fill_max_width()` and `m.padding(padding_values=...)` itself, by this + package's rule, with the Kotlin spelling still working. Nothing here takes part. - **Grouped constants.** Inside a Kotlin object served as a namespace, a type nested in it that is not one of its members groups the constants declared as exactly that type, so the notebook's @@ -50,7 +46,6 @@ import importlib import importlib.util -import inspect import re import sys import types @@ -116,77 +111,16 @@ def binding_layer(): _PRIMED = weakref.WeakSet() -_MEMBER_KEYWORDS: "weakref.WeakKeyDictionary" = weakref.WeakKeyDictionary() - - -def _method_keywords(kotlin_type_name: str, kotlin_member_name: str) -> dict: - """`{snake_case(p): p}` for every parameter `p` of every declaration of a proxy's member. - - Read with `python_multiplatform.describe_member`, which lists each overload's parameters without - invoking anything (the receiver slot is not among them). It is metadata, so it is kept per - (type, member), under the `describe_member` of the binding layer that answered: a new layer - starts with an empty table. A keyword whose snake_case spelling is its own is left out, and a - binder without `describe_member`, or a member it does not describe, has no map. - """ - describe_member = getattr(sys.modules.get(ROOT_MODULE), "describe_member", None) - if describe_member is None: - return {} - try: - known = _MEMBER_KEYWORDS.setdefault(describe_member, {}) - except TypeError: # something that cannot be weakly referenced: nothing to key a cache by - known = {} - key = (kotlin_type_name, kotlin_member_name) - if key not in known: - keywords: dict = {} - try: - declarations = describe_member(kotlin_type_name, kotlin_member_name) - except (TypeError, AttributeError): - declarations = () - for declaration in declarations: - for parameter in declaration.get("parameters", ()): - kotlin_parameter = parameter.get("name") - if kotlin_parameter and snake_case(kotlin_parameter) != kotlin_parameter: - keywords.setdefault(snake_case(kotlin_parameter), kotlin_parameter) - known[key] = keywords - return known[key] - - -def member_name(kotlin_type_name: str, requested: str, kotlin_member_names): - """The binder's member-resolver hook: the Kotlin member a Pythonic name means on a proxy, and - the keywords it takes. - - The binder asks for a name its proxy has no Kotlin member of - (`python_multiplatform.binding.add_member_resolver`): the rule is the module rule applied - forward to the proxy's real member names, so `fill_max_width` finds `fillMaxWidth`. It asks - again for a Kotlin member name when a call passes keywords. The answer is then - `(kotlin_name, {python_keyword: kotlinParameter})`, the parameters of every overload of that - member, so `m.padding(padding_values=...)` reaches `paddingValues`. Without a keyword to map the - answer is the name alone, which is all a binder without `describe_member` can be given. - """ - if requested in kotlin_member_names: - target = requested - else: - matches = [name for name in kotlin_member_names if python_name(name) == requested] - target = matches[0] if len(matches) == 1 else None - if target is None: - return None - keywords = _method_keywords(kotlin_type_name, target) - return (target, keywords) if keywords else target - - def _prime(binding) -> None: - """Hand this binding layer the manifest's value-class allowlist and the member rule, once. + """Hand this binding layer the manifest's value-class allowlist, once. - The member rule needs python-multiplatform's `add_member_resolver`; on a binder without it, - methods on a returned proxy stay reachable by their Kotlin names only. + Method names and keywords on a proxy are the binder's own since python-multiplatform #131 + (snake_case by this package's rule); nothing is registered for them here. """ if binding in _PRIMED: return for kotlin_type in manifest().get("value-classes", {}).get("raw-primitive-allowed", ()): binding.allow_raw_primitive(kotlin_type) - add_member_resolver = getattr(binding, "add_member_resolver", None) - if add_member_resolver is not None: - add_member_resolver(member_name) _PRIMED.add(binding) @@ -233,72 +167,14 @@ def _declared_type(kotlin, kotlin_name: str): return described.get("kind"), described.get("returns") -def _describe(fn): - root = sys.modules.get(ROOT_MODULE) - if root is None or not hasattr(root, "describe"): - return () - try: - return root.describe(fn) - except (TypeError, AttributeError): - return () - - -class PythonicFunction: - """A binder callable seen through the naming rule: snake_case keywords and signature. - - Calls go straight to the binder's callable with the keywords translated; overload selection, - defaults and value classes are the binder's. A keyword that matches no parameter is passed on - unchanged, so the binder's own refusal -- which lists the candidates -- is what the caller sees. - """ - - __slots__ = ("_kotlin", "_keywords", "__name__", "__qualname__") - - def __init__(self, kotlin_fn, name): - self._kotlin = kotlin_fn - self.__name__ = name - self.__qualname__ = name - keywords = {} - for row in _describe(kotlin_fn): - for parameter in row.get("parameters", ()): - kotlin_parameter = parameter["name"] - keywords.setdefault(snake_case(kotlin_parameter), kotlin_parameter) - self._keywords = keywords - - @property - def __kotlin__(self): - """The binder's callable this wraps.""" - return self._kotlin - - @property - def __kotlin_rows__(self): - return self._kotlin.__kotlin_rows__ - - @property - def __signature__(self): - kotlin_signature = inspect.signature(self._kotlin) - return kotlin_signature.replace(parameters=[ - parameter if parameter.kind in (parameter.VAR_POSITIONAL, parameter.VAR_KEYWORD) - else parameter.replace(name=snake_case(parameter.name)) - for parameter in kotlin_signature.parameters.values() - ]) - - def __call__(self, *args, **kwargs): - if kwargs: - kwargs = {self._keywords.get(key, key): value for key, value in kwargs.items()} - return self._kotlin(*args, **kwargs) - - def __repr__(self): - return f" {self._kotlin!r}>" - - def _pythonic(value, name): if isinstance(value, types.ModuleType): # A Kotlin object served as a sub-package. Since python-multiplatform #35 the binder lists it # and exposes it as an attribute of its parent, so the name table reaches it directly. - return KotlinObject(value) - if isinstance(value, type) or not callable(value): - return value - return PythonicFunction(value, name) + return _kotlin_object(value) + # A function is the binder's own callable: since python-multiplatform #131 it takes snake_case + # keywords and reports them in `inspect.signature` itself. + return value def _resolve(kotlin, name: str): @@ -320,7 +196,7 @@ def _resolve(kotlin, name: str): nested = importlib.import_module(kotlin.__name__ + "." + name) except ImportError: return None - return KotlinObject(nested), True + return _kotlin_object(nested), True return None @@ -400,6 +276,25 @@ def __repr__(self): return f"" +class CallableKotlinObject(KotlinObject): + """A Kotlin object whose name is also a function in its package (`Color`), callable as that function. + + The binder makes such a module callable (python-multiplatform #78): `Color(0xFFFFFFFF)` calls + the `Color` function, while `Color.Red` is still a constant of the object. Calling goes to the + binder's module itself, which resolves the function on every call. + """ + + __slots__ = () + + def __call__(self, *args, **kwargs): + return self._kotlin(*args, **kwargs) + + +def _kotlin_object(kotlin_module): + """The namespace for a Kotlin object module: callable only when the binder made the module callable.""" + return CallableKotlinObject(kotlin_module) if callable(kotlin_module) else KotlinObject(kotlin_module) + + class ConstantGroup: """The constants of a Kotlin object declared as one type nested in it: `Alignment.Horizontal`. diff --git a/pythonx/compose/foundation/__init__.pyi b/pythonx/compose/foundation/__init__.pyi index 1b89d73..6783b3a 100644 --- a/pythonx/compose/foundation/__init__.pyi +++ b/pythonx/compose/foundation/__init__.pyi @@ -1,4 +1,4 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t class BorderStroke: diff --git a/pythonx/compose/foundation/layout/__init__.pyi b/pythonx/compose/foundation/layout/__init__.pyi index 1c77602..422a5db 100644 --- a/pythonx/compose/foundation/layout/__init__.pyi +++ b/pythonx/compose/foundation/layout/__init__.pyi @@ -1,2 +1,2 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. from pythonx.compose.layout import * diff --git a/pythonx/compose/foundation/text/input/__init__.pyi b/pythonx/compose/foundation/text/input/__init__.pyi index f40d055..95889de 100644 --- a/pythonx/compose/foundation/text/input/__init__.pyi +++ b/pythonx/compose/foundation/text/input/__init__.pyi @@ -1,4 +1,4 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t class _TextFieldState_clearText(_t.Protocol): @@ -30,7 +30,7 @@ class TextFieldLineLimits: class TextFieldState: """Kotlin: androidx.compose.foundation.text.input.TextFieldState""" - def __init__(self, initialText: str = ..., initialSelection: _t.Any = ...) -> None: + def __init__(self, initial_text: str = ..., initial_selection: _t.Any = ...) -> None: """Kotlin constructor: androidx.compose.foundation.text.input.TextFieldState (androidx.compose.foundation.text.input.TextFieldState(initialText: kotlin.String, initialSelection: androidx.compose.ui.text.TextRange): androidx.compose.foundation.text.input.TextFieldState)""" ... clear_text: _t.ClassVar[_TextFieldState_clearText] diff --git a/pythonx/compose/layout/__init__.pyi b/pythonx/compose/layout/__init__.pyi index 9899f8d..f2c5126 100644 --- a/pythonx/compose/layout/__init__.pyi +++ b/pythonx/compose/layout/__init__.pyi @@ -1,9 +1,19 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t import pythonx.compose.material3 import pythonx.compose.ui import pythonx.compose.ui.unit +class _PaddingValues_callable_module(_t.Protocol): + """Kotlin: the function PaddingValues, and the members of the module PaddingValues""" + @_t.overload + def __call__(self, all: float) -> _t.Any: ... + @_t.overload + def __call__(self, horizontal: float = ..., vertical: float = ...) -> _t.Any: ... # type: ignore[overload-cannot-match] + @_t.overload + def __call__(self, start: float = ..., top: float = ..., end: float = ..., bottom: float = ...) -> _t.Any: ... # type: ignore[overload-cannot-match] + Zero: _t.Any + class _RowScope_NavigationBarItem(_t.Protocol): def __call__(self, selected: bool, on_click: _t.Callable[[], None], icon: _t.Callable[[], None], modifier: pythonx.compose.ui.Modifier = ..., enabled: bool = ..., label: _t.Callable[[], None] | None = ..., always_show_label: bool = ..., colors: pythonx.compose.material3.NavigationBarItemColors = ..., interaction_source: _t.Any | None = ...) -> None: ... @@ -154,7 +164,7 @@ class IntrinsicSize: class MutableWindowInsets: """Kotlin: androidx.compose.foundation.layout.MutableWindowInsets""" - def __init__(self, initialInsets: _t.Any = ...) -> None: + def __init__(self, initial_insets: _t.Any = ...) -> None: """Kotlin constructor: androidx.compose.foundation.layout.MutableWindowInsets (androidx.compose.foundation.layout.MutableWindowInsets(initialInsets: androidx.compose.foundation.layout.WindowInsets): androidx.compose.foundation.layout.MutableWindowInsets)""" ... @property @@ -554,6 +564,8 @@ def wrap_content_width(receiver: pythonx.compose.ui.Modifier, /, align: pythonx. """Kotlin: androidx.compose.ui.Modifier.wrapContentWidth(align: androidx.compose.ui.Alignment.Horizontal, unbounded: kotlin.Boolean): androidx.compose.ui.Modifier""" ... +PaddingValues: _PaddingValues_callable_module + @_t.overload def Box(modifier: pythonx.compose.ui.Modifier) -> None: ... @_t.overload @@ -569,13 +581,6 @@ def FlowRow(modifier: pythonx.compose.ui.Modifier = ..., horizontal_arrangement: @_t.overload def FlowRow(modifier: pythonx.compose.ui.Modifier = ..., horizontal_arrangement: Arrangement.Horizontal = ..., vertical_arrangement: Arrangement.Vertical = ..., item_vertical_alignment: pythonx.compose.ui.Alignment.Vertical = ..., max_items_in_each_row: int = ..., max_lines: int = ..., *, content: _t.Callable[[FlowRowScope], None]) -> None: ... # type: ignore[overload-cannot-match] -@_t.overload -def PaddingValues(all: float) -> _t.Any: ... -@_t.overload -def PaddingValues(horizontal: float = ..., vertical: float = ...) -> _t.Any: ... # type: ignore[overload-cannot-match] -@_t.overload -def PaddingValues(start: float = ..., top: float = ..., end: float = ..., bottom: float = ...) -> _t.Any: ... # type: ignore[overload-cannot-match] - @_t.overload def WindowInsets() -> _t.Any: ... @_t.overload diff --git a/pythonx/compose/material/__init__.pyi b/pythonx/compose/material/__init__.pyi index b912439..311d35c 100644 --- a/pythonx/compose/material/__init__.pyi +++ b/pythonx/compose/material/__init__.pyi @@ -1 +1 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. diff --git a/pythonx/compose/material/icons/__init__.pyi b/pythonx/compose/material/icons/__init__.pyi index 7d337f9..8a983a3 100644 --- a/pythonx/compose/material/icons/__init__.pyi +++ b/pythonx/compose/material/icons/__init__.pyi @@ -1,4 +1,4 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t import pythonx.compose.ui.graphics.vector diff --git a/pythonx/compose/material3/__init__.pyi b/pythonx/compose/material3/__init__.pyi index fbfde90..eb1635d 100644 --- a/pythonx/compose/material3/__init__.pyi +++ b/pythonx/compose/material3/__init__.pyi @@ -1,4 +1,4 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t import pythonx.compose.foundation import pythonx.compose.foundation.text.input @@ -14,6 +14,11 @@ import pythonx.compose.material.icons as _alias_0 DefaultIcons = _alias_0.Icons.Default +class _BottomAppBarState_callable_module(_t.Protocol): + """Kotlin: the function BottomAppBarState, and the members of the module BottomAppBarState""" + def __call__(self, initial_height_offset_limit: float, initial_height_offset: float, initial_content_offset: float) -> _t.Any: ... + Saver: _t.Any + class _ColorScheme_contentColorFor(_t.Protocol): def __call__(self, background_color: _t.Any) -> _t.Any: ... @@ -74,7 +79,7 @@ class BottomAppBarScrollBehavior: class BottomSheetScaffoldState: """Kotlin: androidx.compose.material3.BottomSheetScaffoldState""" - def __init__(self, bottomSheetState: SheetState, snackbarHostState: SnackbarHostState) -> None: + def __init__(self, bottom_sheet_state: SheetState, snackbar_host_state: SnackbarHostState) -> None: """Kotlin constructor: androidx.compose.material3.BottomSheetScaffoldState (androidx.compose.material3.BottomSheetScaffoldState(bottomSheetState: androidx.compose.material3.SheetState, snackbarHostState: androidx.compose.material3.SnackbarHostState): androidx.compose.material3.BottomSheetScaffoldState)""" ... @property @@ -88,7 +93,7 @@ class BottomSheetScaffoldState: class ButtonColors: """Kotlin: androidx.compose.material3.ButtonColors""" - def __init__(self, containerColor: _t.Any, contentColor: _t.Any, disabledContainerColor: _t.Any, disabledContentColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, content_color: _t.Any, disabled_container_color: _t.Any, disabled_content_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.ButtonColors (androidx.compose.material3.ButtonColors(containerColor: androidx.compose.ui.graphics.Color, contentColor: androidx.compose.ui.graphics.Color, disabledContainerColor: androidx.compose.ui.graphics.Color, disabledContentColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.ButtonColors)""" ... @property @@ -113,7 +118,7 @@ class ButtonElevation: class CardColors: """Kotlin: androidx.compose.material3.CardColors""" - def __init__(self, containerColor: _t.Any, contentColor: _t.Any, disabledContainerColor: _t.Any, disabledContentColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, content_color: _t.Any, disabled_container_color: _t.Any, disabled_content_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.CardColors (androidx.compose.material3.CardColors(containerColor: androidx.compose.ui.graphics.Color, contentColor: androidx.compose.ui.graphics.Color, disabledContainerColor: androidx.compose.ui.graphics.Color, disabledContentColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.CardColors)""" ... @property @@ -138,7 +143,7 @@ class CardElevation: class CheckboxColors: """Kotlin: androidx.compose.material3.CheckboxColors""" - def __init__(self, checkedCheckmarkColor: _t.Any, uncheckedCheckmarkColor: _t.Any, checkedBoxColor: _t.Any, uncheckedBoxColor: _t.Any, disabledCheckedBoxColor: _t.Any, disabledUncheckedBoxColor: _t.Any, disabledIndeterminateBoxColor: _t.Any, checkedBorderColor: _t.Any, uncheckedBorderColor: _t.Any, disabledBorderColor: _t.Any, disabledUncheckedBorderColor: _t.Any, disabledIndeterminateBorderColor: _t.Any) -> None: + def __init__(self, checked_checkmark_color: _t.Any, unchecked_checkmark_color: _t.Any, checked_box_color: _t.Any, unchecked_box_color: _t.Any, disabled_checked_box_color: _t.Any, disabled_unchecked_box_color: _t.Any, disabled_indeterminate_box_color: _t.Any, checked_border_color: _t.Any, unchecked_border_color: _t.Any, disabled_border_color: _t.Any, disabled_unchecked_border_color: _t.Any, disabled_indeterminate_border_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.CheckboxColors (androidx.compose.material3.CheckboxColors(checkedCheckmarkColor: androidx.compose.ui.graphics.Color, uncheckedCheckmarkColor: androidx.compose.ui.graphics.Color, checkedBoxColor: androidx.compose.ui.graphics.Color, uncheckedBoxColor: androidx.compose.ui.graphics.Color, disabledCheckedBoxColor: androidx.compose.ui.graphics.Color, disabledUncheckedBoxColor: androidx.compose.ui.graphics.Color, disabledIndeterminateBoxColor: androidx.compose.ui.graphics.Color, checkedBorderColor: androidx.compose.ui.graphics.Color, uncheckedBorderColor: androidx.compose.ui.graphics.Color, disabledBorderColor: androidx.compose.ui.graphics.Color, disabledUncheckedBorderColor: androidx.compose.ui.graphics.Color, disabledIndeterminateBorderColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.CheckboxColors)""" ... @property @@ -192,7 +197,7 @@ class CheckboxColors: class ChipColors: """Kotlin: androidx.compose.material3.ChipColors""" - def __init__(self, containerColor: _t.Any, labelColor: _t.Any, leadingIconContentColor: _t.Any, trailingIconContentColor: _t.Any, disabledContainerColor: _t.Any, disabledLabelColor: _t.Any, disabledLeadingIconContentColor: _t.Any, disabledTrailingIconContentColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, label_color: _t.Any, leading_icon_content_color: _t.Any, trailing_icon_content_color: _t.Any, disabled_container_color: _t.Any, disabled_label_color: _t.Any, disabled_leading_icon_content_color: _t.Any, disabled_trailing_icon_content_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.ChipColors (androidx.compose.material3.ChipColors(containerColor: androidx.compose.ui.graphics.Color, labelColor: androidx.compose.ui.graphics.Color, leadingIconContentColor: androidx.compose.ui.graphics.Color, trailingIconContentColor: androidx.compose.ui.graphics.Color, disabledContainerColor: androidx.compose.ui.graphics.Color, disabledLabelColor: androidx.compose.ui.graphics.Color, disabledLeadingIconContentColor: androidx.compose.ui.graphics.Color, disabledTrailingIconContentColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.ChipColors)""" ... @property @@ -230,7 +235,7 @@ class ChipColors: class ChipElevation: """Kotlin: androidx.compose.material3.ChipElevation""" - def __init__(self, elevation: float, pressedElevation: float, focusedElevation: float, hoveredElevation: float, draggedElevation: float, disabledElevation: float) -> None: + def __init__(self, elevation: float, pressed_elevation: float, focused_elevation: float, hovered_elevation: float, dragged_elevation: float, disabled_elevation: float) -> None: """Kotlin constructor: androidx.compose.material3.ChipElevation (androidx.compose.material3.ChipElevation(elevation: androidx.compose.ui.unit.Dp, pressedElevation: androidx.compose.ui.unit.Dp, focusedElevation: androidx.compose.ui.unit.Dp, hoveredElevation: androidx.compose.ui.unit.Dp, draggedElevation: androidx.compose.ui.unit.Dp, disabledElevation: androidx.compose.ui.unit.Dp): androidx.compose.material3.ChipElevation)""" ... @property @@ -261,11 +266,11 @@ class ChipElevation: class ColorScheme: """Kotlin: androidx.compose.material3.ColorScheme""" @_t.overload - def __init__(self, primary: _t.Any, onPrimary: _t.Any, primaryContainer: _t.Any, onPrimaryContainer: _t.Any, inversePrimary: _t.Any, secondary: _t.Any, onSecondary: _t.Any, secondaryContainer: _t.Any, onSecondaryContainer: _t.Any, tertiary: _t.Any, onTertiary: _t.Any, tertiaryContainer: _t.Any, onTertiaryContainer: _t.Any, background: _t.Any, onBackground: _t.Any, surface: _t.Any, onSurface: _t.Any, surfaceVariant: _t.Any, onSurfaceVariant: _t.Any, surfaceTint: _t.Any, inverseSurface: _t.Any, inverseOnSurface: _t.Any, error: _t.Any, onError: _t.Any, errorContainer: _t.Any, onErrorContainer: _t.Any, outline: _t.Any, outlineVariant: _t.Any, scrim: _t.Any) -> None: ... + def __init__(self, primary: _t.Any, on_primary: _t.Any, primary_container: _t.Any, on_primary_container: _t.Any, inverse_primary: _t.Any, secondary: _t.Any, on_secondary: _t.Any, secondary_container: _t.Any, on_secondary_container: _t.Any, tertiary: _t.Any, on_tertiary: _t.Any, tertiary_container: _t.Any, on_tertiary_container: _t.Any, background: _t.Any, on_background: _t.Any, surface: _t.Any, on_surface: _t.Any, surface_variant: _t.Any, on_surface_variant: _t.Any, surface_tint: _t.Any, inverse_surface: _t.Any, inverse_on_surface: _t.Any, error: _t.Any, on_error: _t.Any, error_container: _t.Any, on_error_container: _t.Any, outline: _t.Any, outline_variant: _t.Any, scrim: _t.Any) -> None: ... @_t.overload - def __init__(self, primary: _t.Any, onPrimary: _t.Any, primaryContainer: _t.Any, onPrimaryContainer: _t.Any, inversePrimary: _t.Any, secondary: _t.Any, onSecondary: _t.Any, secondaryContainer: _t.Any, onSecondaryContainer: _t.Any, tertiary: _t.Any, onTertiary: _t.Any, tertiaryContainer: _t.Any, onTertiaryContainer: _t.Any, background: _t.Any, onBackground: _t.Any, surface: _t.Any, onSurface: _t.Any, surfaceVariant: _t.Any, onSurfaceVariant: _t.Any, surfaceTint: _t.Any, inverseSurface: _t.Any, inverseOnSurface: _t.Any, error: _t.Any, onError: _t.Any, errorContainer: _t.Any, onErrorContainer: _t.Any, outline: _t.Any, outlineVariant: _t.Any, scrim: _t.Any, surfaceBright: _t.Any, surfaceDim: _t.Any, surfaceContainer: _t.Any, surfaceContainerHigh: _t.Any, surfaceContainerHighest: _t.Any, surfaceContainerLow: _t.Any, surfaceContainerLowest: _t.Any) -> None: ... # type: ignore[overload-cannot-match] + def __init__(self, primary: _t.Any, on_primary: _t.Any, primary_container: _t.Any, on_primary_container: _t.Any, inverse_primary: _t.Any, secondary: _t.Any, on_secondary: _t.Any, secondary_container: _t.Any, on_secondary_container: _t.Any, tertiary: _t.Any, on_tertiary: _t.Any, tertiary_container: _t.Any, on_tertiary_container: _t.Any, background: _t.Any, on_background: _t.Any, surface: _t.Any, on_surface: _t.Any, surface_variant: _t.Any, on_surface_variant: _t.Any, surface_tint: _t.Any, inverse_surface: _t.Any, inverse_on_surface: _t.Any, error: _t.Any, on_error: _t.Any, error_container: _t.Any, on_error_container: _t.Any, outline: _t.Any, outline_variant: _t.Any, scrim: _t.Any, surface_bright: _t.Any, surface_dim: _t.Any, surface_container: _t.Any, surface_container_high: _t.Any, surface_container_highest: _t.Any, surface_container_low: _t.Any, surface_container_lowest: _t.Any) -> None: ... # type: ignore[overload-cannot-match] @_t.overload - def __init__(self, primary: _t.Any, onPrimary: _t.Any, primaryContainer: _t.Any, onPrimaryContainer: _t.Any, inversePrimary: _t.Any, secondary: _t.Any, onSecondary: _t.Any, secondaryContainer: _t.Any, onSecondaryContainer: _t.Any, tertiary: _t.Any, onTertiary: _t.Any, tertiaryContainer: _t.Any, onTertiaryContainer: _t.Any, background: _t.Any, onBackground: _t.Any, surface: _t.Any, onSurface: _t.Any, surfaceVariant: _t.Any, onSurfaceVariant: _t.Any, surfaceTint: _t.Any, inverseSurface: _t.Any, inverseOnSurface: _t.Any, error: _t.Any, onError: _t.Any, errorContainer: _t.Any, onErrorContainer: _t.Any, outline: _t.Any, outlineVariant: _t.Any, scrim: _t.Any, surfaceBright: _t.Any, surfaceDim: _t.Any, surfaceContainer: _t.Any, surfaceContainerHigh: _t.Any, surfaceContainerHighest: _t.Any, surfaceContainerLow: _t.Any, surfaceContainerLowest: _t.Any, primaryFixed: _t.Any, primaryFixedDim: _t.Any, onPrimaryFixed: _t.Any, onPrimaryFixedVariant: _t.Any, secondaryFixed: _t.Any, secondaryFixedDim: _t.Any, onSecondaryFixed: _t.Any, onSecondaryFixedVariant: _t.Any, tertiaryFixed: _t.Any, tertiaryFixedDim: _t.Any, onTertiaryFixed: _t.Any, onTertiaryFixedVariant: _t.Any) -> None: ... # type: ignore[overload-cannot-match] + def __init__(self, primary: _t.Any, on_primary: _t.Any, primary_container: _t.Any, on_primary_container: _t.Any, inverse_primary: _t.Any, secondary: _t.Any, on_secondary: _t.Any, secondary_container: _t.Any, on_secondary_container: _t.Any, tertiary: _t.Any, on_tertiary: _t.Any, tertiary_container: _t.Any, on_tertiary_container: _t.Any, background: _t.Any, on_background: _t.Any, surface: _t.Any, on_surface: _t.Any, surface_variant: _t.Any, on_surface_variant: _t.Any, surface_tint: _t.Any, inverse_surface: _t.Any, inverse_on_surface: _t.Any, error: _t.Any, on_error: _t.Any, error_container: _t.Any, on_error_container: _t.Any, outline: _t.Any, outline_variant: _t.Any, scrim: _t.Any, surface_bright: _t.Any, surface_dim: _t.Any, surface_container: _t.Any, surface_container_high: _t.Any, surface_container_highest: _t.Any, surface_container_low: _t.Any, surface_container_lowest: _t.Any, primary_fixed: _t.Any, primary_fixed_dim: _t.Any, on_primary_fixed: _t.Any, on_primary_fixed_variant: _t.Any, secondary_fixed: _t.Any, secondary_fixed_dim: _t.Any, on_secondary_fixed: _t.Any, on_secondary_fixed_variant: _t.Any, tertiary_fixed: _t.Any, tertiary_fixed_dim: _t.Any, on_tertiary_fixed: _t.Any, on_tertiary_fixed_variant: _t.Any) -> None: ... # type: ignore[overload-cannot-match] content_color_for: _t.ClassVar[_ColorScheme_contentColorFor] content_color_for__ColorScheme_Color: _t.ClassVar[_ColorScheme_contentColorFor__ColorScheme_Color] surface_color_at_elevation: _t.ClassVar[_ColorScheme_surfaceColorAtElevation] @@ -464,7 +469,7 @@ class ColorScheme: class DatePickerColors: """Kotlin: androidx.compose.material3.DatePickerColors""" - def __init__(self, containerColor: _t.Any, titleContentColor: _t.Any, headlineContentColor: _t.Any, weekdayContentColor: _t.Any, subheadContentColor: _t.Any, navigationContentColor: _t.Any, yearContentColor: _t.Any, disabledYearContentColor: _t.Any, currentYearContentColor: _t.Any, selectedYearContentColor: _t.Any, disabledSelectedYearContentColor: _t.Any, selectedYearContainerColor: _t.Any, disabledSelectedYearContainerColor: _t.Any, dayContentColor: _t.Any, disabledDayContentColor: _t.Any, selectedDayContentColor: _t.Any, disabledSelectedDayContentColor: _t.Any, selectedDayContainerColor: _t.Any, disabledSelectedDayContainerColor: _t.Any, todayContentColor: _t.Any, todayDateBorderColor: _t.Any, dayInSelectionRangeContainerColor: _t.Any, dayInSelectionRangeContentColor: _t.Any, dividerColor: _t.Any, dateTextFieldColors: TextFieldColors) -> None: + def __init__(self, container_color: _t.Any, title_content_color: _t.Any, headline_content_color: _t.Any, weekday_content_color: _t.Any, subhead_content_color: _t.Any, navigation_content_color: _t.Any, year_content_color: _t.Any, disabled_year_content_color: _t.Any, current_year_content_color: _t.Any, selected_year_content_color: _t.Any, disabled_selected_year_content_color: _t.Any, selected_year_container_color: _t.Any, disabled_selected_year_container_color: _t.Any, day_content_color: _t.Any, disabled_day_content_color: _t.Any, selected_day_content_color: _t.Any, disabled_selected_day_content_color: _t.Any, selected_day_container_color: _t.Any, disabled_selected_day_container_color: _t.Any, today_content_color: _t.Any, today_date_border_color: _t.Any, day_in_selection_range_container_color: _t.Any, day_in_selection_range_content_color: _t.Any, divider_color: _t.Any, date_text_field_colors: TextFieldColors) -> None: """Kotlin constructor: androidx.compose.material3.DatePickerColors (androidx.compose.material3.DatePickerColors(containerColor: androidx.compose.ui.graphics.Color, titleContentColor: androidx.compose.ui.graphics.Color, headlineContentColor: androidx.compose.ui.graphics.Color, weekdayContentColor: androidx.compose.ui.graphics.Color, subheadContentColor: androidx.compose.ui.graphics.Color, navigationContentColor: androidx.compose.ui.graphics.Color, yearContentColor: androidx.compose.ui.graphics.Color, disabledYearContentColor: androidx.compose.ui.graphics.Color, currentYearContentColor: androidx.compose.ui.graphics.Color, selectedYearContentColor: androidx.compose.ui.graphics.Color, disabledSelectedYearContentColor: androidx.compose.ui.graphics.Color, selectedYearContainerColor: androidx.compose.ui.graphics.Color, disabledSelectedYearContainerColor: androidx.compose.ui.graphics.Color, dayContentColor: androidx.compose.ui.graphics.Color, disabledDayContentColor: androidx.compose.ui.graphics.Color, selectedDayContentColor: androidx.compose.ui.graphics.Color, disabledSelectedDayContentColor: androidx.compose.ui.graphics.Color, selectedDayContainerColor: androidx.compose.ui.graphics.Color, disabledSelectedDayContainerColor: androidx.compose.ui.graphics.Color, todayContentColor: androidx.compose.ui.graphics.Color, todayDateBorderColor: androidx.compose.ui.graphics.Color, dayInSelectionRangeContainerColor: androidx.compose.ui.graphics.Color, dayInSelectionRangeContentColor: androidx.compose.ui.graphics.Color, dividerColor: androidx.compose.ui.graphics.Color, dateTextFieldColors: androidx.compose.material3.TextFieldColors): androidx.compose.material3.DatePickerColors)""" ... @property @@ -627,7 +632,7 @@ class DateRangePickerState: class DefaultTooltipCaretShape(pythonx.compose.ui.graphics.Shape): """Kotlin: androidx.compose.material3.DefaultTooltipCaretShape""" - def __init__(self, caretSize: _t.Any = ...) -> None: + def __init__(self, caret_size: _t.Any = ...) -> None: """Kotlin constructor: androidx.compose.material3.DefaultTooltipCaretShape (androidx.compose.material3.DefaultTooltipCaretShape(caretSize: androidx.compose.ui.unit.DpSize): androidx.compose.material3.DefaultTooltipCaretShape)""" ... @property @@ -637,7 +642,7 @@ class DefaultTooltipCaretShape(pythonx.compose.ui.graphics.Shape): class DragHandleColors: """Kotlin: androidx.compose.material3.DragHandleColors""" - def __init__(self, color: _t.Any, pressedColor: _t.Any, draggedColor: _t.Any) -> None: + def __init__(self, color: _t.Any, pressed_color: _t.Any, dragged_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.DragHandleColors (androidx.compose.material3.DragHandleColors(color: androidx.compose.ui.graphics.Color, pressedColor: androidx.compose.ui.graphics.Color, draggedColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.DragHandleColors)""" ... @property @@ -655,7 +660,7 @@ class DragHandleColors: class DragHandleShapes: """Kotlin: androidx.compose.material3.DragHandleShapes""" - def __init__(self, shape: pythonx.compose.ui.graphics.Shape, pressedShape: pythonx.compose.ui.graphics.Shape, draggedShape: pythonx.compose.ui.graphics.Shape) -> None: + def __init__(self, shape: pythonx.compose.ui.graphics.Shape, pressed_shape: pythonx.compose.ui.graphics.Shape, dragged_shape: pythonx.compose.ui.graphics.Shape) -> None: """Kotlin constructor: androidx.compose.material3.DragHandleShapes (androidx.compose.material3.DragHandleShapes(shape: androidx.compose.ui.graphics.Shape, pressedShape: androidx.compose.ui.graphics.Shape, draggedShape: androidx.compose.ui.graphics.Shape): androidx.compose.material3.DragHandleShapes)""" ... @property @@ -673,7 +678,7 @@ class DragHandleShapes: class DragHandleSizes: """Kotlin: androidx.compose.material3.DragHandleSizes""" - def __init__(self, size: _t.Any, pressedSize: _t.Any, draggedSize: _t.Any) -> None: + def __init__(self, size: _t.Any, pressed_size: _t.Any, dragged_size: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.DragHandleSizes (androidx.compose.material3.DragHandleSizes(size: androidx.compose.ui.unit.DpSize, pressedSize: androidx.compose.ui.unit.DpSize, draggedSize: androidx.compose.ui.unit.DpSize): androidx.compose.material3.DragHandleSizes)""" ... @property @@ -691,7 +696,7 @@ class DragHandleSizes: class DrawerState: """Kotlin: androidx.compose.material3.DrawerState""" - def __init__(self, initialValue: DrawerValue, confirmStateChange: _t.Callable[[DrawerValue], bool] = ...) -> None: + def __init__(self, initial_value: DrawerValue, confirm_state_change: _t.Callable[[DrawerValue], bool] = ...) -> None: """Kotlin constructor: androidx.compose.material3.DrawerState (androidx.compose.material3.DrawerState(initialValue: androidx.compose.material3.DrawerValue, confirmStateChange: kotlin.Function1): androidx.compose.material3.DrawerState)""" ... @property @@ -730,7 +735,7 @@ class FloatingActionButtonElevation: class IconButtonColors: """Kotlin: androidx.compose.material3.IconButtonColors""" - def __init__(self, containerColor: _t.Any, contentColor: _t.Any, disabledContainerColor: _t.Any, disabledContentColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, content_color: _t.Any, disabled_container_color: _t.Any, disabled_content_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.IconButtonColors (androidx.compose.material3.IconButtonColors(containerColor: androidx.compose.ui.graphics.Color, contentColor: androidx.compose.ui.graphics.Color, disabledContainerColor: androidx.compose.ui.graphics.Color, disabledContentColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.IconButtonColors)""" ... @property @@ -752,7 +757,7 @@ class IconButtonColors: class IconToggleButtonColors: """Kotlin: androidx.compose.material3.IconToggleButtonColors""" - def __init__(self, containerColor: _t.Any, contentColor: _t.Any, disabledContainerColor: _t.Any, disabledContentColor: _t.Any, checkedContainerColor: _t.Any, checkedContentColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, content_color: _t.Any, disabled_container_color: _t.Any, disabled_content_color: _t.Any, checked_container_color: _t.Any, checked_content_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.IconToggleButtonColors (androidx.compose.material3.IconToggleButtonColors(containerColor: androidx.compose.ui.graphics.Color, contentColor: androidx.compose.ui.graphics.Color, disabledContainerColor: androidx.compose.ui.graphics.Color, disabledContentColor: androidx.compose.ui.graphics.Color, checkedContainerColor: androidx.compose.ui.graphics.Color, checkedContentColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.IconToggleButtonColors)""" ... @property @@ -782,7 +787,7 @@ class IconToggleButtonColors: class ListItemColors: """Kotlin: androidx.compose.material3.ListItemColors""" - def __init__(self, containerColor: _t.Any, headlineColor: _t.Any, leadingIconColor: _t.Any, overlineColor: _t.Any, supportingTextColor: _t.Any, trailingIconColor: _t.Any, disabledHeadlineColor: _t.Any, disabledLeadingIconColor: _t.Any, disabledTrailingIconColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, headline_color: _t.Any, leading_icon_color: _t.Any, overline_color: _t.Any, supporting_text_color: _t.Any, trailing_icon_color: _t.Any, disabled_headline_color: _t.Any, disabled_leading_icon_color: _t.Any, disabled_trailing_icon_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.ListItemColors (androidx.compose.material3.ListItemColors(containerColor: androidx.compose.ui.graphics.Color, headlineColor: androidx.compose.ui.graphics.Color, leadingIconColor: androidx.compose.ui.graphics.Color, overlineColor: androidx.compose.ui.graphics.Color, supportingTextColor: androidx.compose.ui.graphics.Color, trailingIconColor: androidx.compose.ui.graphics.Color, disabledHeadlineColor: androidx.compose.ui.graphics.Color, disabledLeadingIconColor: androidx.compose.ui.graphics.Color, disabledTrailingIconColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.ListItemColors)""" ... @property @@ -824,7 +829,7 @@ class ListItemColors: class MenuItemColors: """Kotlin: androidx.compose.material3.MenuItemColors""" - def __init__(self, textColor: _t.Any, leadingIconColor: _t.Any, trailingIconColor: _t.Any, disabledTextColor: _t.Any, disabledLeadingIconColor: _t.Any, disabledTrailingIconColor: _t.Any) -> None: + def __init__(self, text_color: _t.Any, leading_icon_color: _t.Any, trailing_icon_color: _t.Any, disabled_text_color: _t.Any, disabled_leading_icon_color: _t.Any, disabled_trailing_icon_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.MenuItemColors (androidx.compose.material3.MenuItemColors(textColor: androidx.compose.ui.graphics.Color, leadingIconColor: androidx.compose.ui.graphics.Color, trailingIconColor: androidx.compose.ui.graphics.Color, disabledTextColor: androidx.compose.ui.graphics.Color, disabledLeadingIconColor: androidx.compose.ui.graphics.Color, disabledTrailingIconColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.MenuItemColors)""" ... @property @@ -854,7 +859,7 @@ class MenuItemColors: class ModalBottomSheetProperties: """Kotlin: androidx.compose.material3.ModalBottomSheetProperties""" - def __init__(self, shouldDismissOnBackPress: bool = ..., shouldDismissOnClickOutside: bool = ...) -> None: + def __init__(self, should_dismiss_on_back_press: bool = ..., should_dismiss_on_click_outside: bool = ...) -> None: """Kotlin constructor: androidx.compose.material3.ModalBottomSheetProperties (androidx.compose.material3.ModalBottomSheetProperties(shouldDismissOnBackPress: kotlin.Boolean, shouldDismissOnClickOutside: kotlin.Boolean): androidx.compose.material3.ModalBottomSheetProperties)""" ... @property @@ -868,7 +873,7 @@ class ModalBottomSheetProperties: class ModalWideNavigationRailProperties: """Kotlin: androidx.compose.material3.ModalWideNavigationRailProperties""" - def __init__(self, shouldDismissOnBackPress: bool = ...) -> None: + def __init__(self, should_dismiss_on_back_press: bool = ...) -> None: """Kotlin constructor: androidx.compose.material3.ModalWideNavigationRailProperties (androidx.compose.material3.ModalWideNavigationRailProperties(shouldDismissOnBackPress: kotlin.Boolean): androidx.compose.material3.ModalWideNavigationRailProperties)""" ... @property @@ -883,7 +888,7 @@ class MultiChoiceSegmentedButtonRowScope: class NavigationBarItemColors: """Kotlin: androidx.compose.material3.NavigationBarItemColors""" - def __init__(self, selectedIconColor: _t.Any, selectedTextColor: _t.Any, selectedIndicatorColor: _t.Any, unselectedIconColor: _t.Any, unselectedTextColor: _t.Any, disabledIconColor: _t.Any, disabledTextColor: _t.Any) -> None: + def __init__(self, selected_icon_color: _t.Any, selected_text_color: _t.Any, selected_indicator_color: _t.Any, unselected_icon_color: _t.Any, unselected_text_color: _t.Any, disabled_icon_color: _t.Any, disabled_text_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.NavigationBarItemColors (androidx.compose.material3.NavigationBarItemColors(selectedIconColor: androidx.compose.ui.graphics.Color, selectedTextColor: androidx.compose.ui.graphics.Color, selectedIndicatorColor: androidx.compose.ui.graphics.Color, unselectedIconColor: androidx.compose.ui.graphics.Color, unselectedTextColor: androidx.compose.ui.graphics.Color, disabledIconColor: androidx.compose.ui.graphics.Color, disabledTextColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.NavigationBarItemColors)""" ... @property @@ -920,7 +925,7 @@ class NavigationDrawerItemColors: class NavigationItemColors: """Kotlin: androidx.compose.material3.NavigationItemColors""" - def __init__(self, selectedIconColor: _t.Any, selectedTextColor: _t.Any, selectedIndicatorColor: _t.Any, unselectedIconColor: _t.Any, unselectedTextColor: _t.Any, disabledIconColor: _t.Any, disabledTextColor: _t.Any) -> None: + def __init__(self, selected_icon_color: _t.Any, selected_text_color: _t.Any, selected_indicator_color: _t.Any, unselected_icon_color: _t.Any, unselected_text_color: _t.Any, disabled_icon_color: _t.Any, disabled_text_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.NavigationItemColors (androidx.compose.material3.NavigationItemColors(selectedIconColor: androidx.compose.ui.graphics.Color, selectedTextColor: androidx.compose.ui.graphics.Color, selectedIndicatorColor: androidx.compose.ui.graphics.Color, unselectedIconColor: androidx.compose.ui.graphics.Color, unselectedTextColor: androidx.compose.ui.graphics.Color, disabledIconColor: androidx.compose.ui.graphics.Color, disabledTextColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.NavigationItemColors)""" ... @property @@ -954,7 +959,7 @@ class NavigationItemColors: class NavigationRailItemColors: """Kotlin: androidx.compose.material3.NavigationRailItemColors""" - def __init__(self, selectedIconColor: _t.Any, selectedTextColor: _t.Any, selectedIndicatorColor: _t.Any, unselectedIconColor: _t.Any, unselectedTextColor: _t.Any, disabledIconColor: _t.Any, disabledTextColor: _t.Any) -> None: + def __init__(self, selected_icon_color: _t.Any, selected_text_color: _t.Any, selected_indicator_color: _t.Any, unselected_icon_color: _t.Any, unselected_text_color: _t.Any, disabled_icon_color: _t.Any, disabled_text_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.NavigationRailItemColors (androidx.compose.material3.NavigationRailItemColors(selectedIconColor: androidx.compose.ui.graphics.Color, selectedTextColor: androidx.compose.ui.graphics.Color, selectedIndicatorColor: androidx.compose.ui.graphics.Color, unselectedIconColor: androidx.compose.ui.graphics.Color, unselectedTextColor: androidx.compose.ui.graphics.Color, disabledIconColor: androidx.compose.ui.graphics.Color, disabledTextColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.NavigationRailItemColors)""" ... @property @@ -988,7 +993,7 @@ class NavigationRailItemColors: class RadioButtonColors: """Kotlin: androidx.compose.material3.RadioButtonColors""" - def __init__(self, selectedColor: _t.Any, unselectedColor: _t.Any, disabledSelectedColor: _t.Any, disabledUnselectedColor: _t.Any) -> None: + def __init__(self, selected_color: _t.Any, unselected_color: _t.Any, disabled_selected_color: _t.Any, disabled_unselected_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.RadioButtonColors (androidx.compose.material3.RadioButtonColors(selectedColor: androidx.compose.ui.graphics.Color, unselectedColor: androidx.compose.ui.graphics.Color, disabledSelectedColor: androidx.compose.ui.graphics.Color, disabledUnselectedColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.RadioButtonColors)""" ... @property @@ -1033,7 +1038,7 @@ class RangeSliderState: class RichTooltipColors: """Kotlin: androidx.compose.material3.RichTooltipColors""" - def __init__(self, containerColor: _t.Any, contentColor: _t.Any, titleContentColor: _t.Any, actionContentColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, content_color: _t.Any, title_content_color: _t.Any, action_content_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.RichTooltipColors (androidx.compose.material3.RichTooltipColors(containerColor: androidx.compose.ui.graphics.Color, contentColor: androidx.compose.ui.graphics.Color, titleContentColor: androidx.compose.ui.graphics.Color, actionContentColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.RichTooltipColors)""" ... @property @@ -1055,7 +1060,7 @@ class RichTooltipColors: class RippleConfiguration: """Kotlin: androidx.compose.material3.RippleConfiguration""" - def __init__(self, color: _t.Any = ..., rippleAlpha: _t.Any | None = ...) -> None: + def __init__(self, color: _t.Any = ..., ripple_alpha: _t.Any | None = ...) -> None: """Kotlin constructor: androidx.compose.material3.RippleConfiguration (androidx.compose.material3.RippleConfiguration(color: androidx.compose.ui.graphics.Color, rippleAlpha: androidx.compose.material.ripple.RippleAlpha?): androidx.compose.material3.RippleConfiguration)""" ... @property @@ -1070,9 +1075,9 @@ class RippleConfiguration: class SearchBarColors: """Kotlin: androidx.compose.material3.SearchBarColors""" @_t.overload - def __init__(self, containerColor: _t.Any, dividerColor: _t.Any) -> None: ... + def __init__(self, container_color: _t.Any, divider_color: _t.Any) -> None: ... @_t.overload - def __init__(self, containerColor: _t.Any, dividerColor: _t.Any, inputFieldColors: TextFieldColors) -> None: ... # type: ignore[overload-cannot-match] + def __init__(self, container_color: _t.Any, divider_color: _t.Any, input_field_colors: TextFieldColors) -> None: ... # type: ignore[overload-cannot-match] @property def container_color(self) -> _t.Any: """Kotlin property: androidx.compose.material3.SearchBarColors.containerColor (androidx.compose.material3.SearchBarColors.containerColor: androidx.compose.ui.graphics.Color)""" @@ -1141,7 +1146,7 @@ class SearchBarValue: class SegmentedButtonColors: """Kotlin: androidx.compose.material3.SegmentedButtonColors""" - def __init__(self, activeContainerColor: _t.Any, activeContentColor: _t.Any, activeBorderColor: _t.Any, inactiveContainerColor: _t.Any, inactiveContentColor: _t.Any, inactiveBorderColor: _t.Any, disabledActiveContainerColor: _t.Any, disabledActiveContentColor: _t.Any, disabledActiveBorderColor: _t.Any, disabledInactiveContainerColor: _t.Any, disabledInactiveContentColor: _t.Any, disabledInactiveBorderColor: _t.Any) -> None: + def __init__(self, active_container_color: _t.Any, active_content_color: _t.Any, active_border_color: _t.Any, inactive_container_color: _t.Any, inactive_content_color: _t.Any, inactive_border_color: _t.Any, disabled_active_container_color: _t.Any, disabled_active_content_color: _t.Any, disabled_active_border_color: _t.Any, disabled_inactive_container_color: _t.Any, disabled_inactive_content_color: _t.Any, disabled_inactive_border_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.SegmentedButtonColors (androidx.compose.material3.SegmentedButtonColors(activeContainerColor: androidx.compose.ui.graphics.Color, activeContentColor: androidx.compose.ui.graphics.Color, activeBorderColor: androidx.compose.ui.graphics.Color, inactiveContainerColor: androidx.compose.ui.graphics.Color, inactiveContentColor: androidx.compose.ui.graphics.Color, inactiveBorderColor: androidx.compose.ui.graphics.Color, disabledActiveContainerColor: androidx.compose.ui.graphics.Color, disabledActiveContentColor: androidx.compose.ui.graphics.Color, disabledActiveBorderColor: androidx.compose.ui.graphics.Color, disabledInactiveContainerColor: androidx.compose.ui.graphics.Color, disabledInactiveContentColor: androidx.compose.ui.graphics.Color, disabledInactiveBorderColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.SegmentedButtonColors)""" ... @property @@ -1195,13 +1200,13 @@ class SegmentedButtonColors: class SelectableChipColors: """Kotlin: androidx.compose.material3.SelectableChipColors""" - def __init__(self, containerColor: _t.Any, labelColor: _t.Any, leadingIconColor: _t.Any, trailingIconColor: _t.Any, disabledContainerColor: _t.Any, disabledLabelColor: _t.Any, disabledLeadingIconColor: _t.Any, disabledTrailingIconColor: _t.Any, selectedContainerColor: _t.Any, disabledSelectedContainerColor: _t.Any, selectedLabelColor: _t.Any, selectedLeadingIconColor: _t.Any, selectedTrailingIconColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, label_color: _t.Any, leading_icon_color: _t.Any, trailing_icon_color: _t.Any, disabled_container_color: _t.Any, disabled_label_color: _t.Any, disabled_leading_icon_color: _t.Any, disabled_trailing_icon_color: _t.Any, selected_container_color: _t.Any, disabled_selected_container_color: _t.Any, selected_label_color: _t.Any, selected_leading_icon_color: _t.Any, selected_trailing_icon_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.SelectableChipColors (androidx.compose.material3.SelectableChipColors(containerColor: androidx.compose.ui.graphics.Color, labelColor: androidx.compose.ui.graphics.Color, leadingIconColor: androidx.compose.ui.graphics.Color, trailingIconColor: androidx.compose.ui.graphics.Color, disabledContainerColor: androidx.compose.ui.graphics.Color, disabledLabelColor: androidx.compose.ui.graphics.Color, disabledLeadingIconColor: androidx.compose.ui.graphics.Color, disabledTrailingIconColor: androidx.compose.ui.graphics.Color, selectedContainerColor: androidx.compose.ui.graphics.Color, disabledSelectedContainerColor: androidx.compose.ui.graphics.Color, selectedLabelColor: androidx.compose.ui.graphics.Color, selectedLeadingIconColor: androidx.compose.ui.graphics.Color, selectedTrailingIconColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.SelectableChipColors)""" ... class SelectableChipElevation: """Kotlin: androidx.compose.material3.SelectableChipElevation""" - def __init__(self, elevation: float, pressedElevation: float, focusedElevation: float, hoveredElevation: float, draggedElevation: float, disabledElevation: float) -> None: + def __init__(self, elevation: float, pressed_elevation: float, focused_elevation: float, hovered_elevation: float, dragged_elevation: float, disabled_elevation: float) -> None: """Kotlin constructor: androidx.compose.material3.SelectableChipElevation (androidx.compose.material3.SelectableChipElevation(elevation: androidx.compose.ui.unit.Dp, pressedElevation: androidx.compose.ui.unit.Dp, focusedElevation: androidx.compose.ui.unit.Dp, hoveredElevation: androidx.compose.ui.unit.Dp, draggedElevation: androidx.compose.ui.unit.Dp, disabledElevation: androidx.compose.ui.unit.Dp): androidx.compose.material3.SelectableChipElevation)""" ... @property @@ -1234,7 +1239,7 @@ class SelectableDates: class Shapes: """Kotlin: androidx.compose.material3.Shapes""" - def __init__(self, extraSmall: _t.Any = ..., small: _t.Any = ..., medium: _t.Any = ..., large: _t.Any = ..., extraLarge: _t.Any = ...) -> None: + def __init__(self, extra_small: _t.Any = ..., small: _t.Any = ..., medium: _t.Any = ..., large: _t.Any = ..., extra_large: _t.Any = ...) -> None: """Kotlin constructor: androidx.compose.material3.Shapes (androidx.compose.material3.Shapes(extraSmall: androidx.compose.foundation.shape.CornerBasedShape, small: androidx.compose.foundation.shape.CornerBasedShape, medium: androidx.compose.foundation.shape.CornerBasedShape, large: androidx.compose.foundation.shape.CornerBasedShape, extraLarge: androidx.compose.foundation.shape.CornerBasedShape): androidx.compose.material3.Shapes)""" ... @property @@ -1260,7 +1265,7 @@ class Shapes: class SheetState: """Kotlin: androidx.compose.material3.SheetState""" - def __init__(self, skipPartiallyExpanded: bool, positionalThreshold: _t.Callable[[], float], velocityThreshold: _t.Callable[[], float], initialValue: SheetValue = ..., confirmValueChange: _t.Callable[[SheetValue], bool] = ..., skipHiddenState: bool = ...) -> None: + def __init__(self, skip_partially_expanded: bool, positional_threshold: _t.Callable[[], float], velocity_threshold: _t.Callable[[], float], initial_value: SheetValue = ..., confirm_value_change: _t.Callable[[SheetValue], bool] = ..., skip_hidden_state: bool = ...) -> None: """Kotlin constructor: androidx.compose.material3.SheetState (androidx.compose.material3.SheetState(skipPartiallyExpanded: kotlin.Boolean, positionalThreshold: kotlin.Function0, velocityThreshold: kotlin.Function0, initialValue: androidx.compose.material3.SheetValue, confirmValueChange: kotlin.Function1, skipHiddenState: kotlin.Boolean): androidx.compose.material3.SheetState)""" ... @property @@ -1298,7 +1303,7 @@ class SingleChoiceSegmentedButtonRowScope: class SliderColors: """Kotlin: androidx.compose.material3.SliderColors""" - def __init__(self, thumbColor: _t.Any, activeTrackColor: _t.Any, activeTickColor: _t.Any, inactiveTrackColor: _t.Any, inactiveTickColor: _t.Any, disabledThumbColor: _t.Any, disabledActiveTrackColor: _t.Any, disabledActiveTickColor: _t.Any, disabledInactiveTrackColor: _t.Any, disabledInactiveTickColor: _t.Any) -> None: + def __init__(self, thumb_color: _t.Any, active_track_color: _t.Any, active_tick_color: _t.Any, inactive_track_color: _t.Any, inactive_tick_color: _t.Any, disabled_thumb_color: _t.Any, disabled_active_track_color: _t.Any, disabled_active_tick_color: _t.Any, disabled_inactive_track_color: _t.Any, disabled_inactive_tick_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.SliderColors (androidx.compose.material3.SliderColors(thumbColor: androidx.compose.ui.graphics.Color, activeTrackColor: androidx.compose.ui.graphics.Color, activeTickColor: androidx.compose.ui.graphics.Color, inactiveTrackColor: androidx.compose.ui.graphics.Color, inactiveTickColor: androidx.compose.ui.graphics.Color, disabledThumbColor: androidx.compose.ui.graphics.Color, disabledActiveTrackColor: androidx.compose.ui.graphics.Color, disabledActiveTickColor: androidx.compose.ui.graphics.Color, disabledInactiveTrackColor: androidx.compose.ui.graphics.Color, disabledInactiveTickColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.SliderColors)""" ... @property @@ -1415,9 +1420,9 @@ class SnackbarVisuals: class SwipeToDismissBoxState: """Kotlin: androidx.compose.material3.SwipeToDismissBoxState""" @_t.overload - def __init__(self, initialValue: SwipeToDismissBoxValue, density: _t.Any, confirmValueChange: _t.Callable[[SwipeToDismissBoxValue], bool] = ..., *, positionalThreshold: _t.Callable[[float], float]) -> None: ... + def __init__(self, initial_value: SwipeToDismissBoxValue, density: _t.Any, confirm_value_change: _t.Callable[[SwipeToDismissBoxValue], bool] = ..., *, positional_threshold: _t.Callable[[float], float]) -> None: ... @_t.overload - def __init__(self, initialValue: SwipeToDismissBoxValue, positionalThreshold: _t.Callable[[float], float]) -> None: ... # type: ignore[overload-cannot-match] + def __init__(self, initial_value: SwipeToDismissBoxValue, positional_threshold: _t.Callable[[float], float]) -> None: ... # type: ignore[overload-cannot-match] @property def current_value(self) -> SwipeToDismissBoxValue: """Kotlin property: androidx.compose.material3.SwipeToDismissBoxState.currentValue (androidx.compose.material3.SwipeToDismissBoxState.currentValue: androidx.compose.material3.SwipeToDismissBoxValue)""" @@ -1444,7 +1449,7 @@ class SwipeToDismissBoxValue: class SwitchColors: """Kotlin: androidx.compose.material3.SwitchColors""" - def __init__(self, checkedThumbColor: _t.Any, checkedTrackColor: _t.Any, checkedBorderColor: _t.Any, checkedIconColor: _t.Any, uncheckedThumbColor: _t.Any, uncheckedTrackColor: _t.Any, uncheckedBorderColor: _t.Any, uncheckedIconColor: _t.Any, disabledCheckedThumbColor: _t.Any, disabledCheckedTrackColor: _t.Any, disabledCheckedBorderColor: _t.Any, disabledCheckedIconColor: _t.Any, disabledUncheckedThumbColor: _t.Any, disabledUncheckedTrackColor: _t.Any, disabledUncheckedBorderColor: _t.Any, disabledUncheckedIconColor: _t.Any) -> None: + def __init__(self, checked_thumb_color: _t.Any, checked_track_color: _t.Any, checked_border_color: _t.Any, checked_icon_color: _t.Any, unchecked_thumb_color: _t.Any, unchecked_track_color: _t.Any, unchecked_border_color: _t.Any, unchecked_icon_color: _t.Any, disabled_checked_thumb_color: _t.Any, disabled_checked_track_color: _t.Any, disabled_checked_border_color: _t.Any, disabled_checked_icon_color: _t.Any, disabled_unchecked_thumb_color: _t.Any, disabled_unchecked_track_color: _t.Any, disabled_unchecked_border_color: _t.Any, disabled_unchecked_icon_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.SwitchColors (androidx.compose.material3.SwitchColors(checkedThumbColor: androidx.compose.ui.graphics.Color, checkedTrackColor: androidx.compose.ui.graphics.Color, checkedBorderColor: androidx.compose.ui.graphics.Color, checkedIconColor: androidx.compose.ui.graphics.Color, uncheckedThumbColor: androidx.compose.ui.graphics.Color, uncheckedTrackColor: androidx.compose.ui.graphics.Color, uncheckedBorderColor: androidx.compose.ui.graphics.Color, uncheckedIconColor: androidx.compose.ui.graphics.Color, disabledCheckedThumbColor: androidx.compose.ui.graphics.Color, disabledCheckedTrackColor: androidx.compose.ui.graphics.Color, disabledCheckedBorderColor: androidx.compose.ui.graphics.Color, disabledCheckedIconColor: androidx.compose.ui.graphics.Color, disabledUncheckedThumbColor: androidx.compose.ui.graphics.Color, disabledUncheckedTrackColor: androidx.compose.ui.graphics.Color, disabledUncheckedBorderColor: androidx.compose.ui.graphics.Color, disabledUncheckedIconColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.SwitchColors)""" ... @property @@ -1536,7 +1541,7 @@ class TabPosition: class TextFieldColors: """Kotlin: androidx.compose.material3.TextFieldColors""" - def __init__(self, focusedTextColor: _t.Any, unfocusedTextColor: _t.Any, disabledTextColor: _t.Any, errorTextColor: _t.Any, focusedContainerColor: _t.Any, unfocusedContainerColor: _t.Any, disabledContainerColor: _t.Any, errorContainerColor: _t.Any, cursorColor: _t.Any, errorCursorColor: _t.Any, textSelectionColors: _t.Any, focusedIndicatorColor: _t.Any, unfocusedIndicatorColor: _t.Any, disabledIndicatorColor: _t.Any, errorIndicatorColor: _t.Any, focusedLeadingIconColor: _t.Any, unfocusedLeadingIconColor: _t.Any, disabledLeadingIconColor: _t.Any, errorLeadingIconColor: _t.Any, focusedTrailingIconColor: _t.Any, unfocusedTrailingIconColor: _t.Any, disabledTrailingIconColor: _t.Any, errorTrailingIconColor: _t.Any, focusedLabelColor: _t.Any, unfocusedLabelColor: _t.Any, disabledLabelColor: _t.Any, errorLabelColor: _t.Any, focusedPlaceholderColor: _t.Any, unfocusedPlaceholderColor: _t.Any, disabledPlaceholderColor: _t.Any, errorPlaceholderColor: _t.Any, focusedSupportingTextColor: _t.Any, unfocusedSupportingTextColor: _t.Any, disabledSupportingTextColor: _t.Any, errorSupportingTextColor: _t.Any, focusedPrefixColor: _t.Any, unfocusedPrefixColor: _t.Any, disabledPrefixColor: _t.Any, errorPrefixColor: _t.Any, focusedSuffixColor: _t.Any, unfocusedSuffixColor: _t.Any, disabledSuffixColor: _t.Any, errorSuffixColor: _t.Any) -> None: + def __init__(self, focused_text_color: _t.Any, unfocused_text_color: _t.Any, disabled_text_color: _t.Any, error_text_color: _t.Any, focused_container_color: _t.Any, unfocused_container_color: _t.Any, disabled_container_color: _t.Any, error_container_color: _t.Any, cursor_color: _t.Any, error_cursor_color: _t.Any, text_selection_colors: _t.Any, focused_indicator_color: _t.Any, unfocused_indicator_color: _t.Any, disabled_indicator_color: _t.Any, error_indicator_color: _t.Any, focused_leading_icon_color: _t.Any, unfocused_leading_icon_color: _t.Any, disabled_leading_icon_color: _t.Any, error_leading_icon_color: _t.Any, focused_trailing_icon_color: _t.Any, unfocused_trailing_icon_color: _t.Any, disabled_trailing_icon_color: _t.Any, error_trailing_icon_color: _t.Any, focused_label_color: _t.Any, unfocused_label_color: _t.Any, disabled_label_color: _t.Any, error_label_color: _t.Any, focused_placeholder_color: _t.Any, unfocused_placeholder_color: _t.Any, disabled_placeholder_color: _t.Any, error_placeholder_color: _t.Any, focused_supporting_text_color: _t.Any, unfocused_supporting_text_color: _t.Any, disabled_supporting_text_color: _t.Any, error_supporting_text_color: _t.Any, focused_prefix_color: _t.Any, unfocused_prefix_color: _t.Any, disabled_prefix_color: _t.Any, error_prefix_color: _t.Any, focused_suffix_color: _t.Any, unfocused_suffix_color: _t.Any, disabled_suffix_color: _t.Any, error_suffix_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.TextFieldColors (androidx.compose.material3.TextFieldColors(focusedTextColor: androidx.compose.ui.graphics.Color, unfocusedTextColor: androidx.compose.ui.graphics.Color, disabledTextColor: androidx.compose.ui.graphics.Color, errorTextColor: androidx.compose.ui.graphics.Color, focusedContainerColor: androidx.compose.ui.graphics.Color, unfocusedContainerColor: androidx.compose.ui.graphics.Color, disabledContainerColor: androidx.compose.ui.graphics.Color, errorContainerColor: androidx.compose.ui.graphics.Color, cursorColor: androidx.compose.ui.graphics.Color, errorCursorColor: androidx.compose.ui.graphics.Color, textSelectionColors: androidx.compose.foundation.text.selection.TextSelectionColors, focusedIndicatorColor: androidx.compose.ui.graphics.Color, unfocusedIndicatorColor: androidx.compose.ui.graphics.Color, disabledIndicatorColor: androidx.compose.ui.graphics.Color, errorIndicatorColor: androidx.compose.ui.graphics.Color, focusedLeadingIconColor: androidx.compose.ui.graphics.Color, unfocusedLeadingIconColor: androidx.compose.ui.graphics.Color, disabledLeadingIconColor: androidx.compose.ui.graphics.Color, errorLeadingIconColor: androidx.compose.ui.graphics.Color, focusedTrailingIconColor: androidx.compose.ui.graphics.Color, unfocusedTrailingIconColor: androidx.compose.ui.graphics.Color, disabledTrailingIconColor: androidx.compose.ui.graphics.Color, errorTrailingIconColor: androidx.compose.ui.graphics.Color, focusedLabelColor: androidx.compose.ui.graphics.Color, unfocusedLabelColor: androidx.compose.ui.graphics.Color, disabledLabelColor: androidx.compose.ui.graphics.Color, errorLabelColor: androidx.compose.ui.graphics.Color, focusedPlaceholderColor: androidx.compose.ui.graphics.Color, unfocusedPlaceholderColor: androidx.compose.ui.graphics.Color, disabledPlaceholderColor: androidx.compose.ui.graphics.Color, errorPlaceholderColor: androidx.compose.ui.graphics.Color, focusedSupportingTextColor: androidx.compose.ui.graphics.Color, unfocusedSupportingTextColor: androidx.compose.ui.graphics.Color, disabledSupportingTextColor: androidx.compose.ui.graphics.Color, errorSupportingTextColor: androidx.compose.ui.graphics.Color, focusedPrefixColor: androidx.compose.ui.graphics.Color, unfocusedPrefixColor: androidx.compose.ui.graphics.Color, disabledPrefixColor: androidx.compose.ui.graphics.Color, errorPrefixColor: androidx.compose.ui.graphics.Color, focusedSuffixColor: androidx.compose.ui.graphics.Color, unfocusedSuffixColor: androidx.compose.ui.graphics.Color, disabledSuffixColor: androidx.compose.ui.graphics.Color, errorSuffixColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.TextFieldColors)""" ... @property @@ -1724,7 +1729,7 @@ class TextFieldLabelScope: class TimePickerColors: """Kotlin: androidx.compose.material3.TimePickerColors""" - def __init__(self, clockDialColor: _t.Any, selectorColor: _t.Any, containerColor: _t.Any, periodSelectorBorderColor: _t.Any, clockDialSelectedContentColor: _t.Any, clockDialUnselectedContentColor: _t.Any, periodSelectorSelectedContainerColor: _t.Any, periodSelectorUnselectedContainerColor: _t.Any, periodSelectorSelectedContentColor: _t.Any, periodSelectorUnselectedContentColor: _t.Any, timeSelectorSelectedContainerColor: _t.Any, timeSelectorUnselectedContainerColor: _t.Any, timeSelectorSelectedContentColor: _t.Any, timeSelectorUnselectedContentColor: _t.Any) -> None: + def __init__(self, clock_dial_color: _t.Any, selector_color: _t.Any, container_color: _t.Any, period_selector_border_color: _t.Any, clock_dial_selected_content_color: _t.Any, clock_dial_unselected_content_color: _t.Any, period_selector_selected_container_color: _t.Any, period_selector_unselected_container_color: _t.Any, period_selector_selected_content_color: _t.Any, period_selector_unselected_content_color: _t.Any, time_selector_selected_container_color: _t.Any, time_selector_unselected_container_color: _t.Any, time_selector_selected_content_color: _t.Any, time_selector_unselected_content_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.TimePickerColors (androidx.compose.material3.TimePickerColors(clockDialColor: androidx.compose.ui.graphics.Color, selectorColor: androidx.compose.ui.graphics.Color, containerColor: androidx.compose.ui.graphics.Color, periodSelectorBorderColor: androidx.compose.ui.graphics.Color, clockDialSelectedContentColor: androidx.compose.ui.graphics.Color, clockDialUnselectedContentColor: androidx.compose.ui.graphics.Color, periodSelectorSelectedContainerColor: androidx.compose.ui.graphics.Color, periodSelectorUnselectedContainerColor: androidx.compose.ui.graphics.Color, periodSelectorSelectedContentColor: androidx.compose.ui.graphics.Color, periodSelectorUnselectedContentColor: androidx.compose.ui.graphics.Color, timeSelectorSelectedContainerColor: androidx.compose.ui.graphics.Color, timeSelectorUnselectedContainerColor: androidx.compose.ui.graphics.Color, timeSelectorSelectedContentColor: androidx.compose.ui.graphics.Color, timeSelectorUnselectedContentColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.TimePickerColors)""" ... @property @@ -1792,9 +1797,9 @@ class TooltipScope: class TopAppBarColors: """Kotlin: androidx.compose.material3.TopAppBarColors""" @_t.overload - def __init__(self, containerColor: _t.Any, scrolledContainerColor: _t.Any, navigationIconContentColor: _t.Any, titleContentColor: _t.Any, actionIconContentColor: _t.Any) -> None: ... + def __init__(self, container_color: _t.Any, scrolled_container_color: _t.Any, navigation_icon_content_color: _t.Any, title_content_color: _t.Any, action_icon_content_color: _t.Any) -> None: ... @_t.overload - def __init__(self, containerColor: _t.Any, scrolledContainerColor: _t.Any, navigationIconContentColor: _t.Any, titleContentColor: _t.Any, actionIconContentColor: _t.Any, subtitleContentColor: _t.Any) -> None: ... # type: ignore[overload-cannot-match] + def __init__(self, container_color: _t.Any, scrolled_container_color: _t.Any, navigation_icon_content_color: _t.Any, title_content_color: _t.Any, action_icon_content_color: _t.Any, subtitle_content_color: _t.Any) -> None: ... # type: ignore[overload-cannot-match] @property def action_icon_content_color(self) -> _t.Any: """Kotlin property: androidx.compose.material3.TopAppBarColors.actionIconContentColor (androidx.compose.material3.TopAppBarColors.actionIconContentColor: androidx.compose.ui.graphics.Color)""" @@ -1837,7 +1842,7 @@ class TopAppBarScrollBehavior: class Typography: """Kotlin: androidx.compose.material3.Typography""" - def __init__(self, displayLarge: _t.Any, displayMedium: _t.Any, displaySmall: _t.Any, headlineLarge: _t.Any, headlineMedium: _t.Any, headlineSmall: _t.Any, titleLarge: _t.Any, titleMedium: _t.Any, titleSmall: _t.Any, bodyLarge: _t.Any, bodyMedium: _t.Any, bodySmall: _t.Any, labelLarge: _t.Any, labelMedium: _t.Any, labelSmall: _t.Any) -> None: + def __init__(self, display_large: _t.Any, display_medium: _t.Any, display_small: _t.Any, headline_large: _t.Any, headline_medium: _t.Any, headline_small: _t.Any, title_large: _t.Any, title_medium: _t.Any, title_small: _t.Any, body_large: _t.Any, body_medium: _t.Any, body_small: _t.Any, label_large: _t.Any, label_medium: _t.Any, label_small: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.Typography (androidx.compose.material3.Typography(displayLarge: androidx.compose.ui.text.TextStyle, displayMedium: androidx.compose.ui.text.TextStyle, displaySmall: androidx.compose.ui.text.TextStyle, headlineLarge: androidx.compose.ui.text.TextStyle, headlineMedium: androidx.compose.ui.text.TextStyle, headlineSmall: androidx.compose.ui.text.TextStyle, titleLarge: androidx.compose.ui.text.TextStyle, titleMedium: androidx.compose.ui.text.TextStyle, titleSmall: androidx.compose.ui.text.TextStyle, bodyLarge: androidx.compose.ui.text.TextStyle, bodyMedium: androidx.compose.ui.text.TextStyle, bodySmall: androidx.compose.ui.text.TextStyle, labelLarge: androidx.compose.ui.text.TextStyle, labelMedium: androidx.compose.ui.text.TextStyle, labelSmall: androidx.compose.ui.text.TextStyle): androidx.compose.material3.Typography)""" ... @property @@ -1903,7 +1908,7 @@ class Typography: class WideNavigationRailColors: """Kotlin: androidx.compose.material3.WideNavigationRailColors""" - def __init__(self, containerColor: _t.Any, contentColor: _t.Any, modalContainerColor: _t.Any, modalScrimColor: _t.Any, modalContentColor: _t.Any) -> None: + def __init__(self, container_color: _t.Any, content_color: _t.Any, modal_container_color: _t.Any, modal_scrim_color: _t.Any, modal_content_color: _t.Any) -> None: """Kotlin constructor: androidx.compose.material3.WideNavigationRailColors (androidx.compose.material3.WideNavigationRailColors(containerColor: androidx.compose.ui.graphics.Color, contentColor: androidx.compose.ui.graphics.Color, modalContainerColor: androidx.compose.ui.graphics.Color, modalScrimColor: androidx.compose.ui.graphics.Color, modalContentColor: androidx.compose.ui.graphics.Color): androidx.compose.material3.WideNavigationRailColors)""" ... @property @@ -1977,10 +1982,6 @@ def BasicAlertDialog(on_dismiss_request: _t.Callable[[], None], modifier: python """Kotlin: androidx.compose.material3.BasicAlertDialog(onDismissRequest: kotlin.Function0, modifier: androidx.compose.ui.Modifier, properties: androidx.compose.ui.window.DialogProperties, content: kotlin.Function0): kotlin.Unit""" ... -def BottomAppBarState(initial_height_offset_limit: float, initial_height_offset: float, initial_content_offset: float) -> _t.Any: - """Kotlin: androidx.compose.material3.BottomAppBarState(initialHeightOffsetLimit: kotlin.Float, initialHeightOffset: kotlin.Float, initialContentOffset: kotlin.Float): androidx.compose.material3.BottomAppBarState""" - ... - def BottomAppBar__Modifier_Color_Color_Dp_PaddingValues_WindowInsets_BottomAppBarScrollBehavior_Unit(modifier: pythonx.compose.ui.Modifier = ..., container_color: _t.Any = ..., content_color: _t.Any = ..., tonal_elevation: float = ..., content_padding: _t.Any = ..., window_insets: _t.Any = ..., scroll_behavior: BottomAppBarScrollBehavior | None = ..., *, content: _t.Callable[[pythonx.compose.layout.RowScope], None]) -> None: """Kotlin: androidx.compose.material3.BottomAppBar(modifier: androidx.compose.ui.Modifier, containerColor: androidx.compose.ui.graphics.Color, contentColor: androidx.compose.ui.graphics.Color, tonalElevation: androidx.compose.ui.unit.Dp, contentPadding: androidx.compose.foundation.layout.PaddingValues, windowInsets: androidx.compose.foundation.layout.WindowInsets, scrollBehavior: androidx.compose.material3.BottomAppBarScrollBehavior?, content: kotlin.Function1): kotlin.Unit""" ... @@ -2645,6 +2646,8 @@ def surface_color_at_elevation(receiver: ColorScheme, /, elevation: float) -> _t """Kotlin: androidx.compose.material3.ColorScheme.surfaceColorAtElevation(elevation: androidx.compose.ui.unit.Dp): androidx.compose.ui.graphics.Color""" ... +BottomAppBarState: _BottomAppBarState_callable_module + def ColorScheme__Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color_Color(primary: _t.Any, on_primary: _t.Any, primary_container: _t.Any, on_primary_container: _t.Any, inverse_primary: _t.Any, secondary: _t.Any, on_secondary: _t.Any, secondary_container: _t.Any, on_secondary_container: _t.Any, tertiary: _t.Any, on_tertiary: _t.Any, tertiary_container: _t.Any, on_tertiary_container: _t.Any, background: _t.Any, on_background: _t.Any, surface: _t.Any, on_surface: _t.Any, surface_variant: _t.Any, on_surface_variant: _t.Any, surface_tint: _t.Any, inverse_surface: _t.Any, inverse_on_surface: _t.Any, error: _t.Any, on_error: _t.Any, error_container: _t.Any, on_error_container: _t.Any, outline: _t.Any, outline_variant: _t.Any, scrim: _t.Any) -> ColorScheme: """Kotlin: androidx.compose.material3.ColorScheme(primary: androidx.compose.ui.graphics.Color, onPrimary: androidx.compose.ui.graphics.Color, primaryContainer: androidx.compose.ui.graphics.Color, onPrimaryContainer: androidx.compose.ui.graphics.Color, inversePrimary: androidx.compose.ui.graphics.Color, secondary: androidx.compose.ui.graphics.Color, onSecondary: androidx.compose.ui.graphics.Color, secondaryContainer: androidx.compose.ui.graphics.Color, onSecondaryContainer: androidx.compose.ui.graphics.Color, tertiary: androidx.compose.ui.graphics.Color, onTertiary: androidx.compose.ui.graphics.Color, tertiaryContainer: androidx.compose.ui.graphics.Color, onTertiaryContainer: androidx.compose.ui.graphics.Color, background: androidx.compose.ui.graphics.Color, onBackground: androidx.compose.ui.graphics.Color, surface: androidx.compose.ui.graphics.Color, onSurface: androidx.compose.ui.graphics.Color, surfaceVariant: androidx.compose.ui.graphics.Color, onSurfaceVariant: androidx.compose.ui.graphics.Color, surfaceTint: androidx.compose.ui.graphics.Color, inverseSurface: androidx.compose.ui.graphics.Color, inverseOnSurface: androidx.compose.ui.graphics.Color, error: androidx.compose.ui.graphics.Color, onError: androidx.compose.ui.graphics.Color, errorContainer: androidx.compose.ui.graphics.Color, onErrorContainer: androidx.compose.ui.graphics.Color, outline: androidx.compose.ui.graphics.Color, outlineVariant: androidx.compose.ui.graphics.Color, scrim: androidx.compose.ui.graphics.Color): androidx.compose.material3.ColorScheme""" ... diff --git a/pythonx/compose/runtime/__init__.py b/pythonx/compose/runtime/__init__.py index 3bc9441..429bae3 100644 --- a/pythonx/compose/runtime/__init__.py +++ b/pythonx/compose/runtime/__init__.py @@ -38,7 +38,8 @@ ## The app root and `state` `@app` declares the screen the host draws; `state(initial)` makes a value Compose observes. The host -is configured once with `PythonContent("pythonx.compose.runtime", "app_root")`: it reads the module +is configured once with +`PythonAppView(module = "pythonx.compose.runtime", attribute = "app_root")`: it reads the module attribute `app_root`, a Compose `MutableState` whose `.value` is a zero-argument callable or `None` (nothing is drawn). `app(fn)` writes `app_root.value = fn` and returns `fn` unchanged, so declaring the root again -- in a notebook cell, say -- replaces the screen. There is deliberately no update or @@ -76,7 +77,8 @@ def Composable(target): app_root: State -"""The state the host reads (`PythonContent("pythonx.compose.runtime", "app_root")`); annotated +"""The state the host reads +(`PythonAppView(module = "pythonx.compose.runtime", attribute = "app_root")`); annotated only, created on first read by `__getattr__` below.""" diff --git a/pythonx/compose/runtime/__init__.pyi b/pythonx/compose/runtime/__init__.pyi index beecbf9..45b26b5 100644 --- a/pythonx/compose/runtime/__init__.pyi +++ b/pythonx/compose/runtime/__init__.pyi @@ -1,4 +1,4 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t import pythonx.compose.ui diff --git a/pythonx/compose/ui/__init__.pyi b/pythonx/compose/ui/__init__.pyi index 827559a..54e6c12 100644 --- a/pythonx/compose/ui/__init__.pyi +++ b/pythonx/compose/ui/__init__.pyi @@ -1,4 +1,4 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t import pythonx.compose.layout import pythonx.compose.runtime @@ -545,7 +545,7 @@ class _Modifier_zIndex(_t.Protocol): class BiasAbsoluteAlignment: """Kotlin: androidx.compose.ui.BiasAbsoluteAlignment""" - def __init__(self, horizontalBias: float, verticalBias: float) -> None: + def __init__(self, horizontal_bias: float, vertical_bias: float) -> None: """Kotlin constructor: androidx.compose.ui.BiasAbsoluteAlignment (androidx.compose.ui.BiasAbsoluteAlignment(horizontalBias: kotlin.Float, verticalBias: kotlin.Float): androidx.compose.ui.BiasAbsoluteAlignment)""" ... @property @@ -559,7 +559,7 @@ class BiasAbsoluteAlignment: class BiasAlignment: """Kotlin: androidx.compose.ui.BiasAlignment""" - def __init__(self, horizontalBias: float, verticalBias: float) -> None: + def __init__(self, horizontal_bias: float, vertical_bias: float) -> None: """Kotlin constructor: androidx.compose.ui.BiasAlignment (androidx.compose.ui.BiasAlignment(horizontalBias: kotlin.Float, verticalBias: kotlin.Float): androidx.compose.ui.BiasAlignment)""" ... @property diff --git a/pythonx/compose/ui/graphics/__init__.pyi b/pythonx/compose/ui/graphics/__init__.pyi index 85e7040..6704f9a 100644 --- a/pythonx/compose/ui/graphics/__init__.pyi +++ b/pythonx/compose/ui/graphics/__init__.pyi @@ -1,7 +1,41 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t import pythonx.compose.ui +class _BlurEffect_callable_module(_t.Protocol): + """Kotlin: the function BlurEffect, and the members of the module BlurEffect""" + def __call__(self, radius_x: float, radius_y: float, edge_treatment: _t.Any = ...) -> _t.Any: ... + BlurSigmaScale: float + +class _Color_callable_module(_t.Protocol): + """Kotlin: the function Color, and the members of the module Color""" + @_t.overload + def __call__(self, red: float, green: float, blue: float, alpha: float = ..., color_space: _t.Any = ...) -> _t.Any: ... + @_t.overload + def __call__(self, color: int) -> _t.Any: ... # type: ignore[overload-cannot-match] + @_t.overload + def __call__(self, red: int, green: int, blue: int, alpha: int = ...) -> _t.Any: ... # type: ignore[overload-cannot-match] + @_t.overload + def __call__(self, color: int) -> _t.Any: ... # type: ignore[overload-cannot-match] + Black: _t.Any + Blue: _t.Any + Cyan: _t.Any + DarkGray: _t.Any + Gray: _t.Any + Green: _t.Any + LightGray: _t.Any + Magenta: _t.Any + Red: _t.Any + Transparent: _t.Any + Unspecified: _t.Any + White: _t.Any + Yellow: _t.Any + +class _TransformOrigin_callable_module(_t.Protocol): + """Kotlin: the function TransformOrigin, and the members of the module TransformOrigin""" + def __call__(self, pivot_fraction_x: float, pivot_fraction_y: float) -> _t.Any: ... + Center: _t.Any + class _ColorFilter_asDesktopColorFilter(_t.Protocol): def __call__(self) -> _t.Any: ... @@ -19,7 +53,7 @@ class _RenderEffect_asDesktopImageFilter(_t.Protocol): class BlendModeColorFilter(ColorFilter): """Kotlin: androidx.compose.ui.graphics.BlendModeColorFilter""" - def __init__(self, color: _t.Any, blendMode: _t.Any) -> None: + def __init__(self, color: _t.Any, blend_mode: _t.Any) -> None: """Kotlin constructor: androidx.compose.ui.graphics.BlendModeColorFilter (androidx.compose.ui.graphics.BlendModeColorFilter(color: androidx.compose.ui.graphics.Color, blendMode: androidx.compose.ui.graphics.BlendMode): androidx.compose.ui.graphics.BlendModeColorFilter)""" ... @property @@ -48,7 +82,7 @@ class ColorMatrix: class ColorMatrixColorFilter(ColorFilter): """Kotlin: androidx.compose.ui.graphics.ColorMatrixColorFilter""" - def __init__(self, colorMatrix: ColorMatrix) -> None: + def __init__(self, color_matrix: ColorMatrix) -> None: """Kotlin constructor: androidx.compose.ui.graphics.ColorMatrixColorFilter (androidx.compose.ui.graphics.ColorMatrixColorFilter(colorMatrix: androidx.compose.ui.graphics.ColorMatrix): androidx.compose.ui.graphics.ColorMatrixColorFilter)""" ... @@ -187,7 +221,7 @@ class Shape: class SkiaGraphicsContext(GraphicsContext): """Kotlin: androidx.compose.ui.graphics.SkiaGraphicsContext""" - def __init__(self, measureDrawBounds: bool = ...) -> None: + def __init__(self, measure_draw_bounds: bool = ...) -> None: """Kotlin constructor: androidx.compose.ui.graphics.SkiaGraphicsContext (androidx.compose.ui.graphics.SkiaGraphicsContext(measureDrawBounds: kotlin.Boolean): androidx.compose.ui.graphics.SkiaGraphicsContext)""" ... @property @@ -212,10 +246,6 @@ class Vertices: """Kotlin property: androidx.compose.ui.graphics.Vertices.vertexMode (androidx.compose.ui.graphics.Vertices.vertexMode: androidx.compose.ui.graphics.VertexMode)""" ... -def BlurEffect(radius_x: float, radius_y: float, edge_treatment: _t.Any = ...) -> _t.Any: - """Kotlin: androidx.compose.ui.graphics.BlurEffect(radiusX: kotlin.Float, radiusY: kotlin.Float, edgeTreatment: androidx.compose.ui.graphics.TileMode): androidx.compose.ui.graphics.BlurEffect""" - ... - def Canvas(image: _t.Any) -> _t.Any: """Kotlin: androidx.compose.ui.graphics.Canvas(image: androidx.compose.ui.graphics.ImageBitmap): androidx.compose.ui.graphics.Canvas""" ... @@ -280,10 +310,6 @@ def ShaderBrush(shader: Shader) -> _t.Any: """Kotlin: androidx.compose.ui.graphics.ShaderBrush(shader: androidx.compose.ui.graphics.Shader): androidx.compose.ui.graphics.ShaderBrush""" ... -def TransformOrigin(pivot_fraction_x: float, pivot_fraction_y: float) -> _t.Any: - """Kotlin: androidx.compose.ui.graphics.TransformOrigin(pivotFractionX: kotlin.Float, pivotFractionY: kotlin.Float): androidx.compose.ui.graphics.TransformOrigin""" - ... - def add_outline(receiver: _t.Any, /, outline: Outline) -> None: """Kotlin: androidx.compose.ui.graphics.Path.addOutline(outline: androidx.compose.ui.graphics.Outline): kotlin.Unit""" ... @@ -524,14 +550,11 @@ def with_save_layer(receiver: _t.Any, /, bounds: _t.Any, paint: _t.Any, block: _ """Kotlin: androidx.compose.ui.graphics.Canvas.withSaveLayer(bounds: androidx.compose.ui.geometry.Rect, paint: androidx.compose.ui.graphics.Paint, block: kotlin.Function0): kotlin.Unit""" ... -@_t.overload -def Color(red: float, green: float, blue: float, alpha: float = ..., color_space: _t.Any = ...) -> _t.Any: ... -@_t.overload -def Color(color: int) -> _t.Any: ... # type: ignore[overload-cannot-match] -@_t.overload -def Color(red: int, green: int, blue: int, alpha: int = ...) -> _t.Any: ... # type: ignore[overload-cannot-match] -@_t.overload -def Color(color: int) -> _t.Any: ... # type: ignore[overload-cannot-match] +BlurEffect: _BlurEffect_callable_module + +Color: _Color_callable_module + +TransformOrigin: _TransformOrigin_callable_module @_t.overload def as_compose_path_effect(receiver: _t.Any, /) -> PathEffect: ... @@ -716,7 +739,7 @@ class PathOperation: """Kotlin property: androidx.compose.ui.graphics.intersect (androidx.compose.ui.graphics.PathOperation.Companion.intersect: androidx.compose.ui.graphics.PathOperation)""" ... @property - def reverseDifference(self) -> _t.Any: + def reverse_difference(self) -> _t.Any: """Kotlin property: androidx.compose.ui.graphics.reverseDifference (androidx.compose.ui.graphics.PathOperation.Companion.reverseDifference: androidx.compose.ui.graphics.PathOperation)""" ... @property diff --git a/pythonx/compose/ui/graphics/vector/__init__.pyi b/pythonx/compose/ui/graphics/vector/__init__.pyi index 1e708df..86c7b2b 100644 --- a/pythonx/compose/ui/graphics/vector/__init__.pyi +++ b/pythonx/compose/ui/graphics/vector/__init__.pyi @@ -1,4 +1,4 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t import pythonx.compose.runtime import pythonx.compose.ui.graphics diff --git a/pythonx/compose/ui/text/__init__.pyi b/pythonx/compose/ui/text/__init__.pyi index 6044f24..9c55f19 100644 --- a/pythonx/compose/ui/text/__init__.pyi +++ b/pythonx/compose/ui/text/__init__.pyi @@ -1,8 +1,24 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t import pythonx.compose.ui.graphics import pythonx.compose.ui.unit +class _AnnotatedString_callable_module(_t.Protocol): + """Kotlin: the function AnnotatedString, and the members of the module AnnotatedString""" + @_t.overload + def __call__(self, text: str, paragraph_style: ParagraphStyle) -> _t.Any: ... + @_t.overload + def __call__(self, text: str, span_style: SpanStyle, paragraph_style: ParagraphStyle | None = ...) -> _t.Any: ... # type: ignore[overload-cannot-match] + Saver: _t.Any + +class _TextRange_callable_module(_t.Protocol): + """Kotlin: the function TextRange, and the members of the module TextRange""" + @_t.overload + def __call__(self, index: int) -> _t.Any: ... + @_t.overload + def __call__(self, start: int, end: int) -> _t.Any: ... # type: ignore[overload-cannot-match] + Zero: _t.Any + class LinkAnnotation: """Kotlin: androidx.compose.ui.text.LinkAnnotation""" @property @@ -20,9 +36,9 @@ class LinkInteractionListener: class MultiParagraph: """Kotlin: androidx.compose.ui.text.MultiParagraph""" @_t.overload - def __init__(self, intrinsics: MultiParagraphIntrinsics, constraints: int, maxLines: int = ..., overflow: _t.Any = ...) -> None: ... + def __init__(self, intrinsics: MultiParagraphIntrinsics, constraints: int, max_lines: int = ..., overflow: _t.Any = ...) -> None: ... @_t.overload - def __init__(self, intrinsics: MultiParagraphIntrinsics, maxLines: int = ..., ellipsis: bool = ..., *, width: float) -> None: ... # type: ignore[overload-cannot-match] + def __init__(self, intrinsics: MultiParagraphIntrinsics, max_lines: int = ..., ellipsis: bool = ..., *, width: float) -> None: ... # type: ignore[overload-cannot-match] @property def did_exceed_max_lines(self) -> bool: """Kotlin property: androidx.compose.ui.text.MultiParagraph.didExceedMaxLines (androidx.compose.ui.text.MultiParagraph.didExceedMaxLines: kotlin.Boolean)""" @@ -100,7 +116,7 @@ class ParagraphIntrinsics: class ParagraphStyle: """Kotlin: androidx.compose.ui.text.ParagraphStyle""" - def __init__(self, textAlign: _t.Any, textDirection: _t.Any, lineHeight: _t.Any, textIndent: _t.Any | None, platformStyle: _t.Any | None, lineHeightStyle: _t.Any | None, lineBreak: _t.Any, hyphens: _t.Any, textMotion: _t.Any | None) -> None: + def __init__(self, text_align: _t.Any, text_direction: _t.Any, line_height: _t.Any, text_indent: _t.Any | None, platform_style: _t.Any | None, line_height_style: _t.Any | None, line_break: _t.Any, hyphens: _t.Any, text_motion: _t.Any | None) -> None: """Kotlin constructor: androidx.compose.ui.text.ParagraphStyle (androidx.compose.ui.text.ParagraphStyle(textAlign: androidx.compose.ui.text.style.TextAlign, textDirection: androidx.compose.ui.text.style.TextDirection, lineHeight: androidx.compose.ui.unit.TextUnit, textIndent: androidx.compose.ui.text.style.TextIndent?, platformStyle: androidx.compose.ui.text.PlatformParagraphStyle?, lineHeightStyle: androidx.compose.ui.text.style.LineHeightStyle?, lineBreak: androidx.compose.ui.text.style.LineBreak, hyphens: androidx.compose.ui.text.style.Hyphens, textMotion: androidx.compose.ui.text.style.TextMotion?): androidx.compose.ui.text.ParagraphStyle)""" ... @property @@ -158,7 +174,7 @@ class ParagraphStyle: class Placeholder: """Kotlin: androidx.compose.ui.text.Placeholder""" - def __init__(self, width: _t.Any, height: _t.Any, placeholderVerticalAlign: _t.Any) -> None: + def __init__(self, width: _t.Any, height: _t.Any, placeholder_vertical_align: _t.Any) -> None: """Kotlin constructor: androidx.compose.ui.text.Placeholder (androidx.compose.ui.text.Placeholder(width: androidx.compose.ui.unit.TextUnit, height: androidx.compose.ui.unit.TextUnit, placeholderVerticalAlign: androidx.compose.ui.text.PlaceholderVerticalAlign): androidx.compose.ui.text.Placeholder)""" ... @property @@ -177,9 +193,9 @@ class Placeholder: class PlatformTextStyle: """Kotlin: androidx.compose.ui.text.PlatformTextStyle""" @_t.overload - def __init__(self, spanStyle: _t.Any | None, paragraphStyle: _t.Any | None) -> None: ... + def __init__(self, span_style: _t.Any | None, paragraph_style: _t.Any | None) -> None: ... @_t.overload - def __init__(self, textDecorationLineStyle: _t.Any | None) -> None: ... # type: ignore[overload-cannot-match] + def __init__(self, text_decoration_line_style: _t.Any | None) -> None: ... # type: ignore[overload-cannot-match] @property def paragraph_style(self) -> _t.Any | None: """Kotlin property: androidx.compose.ui.text.PlatformTextStyle.paragraphStyle (androidx.compose.ui.text.PlatformTextStyle.paragraphStyle: androidx.compose.ui.text.PlatformParagraphStyle?)""" @@ -192,9 +208,9 @@ class PlatformTextStyle: class SpanStyle: """Kotlin: androidx.compose.ui.text.SpanStyle""" @_t.overload - def __init__(self, brush: pythonx.compose.ui.graphics.Brush | None, alpha: float, fontSize: _t.Any, fontWeight: _t.Any | None, fontStyle: _t.Any | None, fontSynthesis: _t.Any | None, fontFamily: _t.Any | None, fontFeatureSettings: str | None, letterSpacing: _t.Any, baselineShift: _t.Any | None, textGeometricTransform: _t.Any | None, localeList: _t.Any | None, background: _t.Any, textDecoration: _t.Any | None, shadow: pythonx.compose.ui.graphics.Shadow | None, platformStyle: _t.Any | None, drawStyle: _t.Any | None) -> None: ... + def __init__(self, brush: pythonx.compose.ui.graphics.Brush | None, alpha: float, font_size: _t.Any, font_weight: _t.Any | None, font_style: _t.Any | None, font_synthesis: _t.Any | None, font_family: _t.Any | None, font_feature_settings: str | None, letter_spacing: _t.Any, baseline_shift: _t.Any | None, text_geometric_transform: _t.Any | None, locale_list: _t.Any | None, background: _t.Any, text_decoration: _t.Any | None, shadow: pythonx.compose.ui.graphics.Shadow | None, platform_style: _t.Any | None, draw_style: _t.Any | None) -> None: ... @_t.overload - def __init__(self, color: _t.Any, fontSize: _t.Any, fontWeight: _t.Any | None, fontStyle: _t.Any | None, fontSynthesis: _t.Any | None, fontFamily: _t.Any | None, fontFeatureSettings: str | None, letterSpacing: _t.Any, baselineShift: _t.Any | None, textGeometricTransform: _t.Any | None, localeList: _t.Any | None, background: _t.Any, textDecoration: _t.Any | None, shadow: pythonx.compose.ui.graphics.Shadow | None, platformStyle: _t.Any | None, drawStyle: _t.Any | None) -> None: ... # type: ignore[overload-cannot-match] + def __init__(self, color: _t.Any, font_size: _t.Any, font_weight: _t.Any | None, font_style: _t.Any | None, font_synthesis: _t.Any | None, font_family: _t.Any | None, font_feature_settings: str | None, letter_spacing: _t.Any, baseline_shift: _t.Any | None, text_geometric_transform: _t.Any | None, locale_list: _t.Any | None, background: _t.Any, text_decoration: _t.Any | None, shadow: pythonx.compose.ui.graphics.Shadow | None, platform_style: _t.Any | None, draw_style: _t.Any | None) -> None: ... # type: ignore[overload-cannot-match] @property def alpha(self) -> float: """Kotlin property: androidx.compose.ui.text.SpanStyle.alpha (androidx.compose.ui.text.SpanStyle.alpha: kotlin.Float)""" @@ -313,7 +329,7 @@ class TextLayoutInput: class TextLayoutResult: """Kotlin: androidx.compose.ui.text.TextLayoutResult""" - def __init__(self, layoutInput: TextLayoutInput, multiParagraph: MultiParagraph, size: _t.Any) -> None: + def __init__(self, layout_input: TextLayoutInput, multi_paragraph: MultiParagraph, size: _t.Any) -> None: """Kotlin constructor: androidx.compose.ui.text.TextLayoutResult (androidx.compose.ui.text.TextLayoutResult(layoutInput: androidx.compose.ui.text.TextLayoutInput, multiParagraph: androidx.compose.ui.text.MultiParagraph, size: androidx.compose.ui.unit.IntSize): androidx.compose.ui.text.TextLayoutResult)""" ... @property @@ -355,7 +371,7 @@ class TextLayoutResult: class TextLinkStyles: """Kotlin: androidx.compose.ui.text.TextLinkStyles""" - def __init__(self, style: SpanStyle | None = ..., focusedStyle: SpanStyle | None = ..., hoveredStyle: SpanStyle | None = ..., pressedStyle: SpanStyle | None = ...) -> None: + def __init__(self, style: SpanStyle | None = ..., focused_style: SpanStyle | None = ..., hovered_style: SpanStyle | None = ..., pressed_style: SpanStyle | None = ...) -> None: """Kotlin constructor: androidx.compose.ui.text.TextLinkStyles (androidx.compose.ui.text.TextLinkStyles(style: androidx.compose.ui.text.SpanStyle?, focusedStyle: androidx.compose.ui.text.SpanStyle?, hoveredStyle: androidx.compose.ui.text.SpanStyle?, pressedStyle: androidx.compose.ui.text.SpanStyle?): androidx.compose.ui.text.TextLinkStyles)""" ... @property @@ -377,7 +393,7 @@ class TextLinkStyles: class TextMeasurer: """Kotlin: androidx.compose.ui.text.TextMeasurer""" - def __init__(self, defaultFontFamilyResolver: _t.Any, defaultDensity: _t.Any, defaultLayoutDirection: pythonx.compose.ui.unit.LayoutDirection, cacheSize: int = ...) -> None: + def __init__(self, default_font_family_resolver: _t.Any, default_density: _t.Any, default_layout_direction: pythonx.compose.ui.unit.LayoutDirection, cache_size: int = ...) -> None: """Kotlin constructor: androidx.compose.ui.text.TextMeasurer (androidx.compose.ui.text.TextMeasurer(defaultFontFamilyResolver: androidx.compose.ui.text.font.FontFamily.Resolver, defaultDensity: androidx.compose.ui.unit.Density, defaultLayoutDirection: androidx.compose.ui.unit.LayoutDirection, cacheSize: kotlin.Int): androidx.compose.ui.text.TextMeasurer)""" ... @@ -528,6 +544,10 @@ def to_upper_case__String_LocaleList(receiver: str, /, locale_list: _t.Any) -> s """Kotlin: kotlin.String.toUpperCase(localeList: androidx.compose.ui.text.intl.LocaleList): kotlin.String""" ... +AnnotatedString: _AnnotatedString_callable_module + +TextRange: _TextRange_callable_module + def MultiParagraph__MultiParagraphIntrinsics_Constraints_Int_TextOverflow(intrinsics: MultiParagraphIntrinsics, constraints: int, max_lines: int = ..., overflow: _t.Any = ...) -> MultiParagraph: """Kotlin: androidx.compose.ui.text.MultiParagraph(intrinsics: androidx.compose.ui.text.MultiParagraphIntrinsics, constraints: androidx.compose.ui.unit.Constraints, maxLines: kotlin.Int, overflow: androidx.compose.ui.text.style.TextOverflow): androidx.compose.ui.text.MultiParagraph""" ... @@ -552,21 +572,11 @@ def SpanStyle__Color_TextUnit_FontWeight_FontStyle_FontSynthesis_FontFamily_Stri """Kotlin: androidx.compose.ui.text.SpanStyle(color: androidx.compose.ui.graphics.Color, fontSize: androidx.compose.ui.unit.TextUnit, fontWeight: androidx.compose.ui.text.font.FontWeight?, fontStyle: androidx.compose.ui.text.font.FontStyle?, fontSynthesis: androidx.compose.ui.text.font.FontSynthesis?, fontFamily: androidx.compose.ui.text.font.FontFamily?, fontFeatureSettings: kotlin.String?, letterSpacing: androidx.compose.ui.unit.TextUnit, baselineShift: androidx.compose.ui.text.style.BaselineShift?, textGeometricTransform: androidx.compose.ui.text.style.TextGeometricTransform?, localeList: androidx.compose.ui.text.intl.LocaleList?, background: androidx.compose.ui.graphics.Color, textDecoration: androidx.compose.ui.text.style.TextDecoration?, shadow: androidx.compose.ui.graphics.Shadow?, platformStyle: androidx.compose.ui.text.PlatformSpanStyle?, drawStyle: androidx.compose.ui.graphics.drawscope.DrawStyle?): androidx.compose.ui.text.SpanStyle""" ... -@_t.overload -def AnnotatedString(text: str, paragraph_style: ParagraphStyle) -> _t.Any: ... -@_t.overload -def AnnotatedString(text: str, span_style: SpanStyle, paragraph_style: ParagraphStyle | None = ...) -> _t.Any: ... # type: ignore[overload-cannot-match] - @_t.overload def Paragraph(paragraph_intrinsics: ParagraphIntrinsics, constraints: int, max_lines: int = ..., overflow: _t.Any = ...) -> _t.Any: ... @_t.overload def Paragraph(paragraph_intrinsics: ParagraphIntrinsics, max_lines: int = ..., ellipsis: bool = ..., *, width: float) -> _t.Any: ... # type: ignore[overload-cannot-match] -@_t.overload -def TextRange(index: int) -> _t.Any: ... -@_t.overload -def TextRange(start: int, end: int) -> _t.Any: ... # type: ignore[overload-cannot-match] - @_t.overload def capitalize(receiver: _t.Any, /, locale_list: _t.Any = ...) -> _t.Any: ... @_t.overload diff --git a/pythonx/compose/ui/unit/__init__.pyi b/pythonx/compose/ui/unit/__init__.pyi index bd81f8f..8db6b6e 100644 --- a/pythonx/compose/ui/unit/__init__.pyi +++ b/pythonx/compose/ui/unit/__init__.pyi @@ -1,6 +1,54 @@ -# GENERATED by pythonx/_build/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. +# GENERATED by scripts/gen_stubs.py from python-multiplatform's Kotlin-named stubs (artifact kotlin-stubs, workflow run 37131041879, commit 452cc807, Compose 1.11.1). Do not edit. import typing as _t +class _Constraints_callable_module(_t.Protocol): + """Kotlin: the function Constraints, and the members of the module Constraints""" + def __call__(self, min_width: int = ..., max_width: int = ..., min_height: int = ..., max_height: int = ...) -> _t.Any: ... + Infinity: int + +class _DpOffset_callable_module(_t.Protocol): + """Kotlin: the function DpOffset, and the members of the module DpOffset""" + def __call__(self, x: float, y: float) -> int: ... + Unspecified: int + Zero: int + +class _DpSize_callable_module(_t.Protocol): + """Kotlin: the function DpSize, and the members of the module DpSize""" + def __call__(self, width: float, height: float) -> _t.Any: ... + Unspecified: _t.Any + Zero: _t.Any + +class _IntOffset_callable_module(_t.Protocol): + """Kotlin: the function IntOffset, and the members of the module IntOffset""" + def __call__(self, x: int, y: int) -> int: ... + Max: int + Zero: int + +class _IntRect_callable_module(_t.Protocol): + """Kotlin: the function IntRect, and the members of the module IntRect""" + @_t.overload + def __call__(self, center: int, radius: int) -> _t.Any: ... + @_t.overload + def __call__(self, top_left: int, bottom_right: int) -> _t.Any: ... # type: ignore[overload-cannot-match] + @_t.overload + def __call__(self, offset: int, size: _t.Any) -> _t.Any: ... # type: ignore[overload-cannot-match] + Zero: _t.Any + +class _IntSize_callable_module(_t.Protocol): + """Kotlin: the function IntSize, and the members of the module IntSize""" + def __call__(self, width: int, height: int) -> int: ... + Zero: int + +class _TextUnit_callable_module(_t.Protocol): + """Kotlin: the function TextUnit, and the members of the module TextUnit""" + def __call__(self, value: float, type: int) -> _t.Any: ... + Unspecified: _t.Any + +class _Velocity_callable_module(_t.Protocol): + """Kotlin: the function Velocity, and the members of the module Velocity""" + def __call__(self, x: float, y: float) -> _t.Any: ... + Zero: _t.Any + class DpRect: """Kotlin: androidx.compose.ui.unit.DpRect""" @_t.overload @@ -46,26 +94,10 @@ class FontScalingLinear: class LayoutDirection: """Kotlin: androidx.compose.ui.unit.LayoutDirection""" -def Constraints(min_width: int = ..., max_width: int = ..., min_height: int = ..., max_height: int = ...) -> _t.Any: - """Kotlin: androidx.compose.ui.unit.Constraints(minWidth: kotlin.Int, maxWidth: kotlin.Int, minHeight: kotlin.Int, maxHeight: kotlin.Int): androidx.compose.ui.unit.Constraints""" - ... - def Density(density: float, font_scale: float = ...) -> _t.Any: """Kotlin: androidx.compose.ui.unit.Density(density: kotlin.Float, fontScale: kotlin.Float): androidx.compose.ui.unit.Density""" ... -def DpOffset(x: float, y: float) -> int: - """Kotlin: androidx.compose.ui.unit.DpOffset(x: androidx.compose.ui.unit.Dp, y: androidx.compose.ui.unit.Dp): androidx.compose.ui.unit.DpOffset""" - ... - -def DpSize(width: float, height: float) -> _t.Any: - """Kotlin: androidx.compose.ui.unit.DpSize(width: androidx.compose.ui.unit.Dp, height: androidx.compose.ui.unit.Dp): androidx.compose.ui.unit.DpSize""" - ... - -def IntOffset(x: int, y: int) -> int: - """Kotlin: androidx.compose.ui.unit.IntOffset(x: kotlin.Int, y: kotlin.Int): androidx.compose.ui.unit.IntOffset""" - ... - def IntRect__IntOffset_Int(center: int, radius: int) -> _t.Any: """Kotlin: androidx.compose.ui.unit.IntRect(center: androidx.compose.ui.unit.IntOffset, radius: kotlin.Int): androidx.compose.ui.unit.IntRect""" ... @@ -78,18 +110,6 @@ def IntRect__IntOffset_IntSize(offset: int, size: _t.Any) -> _t.Any: """Kotlin: androidx.compose.ui.unit.IntRect(offset: androidx.compose.ui.unit.IntOffset, size: androidx.compose.ui.unit.IntSize): androidx.compose.ui.unit.IntRect""" ... -def IntSize(width: int, height: int) -> int: - """Kotlin: androidx.compose.ui.unit.IntSize(width: kotlin.Int, height: kotlin.Int): androidx.compose.ui.unit.IntSize""" - ... - -def TextUnit(value: float, type: int) -> _t.Any: - """Kotlin: androidx.compose.ui.unit.TextUnit(value: kotlin.Float, type: androidx.compose.ui.unit.TextUnitType): androidx.compose.ui.unit.TextUnit""" - ... - -def Velocity(x: float, y: float) -> _t.Any: - """Kotlin: androidx.compose.ui.unit.Velocity(x: kotlin.Float, y: kotlin.Float): androidx.compose.ui.unit.Velocity""" - ... - def coerce_at_least(receiver: float, /, minimum_value: float) -> float: """Kotlin: androidx.compose.ui.unit.Dp.coerceAtLeast(minimumValue: androidx.compose.ui.unit.Dp): androidx.compose.ui.unit.Dp""" ... @@ -258,6 +278,22 @@ def to_size(receiver: _t.Any, /) -> int: """Kotlin: androidx.compose.ui.unit.IntSize.toSize(): androidx.compose.ui.geometry.Size""" ... +Constraints: _Constraints_callable_module + +DpOffset: _DpOffset_callable_module + +DpSize: _DpSize_callable_module + +IntOffset: _IntOffset_callable_module + +IntRect: _IntRect_callable_module + +IntSize: _IntSize_callable_module + +TextUnit: _TextUnit_callable_module + +Velocity: _Velocity_callable_module + def DpRect__DpOffset_DpSize(origin: int, size: _t.Any) -> DpRect: """Kotlin: androidx.compose.ui.unit.DpRect(origin: androidx.compose.ui.unit.DpOffset, size: androidx.compose.ui.unit.DpSize): androidx.compose.ui.unit.DpRect""" ... @@ -266,13 +302,6 @@ def DpRect__Dp_Dp_Dp_Dp(left: float, top: float, right: float, bottom: float) -> """Kotlin: androidx.compose.ui.unit.DpRect(left: androidx.compose.ui.unit.Dp, top: androidx.compose.ui.unit.Dp, right: androidx.compose.ui.unit.Dp, bottom: androidx.compose.ui.unit.Dp): androidx.compose.ui.unit.DpRect""" ... -@_t.overload -def IntRect(center: int, radius: int) -> _t.Any: ... -@_t.overload -def IntRect(top_left: int, bottom_right: int) -> _t.Any: ... # type: ignore[overload-cannot-match] -@_t.overload -def IntRect(offset: int, size: _t.Any) -> _t.Any: ... # type: ignore[overload-cannot-match] - @_t.overload def constrain(receiver: int, /, other_constraints: int) -> _t.Any: ... @_t.overload diff --git a/pythonx/_build/gen_stubs.py b/scripts/gen_stubs.py similarity index 92% rename from pythonx/_build/gen_stubs.py rename to scripts/gen_stubs.py index 2ea0680..1473016 100644 --- a/pythonx/_build/gen_stubs.py +++ b/scripts/gen_stubs.py @@ -1,6 +1,6 @@ """Generate `pythonx.compose` stubs from the binder's Kotlin-named stubs, by the runtime's own rule. - python3 pythonx/_build/gen_stubs.py [--out pythonx/compose/..] + python3 scripts/gen_stubs.py [--out pythonx/compose/..] The input is what python-multiplatform's `PythonStubsTask` writes, either as its CI artefact `kotlin-stubs` (a zip, read in place) or unpacked: one `androidx/compose/.../__init__.pyi` per Kotlin @@ -17,12 +17,17 @@ input with explicit overloads only (`padding__Dp`, the first upstream format) also gets `@overload`s of the base name, fewest parameters first because a type checker takes the first match; -- a stub class keeps its name. Its members are renamed with `python_name`, which is the binder's - member-resolver rule, and the parameters of a member's callable protocol are snake_case like a - module function's, because the resolver also translates a method's keywords (the receiver and +- a stub class keeps its name. Its members are renamed with `python_name`, the binder's own + rule since #131, and the parameters of a member's callable protocol are snake_case like a + module function's, because the binder also translates a method's keywords (the receiver and anonymous slots keep their names). The stubs are Pythonic-only: a method's Kotlin keyword still works at run time and is a type error here. A static method (a function of a Kotlin object) is reached through `KotlinObject`, so its parameters are snake_case too; +- the binder's own alias names (python-multiplatform #131: `fill_max_width = fillMaxWidth` in a module + stub, an alias `ClassVar` or property per proxy member) are not carried over. Renaming the + Kotlin-named declaration by the rule above gives the same name, so each name is declared once and + no Kotlin second name or self-referential line remains. A name the binder declares as a callable + module (`PaddingValues: _PaddingValues_callable_module`) gets no base-name overloads beside it; - a Kotlin object served as a sub-package (`Alignment`) becomes a class in its parent module's stub, whether the input has it as `/__init__.pyi` or already as a class in the parent stub (python-multiplatform #44). A sub-package directory holding only `__init__.pyi` would be a @@ -62,7 +67,7 @@ from functools import cached_property from pathlib import Path -REPO = Path(__file__).resolve().parents[2] +REPO = Path(__file__).resolve().parents[1] if str(REPO) not in sys.path: sys.path.insert(0, str(REPO)) @@ -74,7 +79,7 @@ def header(source: str) -> str: - return f"# GENERATED by pythonx/_build/gen_stubs.py from {source}. Do not edit.\n" + return f"# GENERATED by scripts/gen_stubs.py from {source}. Do not edit.\n" # ---------------------------------------------------------------------------------------------- input @@ -188,6 +193,39 @@ def _is_static(function: ast.FunctionDef) -> bool: return any(ast.unparse(d) == "staticmethod" for d in function.decorator_list) +def _is_alias_line(node: ast.stmt) -> bool: + """`fill_max_width = fillMaxWidth`, the binder's own snake_case name (python-multiplatform #131). + + The generator names every declaration by the same rule, so the line adds nothing: dropped. + """ + return isinstance(node, ast.Assign) and isinstance(node.value, ast.Name) and all( + isinstance(target, ast.Name) for target in node.targets + ) + + +def _without_repeats(body: list[ast.stmt]) -> list[ast.stmt]: + """[body] without a member that repeats the one before it under the same name. + + The binder's alias member (`fill_max_width` beside `fillMaxWidth`, python-multiplatform #131) + is, once both are renamed, the same declaration twice. A member keeps its docstring statement. + """ + seen: set[str] = set() + kept: list[ast.stmt] = [] + skipping = False + for node in body: + if isinstance(node, ast.Expr) and kept and (skipping or isinstance(kept[-1], ast.AnnAssign)): + if not skipping: + kept.append(node) + continue + key = ast.dump(node) + skipping = key in seen and not isinstance(node, ast.ClassDef) + if skipping: + continue + seen.add(key) + kept.append(node) + return kept + + def _is_protocol(cls: ast.ClassDef) -> bool: return any(ast.unparse(base) in (f"{TYPING}.Protocol", "Protocol") for base in cls.bases) @@ -416,7 +454,7 @@ def convert(self, source: str, kotlin_package: str = "", module_name: str = "") body: list[ast.stmt] = [] explicit: dict[str, list[ast.FunctionDef]] = {} for node in tree.body: - if isinstance(node, (ast.Import, ast.ImportFrom)): + if isinstance(node, (ast.Import, ast.ImportFrom)) or _is_alias_line(node): continue if isinstance(node, ast.FunctionDef): node.name = python_name(node.name) @@ -431,7 +469,12 @@ def convert(self, source: str, kotlin_package: str = "", module_name: str = "") self._convert_class(node) body.append(node) - defined = {node.name for node in body if isinstance(node, ast.FunctionDef)} + # A name the binder declares as a callable module (`PaddingValues: _PaddingValues_callable_module`) + # is that declaration; its explicit overloads are the protocol's `__call__`, not a second name. + defined = {node.name for node in body if isinstance(node, ast.FunctionDef)} | { + node.target.id for node in body + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name) + } classes = {node.name for node in body if isinstance(node, ast.ClassDef)} for base, variants in explicit.items(): if base in defined: @@ -492,6 +535,7 @@ def _convert_class(self, cls: ast.ClassDef) -> None: _rename_arguments(member.args) elif isinstance(member, ast.ClassDef): self._convert_class(member) + cls.body = _without_repeats(cls.body) def _add_object(self, body: list[ast.stmt], kotlin_package: str, name: str, imported: set[str]) -> None: """Fold the object stub `//__init__.pyi` into a class of the parent.""" @@ -525,6 +569,7 @@ def _add_object(self, body: list[ast.stmt], kotlin_package: str, name: str, impo if isinstance(node, ast.ClassDef): # A Kotlin type the object nests (`Alignment.Horizontal`): a class of the folded object. node.bases = [bases.visit(base) for base in node.bases] + self._convert_class(node) # its members follow the same rule, at any depth groups.append([node]) elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): node.target.id = python_name(node.target.id) diff --git a/tests/fake_host.py b/tests/fake_host.py index c9a9237..ae4ff1e 100644 --- a/tests/fake_host.py +++ b/tests/fake_host.py @@ -62,6 +62,7 @@ ARRANGEMENT_VERTICAL = ARRANGEMENT + ".Vertical" ARRANGEMENT_BOTH = ARRANGEMENT + ".HorizontalOrVertical" ALIGNMENT = "androidx.compose.ui.Alignment" +COLOR = "androidx.compose.ui.graphics.Color" ALIGNMENT_HORIZONTAL = ALIGNMENT + ".Horizontal" ALIGNMENT_VERTICAL = ALIGNMENT + ".Vertical" @@ -303,6 +304,7 @@ def body(receiver, args): def _build(self): self._scalar_boxes() self._build_text_field() + self._build_color() self._add( EMPTY_MODIFIER, 0, (), (), (), "OBJECT", MODIFIER, False, None, (), lambda args: StubModifier(), @@ -542,6 +544,19 @@ def _unbox(self, args): self.release(args[0]) return held + def _build_color(self): + """`Color`: a Kotlin object whose name is also a function in its package (python-multiplatform #78). + + The binder makes such a module callable (`_make_callable`): `Color(0xFFFFFFFF)` calls the + function, `Color.Red` reads a constant of the object. One overload is modelled; the real + `Color(Int)` / `Color(Long)` pair and its ambiguity are python-multiplatform #146. + """ + self._add( + COLOR, 1, ("color",), ("INT",), ("kotlin.Long",), "OBJECT", COLOR, False, None, + (False,), lambda args: StubConstant(f"Color({args[0]:#x})"), + ) + self._constant(f"{COLOR}.Red", COLOR) + def _constant(self, name, declared): self._add(name, 0, (), (), (), "OBJECT", declared, False, None, (), lambda args, label=name.rpartition(".")[2]: StubConstant(label), kind="STATIC_GETTER") diff --git a/tests/fixtures/kotlin_stubs_v5/androidx/compose/foundation/layout/__init__.pyi b/tests/fixtures/kotlin_stubs_v5/androidx/compose/foundation/layout/__init__.pyi new file mode 100644 index 0000000..5d0ba50 --- /dev/null +++ b/tests/fixtures/kotlin_stubs_v5/androidx/compose/foundation/layout/__init__.pyi @@ -0,0 +1,35 @@ +# GENERATED by python-multiplatform-gradle-plugin. Do not edit. + +import typing as _t +import androidx.compose.ui + +class _PaddingValues_callable_module(_t.Protocol): + """Kotlin: the function PaddingValues, and the members of the module PaddingValues""" + def __call__(self, all: float) -> _t.Any: ... + Zero: _t.Any + +def Column(verticalAlignment: androidx.compose.ui.Alignment.Vertical = ..., *, content: _t.Callable[[], None]) -> None: + """Kotlin: androidx.compose.foundation.layout.Column(verticalAlignment: androidx.compose.ui.Alignment.Vertical, content: kotlin.Function0): kotlin.Unit""" + ... + +def fillMaxWidth(receiver: androidx.compose.ui.Modifier, /, fraction: float = ...) -> androidx.compose.ui.Modifier: + """Kotlin: androidx.compose.ui.Modifier.fillMaxWidth(fraction: kotlin.Float): androidx.compose.ui.Modifier""" + ... + +def fillMaxHeight(receiver: androidx.compose.ui.Modifier, /, fraction: float = ...) -> androidx.compose.ui.Modifier: + """Kotlin: androidx.compose.ui.Modifier.fillMaxHeight(fraction: kotlin.Float): androidx.compose.ui.Modifier""" + ... + +def scrollable(receiver: androidx.compose.ui.Modifier, /, state: _t.Any, enabled: bool = ..., on_click: _t.Callable[[], None] = ...) -> androidx.compose.ui.Modifier: + """Kotlin: androidx.compose.ui.Modifier.scrollable(state: kotlin.Any, enabled: kotlin.Boolean, onClick: kotlin.Function0): androidx.compose.ui.Modifier""" + ... + +def PaddingValues__Dp(all: float) -> _t.Any: + """Kotlin: androidx.compose.foundation.layout.PaddingValues(all: androidx.compose.ui.unit.Dp): androidx.compose.foundation.layout.PaddingValues""" + ... + +PaddingValues: _PaddingValues_callable_module + +fill_max_height = fillMaxHeight + +fill_max_width = fillMaxWidth diff --git a/tests/fixtures/kotlin_stubs_v5/androidx/compose/ui/__init__.pyi b/tests/fixtures/kotlin_stubs_v5/androidx/compose/ui/__init__.pyi new file mode 100644 index 0000000..7698b6c --- /dev/null +++ b/tests/fixtures/kotlin_stubs_v5/androidx/compose/ui/__init__.pyi @@ -0,0 +1,44 @@ +# GENERATED by python-multiplatform-gradle-plugin. Do not edit. + +import typing as _t +import androidx.compose.ui.unit + +class _Modifier_fillMaxWidth(_t.Protocol): + def __call__(self, fraction: float = ...) -> Modifier: ... + +class _Modifier_onClick(_t.Protocol): + def __call__(self, enabled: bool = ..., on_click: _t.Callable[[], None] = ...) -> Modifier: ... + +class Modifier: + """Kotlin: androidx.compose.ui.Modifier""" + fillMaxWidth: _t.ClassVar[_Modifier_fillMaxWidth] + onClick: _t.ClassVar[_Modifier_onClick] + fill_max_width: _t.ClassVar[_Modifier_fillMaxWidth] + on_click: _t.ClassVar[_Modifier_onClick] + +class Scene: + """Kotlin: androidx.compose.ui.Scene""" + @property + def layoutDirection(self) -> androidx.compose.ui.unit.LayoutDirection: + """Kotlin property: androidx.compose.ui.Scene.layoutDirection (androidx.compose.ui.Scene.layoutDirection: androidx.compose.ui.unit.LayoutDirection)""" + ... + @layoutDirection.setter + def layoutDirection(self, value: androidx.compose.ui.unit.LayoutDirection) -> None: + """Kotlin property: androidx.compose.ui.Scene.layoutDirection=""" + ... + @property + def isMinimized(self) -> bool: + """Kotlin property: androidx.compose.ui.Scene.isMinimized (androidx.compose.ui.Scene.isMinimized: kotlin.Boolean)""" + ... + @property + def layout_direction(self) -> androidx.compose.ui.unit.LayoutDirection: + """Kotlin property: androidx.compose.ui.Scene.layoutDirection (androidx.compose.ui.Scene.layoutDirection: androidx.compose.ui.unit.LayoutDirection)""" + ... + @layout_direction.setter + def layout_direction(self, value: androidx.compose.ui.unit.LayoutDirection) -> None: + """Kotlin property: androidx.compose.ui.Scene.layoutDirection=""" + ... + @property + def is_minimized(self) -> bool: + """Kotlin property: androidx.compose.ui.Scene.isMinimized (androidx.compose.ui.Scene.isMinimized: kotlin.Boolean)""" + ... diff --git a/tests/test_app_root.py b/tests/test_app_root.py index 2830cc0..70b4278 100644 --- a/tests/test_app_root.py +++ b/tests/test_app_root.py @@ -1,6 +1,7 @@ """`@app`, `state` and `app_root` in `pythonx.compose.runtime` (issue #11, docs/INTENT.md section 5.1). -The host draws with `PythonContent("pythonx.compose.runtime", "app_root")`: it reads the module +The host draws with +`PythonAppView(module = "pythonx.compose.runtime", attribute = "app_root")`: it reads the module attribute `app_root`, a Compose State whose `.value` is a zero-argument callable or None. `@app` writes that value, so redeclaring the root replaces the screen; there is no update function. diff --git a/tests/test_chain.py b/tests/test_chain.py index 723546c..aef4587 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -61,12 +61,14 @@ def describe(self, modifier): return ui.describe_modifier(modifier) def needs_member_resolver(self): - """Receiver methods by snake_case name need the binder's member-resolver hook. + """Receiver methods and their keywords by snake_case name are the binder's own. - python-multiplatform #17 adds it; until a checkout has it, these tests skip rather than pass. + python-multiplatform #131 serves them; on an older binder these tests skip rather than pass. """ - if not hasattr(self.binding, "add_member_resolver"): - self.skipTest("the binder has no add_member_resolver yet (python-multiplatform #17)") + import python_multiplatform + + if not hasattr(python_multiplatform, "python_name"): + self.skipTest("the binder does not serve snake_case names yet (python-multiplatform #131)") class TheBinderSourcesAreReadable(AdapterCase): @@ -207,19 +209,11 @@ def test_the_module_function_dispatches_with_snake_case_keywords(self): class MethodKeywords(AdapterCase): - """SPEC S4.1: a method's keywords are snake_case like a module function's. - - The member resolver answers `(kotlin_name, {python_keyword: kotlinParameter})`, built from - `python_multiplatform.describe_member(type, member)` (python-multiplatform #54). - """ + """SPEC S4.1: a method's keywords are snake_case like a module function's (the binder's, #131).""" def setUp(self): super().setUp() - import python_multiplatform - self.needs_member_resolver() - if not hasattr(python_multiplatform, "describe_member"): - self.skipTest("the binder has no describe_member(type, member) yet (python-multiplatform #54)") def values(self): import pythonx.compose.foundation.layout as layout @@ -253,58 +247,26 @@ def test_an_unknown_keyword_still_reaches_the_binders_refusal_with_its_candidate self.assertIn("Candidates", message) self.assertIn("padding__PaddingValues", message) - def test_the_map_is_metadata_and_is_asked_for_once_per_member(self): - import python_multiplatform - - original, asked = python_multiplatform.describe_member, [] - def counting(*args): - asked.append(args) - return original(*args) - - python_multiplatform.describe_member = counting - self.addCleanup(setattr, python_multiplatform, "describe_member", original) - modifier = self.empty() - modifier.padding(padding_values=self.values()) - modifier.padding(padding_values=self.values()) - self.assertEqual([(fake_host.MODIFIER, "padding")], asked) - - def test_the_map_lists_every_overloads_parameters_and_not_the_receiver(self): - from pythonx.compose._reexport import member_name - - answer = member_name(fake_host.MODIFIER, "padding", ["padding"]) - self.assertEqual("padding", answer[0]) - self.assertEqual("paddingValues", answer[1]["padding_values"]) - self.assertNotIn("receiver", answer[1]) - self.assertNotIn("horizontal", answer[1]) # one word: Kotlin's spelling is the same +class TheBindersNamingRuleIsThisPackages(AdapterCase): + """python-multiplatform #131 restored the binder's own snake_case names, with this package's rule. + The two must not drift: a name the binder serves and a name this package's stubs and re-export + spell would otherwise differ. Skips on a binder that renames nothing. + """ -class MethodKeywordsNeedTheBindersDescription(AdapterCase): - """Without `describe_member` a method's keywords are Kotlin's and its names still resolve.""" + NAMES = ("fillMaxWidth", "toURLString", "zIndex", "padding__Dp_Dp", "onClick", "Modifier", + "paddingValuesOf", "HTMLParser", "layoutDirection", "spacedBy", "x") - def setUp(self): - super().setUp() + def test_both_rules_agree(self): import python_multiplatform + from pythonx.compose._reexport import python_name - self.needs_member_resolver() - original = getattr(python_multiplatform, "describe_member", None) - if original is not None: - del python_multiplatform.describe_member - self.addCleanup(setattr, python_multiplatform, "describe_member", original) - - def test_the_name_resolves_and_kotlin_keywords_work(self): - import pythonx.compose.foundation.layout as layout - - modifier = self.empty().fill_max_width() - self.assertEqual("fillMaxWidth", self.describe(modifier)) - self.empty().padding(paddingValues=layout.padding_values_of(8)) - self.assertEqual("padding__PaddingValues", self.host.calls[-1]) - - def test_a_snake_case_keyword_is_the_binders_refusal(self): - import pythonx.compose.foundation.layout as layout - - with self.assertRaises(TypeError): - self.empty().padding(padding_values=layout.padding_values_of(8)) + if not hasattr(python_multiplatform, "python_name"): + self.skipTest("this binder predates python-multiplatform #131") + for name in self.NAMES: + with self.subTest(name=name): + self.assertEqual(python_name(name), python_multiplatform.python_name(name)) class Names(AdapterCase): @@ -753,6 +715,30 @@ def test_a_kotlin_name_still_says_the_binder_is_missing(self): ui.Modifier # noqa: B018 +class CallableKotlinObjects(AdapterCase): + """A Kotlin object that is also a function (`Color`) is callable through pythonx (issue #88).""" + + def label_of(self, proxy): + return self.host._object(proxy._pm_handle).label + + def test_calling_the_object_calls_the_function(self): + import pythonx.compose.ui.graphics as graphics + + self.assertEqual("Color(0xffffffff)", self.label_of(graphics.Color(0xFFFFFFFF))) + + def test_its_constants_are_still_a_namespace(self): + import pythonx.compose.ui.graphics as graphics + + self.assertEqual("Red", self.label_of(graphics.Color.Red)) + + def test_an_object_that_is_no_function_stays_uncallable(self): + import pythonx.compose.ui as ui + + self.assertFalse(callable(ui.Alignment)) + with self.assertRaises(TypeError): + ui.Alignment() + + class Handles(AdapterCase): """The binder's proxy owns a handle; dropping it gives the handle back.""" diff --git a/tests/test_stubs.py b/tests/test_stubs.py index 654d928..3f28c57 100644 --- a/tests/test_stubs.py +++ b/tests/test_stubs.py @@ -29,7 +29,7 @@ class per bound Kotlin type with its extension members as `ClassVar`s of callabl HERE = Path(__file__).resolve().parent REPO = HERE.parent sys.path.insert(0, str(HERE)) -sys.path.insert(0, str(REPO / "pythonx" / "_build")) +sys.path.insert(0, str(REPO / "scripts")) sys.path.insert(0, str(REPO)) import adapter as adapter_loader # noqa: E402 @@ -40,6 +40,7 @@ class per bound Kotlin type with its extension members as `ClassVar`s of callabl KOTLIN_STUBS_V2 = HERE / "fixtures" / "kotlin_stubs_v2" KOTLIN_STUBS_V3 = HERE / "fixtures" / "kotlin_stubs_v3" KOTLIN_STUBS_V4 = HERE / "fixtures" / "kotlin_stubs_v4" +KOTLIN_STUBS_V5 = HERE / "fixtures" / "kotlin_stubs_v5" ARTEFACT = REPO / ".tmp" / "kotlin-stubs.zip" PACKAGE = REPO / "pythonx" COMPOSE = PACKAGE / "compose" @@ -221,7 +222,7 @@ def test_every_protocol_parameter_is_snake_case_and_keyword_only_stays_so(self): def test_the_header_records_the_input_and_says_nothing_of_kotlin_keywords(self): header = self.stubs[UI].splitlines()[:2] - self.assertTrue(header[0].startswith("# GENERATED by pythonx/_build/gen_stubs.py"), header[0]) + self.assertTrue(header[0].startswith("# GENERATED by scripts/gen_stubs.py"), header[0]) self.assertNotIn("keyword", self.stubs[UI].splitlines()[1]) self.assertNotIn("Methods are reached by", self.stubs[UI]) @@ -411,6 +412,111 @@ def test_a_class_with_no_such_constants_gets_no_groups(self): self.assertEqual({}, _nested(cls)) +class TheNestedCompanion(unittest.TestCase): + """Issue #86: a member of a class nested in an object's class is renamed like any other member.""" + + OBJECT = ( + "import typing as _t\n\n" + "class Companion:\n" + " \"\"\"Kotlin: a.b.Op.Companion\"\"\"\n" + " @property\n" + " def reverseDifference(self) -> _t.Any:\n" + " \"\"\"Kotlin property: a.b.Op.Companion.reverseDifference\"\"\"\n" + " ...\n" + " @property\n" + " def reverse_difference(self) -> _t.Any:\n" + " \"\"\"Kotlin property: a.b.Op.Companion.reverseDifference\"\"\"\n" + " ...\n" + " @property\n" + " def xor(self) -> _t.Any:\n" + " \"\"\"Kotlin property: a.b.Op.Companion.xor\"\"\"\n" + " ...\n" + " @property\n" + " def Upper(self) -> _t.Any:\n" + " \"\"\"Kotlin property: a.b.Op.Companion.Upper\"\"\"\n" + " ...\n" + " def doIt(self, value: int) -> _t.Any: ...\n" + "\n" + "class Inner:\n" + " \"\"\"Kotlin: a.b.Op.Inner\"\"\"\n" + " class Deeper:\n" + " \"\"\"Kotlin: a.b.Op.Inner.Deeper\"\"\"\n" + " def deepName(self) -> _t.Any: ...\n" + ) + + def convert(self) -> ast.ClassDef: + stubs = gen_stubs.KotlinStubs(KOTLIN_STUBS_V2) + stubs.files["a/b/Op/__init__.pyi"] = self.OBJECT + return _classes(gen_stubs.Converter(stubs).convert("import typing as _t\n", "a.b"))["Op"] + + def test_a_nested_classs_members_are_renamed_at_any_depth(self): + op = self.convert() + companion = _nested(op)["Companion"] + names = [n.name for n in companion.body if isinstance(n, ast.FunctionDef)] + self.assertEqual(["reverse_difference", "xor", "Upper", "do_it"], names) + deeper = _nested(_nested(op)["Inner"])["Deeper"] + self.assertEqual(["deep_name"], [n.name for n in deeper.body if isinstance(n, ast.FunctionDef)]) + + +class TheBindersOwnAliases(unittest.TestCase): + """The format of python-multiplatform #131: the binder writes its snake_case aliases itself. + + A module stub carries `fill_max_width = fillMaxWidth` beside the Kotlin-named def, and a proxy + class carries an extra `ClassVar` or property per member. The pythonx stubs stay Pythonic-only: + one name per declaration, no Kotlin second name, no self-referential line. + """ + + def setUp(self): + self.stubs = _generated(KOTLIN_STUBS_V5) + + def test_a_module_function_has_one_declaration_under_its_pythonic_name(self): + functions = _functions(self.stubs[LAYOUT]) + for name in ("fill_max_width", "fill_max_height", "scrollable"): + with self.subTest(name=name): + self.assertEqual(1, len(functions[name])) + self.assertNotIn("fillMaxWidth", functions) + + def test_the_binders_alias_lines_are_not_carried_over(self): + tree = ast.parse(self.stubs[LAYOUT]) + self.assertEqual([], [ast.unparse(n) for n in tree.body if isinstance(n, ast.Assign)]) + + def test_a_module_the_binder_calls_has_no_second_declaration_of_its_name(self): + """`PaddingValues: _PaddingValues_callable_module` is the name; its explicit overloads add no `def`.""" + text = self.stubs[LAYOUT] + self.assertNotIn("PaddingValues", _functions(text)) + self.assertEqual(1, sum(line.startswith("PaddingValues:") for line in text.splitlines())) + self.assertIn("class _PaddingValues_callable_module", text) + + def test_no_line_assigns_a_name_to_itself(self): + for path, text in self.stubs.items(): + for line in text.splitlines(): + left, sep, right = line.partition(" = ") + with self.subTest(path=path.relative_to(PACKAGE).as_posix(), line=line): + self.assertFalse(sep and left.isidentifier() and left == right.strip()) + + def test_a_proxy_class_has_each_member_once_under_its_pythonic_name(self): + modifier = _classes(self.stubs[UI])["Modifier"] + names = [n.target.id for n in modifier.body if isinstance(n, ast.AnnAssign)] + self.assertEqual(["fill_max_width", "on_click"], names) + + def test_a_property_and_its_setter_are_kept_once(self): + scene = _classes(self.stubs[UI])["Scene"] + functions = [n for n in scene.body if isinstance(n, ast.FunctionDef)] + self.assertEqual(["layout_direction", "layout_direction", "is_minimized"], [f.name for f in functions]) + self.assertEqual( + [["property"], ["layout_direction.setter"], ["property"]], + [[ast.unparse(d) for d in f.decorator_list] for f in functions], + ) + + def test_keywords_the_binder_already_wrote_in_snake_case_stay_unchanged(self): + scrollable = _functions(self.stubs[LAYOUT])["scrollable"][0] + self.assertEqual(["receiver", "state", "enabled", "on_click"], _parameter_names(scrollable)) + for name in ("on_click", "fill_max_width", "max_height", "index_in_line", "always_show_label"): + with self.subTest(name=name): + self.assertEqual(name, gen_stubs.python_name(name)) + self.assertEqual(name, gen_stubs.snake_case(name)) + + class TheObjectAndPropertyFormat(unittest.TestCase): """The upstream format of python-multiplatform #53 (objects), #38 (properties) and #44.""" diff --git a/tests/test_wheel.py b/tests/test_wheel.py index db8cfa4..86d76c5 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -11,6 +11,7 @@ from __future__ import annotations import importlib.util +import os import shutil import subprocess import sys @@ -41,6 +42,24 @@ def _build_wheel(out: Path) -> Path: return wheel +_SCENARIO = """ +import sys +from pathlib import Path +sys.path.insert(0, {tests!r}) +import adapter, fake_host +adapter._REPO = Path({tests!r}) # do not put the source checkout on sys.path +binding = adapter.install() +host = fake_host.FakeHost(); host.bind(); host.register(binding) +import pythonx.compose.ui.graphics as graphics +assert "site-packages" in graphics.__file__, graphics.__file__ +white = graphics.Color(0xFFFFFFFF) # held: dropping a proxy releases its handle +red = graphics.Color.Red +assert host._object(white._pm_handle).label == "Color(0xffffffff)" +assert host._object(red._pm_handle).label == "Red" +print("installed wheel ok") +""" + + class TheWheel(unittest.TestCase): @classmethod @@ -51,6 +70,7 @@ def setUpClass(cls): SCRATCH.mkdir(exist_ok=True) cls._dir = tempfile.TemporaryDirectory(dir=SCRATCH) wheel = _build_wheel(Path(cls._dir.name)) + cls.wheel = wheel with zipfile.ZipFile(wheel) as archive: cls.names = set(archive.namelist()) @@ -70,6 +90,11 @@ def test_every_mapped_module_is_a_file_in_the_wheel(self): ) self.assertEqual([], missing, "a mapped module with no file cannot be imported once installed") + def test_no_build_tooling_or_tests_ship(self): + for prefix in ("scripts/", "tests/", "docs/", ".github/"): + with self.subTest(prefix=prefix): + self.assertEqual([], sorted(n for n in self.names if n.startswith(prefix))) + def test_the_re_export_rule_ships(self): self.assertIn("pythonx/compose/_reexport.py", self.names) @@ -81,6 +106,28 @@ def test_the_generated_stubs_ship(self): with self.subTest(stub=stub): self.assertIn(stub, self.names) + def test_color_is_callable_from_the_installed_wheel(self): + """Issue #88: the installed package, not the source tree, calls `Color(0xFFFFFFFF)` through the binder.""" + sys.path.insert(0, str(REPO / "tests")) + try: + import adapter + home = adapter.python_multiplatform_home() + finally: + sys.path.pop(0) + if not (home / adapter._ADAPTER_KT).is_file(): + self.skipTest("no python-multiplatform checkout to install the binder from") + venv = Path(self._dir.name) / "venv" + subprocess.run([sys.executable, "-m", "venv", str(venv)], check=True) + python = venv / "bin" / "python" + subprocess.run([str(python), "-m", "pip", "install", "-q", "--no-deps", "--no-index", str(self.wheel)], + check=True, capture_output=True, text=True) + script = _SCENARIO.format(tests=str(REPO / "tests")) + env = dict(os.environ, PYTHONMULTIPLATFORM_HOME=str(home)) + result = subprocess.run([str(python), "-c", script], capture_output=True, text=True, env=env, + cwd=str(venv)) + self.assertEqual(0, result.returncode, result.stderr) + self.assertIn("installed wheel ok", result.stdout) + if __name__ == "__main__": unittest.main()