/// <summary> /// Returns a new database loaded from the specified path. If null is /// returned, <see cref="GetCurrentDatabase"/> will assume the default /// completion DB is intended. /// </summary> /// <remarks> /// This is intended for overriding in derived classes. To get a /// queryable database instance, use <see cref="GetCurrentDatabase"/> or /// <see cref="CreateInterpreter"/>. /// </remarks> public virtual PythonTypeDatabase MakeTypeDatabase(string databasePath, bool includeSitePackages = true) { if (!_generating && PythonTypeDatabase.IsDatabaseVersionCurrent(databasePath)) { var paths = new List <string>(); paths.Add(databasePath); if (includeSitePackages) { paths.AddRange(Directory.EnumerateDirectories(databasePath)); } try { return(new PythonTypeDatabase(this, paths)); } catch (IOException) { } catch (UnauthorizedAccessException) { } } return(PythonTypeDatabase.CreateDefaultTypeDatabase(this)); }
/// <summary> /// Called to manually trigger a refresh of <see cref="IsCurrent"/>. /// After completion, <see cref="IsCurrentChanged"/> will always be /// raised, regardless of whether the values were changed. /// </summary> public virtual void RefreshIsCurrent() { try { if (!_isCurrentSemaphore.Wait(0)) { // Another thread is working on our state, so we will wait for // them to finish and return, since the value is up to date. _isCurrentSemaphore.Wait(); _isCurrentSemaphore.Release(); return; } } catch (ObjectDisposedException) { // We've been disposed and the call has come in from // externally, probably a timer. return; } try { _isCheckingDatabase = true; OnIsCurrentChanged(); // Wait for a slot that will allow us to scan the disk. This // prevents too many threads from updating at once and causing // I/O saturation. _sharedIsCurrentSemaphore.Wait(); try { _generating = PythonTypeDatabase.IsDatabaseRegenerating(DatabasePath); WatchingLibrary = !_generating; if (_generating) { // Skip the rest of the checks, because we know we're // not current. } else if (!PythonTypeDatabase.IsDatabaseVersionCurrent(DatabasePath)) { _isValid = false; _missingModules = null; _isCurrentException = null; } else { _isValid = true; HashSet <string> existingDatabase = null; string[] missingModules = null; for (int retries = 3; retries > 0; --retries) { try { existingDatabase = GetExistingDatabase(DatabasePath); break; } catch (UnauthorizedAccessException) { } catch (IOException) { } Thread.Sleep(100); } if (existingDatabase == null) { // This will either succeed or throw again. If it throws // then the error is reported to the user. existingDatabase = GetExistingDatabase(DatabasePath); } for (int retries = 3; retries > 0; --retries) { try { missingModules = GetMissingModules(existingDatabase); break; } catch (UnauthorizedAccessException) { } catch (IOException) { } Thread.Sleep(100); } if (missingModules == null) { // This will either succeed or throw again. If it throws // then the error is reported to the user. missingModules = GetMissingModules(existingDatabase); } if (missingModules.Length > 0) { var oldModules = _missingModules; if (oldModules == null || oldModules.Length != missingModules.Length || !oldModules.SequenceEqual(missingModules)) { } _missingModules = missingModules; } else { _missingModules = null; } } _isCurrentException = null; } finally { _sharedIsCurrentSemaphore.Release(); } } catch (Exception ex) { // Report the exception text as the reason. _isCurrentException = ex.ToString(); _missingModules = null; } finally { _isCheckingDatabase = false; try { _isCurrentSemaphore.Release(); } catch (ObjectDisposedException) { // The semaphore is not locked for disposal as it is only // used to prevent reentrance into this function. As a // result, it may have been disposed while we were in here. } } OnIsCurrentChanged(); }
public PythonInterpreterFactoryWithDatabase( InterpreterConfiguration config, InterpreterFactoryCreationOptions options ) { if (config == null) { throw new ArgumentNullException(nameof(config)); } if (options == null) { options = new InterpreterFactoryCreationOptions(); } Configuration = config; _databasePath = options.DatabasePath; // Avoid creating a interpreter with an unsupported version. // https://github.com/Microsoft/PTVS/issues/706 try { var langVer = config.Version.ToLanguageVersion(); } catch (InvalidOperationException ex) { throw new ArgumentException(ex.Message, ex); } if (!GlobalInterpreterOptions.SuppressFileSystemWatchers && options.WatchFileSystem && !string.IsNullOrEmpty(DatabasePath)) { // Assume the database is valid if the version is up to date, then // switch to invalid after we've checked. _isValid = PythonTypeDatabase.IsDatabaseVersionCurrent(DatabasePath); _isCheckingDatabase = true; _verWatcher = CreateDatabaseVerWatcher(); _verDirWatcher = CreateDatabaseDirectoryWatcher(); #if DEBUG var creationStack = new StackTrace(true).ToString(); Task.Delay(1000).ContinueWith(t => { Debug.Assert( _hasEverCheckedDatabase, "Database check was not triggered for {0}".FormatUI(Configuration.Id), creationStack ); }); #endif } else { // Assume the database is valid _isValid = true; } if (!GlobalInterpreterOptions.SuppressPackageManagers) { try { var pm = options.PackageManager; if (pm != null) { pm.SetInterpreterFactory(this); pm.InstalledFilesChanged += PackageManager_InstalledFilesChanged; PackageManager = pm; } } catch (NotSupportedException) { } } }