Skip to content

Commit

Permalink
Merge pull request #70 from AuHau/feat/validation-extension
Browse files Browse the repository at this point in the history
Custom validation message
  • Loading branch information
AuHau authored May 23, 2019
2 parents be842e9 + e166d0d commit 1719640
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 10 deletions.
22 changes: 20 additions & 2 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,29 @@ Optional attribute that allows the program to check if the answer is valid or no
def validate(answers, current): return boolean()
Where ``answers`` is a `dict` with previous answers again and ``current`` is the current answer. Example:
Where ``answers`` is a `dict` with previous answers again and ``current`` is the current answer.
If you want to customize the validation message, you can raise your own error with specific reason:
``inquirer.errors.ValidationError('', reason='your reason that will be displayed to the user')``
inside the validation function, but be aware that if the validation passes you still have to return `True`!

Example:

.. code:: python
from inquirer import errors
import random
def validation_function(answers, current):
if random.random() > 0.5:
raise errors.ValidationError('', reason='Sorry, just have bad mood.')
return True
Text('nothing', "Moody question", validate=validation_function)
Text('age', "How old are you?", validate=lambda _, c: 0 <= c < 120)
Text('age', "how old are you?", validate=lambda _, c: 0 <= c < 120)
ignore
~~~~~~
Expand Down
14 changes: 12 additions & 2 deletions examples/text.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import os
import sys
import re
sys.path.append(os.path.realpath('.'))
from pprint import pprint

import inquirer
from inquirer import errors

sys.path.append(os.path.realpath('.'))


def phone_validation(answers, current):
if not re.match('\+?\d[\d ]+\d', current):
raise errors.ValidationError('', reason='I don\'t like your phone number!')

return True


questions = [
inquirer.Text('name',
Expand All @@ -13,7 +23,7 @@
message="What's your surname, {name}?"),
inquirer.Text('phone',
message="What's your phone number",
validate=lambda _, x: re.match('\+?\d[\d ]+\d', x),
validate=phone_validation,
)
]

Expand Down
9 changes: 5 additions & 4 deletions inquirer/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ class InquirerError(Exception):


class ValidationError(InquirerError):
def __init__(self, value, *args, **kwargs):
super(ValidationError, self).__init__(*args, **kwargs)
def __init__(self, value, reason=None, *args):
super(ValidationError, self).__init__(*args)
self.value = value
self.reason = reason


class UnknownQuestionTypeError(InquirerError):
Expand All @@ -17,8 +18,8 @@ class Aborted(InquirerError):


class EndOfInput(InquirerError):
def __init__(self, selection, *args, **kwargs):
super(EndOfInput, self).__init__(*args, **kwargs)
def __init__(self, selection, *args):
super(EndOfInput, self).__init__(*args)
self.selection = selection


Expand Down
2 changes: 2 additions & 0 deletions inquirer/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ def validate(self, current):
try:
if self._solve(self._validate, current):
return
except errors.ValidationError as e:
raise e
except Exception:
pass
raise errors.ValidationError(current)
Expand Down
3 changes: 3 additions & 0 deletions inquirer/render/console/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@ def read_input(self):
raise NotImplementedError('Abstract')

def handle_validation_error(self, error):
if error.reason:
return error.reason

return '"{e}" is not a valid {q}.'.format(e=error.value,
q=self.question.name)
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-r requirements.txt

flake8==3.7.7
pexpect==4.6.0
pexpect==4.7.0

pytest==4.3.0
pytest-cov==2.6.1
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_invalid_phone(self):
self.set_name()
self.set_surname()
self.set_phone('abcde')
self.sut.expect('"abcde" is not a valid phone', timeout=1)
self.sut.expect('I don\'t like your phone number!', timeout=1)
self.sut.sendline(5*key.BACKSPACE + '12345')
self.sut.expect_list([re.compile(b"'name': 'foo'"),
re.compile(b"'surname': 'bar'"),
Expand Down
27 changes: 27 additions & 0 deletions tests/integration/console_render/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from readchar import key

from inquirer.render import ConsoleRender
from inquirer import errors


class TextRenderTest(unittest.TestCase, helper.BaseTestCase):
Expand Down Expand Up @@ -68,6 +69,32 @@ def test_validation_fails(self):
self.assertInStdout(message)
self.assertInStdout('"Invalid" is not a valid foo')

def test_validation_fails_with_custom_message(self):
stdin_array = [x for x in
'Invalid' + key.ENTER +
key.BACKSPACE*20 +
'9999' + key.ENTER]
stdin = helper.event_factory(*stdin_array)

message = 'Insert number'
variable = 'foo'
expected = '9999'

def raise_exc(x, current):
if current != '9999':
raise errors.ValidationError('', reason='Custom error')
return True

question = questions.Text(variable,
validate=raise_exc,
message=message)

sut = ConsoleRender(event_generator=stdin)
result = sut.render(question)
self.assertEquals(expected, result)
self.assertInStdout(message)
self.assertInStdout('Custom error')

def test_allows_deletion(self):
stdin_array = ['a', key.BACKSPACE, 'b', key.ENTER]
stdin = helper.event_factory(*stdin_array)
Expand Down
14 changes: 14 additions & 0 deletions tests/unit/test_question.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,20 @@ def raise_exc(x, y):
with self.assertRaises(errors.ValidationError):
q.validate(None)

def test_validate_function_raising_validation_error(self):
err = errors.ValidationError('', reason='foo')

def raise_exc(x, y):
raise err

name = 'foo'
q = questions.Question(name, validate=raise_exc)

try:
q.validate(None)
except errors.ValidationError as e:
self.assertIs(e, err)

def test_validate_function_receives_object(self):
expected = object()

Expand Down

0 comments on commit 1719640

Please sign in to comment.