ACME client that automates the process of obtaining, installing, and renewing SSL/TLS certificates from Let's Encrypt certificate authority.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
User interaction utilities for prompting, displaying information, and managing user interface elements in command-line and interactive environments.
Prompt users for email addresses with validation and error handling.
def get_email(invalid: bool = False, **kwargs) -> str:
"""
Prompt for valid email address with interactive input.
Args:
invalid: True if an invalid address was previously provided by user
**kwargs: Additional arguments (unused but maintained for compatibility)
Returns:
Valid email address string, or empty string if user skips
Raises:
errors.Error: If user cancels the operation
"""Usage examples:
from certbot.display import ops
# Prompt for email address
try:
email = ops.get_email()
if email:
print(f"Using email: {email}")
else:
print("No email provided")
except errors.Error:
print("User cancelled email input")
# Prompt after invalid email was provided
try:
email = ops.get_email(invalid=True)
except errors.Error:
print("User cancelled after invalid email")Allow users to choose from available ACME accounts.
def choose_account(accounts: list[Account]) -> Optional[Account]:
"""
Choose an account from available accounts with interactive menu.
Args:
accounts: List containing at least one Account object
Returns:
Selected Account object, or None if user cancels
"""Usage example:
from certbot.display import ops
from certbot._internal import account
# Load available accounts
accounts = account_storage.find_all()
if accounts:
selected_account = ops.choose_account(accounts)
if selected_account:
print(f"Using account: {selected_account.slug}")
else:
print("No account selected")
else:
print("No accounts available")Present users with multiple choice selection interfaces.
def choose_values(values: list[str], question: Optional[str] = None) -> list[str]:
"""
Display screen to let user pick one or multiple values from provided list.
Args:
values: List of values to select from
question: Question to ask user while choosing values
Returns:
List of selected values (may be empty if user cancels)
"""
def choose_names(installer, question: Optional[str] = None) -> list[str]:
"""
Display screen for interactive domain name selection from installer.
Args:
installer: Installer plugin instance
question: Question to display to user
Returns:
List of selected domain names
"""
def get_valid_domains(domains: Iterable[str]) -> list[str]:
"""
Validate and filter domain list for certificate eligibility.
Args:
domains: Iterable of domain names to validate
Returns:
List of valid domain names
"""Usage examples:
from certbot.display import ops
# Let user choose domains to include
domains = ['example.com', 'www.example.com', 'api.example.com', 'mail.example.com']
selected = ops.choose_values(
values=domains,
question="Which domains would you like to include in the certificate?"
)
if selected:
print(f"Selected domains: {', '.join(selected)}")
else:
print("No domains selected")
# Choose enhancements to apply
enhancements = ['redirect', 'hsts', 'uir']
chosen_enhancements = ops.choose_values(
values=enhancements,
question="Which security enhancements would you like to enable?"
)Low-level display utilities for user interaction.
def input_text(message: str, default: str = "",
force_interactive: bool = False) -> tuple[int, str]:
"""
Get text input from user with interactive prompt.
Args:
message: Message to display to user
default: Default value if user provides no input
force_interactive: Force interactive mode even in non-interactive environments
Returns:
Tuple of (status_code, user_input)
Status codes: OK (user provided input), CANCEL (user cancelled)
"""
def menu(message: str, choices: list[str],
force_interactive: bool = False) -> tuple[int, int]:
"""
Display menu and get user selection.
Args:
message: Message to display above menu
choices: List of menu options
force_interactive: Force interactive mode even in non-interactive environments
Returns:
Tuple of (status_code, selected_index)
Status codes: OK (user made selection), CANCEL (user cancelled)
"""
def checklist(message: str, tags: list[str],
force_interactive: bool = False) -> tuple[int, list[str]]:
"""
Display checklist for multiple selection.
Args:
message: Message to display above checklist
tags: List of items for selection
force_interactive: Force interactive mode even in non-interactive environments
Returns:
Tuple of (status_code, selected_items)
Status codes: OK (user made selections), CANCEL (user cancelled)
"""
def yesno(message: str, yes_label: str = "Yes", no_label: str = "No",
force_interactive: bool = False) -> bool:
"""
Display yes/no prompt to user.
Args:
message: Question to display to user
yes_label: Text for yes option
no_label: Text for no option
force_interactive: Force interactive mode
Returns:
True if user selected yes, False if no
"""
def notify(message: str) -> None:
"""
Display notification message to user.
Args:
message: Message to display
"""
def notification(message: str, pause: bool = True, wrap: bool = True) -> None:
"""
Display notification with formatting options.
Args:
message: Message to display
pause: Whether to pause for user acknowledgment
wrap: Whether to wrap long lines
"""
def directory_select(message: str, default: str = "") -> tuple[int, str]:
"""
Display directory selection dialog.
Args:
message: Prompt message for directory selection
default: Default directory path
Returns:
Tuple of (status_code, selected_directory)
"""Usage examples:
from certbot.display import util as display_util
# Get text input from user
code, text = display_util.input_text(
message="Enter your organization name:",
default="Example Corp"
)
if code == display_util.OK:
print(f"Organization: {text}")
# Display menu for single selection
options = ['Apache', 'Nginx', 'Other']
code, index = display_util.menu(
message="Select your web server:",
choices=options,
force_interactive=True
)
if code == display_util.OK:
print(f"Selected: {options[index]}")
# Display checklist for multiple selections
features = ['HTTPS redirect', 'HSTS headers', 'OCSP stapling']
code, selected = display_util.checklist(
message="Select security features to enable:",
tags=features,
force_interactive=True
)
if code == display_util.OK:
print(f"Selected features: {', '.join(selected)}")Functions to display operation status and success notifications.
def success_installation(domains: list[str]) -> None:
"""
Display successful certificate installation message.
Args:
domains: List of domains for which certificates were installed
"""
def success_renewal(domains: list[str]) -> None:
"""
Display successful certificate renewal message.
Args:
domains: List of domains for which certificates were renewed
"""
def success_revocation(cert_path: str) -> None:
"""
Display successful certificate revocation message.
Args:
cert_path: Path to revoked certificate
"""
def report_executed_command(command: str, returncode: int, stdout: str, stderr: str) -> None:
"""
Report results of executed command to user.
Args:
command: Command that was executed
returncode: Process return code
stdout: Standard output from command
stderr: Standard error from command
"""
def validated_input(validator: Callable, message: str, **kwargs) -> tuple[str, str]:
"""
Get user input with validation function.
Args:
validator: Function to validate input
message: Prompt message
**kwargs: Additional arguments
Returns:
Tuple of (status, validated_input)
"""
def validated_directory(validator: Callable, message: str, **kwargs) -> tuple[str, str]:
"""
Get directory path with validation.
Args:
validator: Function to validate directory
message: Prompt message
**kwargs: Additional arguments
Returns:
Tuple of (status, validated_directory_path)
"""Status codes returned by display utility functions.
OK = 0 # User completed operation successfully
CANCEL = 1 # User cancelled operation
WIDTH = 72 # Standard display width for text formattingCertbot can run in non-interactive mode where user prompts are automatically handled:
from certbot import configuration
# Configure non-interactive mode
config.non_interactive = True
# Email collection in non-interactive mode
if config.non_interactive:
if not config.email:
# Use default behavior or raise error
raise errors.Error("Email required in non-interactive mode")
else:
# Use interactive prompts
email = ops.get_email()Handle user cancellation and display errors gracefully:
from certbot.display import ops
from certbot import errors
def get_user_preferences():
"""Get user preferences with error handling."""
try:
# Get email
email = ops.get_email()
# Get domain selection
available_domains = discover_domains()
if available_domains:
selected_domains = ops.choose_values(
values=available_domains,
question="Select domains for certificate:"
)
else:
selected_domains = []
return {
'email': email,
'domains': selected_domains
}
except errors.Error as e:
print(f"User interaction failed: {e}")
return None
# Handle different user interaction scenarios
preferences = get_user_preferences()
if preferences:
if preferences['email']:
print(f"Contact email: {preferences['email']}")
if preferences['domains']:
print(f"Certificate domains: {', '.join(preferences['domains'])}")
else:
print("No domains selected - manual configuration required")
else:
print("User cancelled configuration")Display utilities integrate with Certbot's validation systems:
from certbot.display import ops
from certbot import util
def get_validated_email():
"""Get email with validation loop."""
while True:
try:
email = ops.get_email()
if not email:
return "" # User chose to skip
if util.safe_email(email):
return email
else:
# Will show "invalid email" message on next prompt
email = ops.get_email(invalid=True)
except errors.Error:
# User cancelled
return None
# Usage
email = get_validated_email()
if email is None:
print("User cancelled email input")
elif email == "":
print("User chose to skip email")
else:
print(f"Valid email obtained: {email}")Install with Tessl CLI
npx tessl i tessl/pypi-certbot