[PythonCAD] Seeking code suggestions when handling exceptions
Noel Grandin
noel at peralex.com
Thu May 19 07:32:04 CEST 2005
Hi
My basic suggestion is either
(a) don't do anything. If an exception happens you have already have a
problem. A signal being lost is not such a big deal.
or
(b) have a global application exception handler, which logs the message
and perhaps asks the user if the problem is serious enough to abort.
then do
try:
self.sendMessage('thickness_changed', _ot)
except:
global_ex_handler.log()
try:
self.modified()
except:
global_ex_handler.log()
And I would probably put all of that into a utility method that all of
the setters could use.
Art Haas wrote:
>Hi.
>
>For the last couple of days I've been puzzling about exception handling
>in Python. The thing that got me started down this track was the threads
>on the python-dev mailing list dealing with proposals to enhance and
>simplify the exception handling in Python. For example, one proposal
>was to allow for try/except/finally blocks in the code. This change
>would be a nice addition as it could simplify code that currently nests
>a try/except block within a try/finally block. Another proposal dealt
>with anonymous blocks, generator functions, and templates, and this
>proposal seemed to spawn various others as offshoots, and discussion of
>these proposals is ongoing. Read the python-dev mailing list archives
>for more details:
>
>http://mail.python.org/pipermail/python-dev/
>
>The exception handling thread made me look a bit at how PythonCAD uses
>try/finally and try/except blocks, and while doing this I began looking
>at the code for sending messages between the various entities. Here's
>some sample code to demonstrate how things currently work - the
>following is a shortened version of the setThickness() method
>in the 'graphicobject.py' file:
>
>setThickness(self, t):
> _ot = self.__thickness
> if abs(_ot - t) > 1e-10:
> self.__thickness = t
> self.sendMessage('thickness_changed', _ot)
> self.modified() # calls self.sendMessage('modified')
>
>So, if the abs() test succeeds the new thickness value is stored, and
>then the object calls sendMessgage() and lets any object that listens
>to the 'thickness_changed' message via connect() will invoke some
>method, and finally the modified() method is called which will send out
>the 'modified' message to objects listening for it. That is all fine and
>good, but things get sticky if an exception is thrown during the
>sendMessage() calls. If a exception is thrown during the first
>sendMessage() call above, then modified() is never invoked, and depending
>on when the exception is raised some objects listening for 'thickness_changed'
>messages may not have the appropriate bound method invoked. If the
>exception is raised during the sending of the 'modified' message, then
>the problem is just that potentially one or more objects may not get
>the notification that the entity thickness has changed.
>
>A possible solution is to use try/finally:
>
>setThickness(self, t):
> _ot = self.__thickness
> if abs(_ot - t) > 1e-10:
> self.__thickness = _t
> try:
> self.sendMessage('thickness_changed', _ot)
> finally:
> self.modified()
>
>Now, the modified() method is guaranteed to be invoked whether or not
>an exception was raised during the sending of the 'thickness_changed'
>message. Two potential problems arise when doing this, however. First,
>there are an unknown number of objects that did not receive the
>'thickness_changed' message, and second any exceptions thrown during
>the invocation of self.modified() would wipe out the original exception
>and replace it with the new one, making debugging more difficult.
>
>A second possible solution is the following:
>
>setThickness(self, t):
> _ot = self.__thickness
> if abs(_ot - t) > 1e-10:
> self.__thickness = _t
> try:
> self.sendMessage('thickness_changed', _ot)
> except:
> raise
> else:
> self.modified()
>
>Here, self.modified() will only be called if the 'thickness_changed'
>message was sent successfully, which is what should happen. Any
>exception occuring during the self.modified() call would not be handled,
>so some number of entities may not be notified of the change, though.
>The problems of an exception occurring during the self.modified() call
>are also still present.
>
>A third possibility is this:
>
>setThickness(self, t):
> _ot = self.__thickness
> if abs(_ot - t) > 1e-10:
> self.__thickness = _t
> try:
> self.sendMessage('thickness_changed', _ot)
> except:
> pass
> try:
> self.modified()
> except:
> pass
>
>Any errors are ignored, so for better or worse this method finishes but
>possibly leaves wreckage behind tripping things up later.
>
>I want to ensure in the sample code above that the 'thickness_changed'
>message is sent to all listeners successfully, and also that the
>'modified' message is sent to all listeners successfully, and should
>something go wrong midway either backout the change or, even better,
>inform any listeners that had received messages about the change
>that something went wrong and the change failed. I played around with
>other try/finally, try/except, and try/except/else blocks, and I
>could never hit on a nice, clean solution to this objective. The code
>grew more cumbersome, less clear, and those results indicated I
>was working in the wrong direction.
>
>It seems like this type of problem is something that anyone programming
>in a language which uses exceptions, like C++ or Java, would encounter.
>As such, there are probably a variety of strategies that people have
>used to deal with exception handling during looping constructs, and
>means of reverting things should a problem occur prior to the desired
>end point. If we use database terminology, I'd like something like
>the following:
>
>setThickness(self, t):
> _ot = self.__thickness
> if abs(_ot - t) > 1e-10:
> self.__thickness = _t
> try:
> self.sendMessage('thickness_changed', _ot)
> self.modified()
> except:
> self.rollback()
> else:
> self.commit()
>
>The trick is writing the rollback() and commit() routines, as well as
>constructing a framework where such routines could be made for all
>the various attribute setting methods in PythonCAD.
>
>Writing this note has helped me think about things a little, but I still
>don't have a good idea as how to solve this problem. Any comments people
>care to add about an approach to accomplish rollback/commit type behavior
>would be welcomed.
>
>Art
>
>
NOTICE: Please note that this email, and the contents thereof,
are subject to the standard Peralex email disclaimer, which may
be found at: http://www.peralex.com/disclaimer.html
If you cannot access the disclaimer through the URL attached
and you wish to receive a copy thereof please send
an email to email at peralex.com
More information about the PythonCAD
mailing list