Handling Errors in Perspective

In Perspective, I have had trouble with errors not being raised to the user. If I am in a Designer and I execute a button click or something, any errors will pop up in an Error window and print to the console. But if I launch a browser to that page, errors are not raised to be visible to the user.

If I were to set up a data entry screen and then create a Submit button using a button component, I would set up some scripting in the onActionPerformed event of that button to send the data to the database. If there was some problem inserting into the database, the user would not know.

What is the typical method for setting up error handling so that the user would find out there is a problem in this example?

If it’s something you need to alert the user about, I would wrap your logic in a try/except block, and in the exception handling you should use system.perspective.openPopup(). You’ll want to create a View which is able to convey information to the user, whether it the stacktrace or just a warning that something failed.

4 Likes

That sounds simple enough, thank you.

Hi cmallonee, I'm doing as suggested. To the popup window I'm passing the traceback.

On a View component

def runAction(self, event):
	try:
		_.update_db_with_error()
	except:
		_.error(self)

Script module

def error(_self):
	if _self.session.props.device.type == 'designer':
		raise
	
	params = {'message': traceback.format_exc()}
	system.perspective.openPopup(
		'random_id',
		'Popup/ErrorMessage',
		params=params,
		title='EXECUTION ERROR: contact developers',
		modal=True
	)

I noticed that if I'm NOT using try/catch on the View component I'm getting native error window with full traceback which includes the cause (which is sooo good) of the exception, like this

caused by SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`mes`.`product_code_line`, CONS.....

But when I'm using try/catch with traceback.format_exc() it cuts the trace and last message is

line 122, in update_db Exception: java.lang.Exception: Error executing system.db.runPrepUpdate(DELETE FROM product_code WHERE id = ?, [25], db_con, , false, false)

Is there the way to capture full exception trace by traceback.format_exc() as same as the native error window?

Jython's bare except won't catch Java exceptions, for complicated reasons. You must add an additional catch clause:

3 Likes

Thank you PGriffith

Bare except in the code above catches the same error with the same traceback as java.lang.Throwable. If it'll be an any chance to catch "the real SQL" exception in python script, it would be great.

In gateway scope, you get the real SQL exception. Possibly as a chained cause. Those exceptions are not serializable (at least, not without the same JDBC drivers in the receiving scope), so they get summarized in Vision Client scope and Designer Scope.

It seems like it is impossible to get the chained cause (the SQL exception) to the Designer Scope using python.

Is there any news from the Ignition dev team on solving this? As a temp fix it would be good to concat the message of the chained cause to the java.lang.Throwable message.

We're planning to completely migrate to Perspective and we'll need to get the actual cause message to the logs and on the screen for faster troubleshooting.

It will never be solved, because those classes are only available in the gateway.

Consider using a gateway message handler to run your SQL, and that will be able to analyze any exceptions completely. Use system.util.sendRequest() from Vision or Designer scope to run the handler.

1 Like

Perspective runs in gateway scope, so the complete message should be available there.

What's the best way to catch errors in Perspective? What was I doing wrong in the code above :point_up_2: (4th message from the top) Handling Errors in Perspective - #4 by Alex_SHARIKOV

The goal is to catch the entire error message including the SQL cause.

Jython's traceback module doesn't do java causes. You need to use java except clauses, and use java's stack trace tools. See my post that Paul linked above.

How python's catch should look like? Do I do it incorrectly below?

This is a script on Perspective button onActionPerformed:

def runAction(self, event):
	import java
	
	try:
		_.update_db_with_error()
		
	except java.lang.Exception as e:
		system.perspective.print('java.lang.Exception')
		system.perspective.print(e)
		
	except java.lang.Throwable as e:
		system.perspective.print('java.lang.Throwable')
		system.perspective.print(e)

java.lang.Exception is a subclass of java.lang.Throwable, so that is redundant. If you look at the linked examples scattered over this forum, one except clause catches jython's Exception, the other catches java's Throwable. But printing a throwable (or java's exception) does not include the cause. Use the throwable's printStackTrace() method to get everything.

{ You've responded to the linked post above, so I would presume you've followed the links to related discussions therein? }

I've read the suggested links but didn't understand how to implement it. Thank you for your guidance. My code so far:

View component

def runAction(self, event):
	try:
		_.update_db_with_error()
	except:
		_.error(self)

and my script module:

def error(_self):
	ex_type, ex, stacktrace = sys.exc_info()
	
	is_java_ex = type(ex_type) == type(java.lang.Exception)
	cause = str(ex.getCause()) + ' ***** ' if is_java_ex else ''
	
	if _self.session.props.device.type == 'designer':
		raise Exception, (cause, ex), stacktrace

		
	trigger = ' ***** by ' + _self.name + ' from ' + _self.page.props.path
	
	message = cause + traceback.format_exc() + trigger
	params = {'message': message}
	log(message, 'error')
	system.perspective.openPopup(
		'random_id',
		'Popup/ErrorMessage',
		params=params,
		title='EXECUTION ERROR: Contact developers',
		modal=True
	)

not sure if this is an efficient way but it shows me the desired traceback and the cause in Perspective and in Designer for both jython errors and the Throwables

I think you will need to do this. @pturmel is usually the exception wizard.

def runAction(self, event):
	from java.lang import Throwable
	try:
		_.update_db_with_error()
	except Exception, e:
		_.error(self,e)
	except Throwable, t:
		_.error(self,t)

You will need to modify the error function to take in that error. Then check the type in the function.

1 Like

Yes, he is, he helped me understand it more.

That's what I'm doing in the script module. At line

is_java_ex = type(ex_type) == type(java.lang.Exception)

I check if it is Throwable and then take appropriate action.

The reason I'm doing it that way is that I prefer to keep the View code is tidy as possible. The except: below will catch all errors and I deal with them in the _.error(self) function.

I may be wrong... But Im not sure that's how jython works. A naked except will only catch python errors. You must add the second except for the Java exceptions. I've never seen it work another way.

Seems like it does. I've tested this approach with Java and Python exceptions.
Now I get both types, including Java cause, in Error window when working with the Designer and in Perspective Popup I have the traceback with the Java cause.

I used Developer Guid for Jython at Chapter 7: Exception Handling and Debugging — Definitive Guide to Jython latest documentation in particular Example 5-2: Using sys.exc_info()