diff --git a/DSIronPython/DSIronPython.csproj b/DSIronPython/DSIronPython.csproj index 958c830..d62e6f8 100644 --- a/DSIronPython/DSIronPython.csproj +++ b/DSIronPython/DSIronPython.csproj @@ -19,7 +19,7 @@ - + @@ -27,6 +27,11 @@ <_Parameter1>IronPythonTests + + + Always + + diff --git a/DSIronPython/IronPythonEvaluator.cs b/DSIronPython/IronPythonEvaluator.cs index 366647f..582e8fc 100644 --- a/DSIronPython/IronPythonEvaluator.cs +++ b/DSIronPython/IronPythonEvaluator.cs @@ -12,7 +12,7 @@ using Dynamo.Session; using Dynamo.Utilities; using IronPython.Hosting; - +using Microsoft.Extensions.Configuration; using Microsoft.Scripting.Hosting; using Microsoft.Scripting.Utils; @@ -25,6 +25,8 @@ namespace DSIronPython [IsVisibleInDynamoLibrary(false)] public class IronPythonEvaluator : Dynamo.PythonServices.PythonEngine { + private bool CONFIG_ENABLE_NET48SHIMCOMPAT = false; + private const string DynamoPrintFuncName = "__dynamoprint__"; /// stores a copy of the previously executed code private static string prev_code { get; set; } @@ -120,7 +122,18 @@ public override object Evaluate( if (code != prev_code) { ScriptEngine PythonEngine = Python.CreateEngine(); - if (!string.IsNullOrEmpty(stdLib)) + //to maintain compatability with ironPython code written in .netframework - we load the system.dll shim + //which loads many types which used to be in system.dll(mscorlib) like system.diagnostics.process + //which were moved in .Net. These types are now importable by python code without users needing to add + //clr.addreference. + //TODO consider a preference for this. + //TODO test smaller shims...netstd.dll + + if (CONFIG_ENABLE_NET48SHIMCOMPAT) + { + PythonEngine.Runtime.LoadAssembly(Assembly.Load("System")); + } + if (!string.IsNullOrEmpty(stdLib)) { paths = PythonEngine.GetSearchPaths().ToList(); paths.Add(stdLib); @@ -366,5 +379,23 @@ private void OnEvaluationEnd( bool isSuccessful, #endregion + internal IronPythonEvaluator(IConfiguration config = null) + { + //either inject a config or load from appsettings.json + if (config is not null) + { + + } + else + { + var configPath = Path.Combine(new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName, "appsettings.json"); + config = new ConfigurationBuilder().AddJsonFile(configPath, optional: true).Build(); + } + var enableShimLoad = config.GetSection("config").GetChildren().FirstOrDefault(x => x.Key == nameof(CONFIG_ENABLE_NET48SHIMCOMPAT))?.Value; + if (bool.TryParse(enableShimLoad, out var parsed)) + { + CONFIG_ENABLE_NET48SHIMCOMPAT = parsed; + } + } } } diff --git a/DSIronPython/appsettings.json b/DSIronPython/appsettings.json new file mode 100644 index 0000000..321f440 --- /dev/null +++ b/DSIronPython/appsettings.json @@ -0,0 +1,5 @@ +{ + "config": { + "CONFIG_ENABLE_NET48SHIMCOMPAT": "true" + } +} \ No newline at end of file diff --git a/DSIronPython/pkg.json b/DSIronPython/pkg.json index a63abe8..566574a 100644 --- a/DSIronPython/pkg.json +++ b/DSIronPython/pkg.json @@ -2,7 +2,7 @@ "license": "", "file_hash": null, "name": "DynamoIronPython2.7", - "version": "3.0.0", + "version": "3.1.0", "description": "*** This is the official version of the IronPython Extension which will load the IronPython2 evaluation engine for Dynamo. *** \r\n\r\nIf you see the following warning 'The configured Python engine could not be found' when you try to run a Python Script node which is set to use IronPython2, the host application for Dynamo (Such as Revit or Civil 3d) will have chosen to exclude IronPython2 as default installed content and you will need to download this extension in order to enable IronPython2 again.\r\n\r\nYou have the ability to either download this IronPython2 Extension or use the alternative out-of-the-box CPython3 engine and migrate your legacy IronPython2 code to CPython3. While there are differences in language the Python basis is the same and we have provided a Migration Assistant to help you migrate your code to Python3.", "group": "", "keywords": [ "python", "ironpython" ], diff --git a/IronPythonExtension/IronPythonExtension.cs b/IronPythonExtension/IronPythonExtension.cs index 6c08bf4..a9bf417 100644 --- a/IronPythonExtension/IronPythonExtension.cs +++ b/IronPythonExtension/IronPythonExtension.cs @@ -95,7 +95,7 @@ private static void LoadPythonEngine(Assembly assembly) { if (assembly == null) { - return; + throw new ArgumentNullException($"Error while loading python engine - assembly {PythonEvaluatorAssembly}.dll was not loaded successfully."); } // Currently we are using try-catch to validate loaded assembly and Singleton Instance method exist diff --git a/IronPythonTests/CodeCompletionTests.cs b/IronPythonTests/CodeCompletionTests.cs deleted file mode 100644 index 52729e5..0000000 --- a/IronPythonTests/CodeCompletionTests.cs +++ /dev/null @@ -1,438 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using Dynamo.Logging; -using Dynamo.PythonServices; -using Dynamo.Utilities; -using Microsoft.Scripting; -using Microsoft.Scripting.Hosting; -using NUnit.Framework; - -namespace IronPythonTests -{ - /*//TODO - this should be tested on Dynamo side? Or will need to be exposed to third parties somehow... - [TestFixture] - internal class SharedCodeCompletionProviderTests : CodeCompletionTests - { - [Test] - public void SharedCoreCanFindLoadedProviders() - { - var provider = new SharedCompletionProvider(PythonNodeModels.PythonEngineVersion.IronPython2, ""); - Assert.IsNotNull(provider); - } - - [Test] - public void SharedCoreCanReturnCLRCompletionData() - { - var provider = new SharedCompletionProvider(PythonNodeModels.PythonEngineVersion.IronPython2, ""); - Assert.IsNotNull(provider); - var str = "\nimport System.Collections\nSystem.Collections."; - - var completionData = provider.GetCompletionData(str); - var completionList = completionData.Select(d => d.Text); - Assert.IsTrue(completionList.Any()); - Assert.IsTrue(completionList.Intersect(new[] { "Hashtable", "Queue", "Stack" }).Count() == 3); - Assert.AreEqual(29, completionData.Length); - } - } - */ - [TestFixture] - internal class CodeCompletionTests - { - private AssemblyHelper assemblyHelper; - - private class SimpleLogger : ILogger - { - - public string LogPath - { - get { return ""; } - } - - public void Log(string message) - { - - } - - public void Log(string message, LogLevel level) - { - - } - - public void Log(string tag, string message) - { - - } - - public void LogError(string error) - { - - } - - public void LogWarning(string warning, WarningLevel level) - { - - } - - public void Log(Exception e) - { - - } - - public void ClearLog() - { - - } - - public string LogText - { - get { return ""; } - } - - public string Warning - { - get - { - return ""; - } - set { return; } - } - } - - private ILogger logger; - - // List of expected default imported types - private List defaultImports = new List() - { - "None", - "System" - }; - - [OneTimeSetUp] - public void SetupPythonTests() - { - this.logger = new SimpleLogger(); - - var assemblyPath = Assembly.GetExecutingAssembly().Location; - var moduleRootFolder = Path.GetDirectoryName(assemblyPath); - - var resolutionPaths = new[] - { - // These tests need "DSIronPythonNode.dll" under "nodes" folder. - Path.Combine(moduleRootFolder, "nodes") - }; - //for some legacy tests we'll need the DSIronPython binary loaded manually - //as the types are found using reflection - during normal dynamo use these types are already loaded. - Assembly.LoadFrom(Path.Combine(moduleRootFolder, "DSIronPython.dll")); - - assemblyHelper = new AssemblyHelper(moduleRootFolder, resolutionPaths); - AppDomain.CurrentDomain.AssemblyResolve += assemblyHelper.ResolveAssembly; - } - - [OneTimeTearDown] - public void RunAfterAllTests() - { - AppDomain.CurrentDomain.AssemblyResolve -= assemblyHelper.ResolveAssembly; - assemblyHelper = null; - } - - [Test] - [Category("UnitTests")] - public void CanMatchBasicNumVarSingleLine() - { - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matchNumVar = "a = 5.0"; - var matches = provider.FindAllVariables(matchNumVar); - Assert.AreEqual(1, matches.Count); - Assert.IsTrue(matches.ContainsKey("a")); - Assert.AreEqual("5.0", matches["a"].Item1); - } - - [Test] - [Category("UnitTests")] - public void CanMatchBasicArrayVarSingleLine() - { - var matchArray = "a = []"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = provider.FindAllVariables(matchArray); - Assert.AreEqual(1, matches.Count); - Assert.IsTrue(matches.ContainsKey("a")); - Assert.AreEqual("[]", matches["a"].Item1); - } - - [Test] - [Category("UnitTests")] - public void CanMatchBasicDictVarSingleLine() - { - var matchDict = "a = {}"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = provider.FindAllVariables(matchDict); - Assert.AreEqual(1, matches.Count); - Assert.IsTrue(matches.ContainsKey("a")); - Assert.AreEqual("{}", matches["a"].Item1); - } - - [Test] - [Category("UnitTests")] - public void CanMatchIntSingleLine() - { - var matchDict = "a = 2"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = provider.FindAllVariables(matchDict); - Assert.AreEqual(1, matches.Count); - Assert.IsTrue(matches.ContainsKey("a")); - Assert.AreEqual("2", matches["a"].Item1); - } - - [Test] - [Category("UnitTests")] - public void CanMatchComplexDictVarSingleLine() - { - var matchDict2 = "a = { 'Alice': 7, 'Toby': 'Nuts' }"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = provider.FindAllVariables(matchDict2); - Assert.IsTrue(matches.ContainsKey("a")); - Assert.AreEqual("{ 'Alice': 7, 'Toby': 'Nuts' }", matches["a"].Item1); - } - - [Test] - [Category("UnitTests")] - public void CanMatchComplexDictVarMultiLine() - { - var matchDict2 = "\n\na = { 'Alice': 7, 'Toby': 'Nuts' }\nb = 5.0"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = provider.FindAllVariables(matchDict2); - Assert.IsTrue(matches.ContainsKey("a")); - Assert.AreEqual("{ 'Alice': 7, 'Toby': 'Nuts' }", matches["a"].Item1); - } - - [Test] - [Category("UnitTests")] - public void DoesntMatchBadVariable() - { - var matchDict2 = "a! = { 'Alice': 7, 'Toby': 'Nuts' }"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = provider.FindAllVariableAssignments(matchDict2); - Assert.AreEqual(0, matches.Count); - } - - [Test] - [Category("UnitTests")] - public void CanMatchAllVariablesSingleLine() - { - var str = "a = { 'Alice': 7, 'Toby': 'Nuts' }"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = provider.FindAllVariables(str); - - Assert.AreEqual(1, matches.Count); - Assert.AreEqual(typeof(IronPython.Runtime.PythonDictionary), matches["a"].Item3); - } - - [Test] - [Category("UnitTests")] - public void CanMatchAllVariableTypes() - { - var str = "a = { 'Alice': 7, 'Toby': 'Nuts' }\nb = {}\nc = 5.0\nd = 'pete'\ne = []"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = provider.FindAllVariables(str); - - Assert.AreEqual(5, matches.Count); - Assert.AreEqual(typeof(IronPython.Runtime.PythonDictionary), matches["a"].Item3); - Assert.AreEqual(typeof(IronPython.Runtime.PythonDictionary), matches["b"].Item3); - Assert.AreEqual(typeof(double), matches["c"].Item3); - Assert.AreEqual(typeof(string), matches["d"].Item3); - Assert.AreEqual(typeof(IronPython.Runtime.List), matches["e"].Item3); - } - - [Test] - [Category("UnitTests")] - public void CanImportLibrary() - { - var str = "\nimport System\n"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - provider.UpdateImportedTypes(str); - - Assert.AreEqual(2, provider.ImportedTypes.Count); - Assert.IsTrue(provider.ImportedTypes.ContainsKey("System")); - } - - [Test] - [Category("UnitTests")] - public void DuplicateCallsToImportShouldBeFine() - { - var str = "\nimport System\nimport System"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - provider.UpdateImportedTypes(str); - - Assert.AreEqual(2, provider.ImportedTypes.Count); - Assert.IsTrue(defaultImports.SequenceEqual(provider.ImportedTypes.Keys.ToList())); - } - - [Test] - [Category("UnitTests")] - public void CanImportSystemLibraryAndGetCompletionData() - { - var str = "\nclr.AddReference('System.Console')\nimport System\nSystem."; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - - var completionData = provider.GetCompletionData(str); - - // Randomly verify some namepsaces are in the completion list - var completionList = completionData.Select(d => d.Text); - Assert.IsTrue(completionList.Any()); - Assert.IsTrue(completionList.Intersect(new[] { "IO", "Console", "Reflection" }).Count() == 3); - Assert.AreEqual(2, provider.ImportedTypes.Count); - Assert.IsTrue(defaultImports.SequenceEqual(provider.ImportedTypes.Keys.ToList())); - } - - [Test] - [Category("UnitTests")] - public void CanImportSystemCollectionsLibraryAndGetCompletionData() - { - var str = "\nclr.AddReference('System.Collections.NonGeneric')\nimport System.Collections\nSystem.Collections."; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - - var completionData = provider.GetCompletionData(str); - var completionList = completionData.Select(d => d.Text); - Assert.IsTrue(completionList.Any()); - Assert.IsTrue(completionList.Intersect(new[] { "Hashtable", "Queue", "Stack" }).Count() == 3); - Assert.AreEqual(28, completionData.Length); - } - - - [Test] - [Category("UnitTests")] - public void CanMatchImportSystemAndLoadLibraryAndWithComment() - { - var str = "# Write your script here.\r\nimport System."; - var completionProvider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - completionProvider.UpdateImportedTypes(str); - - Assert.AreEqual(2, completionProvider.ImportedTypes.Count); - Assert.IsTrue(defaultImports.SequenceEqual(completionProvider.ImportedTypes.Keys.ToList())); - } - - [Test] - [Category("UnitTests")] - public void CanIdentifyVariableTypeAndGetCompletionData() - { - var str = "a = 5.0\na."; - - var completionProvider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var completionData = completionProvider.GetCompletionData(str); - - Assert.AreNotEqual(0, completionData.Length); - } - - [Test] - [Category("UnitTests")] - public void CanFindTypeSpecificImportsMultipleTypesSingleLine() - { - var str = "from math import sin, cos\n"; - var provider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - provider.UpdateImportedTypes(str); - Assert.AreEqual(2,provider.ImportedTypes.Count); - } - /*finish - [Test] - [Category("UnitTests")] - public void CanFindTypeSpecificImportsSingleTypeSingleLine() - { - var str = "from math import sin\n"; - - var imports = IronPythonCompletionProvider.FindTypeSpecificImportStatements(str); - Assert.IsTrue(imports.ContainsKey("sin")); - Assert.AreEqual("from math import sin", imports["sin"]); - } - - [Test] - [Category("UnitTests")] - public void CanFindTypeSpecificAutodeskImportsSingleTypeSingleLine() - { - var str = "from Autodesk.Revit.DB import Events\n"; - - var imports = IronPythonCompletionProvider.FindTypeSpecificImportStatements(str); - Assert.IsTrue(imports.ContainsKey("Events")); - Assert.AreEqual("from Autodesk.Revit.DB import Events", imports["Events"]); - } - - [Test] - [Category("UnitTests")] - public void CanFindAllTypeImports() - { - var str = "from Autodesk.Revit.DB import *\n"; - - var imports = IronPythonCompletionProvider.FindAllTypeImportStatements(str); - Assert.IsTrue(imports.ContainsKey("Autodesk.Revit.DB")); - Assert.AreEqual("from Autodesk.Revit.DB import *", imports["Autodesk.Revit.DB"]); - } - */ - [Test] - [Category("UnitTests")] - public void CanFindDifferentTypesOfImportsAndLoad() - { - var str = "from itertools import *\nimport math\nfrom sys import callstats\n"; - - var completionProvider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - completionProvider.UpdateImportedTypes(str); - - Assert.AreEqual(3, completionProvider.ImportedTypes.Count); - Assert.IsTrue((completionProvider.Scope as ScriptScope).ContainsVariable("repeat")); - Assert.IsTrue((completionProvider.Scope as ScriptScope).ContainsVariable("izip")); - Assert.IsTrue((completionProvider.Scope as ScriptScope).ContainsVariable("math")); - Assert.IsTrue((completionProvider.Scope as ScriptScope).ContainsVariable("callstats")); - } - - [Test] - [Category("UnitTests")] - public void CanFindSystemCollectionsAssignmentAndType() - { - var str = "from System.Collections import ArrayList\na = ArrayList()\n"; - var completionProvider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - completionProvider.UpdateImportedTypes(str); - completionProvider.UpdateVariableTypes(str); - - Assert.IsTrue(completionProvider.VariableTypes.ContainsKey("a")); - Assert.AreEqual(typeof(System.Collections.ArrayList), completionProvider.VariableTypes["a"]); - } - - [Test] - [Category("UnitTests")] - public void CanGetCompletionDataForArrayListVariable() - { - var str = "from System.Collections import ArrayList\na = ArrayList()\na."; - var completionProvider = new DSIronPython.IronPythonCodeCompletionProviderCore(); - var matches = completionProvider.GetCompletionData(str); - - Assert.AreNotEqual(0, matches.Length); - //TODO this was commented out. - //Assert.AreEqual(typeof(IronPython.Runtime.PythonDictionary), matches[0].); - } - - [Test] - [Category("UnitTests")] - public void VerifyIronPythonLoadedAssemblies() - { - // Verify IronPython assebmlies are loaded a single time - List matches = new List(); - - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (Assembly assembly in assemblies) - { - if (assembly.FullName.StartsWith("IronPython")) - { - if (matches.Contains(assembly.FullName)) - { - Assert.Fail("Attempted to load an IronPython assembly multiple times: " + assembly.FullName); - } - else - { - matches.Add(assembly.FullName); - } - } - } - } - } -} diff --git a/IronPythonTests/IronPythonTests.csproj b/IronPythonTests/IronPythonTests.csproj index 463ed40..3ac659e 100644 --- a/IronPythonTests/IronPythonTests.csproj +++ b/IronPythonTests/IronPythonTests.csproj @@ -20,6 +20,6 @@ - + diff --git a/IronPythonTests/PythonEvalTests.cs b/IronPythonTests/PythonEvalTests.cs index 20f6b5c..cc88eaa 100644 --- a/IronPythonTests/PythonEvalTests.cs +++ b/IronPythonTests/PythonEvalTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using NUnit.Framework; using Dynamo; +using Microsoft.Extensions.Configuration; namespace IronPythonTests { @@ -46,6 +47,61 @@ public void BindingsWork() Assert.AreEqual(expected, output); } } + [Test] + [Category("UnitTests")] + public void SysDiagProccess_AndOtherShimmedCLRTypesWork() + { + + + foreach (var pythonEvaluator in Evaluators) + { + var output = pythonEvaluator( + @" +import clr +from System.Reflection import Assembly +from System.Diagnostics import Process +dynamoCore = Assembly.Load(""DynamoCore"") +version_long = dynamoCore.GetName().Version.Major.ToString() +proc = Process.GetCurrentProcess().ProcessName +OUT = (version_long,proc) +", + new ArrayList(), + new ArrayList() + ); + Assert.AreEqual("3", (output as IList)[0]); + //we do this because the process name can change depending + //on where tests are running. + Assert.IsNotEmpty((string?)(output as IList)[1]); + } + } + [Test] + [Category("UnitTests")] + public void ConfigLoadShimsCanBeDisabled() + { + + var configb = new ConfigurationBuilder(); + var config = configb.AddInMemoryCollection(new Dictionary() + { + {"config:CONFIG_ENABLE_NET48SHIMCOMPAT","false" } + }).Build(); + + var evaluator = new DSIronPython.IronPythonEvaluator(config); + + var e = Assert.Throws(() => + { + var output = evaluator.Evaluate( + @" +import clr +from System.Reflection import Assembly +from System.Diagnostics import Process +", + new ArrayList(), + new ArrayList() + ); + }); + StringAssert.Contains("ImportError", e.Message); + } + [Test] [Category("UnitTests")] diff --git a/IronPythonTests/README.md b/IronPythonTests/README.md deleted file mode 100644 index 798d300..0000000 --- a/IronPythonTests/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## IronPython Tests ## - -This project has been disabled from the main Dynamo solution because Iron Python is no longer distributed with Dynamo and is no longer maintained. -In case Iron Python needs to be re-enabled follow the listed steps: -1. Re-enable the project to build within the solution - just add back the `.Build.0` configurations in the Dynamo.sln (do the same thing for DSIronPython and IronPythonExtension) -2. Run the IronPython tests to make sure they pass -3. Modify the dll list at https://git.autodesk.com/Dynamo/DynamoBuildscripts/blob/master/eoJenkinsfile#L103 \ No newline at end of file diff --git a/IronPythonTests/TestServices.dll.config b/IronPythonTests/TestServices.dll.config index b9623ed..751fdb3 100644 --- a/IronPythonTests/TestServices.dll.config +++ b/IronPythonTests/TestServices.dll.config @@ -1,7 +1,7 @@  - + \ No newline at end of file