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 PROTECTED]
_______________________________________________ PythonCAD mailing list [email protected] http://mail.python.org/mailman/listinfo/pythoncad
