Generic Python error handler?

I’m trying to code a generic type of error handler that I can call from the except clauses from different scripts where the error_handler procedure can take different routes depending on what is passed to it.

Ideally, I want a generic error handler that will catch any/all types of errors.
My question is, will the str(e) in the except clause below work correctly for all or most types of errors?

try:

    i = 1 / 0  # divide by zero error

except Exception as e:
    error_handler(str(e))  # will this line str(e) work for all errors?
1 Like

We have tons of error handling where the except clauses look like (see image below) from a UDT Tag Value Change script.

I want to replace this code (code that builds parameters, calls Named Query and logs to the database)

I want to replace it with a single line code like error_handler() (see my original post above) where the error_handler procedure can take different routes depending on what is passed to it.

The error_handler() procedure will be in a script procedure that is in an inheritable/parent project

err_handler

For some reason, the code like getattr(e, 'message', repr(e)) throws me for a bit of a loop, possibly making me think the error handling is more complicated than it really is? My experiments using str(e) seem to work correctly. I think I just need someone else to tell me ha

getattr(e, 'message', repr(e)) is retrieving the property named message from e, and if e does not have a message property, it’s returning repr(e). repr can be distinct from str, but usually won’t be for Java objects, including exceptions. It’s not clear from your snippet if Exception is Python’s built-in Exception type or Java’s; someone may be importing from java.lang import Exception earlier in that script.

I no longer use from java.lang import Exception. I use from java.lang import Throwable instead. Python’s Exception will not catch java exceptions at all. Java’s Throwable will not catch any python exceptions. You must have two separate except clauses everywhere.

See also the PythonAsJavaException class in my later.py script–necessary if you want to push python stack traces into Ignition loggers.

3 Likes

What about

import traceback
traceback.format_exc()

Phil? Does this miss something?

It doesn’t play nice with the logging system. For loggers to deliver a neat traceback, you have to supply a subclass of java’s Throwable.

1 Like

Thank you, yes, the (message) property of the e object. You mentioned that (if e does not have a message property). What types of errors or when would e not have the message property?

My snippet is from an Ignition UDT script. There are not any (from java.lang import Exception) lines, or anything similar in the scripts.

Can you give an example of what that would look like (two except clauses).
I want to catch errors on system.tag.write, and try: / except: is not catching the error.

The vision client user does not have writePermissions for the first tag in this list, so I want the whole
routine to end. I'm printing to the console to debug what is happening, and I still get an error popup on the client, and I never get the "Error occurred" message in the console which indicates the Except code is NOT RUNNING.

Here is my code:

image

That's because a write that fails is not an error per se. You need to store the return from each write and check if the value is good or not

4 Likes

As Nick said, a failed write doesn't raise an exception, so there's nothing to catch.

I use this to check for failed writes:

base_logger = system.util.getLogger("Utils").createSubLogger("Tags")

def write(paths, values, logger=None):
	if logger is None:
		logger = base_logger
	write_return = system.tag.writeBlocking(paths, values)
	bad_writes = ["{} ({})".format(path, quality) for path, quality in zip(paths, write_return) if not quality.good]
	if bad_writes:
		logger.warn("failed writes: {}".format(bad_writes))

This logs a warning with the path and quality of every failed writes. You might want to rework it a bit to match your needs.

1 Like

Hey Phil, I'm looking for a little clarification on this post.

We currently use Except and from java.lang.import Exception to catch both Python and Java exceptions.The way I'm interpreting your post is that you have since switched to Throwable since it is able to catch both Java and Python's exceptions.

After some quick tests I'm starting to think I read/understood this incorrectly, but I'm still clinging tightly to the hope that I'll be vindicated in my initial understanding (oh how I'd love to remove an except clause)... care to raise me up or tear me down?

Throwable is a Java class and as such has nothing to do with Python errors.

Not to answer for Phil, but as I understand he has switched to Throwable because all Java exceptions and errors inherit from Throwable. Which makes the except for Java exceptions more robust. It does not remove the need for a second except clause for Python Exceptions.

2 Likes

Entirely correct. It also eliminates the clash between the python and java Exception name.

2 Likes

Ok, this confirms my suspicions. I ended up going this route to remove the double except clause..

import java.lang.Exception
try:
    #some code
except (Exception, java.lang.Exception) as e:
	if isinstance(e, java.lang.Exception):
		message = e.getMessage()
	else:
		message = str(traceback.format_exc())

Thanks for the discussion.
edit: I wonder what the implications will be if the code throws both...guessing that's why no one has done this elsewhere, thoughts?

Why?

The separate except clauses will execute far faster, as the implementation does the class checking without needing to invoke the interpreter to run isinstance().

Just use the two clauses. Really.

Also, you really want to use loggers to report exceptions of any kind, so you get neat tracebacks. Using the separate clauses makes use of my PythonAsJavaException() class really easy.

Hey that makes some sense, but what exactly does "far faster" mean in a situation like this?

Also fair point on the logger comment, I'm strictly using the message variable to supply a popup that the user will get to report errors. Should probably add the logger piece in there as well so we have more information to go off of.

edit: I'll take a peak at your class if you let me know where to find it!

https://www.automation-pros.com/ignition/later.py

There's probably a zillion references to this on the forum.

1 Like

When I saw your example there I thought, hey, that looks just like the error handling I inherited from my predecessor, they must have both followed the same example from somewhere. Then I noticed your username. You are my predecessor! :joy:

FWIW, I have had no problems using str(e) instead of getattr for either Python or Java Exceptions.

Not sure what you mean by this. Once any exception is thrown/raised, no further exception can be thrown by that original code. So only one except clause will ever fire at a single time.

(Exception handlers can throw/raise their own errors, but those get caught by the next enclosing try: block.)