5

I'm making a Python irc bot. For some reason the yield statement in my join() method makes it skip the method altogether, but if I replace it with a return it works fine. However, I need to yield an error per each unsuccessful join attempt.

I have a join method of the bot that returns a server error command code response if the join is unsuccessful for some reason. It's None if the bot joins successfully.

unsuccessful = bot.join(channels)

I would be able to do:

if unsuccessful:
    for error in unsuccessful:
        print(error)

The join method looks like this

def join(self, channels):
    chan_errors = range(471, 480)  # See RFC for commands 471-479

    if isinstance(channels, str):
        channels = [channels,]

    for channel in channels:
        self.send('JOIN %s' % channel)
        for response in self.get_response('JOIN', chan_errors):  # Verify
            if response.command in chan_errors:
                channels.remove(channel)
                yield response
    self.channels.append(channels)

If I switch the "yield response" with "return response" it runs the method.

The get_response method looks like

def get_response(self, commands, terminators=None):
    for msg in self.msg_gen():
        self.handle(msg)
        if msg.command in commands:
            if terminators is None:
                return msg
            yield msg
        if msg.command in terminators:
            return msg

It receives messages from a message generator. The commands are the server command the caller is looking for and the terminators drop out of the generator when one is found. It's sort of like a coroutine.

Does anyone know what is happening here?

5
  • 4
    Do you understand what yield is and how to use generators? bot.join(channels) returns a generator; you need to iterate over that to get the values that it yields.
    – BrenBarn
    Commented Oct 16, 2013 at 20:50
  • @BrenBarn I'll edit the question to illustrate that I will be using 'unsuccessful' as a generator Commented Oct 16, 2013 at 20:52
  • 1
    In what way is it "not working"? What does it do? Are you saying it doesn't print anything in the loop?
    – BrenBarn
    Commented Oct 16, 2013 at 20:56
  • It does not step into the join() method at all and runs the very next method call. No exceptions or anything. Commented Oct 16, 2013 at 20:58
  • 5
    It won't step into the join method until you begin to iterate over unsuccessful. That's how generators work.
    – BrenBarn
    Commented Oct 16, 2013 at 20:58

1 Answer 1

5

By putting a yield statement in the join method, you're making it a generator. Calling a generator method just creates a generator function; you need to iterate through it to get data. However, since execution inside a generator stops every time it hits a yield statement, you need to exhaust all of its contents if you want it to fully run.

This way your code runs all the way through:

join_gen = bot.join(channels)
for error_msg in join_gen:
    print error_msg

Generators in the Python 2 docs

I'm not sure a generator is a good option for you here though. You might be better served returning response right when you have the first error, or if you want to go through all the channels, append each error response to an error list and return that list when you're done.

2
  • Okay, I was going to go with the latter if I couldn't get this to work anyway. Otherwise I'd be pointlessly iterating through None. It'd be like going back to Java or other languages and having to return an array. I'm surprised I couldn't find this elsewhere. I figured the generator adds to the variable as it iterates. This makes sense now. Commented Oct 16, 2013 at 21:08
  • 1
    The link Mark Hildreth posted, The Python yield keyword explained, is one of the best (if not THE best!) resources for Python generators - much better than the pydocs! e-satis just kills it :D
    – crennie
    Commented Oct 16, 2013 at 21:20

Not the answer you're looking for? Browse other questions tagged or ask your own question.