How to catch a custom exception in a try/except block?

I am using Python 3.10. I have an issue catching my custom exception in a try/except block.

I define my custom exception like this:

class InvalidSquareError(TypeError, ValueError):
    """Create a custom exception for an invalid chess board square."""

Then I want to catch my custom exception in my method like this:

def valid_square(self, square):
    """Check whether a valid chess board square was selected."""
    try:
        move = self.board.find_move(self.from_square, self.to_square)
    except InvalidSquareError:
        self.from_square = square
    else:
        # Do something with the move object.

So, if the find_move() method (of a 3rd-party library) raises a TypeError or a ValueError, my custom exception InvalidSquareError (that inherits from TypeError and ValueError) is never caught and handled in the try/except block. Can you please tell me what am I doing wrong?

The except InvalidSquareError catches all exceptions that are InvalidSquareError or subclasses of InvalidSquareError. TypeError and ValueError are superclasses of InvalidSquareError and thus won’t be caught.

To catch TypeError and ValueError, you can do except (TypeError, ValueError).

Yes, I know that I can do except (TypeError, ValueError), but I want to be more descriptive with my InvalidSquareError custom exception. Please tell me how can I define my InvalidSquareError custom exception to be a superclass of TypeError and ValueError. How can I achieve this?

TypeError and ValueError are built-in exception classes; you cannot create a superclass for them. If you want to use your own custom exception, you’ll have to raise it yourself.

Or maybe I can do

InvalidSquareError = (TypeError, ValueError)

and then do

except InvalidSquareError:
    # Handle the exception.

EDIT: As tested just now, this works. Is this even considered Pythonic? Shall I rather declare this as a constant and instead of InvalidSquareError declare it as INVALID_SQUARE_ERROR? Would this be awkward?

Is it generally a bad idea to rename built-in exception class names in Python?

I would say that you are doing a disservice by not being explicit about the exceptions being caught. If someone were to look at the documentation for board.find_square(), they would see it throws TypeError or ValueError. Then they will read your code and see you catching InvalidSquareError which could lead to confusion.

If you were to actually redefine the exception names with your own (i.e. TypeError = “whatever”) that would certainly lead to confusion, so you probably shouldn’t do it. However, your solution is just making an alias for the names rather than redefining them, which is usually fine and even used in the standard library in some places.

You can add a try/except block in your method, catch TypeError and ValueError, then throw InvalidSquareError.

You can optionally enclose that block in a decorator and reuse on other methods.

Btw, most custom exceptions sub-class Exception or BaseException:

class InvalidSquareError(BaseException):
    ...

Hi Boštjan,

You can’t create a new class that is a superclass of TypeError and
ValueError.

TypeError and ValueError already exist, and they already know what their
superclasses are. They inherit from:

object --> BaseException --> Exception --> TypeError

and the same for ValueError.

The descriptive way to catch both TypeError and ValueError is to
explicitly write what you are trying to do:

except (TypeError, ValueError)

That is explicit and clear. But if you want to hide the fact that you
are catching those two exceptions, and make the reader work harder to
understand your code, you can define a named variable at the beginning
of your program:

InvalidSquareExceptions = (TypeError, ValueError)

try:
    ...
except InvalidSquareExceptions:
    ...

I’m trying to understand your valid_square method. By the name and the
description, the method should test whether a square is valid or not,
probably returning True or False. (There are other good ways to perform
validation checks, but returning True/False is the most common.)

Instead, it looks like:

  1. valid_square accepts a square input, but you don’t use it except
    as a fall-back in the case of error.

  2. The board.find_move method takes two squares as input, neither of
    which is the argument given to valid_square. But this makes no
    sense to me: if you know the starting and ending squares, you
    automatically know what the move is: start and the starting square
    and end at the ending square. You don’t have to “find” a move.

  3. The find_move method apparently raises TypeError or ValueError
    to signal… what? a TypeError should only mean a programming
    error on your part. So if your code has a bug, you catch the
    TypeError and pretend it never occurred.

  4. And lastly, instead of testing that the square is valid, your
    valid_square method then does “something” with the move object.
    What? Actually make a move?

None of these methods seem to care about the piece. What is or isn’t a
valid move should depend on the piece, not the squares. What is valid
for a bishop is not valid for a rook or a knight. So your design here
seems to be very strange to me.

If I see something called InvalidSquareError (singular, not plural) then
I expect it to be a subclass of Exception.

issubclass(InvalidSquareError, Exception)

will fail with a TypeError.

@steven.daprano, I already set a to_square elsewhere in my class, then I set a from_square in this valid_square() method. I can’t set both from_square and to_square at the same time, I have to do it separately, and that is because the user clicks the first square (the origin square of a chess move [the from_square]) and then the user clicks the second square (the target square of a chess move [the to_square]).

I know that it is kind of confusing that I set the to_square first and only then the from_square. I must have the to_square as not being None first, then I check whether the from_square is not None as well. And if from_square is None, the find_move() method of the python-chess 3rd-party library raises a TypeError, so I catch a TypeError and make the value of from_square to the value of the square argument (an argument of my valid_square() method).

Note that the find_move() method also raises a ValueError if a chess move is not legal in a chess board position, so I also catch a ValueError in my valid_square() method. The find_move() method tries to find a legal chess move in a chess board position, that’s why the find_move() method must be called on a board object (which is an instance of a python-chess library’s Board() class).

I know my approach is stupid, but it works. Do you have any suggestion of a Pythonic way to implement this?

EDIT: I have removed my custom exception class and I now catch a TypeError and a ValueError explicitly, so like except (TypeError, ValueError).