From dd4cc8a37c2f2a48f396eb7f7cffbcb7ee4e5cca Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 16 Jun 2016 14:38:07 -0700 Subject: [PATCH] Fixes #1333 Error in DB view while analysis DB is changing Switches to using safe enumeration functions instead of the Path ones. Fixes EnvironmentList test --- .../PythonInterpreterFactoryWithDatabase.cs | 7 +++---- Python/Product/Analysis/ModulePath.cs | 10 +++++----- Python/Product/Analyzer/PyLibAnalyzer.cs | 10 ++++------ .../Product/Common/Infrastructure/PathUtils.cs | 4 ++-- .../PythonTools/PythonTools/Project/Conda.cs | 2 +- .../Project/ImportWizard/ImportSettings.cs | 11 ++++------- .../PythonTools/PythonTools/Project/Pip.cs | 16 ++++++++-------- .../PythonTools/Project/PythonProjectNode.cs | 12 ++++-------- .../VSInterpreters/DerivedInterpreterFactory.cs | 6 +----- Python/Tests/Core/EnvironmentListTests.cs | 7 ++++++- 10 files changed, 38 insertions(+), 47 deletions(-) diff --git a/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryWithDatabase.cs b/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryWithDatabase.cs index 178e5004cb..5d74820f2d 100644 --- a/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryWithDatabase.cs +++ b/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryWithDatabase.cs @@ -174,7 +174,7 @@ bool watchLibraryForChanges var paths = new List(); paths.Add(databasePath); if (includeSitePackages) { - paths.AddRange(Directory.EnumerateDirectories(databasePath)); + paths.AddRange(PathUtils.EnumerateDirectories(databasePath, recurse: false)); } try { @@ -448,8 +448,7 @@ bool watchLibraryForChanges private static HashSet GetExistingDatabase(string databasePath) { return new HashSet( - Directory.EnumerateFiles(databasePath, "*.idb", SearchOption.AllDirectories) - .Select(f => Path.GetFileNameWithoutExtension(f)), + PathUtils.EnumerateFiles(databasePath, "*.idb").Select(f => Path.GetFileNameWithoutExtension(f)), StringComparer.InvariantCultureIgnoreCase ); } @@ -622,7 +621,7 @@ select groupedByPackage.Key // Currently we assume that if the file exists, it's up to date. // PyLibAnalyzer will perform timestamp checks if the user manually // refreshes. - return Directory.EnumerateFiles(DatabasePath, "*.idb", SearchOption.AllDirectories) + return PathUtils.EnumerateFiles(DatabasePath, "*.idb") .Select(f => Path.GetFileNameWithoutExtension(f)); } diff --git a/Python/Product/Analysis/ModulePath.cs b/Python/Product/Analysis/ModulePath.cs index c443832566..c05f5add52 100644 --- a/Python/Product/Analysis/ModulePath.cs +++ b/Python/Product/Analysis/ModulePath.cs @@ -151,8 +151,8 @@ bool requireInitPy } if (!skipFiles) { - foreach (var file in Directory.EnumerateFiles(path)) { - var filename = Path.GetFileName(file); + foreach (var file in PathUtils.EnumerateFiles(path, recurse: false)) { + var filename = PathUtils.GetFileOrDirectoryName(file); var match = PythonFileRegex.Match(filename); if (!match.Success) { match = PythonBinaryRegex.Match(filename); @@ -168,8 +168,8 @@ bool requireInitPy } if (recurse) { - foreach (var dir in Directory.EnumerateDirectories(path)) { - var dirname = Path.GetFileName(dir); + foreach (var dir in PathUtils.EnumerateDirectories(path, recurse: false)) { + var dirname = PathUtils.GetFileOrDirectoryName(dir); var match = PythonPackageRegex.Match(dirname); if (match.Success && (!requireInitPy || File.Exists(Path.Combine(dir, "__init__.py")))) { foreach (var entry in GetModuleNamesFromPathHelper( @@ -238,7 +238,7 @@ bool requireInitPy public static IEnumerable ExpandPathFiles(IEnumerable paths) { foreach (var path in paths) { if (Directory.Exists(path)) { - foreach (var file in Directory.EnumerateFiles(path, "*.pth")) { + foreach (var file in PathUtils.EnumerateFiles(path, "*.pth", recurse: false)) { using (var reader = new StreamReader(file)) { string line; while ((line = reader.ReadLine()) != null) { diff --git a/Python/Product/Analyzer/PyLibAnalyzer.cs b/Python/Product/Analyzer/PyLibAnalyzer.cs index 68206fa08e..f4e1a8da9a 100644 --- a/Python/Product/Analyzer/PyLibAnalyzer.cs +++ b/Python/Product/Analyzer/PyLibAnalyzer.cs @@ -649,8 +649,8 @@ internal class PyLibAnalyzer : IDisposable { // If the top level does not contain any .idb files, we won't // bother recursing. if (Directory.Exists(_outDir) && - Directory.EnumerateFiles(_outDir, "*.idb", SearchOption.TopDirectoryOnly).Any()) { - filesInDatabase.UnionWith(Directory.EnumerateFiles(_outDir, "*.idb", SearchOption.AllDirectories)); + PathUtils.EnumerateFiles(_outDir, "*.idb", recurse: false).Any()) { + filesInDatabase.UnionWith(PathUtils.EnumerateFiles(_outDir, "*.idb")); } } else if (firstRun) { Directory.CreateDirectory(_outDir); @@ -704,7 +704,7 @@ internal class PyLibAnalyzer : IDisposable { } if (!_dryRun) { - filesInDatabase.UnionWith(Directory.EnumerateFiles(_outDir, "*.idb", SearchOption.AllDirectories)); + filesInDatabase.UnionWith(PathUtils.EnumerateFiles(_outDir, "*.idb")); } // Store the files we want to keep separately, in case we decide to @@ -728,9 +728,7 @@ internal class PyLibAnalyzer : IDisposable { } else { // Failed to get builtin names, so don't delete anything // from the main output directory. - filesToKeep.UnionWith( - Directory.EnumerateFiles(_outDir, "*.idb", SearchOption.TopDirectoryOnly) - ); + filesToKeep.UnionWith(PathUtils.EnumerateFiles(_outDir, "*.idb", recurse: false)); } } diff --git a/Python/Product/Common/Infrastructure/PathUtils.cs b/Python/Product/Common/Infrastructure/PathUtils.cs index 46a204f9dc..6a65842c22 100644 --- a/Python/Product/Common/Infrastructure/PathUtils.cs +++ b/Python/Product/Common/Infrastructure/PathUtils.cs @@ -558,11 +558,11 @@ public static class PathUtils { } continue; } - var result = Directory.EnumerateFiles(dir, file, SearchOption.TopDirectoryOnly).FirstOrDefault(); + var result = EnumerateFiles(dir, file, recurse: false).FirstOrDefault(); if (result != null) { return result; } - foreach (var subDir in Directory.EnumerateDirectories(dir)) { + foreach (var subDir in EnumerateDirectories(dir, recurse: false)) { dirQueue.Enqueue(subDir); } dirQueue.Enqueue(""); diff --git a/Python/Product/PythonTools/PythonTools/Project/Conda.cs b/Python/Product/PythonTools/PythonTools/Project/Conda.cs index ae9058b342..a0a24d68a0 100644 --- a/Python/Product/PythonTools/PythonTools/Project/Conda.cs +++ b/Python/Product/PythonTools/PythonTools/Project/Conda.cs @@ -44,7 +44,7 @@ IInterpreterRegistryService service string metaFile; try { - metaFile = Directory.EnumerateFiles(condaMetaPath, "*.json").FirstOrDefault(); + metaFile = PathUtils.EnumerateFiles(condaMetaPath, "*.json", recurse: false).FirstOrDefault(); } catch (Exception ex) { if (ex.IsCriticalException()) { throw; diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs index 6f44234800..a2f9fd3173 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs @@ -231,13 +231,13 @@ string filters ) { if (Directory.Exists(sourcePath)) { return await Task.Run(() => { - var files = Directory.EnumerateFiles(sourcePath, "*.py", SearchOption.TopDirectoryOnly); + var files = PathUtils.EnumerateFiles(sourcePath, "*.py", recurse: false); // Also include *.pyw files if they were in the filter list foreach (var pywFilters in filters .Split(';') .Where(filter => filter.TrimEnd().EndsWith(".pyw", StringComparison.OrdinalIgnoreCase)) ) { - files = files.Concat(Directory.EnumerateFiles(sourcePath, pywFilters, SearchOption.TopDirectoryOnly)); + files = files.Concat(PathUtils.EnumerateFiles(sourcePath, pywFilters, recurse: false)); } return files.Select(f => Path.GetFileName(f)).ToList(); }); @@ -490,10 +490,7 @@ List virtualEnvPaths var directories = new List() { source }; var skipDirectories = new HashSet(StringComparer.OrdinalIgnoreCase); - try { - directories.AddRange(Directory.EnumerateDirectories(source, "*", SearchOption.AllDirectories)); - } catch (UnauthorizedAccessException) { - } + directories.AddRange(PathUtils.EnumerateDirectories(source)); foreach (var dir in directories) { if (UnwindDirectory(dir).Any(skipDirectories.Contains)) { @@ -511,7 +508,7 @@ List virtualEnvPaths } foreach (var filter in patterns) { - files.UnionWith(Directory.EnumerateFiles(dir, filter)); + files.UnionWith(PathUtils.EnumerateFiles(dir, filter, recurse: false)); } } catch (UnauthorizedAccessException) { } diff --git a/Python/Product/PythonTools/PythonTools/Project/Pip.cs b/Python/Product/PythonTools/PythonTools/Project/Pip.cs index cf4f717817..46d4fe2fae 100644 --- a/Python/Product/PythonTools/PythonTools/Project/Pip.cs +++ b/Python/Product/PythonTools/PythonTools/Project/Pip.cs @@ -84,14 +84,14 @@ static class Pip { var packagesPath = Path.Combine(factory.Configuration.LibraryPath, "site-packages"); HashSet result = null; if (Directory.Exists(packagesPath)) { - result = await Task.Run(() => new HashSet(Directory.EnumerateDirectories(packagesPath) - .Select(path => Path.GetFileName(path)) - .Select(name => PackageNameRegex.Match(name)) - .Where(match => match.Success) - .Select(match => match.Groups["name"].Value) - )) - .SilenceException>() - .SilenceException>() + result = await Task.Run(() => new HashSet( + PathUtils.EnumerateDirectories(packagesPath, recurse: false) + .Select(path => Path.GetFileName(path)) + .Select(name => PackageNameRegex.Match(name)) + .Where(match => match.Success) + .Select(match => match.Groups["name"].Value) + ) + ) .HandleAllExceptions(null, typeof(Pip)); } diff --git a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs index 576637824d..5971882a88 100644 --- a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs +++ b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs @@ -1966,14 +1966,10 @@ private static T FindImmediateChild(HierarchyNode parent) publishOptions = TaskDialog.CallWithRetry( _ => new PublishProjectOptions( publishOptions.AdditionalFiles.Concat( - Directory.EnumerateFiles( - factory.Configuration.PrefixPath, - "*", - SearchOption.AllDirectories - ) - // Exclude the completion DB - .Where(f => !f.Contains("\\.ptvs\\")) - .Select(f => new PublishFile(f, PathUtils.GetRelativeFilePath(ProjectHome, f))) + PathUtils.EnumerateFiles(factory.Configuration.PrefixPath) + // Exclude the completion DB + .Where(f => !f.Contains("\\.ptvs\\")) + .Select(f => new PublishFile(f, PathUtils.GetRelativeFilePath(ProjectHome, f))) ).ToArray(), publishOptions.DestinationUrl ), diff --git a/Python/Product/VSInterpreters/DerivedInterpreterFactory.cs b/Python/Product/VSInterpreters/DerivedInterpreterFactory.cs index db87adc9af..d05ac494c9 100644 --- a/Python/Product/VSInterpreters/DerivedInterpreterFactory.cs +++ b/Python/Product/VSInterpreters/DerivedInterpreterFactory.cs @@ -121,11 +121,7 @@ InterpreterFactoryCreationOptions options var paths = new List { databasePath }; if (includeSitePackages) { - try { - paths.AddRange(Directory.EnumerateDirectories(databasePath)); - } catch (IOException) { - } catch (UnauthorizedAccessException) { - } + paths.AddRange(PathUtils.EnumerateDirectories(databasePath)); } return new PythonTypeDatabase(this, paths, _baseDb); } diff --git a/Python/Tests/Core/EnvironmentListTests.cs b/Python/Tests/Core/EnvironmentListTests.cs index ff9fffa656..d526a48ca3 100644 --- a/Python/Tests/Core/EnvironmentListTests.cs +++ b/Python/Tests/Core/EnvironmentListTests.cs @@ -96,7 +96,12 @@ public class EnvironmentListTests { Assert.AreEqual(6, environments.Count); AssertUtil.ContainsExactly( wpf.Invoke(() => environments.Select(ev => ev.Description).ToList()), - Enumerable.Range(1, 6).Select(i => string.Format("Test Factory {0}", i)) + "Test Factory 1 2.7", + "Test Factory 2 3.0", + "Test Factory 3 3.3", + "Test Factory 4 2.7", + "Test Factory 5 3.0", + "Test Factory 6 3.3" ); } }