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 moduleB
To 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, visitNodeListpublic 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()
NNodetrue 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
NNodepublic NType resolve(Scope s) throws java.lang.Exception
NNodeNNode.type field
and also return it.public boolean isImportStar()
public java.lang.String toString()
toString in class java.lang.Objectpublic void visit(NNodeVisitor v)
NNode