< 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: 34
Coverable lines: 34
Total lines: 87
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 8
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%
.ctor(...)100%210%
EvaluateAsync()0%620%
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 Elsa.Expressions.Python.Options;
 9using Microsoft.Extensions.Options;
 10using Python.Runtime;
 11
 12namespace Elsa.Expressions.Python.Services;
 13
 14/// <summary>
 15/// Evaluates Python expressions using IronPython.
 16/// </summary>
 17public class PythonNetPythonEvaluator : IPythonEvaluator
 18{
 19    private const string ReturnVarName = "elsa_python_result_variable_name";
 20    private readonly INotificationSender _notificationSender;
 21    private readonly IOptions<PythonOptions> _options;
 22
 23    /// <summary>
 24    /// Initializes a new instance of the <see cref="PythonNetPythonEvaluator"/> class.
 25    /// </summary>
 026    public PythonNetPythonEvaluator(INotificationSender notificationSender) : this(notificationSender, Microsoft.Extensi
 27    {
 028    }
 29
 30    /// <summary>
 31    /// Initializes a new instance of the <see cref="PythonNetPythonEvaluator"/> class.
 32    /// </summary>
 033    public PythonNetPythonEvaluator(INotificationSender notificationSender, IOptions<PythonOptions> options)
 34    {
 035        _notificationSender = notificationSender;
 036        _options = options;
 037    }
 38
 39    /// <inheritdoc />
 40    public async Task<object?> EvaluateAsync(string expression, Type returnType, ExpressionExecutionContext context, Can
 41    {
 042        if (!_options.Value.AllowHostCodeExecution)
 043            throw new InvalidOperationException("Python.NET workflow expression execution is disabled. Set PythonOptions
 44
 045        using var gil = Py.GIL();
 046        using var scope = Py.CreateScope();
 047        var notification = new EvaluatingPython(scope, context);
 48
 049        scope.Import("System");
 50
 51        // Add globals.
 052        scope.Set("execution_context", new ExecutionContextProxy(context));
 053        scope.Set("input", new InputProxy(context));
 054        scope.Set("output", new OutputProxy(context));
 055        scope.Set("outcome", new OutcomeProxy(context));
 56
 057        await _notificationSender.SendAsync(notification, cancellationToken);
 058        var wrappedScript = WrapInExecuteScriptFunction(expression);
 059        scope.Exec(wrappedScript);
 060        var result = scope.Get<object>(ReturnVarName);
 061        return result.ConvertTo(returnType);
 062    }
 63
 64    /// <summary>
 65    /// Wraps the user script in a function called execute_script() and returns the result of that function.
 66    /// </summary>
 67    private static string WrapInExecuteScriptFunction(string userScript, int indentationLevel = 1)
 68    {
 069        var lines = userScript.Split('\n');
 070        var wrappedScript = new StringBuilder();
 071        var indentation = new string(' ', 4 * indentationLevel);
 072        wrappedScript.AppendLine("def execute_script():");
 73
 074        foreach (var line in lines.Take(lines.Length - 1))
 075            wrappedScript.AppendLine(indentation + line);
 76
 077        var lastLine = lines.LastOrDefault() ?? "";
 78
 079        if (!lastLine.StartsWith("return"))
 080            lastLine = $"return {lastLine}";
 81
 082        wrappedScript.AppendLine(indentation + lastLine);
 83
 084        wrappedScript.AppendLine($"{ReturnVarName} = execute_script()");
 085        return wrappedScript.ToString();
 86    }
 87}