142 lines
5.9 KiB
C#
142 lines
5.9 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|