Skip to content

Commit

Permalink
add support for async validation functions
Browse files Browse the repository at this point in the history
  • Loading branch information
falkoschindler committed Nov 22, 2024
1 parent 1f6e3fb commit 27ff2ef
Showing 1 changed file with 32 additions and 8 deletions.
40 changes: 32 additions & 8 deletions nicegui/elements/mixins/validation_element.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
from typing import Any, Callable, Dict, Optional, Union
from typing import Any, Awaitable, Callable, Dict, Optional, Union

from typing_extensions import Self

from ... import background_tasks
from ...helpers import is_coroutine_function
from .value_element import ValueElement


class ValidationElement(ValueElement):

def __init__(self, validation: Optional[Union[Callable[..., Optional[str]], Dict[str, Callable[..., bool]]]], **kwargs: Any) -> None:
def __init__(self, validation: Optional[Union[
Callable[..., Union[Optional[str], Awaitable[Optional[str]]]],
Dict[str, Callable[..., bool]],
]], **kwargs: Any) -> None:
self._validation = validation
self._auto_validation = True
self._error: Optional[str] = None
super().__init__(**kwargs)
self._props['error'] = None if validation is None else False # NOTE: reserve bottom space for error message

@property
def validation(self) -> Optional[Union[Callable[..., Optional[str]], Dict[str, Callable[..., bool]]]]:
def validation(self) -> Optional[Union[
Callable[..., Union[Optional[str], Awaitable[Optional[str]]]],
Dict[str, Callable[..., bool]],
]]:
"""The validation function or dictionary of validation functions."""
return self._validation

@validation.setter
def validation(self, validation: Optional[Union[Callable[..., Optional[str]], Dict[str, Callable[..., bool]]]]) -> None:
def validation(self, validation: Optional[Union[
Callable[..., Union[Optional[str], Awaitable[Optional[str]]]],
Dict[str, Callable[..., bool]],
]]) -> None:
"""Sets the validation function or dictionary of validation functions.
:param validation: validation function or dictionary of validation functions (``None`` to disable validation)
"""
self._validation = validation
self.validate()
self.validate(return_result=False)

@property
def error(self) -> Optional[str]:
Expand All @@ -47,13 +58,26 @@ def error(self, error: Optional[str]) -> None:
self._props['error-message'] = error
self.update()

def validate(self) -> bool:
def validate(self, *, return_result: bool = True) -> bool:
"""Validate the current value and set the error message if necessary.
:return: True if the value is valid, False otherwise
"""
if is_coroutine_function(self._validation):
async def await_error():
assert callable(self._validation)
result = self._validation(self.value)
assert isinstance(result, Awaitable)
self.error = await result
if return_result:
raise NotImplementedError('The validate method cannot return results for async validation functions')
background_tasks.create(await_error())
return True

if callable(self._validation):
self.error = self._validation(self.value)
result = self._validation(self.value)
assert not isinstance(result, Awaitable)
self.error = result
return self.error is None

if isinstance(self._validation, dict):
Expand All @@ -73,4 +97,4 @@ def without_auto_validation(self) -> Self:
def _handle_value_change(self, value: Any) -> None:
super()._handle_value_change(value)
if self._auto_validation:
self.validate()
self.validate(return_result=False)

0 comments on commit 27ff2ef

Please sign in to comment.