Загрузить файлы в «venv/Lib/site-packages/sqlalchemy/util»
This commit is contained in:
420
venv/Lib/site-packages/sqlalchemy/util/compat.py
Normal file
420
venv/Lib/site-packages/sqlalchemy/util/compat.py
Normal 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]
|
||||||
110
venv/Lib/site-packages/sqlalchemy/util/concurrency.py
Normal file
110
venv/Lib/site-packages/sqlalchemy/util/concurrency.py
Normal 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()
|
||||||
401
venv/Lib/site-packages/sqlalchemy/util/deprecations.py
Normal file
401
venv/Lib/site-packages/sqlalchemy/util/deprecations.py
Normal 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
|
||||||
2206
venv/Lib/site-packages/sqlalchemy/util/langhelpers.py
Normal file
2206
venv/Lib/site-packages/sqlalchemy/util/langhelpers.py
Normal file
File diff suppressed because it is too large
Load Diff
151
venv/Lib/site-packages/sqlalchemy/util/preloaded.py
Normal file
151
venv/Lib/site-packages/sqlalchemy/util/preloaded.py
Normal 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:
|
||||||
|
# ...
|
||||||
Reference in New Issue
Block a user