On 30/08/11 06:13:41, Steven D'Aprano wrote:
On Tue, 30 Aug 2011 08:53 am Arnaud Delobelle wrote:
[...]
Yes, but if I am not mistaken, that will require me to put a line or
two after each os.system call. That's almost like whack-a-mole at the
code level rather than the Control-C level. OK, not a huge deal for
one script, but I was hoping for something simpler. I was hoping I
could put one line at the top of the script and be done with it.
Write a function! That's what they're for after all :)
I'm not sure that this is actually as simple as that, especially using
os.system.
As I understand it, the scenario is this:
The main script looks something like this:
for x in whatever:
os.system('something.py x')
Each time through the loop, a new Python process is started. Each process
runs in the foreground, capturing standard input, and so hitting Ctrl-C
kills *that* process, not the main script. Unless, by chance, the Ctrl-C
happens after the system call returns, but before the next one starts, it
is completely invisible to the parent process (the main script). Wrapping
os.system in a function does nothing to fix that.
Possibly using the subprocess module may help.
Using the subprocess module is likely to help, because unlike os.system,
subprocess does not set control-C to 'ignore' in the parent process
Otherwise, the only way around this I can think of is to ensure that
the 'something.py' script (or scripts!) each return an error code for "User
Cancelled":
for x in whatever:
result = os.system('something.py x')
if result == 3: # User Cancelled
break
On my system, os.system returns 2 if the subprocess was killed by a
control-C. The portable way is to use the constant SIGINT from the
signal module.
Not that os.system does not return 2 if the child ended by doing
sys.exit(2); in that case, os.system in the parent returns 2<<8.
But if the 'something.py' scripts are arbitrary scripts, I don't think you
have any easy way around it.
If the arbitrary script does not trap SIGINT, then os.system
will return the number of the signal that kill the script, i.e.
signal.SIGINT.
If the script traps SIGINT and does some cleanup and then
exits, the best you can hope for, is that in the error case,
the script exits with a special value. And you have to
remember the <<8 issue.
Perhaps use threads, and have the main script
ask the thread to kill the child process?
How would that help?
The problem is that the parent process ignores control-C while
os.system is running (by design). If the parent uses threads,
it still won't receive the signal, because signal state is
global.
Regardless, this is a hard problem, and it isn't possible to just have some
magic switch at the top of your script to make it work. You actually have
to do the work yourself.
You could monkey-patch the os module and replace os.system by
(untested):
def sub_system(command):
pr = subprocess.Popen(command, shell=True)
status = os.waitpid(pr.pid, 0)[1]
return status
That should have the same effect as os.system, except it does
not ignore control-C.
(But of course you can do the work inside a function, and re-use it
elsewhere.)
Even if you don't re-use it, you should still do it in a
function, if only for readability.
Hope this helps,
-- HansM
--
http://mail.python.org/mailman/listinfo/python-list