TypeError: catching classes that do not inherit from BaseException is not allowed

I try to compute the factorial of a large number with tail-recursion optimization decorator in Python3. The test python code is as follows:

# This program shows off a python decorator(
# which implements tail call optimization. It
# does this by throwing an exception if it is 
# its own grandparent, and catching such 
# exceptions to recall the stack.

import sys

class TailRecurseException:
  def __init__(self, args, kwargs):
    self.args = args
    self.kwargs = kwargs

def tail_call_optimized(g):
  """
  This function decorates a function with tail call
  optimization. It does this by throwing an exception
  if it is its own grandparent, and catching such
  exceptions to fake the tail call optimization.

  This function fails if the decorated
  function recurses in a non-tail context.
  """
  def func(*args, **kwargs):
    f = sys._getframe()
    if f.f_back and f.f_back.f_back \
        and f.f_back.f_back.f_code == f.f_code:
      raise TailRecurseException(args, kwargs)
    else:
      while 1:
        try:
          return g(*args, **kwargs)
        except TailRecurseException as e:
          args = e.args
          kwargs = e.kwargs
  func.__doc__ = g.__doc__
  return func

@tail_call_optimized
def factorial(n, acc=1):
  "calculate a factorial"
  if n == 0:
    return acc
  return factorial(n-1, n*acc)

print(factorial(10000))
# prints a big, big number,
# but doesn't hit the recursion limit.

@tail_call_optimized
def fib(i, current = 0, next = 1):
  if i == 0:
    return current
  else:
    return fib(i - 1, next, current + next)

print(fib(10000))
# also prints a big number,
# but doesn't hit the recursion limit.

The above code snippet is converted from the example code given here [1] by the following steps:

$ pyenv shell datasci
$ python --version
Python 3.9.1
$ pip install 2to3
$ 2to3 -w this-script.py

However, when I try to test the above script, the following error will be triggered:

$ python this-script.py
Traceback (most recent call last):
File "/home/werner/this-script.py", line 32, in func
return g(*args, **kwargs)
File "/home/werner/this-script.py", line 44, in factorial
return factorial(n-1, n*acc)
File "/home/werner/this-script.py", line 28, in func
raise TailRecurseException(args, kwargs)
TypeError: exceptions must derive from BaseException

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/werner/this-script.py", line 46, in <module>
print(factorial(10000))
File "/home/werner/this-script.py", line 33, in func
except TailRecurseException as e:
TypeError: catching classes that do not inherit from BaseException is not allowed

Any hints for fixing this problem will be highly appreciated.

[1] Tail-Recursion Optimization Decorator in Python - Stack Overflow

Regards,
HZ

Please, try to write code in a code block so that the indentation is not lost.

To solve your problem, make sure that your custom exception class inherits from BaseException:

class TailRecurseException(BaseException):
   ...

Thank you for your reminder. These codes are already formatted by the code block marker. This problem is caused by my pasting code from the posts of Google Group which caused the indentation to go wrong.

Thank you again. It solves the problem.

HZ

The most important hint to solving problems when programming is to read
the error messages. Python tells you twice what the problem is, once
when you try to raise the exception, and then again when you try to
catch it:

TypeError: exceptions must derive from BaseException

TypeError: catching classes that do not inherit from BaseException is not allowed

To fix that, make sure that your error class inherits from
BaseException, or even better, Exception.

class TailRecurseException(Exception)

It is better to inherit from Exception rather than BaseException
directly. Or even from one of the more specialised exception classes.

The docs say:

“programmers are encouraged to derive new exceptions from the Exception
class or one of its subclasses, and not from BaseException.”

https://docs.python.org/3/library/exceptions.html

Thanks. I was just using the info from the traceback; I should have search before answering. (And I should probably add this case to friendly-traceback…)

1 Like