using Python.Runtime; using Indexer.Models; namespace Indexer.ScriptContainers; public class PythonScriptable : IScriptContainer { public ScriptToolSet ToolSet { get; set; } public PyObject? pyToolSet; public PyModule scope; public dynamic sys; public string source; public bool SourceLoaded { get; set; } public ScriptUpdateInfo UpdateInfo { get; set; } public ILogger _logger { get; set; } public PythonScriptable(ScriptToolSet toolSet, ILogger logger) { string runtime = toolSet.Configuration.PythonRuntime; Runtime.PythonDLL ??= runtime; _logger = logger; SourceLoaded = false; if (!PythonEngine.IsInitialized) { PythonEngine.Initialize(); PythonEngine.BeginAllowThreads(); } ToolSet = toolSet; source = File.ReadAllText(ToolSet.FilePath); string fullPath = Path.GetFullPath(ToolSet.FilePath); string? scriptDir = Path.GetDirectoryName(fullPath); using (Py.GIL()) { scope = Py.CreateScope(); sys = Py.Import("sys"); if (scriptDir is not null) { sys.path.append(scriptDir); } } Init(); } public int Init() { return ExecFunction("init"); } public int Update(ICallbackInfos callbackInfos) { return ExecFunction("update"); } public int Stop() { return ExecFunction("stop"); } public int ExecFunction(string name, ICallbackInfos? callbackInfos = null) { int error = 0; int retryCounter = 0; retry: try { using (Py.GIL()) { pyToolSet = ToolSet.ToPython(); pyToolSet.SetAttr("callbackInfos", callbackInfos.ToPython()); scope.Set("toolset", pyToolSet); if (!SourceLoaded) { scope.Exec(source); SourceLoaded = true; } scope.Exec($"{name}(toolset)"); } } catch (Exception ex) { UpdateInfo = new() { DateTime = DateTime.Now, Successful = false, Exception = ex }; if (retryCounter < 3) { _logger.LogWarning("Execution of {name} function in script {Toolset.filePath} failed to an exception {ex}", [name, ToolSet.FilePath, ex]); retryCounter++; goto retry; } _logger.LogError("Execution of {name} function in script {Toolset.filePath} failed to an exception {ex}", [name, ToolSet.FilePath, ex]); error = 1; } UpdateInfo = new() { DateTime = DateTime.Now, Successful = true }; return error; } public static bool IsScript(string fileName) { return fileName.EndsWith(".py"); } }