diff --git a/python/build/plugin-list.txt b/python/build/plugin-list.txt index 9508ab3add3c..16916991e06d 100644 --- a/python/build/plugin-list.txt +++ b/python/build/plugin-list.txt @@ -26,7 +26,6 @@ hg4idea tasks tasks-core tasks-api -jira-connector github webDeployment jdbc-console diff --git a/python/main_pycharm.iml b/python/main_pycharm.iml index 2306c40fc09d..df3756f3fb34 100644 --- a/python/main_pycharm.iml +++ b/python/main_pycharm.iml @@ -28,7 +28,6 @@ - diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsUtil.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsUtil.java index 84e81ae8b8d0..8e5eca8e53b7 100644 --- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsUtil.java @@ -86,7 +86,10 @@ public class PyUserSkeletonsUtil { final VirtualFile directory = getUserSkeletonsDirectory(); if (directory != null) { final PsiDirectory psiDirectory = PsiManager.getInstance(project).findDirectory(directory); - final PsiElement fileSkeleton = new QualifiedNameResolverImpl(qName).resolveModuleAt(psiDirectory); + PsiElement fileSkeleton = new QualifiedNameResolverImpl(qName).resolveModuleAt(psiDirectory); + if (fileSkeleton instanceof PsiDirectory) { + fileSkeleton = PyUtil.getPackageElement((PsiDirectory)fileSkeleton); + } if (fileSkeleton instanceof PyFile) { cache.put(cacheQName, Collections.singletonList(fileSkeleton)); return (PyFile)fileSkeleton; diff --git a/python/src/com/jetbrains/python/editor/PythonSpaceHandler.java b/python/src/com/jetbrains/python/editor/PythonSpaceHandler.java index 8872677f6151..7fa829bded33 100644 --- a/python/src/com/jetbrains/python/editor/PythonSpaceHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonSpaceHandler.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull; */ public class PythonSpaceHandler extends TypedHandlerDelegate { @Override - public Result charTyped(char c, Project project, Editor editor, @NotNull PsiFile file) { + public Result charTyped(char c, Project project, @NotNull Editor editor, @NotNull PsiFile file) { CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance(); if (c == ' ' && codeInsightSettings.JAVADOC_STUB_ON_ENTER) { int offset = editor.getCaretModel().getOffset(); diff --git a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java index 98a3d8ee17ed..f8e4afa3676d 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java @@ -6,7 +6,10 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashSet; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.PyResolveUtil; +import com.jetbrains.python.psi.resolve.RatedResolveResult; +import com.jetbrains.python.psi.types.PyModuleType; import com.jetbrains.python.toolbox.ChainIterable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,13 +56,16 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo final PsiElement source = PyUtil.turnDirIntoInit(importedFile); if (source instanceof PyFile) { PyFile sourceFile = (PyFile)source; - final PsiElement exportedName = sourceFile.getElementNamed(name); - if (exportedName != null) { + final PyModuleType moduleType = new PyModuleType(sourceFile); + final List results = moduleType.resolveMember(name, null, AccessDirection.READ, + PyResolveContext.defaultContext()); + final PsiElement result = results != null && !results.isEmpty() ? results.get(0).getElement() : null; + if (result != null) { final List all = sourceFile.getDunderAll(); if (all != null && !all.contains(name)) { continue; } - return exportedName; + return result; } } } @@ -91,9 +97,7 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo } public String getLocationString() { - StringBuilder buf = new StringBuilder("| "); - buf.append("from ").append(getName()).append(" import *"); - return buf.toString(); + return "| " + "from " + getName() + " import *"; } public Icon getIcon(final boolean open) { diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 9527d30f82ea..1cf482a5fdfd 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -15,6 +15,7 @@ import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.*; +import com.jetbrains.python.psi.types.PyModuleType; import com.jetbrains.python.psi.types.PyType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -248,6 +249,7 @@ public class ResolveImportUtil { PsiDirectory dir = null; PsiElement ret = null; PsiElement possible_ret = null; + final PyResolveContext resolveContext = PyResolveContext.defaultContext(); if (parent instanceof PyFileImpl) { if (PyNames.INIT_DOT_PY.equals(((PyFile)parent).getName())) { // gobject does weird things like '_gobject = sys.modules['gobject._gobject'], so it's preferable to look at @@ -258,9 +260,12 @@ public class ResolveImportUtil { // OTOH, quite often a module named foo exports a class or function named foo, which is used as a fallback // by a module one level higher (e.g. curses.set_key). Prefer it to submodule if possible. - PsiElement elementNamed = ((PyFile)parent).getElementNamed(referencedName); - if (!fileOnly || PyUtil.instanceOf(elementNamed, PsiFile.class, PsiDirectory.class)) { - ret = elementNamed; + final PyModuleType moduleType = new PyModuleType((PyFile)parent); + final List results = moduleType.resolveMember(referencedName, null, AccessDirection.READ, + resolveContext); + final PsiElement moduleMember = results != null && !results.isEmpty() ? results.get(0).getElement() : null; + if (!fileOnly || PyUtil.instanceOf(moduleMember, PsiFile.class, PsiDirectory.class)) { + ret = moduleMember; } if (ret != null && !PyUtil.instanceOf(ret, PsiFile.class, PsiDirectory.class) && PsiTreeUtil.getStubOrPsiParentOfType(ret, PyExceptPart.class) == null) { @@ -273,7 +278,6 @@ public class ResolveImportUtil { dir = (PsiDirectory)parent; } else if (parent != null) { - final PyResolveContext resolveContext = PyResolveContext.defaultContext(); PyType refType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(parent, resolveContext.getTypeEvalContext(), null); if (refType != null) { final List result = refType.resolveMember(referencedName, null, AccessDirection.READ, resolveContext); diff --git a/python/src/com/jetbrains/python/testing/PythonTRunnerConsoleProperties.java b/python/src/com/jetbrains/python/testing/PythonTRunnerConsoleProperties.java index 3ef7e10d4778..2aabe173471a 100644 --- a/python/src/com/jetbrains/python/testing/PythonTRunnerConsoleProperties.java +++ b/python/src/com/jetbrains/python/testing/PythonTRunnerConsoleProperties.java @@ -1,7 +1,7 @@ package com.jetbrains.python.testing; import com.intellij.execution.Executor; -import com.intellij.execution.configurations.RuntimeConfiguration; +import com.intellij.execution.configurations.ModuleRunConfiguration; import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties; import com.jetbrains.django.testRunner.DjangoTestsRunConfiguration; @@ -13,7 +13,7 @@ public class PythonTRunnerConsoleProperties extends SMTRunnerConsoleProperties { private final boolean myIsEditable; - public PythonTRunnerConsoleProperties(final RuntimeConfiguration config, final Executor executor) { + public PythonTRunnerConsoleProperties(final ModuleRunConfiguration config, final Executor executor) { super(config, FRAMEWORK_NAME, executor); myIsEditable = config instanceof DjangoTestsRunConfiguration; diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/NoseToolsDynamicMembers/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection/NoseToolsDynamicMembers/a.py new file mode 100644 index 000000000000..ea8c3d9fa2e0 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/NoseToolsDynamicMembers/a.py @@ -0,0 +1,16 @@ +from nose.tools import assert_equal, ok_ +from nose import tools +import nose.tools + + +def test(): + ok_('foo') + assert_equal('foo', 'bar') + tools.assert_equal('foo', 'bar') + nose.tools.assert_equal('foo', 'bar') + + foo('foo') + tools.foo('foo') + nose.tools.foo('foo') + + diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/NoseToolsDynamicMembers/nose/__init__.py b/python/testData/inspections/PyUnresolvedReferencesInspection/NoseToolsDynamicMembers/nose/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/NoseToolsDynamicMembers/nose/tools/__init__.py b/python/testData/inspections/PyUnresolvedReferencesInspection/NoseToolsDynamicMembers/nose/tools/__init__.py new file mode 100644 index 000000000000..e751b7abbdc6 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/NoseToolsDynamicMembers/nose/tools/__init__.py @@ -0,0 +1,2 @@ +def ok_(expr, msg=None): + pass diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index d64bde673520..fe5a51684aa6 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -276,6 +276,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + // PY-7614 + public void testNoseToolsDynamicMembers() { + doMultiFileTest("a.py"); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyUnresolvedReferencesInspection.class);