diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml
index 92f1aa90db51..c9a09bf3623f 100644
--- a/python/src/META-INF/python-core-common.xml
+++ b/python/src/META-INF/python-core-common.xml
@@ -718,6 +718,7 @@
+
diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingModuleMembersProvider.kt b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingModuleMembersProvider.kt
new file mode 100644
index 000000000000..de0473238b86
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibOverridingModuleMembersProvider.kt
@@ -0,0 +1,36 @@
+// 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.stdlib
+
+import com.intellij.psi.PsiElement
+import com.intellij.psi.util.QualifiedName
+import com.jetbrains.extensions.getQName
+import com.jetbrains.python.PyNames
+import com.jetbrains.python.codeInsight.PyCustomMember
+import com.jetbrains.python.psi.PyFile
+import com.jetbrains.python.psi.resolve.PyResolveContext
+import com.jetbrains.python.psi.types.PyOverridingModuleMembersProvider
+
+class PyStdlibOverridingModuleMembersProvider: PyOverridingModuleMembersProvider() {
+
+ override fun getMembersByQName(module: PyFile?, qName: String?) = emptyList()
+
+ override fun resolveMember(module: PyFile, name: String, resolveContext: PyResolveContext): PsiElement? {
+ if (module.languageLevel.isPython2 &&
+ name == "md5" &&
+ module.name == "hashlib.py" &&
+ module.getQName() == QualifiedName.fromComponents("hashlib")) {
+ // When module member is not found in it,
+ // `__all__` and nearby module with the specified name are suggested as a fallback.
+
+ // In case of `hashlib` in Python 2 stdlib, returned `md5` module is not used in `hashlib`
+ // so the behaviour has to be overridden.
+
+ // Please note that it's important to check against `hashlib.py`
+ // because `hashlib.pyi` stub is defined correctly.
+
+ return module.findExportedName(PyNames.ALL)
+ }
+
+ return null
+ }
+}
\ No newline at end of file
diff --git a/python/testData/deprecation/hashlibMd5/a.py b/python/testData/deprecation/hashlibMd5/a.py
new file mode 100644
index 000000000000..81c701c6c51e
--- /dev/null
+++ b/python/testData/deprecation/hashlibMd5/a.py
@@ -0,0 +1,6 @@
+from hashlib import md5
+
+md5("str")
+
+def f():
+ md5("str")
\ No newline at end of file
diff --git a/python/testData/deprecation/hashlibMd5/hashlib.py b/python/testData/deprecation/hashlibMd5/hashlib.py
new file mode 100644
index 000000000000..0ec5b6dcbde7
--- /dev/null
+++ b/python/testData/deprecation/hashlibMd5/hashlib.py
@@ -0,0 +1,8 @@
+__all__ = ['md5']
+
+
+def my_md5(s):
+ return s
+
+
+globals()["md5"] = my_md5
diff --git a/python/testData/deprecation/hashlibMd5/md5.py b/python/testData/deprecation/hashlibMd5/md5.py
new file mode 100644
index 000000000000..d76ce92718ab
--- /dev/null
+++ b/python/testData/deprecation/hashlibMd5/md5.py
@@ -0,0 +1,3 @@
+import warnings
+warnings.warn("the md5 module is deprecated; use hashlib instead",
+ DeprecationWarning, 2)
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/PyDeprecationTest.java b/python/testSrc/com/jetbrains/python/PyDeprecationTest.java
index 8a778873ae84..db2fba2884bd 100644
--- a/python/testSrc/com/jetbrains/python/PyDeprecationTest.java
+++ b/python/testSrc/com/jetbrains/python/PyDeprecationTest.java
@@ -98,6 +98,13 @@ public class PyDeprecationTest extends PyTestCase {
assertEquals("the deprecated module is deprecated; use a non-deprecated module instead", file.getDeprecationMessage());
assertNotParsed(file);
-
+ }
+
+ // PY-28053
+ public void testHashlibMd5() {
+ myFixture.enableInspections(PyDeprecationInspection.class);
+ myFixture.copyDirectoryToProject("deprecation/hashlibMd5", "");
+ myFixture.configureByFile("a.py");
+ myFixture.checkHighlighting(true, false, false);
}
}