Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-21 Thread Peter Otten
Alan Gauld wrote:

 On 20/03/15 09:37, Peter Otten wrote:
 
 def close_relay(e=None,v=None,t=None):
  try:
 if not relay_closed()
really_close_relay()
  except:
 really_close_relay()
 
 The purpose of the if clause is to ensure that
 if the function is called many times you only
 close the relay once (I surmised that more than
 once could be harmful?)
 
 import sys, atexit
 atexit.register(close_relay)
 sys.excepthook = close_relay
 
 atexit should be overkill, but it might be needed
 if for some reason the interpreter dies while
 performing the usual cleanup.
 
 excepthook replaces the usual exception mechanism
 with a clean up. This is needed for cases where an
 exception occurs before getting to the finally. We
 want to close the relay ASAP, not waiting till
 the interpreter decides to call the finally.
 
 try:
  main program here
 finally:
  close_relay()
 
 This is the happy path where everything shuts down as expected.
 
 That reeks of cargo cult. Are there actual scenarios for each of the
 three mechanisms where it is the only one that works?
 
 In real-time you never trust anything.
 Always cover your back.
 
 I would expect that

 try:
  main program here
 finally:
  close_relay()

 provides the same level of confidence,
 
 Only if the interpreter is behaving as normal.
 The hooks are to try (we hope) to catch cases where
 the interpreter has broken its normal flow.
 
 So the scenarios are:
 
 1) an unexpected exception occurs - close the relay ASAP.
 - Use excepthook
 
 2) The interpreter gets sent a kill or similar unexpected
 termination - use atexit because finally may not get
 called. (I'm not sure, so belt n' braces here)
 (BTW Does anyone know what the interpreter does when
 suspending - Ctrl-Z in Unix land?)
 
 3) Normal program exit. Use the finally clause.
 
 But its only ever going to be a best endeavour, that's
 why Python is not suitable for true real-time/critical apps...
 But I'd never trust any environment to its usual behaviour
 if there is a possibility of something being broken.
 In this case the relay and its battery pack.
 
 the program closes normally or the main code raises an exception, but not
 if the process is killed.
 
 What's not clear in the Python  documentation is how Python responds
 to a kill(or suspend). I'd hope the atexit got called even in a kill.
 I would not expect the finally to be executed.
 
 Of course, if its a seg fault you are probably stuffed either way...

I ran a few experiments:

$ cat bnb.py 
import atexit
import os
import signal
import sys
import time

def handle_except(*args):
print(except, args, flush=True)

def handle_exit():
print(exit, flush=True)

def register_signalhandler(sig):
def handler(*args):
print(receiving signal, sig, args, flush=True)
signal.signal(sig, handler)

def main():
print(Hello from, os.getpid())
while True:
print(., flush=True, end=)
time.sleep(1)

sys.excepthook = handle_except
atexit.register(handle_exit)

for sig in sys.argv[1:]:
register_signalhandler(getattr(signal, sig))

try:
main()
finally:
print(finally, flush=True)
$ python3 bnb.py 
Hello from 32578
^Cfinally
except (class 'KeyboardInterrupt', KeyboardInterrupt(), traceback object 
at 0x7ff97b001bc8)
exit

When there is no signal handler all three mechanisms work, in the order

- finally
- except hook
- exit handler

Now let's kill:

$ python3 bnb.py 
Hello from 32584
.Terminated

None of the three are invoked. Let's install a signal handler for SIGTERM:

$ python3 bnb.py SIGTERM
Hello from 32593
.receiving signal 15 (15, frame object at 0x7f818e0bb648)
...^Cfinally
except (class 'KeyboardInterrupt', KeyboardInterrupt(), traceback object 
at 0x7f818cdc6bc8)
exit

The signal is intercepted (and ignored by the no-op handler thus the 
additional Ctrl-C). If we raise a SystemExit in the handler

- finally
- exit handler

will be invoked, but not the except hook.

$ kill -9

of course cannot be intercepted.

My conclusions: 
- If finally does not work nothing does.
- Signal handlers increase safety

Bonus:

$ python3 bnb.py SIGTSTP
Hello from 32614
^Zreceiving signal 20 (20, frame object at 0x7f2f8a897648)
^Cfinally
except (class 'KeyboardInterrupt', KeyboardInterrupt(), traceback object 
at 0x7f2f895a2bc8)
exit

So Ctrl-Z can be intercepted. The program could put the relay into a safe 
state before it suspends.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-21 Thread Cameron Simpson

On 21Mar2015 09:19, Peter Otten __pete...@web.de wrote:

I ran a few experiments:

[...]

Bonus:
$ python3 bnb.py SIGTSTP
Hello from 32614
^Zreceiving signal 20 (20, frame object at 0x7f2f8a897648)
^Cfinally
except (class 'KeyboardInterrupt', KeyboardInterrupt(), traceback object
at 0x7f2f895a2bc8)
exit

So Ctrl-Z can be intercepted. The program could put the relay into a safe
state before it suspends.


Yes, Ctrl-Z (SIGTSTP) can be caught (stop from terminal, ^Z).
Note, however, that SIGSTOP cannot be caught (stop); it is not SIGTSTP.

Basicly you can kill (SIGKILL - abort process and never schedule it again), 
stop (SIGSTOP - cease scheduling this process) and continue (SIGCONT - resume 
scheduling this process) a process from outside a process and the process 
cannot intercept these.  Which is just great! 

However, it means there are some things you cannot manage from within the 
process. This is where watchdogs of various kinds come into play: an external 
process of some kind which monitors the primary process (or something it 
manages), and take action if the primary process goes away or some activity 
does not occur for a period.


Cheers,
Cameron Simpson c...@zip.com.au

Cordless hoses have been around for quite some time. They're called buckets.
   - Dan Prener pre...@watson.ibm.com
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Peter Otten
Alan Gauld wrote:

 On 20/03/15 02:57, Doug Basberg wrote:
 
 Still, I would like to know if a 'hook' exists on exit from Python.  I am
 running Linux on a Raspberry Pi with Python 2.7.4  I also run an Apache
 server on the Pi for monitor and control of power, HVAC, and security.
 
 Your previous mail got you three options. I'd use all of them!
 
https://docs.python.org/3/library/atexit.html
  
   ... But that's only for normal program termination; sys.excepthook is
   for unexpected exits
 
 
 def close_relay(e=None,v=None,t=None):
 try:
if not relay_closed()
   really_close_relay()
 except:
really_close_relay()
 
 import sys, atexit
 atexit.register(close_relay)
 sys.excepthook = close_relay
 
 
 try:
 main program here
 finally:
 close_relay()

That reeks of cargo cult. Are there actual scenarios for each of the three 
mechanisms where it is the only one that works?

I would expect that

try:
main program here
finally:
close_relay()

provides the same level of confidence, i. e. the relay will be closed when 
the program closes normally or the main code raises an exception, but not if 
the process is killed.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Steven D'Aprano
On Fri, Mar 20, 2015 at 08:35:34PM +, Alan Gauld wrote:

 Yeah, I know you can catch a signal and add your own handler, but I 
 meant what is the default Python suspend behaviour? Does it execute any 
 outstanding exception blocks? What about finally blocks? Or, if about to 
 exit a context manager, the __exit__ method?

Depends on the signal.

I'm not an expert on how signals work in Linux, or other Unixes, but I 
expect that, in the absense of a specific signal handler to catch it, 
the sleep (pause?) signal will cause the interpreter to just stop and 
wait. I think that's signal 19 on Linux, and 18 to wake.

Signal 9 doesn't give the interpreter to do anything. The OS just yanks 
the carpet out from under its feet and terminates the process with 
extreme prejudice. Signal 9 cannot be caught, no signal handlers will 
detect it, no try...finally blocks will run. The process just stops.

Don't use kill -9 unless you need to. I always try three steps to kill a 
rogue process:

First use kill processid with no other arguments. Give it 30 seconds 
or so to let the process tidy up after itself, and if it still hasn't 
quiet, try kill -HUP processid. Again, give it 30 seconds or so. 
Then, if and only if necessary, kill -9 processid.


 Or does it just stop and wait till its resumed? Kind of like
 an implicit yield statement?





 
 
 -- 
 Alan G
 Author of the Learn to Program web site
 http://www.alan-g.me.uk/
 http://www.amazon.com/author/alan_gauld
 Follow my photo-blog on Flickr at:
 http://www.flickr.com/photos/alangauldphotos
 
 
 ___
 Tutor maillist  -  Tutor@python.org
 To unsubscribe or change subscription options:
 https://mail.python.org/mailman/listinfo/tutor
 
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Martin A. Brown


Hi,

This is mostly a distant footnote to Doug Basberg's original 
question, which I believe is largely answered at this point.


Albert-Jan Roskum, Alan Gauld and Steven D'Aprano were asking about 
signals and how they are handled (in Un*xen).  I am trying to 
address that.



Yeah, I know you can catch a signal and add your own handler, but I
meant what is the default Python suspend behaviour? Does it execute any
outstanding exception blocks? What about finally blocks? Or, if about to
exit a context manager, the __exit__ method?


Suspension of the process has nothing to do with Python.  This 
happens in the Un*x scheduler--long before Python is involved.



Depends on the signal.


Correct.  It all depends on the signal.

Short version:

(Apologies, Jack Nicholson, in demonic form or otherwise):

  * You can't 'handle':  STOP, CONT, KILL, SEGV, BUS.

  * You can handle: HUP, INT, QUIT, USR1, USR2, PIPE, ALRM, TERM and others.

Short advice (though I do not have experience dealing with signals 
in a realtime environment).  I suggest the following guidelines:


  1. Do not catch a signal you cannot handle (or do not intend to
 handle).

  2. Do everything you can at startup to make sure that the
 environment in which you are operating is as you expect.

  3. Catch all the signals you want to catch and, in response to
 receiving such a signal, do what you need in order to shut down
 cleanly.  This coexists peacefully with the handy
 atexit handlers suggested earlier.

(I am normally not at all a fan of an unspecific try--finally, but I 
get what Peter Otten is suggesting and might make the same choice, 
were I faced with Doug Basberg's situation.)


I'm not an expert on how signals work in Linux, or other Unixes, 
but I expect that, in the absense of a specific signal handler to 
catch it, the sleep (pause?) signal will cause the interpreter 
to just stop and wait. I think that's signal 19 on Linux, and 18 
to wake.


Longer version:

I have experience with handling Linux signals and Python.  There may 
be subtle differences on other Un*xen.  If you wish to know more, I 
would suggest reading the chapter on Signals in _Advanced 
Programming in the Unix Environment_ (Chapter 10, in my second 
edition by Stevens  Rago).


You cannot catch nor handle:

  * SIGSTOP (19), because that tells Un*x, Please remove this
process from the scheduler, i.e. freeze it!

  * SIGCONT (18), because that tells Unix, Please restore this
process to normal scheduling, i.e. unfreeze it.

  * SIGKILL (9), because that tells Unix, Terminate this thing,
with prejudice!  Do not tell it what happened.

This means, your Un*X will never actually deliver SIGSTOP, SIGCONT 
or SIGKILL to Python and your program.


I believe that you cannot do anything with the following signals:

  * SIGSEGV (11), because this means that there has been a memory
fault.  Python is a sufficiently high-level language, that, if
this happens, this should not be your code doing it.  (Unless
you are writing C extensions for Python, and then, of course,
you know what you are doing)

  * SIGBUS (7), because this is extraordinarily rare (today), but
would be a case of trying to access memory that does not exist.

In practice, I have seen SIGSEGV often over the last 20 years 
(perhaps I have worked with flaky software, or perhaps that is just 
something that happens in this line of work).  I have seen SIGBUS 
very rarely (usually a precursor to a machine eating itself for 
lunch).


The signals STOP and CONT are so rarely exhibited that they are 
perceived as exotic specimens when demonstrated.


The KILL signal is the big hammer that everybody learns in their 
first month using any Un*x (which is unfortunate because of the 
power it commands).


Signal 9 doesn't give the interpreter to do anything. The OS just 
yanks the carpet out from under its feet and terminates the 
process with extreme prejudice. Signal 9 cannot be caught, no 
signal handlers will detect it, no try...finally blocks will run. 
The process just stops.


Correct.  When the (Linux | Un*x) kernel has a signal 9 for a 
process, that process does not get any chance to clean up.  It 
simply disappears.  It is never given an opportunity to run 
again--i.e. it will never be scheduled again.


Don't use kill -9 unless you need to. I always try three steps to 
kill a rogue process:


First use kill processid with no other arguments. Give it 30 
seconds or so to let the process tidy up after itself, and if it 
still hasn't quiet, try kill -HUP processid. Again, give it 30 
seconds or so. Then, if and only if necessary, kill -9 
processid.


Agreed.  In my experience, most mature sysadmins do this, too.


Or does it just stop and wait till its resumed? Kind of like
an implicit yield statement?


Sending a SIGSTOP to a process is equivalent to freezing it in 
memory/process space.  I sometimes think of this as suspend (because 
of ctrl-Z in bash, 

Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Albert-Jan Roskam


On Fri, Mar 20, 2015 10:37 AM CET Peter Otten wrote:

Alan Gauld wrote:

 On 20/03/15 02:57, Doug Basberg wrote:
 
 Still, I would like to know if a 'hook' exists on exit from Python.  I am
 running Linux on a Raspberry Pi with Python 2.7.4  I also run an Apache
 server on the Pi for monitor and control of power, HVAC, and security.
 
 Your previous mail got you three options. I'd use all of them!
 
https://docs.python.org/3/library/atexit.html
  
   ... But that's only for normal program termination; sys.excepthook is
   for unexpected exits
 
 
 def close_relay(e=None,v=None,t=None):
 try:
if not relay_closed()
   really_close_relay()
 except:
really_close_relay()
 
 import sys, atexit
 atexit.register(close_relay)
 sys.excepthook = close_relay
 
 
 try:
 main program here
 finally:
 close_relay()

That reeks of cargo cult. Are there actual scenarios for each of the three 
mechanisms where it is the only one that works?

I would expect that

try:
main program here
finally:
close_relay()

Is this (also) called a diaper pattern? Or is that name reserved for the 
antipattern with try-bare except, where the 'except' catches all the sh*t 
(pardon my language)?


provides the same level of confidence, i. e. the relay will be closed when 
the program closes normally or the main code raises an exception, but not if 
the process is killed.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Peter Otten
Albert-Jan Roskam wrote:

I would expect that

try:
main program here
finally:
close_relay()
 
 Is this (also) called a diaper pattern? 

Cool name, but most parents want the baby's faeces to go into the diapers 
whereas try ... bare except is notorious for swallowing useful information 
that should be propagated to the user or developer.

 Or is that name reserved for the
 antipattern with try-bare except, where the 'except' catches all the sh*t
 (pardon my language)?

For try ... finally it's its raison d'ĂȘtre to execute some code no matter 
what. E. g. before the advent of with ...

with open(...) as f:
... # use file
# file is closed now even if something went wrong while using it and no
# matter how garbage collection works for the Python implementation running
# the code.
# The exception (if any) is not swallowed

it was good style to write

f = open(...)
try:
... # use file
finally:
f.close()
# file is closed...



___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Cameron Simpson

On 20Mar2015 19:20, Martin A. Brown mar...@linux-ip.net wrote:
[...]

Short version:
(Apologies, Jack Nicholson, in demonic form or otherwise):
 * You can't 'handle':  STOP, CONT, KILL, SEGV, BUS.


You can handle SEGV and BUS. Though probably not meaningfully in Python, 
haven't tried; if they fire in Python something internal is already badly 
wrong.


IIRC, in the distant past the Bourne shell used SIGSEGV as a trigger to 
allocate more memory:-)


Really, the only 3 a UNIX process can't intercept are the first three: STOP, 
CONT, KILL.


Since everything else Martin says seems to be in the context of in Python, no 
other quibbles.


Cheers,
Cameron Simpson c...@zip.com.au
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Alan Gauld

On 20/03/15 09:37, Peter Otten wrote:


def close_relay(e=None,v=None,t=None):
 try:
if not relay_closed()
   really_close_relay()
 except:
really_close_relay()


The purpose of the if clause is to ensure that
if the function is called many times you only
close the relay once (I surmised that more than
once could be harmful?)


import sys, atexit
atexit.register(close_relay)
sys.excepthook = close_relay


atexit should be overkill, but it might be needed
if for some reason the interpreter dies while
performing the usual cleanup.

excepthook replaces the usual exception mechanism
with a clean up. This is needed for cases where an
exception occurs before getting to the finally. We
want to close the relay ASAP, not waiting till
the interpreter decides to call the finally.


try:
 main program here
finally:
 close_relay()


This is the happy path where everything shuts down as expected.


That reeks of cargo cult. Are there actual scenarios for each of the three
mechanisms where it is the only one that works?


In real-time you never trust anything.
Always cover your back.


I would expect that

try:
 main program here
finally:
 close_relay()

provides the same level of confidence,


Only if the interpreter is behaving as normal.
The hooks are to try (we hope) to catch cases where
the interpreter has broken its normal flow.

So the scenarios are:

1) an unexpected exception occurs - close the relay ASAP.
   - Use excepthook

2) The interpreter gets sent a kill or similar unexpected
termination - use atexit because finally may not get
called. (I'm not sure, so belt n' braces here)
(BTW Does anyone know what the interpreter does when
suspending - Ctrl-Z in Unix land?)

3) Normal program exit. Use the finally clause.

But its only ever going to be a best endeavour, that's
why Python is not suitable for true real-time/critical apps...
But I'd never trust any environment to its usual behaviour
if there is a possibility of something being broken.
In this case the relay and its battery pack.


the program closes normally or the main code raises an exception, but not if
the process is killed.


What's not clear in the Python  documentation is how Python responds
to a kill(or suspend). I'd hope the atexit got called even in a kill.
I would not expect the finally to be executed.

Of course, if its a seg fault you are probably stuffed either way...


--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Alan Gauld

On 20/03/15 20:04, Albert-Jan Roskam wrote:


(BTW Does anyone know what the interpreter does when
suspending - Ctrl-Z in Unix land?)


No experience with it, but I would first check the 'signal' module


Yeah, I know you can catch a signal and add your own handler, but I 
meant what is the default Python suspend behaviour? Does it execute any 
outstanding exception blocks? What about finally blocks? Or, if about to 
exit a context manager, the __exit__ method?


Or does it just stop and wait till its resumed? Kind of like
an implicit yield statement?


--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] UPDATE: Is there a 'hook' to capture all exits from a python program?

2015-03-20 Thread Albert-Jan Roskam

-
On Fri, Mar 20, 2015 8:05 PM CET Alan Gauld wrote:

On 20/03/15 09:37, Peter Otten wrote:

 def close_relay(e=None,v=None,t=None):
  try:
 if not relay_closed()
really_close_relay()
  except:
 really_close_relay()

The purpose of the if clause is to ensure that
if the function is called many times you only
close the relay once (I surmised that more than
once could be harmful?)

 import sys, atexit
 atexit.register(close_relay)
 sys.excepthook = close_relay

atexit should be overkill, but it might be needed
if for some reason the interpreter dies while
performing the usual cleanup.

excepthook replaces the usual exception mechanism
with a clean up. This is needed for cases where an
exception occurs before getting to the finally. We
want to close the relay ASAP, not waiting till
the interpreter decides to call the finally.

 try:
  main program here
 finally:
  close_relay()

This is the happy path where everything shuts down as expected.

 That reeks of cargo cult. Are there actual scenarios for each of the three
 mechanisms where it is the only one that works?

In real-time you never trust anything.
Always cover your back.

 I would expect that

 try:
  main program here
 finally:
  close_relay()

 provides the same level of confidence,

Only if the interpreter is behaving as normal.
The hooks are to try (we hope) to catch cases where
the interpreter has broken its normal flow.

So the scenarios are:

1) an unexpected exception occurs - close the relay ASAP.
- Use excepthook

2) The interpreter gets sent a kill or similar unexpected
termination - use atexit because finally may not get
called. (I'm not sure, so belt n' braces here)
(BTW Does anyone know what the interpreter does when
suspending - Ctrl-Z in Unix land?)

No experience with it, but I would first check the 'signal' module

import signal
import sys
 
def signal_term_handler(signal, frame):
print 'got SIGTERM'
sys.exit(0)
 
signal.signal(signal.SIGTERM, signal_term_handler)

https://nattster.wordpress.com/2013/06/05/catch-kill-signal-in-python/


3) Normal program exit. Use the finally clause.

But its only ever going to be a best endeavour, that's
why Python is not suitable for true real-time/critical apps...
But I'd never trust any environment to its usual behaviour
if there is a possibility of something being broken.
In this case the relay and its battery pack.

 the program closes normally or the main code raises an exception, but not if
 the process is killed.

What's not clear in the Python  documentation is how Python responds
to a kill(or suspend). I'd hope the atexit got called even in a kill.
I would not expect the finally to be executed.

Of course, if its a seg fault you are probably stuffed either way...


-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor