using Python.Runtime; using System; using System.IO; using System.Reflection; namespace dopt.SharpPython { public class PythonCallDisposedException: Exception { public PythonCallDisposedException() { } public PythonCallDisposedException(string message) : base(message) { } } public abstract class BasePlugin { protected const string relPathToPyDll = @".\python\python311.dll"; protected const string relPathToExtensionDlls = @".\python\DLLs"; //protected const string relPathToVirtualEnv = @".\python\.venv"; protected const string relPathToPythonInit = @".\python\"; private IntPtr _threadState; protected bool _prepared = false; protected bool _initialised = false; protected bool _disposed = false; private bool verbose; public BasePlugin(bool verbose = false) { this.verbose = verbose; PrepareEnv(); } private void PrepareEnv() { Type t = typeof(BasePlugin); Assembly assembly = t.Assembly; if (this.verbose) { Console.WriteLine($"Location:\n{assembly.Location}\n---\nCodeBase:\n{assembly.CodeBase}"); } string assemPath = assembly.CodeBase; if (assemPath.StartsWith("file:///")) { assemPath = assemPath.Replace("file:///", ""); } string dllDir = System.IO.Path.GetDirectoryName(assemPath); if (verbose) { Console.WriteLine($"Current DLL Dir: {dllDir}"); } string pathToPyDll = Path.Combine(dllDir, relPathToPyDll); string pathToExtensionDlls = Path.Combine(dllDir, relPathToExtensionDlls); //string pathToVirtualEnv = Path.Combine(dllDir, relPathToVirtualEnv); string pathToPythonInit = Path.Combine(dllDir, relPathToPythonInit); if (!File.Exists(pathToPyDll)) { throw new FileNotFoundException($"Python DLL not found at: {pathToPyDll}"); } else if (!Directory.Exists(pathToExtensionDlls)) { throw new DirectoryNotFoundException($"Python Extension Dlls not found at: {pathToExtensionDlls}"); } //else if (!Directory.Exists(pathToVirtualEnv)) //{ // throw new DirectoryNotFoundException($"Python Virtual Env not found at: {pathToVirtualEnv}"); //} else if (!Directory.Exists(pathToPythonInit)) { throw new DirectoryNotFoundException($"Python base directory not found at: {pathToPythonInit}"); } // environment preparation Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", pathToPyDll, EnvironmentVariableTarget.Process); var path = Environment.GetEnvironmentVariable("PATH").TrimEnd(';'); //path = string.IsNullOrEmpty(path) ? pathToVirtualEnv : path + ";" + pathToVirtualEnv; path = string.IsNullOrEmpty(path) ? pathToPythonInit : path + ";" + pathToPythonInit; Environment.SetEnvironmentVariable("PATH", path, EnvironmentVariableTarget.Process); //Environment.SetEnvironmentVariable("PYTHONHOME", pathToVirtualEnv, EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("PYTHONHOME", pathToPythonInit, EnvironmentVariableTarget.Process); //Environment.SetEnvironmentVariable("PYTHONPATH", $@"{pathToPythonInit}\Lib;{pathToVirtualEnv}\Lib\site-packages;{pathToVirtualEnv}\Lib;{pathToExtensionDlls}", EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("PYTHONPATH", $@"{pathToPythonInit}\Lib;{pathToPythonInit}\Lib\site-packages;{pathToExtensionDlls}", EnvironmentVariableTarget.Process); //PythonEngine.PythonHome = pathToVirtualEnv; PythonEngine.PythonHome = pathToPythonInit; PythonEngine.PythonPath = Environment.GetEnvironmentVariable("PYTHONPATH", EnvironmentVariableTarget.Process); _prepared = true; } protected void Initialise() { if (_prepared) PrepareEnv(); PythonEngine.Initialize(); Codecs.RegisterAll(verbose); _threadState = PythonEngine.BeginAllowThreads(); // release GIL for use in other threads _initialised = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool disposing) { PythonEngine.EndAllowThreads(_threadState); /* * The shutdown method leads to a Exception because the unsafe BinaryFormatter is used. * Therefore the unsafe formatter option can be allowed temporarily. A fix is already applied, * but not yet published. * see: https://github.com/pythonnet/pythonnet/issues/2282 */ AppContext.SetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", true); PythonEngine.Shutdown(); AppContext.SetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", false); if (disposing) { _initialised = false; _disposed = true; } } protected void AssertNotDisposed() { if ( _disposed ) { string message = @"It was tried to operate on a disposed instance of the plugin, which is not supported. A new instance must be instatiated."; message = message.Replace("\n", "").Replace("\t", ""); throw new PythonCallDisposedException(message); } } ~BasePlugin() { Dispose(false); } } }