Skip to content

Commit e502ebc

Browse files
authored
Merge pull request #3110 from Flow-Launcher/modify-pythonpath-for-plugins
Add `.`, `./lib`, `./plugin` directories to path for Python plugins
2 parents 81c068e + bb7900c commit e502ebc

File tree

2 files changed

+85
-11
lines changed

2 files changed

+85
-11
lines changed

Flow.Launcher.Core/Plugin/PythonPlugin.cs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Diagnostics;
1+
using System;
2+
using System.Diagnostics;
23
using System.IO;
34
using System.Text.Json;
45
using System.Threading;
@@ -25,14 +26,13 @@ public PythonPlugin(string filename)
2526

2627
var path = Path.Combine(Constant.ProgramDirectory, JsonRPC);
2728
_startInfo.EnvironmentVariables["PYTHONPATH"] = path;
29+
// Prevent Python from writing .py[co] files.
30+
// Because .pyc contains location infos which will prevent python portable.
31+
_startInfo.EnvironmentVariables["PYTHONDONTWRITEBYTECODE"] = "1";
2832

2933
_startInfo.EnvironmentVariables["FLOW_VERSION"] = Constant.Version;
3034
_startInfo.EnvironmentVariables["FLOW_PROGRAM_DIRECTORY"] = Constant.ProgramDirectory;
3135
_startInfo.EnvironmentVariables["FLOW_APPLICATION_DIRECTORY"] = Constant.ApplicationDirectory;
32-
33-
34-
//Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable
35-
_startInfo.ArgumentList.Add("-B");
3636
}
3737

3838
protected override Task<Stream> RequestAsync(JsonRPCRequestModel request, CancellationToken token = default)
@@ -50,10 +50,53 @@ protected override string Request(JsonRPCRequestModel rpcRequest, CancellationTo
5050
// TODO: Async Action
5151
return Execute(_startInfo);
5252
}
53+
5354
public override async Task InitAsync(PluginInitContext context)
5455
{
55-
_startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
56-
_startInfo.ArgumentList.Add("");
56+
// Run .py files via `-c <code>`
57+
if (context.CurrentPluginMetadata.ExecuteFilePath.EndsWith(".py", StringComparison.OrdinalIgnoreCase))
58+
{
59+
var rootDirectory = context.CurrentPluginMetadata.PluginDirectory;
60+
var libDirectory = Path.Combine(rootDirectory, "lib");
61+
var libPyWin32Directory = Path.Combine(libDirectory, "win32");
62+
var libPyWin32LibDirectory = Path.Combine(libPyWin32Directory, "lib");
63+
var pluginDirectory = Path.Combine(rootDirectory, "plugin");
64+
65+
// This makes it easier for plugin authors to import their own modules.
66+
// They won't have to add `.`, `./lib`, or `./plugin` to their sys.path manually.
67+
// Instead of running the .py file directly, we pass the code we want to run as a CLI argument.
68+
// This code sets sys.path for the plugin author and then runs the .py file via runpy.
69+
_startInfo.ArgumentList.Add("-c");
70+
_startInfo.ArgumentList.Add(
71+
$"""
72+
import sys
73+
sys.path.append(r'{rootDirectory}')
74+
sys.path.append(r'{libDirectory}')
75+
sys.path.append(r'{libPyWin32LibDirectory}')
76+
sys.path.append(r'{libPyWin32Directory}')
77+
sys.path.append(r'{pluginDirectory}')
78+
79+
import runpy
80+
runpy.run_path(r'{context.CurrentPluginMetadata.ExecuteFilePath}', None, '__main__')
81+
"""
82+
);
83+
// Plugins always expect the JSON data to be in the third argument
84+
// (we're always setting it as _startInfo.ArgumentList[2] = ...).
85+
_startInfo.ArgumentList.Add("");
86+
}
87+
// Run .pyz files as is
88+
else
89+
{
90+
// No need for -B flag because we're using PYTHONDONTWRITEBYTECODE env variable now,
91+
// but the plugins still expect data to be sent as the third argument, so we're keeping
92+
// the flag here, even though it's not necessary anymore.
93+
_startInfo.ArgumentList.Add("-B");
94+
_startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
95+
// Plugins always expect the JSON data to be in the third argument
96+
// (we're always setting it as _startInfo.ArgumentList[2] = ...).
97+
_startInfo.ArgumentList.Add("");
98+
}
99+
57100
await base.InitAsync(context);
58101
_startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
59102
}

Flow.Launcher.Core/Plugin/PythonPluginV2.cs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,45 @@ public PythonPluginV2(string filename)
2626

2727
var path = Path.Combine(Constant.ProgramDirectory, JsonRpc);
2828
StartInfo.EnvironmentVariables["PYTHONPATH"] = path;
29-
30-
//Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable
31-
StartInfo.ArgumentList.Add("-B");
29+
StartInfo.EnvironmentVariables["PYTHONDONTWRITEBYTECODE"] = "1";
3230
}
3331

3432
public override async Task InitAsync(PluginInitContext context)
3533
{
36-
StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
34+
// Run .py files via `-c <code>`
35+
if (context.CurrentPluginMetadata.ExecuteFilePath.EndsWith(".py", StringComparison.OrdinalIgnoreCase))
36+
{
37+
var rootDirectory = context.CurrentPluginMetadata.PluginDirectory;
38+
var libDirectory = Path.Combine(rootDirectory, "lib");
39+
var libPyWin32Directory = Path.Combine(libDirectory, "win32");
40+
var libPyWin32LibDirectory = Path.Combine(libPyWin32Directory, "lib");
41+
var pluginDirectory = Path.Combine(rootDirectory, "plugin");
42+
var filePath = context.CurrentPluginMetadata.ExecuteFilePath;
43+
44+
// This makes it easier for plugin authors to import their own modules.
45+
// They won't have to add `.`, `./lib`, or `./plugin` to their sys.path manually.
46+
// Instead of running the .py file directly, we pass the code we want to run as a CLI argument.
47+
// This code sets sys.path for the plugin author and then runs the .py file via runpy.
48+
StartInfo.ArgumentList.Add("-c");
49+
StartInfo.ArgumentList.Add(
50+
$"""
51+
import sys
52+
sys.path.append(r'{rootDirectory}')
53+
sys.path.append(r'{libDirectory}')
54+
sys.path.append(r'{libPyWin32LibDirectory}')
55+
sys.path.append(r'{libPyWin32Directory}')
56+
sys.path.append(r'{pluginDirectory}')
57+
58+
import runpy
59+
runpy.run_path(r'{filePath}', None, '__main__')
60+
"""
61+
);
62+
}
63+
// Run .pyz files as is
64+
else
65+
{
66+
StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
67+
}
3768
await base.InitAsync(context);
3869
}
3970

0 commit comments

Comments
 (0)