From dcb2ce79955a2b05f01d99b06f6cc98a7b73356d Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Tue, 9 Sep 2025 22:05:57 +0200 Subject: [PATCH] add dei_args(), fix missing imports in .sqlalchemy --- CHANGELOG.md | 1 + src/suou/__init__.py | 3 ++- src/suou/dei.py | 27 +++++++++++++++++++++++++++ src/suou/signing.py | 2 ++ src/suou/sqlalchemy/orm.py | 10 ++++++++-- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c5769..f01e55e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ + Add Z85 (`z85encode()` `z85decode()`) encoding support + Add more strings to `.legal` module + `.signing` module is now covered by tests ++ New decorator `dei_args()`. Now offensive naming is no more a worry! ## 0.5.3 diff --git a/src/suou/__init__.py b/src/suou/__init__.py index 02d96d8..f73b414 100644 --- a/src/suou/__init__.py +++ b/src/suou/__init__.py @@ -23,6 +23,7 @@ from .bits import count_ones, mask_shift, split_bits, join_bits, mod_ceil, mod_f from .calendar import want_datetime, want_isodate, want_timestamp, age_and_days from .configparse import MissingConfigError, MissingConfigWarning, ConfigOptions, ConfigParserConfigSource, ConfigSource, DictConfigSource, ConfigValue, EnvConfigSource from .collections import TimedDict +from .dei import dei_args from .functools import deprecated, not_implemented, timed_cache, none_pass, alru_cache from .classtools import Wanted, Incomplete from .itertools import makelist, kwargs_prefix, ltuple, rtuple, additem, addattr @@ -45,7 +46,7 @@ __all__ = ( 'StringCase', 'TimedDict', 'TomlI18n', 'UserSigner', 'Wanted', 'WantsContentType', 'addattr', 'additem', 'age_and_days', 'alru_cache', 'b2048decode', 'b2048encode', 'b32ldecode', 'b32lencode', 'b64encode', 'b64decode', 'cb32encode', - 'cb32decode', 'count_ones', 'deprecated', 'ilex', 'join_bits', + 'cb32decode', 'count_ones', 'dei_args', 'deprecated', 'ilex', 'join_bits', 'jsonencode', 'kwargs_prefix', 'lex', 'ltuple', 'makelist', 'mask_shift', 'matches', 'mod_ceil', 'mod_floor', 'none_pass', 'not_implemented', 'redact_url_password', 'rtuple', 'split_bits', 'ssv_list', 'symbol_table', diff --git a/src/suou/dei.py b/src/suou/dei.py index c485216..0f7a7a0 100644 --- a/src/suou/dei.py +++ b/src/suou/dei.py @@ -18,6 +18,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from __future__ import annotations +from functools import wraps +from typing import Callable BRICKS = '@abcdefghijklmnopqrstuvwxyz+?-\'/' @@ -108,3 +110,28 @@ class Pronoun(int): i += BRICKS.index(ch) << (5 * j) return Pronoun(i) + + +def dei_args(**renames): + """ + Allow for aliases in the keyword argument names, in form alias='real_name'. + + DEI utility for those programmers who don't want to have to do with + potentially offensive variable naming. + + Dear conservatives, this does not influence the ability to call the wrapped function + with the original parameter names. + """ + def decorator(func: Callable): + @wraps(func) + def wrapper(*args, **kwargs): + for alias_name, actual_name in renames.items(): + if alias_name in kwargs: + val = kwargs.pop(alias_name) + kwargs[actual_name] = val + + return func(*args, **kwargs) + return wrapper + return decorator + + diff --git a/src/suou/signing.py b/src/suou/signing.py index 90ca7fa..c2011ce 100644 --- a/src/suou/signing.py +++ b/src/suou/signing.py @@ -22,6 +22,7 @@ from itsdangerous import TimestampSigner from itsdangerous import Signer as _Signer from itsdangerous.encoding import int_to_bytes as _int_to_bytes +from suou.dei import dei_args from suou.itertools import rtuple from .functools import not_implemented @@ -34,6 +35,7 @@ class UserSigner(TimestampSigner): itsdangerous.TimestampSigner() instanced from a user ID, with token generation and validation capabilities. """ user_id: int + @dei_args(primary_secret='master_secret') def __init__(self, master_secret: bytes, user_id: int, user_secret: bytes, **kwargs): super().__init__(master_secret + user_secret, salt=Siq(user_id).to_bytes(), **kwargs) self.user_id = user_id diff --git a/src/suou/sqlalchemy/orm.py b/src/suou/sqlalchemy/orm.py index 06a876a..403e762 100644 --- a/src/suou/sqlalchemy/orm.py +++ b/src/suou/sqlalchemy/orm.py @@ -20,19 +20,24 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from binascii import Incomplete import os -from typing import Any, Callable +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 suou.classtools import Wanted +from sqlalchemy.ext.hybrid import Comparator +from suou.classtools import Wanted, Incomplete from suou.codecs import StringCase +from suou.dei import dei_args from suou.iding import Siq, SiqCache, SiqGen, SiqType from suou.itertools import kwargs_prefix from suou.snowflake import SnowflakeGen from suou.sqlalchemy import IdType +_T = TypeVar('_T') + + def want_column(cls: type[DeclarativeBase], col: Column[_T] | str) -> Column[_T]: """ Return a table's column given its name. @@ -117,6 +122,7 @@ def bool_column(value: bool = False, nullable: bool = False, **kwargs) -> Column return Column(Boolean, server_default=def_val, nullable=nullable, **kwargs) +@dei_args(primary_secret='master_secret') def declarative_base(domain_name: str, master_secret: bytes, metadata: dict | None = None, **kwargs) -> type[DeclarativeBase]: """ Drop-in replacement for sqlalchemy.orm.declarative_base()