0.9.0 add yesno() + make Waiter usable + document validators
This commit is contained in:
parent
f1f9a95189
commit
def2634f21
10 changed files with 83 additions and 16 deletions
|
|
@ -1,5 +1,12 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.9.0
|
||||||
|
|
||||||
|
+ Fix to make experimental `Waiter` usable
|
||||||
|
+ Suspend `glue()` release indefinitely
|
||||||
|
+ Add `yesno()`
|
||||||
|
+ Document validators
|
||||||
|
|
||||||
## 0.8.2 and 0.7.9
|
## 0.8.2 and 0.7.9
|
||||||
|
|
||||||
+ `.color`: fix `chalk` not behaving as expected
|
+ `.color`: fix `chalk` not behaving as expected
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,11 @@ Please note that you probably already have those dependencies, if you just use t
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
...
|
Read the [documentation](https://suou.readthedocs.io/).
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Just a heads up: SUOU was made to support Sakuragasaki46 (me)'s own selfish, egoistic needs. Not to provide a service to the public.
|
Just a heads up: SUOU was made to support Sakuragasaki46 (me)'s own selfish, egoistic needs. Not certainly to provide a service to the public.
|
||||||
|
|
||||||
As a consequence, 'add this add that' stuff is best-effort.
|
As a consequence, 'add this add that' stuff is best-effort.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,5 @@ ease programmer's QoL and write shorter and cleaner code that works.
|
||||||
|
|
||||||
sqlalchemy
|
sqlalchemy
|
||||||
iding
|
iding
|
||||||
|
validators
|
||||||
api
|
api
|
||||||
15
docs/validators.rst
Normal file
15
docs/validators.rst
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
validators
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. currentmodule:: suou.validators
|
||||||
|
|
||||||
|
Validators for use in frameworks such as Pydantic or Marshmallow.
|
||||||
|
|
||||||
|
.. autofunction:: matches
|
||||||
|
|
||||||
|
.. autofunction:: not_greater_than
|
||||||
|
|
||||||
|
.. autofunction:: not_less_than
|
||||||
|
|
||||||
|
.. autofunction:: yesno
|
||||||
|
|
@ -32,12 +32,12 @@ from .signing import UserSigner
|
||||||
from .snowflake import Snowflake, SnowflakeGen
|
from .snowflake import Snowflake, SnowflakeGen
|
||||||
from .lex import symbol_table, lex, ilex
|
from .lex import symbol_table, lex, ilex
|
||||||
from .strtools import PrefixIdentifier
|
from .strtools import PrefixIdentifier
|
||||||
from .validators import matches
|
from .validators import matches, not_less_than, not_greater_than, yesno
|
||||||
from .redact import redact_url_password
|
from .redact import redact_url_password
|
||||||
from .http import WantsContentType
|
from .http import WantsContentType
|
||||||
from .color import chalk, WebColor
|
from .color import chalk, WebColor
|
||||||
|
|
||||||
__version__ = "0.8.2"
|
__version__ = "0.9.0"
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue',
|
'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue',
|
||||||
|
|
@ -51,9 +51,10 @@ __all__ = (
|
||||||
'cb32decode', 'chalk', 'count_ones', 'dei_args', 'deprecated',
|
'cb32decode', 'chalk', 'count_ones', 'dei_args', 'deprecated',
|
||||||
'future', 'ilex', 'join_bits',
|
'future', 'ilex', 'join_bits',
|
||||||
'jsonencode', 'kwargs_prefix', 'lex', 'ltuple', 'makelist', 'mask_shift',
|
'jsonencode', 'kwargs_prefix', 'lex', 'ltuple', 'makelist', 'mask_shift',
|
||||||
'matches', 'mod_ceil', 'mod_floor', 'none_pass', 'not_implemented',
|
'matches', 'mod_ceil', 'mod_floor', 'must_be', 'none_pass', 'not_implemented',
|
||||||
|
'not_less_than', 'not_greater_than',
|
||||||
'redact_url_password', 'rtuple', 'split_bits', 'ssv_list', 'symbol_table',
|
'redact_url_password', 'rtuple', 'split_bits', 'ssv_list', 'symbol_table',
|
||||||
'timed_cache', 'twocolon_list', 'want_bytes', 'want_datetime', 'want_isodate',
|
'timed_cache', 'twocolon_list', 'want_bytes', 'want_datetime', 'want_isodate',
|
||||||
'want_str', 'want_timestamp', 'want_urlsafe', 'want_urlsafe_bytes',
|
'want_str', 'want_timestamp', 'want_urlsafe', 'want_urlsafe_bytes', 'yesno',
|
||||||
'z85encode', 'z85decode'
|
'z85encode', 'z85decode'
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from suou.functools import not_implemented
|
|
||||||
from suou.luck import lucky
|
from suou.luck import lucky
|
||||||
from suou.validators import not_greater_than
|
from suou.validators import not_greater_than
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ from suou.classtools import MISSING
|
||||||
from suou.functools import future
|
from suou.functools import future
|
||||||
|
|
||||||
|
|
||||||
@future(version="0.9.0")
|
@future()
|
||||||
class FakeModule(ModuleType):
|
class FakeModule(ModuleType):
|
||||||
"""
|
"""
|
||||||
Fake module used in @glue() in case of import error
|
Fake module used in @glue() in case of import error
|
||||||
|
|
@ -34,12 +34,12 @@ class FakeModule(ModuleType):
|
||||||
raise AttributeError(f'Module {self.__name__} not found; this feature is not available ({self._exc})') from self._exc
|
raise AttributeError(f'Module {self.__name__} not found; this feature is not available ({self._exc})') from self._exc
|
||||||
|
|
||||||
|
|
||||||
@future(version = "0.9.0")
|
@future()
|
||||||
def glue(*modules):
|
def glue(*modules):
|
||||||
"""
|
"""
|
||||||
Helper for "glue" code -- it imports the given modules and passes them as keyword arguments to the wrapped functions.
|
Helper for "glue" code -- it imports the given modules and passes them as keyword arguments to the wrapped functions.
|
||||||
|
|
||||||
NEW 0.9.0
|
EXPERIMENTAL
|
||||||
"""
|
"""
|
||||||
module_dict = dict()
|
module_dict = dict()
|
||||||
imports_succeeded = True
|
imports_succeeded = True
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ import re
|
||||||
|
|
||||||
from typing import Any, Iterable, TypeVar
|
from typing import Any, Iterable, TypeVar
|
||||||
|
|
||||||
|
from suou.classtools import MISSING
|
||||||
|
|
||||||
|
from .functools import future
|
||||||
|
|
||||||
_T = TypeVar('_T')
|
_T = TypeVar('_T')
|
||||||
|
|
||||||
def matches(regex: str | int, /, length: int = 0, *, flags=0):
|
def matches(regex: str | int, /, length: int = 0, *, flags=0):
|
||||||
|
|
@ -55,5 +59,13 @@ def not_less_than(y):
|
||||||
"""
|
"""
|
||||||
return lambda x: x >= y
|
return lambda x: x >= y
|
||||||
|
|
||||||
__all__ = ('matches', 'not_greater_than')
|
def yesno(x: str) -> bool:
|
||||||
|
"""
|
||||||
|
Returns False if x.lower() is in '0', '', 'no', 'n', 'false' or 'off'.
|
||||||
|
|
||||||
|
*New in 0.9.0*
|
||||||
|
"""
|
||||||
|
return x not in (None, MISSING) and x.lower() not in ('', '0', 'off', 'n', 'no', 'false', 'f')
|
||||||
|
|
||||||
|
__all__ = ('matches', 'must_be', 'not_greater_than', 'not_less_than', 'yesno')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from typing import Callable
|
||||||
import warnings
|
import warnings
|
||||||
from starlette.applications import Starlette
|
from starlette.applications import Starlette
|
||||||
from starlette.responses import JSONResponse, PlainTextResponse, Response
|
from starlette.responses import JSONResponse, PlainTextResponse, Response
|
||||||
|
|
@ -27,15 +28,22 @@ from suou.functools import future
|
||||||
|
|
||||||
@future()
|
@future()
|
||||||
class Waiter():
|
class Waiter():
|
||||||
|
_cached_app: Callable | None = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.routes: list[Route] = []
|
self.routes: list[Route] = []
|
||||||
self.production = False
|
self.production = False
|
||||||
|
|
||||||
|
async def __call__(self, *args):
|
||||||
|
return await self._build_app()(*args)
|
||||||
|
|
||||||
def _build_app(self) -> Starlette:
|
def _build_app(self) -> Starlette:
|
||||||
return Starlette(
|
if not self._cached_app:
|
||||||
|
self._cached_app = Starlette(
|
||||||
debug = not self.production,
|
debug = not self.production,
|
||||||
routes= self.routes
|
routes= self.routes
|
||||||
)
|
)
|
||||||
|
return self._cached_app
|
||||||
|
|
||||||
def get(self, endpoint: str, *a, **k):
|
def get(self, endpoint: str, *a, **k):
|
||||||
return self._route('GET', endpoint, *a, **k)
|
return self._route('GET', endpoint, *a, **k)
|
||||||
|
|
|
||||||
24
tests/test_validators.py
Normal file
24
tests/test_validators.py
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from suou.validators import yesno
|
||||||
|
|
||||||
|
class TestValidators(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
...
|
||||||
|
def tearDown(self):
|
||||||
|
...
|
||||||
|
def test_yesno(self):
|
||||||
|
self.assertFalse(yesno('false'))
|
||||||
|
self.assertFalse(yesno('FALSe'))
|
||||||
|
self.assertTrue(yesno('fasle'))
|
||||||
|
self.assertTrue(yesno('falso'))
|
||||||
|
self.assertTrue(yesno('zero'))
|
||||||
|
self.assertTrue(yesno('true'))
|
||||||
|
self.assertFalse(yesno('0'))
|
||||||
|
self.assertTrue(yesno('00'))
|
||||||
|
self.assertTrue(yesno('.'))
|
||||||
|
self.assertTrue(yesno('2'))
|
||||||
|
self.assertTrue(yesno('o'))
|
||||||
|
self.assertFalse(yesno('oFF'))
|
||||||
|
self.assertFalse(yesno('no'))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue