1- using System . Diagnostics ;
1+ using System ;
2+ using System . Diagnostics ;
23using System . IO ;
34using System . Text . Json ;
45using 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 }
0 commit comments