Is there a way to display the traceback of a UserError in the console for us developers?
When I catch an exception and “forward” it as a UserError, it looks like there is no way to see the traceback. I could find it during debugging, but that’s a bit more cumbersome.
An example
# Somewhere deep in the code
def some_function():
raise KeyError("Unable to find ...")
# "forwarded" like
try:
some_function()
except Exception as e:
raise UserError(e)
In the UI this will shield the end-user from the traceback
I did find a workaround, but perhaps that’s not the best approach
class DevFriendlyUserError(UserError):
def __new__(cls, *messages):
# Print tracebacks for all messages that are Exceptions
for msg in messages:
if isinstance(msg, Exception):
traceback.print_exception(type(msg), msg, msg.__traceback__)
return super().__new__(cls, *messages)
# and use that class "forward" exceptions
raise DevFriendlyUserError(e)
Thanks for this question. I was wondering, what is the reason for catching exceptions so broadly? From my experience, I would catch specific errors, so that, in the case of an unexpected error from users, they can report these, and I can improve the code for those cases as well. E.g.
# Somewhere deep in the code
def some_function():
raise KeyError("Unable to find ...")
# "forwarded" like
try:
some_function()
except ValueError as e:
raise UserError("Oops, don't use these values")
except IndexError as e:
raise UserError("You did not fill the table")
The generic catch is because I want to show the exception “title” to the user. Our current codebase it’s that robust that it handles all possible exceptions. By showing the the “title”, it is sometimes informative enough for the end-user that he can fix the problem on his own. For example: Impossible param combinations, or an empty param that is required for a specific situation.
If we don’t “forward” it as a UserError, the end-user will only see “Something went wrong”. But then the traceback isn’t shown when I’m developing the app.
Considering your approach from a user’s perspective, I can imagine that the user does not always understand the technical error communicated to them. This assumes, however, that the errors communicated by them are the generic errors that get printed in the terminal, such as:
IndexError: list index out of range
And for these cases, I generally believe it is better to communicate more clearly what actually went wrong. I can understand that providing more context than only “Something went wrong” in the short term is better for the user, but reporting it to the developer to improve the communication in the long run would result in a better application.
Could you confirm whether my assumption is correct on whether generic errors are being communicated? And also, I’m curious to hear what you think of this take of mine.
I see in your example that you did create an exception with a custom title. If it is expected errors due to wrong input, maybe a workaround would be to create a custom exception, such as:
class ViktorException(Exception):
"Raised when the error is due to exceptions related to a VIKTOR input."
pass
And then catching these specifically by:
# Somewhere deep in the code
def some_function():
raise ViktorException("Unable to find ...")
# "forwarded" like
try:
some_function()
except ViktorException as e:
raise UserError(e)