pmb.helpers.cli: add tab completion option for ask() helper (!1875)

Add a helper class that provides readline-based tab completion and an
extra optional argument "completion_choices" with possible completion
targets.

While at this, also improve paramters documentation for ask() func.
This commit is contained in:
Alexey Min 2020-02-18 18:00:37 +03:00
parent 804743e65a
commit 7e61e62044
No known key found for this signature in database
GPG Key ID: 0B19D2A65870B448
2 changed files with 46 additions and 5 deletions

View File

@ -3,14 +3,46 @@
import datetime
import logging
import re
import readline
class ReadlineTabCompleter:
""" Stores intermediate state for completer function """
def __init__(self, options):
"""
:param options: list of possible completions
"""
self.options = sorted(options)
self.matches = []
def completer_func(self, input_text, iteration):
"""
:param input_text: text that shall be autocompleted
:param iteration: how many times "tab" was hit
"""
# First time: build match list
if iteration == 0:
if input_text:
self.matches = [s for s in self.options if s and s.startswith(input_text)]
else:
self.matches = self.options[:]
# Return the N'th item from the match list, if we have that many.
if iteration < len(self.matches):
return self.matches[iteration]
return None
def ask(args, question="Continue?", choices=["y", "n"], default="n",
lowercase_answer=True, validation_regex=None):
lowercase_answer=True, validation_regex=None, complete=None):
"""
Ask a question on the terminal. When validation_regex is set, the user gets
asked until the answer matches the regex.
:returns: the user's answer
Ask a question on the terminal.
:param question: display prompt
:param choices: short list of possible answers, displayed after prompt if set
:param default: default value to return if user doesn't input anything
:param lowercase_answer: if True, convert return value to lower case
:param validation_regex: if set, keep asking until regex matches
:param complete: set to a list to enable tab completion
"""
while True:
date = datetime.datetime.now().strftime("%H:%M:%S")
@ -20,7 +52,16 @@ def ask(args, question="Continue?", choices=["y", "n"], default="n",
if default:
question_full += " [" + str(default) + "]"
if complete:
readline.parse_and_bind('tab: complete')
readline.set_completer(ReadlineTabCompleter(complete).completer_func)
ret = input(question_full + ": ")
# Stop completing (question is answered)
if complete:
readline.set_completer(None)
if lowercase_answer:
ret = ret.lower()
if ret == "":

View File

@ -34,7 +34,7 @@ def fake_answers(monkeypatch, answers):
the second question with "n" and so on.
"""
def fake_ask(args, question="Continue?", choices=["y", "n"], default="n",
lowercase_answer=True, validation_regex=None):
lowercase_answer=True, validation_regex=None, complete=None):
answer = answers.pop(0)
logging.info("pmb.helpers.cli.ask() fake answer: " + answer)
return answer