0.14.0a5 add i4_column()

This commit is contained in:
Yusur 2026-07-11 22:43:00 +02:00
parent 9b4b57a391
commit f08af88821
5 changed files with 52 additions and 8 deletions

View file

@ -5,9 +5,10 @@
* Added `ast` module * Added `ast` module
* Deprecate `dei_args()` for problems with the typing system. The function is not going away tho * Deprecate `dei_args()` for problems with the typing system. The function is not going away tho
* Module `sqlalchemy`: * Module `sqlalchemy`:
* added `email_column()`, `ascii_column()` * added `email_column()`, `ascii_column()`, `i4_column()`
* added application level checks for `match_column()` * added application level checks for `match_column()`
* Added common values for snowflake epoch in `SnowflakeEpoch` enum * Added common values for snowflake epoch in `SnowflakeEpoch` enum
* Module `bitś`: added `i4_to_int()`, `int_to_i4()`
## 0.13.1 and 0.12.7 ## 0.13.1 and 0.12.7

View file

@ -21,7 +21,7 @@ from .iding import Siq, SiqCache, SiqType, SiqGen
from .codecs import (StringCase, cb32encode, cb32decode, b32lencode, b32ldecode, b64encode, b64decode, b2048encode, b2048decode, from .codecs import (StringCase, cb32encode, cb32decode, b32lencode, b32ldecode, b64encode, b64decode, b2048encode, b2048decode,
jsonencode, twocolon_list, want_bytes, want_str, ssv_list, want_urlsafe, want_urlsafe_bytes, jsonencode, twocolon_list, want_bytes, want_str, ssv_list, want_urlsafe, want_urlsafe_bytes,
z85encode, z85decode) z85encode, z85decode)
from .bits import count_ones, mask_shift, split_bits, join_bits, mod_ceil, mod_floor from .bits import count_ones, i4_to_int, int_to_i4, mask_shift, split_bits, join_bits, mod_ceil, mod_floor
from .calendar import want_datetime, want_isodate, want_timestamp, age_and_days from .calendar import want_datetime, want_isodate, want_timestamp, age_and_days
from .configparse import MissingConfigError, MissingConfigWarning, ConfigOptions, ConfigParserConfigSource, ConfigSource, DictConfigSource, ConfigValue, EnvConfigSource from .configparse import MissingConfigError, MissingConfigWarning, ConfigOptions, ConfigParserConfigSource, ConfigSource, DictConfigSource, ConfigValue, EnvConfigSource
from .collections import TimedDict from .collections import TimedDict
@ -41,7 +41,7 @@ from .color import OKLabColor, chalk, WebColor, RGBColor, LinearRGBColor, \
from .mat import Matrix from .mat import Matrix
from .argparse import LetterSubparsers from .argparse import LetterSubparsers
__version__ = "0.14.0a4" __version__ = "0.14.0a5"
__all__ = ( __all__ = (
'ColorFormatter', 'ColorFormatter',
@ -56,7 +56,7 @@ __all__ = (
'addattr', 'additem', 'age_and_days', 'alru_cache', 'b2048decode', 'b2048encode', 'addattr', 'additem', 'age_and_days', 'alru_cache', 'b2048decode', 'b2048encode',
'b32ldecode', 'b32lencode', 'b64encode', 'b64decode', 'cb32encode', 'b32ldecode', 'b32lencode', 'b64encode', 'b64decode', 'cb32encode',
'cb32decode', 'chalk', 'cooldown', 'count_ones', 'dei_args', 'deprecated', 'do_not_flood', 'cb32decode', 'chalk', 'cooldown', 'count_ones', 'dei_args', 'deprecated', 'do_not_flood',
'future', 'ilex', 'join_bits', 'jsonencode', 'kwargs_prefix', 'future', 'i4_to_int', 'int_to_i4', 'ilex', 'join_bits', 'jsonencode', 'kwargs_prefix',
'lex', 'ltuple', 'makelist', 'mask_shift', 'lex', 'ltuple', 'makelist', 'mask_shift',
'matches', 'mod_ceil', 'mod_floor', 'must_be', 'none_pass', 'not_implemented', 'matches', 'mod_ceil', 'mod_floor', 'must_be', 'none_pass', 'not_implemented',
'not_less_than', 'not_greater_than', 'not_less_than', 'not_greater_than',

View file

@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
''' '''
import math import math
import re
def mask_shift(n: int, mask: int) -> int: def mask_shift(n: int, mask: int) -> int:
''' '''
@ -108,4 +109,29 @@ def mod_ceil(x: int, y: int) -> int:
return x + (y - x % y) % y return x + (y - x % y) % y
__all__ = ('count_ones', 'mask_shift', 'split_bits', 'join_bits', 'mod_floor', 'mod_ceil') def i4_to_int(value):
if not (mo := re.fullmatch(r"([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})", value)):
raise ValueError
parts = [int(m) for m in mo.groups()]
for p in parts:
if p > 255 or p < 0:
raise ValueError
return (
(parts[0] << 24) |
(parts[1] << 16) |
(parts[2] << 8) |
parts[3]
)
def int_to_i4(value):
parts = (
(value >> 24) % 256,
(value >> 16) % 256,
(value >> 8) % 256,
value % 256
)
return '.'.join(f'{x}' for x in parts)
__all__ = ('count_ones', 'mask_shift', 'split_bits', 'join_bits', 'mod_floor', 'mod_ceil', 'i4_to_int', 'int_to_i4')

View file

@ -158,7 +158,7 @@ from .asyncio import SQLAlchemy, async_query, SessionWrapper, AsyncSelectPaginat
from .orm import ( from .orm import (
id_column, snowflake_column, match_column, match_constraint, bool_column, declarative_base, parent_children, 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, author_pair, age_pair, bound_fk, unbound_fk, want_column, a_relationship, BitSelector, secret_column, username_column,
ascii_column ascii_column, i4_column
) )
try: try:
@ -173,7 +173,7 @@ __all__ = (
'match_column', 'match_constraint', 'bool_column', 'parent_children', 'match_column', 'match_constraint', 'bool_column', 'parent_children',
'author_pair', 'age_pair', 'bound_fk', 'unbound_fk', 'want_column', 'author_pair', 'age_pair', 'bound_fk', 'unbound_fk', 'want_column',
'a_relationship', 'BitSelector', 'secret_column', 'username_column', 'a_relationship', 'BitSelector', 'secret_column', 'username_column',
'ascii_column', 'ascii_column', 'i4_column',
# .asyncio # .asyncio
'SQLAlchemy', 'AsyncSelectPagination', 'async_query', 'SessionWrapper' 'SQLAlchemy', 'AsyncSelectPagination', 'async_query', 'SessionWrapper'
) )

View file

@ -22,10 +22,11 @@ import os
import re import re
from typing import Any, Callable, TypeAlias, TypeVar from typing import Any, Callable, TypeAlias, TypeVar
import warnings import warnings
from sqlalchemy import VARCHAR, BigInteger, Boolean, CheckConstraint, Column, Date, ForeignKey, LargeBinary, MetaData, SmallInteger, String, TypeDecorator, text from sqlalchemy import BigInteger, Boolean, CheckConstraint, Column, Date, ForeignKey, Integer, LargeBinary, MetaData, SmallInteger, String, TypeDecorator, text
from sqlalchemy.orm import DeclarativeBase, InstrumentedAttribute, Relationship, declarative_base as _declarative_base, relationship from sqlalchemy.orm import DeclarativeBase, InstrumentedAttribute, Relationship, declarative_base as _declarative_base, relationship
from sqlalchemy.types import TypeEngine from sqlalchemy.types import TypeEngine
from sqlalchemy.ext.hybrid import Comparator from sqlalchemy.ext.hybrid import Comparator
from suou.bits import i4_to_int, int_to_i4
from suou.classtools import Wanted, Incomplete from suou.classtools import Wanted, Incomplete
from suou.codecs import StringCase from suou.codecs import StringCase
from suou.iding import Siq, SiqCache, SiqGen, SiqType from suou.iding import Siq, SiqCache, SiqGen, SiqType
@ -125,6 +126,15 @@ class AsciiString(TypeDecorator):
raise ValueError('only ASCII strings are allowed') raise ValueError('only ASCII strings are allowed')
return value return value
class I4Addr(TypeDecorator):
impl: TypeAlias = Integer
def process_bind_param(self, value, dialect):
return i4_to_int(value)
def process_result_value(self, value, dialect):
return int_to_i4(value)
## END type decorators ## END type decorators
@ -185,6 +195,13 @@ def email_column(
unique = unique, nullable = nullable, *args, **kwargs) unique = unique, nullable = nullable, *args, **kwargs)
def i4_column(*args, nullable : bool = False, **kwargs):
"""
*New in 0.14.0*
"""
return Column(I4Addr, nullable=nullable, *args, **kwargs)
def bool_column(value: bool = False, nullable: bool = False, **kwargs) -> Column[bool]: def bool_column(value: bool = False, nullable: bool = False, **kwargs) -> Column[bool]:
""" """
Column for a single boolean value. Column for a single boolean value.