InquirerPy icon indicating copy to clipboard operation
InquirerPy copied to clipboard

Exceptions from class-based validators

Open irons opened this issue 4 years ago • 2 comments

I'm trying to migrate code from PyInquirer, which leans on class-based validators. Here's an example, borrowing from https://inquirerpy.readthedocs.io/en/latest/pages/validator.html:

#!/usr/bin/env python3 

from InquirerPy import prompt
from prompt_toolkit.validation import ValidationError, Validator

class EmptyInputValidator(Validator):
    def validate(self, document):
        if not len(document.text) > 0:
            raise ValidationError(
                message="Input cannot be empty.",
                cursor_position=document.cursor_position,
            )

questions = [
    {
        'type': 'input',
        'name': 'color',
        'message': f"How do you feel?",
        'validate': EmptyInputValidator
    }
]

result = prompt(questions)
print(result)

This approach crashes InquirerPy, whether the prompt response is empty or not:

Unhandled exception in event loop:
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/asyncio/events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/prompt_toolkit/input/vt100.py", line 170, in callback_wrapper
    callback()
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/prompt_toolkit/application/application.py", line 708, in read_from_input
    self.key_processor.process_keys()
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/prompt_toolkit/key_binding/key_processor.py", line 271, in process_keys
    self._process_coroutine.send(key_press)
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/prompt_toolkit/key_binding/key_processor.py", line 186, in _process
    self._call_handler(matches[-1], key_sequence=buffer[:])
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/prompt_toolkit/key_binding/key_processor.py", line 321, in _call_handler
    handler.call(event)
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/prompt_toolkit/key_binding/key_bindings.py", line 124, in call
    result = self.handler(event)
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/InquirerPy/base/simple.py", line 240, in executable
    func(event)
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/InquirerPy/base/simple.py", line 142, in _
    method["func"](event, *method.get("args", []))
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/InquirerPy/prompts/input.py", line 188, in _handle_enter
    self._session.validator.validate(self._session.default_buffer)  # type: ignore
  File "/Users/whomever/.pyenv/versions/3.9.10/lib/python3.9/site-packages/prompt_toolkit/validation.py", line 120, in validate
    if not self.func(document.text):

Exception EmptyInputValidator() takes no arguments

The outcome and stack trace are identical on python versions 3.7.10, 3.8.12, 3.9.10, and 3.10.2, all running on macOS 11.6.3 (under pyenv) with inquirerpy 0.3.3.

I can fix the exception's complaint by adding a stub __init__ method:

class EmptyInputValidator(Validator):
    def __init__(self, document):
      pass

This no longer crashes, but the validator never fires. It also breaks the contract from prompt_toolkit about only needing to override the validate function.

For the record, the subclass approach succeeds in PyInquirer, until python 3.10, where it falls victim to the removal of Mapping from the collections interface. And though validators using from_callable succeed in InquirerPy, this is not a good fit for my purposes, unless I'm overlooking how it can be used to return distinct error messages for different kinds of validation failure.

Do you have any guidance for class-based validators with InquirerPy? Thanks for your time.

irons avatar Feb 05 '22 19:02 irons

Hi @irons ,

Thanks for reporting. This seems like another difference with PyInquirer that I didn't catch. You'll need to initialise the validator before providing it to the prompt.

questions = [
    {
        'type': 'input',
        'name': 'color',
        'message': f"How do you feel?",
        'validate': EmptyInputValidator()  # initialise here
    }
]

I'll update the doc to note this.

Thanks, Kevin

kazhala avatar Feb 05 '22 23:02 kazhala

Thanks for the rapid response. Easy change on my end, looks to be working well.

irons avatar Feb 06 '22 01:02 irons