[python] PY-83881 Detect existing environments when creating SDK

Before the changes, there wasn't any mechanism to detect that
environment was already created (for example, .venv exists in the
project). In these situations, during SDK creation we could've created
another environment which was not expected by users.

With these changes, it's now possible to detect in the configurator that
environment already exists, and use it when creating SDK.


Merge-request: IJ-MR-177317
Merged-by: Alexey Katsman <alexey.katsman@jetbrains.com>

GitOrigin-RevId: a612a1069ca5275bedbcfa6af9377059dab7c072
This commit is contained in:
Alexey Katsman
2025-10-14 19:24:05 +00:00
committed by intellij-monorepo-bot
parent 12b01ad006
commit 59fa0db43c
54 changed files with 894 additions and 421 deletions
@@ -254,11 +254,11 @@ data class HatchEnvironments(
data class HatchEnvironment(
val name: @NlsSafe String,
val type: @NlsSafe String,
val features: String? = null,
val dependencies: String? = null,
val environmentVariables: String? = null,
val scripts: String? = null,
val description: String? = null,
val features: String = "",
val dependencies: String = "",
val environmentVariables: String = "",
val scripts: String = "",
val description: String = "",
) {
companion object {
val DEFAULT: HatchEnvironment = HatchEnvironment(name = DEFAULT_ENV_NAME, type = ENV_TYPE_VIRTUAL)
@@ -286,11 +286,11 @@ private fun AsciiTable.parseHatchEnvironments(): List<Pair<HatchEnvironment, Lis
HatchEnvironment(
name = row[nameIdx],
type = row[typeIdx],
features = row.cell(featuresIdx),
dependencies = row.cell(dependenciesIdx),
environmentVariables = row.cell(environmentVariablesIdx),
scripts = row.cell(scriptsIdx),
description = row.cell(descriptionIdx),
features = row.cell(featuresIdx) ?: "",
dependencies = row.cell(dependenciesIdx) ?: "",
environmentVariables = row.cell(environmentVariablesIdx) ?: "",
scripts = row.cell(scriptsIdx) ?: "",
description = row.cell(descriptionIdx) ?: "",
) to matrixEnvironments
}
}
@@ -95,12 +95,18 @@ interface HatchService {
suspend fun createVirtualEnvironment(basePythonBinaryPath: PythonBinary? = null, envName: String? = null): PyResult<PythonVirtualEnvironment.Existing>
suspend fun findVirtualEnvironments(): PyResult<List<HatchVirtualEnvironment>>
/**
* This function detects all Hatch virtual environments and returns the 'default' one if it exists. If such an environment
* doesn't exist, `null` is returned. In case of errors `PyError` is returned.
*/
suspend fun findDefaultVirtualEnvironmentOrNull(): PyResult<HatchVirtualEnvironment?>
}
/**
* Hatch Service for working directory (where hatch.toml / pyproject.toml is usually placed)
*/
suspend fun Path?.getHatchService(hatchExecutablePath: Path? = null, hatchEnvironmentName: String? = null): PyResult<HatchService> {
suspend fun Path?.getHatchService(hatchExecutablePath: Path? = null, hatchEnvironmentName: String? = null): PyResult<HatchService> {
return CliBasedHatchService(hatchExecutablePath = hatchExecutablePath, workingDirectoryPath = this, hatchEnvironmentName = hatchEnvironmentName)
}
@@ -93,6 +93,9 @@ internal class CliBasedHatchService private constructor(
return Result.success(available)
}
override suspend fun findDefaultVirtualEnvironmentOrNull(): PyResult<HatchVirtualEnvironment?> =
findVirtualEnvironments().mapSuccess { envs -> envs.singleOrNull { it.hatchEnvironment == HatchEnvironment.DEFAULT } }
override suspend fun createNewProject(projectName: String): PyResult<ProjectStructure> {
val eelApi = workingDirectoryPath.getEelDescriptor().toEelApi()
@@ -138,6 +141,7 @@ private fun HatchEnvironments.getAvailableVirtualHatchEnvironments(): List<Hatch
HatchEnvironment(
name = envName,
type = type,
features = features,
dependencies = dependencies,
environmentVariables = environmentVariables,
scripts = scripts,