0.13.0a1 add .argparse, fix .sqlalchemy imports

This commit is contained in:
Yusur 2026-05-31 01:44:45 +02:00
parent d0701599fe
commit 11baf91dfd
6 changed files with 126 additions and 14 deletions

View file

@ -1,5 +1,12 @@
# Changelog
## 0.13.0
+ Added module `argparse` with class `LetterSubparsers()`
+ module `sqlalchemy`:
* removed deprecated alias `entity_base()`. use `declarative_base()` instead.
* fix imports.
## 0.12.6
+ Added unittests to `dei_args()`

View file

@ -1,6 +1,6 @@
# SIS Unified Object Underarmor
Good morning, my brother! Welcome **SUOU** (**S**IS **U**nified **O**bject **U**nderarmor), the Python library which speeds up and makes it pleasing to develop API, database schemas and stuff in Python.
Good morning, my brother! Welcome **SUOU** (**S**IS **U**nified **O**bject **U**nderarmor), the Python library which (maybe) speeds up and makes it pleasing to develop API, database schemas and stuff in Python.
It provides utilities such as:
* SIQ ([specification](https://yusur.moe/protocols/siq.html) \[LINK BROKEN - WON'T FIX\] - [copy](https://suou.readthedocs.io/en/latest/iding.html))
@ -15,13 +15,13 @@ It provides utilities such as:
**Python 3.10**+ with Pip is required.
```bash
$ pip install sakuragasaki46-suou
$ pip install suou
```
To install optional dependencies (i.e. `sqlalchemy`) for development use:
```bash
$ pip install sakuragasaki46-suou[sqlalchemy]
$ pip install suou[sqlalchemy]
```
Please note that you probably already have those dependencies, if you just use the library.
@ -34,7 +34,7 @@ Read the [documentation](https://suou.readthedocs.io/).
### Disclaimer
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.
Just a heads up: SUOU was made to support yusurko (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.
@ -42,8 +42,6 @@ Expect breaking changes, disruptive renames in bugfix releases, sudden deprecati
Don't want to depend on my codebase for moral reasons (albeit unrelated)? It's fine. I did not ask you.
**DO NOT ASK TO MAKE SUOU SAFE FOR CHILDREN**. Enjoy having your fingers cut.
### "LTS"
The following versions are supported: the latest, the second-to-latest, 0.12.x and 0.7.x.
@ -54,7 +52,7 @@ Licensed under the [Apache License, Version 2.0](LICENSE), a non-copyleft free a
This is a hobby project, made available “AS IS”, with __no warranty__ express or implied.
I (sakuragasaki46) may NOT be held accountable for Your use of my code.
I (yusurko) may NOT be held accountable for Your use of my code.
> It's pointless to file a lawsuit because you feel damaged, and it's only going to turn against you. What a waste of money you could have spent on a vacation or charity, or invested in stocks.

View file

@ -37,13 +37,14 @@ from .redact import redact_url_password
from .http import WantsContentType
from .color import OKLabColor, chalk, WebColor, RGBColor, LinearRGBColor, XYZColor, OKLCHColor
from .mat import Matrix
from .argparse import LetterSubparsers
__version__ = "0.12.6"
__version__ = "0.13.0a1"
__all__ = (
'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue',
'DictConfigSource', 'EnvConfigSource', 'I18n', 'Incomplete', 'JsonI18n',
'LinearRGBColor',
'LetterSubparsers', 'LinearRGBColor',
'Matrix', 'MissingConfigError', 'MissingConfigWarning', 'OKLabColor', 'OKLCHColor',
'PrefixIdentifier', 'RGBColor',
'Siq', 'SiqCache', 'SiqGen', 'SiqType', 'Snowflake', 'SnowflakeGen',
@ -52,8 +53,8 @@ __all__ = (
'addattr', 'additem', 'age_and_days', 'alru_cache', 'b2048decode', 'b2048encode',
'b32ldecode', 'b32lencode', 'b64encode', 'b64decode', 'cb32encode',
'cb32decode', 'chalk', 'count_ones', 'dei_args', 'deprecated',
'future', 'ilex', 'join_bits',
'jsonencode', 'kwargs_prefix', 'lex', 'ltuple', 'makelist', 'mask_shift',
'future', 'ilex', 'join_bits', 'jsonencode', 'kwargs_prefix',
'lex', 'ltuple', 'makelist', 'mask_shift',
'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',

105
src/suou/argparse.py Normal file
View file

@ -0,0 +1,105 @@
"""
Utilities for parsing arguments. Based on argparse.
---
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 __future__ import annotations
import argparse
import sys
from typing import Callable, Mapping
class LetterSubparsers(object):
"""
Subparsers in pacman style, where action name can be shortened to a single letter, prefixed by a hyphen
(i.e. "-S" is expanded to "sync")
*New in 0.13.0*
"""
_parser: argparse.ArgumentParser
_letters: dict[str, str]
_subparsers: argparse._SubParsersAction[argparse.ArgumentParser]
def __init__(self, parser : argparse.ArgumentParser, *, dest: str = 'action', **kwargs):
self._parser = parser
self._letters = {}
self._subparsers = parser.add_subparsers(dest = dest, **kwargs)
def action(self, /, letter: str, name: str | None = None, **kwargs):
"""
Decorator which adds a subparser of an argument parser's subparsers, and specifies a letter to make a shorthand in pacman style.
For example, assuming name="sync" and letter="S", if the first argument is "-S", it will be turned to "sync", .
The first argument is always the object returned by ArgumentParser.add_subparsers().
Additional kwargs are passed to the add_parser constructor.
"""
if len(letter) != 1:
raise ValueError('letter must be one character')
o_name = name
def decorator(func: Callable[argparse.ArgumentParser, ...]):
name = o_name or func.__name__
parser = self._subparsers.add_parser(name, **kwargs)
func(parser)
self._letters[letter] = name
return func
return decorator
def parse_args(self, argv = None, system_exit: bool = True):
"""
Variation of ArgumentParser.parse_args() that takes shortcut letters into account.
Best used together with letter_action().
"""
if argv is None:
argv = sys.argv[1:]
else:
argv = list(argv)
if len(argv) > 0:
first_arg = argv.pop(0)
if first_arg.startswith('-') and len(first_arg) >= 2:
letter, rest = first_arg[1], first_arg[2:]
if letter in self._letters:
argv.insert(0, self._letters[letter])
if rest:
argv.insert(1, "-" + rest)
else:
# put it back
argv.insert(0, first_arg)
else:
# put it back
argv.insert(0, first_arg)
try:
return self._parser.parse_args(argv)
except SystemExit:
# prevent SystemExit at parse fail
if system_exit:
raise
__all__ = ('LetterSubparsers',)

View file

@ -154,7 +154,7 @@ def require_auth_base(cls: type[DeclarativeBase], *, src: AuthSrc, column: str |
return decorator
from .asyncio import SQLAlchemy, async_query
from .asyncio import SQLAlchemy, async_query, SessionWrapper, AsyncSelectPagination
from .orm import (
id_column, snowflake_column, match_column, match_constraint, bool_column, declarative_base, parent_children,
author_pair, age_pair, bound_fk, unbound_fk, want_column, a_relationship, BitSelector, secret_column, username_column
@ -168,7 +168,7 @@ except ImportError:
# Optional dependency: do not import into __init__.py
__all__ = (
'IdType', 'id_column', 'snowflake_column', 'entity_base', 'declarative_base', 'token_signer',
'IdType', 'id_column', 'snowflake_column', 'declarative_base', 'token_signer',
'match_column', 'match_constraint', 'bool_column', 'parent_children',
'author_pair', 'age_pair', 'bound_fk', 'unbound_fk', 'want_column',
'a_relationship', 'BitSelector', 'secret_column', 'username_column',

View file

@ -146,6 +146,8 @@ def declarative_base(domain_name: str, master_secret: bytes, metadata: dict | No
"""
Drop-in replacement for sqlalchemy.orm.declarative_base()
taking in account requirements for SIQ generation (i.e. domain name).
Also supports snowflake generation parameters such as epoch.
"""
if not isinstance(metadata, dict):
metadata = dict()
@ -160,7 +162,6 @@ def declarative_base(domain_name: str, master_secret: bytes, metadata: dict | No
)
Base = _declarative_base(metadata=MetaData(**metadata), **kwargs)
return Base
entity_base = warnings.deprecated('use declarative_base() instead')(declarative_base)