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/_static
docs/templates
# changes during CD/CI
aliases/*/pyproject.toml

View file

@ -1,5 +1,12 @@
# 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
+ `.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
...
Read the [documentation](https://suou.readthedocs.io/).
## 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.

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
iding
validators
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 .lex import symbol_table, lex, ilex
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 .http import WantsContentType
from .color import chalk, WebColor
__version__ = "0.8.2"
__version__ = "0.9.0"
__all__ = (
'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue',
@ -51,9 +51,10 @@ __all__ = (
'cb32decode', 'chalk', 'count_ones', 'dei_args', 'deprecated',
'future', 'ilex', 'join_bits',
'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',
'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'
)

View file

@ -17,7 +17,6 @@ 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

View file

@ -22,7 +22,7 @@ from suou.classtools import MISSING
from suou.functools import future
@future(version="0.9.0")
@future()
class FakeModule(ModuleType):
"""
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
@future(version = "0.9.0")
@future()
def glue(*modules):
"""
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()
imports_succeeded = True

View file

@ -18,6 +18,10 @@ import re
from typing import Any, Iterable, TypeVar
from suou.classtools import MISSING
from .functools import future
_T = TypeVar('_T')
def matches(regex: str | int, /, length: int = 0, *, flags=0):
@ -55,5 +59,13 @@ def not_less_than(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
from starlette.applications import Starlette
from starlette.responses import JSONResponse, PlainTextResponse, Response
@ -27,15 +28,22 @@ from suou.functools import future
@future()
class Waiter():
_cached_app: Callable | None = None
def __init__(self):
self.routes: list[Route] = []
self.production = False
async def __call__(self, *args):
return await self._build_app()(*args)
def _build_app(self) -> Starlette:
return Starlette(
if not self._cached_app:
self._cached_app = Starlette(
debug = not self.production,
routes= self.routes
)
return self._cached_app
def get(self, endpoint: str, *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'))