mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
PY-79792 Refactored repository handling in Python packaging subsystem. Unified repository management by removing redundant methods (packagesFromRepository, allPackages) and standardized package retrieval via getPackages from PyPackageRepository. Updated related classes to align with the new CompositePythonRepositoryManager and PythonRepositoryManager interfaces. Added Test. PY-80168 Replace latest text with real latest version.
GitOrigin-RevId: f9d826a84d469c75a0c66f34a308cdde16c2f5b0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
48211caacf
commit
ff86ab4b82
@@ -145,5 +145,8 @@
|
||||
<orderEntry type="library" name="io.github.z4kn4fein.semver.jvm" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.python.pyproject" />
|
||||
<orderEntry type="module" module-name="intellij.python.hatch" />
|
||||
<orderEntry type="module" module-name="intellij.platform.externalSystem.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.externalSystem" />
|
||||
<orderEntry type="library" name="http-client" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -6,6 +6,7 @@ import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.asContextElement
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.progress.runBlockingCancellable
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.util.CatchingConsumer
|
||||
@@ -15,90 +16,65 @@ import com.intellij.webcore.packaging.RepoPackage
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.packaging.PyPackagingSettings
|
||||
import com.jetbrains.python.packaging.common.*
|
||||
import com.jetbrains.python.packaging.conda.CondaPackage
|
||||
import com.jetbrains.python.packaging.conda.CondaPackageCache
|
||||
import com.jetbrains.python.packaging.conda.CondaPackageManager
|
||||
import com.jetbrains.python.packaging.conda.CondaPackageRepository
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.packagesByRepository
|
||||
import com.jetbrains.python.packaging.management.runPackagingTool
|
||||
import com.jetbrains.python.packaging.repository.PyPIPackageRepository
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService
|
||||
import com.jetbrains.python.packaging.ui.PyPackageManagementService
|
||||
import kotlinx.coroutines.*
|
||||
import com.jetbrains.python.sdk.conda.isConda
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class PythonPackageManagementServiceBridge(project: Project,sdk: Sdk) : PyPackageManagementService(project, sdk), Disposable {
|
||||
class PythonPackageManagementServiceBridge(project: Project, sdk: Sdk) : PyPackageManagementService(project, sdk), Disposable {
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
private val scope = project.service<PyPackagingToolWindowService>().serviceScope
|
||||
|
||||
private val manager: PythonPackageManager
|
||||
get() = PythonPackageManager.forSdk(project, sdk)
|
||||
|
||||
var useConda = true
|
||||
|
||||
var useConda: Boolean = true
|
||||
val isConda: Boolean
|
||||
get() = manager is CondaPackageManager
|
||||
|
||||
get() = sdk.isConda()
|
||||
|
||||
override fun getInstalledPackagesList(): List<InstalledPackage> {
|
||||
if (manager.installedPackages.isEmpty()) runBlocking {
|
||||
if (manager.installedPackages.isEmpty()) runBlockingCancellable {
|
||||
manager.reloadPackages()
|
||||
}
|
||||
if (isConda) {
|
||||
if (useConda) {
|
||||
return manager.installedPackages.asSequence()
|
||||
.filterIsInstance<CondaPackage>()
|
||||
.filter { it.installedWithPip != useConda }
|
||||
.map { InstalledPackage(it.name, it.version) }
|
||||
.toList()
|
||||
}
|
||||
else {
|
||||
return runBlocking {
|
||||
val result = runPackagingOperationOrShowErrorDialog(sdk, PyBundle.message("python.packaging.operation.failed.title")) {
|
||||
val output = manager.runPackagingTool("list", emptyList(), PyBundle.message("python.packaging.list.progress"))
|
||||
|
||||
val packages = output.lineSequence()
|
||||
.filter { it.isNotBlank() }
|
||||
.map {
|
||||
val line = it.split("\t")
|
||||
PythonPackage(line[0], line[1], isEditableMode = false)
|
||||
}
|
||||
.sortedWith(compareBy(PythonPackage::name))
|
||||
.toList()
|
||||
Result.success(packages)
|
||||
}
|
||||
return@runBlocking if (result.isSuccess) {
|
||||
result.getOrThrow().map { InstalledPackage(it.name, it.version) }
|
||||
} else emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
return manager.installedPackages.map { InstalledPackage(it.name, it.version) }
|
||||
}
|
||||
|
||||
override fun getAllPackages(): List<RepoPackage> {
|
||||
if (isConda && useConda) {
|
||||
val settings = PyPackagingSettings.getInstance(project)
|
||||
val cache = service<CondaPackageCache>()
|
||||
return manager
|
||||
.repositoryManager
|
||||
.packagesFromRepository(CondaPackageRepository)
|
||||
.asSequence()
|
||||
.map { RepoPackage(it, null, settings.selectLatestVersion(cache[it] ?: emptyList())) }
|
||||
.toMutableList()
|
||||
val packagesWithRepositories = manager.repositoryManager.packagesByRepository()
|
||||
return packagesWithRepositories
|
||||
.flatMap { (repository, packages) ->
|
||||
packages.asSequence().map { pkg ->
|
||||
createRepoPackage(pkg, repository)
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
private fun createRepoPackage(pkg: String, repository: PyPackageRepository): RepoPackage {
|
||||
val repositoryUrl = when {
|
||||
repository.isCustom -> repository.repositoryUrl
|
||||
else -> null
|
||||
}
|
||||
val latestVersion = getLatestVersion(pkg)
|
||||
return RepoPackage(pkg, repositoryUrl, latestVersion)
|
||||
}
|
||||
|
||||
val hasRepositories = manager
|
||||
.repositoryManager
|
||||
.repositories
|
||||
.any { it !is PyPIPackageRepository && it !is CondaPackageRepository }
|
||||
// TODO unify logic of retrieving package versions for pypi and conda
|
||||
private fun getLatestVersion(pkg: String): String? {
|
||||
if (!isConda || !useConda) return null
|
||||
|
||||
return manager
|
||||
.repositoryManager
|
||||
.packagesByRepository()
|
||||
.filterNot { it.first is CondaPackageRepository }
|
||||
.flatMap { (repo, pkgs) -> pkgs.asSequence().map { RepoPackage(it, if (hasRepositories) repo.repositoryUrl else null) } }
|
||||
.toMutableList()
|
||||
val settings = PyPackagingSettings.getInstance(project)
|
||||
val cache = service<CondaPackageCache>()
|
||||
val versions = cache[pkg] ?: emptyList()
|
||||
return settings.selectLatestVersion(versions)
|
||||
}
|
||||
|
||||
override fun getAllPackagesCached(): List<RepoPackage> {
|
||||
@@ -106,7 +82,7 @@ class PythonPackageManagementServiceBridge(project: Project,sdk: Sdk) : PyPackag
|
||||
}
|
||||
|
||||
override fun reloadAllPackages(): List<RepoPackage> {
|
||||
return runBlocking {
|
||||
return runBlockingCancellable {
|
||||
manager.repositoryManager.refreshCaches()
|
||||
allPackages
|
||||
}
|
||||
@@ -185,15 +161,12 @@ class PythonPackageManagementServiceBridge(project: Project,sdk: Sdk) : PyPackag
|
||||
}
|
||||
}
|
||||
|
||||
private fun findRepositoryForPackage(name: String): PyPackageRepository {
|
||||
if (manager is CondaPackageManager && useConda) return CondaPackageRepository
|
||||
return manager
|
||||
private fun findRepositoryForPackage(name: String): PyPackageRepository =
|
||||
manager
|
||||
.repositoryManager
|
||||
.packagesByRepository()
|
||||
.filterNot { it.first is CondaPackageRepository || it.first is PyPIPackageRepository }
|
||||
.firstOrNull { (_, packages) -> name in packages }
|
||||
?.first ?: PyPIPackageRepository
|
||||
}
|
||||
|
||||
private fun buildDescription(details: PythonPackageDetails): String {
|
||||
return buildString {
|
||||
@@ -235,6 +208,6 @@ class PythonPackageManagementServiceBridge(project: Project,sdk: Sdk) : PyPackag
|
||||
}
|
||||
|
||||
companion object {
|
||||
var runningUnderOldUI = false
|
||||
var runningUnderOldUI: Boolean = false
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ interface PythonPackageSpecification {
|
||||
}
|
||||
if (repository != null && repository != PyPIPackageRepository) {
|
||||
add("--index-url")
|
||||
add(repository!!.urlForInstallation)
|
||||
add(repository!!.urlForInstallation.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ internal class CompositePythonPackageManager(
|
||||
override var installedPackages: List<PythonPackage> = emptyList()
|
||||
|
||||
override var repositoryManager: PythonRepositoryManager =
|
||||
CompositePythonRepositoryManager(project, sdk, managers.map { it.repositoryManager })
|
||||
CompositePythonRepositoryManager(project, managers.map { it.repositoryManager }, sdk)
|
||||
|
||||
private val managerNames = managers.joinToString { it.javaClass.simpleName }
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.packaging.PyPackageVersion
|
||||
import com.jetbrains.python.packaging.PyPackageVersionComparator
|
||||
import com.jetbrains.python.packaging.common.EmptyPythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManagerService
|
||||
import com.jetbrains.python.packaging.management.PythonRepositoryManager
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import io.github.z4kn4fein.semver.toVersion
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
@@ -21,32 +21,16 @@ import kotlinx.coroutines.sync.withPermit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
internal class CompositePythonRepositoryManager(
|
||||
project: Project,
|
||||
sdk: Sdk,
|
||||
override val project: Project,
|
||||
private val managers: List<PythonRepositoryManager>,
|
||||
) : PythonRepositoryManager(project, sdk) {
|
||||
@Deprecated("Don't use sdk from here") override val sdk: Sdk
|
||||
) : PythonRepositoryManager {
|
||||
|
||||
override val repositories: List<PyPackageRepository> =
|
||||
managers.flatMap { it.repositories }
|
||||
|
||||
override fun allPackages(): Set<String> {
|
||||
return managers.fold(emptySet()) { acc, manager -> acc + manager.allPackages() }
|
||||
}
|
||||
|
||||
override fun packagesFromRepository(repository: PyPackageRepository): Set<String> {
|
||||
return findPackagesInRepository(repository)
|
||||
?: error("No packages for requested repository in cache")
|
||||
}
|
||||
|
||||
private fun findPackagesInRepository(repository: PyPackageRepository): Set<String>? {
|
||||
for (manager in managers) {
|
||||
val packages = manager.packagesFromRepository(repository)
|
||||
if (packages.isNotEmpty()) {
|
||||
return packages
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
override fun allPackages(): Set<String> =
|
||||
repositories.flatMap { it.getPackages() }.toSet()
|
||||
|
||||
override suspend fun getPackageDetails(pkg: PythonPackageSpecification): PythonPackageDetails {
|
||||
for (manager in managers) {
|
||||
@@ -61,9 +45,7 @@ internal class CompositePythonRepositoryManager(
|
||||
var latestVersion: PyPackageVersion? = null
|
||||
for (manager in managers) {
|
||||
val version = manager.getLatestVersion(spec)
|
||||
if (version != null &&
|
||||
(latestVersion == null || version.presentableText.toVersion() > latestVersion.presentableText.toVersion())
|
||||
) {
|
||||
if (version != null && (latestVersion == null || PyPackageVersionComparator.compare(version, latestVersion) > 0)) {
|
||||
latestVersion = version
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,14 @@ import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
internal class CondaRepositoryManger(project: Project, sdk: Sdk) : PipBasedRepositoryManager(project, sdk) {
|
||||
internal class CondaRepositoryManger(
|
||||
override val project: Project,
|
||||
@Deprecated("Don't use sdk from here") override val sdk: Sdk
|
||||
) : PipBasedRepositoryManager() {
|
||||
|
||||
override val repositories: List<PyPackageRepository>
|
||||
get() = listOf(CondaPackageRepository) + super.repositories
|
||||
|
||||
override fun allPackages(): Set<String> = service<CondaPackageCache>().packages
|
||||
|
||||
override fun packagesFromRepository(repository: PyPackageRepository): Set<String> {
|
||||
return if (repository is CondaPackageRepository) service<CondaPackageCache>().packages else super.packagesFromRepository(repository)
|
||||
}
|
||||
|
||||
override fun buildPackageDetails(rawInfo: String?, spec: PythonPackageSpecification): PythonPackageDetails {
|
||||
if (spec is CondaPackageSpecification) {
|
||||
val versions = service<CondaPackageCache>()[spec.name] ?: error("No conda package versions in cache")
|
||||
|
||||
@@ -23,7 +23,7 @@ class CondaPackageSpecification(name: String,
|
||||
relation: PyRequirementRelation? = null) : PythonPackageSpecificationBase(name, version, relation, CondaPackageRepository) {
|
||||
override val repository: PyPackageRepository = CondaPackageRepository
|
||||
override var versionSpecs: String? = null
|
||||
get() = if (field != null) "${field}" else if (version != null) "${relation?.presentableText ?: "="}$version" else ""
|
||||
get() = if (field != null) "${field}" else if (version != null) "${relation?.presentableText ?: "=="}$version" else ""
|
||||
|
||||
override fun buildInstallationString(): List<String> {
|
||||
return listOf("$name$versionSpecs")
|
||||
@@ -42,8 +42,18 @@ class CondaPackageDetails(override val name: String,
|
||||
}
|
||||
}
|
||||
|
||||
object CondaPackageRepository : PyPackageRepository("Conda", "", "") {
|
||||
object CondaPackageRepository : PyPackageRepository("Conda", null, null) {
|
||||
override fun createPackageSpecification(packageName: String, version: String?, relation: PyRequirementRelation?): PythonPackageSpecification {
|
||||
return CondaPackageSpecification(packageName, version, relation)
|
||||
}
|
||||
|
||||
override fun createForcedSpecPackageSpecification(packageName: String, versionSpecs: String?): PythonPackageSpecification {
|
||||
val spec = CondaPackageSpecification(packageName, null, null)
|
||||
spec.versionSpecs = versionSpecs
|
||||
return spec
|
||||
}
|
||||
|
||||
override fun getPackages(): Set<String> {
|
||||
return service<CondaPackageCache>().packages
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ private val proxyString: String?
|
||||
}
|
||||
|
||||
fun PythonRepositoryManager.packagesByRepository(): Sequence<Pair<PyPackageRepository, Set<String>>> {
|
||||
return repositories.asSequence().map { it to packagesFromRepository(it) }
|
||||
return repositories.asSequence().map { it to it.getPackages() }
|
||||
}
|
||||
|
||||
fun PythonPackageManager.isInstalled(name: String): Boolean {
|
||||
|
||||
@@ -8,23 +8,25 @@ import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.io.IOException
|
||||
|
||||
@ApiStatus.Experimental
|
||||
abstract class PythonRepositoryManager(val project: Project, val sdk: Sdk) {
|
||||
abstract val repositories: List<PyPackageRepository>
|
||||
interface PythonRepositoryManager {
|
||||
@Deprecated("Don't use sdk from here")
|
||||
val sdk: Sdk
|
||||
val project: Project
|
||||
val repositories: List<PyPackageRepository>
|
||||
|
||||
abstract fun allPackages(): Set<String>
|
||||
fun allPackages(): Set<String>
|
||||
fun searchPackages(query: String): Map<PyPackageRepository, List<String>>
|
||||
fun searchPackages(query: String, repository: PyPackageRepository): List<String>
|
||||
|
||||
abstract fun packagesFromRepository(repository: PyPackageRepository): Set<String>
|
||||
abstract suspend fun getPackageDetails(pkg: PythonPackageSpecification): PythonPackageDetails
|
||||
abstract suspend fun getLatestVersion(spec: PythonPackageSpecification): PyPackageVersion?
|
||||
suspend fun getPackageDetails(pkg: PythonPackageSpecification): PythonPackageDetails
|
||||
suspend fun getLatestVersion(spec: PythonPackageSpecification): PyPackageVersion?
|
||||
fun buildPackageDetails(rawInfo: String?, spec: PythonPackageSpecification): PythonPackageDetails
|
||||
|
||||
abstract suspend fun refreshCaches()
|
||||
|
||||
abstract suspend fun initCaches()
|
||||
|
||||
abstract fun buildPackageDetails(rawInfo: String?, spec: PythonPackageSpecification): PythonPackageDetails
|
||||
|
||||
abstract fun searchPackages(query: String, repository: PyPackageRepository): List<String>
|
||||
abstract fun searchPackages(query: String): Map<PyPackageRepository, List<String>>
|
||||
}
|
||||
@Throws(IOException::class)
|
||||
suspend fun refreshCaches()
|
||||
@Throws(IOException::class)
|
||||
suspend fun initCaches()
|
||||
}
|
||||
|
||||
@@ -5,25 +5,30 @@ import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.google.gson.Gson
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.util.io.HttpRequests
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.packaging.*
|
||||
import com.jetbrains.python.packaging.PyPIPackageUtil
|
||||
import com.jetbrains.python.packaging.PyPackageVersion
|
||||
import com.jetbrains.python.packaging.PyPackageVersionComparator
|
||||
import com.jetbrains.python.packaging.PyPackageVersionNormalizer
|
||||
import com.jetbrains.python.packaging.cache.PythonSimpleRepositoryCache
|
||||
import com.jetbrains.python.packaging.common.EmptyPythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.common.PythonSimplePackageDetails
|
||||
import com.jetbrains.python.packaging.management.PythonRepositoryManager
|
||||
import com.jetbrains.python.packaging.management.packagesByRepository
|
||||
import com.jetbrains.python.packaging.repository.*
|
||||
import com.jetbrains.python.packaging.normalizePackageName
|
||||
import com.jetbrains.python.packaging.repository.PyEmptyPackagePackageRepository
|
||||
import com.jetbrains.python.packaging.repository.PyPIPackageRepository
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepositories
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import com.jetbrains.python.packaging.repository.withBasicAuthorization
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.time.Duration
|
||||
|
||||
@ApiStatus.Experimental
|
||||
internal abstract class PipBasedRepositoryManager(project: Project, sdk: Sdk) : PythonRepositoryManager(project, sdk) {
|
||||
internal abstract class PipBasedRepositoryManager() : PythonRepositoryManager {
|
||||
|
||||
override val repositories: List<PyPackageRepository>
|
||||
get() = listOf(PyPIPackageRepository) + service<PythonSimpleRepositoryCache>().repositories
|
||||
@@ -34,9 +39,8 @@ internal abstract class PipBasedRepositoryManager(project: Project, sdk: Sdk) :
|
||||
.expireAfterWrite(Duration.ofHours(1))
|
||||
.build<PythonPackageSpecification, PythonPackageDetails> {
|
||||
// todo[akniazev] make it possible to show info from several repos
|
||||
val repositoryUrl = it.repository?.repositoryUrl ?: PyPIPackageRepository.repositoryUrl!!
|
||||
val repositoryUrl = it.repository?.repositoryUrl ?: PyPIPackageRepository.repositoryUrl ?: ""
|
||||
val result = runCatching {
|
||||
|
||||
val packageDetailsUrl = PyPIPackageUtil.buildDetailsUrl(repositoryUrl, it.name)
|
||||
HttpRequests.request(packageDetailsUrl)
|
||||
.withBasicAuthorization(it.repository)
|
||||
@@ -72,7 +76,7 @@ internal abstract class PipBasedRepositoryManager(project: Project, sdk: Sdk) :
|
||||
if (rawInfo == null) {
|
||||
val versions = tryParsingVersionsFromPage(spec.name, spec.repository?.repositoryUrl)
|
||||
val repository = if (spec.repository !is PyEmptyPackagePackageRepository) spec.repository else PyPIPackageRepository
|
||||
val repositoryName = repository?.name ?: PyPIPackageRepository.name!!
|
||||
val repositoryName = repository?.name ?: PyPIPackageRepository.name
|
||||
if (versions != null) {
|
||||
return PythonSimplePackageDetails(
|
||||
spec.name,
|
||||
@@ -133,14 +137,8 @@ internal abstract class PipBasedRepositoryManager(project: Project, sdk: Sdk) :
|
||||
service<PythonSimpleRepositoryCache>().refresh()
|
||||
}
|
||||
|
||||
override fun allPackages(): Set<String> {
|
||||
return packagesByRepository().fold(emptySet()) { acc, manager -> acc + manager.second }
|
||||
}
|
||||
|
||||
override fun packagesFromRepository(repository: PyPackageRepository): Set<String> {
|
||||
return if (repository is PyPIPackageRepository) service<PypiPackageCache>().packages
|
||||
else service<PythonSimpleRepositoryCache>()[repository] ?: error("No packages for requested repository in cache")
|
||||
}
|
||||
override fun allPackages(): Set<String> =
|
||||
repositories.flatMap { it.getPackages() }.toSet()
|
||||
|
||||
override suspend fun getPackageDetails(pkg: PythonPackageSpecification): PythonPackageDetails {
|
||||
return packageDetailsCache[pkg]
|
||||
@@ -152,7 +150,7 @@ internal abstract class PipBasedRepositoryManager(project: Project, sdk: Sdk) :
|
||||
|
||||
override fun searchPackages(query: String, repository: PyPackageRepository): List<String> {
|
||||
val normalizedQuery = normalizePackageName(query)
|
||||
return packagesFromRepository(repository).filter { StringUtil.containsIgnoreCase(normalizePackageName(it), normalizedQuery) }
|
||||
return repository.getPackages().filter { StringUtil.containsIgnoreCase(normalizePackageName(it), normalizedQuery) }
|
||||
}
|
||||
|
||||
override fun searchPackages(query: String): Map<PyPackageRepository, List<String>> {
|
||||
|
||||
@@ -6,4 +6,7 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
internal class PipRepositoryManager(project: Project, sdk: Sdk) : PipBasedRepositoryManager(project, sdk)
|
||||
internal class PipRepositoryManager(
|
||||
override val project: Project,
|
||||
@Deprecated("Don't use sdk from here") override val sdk: Sdk,
|
||||
) : PipBasedRepositoryManager()
|
||||
@@ -3,4 +3,4 @@ package com.jetbrains.python.packaging.repository
|
||||
|
||||
import com.jetbrains.python.PyBundle
|
||||
|
||||
class InstalledPyPackagedRepository : PyPackageRepository(PyBundle.message("python.toolwindow.packages.installed.label"), "", "") {}
|
||||
class InstalledPyPackagedRepository : PyPackageRepository(PyBundle.message("python.toolwindow.packages.installed.label"), null, null)
|
||||
@@ -5,70 +5,86 @@ import com.intellij.credentialStore.CredentialAttributes
|
||||
import com.intellij.credentialStore.Credentials
|
||||
import com.intellij.credentialStore.generateServiceName
|
||||
import com.intellij.ide.passwordSafe.PasswordSafe
|
||||
import com.intellij.openapi.components.BaseState
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.util.xmlb.annotations.Transient
|
||||
import com.jetbrains.python.packaging.cache.PythonSimpleRepositoryCache
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.common.PythonSimplePackageSpecification
|
||||
import com.jetbrains.python.packaging.conda.CondaPackageRepository
|
||||
import com.jetbrains.python.packaging.requirement.PyRequirementRelation
|
||||
import org.apache.http.client.utils.URIBuilder
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.net.URL
|
||||
|
||||
@ApiStatus.Internal
|
||||
open class PyPackageRepository() : BaseState() {
|
||||
var name by string("")
|
||||
var repositoryUrl by string("")
|
||||
var authorizationType by enum(PyPackageRepositoryAuthenticationType.NONE)
|
||||
var login by string("")
|
||||
open class PyPackageRepository() {
|
||||
|
||||
val urlForInstallation: String
|
||||
get() {
|
||||
val fullUrl = repositoryUrl!!
|
||||
if (login != null && login!!.isNotBlank()) {
|
||||
val password = getPassword()
|
||||
if (password != null) {
|
||||
val protocol = fullUrl.substringBefore("//")
|
||||
val url = fullUrl.substringAfter("//")
|
||||
return "$protocol//${encodeCredentialsForUrl(login!!, password)}@$url"
|
||||
}
|
||||
}
|
||||
return fullUrl
|
||||
}
|
||||
var name: String = ""
|
||||
internal set
|
||||
var repositoryUrl: String? = null
|
||||
internal set
|
||||
var login: String? = null
|
||||
internal set
|
||||
var authorizationType: PyPackageRepositoryAuthenticationType = PyPackageRepositoryAuthenticationType.NONE
|
||||
internal set
|
||||
|
||||
constructor(name: String, repositoryUrl: String?, login: String?) : this() {
|
||||
this.name = name
|
||||
this.repositoryUrl = repositoryUrl
|
||||
this.login = login
|
||||
}
|
||||
|
||||
internal val isCustom: Boolean
|
||||
get() = this !is PyPIPackageRepository && this !is CondaPackageRepository
|
||||
|
||||
private val serviceName: String
|
||||
get() = generateServiceName(SUBSYSTEM_NAME, name)
|
||||
|
||||
val urlForInstallation: URL
|
||||
get() = repositoryUrl?.let { baseUrl ->
|
||||
val userLogin = login.takeUnless { it.isNullOrBlank() } ?: return URL(baseUrl)
|
||||
val userPassword = getPassword() ?: return URL(baseUrl)
|
||||
buildAuthenticatedUrl(baseUrl, userLogin, userPassword)
|
||||
} ?: URL("")
|
||||
|
||||
private fun buildAuthenticatedUrl(baseUrl: String, login: String, password: String): URL =
|
||||
URIBuilder(baseUrl).setUserInfo(login, password).build().toURL()
|
||||
|
||||
@Transient
|
||||
fun getPassword(): String? {
|
||||
val serviceName = generateServiceName("PyCharm", name!!)
|
||||
val attributes = CredentialAttributes(serviceName, login)
|
||||
return PasswordSafe.instance.getPassword(attributes)
|
||||
}
|
||||
|
||||
fun setPassword(pass: String?) {
|
||||
val serviceName = generateServiceName("PyCharm", name!!)
|
||||
val attributes = CredentialAttributes(serviceName, login)
|
||||
PasswordSafe.instance.set(attributes, Credentials(login, pass))
|
||||
}
|
||||
|
||||
fun clearCredentials() {
|
||||
val serviceName = generateServiceName("PyCharm", name!!)
|
||||
val attributes = CredentialAttributes(serviceName, login)
|
||||
PasswordSafe.instance.set(attributes, null)
|
||||
}
|
||||
|
||||
constructor(name: String, repositoryUrl: String, username: String) : this() {
|
||||
this.name = name
|
||||
this.repositoryUrl = repositoryUrl
|
||||
this.login = username
|
||||
}
|
||||
open fun createPackageSpecification(
|
||||
packageName: String,
|
||||
version: String? = null,
|
||||
relation: PyRequirementRelation? = null,
|
||||
): PythonPackageSpecification =
|
||||
PythonSimplePackageSpecification(packageName, version, this, relation)
|
||||
|
||||
open fun createPackageSpecification(packageName: String,
|
||||
version: String? = null,
|
||||
relation: PyRequirementRelation? = null): PythonPackageSpecification {
|
||||
return PythonSimplePackageSpecification(packageName, version, this, relation)
|
||||
}
|
||||
open fun createForcedSpecPackageSpecification(
|
||||
packageName: String,
|
||||
versionSpecs: String? = null,
|
||||
): PythonPackageSpecification =
|
||||
PythonSimplePackageSpecification(packageName, null, this).apply {
|
||||
this.versionSpecs = versionSpecs
|
||||
}
|
||||
|
||||
open fun createForcedSpecPackageSpecification(packageName: String,
|
||||
versionSpecs: String? = null): PythonPackageSpecification {
|
||||
val spec = PythonSimplePackageSpecification(packageName, null, this)
|
||||
spec.versionSpecs = versionSpecs
|
||||
return spec
|
||||
open fun getPackages(): Set<String> =
|
||||
service<PythonSimpleRepositoryCache>()[this] ?: emptySet()
|
||||
|
||||
companion object {
|
||||
private const val SUBSYSTEM_NAME = "PyCharm"
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,15 @@
|
||||
@file:JvmName("PyPackageRepositoryUtil")
|
||||
package com.jetbrains.python.packaging.repository
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.util.io.HttpRequests
|
||||
import com.intellij.util.io.RequestBuilder
|
||||
import com.jetbrains.python.packaging.PyPIPackageUtil
|
||||
import com.jetbrains.python.packaging.pip.PypiPackageCache
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.*
|
||||
import java.util.Base64
|
||||
|
||||
@ApiStatus.Experimental
|
||||
internal fun RequestBuilder.withBasicAuthorization(repository: PyPackageRepository?): RequestBuilder {
|
||||
@@ -37,7 +39,11 @@ internal fun encodeCredentialsForUrl(login: String, password: String): String {
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
object PyEmptyPackagePackageRepository : PyPackageRepository("empty repository", "", "")
|
||||
object PyEmptyPackagePackageRepository : PyPackageRepository("empty repository", null, null)
|
||||
|
||||
@ApiStatus.Experimental
|
||||
object PyPIPackageRepository : PyPackageRepository("PyPI", PyPIPackageUtil.PYPI_LIST_URL, "")
|
||||
object PyPIPackageRepository : PyPackageRepository("PyPI", PyPIPackageUtil.PYPI_LIST_URL, null) {
|
||||
override fun getPackages(): Set<String> {
|
||||
return service<PypiPackageCache>().packages
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,9 @@ class PyRepositoriesList(val project: Project) : MasterDetailsComponent() {
|
||||
|
||||
init {
|
||||
initTree()
|
||||
service<PyPackageRepositories>().repositories
|
||||
.map { MyNode(PyRepositoryListItem(it)) }
|
||||
service<PyPackageRepositories>()
|
||||
.repositories
|
||||
.map { MyNode(PyRepositoryListItem(it, project)) }
|
||||
.forEach { addNode(it, myRoot) }
|
||||
}
|
||||
|
||||
@@ -34,7 +35,7 @@ class PyRepositoriesList(val project: Project) : MasterDetailsComponent() {
|
||||
PyBundle.message("python.packaging.repository.form.default.name"),
|
||||
remainingRepositories().map { it.name }.toMutableList())
|
||||
|
||||
val newNode = MyNode(PyRepositoryListItem(PyPackageRepository(uniqueName, "", "")))
|
||||
val newNode = MyNode(PyRepositoryListItem(PyPackageRepository(uniqueName, null, null), project))
|
||||
addNode(newNode, myRoot)
|
||||
selectNodeInTree(newNode)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging.repository
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.observable.properties.PropertyGraph
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.NamedConfigurable
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.ui.components.JBTextField
|
||||
@@ -10,6 +12,8 @@ import com.intellij.ui.dsl.builder.bindText
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Dimension
|
||||
@@ -17,7 +21,7 @@ import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
|
||||
@ApiStatus.Internal
|
||||
internal class PyRepositoryListItem(val repository: PyPackageRepository) : NamedConfigurable<PyPackageRepository>(true, null) {
|
||||
internal class PyRepositoryListItem(val repository: PyPackageRepository, private val project: Project) : NamedConfigurable<PyPackageRepository>(true, null) {
|
||||
@NlsSafe
|
||||
private var currentName = repository.name!!
|
||||
private var password = repository.getPassword()
|
||||
@@ -25,7 +29,7 @@ internal class PyRepositoryListItem(val repository: PyPackageRepository) : Named
|
||||
private val propertyGraph = PropertyGraph()
|
||||
private val urlProperty = propertyGraph.lazyProperty { repository.repositoryUrl ?: "" }
|
||||
private val loginProperty = propertyGraph.lazyProperty { repository.login ?: "" }
|
||||
private val passwordProperty = propertyGraph.lazyProperty { repository.getPassword() ?: "" }
|
||||
private val passwordProperty = propertyGraph.lazyProperty { getPassword(repository) }
|
||||
private val authorizationTypeProperty = propertyGraph.lazyProperty { repository.authorizationType }
|
||||
|
||||
override fun getDisplayName(): String {
|
||||
@@ -87,7 +91,7 @@ internal class PyRepositoryListItem(val repository: PyPackageRepository) : Named
|
||||
.bindText(urlProperty)
|
||||
}
|
||||
row(message("python.packaging.repository.form.authorization")) {
|
||||
segmentedButton(PyPackageRepositoryAuthenticationType.values().toList()) { text = it.text }
|
||||
segmentedButton(PyPackageRepositoryAuthenticationType.entries) { text = it.text }
|
||||
.bind(authorizationTypeProperty)
|
||||
}
|
||||
val row1 = row(message("python.packaging.repository.form.login")) {
|
||||
@@ -95,7 +99,7 @@ internal class PyRepositoryListItem(val repository: PyPackageRepository) : Named
|
||||
.bindText(loginProperty)
|
||||
}.visible(repository.authorizationType != PyPackageRepositoryAuthenticationType.NONE)
|
||||
val row2 = row(message("python.packaging.repository.form.password")) {
|
||||
passwordField().applyToComponent { text = repository.getPassword() }
|
||||
passwordField().applyToComponent { text = getPassword(repository) }
|
||||
.apply { component.preferredSize = Dimension(250, component.preferredSize.height) }
|
||||
.bindText(passwordProperty)
|
||||
}.visible(repository.authorizationType != PyPackageRepositoryAuthenticationType.NONE)
|
||||
@@ -109,4 +113,13 @@ internal class PyRepositoryListItem(val repository: PyPackageRepository) : Named
|
||||
mainPanel.add(repositoryForm, BorderLayout.CENTER)
|
||||
return mainPanel
|
||||
}
|
||||
|
||||
private fun getPassword(repository: PyPackageRepository): String {
|
||||
val toolWindowService = project.service<PyPackagingToolWindowService>()
|
||||
var retrievedPassword: String? = null
|
||||
toolWindowService.serviceScope.launch {
|
||||
retrievedPassword = repository.getPassword()
|
||||
}
|
||||
return retrievedPassword ?: ""
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,12 @@ import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||
import com.intellij.platform.util.progress.reportRawProgress
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.packaging.*
|
||||
import com.jetbrains.python.packaging.PyPIPackageRanking
|
||||
import com.jetbrains.python.packaging.PyPIPackageUtil
|
||||
import com.jetbrains.python.packaging.PyPackageService
|
||||
import com.jetbrains.python.packaging.PyPackageVersion
|
||||
import com.jetbrains.python.packaging.PyPackageVersionComparator
|
||||
import com.jetbrains.python.packaging.PyPackageVersionNormalizer
|
||||
import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageManagementListener
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
@@ -30,13 +35,30 @@ import com.jetbrains.python.packaging.common.runPackagingOperationOrShowErrorDia
|
||||
import com.jetbrains.python.packaging.conda.CondaPackage
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.packagesByRepository
|
||||
import com.jetbrains.python.packaging.repository.*
|
||||
import com.jetbrains.python.packaging.normalizePackageName
|
||||
import com.jetbrains.python.packaging.repository.PyEmptyPackagePackageRepository
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepositories
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import com.jetbrains.python.packaging.repository.PyRepositoriesList
|
||||
import com.jetbrains.python.packaging.repository.checkValid
|
||||
import com.jetbrains.python.packaging.statistics.PythonPackagesToolwindowStatisticsCollector
|
||||
import com.jetbrains.python.packaging.toolwindow.model.*
|
||||
import com.jetbrains.python.packaging.toolwindow.model.DisplayablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstallablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstalledPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.PyInvalidRepositoryViewData
|
||||
import com.jetbrains.python.packaging.toolwindow.model.PyPackagesViewData
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil
|
||||
import com.jetbrains.python.sdk.pythonSdk
|
||||
import com.jetbrains.python.statistics.modules
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
@@ -353,7 +375,7 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
|
||||
return sortPackagesForRepo(manager.repositoryManager.searchPackages(currentQuery, repository), currentQuery, repository, skipItems)
|
||||
}
|
||||
else {
|
||||
val packagesFromRepo = manager.repositoryManager.packagesFromRepository(repository)
|
||||
val packagesFromRepo = repository.getPackages()
|
||||
val page = packagesFromRepo.asSequence().limitResultAndFilterOutInstalled(repository, skipItems)
|
||||
return PyPackagesViewData(repository, page, moreItems = packagesFromRepo.size - (PACKAGES_LIMIT + skipItems))
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.ide.plugins.newui.OneLineProgressIndicator
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.UiDataProvider
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.observable.properties.AtomicBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.AtomicProperty
|
||||
@@ -24,7 +25,11 @@ import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.SideBorder
|
||||
import com.intellij.ui.components.JBComboBoxLabel
|
||||
import com.intellij.ui.components.JBOptionButton
|
||||
import com.intellij.ui.dsl.builder.*
|
||||
import com.intellij.ui.dsl.builder.BottomGap
|
||||
import com.intellij.ui.dsl.builder.RightGap
|
||||
import com.intellij.ui.dsl.builder.TopGap
|
||||
import com.intellij.ui.dsl.builder.bindText
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
import com.intellij.ui.jcef.JCEFHtmlPanel
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.packaging.PyPackageUtil
|
||||
@@ -38,19 +43,25 @@ import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.annotations.Nls
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Font
|
||||
import java.awt.event.ActionEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.*
|
||||
import javax.swing.AbstractAction
|
||||
import javax.swing.Action
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
class PyPackageDescriptionController(val project: Project) : Disposable {
|
||||
private val latestText: String
|
||||
get() = message("python.toolwindow.packages.latest.version.label")
|
||||
|
||||
val service = project.service<PyPackagingToolWindowService>()
|
||||
val service: PyPackagingToolWindowService = project.service<PyPackagingToolWindowService>()
|
||||
|
||||
internal val selectedPackage = AtomicProperty<DisplayablePackage?>(null)
|
||||
private val isManagement = AtomicBooleanProperty(false)
|
||||
@@ -114,23 +125,26 @@ class PyPackageDescriptionController(val project: Project) : Disposable {
|
||||
private val rightPanel = panel {
|
||||
row {
|
||||
cell(progressIndicatorComponent).gap(RightGap.SMALL).visibleIf(progressEnabledProperty)
|
||||
|
||||
versionSelector.apply {
|
||||
versionSelector.text = packageVersionProperty.get()
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent?) {
|
||||
val versions = listOf(latestText) + (selectedPackageDetails.get()?.availableVersions ?: emptyList())
|
||||
val availableVersions = selectedPackageDetails.get()?.availableVersions ?: emptyList()
|
||||
val latestVersion = availableVersions.first()
|
||||
val versions = listOf(latestText) + availableVersions
|
||||
JBPopupFactory.getInstance().createListPopup(
|
||||
object : BaseListPopupStep<String>(null, versions) {
|
||||
override fun onChosen(@NlsContexts.Label selectedValue: String, finalChoice: Boolean): PopupStep<*>? {
|
||||
packageVersionProperty.set(selectedValue)
|
||||
suggestInstallPackage(selectedValue)
|
||||
val effectiveVersion = if (selectedValue == latestText) latestVersion else selectedValue
|
||||
suggestInstallPackage(effectiveVersion)
|
||||
return FINAL_CHOICE
|
||||
}
|
||||
}, 8).showUnderneathOf(this@apply)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
packageVersionProperty.afterChange {
|
||||
versionSelector.text = it
|
||||
}
|
||||
@@ -165,7 +179,7 @@ class PyPackageDescriptionController(val project: Project) : Disposable {
|
||||
add(htmlPanel.component, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
val wrappedComponent = UiDataProvider.wrapComponent(component, UiDataProvider {})
|
||||
val wrappedComponent: JComponent = UiDataProvider.wrapComponent(component, UiDataProvider {})
|
||||
|
||||
override fun dispose() {}
|
||||
|
||||
@@ -182,6 +196,7 @@ class PyPackageDescriptionController(val project: Project) : Disposable {
|
||||
private fun updatePackageVersion(newVersion: String) {
|
||||
val details = selectedPackageDetails.get() ?: return
|
||||
val newVersionSpec = details.toPackageSpecification(newVersion)
|
||||
println(newVersionSpec.versionSpecs)
|
||||
val pyPackagingToolWindowService = PyPackagingToolWindowService.getInstance(project)
|
||||
PyPackageCoroutine.launch(project, Dispatchers.IO) {
|
||||
pyPackagingToolWindowService.installPackage(newVersionSpec)
|
||||
@@ -218,7 +233,9 @@ class PyPackageDescriptionController(val project: Project) : Disposable {
|
||||
actionPerformed()
|
||||
}
|
||||
finally {
|
||||
progressEnabledProperty.set(false)
|
||||
withContext(Dispatchers.EDT) {
|
||||
progressEnabledProperty.set(false)
|
||||
}
|
||||
progressIndicator.stop()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ abstract class PyRunAnythingPackageProvider : RunAnythingCommandLineProvider() {
|
||||
initCaches(packageManager)
|
||||
if (isInstall) {
|
||||
val packageRepository = getPackageRepository(dataContext) ?: return emptySequence()
|
||||
return packageManager.repositoryManager.packagesFromRepository(packageRepository).filter {
|
||||
return packageRepository.getPackages().filter {
|
||||
it.startsWith(commandLine.toComplete)
|
||||
}.asSequence()
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.common.PythonSimplePackageDetails
|
||||
import com.jetbrains.python.packaging.repository.PyEmptyPackagePackageRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
@@ -95,7 +94,7 @@ class TestPythonPackageManagerService(val installedPackages: List<PythonPackage>
|
||||
return TestPythonPackageManager(project, sdk)
|
||||
.withPackageInstalled(installedPackages)
|
||||
.withPackageNames(installedPackages.map { it.name })
|
||||
.withPackageDetails(PythonSimplePackageDetails(installedPackages.first().name, listOf(installedPackages.first().version),PyEmptyPackagePackageRepository))
|
||||
.withPackageDetails(PythonSimplePackageDetails(installedPackages.first().name, listOf(installedPackages.first().version), TestPackageRepository(installedPackages.map { it.name }.toSet())))
|
||||
}
|
||||
|
||||
override fun bridgeForSdk(project: Project, sdk: Sdk): PythonPackageManagementServiceBridge {
|
||||
|
||||
@@ -6,12 +6,14 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.jetbrains.python.packaging.PyPackageVersion
|
||||
import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.repository.PyEmptyPackagePackageRepository
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
|
||||
@TestOnly
|
||||
class TestPythonRepositoryManager(project: Project, sdk: Sdk) : PythonRepositoryManager(project, sdk) {
|
||||
internal class TestPythonRepositoryManager(
|
||||
override val project: Project,
|
||||
@Deprecated("Don't use sdk from here") override val sdk: Sdk
|
||||
) : PythonRepositoryManager {
|
||||
|
||||
private var packageNames: Set<String> = emptySet()
|
||||
private var packageDetails: PythonPackageDetails? = null
|
||||
@@ -39,16 +41,12 @@ class TestPythonRepositoryManager(project: Project, sdk: Sdk) : PythonRepository
|
||||
}
|
||||
|
||||
override val repositories: List<PyPackageRepository>
|
||||
get() = listOf(PyEmptyPackagePackageRepository)
|
||||
get() = listOf(TestPackageRepository(packageNames))
|
||||
|
||||
override fun allPackages(): Set<String> {
|
||||
return packageNames
|
||||
}
|
||||
|
||||
override fun packagesFromRepository(repository: PyPackageRepository): Set<String> {
|
||||
return packageNames
|
||||
}
|
||||
|
||||
override suspend fun getPackageDetails(pkg: PythonPackageSpecification): PythonPackageDetails {
|
||||
assert(packageDetails != null)
|
||||
return packageDetails!!
|
||||
@@ -63,4 +61,10 @@ class TestPythonRepositoryManager(project: Project, sdk: Sdk) : PythonRepository
|
||||
|
||||
override suspend fun initCaches() {
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestPackageRepository(private val packages: Set<String>): PyPackageRepository("test repository", null, null) {
|
||||
override fun getPackages(): Set<String> {
|
||||
return packages
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user