add parse_time(), validators.not_greater_than()

This commit is contained in:
Yusur 2025-09-19 16:52:23 +02:00
parent 18950c3445
commit f07d691004
4 changed files with 43 additions and 4 deletions

View file

@ -6,6 +6,8 @@
+ Add 7 new throwable exceptions
+ Add color utilities: `chalk` module
+ Add `.terminal` module, to ease TUI development.
+ `calendar`: add `parse_time()`.
+ Add validator `not_greater_than()`.
## 0.6.1

View file

@ -18,6 +18,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import datetime
from suou.functools import not_implemented
from suou.luck import lucky
from suou.validators import not_greater_than
def want_isodate(d: datetime.datetime | str | float | int, *, tz = None) -> str:
@ -63,4 +65,28 @@ def age_and_days(date: datetime.datetime, now: datetime.datetime | None = None)
d = (now - datetime.date(date.year + y, date.month, date.day)).days
return y, d
__all__ = ('want_datetime', 'want_timestamp', 'want_isodate', 'age_and_days')
@lucky([not_greater_than(259200)])
def parse_time(timestr: str, /) -> int:
"""
Parse a number-suffix (es. 3s, 15m) or colon (1:30) time expression.
Returns seconds as an integer.
"""
if timestr.isdigit():
return int(timestr)
elif ':' in timestr:
timeparts = timestr.split(':')
if not timeparts[0].isdigit() and not all(x.isdigit() and len(x) == 2 for x in timeparts[1:]):
raise ValueError('invalid time format')
return sum(int(x) * 60 ** (len(timeparts) - 1 - i) for i, x in enumerate(timeparts))
elif timestr.endswith('s') and timestr[:-1].isdigit():
return int(timestr[:-1])
elif timestr.endswith('m') and timestr[:-1].isdigit():
return int(timestr[:-1]) * 60
elif timestr.endswith('h') and timestr[:-1].isdigit():
return int(float(timestr[:-1]) * 3600)
else:
raise ValueError('invalid time format')
__all__ = ('want_datetime', 'want_timestamp', 'want_isodate', 'age_and_days', 'parse_time')

View file

@ -44,7 +44,8 @@ def lucky(validators: Iterable[Callable[[_U], bool]] = ()):
for v in validators:
try:
if not v(result):
raise BadLuckError(f'result not expected: {result!r}')
message = 'result not expected'
raise BadLuckError(f'{message}: {result!r}')
except BadLuckError:
raise
except Exception as e:

View file

@ -22,7 +22,7 @@ _T = TypeVar('_T')
def matches(regex: str | int, /, length: int = 0, *, flags=0):
"""
Return a function which returns true if X is shorter than length and matches the given regex.
Return a function which returns True if X is shorter than length and matches the given regex.
"""
if isinstance(regex, int):
length = regex
@ -31,13 +31,23 @@ def matches(regex: str | int, /, length: int = 0, *, flags=0):
return (not length or len(s) < length) and bool(re.fullmatch(regex, s, flags=flags))
return validator
def must_be(obj: _T | Any, typ: type[_T] | Iterable[type], message: str, *, exc = TypeError) -> _T:
"""
Raise TypeError if the requested object is not of the desired type(s), with a nice message.
(Not properly a validator.)
"""
if not isinstance(obj, typ):
raise TypeError(f'{message}, not {obj.__class__.__name__!r}')
return obj
__all__ = ('matches', )
def not_greater_than(y):
"""
Return a function that returns True if X is not greater than (i.e. lesser than or equal to) the given value.
"""
return lambda x: x <= y
__all__ = ('matches', 'not_greater_than')