diff --git a/python/resources/inspectionDescriptions/PyStubPackagesCompatibilityInspection.html b/python/resources/inspectionDescriptions/PyStubPackagesCompatibilityInspection.html
new file mode 100644
index 000000000000..73cfe89ffc1c
--- /dev/null
+++ b/python/resources/inspectionDescriptions/PyStubPackagesCompatibilityInspection.html
@@ -0,0 +1,8 @@
+
+
+Checks that a stub package supports the version of the corresponding runtime package.
+
+Stub package is a package that contains type information for some runtime package.
+See PEP 561 for more details.
+
+
\ No newline at end of file
diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml
index 12add1f70873..230713ae7c5a 100644
--- a/python/src/META-INF/python-core-common.xml
+++ b/python/src/META-INF/python-core-common.xml
@@ -434,6 +434,7 @@
+
diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyStubPackagesCompatibilityInspection.kt b/python/src/com/jetbrains/python/codeInsight/typing/PyStubPackagesCompatibilityInspection.kt
new file mode 100644
index 000000000000..8036e29eaaa0
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/typing/PyStubPackagesCompatibilityInspection.kt
@@ -0,0 +1,84 @@
+// 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.codeInsight.typing
+
+import com.intellij.codeInspection.LocalInspectionToolSession
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.codeInspection.ui.ListEditForm
+import com.intellij.openapi.module.ModuleUtilCore
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
+import com.intellij.psi.PsiElementVisitor
+import com.jetbrains.python.inspections.PyInspection
+import com.jetbrains.python.inspections.PyInspectionVisitor
+import com.jetbrains.python.inspections.PyInterpreterInspection
+import com.jetbrains.python.packaging.PyPackage
+import com.jetbrains.python.packaging.PyPackageManager
+import com.jetbrains.python.packaging.requirement.PyRequirementRelation
+import com.jetbrains.python.psi.PyFile
+import com.jetbrains.python.sdk.PythonSdkType
+import javax.swing.JComponent
+
+class PyStubPackagesCompatibilityInspection : PyInspection() {
+
+ @Suppress("MemberVisibilityCanBePrivate")
+ var ignoredStubPackages: MutableList = mutableListOf()
+
+ override fun createOptionsPanel(): JComponent = ListEditForm("Ignored stub packages", ignoredStubPackages).contentPanel
+
+ override fun buildVisitor(holder: ProblemsHolder,
+ isOnTheFly: Boolean,
+ session: LocalInspectionToolSession): PsiElementVisitor {
+ return Visitor(ignoredStubPackages, holder, session)
+ }
+
+ private class Visitor(val ignoredStubPackages: MutableList,
+ holder: ProblemsHolder,
+ session: LocalInspectionToolSession) : PyInspectionVisitor(holder, session) {
+
+ override fun visitPyFile(node: PyFile) {
+ val module = ModuleUtilCore.findModuleForFile(node) ?: return
+ val sdk = PythonSdkType.findPythonSdk(module) ?: return
+
+ val installedPackages = PyPackageManager.getInstance(sdk).packages ?: emptyList()
+ if (installedPackages.isEmpty()) return
+
+ val nameToPkg = mutableMapOf()
+ installedPackages.forEach { nameToPkg[it.name] = it }
+
+ installedPackages
+ .asSequence()
+ .filter { pkg -> pkg.name.let { it.endsWith(STUBS_SUFFIX) && it !in ignoredStubPackages } }
+ .mapNotNull { stubPkg -> nameToPkg[stubPkg.name.removeSuffix(STUBS_SUFFIX)]?.let { it to stubPkg } }
+ .forEach { (runtimePkg, stubPkg) ->
+ val runtimePkgName = runtimePkg.name
+ val requirement = stubPkg.requirements.firstOrNull { it.name == runtimePkgName } ?: return@forEach
+
+ if (requirement.match(listOf(runtimePkg)) == null) {
+ val stubPkgName = stubPkg.name
+ val specsToString = StringUtil.join(requirement.versionSpecs, { it.presentableText }, ", ")
+
+ registerProblem(node,
+ "'$stubPkgName${PyRequirementRelation.EQ.presentableText}${stubPkg.version}' " +
+ "is incompatible with " +
+ "'$runtimePkgName${PyRequirementRelation.EQ.presentableText}${runtimePkg.version}'. " +
+ "Expected '$runtimePkgName' version: [$specsToString]",
+ PyInterpreterInspection.ConfigureInterpreterFix(),
+ createIgnoreStubPackageQuickFix(stubPkgName, ignoredStubPackages))
+ }
+ }
+ }
+
+ private fun createIgnoreStubPackageQuickFix(stubPkgName: String, ignoredStubPkgs: MutableList): LocalQuickFix {
+ return object : LocalQuickFix {
+ override fun getFamilyName() = "Ignore '$stubPkgName' compatibility"
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ if (ignoredStubPkgs.add(stubPkgName)) ProjectInspectionProfileManager.getInstance(project).fireProfileChanged()
+ }
+ }
+ }
+ }
+}
\ No newline at end of file