add PrefixIdentifier() and some tests

This commit is contained in:
Yusur 2025-07-19 11:31:01 +02:00
parent e5ca63953d
commit 8a16fe159f
6 changed files with 162 additions and 6 deletions

View file

@ -6,7 +6,8 @@
+ \[BREAKING] Changed the behavior of `makelist()`: now it's also a decorator, converting its return type to a list (revertable with `wrap=False`)
+ New module `lex` with functions `symbol_table()` and `lex()` — make tokenization more affordable
+ Add `dorks` module and `flask.harden()`
+ Added `addattr()`
+ Add `sqlalchemy.bool_column()`: make making flags painless
+ Added `addattr()`, `PrefixIdentifier()`
## 0.3.6

View file

@ -37,9 +37,7 @@ sqlalchemy = [
]
flask = [
"Flask>=2.0.0",
"Flask-RestX",
"Quart",
"Quart-Schema"
"Flask-RestX"
]
flask_sqlalchemy = [
"Flask-SqlAlchemy",
@ -50,6 +48,21 @@ peewee = [
markdown = [
"markdown>=3.0.0"
]
quart = [
"Flask>=2.0.0",
"Quart",
"Quart-Schema",
"uvloop; os_name=='posix'"
]
full = [
"sakuragasaki46-suou[sqlalchemy]",
"sakuragasaki46-suou[flask]",
"sakuragasaki46-suou[quart]",
"sakuragasaki46-suou[peewee]",
"sakuragasaki46-suou[markdown]"
]
[tool.setuptools.dynamic]
version = { attr = "suou.__version__" }

View file

@ -20,7 +20,7 @@ from abc import ABCMeta, abstractmethod
from functools import wraps
from typing import Callable, Iterable, Never, TypeVar
import warnings
from sqlalchemy import BigInteger, CheckConstraint, Date, Dialect, ForeignKey, LargeBinary, Column, MetaData, SmallInteger, String, create_engine, select, text
from sqlalchemy import BigInteger, Boolean, CheckConstraint, Date, Dialect, ForeignKey, LargeBinary, Column, MetaData, SmallInteger, String, create_engine, select, text
from sqlalchemy.orm import DeclarativeBase, Session, declarative_base as _declarative_base, relationship
from .snowflake import SnowflakeGen
@ -120,7 +120,17 @@ def match_column(length: int, regex: str, /, case: StringCase = StringCase.AS_IS
constraint_name=constraint_name or f'{x.__tablename__}_{n}_valid')), *args, **kwargs)
def declarative_base(domain_name: str, master_secret: bytes, metadata: dict | None = None, **kwargs):
def bool_column(value: bool = False, nullable: bool = False, **kwargs):
"""
Column for a single boolean value.
NEW in 0.4.0
"""
def_val = text('true') if value else text('false')
return Column(Boolean, server_default=def_val, nullable=nullable, **kwargs)
def declarative_base(domain_name: str, master_secret: bytes, metadata: dict | None = None, **kwargs) -> DeclarativeBase:
"""
Drop-in replacement for sqlalchemy.orm.declarative_base()
taking in account requirements for SIQ generation (i.e. domain name).

44
src/suou/strtools.py Normal file
View file

@ -0,0 +1,44 @@
"""
Utilities for string manipulation.
Why `strtools`? Why not `string`? I just~ happen to not like it
---
Copyright (c) 2025 Sakuragasaki46.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
See LICENSE for the specific language governing permissions and
limitations under the License.
This software is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
from typing import Callable, Iterable
from .itertools import makelist
class PrefixIdentifier:
_prefix: str
def __init__(self, prefix: str | None, validators: Iterable[Callable[[str], bool]] | Callable[[str], bool] | None = None):
prefix = '' if prefix is None else prefix
if not isinstance(prefix, str):
raise TypeError
validators = makelist(validators, wrap=False)
for validator in validators:
if not validator(prefix):
raise ValueError('invalid prefix')
self._prefix = prefix
def __getattr__(self, key: str):
return f'{self._prefix}{key}'
def __getitem__(self, key: str) -> str:
return f'{self._prefix}{key}'
__all__ = ('PrefixIdentifier',)

50
tests/test_codecs.py Normal file
View file

@ -0,0 +1,50 @@
import binascii
import unittest
from suou.codecs import b64encode, b64decode
B1 = b'N\xf0\xb4\xc3\x85\n\xf9\xb6\x9a\x0f\x82\xa6\x99G\x07#'
B2 = b'\xbcXiF,@|{\xbe\xe3\x0cz\xa8\xcbQ\x82'
B3 = b"\xe9\x18)\xcb'\xc2\x96\xae\xde\x86"
B4 = B1[-2:] + B2[:-2]
B5 = b'\xff\xf8\xa7\x8a\xdf\xff'
class TestCodecs(unittest.TestCase):
def setUp(self) -> None:
...
def tearDown(self) -> None:
...
#def runTest(self):
# self.test_b64encode()
# self.test_b64decode()
def test_b64encode(self):
self.assertEqual(b64encode(B1), 'TvC0w4UK-baaD4KmmUcHIw')
self.assertEqual(b64encode(B2), 'vFhpRixAfHu-4wx6qMtRgg')
self.assertEqual(b64encode(B3), '6RgpyyfClq7ehg')
self.assertEqual(b64encode(B4), 'ByO8WGlGLEB8e77jDHqoyw')
self.assertEqual(b64encode(B5), '__init__')
self.assertEqual(b64encode(B1[:4]), 'TvC0ww')
self.assertEqual(b64encode(b'\0' + B1[:4]), 'AE7wtMM')
self.assertEqual(b64encode(b'\0\0\0\0\0' + B1[:4]), 'AAAAAABO8LTD')
self.assertEqual(b64encode(b'\xff'), '_w')
self.assertEqual(b64encode(b''), '')
def test_b64decode(self):
self.assertEqual(b64decode('TvC0w4UK-baaD4KmmUcHIw'), B1)
self.assertEqual(b64decode('vFhpRixAfHu-4wx6qMtRgg'), B2)
self.assertEqual(b64decode('6RgpyyfClq7ehg'), B3)
self.assertEqual(b64decode('ByO8WGlGLEB8e77jDHqoyw'), B4)
self.assertEqual(b64decode('__init__'), B5)
self.assertEqual(b64decode('TvC0ww'), B1[:4])
self.assertEqual(b64decode('AE7wtMM'), b'\0' + B1[:4])
self.assertEqual(b64decode('AAAAAABO8LTD'), b'\0\0\0\0\0' + B1[:4])
self.assertEqual(b64decode('_w'), b'\xff')
self.assertEqual(b64decode(''), b'')
self.assertRaises(binascii.Error, b64decode, 'C')

38
tests/test_strtools.py Normal file
View file

@ -0,0 +1,38 @@
import unittest
from suou.strtools import PrefixIdentifier
class TestStrtools(unittest.TestCase):
def setUp(self) -> None:
...
def tearDown(self) -> None:
...
def test_PrefixIdentifier_empty(self):
pi = PrefixIdentifier(None)
self.assertEqual(pi.hello, 'hello')
self.assertEqual(pi['with spaces'], 'with spaces')
self.assertEqual(pi['\x1b\x00'], '\x1b\0')
self.assertEqual(pi.same_thing, pi['same_thing'])
with self.assertRaises(TypeError):
pi[0]
self.assertEqual(PrefixIdentifier(None), PrefixIdentifier(''))
def test_PrefixIdentifier_invalid(self):
with self.assertRaises(TypeError):
pi = PrefixIdentifier(1)
pi.hello
with self.assertRaises(TypeError):
PrefixIdentifier([99182])
with self.assertRaises(TypeError):
PrefixIdentifier(b'alpha_')