C# – How Pythonnet properly stops script execution

How Pythonnet properly stops script execution… here is a solution to the problem.

How Pythonnet properly stops script execution

I’m using Pythonnet to embed the Python script launcher into a C# WPF application. I can use Scope to pass variables to a python script and then use the MVVM pattern to get the results on the console.

Now I want to allow the user to stop script execution at any time. I can’t find how to get it to work properly to close threads.

class PythonRuntime
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

private MainViewModel viewModel;
    private string pythonCode;

private bool runtimeThreadLock = false;
    Thread thread;

private PyScope scope;
    private dynamic pyThread;
    private dynamic pyLock;

ConsoleWriter consoleWriter;

public PythonRuntime(MainViewModel viewModel, ConsoleWriter consoleWriter)
    {
        this.viewModel = viewModel;
        this.consoleWriter = consoleWriter;
        SetUpPython();    
    }

public string PythonCode { get => pythonCode; set => pythonCode = value; }

private void SetUpPython()
    {
        PythonEngine.Initialize(true);

scope = Py.CreateScope();
         consoleWriter to make python prints into C# UI
        scope. Set("Console", consoleWriter);
    }

public void LaunchScript()
    {
        if (!runtimeThreadLock)
        {
            thread = new Thread(PythonNetTest);                
            thread. Start();
        }
    }

public void StopScript()
    {
        // ???
    }

[HandleProcessCorruptedStateExceptions]
    private void PythonNetTest()
    {
        runtimeThreadLock = true;
        pyThread = PythonEngine.BeginAllowThreads();
        pyLock = PythonEngine.AcquireLock();

using (Py.GIL())
        {
            try
            {
                scope. Exec(pythonCode);
            }
            catch (PythonException exception)
            {
                consoleWriter.WriteError(exception. ToString());
            }
        }

PythonEngine.ReleaseLock(pyLock);
        PythonEngine.EndAllowThreads(pyThread);
        runtimeThreadLock = false;
    }
}  

IN ADDITION TO MY QUESTION, I WOULD ALSO LIKE TO KNOW WHAT THE PURPOSE OF WRAPPING THE CODE IN USING(PY.GIL()) IS. Because with or without it, my script runs the same way.

  • Python Network: 2.4.0
  • python: 2.7.2 32-bit
  • Network framework: 4.7.1

Solution

Okay, I’m just starting to embed CPython, probably just a little more than you know. What a warning….

First, you need to let the script terminate. When it executes, the . The call to Exec() will return and the thread will exit. If your script has a limited time to run, then you can just wait. Otherwise, you have to schedule some signals to get it out.

Second, the main line will wait for the thread to complete using one of several .NET patterns described below: How to wait for thread to finish with .NET?

using(Py.GIL()) is PythonEngine.AcquireLock(); and PythonEngine.ReleaseLock(pyLock); The shorthand for creates an IDisposable object that acquires a lock and then releases it on Dispose(). So, in your example, it’s redundant.

I’m not sure what you call BeginAllowThreads(). The documentation says it releases locks to allow other threads. When you call it, you don’t have a GIL. The next line gets the GIL. So it doesn’t seem to work anything to me.

See also https://docs.python.org/3/c-api/init.html Details about threads. This seems to be more relevant to python threads and saving thread state so that other non-python things can be done. This is Python 3. Python 2 doesn’t seem to support equivalents.

Related Problems and Solutions