public class NImportFrom extends NNode
from moduleA import a, b as c, d
and from foo.bar.moduleB import *
. The indexer's implementation of import * uses different semantics from all the other forms of import. It's basically a bug, although the jury is still out as to which implementation is better.
For the others we define name bindings anywhere an actual name is introduced into the scope containing the import statement, and references to the imported module or name everywhere else. This mimics the behavior of Python at runtime, but it may be confusing to anyone with only a casual understanding of Python's data model, who might think it works more like Java.
For import * we just enter the imported names into the symbol table, which lets other code reference them, but the references "pass through" automatically to the module from which the names were imported.
To illustate the difference, consider the following four modules:
moduleA.py: a = 1 b = 2 moduleB.py: c = 3 d = 4 moduleC.py: from moduleA import a, b from moduleB import * print a # indexer finds definition of 'a' 2 lines up print b # indexer finds definition of 'b' 3 lines up print c # indexer finds definition of 'c' in moduleB print d # indexer finds definition of 'd' in moduleB moduleD.py: import moduleC print moduleC.a # indexer finds definition of 'a' in moduleC print moduleC.b # indexer finds definition of 'b' in moduleC print moduleC.c # indexer finds definition of 'c' in moduleB print moduleC.c # indexer finds definition of 'd' in moduleBTo make import * work like the others, we need only create bindings for the imported names. But where would the bindings be located? Assuming that we were to co-locate them all at the "*" name node, clicking on a reference to any of the names would jump to the "*". It's not clear that this is a better user experience.
We could make the other import statement forms work like import *
,
but that path is even more fraught with confusing inconsistencies.
Modifier and Type | Field and Description |
---|---|
java.util.List<NAlias> |
aliases |
java.lang.String |
module |
NQname |
qname |
Constructor and Description |
---|
NImportFrom(java.lang.String module,
NQname qname,
java.util.List<NAlias> aliases) |
NImportFrom(java.lang.String module,
NQname qname,
java.util.List<NAlias> aliases,
int start,
int end) |
Modifier and Type | Method and Description |
---|---|
protected void |
bindNames(Scope s)
Called by resolver to bind names into the passed scope.
|
boolean |
bindsName()
Returns
true if this is a name-binding node. |
boolean |
isImportStar() |
NType |
resolve(Scope s)
Node should set the resolved type in its
NNode.type field
and also return it. |
java.lang.String |
toString() |
void |
visit(NNodeVisitor v)
Visits this node and optionally its children.
|
addChildren, addChildren, addError, addError, addType, addWarning, addWarning, end, getAstRoot, getDeepestNodeAtOffset, getEnclosingNamespace, getFile, getParent, getTable, getType, isCall, isClassDef, isFunctionDef, isLambda, isModule, isName, length, resolveExpr, resolveList, resolveListAsUnion, setEnd, setParent, setStart, setType, start, visitNode, visitNodeList
public java.lang.String module
public NQname qname
public java.util.List<NAlias> aliases
public NImportFrom(java.lang.String module, NQname qname, java.util.List<NAlias> aliases)
public boolean bindsName()
NNode
true
if this is a name-binding node. Includes functions/lambdas,
function/lambda params, classes, assignments, imports, and implicit assignment via for
statements and except clauses.protected void bindNames(Scope s) throws java.lang.Exception
NNode
public NType resolve(Scope s) throws java.lang.Exception
NNode
NNode.type
field
and also return it.public boolean isImportStar()
public java.lang.String toString()
toString
in class java.lang.Object
public void visit(NNodeVisitor v)
NNode