Skip to content
This repository was archived by the owner on Nov 4, 2024. It is now read-only.

Commit c0df2aa

Browse files
author
Mikhail Arkhipov
authored
Reload modules on pip install (microsoft#667)
* Fix NRE * Don't eval stub functions * Better stub eval fix * Merge issues * Reload modules * Undo a change * Clear module table on reload
1 parent 3fdeeb2 commit c0df2aa

File tree

7 files changed

+58
-27
lines changed

7 files changed

+58
-27
lines changed

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ public virtual IEnumerable<string> GetMemberNames() {
155155
return collection.Contents
156156
.OfType<IPythonConstant>()
157157
.Select(c => c.GetString())
158-
.ExcludeDefault()
159158
.Where(s => !string.IsNullOrEmpty(s));
160159
}
161160

@@ -236,7 +235,7 @@ private void LoadContent(string content) {
236235
if (ContentState < State.Loading) {
237236
try {
238237
content = content ?? LoadContent();
239-
_buffer.Reset(0, content);
238+
_buffer.Reset(Version + 1, content);
240239
ContentState = State.Loaded;
241240
} catch (IOException) { } catch (UnauthorizedAccessException) { }
242241
}
@@ -331,7 +330,9 @@ public void Update(IEnumerable<DocumentChange> changes) {
331330

332331
public void Reset(string content) {
333332
lock (AnalysisLock) {
334-
if (content != Content) {
333+
if (content != Content || content == null) {
334+
ContentState = State.None;
335+
Services.GetService<IPythonAnalyzer>().InvalidateAnalysis(this);
335336
InitializeContent(content);
336337
}
337338
}
@@ -441,7 +442,7 @@ protected virtual void OnAnalysisComplete() { }
441442
#region Analysis
442443
public IDocumentAnalysis GetAnyAnalysis() => Analysis;
443444

444-
public Task<IDocumentAnalysis> GetAnalysisAsync(int waitTime = 200, CancellationToken cancellationToken = default)
445+
public Task<IDocumentAnalysis> GetAnalysisAsync(int waitTime = 200, CancellationToken cancellationToken = default)
445446
=> Services.GetService<IPythonAnalyzer>().GetAnalysisAsync(this, waitTime, cancellationToken);
446447

447448
#endregion

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
using Microsoft.Python.Analysis.Types;
2929
using Microsoft.Python.Core;
3030
using Microsoft.Python.Core.Diagnostics;
31-
using Microsoft.Python.Core.IO;
3231

3332
namespace Microsoft.Python.Analysis.Modules.Resolution {
3433
internal sealed class MainModuleResolution : ModuleResolutionBase, IModuleManagement {
@@ -49,7 +48,7 @@ internal async Task InitializeAsync(CancellationToken cancellationToken = defaul
4948

5049
var b = new BuiltinsPythonModule(moduleName, modulePath, _services);
5150
BuiltinsModule = b;
52-
_modules[BuiltinModuleName] = new ModuleRef(b);
51+
Modules[BuiltinModuleName] = new ModuleRef(b);
5352
}
5453

5554
public async Task<IReadOnlyList<string>> GetSearchPathsAsync(CancellationToken cancellationToken = default) {
@@ -59,8 +58,8 @@ public async Task<IReadOnlyList<string>> GetSearchPathsAsync(CancellationToken c
5958

6059
_searchPaths = await GetInterpreterSearchPathsAsync(cancellationToken);
6160
Debug.Assert(_searchPaths != null, "Should have search paths");
62-
_searchPaths = _searchPaths != null
63-
? Configuration.SearchPaths != null
61+
_searchPaths = _searchPaths != null
62+
? Configuration.SearchPaths != null
6463
? _searchPaths.Concat(Configuration.SearchPaths).ToArray()
6564
: _searchPaths
6665
: Array.Empty<string>();
@@ -99,7 +98,7 @@ protected override IPythonModule CreateModule(string name) {
9998
if (stub != null && stub.FilePath.PathEquals(moduleImport.ModulePath)) {
10099
return stub;
101100
}
102-
101+
103102
if (moduleImport.IsBuiltin) {
104103
_log?.Log(TraceEventType.Verbose, "Create built-in compiled (scraped) module: ", name, Configuration.InterpreterPath);
105104
module = new CompiledBuiltinPythonModule(name, stub, _services);
@@ -173,15 +172,17 @@ internal async Task LoadBuiltinTypesAsync(CancellationToken cancellationToken =
173172
}
174173

175174
public async Task ReloadAsync(CancellationToken cancellationToken = default) {
175+
Modules.Clear();
176+
176177
ModuleCache = new ModuleCache(_interpreter, _services);
177178
PathResolver = new PathResolver(_interpreter.LanguageVersion);
178-
179+
179180
var addedRoots = new HashSet<string>();
180181
addedRoots.UnionWith(PathResolver.SetRoot(_root));
181-
182+
182183
var interpreterPaths = await GetSearchPathsAsync(cancellationToken);
183184
addedRoots.UnionWith(PathResolver.SetInterpreterSearchPaths(interpreterPaths));
184-
185+
185186
addedRoots.UnionWith(SetUserSearchPaths(_interpreter.Configuration.SearchPaths));
186187
ReloadModulePaths(addedRoots);
187188
}
@@ -190,8 +191,8 @@ public IEnumerable<string> SetUserSearchPaths(in IEnumerable<string> searchPaths
190191
=> PathResolver.SetUserSearchPaths(searchPaths);
191192

192193
// For tests
193-
internal void AddUnimportableModule(string moduleName)
194-
=> _modules[moduleName] = new ModuleRef(new SentinelModule(moduleName, _services));
194+
internal void AddUnimportableModule(string moduleName)
195+
=> Modules[moduleName] = new ModuleRef(new SentinelModule(moduleName, _services));
195196

196197
private bool TryCreateModuleStub(string name, string modulePath, out IPythonModule module) {
197198
// First check stub next to the module.

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@
2828

2929
namespace Microsoft.Python.Analysis.Modules.Resolution {
3030
internal abstract class ModuleResolutionBase {
31-
protected readonly ConcurrentDictionary<string, ModuleRef> _modules = new ConcurrentDictionary<string, ModuleRef>();
3231
protected readonly IServiceContainer _services;
3332
protected readonly IPythonInterpreter _interpreter;
3433
protected readonly IFileSystem _fs;
3534
protected readonly ILogger _log;
3635
protected readonly bool _requireInitPy;
3736
protected string _root;
3837

38+
protected ConcurrentDictionary<string, ModuleRef> Modules { get; } = new ConcurrentDictionary<string, ModuleRef>();
3939
protected PathResolver PathResolver { get; set; }
4040

4141
protected InterpreterConfiguration Configuration => _interpreter.Configuration;
@@ -75,10 +75,10 @@ public IReadOnlyCollection<string> GetPackagesFromDirectory(string searchPath, C
7575
}
7676

7777
public IPythonModule GetImportedModule(string name)
78-
=> _modules.TryGetValue(name, out var moduleRef) ? moduleRef.Value : _interpreter.ModuleResolution.GetSpecializedModule(name);
78+
=> Modules.TryGetValue(name, out var moduleRef) ? moduleRef.Value : _interpreter.ModuleResolution.GetSpecializedModule(name);
7979

8080
public IPythonModule GetOrLoadModule(string name) {
81-
if (_modules.TryGetValue(name, out var moduleRef)) {
81+
if (Modules.TryGetValue(name, out var moduleRef)) {
8282
return moduleRef.GetOrCreate(name, this);
8383
}
8484

@@ -87,7 +87,7 @@ public IPythonModule GetOrLoadModule(string name) {
8787
return module;
8888
}
8989

90-
moduleRef = _modules.GetOrAdd(name, new ModuleRef());
90+
moduleRef = Modules.GetOrAdd(name, new ModuleRef());
9191
return moduleRef.GetOrCreate(name, this);
9292
}
9393

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ internal sealed class TypeshedResolution : ModuleResolutionBase, IModuleResoluti
3131

3232
public TypeshedResolution(IServiceContainer services) : base(null, services) {
3333
BuiltinsModule = _interpreter.ModuleResolution.BuiltinsModule;
34-
_modules[BuiltinModuleName] = new ModuleRef(BuiltinsModule);
34+
Modules[BuiltinModuleName] = new ModuleRef(BuiltinsModule);
3535

3636
_root = _interpreter.Configuration?.TypeshedPath;
3737
// TODO: merge with user-provided stub paths
@@ -67,6 +67,7 @@ protected override IPythonModule CreateModule(string name) {
6767
}
6868

6969
public Task ReloadAsync(CancellationToken cancellationToken = default) {
70+
Modules.Clear();
7071
PathResolver = new PathResolver(_interpreter.LanguageVersion);
7172

7273
var addedRoots = PathResolver.SetRoot(_root);

src/LanguageServer/Impl/LanguageServer.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
using System;
1818
using System.Collections.Generic;
1919
using System.Diagnostics;
20+
using System.Linq;
2021
using System.Threading;
2122
using System.Threading.Tasks;
2223
using Microsoft.Python.Analysis;
2324
using Microsoft.Python.Analysis.Diagnostics;
25+
using Microsoft.Python.Analysis.Documents;
26+
using Microsoft.Python.Analysis.Modules;
2427
using Microsoft.Python.Core;
2528
using Microsoft.Python.Core.Disposables;
2629
using Microsoft.Python.Core.Idle;
@@ -377,19 +380,32 @@ private void HandlePathWatchChange(JToken section, CancellationToken cancellatio
377380
// Were not watching OR were watching but paths have changed. Recreate the watcher.
378381
_pathsWatcher?.Dispose();
379382
var interpreter = _services.GetService<IPythonInterpreter>();
383+
var logger = _services.GetService<ILogger>();
380384
_pathsWatcher = new PathsWatcher(
381385
_initParams.initializationOptions.searchPaths,
382-
() => interpreter.ModuleResolution.ReloadAsync(cancellationToken).DoNotWait(),
383-
_services.GetService<ILogger>()
384-
);
386+
() => {
387+
logger.Log(TraceEventType.Information, Resources.ReloadingModules);
388+
// No need to reload typeshed resolution since it is a static storage.
389+
// User does can add stubs while application is running, but it is
390+
// by design at this time that the app should be restarted.
391+
interpreter.ModuleResolution.ReloadAsync(cancellationToken).ContinueWith(t => {
392+
logger.Log(TraceEventType.Information, Resources.Done);
393+
logger.Log(TraceEventType.Information, Resources.RestartingAnalysis);
394+
var rdt = _services.GetService<IRunningDocumentTable>();
395+
foreach (var doc in rdt) {
396+
doc.Reset(null);
397+
}
398+
}, cancellationToken).DoNotWait();
399+
},
400+
_services.GetService<ILogger>()
401+
);
402+
403+
_watchSearchPaths = true;
404+
_searchPaths = _initParams.initializationOptions.searchPaths;
385405
}
386-
387-
_watchSearchPaths = true;
388-
_searchPaths = _initParams.initializationOptions.searchPaths;
389406
}
390-
391407
private static CancellationToken GetToken(CancellationToken original)
392-
=> Debugger.IsAttached ? CancellationToken.None : original;
408+
=> Debugger.IsAttached ? CancellationToken.None : original;
393409

394410
private class Prioritizer : IDisposable {
395411
private const int InitializePriority = 0;

src/LanguageServer/Impl/Resources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LanguageServer/Impl/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,7 @@
156156
<data name="RenameVariable_UnableGetExpressionAnalysis" xml:space="preserve">
157157
<value>Unable to get analysis for the selected expression.</value>
158158
</data>
159+
<data name="RestartingAnalysis" xml:space="preserve">
160+
<value>Restarting analysis...</value>
161+
</data>
159162
</root>

0 commit comments

Comments
 (0)