ironpython3 icon indicating copy to clipboard operation
ironpython3 copied to clipboard

Interrupt execution of a script

Open ch-hristov opened this issue 8 years ago • 19 comments

Is there a way to use a CancellationToken to interrupt the execution of a script?

ch-hristov avatar May 17 '16 13:05 ch-hristov

Yo can start script execution on a separate thread, then of course it is.

lafrank avatar Jul 17 '16 14:07 lafrank

It's on a separate thread than the main, but as much as I saw there's no built in way to provide a cancellation token to the Execute()method of CompiledCodeobject. What's the general tactic to cancel it?

ch-hristov avatar Jul 21 '16 12:07 ch-hristov

Maybe we want to add an ExecuteAsync, or BeginExecute or something.

On Thu, Jul 21, 2016 at 5:19 AM Hristo Hristov [email protected] wrote:

It's on a separate thread than the main, but as much as I saw there's no built in way to provide a cancellation token to the Execute() method of ScriptingEngine. What's the general tactic to cancel it?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/IronLanguages/ironpython3/issues/64#issuecomment-234236908, or mute the thread https://github.com/notifications/unsubscribe-auth/AADaP0bPC_EXwVdZVdDOQLVkFEGDAwbeks5qX2O-gaJpZM4IgR0T .

slide avatar Jul 21 '16 15:07 slide

Passing a token to it wouldn't really accomplish much - you still need to inspect the token from the script code and abort (CancellationToken is a cooperative yielding mechanism, not pre-emptive).

I would just publish the cancellation token into your script's context dictionary and allow scripts to call ThrowIfCancellationRequested() or perform more graceful aborts if necessary. Otherwise, requesting cancellation on the token's source will not really do anything.

KeithJRome avatar Jul 21 '16 16:07 KeithJRome

One option would be to have an easy way to inject a KeyboardInterrupt (i.e. Ctrl-C) into a running script. I have a funny feeling there's already a way to do this (so that ipy.ee can generate KeyboardInterrupt when ^C is pressed), but maybe it was one of those ideas I never got around to finishing.

On Thu, Jul 21, 2016 at 9:42 AM, Keith Rome [email protected] wrote:

Passing a token to it wouldn't really accomplish much - you still need to inspect the token from the script code and abort (CancellationToken is a cooperative yielding mechanism, not pre-emptive).

I would just publish the cancellation token into your script's context dictionary and allow scripts to call ThrowIfCancellationRequested() or perform more graceful aborts if necessary. Otherwise, requesting cancellation on the token's source will not really do anything.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/IronLanguages/ironpython3/issues/64#issuecomment-234312062, or mute the thread https://github.com/notifications/unsubscribe-auth/AAGmdYUGIJDAMjFTYkilg5PtmCvG2qoeks5qX6FmgaJpZM4IgR0T .

jdhardy avatar Jul 21 '16 22:07 jdhardy

I am not sure whether you want to cancel the running script from within the script or from the hosting environment. On the other hand, why do you want to use CancellationToken and not calling Thread.Abort() instead and handle ThreadAbortException as you need ?

Option 1, cancelling from script : you could add the Thread.CurrentThread object of the thread called Execute() to the script context as a variable. Then from python the script call its Abort() method. Something like this :

try
{
  //...
  ScriptScope execScope = PyEngine.CreateScope();
  ScriptScope .SetVariable("ExecThread", Thread.CurentThread);
  PyEngine.Execute(execScope);
  //...
}
catch (ThreadAbortException Ex)
{
  // cleanup code
}

And from Python script :


ExecThread.Abort()

Option 2, from hosting application : simply call Abort() method of the thread called Execute()

lafrank avatar Jul 22 '16 08:07 lafrank

Thread.Abort() is pre-emptive and a pretty nasty last-ditch thing to do. And it won't give you the expected result if the script spawns any other threads, submits background work using TP.QUWI or uses async completions (which can resume on other threads in the threadpool) - either directly or indirectly.

KeithJRome avatar Jul 22 '16 13:07 KeithJRome

Yeah, I know it's a nasty way but I do not have any better idea, and for simple scripts it works. I believe, the ultimate solution could be some method of signaling if IronPython supported it. I mean like calling Engine.Abort()...whatever it would do internally.

lafrank avatar Jul 22 '16 15:07 lafrank

I want to resurrect the discussion here...I don't want to deal with Thread.Abort() and all the nasty things that it could leave ( e.g unallocated resources ).. Maybe there should be a built in way to do this with IronPython .. telling the CompiledCode.Cancel = truemaybe will break the execution of the script if its active by pushing sys.exit() in the execution stack? I also don't want to tell to the users that they should always check if the task in the hosting environment has been cancelled or not it's very annoying.

PS.My scripts aren't simple..they consist of around 1k lines of code each so you can imagine how tedious it would be to check on every occasion if the task has been cancelled or not..

Edit 2 : @lafrank you could pass the cancellation token , the problem is that you still need to check it everytime you do something in your script

Edit 3 : I'm really expecting to pass my cancellation token to the execute method of the CompiledCodeobject and have it stop when I tell it to stop. Isn't there a way to hook before python performs any operation?

Edit 4 : You could subscribe to C# object event and sys.exit() when the event is invoked :)

ch-hristov avatar Sep 01 '16 15:09 ch-hristov

I would love to see this feature added. I have an application that uses IronPython to execute user provided scripts so graceful exit hooks within the script are not an option. I want to add a "Stop" button to terminate the execution of any script at an arbitrary point and Thread.Abort is to ugly.

simo9000 avatar Dec 19 '16 21:12 simo9000

You could always use sys.settrace() to install your own per-statement trace handler function (from the C# side), and decide whether to let the script continue execution. It's a pretty simple thing to do, and might give you just enough control to do what you want.

KeithJRome avatar Dec 19 '16 22:12 KeithJRome

@KeithJRome I just gave the sys.settrace() idea a try. It "works" but it is not providing per-statement trace handling but rather per-scope handling. The python documentation indicates this:

The trace function is invoked (with event set to 'call') whenever a new local scope is entered

To confirm, I attempted to interrupt the following two scripts using a handler via sys.settrace():

import time
time.sleep(3)
print('done')

and

import time
def sleep():
	time.sleep(3)
def p():
	print('done')
sleep()
p()

I was not able to prevent the print statement in the first script but was able to interrupt the second one. I think sys.settrace() is better than nothing but I still think a true per-statement handler or a CancellationToken would be a nice feature to stop a script dead in it's tracks without resorting to Thread.Abort.

simo9000 avatar Dec 20 '16 17:12 simo9000

@simo9000 try using the 'line' event. That's the event I've used before to suspend running code on "breakpoints" in a custom debugger IDE.

KeithJRome avatar Dec 20 '16 18:12 KeithJRome

@KeithJRome thanks for pushing me to understand sys.settrace() better. It turns out I was not returning anything from my handler so tracing behavior was not as I excepted. I now have a configuration that does exactly what I expect it to.

simo9000 avatar Dec 20 '16 19:12 simo9000

So can this be closed out, or do you want another solution?

slide avatar Dec 20 '16 20:12 slide

I'm good with the sys.settrace() option. I don't think it should be embedded in the library since tracing will have a performance impact. I only wanted it in a debug context where performance can be sacrificed for enhanced control. I like the option @KeithJRome helped steer me to since i can toggle it on and off based on the execution context.

However, it was @juno-craft that asked for the feature originally...

simo9000 avatar Dec 20 '16 20:12 simo9000

it would be cool to get an example on how to do that what you did @simo9000

ch-hristov avatar Dec 22 '16 09:12 ch-hristov

I'd also appreciate seeing an example of how to stop the script gracefully from the traceback handler. I write a .Net app in c# for hosting Python scripts and do use a traceback handler for debugging it, but I have no idea how to stop the script from within the handler. Well, except Thread.Abort() :-)

On Thu, Dec 22, 2016 at 10:47 AM, juno-craft [email protected] wrote:

it would be cool if to get an example on how to do that what you used @simo9000 https://github.com/simo9000

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/IronLanguages/ironpython3/issues/64#issuecomment-268760853, or mute the thread https://github.com/notifications/unsubscribe-auth/AO-XZLyTV8v_tEjV2jhpfANC2gdn57M9ks5rKkcigaJpZM4IgR0T .

lafrank avatar Dec 22 '16 14:12 lafrank

https://github.com/simo9000/Consola/blob/editor/Consola/Library/scriptSession.cs

simo9000 avatar Dec 22 '16 14:12 simo9000