'C# run python script from virtual environment

I am trying to run python script in c# with process.

private void RunScript()
{
    ProcessStartInfo processStartInfo = new ProcessStartInfo("python.exe", "c:\\path\\to\\script\\PullRequest.py");
    processStartInfo.CreateNoWindow = true;
    processStartInfo.UseShellExecute = false;
    processStartInfo.RedirectStandardError = true;
    processStartInfo.RedirectStandardOutput = true;

    Process process = new Process();
    process.StartInfo = processStartInfo;
    process.Start();

    m_Output = process.StandardOutput.ReadToEnd();
    m_Error = process.StandardError.ReadToEnd();

    process.WaitForExit();
}

However I got the following error :

No module named requests

How can i run this script from my virtual environment, where the requests module is installed ?



Solution 1:[1]

You have to first activate your virtual environment, you can simply specify the path to the pip and python executables in the .venv/bin directory.

Another way is to simply activate the virtual environment so that the commands are executed in the venv. I am currently running it like this in a Linux container:

const string cmd = "bash";
const string args = "";
const string activateVenv = "source .venv/bin/activate";
var commandsToExecute = new List<string>(){
    "pip install -r requirements.txt",
    "python /path/to/script arg1 arg2 arg3"
};

var startInfo = new ProcessStartInfo
{
    RedirectStandardOutput = true,
    RedirectStandardInput = true,
    RedirectStandardError = true,
    UseShellExecute = false,
    CreateNoWindow = true,
    Arguments = args,
    FileName = cmd,
    WorkingDirectory = workingDirectory
};

var process = Process.Start(startInfo);
if (process == null)
    throw new Exception("Could not start process");

using var sw = process.StandardInput;
if (sw.BaseStream.CanWrite)
{
    sw.WriteLine(activateVenv);
    foreach (var command in commandsToExecute)
    {
        sw.WriteLine(command);
    }
    sw.Flush();
    sw.Close();
}

var sb = new StringBuilder();
while (!process.HasExited)
    sb.Append(process.StandardOutput.ReadToEnd());

var error = process.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(error))
    throw new Exception($"Something went wrong: \n{error}");

return sb.ToString();

This enables me to simply pass the commands as a list of strings, e.g. first I'm running pip install -r requirements.txt then python /path/to/script arg1 arg2 arg3, thus not having to deal with paths to the pip and python executables in the venv directory respectively.

If you are using it on Windows you would have to change the way it is invoked to use powershell and the Activate.ps1 script in the venv directory. There are probably a few things that could be optimized but this is how I got it working. If I find improvements I'll update the answer.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1