mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
PY-54850 Normalized package names in PyPackageRequirementsInspection for consistent matching and refactored requirement satisfaction checks. Unified normalizePackageName logic. introduced a test to verify that requirement mismatch warnings disappear upon package installation.
GitOrigin-RevId: edb02fa9c575b3cc51d95bbe21bf5cd2f0d25cba
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cf32cfa669
commit
a7bdd63a2e
@@ -58,9 +58,11 @@ public interface PyRequirement {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return concatenated representation of name, extras and version specs so it could be easily displayed.
|
||||
* @return concatenated representation of name, extras and version specs, so it could be easily displayed.
|
||||
*/
|
||||
default @NotNull @NlsSafe String getPresentableText() {
|
||||
return getName() + getExtras() + StringUtil.join(getVersionSpecs(), PyRequirementVersionSpec::getPresentableText, ",");
|
||||
return getPresentableTextWithoutVersion() + getExtras() + StringUtil.join(getVersionSpecs(), PyRequirementVersionSpec::getPresentableText, ",");
|
||||
}
|
||||
|
||||
@NotNull @NlsSafe String getPresentableTextWithoutVersion();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.jetbrains.python.packaging
|
||||
|
||||
|
||||
private val QUOTES_REGEX: Regex = Regex("^\"|\"$")
|
||||
private val SEPARATOR_REGEX: Regex = Regex("[-_.]+")
|
||||
|
||||
/**
|
||||
* Normalizes a package name by removing quotes, replacing separators, and converting to lowercase.
|
||||
*/
|
||||
fun normalizePackageName(name: String): String = name
|
||||
.replace(QUOTES_REGEX, "")
|
||||
.replace(SEPARATOR_REGEX, "-")
|
||||
.lowercase()
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.packaging
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.jetbrains.python.packaging.requirement.PyRequirementVersionSpec
|
||||
|
||||
/**
|
||||
@@ -13,20 +14,45 @@ import com.jetbrains.python.packaging.requirement.PyRequirementVersionSpec
|
||||
* @see PyRequirementParser.fromLine
|
||||
* @see PyRequirementParser.fromFile
|
||||
*/
|
||||
data class PyRequirementImpl(private val name: String,
|
||||
private val versionSpecs: List<PyRequirementVersionSpec>,
|
||||
private val installOptions: List<String>,
|
||||
private val extras: String) : PyRequirement {
|
||||
data class PyRequirementImpl(
|
||||
private val name: String,
|
||||
private val versionSpecs: List<PyRequirementVersionSpec>,
|
||||
private val installOptions: List<String>,
|
||||
private val extras: String,
|
||||
) : PyRequirement {
|
||||
|
||||
override fun getName(): String = name
|
||||
override fun getName(): String = NormalizedPackageName.from(name).name
|
||||
override fun getExtras(): String = extras
|
||||
override fun getVersionSpecs(): List<PyRequirementVersionSpec> = versionSpecs
|
||||
override fun getInstallOptions(): List<String> = installOptions
|
||||
override fun getPresentableTextWithoutVersion(): @NlsSafe String = name
|
||||
|
||||
override fun match(packages: Collection<PyPackage>): PyPackage? {
|
||||
return packages.firstOrNull { `package` ->
|
||||
name.replace('_', '-').equals(`package`.name.replace('_', '-'), true)
|
||||
&& versionSpecs.all { it.matches(`package`.version) }
|
||||
return packages.firstOrNull { pkg ->
|
||||
isPackageNameEqual(pkg.name) && versionSpecs.all { it.matches(pkg.version) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isPackageNameEqual(otherName: String): Boolean {
|
||||
return normalizePackageName(name) == normalizePackageName(otherName)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
return when (other) {
|
||||
is String -> getName() == NormalizedPackageName.from(other).name
|
||||
is PyRequirementImpl -> getName().equals(other.getName(), ignoreCase = true)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = normalizePackageName(name).hashCode()
|
||||
|
||||
@JvmInline
|
||||
value class NormalizedPackageName private constructor(val name: String) {
|
||||
companion object {
|
||||
fun from(name: String): NormalizedPackageName =
|
||||
NormalizedPackageName(normalizePackageName(name))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class PyStubPackagesCompatibilityInspection : PyInspection() {
|
||||
.mapNotNull { stubPkg -> nameToPkg[stubPkg.name.removeSuffix(STUBS_SUFFIX)]?.let { it to stubPkg } }
|
||||
.filter {
|
||||
val runtimePkgName = it.first.name
|
||||
val requirement = it.second.requirements.firstOrNull { req -> req.name == runtimePkgName } ?: return@filter false
|
||||
val requirement = it.second.requirements.firstOrNull { req -> req.equals(runtimePkgName) } ?: return@filter false
|
||||
|
||||
requirement.match(listOf(it.first)) == null
|
||||
}
|
||||
@@ -84,7 +84,7 @@ class PyStubPackagesCompatibilityInspection : PyInspection() {
|
||||
}
|
||||
.forEach { (runtimePkg, stubPkg) ->
|
||||
val runtimePkgName = runtimePkg.name
|
||||
val requirement = stubPkg.requirements.firstOrNull { it.name == runtimePkgName } ?: return@forEach
|
||||
val requirement = stubPkg.requirements.firstOrNull { it.equals(runtimePkgName) } ?: return@forEach
|
||||
|
||||
if (requirement.match(listOf(runtimePkg)) == null) {
|
||||
val stubPkgName = stubPkg.name
|
||||
|
||||
@@ -17,10 +17,7 @@ import com.jetbrains.python.inspections.PyInspectionVisitor
|
||||
import com.jetbrains.python.inspections.quickfix.IgnoreRequirementFix
|
||||
import com.jetbrains.python.inspections.quickfix.PyGenerateRequirementsFileQuickFix
|
||||
import com.jetbrains.python.inspections.quickfix.PyInstallRequirementsFix
|
||||
import com.jetbrains.python.packaging.PyPIPackageUtil
|
||||
import com.jetbrains.python.packaging.PyPackage
|
||||
import com.jetbrains.python.packaging.PyPackageUtil
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.*
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.psi.*
|
||||
@@ -65,7 +62,7 @@ class PyRequirementVisitor(
|
||||
.firstOrNull()
|
||||
|
||||
add(providedFix ?: PyInstallRequirementsFix(null, module, sdk, unsatisfied))
|
||||
add(IgnoreRequirementFix(unsatisfied.mapTo(mutableSetOf()) { it.name }))
|
||||
add(IgnoreRequirementFix(unsatisfied.mapTo(mutableSetOf()) { it.presentableTextWithoutVersion }))
|
||||
}
|
||||
|
||||
registerProblem(
|
||||
@@ -91,7 +88,7 @@ class PyRequirementVisitor(
|
||||
ignoredPackages: Set<String?>,
|
||||
): List<PyRequirement> {
|
||||
val requirements = getRequirements(module) ?: return emptyList()
|
||||
val installedPackages = manager.installedPackages.toPyPackages()
|
||||
val installedPackages = manager.installedPackages
|
||||
val modulePackages = collectPackagesInModule(module)
|
||||
|
||||
return requirements.filter { requirement ->
|
||||
@@ -102,19 +99,23 @@ class PyRequirementVisitor(
|
||||
private fun isRequirementUnsatisfied(
|
||||
requirement: PyRequirement,
|
||||
ignoredPackages: Set<String?>,
|
||||
installedPackages: List<PyPackage>,
|
||||
modulePackages: List<PyPackage>,
|
||||
): Boolean =
|
||||
!ignoredPackages.contains(requirement.name) &&
|
||||
requirement.match(installedPackages) == null &&
|
||||
requirement.match(modulePackages) == null
|
||||
installedPackages: List<PythonPackage>,
|
||||
modulePackages: List<PythonPackage>,
|
||||
): Boolean {
|
||||
if (requirement.name in ignoredPackages.map { normalizePackageName(it ?: EMPTY_STRING) }) {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun List<PythonPackage>.toPyPackages(): List<PyPackage> = map { PyPackage(it.name, it.version) }
|
||||
val isSatisfiedInInstalled = requirement.match(installedPackages) != null
|
||||
val isSatisfiedInModule = requirement.match(modulePackages) != null
|
||||
|
||||
return !(isSatisfiedInInstalled || isSatisfiedInModule)
|
||||
}
|
||||
|
||||
private fun getRequirements(module: Module): List<PyRequirement>? =
|
||||
PyPackageUtil.getRequirementsFromTxt(module) ?: PyPackageUtil.findSetupPyRequires(module)
|
||||
|
||||
private fun collectPackagesInModule(module: Module): List<PyPackage> {
|
||||
private fun collectPackagesInModule(module: Module): List<PythonPackage> {
|
||||
return PyUtil.getSourceRoots(module).flatMap { srcRoot ->
|
||||
VfsUtil.getChildren(srcRoot).filter { file ->
|
||||
METADATA_EXTENSIONS.contains(file.extension)
|
||||
@@ -124,9 +125,9 @@ class PyRequirementVisitor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parsePackageNameAndVersion(nameWithoutExtension: String): PyPackage? {
|
||||
private fun parsePackageNameAndVersion(nameWithoutExtension: String): PythonPackage? {
|
||||
val components = splitNameIntoComponents(nameWithoutExtension)
|
||||
return if (components.size >= 2) PyPackage(components[0], components[1]) else null
|
||||
return if (components.size >= 2) PythonPackage(components[0], components[1], false) else null
|
||||
}
|
||||
|
||||
private fun checkPackageNameInRequirements(importedExpression: PyQualifiedExpression) {
|
||||
@@ -174,14 +175,8 @@ class PyRequirementVisitor(
|
||||
possiblePyPIPackageNames: String,
|
||||
requirements: Collection<PyRequirement>,
|
||||
): Boolean =
|
||||
requirements.map { it.name.variations() }.flatten().contains(packageName) ||
|
||||
requirements.map { it.name.variations() }.flatten().contains(possiblePyPIPackageNames)
|
||||
|
||||
private fun String.variations() = listOf(
|
||||
this,
|
||||
this.replace("_", "-"),
|
||||
this.replace("-", "_")
|
||||
)
|
||||
requirements.map { it.name }.contains(normalizePackageName(packageName)) ||
|
||||
requirements.map { it.name }.contains(normalizePackageName(possiblePyPIPackageNames))
|
||||
|
||||
private fun isLocalModule(packageReferenceExpression: PyExpression, module: Module): Boolean {
|
||||
val reference = packageReferenceExpression.reference ?: return false
|
||||
@@ -242,6 +237,15 @@ class PyRequirementVisitor(
|
||||
return value != null && value
|
||||
}
|
||||
|
||||
private fun PyRequirement.match(packages: Collection<PythonPackage>): PythonPackage? {
|
||||
return packages.firstOrNull { pkg ->
|
||||
name == pkg.name
|
||||
&& versionSpecs.all { it.matches(pkg.version) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<PythonPackage>.toPyPackages(): List<PyPackage> = map { PyPackage(it.name, it.version) }
|
||||
|
||||
companion object {
|
||||
private const val PACKAGE_NOT_LISTED = "INSP.requirements.package.containing.module.not.listed.in.project.requirements"
|
||||
private const val REQUIREMENT_NOT_SATISFIED = "INSP.requirements.package.requirements.not.satisfied"
|
||||
|
||||
@@ -31,7 +31,7 @@ import com.jetbrains.python.inspections.PyUnresolvedReferenceQuickFixProvider
|
||||
import com.jetbrains.python.inspections.quickfix.*
|
||||
import com.jetbrains.python.packaging.PyPIPackageUtil
|
||||
import com.jetbrains.python.packaging.PyPackageUtil
|
||||
import com.jetbrains.python.packaging.common.normalizePackageName
|
||||
import com.jetbrains.python.packaging.normalizePackageName
|
||||
import com.jetbrains.python.psi.*
|
||||
import com.jetbrains.python.psi.impl.PyFromImportStatementImpl
|
||||
import com.jetbrains.python.psi.impl.PyImportElementImpl
|
||||
|
||||
@@ -41,7 +41,8 @@ import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.jetbrains.python.packaging.common.PackagesKt.normalizePackageName;
|
||||
import static com.jetbrains.python.packaging.PyPackageNameNormalizeUtilKt.normalizePackageName;
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class PyPIPackageUtil {
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.inspections.quickfix.InstallPackageQuickFix
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.normalizePackageName
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.ui.PyChooseRequirementsDialog
|
||||
import com.jetbrains.python.statistics.PyPackagesUsageCollector
|
||||
@@ -138,7 +137,7 @@ internal fun getConfirmedPackages(packageNames: List<PyRequirement>, project: Pr
|
||||
|
||||
if (!confirmationEnabled || packageNames.isEmpty()) return packageNames.toSet()
|
||||
|
||||
val dialog = PyChooseRequirementsDialog(project, packageNames) { it.presentableText }
|
||||
val dialog = PyChooseRequirementsDialog(project, packageNames) { it.presentableTextWithoutVersion }
|
||||
|
||||
if (!dialog.showAndGet()) {
|
||||
PyPackagesUsageCollector.installAllCanceledEvent.log()
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.jetbrains.python.PyBundle
|
||||
import java.util.Locale
|
||||
import java.util.*
|
||||
|
||||
class PyRequirementsFileVisitor(private val importedPackages: MutableMap<String, PyPackage>,
|
||||
private val settings: PyPackageRequirementsSettings) {
|
||||
@@ -75,7 +75,7 @@ class PyRequirementsFileVisitor(private val importedPackages: MutableMap<String,
|
||||
}
|
||||
else {
|
||||
val requirement = parsed.first()
|
||||
val name = requirement.name.lowercase(Locale.getDefault())
|
||||
val name = requirement.name
|
||||
if (name in importedPackages) {
|
||||
val pkg = importedPackages.remove(name)!!
|
||||
val formatted = formatRequirement(requirement, pkg, lines)
|
||||
@@ -131,10 +131,10 @@ class PyRequirementsFileVisitor(private val importedPackages: MutableMap<String,
|
||||
private fun convertToRequirementsEntry(requirement: PyRequirement, settings: PyPackageRequirementsSettings, version: String? = null): String {
|
||||
val packageName = when {
|
||||
settings.specifyVersion -> when {
|
||||
version != null -> requirement.name + requirement.extras + settings.versionSpecifier.separator + version
|
||||
else -> requirement.presentableText
|
||||
version != null -> requirement.presentableTextWithoutVersion + requirement.extras + settings.versionSpecifier.separator + version
|
||||
else -> requirement.presentableTextWithoutVersion
|
||||
}
|
||||
else -> requirement.name + requirement.extras
|
||||
else -> requirement.presentableTextWithoutVersion + requirement.extras
|
||||
}
|
||||
|
||||
if (requirement.installOptions.size == 1) return packageName
|
||||
|
||||
@@ -2,17 +2,21 @@
|
||||
package com.jetbrains.python.packaging.common
|
||||
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
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.PyPackageRepository
|
||||
import com.jetbrains.python.packaging.requirement.PyRequirementRelation
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
open class PythonPackage(val name: String, val version: String, val isEditableMode: Boolean) {
|
||||
open class PythonPackage(name: String, val version: String, val isEditableMode: Boolean) {
|
||||
companion object {
|
||||
private const val HASH_MULTIPLIER = 31
|
||||
}
|
||||
|
||||
val name: String = normalizePackageName(name)
|
||||
val presentableName: String = name
|
||||
|
||||
override fun toString(): String {
|
||||
return "PythonPackage(name='$name', version='$version')"
|
||||
}
|
||||
@@ -128,8 +132,4 @@ data class PythonLocalPackageSpecification(override val name: String,
|
||||
data class PythonVcsPackageSpecification(override val name: String,
|
||||
override val location: String,
|
||||
override val prefix: String,
|
||||
override val editable: Boolean) : PythonLocationBasedPackageSpecification
|
||||
|
||||
fun normalizePackageName(name: String): String {
|
||||
return name.replace(Regex("[-_.]+"), "-").lowercase()
|
||||
}
|
||||
override val editable: Boolean) : PythonLocationBasedPackageSpecification
|
||||
@@ -23,8 +23,8 @@ import com.jetbrains.python.PySdkBundle
|
||||
import com.jetbrains.python.PythonHelper
|
||||
import com.jetbrains.python.packaging.PyExecutionException
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.common.normalizePackageName
|
||||
import com.jetbrains.python.packaging.common.runPackagingOperationOrShowErrorDialog
|
||||
import com.jetbrains.python.packaging.normalizePackageName
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import com.jetbrains.python.run.PythonInterpreterTargetEnvironmentFactory
|
||||
import com.jetbrains.python.run.buildTargetedCommandLine
|
||||
|
||||
@@ -10,12 +10,12 @@ 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.PyPIPackageUtil
|
||||
import com.jetbrains.python.packaging.PyPackageVersion
|
||||
import com.jetbrains.python.packaging.PyPackageVersionComparator
|
||||
import com.jetbrains.python.packaging.PyPackageVersionNormalizer
|
||||
import com.jetbrains.python.packaging.*
|
||||
import com.jetbrains.python.packaging.cache.PythonSimpleRepositoryCache
|
||||
import com.jetbrains.python.packaging.common.*
|
||||
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.*
|
||||
|
||||
@@ -23,6 +23,10 @@ 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.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonPackageManagementListener
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.packaging.common.runPackagingOperationOrShowErrorDialog
|
||||
import com.jetbrains.python.packaging.common.*
|
||||
import com.jetbrains.python.packaging.conda.CondaPackage
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
|
||||
@@ -14,10 +14,10 @@ import javax.swing.Icon
|
||||
|
||||
sealed class DisplayablePackage(@NlsSafe val name: String, val repository: PyPackageRepository)
|
||||
|
||||
class InstalledPackage(val instance: PythonPackage, repository: PyPackageRepository, val nextVersion: PyPackageVersion? = null) : DisplayablePackage(instance.name, repository) {
|
||||
val currentVersion = PyPackageVersionNormalizer.normalize(instance.version)
|
||||
class InstalledPackage(val instance: PythonPackage, repository: PyPackageRepository, val nextVersion: PyPackageVersion? = null) : DisplayablePackage(instance.presentableName, repository) {
|
||||
val currentVersion: PyPackageVersion? = PyPackageVersionNormalizer.normalize(instance.version)
|
||||
|
||||
val isEditMode = instance.isEditableMode
|
||||
val isEditMode: Boolean = instance.isEditableMode
|
||||
val sourceRepoIcon: Icon?
|
||||
get() {
|
||||
val condaPackage = instance as? CondaPackage ?: return null
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.inspections.quickfix.InstallPackageQuickFix
|
||||
import com.jetbrains.python.packaging.*
|
||||
import com.jetbrains.python.packaging.common.normalizePackageName
|
||||
import com.jetbrains.python.packaging.common.runPackagingOperationOrShowErrorDialog
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.createSpecification
|
||||
@@ -100,7 +99,7 @@ class InstallAllRequirementsQuickFix(requirements: List<Requirement>) : LocalQui
|
||||
InstallRequirementQuickFix.installPackages(
|
||||
project,
|
||||
descriptor,
|
||||
requirementElements.filter { pkg -> confirmedPackages.any { it.name == pkg.displayName } }
|
||||
requirementElements.filter { pkg -> confirmedPackages.any { it.equals(pkg.displayName) } }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.intellij.internal.statistic.service.fus.collectors.ProjectUsagesColle
|
||||
import com.intellij.openapi.fileTypes.FileTypeRegistry
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.jetbrains.python.PythonFileType
|
||||
import com.jetbrains.python.packaging.normalizePackageName
|
||||
|
||||
class PyPackageInEditorUsageCollector : ProjectUsagesCollector() {
|
||||
override fun getMetrics(project: Project): Set<MetricEvent> {
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.jetbrains.python.extensions.getSdk
|
||||
import com.jetbrains.python.packaging.PyPIPackageCache
|
||||
import com.jetbrains.python.packaging.PyPackageManager
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.normalizePackageName
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.search.FileTypeIndex
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import com.jetbrains.python.packaging.PyRequirementParser
|
||||
import com.jetbrains.python.packaging.normalizePackageName
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import org.toml.lang.psi.*
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.jetbrains.python.sdk.PySdkUtil
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader
|
||||
import com.jetbrains.python.sdk.flavors.conda.CondaEnvSdkFlavor
|
||||
import com.jetbrains.python.sdk.pipenv.isPipEnv
|
||||
import com.jetbrains.python.sdk.poetry.isPoetry
|
||||
@@ -28,6 +27,7 @@ import com.jetbrains.python.statistics.InterpreterCreationMode.*
|
||||
import com.jetbrains.python.statistics.InterpreterTarget.*
|
||||
import com.jetbrains.python.statistics.InterpreterType.*
|
||||
import com.jetbrains.python.target.PyTargetAwareAdditionalData
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader
|
||||
|
||||
val Project.modules get() = ModuleManager.getInstance(this).modules
|
||||
val Project.sdks get() = modules.mapNotNull(Module::getSdk)
|
||||
@@ -52,19 +52,6 @@ fun getPythonSpecificInfo(sdk: Sdk): List<EventPair<*>> {
|
||||
return data
|
||||
}
|
||||
|
||||
fun normalizePackageName(packageName: String): String {
|
||||
var name = packageName
|
||||
if (!name.startsWith("_")) {
|
||||
// for cases such as __future__, etc
|
||||
name = name.replace('_', '-')
|
||||
}
|
||||
|
||||
return name
|
||||
.replace(".", "-")
|
||||
.replace("\"", "")
|
||||
.lowercase()
|
||||
}
|
||||
|
||||
@Deprecated("""
|
||||
It makes no sense to add a Python version or something similar to the event.
|
||||
If you need to get an event with a specific execution type, interpreter type, or whatsoever, please use the corresponding segment in the analytics platform.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
zope-interface==5.4.0
|
||||
@@ -7,6 +7,7 @@ import com.intellij.testFramework.ServiceContainerUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.fixtures.PyInspectionTestCase;
|
||||
import com.jetbrains.python.packaging.PyRequirement;
|
||||
import com.jetbrains.python.packaging.common.PythonPackage;
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManagerService;
|
||||
import com.jetbrains.python.packaging.management.TestPythonPackageManagerService;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
@@ -14,8 +15,10 @@ import com.jetbrains.python.sdk.PythonSdkUtil;
|
||||
import com.jetbrains.python.sdk.pipenv.PipenvFilesUtilsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class PyPackageRequirementsInspectionTest extends PyInspectionTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -23,18 +26,23 @@ public class PyPackageRequirementsInspectionTest extends PyInspectionTestCase {
|
||||
return PyPackageRequirementsInspection.class;
|
||||
}
|
||||
|
||||
private void replacePythonPackageManagerServiceWithTestInstance(
|
||||
PythonPackageManagerService serviceInstance) {
|
||||
ServiceContainerUtil.replaceService(
|
||||
myFixture.getProject(),
|
||||
PythonPackageManagerService.class,
|
||||
serviceInstance,
|
||||
myFixture.getProject()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
final Sdk sdk = PythonSdkUtil.findPythonSdk(myFixture.getModule());
|
||||
assertNotNull(sdk);
|
||||
|
||||
ServiceContainerUtil.replaceService(
|
||||
myFixture.getProject(),
|
||||
PythonPackageManagerService.class,
|
||||
new TestPythonPackageManagerService(),
|
||||
myFixture.getProject()
|
||||
);
|
||||
replacePythonPackageManagerServiceWithTestInstance(new TestPythonPackageManagerService());
|
||||
}
|
||||
|
||||
public void testPartiallySatisfiedRequirementsTxt() {
|
||||
@@ -116,4 +124,15 @@ public class PyPackageRequirementsInspectionTest extends PyInspectionTestCase {
|
||||
myFixture.enableInspections(inspection);
|
||||
myFixture.checkHighlighting(isWarning(), isInfo(), isWeakWarning());
|
||||
}
|
||||
}
|
||||
|
||||
// PY-54850
|
||||
public void testRequirementMismatchWarningDisappearsOnInstall() {
|
||||
PythonPackage zopeInterfacePackage = new PythonPackage("zope.interface", "5.4.0", false);
|
||||
|
||||
replacePythonPackageManagerServiceWithTestInstance(
|
||||
new TestPythonPackageManagerService(Collections.singletonList(zopeInterfacePackage))
|
||||
);
|
||||
|
||||
doMultiFileTest("a.py");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import com.jetbrains.python.packaging.bridge.PythonPackageManagementServiceBridg
|
||||
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
|
||||
@@ -63,6 +65,11 @@ class TestPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageManage
|
||||
return this
|
||||
}
|
||||
|
||||
fun withPackageInstalled(packages: List<PythonPackage>): TestPythonPackageManager {
|
||||
this.installedPackages = packages
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_PACKAGES = listOf(
|
||||
PythonPackage(PIP_PACKAGE, EMPTY_STRING, false),
|
||||
@@ -78,10 +85,17 @@ class TestPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageManage
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
class TestPythonPackageManagerService(): PythonPackageManagerService {
|
||||
class TestPythonPackageManagerService(val installedPackages: List<PythonPackage> = emptyList()): PythonPackageManagerService {
|
||||
|
||||
override fun forSdk(project: Project, sdk: Sdk): PythonPackageManager {
|
||||
installedPackages.ifEmpty {
|
||||
return TestPythonPackageManager(project, sdk)
|
||||
}
|
||||
|
||||
return TestPythonPackageManager(project, sdk)
|
||||
.withPackageInstalled(installedPackages)
|
||||
.withPackageNames(installedPackages.map { it.name })
|
||||
.withPackageDetails(PythonSimplePackageDetails(installedPackages.first().name, listOf(installedPackages.first().version),PyEmptyPackagePackageRepository))
|
||||
}
|
||||
|
||||
override fun bridgeForSdk(project: Project, sdk: Sdk): PythonPackageManagementServiceBridge {
|
||||
@@ -97,6 +111,7 @@ class TestPythonPackageManagerService(): PythonPackageManagerService {
|
||||
class TestPackageManagerProvider : PythonPackageManagerProvider {
|
||||
private var packageNames: List<String> = emptyList()
|
||||
private var packageDetails: PythonPackageDetails? = null
|
||||
private var packageInstalled: List<PythonPackage> = emptyList()
|
||||
|
||||
fun withPackageNames(packageNames: List<String>): TestPackageManagerProvider {
|
||||
this.packageNames = packageNames
|
||||
@@ -109,6 +124,6 @@ class TestPackageManagerProvider : PythonPackageManagerProvider {
|
||||
}
|
||||
|
||||
override fun createPackageManagerForSdk(project: Project, sdk: Sdk): PythonPackageManager {
|
||||
return TestPythonPackageManager(project, sdk).withPackageNames(packageNames).withPackageDetails(packageDetails)
|
||||
return TestPythonPackageManager(project, sdk).withPackageNames(packageNames).withPackageDetails(packageDetails).withPackageInstalled(packageInstalled)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user