1

I know that I shouldn't use exec because it's insecure but the code it run comes from the same file and is a string. How can I use exec to run an async function? It always returns me SyntaxError: 'await' outside function if I try it.

Edit: This is the Code where I am using exec:

async def command_processor(guild):
    while True:
        commands = {"[1] - Bot Permissions abrufen" : "print_permissions(guild)",
                    "Derzeit nicht verwendbar - [2] - Invite zu Gilde erstellen (max Nutzungen pro Invite: 1)" : "await print_invite(guild)",
                    "[3] - Gebannte Benutzer auflisten (benötigt Berechtigung {Mitglieder bannen}" : "print_banned(guild)",
                    "[4] - Ausgewählten Server wechseln" : "change_guild()",
                    "[5] - Ausgewählten Bot wechseln" : "change_bot()",
                    "[6] - Beenden" : "sys.exit()"
            }
        print("\n\n--------------- Befehle ---------------\n")
        for i in range(len(commands.keys())):
            print(list(commands.keys())[i])
        sel = input()
        print()
        try:
            int(sel)
        except:
            print("Bitte hier nur die Zahl eingeben")
        if int(sel) in range(len(commands.keys())):
            exec(list(commands.values())[int(sel)-1])
8
  • Out of curiosity, if the code comes from the same file, why does it need to be a string? Why can't it just be code? (An example in the question might help.) Commented Jan 6, 2021 at 13:45
  • It's just so that I can extend it more easily, because I have a dictionary with the Description what will be printed as the key and the command that will run as the value. Commented Jan 6, 2021 at 13:49
  • Why not have the commands as values instead? Commented Jan 6, 2021 at 13:55
  • What? I have the Commands as the value... Commented Jan 6, 2021 at 13:57
  • No, you have strings as values. You could just have the methods as values: {'1. Exit': sys.exit}. Then you can do list(commands.values())[int(sel)-1])() later on. Commented Jan 6, 2021 at 14:00

1 Answer 1

1

You could do this, I think. The problem with exec is that it doesn't return anything, so you would need to use eval instead. I think this could work:

async def command_processor(guild):
    while True:
        commands = {"[1] - Bot Permissions abrufen" : "print_permissions(guild)",
                    "Derzeit nicht verwendbar - [2] - Invite zu Gilde erstellen (max Nutzungen pro Invite: 1)" : "await print_invite(guild)",
                    "[3] - Gebannte Benutzer auflisten (benötigt Berechtigung {Mitglieder bannen}" : "print_banned(guild)",
                    "[4] - Ausgewählten Server wechseln" : "change_guild()",
                    "[5] - Ausgewählten Bot wechseln" : "change_bot()",
                    "[6] - Beenden" : "sys.exit()"
            }

# ...

        if int(sel) in range(len(commands.keys())):
            await eval(list(commands.values())[int(sel)-1])

But there's no need to use eval here, and you have a lot to lose. A simple way could be to use a dict, something along the lines:

commands = {1: {"description": "[1] - Bot Permission abrufen", "command" : print_permissions, "args": [guild]},
# ...
}

for cmd in commands:
    print(cmd['description'])

# ...
selected_command = commands[sel]
await selected_command['command'](*selected_command['args'])

But it gets tricky, since sometimes you have a coroutine and sometimes a regular method. To get around that, I guess you could do this:

commands = {
    1: {"description": "[1] - Bot Permission abrufen", "exec" : print_permissions, "args": [guild]},
    2: {"description": "Derzeit nicht verwendbar - [2] - Invite zu Gilde erstellen (max Nutzungen pro Invite: 1)", "exec": print_invite, "args": [guild]},
# ...
}

for cmd in commands:
    print(cmd['description'])

# ...
selected_command = commands[sel]
if asyncio.iscoroutinefunction(selected_command['exec']):
    await selected_command['exec'](*selected_command['args'])
else:
    selected_command['exec'](*selected_command['args'])

On a final note: It make's me nervous that we now have the key that needs to pressed in two different places. This will lead to tears in the end, mark my words. I would do something like this:

commands = [
    {
        "description": "[{keynum}] - Bot Permission abrufen",
        "exec" : print_permissions, "args": [guild]
    },
    {
        "description": "Derzeit nicht verwendbar - [{keynum}] - Invite zu Gilde erstellen (max Nutzungen pro Invite: 1)",
        "exec": print_invite, "args": [guild]
    },
]

for keynum, command in enumerate(commands):
    print(command['description'].format(keynum=keynum))

I haven't tested that code, so there's probably a bug, but you get the idea.

Sign up to request clarification or add additional context in comments.

2 Comments

I just got this error TypeError: print_invite() argument after * must be an iterable, not Guild of the line selected_command['command'](*selected_command['args'])
Of course! Sorry. Args need to be a list. Fixed now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.