0.7.3 fix imports (?) in .sqlalchemy, add experimental .glue, docs for .sqlalchemy

This commit is contained in:
Yusur 2025-10-18 14:48:32 +02:00
parent 7e80c84de6
commit 10e6c202f0
9 changed files with 184 additions and 70 deletions

View file

@ -1,5 +1,11 @@
# Changelog # Changelog
## 0.7.3
+ Fixed some broken imports in `.sqlalchemy`
+ Stage `@glue()` for release in 0.8.0
+ Add docs to `.sqlalchemy`
## 0.7.2 ## 0.7.2
+ `@future()` now can take a `version=` argument + `@future()` now can take a `version=` argument

View file

@ -10,7 +10,7 @@ license = "Apache-2.0"
readme = "README.md" readme = "README.md"
dependencies = [ dependencies = [
"suou==0.7.1", "suou==0.7.2",
"itsdangerous", "itsdangerous",
"toml", "toml",
"pydantic", "pydantic",
@ -28,7 +28,8 @@ classifiers = [
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13" "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14"
] ]
[project.urls] [project.urls]

View file

@ -1,4 +1,4 @@
suou.sqlalchemy.orm suou.sqlalchemy.orm
=================== ===================
.. automodule:: suou.sqlalchemy.orm .. automodule:: suou.sqlalchemy.orm
@ -22,6 +22,7 @@ suou.sqlalchemy.orm
secret_column secret_column
snowflake_column snowflake_column
unbound_fk unbound_fk
username_column
want_column want_column
.. rubric:: Classes .. rubric:: Classes

View file

@ -1,4 +1,4 @@
suou.waiter suou.waiter
=========== ===========
.. automodule:: suou.waiter .. automodule:: suou.waiter
@ -8,12 +8,7 @@ suou.waiter
.. autosummary:: .. autosummary::
Waiter
ko ko
ok ok
.. rubric:: Classes
.. autosummary::
Waiter

View file

@ -3,16 +3,15 @@
You can adapt this file completely to your liking, but it should at least You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive. contain the root `toctree` directive.
suou documentation SUOU
================== ==================
SUOU (acronym for ) is a casual Python library providing utilities to SUOU (acronym for **SIS Unified Object Underarmour**) is a casual Python library providing utilities to
ease programmer's QoL. ease programmer's QoL and write shorter and cleaner code that works.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
sqlalchemy
api api

45
docs/sqlalchemy.rst Normal file
View file

@ -0,0 +1,45 @@
sqlalchemy helpers
==================
.. currentmodule:: suou.sqlalchemy
SUOU provides several helpers to make sqlalchemy learning curve less steep.
In fact, there are pre-made column presets for a specific purpose.
Columns
-------
.. autofunction:: id_column
.. warning::
``id_column()`` expects SIQ's!
.. autofunction:: snowflake_column
.. autofunction:: match_column
.. autofunction:: secret_column
.. autofunction:: bool_column
.. autofunction:: unbound_fk
.. autofunction:: bound_fk
Column pairs
------------
.. autofunction:: age_pair
.. autofunction:: author_pair
.. autofunction:: parent_children
Misc
----
.. autofunction:: BitSelector
.. autofunction:: match_constraint
.. autofunction:: a_relationship
.. autofunction:: declarative_base
.. autofunction:: want_column

View file

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

59
src/suou/glue.py Normal file
View file

@ -0,0 +1,59 @@
"""
Helpers for "Glue" code, aka code meant to adapt or patch other libraries
---
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.
"""
import importlib
from types import ModuleType
from functools import wraps
from suou.functools import future
class FakeModule(ModuleType):
"""
Fake module used in @glue() in case of import error
"""
def __init__(self, name: str, exc: Exception):
super().__init__(name)
self._exc = exc
def __getattr__(self, name: str):
raise AttributeError(f'Module {self.__name__} not found; this feature is not available ({self._exc})') from self._exc
@future(version = "0.8.0")
def glue(*modules):
"""
Helper for "glue" code -- it imports the given modules and passes them as keyword arguments to the wrapped functions.
NEW 0.8.0
"""
module_dict = dict()
for module in modules:
try:
module_dict[module] = importlib.import_module(module)
except Exception as e:
module_dict[module] = FakeModule(module, e)
def decorator(func):
@wraps(func)
def wrapper(*a, **k):
k.update(module_dict)
return func(*a, **k)
return wrapper
return decorator
# This module is experimental and therefore not re-exported into __init__
__all__ = ('glue',)

View file

@ -25,9 +25,9 @@ from typing import Callable, TypeVar
from sqlalchemy import Select, Table, func, select from sqlalchemy import Select, Table, func, select
from sqlalchemy.orm import DeclarativeBase, lazyload from sqlalchemy.orm import DeclarativeBase, lazyload
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
from flask_sqlalchemy.pagination import Pagination
from suou.exceptions import NotFoundError from suou.exceptions import NotFoundError
from suou.glue import glue
_T = TypeVar('_T') _T = TypeVar('_T')
_U = TypeVar('_U') _U = TypeVar('_U')
@ -119,7 +119,10 @@ class SQLAlchemy:
# XXX NOT public API! DO NOT USE # XXX NOT public API! DO NOT USE
current_session: ContextVar[AsyncSession] = ContextVar('current_session') current_session: ContextVar[AsyncSession] = ContextVar('current_session')
class AsyncSelectPagination(Pagination): ## experimental
@glue('flask_sqlalchemy')
def _make_AsyncSelectPagination(flask_sqlalchemy):
class AsyncSelectPagination(flask_sqlalchemy.pagination.Pagination):
""" """
flask_sqlalchemy.SelectPagination but asynchronous. flask_sqlalchemy.SelectPagination but asynchronous.
@ -182,6 +185,11 @@ class AsyncSelectPagination(Pagination):
for i in self.items: for i in self.items:
yield i yield i
return AsyncSelectPagination
AsyncSelectPagination = _make_AsyncSelectPagination()
del _make_AsyncSelectPagination
def async_query(db: SQLAlchemy, multi: False): def async_query(db: SQLAlchemy, multi: False):
""" """