添加注册登录功能

This commit is contained in:
2025-08-29 00:34:40 +08:00
parent 09065f2ce7
commit 2fe3474d9e
3060 changed files with 29217 additions and 87137 deletions

View File

@@ -32,9 +32,9 @@ def _distill_params_20(
# Assume list is more likely than tuple
elif isinstance(params, list) or isinstance(params, tuple):
# collections_abc.MutableSequence): # avoid abc.__instancecheck__
if params and not isinstance(params[0], (tuple, Mapping)):
if params and not isinstance(params[0], Mapping):
raise exc.ArgumentError(
"List argument must consist only of tuples or dictionaries"
"List argument must consist only of dictionaries"
)
return params

View File

@@ -4,9 +4,7 @@
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
"""Defines :class:`_engine.Connection` and :class:`_engine.Engine`.
"""
"""Defines :class:`_engine.Connection` and :class:`_engine.Engine`."""
from __future__ import annotations
import contextlib
@@ -70,12 +68,11 @@ if typing.TYPE_CHECKING:
from ..sql._typing import _InfoType
from ..sql.compiler import Compiled
from ..sql.ddl import ExecutableDDLElement
from ..sql.ddl import SchemaDropper
from ..sql.ddl import SchemaGenerator
from ..sql.ddl import InvokeDDLBase
from ..sql.functions import FunctionElement
from ..sql.schema import DefaultGenerator
from ..sql.schema import HasSchemaAttr
from ..sql.schema import SchemaItem
from ..sql.schema import SchemaVisitable
from ..sql.selectable import TypedReturnsRows
@@ -1115,10 +1112,16 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
if self._still_open_and_dbapi_connection_is_valid:
if self._echo:
if self._is_autocommit_isolation():
self._log_info(
"ROLLBACK using DBAPI connection.rollback(), "
"DBAPI should ignore due to autocommit mode"
)
if self.dialect.skip_autocommit_rollback:
self._log_info(
"ROLLBACK will be skipped by "
"skip_autocommit_rollback"
)
else:
self._log_info(
"ROLLBACK using DBAPI connection.rollback(); "
"set skip_autocommit_rollback to prevent fully"
)
else:
self._log_info("ROLLBACK")
try:
@@ -1134,7 +1137,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
if self._is_autocommit_isolation():
self._log_info(
"COMMIT using DBAPI connection.commit(), "
"DBAPI should ignore due to autocommit mode"
"has no effect due to autocommit mode"
)
else:
self._log_info("COMMIT")
@@ -2428,9 +2431,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
break
if sqlalchemy_exception and is_disconnect != ctx.is_disconnect:
sqlalchemy_exception.connection_invalidated = is_disconnect = (
ctx.is_disconnect
)
sqlalchemy_exception.connection_invalidated = ctx.is_disconnect
if newraise:
raise newraise.with_traceback(exc_info[2]) from e
@@ -2443,8 +2444,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
def _run_ddl_visitor(
self,
visitorcallable: Type[Union[SchemaGenerator, SchemaDropper]],
element: SchemaItem,
visitorcallable: Type[InvokeDDLBase],
element: SchemaVisitable,
**kwargs: Any,
) -> None:
"""run a DDL visitor.
@@ -2453,7 +2454,9 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
options given to the visitor so that "checkfirst" is skipped.
"""
visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
visitorcallable(
dialect=self.dialect, connection=self, **kwargs
).traverse_single(element)
class ExceptionContextImpl(ExceptionContext):
@@ -3241,8 +3244,8 @@ class Engine(
def _run_ddl_visitor(
self,
visitorcallable: Type[Union[SchemaGenerator, SchemaDropper]],
element: SchemaItem,
visitorcallable: Type[InvokeDDLBase],
element: SchemaVisitable,
**kwargs: Any,
) -> None:
with self.begin() as conn:

View File

@@ -468,6 +468,9 @@ def create_engine(url: Union[str, _url.URL], **kwargs: Any) -> Engine:
:ref:`pool_reset_on_return`
:ref:`dbapi_autocommit_skip_rollback` - a more modern approach
to using connections with no transactional instructions
:param pool_timeout=30: number of seconds to wait before giving
up on getting a connection from the pool. This is only used
with :class:`~sqlalchemy.pool.QueuePool`. This can be a float but is
@@ -524,6 +527,18 @@ def create_engine(url: Union[str, _url.URL], **kwargs: Any) -> Engine:
.. versionadded:: 1.4
:param skip_autocommit_rollback: When True, the dialect will
unconditionally skip all calls to the DBAPI ``connection.rollback()``
method if the DBAPI connection is confirmed to be in "autocommit" mode.
The availability of this feature is dialect specific; if not available,
a ``NotImplementedError`` is raised by the dialect when rollback occurs.
.. seealso::
:ref:`dbapi_autocommit_skip_rollback`
.. versionadded:: 2.0.43
:param use_insertmanyvalues: True by default, use the "insertmanyvalues"
execution style for INSERT..RETURNING statements by default.

View File

@@ -20,6 +20,7 @@ from typing import Any
from typing import cast
from typing import ClassVar
from typing import Dict
from typing import Iterable
from typing import Iterator
from typing import List
from typing import Mapping
@@ -1289,12 +1290,16 @@ class FullyBufferedCursorFetchStrategy(CursorFetchStrategy):
__slots__ = ("_rowbuffer", "alternate_cursor_description")
def __init__(
self, dbapi_cursor, alternate_description=None, initial_buffer=None
self,
dbapi_cursor: Optional[DBAPICursor],
alternate_description: Optional[_DBAPICursorDescription] = None,
initial_buffer: Optional[Iterable[Any]] = None,
):
self.alternate_cursor_description = alternate_description
if initial_buffer is not None:
self._rowbuffer = collections.deque(initial_buffer)
else:
assert dbapi_cursor is not None
self._rowbuffer = collections.deque(dbapi_cursor.fetchall())
def yield_per(self, result, dbapi_cursor, num):
@@ -1353,15 +1358,15 @@ class _NoResultMetaData(ResultMetaData):
self._we_dont_return_rows()
@property
def _keymap(self):
def _keymap(self): # type: ignore[override]
self._we_dont_return_rows()
@property
def _key_to_index(self):
def _key_to_index(self): # type: ignore[override]
self._we_dont_return_rows()
@property
def _processors(self):
def _processors(self): # type: ignore[override]
self._we_dont_return_rows()
@property

View File

@@ -77,10 +77,13 @@ if typing.TYPE_CHECKING:
from .interfaces import _CoreSingleExecuteParams
from .interfaces import _DBAPICursorDescription
from .interfaces import _DBAPIMultiExecuteParams
from .interfaces import _DBAPISingleExecuteParams
from .interfaces import _ExecuteOptions
from .interfaces import _MutableCoreSingleExecuteParams
from .interfaces import _ParamStyle
from .interfaces import ConnectArgsType
from .interfaces import DBAPIConnection
from .interfaces import DBAPIModule
from .interfaces import IsolationLevel
from .row import Row
from .url import URL
@@ -99,6 +102,7 @@ if typing.TYPE_CHECKING:
from ..sql.type_api import _ResultProcessorType
from ..sql.type_api import TypeEngine
# When we're handed literal SQL, ensure it's a SELECT query
SERVER_SIDE_CURSOR_RE = re.compile(r"\s*SELECT", re.I | re.UNICODE)
@@ -303,6 +307,7 @@ class DefaultDialect(Dialect):
# Linting.NO_LINTING constant
compiler_linting: Linting = int(compiler.NO_LINTING), # type: ignore
server_side_cursors: bool = False,
skip_autocommit_rollback: bool = False,
**kwargs: Any,
):
if server_side_cursors:
@@ -327,6 +332,8 @@ class DefaultDialect(Dialect):
self.dbapi = dbapi
self.skip_autocommit_rollback = skip_autocommit_rollback
if paramstyle is not None:
self.paramstyle = paramstyle
elif self.dbapi is not None:
@@ -425,7 +432,7 @@ class DefaultDialect(Dialect):
delete_executemany_returning = False
@util.memoized_property
def loaded_dbapi(self) -> ModuleType:
def loaded_dbapi(self) -> DBAPIModule:
if self.dbapi is None:
raise exc.InvalidRequestError(
f"Dialect {self} does not have a Python DBAPI established "
@@ -437,7 +444,7 @@ class DefaultDialect(Dialect):
def _bind_typing_render_casts(self):
return self.bind_typing is interfaces.BindTyping.RENDER_CASTS
def _ensure_has_table_connection(self, arg):
def _ensure_has_table_connection(self, arg: Connection) -> None:
if not isinstance(arg, Connection):
raise exc.ArgumentError(
"The argument passed to Dialect.has_table() should be a "
@@ -474,7 +481,7 @@ class DefaultDialect(Dialect):
return weakref.WeakKeyDictionary()
@property
def dialect_description(self):
def dialect_description(self): # type: ignore[override]
return self.name + "+" + self.driver
@property
@@ -515,7 +522,7 @@ class DefaultDialect(Dialect):
else:
return None
def initialize(self, connection):
def initialize(self, connection: Connection) -> None:
try:
self.server_version_info = self._get_server_version_info(
connection
@@ -551,7 +558,7 @@ class DefaultDialect(Dialect):
% (self.label_length, self.max_identifier_length)
)
def on_connect(self):
def on_connect(self) -> Optional[Callable[[Any], None]]:
# inherits the docstring from interfaces.Dialect.on_connect
return None
@@ -610,18 +617,18 @@ class DefaultDialect(Dialect):
) -> bool:
return schema_name in self.get_schema_names(connection, **kw)
def validate_identifier(self, ident):
def validate_identifier(self, ident: str) -> None:
if len(ident) > self.max_identifier_length:
raise exc.IdentifierError(
"Identifier '%s' exceeds maximum length of %d characters"
% (ident, self.max_identifier_length)
)
def connect(self, *cargs, **cparams):
def connect(self, *cargs: Any, **cparams: Any) -> DBAPIConnection:
# inherits the docstring from interfaces.Dialect.connect
return self.loaded_dbapi.connect(*cargs, **cparams)
return self.loaded_dbapi.connect(*cargs, **cparams) # type: ignore[no-any-return] # NOQA: E501
def create_connect_args(self, url):
def create_connect_args(self, url: URL) -> ConnectArgsType:
# inherits the docstring from interfaces.Dialect.create_connect_args
opts = url.translate_connect_args()
opts.update(url.query)
@@ -697,6 +704,10 @@ class DefaultDialect(Dialect):
pass
def do_rollback(self, dbapi_connection):
if self.skip_autocommit_rollback and self.detect_autocommit_setting(
dbapi_connection
):
return
dbapi_connection.rollback()
def do_commit(self, dbapi_connection):
@@ -736,8 +747,6 @@ class DefaultDialect(Dialect):
raise
def do_ping(self, dbapi_connection: DBAPIConnection) -> bool:
cursor = None
cursor = dbapi_connection.cursor()
try:
cursor.execute(self._dialect_specific_select_one)
@@ -944,7 +953,14 @@ class DefaultDialect(Dialect):
def do_execute_no_params(self, cursor, statement, context=None):
cursor.execute(statement)
def is_disconnect(self, e, connection, cursor):
def is_disconnect(
self,
e: DBAPIModule.Error,
connection: Union[
pool.PoolProxiedConnection, interfaces.DBAPIConnection, None
],
cursor: Optional[interfaces.DBAPICursor],
) -> bool:
return False
@util.memoized_instancemethod
@@ -1044,7 +1060,7 @@ class DefaultDialect(Dialect):
name = name_upper
return name
def get_driver_connection(self, connection):
def get_driver_connection(self, connection: DBAPIConnection) -> Any:
return connection
def _overrides_default(self, method):
@@ -1618,7 +1634,7 @@ class DefaultExecutionContext(ExecutionContext):
return "unknown"
@property
def executemany(self):
def executemany(self): # type: ignore[override]
return self.execute_style in (
ExecuteStyle.EXECUTEMANY,
ExecuteStyle.INSERTMANYVALUES,
@@ -1660,7 +1676,12 @@ class DefaultExecutionContext(ExecutionContext):
def no_parameters(self):
return self.execution_options.get("no_parameters", False)
def _execute_scalar(self, stmt, type_, parameters=None):
def _execute_scalar(
self,
stmt: str,
type_: Optional[TypeEngine[Any]],
parameters: Optional[_DBAPISingleExecuteParams] = None,
) -> Any:
"""Execute a string statement on the current cursor, returning a
scalar result.
@@ -1734,7 +1755,7 @@ class DefaultExecutionContext(ExecutionContext):
return use_server_side
def create_cursor(self):
def create_cursor(self) -> DBAPICursor:
if (
# inlining initial preference checks for SS cursors
self.dialect.supports_server_side_cursors
@@ -1755,10 +1776,10 @@ class DefaultExecutionContext(ExecutionContext):
def fetchall_for_returning(self, cursor):
return cursor.fetchall()
def create_default_cursor(self):
def create_default_cursor(self) -> DBAPICursor:
return self._dbapi_connection.cursor()
def create_server_side_cursor(self):
def create_server_side_cursor(self) -> DBAPICursor:
raise NotImplementedError()
def pre_exec(self):
@@ -1827,9 +1848,10 @@ class DefaultExecutionContext(ExecutionContext):
if self._rowcount is None and exec_opt.get("preserve_rowcount", False):
self._rowcount = self.cursor.rowcount
yp: Optional[Union[int, bool]]
if self.is_crud or self.is_text:
result = self._setup_dml_or_text_result()
yp = sr = False
yp = False
else:
yp = exec_opt.get("yield_per", None)
sr = self._is_server_side or exec_opt.get("stream_results", False)

View File

@@ -10,7 +10,6 @@
from __future__ import annotations
from enum import Enum
from types import ModuleType
from typing import Any
from typing import Awaitable
from typing import Callable
@@ -34,7 +33,7 @@ from typing import Union
from .. import util
from ..event import EventTarget
from ..pool import Pool
from ..pool import PoolProxiedConnection
from ..pool import PoolProxiedConnection as PoolProxiedConnection
from ..sql.compiler import Compiled as Compiled
from ..sql.compiler import Compiled # noqa
from ..sql.compiler import TypeCompiler as TypeCompiler
@@ -51,6 +50,7 @@ if TYPE_CHECKING:
from .base import Engine
from .cursor import CursorResult
from .url import URL
from ..connectors.asyncio import AsyncIODBAPIConnection
from ..event import _ListenerFnType
from ..event import dispatcher
from ..exc import StatementError
@@ -70,6 +70,7 @@ if TYPE_CHECKING:
from ..sql.sqltypes import Integer
from ..sql.type_api import _TypeMemoDict
from ..sql.type_api import TypeEngine
from ..util.langhelpers import generic_fn_descriptor
ConnectArgsType = Tuple[Sequence[str], MutableMapping[str, Any]]
@@ -106,6 +107,22 @@ class ExecuteStyle(Enum):
"""
class DBAPIModule(Protocol):
class Error(Exception):
def __getattr__(self, key: str) -> Any: ...
class OperationalError(Error):
pass
class InterfaceError(Error):
pass
class IntegrityError(Error):
pass
def __getattr__(self, key: str) -> Any: ...
class DBAPIConnection(Protocol):
"""protocol representing a :pep:`249` database connection.
@@ -122,11 +139,13 @@ class DBAPIConnection(Protocol):
def commit(self) -> None: ...
def cursor(self) -> DBAPICursor: ...
def cursor(self, *args: Any, **kwargs: Any) -> DBAPICursor: ...
def rollback(self) -> None: ...
autocommit: bool
def __getattr__(self, key: str) -> Any: ...
def __setattr__(self, key: str, value: Any) -> None: ...
class DBAPIType(Protocol):
@@ -658,7 +677,7 @@ class Dialect(EventTarget):
dialect_description: str
dbapi: Optional[ModuleType]
dbapi: Optional[DBAPIModule]
"""A reference to the DBAPI module object itself.
SQLAlchemy dialects import DBAPI modules using the classmethod
@@ -682,7 +701,7 @@ class Dialect(EventTarget):
"""
@util.non_memoized_property
def loaded_dbapi(self) -> ModuleType:
def loaded_dbapi(self) -> DBAPIModule:
"""same as .dbapi, but is never None; will raise an error if no
DBAPI was set up.
@@ -760,6 +779,14 @@ class Dialect(EventTarget):
default_isolation_level: Optional[IsolationLevel]
"""the isolation that is implicitly present on new connections"""
skip_autocommit_rollback: bool
"""Whether or not the :paramref:`.create_engine.skip_autocommit_rollback`
parameter was set.
.. versionadded:: 2.0.43
"""
# create_engine() -> isolation_level currently goes here
_on_connect_isolation_level: Optional[IsolationLevel]
@@ -779,8 +806,14 @@ class Dialect(EventTarget):
max_identifier_length: int
"""The maximum length of identifier names."""
max_index_name_length: Optional[int]
"""The maximum length of index names if different from
``max_identifier_length``."""
max_constraint_name_length: Optional[int]
"""The maximum length of constraint names if different from
``max_identifier_length``."""
supports_server_side_cursors: bool
supports_server_side_cursors: Union[generic_fn_descriptor[bool], bool]
"""indicates if the dialect supports server side cursors"""
server_side_cursors: bool
@@ -1233,7 +1266,7 @@ class Dialect(EventTarget):
raise NotImplementedError()
@classmethod
def import_dbapi(cls) -> ModuleType:
def import_dbapi(cls) -> DBAPIModule:
"""Import the DBAPI module that is used by this dialect.
The Python module object returned here will be assigned as an
@@ -1282,8 +1315,6 @@ class Dialect(EventTarget):
"""
pass
if TYPE_CHECKING:
def _overrides_default(self, method_name: str) -> bool: ...
@@ -2205,7 +2236,7 @@ class Dialect(EventTarget):
def is_disconnect(
self,
e: Exception,
e: DBAPIModule.Error,
connection: Optional[Union[PoolProxiedConnection, DBAPIConnection]],
cursor: Optional[DBAPICursor],
) -> bool:
@@ -2309,7 +2340,7 @@ class Dialect(EventTarget):
"""
return self.on_connect()
def on_connect(self) -> Optional[Callable[[Any], Any]]:
def on_connect(self) -> Optional[Callable[[Any], None]]:
"""return a callable which sets up a newly created DBAPI connection.
The callable should accept a single argument "conn" which is the
@@ -2458,6 +2489,30 @@ class Dialect(EventTarget):
raise NotImplementedError()
def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
"""Detect the current autocommit setting for a DBAPI connection.
:param dbapi_connection: a DBAPI connection object
:return: True if autocommit is enabled, False if disabled
:rtype: bool
This method inspects the given DBAPI connection to determine
whether autocommit mode is currently enabled. The specific
mechanism for detecting autocommit varies by database dialect
and DBAPI driver, however it should be done **without** network
round trips.
.. note::
Not all dialects support autocommit detection. Dialects
that do not support this feature will raise
:exc:`NotImplementedError`.
"""
raise NotImplementedError(
"This dialect cannot detect autocommit on a DBAPI connection"
)
def get_default_isolation_level(
self, dbapi_conn: DBAPIConnection
) -> IsolationLevel:
@@ -2482,7 +2537,7 @@ class Dialect(EventTarget):
def get_isolation_level_values(
self, dbapi_conn: DBAPIConnection
) -> List[IsolationLevel]:
) -> Sequence[IsolationLevel]:
"""return a sequence of string isolation level names that are accepted
by this dialect.
@@ -2656,6 +2711,9 @@ class Dialect(EventTarget):
"""return a Pool class to use for a given URL"""
raise NotImplementedError()
def validate_identifier(self, ident: str) -> None:
"""Validates an identifier name, raising an exception if invalid"""
class CreateEnginePlugin:
"""A set of hooks intended to augment the construction of an
@@ -3363,7 +3421,7 @@ class AdaptedConnection:
__slots__ = ("_connection",)
_connection: Any
_connection: AsyncIODBAPIConnection
@property
def driver_connection(self) -> Any:

View File

@@ -27,10 +27,9 @@ if typing.TYPE_CHECKING:
from .interfaces import Dialect
from .url import URL
from ..sql.base import Executable
from ..sql.ddl import SchemaDropper
from ..sql.ddl import SchemaGenerator
from ..sql.ddl import InvokeDDLBase
from ..sql.schema import HasSchemaAttr
from ..sql.schema import SchemaItem
from ..sql.visitors import Visitable
class MockConnection:
@@ -53,12 +52,14 @@ class MockConnection:
def _run_ddl_visitor(
self,
visitorcallable: Type[Union[SchemaGenerator, SchemaDropper]],
element: SchemaItem,
visitorcallable: Type[InvokeDDLBase],
element: Visitable,
**kwargs: Any,
) -> None:
kwargs["checkfirst"] = False
visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
visitorcallable(
dialect=self.dialect, connection=self, **kwargs
).traverse_single(element)
def execute(
self,

View File

@@ -1714,9 +1714,12 @@ class Inspector(inspection.Inspectable["Inspector"]):
if pk in cols_by_orig_name and pk not in exclude_columns
]
# update pk constraint name and comment
# update pk constraint name, comment and dialect_kwargs
table.primary_key.name = pk_cons.get("name")
table.primary_key.comment = pk_cons.get("comment", None)
dialect_options = pk_cons.get("dialect_options")
if dialect_options:
table.primary_key.dialect_kwargs.update(dialect_options)
# tell the PKConstraint to re-initialize
# its column collection

View File

@@ -52,11 +52,11 @@ else:
from sqlalchemy.cyextension.resultproxy import tuplegetter as tuplegetter
if typing.TYPE_CHECKING:
from ..sql.schema import Column
from ..sql.elements import SQLCoreOperations
from ..sql.type_api import _ResultProcessorType
_KeyType = Union[str, "Column[Any]"]
_KeyIndexType = Union[str, "Column[Any]", int]
_KeyType = Union[str, "SQLCoreOperations[Any]"]
_KeyIndexType = Union[_KeyType, int]
# is overridden in cursor using _CursorKeyMapRecType
_KeyMapRecType = Any
@@ -722,6 +722,14 @@ class ResultInternal(InPlaceGenerative, Generic[_R]):
return manyrows
@overload
def _only_one_row(
self: ResultInternal[Row[Any]],
raise_for_second_row: bool,
raise_for_none: bool,
scalar: Literal[True],
) -> Any: ...
@overload
def _only_one_row(
self,
@@ -809,7 +817,6 @@ class ResultInternal(InPlaceGenerative, Generic[_R]):
"was required"
)
else:
next_row = _NO_ROW
# if we checked for second row then that would have
# closed us :)
self._soft_close(hard=True)
@@ -1489,8 +1496,8 @@ class Result(_WithKeys, ResultInternal[Row[_TP]]):
def one(self) -> Row[_TP]:
"""Return exactly one row or raise an exception.
Raises :class:`.NoResultFound` if the result returns no
rows, or :class:`.MultipleResultsFound` if multiple rows
Raises :class:`_exc.NoResultFound` if the result returns no
rows, or :class:`_exc.MultipleResultsFound` if multiple rows
would be returned.
.. note:: This method returns one **row**, e.g. tuple, by default.
@@ -2006,7 +2013,7 @@ class MappingResult(_WithKeys, FilterResult[RowMapping]):
return self
def columns(self, *col_expressions: _KeyIndexType) -> Self:
r"""Establish the columns that should be returned in each row."""
"""Establish the columns that should be returned in each row."""
return self._column_slices(col_expressions)
def partitions(

View File

@@ -5,10 +5,7 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
"""Deprecated mock engine strategy used by Alembic.
"""
"""Deprecated mock engine strategy used by Alembic."""
from __future__ import annotations

View File

@@ -920,5 +920,5 @@ def _parse_url(name: str) -> URL:
else:
raise exc.ArgumentError(
"Could not parse SQLAlchemy URL from string '%s'" % name
"Could not parse SQLAlchemy URL from given URL string"
)