Compare commits

..

3 commits

12 changed files with 87 additions and 104 deletions

3
.gitignore vendored
View file

@ -29,3 +29,6 @@ aliases/*/src
docs/_build docs/_build
docs/_static docs/_static
docs/templates docs/templates
# changes during CD/CI
aliases/*/pyproject.toml

View file

@ -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

View file

@ -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.

View file

@ -1,87 +0,0 @@
[project]
name = "sakuragasaki46_suou"
description = "casual utility library for coding QoL"
authors = [
{ name = "Sakuragasaki46" }
]
dynamic = [ "version" ]
requires-python = ">=3.10"
license = "Apache-2.0"
readme = "README.md"
dependencies = [
"suou==0.8.1",
"itsdangerous",
"toml",
"pydantic",
"setuptools>=78.0.0",
"uvloop; os_name=='posix'"
]
# - further devdependencies below - #
# - publishing -
classifiers = [
"Development Status :: 2 - Pre-Alpha",
# actively supported Pythons
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14"
]
[project.urls]
Repository = "https://nekode.yusur.moe/yusur/suou"
Documentation = "https://suou.readthedocs.io"
[project.optional-dependencies]
# the below are all dev dependencies (and probably already installed)
sqlalchemy = [
"SQLAlchemy[asyncio]>=2.0.0",
"flask-sqlalchemy"
]
flask = [
"Flask>=2.0.0",
"Flask-RestX"
]
flask_sqlalchemy = [
"sakuragasaki46_suou[sqlalchemy]",
"sakuragasaki46_suou[flask]"
]
peewee = [
## HEADS UP! peewee has setup.py, may slow down installation
"peewee>=3.0.0"
]
markdown = [
"markdown>=3.0.0"
]
quart = [
"Quart",
"Quart-Schema",
"starlette>=0.47.2"
]
sass = [
## HEADS UP!! libsass carries a C extension + uses setup.py
"libsass"
]
full = [
"sakuragasaki46_suou[sqlalchemy]",
"sakuragasaki46_suou[flask]",
"sakuragasaki46_suou[quart]",
"sakuragasaki46_suou[peewee]",
"sakuragasaki46_suou[markdown]",
"sakuragasaki46_suou[sass]"
]
docs = [
"sphinx>=2.1",
"myst_parser",
"sphinx_rtd_theme"
]
[tool.setuptools.dynamic]
version = { attr = "suou.__version__" }

View file

@ -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
View 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

View file

@ -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'
) )

View file

@ -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

View file

@ -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

View file

@ -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')

View file

@ -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:
debug = not self.production, self._cached_app = Starlette(
routes= self.routes debug = not self.production,
) 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
View 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'))