interactive

Interactive module.

wmflib.interactive.NOTIFY_LOGGER_NAME = 'wmflib_interactive_notify'

Name of the logger used to send notifications when the process is awaiting user input.

wmflib.interactive.notify_logger = <Logger wmflib_interactive_notify (WARNING)>

Instance of the notification logger that is used to send notification when the process is awaiting user input.

wmflib.interactive.MIN_SECRET_SIZE: int = 6

The minimum number of characters for a secret.

wmflib.interactive.NOTIFY_AFTER_SECONDS: float = 180.0

The amount of time in seconds after which a notification log will be triggered.

exception wmflib.interactive.InputError[source]

Bases: WmflibError

Custom exception class raised on invalid input from the user in interactive mode.

__annotations__ = {}
__doc__ = 'Custom exception class raised on invalid input from the user in interactive mode.'
__module__ = 'wmflib.interactive'
exception wmflib.interactive.AbortError[source]

Bases: WmflibError

Custom exception class raised when an action is manually aborted.

__annotations__ = {}
__doc__ = 'Custom exception class raised when an action is manually aborted.'
__module__ = 'wmflib.interactive'
wmflib.interactive.ask_input(message: str, choices: Sequence[str], *, validator: Callable[[str], None] | None = None) str[source]

Ask the user for input in interactive mode. Can be used with a list of valid answers or a custom validator.

If the user doesn’t reply within wmflib.interactive.NOTIFY_AFTER_SECONDS seconds, a notification message will be sent to the wmflib.interactive.notify_logger logger instance at warning level. The client of this code is responsible of setting up the logger handler in a way to notify the user. By default there is a logging.NullHandler handler and the messages will not be propagated to the parent logger. It is safe to call notify_logger.handlers.clear() when setting up this handler.

Examples

>>> choices = ["A", "B"]
>>> response = ask_input(f"Choose a door between {choices}", choices)
==> Choose a door between ['A', 'B']
> a
==> Invalid response, please type one of: A,B. After 3 wrong answers the task will be aborted.
> A
>>> response
'A'

>>> def my_validator(answer: str) -> None:
...     if len(answer) < 5:
...         raise RuntimeError("The directory name must be at least 5 characters long")
...
>>> response = ask_input(f"Provide a directory name", choices=[], validator=my_validator)
==> Provide a directory name
> tmp
==> Invalid response. The directory name must be at least 5 characters long. After 3 wrong answers the
task will be aborted.
> tmpdir
>>> response
'tmpdir'
Parameters:
  • message (str) – the message to be printed before asking for confirmation.

  • choices (sequence) – the available choices of possible answers that the user can give. Values must be strings. It must be set to an empty sequence in order to use the custom validator for validating free-form answers.

  • validator (callable, optional) – a custom validator callable that accepts a single string parameter as input and returns None if the answer is valid and raises any Exception with a meaningful message if the answer is invalid. When using a custom validator the choices argument must be set to an empty sequence like an empty string.

Returns:

the selected choice or free answer if choices is set to None explicitly.

Return type:

str

Raises:
wmflib.interactive.ask_confirmation(message: str) None[source]

Ask the use for confirmation in interactive mode.

If the user doesn’t reply within wmflib.interactive.NOTIFY_AFTER_SECONDS seconds, a notification message will be sent to the wmflib.interactive.notify_logger logger instance at warning level. The client of this code is responsible of setting up the logger handler in a way to notify the user. By default there is a logging.NullHandler handler and the messages will not be propagated to the parent logger. It is safe to call notify_logger.handlers.clear() when setting up this handler.

Examples

>>> ask_confirmation("Ready to continue?")
==> Ready to continue?
Type "go" to proceed or "abort" to interrupt the execution
> go
>>> ask_confirmation("Ready to continue?")
==> Ready to continue?
Type "go" to proceed or "abort" to interrupt the execution
> abort
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/wmflib/interactive.py", line 69, in ask_confirmation
    raise AbortError('Confirmation manually aborted')
wmflib.interactive.AbortError: Confirmation manually aborted
Parameters:

message (str) – the message to be printed before asking for confirmation.

Raises:
wmflib.interactive.confirm_on_failure(func: Callable, *args: Any, **kwargs: Any) Any[source]

Execute a function asking for confirmation to retry, abort or skip.

If the user doesn’t reply within wmflib.interactive.NOTIFY_AFTER_SECONDS seconds, a notification message will be sent to the wmflib.interactive.notify_logger logger instance at warning level. The client of this code is responsible of setting up the logger handler in a way to notify the user. By default there is a logging.NullHandler handler and the messages will not be propagated to the parent logger. It is safe to call notify_logger.handlers.clear() when setting up this handler.

Examples

>>> def test(fail=False):
...     if fail:
...         raise RuntimeError("Failed")
...
>>> confirm_on_failure(test)
>>> confirm_on_failure(test, fail=True)
Failed to run __main__.test: Failed
==> What do you want to do? "retry" the last command, manually fix the issue and "skip" the last command to
    continue the execution or completely "abort" the execution.
> retry
Failed to run __main__.test: Failed
==> What do you want to do? "retry" the last command, manually fix the issue and "skip" the last command to
    continue the execution or completely "abort" the execution.
> skip
>>>
Parameters:
  • func (callable) – the function/method to execute.

  • *args (mixed) – all the positional arguments to pass to the function/method.

  • **kwargs (mixed) – all the keyword arguments to pass to the function/method.

Returns:

what the called function returns, or None if the execution should continue skipping this step because has been manually fixed.

Return type:

mixed

Raises:

wmflib.interactive.AbortError – on manually aborted tasks.

wmflib.interactive.get_username() str[source]

Detect and return the name of the effective running user even if run as root.

Examples

>>> get_username()
'user'
Returns:

the name of the effective running user or - if unable to detect it.

Return type:

str

wmflib.interactive.ensure_shell_is_durable() None[source]

Ensure it is running either in non-interactive mode or in a screen/tmux session, raise otherwise.

Examples

>>> ensure_shell_is_durable()  # Will raise if not in a tmux/screen session
>>>
Raises:

wmflib.exceptions.WmflibError – if in a non-durable shell session.

wmflib.interactive.get_secret(title: str, *, confirm: bool = False) str[source]

Ask the user for a secret e.g. password.

If the user doesn’t reply within wmflib.interactive.NOTIFY_AFTER_SECONDS seconds, a notification message will be sent to the wmflib.interactive.notify_logger logger instance at warning level. The client of this code is responsible of setting up the logger handler in a way to notify the user. By default there are no handlers and the messages will not be propagated to the parent logger.

Examples

>>> secret = get_secret("Secret key")
Secret key:
Secret must be at least 6 characters. try again:
>>> secret = get_secret("Secret key", confirm=True)  # Will raise if the confirmation doesn't match
Secret key:
Again, just to be sure:
>>>
Parameters:
  • title (str) – The message to show the user.

  • confirm (bool, optional) – If True ask the user to confirm the password.

Returns:

the secret.

Return type:

str

Raises:

wmflib.exceptions.WmflibError – if the password confirmation does not match and confirm is True.