209

Is there any short way to achieve what the APT (Advanced Package Tool) command line interface does in Python?

I mean, when the package manager prompts a yes/no question followed by [Yes/no], the script accepts YES/Y/yes/y or Enter (defaults to Yes as hinted by the capital letter).

The only thing I find in the official docs is input and raw_input...

I know it's not that hard to emulate, but it's annoying to rewrite :|

martineau
  • 119,623
  • 25
  • 170
  • 301
h3.
  • 10,688
  • 15
  • 51
  • 54
  • 16
    In Python 3, `raw_input()` is called [`input()`](http://docs.python.org/dev/library/functions.html#input). – Tobu Dec 02 '12 at 17:24

22 Answers22

279

As you mentioned, the easiest way is to use raw_input() (or simply input() for Python 3). There is no built-in way to do this. From Recipe 577058:

import sys


def query_yes_no(question, default="yes"):
    """Ask a yes/no question via raw_input() and return their answer.

    "question" is a string that is presented to the user.
    "default" is the presumed answer if the user just hits <Enter>.
            It must be "yes" (the default), "no" or None (meaning
            an answer is required of the user).

    The "answer" return value is True for "yes" or False for "no".
    """
    valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False}
    if default is None:
        prompt = " [y/n] "
    elif default == "yes":
        prompt = " [Y/n] "
    elif default == "no":
        prompt = " [y/N] "
    else:
        raise ValueError("invalid default answer: '%s'" % default)

    while True:
        sys.stdout.write(question + prompt)
        choice = input().lower()
        if default is not None and choice == "":
            return valid[default]
        elif choice in valid:
            return valid[choice]
        else:
            sys.stdout.write("Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n")

(For Python 2, use raw_input instead of input.) Usage example:

>>> query_yes_no("Is cabbage yummier than cauliflower?")
Is cabbage yummier than cauliflower? [Y/n] oops
Please respond with 'yes' or 'no' (or 'y' or 'n').
Is cabbage yummier than cauliflower? [Y/n] [ENTER]
>>> True

>>> query_yes_no("Is cabbage yummier than cauliflower?", None)
Is cabbage yummier than cauliflower? [y/n] [ENTER]
Please respond with 'yes' or 'no' (or 'y' or 'n').
Is cabbage yummier than cauliflower? [y/n] y
>>> True
Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
fmark
  • 57,259
  • 27
  • 100
  • 107
99

I'd do it this way:

# raw_input returns the empty string for "enter"
yes = {'yes','y', 'ye', ''}
no = {'no','n'}

choice = raw_input().lower()
if choice in yes:
   return True
elif choice in no:
   return False
else:
   sys.stdout.write("Please respond with 'yes' or 'no'")
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Vicki Laidler
  • 3,415
  • 1
  • 20
  • 17
88

You can use click's confirm method.

import click

if click.confirm('Do you want to continue?', default=True):
    print('Do something')

This will print:

$ Do you want to continue? [Y/n]:

Should work for Python 2/3 on Linux, Mac or Windows.

Docs: http://click.pocoo.org/5/prompts/#confirmation-prompts

Eyal Levin
  • 16,271
  • 6
  • 66
  • 56
54

There is a function strtobool in Python's standard library: http://docs.python.org/2/distutils/apiref.html?highlight=distutils.util#distutils.util.strtobool

You can use it to check user's input and transform it to True or False value.

Danilo Bargen
  • 18,626
  • 15
  • 91
  • 127
Alexander Artemenko
  • 21,378
  • 8
  • 39
  • 36
  • `f` probably stands for False, and `False == 0`, so I get the logic. Why the function would return an `int` instead of a `bool` is a mystery to me though. – François Leblanc Jan 19 '18 at 13:38
  • @FrançoisLeblanc as to Why it is most common in Databases. If it is not explicitly `False` or `0` (Zero). Anything, else that is evaluated using the bool function becomes true and will return: `1`. – JayRizzo May 15 '18 at 07:40
  • @JayRizzo I get that, and they're [both functionally similar](https://stackoverflow.com/questions/40640674/differentiate-false-and-0) in most respects. But it means you can't use singleton comparison, i.e. `if strtobool(string) is False: do_stuff()`. – François Leblanc Jun 15 '18 at 18:00
50

A very simple (but not very sophisticated) way of doing this for a single choice would be:

msg = 'Shall I?'
shall = input("%s (y/N) " % msg).lower() == 'y'

You could also write a simple (slightly improved) function around this:

def yn_choice(message, default='y'):
    choices = 'Y/n' if default.lower() in ('y', 'yes') else 'y/N'
    choice = input("%s (%s) " % (message, choices))
    values = ('y', 'yes', '') if choices == 'Y/n' else ('y', 'yes')
    return choice.strip().lower() in values

Note: On Python 2, use raw_input instead of input.

Danilo Bargen
  • 18,626
  • 15
  • 91
  • 127
  • 9
    Love the first approach. Short and easy. I used something like `result = raw_input("message").lower() in ('y','yes')` – Adrian Shum Mar 03 '16 at 01:27
28

as mentioned by @Alexander Artemenko, here's a simple solution using strtobool

from distutils.util import strtobool

def user_yes_no_query(question):
    sys.stdout.write('%s [y/n]\n' % question)
    while True:
        try:
            return strtobool(raw_input().lower())
        except ValueError:
            sys.stdout.write('Please respond with \'y\' or \'n\'.\n')

#usage

>>> user_yes_no_query('Do you like cheese?')
Do you like cheese? [y/n]
Only on tuesdays
Please respond with 'y' or 'n'.
ok
Please respond with 'y' or 'n'.
y
>>> True
james
  • 4,150
  • 2
  • 30
  • 36
16

I know this has been answered a bunch of ways and this may not answer OP's specific question (with the list of criteria) but this is what I did for the most common use case and it's far simpler than the other responses:

answer = input('Please indicate approval: [y/n]')
if not answer or answer[0].lower() != 'y':
    print('You did not indicate approval')
    exit(1)
Douglas Adams
  • 1,550
  • 1
  • 11
  • 7
  • this does not work with python 2 -- `raw_input` was renamed `input` in python 3 https://stackoverflow.com/questions/21122540/input-error-nameerror-name-is-not-defined – Brian Tingle Oct 13 '17 at 01:30
11

You can also use prompter.

Shamelessly taken from the README:

#pip install prompter

from prompter import yesno

>>> yesno('Really?')
Really? [Y/n]
True

>>> yesno('Really?')
Really? [Y/n] no
False

>>> yesno('Really?', default='no')
Really? [y/N]
True
ibizaman
  • 3,053
  • 1
  • 23
  • 34
  • 6
    Beware that the behaviour of prompter is pretty backwards when you're using it with "default='no'"; it will return True when you choose 'no' and False when you choose 'yes'. – rem Apr 05 '17 at 10:34
7

I modified fmark's answer to by python 2/3 compatible more pythonic.

See ipython's utility module if you are interested in something with more error handling

# PY2/3 compatibility
from __future__ import print_function
# You could use the six package for this
try:
    input_ = raw_input
except NameError:
    input_ = input

def query_yes_no(question, default=True):
    """Ask a yes/no question via standard input and return the answer.

    If invalid input is given, the user will be asked until
    they acutally give valid input.

    Args:
        question(str):
            A question that is presented to the user.
        default(bool|None):
            The default value when enter is pressed with no value.
            When None, there is no default value and the query
            will loop.
    Returns:
        A bool indicating whether user has entered yes or no.

    Side Effects:
        Blocks program execution until valid input(y/n) is given.
    """
    yes_list = ["yes", "y"]
    no_list = ["no", "n"]

    default_dict = {  # default => prompt default string
        None: "[y/n]",
        True: "[Y/n]",
        False: "[y/N]",
    }

    default_str = default_dict[default]
    prompt_str = "%s %s " % (question, default_str)

    while True:
        choice = input_(prompt_str).lower()

        if not choice and default is not None:
            return default
        if choice in yes_list:
            return True
        if choice in no_list:
            return False

        notification_str = "Please respond with 'y' or 'n'"
        print(notification_str)
Bryce Guinta
  • 3,456
  • 1
  • 35
  • 36
7

For Python 3, I'm using this function:

def user_prompt(question: str) -> bool:
    """ Prompt the yes/no-*question* to the user. """
    from distutils.util import strtobool

    while True:
        user_input = input(question + " [y/n]: ")
        try:
            return bool(strtobool(user_input))
        except ValueError:
            print("Please use y/n or yes/no.\n")

The strtobool() function converts a string into a bool. If the string cant be parsed it will raise a ValueError.

In Python 3 raw_input() has been renamed to input().

As Geoff said, strtobool actually returns 0 or 1, therefore the result has to be cast to bool.


This is the implementation of strtobool, if you want special words to be recognized as true, you can copy the code and add your own cases.

def strtobool (val):
    """Convert a string representation of truth to true (1) or false (0).
    True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
    are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if
    'val' is anything else.
    """
    val = val.lower()
    if val in ('y', 'yes', 't', 'true', 'on', '1'):
        return 1
    elif val in ('n', 'no', 'f', 'false', 'off', '0'):
        return 0
    else:
        raise ValueError("invalid truth value %r" % (val,))
Josef
  • 2,869
  • 2
  • 22
  • 23
  • 1
    And one minor thing that probably doesn't matter much in practice: `strtobool`, for some reason, returns 0 and 1, rather than `False` and `True`. So if you need `False` and `True`, be sure to call `bool(strtobool(user_input))`. – Geoff Aug 31 '20 at 17:18
6

on 2.7, is this too non-pythonic?

if raw_input('your prompt').lower()[0]=='y':
   your code here
else:
   alternate code here

it captures any variation of Yes at least.

Speqtre
  • 61
  • 1
  • 1
4

Doing the same with python 3.x, where raw_input() doesn't exist:

def ask(question, default = None):
    hasDefault = default is not None
    prompt = (question 
               + " [" + ["y", "Y"][hasDefault and default] + "/" 
               + ["n", "N"][hasDefault and not default] + "] ")

    while True:
        sys.stdout.write(prompt)
        choice = input().strip().lower()
        if choice == '':
            if default is not None:
                return default
        else:
            if "yes".startswith(choice):
                return True
            if "no".startswith(choice):
                return False

        sys.stdout.write("Please respond with 'yes' or 'no' "
                             "(or 'y' or 'n').\n")
pjm
  • 41
  • 4
  • Nope, this doesn't work. In more than one way actually. Currently trying to fix it, but I think this will look a lot like the accepted answer after I'm done. – Gormador Nov 04 '16 at 09:58
  • I edited you anwser @pjm . Please consider reviewing it :-) – Gormador Nov 04 '16 at 10:29
2

You could try something like the code below to be able to work with choices from the variable 'accepted' show here:

print( 'accepted: {}'.format(accepted) )
# accepted: {'yes': ['', 'Yes', 'yes', 'YES', 'y', 'Y'], 'no': ['No', 'no', 'NO', 'n', 'N']}

Here is the code ..

#!/usr/bin/python3

def makeChoi(yeh, neh):
    accept = {}
    # for w in words:
    accept['yes'] = [ '', yeh, yeh.lower(), yeh.upper(), yeh.lower()[0], yeh.upper()[0] ]
    accept['no'] = [ neh, neh.lower(), neh.upper(), neh.lower()[0], neh.upper()[0] ]
    return accept

accepted = makeChoi('Yes', 'No')

def doYeh():
    print('Yeh! Let\'s do it.')

def doNeh():
    print('Neh! Let\'s not do it.')

choi = None
while not choi:
    choi = input( 'Please choose: Y/n? ' )
    if choi in accepted['yes']:
        choi = True
        doYeh()
    elif choi in accepted['no']:
        choi = True
        doNeh()
    else:
        print('Your choice was "{}". Please use an accepted input value ..'.format(choi))
        print( accepted )
        choi = None
Randy Skretka
  • 3,488
  • 3
  • 22
  • 14
2

As a programming noob, I found a bunch of the above answers overly complex, especially if the goal is to have a simple function that you can pass various yes/no questions to, forcing the user to select yes or no. After scouring this page and several others, and borrowing all of the various good ideas, I ended up with the following:

def yes_no(question_to_be_answered):
    while True:
        choice = input(question_to_be_answered).lower()
        if choice[:1] == 'y': 
            return True
        elif choice[:1] == 'n':
            return False
        else:
            print("Please respond with 'Yes' or 'No'\n")

#See it in Practice below 

musical_taste = yes_no('Do you like Pine Coladas?')
if musical_taste == True:
    print('and getting caught in the rain')
elif musical_taste == False:
    print('You clearly have no taste in music')
Mike
  • 201
  • 2
  • 14
1

This is what I use:

import sys

# cs = case sensitive
# ys = whatever you want to be "yes" - string or tuple of strings

#  prompt('promptString') == 1:               # only y
#  prompt('promptString',cs = 0) == 1:        # y or Y
#  prompt('promptString','Yes') == 1:         # only Yes
#  prompt('promptString',('y','yes')) == 1:   # only y or yes
#  prompt('promptString',('Y','Yes')) == 1:   # only Y or Yes
#  prompt('promptString',('y','yes'),0) == 1: # Yes, YES, yes, y, Y etc.

def prompt(ps,ys='y',cs=1):
    sys.stdout.write(ps)
    ii = raw_input()
    if cs == 0:
        ii = ii.lower()
    if type(ys) == tuple:
        for accept in ys:
            if cs == 0:
                accept = accept.lower()
            if ii == accept:
                return True
    else:
        if ii == ys:
            return True
    return False
fyngyrz
  • 2,458
  • 2
  • 36
  • 43
1
def question(question, answers):
    acceptable = False
    while not acceptable:
        print(question + "specify '%s' or '%s'") % answers
        answer = raw_input()
        if answer.lower() == answers[0].lower() or answers[0].lower():
            print('Answer == %s') % answer
            acceptable = True
    return answer

raining = question("Is it raining today?", ("Y", "N"))

This is how I'd do it.

Output

Is it raining today? Specify 'Y' or 'N'
> Y
answer = 'Y'
Callam Delaney
  • 641
  • 3
  • 15
1

Here's my take on it, I simply wanted to abort if the user did not affirm the action.

import distutils

if unsafe_case:
    print('Proceed with potentially unsafe thing? [y/n]')
    while True:
        try:
            verify = distutils.util.strtobool(raw_input())
            if not verify:
                raise SystemExit  # Abort on user reject
            break
        except ValueError as err:
            print('Please enter \'yes\' or \'no\'')
            # Try again
    print('Continuing ...')
do_unsafe_thing()
ThorSummoner
  • 16,657
  • 15
  • 135
  • 147
0

How about this:

def yes(prompt = 'Please enter Yes/No: '):
while True:
    try:
        i = raw_input(prompt)
    except KeyboardInterrupt:
        return False
    if i.lower() in ('yes','y'): return True
    elif i.lower() in ('no','n'): return False
Visgean Skeloru
  • 2,237
  • 1
  • 24
  • 33
0

Since the answer is expected yes or no, in the examples below, the first solution is to repeat the question using the function while, and the second solution is to use recursion - is the process of defining something in terms of itself.

def yes_or_no(question):
    while "the answer is invalid":
        reply = str(input(question+' (y/n): ')).lower().strip()
        if reply[:1] == 'y':
            return True
        if reply[:1] == 'n':
            return False

yes_or_no("Do you know who Novak Djokovic is?")

second solution:

def yes_or_no(question):
    """Simple Yes/No Function."""
    prompt = f'{question} ? (y/n): '
    answer = input(prompt).strip().lower()
    if answer not in ['y', 'n']:
        print(f'{answer} is invalid, please try again...')
        return yes_or_no(question)
    if answer == 'y':
        return True
    return False

def main():
    """Run main function."""
    answer = yes_or_no("Do you know who Novak Djokovic is?")
    print(f'you answer was: {answer}')


if __name__ == '__main__':
    main()
Milovan Tomašević
  • 6,823
  • 1
  • 50
  • 42
-1

One-liner with Python 3.8 and above:

while res:= input("When correct, press enter to continue...").lower() not in {'y','yes','Y','YES',''}: pass
scenox
  • 698
  • 7
  • 17
-1

Python x.x

res = True
while res:
    res = input("Please confirm with y/yes...").lower(); res = res not in {'y','yes','Y','YES',''}
AK_
  • 1,879
  • 4
  • 21
  • 30
-1

What I used to do is...

question = 'Will the apple fall?'
print(question)
answer = int(input("Pls enter the answer: "
if answer == "y",
print('Well done')
print(answer)