diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/PyUtil.java b/python/python-psi-impl/src/com/jetbrains/python/psi/PyUtil.java index aca529325209..33329953711d 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/PyUtil.java @@ -913,6 +913,22 @@ public final class PyUtil { } // don't touch non-dirs } + @Nullable + public static PsiElement turnDirIntoInitPy(@Nullable PsiElement target) { + if (!(target instanceof PsiDirectory psiDirectory)) return target; + return psiDirectory.findFile(PyNames.INIT_DOT_PY); + } + + @Nullable + public static PsiElement turnDirIntoInitPyi(@Nullable PsiElement target) { + if (!(target instanceof PsiDirectory psiDirectory)) return target; + final PsiFile initStub = psiDirectory.findFile(PyNames.INIT_DOT_PYI); + if (initStub != null && !PyiStubSuppressor.isIgnoredStub(initStub)) { + return initStub; + } + return null; + } + /** * If directory is a PsiDirectory, that is also a valid Python package, return PsiFile that points to __init__.py, * if such file exists, or directory itself (i.e. namespace package). Otherwise, return {@code null}. diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 2681430a8a4d..fad0ce37661b 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -388,7 +388,7 @@ public final class ResolveImportUtil { // VFS may be case insensitive on Windows, but resolve is always case sensitive (PEP 235, PY-18958), so we check name here if (subdir != null && subdir.getName().equals(referencedName) && (!checkForPackage || PyUtil.isPackage(subdir, containingFile)) && - (!withoutStubs || !PyiUtil.isPyiFileOfPackage(subdir))) { + (!withoutStubs || PyUtil.isOrdinaryPackage(subdir) || !PyiUtil.isPyiFileOfPackage(subdir))) { result.add(new RatedResolveResult(RatedResolveResult.RATE_NORMAL, PyStubPackages.transferStubPackageMarker(dir, subdir))); } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/search/PyDefinitionsSearch.java b/python/python-psi-impl/src/com/jetbrains/python/psi/search/PyDefinitionsSearch.java index 877b3974045f..533184b6841e 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/search/PyDefinitionsSearch.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/search/PyDefinitionsSearch.java @@ -4,43 +4,50 @@ package com.jetbrains.python.psi.search; import com.intellij.openapi.application.ReadAction; import com.intellij.psi.PsiElement; import com.intellij.util.Processor; -import com.intellij.util.Query; import com.intellij.util.QueryExecutor; -import com.jetbrains.python.psi.PyAssignmentStatement; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.PyTargetExpression; -import com.jetbrains.python.pyi.PyiFile; +import com.jetbrains.python.psi.*; import com.jetbrains.python.pyi.PyiUtil; import org.jetbrains.annotations.NotNull; public class PyDefinitionsSearch implements QueryExecutor { @Override - public boolean execute(@NotNull final PsiElement e, @NotNull final Processor consumer) { - if (e instanceof PyClass) { - final Query query = PyClassInheritorsSearch.search((PyClass)e, true); - return query.forEach(consumer); - } - else if (e instanceof PyFunction) { - final Query query = - ReadAction.compute(() -> PyOverridingMethodsSearch.search((PyFunction)e, true)); - - return query.forEach(consumer); - } - else if (e instanceof PyTargetExpression) { // PY-237 - final PsiElement parent = ReadAction.compute(() -> e.getParent()); - - if (parent instanceof PyAssignmentStatement) { - return consumer.process(parent); + public boolean execute(@NotNull PsiElement element, @NotNull Processor consumer) { + if (element instanceof PyElement) { + PsiElement finalElement = element; + boolean isInsideStub = ReadAction.compute(() -> PyiUtil.isInsideStub(finalElement)); + if (isInsideStub) { + var originalElement = ReadAction.compute(() -> PyiUtil.getOriginalElement((PyElement)finalElement)); + if (originalElement != null) { + element = originalElement; + if (!consumer.process(element)) return false; + } } } - else if (e instanceof PyiFile) { - final PsiElement originalElement = ReadAction.compute(() -> PyiUtil.getOriginalElement((PyiFile)e)); - if (originalElement != null) { - consumer.process(originalElement); - } + if (element instanceof PyClass) { + return processInheritors((PyClass)element, consumer); + } + else if (element instanceof PyFunction) { + return processOverridingMethods((PyFunction)element, consumer); + } + else if (element instanceof PyTargetExpression) { + return processAssignmentStatement(element, consumer); } return true; } + + private static boolean processInheritors(@NotNull PyClass pyClass, @NotNull Processor consumer) { + return ReadAction.compute(() -> PyClassInheritorsSearch.search(pyClass, true)).forEach(consumer); + } + + private static boolean processOverridingMethods(@NotNull PyFunction pyFunction, + @NotNull Processor consumer) { + return ReadAction.compute(() -> PyOverridingMethodsSearch.search(pyFunction, true)).forEach(consumer); + } + + private static boolean processAssignmentStatement(@NotNull PsiElement element, + @NotNull Processor consumer) { + PsiElement parent = ReadAction.compute(() -> element.getParent()); + return !(parent instanceof PyAssignmentStatement) || consumer.process(parent); + } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/pyi/PyiUtil.java b/python/python-psi-impl/src/com/jetbrains/python/pyi/PyiUtil.java index 396a93059253..b2a0d529d657 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/pyi/PyiUtil.java +++ b/python/python-psi-impl/src/com/jetbrains/python/pyi/PyiUtil.java @@ -70,7 +70,20 @@ public final class PyiUtil { if (originalFile == null) return null; PsiElement result = findSimilarElement(element, originalFile); - if (result == null && element instanceof PyFunction) { + + // If a name is defined in a .pyi stub and the corresponding .py module in a different manner, e.g. + // it's exported through an assignment to a top-level attribute in a .pyi stub, but though a regular + // "from" import in .py file, we might end up in another stub file again. It happens because resolving + // an imported name in a .py file in findSimilarElement() still prioritizes .pyi stubs over implementations + // as PyResolveContext doesn't retain the PyQualifiedNameResolveContext.getWithoutStubs flag. + // TODO propagate "without stubs" property through PyResolveContext + if (result instanceof PyElement && isInsideStub(result) && result.getContainingFile() != file) { + result = getOriginalElement((PyElement)result); + } + + if (result != null) return result; + + if (element instanceof PyFunction) { PyClass containingClass = PyUtil.turnConstructorIntoClass((PyFunction)element); if (containingClass != null) { result = findSimilarElement(containingClass, originalFile); @@ -147,7 +160,7 @@ public final class PyiUtil { } public static boolean isPyiFileOfPackage(@NotNull PsiElement element) { - return element instanceof PyiFile || PyUtil.turnDirIntoInit(element) instanceof PyiFile; + return element instanceof PyiFile || PyUtil.turnDirIntoInitPyi(element) instanceof PyiFile; } private static boolean pyButNotPyiFile(@Nullable PsiFile file) { @@ -164,7 +177,7 @@ public final class PyiUtil { return PyUtil.as(PyResolveImportUtil.resolveQualifiedName(name, context) .stream() .findFirst() - .map(PyUtil::turnDirIntoInit) + .map(PyUtil::turnDirIntoInitPyi) .orElse(null), PyiFile.class); } @@ -178,7 +191,7 @@ public final class PyiUtil { return PyUtil.as(PyResolveImportUtil.resolveQualifiedName(name, context) .stream() .findFirst() - .map(PyUtil::turnDirIntoInit) + .map(PyUtil::turnDirIntoInitPy) .orElse(null), PyFile.class); } diff --git a/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/__init__.py b/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/__init__.pyi b/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/foo.py b/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/foo.py new file mode 100644 index 000000000000..4371a5a1f6ed --- /dev/null +++ b/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/foo.py @@ -0,0 +1,2 @@ +def bar(): + pass diff --git a/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/foo.pyi b/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/foo.pyi new file mode 100644 index 000000000000..a70793f3a60b --- /dev/null +++ b/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/pkg/foo.pyi @@ -0,0 +1 @@ +def bar() -> None: ... \ No newline at end of file diff --git a/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/test.py b/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/test.py new file mode 100644 index 000000000000..855d762234e8 --- /dev/null +++ b/python/testData/navigation/goToDeclarationClassInPackageWithInitPyi/test.py @@ -0,0 +1,4 @@ +from pkg.foo import bar + +if __name__ == '__main__': + bar() diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/__init__.py b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/__init__.py new file mode 100644 index 000000000000..1d9916bd5eba --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/__init__.py @@ -0,0 +1 @@ +from blues.client import Blues \ No newline at end of file diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/__init__.pyi b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/__init__.pyi new file mode 100644 index 000000000000..8fc3081dd929 --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/__init__.pyi @@ -0,0 +1,3 @@ +from . import client + +Blues = client.Blues \ No newline at end of file diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/client.py b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/client.py new file mode 100644 index 000000000000..eebfdde3f31c --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/client.py @@ -0,0 +1,2 @@ +class Blues: + ... diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/client.pyi b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/client.pyi new file mode 100644 index 000000000000..9952ca608a0e --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/blues/client.pyi @@ -0,0 +1,2 @@ +class Blues: + ... \ No newline at end of file diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/test.py b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/test.py new file mode 100644 index 000000000000..8d5cedf8dfc6 --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStub/test.py @@ -0,0 +1,2 @@ +import blues +blues.Blues diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.py b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.py new file mode 100644 index 000000000000..108ac7674448 --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.py @@ -0,0 +1 @@ +from .mod import Class \ No newline at end of file diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.pyi b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.pyi new file mode 100644 index 000000000000..0698e1b146af --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.pyi @@ -0,0 +1,3 @@ +from . import mod + +Class = mod.Class \ No newline at end of file diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.py b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.py new file mode 100644 index 000000000000..798647a18d7a --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.py @@ -0,0 +1 @@ +from .mod2 import Class \ No newline at end of file diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.pyi b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.pyi new file mode 100644 index 000000000000..bc707f9fc7fc --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.pyi @@ -0,0 +1,3 @@ +from . import mod2 + +Class = mod2.Class diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.py b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.py new file mode 100644 index 000000000000..050e8a09605b --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.py @@ -0,0 +1,2 @@ +class Class: + pass diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.pyi b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.pyi new file mode 100644 index 000000000000..b249565c4cc3 --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.pyi @@ -0,0 +1,2 @@ +class Class: + pass \ No newline at end of file diff --git a/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/test.py b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/test.py new file mode 100644 index 000000000000..73c22ca45d42 --- /dev/null +++ b/python/testData/navigation/goToDeclarationNameReExportedThroughAssignmentInPyiStubTwice/test.py @@ -0,0 +1,3 @@ +import pkg + +pkg.Class \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/__init__.py b/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/__init__.py new file mode 100644 index 000000000000..f5d825254710 --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/__init__.py @@ -0,0 +1 @@ +import mymodule \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/mymodule.py b/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/mymodule.py new file mode 100644 index 000000000000..e5ded6222e4b --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/mymodule.py @@ -0,0 +1,2 @@ +class Clazz: + ... \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/mymodule.pyi b/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/mymodule.pyi new file mode 100644 index 000000000000..e5ded6222e4b --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPy/pkg/mymodule.pyi @@ -0,0 +1,2 @@ +class Clazz: + ... \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPy/test.py b/python/testData/navigation/goToImplementationClassInPackageWithInitPy/test.py new file mode 100644 index 000000000000..ee893751842a --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPy/test.py @@ -0,0 +1,2 @@ +import pkg.mymodule +pkg.mymodule.Clazz \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/__init__.py b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/__init__.py new file mode 100644 index 000000000000..473c90fd5ef3 --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/__init__.py @@ -0,0 +1 @@ +from . import mymodule \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/__init__.pyi b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/__init__.pyi new file mode 100644 index 000000000000..473c90fd5ef3 --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/__init__.pyi @@ -0,0 +1 @@ +from . import mymodule \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/mymodule.py b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/mymodule.py new file mode 100644 index 000000000000..e5ded6222e4b --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/mymodule.py @@ -0,0 +1,2 @@ +class Clazz: + ... \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/mymodule.pyi b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/mymodule.pyi new file mode 100644 index 000000000000..e5ded6222e4b --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/pkg/mymodule.pyi @@ -0,0 +1,2 @@ +class Clazz: + ... \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/test.py b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/test.py new file mode 100644 index 000000000000..ee893751842a --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInPackageWithInitPyi/test.py @@ -0,0 +1,2 @@ +import pkg.mymodule +pkg.mymodule.Clazz \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationClassInherits/mod.py b/python/testData/navigation/goToImplementationClassInherits/mod.py new file mode 100644 index 000000000000..09db11d11be4 --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInherits/mod.py @@ -0,0 +1,5 @@ +class Parent: + pass + +class Child(Parent): + pass diff --git a/python/testData/navigation/goToImplementationClassInherits/mod.pyi b/python/testData/navigation/goToImplementationClassInherits/mod.pyi new file mode 100644 index 000000000000..b76459355e67 --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInherits/mod.pyi @@ -0,0 +1,6 @@ +class Parent: + ... + + +class Child(Parent): + ... diff --git a/python/testData/navigation/goToImplementationClassInherits/test.py b/python/testData/navigation/goToImplementationClassInherits/test.py new file mode 100644 index 000000000000..b388240dd848 --- /dev/null +++ b/python/testData/navigation/goToImplementationClassInherits/test.py @@ -0,0 +1,3 @@ +from mod import Parent + +print(Parent) diff --git a/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/pkg/__init__.py b/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/pkg/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/pkg/mymodule.py b/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/pkg/mymodule.py new file mode 100644 index 000000000000..3392bc062e96 --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/pkg/mymodule.py @@ -0,0 +1,2 @@ +def bar(): + pass \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/pkg/mymodule.pyi b/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/pkg/mymodule.pyi new file mode 100644 index 000000000000..a70793f3a60b --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/pkg/mymodule.pyi @@ -0,0 +1 @@ +def bar() -> None: ... \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/test.py b/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/test.py new file mode 100644 index 000000000000..711712f3f40a --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionInPackageWithInitPy/test.py @@ -0,0 +1,4 @@ +from pkg.mymodule import bar + +if __name__ == '__main__': + bar() \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/__init__.py b/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/__init__.py new file mode 100644 index 000000000000..c12c6002f334 --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/__init__.py @@ -0,0 +1,3 @@ +from .mymodule import foo + +aaa = foo diff --git a/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/__init__.pyi b/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/__init__.pyi new file mode 100644 index 000000000000..518dea8fd202 --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/__init__.pyi @@ -0,0 +1,4 @@ +def foo(): + ... + +aaa = foo diff --git a/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/mymodule.py b/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/mymodule.py new file mode 100644 index 000000000000..7d607b7bf4da --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionInPyNotPyi/pkg/mymodule.py @@ -0,0 +1,2 @@ +def foo(): + print(42) diff --git a/python/testData/navigation/goToImplementationFunctionInPyNotPyi/test.py b/python/testData/navigation/goToImplementationFunctionInPyNotPyi/test.py new file mode 100644 index 000000000000..628c4f2fcc23 --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionInPyNotPyi/test.py @@ -0,0 +1,3 @@ +import pkg + +pkg.aaa \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationFunctionOverrides/mod.py b/python/testData/navigation/goToImplementationFunctionOverrides/mod.py new file mode 100644 index 000000000000..07ce9b912aba --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionOverrides/mod.py @@ -0,0 +1,8 @@ +class Parent: + def show(self): + pass + + +class Child(Parent): + def show(self): + pass diff --git a/python/testData/navigation/goToImplementationFunctionOverrides/mod.pyi b/python/testData/navigation/goToImplementationFunctionOverrides/mod.pyi new file mode 100644 index 000000000000..53b35be23836 --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionOverrides/mod.pyi @@ -0,0 +1,8 @@ +class Parent: + def show(self): + ... + + +class Child(Parent): + def show(self): + ... diff --git a/python/testData/navigation/goToImplementationFunctionOverrides/test.py b/python/testData/navigation/goToImplementationFunctionOverrides/test.py new file mode 100644 index 000000000000..d8237882a5ee --- /dev/null +++ b/python/testData/navigation/goToImplementationFunctionOverrides/test.py @@ -0,0 +1,5 @@ +from mod import Parent + + +def foo(p: Parent): + p.show() diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/__init__.py b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/__init__.py new file mode 100644 index 000000000000..1d9916bd5eba --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/__init__.py @@ -0,0 +1 @@ +from blues.client import Blues \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/__init__.pyi b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/__init__.pyi new file mode 100644 index 000000000000..8fc3081dd929 --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/__init__.pyi @@ -0,0 +1,3 @@ +from . import client + +Blues = client.Blues \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/client.py b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/client.py new file mode 100644 index 000000000000..eebfdde3f31c --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/client.py @@ -0,0 +1,2 @@ +class Blues: + ... diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/client.pyi b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/client.pyi new file mode 100644 index 000000000000..9952ca608a0e --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/blues/client.pyi @@ -0,0 +1,2 @@ +class Blues: + ... \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/test.py b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/test.py new file mode 100644 index 000000000000..8d5cedf8dfc6 --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStub/test.py @@ -0,0 +1,2 @@ +import blues +blues.Blues diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.py b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.py new file mode 100644 index 000000000000..108ac7674448 --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.py @@ -0,0 +1 @@ +from .mod import Class \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.pyi b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.pyi new file mode 100644 index 000000000000..0698e1b146af --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/__init__.pyi @@ -0,0 +1,3 @@ +from . import mod + +Class = mod.Class \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.py b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.py new file mode 100644 index 000000000000..798647a18d7a --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.py @@ -0,0 +1 @@ +from .mod2 import Class \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.pyi b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.pyi new file mode 100644 index 000000000000..bc707f9fc7fc --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod.pyi @@ -0,0 +1,3 @@ +from . import mod2 + +Class = mod2.Class diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.py b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.py new file mode 100644 index 000000000000..050e8a09605b --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.py @@ -0,0 +1,2 @@ +class Class: + pass diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.pyi b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.pyi new file mode 100644 index 000000000000..b249565c4cc3 --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/pkg/mod2.pyi @@ -0,0 +1,2 @@ +class Class: + pass \ No newline at end of file diff --git a/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/test.py b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/test.py new file mode 100644 index 000000000000..73c22ca45d42 --- /dev/null +++ b/python/testData/navigation/goToImplementationNameReExportedThroughAssignmentInPyiStubTwice/test.py @@ -0,0 +1,3 @@ +import pkg + +pkg.Class \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyNavigationTest.kt b/python/testSrc/com/jetbrains/python/PyNavigationTest.kt index 5f8cca711c55..be9585e79d9b 100644 --- a/python/testSrc/com/jetbrains/python/PyNavigationTest.kt +++ b/python/testSrc/com/jetbrains/python/PyNavigationTest.kt @@ -218,6 +218,73 @@ class PyNavigationTest : PyTestCase() { checkPyNotPyi(target?.containingFile) } + // PY-54905 + fun testGoToImplementationFunctionInPackageWithInitPy() { + doTestGotoImplementationNavigatesToPyNotPyi() + } + + // PY-54905 + fun testGoToImplementationClassInPackageWithInitPy() { + doTestGotoImplementationNavigatesToPyNotPyi() + } + + // PY-54905 PY-54620 + fun testGoToImplementationClassInPackageWithInitPyi() { + doTestGotoImplementationNavigatesToPyNotPyi() + } + + // PY-54905 + fun testGoToImplementationFunctionInPyNotPyi() { + doTestGotoImplementationNavigatesToPyNotPyi(2) + } + + // PY-54905 + fun testGoToImplementationNameReExportedThroughAssignmentInPyiStub() { + doTestGotoImplementationNavigatesToPyNotPyi() + } + + // PY-61740 + fun testGoToDeclarationNameReExportedThroughAssignmentInPyiStub() { + doTestGotoDeclarationNavigatesToPyNotPyi() + } + + // PY-61740 + fun testGoToDeclarationNameReExportedThroughAssignmentInPyiStubTwice() { + doTestGotoDeclarationNavigatesToPyNotPyi() + } + + // PY-54905 + fun testGoToImplementationFunctionOverrides() { + doTestGotoImplementationNavigatesToPyNotPyi(2) + } + + // PY-54905 + fun testGoToImplementationClassInherits() { + doTestGotoImplementationNavigatesToPyNotPyi(2) + } + + // PY-61740 + fun testGoToDeclarationClassInPackageWithInitPyi() { + doTestGotoDeclarationNavigatesToPyNotPyi() + } + + private fun doTestGotoDeclarationNavigatesToPyNotPyi() { + myFixture.copyDirectoryToProject(getTestName(true), "") + myFixture.configureByFile("test.py") + val target = PyGotoDeclarationHandler().getGotoDeclarationTarget(elementAtCaret, myFixture.editor) + checkPyNotPyi(target!!.containingFile) + } + + private fun doTestGotoImplementationNavigatesToPyNotPyi(numTargets: Int = 1) { + myFixture.copyDirectoryToProject(getTestName(true), "") + myFixture.configureByFile("test.py") + val gotoData = CodeInsightTestUtil.gotoImplementation(myFixture.editor, myFixture.file) + assertSize(numTargets, gotoData.targets) + for (target in gotoData.targets) { + checkPyNotPyi(target.containingFile) + } + } + private fun doTestGotoDeclarationOrUsagesOutcome(expectedOutcome: GTDUOutcome, text: String) { myFixture.configureByText("a.py", text) val actualOutcome = GotoDeclarationOrUsageHandler2.testGTDUOutcomeInNonBlockingReadAction(myFixture.editor, myFixture.file, myFixture.caretOffset)