Skip to content

Commit

Permalink
Merge pull request #715 from zooba/v2.1
Browse files Browse the repository at this point in the history
V2.1
  • Loading branch information
zooba committed Aug 10, 2015
2 parents a5935c5 + fbc1339 commit 88860a3
Show file tree
Hide file tree
Showing 13 changed files with 137 additions and 72 deletions.
2 changes: 2 additions & 0 deletions Python/Product/Analysis/Analyzer/ExpressionEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ internal class ExpressionEvaluator {
}

res[i - (args.Count - res.Length)] = (NameExpression)args[i].NameExpression;
} else if (res != null) {
res[i - (args.Count - res.Length)] = NameExpression.Empty;
}
}
return res ?? EmptyNames;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using System.Threading.Tasks;
using Microsoft.PythonTools.Analysis;
using Microsoft.PythonTools.Interpreter.Default;
using Microsoft.PythonTools.Parsing;
using Microsoft.VisualStudioTools;

namespace Microsoft.PythonTools.Interpreter {
Expand Down Expand Up @@ -68,6 +69,18 @@ bool watchLibraryForChanges
_id = id;
_config = config;

if (_config == null) {
throw new ArgumentNullException("config");
}

// 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 (watchLibraryForChanges && Directory.Exists(_config.LibraryPath)) {
_refreshIsCurrentTrigger = new Timer(RefreshIsCurrentTimer_Elapsed);

Expand Down
1 change: 1 addition & 0 deletions Python/Product/Analysis/Parsing/Ast/NameExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
namespace Microsoft.PythonTools.Parsing.Ast {
public class NameExpression : Expression {
public static readonly NameExpression[] EmptyArray = new NameExpression[0];
public static readonly NameExpression Empty = new NameExpression("");

private readonly string _name;

Expand Down
5 changes: 5 additions & 0 deletions Python/Product/Analysis/Parsing/PythonLanguageVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public static class PythonLanguageVersionExtensions {

public static PythonLanguageVersion ToLanguageVersion(this Version version) {
switch (version.Major) {
case 0:
switch (version.Minor) {
case 0: return PythonLanguageVersion.None;
}
break;
case 2:
switch (version.Minor) {
case 4: return PythonLanguageVersion.V24;
Expand Down
5 changes: 4 additions & 1 deletion Python/Product/Analysis/PythonAnalyzer.Specializations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,10 @@ int index
for (int i = 0, j = args.Length - keywordArgNames.Length;
i < keywordArgNames.Length && j < args.Length;
++i, ++j) {
if (keywordArgNames[i].Name == name) {
var kwArg = keywordArgNames[i];
if (kwArg == null) {
Debug.Fail("Null keyword argument");
} else if (kwArg.Name == name) {
return args[j];
}
}
Expand Down
5 changes: 4 additions & 1 deletion Python/Product/Django/Project/DjangoAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,10 @@ class ContextMarker : AnalysisValue {
for (int i = 0, j = args.Length - keywordArgNames.Length;
i < keywordArgNames.Length && j < args.Length;
++i, ++j) {
if (keywordArgNames[i].Name == name) {
var kwArg = keywordArgNames[i];
if (kwArg == null) {
Debug.Fail("Null keyword argument");
} else if (kwArg.Name == name) {
return args[j];
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ internal sealed class ExecuteInReplCommand : Command {
var interpreterService = PythonToolsPackage.ComponentModel.GetService<IInterpreterOptionsService>();
string filename, dir;
if (!PythonToolsPackage.TryGetStartupFileAndDirectory(out filename, out dir, out analyzer) ||
string.IsNullOrEmpty(filename) ||
interpreterService == null ||
interpreterService.NoInterpretersValue == analyzer.InterpreterFactory) {
// no interpreters installed, disable the command.
Expand Down Expand Up @@ -113,7 +114,9 @@ internal sealed class ExecuteInReplCommand : Command {
VsProjectAnalyzer analyzer;
string filename, dir;
var pyProj = CommonPackage.GetStartupProject() as PythonProjectNode;
if (!PythonToolsPackage.TryGetStartupFileAndDirectory(out filename, out dir, out analyzer)) {
if (!PythonToolsPackage.TryGetStartupFileAndDirectory(out filename, out dir, out analyzer) ||
string.IsNullOrEmpty(filename)
) {
// TODO: Error reporting
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1979,7 +1979,12 @@ bool addNew
);
}

var existing = _interpreters.FindInterpreter(path);
var interpreters = _interpreters;
if (interpreters == null) {
return null;
}

var existing = interpreters.FindInterpreter(path);
if (existing != null) {
return existing;
}
Expand All @@ -1996,8 +2001,14 @@ bool addNew
throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
}

var id = _interpreters.CreateInterpreterFactory(options);
return _interpreters.FindInterpreter(id, options.LanguageVersion);
Guid id;
try {
id = interpreters.CreateInterpreterFactory(options);
} catch (ArgumentException ex) {
TaskDialog.ForException(Site, ex, issueTrackerUrl: IssueTrackerUrl).ShowModal();
return null;
}
return interpreters.FindInterpreter(id, options.LanguageVersion);
}


Expand Down
12 changes: 11 additions & 1 deletion Python/Product/PythonTools/PythonToolsPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,17 @@ public sealed class PythonToolsPackage : CommonPackage, IVsComponentSelectorProv

private List<OpenReplCommand> GetReplCommands() {
var replCommands = new List<OpenReplCommand>();
var interpreterService = ComponentModel.GetService<IInterpreterOptionsService>();

var compModel = ComponentModel;
if (compModel == null) {
return replCommands;
}

var interpreterService = compModel.GetService<IInterpreterOptionsService>();
if (interpreterService == null) {
return replCommands;
}

var factories = interpreterService.Interpreters.ToList();
if (factories.Count == 0) {
return replCommands;
Expand Down
33 changes: 20 additions & 13 deletions Python/Product/VSInterpreters/CPythonInterpreterFactoryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,26 @@ class CPythonInterpreterFactoryProvider : IPythonInterpreterFactoryProvider {
}

if (!_interpreters.Any(f => f.Id == id && f.Configuration.Version == version)) {
_interpreters.Add(InterpreterFactoryCreator.CreateInterpreterFactory(
new InterpreterFactoryCreationOptions {
LanguageVersion = version,
Id = id,
Description = string.Format("{0} {1}", description, version),
InterpreterPath = Path.Combine(basePath, CPythonInterpreterFactoryConstants.ConsoleExecutable),
WindowInterpreterPath = Path.Combine(basePath, CPythonInterpreterFactoryConstants.WindowsExecutable),
LibraryPath = Path.Combine(basePath, CPythonInterpreterFactoryConstants.LibrarySubPath),
PathEnvironmentVariableName = CPythonInterpreterFactoryConstants.PathEnvironmentVariableName,
Architecture = actualArch ?? ProcessorArchitecture.None,
WatchLibraryForNewModules = true
}
));
IPythonInterpreterFactory fact;
try {
fact = InterpreterFactoryCreator.CreateInterpreterFactory(
new InterpreterFactoryCreationOptions {
LanguageVersion = version,
Id = id,
Description = string.Format("{0} {1}", description, version),
InterpreterPath = Path.Combine(basePath, CPythonInterpreterFactoryConstants.ConsoleExecutable),
WindowInterpreterPath = Path.Combine(basePath, CPythonInterpreterFactoryConstants.WindowsExecutable),
LibraryPath = Path.Combine(basePath, CPythonInterpreterFactoryConstants.LibrarySubPath),
PathEnvironmentVariableName = CPythonInterpreterFactoryConstants.PathEnvironmentVariableName,
Architecture = actualArch ?? ProcessorArchitecture.None,
WatchLibraryForNewModules = true
}
);
} catch (ArgumentException) {
continue;
}

_interpreters.Add(fact);
anyAdded = true;
}
}
Expand Down
42 changes: 35 additions & 7 deletions Python/Tests/Analysis/AnalysisTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6465,13 +6465,13 @@ class A:
public void RecursiveDecorators() {
// See https://github.com/Microsoft/PTVS/issues/542
// Should not crash/OOM
var code = @"
def f():
def d(fn):
@f()
def g(): pass
return d
var code = @"
def f():
def d(fn):
@f()
def g(): pass
return d
";

var cancelAt = new CancellationTokenSource(TimeSpan.FromSeconds(10));
Expand All @@ -6484,6 +6484,34 @@ class A:
}
}

[TestMethod, Priority(0)]
public void NullNamedArgument() {
CallDelegate callable = (node, unit, args, keywordArgNames) => {
bool anyNull = false;
Console.WriteLine("fn({0})", string.Join(", ", keywordArgNames.Select(n => {
if (n == null) {
anyNull = true;
return "(null)";
} else {
return n.Name + "=(value)";
}
})));
Assert.IsFalse(anyNull, "Some arguments were null");
return AnalysisSet.Empty;
};

using (var state = CreateAnalyzer(PythonLanguageVersion.V27)) {
state.SpecializeFunction("NullNamedArgument", "fn", callable);

var entry1 = state.AddModule("NullNamedArgument", "NullNamedArgument.py");
Prepare(entry1, GetSourceUnit("def fn(**kwargs): pass", "NullNamedArgument"), state.LanguageVersion);
entry1.Analyze(CancellationToken.None);
var entry2 = state.AddModule("test", "test.py");
Prepare(entry2, GetSourceUnit("import NullNamedArgument; NullNamedArgument.fn(a=0, ]]])", "test"), state.LanguageVersion);
entry2.Analyze(CancellationToken.None);
}
}

#endregion

#region Helpers
Expand Down
5 changes: 3 additions & 2 deletions Python/Tests/Core.UI/BuildTasksUITests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* ***************************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -246,14 +247,14 @@ public class BuildTasksUI27Tests {
EnvDTE.Project proj;
OpenProject(app, "Commands3.sln", out node, out proj);

var existingProcesses = Process.GetProcessesByName("cmd");
var existingProcesses = new HashSet<int>(Process.GetProcessesByName("cmd").Select(p => p.Id));

Execute(node, "Write to Console");

Process newProcess = null;
for (int retries = 100; retries > 0 && newProcess == null; --retries) {
Thread.Sleep(100);
newProcess = Process.GetProcessesByName("cmd").Except(existingProcesses).FirstOrDefault();
newProcess = Process.GetProcessesByName("cmd").Where(p => !existingProcesses.Contains(p.Id)).FirstOrDefault();
}
Assert.IsNotNull(newProcess, "Process did not start");
try {
Expand Down
64 changes: 21 additions & 43 deletions Python/Tests/VSInterpretersTests/VSInterpretersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.PythonTools.Interpreter;
using Microsoft.PythonTools.Parsing;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestUtilities;
Expand Down Expand Up @@ -48,49 +49,6 @@ public class VSInterpretersTests {
}
}

[TestMethod, Priority(0)]
public void MinimumAssembliesLoaded() {
var assembliesBefore = new HashSet<Assembly>(AppDomain.CurrentDomain.GetAssemblies());
// This assembly is probably already loaded, but let's pretend that
// we've loaded it again for this test.
assembliesBefore.Remove(typeof(IInterpreterOptionsService).Assembly);

var catalog = new AssemblyCatalog(typeof(IInterpreterOptionsService).Assembly);
var container = new CompositionContainer(catalog);
var service = container.GetExportedValue<IInterpreterOptionsService>();

Assert.IsInstanceOfType(service, typeof(InterpreterOptionsService));

// Ensure these assemblies were loaded.
var expectedAssemblies = new HashSet<string> {
"Microsoft.PythonTools.Analysis",
"Microsoft.PythonTools.VSInterpreters",
"Microsoft.PythonTools.IronPython.Interpreter"
};

// Ensure these assemblies were not loaded. In the out-of-VS
// scenario, we cannot always resolve these and so will crash.
// For tests, they are always available, and when installed they may
// always be available in the GAC, but we want to ensure that they
// are not loaded anyway.
var notExpectedAssemblies = new HashSet<string> {
"Microsoft.PythonTools",
"Microsoft.VisualStudio.ReplWindow"
};

Console.WriteLine("Loaded assemblies:");
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
if (!assembliesBefore.Remove(assembly)) {
var name = assembly.GetName().Name;
Console.WriteLine("{0}: {1}", name, assembly.FullName);
expectedAssemblies.Remove(name);
Assert.IsFalse(notExpectedAssemblies.Remove(name), assembly.FullName + " should not have been loaded");
}
}

Assert.AreEqual(0, expectedAssemblies.Count, "Was not loaded: " + string.Join(", ", expectedAssemblies));
}

private static string CompileString(string csharpCode, string outFile) {
var provider = new Microsoft.CSharp.CSharpCodeProvider();
var parameters = new System.CodeDom.Compiler.CompilerParameters {
Expand Down Expand Up @@ -323,5 +281,25 @@ public class FactoryProviderTypeLoadException : IPythonInterpreterFactoryProvide

Assert.AreEqual(1, service.KnownProviders.Count());
}

[TestMethod, Priority(0)]
public void InvalidInterpreterVersion() {
try {
var lv = new Version(1, 0).ToLanguageVersion();
Assert.Fail("Expected InvalidOperationException");
} catch (InvalidOperationException) {
}

try {
InterpreterFactoryCreator.CreateInterpreterFactory(new InterpreterFactoryCreationOptions {
Id = Guid.NewGuid(),
LanguageVersionString = "1.0"
});
Assert.Fail("Expected ArgumentException");
} catch (ArgumentException ex) {
// Expect version number in message
AssertUtil.Contains(ex.Message, "1.0");
}
}
}
}

0 comments on commit 88860a3

Please sign in to comment.