mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-85503 Introduce PyPIPackageRanking service for managing PyPI package rankings. Added logic to not asking confirmation for installing well-known packages by quick-fix.
GitOrigin-RevId: 5e57bb637d8ad66ffd186b8dcc130da7f090e731
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a7842dfbda
commit
32b3d56932
File diff suppressed because it is too large
Load Diff
+30
-16
@@ -11,6 +11,7 @@ import com.intellij.openapi.ui.Messages
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.ui.PyChooseRequirementsDialog
|
||||
import com.jetbrains.python.packaging.utils.PyPIPackageRanking
|
||||
import com.jetbrains.python.statistics.PyPackagesUsageCollector
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -21,34 +22,47 @@ internal object PyPackageManagerUiConfirmationHelpers {
|
||||
internal suspend fun getConfirmedPackages(pyRequirements: List<PyRequirement>, project: Project): List<PyRequirement> {
|
||||
if (pyRequirements.isEmpty())
|
||||
return emptyList()
|
||||
|
||||
val (rankedPackages, unrankedPackages) = pyRequirements.partition { requirement ->
|
||||
PyPIPackageRanking().rankedPackages.await().map { it.name }.contains(requirement.name)
|
||||
}
|
||||
|
||||
if (unrankedPackages.isEmpty()) {
|
||||
return pyRequirements
|
||||
}
|
||||
|
||||
val confirmationEnabled = PropertiesComponent.getInstance()
|
||||
.getBoolean(CONFIRM_PACKAGE_INSTALLATION_PROPERTY, true)
|
||||
|
||||
if (!confirmationEnabled)
|
||||
if (!confirmationEnabled) {
|
||||
return pyRequirements
|
||||
}
|
||||
|
||||
if (pyRequirements.size == 1) {
|
||||
val pyPackage = pyRequirements.first()
|
||||
val confirmedUnranked = if (unrankedPackages.size == 1) {
|
||||
val pyPackage = unrankedPackages.first()
|
||||
if (askSingleFileConfirmation(pyPackage, project)) {
|
||||
return listOf(pyPackage)
|
||||
listOf(pyPackage)
|
||||
}
|
||||
else {
|
||||
PyPackagesUsageCollector.installAllCanceledEvent.log()
|
||||
return listOf()
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
else {
|
||||
withContext(Dispatchers.EDT) {
|
||||
val dialog = PyChooseRequirementsDialog(project, unrankedPackages) { it.presentableText }
|
||||
val result = dialog.showAndGet()
|
||||
if (!result) {
|
||||
PyPackagesUsageCollector.installAllCanceledEvent.log()
|
||||
emptyList()
|
||||
}
|
||||
else {
|
||||
dialog.markedElements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return withContext(Dispatchers.EDT) {
|
||||
val dialog = PyChooseRequirementsDialog(project, pyRequirements) { it.presentableText }
|
||||
val result = dialog.showAndGet()
|
||||
if (!result) {
|
||||
PyPackagesUsageCollector.installAllCanceledEvent.log()
|
||||
listOf()
|
||||
}
|
||||
else {
|
||||
dialog.markedElements
|
||||
}
|
||||
}
|
||||
return rankedPackages + confirmedUnranked
|
||||
}
|
||||
|
||||
private suspend fun askSingleFileConfirmation(pyRequirement: PyRequirement, project: Project): Boolean = withContext(Dispatchers.EDT) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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.utils
|
||||
|
||||
import com.google.common.io.Resources
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.jetbrains.python.packaging.PyPackageName
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.net.URL
|
||||
|
||||
@ApiStatus.Internal
|
||||
fun PyPIPackageRanking(): PyPIPackageRanking = service<PyPIPackageRankingImpl>()
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface PyPIPackageRanking {
|
||||
val rankedPackages: Deferred<Set<PyPackageName>>
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class Packages(
|
||||
val packages: List<PackageEntry>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class PackageEntry(
|
||||
val name: String,
|
||||
val downloads: Int
|
||||
)
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
private class PyPIPackageRankingImpl(coroutineScope: CoroutineScope) : PyPIPackageRanking {
|
||||
|
||||
override val rankedPackages: Deferred<HashSet<PyPackageName>> = coroutineScope.async(Dispatchers.IO) {
|
||||
loadAndParseRankingData()
|
||||
}
|
||||
|
||||
private suspend fun loadAndParseRankingData(): HashSet<PyPackageName> = withContext(Dispatchers.IO) {
|
||||
val resource = loadResource()
|
||||
parseJsonToList(resource).packages.map { PyPackageName.from(it.name) }.toHashSet()
|
||||
}
|
||||
|
||||
private fun loadResource(): URL =
|
||||
PyPIPackageRanking::class.java.getResource(RANKING_RESOURCE_PATH)
|
||||
?: error("Python package ranking not found")
|
||||
|
||||
private fun parseJsonToList(resource: URL): Packages {
|
||||
val jsonString = Resources.asCharSource(resource, Charsets.UTF_8).read()
|
||||
return Json.decodeFromString<Packages>(jsonString)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val RANKING_RESOURCE_PATH = "/packaging/pypi-rank.json"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user