< Summary

Information
Class: Elsa.Expressions.Python.Services.PythonNetPythonEvaluator
Assembly: Elsa.Expressions.Python
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions.Python/Services/PythonNetPythonEvaluator.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 29
Coverable lines: 29
Total lines: 73
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 6
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
EvaluateAsync()100%210%
WrapInExecuteScriptFunction(...)0%4260%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions.Python/Services/PythonNetPythonEvaluator.cs

#LineLine coverage
 1using System.Text;
 2using Elsa.Expressions.Helpers;
 3using Elsa.Expressions.Models;
 4using Elsa.Mediator.Contracts;
 5using Elsa.Expressions.Python.Contracts;
 6using Elsa.Expressions.Python.Models;
 7using Elsa.Expressions.Python.Notifications;
 8using Python.Runtime;
 9
 10namespace Elsa.Expressions.Python.Services;
 11
 12/// <summary>
 13/// Evaluates Python expressions using IronPython.
 14/// </summary>
 15public class PythonNetPythonEvaluator : IPythonEvaluator
 16{
 17    private const string ReturnVarName = "elsa_python_result_variable_name";
 18    private readonly INotificationSender _notificationSender;
 19
 20    /// <summary>
 21    /// Initializes a new instance of the <see cref="PythonNetPythonEvaluator"/> class.
 22    /// </summary>
 023    public PythonNetPythonEvaluator(INotificationSender notificationSender)
 24    {
 025        _notificationSender = notificationSender;
 026    }
 27
 28    /// <inheritdoc />
 29    public async Task<object?> EvaluateAsync(string expression, Type returnType, ExpressionExecutionContext context, Can
 30    {
 031        using var gil = Py.GIL();
 032        using var scope = Py.CreateScope();
 033        var notification = new EvaluatingPython(scope, context);
 34
 035        scope.Import("System");
 36
 37        // Add globals.
 038        scope.Set("execution_context", new ExecutionContextProxy(context));
 039        scope.Set("input", new InputProxy(context));
 040        scope.Set("output", new OutputProxy(context));
 041        scope.Set("outcome", new OutcomeProxy(context));
 42
 043        await _notificationSender.SendAsync(notification, cancellationToken);
 044        var wrappedScript = WrapInExecuteScriptFunction(expression);
 045        scope.Exec(wrappedScript);
 046        var result = scope.Get<object>(ReturnVarName);
 047        return result.ConvertTo(returnType);
 048    }
 49
 50    /// <summary>
 51    /// Wraps the user script in a function called execute_script() and returns the result of that function.
 52    /// </summary>
 53    private static string WrapInExecuteScriptFunction(string userScript, int indentationLevel = 1)
 54    {
 055        var lines = userScript.Split('\n');
 056        var wrappedScript = new StringBuilder();
 057        var indentation = new string(' ', 4 * indentationLevel);
 058        wrappedScript.AppendLine("def execute_script():");
 59
 060        foreach (var line in lines.Take(lines.Length - 1))
 061            wrappedScript.AppendLine(indentation + line);
 62
 063        var lastLine = lines.LastOrDefault() ?? "";
 64
 065        if (!lastLine.StartsWith("return"))
 066            lastLine = $"return {lastLine}";
 67
 068        wrappedScript.AppendLine(indentation + lastLine);
 69
 070        wrappedScript.AppendLine($"{ReturnVarName} = execute_script()");
 071        return wrappedScript.ToString();
 72    }
 73}