23 using System.Collections.Generic;
33 private static bool IncludeSystemPackages;
34 private static string PathToVirtualEnv;
37 private static bool _isInitialized;
40 private static List<string> _pendingPathAdditions =
new List<string>();
42 private static string _algorithmLocation;
51 public static void Initialize(
bool beginAllowThreads =
true)
55 Log.
Trace($
"PythonInitializer.Initialize(): {Messages.PythonInitializer.Start}...");
56 PythonEngine.Initialize();
58 if (beginAllowThreads)
61 PythonEngine.BeginAllowThreads();
64 _isInitialized =
true;
66 ConfigurePythonPaths();
68 TryInitPythonVirtualEnvironment();
69 Log.
Trace($
"PythonInitializer.Initialize(): {Messages.PythonInitializer.Ended}");
80 Log.
Trace($
"PythonInitializer.Shutdown(): {Messages.PythonInitializer.Start}");
81 _isInitialized =
false;
85 var pyLock = Py.GIL();
86 PythonEngine.Shutdown();
93 Log.
Trace($
"PythonInitializer.Shutdown(): {Messages.PythonInitializer.Ended}");
103 if (paths.IsNullOrEmpty())
109 _pendingPathAdditions.AddRange(paths.Where(x => !_pendingPathAdditions.Contains(x)));
115 using dynamic sys = Py.Import(
"sys");
116 using var locals =
new PyDict();
117 locals.SetItem(
"sys", sys);
120 using var pythonCurrentPath = PythonEngine.Eval(
"sys.path", locals: locals);
121 var currentPath = pythonCurrentPath.As<List<string>>();
122 _pendingPathAdditions = _pendingPathAdditions.Where(x => !currentPath.Contains(x.Replace(
'\\',
'/'))).ToList();
125 var insertionIndex = 0;
126 if (!_algorithmLocation.IsNullOrEmpty())
128 insertionIndex = currentPath.IndexOf(_algorithmLocation.Replace(
'\\',
'/')) + 1;
130 if (insertionIndex == 0)
134 _pendingPathAdditions.Remove(_algorithmLocation);
135 _pendingPathAdditions.Add(_algorithmLocation);
140 if (!_pendingPathAdditions.IsNullOrEmpty())
142 var code =
string.Join(
";", _pendingPathAdditions
143 .Select(s => $
"sys.path.insert({insertionIndex}, '{s}')")).Replace(
'\\',
'/');
144 PythonEngine.Exec(code, locals: locals);
146 _pendingPathAdditions.Clear();
161 if (!_algorithmLocation.IsNullOrEmpty())
166 if (!Directory.Exists(algorithmLocation))
168 Log.
Error($
@"PythonInitializer.AddAlgorithmLocationPath(): {
169 Messages.PythonInitializer.UnableToLocateAlgorithm(algorithmLocation)}");
173 _algorithmLocation = algorithmLocation;
182 _algorithmLocation =
null;
194 if (
string.IsNullOrEmpty(pathToVirtualEnv))
199 if(!Directory.Exists(pathToVirtualEnv))
201 Log.
Error($
@"PythonIntializer.ActivatePythonVirtualEnvironment(): {
202 Messages.PythonInitializer.VirutalEnvironmentNotFound(pathToVirtualEnv)}");
206 PathToVirtualEnv = pathToVirtualEnv;
208 bool? includeSystemPackages =
null;
209 var configFile =
new FileInfo(Path.Combine(PathToVirtualEnv,
"pyvenv.cfg"));
210 if(configFile.Exists)
212 foreach (var line
in File.ReadAllLines(configFile.FullName))
214 if (line.Contains(
"include-system-site-packages", StringComparison.InvariantCultureIgnoreCase))
217 var equalsIndex = line.IndexOf(
'=', StringComparison.InvariantCultureIgnoreCase);
218 if(equalsIndex != -1 && line.Length > (equalsIndex + 1) &&
bool.TryParse(line.Substring(equalsIndex + 1).Trim(), out var result))
220 includeSystemPackages = result;
227 if(!includeSystemPackages.HasValue)
229 includeSystemPackages =
true;
230 Log.
Error($
@"PythonIntializer.ActivatePythonVirtualEnvironment(): {
231 Messages.PythonInitializer.FailedToFindSystemPackagesConfiguration(pathToVirtualEnv, configFile)}");
235 Log.
Trace($
@"PythonIntializer.ActivatePythonVirtualEnvironment(): {
236 Messages.PythonInitializer.SystemPackagesConfigurationFound(pathToVirtualEnv, includeSystemPackages.Value)}");
239 if (!includeSystemPackages.Value)
241 PythonEngine.SetNoSiteFlag();
244 IncludeSystemPackages = includeSystemPackages.Value;
246 TryInitPythonVirtualEnvironment();
250 private static void TryInitPythonVirtualEnvironment()
252 if (!_isInitialized ||
string.IsNullOrEmpty(PathToVirtualEnv))
259 using dynamic sys = Py.Import(
"sys");
260 using var locals =
new PyDict();
261 locals.SetItem(
"sys", sys);
263 if (!IncludeSystemPackages)
265 var currentPath = (List<string>)sys.path.As<List<string>>();
266 var toRemove =
new List<string>(currentPath.Where(s => s.Contains(
"site-packages", StringComparison.InvariantCultureIgnoreCase)));
267 if (toRemove.Count > 0)
269 var code =
string.Join(
";", toRemove.Select(s => $
"sys.path.remove('{s}')"));
270 PythonEngine.Exec(code, locals: locals);
275 sys.prefix = PathToVirtualEnv;
276 sys.exec_prefix = PathToVirtualEnv;
278 using dynamic site = Py.Import(
"site");
280 site.PREFIXES =
new List<PyObject> { sys.prefix, sys.exec_prefix };
284 if (IncludeSystemPackages)
287 PythonEngine.Exec(@$
"if sys.path[-1].startswith('{PathToVirtualEnv}'):
288 sys.path.insert(0, sys.path.pop())", locals: locals);
293 using dynamic os = Py.Import(
"os");
294 var path =
new List<string>();
295 foreach (var p
in sys.path)
300 Log.
Debug($
"PythonIntializer.InitPythonVirtualEnvironment(): PYTHONHOME: {os.getenv("PYTHONHOME
")}." +
301 $
" PYTHONPATH: {os.getenv("PYTHONPATH
")}." +
302 $
" sys.executable: {sys.executable}." +
303 $
" sys.prefix: {sys.prefix}." +
304 $
" sys.base_prefix: {sys.base_prefix}." +
305 $
" sys.exec_prefix: {sys.exec_prefix}." +
306 $
" sys.base_exec_prefix: {sys.base_exec_prefix}." +
307 $
" sys.path: [{string.Join(",
", path)}]");
315 private static void ConfigurePythonPaths()
317 var pythonAdditionalPaths =
new List<string> { Environment.CurrentDirectory };
318 pythonAdditionalPaths.AddRange(
Config.GetValue(
"python-additional-paths", Enumerable.Empty<
string>()));
321 var pathExists = Directory.Exists(path);
324 Log.Error($
"PythonInitializer.ConfigurePythonPaths(): {Messages.PythonInitializer.PythonPathNotFound(path)}");