1

I would like to return a custom set of messages based on the specific cause of the TypeError.

def f(x, y):
    value = x + y
    return "Success! ({})".format(value)

def safe(function, *args):
    try:
        result = function(*args)
    except TypeError:
        if "not_enough_args":  # What actual condition can go here?
            return "Not enough arguments."
        elif "too_many_args":  # What actual condition can go here?
            return "Too many arguments."
        else:
            return "A TypeError occurred!"
    else:
        return result

safe(f, 2)  # "Not enough arguments."
safe(f, 2, 2)  # "Success!"
safe(f, 2, 2, 2)  # "Too many arguments."
safe(f, '2', 2)  # "A TypeError occurred!"

Use of the actual TypeError object would be preferable.

2Cubed
  • 3,401
  • 7
  • 23
  • 40

2 Answers2

0

Here's a possible solution to your question:

def f(x, y):
    value = x + y
    return "Success! ({})".format(value)


def safe(function, *args):
    try:
        result = function(*args)
    except TypeError as e:
        return str(e)
    else:
        return result


def safe2(function, *args):
    try:
        result = function(*args)
    except TypeError as e:
        if "required positional argument" in str(e):
            return "Not enough arguments."
        elif "positional arguments but" in str(e):
            return "Too many arguments."
        else:
            return "A TypeError occurred!"
    else:
        return result

print(safe(f, 2))
print(safe(f, 2, 2))
print(safe(f, 2, 2, 2))
print(safe(f, '2', 2))
print('-' * 80)
print(safe2(f, 2))
print(safe2(f, 2, 2))
print(safe2(f, 2, 2, 2))
print(safe2(f, '2', 2))

If you don't need to identify which TypeError it's been raised (safe), returning the exception could just do the job. If not (safe2), you could just parse somehow the exception as a string.

If you don't want to parse strings and having custom TypeError exceptions, then you'd just need to subclass TypeError because the existing hierarchy isn't providind you specializations of that one.

BPL
  • 9,632
  • 9
  • 59
  • 117
  • I need to identify which `TypeError` has been raised - I would like to generate custom error messages for each situation. I'm not too keen on using string parsing either, if possible, but it might have to be the route I take. – 2Cubed Aug 21 '16 at 15:07
  • @2Cubed I've edited and completed my answer. In any case, I had provided you this solution as the fastest & easiest way to go. – BPL Aug 21 '16 at 15:19
0

I wouldn't mess around with changing the nature of the exception object, and having a long list of conditional statements to print out your own custom message. Instead just return back the exception object. You can do this in your except line as:

except TypeError as e

So, now you will have an object e of your TypeError exception. From there, with the object in hand, you can do whatever you want.

Observe the example below. You will get whatever exception happens, and you will not modify its nature, instead you are using the object as is and you can print out the exact message of the failure encountered.

def f(x, y):
    value = x + y
    return "Success! ({})".format(value)

def safe(function, *args):
    try:
        result = function(*args)
    except TypeError as e:
        return e
    else:
        return result

print(safe(f, 5, 6, 7))
# f() takes 2 positional arguments but 3 were given
print(safe(6))
# 'int' object is not callable

Furthermore, what you can do outside of the safe method, you can simply check for the type coming back from the safe method and act accordingly.

Example:

result = safe(5)
if type(result) is TypeError:
    print(result)
idjaw
  • 25,487
  • 7
  • 64
  • 83
  • I have no need to change the exception object itself, but I do need to generate a custom exception message. I'm using Python as a backend for a UI, and I would like to be able to display a custom error message if the number of given arguments does not match the expected. – 2Cubed Aug 21 '16 at 15:05
  • Inside the exception, using `e`, you will have access to the message. Check `e.args`. Even if you take a simple string representation: `str(e)`, you will have the exception message. From there, you can match whatever you need to create a custom message. Furthermore, you can actually create a custom Exception, say `UnsafeCallException` that inherits from `TypeError` and have your logic in there to handle what you need and display the appropriate message. – idjaw Aug 21 '16 at 15:07
  • I would like to use values in the actual exception object to generate custom messages, rather than parsing/matching specific parts of the existing error message. – 2Cubed Aug 21 '16 at 15:10
  • Have you inspected the exception object to see what is actually available to you, to know how feasible what you are trying to do is? Do a `dir(e)` to take a look what is available to you to see what is close to what you need. Furthermore, if you perform the following: `e.__traceback__.tb_frame.f_locals` you will have a dictionary of details you can use. Finally, take a look at this answer that provides a good breakdown on what you can do with exceptions: http://stackoverflow.com/a/1278740/1832539 – idjaw Aug 21 '16 at 15:22
  • `e.__traceback__.tb_frame` seems to be helpful. Unfortunately, I can't seem to find the actual error parameters (for example, the `2` and `3` in `f() takes 2 positional arguments but 3 were given`). – 2Cubed Aug 21 '16 at 16:10