8

I'm thinking in a direction more advanced as well as difficult to find solutions this problem. Before coming to any decision, I thought of asking expert advice to address this problem.

The enhanced generators have new methods .send() and .throw() that allow the caller to pass messages or to raise exceptions into the generator (coroutine).

From python documentation: This can be very handy, especially the .throw() method that requests the generator to handle exceptions raised in the caller.

Request #1: Any example code for the above statement. I didn't find any code snippets for this explanation.

However, I'm considering the inverse problem as well: can a generator raise an exception, pass it to the caller, let the caller "repair" it, and continue the generator's own execution? That is what I would like to call a "reverse throw".

Request #2: Any example code for the above statement. I didn't find any code snippets for this explanation.

Simply raising exceptions in the generator is not OK. I tried "raise SomeException" in the generator, and that didn't work, because after a "raise" the generator can no longer be executed --- it simply stops, and further attempts to run the generator cause the StopIteration exception. In other words, "raise" is much more deadly than "yield": one can resume itself after yielding to the caller but a "raise" sends itself to the dead end.

I wonder if there are simple ways to do the "reverse throw" in Python? That will enable us to write coroutines that cooperate by throwing exceptions at each other. But why use exceptions? Well, I dunno... it all began as some rough idea.

CASE STUDY CODE:

class MyException(Exception):pass


def handleError(func):
''' handle an error'''
    errors =[]
    def wrapper(arg1):
        result = func(arg1)

        for err in findError(result):
            errors.append(err)

        print errors
        return result

    return wrapper

def findError(result):
'''
Find an error if any
'''
    print result
    for k, v in result.iteritems():
        error_nr = v % 2
        if error_nr ==0:
            pass
        elif error_nr > 0:
            yield MyException

@handleError
def numGen(input):

''' This function take the input and generates 10 random numbers. 10 random numbers are saved in result dictionary with indices. Find error decorator is called based on the result dictionary'''

    from random import randint
    result= {}
    errors = []
    for i in range(9):
        j = (randint(0,4))
        result[i] = input + j
    return result

if __name__ == '__main__':
    numGen(4)

Could anyone explain please both the ideas based on case study example(Raising exception in a generator and handle it elsewhere vice versa)? I do expect pro's and con's of both methods.

Thanks in advance.

Looking for an answer drawing from credible and/or official sources.

3 Answers3

0

Request #1 (Example for .throw())

I have never actually used this, but you could use it to change behaviour in the generator after the fact. You can also do this with .send of course, but then you'll need to deal with it in the line with the yield expressions (which might be in several locations in the code), rather than centralized with a try-except block.

def getstuff():
    i=0
    try:
        while True:
            yield i
            i+=1
    except ValueError:
        while True:
            yield i**2
            i+=1

generator = getstuff()

print("Get some numbers...")
print(next(generator))
print(next(generator))
print(next(generator))

print("Oh, actually, I want squares!")
print(generator.throw(ValueError))
print(next(generator))
print(next(generator))
Community
  • 1
  • 1
L3viathan
  • 26,748
  • 2
  • 58
  • 81
0

Request #1: Any example code for the above statement. I didn't find any code snippets for this explanation.

Take a look at ayscio source code

https://github.com/python/asyncio/search?utf8=%E2%9C%93&q=.throw

Request #2: Any example code for the above statement. I didn't find any code snippets for this explanation.

There is no way* to it today in python - maybe (if proven useful) can be a nice enhancement

*That is you can use yield to signal a framework to raise an exception elsewhere..

Yoav Glazner
  • 7,936
  • 1
  • 19
  • 36
  • The asyncio library has since been moved to the CPython repository. Try [this](https://github.com/python/cpython/blob/6281affee6423296893b509cd78dc563ca58b196/Lib/asyncio/tasks.py#L274) instead. – pyansharp Sep 28 '22 at 16:53
0

I have needed to solve this problem a couple of times and came upon this question after a search for what other people have done. I don't think I would used either of the methods suggested by the OP- they're pretty complicated.

One option- which will probably require refactoring things a little bit- would be to simply throw the exception in the generator (to another error handling generator) rather than raise it. Here is what that might look like:

def f(handler):
    # the handler argument fixes errors/problems separately
    while something():
        try:
            yield something_else()
        except Exception as e:
            handler.throw(e)
    handler.close()

def err_handler():
    # a generator for processing errors
    while True:
        try:
            yield
        except Exception1:
            handle_exc1()
        except Exception2:
            handle_exc2()
        except Exception3:
            handle_exc3()
        except Exception:
            raise

def process():
    handler = err_handler()
    for item in f(handler):
        do stuff

This isn't always going to be the best solution, but it's certainly an option, and relatively easy to understand.

Rick
  • 43,029
  • 15
  • 76
  • 119