0

I'm trying to make a command prompt style program in Python. I want to have a list of possible functions to run, and then check the list; if any of them match, take the input that matched and call a function with the same name. I currently get a "str" object is not callable error.

import os
import time

able_commands = ["clear", "test"]


def test():
    print("worked")


def run_command(command):
    command()
    input_command()


def clear():
    print("clearing...")
    time.sleep(2)
    os.system('cls')


def input_command():
    command = input(os.path.abspath(os.sep) + " ")
    check_if = command.lower()
    if check_if in able_commands:
        run_command(check_if)
    elif check_if == "":
        input_command()
    else:
        print("ERROR \nPlease specify a valid command")
        input_command()


input_command()

I'm a beginner with Python.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • 2
    Having `input_command` recursively call itself is probably not a good idea. You are basically looking for a `while True:` loop with a `break` when you get valid input. See also https://stackoverflow.com/questions/23294658/asking-the-user-for-input-until-they-give-a-valid-response – tripleee Dec 31 '20 at 06:23
  • Pro tip: stop using `os.path`. It's api is clunky. Use the much more expressive `pathlib` which has a very nice OOP approach – juanpa.arrivillaga Dec 31 '20 at 06:45
  • Why would you print the path as the root directory as the prompt anyway? If you want to make a CLI, print the current working directory; but none of this is important to your actual question, so it should be removed from the question. See also the guidance for providing a [mre]. – tripleee Dec 31 '20 at 07:10

3 Answers3

1

In Python, functions are first-class objects. So, you could do something like this:

def foo():
    print('foo')

def bar():
    print('bar')

# Create a dict of function name to the function object. 
# This dict must be declared after your functions otherwise
# you will get a NameError exception.
funcs = {
   'run_foo': foo
   'run_bar': bar
}

# Select the function from the dict
f = funcs['run_foo']

# Run the selected function
f()
Safwan
  • 29
  • 6
0

A simple implementation of what you require is to change your run_command function to the following:

def run_command(command):
    functionToCall = globals()[command]
    functionToCall()
    input_command()

There are similar questions already that answer this, however for a "newbie", it may not be clear that they in fact are the same, or how they'd fit into your implementation.

00willo
  • 68
  • 4
  • 2
    Don't encourage use of globals, even (or especially) for beginners. – tripleee Dec 31 '20 at 06:23
  • @tripleee Don't confuse system global scope and custom globals, there is no crime here. – iqmaker Dec 31 '20 at 06:30
  • I don't think the main crime is the use of globals, but even nudging them in that direction is dubious here. At the very minimum this should include a `try:` or other guard so that `command` can robustly be any random string (but then you run into security issues). – tripleee Dec 31 '20 at 06:32
  • @tripleee Agree on something like a `try/except`, and other security considerations here. There's a lot that could be done as it's accepting user input. – 00willo Dec 31 '20 at 07:06
0

In python, you can call functions like this using globals() and locals(). In your case, you can replace line command() inside def run_command(command) with this:

globals()[command]()

This will find your function by name and call it. Be careful when using globals though, and please also read this SO post. You will find more ways of calling your function and explanations there. I hope, this helps.

Svetlana Levinsohn
  • 1,550
  • 3
  • 10
  • 19