Skip to content

Commit d6175bb

Browse files
committed
fix: Resolve the package of class/module symbols from the declaring file
typeToSymbol resolved the package of a class or module type with getPackageInfo(node, ...), which classifies by the file containing the reference. Every reference emitted while visiting a project file is in the project, so any class reached through an alias declaration (`pkg.Thing`, or `Thing` re-exported from a submodule) was emitted under the project package instead of the package that declares it. Functions already used guessPackage(decl.moduleName, decl.path) and were correct. Use guessPackage with the type's own moduleName and filePath for classes, modules and TypeVar bounds, falling back to a local symbol when the package is unknown, as getFunctionSymbol does. Adds a snapshot test with a fake `widgets` package exposed through extraPaths and listed in testEnv.json, so references to classes re-exported from a submodule are checked against the dependency's package.
1 parent 8b60bbc commit d6175bb

7 files changed

Lines changed: 87 additions & 3 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import widgets
2+
from widgets import Widget
3+
4+
5+
def render(name: str, width: int) -> str:
6+
widget: widgets.Widget = widgets.Widget(name, width=width)
7+
try:
8+
return widgets.Widget.render(widget, prefix="> ")
9+
except widgets.WidgetError:
10+
return ""
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"pythonVersion": "3.11",
3+
"extraPaths": ["../../site-packages"]
4+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# < definition scip-python python snapshot-util 0.1 external_class/__init__:
2+
3+
import widgets
4+
# ^^^^^^^ reference widgets 1.2.3 widgets/__init__:
5+
from widgets import Widget
6+
# ^^^^^^^ reference widgets 1.2.3 widgets/__init__:
7+
# ^^^^^^ reference widgets 1.2.3 `widgets.models`/Widget#
8+
9+
10+
def render(name: str, width: int) -> str:
11+
# ^^^^^^ definition snapshot-util 0.1 external_class/render().
12+
# ^^^^ definition snapshot-util 0.1 external_class/render().(name)
13+
# ^^^ reference python-stdlib 3.11 builtins/str#
14+
# ^^^^^ definition snapshot-util 0.1 external_class/render().(width)
15+
# ^^^ reference python-stdlib 3.11 builtins/int#
16+
# ^^^ reference python-stdlib 3.11 builtins/str#
17+
widget: widgets.Widget = widgets.Widget(name, width=width)
18+
# ^^^^^^ definition local 0
19+
# ^^^^^^^ reference widgets 1.2.3 widgets/__init__:
20+
# ^^^^^^ reference widgets 1.2.3 `widgets.models`/Widget#
21+
# ^^^^^^^ reference widgets 1.2.3 widgets/__init__:
22+
# ^^^^^^ reference widgets 1.2.3 `widgets.models`/Widget#
23+
# ^^^^ reference snapshot-util 0.1 external_class/render().(name)
24+
# ^^^^^ reference widgets 1.2.3 `widgets.models`/Widget#__init__().(width)
25+
# ^^^^^ reference snapshot-util 0.1 external_class/render().(width)
26+
try:
27+
return widgets.Widget.render(widget, prefix="> ")
28+
# ^^^^^^^ reference widgets 1.2.3 widgets/__init__:
29+
# ^^^^^^ reference widgets 1.2.3 `widgets.models`/Widget#
30+
# ^^^^^^ reference widgets 1.2.3 `widgets.models`/Widget#render().
31+
# ^^^^^^ reference local 0
32+
# ^^^^^^ reference widgets 1.2.3 `widgets.models`/Widget#render().(prefix)
33+
except widgets.WidgetError:
34+
# ^^^^^^^ reference widgets 1.2.3 widgets/__init__:
35+
# ^^^^^^^^^^^ reference widgets 1.2.3 `widgets.models`/WidgetError#
36+
return ""
37+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .models import Widget, WidgetError
2+
3+
__all__ = ["Widget", "WidgetError"]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class WidgetError(Exception):
2+
pass
3+
4+
5+
class Widget:
6+
def __init__(self, name: str, width: int = 0) -> None:
7+
self.name = name
8+
self.width = width
9+
10+
def render(self, prefix: str = "") -> str:
11+
return prefix + self.name

‎packages/pyright-scip/snapshots/testEnv.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,10 @@
1313
"name": "sqlparse",
1414
"version": "0.4.2",
1515
"files": []
16+
},
17+
{
18+
"name": "widgets",
19+
"version": "1.2.3",
20+
"files": ["widgets/__init__.py", "widgets/models.py"]
1621
}
1722
]

‎packages/pyright-scip/src/treeVisitor.ts‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,13 @@ export class TreeVisitor extends ParseTreeWalker {
11231123
const bound = typeVar.details.boundType! as ClassType;
11241124

11251125
return this.getSymbolOnce(node, () => {
1126-
const pythonPackage = this.getPackageInfo(node, bound.details.moduleName)!;
1126+
const pythonPackage = this.guessPackage(
1127+
bound.details.moduleName,
1128+
bound.details.filePath
1129+
);
1130+
if (!pythonPackage) {
1131+
return ScipSymbol.local(this.counter.next());
1132+
}
11271133
let symbol = Symbols.makeTerm(
11281134
Symbols.makeType(
11291135
Symbols.makeModule(pythonPackage, bound.details.moduleName),
@@ -1343,7 +1349,12 @@ export class TreeVisitor extends ParseTreeWalker {
13431349
}
13441350
return this.getFunctionSymbol(decl);
13451351
} else if (Types.isClass(typeObj)) {
1346-
const pythonPackage = this.getPackageInfo(node, typeObj.details.moduleName)!;
1352+
// The package comes from the declaring file. `node` is the referencing
1353+
// site, which is in the project whenever a project file is visited.
1354+
const pythonPackage = this.guessPackage(typeObj.details.moduleName, typeObj.details.filePath);
1355+
if (!pythonPackage) {
1356+
return ScipSymbol.local(this.counter.next());
1357+
}
13471358
return Symbols.makeClass(pythonPackage, typeObj.details.moduleName, node.value);
13481359
} else if (Types.isClassInstance(typeObj)) {
13491360
typeObj = typeObj as ClassType;
@@ -1352,7 +1363,10 @@ export class TreeVisitor extends ParseTreeWalker {
13521363
} else if (Types.isTypeVar(typeObj)) {
13531364
throw 'typevar';
13541365
} else if (Types.isModule(typeObj)) {
1355-
const pythonPackage = this.getPackageInfo(node, typeObj.moduleName)!;
1366+
const pythonPackage = this.guessPackage(typeObj.moduleName, typeObj.filePath);
1367+
if (!pythonPackage) {
1368+
return ScipSymbol.local(this.counter.next());
1369+
}
13561370
return Symbols.makeModuleInit(pythonPackage, typeObj.moduleName);
13571371
} else if (Types.isOverloadedFunction(typeObj)) {
13581372
if (!typeObj.overloads) {

0 commit comments

Comments
 (0)