CITGuru / PyInquirer

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

Can't use "message" as a function(answers: dict) -> str #49

Open AndrewOwenMartin opened 5 years ago

AndrewOwenMartin commented 5 years ago

The documentation says that the "message" attribute of a question can be a string or a function that takes the current answers and returns a string. That doesn't appear to be accurate documentation as any function, or lambda, I use as a questions "message" gets evaluated as a string in the prompt.

e.g. This question definition;

def my_message(answers):
    return "Hello {x}".format(x=len(answers))

questions = [
    dict(
        type="list",
        name="answer",
        message=my_message,
        choices=[dict(name=x) for x in "foo bar baz".split()],
]

... renders like so;

? <function get_questions.<locals>.my_message at 0x7ff19399d158>  (Use arrow keys)
 ❯ foo
   bar
   baz

Originally posted by @AndrewOwenMartin in https://github.com/CITGuru/PyInquirer/issues/39#issuecomment-487012662

AndrewOwenMartin commented 5 years ago

I have a workaround for this, you need to do a few things.

Firstly, you need to make an answers variable, to which you keep a reference and pass to prompt.

Secondly, you need to make a class that has a __str__ function, you will pass this as your message so when PyInquirer casts it to a string, that function gets called.

Thirdly you must be very careful with the answers variable, if you initialise it as an empty dictionary then PyInquirer will assume you passed nothing and make its own variable, so you need to also do one of two things

  1. Initialise answers as anything not empty e.g. `answers = {"ignore this":"ignore this"}
  2. Define a new class, derived from dict which evaluates to True when empty (and hence always evaluates as True)

Here's a minimum working example...

from PyInquirer import prompt

class Message:
    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return "{msg} Your previous answers were {a}".format(msg=self.msg, a=answers)

questions = [
    dict(
        type="input",
        message="Type anything",
        default="foo",
        name="first answer",
    ),
    dict(
        type="input",
        message=Message("Type anything again."),
        name="second answer",
    ),
]

answers = {'ignore me':'ignore me'}
prompt(questions, answers)
print(answers)

class TruthyDict(dict):
    def __bool__(self):
        return True

answers = TruthyDict()
prompt(questions, answers)
print(answers)

Produces this output...

? Type anything  first example
? Type anything again. Your previous answers were {'first answer': 'first example', 'ignore me': 'ignore me'}  notice the extra "ignore me"
{'first answer': 'first example', 'second answer': 'notice the extra "ignore me"', 'ignore me': 'ignore me'}
? Type anything  second example
? Type anything again. Your previous answers were {'first answer': 'second example'}  much nicer :)
{'first answer': 'second example', 'second answer': 'much nicer :)'}
CITGuru commented 5 years ago

@AndrewOwenMartin Sorry for my late response. I am currently working on this. I will get back to you

tillchen commented 3 years ago

Any updates?