API refactor
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-07 16:25:52 +09:00
parent 76d0d86211
commit 91c7e04474
1171 changed files with 81940 additions and 44117 deletions

View File

@@ -1,34 +1,51 @@
# mysql/enumerated.py
# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
# dialects/mysql/enumerated.py
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
# mypy: ignore-errors
from __future__ import annotations
import enum
import re
from typing import Any
from typing import Dict
from typing import Optional
from typing import Set
from typing import Type
from typing import TYPE_CHECKING
from typing import Union
from .types import _StringType
from ... import exc
from ... import sql
from ... import util
from ...sql import sqltypes
from ...sql import type_api
if TYPE_CHECKING:
from ...engine.interfaces import Dialect
from ...sql.elements import ColumnElement
from ...sql.type_api import _BindProcessorType
from ...sql.type_api import _ResultProcessorType
from ...sql.type_api import TypeEngine
from ...sql.type_api import TypeEngineMixin
class ENUM(sqltypes.NativeForEmulated, sqltypes.Enum, _StringType):
class ENUM(type_api.NativeForEmulated, sqltypes.Enum, _StringType):
"""MySQL ENUM type."""
__visit_name__ = "ENUM"
native_enum = True
def __init__(self, *enums, **kw):
def __init__(self, *enums: Union[str, Type[enum.Enum]], **kw: Any) -> None:
"""Construct an ENUM.
E.g.::
Column('myenum', ENUM("foo", "bar", "baz"))
Column("myenum", ENUM("foo", "bar", "baz"))
:param enums: The range of valid values for this ENUM. Values in
enums are not quoted, they will be escaped and surrounded by single
@@ -62,21 +79,27 @@ class ENUM(sqltypes.NativeForEmulated, sqltypes.Enum, _StringType):
"""
kw.pop("strict", None)
self._enum_init(enums, kw)
self._enum_init(enums, kw) # type: ignore[arg-type]
_StringType.__init__(self, length=self.length, **kw)
@classmethod
def adapt_emulated_to_native(cls, impl, **kw):
def adapt_emulated_to_native(
cls,
impl: Union[TypeEngine[Any], TypeEngineMixin],
**kw: Any,
) -> ENUM:
"""Produce a MySQL native :class:`.mysql.ENUM` from plain
:class:`.Enum`.
"""
if TYPE_CHECKING:
assert isinstance(impl, ENUM)
kw.setdefault("validate_strings", impl.validate_strings)
kw.setdefault("values_callable", impl.values_callable)
kw.setdefault("omit_aliases", impl._omit_aliases)
return cls(**kw)
def _object_value_for_elem(self, elem):
def _object_value_for_elem(self, elem: str) -> Union[str, enum.Enum]:
# mysql sends back a blank string for any value that
# was persisted that was not in the enums; that is, it does no
# validation on the incoming data, it "truncates" it to be
@@ -86,24 +109,27 @@ class ENUM(sqltypes.NativeForEmulated, sqltypes.Enum, _StringType):
else:
return super()._object_value_for_elem(elem)
def __repr__(self):
def __repr__(self) -> str:
return util.generic_repr(
self, to_inspect=[ENUM, _StringType, sqltypes.Enum]
)
# TODO: SET is a string as far as configuration but does not act like
# a string at the python level. We either need to make a py-type agnostic
# version of String as a base to be used for this, make this some kind of
# TypeDecorator, or just vendor it out as its own type.
class SET(_StringType):
"""MySQL SET type."""
__visit_name__ = "SET"
def __init__(self, *values, **kw):
def __init__(self, *values: str, **kw: Any):
"""Construct a SET.
E.g.::
Column('myset', SET("foo", "bar", "baz"))
Column("myset", SET("foo", "bar", "baz"))
The list of potential values is required in the case that this
set will be used to generate DDL for a table, or if the
@@ -151,17 +177,19 @@ class SET(_StringType):
"setting retrieve_as_bitwise=True"
)
if self.retrieve_as_bitwise:
self._bitmap = {
self._inversed_bitmap: Dict[str, int] = {
value: 2**idx for idx, value in enumerate(self.values)
}
self._bitmap.update(
(2**idx, value) for idx, value in enumerate(self.values)
)
self._bitmap: Dict[int, str] = {
2**idx: value for idx, value in enumerate(self.values)
}
length = max([len(v) for v in values] + [0])
kw.setdefault("length", length)
super().__init__(**kw)
def column_expression(self, colexpr):
def column_expression(
self, colexpr: ColumnElement[Any]
) -> ColumnElement[Any]:
if self.retrieve_as_bitwise:
return sql.type_coerce(
sql.type_coerce(colexpr, sqltypes.Integer) + 0, self
@@ -169,10 +197,12 @@ class SET(_StringType):
else:
return colexpr
def result_processor(self, dialect, coltype):
def result_processor(
self, dialect: Dialect, coltype: Any
) -> Optional[_ResultProcessorType[Any]]:
if self.retrieve_as_bitwise:
def process(value):
def process(value: Union[str, int, None]) -> Optional[Set[str]]:
if value is not None:
value = int(value)
@@ -183,11 +213,14 @@ class SET(_StringType):
else:
super_convert = super().result_processor(dialect, coltype)
def process(value):
def process(value: Union[str, Set[str], None]) -> Optional[Set[str]]: # type: ignore[misc] # noqa: E501
if isinstance(value, str):
# MySQLdb returns a string, let's parse
if super_convert:
value = super_convert(value)
assert value is not None
if TYPE_CHECKING:
assert isinstance(value, str)
return set(re.findall(r"[^,]+", value))
else:
# mysql-connector-python does a naive
@@ -198,43 +231,48 @@ class SET(_StringType):
return process
def bind_processor(self, dialect):
def bind_processor(
self, dialect: Dialect
) -> _BindProcessorType[Union[str, int]]:
super_convert = super().bind_processor(dialect)
if self.retrieve_as_bitwise:
def process(value):
def process(
value: Union[str, int, set[str], None],
) -> Union[str, int, None]:
if value is None:
return None
elif isinstance(value, (int, str)):
if super_convert:
return super_convert(value)
return super_convert(value) # type: ignore[arg-type, no-any-return] # noqa: E501
else:
return value
else:
int_value = 0
for v in value:
int_value |= self._bitmap[v]
int_value |= self._inversed_bitmap[v]
return int_value
else:
def process(value):
def process(
value: Union[str, int, set[str], None],
) -> Union[str, int, None]:
# accept strings and int (actually bitflag) values directly
if value is not None and not isinstance(value, (int, str)):
value = ",".join(value)
if super_convert:
return super_convert(value)
return super_convert(value) # type: ignore
else:
return value
return process
def adapt(self, impltype, **kw):
def adapt(self, cls: type, **kw: Any) -> Any:
kw["retrieve_as_bitwise"] = self.retrieve_as_bitwise
return util.constructor_copy(self, impltype, *self.values, **kw)
return util.constructor_copy(self, cls, *self.values, **kw)
def __repr__(self):
def __repr__(self) -> str:
return util.generic_repr(
self,
to_inspect=[SET, _StringType],