PY-85822 remove @RequiresBackgroundThread from PythonPackageManager.forSdk()

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
This commit is contained in:
Vitaly Legchilkin
2026-03-04 08:18:44 +00:00
committed by intellij-monorepo-bot
parent bc460c94e5
commit 22c2cbb69e
2 changed files with 10 additions and 2 deletions
@@ -346,7 +346,6 @@ abstract class PythonPackageManager(val project: Project, val sdk: Sdk) : Dispos
companion object {
private val CACHE_KEY = Key.create<CachedValue<Deferred<PyResult<List<PythonPackage>>?>>>("PythonPackageManagerDependenciesCache")
@RequiresBackgroundThread
@Throws(AlreadyDisposedException::class)
fun forSdk(project: Project, sdk: Sdk): PythonPackageManager {
val pythonPackageManagerService = project.service<PythonPackageManagerService>()
@@ -24,7 +24,16 @@ internal class PythonPackageManagerServiceImpl(private val serviceScope: Corouti
private val bridgeCache = ConcurrentHashMap<UUID, PythonPackageManagementServiceBridge>()
/**
* 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