3

I have a global position keeper of screen items so items don't need store their own positions.

class Screen_Position_Keeper:

    # functions to add stuff

    def get_px_row( self, item ):
        try:
            return self.item_to_row[ item ]
        except KeyError:
            raise KeyError( 'You never registered that item' )

Why not just do the following?

class Screen_Position_Keeper:

    # functions to add stuff

    def get_px_row( self, item ):
        return self.item_to_row[ item ]
4
  • Are you asking in a generalized sense or specific for this situation?
    – enderland
    Sep 25 '16 at 22:34
  • 2
    You can certainly do that. But the error message isn't going to be as helpful as 'you never registered that item.' Instead It will be something along the lines of 'key not found in collection,' which is going to be meaningless to a user if the error is being displayed in the UI. Sep 25 '16 at 22:35
  • @enderland in the general sense. I provided an example because people like examples and signs of effort to think about the problem here. Sep 25 '16 at 22:40
  • 3
    In the general sense, you catch the exception and raise a new one if you want to translate it to a more specific exception and/or if you want to do something here first (like write a log entry) and then rethrow. Sep 25 '16 at 22:41
6

The answer to your question is you want meaningful exceptions.

In most contexts, this involves an exception that adds actionable information in one of two ways:

  • Useful typing to the developer who might be catching the exception
  • Useful error information to the user (and/or to the developer who can translate to that the user)

In your example, you aren't really adding anything to the normal exception. Catching and then effectively reraising the exception isn't useful. You could log this information, in which case you now added some value to the exception, but otherwise it's pointless.

Imagine this scenario:

try:
   my_web_function()
except my_custom_web_exception:
   do_something_with_this_exception()

It's entirely reasonable to imagine this scenario. You might make an http request. Most libraries have defined exception types, so you could catch those exceptions, and if it happens, do something.

The "something" is dependent on the specific application. In a http exception, maybe it's just retry the request. Etc.

But the point is catching the exception adds value and information and is actionable.

1
  • I would add: to avoid exposing your use of a third-party dependency by hiding the exceptions it throws. Sep 26 '16 at 2:37
3

An error I encountered late last week wasn't due to a python script, but the same principle applies. I had the "privilege" of having to use an old version of subversion on a project. I mistakenly mistyped

prompt: svn co https://some.site.com/some/path

The system's response was

svn: OPTIONS of 'https://some.site.com/some/path': 200 OK (https://some.site.com)

This message was not only unhelpful, it was incorrect. (Giving someone an HTTP/HTTPS "200 OK" status when what happened was far from okay is not OK.) Moreover, there are multiple pathways in subversion 1.9 that result in that erroneous "200 OK" error message. A much more helpful message would have been to tell me that I had mistyped the repository's URL.

Strictly speaking, this was not a bug in subversion. It was just a poorly worded error message. After all, subversion did properly detect and report the problem. From a user perspective, this was a huge bug that was mostly fixed in 2010. Thankfully, a quick google search resulted in multiple hits at stackoverflow.com.


Programmers are often taught that they should simply let an exception pass through if they can't do something about it. Instructors as well as students think "doing something" means correcting the problem. In many cases, there is nothing that can be done to correct a problem. This is an overly narrow view of "doing something." Adding context that enables a user to hone in on the problem and then fix it is "doing something."

Another way of looking at letting low level exceptions bubble up is that doing so is a leaky abstraction.

2

In Python, you absolutely should catch and re-throw that exception, for the sole reason that a reader of the code immediately sees that it might raise during normal execution.

This tells a reader that the corresponding value is returned, but you haven't thought about the proper behaviour when the key is not found:

def get(key):
  return items[key]

This tells me that you expect to be given missing keys, and that callers may receive a KeyError. A caller is expected to catch that error:

def get(key):
  try:
    return items[key]
  except KeyError:
    raise

This tells me that the key must be present, anything else would be a programming error:

def get(key):
  assert key in items
  return items[key]

If you create a custom error message, make sure that this message contains all information needed to fix and understand the problem. In particular, the error message should contain the missing key – use repr() not str() for this.

"item {!r} was not registered".format(key)

If formatting of the error message is expensive and the error message would be rarely read because it will be caught in most cases, you can create a custom exception to defer formatting until the error is displayed. You can still inherit from KeyError so that it can be caught without any changes:

class NotRegisteredError(KeyError):
  def __init__(self, key):
    super().__init__(key)
    self.key = key

  def __str__(self):
    return "item {!r} was not registered".format(self.key)
-1

One should not handle exceptions in core logic layer. It is the responsibility of the view layer to handle it and improve readability of the message. If given code is part of core logic layer then do not do exception handling here, instead delegate it to the calling view layer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.