Prompts

Quo supports prompts in two different places. The first is automated prompts when the parameter handling happens, and the second is to ask for prompts at a later point independently.

This can be accomplished with the prompt() function, which asks for valid input according to a type, or the quo.prompt.Prompt object, this makes it possible to create a Prompt instance followed by calling prompt() method for every input. This creates a kind of an input session and its packed with lots of features. You can also use the quo.confirm() function, which asks for confirmation (yes/no).

The prompt function is a quo function that displays a prompt to the user and waits for input. The purpose of the prompt function is to obtain user input from the console. It can be used to ask the user for a variety of input, including text, numbers, and boolean values. It has several optional arguments which can be used to customize the prompt and how the input is handled.

Parameters

The prompt function takes several parameters, which are explained below:

text: The text to show for the prompt. This parameter is required and must be a string. default: The default value to use if no input happens. If this is not given, it will prompt until it’s aborted. This parameter is optional and can be any data type. hide: If this is set to true, then the input value will be hidden, and asterisks printed instead. This parameter is optional and defaults to False. affirm: Asks for confirmation for the value. This parameter is optional and defaults to False. type: The type to use to check the value against. This parameter is optional and defaults to str. suffix: A suffix that should be added to the prompt. This parameter is optional and defaults to an empty string. show_default: Shows or hides the default value in the prompt. This parameter is optional and defaults to True. fg: The color for the prompt text. This parameter is optional and defaults to None. bg: The color for the prompt background. This parameter is optional and defaults to None.

Examples

Here’s a simple example

import quo

quo.prompt('Give me some input: ')
_images/prompt.png

The prompt function returns the input value provided by the user, or the default value if no input was provided. The data type of the return value will depend on the type parameter.

import quo

name = quo.prompt('What is your name?', type=str)
age =  quo.prompt('What is your age?', default=18, type=int)

Additionally, the type will be determined automatically if a default value is provided. For instance, the following will only accept floats:

import quo

quo.prompt('Please enter a number', default=42.0)

App Prompts

App prompts are integrated into the app interface. See app-prompting for more information. Internally, it automatically calls either quo.prompt() or quo.confirm() as necessary.

Input Validation

A prompt can have a validator attached. To manually ask for user input, you can use the quo.prompt() function or the quo.prompt.Prompt object. For instance, you can ask for a valid integer:

from quo import prompt

prompt('Please enter a valid integer', type=int)

You can also pass the affirm flag to quo.prompt()

from quo import prompt
prompt("What is your name?: ", affirm=True)

Alternatively, you can use class:quo.types.Validator This should implement the Validator abstract base class. This requires only one method, named type that takes a Document as input and raises ValidationError when the validation fails.

Added on v2022.4.4 :meth:int [bool] can be used when validating numerical characters.

Integer Validator

from quo.prompt import Prompt

session = Prompt(int=True)

number = int(session.prompt('Give a number: '))
print(f"You said: {number}")
_images/number-validator.png

By default, the input is validated in real-time while the user is typing, but Quo can also validate after the user presses the enter key:

session = Prompt(
             int=True,
             validate_while_typing=False
             )

session.prompt('Give a number: ')

If the input validation contains some heavy CPU intensive code, but you don’t want to block the event loop, then it’s recommended to wrap the validation class in a ThreadedValidator.

Input Prompts using Prompt() class

Input history can be kept between consecutive quo.prompt() and quo.prompt.Prompt calls incase you want to ask for multiple inputs, but each input call needs about the same arguments.

from quo import prompt

tex1 = prompt("What is your name?")
text2 = prompt("Where are you from?")
from quo.prompt import Prompt

# Create prompt object.
session = Prompt()

# Do multiple input calls.
text1 = session.prompt("What's your name?")
text2 = session.prompt("Where are you from?")

Multiline Input

Reading multiline input is as easy as passing the multiline=True parameter.

from quo.prompt import Prompt

session = Prompt(multiline=True)
session.prompt('> ')

A side effect of this is that the enter key will now insert a newline instead of accepting and returning the input. The user will now have to press Meta+Enter in order to accept the input. (Or Escape followed by Enter.)

It is possible to specify a continuation prompt. This works by passing :meth:continuation [bool] to Prompt. This function is supposed to return formatted text, or a list of (style, text) tuples. The width of the returned text should not exceed the given width. (The width of the prompt margin is defined by the prompt.)

continuation() was added on v2022.4.4

from quo.prompt import Prompt

session = Prompt(multiline=True, continuation=True)

session.prompt('multiline input> ')
_images/multiline-input.png

Hide Input

When the hide=True flag in quo.prompt() or quo.prompt.Prompt has been given, the input is hidden in quo.prompt() or replaced by asterisks (* characters) in quo.prompt.Prompt

Using function quo.prompt()

from quo import prompt

prompt("Enter password: ", hide=True)

Using class `quo.prompt.Prompt()

from quo.prompt import Prompt

session = Prompt(hide=True)

session.prompt("Password: ")
_images/promptclasspassword.png

Confirmation Prompts

To ask if a user wants to continue with an action, the confirm() function comes in handy. By default, it returns the result of the prompt as a boolean value: Parameters

  • text (str) – the question to ask.

  • default (Optional[str, int]) – The default value to use when no input is given. If None, repeat until input is given.

  • abort (Optional[bool]) – if this is set to True a negative answer aborts the exception by raising Abort.

  • suffix (str) – a suffix that should be added to the prompt.

  • show_default (Optional[bool]) – shows or hides the default value in the prompt.

  • err (bool) – if set to true the file defaults to stderr instead of stdout, the same as with echo.

from quo import confirm

confirm('Do you want to continue?')

System prompt

If you press meta-! or esc-!, you can enter system commands like ls or cd.

from quo.prompt import Prompt

session = Prompt(system_prompt=True)

session.prompt("Give me some input: ")

Suspend prompt

Pressing ctrl-z will suspend the process from running and then run the command fg to continue the process.

from quo.prompt import Prompt

session = Prompt(suspend=True)

sessiom.prompr("Give me some input: ")

Prompt bottom toolbar

Adding a bottom toolbar is as easy as passing a bottom_toolbar argument to prompt(). This argument be either plain text, formatted text or a callable that returns plain or formatted text.

When a function is given, it will be called every time the prompt is rendered, so the bottom toolbar can be used to display dynamic information.

By default, the toolbar has the reversed style, which is why we are setting the background instead of the foreground.

from quo.prompt import Prompt

session = Prompt()

session.prompt('> ', bottom_toolbar="<i>This is a</i><b><style bg='red'> Toolbar</style></b>")
_images/bottom-toolbar.png

Here’s an example of a multiline bottom toolbar.

from quo.prompt import Prompt

session = Prompt()

session.prompt("Say something: ", bottom_toolbar="This is\na multiline toolbar")
_images/multiline-bottom-toolbar.png

Right prompt(rprompt)

The quo.prompt.Prompt class has out of the box support for right prompts as well. People familiar to ZSH could recognise this as the RPROMPT option.

This can be either plain text, formatted text or a callable which returns either.

The following example returns a formatted text:

from quo.prompt import Prompt

session = Prompt()
session.prompt(">> ", rprompt='<style fg="red" bg="green">Quo rprompt</style>')
_images/red-and-green-rprompt.png

Syntax highlighting

Quo ships with an intuitive syntax highligher. It is also possible to create a custom highligher by implementing the Highlight class.

(changed since v2022.9)

from quo.prompt import Prompt
from quo.highlight import Highlight

session = Prompt(highlighter=Highlight.html)

session.prompt('Enter HTML: ')
_images/highlighthtml.png

If you want to use another style you can do the following:Β»

from quo.prompt import Prompt
from quo.highlight import Highlight

session = Prompt(highlighter=Highlight.python)
session.prompt('Enter Python code: ')
_images/highlightpython.png

or:Β»

from quo.prompt import Prompt
from quo.highlight import Highlight

session = Prompt(highlighter=Highlight.css)
session.prompt('Enter css: ')

Syntax highlighting is as simple as adding a highlighter. All of the available syntax styles can be found here or Read more about styling.

Placeholder text

A placeholer is a text that’s displayed as long as no input is given. This won’t be returned as part of the output. This can be a string, formatted text or a callable that returns formatted text.

Plain text placeholder

from quo.prompt import Prompt

session = Prompt()

session.prompt("What is your name?: ", placeholder="..(please type something)")

Formatted text placeholder

from quo.prompt import Prompt

session = Prompt()
session.prompt("What is your name?: ", placeholder='<gray>(please type something)</gray>')
_images/gray-placeholder.png

Colors

By default, a neutral built-in color syntax is used, but any style instance can be passed to the Prompt class.

Note

quo.prompt() has different semantics and cannot output colored text but quo.prompt.Prompt has several ways on how this can be achieved.

Plain text prompt

from quo.prompt import Prompt

session = Prompt()

session.prompt("What is your name?: ")

Formatted text prompt

added on v2023.2

It is possible to add some colors to the prompt itself. In the following example, the prompt will be in green. added on v2023.2

from quo.prompt import Prompt

session = Prompt()
session.prompt("<green>What is your name?: </green>")

Styled prompt

from quo.prompt import Prompt

session = Prompt()
session.prompt("<red>john</red><white>@</white><green>localhost</green><red>:</red><cyan><u>/user/john</u></cyan><purple>$ </purple>")
_images/styled-prompt.png

Coloring the prompt and the input

It is possible to add some colors to the prompt itself and the input. In the following example, the prompt and the input will be in red

version changed 2023.2

from quo.prompt import Prompt

session = Prompt(fg="red")
session.prompt("Type something: ")
_images/red-prompt.png

fg and bg parameters added on version 2023.2 .. code:: python

from quo.prompt import Prompt

session = Prompt(fg=”red”, bg=”green”)

session.prompt(β€œType something: β€œ)

Here’s an example upgrade:

from quo.prompt import Prompt

session = Prompt(fg="blue") #The input will be colored blue

session.prompt("<red>john</red><white>@</white><green>localhost</green><red>:</red><cyan><u>/user/john</u></cyan><purple>$ </purple>")
_images/blue-input.png

The message can be any kind of formatted text, as discussed here. It can also be a callable that returns some formatted text.

By default, colors are taken from the 256 color palette. If you want to have 24-bit true color, this is possible by adding the color_depth=ColorDepth.TRUE_COLOR option to the Prompt .

from quo.prompt import Prompt
from quo.color import ColorDepth

session = Prompt(color_depth=ColorDepth.TRUE_COLOR)

session.prompt("<style fg='red' bg='blue'>What is your name:? </style>")

Completion

Auto suggestion

Auto suggestion is a way to propose some input completions to the user like the fish shell.

Usually, the input is compared to the history and when there is another entry starting with the given text, the completion will be shown as gray text behind the current input. Pressing the right arrow β†’ or ctrl-e will insert this suggestion, alt-f will insert the first word of the suggestion.

Added :param:`suggest` on v2022.5

Note

When suggestions are based on the history, don’t forget to share one History object between consecutive prompt calls. Using a Prompt

Example:

from quo.prompt import Prompt
from quo.history import MemoryHistory

MemoryHistory.append("import os")
MemoryHistory.append('print("hello")')
MemoryHistory.append('print("world")')
MemoryHistory.append("import path")

session = Prompt(history=MemoryHistory, suggest="history")
while True:
      text = session.prompt('> ')
      print(f"You said: {text}")
_images/auto-suggestion.png

A suggestion does not have to come from the history. Any implementation of the AutoSuggest abstract base class can be passed as a string i.e history, dynamic or conditional

Autocompletion

Autocompletion can be added by passing a completer parameter.

Press [Tab] to autocomplete

from quo.prompt import Prompt
from quo.completion import WordCompleter

example = WordCompleter(['USA', 'UK', 'Canada', 'Kenya'])
session = Prompt(completer=example)
session.prompt('Which country are you from?: ')

WordCompleter is a simple completer that completes the last word before the cursor with any of the given words.

_images/wordcompleter.png

Demonstration of a custom completer class and the possibility of styling completions independently.

from quo.completion import Completer, Completion
from quo.prompt import Prompt

colors = [
    "red",
    "blue",
    "green",
    "orange",
    "purple",
    "yellow",
    "cyan",
    "magenta",
    "pink",
    ]

class ColorCompleter(Completer):
    def get_completions(self, document, complete_event):
         word = document.get_word_before_cursor()
         for color in colors:
             if color.startswith(word):
                 yield Completion(
                           color,
                           start_position=-len(word),
                           style="fg:" + color,
                           selected_style="fg:white bg:" + color,
                           )

session = Prompt(completer=ColorCompleter(), complete_style="multi_column")
session.prompt("Type a color: ")
_images/custom-completion.png

Nested completion

Sometimes you have a command line interface where the completion depends on the previous words from the input. Examples are the CLIs from routers and switches. A simple WordCompleter is not enough in that case. We want to to be able to define completions at multiple hierarchical levels. NestedCompleter solves this issue:

from quo.prompt import Prompt
from quo.completion import NestedCompleter

completer = NestedCompleter.add({
    'show': {
        'version': None,
        'clock': None,
        'ip': {
            'interface': {'brief'}
        }
    },
    'exit': None
 })
 session = Prompt(completer=completer)
 session.prompt('# ')

Whenever there is a None value in the dictionary, it means that there is no further nested completion at that point. When all values of a dictionary would be None, it can also be replaced with a set.

Complete while typing

Autcompletions can be generated automatically while typing or when the user presses the tab key. This can be configured with the complete_while_typing option:

session.prompt('Enter HTML: ', completer=completer, complete_while_typing=True)

Notice that this setting is incompatible with the enable_history_search option. The reason for this is that the up and down key bindings would conflict otherwise. So, make sure to disable history search for this.

History

A History object keeps track of all the previously entered strings, so that the up-arrow can reveal previously entered items.

MemoryHistory

The recommended way is to use a Prompt, which uses an MemoryHistory which has ^ (up) arrow partial string matching enabled by default.

from quo.history import MemoryHistory
from quo.prompt import Prompt

MemoryHistory.append("import os")
MemoryHistory.append('print("hello")')
MemoryHistory.append('print("world")')
MemoryHistory.append("import path")

session = Prompt(history=MemoryHistory)

while True:
    session.prompt()

FileHistory

To persist a history to disk, use a FileHistory instead of the default MemoryHistory. This history object can be passed to a Prompt. For instance:

from quo.history import FileHistory
from quo.prompt import Prompt

history = FileHistory("~/.myhistory")
session = Prompt(history=history)

while True:
    session.prompt()

Adding custom key bindings

By default, every prompt already has a set of key bindings which implements the usual Vi or Emacs behaviour. We can extend this by passing quo.keys.bind() which is an instance of Bind.

Note

quo.prompt() function does not support key bindings but quo.prompt.Prompt does

An example of a prompt that prints 'hello world' when Control-T is pressed.

from quo import print
from quo.keys import bind
from quo.prompt import Prompt

@bind.add('ctrl-t')
def _(event):
# Print `Hello, World!` when `ctrl-t` is pressed."
    print("Hello, World!")

   @bind.add('ctrl-x')
def _(event):
  #Exit when `ctrl-x` is pressed. "
    event.app.exit()

session = Prompt()

session.prompt('> ')

Conditional Key bindings

Often, some key bindings can be enabled or disabled according to a certain condition. For instance, the Emacs and Vi bindings will never be active at the same time, but it is possible to switch between Emacs and Vi bindings at run time.

In order to enable a key binding according to a certain condition, we have to pass it a Condition instance. (Read more about filters.)

import datetime
from quo.filters import Condition
from quo.keys import bind
from quo.prompt import Prompt

@Condition
def second_half():
    " Only activate key binding on the second half of each minute. "
    return datetime.datetime.now().second > 30

@bind.add('ctrl-t', filter=second_half)
def _(event):
    # ...
    pass
session = Prompt()
session.prompt('> ')

Toggle visibility of input

Display asterisks instead of the actual characters with the addition of a ControlT shortcut to hide/show the input.

from quo.filters import Condition
from quo.keys import bind
from quo.prompt import Prompt

hidden = [True]  # Nonlocal

@bind.add("ctrl-t")
def _(event):
    "When ControlT has been pressed, toggle visibility."
    hidden[0] = not hidden[0]

session = Prompt(hide=Condition(lambda : hidden[0]))
session.prompt( "Password: ")

Mouse support

There is limited mouse support for positioning the cursor, for scrolling (in case of large multiline inputs) and for clicking in the autocompletion menu.

Enabling this can be done by passing the mouse_support=True option.

from quo.prompt import Prompt

session = Prompt(mouse_support=True)
session.prompt('What is your name: ')

Line wrapping

Line wrapping is enabled by default. This is what most people are used to and this is what GNU Readline does. When it is disabled, the input string will scroll horizontally.

from quo.prompt import Prompt

session = Prompt(wrap_lines=False)
session.prompt('What is your name: ')

Β» Check out more examples here