How to raise an exeption that tells me in what loop I was in?

Question:

I have the following input to an assign_points(lists) function:

[['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'],
['0', '0', '10', '10', '0', '0', '10', '10', '0', '0', '0', '10', '10', '10', '10', '10', '0', '0', '0'],
['0', '0', '10', '10', '0', '0', '10', '10', '0', '0', '0', '0', '0', '0', '0', '10', '10', '0', '0'],
['0', '0', '10', '10', '0', '0', '10', '10', '0', '0', '0', '0', '0', '0', '0', '10', '10', '0', '0'],
['0', '0', '10', '10', '10', '10', '10', '10', '0', '0', '0', '0', '10', '10', '10', '10', '0', '0', '0'],
['0', '0', '0', '10', '10', '10', '10', '10', '0', '0', '0', '10', '10', '0', '0', '0', '0', '0', '0'],
['0', '0', '0', '0', '0', '0', '10', '10', '0', '0', '0', '10', '10', '0', '0', '0', '0', '0', '0'],
['0', '0', '0', '0', '0', '0', '10', '10', '0', '0', '0', '10', '10', '10', '10', '10', '10', '0', '0'],
['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'],
['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0']]

And in my code I have something like:

def A(point):
    try:
        #something
        B(val_1, val_2, val_3)
    except ValueError:
        #It should quit here but I want to say what line and item (Y, X loop) it was in


def B(val_1, val_2, val_3):
    try:
        ...
    except ValueError:
        #It should quit here but I want to say what line and item (Y, X loop) it was in

def assign_points(lists)
    for y, list in enumerate(lists):
        for x, point in enumerate(list):
            A(point)

I have a function B() inside of a function A() inside two for loops.
If there’s anything wrong inside the function B I want to raise an exception that gives me what loop it was on.

I know I can pass x and y but I feel like it bloats the code and makes it less reusable. I was wondering if there was an alternative to that.
I want the exception to say " item on line is improperly formatted"

Is this possible to do?

Asked By: MiguelP

||

Answers:

Neither A nor B know (nor should know) about the outer iteration. But assign_points knows exactly where it is. Move the exception handling there.

def A(point):
    B(val_1, val_2, val_3) # exception raised in here

def B(val_1, val_2, val_3):
        ... an exception raised here

def assign_points(lists)
    for y, list in enumerate(lists):
        for x, point in enumerate(list):
            try:
                A(point)
            except ValueError as e:
                print(x, y, e)

If you need context information from the lower level functions, they could catch ValueError and reraise with details. Perhaps by adding attributes to the existing exception or creating a new one with the specifics.

For example

class BError(Exception):
    
    def __init__(self, val_1, val_2, val_3):
        self.val_1 = val_1
        self.val_2 = val_2
        self.val_3 = val_3
        self.message = f"{val_1}, {val_2}, {val_3} are super bad"
        super().__init__(self.message)

def A(point):
    B(val_1, val_2, val_3) # exception raised in here

def B(val_1, val_2, val_3):
    try:
        .... calculation raises ValueError
    except ValueError as e:
        raise BError(val_1, val_2, val_3)

def assign_points(lists):
    x = y = None
    try:
        for y, list in enumerate(lists):
            for x, point in enumerate(list):
                A(point)
    except BError as e:
        print(x, y, e)

There are lots of options here. For instance, perhaps BError should keep the original error message. But you get the idea.

Answered By: tdelaney
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.