From 0e6d4c0f1a4fbcba511bae2393b4f30eafb408fd Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Mon, 11 Mar 2024 16:34:10 -0700 Subject: [PATCH] `Field.number(kind=...)` supports any function that returns a number For example, in our internal codebase, we have a function: ```python def decimal_from_str(value: str, parens_negate: bool = False) -> Decimal: ... amount = Field.number(kind=decimal_from_str) ``` --- src/klein/_form.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/klein/_form.py b/src/klein/_form.py index 8f09de91..da93a332 100644 --- a/src/klein/_form.py +++ b/src/klein/_form.py @@ -12,7 +12,7 @@ NoReturn, Optional, Sequence, - Type, + TypeVar, cast, ) @@ -29,6 +29,7 @@ from ._app import KleinRenderable, _call from ._decorators import bindable +from ._typing_compat import Protocol from .interfaces import ( EarlyExit, IDependencyInjector, @@ -41,6 +42,20 @@ ) +_T = TypeVar("_T", contravariant=True) + + +class _Numeric(Protocol[_T]): + def __float__(self) -> float: + ... + + def __lt__(self, other: _T) -> bool: + ... + + def __gt__(self, other: _T) -> bool: + ... + + class CrossSiteRequestForgery(Resource): """ Cross site request forgery detected. Request aborted. @@ -258,9 +273,9 @@ def hidden(cls, name: str, value: str, **kw: Any) -> "Field": @classmethod def number( cls, - minimum: Optional[int] = None, - maximum: Optional[int] = None, - kind: Type = float, + minimum: Optional[_Numeric] = None, + maximum: Optional[_Numeric] = None, + kind: Callable[[str], _Numeric] = float, **kw: Any, ) -> "Field": """