Apply new architecture with base plugin dopt.SharpPython #3

Merged
foefl merged 3 commits from new_arch into main 2025-06-19 07:56:08 +00:00
9 changed files with 129 additions and 160 deletions
Showing only changes of commit fe03ad41fb - Show all commits

3
.gitignore vendored
View File

@ -3,6 +3,9 @@
##
## Get latest from `dotnet new gitignore`
# local python runtime
python/
# dotenv files
.env

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "doptPlugin", "doptPlugin\doptPlugin.csproj", "{A7CD5D7D-A70B-45E3-A663-2F7C7A6D041A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A7CD5D7D-A70B-45E3-A663-2F7C7A6D041A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7CD5D7D-A70B-45E3-A663-2F7C7A6D041A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7CD5D7D-A70B-45E3-A663-2F7C7A6D041A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7CD5D7D-A70B-45E3-A663-2F7C7A6D041A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {437CE6B1-EBF0-4E0B-86AD-21EB2E9FAE74}
EndGlobalSection
EndGlobal

31
dopt.TOM.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dopt.TOMTests", "dopt.TOMTests\dopt.TOMTests.csproj", "{28496580-EF8C-4D54-8FEF-BAA2E4970367}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{28496580-EF8C-4D54-8FEF-BAA2E4970367}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28496580-EF8C-4D54-8FEF-BAA2E4970367}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28496580-EF8C-4D54-8FEF-BAA2E4970367}.Debug|x64.ActiveCfg = Debug|Any CPU
{28496580-EF8C-4D54-8FEF-BAA2E4970367}.Debug|x64.Build.0 = Debug|Any CPU
{28496580-EF8C-4D54-8FEF-BAA2E4970367}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28496580-EF8C-4D54-8FEF-BAA2E4970367}.Release|Any CPU.Build.0 = Release|Any CPU
{28496580-EF8C-4D54-8FEF-BAA2E4970367}.Release|x64.ActiveCfg = Release|Any CPU
{28496580-EF8C-4D54-8FEF-BAA2E4970367}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {437CE6B1-EBF0-4E0B-86AD-21EB2E9FAE74}
EndGlobalSection
EndGlobal

52
dopt.TOM/Plugin.cs Normal file
View File

@ -0,0 +1,52 @@
using Python.Runtime;
using System;
using static System.Net.Mime.MediaTypeNames;
using System.IO;
using System.Reflection;
using System.Dynamic;
namespace dopt.TOM
{
public class Plugin : SharpPython.BasePlugin
{
internal const string langMainBaseFolderName = "bin";
internal const string langMainStopSearchFolderName = "python";
internal const string doptTOMPluginLibraryUsage = "1";
protected dynamic tomWrapperPipelines;
public Plugin(string runtimePath) : base(SharpPython.PyOptimLevels.O, threaded: false, runtimePath: runtimePath, verbose: true)
{
base.Initialise();
using (Py.GIL())
{
tomWrapperPipelines = Py.Import("tom_plugin.pipeline");
}
}
//private void Run() // for testing purposes
//{
// if (!_initialised) Initialise();
// using (Py.GIL())
// {
// dynamic np = Py.Import("numpy");
// Console.WriteLine(np.cos(np.pi * 2));
// dynamic rand = Py.Import("random");
// Console.WriteLine(rand.randint(1, 2));
// dynamic pathlib = Py.Import("pathlib");
// dynamic ret = pathlib.Path.cwd();
// Console.WriteLine(ret);
// }
// Finalise();
//}
public void RunOnCSV(string identifier, string filename)
{
AssertNotDisposed();
using (Py.GIL())
{
dynamic tomWrapperPipeline = Py.Import("tom_plugin.pipeline");
tomWrapperPipeline.run_on_csv_data(identifier, filename);
}
}
}
}

View File

@ -4,10 +4,11 @@
<TargetFramework>netstandard2.0</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<Version>0.1.1-beta1</Version>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="pythonnet" Version="3.0.4" />
<PackageReference Include="dopt.SharpPython" Version="0.4.2" />
</ItemGroup>
</Project>

View File

@ -0,0 +1 @@
[assembly: Parallelize(Scope = ExecutionScope.ClassLevel)]

11
dopt.TOMTests/Test1.cs Normal file
View File

@ -0,0 +1,11 @@
namespace dopt.TOMTests
{
[TestClass]
public sealed class Test1
{
[TestMethod]
public void TestMethod1()
{
}
}
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnableMSTestRunner>true</EnableMSTestRunner>
<OutputType>Exe</OutputType>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<!--
Displays error on console in addition to the log file. Note that this feature comes with a performance impact.
For more information, visit https://learn.microsoft.com/dotnet/core/testing/unit-testing-platform-integration-dotnet-test#show-failure-per-test
-->
<TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.12.6" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" Version="1.4.3" />
<PackageReference Include="MSTest" Version="3.6.4" />
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
</Project>

View File

@ -1,134 +0,0 @@
using Python.Runtime;
using System;
using static System.Net.Mime.MediaTypeNames;
using System.IO;
using System.Reflection;
namespace doptPlugin
{
public class Plugin
{
internal const string relPathToPyDll = @".\python\python311.dll";
internal const string relPathToExtensionDlls = @".\python\DLLs";
internal const string relPathToVirtualEnv = @".\python\.venv";
internal const string relPathToPythonInit = @".\python\";
internal const string langMainBaseFolderName = "bin";
internal const string langMainStopSearchFolderName = "python";
internal const string doptTOMPluginLibraryUsage = "1";
private IntPtr _threadState;
private bool _prepared = false;
private bool _initialised = false;
public Plugin()
{
PrepareEnv();
}
internal void PrepareEnv()
{
Type t = typeof(Plugin);
Assembly assembly = t.Assembly;
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);
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);
Environment.SetEnvironmentVariable("LANG_MAIN_BASE_FOLDERNAME", langMainBaseFolderName, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("LANG_MAIN_STOP_SEARCH_FOLDERNAME", langMainStopSearchFolderName, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("DOPT_TOM_PLUGIN_LIBRARY_USAGE", doptTOMPluginLibraryUsage, EnvironmentVariableTarget.Process);
var path = Environment.GetEnvironmentVariable("PATH").TrimEnd(';');
path = string.IsNullOrEmpty(path) ? pathToVirtualEnv : path + ";" + pathToVirtualEnv;
Environment.SetEnvironmentVariable("PATH", path, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONHOME", pathToVirtualEnv, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONPATH", $@"{pathToPythonInit}\Lib;{pathToVirtualEnv}\Lib\site-packages;{pathToVirtualEnv}\Lib;{pathToExtensionDlls}", EnvironmentVariableTarget.Process);
PythonEngine.PythonHome = pathToVirtualEnv;
PythonEngine.PythonPath = Environment.GetEnvironmentVariable("PYTHONPATH", EnvironmentVariableTarget.Process);
_prepared = true;
}
internal void Initialise()
{
if (!_prepared) PrepareEnv();
PythonEngine.Initialize();
_threadState = PythonEngine.BeginAllowThreads(); // release GIL for use in other threads
_initialised = true;
}
internal void Finalise()
{
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);
_initialised = false;
}
private void Run() // for testing purposes
{
if (!_initialised) Initialise();
using (Py.GIL())
{
dynamic np = Py.Import("numpy");
Console.WriteLine(np.cos(np.pi * 2));
dynamic rand = Py.Import("random");
Console.WriteLine(rand.randint(1, 2));
dynamic pathlib = Py.Import("pathlib");
dynamic ret = pathlib.Path.cwd();
Console.WriteLine(ret);
}
Finalise();
}
public void RunOnCSV(string identifier, string filename)
{
if (!_initialised) Initialise();
using (Py.GIL())
{
dynamic tomWrapperPipeline = Py.Import("tom_plugin.pipeline");
tomWrapperPipeline.run_on_csv_data(identifier, filename);
}
Finalise();
}
}
}