Skip to content

Commit a50be42

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feat/tp-module-report
2 parents aea7fca + 902a389 commit a50be42

31 files changed

Lines changed: 1653 additions & 113 deletions

File tree

‎.github/workflows/android-native.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ on:
66
pull_request:
77
branches: [ "develop", "main" ]
88

9+
concurrency:
10+
# A newer push to the same ref supersedes this run; frees the shared macOS runners.
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
914
jobs:
1015
compile:
1116
name: Compile Android Native

‎.github/workflows/desktop.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ on:
66
pull_request:
77
branches: [ "develop", "main" ]
88

9+
concurrency:
10+
# A newer push to the same ref supersedes this run; frees the shared macOS runners.
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
914
jobs:
1015
test:
1116
name: Desktop Tests

‎.github/workflows/ios.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ on:
66
pull_request:
77
branches: [ "develop", "main" ]
88

9+
concurrency:
10+
# A newer push to the same ref supersedes this run; frees the shared macOS runners.
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
914
jobs:
1015
compile:
1116
name: Compile iOS Simulator Arm64

‎.github/workflows/main-source-guard.yml‎

Lines changed: 0 additions & 24 deletions
This file was deleted.

‎.github/workflows/release-sync.yml‎

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ on:
1010
permissions:
1111
contents: write
1212
pull-requests: write
13-
statuses: write
1413

1514
concurrency:
1615
group: release-sync
@@ -51,17 +50,3 @@ jobs:
5150
else
5251
gh pr create --head release --base main --title "$title" --body "$body"
5352
fi
54-
55-
# A PR opened with GITHUB_TOKEN triggers no workflows, so main-source-guard
56-
# never runs on it. Report the guard's verdict for the release commit here:
57-
# this job just generated it from develop, which is exactly what the guard
58-
# requires. A status set with GITHUB_TOKEN satisfies a required check.
59-
- name: Mark release as passing the main source guard
60-
env:
61-
GH_TOKEN: ${{ github.token }}
62-
run: |
63-
set -eu
64-
if ! git rev-parse -q --verify refs/heads/release >/dev/null; then exit 0; fi
65-
gh api "repos/${GITHUB_REPOSITORY}/statuses/$(git rev-parse release)" \
66-
-f state=success -f context=only-release-into-main \
67-
-f description="generated from develop ${GITHUB_SHA::7} by release-sync"

‎.github/workflows/typedpython.yml‎

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ name: TypedPython CI
55
on:
66
push:
77
branches: [ "develop", "main" ]
8-
paths: [ "python-multiplatform-ksp/**", ".github/workflows/typedpython.yml" ]
8+
paths: [ "python-multiplatform-ksp/**", "ksp-fixtures/app/**", ".github/workflows/typedpython.yml" ]
99
pull_request:
1010
branches: [ "develop", "main" ]
11-
paths: [ "python-multiplatform-ksp/**", ".github/workflows/typedpython.yml" ]
11+
paths: [ "python-multiplatform-ksp/**", "ksp-fixtures/app/**", ".github/workflows/typedpython.yml" ]
12+
13+
concurrency:
14+
# A newer push to the same ref supersedes this run; frees the shared macOS runners.
15+
group: ${{ github.workflow }}-${{ github.ref }}
16+
cancel-in-progress: true
1217

1318
jobs:
1419
test:
@@ -75,3 +80,48 @@ jobs:
7580
env $(.venv/bin/python tools/sanitizers.py env) \
7681
.venv/bin/python -m pytest -q -m "not measurement" \
7782
src/test/python/test_cgen.py src/test/python/test_runtime.py src/test/python/test_pipeline.py
83+
84+
app-fixture:
85+
# #45: a TypedPython-compiled module calling Kotlin through the upcall table, in the app
86+
# fixture's desktop JVM. buildTypedPythonBridge needs a host Python with pyrefly; without one the
87+
# four TypedPythonKotlinCallTest tests skip. Locally that is allowed; here a skip fails the job.
88+
# macOS only: elsewhere the JVM does not load libpython with global symbols, so the
89+
# dynamic_lookup extension cannot resolve the C API (ksp-fixtures/app/build.gradle.kts).
90+
name: TypedPython from Kotlin, app fixture (macos-latest)
91+
runs-on: macos-latest
92+
steps:
93+
- uses: actions/checkout@v4
94+
95+
- uses: actions/setup-java@v4
96+
with:
97+
java-version: '21'
98+
distribution: 'zulu'
99+
cache: 'gradle'
100+
101+
- uses: astral-sh/setup-uv@v5
102+
103+
- name: Create the TypedPython host environment
104+
working-directory: python-multiplatform-ksp
105+
run: |
106+
uv venv .venv --python 3.13
107+
uv pip install --python .venv/bin/python "pyrefly==1.3.2" pytest
108+
109+
- name: App fixture tests
110+
run: |
111+
rm -rf ksp-fixtures/app/build/test-results/desktopTest
112+
./gradlew :ksp-fixtures:app:desktopTest --rerun --console=plain \
113+
-PtypedpythonPython="$PWD/python-multiplatform-ksp/.venv/bin/python"
114+
115+
- name: The TypedPython tests ran (none skipped)
116+
run: |
117+
python3 - <<'PY'
118+
import glob, sys, xml.etree.ElementTree as ET
119+
cases = [c for p in glob.glob("ksp-fixtures/app/build/test-results/desktopTest/*.xml")
120+
for c in ET.parse(p).getroot().iter("testcase")
121+
if c.get("classname") == "fixture.app.TypedPythonKotlinCallTest"]
122+
skipped = [c.get("name") for c in cases if c.find("skipped") is not None]
123+
print(f"TypedPythonKotlinCallTest: {len(cases)} test(s), {len(skipped)} skipped")
124+
if not cases or skipped:
125+
print("skipped: " + ", ".join(skipped) if skipped else "no TypedPythonKotlinCallTest results")
126+
sys.exit(1)
127+
PY

‎.github/workflows/wasm.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ on:
1818
pull_request:
1919
branches: [ "develop", "main" ]
2020

21+
concurrency:
22+
# A newer push to the same ref supersedes this run; frees the shared macOS runners.
23+
group: ${{ github.workflow }}-${{ github.ref }}
24+
cancel-in-progress: true
25+
2126
jobs:
2227
compile:
2328
name: WasmJS compile (no interpreter)

‎AGENTS.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ ask first.**
3131

3232
Writing to *another* repository is not an exception either. Do it only when told to work there.
3333

34+
### Do not add top-level folders
35+
36+
**Never add a new directory (or a new file) at the repository root on your own.** The root layout is
37+
the maintainer's: source modules, `docs/`, `gradle/`, `.github/` and the files that tools require
38+
there. Work belongs inside an existing module or directory — sources under `src/<sourceSet>/`,
39+
CI-only scripts under `.github/scripts/`, temporary files under the git-ignored `.tmp/`. If you think
40+
a new top-level entry is needed, propose it (what, why, which alternatives inside existing
41+
directories you ruled out) and wait for approval. This was added after unapproved root folders
42+
(`ksp-fixtures/`, `tools/`, `kotlin-js-store/`, `iosApp/`, `sample/python`) had to be dismantled.
43+
3444
## 3. Worktrees link large artefacts instead of copying them
3545

3646
A worktree is a full checkout. Copying large untracked artefacts (prebuilt runtimes, vendored trees,

‎build.gradle.kts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,14 @@ tasks.register("publishAllToMavenLocal") {
2929
dependsOn(":python-multiplatform-ksp:publishToMavenLocal")
3030
dependsOn(gradle.includedBuild("python-multiplatform-gradle-plugin").task(":publishToMavenLocal"))
3131
}
32+
33+
// The build's single Kotlin/Wasm npm lock (one workspace for every wasm module) lives next to the
34+
// version catalog instead of a root `kotlin-js-store/` (issue #110). KGP only allows this on the
35+
// root project; the `*Property` forms are the non-deprecated API from Kotlin 2.4 on. Mismatch
36+
// reporting keeps its default (FAIL), so CI notices a drifted lock.
37+
plugins.withType<org.jetbrains.kotlin.gradle.targets.wasm.yarn.WasmYarnPlugin> {
38+
extensions.getByType<org.jetbrains.kotlin.gradle.targets.wasm.yarn.WasmYarnRootExtension>().apply {
39+
lockFileDirectoryProperty.set(layout.projectDirectory.dir("gradle"))
40+
lockFileNameProperty.set("wasm-yarn.lock")
41+
}
42+
}

‎docs/SPEC.md‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,13 @@ All in `PM/commonTest`, so they run wherever the interpreter loads.
244244
one as an attribute imports it (#35) — Kotlin names only. No
245245
member or parameter is renamed; the binder creates no `pythonx` module and a real `pythonx` package
246246
on disk is what `import pythonx` loads. The answer is the same whichever installer
247-
(`PythonProxySource`, `PythonxAdapter`) ran first for a table. `Status: implemented` on desktop —
247+
(`PythonProxySource`, `PythonxAdapter`) ran first for a table. **A name that is both a function and a
248+
module is callable** (#78): Kotlin has `TextRange(2)` (a top-level factory) and `TextRange.Zero` (a
249+
companion constant), so the module `androidx.compose.ui.text.TextRange` is itself callable — calling it
250+
runs the function or overload set of that name in its parent package — while its attributes stay the
251+
companion's constants and functions. One rule with the class case (#73): a Kotlin name that is a
252+
constructor or factory is what calling it does, whatever else lives under that name. `Status:
253+
implemented` on desktop —
248254
`PM/desktopTest/.../pythonx/KotlinNamedSurfaceTest.kt`, `BinderNamespaceTest.kt`, `ksp-fixtures/compose/.../KotlinSignatureMetadataTest.kt`.
249255
- **U-9** A Pythonic package can serve extra member names on a Kotlin proxy through one hook,
250256
`python_multiplatform.binding.add_member_resolver(fn)`, `fn(kotlin_type_name, requested_name,
@@ -277,6 +283,12 @@ All in `PM/commonTest`, so they run wherever the interpreter loads.
277283
other Kotlin object. `Status: implemented` —
278284
`PM/commonTest/.../pythonx/PythonxPropertyTest.kt`, `ksp-fixtures/compose/.../PythonContentRenderTest.kt`,
279285
`AnySlotScalarRenderTest.kt`, `MaterialIconsRenderTest.kt`.
286+
- **U-11** A KSP-bound function, method or property whose declared return type is a Kotlin class that
287+
has a generated proxy returns an instance of **that class's proxy** (#94), not a generic owner: its
288+
methods and properties work and `isinstance(result, TheClass)` holds. The class is found by the declared
289+
return type's Kotlin name; a return type with no generated proxy stays a generic owner object. The
290+
result's ownership is the same as for any owned result. `Status: implemented` —
291+
`ksp-fixtures/app/.../KspClassResultProxyTest.kt` (red until #94 lands).
280292

281293
## 6. Binding prebuilt libraries (Gradle plugin)
282294

@@ -343,6 +355,10 @@ All in `PM/commonTest`, so they run wherever the interpreter loads.
343355
Pythonic stub product belongs to pythonx-compose. `Status: partial` — `GP/stubs/PyiRenderingTest.kt`,
344356
`GP/stubs/TypedStubTest.kt`, `GP/stubs/KotlinNamesOnlyStubTest.kt`,
345357
`ksp-fixtures/artifact/.../WalkedArtifactStubTest.kt`, `.../StubSignatureAgreesWithRuntimeTest.kt`,
358+
`GP/stubs/CompanionFactoryStubTest.kt` (#78: a name that is both a function and a Kotlin-named
359+
module is stubbed in its parent package as one attribute whose type has `__call__` (the function or
360+
its `@overload`s) and the module's constants and functions as members — never as a bare `def`, which
361+
would hide `TextRange.Zero` from a checker),
346362
`tools/stubs/check-stubs.sh` (mypy over the Compose stubs). A class whose name is also a function in
347363
its module is `Any` — except that a class's own bound constructor is that class's `__init__`
348364
(`@overload`ed for several), so the class keeps its stub, members and properties; a single one's

0 commit comments

Comments
 (0)