From e14e08922cab63d4fade87edfe7cc969b8fcf9b2 Mon Sep 17 00:00:00 2001 From: Trygve Wastvedt Date: Thu, 23 Jan 2025 14:41:38 -0600 Subject: [PATCH] 8150: DYN-8176: Fixes from PythonNet3 (#15774) --- src/Libraries/DSCPython/CPythonEvaluator.cs | 90 +++++++++++-------- .../DynamoPythonTests/PythonEditTests.cs | 2 - 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/Libraries/DSCPython/CPythonEvaluator.cs b/src/Libraries/DSCPython/CPythonEvaluator.cs index e52b1cc84ea..4a5de711592 100644 --- a/src/Libraries/DSCPython/CPythonEvaluator.cs +++ b/src/Libraries/DSCPython/CPythonEvaluator.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using Autodesk.DesignScript.Runtime; @@ -145,6 +146,8 @@ public class CPythonEvaluator : Dynamo.PythonServices.PythonEngine private static PyScope globalScope; private static DynamoLogger dynamoLogger; + private static string path; + internal static DynamoLogger DynamoLogger { get { // Session is null when running unit tests. @@ -261,60 +264,66 @@ public override object Evaluate( { PythonEngine.Initialize(); PythonEngine.BeginAllowThreads(); - - } + using (Py.GIL()) + using (PyScope scope = Py.CreateScope()) { - if (globalScope == null) - { - globalScope = CreateGlobalScope(); - } + scope.Exec("import sys" + Environment.NewLine + "path = str(sys.path)"); + path = scope.Get("path"); + } + } - using (PyScope scope = Py.CreateScope()) + using (Py.GIL()) + { + globalScope ??= CreateGlobalScope(); + using (PyScope scope = Py.CreateScope()) + { + if (path is not null) { - // Reset the 'sys.path' value to the default python paths on node evaluation. - var pythonNodeSetupCode = "import sys" + Environment.NewLine + "sys.path = sys.path[0:3]"; + // Reset the 'sys.path' value to the default python paths on node evaluation. See https://github.com/DynamoDS/Dynamo/pull/10977. + var pythonNodeSetupCode = "import sys" + Environment.NewLine + $"sys.path = {path}"; scope.Exec(pythonNodeSetupCode); + } - ProcessAdditionalBindings(scope, bindingNames, bindingValues); + ProcessAdditionalBindings(scope, bindingNames, bindingValues); - int amt = Math.Min(bindingNames.Count, bindingValues.Count); + int amt = Math.Min(bindingNames.Count, bindingValues.Count); - for (int i = 0; i < amt; i++) - { - scope.Set((string)bindingNames[i], InputMarshaler.Marshal(bindingValues[i]).ToPython()); - } + for (int i = 0; i < amt; i++) + { + scope.Set((string)bindingNames[i], InputMarshaler.Marshal(bindingValues[i]).ToPython()); + } - try - { - OnEvaluationBegin(scope, code, bindingValues); - scope.Exec(code); - var result = scope.Contains("OUT") ? scope.Get("OUT") : null; + try + { + OnEvaluationBegin(scope, code, bindingValues); + scope.Exec(code); + var result = scope.Contains("OUT") ? scope.Get("OUT") : null; - return OutputMarshaler.Marshal(result); + return OutputMarshaler.Marshal(result); + } + catch (Exception e) + { + evaluationSuccess = false; + var traceBack = GetTraceBack(e); + if (!string.IsNullOrEmpty(traceBack)) + { + // Throw a new error including trace back info added to the message + throw new InvalidOperationException($"{e.Message} {traceBack}", e); } - catch (Exception e) + else { - evaluationSuccess = false; - var traceBack = GetTraceBack(e); - if (!string.IsNullOrEmpty(traceBack)) - { - // Throw a new error including trace back info added to the message - throw new InvalidOperationException($"{e.Message} {traceBack}", e); - } - else - { #pragma warning disable CA2200 // Rethrow to preserve stack details - throw e; + throw e; #pragma warning restore CA2200 // Rethrow to preserve stack details - } - } - finally - { - OnEvaluationEnd(evaluationSuccess, scope, code, bindingValues); } } + finally + { + OnEvaluationEnd(evaluationSuccess, scope, code, bindingValues); + } } + } } public static object EvaluatePythonScript( @@ -476,6 +485,13 @@ internal override void RegisterHostDataMarshalers() } } var unmarshalled = pyObj.AsManagedObject(typeof(object)); + + // Avoid calling this marshaler infinitely. + if (unmarshalled is PyObject) + { + return unmarshalled; + } + return dataMarshalerToUse.Marshal(unmarshalled); } } diff --git a/test/Libraries/DynamoPythonTests/PythonEditTests.cs b/test/Libraries/DynamoPythonTests/PythonEditTests.cs index af33cd6bc14..c0d3f53548f 100644 --- a/test/Libraries/DynamoPythonTests/PythonEditTests.cs +++ b/test/Libraries/DynamoPythonTests/PythonEditTests.cs @@ -633,7 +633,6 @@ public void VerifySysPathValueForCPythonEngine() var sysPathList = GetFlattenedPreviewValues(firstPythonNodeGUID); // Verify that the custom path is added to the 'sys.path'. - Assert.AreEqual(sysPathList.Count(), 4); Assert.AreEqual(sysPathList.Last(), "C:\\Program Files\\dotnet"); // Change the python engine for the 2nd node and verify that the custom path is not reflected in the 2nd node. @@ -642,7 +641,6 @@ public void VerifySysPathValueForCPythonEngine() var pynode = nodeModel as PythonNode; UpdatePythonEngineAndRun(pynode, PythonEngineManager.CPython3EngineName); sysPathList = GetFlattenedPreviewValues(secondPythonNodeGUID); - Assert.AreEqual(sysPathList.Count(), 3); Assert.AreNotEqual(sysPathList.Last(), "C:\\Program Files\\dotnet"); }