Craig
Craig

Reputation: 583

How can I "double return" through multiple functions in Python?

I have three functions that "trickle down" and get called within another like so:

async def start_swings(self, server, user):
    try:
        while self.jailed[server.id][user.id]['current'] != 0:
            await self.get_question(server, user)
        else:
            await self.escape_jail(server, user)
    except KeyError:  # User isn't jailed
        pass

async def get_question(self, server, user):
    rnd = random.choice(range(2))
    if rnd == 0:
        await self.swing_math(server, user)
    elif rnd == 1:
        await self.swing_reverse(server, user)

async def swing_math(self, server, user):
    num1 = random.randint(1, 1000)
    num2 = random.randint(1, 1000)
    ops = ['+', '-']
    op = random.choice(ops)
    ans = int(eval(str(num1) + op + str(num2)))
    await self.bot.send_message(user, "**\N{HAMMER} It's going to take you `{}` more swings to break free.**\n```What is {} {} {}?```\n*I might doze off if you take too long to answer. If you don't get a reply from me within a few seconds, type `>swing`. If you try to use `>swing` as a way to reset your current question, you'll get a cooldown penalty.*".format(self.jailed[server.id][user.id]['current'], num1, op, num2))
    resp = await self.bot.wait_for_message(author=user)
    resp = resp.clean_content
    if resp == str(ans):
        await self.bot.send_message(user, "Correct. \N{WHITE HEAVY CHECK MARK} Take a swing.")
        self.jailed[server.id][user.id]['current'] -= 1
        dataIO.save_json('data/jail/jailed.json', self.jailed)
    elif resp == ">swing":
        return
    else:
        await self.bot.send_message(user, "Wrong. \N{CROSS MARK} Let me put that brick back in place.")
        self.jailed[server.id][user.id]['current'] += 1
        dataIO.save_json('data/jail/jailed.json', self.jailed)

In the third function, you'll see this statement:

elif resp == ">swing":
    return

How can I get that to effectively "double return" so that it instead returns to start_swings instead of get_questions? I am fairly new to Python and don't know exactly what to call this. I'm effectively trying to get execution of this script to stop completely at this area of the code:

elif resp == ">swing":
   return

… rather than just having it return to the last function called.

Upvotes: 3

Views: 611

Answers (1)

Jean-François Fabre
Jean-François Fabre

Reputation: 140168

You cannot "pop" the stack manually like you could do in old times basic, but you can use an exception (even if it's considered bad practice if the exception isn't thrown "exceptionnally"):

Inherit from Exception:

class DoubleReturnException(Exception):
     pass

Install the handler:

    while self.jailed[server.id][user.id]['current'] != 0:
        try:
            await self.get_question(server, user)
        except DoubleReturnException:
            pass
    else:

Then instead of returning, just:

raise DoubleReturnException

to return to the except part of the handler (whatever the number of calls are).

Defining your own exception is necessary because it ensures that the program returns for this reason, and not for a ValueError or any other exception that the program could throw.

That must remain an exceptional case. Programming by throwing exceptions everywhere lead to "spaghetti code" and somehow reinstates the evil goto statement. You may find that interesting in the last stages of a stable product, when you cannot break all the (flawed) design for a particular case (but personally I never had to use tricks like that).

Upvotes: 4

Related Questions