From 22c2cbb69e80449b549e2a32b614df4fc07d3d43 Mon Sep 17 00:00:00 2001 From: Vitaly Legchilkin Date: Tue, 3 Mar 2026 11:36:50 +0100 Subject: [PATCH] PY-85822 remove @RequiresBackgroundThread from PythonPackageManager.forSdk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The annotation was guarding the old blocking implementation that called runBlockingMaybeCancellable unconditionally. Since PY-87287, that call is gated behind shouldBeInitInstantly() (true only in unit-test mode), so forSdk() no longer blocks in production. On cache hit the method is a plain ConcurrentHashMap lookup. On cache miss it creates the manager, registers Disposer listeners, and sets up VFS watchers — lightweight work that in practice runs on a background thread during project/SDK setup, so subsequent EDT callers always get a cached instance. The stale annotation caused ISE on EDT, killing the entire Run action (ExecutorAction.runCurrentFile → PyTestsConfigurationProducer → isFrameworkInstalled → PythonPackageManager.forSdk). (cherry picked from commit f2dae4a6e2e3b2915e92641007302e7e641eae3e) IJ-MR-194254 GitOrigin-RevId: f197757763ca50acc9e927a82a79e75ff8151889 --- .../packaging/management/PythonPackageManager.kt | 1 - .../management/PythonPackageManagerServiceImpl.kt | 11 ++++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManager.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManager.kt index 01e972a4ec44..5c2d75cebb6b 100644 --- a/python/src/com/jetbrains/python/packaging/management/PythonPackageManager.kt +++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageManager.kt @@ -346,7 +346,6 @@ abstract class PythonPackageManager(val project: Project, val sdk: Sdk) : Dispos companion object { private val CACHE_KEY = Key.create>?>>>("PythonPackageManagerDependenciesCache") - @RequiresBackgroundThread @Throws(AlreadyDisposedException::class) fun forSdk(project: Project, sdk: Sdk): PythonPackageManager { val pythonPackageManagerService = project.service() diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerServiceImpl.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerServiceImpl.kt index 04ba96911d30..218d3d32066a 100644 --- a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerServiceImpl.kt +++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerServiceImpl.kt @@ -24,7 +24,16 @@ internal class PythonPackageManagerServiceImpl(private val serviceScope: Corouti private val bridgeCache = ConcurrentHashMap() /** - * Requires Sdk to be Python Sdk and have PythonSdkAdditionalData. + * Returns a cached [PythonPackageManager] for the given [sdk], creating one on first access. + * + * On cache hit the call is effectively free (a [ConcurrentHashMap] lookup). + * On cache miss (once per SDK per project lifetime) the method creates the manager, + * registers Disposer listeners and sets up VFS watchers — this may involve lightweight I/O + * (e.g. flavor detection in [com.jetbrains.python.sdk.getOrCreateAdditionalData]). + * In practice the first call happens during project/SDK setup on a background thread, + * so subsequent EDT callers always get a cached instance. + * + * Requires [sdk] to be a Python SDK with [com.jetbrains.python.sdk.PythonSdkAdditionalData]. */ override fun forSdk(project: Project, sdk: Sdk): PythonPackageManager { val cacheKey = (sdk.getOrCreateAdditionalData()).uuid