diff --git a/src/Analysis/Ast/Impl/Modules/Definitions/IModuleResolution.cs b/src/Analysis/Ast/Impl/Modules/Definitions/IModuleResolution.cs
index 950506809..caa66b332 100644
--- a/src/Analysis/Ast/Impl/Modules/Definitions/IModuleResolution.cs
+++ b/src/Analysis/Ast/Impl/Modules/Definitions/IModuleResolution.cs
@@ -13,6 +13,7 @@
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
+using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Python.Analysis.Core.DependencyResolution;
@@ -50,6 +51,12 @@ public interface IModuleResolution {
///
IPythonModule GetImportedModule(string name);
+ ///
+ /// Sets user search paths. This changes .
+ ///
+ /// Added roots.
+ IEnumerable SetUserSearchPaths(in IEnumerable searchPaths);
+
Task ReloadAsync(CancellationToken token = default);
}
}
diff --git a/src/Analysis/Ast/Impl/Modules/Resolution/MainModuleResolution.cs b/src/Analysis/Ast/Impl/Modules/Resolution/MainModuleResolution.cs
index 41f1a2d2f..039736b04 100644
--- a/src/Analysis/Ast/Impl/Modules/Resolution/MainModuleResolution.cs
+++ b/src/Analysis/Ast/Impl/Modules/Resolution/MainModuleResolution.cs
@@ -147,25 +147,25 @@ internal async Task LoadBuiltinTypesAsync(CancellationToken cancellationToken =
var builtinModuleNamesMember = BuiltinsModule.GetAnyMember("__builtin_module_names__");
if (builtinModuleNamesMember.TryGetConstant(out var s)) {
var builtinModuleNames = s.Split(',').Select(n => n.Trim());
- _pathResolver.SetBuiltins(builtinModuleNames);
+ PathResolver.SetBuiltins(builtinModuleNames);
}
}
public override async Task ReloadAsync(CancellationToken cancellationToken = default) {
ModuleCache = new ModuleCache(_interpreter, _services);
- _pathResolver = new PathResolver(_interpreter.LanguageVersion);
+ PathResolver = new PathResolver(_interpreter.LanguageVersion);
- var addedRoots = _pathResolver.SetRoot(_root);
+ var addedRoots = PathResolver.SetRoot(_root);
ReloadModulePaths(addedRoots);
var interpreterPaths = await GetSearchPathsAsync(cancellationToken);
- addedRoots = _pathResolver.SetInterpreterSearchPaths(interpreterPaths);
+ addedRoots = PathResolver.SetInterpreterSearchPaths(interpreterPaths);
ReloadModulePaths(addedRoots);
cancellationToken.ThrowIfCancellationRequested();
- addedRoots = _pathResolver.SetUserSearchPaths(_interpreter.Configuration.SearchPaths);
+ addedRoots = SetUserSearchPaths(_interpreter.Configuration.SearchPaths);
ReloadModulePaths(addedRoots);
}
diff --git a/src/Analysis/Ast/Impl/Modules/Resolution/ModuleResolutionBase.cs b/src/Analysis/Ast/Impl/Modules/Resolution/ModuleResolutionBase.cs
index d7e01b06d..b46a3b0ca 100644
--- a/src/Analysis/Ast/Impl/Modules/Resolution/ModuleResolutionBase.cs
+++ b/src/Analysis/Ast/Impl/Modules/Resolution/ModuleResolutionBase.cs
@@ -38,7 +38,7 @@ internal abstract class ModuleResolutionBase {
protected readonly bool _requireInitPy;
protected string _root;
- protected PathResolver _pathResolver;
+ protected PathResolver PathResolver { get; set; }
protected InterpreterConfiguration Configuration => _interpreter.Configuration;
@@ -58,7 +58,7 @@ protected ModuleResolutionBase(string root, IServiceContainer services) {
///
/// Path resolver providing file resolution in module imports.
///
- public PathResolverSnapshot CurrentPathResolver => _pathResolver.CurrentSnapshot;
+ public PathResolverSnapshot CurrentPathResolver => PathResolver.CurrentSnapshot;
///
/// Builtins module.
@@ -68,7 +68,7 @@ protected ModuleResolutionBase(string root, IServiceContainer services) {
public abstract Task ReloadAsync(CancellationToken cancellationToken = default);
protected abstract Task DoImportAsync(string name, CancellationToken cancellationToken = default);
- public IReadOnlyCollection GetPackagesFromDirectory(string searchPath, CancellationToken cancellationToken) {
+ public IReadOnlyCollection GetPackagesFromDirectory(string searchPath, CancellationToken cancellationToken) {
return ModulePath.GetModulesInPath(
searchPath,
recurse: false,
@@ -77,10 +77,18 @@ public IReadOnlyCollection GetPackagesFromDirectory(string searchPath, C
).Select(mp => mp.ModuleName).Where(n => !string.IsNullOrEmpty(n)).TakeWhile(_ => !cancellationToken.IsCancellationRequested).ToList();
}
- public IPythonModule GetImportedModule(string name)
- => _modules.TryGetValue(name, out var module) ? module : null;
+ public IPythonModule GetImportedModule(string name) {
+ var module = _interpreter.ModuleResolution.GetSpecializedModule(name);
+ if (module != null) {
+ return module;
+ }
+ return _modules.TryGetValue(name, out module) ? module : null;
+ }
+
+ public IEnumerable SetUserSearchPaths(in IEnumerable searchPaths)
+ => PathResolver.SetUserSearchPaths(searchPaths);
- public void AddModulePath(string path) => _pathResolver.TryAddModulePath(path, out var _);
+ public void AddModulePath(string path) => PathResolver.TryAddModulePath(path, out var _);
public ModulePath FindModule(string filePath) {
var bestLibraryPath = string.Empty;
@@ -207,7 +215,7 @@ private async Task TryImportModuleAsync(string name, Canc
protected void ReloadModulePaths(in IEnumerable rootPaths) {
foreach (var modulePath in rootPaths.Where(Directory.Exists).SelectMany(p => PathUtils.EnumerateFiles(p))) {
- _pathResolver.TryAddModulePath(modulePath, out _);
+ PathResolver.TryAddModulePath(modulePath, out _);
}
}
diff --git a/src/Analysis/Ast/Impl/Modules/Resolution/TypeshedResolution.cs b/src/Analysis/Ast/Impl/Modules/Resolution/TypeshedResolution.cs
index ef8850ff1..51376d6d6 100644
--- a/src/Analysis/Ast/Impl/Modules/Resolution/TypeshedResolution.cs
+++ b/src/Analysis/Ast/Impl/Modules/Resolution/TypeshedResolution.cs
@@ -65,12 +65,12 @@ protected override async Task DoImportAsync(string name, Cancella
}
public override Task ReloadAsync(CancellationToken cancellationToken = default) {
- _pathResolver = new PathResolver(_interpreter.LanguageVersion);
+ PathResolver = new PathResolver(_interpreter.LanguageVersion);
- var addedRoots = _pathResolver.SetRoot(_root);
+ var addedRoots = PathResolver.SetRoot(_root);
ReloadModulePaths(addedRoots);
- addedRoots = _pathResolver.SetInterpreterSearchPaths(_typeStubPaths);
+ addedRoots = PathResolver.SetInterpreterSearchPaths(_typeStubPaths);
ReloadModulePaths(addedRoots);
cancellationToken.ThrowIfCancellationRequested();
diff --git a/src/Analysis/Ast/Test/AnalysisTestBase.cs b/src/Analysis/Ast/Test/AnalysisTestBase.cs
index 3d69856dc..15a2795c7 100644
--- a/src/Analysis/Ast/Test/AnalysisTestBase.cs
+++ b/src/Analysis/Ast/Test/AnalysisTestBase.cs
@@ -100,6 +100,12 @@ protected async Task CreateServicesAsync(string root, Interpret
return sm;
}
+ protected async Task CreateServicesAsync(InterpreterConfiguration configuration, string modulePath) {
+ modulePath = modulePath ?? TestData.GetDefaultModulePath();
+ var moduleDirectory = Path.GetDirectoryName(modulePath);
+ await CreateServicesAsync(moduleDirectory, configuration);
+ }
+
protected Task GetAnalysisAsync(string code, PythonLanguageVersion version, string modulePath = null)
=> GetAnalysisAsync(code, PythonVersions.GetRequiredCPythonConfiguration(version), modulePath);
@@ -129,7 +135,7 @@ protected async Task GetAnalysisAsync(
string modulePath = null) {
var moduleUri = modulePath != null ? new Uri(modulePath) : TestData.GetDefaultModuleUri();
- modulePath = modulePath ?? TestData .GetDefaultModulePath();
+ modulePath = modulePath ?? TestData.GetDefaultModulePath();
moduleName = moduleName ?? Path.GetFileNameWithoutExtension(modulePath);
IDocument doc;
diff --git a/src/LanguageServer/Impl/Completion/ImportCompletion.cs b/src/LanguageServer/Impl/Completion/ImportCompletion.cs
index 3b458dc68..66131c709 100644
--- a/src/LanguageServer/Impl/Completion/ImportCompletion.cs
+++ b/src/LanguageServer/Impl/Completion/ImportCompletion.cs
@@ -158,8 +158,6 @@ private static CompletionResult GetResultFromSearch(IImportSearchResult importSe
break;
case PackageImport packageImports:
return new CompletionResult(packageImports.Modules
- .Select(m => mres.GetImportedModule(m.FullName))
- .ExcludeDefault()
.Select(m => CompletionItemSource.CreateCompletionItem(m.Name, CompletionItemKind.Module))
.Prepend(CompletionItemSource.Star));
default:
diff --git a/src/LanguageServer/Test/CompletionTests.cs b/src/LanguageServer/Test/CompletionTests.cs
index f5a83b82a..e09d0548c 100644
--- a/src/LanguageServer/Test/CompletionTests.cs
+++ b/src/LanguageServer/Test/CompletionTests.cs
@@ -53,8 +53,8 @@ def method(self):
";
var analysis = await GetAnalysisAsync(code);
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
- var comps = (await cs.GetCompletionsAsync(analysis, new SourceLocation(8, 1))).Completions.ToArray();
- comps.Select(c => c.label).Should().Contain("C", "x", "y", "while", "for", "yield");
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(8, 1));
+ comps.Should().HaveLabels("C", "x", "y", "while", "for", "yield");
}
[TestMethod, Priority(0)]
@@ -65,8 +65,8 @@ public async Task StringMembers() {
";
var analysis = await GetAnalysisAsync(code);
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
- var comps = (await cs.GetCompletionsAsync(analysis, new SourceLocation(3, 3))).Completions.ToArray();
- comps.Select(c => c.label).Should().Contain(new[] { @"isupper", @"capitalize", @"split" });
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(3, 3));
+ comps.Should().HaveLabels(@"isupper", @"capitalize", @"split" );
}
[TestMethod, Priority(0)]
@@ -77,8 +77,8 @@ import datetime
";
var analysis = await GetAnalysisAsync(code);
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
- var comps = (await cs.GetCompletionsAsync(analysis, new SourceLocation(3, 19))).Completions.ToArray();
- comps.Select(c => c.label).Should().Contain(new[] { "now", @"tzinfo", @"ctime" });
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(3, 19));
+ comps.Should().HaveLabels("now", @"tzinfo", @"ctime");
}
[TestMethod, Priority(0)]
@@ -92,11 +92,11 @@ def method1(self): pass
";
var analysis = await GetAnalysisAsync(code);
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
- var comps = (await cs.GetCompletionsAsync(analysis, new SourceLocation(5, 4))).Completions.ToArray();
- comps.Select(c => c.label).Should().Contain(@"ABCDE");
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(5, 4));
+ comps.Should().HaveLabels(@"ABCDE");
- comps = (await cs.GetCompletionsAsync(analysis, new SourceLocation(6, 9))).Completions.ToArray();
- comps.Select(c => c.label).Should().Contain("method1");
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(6, 9));
+ comps.Should().HaveLabels("method1");
}
[DataRow(PythonLanguageVersion.V36, "value")]
diff --git a/src/LanguageServer/Test/ImportsTests.cs b/src/LanguageServer/Test/ImportsTests.cs
new file mode 100644
index 000000000..82ad89b28
--- /dev/null
+++ b/src/LanguageServer/Test/ImportsTests.cs
@@ -0,0 +1,301 @@
+// Copyright(c) Microsoft Corporation
+// All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the License); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+//
+// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
+// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
+// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+// MERCHANTABILITY OR NON-INFRINGEMENT.
+//
+// See the Apache Version 2.0 License for specific language governing
+// permissions and limitations under the License.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Microsoft.Python.Analysis;
+using Microsoft.Python.Analysis.Documents;
+using Microsoft.Python.Core.Text;
+using Microsoft.Python.LanguageServer.Completion;
+using Microsoft.Python.LanguageServer.Sources;
+using Microsoft.Python.LanguageServer.Tests.FluentAssertions;
+using Microsoft.Python.Parsing.Tests;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TestUtilities;
+
+namespace Microsoft.Python.LanguageServer.Tests {
+ [TestClass]
+ public class ImportsTests : LanguageServerTestBase {
+ public TestContext TestContext { get; set; }
+
+ [TestInitialize]
+ public void TestInitialize()
+ => TestEnvironmentImpl.TestInitialize($"{TestContext.FullyQualifiedTestClassName}.{TestContext.TestName}");
+
+ [TestCleanup]
+ public void Cleanup() => TestEnvironmentImpl.TestCleanup();
+
+ [TestMethod, Priority(0)]
+ public async Task ExplicitImplicitPackageMix() {
+ const string appCode = @"
+import projectA.foo
+import projectA.foo.bar
+import projectB.foo
+import projectB.foo.baz
+
+projectA.";
+
+ var appPath = TestData.GetTestSpecificPath("app.py");
+ var root = Path.GetDirectoryName(appPath);
+ var init1Path = Path.Combine(root, "projectA", "foo", "bar", "__init__.py");
+ var init2Path = Path.Combine(root, "projectA", "foo", "__init__.py");
+ var init3Path = Path.Combine(root, "projectB", "foo", "bar", "__init__.py");
+ var init4Path = Path.Combine(root, "projectB", "foo", "__init__.py");
+
+ await CreateServicesAsync(PythonVersions.LatestAvailable3X, appPath);
+ var rdt = Services.GetService();
+
+ rdt.OpenDocument(new Uri(init1Path), string.Empty);
+ rdt.OpenDocument(new Uri(init2Path), string.Empty);
+ rdt.OpenDocument(new Uri(init3Path), string.Empty);
+ rdt.OpenDocument(new Uri(init4Path), string.Empty);
+
+ var doc = rdt.OpenDocument(new Uri(appPath), appCode, appPath);
+ var analysis = await doc.GetAnalysisAsync();
+
+ var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(7, 10));
+ comps.Should().HaveLabels("foo");
+ }
+
+ [TestMethod, Priority(0)]
+ public async Task SysModuleChain() {
+ const string content1 = @"import module2.mod as mod
+mod.";
+ const string content2 = @"import module3 as mod";
+ const string content3 = @"import sys
+sys.modules['module2.mod'] = None
+VALUE = 42";
+
+ var uri1 = await TestData.CreateTestSpecificFileAsync("module1.py", content1);
+ var uri2 = await TestData.CreateTestSpecificFileAsync("module2.py", content2);
+ var uri3 = await TestData.CreateTestSpecificFileAsync("module3.py", content3);
+
+ var root = TestData.GetTestSpecificRootUri().AbsolutePath;
+ await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
+ var rdt = Services.GetService();
+
+ var doc1 = rdt.OpenDocument(uri1, content1);
+ rdt.OpenDocument(uri2, content2);
+ rdt.OpenDocument(uri3, content3);
+
+ var analysis = await doc1.GetAnalysisAsync();
+
+ var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(2, 5));
+ comps.Should().HaveLabels("VALUE");
+ }
+
+ [TestMethod, Priority(0)]
+ public async Task SysModuleChain_SingleOpen() {
+ const string content = @"import module1.mod as mod
+mod.";
+ await TestData.CreateTestSpecificFileAsync("module1.py", @"import module2 as mod");
+ await TestData.CreateTestSpecificFileAsync("module2.py", @"import sys
+sys.modules['module1.mod'] = None
+VALUE = 42");
+
+ var root = TestData.GetTestSpecificRootUri().AbsolutePath;
+ await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
+ var rdt = Services.GetService();
+
+ var doc = rdt.OpenDocument(TestData.GetDefaultModuleUri(), content);
+ var analysis = await doc.GetAnalysisAsync();
+
+ var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(2, 5));
+ comps.Should().HaveLabels("VALUE");
+ }
+
+ [TestMethod, Priority(0)]
+ public async Task UncSearchPaths() {
+ const string module1Path = @"q:\Folder\package\module1.py";
+ const string module2Path = @"\\machine\share\package\module2.py";
+
+ const string appCode1 = @"from package import ";
+ const string appCode2 = @"from package import module1, module2
+module1.
+module2.";
+ var appPath = TestData.GetTestSpecificPath("app.py");
+ var root = Path.GetDirectoryName(appPath);
+
+ await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
+ var rdt = Services.GetService();
+ var interpreter = Services.GetService();
+ interpreter.ModuleResolution.SetUserSearchPaths(new[] { @"q:\Folder\", @"\\machine\share\" });
+
+ rdt.OpenDocument(new Uri(module1Path), "X = 42");
+ rdt.OpenDocument(new Uri(module2Path), "Y = 6 * 9");
+
+ var doc = rdt.OpenDocument(new Uri(appPath), appCode1);
+ var analysis = await doc.GetAnalysisAsync();
+
+ var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(1, 21));
+ comps.Should().HaveLabels("module1", "module2");
+
+ doc.Update(new[] {
+ new DocumentChange {
+ InsertedText = appCode2,
+ ReplacedSpan = new SourceSpan(1, 1, 1, 21)
+ }
+ });
+
+ analysis = await doc.GetAnalysisAsync();
+
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(2, 9));
+ comps.Should().HaveLabels("X").And.NotContainLabels("Y");
+
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(3, 9));
+ comps.Should().HaveLabels("Y").And.NotContainLabels("X");
+ }
+
+ [TestMethod, Priority(0)]
+ public async Task UserSearchPathsInsideWorkspace() {
+ var folder1 = TestData.GetTestSpecificPath("folder1");
+ var folder2 = TestData.GetTestSpecificPath("folder2");
+ var packageInFolder1 = Path.Combine(folder1, "package");
+ var packageInFolder2 = Path.Combine(folder2, "package");
+ var module1Path = Path.Combine(packageInFolder1, "module1.py");
+ var module2Path = Path.Combine(packageInFolder2, "module2.py");
+ const string module1Content = @"class A():
+ @staticmethod
+ def method1():
+ pass";
+ const string module2Content = @"class B():
+ @staticmethod
+ def method2():
+ pass";
+ const string mainContent = @"from package import module1 as mod1, module2 as mod2
+mod1.
+mod2.
+mod1.A.
+mod2.B.";
+ var root = Path.GetDirectoryName(folder1);
+ await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
+
+ var rdt = Services.GetService();
+ var interpreter = Services.GetService();
+ interpreter.ModuleResolution.SetUserSearchPaths(new[] { folder1, folder2 });
+
+ rdt.OpenDocument(new Uri(module1Path), module1Content);
+ rdt.OpenDocument(new Uri(module2Path), module2Content);
+
+ var mainPath = Path.Combine(root, "main.py");
+ var doc = rdt.OpenDocument(new Uri(mainPath), mainContent);
+ var analysis = await doc.GetAnalysisAsync();
+
+ var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(2, 6));
+ comps.Should().HaveLabels("A").And.NotContainLabels("B");
+
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(3, 6));
+ comps.Should().HaveLabels("B").And.NotContainLabels("A");
+
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(4, 8));
+ comps.Should().HaveLabels("method1");
+
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(5, 8));
+ comps.Should().HaveLabels("method2");
+ }
+
+ [TestMethod, Priority(0)]
+ public async Task PackageModuleImport() {
+ const string appCode = @"
+import package.sub_package.module1
+import package.sub_package.module2
+
+package.
+package.sub_package.
+package.sub_package.module1.
+package.sub_package.module2.";
+
+ var appPath = TestData.GetTestSpecificPath("app.py");
+ var root = Path.GetDirectoryName(appPath);
+ await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
+ var rdt = Services.GetService();
+
+ var module1Path = Path.Combine(root, "package", "sub_package", "module1.py");
+ var module2Path = Path.Combine(root, "package", "sub_package", "module2.py");
+
+ rdt.OpenDocument(new Uri(module1Path), "X = 42");
+ rdt.OpenDocument(new Uri(module2Path), "Y = 6 * 9");
+
+ var doc = rdt.OpenDocument(new Uri(appPath), appCode);
+ var analysis = await doc.GetAnalysisAsync();
+
+ var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(5, 9));
+ comps.Should().OnlyHaveLabels("sub_package");
+
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(6, 21));
+ comps.Should().OnlyHaveLabels("module1", "module2");
+
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(7, 29));
+ comps.Should().HaveLabels("X").And.NotContainLabels("Y");
+
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(8, 29));
+ comps.Should().HaveLabels("Y").And.NotContainLabels("X");
+ }
+
+ [TestMethod, Priority(0)]
+ public async Task TypingModule() {
+ var analysis = await GetAnalysisAsync(@"from typing import ");
+ var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(1, 20));
+ comps.Should().HaveLabels("TypeVar", "List", "Dict", "Union");
+ }
+
+ [DataRow(@"from package import sub_package; import package.sub_package.module1")]
+ [DataRow(@"import package.sub_package.module1; from package import sub_package")]
+ [DataRow(@"from package import sub_package; from package.sub_package import module")]
+ [DataRow(@"from package.sub_package import module; from package import sub_package")]
+ [Ignore("Not yet implemented")]
+ [TestMethod, Priority(0)]
+ public async Task FromImport_ModuleAffectsPackage(string appCodeImport) {
+ var appCode1 = appCodeImport + Environment.NewLine + "sub_package.";
+ var appCode2 = appCodeImport + Environment.NewLine + "sub_package.module.";
+
+ var appPath = TestData.GetTestSpecificPath("app.py");
+ var root = Path.GetDirectoryName(appPath);
+ await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
+ var rdt = Services.GetService();
+
+ var modulePath = Path.Combine(root, "package", "sub_package", "module.py");
+
+ rdt.OpenDocument(new Uri(modulePath), "X = 42");
+ var doc = rdt.OpenDocument(new Uri(appPath), appCode1);
+ var analysis = await doc.GetAnalysisAsync();
+
+ var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
+ var comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(2, 13));
+ comps.Should().OnlyHaveLabels("module");
+
+ doc.Update(new [] {
+ new DocumentChange {
+ InsertedText = appCode2,
+ ReplacedSpan = new SourceSpan(1, 1, 2, 13)
+ }
+ });
+
+ analysis = await doc.GetAnalysisAsync();
+ comps = await cs.GetCompletionsAsync(analysis, new SourceLocation(2, 21));
+ comps.Should().HaveLabels("X");
+ }
+ }
+}