Загрузить файлы в «venv/Lib/site-packages/sqlalchemy/util»

This commit is contained in:
2026-07-02 20:44:03 +00:00
parent e76eb4697e
commit b968b71d9a
5 changed files with 3288 additions and 0 deletions

View File

@@ -0,0 +1,420 @@
# util/compat.py
# Copyright (C) 2005-2026 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: allow-untyped-defs, allow-untyped-calls
"""Handle Python version/platform incompatibilities."""
from __future__ import annotations
import base64
import dataclasses
import hashlib
import inspect
import operator
import platform
import sys
import sysconfig
import typing
from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
from typing import Iterable
from typing import List
from typing import Mapping
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Tuple
from typing import Type
from typing import TypeVar
py314b1 = sys.version_info >= (3, 14, 0, "beta", 1)
py314 = sys.version_info >= (3, 14)
py313 = sys.version_info >= (3, 13)
py312 = sys.version_info >= (3, 12)
py311 = sys.version_info >= (3, 11)
py310 = sys.version_info >= (3, 10)
py39 = sys.version_info >= (3, 9)
py38 = sys.version_info >= (3, 8)
pypy = platform.python_implementation() == "PyPy"
cpython = platform.python_implementation() == "CPython"
freethreading = bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
win32 = sys.platform.startswith("win")
osx = sys.platform.startswith("darwin")
arm = "aarch" in platform.machine().lower()
is64bit = sys.maxsize > 2**32
has_refcount_gc = bool(cpython)
dottedgetter = operator.attrgetter
_T_co = TypeVar("_T_co", covariant=True)
if py314:
# vendor a minimal form of get_annotations per
# https://github.com/python/cpython/issues/133684#issuecomment-2863841891
from annotationlib import call_annotate_function # type: ignore[import-not-found,unused-ignore] # noqa: E501
from annotationlib import Format
def _get_and_call_annotate(obj, format): # noqa: A002
annotate = getattr(obj, "__annotate__", None)
if annotate is not None:
ann = call_annotate_function(annotate, format, owner=obj)
if not isinstance(ann, dict):
raise ValueError(f"{obj!r}.__annotate__ returned a non-dict")
return ann
return None
# this is ported from py3.13.0a7
_BASE_GET_ANNOTATIONS = type.__dict__["__annotations__"].__get__
def _get_dunder_annotations(obj):
if isinstance(obj, type):
try:
ann = _BASE_GET_ANNOTATIONS(obj)
except AttributeError:
# For static types, the descriptor raises AttributeError.
return {}
else:
ann = getattr(obj, "__annotations__", None)
if ann is None:
return {}
if not isinstance(ann, dict):
raise ValueError(
f"{obj!r}.__annotations__ is neither a dict nor None"
)
return dict(ann)
def _vendored_get_annotations(
obj: Any, *, format: Format # noqa: A002
) -> Mapping[str, Any]:
"""A sparse implementation of annotationlib.get_annotations()"""
try:
ann = _get_dunder_annotations(obj)
except Exception:
pass
else:
if ann is not None:
return dict(ann)
# But if __annotations__ threw a NameError, we try calling __annotate__
ann = _get_and_call_annotate(obj, format)
if ann is None:
# If that didn't work either, we have a very weird object:
# evaluating
# __annotations__ threw NameError and there is no __annotate__.
# In that case,
# we fall back to trying __annotations__ again.
ann = _get_dunder_annotations(obj)
if ann is None:
if isinstance(obj, type) or callable(obj):
return {}
raise TypeError(f"{obj!r} does not have annotations")
if not ann:
return {}
return dict(ann)
def get_annotations(obj: Any) -> Mapping[str, Any]:
# FORWARDREF has the effect of giving us ForwardRefs and not
# actually trying to evaluate the annotations. We need this so
# that the annotations act as much like
# "from __future__ import annotations" as possible, which is going
# away in future python as a separate mode
return _vendored_get_annotations(obj, format=Format.FORWARDREF)
elif py310:
def get_annotations(obj: Any) -> Mapping[str, Any]:
return inspect.get_annotations(obj)
else:
def get_annotations(obj: Any) -> Mapping[str, Any]:
# it's been observed that cls.__annotations__ can be non present.
# it's not clear what causes this, running under tox py37/38 it
# happens, running straight pytest it doesnt
# https://docs.python.org/3/howto/annotations.html#annotations-howto
if isinstance(obj, type):
ann = obj.__dict__.get("__annotations__", None)
else:
ann = getattr(obj, "__annotations__", None)
from . import _collections
if ann is None:
return _collections.EMPTY_DICT
else:
return cast("Mapping[str, Any]", ann)
class FullArgSpec(typing.NamedTuple):
args: List[str]
varargs: Optional[str]
varkw: Optional[str]
defaults: Optional[Tuple[Any, ...]]
kwonlyargs: List[str]
kwonlydefaults: Optional[Dict[str, Any]]
annotations: Mapping[str, Any]
def inspect_getfullargspec(func: Callable[..., Any]) -> FullArgSpec:
"""Fully vendored version of getfullargspec from Python 3.3."""
if inspect.ismethod(func):
func = func.__func__
if not inspect.isfunction(func) and not hasattr(func, "__code__"):
raise TypeError(f"{func!r} is not a Python function")
co = func.__code__
if not inspect.iscode(co):
raise TypeError(f"{co!r} is not a code object")
nargs = co.co_argcount
names = co.co_varnames
nkwargs = co.co_kwonlyargcount
args = list(names[:nargs])
kwonlyargs = list(names[nargs : nargs + nkwargs])
nargs += nkwargs
varargs = None
if co.co_flags & inspect.CO_VARARGS:
varargs = co.co_varnames[nargs]
nargs = nargs + 1
varkw = None
if co.co_flags & inspect.CO_VARKEYWORDS:
varkw = co.co_varnames[nargs]
return FullArgSpec(
args,
varargs,
varkw,
func.__defaults__,
kwonlyargs,
func.__kwdefaults__,
get_annotations(func),
)
if py39:
# python stubs don't have a public type for this. not worth
# making a protocol
def md5_not_for_security() -> Any:
return hashlib.md5(usedforsecurity=False)
else:
def md5_not_for_security() -> Any:
return hashlib.md5()
if typing.TYPE_CHECKING or py38:
from importlib import metadata as importlib_metadata
else:
import importlib_metadata # noqa
if typing.TYPE_CHECKING or py39:
# pep 584 dict union
dict_union = operator.or_ # noqa
else:
def dict_union(a: dict, b: dict) -> dict:
a = a.copy()
a.update(b)
return a
if py310:
anext_ = anext
else:
_NOT_PROVIDED = object()
from collections.abc import AsyncIterator
async def anext_(async_iterator, default=_NOT_PROVIDED):
"""vendored from https://github.com/python/cpython/pull/8895"""
if not isinstance(async_iterator, AsyncIterator):
raise TypeError(
f"anext expected an AsyncIterator, got {type(async_iterator)}"
)
anxt = type(async_iterator).__anext__
try:
return await anxt(async_iterator)
except StopAsyncIteration:
if default is _NOT_PROVIDED:
raise
return default
def importlib_metadata_get(group):
ep = importlib_metadata.entry_points()
if typing.TYPE_CHECKING or hasattr(ep, "select"):
return ep.select(group=group)
else:
return ep.get(group, ())
def b(s):
return s.encode("latin-1")
def b64decode(x: str) -> bytes:
return base64.b64decode(x.encode("ascii"))
def b64encode(x: bytes) -> str:
return base64.b64encode(x).decode("ascii")
def decode_backslashreplace(text: bytes, encoding: str) -> str:
return text.decode(encoding, errors="backslashreplace")
def cmp(a, b):
return (a > b) - (a < b)
def _formatannotation(annotation, base_module=None):
"""vendored from python 3.7"""
if isinstance(annotation, str):
return annotation
if getattr(annotation, "__module__", None) == "typing":
return repr(annotation).replace("typing.", "").replace("~", "")
if isinstance(annotation, type):
if annotation.__module__ in ("builtins", base_module):
return repr(annotation.__qualname__)
return annotation.__module__ + "." + annotation.__qualname__
elif isinstance(annotation, typing.TypeVar):
return repr(annotation).replace("~", "")
return repr(annotation).replace("~", "")
def inspect_formatargspec(
args: List[str],
varargs: Optional[str] = None,
varkw: Optional[str] = None,
defaults: Optional[Sequence[Any]] = None,
kwonlyargs: Optional[Sequence[str]] = (),
kwonlydefaults: Optional[Mapping[str, Any]] = {},
annotations: Mapping[str, Any] = {},
formatarg: Callable[[str], str] = str,
formatvarargs: Callable[[str], str] = lambda name: "*" + name,
formatvarkw: Callable[[str], str] = lambda name: "**" + name,
formatvalue: Callable[[Any], str] = lambda value: "=" + repr(value),
formatreturns: Callable[[Any], str] = lambda text: " -> " + str(text),
formatannotation: Callable[[Any], str] = _formatannotation,
) -> str:
"""Copy formatargspec from python 3.7 standard library.
Python 3 has deprecated formatargspec and requested that Signature
be used instead, however this requires a full reimplementation
of formatargspec() in terms of creating Parameter objects and such.
Instead of introducing all the object-creation overhead and having
to reinvent from scratch, just copy their compatibility routine.
Ultimately we would need to rewrite our "decorator" routine completely
which is not really worth it right now, until all Python 2.x support
is dropped.
"""
kwonlydefaults = kwonlydefaults or {}
annotations = annotations or {}
def formatargandannotation(arg):
result = formatarg(arg)
if arg in annotations:
result += ": " + formatannotation(annotations[arg])
return result
specs = []
if defaults:
firstdefault = len(args) - len(defaults)
else:
firstdefault = -1
for i, arg in enumerate(args):
spec = formatargandannotation(arg)
if defaults and i >= firstdefault:
spec = spec + formatvalue(defaults[i - firstdefault])
specs.append(spec)
if varargs is not None:
specs.append(formatvarargs(formatargandannotation(varargs)))
else:
if kwonlyargs:
specs.append("*")
if kwonlyargs:
for kwonlyarg in kwonlyargs:
spec = formatargandannotation(kwonlyarg)
if kwonlydefaults and kwonlyarg in kwonlydefaults:
spec += formatvalue(kwonlydefaults[kwonlyarg])
specs.append(spec)
if varkw is not None:
specs.append(formatvarkw(formatargandannotation(varkw)))
result = "(" + ", ".join(specs) + ")"
if "return" in annotations:
result += formatreturns(formatannotation(annotations["return"]))
return result
def dataclass_fields(cls: Type[Any]) -> Iterable[dataclasses.Field[Any]]:
"""Return a sequence of all dataclasses.Field objects associated
with a class as an already processed dataclass.
The class must **already be a dataclass** for Field objects to be returned.
"""
if dataclasses.is_dataclass(cls):
return dataclasses.fields(cls)
else:
return []
def local_dataclass_fields(cls: Type[Any]) -> Iterable[dataclasses.Field[Any]]:
"""Return a sequence of all dataclasses.Field objects associated with
an already processed dataclass, excluding those that originate from a
superclass.
The class must **already be a dataclass** for Field objects to be returned.
"""
if dataclasses.is_dataclass(cls):
super_fields: Set[dataclasses.Field[Any]] = set()
for sup in cls.__bases__:
super_fields.update(dataclass_fields(sup))
return [f for f in dataclasses.fields(cls) if f not in super_fields]
else:
return []
if freethreading:
import threading
mini_gil = threading.RLock()
"""provide a threading.RLock() under python freethreading only"""
else:
import contextlib
mini_gil = contextlib.nullcontext() # type: ignore[assignment]

View File

@@ -0,0 +1,110 @@
# util/concurrency.py
# Copyright (C) 2005-2026 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: allow-untyped-defs, allow-untyped-calls
"""asyncio-related concurrency functions."""
from __future__ import annotations
import asyncio # noqa
import typing
from typing import Any
from typing import Callable
from typing import Coroutine
from typing import TypeVar
have_greenlet = False
greenlet_error = None
try:
import greenlet # type: ignore[import-untyped,unused-ignore] # noqa: F401,E501
except ImportError as e:
greenlet_error = str(e)
pass
else:
have_greenlet = True
from ._concurrency_py3k import await_only as await_only
from ._concurrency_py3k import await_fallback as await_fallback
from ._concurrency_py3k import in_greenlet as in_greenlet
from ._concurrency_py3k import greenlet_spawn as greenlet_spawn
from ._concurrency_py3k import is_exit_exception as is_exit_exception
from ._concurrency_py3k import AsyncAdaptedLock as AsyncAdaptedLock
from ._concurrency_py3k import _Runner
_T = TypeVar("_T")
class _AsyncUtil:
"""Asyncio util for test suite/ util only"""
def __init__(self) -> None:
if have_greenlet:
self.runner = _Runner()
def run(
self,
fn: Callable[..., Coroutine[Any, Any, _T]],
*args: Any,
**kwargs: Any,
) -> _T:
"""Run coroutine on the loop"""
return self.runner.run(fn(*args, **kwargs))
def run_in_greenlet(
self, fn: Callable[..., _T], *args: Any, **kwargs: Any
) -> _T:
"""Run sync function in greenlet. Support nested calls"""
if have_greenlet:
if self.runner.get_loop().is_running():
return fn(*args, **kwargs)
else:
return self.runner.run(greenlet_spawn(fn, *args, **kwargs))
else:
return fn(*args, **kwargs)
def close(self) -> None:
if have_greenlet:
self.runner.close()
if not typing.TYPE_CHECKING and not have_greenlet:
def _not_implemented():
# this conditional is to prevent pylance from considering
# greenlet_spawn() etc as "no return" and dimming out code below it
if have_greenlet:
return None
raise ValueError(
"the greenlet library is required to use this function."
" %s" % greenlet_error
if greenlet_error
else ""
)
def is_exit_exception(e): # noqa: F811
return not isinstance(e, Exception)
def await_only(thing): # type: ignore # noqa: F811
_not_implemented()
def await_fallback(thing): # type: ignore # noqa: F811
return thing
def in_greenlet(): # type: ignore # noqa: F811
_not_implemented()
def greenlet_spawn(fn, *args, **kw): # type: ignore # noqa: F811
_not_implemented()
def AsyncAdaptedLock(*args, **kw): # type: ignore # noqa: F811
_not_implemented()
def _util_async_run(fn, *arg, **kw): # type: ignore # noqa: F811
return fn(*arg, **kw)
def _util_async_run_coroutine_function(fn, *arg, **kw): # type: ignore # noqa: F811,E501
_not_implemented()

View File

@@ -0,0 +1,401 @@
# util/deprecations.py
# Copyright (C) 2005-2026 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: allow-untyped-defs, allow-untyped-calls
"""Helpers related to deprecation of functions, methods, classes, other
functionality."""
from __future__ import annotations
import re
from typing import Any
from typing import Callable
from typing import Dict
from typing import Match
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Tuple
from typing import Type
from typing import TypeVar
from typing import Union
from . import compat
from .langhelpers import _hash_limit_string
from .langhelpers import _warnings_warn
from .langhelpers import decorator
from .langhelpers import inject_docstring_text
from .langhelpers import inject_param_text
from .. import exc
_T = TypeVar("_T", bound=Any)
# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
_F = TypeVar("_F", bound="Callable[..., Any]")
def _warn_with_version(
msg: str,
version: str,
type_: Type[exc.SADeprecationWarning],
stacklevel: int,
code: Optional[str] = None,
) -> None:
warn = type_(msg, code=code)
warn.deprecated_since = version
_warnings_warn(warn, stacklevel=stacklevel + 1)
def warn_deprecated(
msg: str, version: str, stacklevel: int = 3, code: Optional[str] = None
) -> None:
_warn_with_version(
msg, version, exc.SADeprecationWarning, stacklevel, code=code
)
def warn_deprecated_limited(
msg: str,
args: Sequence[Any],
version: str,
stacklevel: int = 3,
code: Optional[str] = None,
) -> None:
"""Issue a deprecation warning with a parameterized string,
limiting the number of registrations.
"""
if args:
msg = _hash_limit_string(msg, 10, args)
_warn_with_version(
msg, version, exc.SADeprecationWarning, stacklevel, code=code
)
def deprecated_cls(
version: str, message: str, constructor: Optional[str] = "__init__"
) -> Callable[[Type[_T]], Type[_T]]:
header = ".. deprecated:: %s %s" % (version, (message or ""))
def decorate(cls: Type[_T]) -> Type[_T]:
return _decorate_cls_with_warning(
cls,
constructor,
exc.SADeprecationWarning,
message % dict(func=constructor),
version,
header,
)
return decorate
def deprecated(
version: str,
message: Optional[str] = None,
add_deprecation_to_docstring: bool = True,
warning: Optional[Type[exc.SADeprecationWarning]] = None,
enable_warnings: bool = True,
) -> Callable[[_F], _F]:
"""Decorates a function and issues a deprecation warning on use.
:param version:
Issue version in the warning.
:param message:
If provided, issue message in the warning. A sensible default
is used if not provided.
:param add_deprecation_to_docstring:
Default True. If False, the wrapped function's __doc__ is left
as-is. If True, the 'message' is prepended to the docs if
provided, or sensible default if message is omitted.
"""
if add_deprecation_to_docstring:
header = ".. deprecated:: %s %s" % (
version,
(message or ""),
)
else:
header = None
if message is None:
message = "Call to deprecated function %(func)s"
if warning is None:
warning = exc.SADeprecationWarning
message += " (deprecated since: %s)" % version
def decorate(fn: _F) -> _F:
assert message is not None
assert warning is not None
return _decorate_with_warning(
fn,
warning,
message % dict(func=fn.__name__),
version,
header,
enable_warnings=enable_warnings,
)
return decorate
def moved_20(
message: str, **kw: Any
) -> Callable[[Callable[..., _T]], Callable[..., _T]]:
return deprecated(
"2.0", message=message, warning=exc.MovedIn20Warning, **kw
)
def became_legacy_20(
api_name: str, alternative: Optional[str] = None, **kw: Any
) -> Callable[[_F], _F]:
type_reg = re.match("^:(attr|func|meth):", api_name)
if type_reg:
type_ = {"attr": "attribute", "func": "function", "meth": "method"}[
type_reg.group(1)
]
else:
type_ = "construct"
message = (
"The %s %s is considered legacy as of the "
"1.x series of SQLAlchemy and %s in 2.0."
% (
api_name,
type_,
"becomes a legacy construct",
)
)
if ":attr:" in api_name:
attribute_ok = kw.pop("warn_on_attribute_access", False)
if not attribute_ok:
assert kw.get("enable_warnings") is False, (
"attribute %s will emit a warning on read access. "
"If you *really* want this, "
"add warn_on_attribute_access=True. Otherwise please add "
"enable_warnings=False." % api_name
)
if alternative:
message += " " + alternative
warning_cls = exc.LegacyAPIWarning
return deprecated("2.0", message=message, warning=warning_cls, **kw)
def deprecated_params(**specs: Tuple[str, str]) -> Callable[[_F], _F]:
"""Decorates a function to warn on use of certain parameters.
e.g. ::
@deprecated_params(
weak_identity_map=(
"0.7",
"the :paramref:`.Session.weak_identity_map parameter "
"is deprecated.",
)
)
def some_function(**kwargs): ...
"""
messages: Dict[str, str] = {}
versions: Dict[str, str] = {}
version_warnings: Dict[str, Type[exc.SADeprecationWarning]] = {}
for param, (version, message) in specs.items():
versions[param] = version
messages[param] = _sanitize_restructured_text(message)
version_warnings[param] = exc.SADeprecationWarning
def decorate(fn: _F) -> _F:
spec = compat.inspect_getfullargspec(fn)
check_defaults: Union[Set[str], Tuple[()]]
if spec.defaults is not None:
defaults = dict(
zip(
spec.args[(len(spec.args) - len(spec.defaults)) :],
spec.defaults,
)
)
check_defaults = set(defaults).intersection(messages)
check_kw = set(messages).difference(defaults)
elif spec.kwonlydefaults is not None:
defaults = spec.kwonlydefaults
check_defaults = set(defaults).intersection(messages)
check_kw = set(messages).difference(defaults)
else:
check_defaults = ()
check_kw = set(messages)
check_any_kw = spec.varkw
# latest mypy has opinions here, not sure if they implemented
# Concatenate or something
@decorator
def warned(fn: _F, *args: Any, **kwargs: Any) -> _F:
for m in check_defaults:
if (defaults[m] is None and kwargs[m] is not None) or (
defaults[m] is not None and kwargs[m] != defaults[m]
):
_warn_with_version(
messages[m],
versions[m],
version_warnings[m],
stacklevel=3,
)
if check_any_kw in messages and set(kwargs).difference(
check_defaults
):
assert check_any_kw is not None
_warn_with_version(
messages[check_any_kw],
versions[check_any_kw],
version_warnings[check_any_kw],
stacklevel=3,
)
for m in check_kw:
if m in kwargs:
_warn_with_version(
messages[m],
versions[m],
version_warnings[m],
stacklevel=3,
)
return fn(*args, **kwargs) # type: ignore[no-any-return]
doc = fn.__doc__ is not None and fn.__doc__ or ""
if doc:
doc = inject_param_text(
doc,
{
param: ".. deprecated:: %s %s"
% ("1.4" if version == "2.0" else version, (message or ""))
for param, (version, message) in specs.items()
},
)
decorated = warned(fn)
decorated.__doc__ = doc
return decorated
return decorate
def _sanitize_restructured_text(text: str) -> str:
def repl(m: Match[str]) -> str:
type_, name = m.group(1, 2)
if type_ in ("func", "meth"):
name += "()"
return name
text = re.sub(r":ref:`(.+) <.*>`", lambda m: '"%s"' % m.group(1), text)
return re.sub(r"\:(\w+)\:`~?(?:_\w+)?\.?(.+?)`", repl, text)
def _decorate_cls_with_warning(
cls: Type[_T],
constructor: Optional[str],
wtype: Type[exc.SADeprecationWarning],
message: str,
version: str,
docstring_header: Optional[str] = None,
) -> Type[_T]:
doc = cls.__doc__ is not None and cls.__doc__ or ""
if docstring_header is not None:
if constructor is not None:
docstring_header %= dict(func=constructor)
if issubclass(wtype, exc.Base20DeprecationWarning):
docstring_header += (
" (Background on SQLAlchemy 2.0 at: "
":ref:`migration_20_toplevel`)"
)
doc = inject_docstring_text(doc, docstring_header, 1)
constructor_fn = None
if type(cls) is type:
clsdict = dict(cls.__dict__)
clsdict["__doc__"] = doc
clsdict.pop("__dict__", None)
clsdict.pop("__weakref__", None)
cls = type(cls.__name__, cls.__bases__, clsdict)
if constructor is not None:
constructor_fn = clsdict[constructor]
else:
cls.__doc__ = doc
if constructor is not None:
constructor_fn = getattr(cls, constructor)
if constructor is not None:
assert constructor_fn is not None
assert wtype is not None
setattr(
cls,
constructor,
_decorate_with_warning(
constructor_fn, wtype, message, version, None
),
)
return cls
def _decorate_with_warning(
func: _F,
wtype: Type[exc.SADeprecationWarning],
message: str,
version: str,
docstring_header: Optional[str] = None,
enable_warnings: bool = True,
) -> _F:
"""Wrap a function with a warnings.warn and augmented docstring."""
message = _sanitize_restructured_text(message)
if issubclass(wtype, exc.Base20DeprecationWarning):
doc_only = (
" (Background on SQLAlchemy 2.0 at: "
":ref:`migration_20_toplevel`)"
)
else:
doc_only = ""
@decorator
def warned(fn: _F, *args: Any, **kwargs: Any) -> _F:
skip_warning = not enable_warnings or kwargs.pop(
"_sa_skip_warning", False
)
if not skip_warning:
_warn_with_version(message, version, wtype, stacklevel=3)
return fn(*args, **kwargs) # type: ignore[no-any-return]
doc = func.__doc__ is not None and func.__doc__ or ""
if docstring_header is not None:
docstring_header %= dict(func=func.__name__)
docstring_header += doc_only
doc = inject_docstring_text(doc, docstring_header, 1)
decorated = warned(func)
decorated.__doc__ = doc
decorated._sa_warn = lambda: _warn_with_version( # type: ignore
message, version, wtype, stacklevel=3
)
return decorated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,151 @@
# util/preloaded.py
# Copyright (C) 2005-2026 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: allow-untyped-defs, allow-untyped-calls
"""supplies the "preloaded" registry to resolve circular module imports at
runtime.
"""
from __future__ import annotations
import sys
from typing import Any
from typing import Callable
from typing import TYPE_CHECKING
from typing import TypeVar
_FN = TypeVar("_FN", bound=Callable[..., Any])
if TYPE_CHECKING:
from sqlalchemy import dialects as _dialects
from sqlalchemy import orm as _orm
from sqlalchemy.engine import cursor as _engine_cursor
from sqlalchemy.engine import default as _engine_default
from sqlalchemy.engine import reflection as _engine_reflection
from sqlalchemy.engine import result as _engine_result
from sqlalchemy.engine import url as _engine_url
from sqlalchemy.orm import attributes as _orm_attributes
from sqlalchemy.orm import base as _orm_base
from sqlalchemy.orm import clsregistry as _orm_clsregistry
from sqlalchemy.orm import decl_api as _orm_decl_api
from sqlalchemy.orm import decl_base as _orm_decl_base
from sqlalchemy.orm import dependency as _orm_dependency
from sqlalchemy.orm import descriptor_props as _orm_descriptor_props
from sqlalchemy.orm import mapperlib as _orm_mapper
from sqlalchemy.orm import properties as _orm_properties
from sqlalchemy.orm import relationships as _orm_relationships
from sqlalchemy.orm import session as _orm_session
from sqlalchemy.orm import state as _orm_state
from sqlalchemy.orm import strategies as _orm_strategies
from sqlalchemy.orm import strategy_options as _orm_strategy_options
from sqlalchemy.orm import util as _orm_util
from sqlalchemy.sql import default_comparator as _sql_default_comparator
from sqlalchemy.sql import dml as _sql_dml
from sqlalchemy.sql import elements as _sql_elements
from sqlalchemy.sql import functions as _sql_functions
from sqlalchemy.sql import naming as _sql_naming
from sqlalchemy.sql import schema as _sql_schema
from sqlalchemy.sql import selectable as _sql_selectable
from sqlalchemy.sql import sqltypes as _sql_sqltypes
from sqlalchemy.sql import traversals as _sql_traversals
from sqlalchemy.sql import util as _sql_util
# sigh, appease mypy 0.971 which does not accept imports as instance
# variables of a module
dialects = _dialects
engine_cursor = _engine_cursor
engine_default = _engine_default
engine_reflection = _engine_reflection
engine_result = _engine_result
engine_url = _engine_url
orm_clsregistry = _orm_clsregistry
orm_base = _orm_base
orm = _orm
orm_attributes = _orm_attributes
orm_decl_api = _orm_decl_api
orm_decl_base = _orm_decl_base
orm_descriptor_props = _orm_descriptor_props
orm_dependency = _orm_dependency
orm_mapper = _orm_mapper
orm_properties = _orm_properties
orm_relationships = _orm_relationships
orm_session = _orm_session
orm_strategies = _orm_strategies
orm_strategy_options = _orm_strategy_options
orm_state = _orm_state
orm_util = _orm_util
sql_default_comparator = _sql_default_comparator
sql_dml = _sql_dml
sql_elements = _sql_elements
sql_functions = _sql_functions
sql_naming = _sql_naming
sql_selectable = _sql_selectable
sql_traversals = _sql_traversals
sql_schema = _sql_schema
sql_sqltypes = _sql_sqltypes
sql_util = _sql_util
class _ModuleRegistry:
"""Registry of modules to load in a package init file.
To avoid potential thread safety issues for imports that are deferred
in a function, like https://bugs.python.org/issue38884, these modules
are added to the system module cache by importing them after the packages
has finished initialization.
A global instance is provided under the name :attr:`.preloaded`. Use
the function :func:`.preload_module` to register modules to load and
:meth:`.import_prefix` to load all the modules that start with the
given path.
While the modules are loaded in the global module cache, it's advisable
to access them using :attr:`.preloaded` to ensure that it was actually
registered. Each registered module is added to the instance ``__dict__``
in the form `<package>_<module>`, omitting ``sqlalchemy`` from the package
name. Example: ``sqlalchemy.sql.util`` becomes ``preloaded.sql_util``.
"""
def __init__(self, prefix="sqlalchemy."):
self.module_registry = set()
self.prefix = prefix
def preload_module(self, *deps: str) -> Callable[[_FN], _FN]:
"""Adds the specified modules to the list to load.
This method can be used both as a normal function and as a decorator.
No change is performed to the decorated object.
"""
self.module_registry.update(deps)
return lambda fn: fn
def import_prefix(self, path: str) -> None:
"""Resolve all the modules in the registry that start with the
specified path.
"""
for module in self.module_registry:
if self.prefix:
key = module.split(self.prefix)[-1].replace(".", "_")
else:
key = module
if (
not path or module.startswith(path)
) and key not in self.__dict__:
__import__(module, globals(), locals())
self.__dict__[key] = globals()[key] = sys.modules[module]
_reg = _ModuleRegistry()
preload_module = _reg.preload_module
import_prefix = _reg.import_prefix
# this appears to do absolutely nothing for any version of mypy
# if TYPE_CHECKING:
# def __getattr__(key: str) -> ModuleType:
# ...