How to answer to prompts automatically with python fabric?

Solution 1:

Starting from version 1.9, Fabric includes a way of managing this properly.

The section about Prompts in the Fabric documentation says:

The prompts dictionary allows users to control interactive prompts. If a key in the dictionary is found in a command’s standard output stream, Fabric will automatically answer with the corresponding dictionary value.

You should be able to make Fabric automatically answer prompts like this:

with settings(prompts={'Do you want to continue [Y/n]? ': 'Y'}):
    run('apt-get update')
    run('apt-get upgrade')

Solution 2:

I have used simple echo pipes to answer prompts with Fabric.

run('echo "yes\n"| my_command')

Solution 3:

Note: this answer is several years old, and in the mean time fabric has (interestingly similar looking) implementation of this. See the answer by @timothée-jeannin below.

See https://stackoverflow.com/a/10007635/708221

pip install fexpect

from ilogue.fexpect import expect, expecting, run 

prompts = []
prompts += expect('What is your name?','John')
prompts += expect('Are you at stackoverflow?','Yes')

with expecting(prompts):
    run('my_command')

Fexpect adds answering to prompts to fabric with use of pexpect

Solution 4:

To expand a bit on Timothée's excellent answer, here's the code that Fabric uses when checking the prompts dictionary.

def _get_prompt_response(self):
    """
    Iterate through the request prompts dict and return the response and
    original request if we find a match
    """
    for tup in env.prompts.iteritems():
        if _endswith(self.capture, tup[0]):
            return tup
    return None, None

Fabric uses .endswith for its check, so make sure you include trailing spaces in the string you use as a key in the prompts dictionary.

For example - let's say you are trying to automate the Django test database prompt

Type 'yes' if you would like to try deleting the test database 'test_my_app', or 'no' to cancel:

All we need is enough of the end of the prompt so that it is unique. Include trailing spaces.

django_test_database_prompt = "or 'no' to cancel: "
#         won't work without this trailing space ^

with settings(
    prompts={django_test_database_prompt : 'yes'}
):
    run('%s %s' % (virtualenv_python_path,
                   test_runner_file_path,
                  )
       )