CITGuru / PyInquirer

A Python module for common interactive command line user interfaces
MIT License
1.94k stars 236 forks source link

Feature Request: Callback for choices and input questions #103

Open craigh92 opened 4 years ago

craigh92 commented 4 years ago

I often want to perform an action as a result of a prompt choice/input:

For example:

q = {
    'type' : 'list,
    'name' : 'action',
    'message' : 'choose an option',
    'choices' : [
        {
            'name' : 'foo',
            'disabled' : False
        },
        {
            'name' : 'bar',
            'disabled' : False
        }
   ]
}

def foo() : print("you chose foo!")
def bar() : print("you chose bar!")

a = prompt(q)
if a['action'] == 'foo':
    foo()
elif a['action'] == 'bar':
   bar()

As the number of options grows, and the prompts become more complex this can become messy, as you have to update the logic that handles the choice every time you change/add a choice.

It would be good if you could do something like:

q = {
    'type' : 'list,
    'name' : 'action',
    'message' : 'choose an option',
    'choices' : [
        {
            'name' : 'foo',
            'disabled' : False,
            'callback' : lambda : print("you chose foo!")
        },
        {
            'name' : 'bar',
            'disabled' : False
            'callback' : lambda : print("you chose bar!")
        }
   ]
}

a = prompt(q)

#The callback could be called automatically as a result of the choice, or an alternative design could be to call it with:
handle(a, q)  # This would call the appropriate callback depending on the answer 

This is better than the previous example because the callbacks are defined with the choices, so adding new behaviour to handle a new choice is easy.

The handle function can already be implemented now, without having to change any of the rest of the application. See below:

def handle(a, q):
    choicesList = q['choices']
        for opt in choicesList:
            if opt['name'] == a:
                opt['callback']()
# a similar function can be written for the case that a is a future, and not a string (when `return_asyncio_coroutine = True` is used)

But this relies on me being able to add the callback Dict item to the choices List. This currently happens to work, but as it is not an official feature I cannot guarantee that this will work with future versions of PyInquire. It would be good if the callback property could be officially supported.

It would also be good if I could do the same with different types of prompts, such as input. For example:

q = {
    'type' : 'input',
    'name' : 'value',
    'message' : "choose a value",
    'callback' : lambda val : print("you typed " + val)
}

a = prompt(q)

But unlike the choices example, this currently doesn't work and gives the error:

TypeError: create_prompt_application() got an unexpected keyword argument 'callback'

Again, it would be good if the callback function could be called (with the input value) after the user has entered the input. Again, this could happen automatically, or by using some kind of handle function.

What do you think of this idea?

craigh92 commented 4 years ago

I could create a fork / branch and assign myself to this if others think it is a useful feature?

craigh92 commented 4 years ago

I've gone ahead and made a fork, as this feature was easy to implement. A working example can be found here: https://github.com/craigh92/PyInquirer/blob/master/examples/callbacks.py

CITGuru commented 4 years ago

This looks good, I would go through it but I think we have something like this. @craigh92

craigh92 commented 4 years ago

@CITGuru Thanks for your reply.

If that is the case, could you please let me know how this is currently done?

Also, here's a better example of how it could be used:

https://github.com/craigh92/PyInquirer/blob/master/examples/calculator.py

CITGuru commented 4 years ago

I will be looking into this, this is fairly easy. I hope I could get enough time