Skip to content

Commit a9699a4

Browse files
committed
Add cross-platform support for local python home
1 parent c5a1cdb commit a9699a4

File tree

3 files changed

+95
-45
lines changed

3 files changed

+95
-45
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.IO;
2+
3+
namespace Bonsai.Scripting.Python
4+
{
5+
internal class EnvironmentConfig
6+
{
7+
private EnvironmentConfig()
8+
{
9+
}
10+
11+
public EnvironmentConfig(string pythonHome, string pythonVersion)
12+
{
13+
Path = pythonHome;
14+
PythonHome = pythonHome;
15+
PythonVersion = pythonVersion;
16+
}
17+
18+
public string Path { get; private set; }
19+
20+
public string PythonHome { get; private set; }
21+
22+
public string PythonVersion { get; private set; }
23+
24+
public static EnvironmentConfig FromConfigFile(string configFileName)
25+
{
26+
var config = new EnvironmentConfig();
27+
config.Path = System.IO.Path.GetDirectoryName(configFileName);
28+
using var configReader = new StreamReader(File.OpenRead(configFileName));
29+
while (!configReader.EndOfStream)
30+
{
31+
var line = configReader.ReadLine();
32+
static string GetConfigValue(string line)
33+
{
34+
var parts = line.Split('=');
35+
return parts.Length > 1 ? parts[1].Trim() : string.Empty;
36+
}
37+
38+
if (line.StartsWith("home"))
39+
{
40+
config.PythonHome = GetConfigValue(line);
41+
}
42+
else if (line.StartsWith("version"))
43+
{
44+
var pythonVersion = GetConfigValue(line);
45+
if (!string.IsNullOrEmpty(pythonVersion))
46+
{
47+
pythonVersion = pythonVersion.Substring(0, pythonVersion.LastIndexOf('.'));
48+
}
49+
config.PythonVersion = pythonVersion;
50+
}
51+
}
52+
53+
return config;
54+
}
55+
}
56+
}

src/Bonsai.Scripting.Python/EnvironmentHelper.cs

+32-38
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ namespace Bonsai.Scripting.Python
77
{
88
static class EnvironmentHelper
99
{
10-
public static string GetPythonDLL(string pythonVersion)
10+
public static string GetPythonDLL(EnvironmentConfig config)
1111
{
1212
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
13-
? $"python{pythonVersion.Replace(".", string.Empty)}.dll"
14-
: $"libpython{pythonVersion}.so";
13+
? $"python{config.PythonVersion.Replace(".", string.Empty)}.dll"
14+
: $"libpython{config.PythonVersion}.so";
1515
}
1616

1717
public static void SetRuntimePath(string pythonHome)
@@ -24,7 +24,7 @@ public static void SetRuntimePath(string pythonHome)
2424
}
2525
}
2626

27-
public static string GetVirtualEnvironmentPath(string path)
27+
public static string GetEnvironmentPath(string path)
2828
{
2929
if (string.IsNullOrEmpty(path))
3030
{
@@ -35,42 +35,36 @@ public static string GetVirtualEnvironmentPath(string path)
3535
return Path.GetFullPath(path);
3636
}
3737

38-
public static string GetPythonHome(string path, out string pythonVersion)
38+
public static EnvironmentConfig GetEnvironmentConfig(string path)
3939
{
40-
var pythonHome = path;
41-
pythonVersion = string.Empty;
4240
var configFileName = Path.Combine(path, "pyvenv.cfg");
4341
if (File.Exists(configFileName))
4442
{
45-
using var configReader = new StreamReader(File.OpenRead(configFileName));
46-
while (!configReader.EndOfStream)
43+
return EnvironmentConfig.FromConfigFile(configFileName);
44+
}
45+
else
46+
{
47+
var pythonHome = path;
48+
var pythonVersion = string.Empty;
49+
const string DefaultPythonName = "python";
50+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
4751
{
48-
var line = configReader.ReadLine();
49-
static string GetConfigValue(string line)
50-
{
51-
var parts = line.Split('=');
52-
return parts.Length > 1 ? parts[1].Trim() : string.Empty;
53-
}
52+
var baseDirectory = Directory.GetParent(path).Parent;
53+
pythonHome = Path.Combine(baseDirectory.FullName, "bin");
54+
}
5455

55-
if (line.StartsWith("home"))
56-
{
57-
pythonHome = GetConfigValue(line);
58-
}
59-
else if (line.StartsWith("version"))
60-
{
61-
pythonVersion = GetConfigValue(line);
62-
if (!string.IsNullOrEmpty(pythonVersion))
63-
{
64-
pythonVersion = pythonVersion.Substring(0, pythonVersion.LastIndexOf('.'));
65-
}
66-
}
56+
var pythonName = Path.GetFileName(path);
57+
var pythonVersionIndex = pythonName.LastIndexOf(DefaultPythonName, StringComparison.OrdinalIgnoreCase);
58+
if (pythonVersionIndex >= 0)
59+
{
60+
pythonVersion = pythonName.Substring(pythonVersionIndex + DefaultPythonName.Length);
6761
}
68-
}
6962

70-
return pythonHome;
63+
return new EnvironmentConfig(pythonHome, pythonVersion);
64+
}
7165
}
7266

73-
public static string GetPythonPath(string pythonHome, string pythonVersion, string path)
67+
public static string GetPythonPath(EnvironmentConfig config)
7468
{
7569
string sitePackages;
7670
var basePath = PythonEngine.PythonPath;
@@ -79,28 +73,28 @@ public static string GetPythonPath(string pythonHome, string pythonVersion, stri
7973
{
8074
if (string.IsNullOrEmpty(basePath))
8175
{
82-
var pythonZip = Path.Combine(pythonHome, Path.ChangeExtension(Runtime.PythonDLL, ".zip"));
83-
var pythonDLLs = Path.Combine(pythonHome, "DLLs");
84-
var pythonLib = Path.Combine(pythonHome, "Lib");
76+
var pythonZip = Path.Combine(config.PythonHome, Path.ChangeExtension(Runtime.PythonDLL, ".zip"));
77+
var pythonDLLs = Path.Combine(config.PythonHome, "DLLs");
78+
var pythonLib = Path.Combine(config.PythonHome, "Lib");
8579
basePath = string.Join(Path.PathSeparator.ToString(), pythonZip, pythonDLLs, pythonLib, baseDirectory);
8680
}
8781

88-
sitePackages = Path.Combine(path, "Lib", "site-packages");
82+
sitePackages = Path.Combine(config.Path, "Lib", "site-packages");
8983
}
9084
else
9185
{
9286
if (string.IsNullOrEmpty(basePath))
9387
{
94-
var pythonBase = Path.GetDirectoryName(pythonHome);
95-
pythonBase = Path.Combine(pythonBase, "lib", $"python{pythonVersion}");
88+
var pythonBase = Path.GetDirectoryName(config.PythonHome);
89+
pythonBase = Path.Combine(pythonBase, "lib", $"python{config.PythonVersion}");
9690
var pythonLibDynload = Path.Combine(pythonBase, "lib-dynload");
9791
basePath = string.Join(Path.PathSeparator.ToString(), pythonBase, pythonLibDynload, baseDirectory);
9892
}
9993

100-
sitePackages = Path.Combine(path, "lib", $"python{pythonVersion}", "site-packages");
94+
sitePackages = Path.Combine(config.Path, "lib", $"python{config.PythonVersion}", "site-packages");
10195
}
10296

103-
return $"{basePath}{Path.PathSeparator}{path}{Path.PathSeparator}{sitePackages}";
97+
return $"{basePath}{Path.PathSeparator}{config.Path}{Path.PathSeparator}{sitePackages}";
10498
}
10599
}
106100
}

src/Bonsai.Scripting.Python/RuntimeManager.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,15 @@ static void Initialize(string path)
115115
{
116116
if (!PythonEngine.IsInitialized)
117117
{
118-
path = EnvironmentHelper.GetVirtualEnvironmentPath(path);
119-
var pythonHome = EnvironmentHelper.GetPythonHome(path, out string pythonVersion);
120-
Runtime.PythonDLL = EnvironmentHelper.GetPythonDLL(pythonVersion);
121-
EnvironmentHelper.SetRuntimePath(pythonHome);
122-
PythonEngine.PythonHome = pythonHome;
123-
if (pythonHome != path)
118+
path = EnvironmentHelper.GetEnvironmentPath(path);
119+
var config = EnvironmentHelper.GetEnvironmentConfig(path);
120+
Runtime.PythonDLL = EnvironmentHelper.GetPythonDLL(config);
121+
EnvironmentHelper.SetRuntimePath(config.PythonHome);
122+
PythonEngine.PythonHome = config.PythonHome;
123+
if (config.PythonHome != path)
124124
{
125125
var version = PythonEngine.Version;
126-
PythonEngine.PythonPath = EnvironmentHelper.GetPythonPath(pythonHome, pythonVersion, path);
126+
PythonEngine.PythonPath = EnvironmentHelper.GetPythonPath(config);
127127
}
128128
PythonEngine.Initialize();
129129
}

0 commit comments

Comments
 (0)