Exception Handling Approaches

I’m nearly entirely self taught when it comes to my coding knowledge, and have been slowly expanding my capabilities as needs have come up/as I’ve had time to delve deeper and try to improve my scripts.

One thing that I’ve been trying to get better about is checking for, and handling errors in the code. My approach thus far has essentially been trying to keep in mind where an exception might arise, catching it, and using TaskDialog to put that in a pop-up so that I don’t have to open the file, and re-run the script if I hit an error.
Or, at least make the script output that error into a watch node so I can see the error from within the player.

The reason I’m making this post is that there are a number of more experienced/educated individuals in here and I was hoping some would be kind enough to take the time to share knowledge on the subject. :slight_smile:
Do you have a different, potentially better approach?
Do you use a catch-all? Something like creating a main() function and then running try/except on that?

It depends on how and where you’re authoring code.

Via nodes I used to use the DataShapes UI++ nodes to let users know there was a problem, tell them to hit undo in Revit, and give them a list of ‘known issues’ to look into.

With Python we can be more controlling, which is nice, but it means you have to be more controlling. I have been utilizing a try / except statement, with a nested combination of a ctypes messagebox and sys.exit on a few larger Python code bases lately, which pops up the formatted error message for the user to see, discards the transaction group previously started, and then terminates the active execution of the Python node in the Dynamo environment.

WIth zero touch nodes I imagine similar could be worked out, depending on how you wanted things to execute.

3 Likes

Ah, yeah I suppose I could have specified I write everything in Python.

I’m not familiar with the ctypes library, I might have to look into that. I’m vaguely familiar with exit commands, but haven’t looked at them in a while, nor tried using it within Dynamo. I might have to dig in there and see what comes up.
I’d be interested to see an example of some of the larger code bases if you have something you can reasonably share? Either way I appreciate the input! You’ve given me an extra direction to start snooping in.

Sadly no - these are customer paid code bases only at this point. I can likely put a sample of the combination effort together at some point; perhaps after BiLT?

1 Like

I expected as much, but figured it didn’t hurt to ask. I certainly wouldn’t complain if you were to upload a sample at some point!

To be honest, I’m mostly just curious to see some more ‘professional’ examples. As I’ve been getting more complex in what I’m writing, I imagine my knowledge gaps start to shine through more. I feel like it’d be good to have something to compare and contrast to as a learning aide.

1 Like

If you want to see some elaborate Python in the context of Dynamo, I highly recommend checking out some of the awesome packages out there. Genius Loci and Clockwork are great example packages in the Revit context. For significant complexity you can check out the Datashapes UI++ nodes (popped my head in to see about a migration to CPython the other day and about 2 minutes later said “Nope” and went another route - there is a LOT going on there).

For the sample… I actually needed to assemble this for a work thing after an unforeseen error possibility showed up, so I figured I’d assemble a boilerplate for you while I was at it.

import sys, ctypes #imports the relevant libraries into the Dynamo Python environment
ifnot IN[0]: #if the condition is not met
    msg = "There was an error with the inputs. Check provided data carefully."#formats the message
    stop = 0x10#option to add a stop icon to the message - includes chime sound
    exclam = 0x30#optoin to add a exclamation icon to the message - includes oh no sound
    info = 0x20#option to add an info icon to the message - no sound
    alwaysOnTop = 0x1000#option to keep the window above everything else
    ctypes.windll.user32.MessageBoxW(0, msg, "HEADS UP!", 0 | stop | alwaysOnTop ) #generate a pop up displaying the message
    #add " | feature" to the final input incorporate icons and window modes to the popup
    sys.exit("\n\n{0}\n\n".format(msg)) #terminates the python code exeution, putting it in a warning state in Dynamo
OUT = "This code was executed."#other code can go here - this is to prove the point

image

3 Likes

Sorry, I totally forgot to get back to this!

I really should poke my head around in the other packages more. I have looked around a bit here and there previously.

I appreciate the sample. Is there a reason you prefer to use ctypes instead of a task dialog box? Is it so you can use it in conjunction with sys.exit incase you need to exit in the middle of a run?

I tend to lean towards ctypes as it’s more robust for the simple “yes/no/ok” messages which I tend to stick to. More complex UI packages often make things more complex then I need them, though they certainly have uses and are worth knowing.

1 Like

An alternative example, using a context manager that handles exceptions (can be used on both engine).
It’s a bit advanced, but interesting to know
context-manager-handles-exceptions

import sys
import clr

clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import MessageBox, MessageBoxButtons, MessageBoxIcon

class ValidationError(Exception):
	def __init__(self, typeError = "Error", messageError = "An error occurred"):
		self._typeError = typeError
		self._messageError = messageError
		Exception.__init__(self, self._typeError)
		self.CMessageBox()
		
	def __str__(self):
		return "{} : {}".format(self._typeError , self._messageError)
		
	def CMessageBox(self):
		MessageBox.Show(self._messageError, self._typeError, MessageBoxButtons.OK, MessageBoxIcon.Error)

class ErrorContextManager:
	def __init__(self):
		pass
		
	def __enter__(self):
		return self
		
	def __exit__(self, exc_type, exc_value, exc_tb):
		if exc_type:
			error = "Error : {} at line {}\n".format( exc_value, exc_tb.tb_lineno)
			raise ValidationError(exc_type.__name__, error)
		else:
			MessageBox.Show("Script Finished", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information)
		return self
		
with ErrorContextManager() as sc:
	test1 = 10 / 2
	test2 = 10 / 2
	test3 = 10 / 1
	test4 = 4 / 1
	test5 = 10 / 2
	test6 = 10 / 1
	test7 = 4 / 0
	test8 = 10 / 2
	test9 = 10 / 1
2 Likes

Oh this does seem very nice, and I’ve been wanting venture into .NET territory anyways. I’ll have to keep this in mind. Thanks!

2 Likes