-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathRuntimeManager.cs
152 lines (140 loc) · 5.23 KB
/
RuntimeManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
using System;
using System.IO;
using System.Reflection;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using Python.Runtime;
using SystemPath = System.IO.Path;
namespace Bonsai.Scripting.Python
{
/// <summary>
/// Provides functionality for initializing and managing resources held
/// by the Python runtime and an interface for scheduling work in the
/// runtime scheduler.
/// </summary>
public class RuntimeManager : IDisposable
{
const char AssemblySeparator = ':';
readonly EventLoopScheduler runtimeScheduler;
readonly IObserver<RuntimeManager> runtimeObserver;
IntPtr threadState;
internal RuntimeManager(string pythonHome, string scriptPath, IObserver<RuntimeManager> observer)
{
runtimeScheduler = new EventLoopScheduler();
runtimeObserver = observer;
Schedule(() =>
{
Initialize(pythonHome);
threadState = PythonEngine.BeginAllowThreads();
MainModule = CreateModule(scriptPath: scriptPath);
observer.OnNext(this);
});
}
internal PyModule MainModule { get; private set; }
internal static IObservable<RuntimeManager> RuntimeSource { get; } = Observable.Using(
() => SubjectManager.ReserveSubject(),
disposable => disposable.Subject)
.Take(1);
static bool IsEmbeddedResourcePath(string path)
{
var separatorIndex = path.IndexOf(AssemblySeparator);
return separatorIndex >= 0 && !SystemPath.IsPathRooted(path);
}
static string ReadAllText(string path)
{
if (IsEmbeddedResourcePath(path))
{
var nameElements = path.Split(new[] { AssemblySeparator }, 2);
if (string.IsNullOrEmpty(nameElements[0]))
{
throw new InvalidOperationException(
"The embedded resource path \"" + path +
"\" must be qualified with a valid assembly name.");
}
var assembly = Assembly.Load(nameElements[0]);
var resourceName = string.Join(ExpressionHelper.MemberSeparator, nameElements);
using var resourceStream = assembly.GetManifestResourceStream(resourceName);
if (resourceStream == null)
{
throw new InvalidOperationException(
"The specified embedded resource \"" + nameElements[1] +
"\" was not found in assembly \"" + nameElements[0] + "\"");
}
using var reader = new StreamReader(resourceStream);
return reader.ReadToEnd();
}
else return File.ReadAllText(path);
}
internal static DynamicModule CreateModule(string name = "", string scriptPath = "")
{
using (Py.GIL())
{
var module = new DynamicModule(name);
if (!string.IsNullOrEmpty(scriptPath))
{
try
{
var code = ReadAllText(scriptPath);
module.Exec(code);
}
catch (Exception)
{
module.Dispose();
throw;
}
}
return module;
}
}
internal void Schedule(Action action)
{
runtimeScheduler.Schedule(() =>
{
try { action(); }
catch (Exception ex)
{
runtimeObserver.OnError(ex);
}
});
}
static void Initialize(string path)
{
if (!PythonEngine.IsInitialized)
{
path = EnvironmentHelper.GetEnvironmentPath(path);
var config = EnvironmentHelper.GetEnvironmentConfig(path);
Runtime.PythonDLL = EnvironmentHelper.GetPythonDLL(config);
EnvironmentHelper.SetRuntimePath(config.PythonHome);
PythonEngine.PythonHome = config.PythonHome;
if (config.PythonHome != path)
{
PythonEngine.PythonPath = EnvironmentHelper.GetPythonPath(config);
}
PythonEngine.Initialize();
}
}
/// <summary>
/// Shutdown the thread and release all resources associated with the Python runtime.
/// All remaining work scheduled after shutdown is abandoned.
/// </summary>
public void Dispose()
{
Schedule(() =>
{
if (PythonEngine.IsInitialized)
{
if (MainModule != null)
{
using (Py.GIL())
{
MainModule.Dispose();
}
}
PythonEngine.EndAllowThreads(threadState);
PythonEngine.Shutdown();
}
runtimeScheduler.Dispose();
});
}
}
}