Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Commit c66e404

Browse files
author
Mikhail Arkhipov
authored
Add submodules to completion list (#1896)
* Remove stale reference * Don't suppress LHS diagnostics on augmented assign * Revert "Don't suppress LHS diagnostics on augmented assign" This reverts commit 6109ac7. * Escape [ and ] * PR feedback * Add imported submodules to completions * Fix test
1 parent 4357545 commit c66e404

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

src/Analysis/Ast/Impl/Modules/PythonModule.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
using Microsoft.Python.Analysis.Analyzer;
2525
using Microsoft.Python.Analysis.Analyzer.Evaluation;
2626
using Microsoft.Python.Analysis.Analyzer.Handlers;
27+
using Microsoft.Python.Analysis.Core.DependencyResolution;
2728
using Microsoft.Python.Analysis.Dependencies;
2829
using Microsoft.Python.Analysis.Diagnostics;
2930
using Microsoft.Python.Analysis.Documents;
@@ -148,9 +149,26 @@ public virtual string Documentation {
148149
#endregion
149150

150151
#region IMemberContainer
151-
public virtual IMember GetMember(string name) => GlobalScope.Variables[name]?.Value;
152152

153-
public virtual IEnumerable<string> GetMemberNames() => GlobalScope.GetExportableVariableNames();
153+
public virtual IMember GetMember(string name) {
154+
var v = GlobalScope.Variables[name]?.Value;
155+
if (v == null) {
156+
var mres = Interpreter.ModuleResolution;
157+
var result = mres.CurrentPathResolver.FindImports(FilePath, Enumerable.Repeat(name, 1), 0, false);
158+
if (result is ModuleImport moduleImports) {
159+
v = mres.GetImportedModule(moduleImports.FullName);
160+
}
161+
}
162+
return v;
163+
}
164+
165+
public virtual IEnumerable<string> GetMemberNames() {
166+
var names = GlobalScope.GetExportableVariableNames();
167+
// Get submodules since they may not be explicitly exported
168+
// and yet are available. Consider 'pandas.io'.
169+
var mi = Interpreter.ModuleResolution.CurrentPathResolver.GetModuleImportFromModuleName(Name);
170+
return names.Concat(mi?.GetChildrenNames() ?? Enumerable.Empty<string>()).Distinct();
171+
}
154172

155173
#endregion
156174

src/LanguageServer/Impl/Completion/ImportCompletion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ private static CompletionResult GetResultFromImportSearch(IImportSearchResult im
194194
break;
195195
}
196196

197-
if (name != null && !memberNames.Contains(name)) {
197+
if (name != null && !completions.Any(c => c.label == name && c.kind == CompletionItemKind.Module)) {
198198
completions.Add(CompletionItemSource.CreateCompletionItem(name, CompletionItemKind.Module));
199199
}
200200
}

src/LanguageServer/Test/CompletionTests.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,7 @@ public async Task FromDotInRootWithInitPy() {
10171017

10181018
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion, Services);
10191019
var result = cs.GetCompletions(analysis, new SourceLocation(1, 7));
1020-
result.Should().OnlyHaveLabels("__dict__", "__file__", "__doc__", "__package__", "__debug__", "__name__", "__path__", "__spec__");
1020+
result.Should().OnlyHaveLabels("__dict__", "__file__", "__doc__", "__package__", "__debug__", "__name__", "__path__", "__spec__", "module1");
10211021
}
10221022

10231023
[TestMethod, Priority(0)]
@@ -1511,5 +1511,39 @@ def print_EQ2(cls):
15111511
result = cs.GetCompletions(analysis, new SourceLocation(12, 13));
15121512
result.Should().HaveLabels("__EQ", "__NOT_EQ", "__OP_LIST");
15131513
}
1514+
1515+
[DataTestMethod, Priority(0)]
1516+
[DataRow(true)]
1517+
[DataRow(false)]
1518+
public async Task ImplicitSubmodule(bool imported) {
1519+
var appUri = TestData.GetTestSpecificUri("app.py");
1520+
await TestData.CreateTestSpecificFileAsync(
1521+
Path.Combine("package", "__init__.py"),
1522+
imported ? "import package.m1 as m2" : string.Empty
1523+
);
1524+
await TestData.CreateTestSpecificFileAsync(Path.Combine("package", "m1", "__init__.py"), "x = 1");
1525+
1526+
await CreateServicesAsync(PythonVersions.LatestAvailable3X);
1527+
var rdt = Services.GetService<IRunningDocumentTable>();
1528+
const string code = @"
1529+
import package
1530+
package.
1531+
package.m1.
1532+
";
1533+
var doc = rdt.OpenDocument(appUri, code);
1534+
1535+
await Services.GetService<IPythonAnalyzer>().WaitForCompleteAnalysisAsync();
1536+
var analysis = await doc.GetAnalysisAsync(Timeout.Infinite);
1537+
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion, Services);
1538+
1539+
var result = cs.GetCompletions(analysis, new SourceLocation(3, 9));
1540+
result.Should().HaveLabels("m1");
1541+
result = cs.GetCompletions(analysis, new SourceLocation(4, 12));
1542+
if (imported) {
1543+
result.Should().HaveLabels("x");
1544+
} else {
1545+
result.Should().NotContainLabels("x");
1546+
}
1547+
}
15141548
}
15151549
}

0 commit comments

Comments
 (0)