PY-83031 Packaging toolwindow: Remove Package Ranking and sorted map for pypi cache

Signed-off-by: Nikita.Ashihmin <nikita.ashihmin@jetbrains.com>

GitOrigin-RevId: 66a41bb255fee3f58a9ff2171d626a4de580f855
This commit is contained in:
Nikita.Ashihmin
2025-08-02 15:08:26 +00:00
committed by intellij-monorepo-bot
parent 7116084db0
commit 63fcd9f096
9 changed files with 14 additions and 116 deletions
File diff suppressed because one or more lines are too long
@@ -1,45 +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
import com.google.common.io.Resources
import com.google.gson.Gson
import com.intellij.openapi.components.Service
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import java.util.*
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
@Service
@ApiStatus.Internal
class PyPIPackageRanking {
private val lock = ReentrantReadWriteLock()
private var myPackageRank: Map<String, Int> = emptyMap()
get() = lock.read { field }
set(value) {
lock.write { field = value }
}
val packageRank: Map<String, Int>
get() = myPackageRank
val names: Sequence<String>
get() = myPackageRank.asSequence().map { it.key }
suspend fun reload() {
withContext(Dispatchers.IO) {
val gson = Gson()
val resource = PyPIPackageRanking::class.java.getResource("/packaging/pypi-ranking.json") ?: error("Python package ranking not found")
val array = Resources.asCharSource(resource, Charsets.UTF_8).openBufferedStream().use {
gson.fromJson(it, Array<Array<String>>::class.java)
}
val newRanked = array.asSequence()
.map { Pair(it[0].lowercase(), it[1].toInt()) }
.toMap(LinkedHashMap())
withContext(Dispatchers.Default) {
myPackageRank = Collections.unmodifiableMap(newRanked)
}
}
}
}
@@ -1,9 +1,7 @@
// 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.common
import com.intellij.openapi.components.service
import com.intellij.openapi.projectRoots.Sdk
import com.jetbrains.python.packaging.PyPIPackageRanking
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Experimental
@@ -11,20 +9,6 @@ interface PythonPackageManagementListener {
fun packagesChanged(sdk: Sdk) {}
@ApiStatus.Internal
fun outdatedPackagesChanged(sdk: Sdk) {}
}
internal class PythonRankingAwarePackageNameComparator : Comparator<String> {
val ranking = service<PyPIPackageRanking>().packageRank
override fun compare(name1: String, name2: String): Int {
val rank1 = ranking[name1.lowercase()]
val rank2 = ranking[name2.lowercase()]
return when {
rank1 != null && rank2 == null -> -1
rank1 == null && rank2 != null -> 1
rank1 != null && rank2 != null && rank1 != rank2 -> rank2 - rank1
else -> String.CASE_INSENSITIVE_ORDER.compare(name1, name2)
}
fun outdatedPackagesChanged(sdk: Sdk) {
}
}
}
@@ -15,7 +15,6 @@ import com.jetbrains.python.PyBundle
import com.jetbrains.python.getOrThrow
import com.jetbrains.python.packaging.PyPackageVersionComparator
import com.jetbrains.python.packaging.cache.PythonPackageCache
import com.jetbrains.python.packaging.common.PythonRankingAwarePackageNameComparator
import com.jetbrains.python.run.PythonInterpreterTargetEnvironmentFactory
import com.jetbrains.python.sdk.flavors.conda.PyCondaEnv
import com.jetbrains.python.sdk.flavors.conda.PyCondaEnvIdentity
@@ -103,7 +102,7 @@ internal class CondaPackageCache : PythonPackageCache<String> {
.filterNot { it[0].startsWith("r-") } // todo[akniazev]: make sure it's the best way to get rid of R packages
.groupBy({ it[0] }, { it[1] })
.mapValues { it.value.distinct().sortedWith(PyPackageVersionComparator.STR_COMPARATOR.reversed()) }
.toSortedMap(PythonRankingAwarePackageNameComparator())
.toMap()
cache = packages
}
@@ -10,7 +10,6 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.openapi.util.registry.Registry
import com.jetbrains.python.Result
import com.jetbrains.python.packaging.PyPIPackageRanking
import com.jetbrains.python.packaging.pip.PypiPackageCache
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -25,7 +24,6 @@ private class PythonPackagesUpdater : ProjectActivity {
override suspend fun execute(project: Project) {
withContext(Dispatchers.IO) {
thisLogger().debug("Updating PyPI cache and ranking")
serviceAsync<PyPIPackageRanking>().reload()
when (val r = serviceAsync<PypiPackageCache>().reloadCache()) {
is Result.Success -> Unit
is Result.Failure -> {
@@ -14,7 +14,6 @@ import com.intellij.util.io.SafeFileOutputStream
import com.jetbrains.python.Result
import com.jetbrains.python.packaging.PyPIPackageUtil
import com.jetbrains.python.packaging.cache.PythonPackageCache
import com.jetbrains.python.packaging.common.PythonRankingAwarePackageNameComparator
import com.jetbrains.python.packaging.normalizePackageName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
@@ -30,7 +29,6 @@ import java.nio.file.Path
import java.nio.file.Paths
import java.time.Duration
import java.time.Instant
import java.util.*
import kotlin.io.path.exists
private val LOG = logger<PypiPackageCache>()
@@ -116,10 +114,7 @@ open class PypiPackageCache : PythonPackageCache<String> {
withContext(Dispatchers.IO) {
LOG.info("Loading python packages from PyPi")
val pypiList = service<PypiPackageLoader>().loadPackages().getOr { return@withContext it }
val newCache = TreeSet(PythonRankingAwarePackageNameComparator())
newCache.addAll(pypiList)
cache = newCache
cache = pypiList.toSet()
store()
}
return Result.success(Unit)
@@ -21,14 +21,17 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFileManager
import com.jetbrains.python.PyBundle.message
import com.jetbrains.python.getOrNull
import com.jetbrains.python.packaging.*
import com.jetbrains.python.packaging.PyPackageService
import com.jetbrains.python.packaging.PyPackageVersionNormalizer
import com.jetbrains.python.packaging.cache.PythonSimpleRepositoryCache
import com.jetbrains.python.packaging.common.*
import com.jetbrains.python.packaging.conda.CondaPackage
import com.jetbrains.python.packaging.management.*
import com.jetbrains.python.packaging.management.ui.PythonPackageManagerUI
import com.jetbrains.python.packaging.normalizePackageName
import com.jetbrains.python.packaging.packageRequirements.PackageNode
import com.jetbrains.python.packaging.packageRequirements.PythonPackageRequirementsTreeExtractor
import com.jetbrains.python.packaging.pyRequirement
import com.jetbrains.python.packaging.repository.*
import com.jetbrains.python.packaging.statistics.PythonPackagesToolwindowStatisticsCollector
import com.jetbrains.python.packaging.toolwindow.model.*
@@ -58,7 +61,6 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
fun initialize(toolWindowPanel: PyPackagingToolWindowPanel) {
this.toolWindowPanel = toolWindowPanel
serviceScope.launch(Dispatchers.IO) {
service<PyPIPackageRanking>().reload()
initForSdk(project.modules.firstOrNull()?.pythonSdk)
}
subscribeToChanges()
@@ -89,7 +91,8 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
return if (shouldUseStraightComparison) {
StringUtil.containsIgnoreCase(pkg.name, query)
} else {
}
else {
StringUtil.containsIgnoreCase(normalizePackageName(pkg.name), normalizePackageName(query))
}
}
@@ -486,20 +489,6 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
}
}
if (PyPIPackageUtil.isPyPIRepository(url)) {
val ranking = service<PyPIPackageRanking>().packageRank
return Comparator { p1, p2 ->
val rank1 = ranking[p1.lowercase()]
val rank2 = ranking[p2.lowercase()]
return@Comparator when {
rank1 != null && rank2 == null -> -1
rank1 == null && rank2 != null -> 1
rank1 != null && rank2 != null -> rank2 - rank1
else -> nameComparator.compare(p1, p2)
}
}
}
return nameComparator
}
}
@@ -13,7 +13,6 @@ import com.intellij.platform.util.progress.reportRawProgress
import com.intellij.python.community.helpersLocator.PythonHelpersLocator
import com.jetbrains.python.PyBundle.message
import com.jetbrains.python.PythonHelper
import com.jetbrains.python.packaging.PyPIPackageRanking
import com.jetbrains.python.packaging.common.PythonPackageDetails
import com.jetbrains.python.run.PythonInterpreterTargetEnvironmentFactory
import com.jetbrains.python.run.applyHelperPackageToPythonPath
@@ -99,7 +98,7 @@ class PyPackageDetailsHtmlRender(val project: Project, val currentSdk: Sdk?) {
}
private fun markdownToHtml(text: String): String {
val mdHtml = PyPIPackageRanking::class.java.getResource("/packaging/md.template.html")?.readText() ?: error("Cannot get md template")
val mdHtml = this::class.java.getResource("/packaging/md.template.html")?.readText() ?: error("Cannot get md template")
val quotedText = text.replace("`", "\\`")
val prepared = mdHtml.replace("{MD_TEXT}", "\n" + quotedText)
@@ -8,7 +8,6 @@ import com.intellij.util.io.delete
import com.intellij.util.io.write
import com.jetbrains.python.Result
import com.jetbrains.python.fixtures.PyTestCase
import com.jetbrains.python.packaging.PyPIPackageRanking
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.mockito.Mockito
@@ -19,39 +18,29 @@ import kotlin.io.path.setLastModifiedTime
class PypiPackageCacheTest : PyTestCase() {
fun testCachedPackagesShouldBeOrdered() {
withPypiPackages(listOf("c-pkg", "b-pkg", "a-pkg"))
withPypiPackagesRanking(mapOf("c-pkg" to 2, "b-pkg" to 1, "a-pkg" to 1))
withEmptyCacheStorage()
val cache = PypiPackageCache()
runBlocking { cache.reloadCache().orThrow() }
assertThat(cache.packages).containsExactly("c-pkg", "a-pkg", "b-pkg")
}
fun testCacheShouldNotBeUpdatedIfLocalStorageIsntExpired() {
withLocalStoredPackages(listOf("c-pkg", "a-pkg", "b-pkg"), Instant.now())
withPypiLoaderThrowingError()
val cache = PypiPackageCache()
runBlocking { cache.reloadCache().orThrow() }
assertThat(cache.packages).containsExactly("c-pkg", "a-pkg", "b-pkg")
assertThat(cache.packages).contains("c-pkg", "a-pkg", "b-pkg")
}
fun testCacheShouldBeUpdatedIfLocalStorageIsExpired() {
withLocalStoredPackages(listOf("a-pkg"), Instant.now().minus(Duration.ofDays(2)))
withPypiPackages(listOf("c-pkg", "b-pkg", "a-pkg"))
withPypiPackagesRanking(mapOf("c-pkg" to 2, "b-pkg" to 1, "a-pkg" to 1))
val cache = PypiPackageCache()
runBlocking { cache.reloadCache().orThrow() }
assertThat(cache.packages).containsExactly("c-pkg", "a-pkg", "b-pkg")
assertThat(cache.packages).contains("c-pkg", "a-pkg", "b-pkg")
}
fun testBrokenLocalStorageShouldBeGracefullyHandled() {
withBrokenLocalStorage()
withPypiPackages(listOf("c-pkg", "b-pkg", "a-pkg"))
withPypiPackagesRanking(mapOf("c-pkg" to 2, "b-pkg" to 1, "a-pkg" to 1))
val cache = PypiPackageCache()
runBlocking { cache.reloadCache().orThrow() }
assertThat(cache.packages).containsExactly("c-pkg", "a-pkg", "b-pkg")
assertThat(cache.packages).contains("c-pkg", "a-pkg", "b-pkg")
}
private fun withEmptyCacheStorage() {
@@ -87,13 +76,4 @@ class PypiPackageCacheTest : PyTestCase() {
mock
)
}
private fun withPypiPackagesRanking(ranking: Map<String, Int>) {
val mock = Mockito.mock(PyPIPackageRanking::class.java)
Mockito.`when`(mock.packageRank).thenReturn(ranking)
ApplicationManager.getApplication().registerServiceInstance(
PyPIPackageRanking::class.java,
mock
)
}
}