pythoncoroutineyield

how to catch throw and other exceptions in coroutine with 1 yield?


I have var DICTIONARY, which is a dictionary where the keys are English letters and the values are words that start with the corresponding letter. The initial filling of DICTIONARY looks like this:

DICTIONARY = {
    'a': 'apple',
    'b': 'banana',
    'c': 'cat',
    'd': 'dog',
}

My code has 2 while loops, since higher try-except would end the entire generator loop:

def alphabet():
    while True:
        try:
            letter = yield  #waiting for first input from send
            while True:
                try:
                    letter = yield DICTIONARY[letter]
                except KeyError:  
                    letter = yield 'default'  # return 'default', if key is not found
                except Exception:
                    letter = yield 'default'
        except KeyError:
            letter = yield 'default' 
        except Exception:
            letter = yield 'default' 

However for the input:

coro = alphabet()
next(coro)
print(coro.send('apple'))
print(coro.send('banana'))
print(coro.throw(KeyError))
print(coro.send('dog'))
print(coro.send('d'))

expected output:

default
default
default
default
dog

But I don't catch last default - it's None:

default
default
default
None
dog

What is wrong?


Solution

  • I believe that there is two errors. First - is that you are using while True: block twice. I would simplify the code into smth like this:

    def alphabet():
        while True:
            try:
                letter = yield  #waiting for first input from send
                try:
                    word = DICTIONARY[letter]
                except KeyError:  
                    word = 'default'  # return 'default', if key is not found
                except Exception:
                    word = 'default'
                yield word
            except KeyError:
                yield 'default' 
            except Exception:
                yield 'default' 
    

    Second issue is that generator stack on previous yield function. If you'll add more next calls the issue will be fixed:

    coro = alphabet()
    next(coro)
    print(coro.send('apple'))
    next(coro)
    print(coro.send('banana'))
    next(coro)
    print(coro.throw(KeyError))
    next(coro)
    print(coro.send('dog'))
    next(coro)
    print(coro.send('d'))
    

    Output:

    default
    default
    default
    default
    dog