diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml index 864350b4ff30..d8df1637eecc 100644 --- a/python/pluginResources/intellij.python.community.impl.xml +++ b/python/pluginResources/intellij.python.community.impl.xml @@ -572,10 +572,6 @@ - - diff --git a/python/python-exec-service/src/com/intellij/python/community/execService/api.kt b/python/python-exec-service/src/com/intellij/python/community/execService/api.kt index a7ade91de516..83c286dc479f 100644 --- a/python/python-exec-service/src/com/intellij/python/community/execService/api.kt +++ b/python/python-exec-service/src/com/intellij/python/community/execService/api.kt @@ -185,7 +185,7 @@ open class ZeroCodeStdoutParserTransformer(val stdoutParser: (String) -> Resu * @property[env] Environment variables to be applied with the process run * @property[timeout] Process gets killed after this timeout * @property[processDescription] optional description to be displayed to user - * [tty] Much like [com.intellij.platform.eel.EelExecApi.Pty] + * @property[tty] Much like [com.intellij.platform.eel.EelExecApi.Pty] */ data class ExecOptions( override val env: Map = emptyMap(), diff --git a/python/src/com/jetbrains/python/PythonHelper.java b/python/src/com/jetbrains/python/PythonHelper.java index 8922a1dab412..781b8218a80a 100644 --- a/python/src/com/jetbrains/python/PythonHelper.java +++ b/python/src/com/jetbrains/python/PythonHelper.java @@ -22,13 +22,14 @@ import java.util.stream.Collectors; import static com.intellij.python.community.helpersLocator.PythonHelpersLocator.*; import static com.intellij.python.community.impl.venv.VenvKt.LEGACY_VIRTUALENV_ZIPAPP_NAME; import static com.intellij.python.community.impl.venv.VenvKt.VIRTUALENV_ZIPAPP_NAME; +import static com.jetbrains.python.packaging.pip.PipPackageManagerEngine.PACKAGING_TOOL_NAME; public enum PythonHelper implements HelperPackage { GENERATOR3("generator3/__main__.py"), REMOTE_SYNC("remote_sync.py"), // Packaging tools - PACKAGING_TOOL("packaging_tool.py"), + PACKAGING_TOOL(PACKAGING_TOOL_NAME), VIRTUALENV_ZIPAPP(VIRTUALENV_ZIPAPP_NAME), LEGACY_VIRTUALENV_ZIPAPP(LEGACY_VIRTUALENV_ZIPAPP_NAME), // virtualenv used to create virtual environments for python 2.7, 3.6, 3.7 diff --git a/python/src/com/jetbrains/python/packaging/management/PyPackageProcessHandler.kt b/python/src/com/jetbrains/python/packaging/management/PyPackageProcessHandler.kt deleted file mode 100644 index e71ed23b3273..000000000000 --- a/python/src/com/jetbrains/python/packaging/management/PyPackageProcessHandler.kt +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.packaging.management - -import com.intellij.execution.process.CapturingAnsiEscapesAwareProcessHandler -import com.intellij.execution.process.CapturingProcessAdapter -import com.intellij.execution.process.ProcessOutput -import com.intellij.execution.process.ProcessOutputTypes -import com.intellij.openapi.util.Key - -internal class PyPackageProcessHandler : CapturingAnsiEscapesAwareProcessHandler { - constructor(process: Process, commandLine: String) : super(process, commandLine) - - var lastLine: String = "" - private set - - var lastLineNotifier: ((String) -> Unit)? = null - - override fun createProcessAdapter(processOutput: ProcessOutput): CapturingProcessAdapter? { - return PackageProcessAdapter(processOutput) - } - - private inner class PackageProcessAdapter(processOutput: ProcessOutput) : AnsiEscapesAwareAdapter(processOutput) { - override fun addToOutput(text: String, outputType: Key<*>) { - super.addToOutput(text, outputType) - - when { - outputType === ProcessOutputTypes.STDOUT -> { - lastLine = output.stdoutLines.lastOrNull { it.isNotBlank() } ?: "" - - } - outputType === ProcessOutputTypes.STDERR -> { - lastLine = output.stderrLines.lastOrNull { it.isNotBlank() } ?: "" - } - } - lastLine = lastLine.trim() - if (lastLine.isBlank()) - return - lastLineNotifier?.invoke(lastLine) - } - } -} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerRunner.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerRunner.kt deleted file mode 100644 index 7d2fbfc25ba6..000000000000 --- a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerRunner.kt +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.packaging.management - -import com.intellij.execution.process.ProcessOutput -import com.intellij.openapi.progress.ProgressManager -import com.intellij.openapi.progress.coroutineToIndicator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.jetbrains.annotations.ApiStatus - -@ApiStatus.Internal -internal object PythonPackageManagerRunner { - suspend fun runProcess( - process: Process, - command: String, - ): ProcessOutput { - val handler = PyPackageProcessHandler(process, command) - - return runProcessInternal(handler) - } - - private suspend fun runProcessInternal(processHandler: PyPackageProcessHandler): ProcessOutput = withContext(Dispatchers.IO) { - coroutineToIndicator { - val progressIndicator = ProgressManager.getInstance().progressIndicator - processHandler.lastLineNotifier = { line: String -> - @Suppress("HardCodedStringLiteral") - progressIndicator.text = line.trim() - } - processHandler.runProcessWithProgressIndicator(progressIndicator, 10 * 60 * 1000) - } - } -} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/packaging/pip/PipPackageManagerEngine.kt b/python/src/com/jetbrains/python/packaging/pip/PipPackageManagerEngine.kt index d8037104edd6..491223643578 100644 --- a/python/src/com/jetbrains/python/packaging/pip/PipPackageManagerEngine.kt +++ b/python/src/com/jetbrains/python/packaging/pip/PipPackageManagerEngine.kt @@ -1,23 +1,14 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.jetbrains.python.packaging.pip -import com.intellij.execution.target.TargetProgressIndicator -import com.intellij.execution.target.local.LocalTargetEnvironmentRequest -import com.intellij.execution.target.value.targetPath import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.progress.EmptyProgressIndicator -import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project -import com.intellij.openapi.project.guessProjectDir import com.intellij.openapi.projectRoots.Sdk -import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vfs.VirtualFile -import com.intellij.util.io.IdeUtilIoBundle -import com.jetbrains.python.sdk.impl.PySdkBundle -import com.jetbrains.python.PythonHelper +import com.intellij.python.community.execService.ExecService +import com.intellij.python.community.execService.python.HelperName import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.onFailure -import com.jetbrains.python.packaging.PyExecutionException import com.jetbrains.python.packaging.PyPIPackageUtil import com.jetbrains.python.packaging.common.PythonOutdatedPackage import com.jetbrains.python.packaging.common.PythonPackage @@ -25,16 +16,11 @@ import com.jetbrains.python.packaging.common.PythonRepositoryPackageSpecificatio import com.jetbrains.python.packaging.management.PythonPackageInstallRequest import com.jetbrains.python.packaging.management.PythonPackageManager import com.jetbrains.python.packaging.management.PythonPackageManagerEngine -import com.jetbrains.python.packaging.management.PythonPackageManagerRunner import com.jetbrains.python.packaging.utils.PyProxyUtils -import com.jetbrains.python.run.PythonInterpreterTargetEnvironmentFactory -import com.jetbrains.python.run.buildTargetedCommandLine -import com.jetbrains.python.run.ensureProjectSdkAndModuleDirsAreOnTarget -import com.jetbrains.python.run.prepareHelperScriptExecution +import com.jetbrains.python.sdk.executeHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jetbrains.annotations.ApiStatus -import kotlin.math.min @ApiStatus.Internal @@ -95,91 +81,22 @@ class PipPackageManagerEngine( return PyResult.success(packages) } - @ApiStatus.Internal suspend fun runPackagingTool(operation: String, arguments: List): PyResult = withContext(Dispatchers.IO) { - // todo[akniazev]: check for package management tools - val helpersAwareTargetRequest = PythonInterpreterTargetEnvironmentFactory.findPythonTargetInterpreter(sdk, project) - val targetEnvironmentRequest = helpersAwareTargetRequest.targetEnvironmentRequest - val pythonExecution = prepareHelperScriptExecution(PythonHelper.PACKAGING_TOOL, helpersAwareTargetRequest) - - if (targetEnvironmentRequest is LocalTargetEnvironmentRequest) { - if (Registry.`is`("python.packaging.tool.use.project.location.as.working.dir")) { - project.guessProjectDir()?.toNioPath()?.let { - pythonExecution.workingDir = targetPath(it) - } - } - } - else { - if (Registry.`is`("python.packaging.tool.upload.project")) { - project.guessProjectDir()?.toNioPath()?.let { - targetEnvironmentRequest.ensureProjectSdkAndModuleDirsAreOnTarget(project) - pythonExecution.workingDir = targetPath(it) - } - } - } - - pythonExecution.addParameter(operation) + val parameters = mutableListOf(operation) if (operation == "install") { PyProxyUtils.proxyString?.let { - pythonExecution.addParameter("--proxy") - pythonExecution.addParameter(it) + parameters += "--proxy" + parameters += it } } - - arguments.forEach(pythonExecution::addParameter) - - // // todo[akniazev]: add extra args to package specification - - val targetProgressIndicator = TargetProgressIndicator.EMPTY - val targetEnvironment = targetEnvironmentRequest.prepareEnvironment(targetProgressIndicator) - - targetEnvironment.uploadVolumes.entries.forEach { (_, value) -> - value.upload(".", targetProgressIndicator) - } - - val targetedCommandLine = pythonExecution.buildTargetedCommandLine(targetEnvironment, sdk, emptyList()) - - val indicator = ProgressManager.getInstance().progressIndicator ?: EmptyProgressIndicator() - // from targets package manager - // TODO [targets] Apply environment variables: setPythonUnbuffered(...), setPythonDontWriteBytecode(...), resetHomePathChanges(...) - // TODO [targets] Apply flavor from PythonSdkFlavor.getFlavor(mySdk) - // TODO [targets] check askForSudo - - val process = targetEnvironment.createProcess(targetedCommandLine, indicator) - - val commandLine = targetedCommandLine.collectCommandsSynchronously() - val commandLineString = commandLine.joinToString(" ") + parameters += arguments thisLogger().debug("Running python packaging tool. Operation: $operation") - - val result = PythonPackageManagerRunner.runProcess( - process, - commandLineString + ExecService().executeHelper( + sdk, + PACKAGING_TOOL_NAME, + parameters, ) - if (result.isCancelled) { - return@withContext PyResult.localizedError(IdeUtilIoBundle.message("run.canceled.by.user.message")) - } - - result.checkSuccess(thisLogger()) - val exitCode = result.exitCode - val helperPath = commandLine.firstOrNull() ?: "" - val args: List = commandLine.subList(min(1, commandLine.size), commandLine.size) - if (exitCode != 0) { - val message = if (result.stdout.isBlank() && result.stderr.isBlank()) PySdkBundle.message( - "python.conda.permission.denied") - else PySdkBundle.message("python.sdk.packaging.non.zero.exit.code", exitCode) - PyExecutionException(message, commandLine[0], args, result).let { - return@withContext PyResult.failure(it.pyError) - } - } - - if (result.isTimeout) { - PyExecutionException.createForTimeout(PySdkBundle.message("python.sdk.packaging.timed.out"), helperPath, args).let { - return@withContext PyResult.failure(it.pyError) - } - } - - return@withContext PyResult.success(result.stdout) } @@ -235,4 +152,8 @@ class PipPackageManagerEngine( return PyResult.success(Unit) } -} \ No newline at end of file + + companion object { + const val PACKAGING_TOOL_NAME: HelperName = "packaging_tool.py" + } +}