diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 3925a378b010..69c5391700b6 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -475,7 +475,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { List ret = new ArrayList(); boolean suppressParentheses = context.get(CTX_SUPPRESS_PARENTHESES) != null; - addOwnClassMembers(location, namesAlready, suppressParentheses, ret); + addOwnClassMembers(location, namesAlready, suppressParentheses, ret, prefix); PsiFile origin = (location != null) ? CompletionUtil.getOriginalOrSelf(location) @@ -534,7 +534,11 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } } - private void addOwnClassMembers(PsiElement expressionHook, Set namesAlready, boolean suppressParentheses, List ret) { + private void addOwnClassMembers(PsiElement expressionHook, + Set namesAlready, + boolean suppressParentheses, + List ret, + @Nullable final String prefix) { PyClass containingClass = PsiTreeUtil.getParentOfType(expressionHook, PyClass.class); if (containingClass != null) { containingClass = CompletionUtil.getOriginalElement(containingClass); @@ -563,6 +567,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { String name = le.getLookupString(); if (namesAlready.contains(name)) continue; if (!withinOurClass && isClassPrivate(name)) continue; + if (!withinOurClass && isClassProtected(name) && prefix == null) continue; namesAlready.add(name); ret.add(le); } @@ -623,6 +628,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return lookup_string.startsWith("__") && !lookup_string.endsWith("__"); } + private static boolean isClassProtected(@NotNull final String lookupString) { + return lookupString.startsWith("_") && !lookupString.startsWith("__"); + } + public String getName() { return getPyClass().getName(); } diff --git a/python/testData/completion/protectedClassNameNoPrefix.py b/python/testData/completion/protectedClassNameNoPrefix.py new file mode 100644 index 000000000000..467360f81bb5 --- /dev/null +++ b/python/testData/completion/protectedClassNameNoPrefix.py @@ -0,0 +1,6 @@ +class A: + def _foo(self): + pass + + +A(). \ No newline at end of file diff --git a/python/testData/completion/protectedClassNames.after.py b/python/testData/completion/protectedClassNames.after.py new file mode 100644 index 000000000000..992117797491 --- /dev/null +++ b/python/testData/completion/protectedClassNames.after.py @@ -0,0 +1,6 @@ +class A: + def _foo(self): + pass + + +A()._foo() \ No newline at end of file diff --git a/python/testData/completion/protectedClassNames.py b/python/testData/completion/protectedClassNames.py new file mode 100644 index 000000000000..8f35bf2bc16c --- /dev/null +++ b/python/testData/completion/protectedClassNames.py @@ -0,0 +1,6 @@ +class A: + def _foo(self): + pass + + +A()._f \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 9f7a20b8d280..c71977c4c770 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -932,6 +932,16 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } + public void testProtectedClassNames() { + doTest(); + } + + public void testProtectedClassNameNoPrefix() { + final List variants = doTestByFile(); + assertNotNull(variants); + assertDoesntContain(variants, "_foo(self)"); + } + @Override protected String getTestDataPath() { return super.getTestDataPath() + "/completion";