Reliable destruction

  • Thread starter Pierre-Eric.Melchy
  • Start date
P

Pierre-Eric.Melchy

Hello,

I have a class measurement representing a physical measurement.
Different objects in this class represent laboratory equipment, which
might raise an exception (e.g. overtemperature).

In any case the equipment has to be switched off after the experiment,
since if a
power supply stays in the on state for a prolonged time equipment may
be
destroyed. Switching off is done by invoking the destructors of the
instruments.

My measurement looks like this:

class measurement:
def __init__(self):
self.setup()
self.run()

def setup(self):
self.powerSupply=apparate.PowerSupply()
self.magnet=apparate.magnet() # Exception("Communication Error")
self.thermo=apparate.thermometer()
# some 5 more instruments

def run():
for i in range(100)
self.powerSupply.setCurrent(i) # Exception("Overcurrent")
self.magnet.setField(0.5*i)


Different measurements are executed in a script which might run
overnight:
If one measurement raises an exception the next one might still work
and I don't
want to loose the results from the following experiments.

try:
measurement()
except:
pass
try:
measurement2()
except:
pass


An exception might be thrown anywhere in init or run if e.g. the
PowerSupply
overheats. Maybe an asynchronous event might happen, too (user
interrupt with ^C but I might live without that if it is impossible to
handle)

My questions are:
1) under normal conditions (no exceptions) is there a guarantee, that
__del__ of
all instruments is called at the end of measurement()?

2) if an exception is thrown, will all instruments be deleted if the
error
occurs in run() ?
(only the instruments already initialized if the error occurs
in setup() )?

I am using CPython (on WinXP) and there are no reference cycles between
the instruments.

I have to stress again that a reliable finalization is important and
cannot wait
until the interpreter shuts down.

I have tried this and it seems to work but this is of course no
guarantee.



Many thanks
 
B

Benjamin Niemann

Hello,

I have a class measurement representing a physical measurement.
Different objects in this class represent laboratory equipment, which
might raise an exception (e.g. overtemperature).

In any case the equipment has to be switched off after the experiment,
since if a
power supply stays in the on state for a prolonged time equipment may
be
destroyed. Switching off is done by invoking the destructors of the
instruments.

My measurement looks like this:

class measurement:
def __init__(self):
self.setup()
self.run()

def setup(self):
self.powerSupply=apparate.PowerSupply()
self.magnet=apparate.magnet() # Exception("Communication Error")
self.thermo=apparate.thermometer()
# some 5 more instruments

def run():
for i in range(100)
self.powerSupply.setCurrent(i) # Exception("Overcurrent")
self.magnet.setField(0.5*i)


Different measurements are executed in a script which might run
overnight:
If one measurement raises an exception the next one might still work
and I don't
want to loose the results from the following experiments.

try:
measurement()
except:
pass
try:
measurement2()
except:
pass


An exception might be thrown anywhere in init or run if e.g. the
PowerSupply
overheats. Maybe an asynchronous event might happen, too (user
interrupt with ^C but I might live without that if it is impossible to
handle)

My questions are:
1) under normal conditions (no exceptions) is there a guarantee, that
__del__ of
all instruments is called at the end of measurement()?

2) if an exception is thrown, will all instruments be deleted if the
error
occurs in run() ?
(only the instruments already initialized if the error occurs
in setup() )?

I am using CPython (on WinXP) and there are no reference cycles between
the instruments.

I have to stress again that a reliable finalization is important and
cannot wait
until the interpreter shuts down.

I have tried this and it seems to work but this is of course no
guarantee.

I would suggest an explicit tearDown() method

try:
m = measurement()
try:
m.setup()
m.run()
finally:
m.tearDown()
except KeyboardInterrupt:
# user pressed ctrl-C
print "***BREAK"
sys.exit(1)
except:
# you should at least log the exception for later debugging
traceback.print_exc()

and remove the calls to setup() and run() from the constructor.
 
P

Pierre-Eric.Melchy

Hello Benjamin,

What would happen if an exception was thrown in the middle of setup()?
tearDown could not handle this case without having a list of the
objects already constructed (Or I would have to rely on the automatic
call to __del__, if it is reliable).


There is still some problem:
Imagine a communication error in run() which would cause del to fail on
the instrument.
Anyway, I think this case is still more difficult to handle.
 
B

Benjamin Niemann

Hello Benjamin,

What would happen if an exception was thrown in the middle of setup()?
tearDown could not handle this case without having a list of the
objects already constructed (Or I would have to rely on the automatic
call to __del__, if it is reliable).

class measurement:
def __init__(self):
self.powerSupply = None
...

def setup(self):
self.powerSupply=apparate.PowerSupply()
...

def tearDown(self):
if self.powerSupply is not None:
try:
self.powerSupply.tearDown()
except:
# Exception in powerSupply.tearDown() should not stop
# the following tearDown()s from being executed
traceback.print_exc()

...

There is still some problem:
Imagine a communication error in run() which would cause del to fail on
the instrument.

Not really sure, if I understand what you mean? Does my tearDown() above
covers this?
Anyway, I think this case is still more difficult to handle.

Reliable, fail-safe software *is* hard to design and implement, that's for
sure..
Be happy that it's just a power supply that could overheat and not the core
of a nuclear power plant.
 
R

Raymond Hettinger

[[email protected]]
My questions are:
1) under normal conditions (no exceptions) is there a guarantee, that
__del__ of
all instruments is called at the end of measurement()?

2) if an exception is thrown, will all instruments be deleted if the
error
occurs in run() ?
(only the instruments already initialized if the error occurs
in setup() )?

I am using CPython (on WinXP) and there are no reference cycles between
the instruments.

I have to stress again that a reliable finalization is important and
cannot wait
until the interpreter shuts down.

I have tried this and it seems to work but this is of course no
guarantee.

On the plus side, Python does guarantee destruction in the absence of
cycles when the last reference disappears. If there is a cycle, you
have to wait for GC. If you can't wait, then schedule a gc.collect()
to run periodically.

On the minus side, this is a somewhat brittle and error-prone design
strategy. A single, accidental persistent reference is sufficient to
cause failure -- that is a land-mine for all future maintainers of your
code. It is better to make explicit tear-down calls and to wrap
finalization is a try/finally suite.

The simplified code in your post suggests that the instrument shut-off
can be placed at the end of the run() method -- loosely translated as
turn-off the lights when you're done.

An alternative strategy is to periodically poll resources and shut them
off if they are not in use -- loosely translated as having a security
guard turn-off any coffee-pots that were left on by frazzled
programmers as they leave at odd hours of the night.


Raymond
 
P

Pierre-Eric.Melchy

Hello,

your idea sounds good and handles the exception on teardown as well.
(I did not think about the if XXX!=None check in teardown())
I will now provide each of the instruments with an explicit shutdown()
method which frees the interface card as well.

These methods will be called in a finally clause after run.
I think this is much better than to wait for or rely on garbage
collection.

Of course there is a hardware protection of the setup as well.

I am amazed how well Python handles lab instrumentation, it is really
fun to develop with Py.


Many thanks for your help
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top