0.7.2 add version= to @future(), support Py3.14, mark .waiter as future

This commit is contained in:
Yusur 2025-10-11 18:39:06 +02:00
parent be4404c520
commit 7e80c84de6
6 changed files with 43 additions and 6 deletions

View file

@ -1,5 +1,12 @@
# Changelog
## 0.7.2
+ `@future()` now can take a `version=` argument
+ `Waiter()` got marked `@future` indefinitely
+ Stage `username_column()` for release in 0.8.0
+ Explicit support for Python 3.14 (aka python pi)
## 0.7.1
+ Add documentation ([Read The Docs](https://suou.readthedocs.io/))

View file

@ -27,7 +27,8 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13"
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14"
]
[project.urls]

View file

@ -37,7 +37,7 @@ from .redact import redact_url_password
from .http import WantsContentType
from .color import chalk
__version__ = "0.7.1"
__version__ = "0.7.2"
__all__ = (
'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue',

View file

@ -80,17 +80,23 @@ def not_implemented(msg: Callable | str | None = None):
return decorator(msg)
return decorator
def future(message: str | None = None):
def future(message: str | None = None, *, version: str = None):
"""
Describes experimental or future API's introduced as bug fixes (including as backports)
but not yet intended for general use (mostly to keep semver consistent).
version= is the intended version release.
NEW 0.7.0
"""
def decorator(func: Callable[_T, _U]) -> Callable[_T, _U]:
@wraps(func)
def wrapper(*a, **k) -> _U:
warnings.warn(message or f'{func.__name__}() is intended for a future release and not intended for use right now', FutureWarning)
warnings.warn(message or (
f'{func.__name__}() is intended for release on {version} and not ready for use right now'
if version else
f'{func.__name__}() is intended for a future release and not ready for use right now'
), FutureWarning)
return func(*a, **k)
return wrapper
return decorator

View file

@ -1,7 +1,7 @@
"""
Utilities for SQLAlchemy; ORM
NEW 0.6.0
NEW 0.6.0 (moved)
---
@ -20,12 +20,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
from binascii import Incomplete
import os
import re
from typing import Any, Callable, TypeVar
import warnings
from sqlalchemy import BigInteger, Boolean, CheckConstraint, Column, Date, ForeignKey, LargeBinary, MetaData, SmallInteger, String, text
from sqlalchemy.orm import DeclarativeBase, InstrumentedAttribute, Relationship, declarative_base as _declarative_base, relationship
from sqlalchemy.types import TypeEngine
from sqlalchemy.ext.hybrid import Comparator
from suou.functools import future
from suou.classtools import Wanted, Incomplete
from suou.codecs import StringCase
from suou.dei import dei_args
@ -101,7 +103,7 @@ match_constraint.TEXT_DIALECTS = {
'mariadb': ':n RLIKE :re'
}
def match_column(length: int, regex: str, /, case: StringCase = StringCase.AS_IS, *args, constraint_name: str | None = None, **kwargs) -> Incomplete[Column[str]]:
def match_column(length: int, regex: str | re.Pattern, /, case: StringCase = StringCase.AS_IS, *args, constraint_name: str | None = None, **kwargs) -> Incomplete[Column[str]]:
"""
Syntactic sugar to create a String() column with a check constraint matching the given regular expression.
@ -112,6 +114,24 @@ def match_column(length: int, regex: str, /, case: StringCase = StringCase.AS_IS
return Incomplete(Column, String(length), Wanted(lambda x, n: match_constraint(n, regex, #dialect=x.metadata.engine.dialect.name,
constraint_name=constraint_name or f'{x.__tablename__}_{n}_valid')), *args, **kwargs)
@future(version='0.8.0')
def username_column(
length: int = 32, regex: str | re.Pattern = '[a-z_][a-z0-9_-]+', *args, case: StringCase = StringCase.LOWER,
nullable : bool = False, **kwargs) -> Incomplete[Column[str] | Column[str | None]]:
"""
Construct a column containing a unique handle / username.
Username must match the given `regex` and be at most `length` characters long.
NEW 0.8.0
"""
if case is StringCase.AS_IS:
warnings.warn('case sensitive usernames may lead to impersonation and unexpected behavior', UserWarning)
return match_column(length, regex, case=case, nullable=nullable, unique=True, *args, **kwargs)
def bool_column(value: bool = False, nullable: bool = False, **kwargs) -> Column[bool]:
"""
Column for a single boolean value.

View file

@ -21,6 +21,9 @@ from starlette.applications import Starlette
from starlette.responses import JSONResponse, PlainTextResponse, Response
from starlette.routing import Route
from suou.functools import future
@future()
class Waiter():
def __init__(self):
self.routes: list[Route] = []