Trivial Terminal User Interfaces (TUI) with Databricks Labs Blueprint

Serge Smertin
Databricks Labs
Published in
6 min readJan 15, 2024

Ensuring testable interactivity is a crucial aspect that cannot be overlooked. Thankfully, the solution lies in leveraging the powerful capabilities of the databricks.labs.blueprint.tui. This essential import opens up a world of possibilities for enhancing the user experience and facilitating thorough testing of your command-line apps. Let’s delve into a few illustrative examples of how Prompts can elevate the interactivity of your applications. We’ve decoupled this library from the installer of the UCX project so that more people can use it:

Simple Text Questions

When it comes to soliciting user input in command-line applications, the question() method proves to be a more sophisticated alternative to the standard input() built-in function. By incorporating the Prompts module from databricks.labs.blueprint.tui, you can introduce a higher level of user engagement and control. Let’s explore the versatility of prompts.question() through a practical example:

from databricks.labs.blueprint.tui import Prompts

prompts = Prompts()
answer = prompts.question('Enter a year', default='2024', valid_number=True)
print(answer)

This concise snippet initiates a user prompt for entering a year. However, the features don’t stop there; optional arguments further amplify its functionality:

  • default: This argument allows you to provide a default value that will be used if the user doesn’t input anything.
  • max_attempts (default 10): With this parameter, you can specify the maximum number of attempts before an exception is raised, offering robust error handling for invalid or empty inputs.
  • valid_number: Setting this to True enforces that the user’s input must be a valid number.
  • valid_regex: This parameter ensures that the input adheres to a specified regular expression.
  • validate: This option enables you to define a custom validation function, such as a lambda expression (lambda x: 'awesome' in x), allowing for intricate validation beyond basic checks.

Confirming Actions

Ensuring the safety and intentionality of optional or potentially destructive actions within your command-line application is paramount. The confirm() method emerges as a reliable guardian for such scenarios, allowing users to confirm their choices explicitly. Here’s a practical implementation in action:

if prompts.confirm('Destroy database?'):
print('DESTROYING DATABASE')

In this succinct snippet, the script prompts the user with a confirmation, asking whether they truly want to destroy the database. The following condition ensures that the subsequent action, in this case, the message ‘DESTROYING DATABASE,’ is only executed if the user affirms the confirmation prompt. This straightforward yet robust approach adds a layer of security to your application and empowers users to make informed decisions when faced with optional or potentially hazardous actions. It acts as a guardian, preventing accidental or uninformed execution of critical commands, thereby enhancing your command-line app's reliability and user-friendly nature.

Single Choice from a List

Empowering users to select from a predefined list is a common requirement in command-line applications, and the choice() method is tailor-made for such scenarios. Here’s an illustrative example:

answer = prompts.choice('Select a language', ['Python', 'Rust', 'Go', 'Java'])
print(answer)

In this concise code snippet, the choice()method initiates an interactive prompt for the user to select a language from the provided list: Python, Rust, Go, or Java. The selected choice is then stored in the ‘answer’ variable and is subsequently printed to the console. This feature enhances the user experience by providing a structured and intuitive way to make selections and ensures that the chosen value aligns with the predefined options.

Single Choice from Dictionary

Facilitating user selection from a dictionary while presenting them with sorted keys is made effortless by the choice_from_dict() method. Here’s a practical example to showcase its functionality:

answer = prompts.choice_from_dict('Select a locale', {
'Українська': 'ua',
'English': 'en'
})
print(f'Locale is: {answer}')

In this succinct code snippet, the prompts.choice_from_dict() triggers an interactive prompt, displaying sorted keys from the provided dictionary: Українська key mapped to ua value and English mapped to en. The user is prompted to choose a locale, and the selected value is stored in the answer variable. Subsequently, the chosen locale is printed to the console. It also enhances clarity by presenting the options in a sorted order. This feature is particularly useful when dealing with any scenario where presenting options in an organized manner is essential for a more user-friendly experience in your command-line applications.

Multiple Choices from Dictionary

Enabling users to select multiple items from a dictionary in a clear and organized manner is effortlessly achieved through the multiple_choice_from_dict() method. Here’s an example illustrating its usage:

answer = prompts.multiple_choice_from_dict(
'What projects are written in Python? Select [DONE] when ready.', {
'Databricks Labs UCX': 'ucx',
'Databricks SDK for Python': 'sdk-py',
'Databricks SDK for Go': 'sdk-go',
'Databricks CLI': 'cli',
})
print(f'Answer is: {answer}')

In this concise code snippet, prompts.multiple_choice_from_dict() initiates an interactive prompt, presenting the user with a selection of projects written in Python. The user can choose multiple items, and the selected values are stored in the answer variable. Once the user is done making selections, the chosen answers are printed to the console. This functionality is particularly useful when users must make multiple selections from a predefined set of options, providing a seamless and user-friendly way to gather input in your command-line applications. This ensures clarity and ease of use, enhancing the overall interactive experience for the user.

Unit Testing Prompts

Testing user interactions in a controlled environment is essential for ensuring the robustness of your code. The MockPrompts class is designed to facilitate this by allowing you to simulate user input for testing purposes. Here’s an example test for the question method using MockPrompts with regular expressions as keys and values as answers:

from databricks.labs.blueprint.tui import MockPrompts

def test_ask_for_int():
# Define a MockPrompts instance with a regular expression as the key
# and an empty string as the default answer for any input.
prompts = MockPrompts({r".*": ""})

# Simulate a user interaction by calling the question method.
# The default answer will be returned as the response.
res = prompts.question("Number of threads", default="8", valid_number=True)

# Assert that the result matches the expected default answer.
assert "8" == res

In this test, the MockPrompts instance is configured to match any input (using the regular expression .*) and respond with an empty string. This allows the test to focus on the behavior of the question method with a valid_number constraint and a default value. The assertion checks whether the result matches the expected default answer. This approach provides a controlled environment for testing your interactive prompts, allowing you to simulate various user inputs and ensure that your code handles them correctly.

Installation

If you build apps on top of Databricks Lakehouse, go ahead and try this experimental library out by installing it via pip install databricks-labs-blueprint. Please give it a star on GitHub and PyPI if you'd like.

Please share and subscribe to Updates from Databricks Labs newsletter to stay up-to-date with the latest releases from GitHub namespace. Subscribing ensures you’re the first to know about the latest enhancements, bug fixes, and exciting features that will take your Data Intelligence Platform experience to the next level. You‘re encouraged to follow the GitHub org as well ;)

--

--

Serge Smertin
Databricks Labs

Extreme Programming. Digital Forensics. Thoughts are my own.