/python/src/com/jetbrains/python/console/PydevConsoleCli.kt

https://github.com/JetBrains/intellij-community · Kotlin · 140 lines · 78 code · 17 blank · 45 comment · 0 complexity · a155aae6696069fc3a0cd1737294107c MD5 · raw file

  1. // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
  2. @file:JvmName("PydevConsoleCli")
  3. package com.jetbrains.python.console
  4. import com.intellij.execution.ExecutionException
  5. import com.intellij.execution.configurations.GeneralCommandLine
  6. import com.intellij.execution.configurations.ParamsGroup
  7. import com.intellij.execution.target.HostPort
  8. import com.intellij.execution.target.TargetEnvironment
  9. import com.intellij.execution.target.value.TargetEnvironmentFunction
  10. import com.intellij.openapi.projectRoots.Sdk
  11. import com.intellij.openapi.projectRoots.SdkAdditionalData
  12. import com.jetbrains.python.PythonHelper
  13. import com.jetbrains.python.run.PythonCommandLineState
  14. import com.jetbrains.python.run.PythonExecution
  15. import com.jetbrains.python.run.prepareHelperScriptExecution
  16. import com.jetbrains.python.run.target.HelpersAwareTargetEnvironmentRequest
  17. import com.jetbrains.python.sdk.PythonEnvUtil
  18. import com.jetbrains.python.sdk.PythonSdkAdditionalData
  19. import com.jetbrains.python.sdk.flavors.PythonSdkFlavor
  20. import java.io.File
  21. import java.util.function.Function
  22. const val MODE_OPTION = "mode"
  23. const val MODE_OPTION_SERVER_VALUE = "server"
  24. const val MODE_OPTION_CLIENT_VALUE = "client"
  25. const val HOST_OPTION = "host"
  26. const val PORT_OPTION = "port"
  27. private fun getOptionString(name: String, value: Any): String = "--$name=$value"
  28. private fun getOptionString(name: String, value: TargetEnvironmentFunction<*>): TargetEnvironmentFunction<String> =
  29. value.andThen { it: Any? -> "--$name=$it" }
  30. /**
  31. * Adds new or replaces existing [PythonCommandLineState.GROUP_SCRIPT]
  32. * parameters of [this] command line with the path to Python console script
  33. * (*pydevconsole.py*) and parameters required for running it in the *client*
  34. * mode.
  35. *
  36. * @param port the port that Python console script will connect to
  37. *
  38. * @see PythonHelper.CONSOLE
  39. */
  40. fun GeneralCommandLine.setupPythonConsoleScriptInClientMode(sdk: Sdk, port: Int) {
  41. initializePydevConsoleScriptGroup(PythonSdkFlavor.getFlavor(sdk)).appendClientModeParameters(port)
  42. }
  43. /**
  44. * Adds new or replaces existing [PythonCommandLineState.GROUP_SCRIPT]
  45. * parameters of [this] command line with the path to Python console script
  46. * (*pydevconsole.py*) and parameters required for running it in the *server*
  47. * mode.
  48. *
  49. * Updates Python path according to the flavor defined in [sdkAdditionalData].
  50. *
  51. * @param sdkAdditionalData the additional data where [PythonSdkFlavor] is taken
  52. * from
  53. * @param port the optional port that Python console script will listen at
  54. *
  55. * @see PythonHelper.CONSOLE
  56. */
  57. @JvmOverloads
  58. fun GeneralCommandLine.setupPythonConsoleScriptInServerMode(sdkAdditionalData: SdkAdditionalData, port: Int? = null) {
  59. initializePydevConsoleScriptGroup((sdkAdditionalData as? PythonSdkAdditionalData)?.flavor).appendServerModeParameters(port)
  60. }
  61. private fun GeneralCommandLine.initializePydevConsoleScriptGroup(pythonSdkFlavor: PythonSdkFlavor?): ParamsGroup {
  62. val group: ParamsGroup = parametersList.getParamsGroup(PythonCommandLineState.GROUP_SCRIPT)?.apply { parametersList.clearAll() }
  63. ?: parametersList.addParamsGroup(PythonCommandLineState.GROUP_SCRIPT)
  64. val pythonPathEnv = hashMapOf<String, String>()
  65. PythonHelper.CONSOLE.addToPythonPath(pythonPathEnv)
  66. val consolePythonPath = pythonPathEnv[PythonEnvUtil.PYTHONPATH]
  67. // here we get Python console path for the system interpreter
  68. // let us convert it to the project interpreter path
  69. consolePythonPath?.split(File.pathSeparator)?.let { pythonPathList ->
  70. pythonSdkFlavor?.initPythonPath(pythonPathList, false, environment) ?: PythonEnvUtil.addToPythonPath(environment, pythonPathList)
  71. }
  72. group.addParameter(PythonHelper.CONSOLE.asParamString())
  73. // fix `AttributeError` when running Python Console on IronPython
  74. pythonSdkFlavor?.extraDebugOptions?.let { extraDebugOptions ->
  75. val exeGroup = parametersList.getParamsGroup(PythonCommandLineState.GROUP_EXE_OPTIONS)
  76. extraDebugOptions.forEach { exeGroup?.addParameter(it) }
  77. }
  78. return group
  79. }
  80. private fun ParamsGroup.appendServerModeParameters(port: Int? = null) {
  81. addParameter(getOptionString(MODE_OPTION, MODE_OPTION_SERVER_VALUE))
  82. port?.let { addParameter(getOptionString(PORT_OPTION, it)) }
  83. }
  84. private fun ParamsGroup.appendClientModeParameters(port: Int) {
  85. addParameter(getOptionString(MODE_OPTION, MODE_OPTION_CLIENT_VALUE))
  86. addParameter(getOptionString(PORT_OPTION, port))
  87. }
  88. /**
  89. * Waits for Python console server to be started. The indication for this is
  90. * the server port that Python console script outputs to *stdout* when the
  91. * server socket is bound to the port and it is listening to it.
  92. *
  93. * The connection to Python console script server should be established *after*
  94. * this method finishes.
  95. *
  96. * @throws ExecutionException if timeout occurred or an other error
  97. *
  98. * @see PydevConsoleRunnerImpl.PORTS_WAITING_TIMEOUT
  99. * @see PydevConsoleRunnerImpl.getRemotePortFromProcess
  100. */
  101. @Throws(ExecutionException::class)
  102. fun waitForPythonConsoleServerToBeStarted(process: Process) {
  103. PydevConsoleRunnerImpl.getRemotePortFromProcess(process)
  104. }
  105. fun createPythonConsoleScriptInServerMode(serverPort: Int,
  106. helpersAwareTargetRequest: HelpersAwareTargetEnvironmentRequest): PythonExecution {
  107. val pythonScriptExecution = prepareHelperScriptExecution(PythonHelper.CONSOLE, helpersAwareTargetRequest)
  108. pythonScriptExecution.addParameter(getOptionString(MODE_OPTION, MODE_OPTION_SERVER_VALUE))
  109. pythonScriptExecution.addParameter(getOptionString(PORT_OPTION, serverPort))
  110. return pythonScriptExecution
  111. }
  112. /**
  113. * @param ideServerPort the host and port where the IDE being Python
  114. * Console frontend listens for the connection
  115. */
  116. fun createPythonConsoleScriptInClientMode(ideServerPort: Function<TargetEnvironment, HostPort>,
  117. helpersAwareTargetRequest: HelpersAwareTargetEnvironmentRequest): PythonExecution {
  118. val pythonScriptExecution = prepareHelperScriptExecution(PythonHelper.CONSOLE, helpersAwareTargetRequest)
  119. pythonScriptExecution.addParameter(getOptionString(MODE_OPTION, MODE_OPTION_CLIENT_VALUE))
  120. pythonScriptExecution.addParameter(getOptionString(HOST_OPTION, ideServerPort.andThen(HostPort::host)))
  121. pythonScriptExecution.addParameter(getOptionString(PORT_OPTION, ideServerPort.andThen(HostPort::port)))
  122. return pythonScriptExecution
  123. }