subpackage names are visible in the __init__.py of a package even if something was imported from the subpackages, and the subpackages themselves weren't imported (related to PY-3201)

This commit is contained in:
Dmitry Jemerov
2011-05-24 17:13:42 +04:00
parent 940a1a2af3
commit 1a19cbf99b
17 changed files with 123 additions and 28 deletions
@@ -9,7 +9,6 @@ import com.intellij.usages.UsageTarget;
import com.intellij.usages.UsageViewManager;
import com.intellij.usages.UsageViewPresentation;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.NameDefiner;
import com.jetbrains.python.psi.resolve.PyResolveUtil;
import com.jetbrains.python.psi.resolve.ResolveProcessor;
import org.jetbrains.annotations.NotNull;
@@ -46,7 +45,7 @@ public class DeclarationConflictChecker {
PyResolveUtil.treeCrawlUp(processor, ref.getElement());
PsiElement result = processor.getResult();
if (result != null) {
List<NameDefiner> definers = processor.getDefiners();
List<PsiElement> definers = processor.getDefiners();
if (definers != null && definers.size() > 0) {
result = definers.get(0); // in this case, processor's result is one hop of resolution too far from what we want.
}
@@ -87,7 +87,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
}
public static class Visitor extends PyInspectionVisitor {
private Set<NameDefiner> myUsedImports = Collections.synchronizedSet(new HashSet<NameDefiner>());
private Set<PsiElement> myUsedImports = Collections.synchronizedSet(new HashSet<PsiElement>());
private Set<NameDefiner> myAllImports = Collections.synchronizedSet(new HashSet<NameDefiner>());
private final ImmutableSet<String> myIgnoredIdentifiers;
@@ -610,6 +610,14 @@ public class PyUtil {
} // don't touch non-dirs
}
@Nullable
public static PsiElement turnInitIntoDir(PsiElement target) {
if (target instanceof PyFile && PyNames.INIT_DOT_PY.equals(((PyFile) target).getName())) {
return ((PyFile) target).getContainingDirectory();
}
return target;
}
/**
* Counts initial underscores of an identifier.
*
@@ -15,8 +15,6 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndex;
import com.jetbrains.python.*;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeImpl;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveUtil;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
@@ -32,7 +30,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.lang.ref.SoftReference;
import java.util.*;
public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
@@ -248,12 +245,12 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
}
@Nullable
private static PsiElement findNameInFromImportStatementStub(String name, PyFromImportStatementStub child) {
if (child.isStarImport()) {
private PsiElement findNameInFromImportStatementStub(String name, PyFromImportStatementStub stub) {
if (stub.isStarImport()) {
if (PyUtil.isClassPrivateName(name)) {
return null;
}
final PyFromImportStatement statement = child.getPsi();
final PyFromImportStatement statement = stub.getPsi();
PsiElement starImportSource = ResolveImportUtil.resolveFromImportStatementSource(statement);
if (starImportSource != null) {
starImportSource = PyUtil.turnDirIntoInit(starImportSource);
@@ -266,7 +263,7 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
}
}
else {
final List<StubElement> importElements = child.getChildrenStubs();
final List<StubElement> importElements = stub.getChildrenStubs();
for (StubElement importElement : importElements) {
final PsiElement psi = importElement.getPsi();
if (psi instanceof PyImportElement && name.equals(((PyImportElement)psi).getVisibleName())) {
@@ -277,6 +274,16 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
}
}
}
// http://stackoverflow.com/questions/6048786/from-module-import-in-init-py-makes-module-name-visible
if (PyNames.INIT_DOT_PY.equals(getName())) {
final PyQualifiedName qName = stub.getImportSourceQName();
if (qName.endsWith(name)) {
final PsiElement element = PyUtil.turnInitIntoDir(ResolveImportUtil.resolveFromImportStatementSource(stub.getPsi()));
if (element != null && element.getParent() == getContainingDirectory()) {
return element;
}
}
}
return null;
}
@@ -302,10 +309,11 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
}
return new PyImportedModule(this, PyQualifiedName.fromComponents(name));
}
if (name.equals(((PyImportElement)psi).getVisibleName())) {
final PsiElement resolved = importElement.getElementNamed(name);
if (resolved != null) {
return resolved;
// http://stackoverflow.com/questions/6048786/from-module-import-in-init-py-makes-module-name-visible
if (qName.getComponentCount() > 1 && name.equals(qName.getLastComponent()) && PyNames.INIT_DOT_PY.equals(getName())) {
final PsiElement element = ResolveImportUtil.resolveImportElement(importElement, qName.removeLastComponent());
if (PyUtil.turnDirIntoInit(element) == this) {
return importElement;
}
}
}
@@ -171,7 +171,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
PsiElement uexpr = PyResolveUtil.treeCrawlUp(processor, false, realContext, roof);
if ((uexpr != null)) {
// sort what we got
for (NameDefiner hit : processor.getDefiners()) {
for (PsiElement hit : processor.getDefiners()) {
ret.poke(hit, getRate(hit));
}
uexpr = PyUtil.turnDirIntoInit(uexpr); // an import statement may have returned a dir
@@ -1,7 +1,6 @@
package com.jetbrains.python.psi.resolve;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.NameDefiner;
import java.util.List;
@@ -9,14 +8,14 @@ import java.util.List;
* @author yole
*/
public class ImportedResolveResult extends RatedResolveResult {
private final List<NameDefiner> myNameDefiners;
private final List<PsiElement> myNameDefiners;
public ImportedResolveResult(PsiElement element, int rate, List<NameDefiner> nameDefiners) {
public ImportedResolveResult(PsiElement element, int rate, List<PsiElement> nameDefiners) {
super(rate, element);
myNameDefiners = nameDefiners;
}
public List<NameDefiner> getNameDefiners() {
public List<PsiElement> getNameDefiners() {
return myNameDefiners;
}
@@ -91,7 +91,9 @@ public class PyResolveUtil {
PyElementTypes.ASSIGNMENT_STATEMENT,
PyElementTypes.PARAMETER_LIST,
PyElementTypes.EXCEPT_PART,
PyElementTypes.FOR_STATEMENT);
PyElementTypes.FOR_STATEMENT,
PyElementTypes.FROM_IMPORT_STATEMENT); // this is not exactly a NameDefiner but needed anyway
// in mypackage/__init__.py, 'from mypackage.foo import bar' makes 'foo' name visible
/**
* Crawls up the PSI tree, checking nodes as if crawling backwards through source lexemes.
@@ -190,7 +190,7 @@ public class ResolveImportUtil {
}
@NotNull
private static List<PsiElement> resolveFromImportStatementSource(PyFromImportStatement from_import_statement, PyQualifiedName qName) {
public static List<PsiElement> resolveFromImportStatementSource(PyFromImportStatement from_import_statement, PyQualifiedName qName) {
boolean absolute_import_enabled = isAbsoluteImportEnabledFor(from_import_statement);
PsiFile file = from_import_statement.getContainingFile();
return resolveModule(qName, file, absolute_import_enabled, from_import_statement.getRelativeLevel());
@@ -20,11 +20,11 @@ import java.util.List;
public class ResolveProcessor implements PsiScopeProcessor {
@NotNull private final String myName;
private PsiElement myResult = null;
private final List<NameDefiner> myDefiners;
private final List<PsiElement> myDefiners;
public ResolveProcessor(@NotNull final String name) {
myName = name;
myDefiners = new ArrayList<NameDefiner>(2); // 1 is typical, 2 is sometimes, more is rare.
myDefiners = new ArrayList<PsiElement>(2); // 1 is typical, 2 is sometimes, more is rare.
}
public PsiElement getResult() {
@@ -36,11 +36,11 @@ public class ResolveProcessor implements PsiScopeProcessor {
*
* @param definer
*/
protected void addNameDefiner(NameDefiner definer) {
protected void addNameDefiner(PsiElement definer) {
myDefiners.add(definer);
}
public List<NameDefiner> getDefiners() {
public List<PsiElement> getDefiners() {
return myDefiners;
}
@@ -98,11 +98,21 @@ public class ResolveProcessor implements PsiScopeProcessor {
}
}
else if (element instanceof PyImportElement) {
// name is resolved to unresolved import (PY-956)
final PyImportElement importElement = (PyImportElement) element;
final PyQualifiedName qName = importElement.getImportedQName();
// http://stackoverflow.com/questions/6048786/from-module-import-in-init-py-makes-module-name-visible
if (qName != null && qName.getComponentCount() > 1 && myName.equals(qName.getLastComponent()) &&
PyNames.INIT_DOT_PY.equals(importElement.getContainingFile().getName())) {
final PsiElement packageElement = ResolveImportUtil.resolveImportElement(importElement, qName.removeLastComponent());
if (PyUtil.turnDirIntoInit(packageElement) == importElement.getContainingFile()) {
myResult = PyUtil.turnDirIntoInit(ResolveImportUtil.resolveImportElement(importElement));
addNameDefiner(importElement);
}
}
// name is resolved to unresolved import (PY-956)
String definedName = importElement.getAsName();
if (definedName == null) {
final PyQualifiedName qName = importElement.getImportedQName();
if (qName != null && qName.getComponentCount() == 1) {
definedName = qName.getComponents().get(0);
}
@@ -112,6 +122,17 @@ public class ResolveProcessor implements PsiScopeProcessor {
}
}
}
else if (element instanceof PyFromImportStatement && PyNames.INIT_DOT_PY.equals(element.getContainingFile().getName())) {
final PyFromImportStatement fromImportStatement = (PyFromImportStatement)element;
final PyQualifiedName qName = fromImportStatement.getImportSourceQName();
if (qName != null && qName.endsWith(myName)) {
final PsiElement source = PyUtil.turnInitIntoDir(ResolveImportUtil.resolveFromImportStatementSource(fromImportStatement));
if (source != null && source.getParent() == element.getContainingFile().getContainingDirectory()) {
myResult = source;
addNameDefiner(fromImportStatement);
}
}
}
return true;
}
@@ -0,0 +1,4 @@
import mygame
mygame.display.foo()
# <ref>
@@ -0,0 +1,5 @@
from display import foo
print('calling display.foo()')
display.foo()
# <ref>
@@ -0,0 +1 @@
def foo(): print 'foo'
@@ -0,0 +1,4 @@
import mygame
mygame.display.foo()
# <ref>
@@ -0,0 +1,5 @@
import mygame.display
display.foo()
# <ref>
@@ -0,0 +1 @@
def foo(): pass
@@ -13,6 +13,7 @@ import com.jetbrains.python.psi.*;
* @author yole
*/
public class PyMultiFileResolveTest extends PyResolveTestCase {
protected String myTestFileName;
private static void checkInitPyDir(PsiElement elt, String dirname) {
assertTrue(elt instanceof PyFile);
@@ -223,6 +224,27 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
assertResolvesTo(PyFunction.class, "foo", "/src/mygame/display.py");
}
public void testImportPackageIntoSelfInit() {
myTestFileName = "mygame/__init__.py";
try {
assertResolvesTo(PyFile.class, "display.py");
}
finally {
myTestFileName = null;
}
}
public void testFromImportPackageIntoSelf() {
myTestFileName = "mygame/__init__.py";
try {
assertResolvesTo(PyFile.class, "display.py");
}
finally {
myTestFileName = null;
}
}
public void testImportPrivateNameWithStar() { // PY-2717
PsiElement psiElement = doResolve();
assertNull(psiElement);
@@ -230,7 +252,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
private PsiFile prepareFile() {
String testName = getTestName(true);
String fileName = getTestName(false) + ".py";
String fileName = myTestFileName != null ? myTestFileName : getTestName(false) + ".py";
myFixture.copyDirectoryToProject(testName, "");
PsiDocumentManager.getInstance(myFixture.getProject()).commitAllDocuments();
@@ -279,6 +279,22 @@ public class PyStubsTest extends PyLightFixtureTestCase {
assertNotParsed(file);
}
public void testInitPyExportsSubpackageImport() {
myFixture.copyDirectoryToProject("../resolve/multiFile/importPackageIntoSelf", "");
final PyFile initPy = (PyFile) myFixture.getPsiManager().findFile(myFixture.findFileInTempDir("mygame/__init__.py"));
final PsiElement element = initPy.findExportedName("display");
assertTrue(element instanceof PyImportElement);
assertNotParsed(initPy);
}
public void testInitPyExportsSubpackageFromImport() {
myFixture.copyDirectoryToProject("../resolve/multiFile/fromImportPackageIntoSelf", "");
final PyFile initPy = (PyFile) myFixture.getPsiManager().findFile(myFixture.findFileInTempDir("mygame/__init__.py"));
final PsiElement element = initPy.findExportedName("display");
assertTrue(element instanceof PyFile);
assertNotParsed(initPy);
}
public void testVariableIndex() {
getTestFile();
GlobalSearchScope scope = GlobalSearchScope.allScope(myFixture.getProject());