Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
echo "This job compiled \`wasmJsMain\` and \`wasmJsTest\`. **No wasm test was executed.**"
echo
echo "Running them needs a CPython Emscripten build, which this repository publishes as"
echo "\`io.github.thisisthepy:python-multiplatform-wasm-runtime\` (a 7 MB zip built by"
echo "\`org.thisisthepy:python-multiplatform-wasm-runtime\` (a 7 MB zip built by"
echo "\`:python-multiplatform:wasmBrowserRuntimeZip\`) but has never pushed to a remote"
echo "repository -- it exists only in the maintainer's \`mavenLocal\`."
echo
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ name, unchanged.
plugins {
kotlin("jvm")
application
id("io.github.thisisthepy.python.multiplatform.bindings") version "<version>"
id("org.thisisthepy.python.multiplatform.bindings") version "<version>"
}
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ plugins {
val publishedVersion = "${property("pythonVersion")}-${property("libraryVersionSuffix")}"

allprojects {
group = "io.github.thisisthepy"
group = "org.thisisthepy"
version = publishedVersion
}

Expand Down
11 changes: 10 additions & 1 deletion docs/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,16 @@ fail, then implement.
into a catchable `IllegalStateException` naming the path and version, instead of CPython's process
abort. `Status: implemented` on desktop (`PM/desktopTest/.../env/PythonHomeCheckTest.kt`);
smoke-tested on all targets (`PM/commonTest/.../PythonHomeCheckSmokeTest.kt`).
- **L-4** Desktop: applying the Gradle plugin `io.github.thisisthepy.python.multiplatform.bindings`
- **L-0** Coordinates (#95, maintainer decision 2026-10-03, following JetBrains): libraries and the Gradle
plugin are published under the Maven group `org.thisisthepy` (`org.thisisthepy:python-multiplatform`,
`-ksp`, `-compose`, `-gradle-plugin`; plugin ID `org.thisisthepy.python.multiplatform.bindings`), shipped
apps under `io.github.thisisthepy` (the sample's application and bundle ID
`io.github.thisisthepy.python.multiplatform.demo`). Kotlin code packages (`python.*`, the sample's
`org.thisisthepy.python.multiplatform.demo.*`) do not change. The generated table fragment of a module is
named from its Maven group and project path (`deriveModuleName`), so since #95 it is
`Fragment_org_thisisthepy_<path>` (before: `Fragment_io_github_thisisthepy_<path>`); discovery scans the
whole package, so only the name changed. `Status: implemented`, `GP/PublishedVersionTest.kt`, `GP/WiringTest.kt`.
- **L-4** Desktop: applying the Gradle plugin `org.thisisthepy.python.multiplatform.bindings`
registers `stagePythonHome`, which downloads the matching CPython, verifies it against the release
`SHA256SUMS`, caches it machine-wide and sets `PYTHONHOME` on `run`/`test`. A user-set `PYTHONHOME`
is never overridden; `pythonBindings { stagePythonHome.set(false) }` disables it.
Expand Down
2 changes: 1 addition & 1 deletion docs/design/ecosystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ This section documents the current state, language/build system/distribution art
- **Language / Build System / Distribution**:
- Kotlin Multiplatform (Kotlin `2.4.20-Beta2` pinned in `gradle/libs.versions.toml` as of 2026-10-03), Compose Multiplatform 1.11.1 for the sample and fixtures, Java 21, C/C++ FFI.
- Gradle (`build.gradle.kts`, `settings.gradle.kts`, included build `python-multiplatform-gradle-plugin`).
- Maven artifact publication (`id("maven-publish")` in `python-multiplatform/build.gradle.kts`, published under group `io.github.thisisthepy:python-multiplatform`).
- Maven artifact publication (`id("maven-publish")` in `python-multiplatform/build.gradle.kts`, published under group `org.thisisthepy:python-multiplatform`).
- **Goal in Ecosystem**:
- Core language boundary and binder between Kotlin Multiplatform and CPython.
- Manages low-level FFI, object reference handles, upcall/downcall function tables, GIL lifecycle, and proxy injection into CPython `sys.modules`.
Expand Down
8 changes: 4 additions & 4 deletions docs/design/upcall.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ point. Each module emits one **fragment object** into the well-known package
// GENERATED by python-multiplatform-ksp. Do not edit.
package python.multiplatform.generated.fragments

object Fragment_io_github_thisisthepy_python_multiplatform_ksp_fixtures_library : python.multiplatform.reflection.FunctionTableFragment {
override val moduleName: String = "io_github_thisisthepy_python_multiplatform_ksp_fixtures_library"
object Fragment_org_thisisthepy_python_multiplatform_ksp_fixtures_library : python.multiplatform.reflection.FunctionTableFragment {
override val moduleName: String = "org_thisisthepy_python_multiplatform_ksp_fixtures_library"
override fun entries(): List<ExposedCallable> = listOf(
ExposedCallable(name = "fixture.library.greet", arity = 1, paramTypes = listOf(TypeTag.STRING),
returnType = TypeTag.STRING, paramNames = listOf("name"), callable = { args -> fixture.library.greet(args[0] as String) }),
Expand All @@ -148,7 +148,7 @@ object Fragment_io_github_thisisthepy_python_multiplatform_ksp_fixtures_library
- **Name**: `Fragment_<moduleName>`, where `moduleName` is the KSP option
`python.multiplatform.moduleName` with `.` and `-` replaced by `_` (`Constants.kt`). The Gradle plugin
derives it from the **Maven group plus the project path** (`:python-multiplatform-ksp:fixtures:app` with group
`io.github.thisisthepy` → `io_github_thisisthepy_python_multiplatform_ksp_fixtures_app`), because fragments from every
`org.thisisthepy` → `org_thisisthepy_python_multiplatform_ksp_fixtures_app`), because fragments from every
artifact share one package and two libraries both called `:core` would otherwise collide. The name is
not load-bearing for discovery (the whole package is scanned) but prevents collisions.
- **Visibility: `public`.** An `internal` fragment does not work: `internal` is enforced per Kotlin
Expand Down Expand Up @@ -297,7 +297,7 @@ One `SymbolProcessorProvider` (`PythonBindingProcessorProvider.kt`) with two rol
| `python.multiplatform.excludePackages` | no | comma-separated packages to skip, on top of the always-excluded ones |

**The Gradle plugin is built**: `python-multiplatform-gradle-plugin/`, id
`io.github.thisisthepy.python.multiplatform.bindings`. Applying it applies KSP, adds the processor to every
`org.thisisthepy.python.multiplatform.bindings`. Applying it applies KSP, adds the processor to every
target's **main** KSP configuration (never a `…Test` one and never `kspCommonMainMetadata`, either of which
would emit a second fragment under the same name), infers the role (`application` /
`com.android.application` → `app`, else `library`; override with `pythonBindings { role.set("app") }`) and
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/guide-binding-libraries.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ <h1><span data-lang="en">Bind a prebuilt library</span><span data-lang="ko">빌
<main><div class="wrap"><div class="prose">
<h2><span class="num">01</span><span data-lang="en">Apply the plugin</span><span data-lang="ko">플러그인 적용</span></h2>
<pre translate="no"><code class="language-kotlin">plugins {
id(&quot;io.github.thisisthepy.python.multiplatform.bindings&quot;) version &quot;&lt;version&gt;&quot;
id(&quot;org.thisisthepy.python.multiplatform.bindings&quot;) version &quot;&lt;version&gt;&quot;
}</code></pre>
<p><span data-lang="en">The walker reads Kotlin metadata with ASM, generates Kotlin source for every reachable declaration, and lets <code>kotlinc</code> compile it. It never looks up JVM methods by name: value-class name mangling makes that impossible in principle.</span><span data-lang="ko">워커는 ASM 으로 Kotlin 메타데이터를 읽고, 닿을 수 있는 모든 선언에 대해 Kotlin 소스를 생성해 <code>kotlinc</code> 가 컴파일하게 한다. JVM 메서드를 이름으로 찾지 않는다. 값 클래스 이름 맹글링 때문에 그것은 원리적으로 불가능하다.</span></p>
<h2><span class="num">02</span><span data-lang="en">What you get</span><span data-lang="ko">얻는 것</span></h2>
Expand Down
2 changes: 1 addition & 1 deletion docs/locale/README_ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ async def main():
plugins {
kotlin("jvm")
application
id("io.github.thisisthepy.python.multiplatform.bindings") version "<version>"
id("org.thisisthepy.python.multiplatform.bindings") version "<version>"
}
```

Expand Down
8 changes: 4 additions & 4 deletions docs/platforms/ios-app-bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ prefix and payload, why, and how to check it.
## What happens

**Gradle** (the bindings plugin, `IosConsumerWiring.kt`; rules in `IosPythonHomeLayout`). The app module
applies `io.github.thisisthepy.python.multiplatform.bindings`, which registers one task per slice,
applies `org.thisisthepy.python.multiplatform.bindings`, which registers one task per slice,
`stageIosPythonHome_<sdk>_<arch>` (`iphoneos_arm64`, `iphonesimulator_arm64`, `iphonesimulator_x86_64`),
and writes `<module>/build/python-ios-home/<sdk>-<arch>/` (for the sample, `sample/build/python-ios-home/...`):

Expand Down Expand Up @@ -92,7 +92,7 @@ before. `PyConfig.home` is the non-deprecated route, but the library carries no
## Wiring an app

The sample's project is `sample/src/iosMain/app.xcodeproj` (target `app`, product `PythonDemo.app`, bundle id
`org.thisisthepy.python.multiplatform.demo`). Its phase:
`io.github.thisisthepy.python.multiplatform.demo`). Its phase:

```sh
set -e
Expand Down Expand Up @@ -165,10 +165,10 @@ codesign --verify --deep --strict "$APP" && echo SIGNED_OK
# 3. Install and launch with no SIMCTL_CHILD_* variable
UDID=$(xcrun simctl list devices booted | grep -oE '[0-9A-F-]{36}' | head -n 1); echo "$UDID"
env | grep '^SIMCTL_CHILD_' && echo "unset these first" # must print nothing
xcrun simctl uninstall "$UDID" org.thisisthepy.python.multiplatform.demo
xcrun simctl uninstall "$UDID" io.github.thisisthepy.python.multiplatform.demo
xcrun simctl install "$UDID" "$APP"
xcrun simctl launch --console-pty --terminate-running-process "$UDID" \
org.thisisthepy.python.multiplatform.demo > .tmp/launch.log 2>&1 &
io.github.thisisthepy.python.multiplatform.demo > .tmp/launch.log 2>&1 &
LAUNCH=$!
# wait for the DEMO block (Monitor/until-loop; no sleep in this shell), then:
kill "$LAUNCH"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.jetpack.compose)
alias(libs.plugins.compose.compiler)
id("io.github.thisisthepy.python.multiplatform.bindings")
id("org.thisisthepy.python.multiplatform.bindings")
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.jetpack.compose)
alias(libs.plugins.compose.compiler)
id("io.github.thisisthepy.python.multiplatform.bindings")
id("org.thisisthepy.python.multiplatform.bindings")
}

kotlin {
Expand Down
4 changes: 2 additions & 2 deletions python-multiplatform-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
`maven-publish`
}

group = "io.github.thisisthepy"
group = "org.thisisthepy"
// Same published version as the library and the KSP processor (#70): "$pythonVersion-$suffix" from
// the root's gradle.properties, a -P override winning as it does in the root build (Gradle hands
// start-parameter properties to included builds too).
Expand Down Expand Up @@ -221,7 +221,7 @@ dependencies {
gradlePlugin {
plugins {
create("pythonMultiplatformBindings") {
id = "io.github.thisisthepy.python.multiplatform.bindings"
id = "org.thisisthepy.python.multiplatform.bindings"
implementationClass = "python.multiplatform.gradle.PythonBindingsPlugin"
displayName = "Python Multiplatform bindings"
description = "Wires the python-multiplatform KSP processor into every Kotlin target of a module."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
*/
plugins {
alias(libs.plugins.kotlin.multiplatform)
id("io.github.thisisthepy.python.multiplatform.bindings")
id("org.thisisthepy.python.multiplatform.bindings")
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* `:python-multiplatform`.
*
* This module declares no `downloadPython_ios`, no linker option and no Xcode script of its own. All
* of it comes from `io.github.thisisthepy.python.multiplatform.bindings`:
* of it comes from `org.thisisthepy.python.multiplatform.bindings`:
*
* - `downloadPython_ios` and `preparePythonXcframework` (into this module's `build/`),
* - `-framework Python -F...` on the two `Probe` framework binaries below,
Expand All @@ -14,7 +14,7 @@
*/
plugins {
alias(libs.plugins.kotlin.multiplatform)
id("io.github.thisisthepy.python.multiplatform.bindings")
id("org.thisisthepy.python.multiplatform.bindings")
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
*/
plugins {
alias(libs.plugins.kotlin.multiplatform)
id("io.github.thisisthepy.python.multiplatform.bindings")
id("org.thisisthepy.python.multiplatform.bindings")
}

val pythonDownloadDir = project(":python-multiplatform").layout.buildDirectory.dir("python-standalone").get().asFile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ interface PythonBindingsExtension {
* target, is now one plugin.
*
* plugins {
* id("io.github.thisisthepy.python.multiplatform.bindings")
* id("org.thisisthepy.python.multiplatform.bindings")
* }
*
* A module that produces the final binary adds `pythonBindings { role.set("app") }` unless it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ class PublishedVersionTest {
@Test
fun theProcessorCoordinateCarriesTheLibraryVersion() {
assertEquals(
"io.github.thisisthepy:python-multiplatform-ksp:$expected",
"org.thisisthepy:python-multiplatform-ksp:$expected",
DEFAULT_PROCESSOR_COORDINATES,
)
}

@Test
fun theWasmRuntimeCoordinateCarriesTheLibraryVersion() {
assertEquals(
"io.github.thisisthepy:python-multiplatform-wasm-runtime:$expected",
"org.thisisthepy:python-multiplatform-wasm-runtime:$expected",
DEFAULT_WASM_RUNTIME_COORDINATES,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ class WiringTest {
// Two independent libraries both called `:core` would otherwise both emit
// `python.multiplatform.generated.fragments.Fragment_core` and collide on one classpath.
assertEquals(
"io_github_thisisthepy_python_multiplatform_ksp_fixtures_app",
deriveModuleName(group = "io.github.thisisthepy", path = ":python-multiplatform-ksp:fixtures:app", name = "app"),
"org_thisisthepy_python_multiplatform_ksp_fixtures_app",
deriveModuleName(group = "org.thisisthepy", path = ":python-multiplatform-ksp:fixtures:app", name = "app"),
)
assertEquals(
"com_example_core",
Expand Down
4 changes: 2 additions & 2 deletions python-multiplatform-ksp/fixtures/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
/**
* The fixture that carries an Android plugin.
*
* ROADMAP §13: `id("io.github.thisisthepy.python.multiplatform.bindings")` applies
* ROADMAP §13: `id("org.thisisthepy.python.multiplatform.bindings")` applies
* `com.google.devtools.ksp`, and KSP declares `MINIMUM_SUPPORTED_AGP_VERSION`. Below that AGP the
* plugin dies at configuration time with
*
Expand All @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.android.library)
id("io.github.thisisthepy.python.multiplatform.bindings")
id("org.thisisthepy.python.multiplatform.bindings")
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class GeneratedAndroidTableTest {

@Test
fun theAndroidModulesFragmentIsInTheAggregatedTable() {
assertEquals(setOf("io_github_thisisthepy_python_multiplatform_ksp_fixtures_android"), UpcallTable.moduleNames)
assertEquals(setOf("org_thisisthepy_python_multiplatform_ksp_fixtures_android"), UpcallTable.moduleNames)
assertTrue(UpcallTable.resolve("fixture.android.androidDouble").isValid)
}

Expand Down Expand Up @@ -115,7 +115,7 @@ class GeneratedAndroidTableTest {
val count = installAndCountEntries()

assertTrue(count > 0)
assertEquals(setOf("io_github_thisisthepy_python_multiplatform_ksp_fixtures_android"), UpcallTable.moduleNames)
assertEquals(setOf("org_thisisthepy_python_multiplatform_ksp_fixtures_android"), UpcallTable.moduleNames)
assertTrue(UpcallTable.resolve("fixture.android.androidDouble").isValid)
assertFalse(
UpcallTable.resolve("fixture.android.installGeneratedUpcallTable").isValid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class GeneratedAndroidTableTest {
// The name the gradle plugin derives from this module's group and path. Its presence is
// what says KSP ran here at all -- the state ROADMAP §13 describes has no fragment
// because the build never got past configuration.
assertEquals(setOf("io_github_thisisthepy_python_multiplatform_ksp_fixtures_android"), UpcallTable.moduleNames)
assertEquals(setOf("org_thisisthepy_python_multiplatform_ksp_fixtures_android"), UpcallTable.moduleNames)
assertTrue(UpcallTable.resolve("fixture.android.androidDouble").isValid)
}

Expand Down Expand Up @@ -132,7 +132,7 @@ class GeneratedAndroidTableTest {
val count = installAndCountEntries()

assertTrue(count > 0)
assertEquals(setOf("io_github_thisisthepy_python_multiplatform_ksp_fixtures_android"), UpcallTable.moduleNames)
assertEquals(setOf("org_thisisthepy_python_multiplatform_ksp_fixtures_android"), UpcallTable.moduleNames)
assertTrue(UpcallTable.resolve("fixture.android.androidDouble").isValid)
assertFalse(
UpcallTable.resolve("fixture.android.installGeneratedUpcallTable").isValid,
Expand Down
2 changes: 1 addition & 1 deletion python-multiplatform-ksp/fixtures/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
alias(libs.plugins.kotlin.multiplatform)
id("io.github.thisisthepy.python.multiplatform.bindings")
id("org.thisisthepy.python.multiplatform.bindings")
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class CommonInstallTest {

assertTrue(UpcallTable.callableCount > 0, "commonMain installed nothing: $summary")
assertEquals(
setOf("io_github_thisisthepy_python_multiplatform_ksp_fixtures_library", "io_github_thisisthepy_python_multiplatform_ksp_fixtures_app"),
setOf("org_thisisthepy_python_multiplatform_ksp_fixtures_library", "org_thisisthepy_python_multiplatform_ksp_fixtures_app"),
UpcallTable.moduleNames,
"the seam must install the same aggregate FunctionTable.fragments does",
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class GeneratedTableTest {
// group and path -- neither fixture states a module name any more, and the group is in
// there because every fragment object in every artifact shares one package.
assertEquals(
setOf("io_github_thisisthepy_python_multiplatform_ksp_fixtures_library", "io_github_thisisthepy_python_multiplatform_ksp_fixtures_app"),
setOf("org_thisisthepy_python_multiplatform_ksp_fixtures_library", "org_thisisthepy_python_multiplatform_ksp_fixtures_app"),
UpcallTable.moduleNames,
)
assertTrue(UpcallTable.resolve("fixture.library.add").isValid)
Expand Down
2 changes: 1 addition & 1 deletion python-multiplatform-ksp/fixtures/library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
// One line in place of the KSP plugin, two per-target `add("ksp<Target>", ...)` lines and a
// `ksp { arg(...) }` block. The role defaults to `library`; the module name is derived from
// the project's group and path.
id("io.github.thisisthepy.python.multiplatform.bindings")
id("org.thisisthepy.python.multiplatform.bindings")
}

kotlin {
Expand Down
Loading