Spaces:
Sleeping
Sleeping
| """ | |
| Input validation for a `Buffer`. | |
| (Validators will be called before accepting input.) | |
| """ | |
| from __future__ import annotations | |
| from abc import ABCMeta, abstractmethod | |
| from typing import Callable | |
| from prompt_toolkit.eventloop import run_in_executor_with_context | |
| from .document import Document | |
| from .filters import FilterOrBool, to_filter | |
| __all__ = [ | |
| "ConditionalValidator", | |
| "ValidationError", | |
| "Validator", | |
| "ThreadedValidator", | |
| "DummyValidator", | |
| "DynamicValidator", | |
| ] | |
| class ValidationError(Exception): | |
| """ | |
| Error raised by :meth:`.Validator.validate`. | |
| :param cursor_position: The cursor position where the error occurred. | |
| :param message: Text. | |
| """ | |
| def __init__(self, cursor_position: int = 0, message: str = "") -> None: | |
| super().__init__(message) | |
| self.cursor_position = cursor_position | |
| self.message = message | |
| def __repr__(self) -> str: | |
| return f"{self.__class__.__name__}(cursor_position={self.cursor_position!r}, message={self.message!r})" | |
| class Validator(metaclass=ABCMeta): | |
| """ | |
| Abstract base class for an input validator. | |
| A validator is typically created in one of the following two ways: | |
| - Either by overriding this class and implementing the `validate` method. | |
| - Or by passing a callable to `Validator.from_callable`. | |
| If the validation takes some time and needs to happen in a background | |
| thread, this can be wrapped in a :class:`.ThreadedValidator`. | |
| """ | |
| def validate(self, document: Document) -> None: | |
| """ | |
| Validate the input. | |
| If invalid, this should raise a :class:`.ValidationError`. | |
| :param document: :class:`~prompt_toolkit.document.Document` instance. | |
| """ | |
| pass | |
| async def validate_async(self, document: Document) -> None: | |
| """ | |
| Return a `Future` which is set when the validation is ready. | |
| This function can be overloaded in order to provide an asynchronous | |
| implementation. | |
| """ | |
| try: | |
| self.validate(document) | |
| except ValidationError: | |
| raise | |
| def from_callable( | |
| cls, | |
| validate_func: Callable[[str], bool], | |
| error_message: str = "Invalid input", | |
| move_cursor_to_end: bool = False, | |
| ) -> Validator: | |
| """ | |
| Create a validator from a simple validate callable. E.g.: | |
| .. code:: python | |
| def is_valid(text): | |
| return text in ['hello', 'world'] | |
| Validator.from_callable(is_valid, error_message='Invalid input') | |
| :param validate_func: Callable that takes the input string, and returns | |
| `True` if the input is valid input. | |
| :param error_message: Message to be displayed if the input is invalid. | |
| :param move_cursor_to_end: Move the cursor to the end of the input, if | |
| the input is invalid. | |
| """ | |
| return _ValidatorFromCallable(validate_func, error_message, move_cursor_to_end) | |
| class _ValidatorFromCallable(Validator): | |
| """ | |
| Validate input from a simple callable. | |
| """ | |
| def __init__( | |
| self, func: Callable[[str], bool], error_message: str, move_cursor_to_end: bool | |
| ) -> None: | |
| self.func = func | |
| self.error_message = error_message | |
| self.move_cursor_to_end = move_cursor_to_end | |
| def __repr__(self) -> str: | |
| return f"Validator.from_callable({self.func!r})" | |
| def validate(self, document: Document) -> None: | |
| if not self.func(document.text): | |
| if self.move_cursor_to_end: | |
| index = len(document.text) | |
| else: | |
| index = 0 | |
| raise ValidationError(cursor_position=index, message=self.error_message) | |
| class ThreadedValidator(Validator): | |
| """ | |
| Wrapper that runs input validation in a thread. | |
| (Use this to prevent the user interface from becoming unresponsive if the | |
| input validation takes too much time.) | |
| """ | |
| def __init__(self, validator: Validator) -> None: | |
| self.validator = validator | |
| def validate(self, document: Document) -> None: | |
| self.validator.validate(document) | |
| async def validate_async(self, document: Document) -> None: | |
| """ | |
| Run the `validate` function in a thread. | |
| """ | |
| def run_validation_thread() -> None: | |
| return self.validate(document) | |
| await run_in_executor_with_context(run_validation_thread) | |
| class DummyValidator(Validator): | |
| """ | |
| Validator class that accepts any input. | |
| """ | |
| def validate(self, document: Document) -> None: | |
| pass # Don't raise any exception. | |
| class ConditionalValidator(Validator): | |
| """ | |
| Validator that can be switched on/off according to | |
| a filter. (This wraps around another validator.) | |
| """ | |
| def __init__(self, validator: Validator, filter: FilterOrBool) -> None: | |
| self.validator = validator | |
| self.filter = to_filter(filter) | |
| def validate(self, document: Document) -> None: | |
| # Call the validator only if the filter is active. | |
| if self.filter(): | |
| self.validator.validate(document) | |
| class DynamicValidator(Validator): | |
| """ | |
| Validator class that can dynamically returns any Validator. | |
| :param get_validator: Callable that returns a :class:`.Validator` instance. | |
| """ | |
| def __init__(self, get_validator: Callable[[], Validator | None]) -> None: | |
| self.get_validator = get_validator | |
| def validate(self, document: Document) -> None: | |
| validator = self.get_validator() or DummyValidator() | |
| validator.validate(document) | |
| async def validate_async(self, document: Document) -> None: | |
| validator = self.get_validator() or DummyValidator() | |
| await validator.validate_async(document) | |