Terminating an IronPython script

Question:

This may not specifically be an IronPython question, so a Python dev out there might be able to assist.

I want to run python scripts in my .Net desktop app using IronPython, and would like to give users the ability to forcibly terminate a script. Here’s my test script (I’m new to Python so it might not be totally correct):-

import atexit
import time
import sys

@atexit.register
def cleanup():
    print 'doing cleanup/termination code'
    sys.exit()

for i in range(100):
    print 'doing something'
    time.sleep(1)

(Note that I might want to specify an “atexit” function in some scripts, allowing them to perform any cleanup during normal or forced termination).

In my .Net code I’m using the following code to terminate the script:

_engine.Runtime.Shutdown();

This results in the script’s atexit function being called, but the script doesn’t actually terminate – the for loop keeps going. A couple of other SO articles (here and here) say that sys.exit() should do the trick, so what am I missing?

Asked By: Andrew Stephens

||

Answers:

It seems that it’s not possible to terminate a running script – at least not in a “friendly” way. One approach I’ve seen is to run the IronPython engine in another thread, and abort the thread if you need to stop the script.

I wasn’t keen on this brute-force approach, which would risk leaving any resources used by the script (e.g. files) open.

In the end, I create a C# helper class like this:-

public class HostFunctions
{
    public bool AbortScript { get; set; }

    // Other properties and functions that I want to expose to the script...
}

When the hosting application wants to terminate the script it sets AbortScript to true. This object is passed to the running script via the scope:-

_hostFunctions = new HostFunctions();
_scriptScope = _engine.CreateScope();
_scriptScope.SetVariable("HostFunctions", _hostFunctions);

In my scripts I just need to strategically place checks to see if an abort has been requested, and deal with it appropriately, e.g.:-

for i in range(100):
    print 'doing something'
    time.sleep(1)
    if HostFunctions.AbortScript:
        cleanup()
Answered By: Andrew Stephens

It seems that if you are using ".NET 5" or higher then aborting Thread might work imperfect.

Thread.Abort() is not supported on ".NET 5" or higher and throws PlatformNotSupportedException.
You probably will find a solution to use Thread.Interrupt(), but it has slightly different behavior:

  1. If your Python script does not have any Thread.Sleep() it won’t stop your script;
  2. It looks like you couldn’t Abort that Thread twice, but you can Interrupt that Thread twice. So, if your Python script is using finally blocks or "Context Manager", you will be able to Interrupt it by calling Thread.Interrupt() twice (with some delays between those calls).
Answered By: Smičius Mantas
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.