[python] (PY-78749) support ssh upload

Merge-request: IJ-MR-178510
Merged-by: Vitaly Legchilkin <Vitaly.Legchilkin@jetbrains.com>

(cherry picked from commit 1085eccff5433c0902aac46d648c9f536241e5d5)

IJ-MR-178510

GitOrigin-RevId: bf18b88f5b6ea2129d05374df722699ab605b2e3
This commit is contained in:
Vitaly Legchilkin
2025-10-15 20:11:52 +00:00
committed by intellij-monorepo-bot
parent 548a5a9b0b
commit 96be92568d
7 changed files with 82 additions and 10 deletions
@@ -3,5 +3,19 @@
<module name="intellij.python.community"/>
<module name="intellij.python.community.helpersLocator"/>
</dependencies>
<extensionPoints>
<extensionPoint qualifiedName="Pythonid.execService.targetEnvironmentRequestHandler"
interface="com.intellij.python.community.execService.spi.TargetEnvironmentRequestHandler"
dynamic="true"/>
</extensionPoints>
<extensions defaultExtensionNs="Pythonid">
<execService.targetEnvironmentRequestHandler
implementation="com.intellij.python.community.execService.impl.processLaunchers.DefaultTargetEnvironmentRequestHandler"
order="last"
/>
</extensions>
</idea-plugin>
@@ -0,0 +1,22 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.python.community.execService.impl.processLaunchers
import com.intellij.execution.target.TargetEnvironment
import com.intellij.execution.target.TargetEnvironmentRequest
import com.intellij.python.community.execService.spi.TargetEnvironmentRequestHandler
import java.nio.file.Path
class DefaultTargetEnvironmentRequestHandler : TargetEnvironmentRequestHandler {
override fun mapUploadRoots(request: TargetEnvironmentRequest, localDirs: Set<Path>): List<TargetEnvironment.UploadRoot> {
val result = localDirs.map { localDir ->
TargetEnvironment.UploadRoot(
localRootPath = localDir,
targetRootPath = TargetEnvironment.TargetPath.Temporary(),
removeAtShutdown = true
)
}
return result
}
override fun isApplicable(request: TargetEnvironmentRequest): Boolean = true
}
@@ -14,6 +14,7 @@ import com.intellij.platform.eel.provider.utils.ProcessFunctions
import com.intellij.platform.eel.provider.utils.bindProcessToScopeImpl
import com.intellij.python.community.execService.BinOnTarget
import com.intellij.python.community.execService.ExecuteGetProcessError
import com.intellij.python.community.execService.spi.TargetEnvironmentRequestHandler
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.Exe
import com.jetbrains.python.errorProcessing.ExecErrorReason
@@ -44,9 +45,10 @@ internal suspend fun createProcessLauncherOnTarget(binOnTarget: BinOnTarget, lau
// Broken Targets API can only upload the whole directory
val dirsToMap = launchRequest.args.localFiles.map { it.parent }.toSet()
for (localDir in dirsToMap) {
request.uploadVolumes.add(TargetEnvironment.UploadRoot(localDir, TargetEnvironment.TargetPath.Temporary(), removeAtShutdown = true))
}
val handler = TargetEnvironmentRequestHandler.getHandler(request)
val uploadRoots = handler.mapUploadRoots(request, dirsToMap)
request.uploadVolumes.addAll(uploadRoots)
val targetEnv = try {
request.prepareEnvironment(TargetProgressIndicator.EMPTY)
}
@@ -0,0 +1,30 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.python.community.execService.spi
import com.intellij.execution.target.TargetEnvironment
import com.intellij.execution.target.TargetEnvironmentRequest
import com.intellij.openapi.extensions.ExtensionPointName
import org.jetbrains.annotations.ApiStatus
import java.nio.file.Path
interface TargetEnvironmentRequestHandler {
fun isApplicable(request: TargetEnvironmentRequest): Boolean
fun mapUploadRoots(request: TargetEnvironmentRequest, localDirs: Set<Path>): List<TargetEnvironment.UploadRoot>
companion object {
@JvmField
val EP_NAME: ExtensionPointName<TargetEnvironmentRequestHandler> = ExtensionPointName.create<TargetEnvironmentRequestHandler>(
"Pythonid.execService.targetEnvironmentRequestHandler"
)
@JvmStatic
@ApiStatus.Internal
fun getHandler(request: TargetEnvironmentRequest): TargetEnvironmentRequestHandler {
val handler = EP_NAME.extensionList.firstOrNull { it.isApplicable(request) }
?: error("No implementation of [${TargetEnvironmentRequestHandler::class.java}] is found for $request, bundle is broken?")
return handler
}
}
}
@@ -15,11 +15,12 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.ui.dsl.builder.Panel
import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase
import com.jetbrains.python.run.PythonInterpreterTargetEnvironmentFactory.Companion.isPackageManagementSupported
import com.jetbrains.python.run.target.HelpersAwareLocalTargetEnvironmentRequest
import com.jetbrains.python.run.target.HelpersAwareTargetEnvironmentRequest
import com.jetbrains.python.target.ui.TargetPanelExtension
import com.jetbrains.python.target.PyTargetAwareAdditionalData
import com.jetbrains.python.target.targetWithVfs.TargetWithMappedLocalVfs
import com.jetbrains.python.target.ui.TargetPanelExtension
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
@@ -33,6 +34,8 @@ interface PythonInterpreterTargetEnvironmentFactory : PluginAware {
fun getPythonTargetInterpreter(sdk: Sdk, project: Project): HelpersAwareTargetEnvironmentRequest?
fun getPythonTargetInterpreter(sdkAdditionalData: PyTargetAwareAdditionalData, project: Project?): HelpersAwareTargetEnvironmentRequest? = null
fun getTargetType(): TargetEnvironmentType<*>
/**
@@ -108,6 +111,11 @@ interface PythonInterpreterTargetEnvironmentFactory : PluginAware {
else -> null
} ?: HelpersAwareLocalTargetEnvironmentRequest()
@JvmStatic
fun findPythonTargetInterpreter(sdkAdditionalData: PyTargetAwareAdditionalData, project: Project?): HelpersAwareTargetEnvironmentRequest? =
EP_NAME.extensionList.firstNotNullOfOrNull { it.getPythonTargetInterpreter(sdkAdditionalData, project) }
@JvmStatic
fun findDefaultSdkName(project: Project?, data: PyTargetAwareAdditionalData, version: String?): String =
EP_NAME.extensionList.firstNotNullOfOrNull { it.getDefaultSdkName(project, data, version) } ?: getFallbackSdkName(data, version)
@@ -9,10 +9,7 @@ import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.util.io.FileUtil
import com.intellij.platform.eel.EelApi
import com.intellij.platform.eel.provider.localEel
import com.intellij.python.community.execService.BinOnEel
import com.intellij.python.community.execService.BinOnTarget
import com.intellij.python.community.execService.BinaryToExec
import com.intellij.python.community.execService.ExecService
import com.intellij.python.community.execService.*
import com.intellij.python.community.execService.python.validatePythonAndGetVersion
import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl
import com.intellij.python.community.services.shared.VanillaPythonWithLanguageLevel
@@ -50,7 +50,6 @@ private suspend fun <P : PathHolder> PythonAddInterpreterModel<P>.createSdkFromB
existingSdks: List<Sdk>,
): PyResult<SdkWrapper<P>> {
val basePython = fileSystem.getBinaryToExec(pathToBasePython)
createVenv(basePython, pathToVenvHome.toString(), inheritSitePackages).getOr(message("project.error.cant.venv")) { return it }
val pythonBinaryPath = fileSystem.resolvePythonBinary(pathToVenvHome)