Preliminarily closes PY-12 and PY-8. Import resolution is still quite suboptimal, though.

PyFile and PyFileImpl: added getUrl() method.
PyResolveUtil and PyFile: more of (lame) means to squash SOEs on circular imports.
ResolveImportUtil: actual PY-12 resolve order fix.
PyMultiFileResolveTest and friends: a test case for name resolution inside subpackage.
This commit is contained in:
Dmitry Cheryasov
2008-05-28 08:55:29 +04:00
parent 6ad081b98d
commit d538ddb5f5
8 changed files with 87 additions and 12 deletions
@@ -5,6 +5,7 @@ package com.jetbrains.python.psi;
import com.intellij.openapi.util.Key;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -19,4 +20,10 @@ public interface PyFile extends PyElement, PsiFile {
List<PyFunction> getTopLevelFunctions();
List<PyTargetExpression> getTopLevelAttributes();
/**
@return an URL of file, maybe bogus if virtual file is not present.
*/
@NotNull
String getUrl();
}
@@ -63,6 +63,10 @@ public class PyResolveUtil {
if ((processor instanceof ResolveProcessor) && !(((ResolveProcessor)processor).approve(cur))) {
return null;
}
/* // resolution debug tracker
if (cur instanceof PsiFile) System.out.println(processor.toString() + ": " + cur.toString());
else System.out.println(processor.toString() + ": " + _fmt_node(cur));
*/
if (!cur.processDeclarations(processor, ResolveState.initial(), cur == elt ? lastParent : null, elt)) {
if (processor instanceof ResolveProcessor) {
return ((ResolveProcessor)processor).getResult();
@@ -140,7 +144,20 @@ public class PyResolveUtil {
public PsiElement getResult() {
return myResult;
}
static String _nvl(Object s) {
if (s != null) return "'" + s.toString() + "'";
else return "null";
}
public Set<String> getSeen() {
return mySeen;
}
public String toString() {
return _nvl(myName) + ", " + _nvl(myResult);
}
public boolean execute(PsiElement element, ResolveState substitutor) {
if (element instanceof PyFile) {
final VirtualFile file = ((PyFile)element).getVirtualFile();
@@ -18,10 +18,8 @@ package com.jetbrains.python.psi.impl;
import com.intellij.extapi.psi.PsiFileBase;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.ResolveState;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.tree.IElementType;
@@ -35,7 +33,9 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class PyFileImpl extends PsiFileBase implements PyFile {
public PyFileImpl(FileViewProvider viewProvider) {
@@ -50,6 +50,15 @@ public class PyFileImpl extends PsiFileBase implements PyFile {
public String toString() {
return "PyFile:" + getName();
}
@NotNull
public String getUrl() {
String fname;
VirtualFile vfile = getVirtualFile();
if (vfile != null) fname = vfile.getUrl();
else fname = "(null)://" + ((Object)this).toString();
return fname;
}
public Icon getIcon(int flags) {
return PythonFileType.INSTANCE.getIcon();
@@ -87,7 +96,11 @@ public class PyFileImpl extends PsiFileBase implements PyFile {
if (!processor.execute(e, substitutor)) return false;
}
for(PyFromImportStatement e: getFromImports()) {
Set<String> procSeen = null;
if (processor instanceof PyResolveUtil.ResolveProcessor) {
procSeen = ((PyResolveUtil.ResolveProcessor)processor).getSeen();
}
for(PyFromImportStatement e: getFromImports(procSeen)) {
if (e == lastParent) continue;
if (!e.processDeclarations(processor, substitutor, null, this)) return false;
}
@@ -153,10 +166,20 @@ public class PyFileImpl extends PsiFileBase implements PyFile {
return ret;
}
public List<PyFromImportStatement> getFromImports() {
public List<PyFromImportStatement> getFromImports(final Set<String> outerSeen) {
final List<PyFromImportStatement> result = new ArrayList<PyFromImportStatement>();
accept(new PyRecursiveElementVisitor() {
Set<String> alreadySeen;
{
alreadySeen = new HashSet<String>();
if (outerSeen != null) alreadySeen.addAll(outerSeen);
}
public void visitPyElement(final PyElement node) {
if (node instanceof PyFile) {
final String fname = ((PyFile)node).getUrl();
if (alreadySeen.contains(fname)) return; // don't descend into the same file again
else alreadySeen.add(fname);
}
super.visitPyElement(node);
if (PyFromImportStatement.class.isInstance(node)) {
//noinspection unchecked
@@ -54,13 +54,14 @@ public class ResolveImportUtil {
/*
True resolve order is:
- local modules,
- builtins (in fact),
- builtins? (check),
- modules from sys.path (aka SdkOrderEntries).
(http://docs.python.org/ref/import.html)
*/
// TODO: assume some things like sys to be only from __builtins__
// FIXME: resolve sources relatively to file in hand, not project root
// TODO: rewrite entirely imitating Python import process: global module table, under-initialisation, etc.
// qualified imports resolve their children
final PyExpression qualifier = importRef.getQualifier();
if (qualifier instanceof PyReferenceExpression) {
PsiElement qualifierElement = ((PyReferenceExpression) qualifier).resolve();
@@ -71,15 +72,29 @@ public class ResolveImportUtil {
if (importFrom != null) {
return resolveChild(importFrom, referencedName, importRef);
}
// unqualified import can be found:
// in the same dir
final PsiFile pfile = importRef.getContainingFile();
if (pfile != null) {
PsiDirectory pdir = pfile.getContainingDirectory();
if (pdir != null) {
PsiElement elt = resolveChild(pdir, referencedName, importRef);
if (elt != null) return elt;
}
}
// .. or in SDK roots
final Module module = ModuleUtil.findModuleForPsiElement(importRef);
if (module != null) {
RootPolicy<PsiElement> resolvePolicy = new RootPolicy<PsiElement>() {
/*
public PsiElement visitModuleSourceOrderEntry(final ModuleSourceOrderEntry moduleOrderEntry, final PsiElement value) {
if (value != null) return value;
return resolveInRoots(moduleOrderEntry.getRootModel().getContentRoots(), referencedName, importRef);
}
*/
public PsiElement visitJdkOrderEntry(final JdkOrderEntry jdkOrderEntry, final PsiElement value) {
if (value != null) return value;
return resolveInRoots(jdkOrderEntry.getRootFiles(OrderRootType.SOURCES), referencedName, importRef);
@@ -197,6 +212,7 @@ public class ResolveImportUtil {
if (subdir != null) return subdir;
else { // not a subdir, not a file; could be a name in parent/__init__.py
final PsiFile initPy = dir.findFile(INIT_PY);
if ((importRef != null) && (initPy == importRef.getContainingFile())) return ret; // don't dive into the file we're in
if (initPy != null) {
if (processor == null) processor = new PyResolveUtil.ResolveProcessor(referencedName); // should not normally happen
return PyResolveUtil.treeWalkUp(processor, initPy, null, importRef);
@@ -0,0 +1,4 @@
from pkg import token
x = token
# <ref>
@@ -0,0 +1 @@
from submodule import token # import and reexport
@@ -0,0 +1,2 @@
def token():
pass
@@ -84,6 +84,11 @@ public class PyMultiFileResolveTest extends CodeInsightTestCase {
assertTrue(element instanceof PyTargetExpression);
}
public void testResolveInPkg() throws Exception {
PsiElement element = doResolve();
assertTrue(element instanceof PyFunction);
}
private PsiElement doResolve() throws Exception {
String testName = getTestName(true);
String fileName = getTestName(false) + ".py";