Search code examples
pythonfunctiongeneratoryield

How can a generator be converted to a function that returns only the first value yielded by the generator?


I have some code that prints out any messages that it receives in a loop:

import pytg
import pytg.utils
import pytg.receiver

@pytg.utils.coroutine
def receiver_function(tg_receiver):
    while True:
        message = (yield)
        print(message)

tg_receiver = pytg.receiver.Receiver()
tg_receiver.start()
tg_receiver.message(receiver_function(tg_receiver))
receiver.stop()

I want to change this code such that it uses a simple function that halts until a message is encountered and then returns that one message and, of course, control:

import pytg
import pytg.receiver

def get_one_message():
    tg_receiver.start()
    while #<insert magic>:
        #<insert magic>
        message = #<insert magic>
    tg.receiver.stop()
    return message

print(get_one_message())

How could this be done?


Solution

  • pytg’s Receiver.message doesn’t seem to give you access to the generator’s GeneratorExit, so you might have to hack it:

    def get_one_message():
        message = None
    
        @pytg.utils.coroutine
        def receiver_function(tg_receiver):
            nonlocal message
            message = (yield)
    
        tg_receiver.start()
        tg_receiver.message(receiver_function(tg_receiver))
        tg_receiver.stop()
        return message
    

    Or dive into its undocumented (and therefore unstable) parts, though it’s not a good idea to do so:

    def get_one_message():
        tg_receiver.start()
        tg_receiver._new_messages.acquire()
        with tg_receiver._queue_access:
            message = tg_receiver._queue.popleft()
        tg_receiver.stop()
        return message