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

Commit c33774d

Browse files
AlexanderSherMikhail Arkhipov
authored and
Mikhail Arkhipov
committed
Fix #1156: Unresolved import for builtin packages after reload (#1496)
* Fix #1156: Unresolved import for builtin packages after reload * Address CR comments
1 parent b9c4d88 commit c33774d

File tree

11 files changed

+166
-181
lines changed

11 files changed

+166
-181
lines changed

src/Analysis/Ast/Impl/Analyzer/PythonInterpreter.cs

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace Microsoft.Python.Analysis.Analyzer {
3333
public sealed class PythonInterpreter : IPythonInterpreter {
3434
private MainModuleResolution _moduleResolution;
3535
private TypeshedResolution _stubResolution;
36+
private IPythonType _unknownType;
3637
private readonly object _lock = new object();
3738

3839
private readonly Dictionary<BuiltinTypeId, IPythonType> _builtinTypes = new Dictionary<BuiltinTypeId, IPythonType>();
@@ -42,30 +43,20 @@ private PythonInterpreter(InterpreterConfiguration configuration) {
4243
LanguageVersion = Configuration.Version.ToLanguageVersion();
4344
}
4445

45-
private async Task LoadBuiltinTypesAsync(string root, IServiceManager sm, CancellationToken cancellationToken = default) {
46+
private async Task InitializeAsync(string root, IServiceManager sm, CancellationToken cancellationToken = default) {
4647
cancellationToken.ThrowIfCancellationRequested();
4748

4849
sm.AddService(this);
4950
_moduleResolution = new MainModuleResolution(root, sm);
50-
_stubResolution = new TypeshedResolution(sm);
51-
52-
await _moduleResolution.InitializeAsync(cancellationToken);
53-
await _stubResolution.InitializeAsync(cancellationToken);
54-
55-
lock (_lock) {
56-
var builtinModule = _moduleResolution.CreateBuiltinsModule();
57-
_builtinTypes[BuiltinTypeId.NoneType]
58-
= new PythonType("NoneType", new Location(builtinModule), string.Empty, BuiltinTypeId.NoneType);
59-
_builtinTypes[BuiltinTypeId.Unknown]
60-
= UnknownType = new PythonType("Unknown", new Location(builtinModule), string.Empty);
61-
}
62-
63-
await _moduleResolution.LoadBuiltinTypesAsync(cancellationToken);
51+
_stubResolution = new TypeshedResolution(Configuration.TypeshedPath, sm);
52+
53+
await _stubResolution.ReloadAsync(cancellationToken);
54+
await _moduleResolution.ReloadAsync(cancellationToken);
6455
}
6556

6657
public static async Task<IPythonInterpreter> CreateAsync(InterpreterConfiguration configuration, string root, IServiceManager sm, CancellationToken cancellationToken = default) {
6758
var pi = new PythonInterpreter(configuration);
68-
await pi.LoadBuiltinTypesAsync(root, sm, cancellationToken);
59+
await pi.InitializeAsync(root, sm, cancellationToken);
6960

7061
// Specialize typing
7162
TypingModule.Create(sm);
@@ -92,6 +83,24 @@ public static async Task<IPythonInterpreter> CreateAsync(InterpreterConfiguratio
9283
/// </summary>
9384
public IModuleResolution TypeshedResolution => _stubResolution;
9485

86+
/// <summary>
87+
/// Unknown type.
88+
/// </summary>
89+
public IPythonType UnknownType {
90+
get {
91+
lock (_lock) {
92+
var type = _unknownType;
93+
if (type != null) {
94+
return type;
95+
}
96+
97+
_unknownType = new PythonType("Unknown", new Location(_moduleResolution.BuiltinsModule), string.Empty);
98+
_builtinTypes[BuiltinTypeId.Unknown] = _unknownType;
99+
return _unknownType;
100+
}
101+
}
102+
}
103+
95104
/// <summary>
96105
/// Gets a well known built-in type such as int, list, dict, etc...
97106
/// </summary>
@@ -106,31 +115,34 @@ public IPythonType GetBuiltinType(BuiltinTypeId id) {
106115
}
107116

108117
lock (_lock) {
109-
if (_builtinTypes.TryGetValue(id, out var res) && res != null) {
110-
return res;
118+
if (id == BuiltinTypeId.Unknown) {
119+
return UnknownType;
111120
}
112121

113-
var bm = ModuleResolution.BuiltinsModule;
114-
var typeName = id.GetTypeName(LanguageVersion);
115-
if (typeName != null) {
116-
res = bm.GetMember(typeName) as IPythonType;
122+
if (_builtinTypes.TryGetValue(id, out var type) && type != null) {
123+
return type;
117124
}
118125

119-
if (res == null) {
120-
res = bm.GetAnyMember("__{0}__".FormatInvariant(id)) as IPythonType;
121-
if (res == null) {
122-
return _builtinTypes[BuiltinTypeId.Unknown];
126+
if (id == BuiltinTypeId.NoneType) {
127+
type = new PythonType("NoneType", new Location(_moduleResolution.BuiltinsModule), string.Empty, BuiltinTypeId.NoneType);
128+
} else {
129+
var bm = _moduleResolution.BuiltinsModule;
130+
var typeName = id.GetTypeName(LanguageVersion);
131+
if (typeName != null) {
132+
type = _moduleResolution.BuiltinsModule.GetMember(typeName) as IPythonType;
133+
}
134+
135+
if (type == null) {
136+
type = bm.GetAnyMember("__{0}__".FormatInvariant(id)) as IPythonType;
137+
if (type == null) {
138+
return UnknownType;
139+
}
123140
}
124141
}
125142

126-
_builtinTypes[id] = res;
127-
return res;
143+
_builtinTypes[id] = type;
144+
return type;
128145
}
129146
}
130-
131-
/// <summary>
132-
/// Unknown type.
133-
/// </summary>
134-
public IPythonType UnknownType { get; private set; }
135147
}
136148
}

src/Analysis/Ast/Impl/Modules/Definitions/IModuleManagement.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ namespace Microsoft.Python.Analysis.Modules {
2626
/// Represents module resolution and search subsystem.
2727
/// </summary>
2828
public interface IModuleManagement: IModuleResolution {
29+
/// <summary>
30+
/// Builtins module name.
31+
/// </summary>
32+
string BuiltinModuleName { get; }
33+
34+
/// <summary>
35+
/// Builtins module.
36+
/// </summary>
37+
IBuiltinsPythonModule BuiltinsModule { get; }
38+
2939
/// <summary>
3040
/// Locates module by path.
3141
/// </summary>

src/Analysis/Ast/Impl/Modules/Definitions/IModuleResolution.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,6 @@ namespace Microsoft.Python.Analysis.Modules {
2323
/// Represents basic module resolution and search subsystem.
2424
/// </summary>
2525
public interface IModuleResolution {
26-
/// <summary>
27-
/// Builtins module name.
28-
/// </summary>
29-
string BuiltinModuleName { get; }
30-
31-
/// <summary>
32-
/// Builtins module.
33-
/// </summary>
34-
IBuiltinsPythonModule BuiltinsModule { get; }
35-
3626
/// <summary>
3727
/// Path resolver providing file resolution in module imports.
3828
/// </summary>

src/Analysis/Ast/Impl/Modules/Resolution/MainModuleResolution.cs

Lines changed: 70 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -33,41 +33,30 @@
3333
using Microsoft.Python.Core.Diagnostics;
3434
using Microsoft.Python.Core.IO;
3535
using Microsoft.Python.Core.OS;
36+
using Microsoft.Python.Core.Services;
3637

3738
namespace Microsoft.Python.Analysis.Modules.Resolution {
3839
internal sealed class MainModuleResolution : ModuleResolutionBase, IModuleManagement {
3940
private readonly ConcurrentDictionary<string, IPythonModule> _specialized = new ConcurrentDictionary<string, IPythonModule>();
41+
private readonly IUIService _ui;
4042
private IRunningDocumentTable _rdt;
4143

4244
private ImmutableArray<string> _userPaths = ImmutableArray<string>.Empty;
4345

4446
public MainModuleResolution(string root, IServiceContainer services)
45-
: base(root, services) { }
47+
: base(root, services) {
4648

47-
internal IBuiltinsPythonModule CreateBuiltinsModule() {
48-
if (BuiltinsModule == null) {
49-
// Initialize built-in
50-
var moduleName = BuiltinTypeId.Unknown.GetModuleName(_interpreter.LanguageVersion);
51-
52-
StubCache = _services.GetService<IStubCache>();
53-
var modulePath = StubCache.GetCacheFilePath(_interpreter.Configuration.InterpreterPath);
54-
55-
var b = new BuiltinsPythonModule(moduleName, modulePath, _services);
56-
BuiltinsModule = b;
57-
Modules[BuiltinModuleName] = new ModuleRef(b);
58-
}
59-
return BuiltinsModule;
49+
_ui = services.GetService<IUIService>();
6050
}
51+
52+
public string BuiltinModuleName => BuiltinTypeId.Unknown.GetModuleName(Interpreter.LanguageVersion);
6153

62-
internal async Task InitializeAsync(CancellationToken cancellationToken = default) {
63-
await ReloadAsync(cancellationToken);
64-
cancellationToken.ThrowIfCancellationRequested();
65-
}
54+
public IBuiltinsPythonModule BuiltinsModule { get; private set; }
6655

6756
protected override IPythonModule CreateModule(string name) {
6857
var moduleImport = CurrentPathResolver.GetModuleImportFromModuleName(name);
6958
if (moduleImport == null) {
70-
_log?.Log(TraceEventType.Verbose, "Import not found: ", name);
59+
Log?.Log(TraceEventType.Verbose, "Import not found: ", name);
7160
return null;
7261
}
7362

@@ -83,7 +72,7 @@ protected override IPythonModule CreateModule(string name) {
8372
// First check stub next to the module.
8473
if (!TryCreateModuleStub(name, moduleImport.ModulePath, out var stub)) {
8574
// If nothing found, try Typeshed.
86-
stub = _interpreter.TypeshedResolution.GetOrLoadModule(moduleImport.IsBuiltin ? name : moduleImport.FullName);
75+
stub = Interpreter.TypeshedResolution.GetOrLoadModule(moduleImport.IsBuiltin ? name : moduleImport.FullName);
8776
}
8877

8978
// If stub is created and its path equals to module, return that stub as module
@@ -92,16 +81,16 @@ protected override IPythonModule CreateModule(string name) {
9281
}
9382

9483
if (moduleImport.IsBuiltin) {
95-
_log?.Log(TraceEventType.Verbose, "Create built-in compiled (scraped) module: ", name, Configuration.InterpreterPath);
96-
return new CompiledBuiltinPythonModule(name, stub, _services);
84+
Log?.Log(TraceEventType.Verbose, "Create built-in compiled (scraped) module: ", name, Configuration.InterpreterPath);
85+
return new CompiledBuiltinPythonModule(name, stub, Services);
9786
}
9887

9988
if (moduleImport.IsCompiled) {
100-
_log?.Log(TraceEventType.Verbose, "Create compiled (scraped): ", moduleImport.FullName, moduleImport.ModulePath, moduleImport.RootPath);
101-
return new CompiledPythonModule(moduleImport.FullName, ModuleType.Compiled, moduleImport.ModulePath, stub, _services);
89+
Log?.Log(TraceEventType.Verbose, "Create compiled (scraped): ", moduleImport.FullName, moduleImport.ModulePath, moduleImport.RootPath);
90+
return new CompiledPythonModule(moduleImport.FullName, ModuleType.Compiled, moduleImport.ModulePath, stub, Services);
10291
}
10392

104-
_log?.Log(TraceEventType.Verbose, "Import: ", moduleImport.FullName, moduleImport.ModulePath);
93+
Log?.Log(TraceEventType.Verbose, "Import: ", moduleImport.FullName, moduleImport.ModulePath);
10594
// Module inside workspace == user code.
10695

10796
var mco = new ModuleCreationOptions {
@@ -114,24 +103,22 @@ protected override IPythonModule CreateModule(string name) {
114103
return GetRdt().AddModule(mco);
115104
}
116105

117-
private async Task<IReadOnlyList<PythonLibraryPath>> GetInterpreterSearchPathsAsync(CancellationToken cancellationToken = default) {
118-
if (!_fs.FileExists(Configuration.InterpreterPath)) {
119-
_log?.Log(TraceEventType.Warning, "Interpreter does not exist:", Configuration.InterpreterPath);
106+
private async Task<ImmutableArray<PythonLibraryPath>> GetInterpreterSearchPathsAsync(CancellationToken cancellationToken = default) {
107+
if (!FileSystem.FileExists(Configuration.InterpreterPath)) {
108+
Log?.Log(TraceEventType.Warning, "Interpreter does not exist:", Configuration.InterpreterPath);
120109
_ui?.ShowMessageAsync(Resources.InterpreterNotFound, TraceEventType.Error);
121-
return Array.Empty<PythonLibraryPath>();
110+
return ImmutableArray<PythonLibraryPath>.Empty;
122111
}
123112

124-
_log?.Log(TraceEventType.Information, "GetCurrentSearchPaths", Configuration.InterpreterPath);
113+
Log?.Log(TraceEventType.Information, "GetCurrentSearchPaths", Configuration.InterpreterPath);
125114
try {
126-
var fs = _services.GetService<IFileSystem>();
127-
var ps = _services.GetService<IProcessServices>();
128-
var paths = await PythonLibraryPath.GetSearchPathsAsync(Configuration, fs, ps, cancellationToken);
129-
cancellationToken.ThrowIfCancellationRequested();
130-
return paths.ToArray();
115+
var fs = Services.GetService<IFileSystem>();
116+
var ps = Services.GetService<IProcessServices>();
117+
return await PythonLibraryPath.GetSearchPathsAsync(Configuration, fs, ps, cancellationToken);
131118
} catch (InvalidOperationException ex) {
132-
_log?.Log(TraceEventType.Warning, "Exception getting search paths", ex);
119+
Log?.Log(TraceEventType.Warning, "Exception getting search paths", ex);
133120
_ui?.ShowMessageAsync(Resources.ExceptionGettingSearchPaths, TraceEventType.Error);
134-
return Array.Empty<PythonLibraryPath>();
121+
return ImmutableArray<PythonLibraryPath>.Empty;
135122
}
136123
}
137124

@@ -157,8 +144,8 @@ public IPythonModule SpecializeModule(string name, Func<string, IPythonModule> s
157144
public IPythonModule GetSpecializedModule(string name)
158145
=> _specialized.TryGetValue(name, out var module) ? module : null;
159146

160-
internal async Task LoadBuiltinTypesAsync(CancellationToken cancellationToken = default) {
161-
var analyzer = _services.GetService<IPythonAnalyzer>();
147+
internal async Task AddBuiltinTypesToPathResolverAsync(CancellationToken cancellationToken = default) {
148+
var analyzer = Services.GetService<IPythonAnalyzer>();
162149
await analyzer.GetAnalysisAsync(BuiltinsModule, -1, cancellationToken);
163150

164151
Check.InvalidOperation(!(BuiltinsModule.Analysis is EmptyAnalysis), "After await");
@@ -172,26 +159,6 @@ internal async Task LoadBuiltinTypesAsync(CancellationToken cancellationToken =
172159
}
173160
}
174161

175-
internal async Task ReloadSearchPaths(CancellationToken cancellationToken = default) {
176-
var ps = _services.GetService<IProcessServices>();
177-
178-
var paths = await GetInterpreterSearchPathsAsync(cancellationToken);
179-
var (interpreterPaths, userPaths) = PythonLibraryPath.ClassifyPaths(Root, _fs, paths, Configuration.SearchPaths);
180-
181-
InterpreterPaths = interpreterPaths.Select(p => p.Path);
182-
_userPaths = userPaths.Select(p => p.Path);
183-
184-
_log?.Log(TraceEventType.Information, "Interpreter search paths:");
185-
foreach (var s in InterpreterPaths) {
186-
_log?.Log(TraceEventType.Information, $" {s}");
187-
}
188-
189-
_log?.Log(TraceEventType.Information, "User search paths:");
190-
foreach (var s in _userPaths) {
191-
_log?.Log(TraceEventType.Information, $" {s}");
192-
}
193-
}
194-
195162
public async Task ReloadAsync(CancellationToken cancellationToken = default) {
196163
foreach (var uri in Modules
197164
.Where(m => m.Value.Value?.Name != BuiltinModuleName)
@@ -201,45 +168,78 @@ public async Task ReloadAsync(CancellationToken cancellationToken = default) {
201168
}
202169

203170
// Preserve builtins, they don't need to be reloaded since interpreter does not change.
204-
if (Modules.TryGetValue(BuiltinModuleName, out var builtins)) {
205-
Modules.Clear();
206-
Modules[BuiltinModuleName] = builtins;
207-
}
171+
var builtinsIsCreated = Modules.TryGetValue(BuiltinModuleName, out var builtinsRef);
172+
Modules.Clear();
208173

209174
await ReloadSearchPaths(cancellationToken);
210175

211-
PathResolver = new PathResolver(_interpreter.LanguageVersion, Root, InterpreterPaths, _userPaths);
176+
PathResolver = new PathResolver(Interpreter.LanguageVersion, Root, InterpreterPaths, _userPaths);
212177

213178
var addedRoots = new HashSet<string> { Root };
214179
addedRoots.UnionWith(InterpreterPaths);
215180
addedRoots.UnionWith(_userPaths);
216181
ReloadModulePaths(addedRoots);
182+
183+
if (!builtinsIsCreated) {
184+
var builtinsModule = CreateBuiltinsModule(Services, Interpreter, StubCache);
185+
BuiltinsModule = builtinsModule;
186+
builtinsRef = new ModuleRef(builtinsModule);
187+
}
188+
189+
Modules[BuiltinModuleName] = builtinsRef;
190+
await AddBuiltinTypesToPathResolverAsync(cancellationToken);
191+
}
192+
193+
private static IBuiltinsPythonModule CreateBuiltinsModule(IServiceContainer services, IPythonInterpreter interpreter, IStubCache stubCache) {
194+
var moduleName = BuiltinTypeId.Unknown.GetModuleName(interpreter.LanguageVersion);
195+
var modulePath = stubCache.GetCacheFilePath(interpreter.Configuration.InterpreterPath);
196+
return new BuiltinsPythonModule(moduleName, modulePath, services);
197+
}
198+
199+
private async Task ReloadSearchPaths(CancellationToken cancellationToken = default) {
200+
var paths = await GetInterpreterSearchPathsAsync(cancellationToken);
201+
var (interpreterPaths, userPaths) = PythonLibraryPath.ClassifyPaths(Root, FileSystem, paths, Configuration.SearchPaths);
202+
203+
InterpreterPaths = interpreterPaths.Select(p => p.Path);
204+
_userPaths = userPaths.Select(p => p.Path);
205+
206+
if (Log != null) {
207+
Log.Log(TraceEventType.Information, "Interpreter search paths:");
208+
foreach (var s in InterpreterPaths) {
209+
Log.Log(TraceEventType.Information, $" {s}");
210+
}
211+
212+
Log.Log(TraceEventType.Information, "User search paths:");
213+
foreach (var s in _userPaths) {
214+
Log.Log(TraceEventType.Information, $" {s}");
215+
}
216+
}
217217
}
218218

219219
public bool TryAddModulePath(in string path, in long fileSize, in bool allowNonRooted, out string fullModuleName)
220220
=> PathResolver.TryAddModulePath(path, fileSize, allowNonRooted, out fullModuleName);
221221

222222
// For tests
223223
internal void AddUnimportableModule(string moduleName)
224-
=> Modules[moduleName] = new ModuleRef(new SentinelModule(moduleName, _services));
224+
=> Modules[moduleName] = new ModuleRef(new SentinelModule(moduleName, Services));
225225

226226
private bool TryCreateModuleStub(string name, string modulePath, out IPythonModule module) {
227227
// First check stub next to the module.
228228
if (!string.IsNullOrEmpty(modulePath)) {
229229
var pyiPath = Path.ChangeExtension(modulePath, "pyi");
230-
if (_fs.FileExists(pyiPath)) {
231-
module = new StubPythonModule(name, pyiPath, false, _services);
230+
if (FileSystem.FileExists(pyiPath)) {
231+
module = new StubPythonModule(name, pyiPath, false, Services);
232232
return true;
233233
}
234234
}
235235

236236
// Try location of stubs that are in a separate folder next to the package.
237-
var stubPath = CurrentPathResolver.GetPossibleModuleStubPaths(name).FirstOrDefault(p => _fs.FileExists(p));
238-
module = !string.IsNullOrEmpty(stubPath) ? new StubPythonModule(name, stubPath, false, _services) : null;
237+
var stubPath = CurrentPathResolver.GetPossibleModuleStubPaths(name).FirstOrDefault(p => FileSystem.FileExists(p));
238+
module = !string.IsNullOrEmpty(stubPath) ? new StubPythonModule(name, stubPath, false, Services) : null;
239239
return module != null;
240240
}
241241

242242
private IRunningDocumentTable GetRdt()
243-
=> _rdt ?? (_rdt = _services.GetService<IRunningDocumentTable>());
243+
=> _rdt ?? (_rdt = Services.GetService<IRunningDocumentTable>());
244244
}
245245
}

0 commit comments

Comments
 (0)