From b233a688018013493b34d4a1440af0ca43780626 Mon Sep 17 00:00:00 2001 From: Polina Date: Thu, 2 Jul 2026 20:40:37 +0000 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?venv/Lib/site-packages/sqlalchemy/util=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../site-packages/sqlalchemy/util/__init__.py | 421 +++++---- .../site-packages/sqlalchemy/util/events.py | 17 + venv/Lib/site-packages/sqlalchemy/util/exc.py | 833 ++++++++++++++++++ .../sqlalchemy/util/inspection.py | 175 ++++ venv/Lib/site-packages/sqlalchemy/util/log.py | 288 ++++++ 5 files changed, 1584 insertions(+), 150 deletions(-) create mode 100644 venv/Lib/site-packages/sqlalchemy/util/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/exc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/inspection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/log.py diff --git a/venv/Lib/site-packages/sqlalchemy/util/__init__.py b/venv/Lib/site-packages/sqlalchemy/util/__init__.py index 820f4cb..d9d3070 100644 --- a/venv/Lib/site-packages/sqlalchemy/util/__init__.py +++ b/venv/Lib/site-packages/sqlalchemy/util/__init__.py @@ -1,162 +1,283 @@ -# util/__init__.py +# __init__.py # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors # # # This module is part of SQLAlchemy and is released under # the MIT License: https://www.opensource.org/licenses/mit-license.php +from __future__ import annotations -from collections import defaultdict as defaultdict -from functools import partial as partial -from functools import update_wrapper as update_wrapper +from typing import Any -from . import preloaded as preloaded -from ._collections import coerce_generator_arg as coerce_generator_arg -from ._collections import coerce_to_immutabledict as coerce_to_immutabledict -from ._collections import column_dict as column_dict -from ._collections import column_set as column_set -from ._collections import EMPTY_DICT as EMPTY_DICT -from ._collections import EMPTY_SET as EMPTY_SET -from ._collections import FacadeDict as FacadeDict -from ._collections import flatten_iterator as flatten_iterator -from ._collections import has_dupes as has_dupes -from ._collections import has_intersection as has_intersection -from ._collections import IdentitySet as IdentitySet -from ._collections import immutabledict as immutabledict -from ._collections import LRUCache as LRUCache -from ._collections import merge_lists_w_ordering as merge_lists_w_ordering -from ._collections import NONE_SET as NONE_SET -from ._collections import ordered_column_set as ordered_column_set -from ._collections import OrderedDict as OrderedDict -from ._collections import OrderedIdentitySet as OrderedIdentitySet -from ._collections import OrderedProperties as OrderedProperties -from ._collections import OrderedSet as OrderedSet -from ._collections import PopulateDict as PopulateDict -from ._collections import Properties as Properties -from ._collections import ReadOnlyContainer as ReadOnlyContainer -from ._collections import ReadOnlyProperties as ReadOnlyProperties -from ._collections import ScopedRegistry as ScopedRegistry -from ._collections import sort_dictionary as sort_dictionary -from ._collections import ThreadLocalRegistry as ThreadLocalRegistry -from ._collections import to_column_set as to_column_set -from ._collections import to_list as to_list -from ._collections import to_set as to_set -from ._collections import unique_list as unique_list -from ._collections import UniqueAppender as UniqueAppender -from ._collections import update_copy as update_copy -from ._collections import WeakPopulateDict as WeakPopulateDict -from ._collections import WeakSequence as WeakSequence -from .compat import anext_ as anext_ -from .compat import arm as arm -from .compat import b as b -from .compat import b64decode as b64decode -from .compat import b64encode as b64encode -from .compat import cmp as cmp -from .compat import cpython as cpython -from .compat import dataclass_fields as dataclass_fields -from .compat import decode_backslashreplace as decode_backslashreplace -from .compat import dottedgetter as dottedgetter -from .compat import freethreading as freethreading -from .compat import get_annotations as get_annotations -from .compat import has_refcount_gc as has_refcount_gc -from .compat import inspect_getfullargspec as inspect_getfullargspec -from .compat import is64bit as is64bit -from .compat import local_dataclass_fields as local_dataclass_fields -from .compat import mini_gil as mini_gil -from .compat import osx as osx -from .compat import py310 as py310 -from .compat import py311 as py311 -from .compat import py312 as py312 -from .compat import py313 as py313 -from .compat import py314 as py314 -from .compat import py38 as py38 -from .compat import py39 as py39 -from .compat import pypy as pypy -from .compat import win32 as win32 -from .concurrency import await_fallback as await_fallback -from .concurrency import await_only as await_only -from .concurrency import greenlet_spawn as greenlet_spawn -from .concurrency import is_exit_exception as is_exit_exception -from .deprecations import became_legacy_20 as became_legacy_20 -from .deprecations import deprecated as deprecated -from .deprecations import deprecated_cls as deprecated_cls -from .deprecations import deprecated_params as deprecated_params -from .deprecations import moved_20 as moved_20 -from .deprecations import warn_deprecated as warn_deprecated -from .langhelpers import add_parameter_text as add_parameter_text -from .langhelpers import as_interface as as_interface -from .langhelpers import asbool as asbool -from .langhelpers import asint as asint -from .langhelpers import assert_arg_type as assert_arg_type -from .langhelpers import attrsetter as attrsetter -from .langhelpers import bool_or_str as bool_or_str -from .langhelpers import chop_traceback as chop_traceback -from .langhelpers import class_hierarchy as class_hierarchy -from .langhelpers import classproperty as classproperty -from .langhelpers import clsname_as_plain_name as clsname_as_plain_name -from .langhelpers import coerce_kw_type as coerce_kw_type -from .langhelpers import constructor_copy as constructor_copy -from .langhelpers import constructor_key as constructor_key -from .langhelpers import counter as counter -from .langhelpers import create_proxy_methods as create_proxy_methods -from .langhelpers import decode_slice as decode_slice -from .langhelpers import decorator as decorator -from .langhelpers import dictlike_iteritems as dictlike_iteritems -from .langhelpers import duck_type_collection as duck_type_collection -from .langhelpers import ellipses_string as ellipses_string -from .langhelpers import EnsureKWArg as EnsureKWArg -from .langhelpers import FastIntFlag as FastIntFlag -from .langhelpers import format_argspec_init as format_argspec_init -from .langhelpers import format_argspec_plus as format_argspec_plus -from .langhelpers import generic_fn_descriptor as generic_fn_descriptor -from .langhelpers import generic_repr as generic_repr -from .langhelpers import get_callable_argspec as get_callable_argspec -from .langhelpers import get_cls_kwargs as get_cls_kwargs -from .langhelpers import get_func_kwargs as get_func_kwargs -from .langhelpers import getargspec_init as getargspec_init -from .langhelpers import has_compiled_ext as has_compiled_ext -from .langhelpers import HasMemoized as HasMemoized -from .langhelpers import ( - HasMemoized_ro_memoized_attribute as HasMemoized_ro_memoized_attribute, +from . import util as _util +from .engine import AdaptedConnection as AdaptedConnection +from .engine import BaseRow as BaseRow +from .engine import BindTyping as BindTyping +from .engine import ChunkedIteratorResult as ChunkedIteratorResult +from .engine import Compiled as Compiled +from .engine import Connection as Connection +from .engine import create_engine as create_engine +from .engine import create_mock_engine as create_mock_engine +from .engine import create_pool_from_url as create_pool_from_url +from .engine import CreateEnginePlugin as CreateEnginePlugin +from .engine import CursorResult as CursorResult +from .engine import Dialect as Dialect +from .engine import Engine as Engine +from .engine import engine_from_config as engine_from_config +from .engine import ExceptionContext as ExceptionContext +from .engine import ExecutionContext as ExecutionContext +from .engine import FrozenResult as FrozenResult +from .engine import Inspector as Inspector +from .engine import IteratorResult as IteratorResult +from .engine import make_url as make_url +from .engine import MappingResult as MappingResult +from .engine import MergedResult as MergedResult +from .engine import NestedTransaction as NestedTransaction +from .engine import Result as Result +from .engine import result_tuple as result_tuple +from .engine import ResultProxy as ResultProxy +from .engine import RootTransaction as RootTransaction +from .engine import Row as Row +from .engine import RowMapping as RowMapping +from .engine import ScalarResult as ScalarResult +from .engine import Transaction as Transaction +from .engine import TwoPhaseTransaction as TwoPhaseTransaction +from .engine import TypeCompiler as TypeCompiler +from .engine import URL as URL +from .inspection import inspect as inspect +from .pool import AssertionPool as AssertionPool +from .pool import AsyncAdaptedQueuePool as AsyncAdaptedQueuePool +from .pool import ( + FallbackAsyncAdaptedQueuePool as FallbackAsyncAdaptedQueuePool, ) -from .langhelpers import hybridmethod as hybridmethod -from .langhelpers import hybridproperty as hybridproperty -from .langhelpers import inject_docstring_text as inject_docstring_text -from .langhelpers import iterate_attributes as iterate_attributes -from .langhelpers import map_bits as map_bits -from .langhelpers import md5_hex as md5_hex -from .langhelpers import memoized_instancemethod as memoized_instancemethod -from .langhelpers import memoized_property as memoized_property -from .langhelpers import MemoizedSlots as MemoizedSlots -from .langhelpers import method_is_overridden as method_is_overridden -from .langhelpers import methods_equivalent as methods_equivalent -from .langhelpers import ( - monkeypatch_proxied_specials as monkeypatch_proxied_specials, +from .pool import NullPool as NullPool +from .pool import Pool as Pool +from .pool import PoolProxiedConnection as PoolProxiedConnection +from .pool import PoolResetState as PoolResetState +from .pool import QueuePool as QueuePool +from .pool import SingletonThreadPool as SingletonThreadPool +from .pool import StaticPool as StaticPool +from .schema import BaseDDLElement as BaseDDLElement +from .schema import BLANK_SCHEMA as BLANK_SCHEMA +from .schema import CheckConstraint as CheckConstraint +from .schema import Column as Column +from .schema import ColumnDefault as ColumnDefault +from .schema import Computed as Computed +from .schema import Constraint as Constraint +from .schema import DDL as DDL +from .schema import DDLElement as DDLElement +from .schema import DefaultClause as DefaultClause +from .schema import ExecutableDDLElement as ExecutableDDLElement +from .schema import FetchedValue as FetchedValue +from .schema import ForeignKey as ForeignKey +from .schema import ForeignKeyConstraint as ForeignKeyConstraint +from .schema import Identity as Identity +from .schema import Index as Index +from .schema import insert_sentinel as insert_sentinel +from .schema import MetaData as MetaData +from .schema import PrimaryKeyConstraint as PrimaryKeyConstraint +from .schema import Sequence as Sequence +from .schema import Table as Table +from .schema import UniqueConstraint as UniqueConstraint +from .sql import ColumnExpressionArgument as ColumnExpressionArgument +from .sql import NotNullable as NotNullable +from .sql import Nullable as Nullable +from .sql import SelectLabelStyle as SelectLabelStyle +from .sql.expression import Alias as Alias +from .sql.expression import alias as alias +from .sql.expression import AliasedReturnsRows as AliasedReturnsRows +from .sql.expression import all_ as all_ +from .sql.expression import and_ as and_ +from .sql.expression import any_ as any_ +from .sql.expression import asc as asc +from .sql.expression import between as between +from .sql.expression import BinaryExpression as BinaryExpression +from .sql.expression import bindparam as bindparam +from .sql.expression import BindParameter as BindParameter +from .sql.expression import bitwise_not as bitwise_not +from .sql.expression import BooleanClauseList as BooleanClauseList +from .sql.expression import CacheKey as CacheKey +from .sql.expression import Case as Case +from .sql.expression import case as case +from .sql.expression import Cast as Cast +from .sql.expression import cast as cast +from .sql.expression import ClauseElement as ClauseElement +from .sql.expression import ClauseList as ClauseList +from .sql.expression import collate as collate +from .sql.expression import CollectionAggregate as CollectionAggregate +from .sql.expression import column as column +from .sql.expression import ColumnClause as ColumnClause +from .sql.expression import ColumnCollection as ColumnCollection +from .sql.expression import ColumnElement as ColumnElement +from .sql.expression import ColumnOperators as ColumnOperators +from .sql.expression import CompoundSelect as CompoundSelect +from .sql.expression import CTE as CTE +from .sql.expression import cte as cte +from .sql.expression import custom_op as custom_op +from .sql.expression import Delete as Delete +from .sql.expression import delete as delete +from .sql.expression import desc as desc +from .sql.expression import distinct as distinct +from .sql.expression import except_ as except_ +from .sql.expression import except_all as except_all +from .sql.expression import Executable as Executable +from .sql.expression import Exists as Exists +from .sql.expression import exists as exists +from .sql.expression import Extract as Extract +from .sql.expression import extract as extract +from .sql.expression import false as false +from .sql.expression import False_ as False_ +from .sql.expression import FromClause as FromClause +from .sql.expression import FromGrouping as FromGrouping +from .sql.expression import func as func +from .sql.expression import funcfilter as funcfilter +from .sql.expression import Function as Function +from .sql.expression import FunctionElement as FunctionElement +from .sql.expression import FunctionFilter as FunctionFilter +from .sql.expression import GenerativeSelect as GenerativeSelect +from .sql.expression import Grouping as Grouping +from .sql.expression import HasCTE as HasCTE +from .sql.expression import HasPrefixes as HasPrefixes +from .sql.expression import HasSuffixes as HasSuffixes +from .sql.expression import Insert as Insert +from .sql.expression import insert as insert +from .sql.expression import intersect as intersect +from .sql.expression import intersect_all as intersect_all +from .sql.expression import Join as Join +from .sql.expression import join as join +from .sql.expression import Label as Label +from .sql.expression import label as label +from .sql.expression import LABEL_STYLE_DEFAULT as LABEL_STYLE_DEFAULT +from .sql.expression import ( + LABEL_STYLE_DISAMBIGUATE_ONLY as LABEL_STYLE_DISAMBIGUATE_ONLY, ) -from .langhelpers import non_memoized_property as non_memoized_property -from .langhelpers import NoneType as NoneType -from .langhelpers import only_once as only_once -from .langhelpers import ( - parse_user_argument_for_enum as parse_user_argument_for_enum, +from .sql.expression import LABEL_STYLE_NONE as LABEL_STYLE_NONE +from .sql.expression import ( + LABEL_STYLE_TABLENAME_PLUS_COL as LABEL_STYLE_TABLENAME_PLUS_COL, ) -from .langhelpers import PluginLoader as PluginLoader -from .langhelpers import portable_instancemethod as portable_instancemethod -from .langhelpers import quoted_token_parser as quoted_token_parser -from .langhelpers import ro_memoized_property as ro_memoized_property -from .langhelpers import ro_non_memoized_property as ro_non_memoized_property -from .langhelpers import rw_hybridproperty as rw_hybridproperty -from .langhelpers import safe_reraise as safe_reraise -from .langhelpers import set_creation_order as set_creation_order -from .langhelpers import string_or_unprintable as string_or_unprintable -from .langhelpers import symbol as symbol -from .langhelpers import TypingOnly as TypingOnly -from .langhelpers import ( - unbound_method_to_callable as unbound_method_to_callable, +from .sql.expression import lambda_stmt as lambda_stmt +from .sql.expression import LambdaElement as LambdaElement +from .sql.expression import Lateral as Lateral +from .sql.expression import lateral as lateral +from .sql.expression import literal as literal +from .sql.expression import literal_column as literal_column +from .sql.expression import modifier as modifier +from .sql.expression import not_ as not_ +from .sql.expression import Null as Null +from .sql.expression import null as null +from .sql.expression import nulls_first as nulls_first +from .sql.expression import nulls_last as nulls_last +from .sql.expression import nullsfirst as nullsfirst +from .sql.expression import nullslast as nullslast +from .sql.expression import Operators as Operators +from .sql.expression import or_ as or_ +from .sql.expression import outerjoin as outerjoin +from .sql.expression import outparam as outparam +from .sql.expression import Over as Over +from .sql.expression import over as over +from .sql.expression import quoted_name as quoted_name +from .sql.expression import ReleaseSavepointClause as ReleaseSavepointClause +from .sql.expression import ReturnsRows as ReturnsRows +from .sql.expression import ( + RollbackToSavepointClause as RollbackToSavepointClause, ) -from .langhelpers import walk_subclasses as walk_subclasses -from .langhelpers import warn as warn -from .langhelpers import warn_exception as warn_exception -from .langhelpers import warn_limited as warn_limited -from .langhelpers import wrap_callable as wrap_callable -from .preloaded import preload_module as preload_module -from .typing import is_non_string_iterable as is_non_string_iterable +from .sql.expression import SavepointClause as SavepointClause +from .sql.expression import ScalarSelect as ScalarSelect +from .sql.expression import Select as Select +from .sql.expression import select as select +from .sql.expression import Selectable as Selectable +from .sql.expression import SelectBase as SelectBase +from .sql.expression import SQLColumnExpression as SQLColumnExpression +from .sql.expression import StatementLambdaElement as StatementLambdaElement +from .sql.expression import Subquery as Subquery +from .sql.expression import table as table +from .sql.expression import TableClause as TableClause +from .sql.expression import TableSample as TableSample +from .sql.expression import tablesample as tablesample +from .sql.expression import TableValuedAlias as TableValuedAlias +from .sql.expression import text as text +from .sql.expression import TextAsFrom as TextAsFrom +from .sql.expression import TextClause as TextClause +from .sql.expression import TextualSelect as TextualSelect +from .sql.expression import true as true +from .sql.expression import True_ as True_ +from .sql.expression import try_cast as try_cast +from .sql.expression import TryCast as TryCast +from .sql.expression import Tuple as Tuple +from .sql.expression import tuple_ as tuple_ +from .sql.expression import type_coerce as type_coerce +from .sql.expression import TypeClause as TypeClause +from .sql.expression import TypeCoerce as TypeCoerce +from .sql.expression import UnaryExpression as UnaryExpression +from .sql.expression import union as union +from .sql.expression import union_all as union_all +from .sql.expression import Update as Update +from .sql.expression import update as update +from .sql.expression import UpdateBase as UpdateBase +from .sql.expression import Values as Values +from .sql.expression import values as values +from .sql.expression import ValuesBase as ValuesBase +from .sql.expression import Visitable as Visitable +from .sql.expression import within_group as within_group +from .sql.expression import WithinGroup as WithinGroup +from .types import ARRAY as ARRAY +from .types import BIGINT as BIGINT +from .types import BigInteger as BigInteger +from .types import BINARY as BINARY +from .types import BLOB as BLOB +from .types import BOOLEAN as BOOLEAN +from .types import Boolean as Boolean +from .types import CHAR as CHAR +from .types import CLOB as CLOB +from .types import DATE as DATE +from .types import Date as Date +from .types import DATETIME as DATETIME +from .types import DateTime as DateTime +from .types import DECIMAL as DECIMAL +from .types import DOUBLE as DOUBLE +from .types import Double as Double +from .types import DOUBLE_PRECISION as DOUBLE_PRECISION +from .types import Enum as Enum +from .types import FLOAT as FLOAT +from .types import Float as Float +from .types import INT as INT +from .types import INTEGER as INTEGER +from .types import Integer as Integer +from .types import Interval as Interval +from .types import JSON as JSON +from .types import LargeBinary as LargeBinary +from .types import NCHAR as NCHAR +from .types import NUMERIC as NUMERIC +from .types import Numeric as Numeric +from .types import NVARCHAR as NVARCHAR +from .types import PickleType as PickleType +from .types import REAL as REAL +from .types import SMALLINT as SMALLINT +from .types import SmallInteger as SmallInteger +from .types import String as String +from .types import TEXT as TEXT +from .types import Text as Text +from .types import TIME as TIME +from .types import Time as Time +from .types import TIMESTAMP as TIMESTAMP +from .types import TupleType as TupleType +from .types import TypeDecorator as TypeDecorator +from .types import Unicode as Unicode +from .types import UnicodeText as UnicodeText +from .types import UUID as UUID +from .types import Uuid as Uuid +from .types import VARBINARY as VARBINARY +from .types import VARCHAR as VARCHAR + +__version__ = "2.0.51" + + +def __go(lcls: Any) -> None: + _util.preloaded.import_prefix("sqlalchemy") + + from . import exc + + exc._version_token = "".join(__version__.split(".")[0:2]) + + +__go(locals()) diff --git a/venv/Lib/site-packages/sqlalchemy/util/events.py b/venv/Lib/site-packages/sqlalchemy/util/events.py new file mode 100644 index 0000000..03d3990 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/events.py @@ -0,0 +1,17 @@ +# events.py +# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors +# +# +# This module is part of SQLAlchemy and is released under +# the MIT License: https://www.opensource.org/licenses/mit-license.php + +"""Core event interfaces.""" + +from __future__ import annotations + +from .engine.events import ConnectionEvents +from .engine.events import DialectEvents +from .pool import PoolResetState +from .pool.events import PoolEvents +from .sql.base import SchemaEventTarget +from .sql.events import DDLEvents diff --git a/venv/Lib/site-packages/sqlalchemy/util/exc.py b/venv/Lib/site-packages/sqlalchemy/util/exc.py new file mode 100644 index 0000000..50288f6 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/exc.py @@ -0,0 +1,833 @@ +# exc.py +# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors +# +# +# This module is part of SQLAlchemy and is released under +# the MIT License: https://www.opensource.org/licenses/mit-license.php + +"""Exceptions used with SQLAlchemy. + +The base exception class is :exc:`.SQLAlchemyError`. Exceptions which are +raised as a result of DBAPI exceptions are all subclasses of +:exc:`.DBAPIError`. + +""" + +from __future__ import annotations + +import typing +from typing import Any +from typing import List +from typing import Optional +from typing import overload +from typing import Tuple +from typing import Type +from typing import Union + +from .util import compat +from .util import preloaded as _preloaded + +if typing.TYPE_CHECKING: + from .engine.interfaces import _AnyExecuteParams + from .engine.interfaces import Dialect + from .sql.compiler import Compiled + from .sql.compiler import TypeCompiler + from .sql.elements import ClauseElement + +if typing.TYPE_CHECKING: + _version_token: str +else: + # set by __init__.py + _version_token = None + + +class HasDescriptionCode: + """helper which adds 'code' as an attribute and '_code_str' as a method""" + + code: Optional[str] = None + + def __init__(self, *arg: Any, **kw: Any): + code = kw.pop("code", None) + if code is not None: + self.code = code + super().__init__(*arg, **kw) + + _what_are_we = "error" + + def _code_str(self) -> str: + if not self.code: + return "" + else: + return ( + f"(Background on this {self._what_are_we} at: " + f"https://sqlalche.me/e/{_version_token}/{self.code})" + ) + + def __str__(self) -> str: + message = super().__str__() + if self.code: + message = "%s %s" % (message, self._code_str()) + return message + + +class SQLAlchemyError(HasDescriptionCode, Exception): + """Generic error class.""" + + def _message(self) -> str: + # rules: + # + # 1. single arg string will usually be a unicode + # object, but since __str__() must return unicode, check for + # bytestring just in case + # + # 2. for multiple self.args, this is not a case in current + # SQLAlchemy though this is happening in at least one known external + # library, call str() which does a repr(). + # + text: str + + if len(self.args) == 1: + arg_text = self.args[0] + + if isinstance(arg_text, bytes): + text = compat.decode_backslashreplace(arg_text, "utf-8") + # This is for when the argument is not a string of any sort. + # Otherwise, converting this exception to string would fail for + # non-string arguments. + else: + text = str(arg_text) + + return text + else: + # this is not a normal case within SQLAlchemy but is here for + # compatibility with Exception.args - the str() comes out as + # a repr() of the tuple + return str(self.args) + + def _sql_message(self) -> str: + message = self._message() + + if self.code: + message = "%s %s" % (message, self._code_str()) + + return message + + def __str__(self) -> str: + return self._sql_message() + + +class ArgumentError(SQLAlchemyError): + """Raised when an invalid or conflicting function argument is supplied. + + This error generally corresponds to construction time state errors. + + """ + + +class DuplicateColumnError(ArgumentError): + """a Column is being added to a Table that would replace another + Column, without appropriate parameters to allow this in place. + + .. versionadded:: 2.0.0b4 + + """ + + +class ObjectNotExecutableError(ArgumentError): + """Raised when an object is passed to .execute() that can't be + executed as SQL. + + """ + + def __init__(self, target: Any): + super().__init__("Not an executable object: %r" % target) + self.target = target + + def __reduce__(self) -> Union[str, Tuple[Any, ...]]: + return self.__class__, (self.target,) + + +class NoSuchModuleError(ArgumentError): + """Raised when a dynamically-loaded module (usually a database dialect) + of a particular name cannot be located.""" + + +class NoForeignKeysError(ArgumentError): + """Raised when no foreign keys can be located between two selectables + during a join.""" + + +class AmbiguousForeignKeysError(ArgumentError): + """Raised when more than one foreign key matching can be located + between two selectables during a join.""" + + +class ConstraintColumnNotFoundError(ArgumentError): + """raised when a constraint refers to a string column name that + is not present in the table being constrained. + + .. versionadded:: 2.0 + + """ + + +class CircularDependencyError(SQLAlchemyError): + """Raised by topological sorts when a circular dependency is detected. + + There are two scenarios where this error occurs: + + * In a Session flush operation, if two objects are mutually dependent + on each other, they can not be inserted or deleted via INSERT or + DELETE statements alone; an UPDATE will be needed to post-associate + or pre-deassociate one of the foreign key constrained values. + The ``post_update`` flag described at :ref:`post_update` can resolve + this cycle. + * In a :attr:`_schema.MetaData.sorted_tables` operation, two + :class:`_schema.ForeignKey` + or :class:`_schema.ForeignKeyConstraint` objects mutually refer to each + other. Apply the ``use_alter=True`` flag to one or both, + see :ref:`use_alter`. + + """ + + def __init__( + self, + message: str, + cycles: Any, + edges: Any, + msg: Optional[str] = None, + code: Optional[str] = None, + ): + if msg is None: + message += " (%s)" % ", ".join(repr(s) for s in cycles) + else: + message = msg + SQLAlchemyError.__init__(self, message, code=code) + self.cycles = cycles + self.edges = edges + + def __reduce__(self) -> Union[str, Tuple[Any, ...]]: + return ( + self.__class__, + (None, self.cycles, self.edges, self.args[0]), + {"code": self.code} if self.code is not None else {}, + ) + + +class CompileError(SQLAlchemyError): + """Raised when an error occurs during SQL compilation""" + + +class UnsupportedCompilationError(CompileError): + """Raised when an operation is not supported by the given compiler. + + .. seealso:: + + :ref:`faq_sql_expression_string` + + :ref:`error_l7de` + """ + + code = "l7de" + + def __init__( + self, + compiler: Union[Compiled, TypeCompiler], + element_type: Type[ClauseElement], + message: Optional[str] = None, + ): + super().__init__( + "Compiler %r can't render element of type %s%s" + % (compiler, element_type, ": %s" % message if message else "") + ) + self.compiler = compiler + self.element_type = element_type + self.message = message + + def __reduce__(self) -> Union[str, Tuple[Any, ...]]: + return self.__class__, (self.compiler, self.element_type, self.message) + + +class IdentifierError(SQLAlchemyError): + """Raised when a schema name is beyond the max character limit""" + + +class DisconnectionError(SQLAlchemyError): + """A disconnect is detected on a raw DB-API connection. + + This error is raised and consumed internally by a connection pool. It can + be raised by the :meth:`_events.PoolEvents.checkout` + event so that the host pool + forces a retry; the exception will be caught three times in a row before + the pool gives up and raises :class:`~sqlalchemy.exc.InvalidRequestError` + regarding the connection attempt. + + """ + + invalidate_pool: bool = False + + +class InvalidatePoolError(DisconnectionError): + """Raised when the connection pool should invalidate all stale connections. + + A subclass of :class:`_exc.DisconnectionError` that indicates that the + disconnect situation encountered on the connection probably means the + entire pool should be invalidated, as the database has been restarted. + + This exception will be handled otherwise the same way as + :class:`_exc.DisconnectionError`, allowing three attempts to reconnect + before giving up. + + .. versionadded:: 1.2 + + """ + + invalidate_pool: bool = True + + +class TimeoutError(SQLAlchemyError): # noqa + """Raised when a connection pool times out on getting a connection.""" + + +class InvalidRequestError(SQLAlchemyError): + """SQLAlchemy was asked to do something it can't do. + + This error generally corresponds to runtime state errors. + + """ + + +class IllegalStateChangeError(InvalidRequestError): + """An object that tracks state encountered an illegal state change + of some kind. + + .. versionadded:: 2.0 + + """ + + +class NoInspectionAvailable(InvalidRequestError): + """A subject passed to :func:`sqlalchemy.inspection.inspect` produced + no context for inspection.""" + + +class PendingRollbackError(InvalidRequestError): + """A transaction has failed and needs to be rolled back before + continuing. + + .. versionadded:: 1.4 + + """ + + +class ResourceClosedError(InvalidRequestError): + """An operation was requested from a connection, cursor, or other + object that's in a closed state.""" + + +class NoSuchColumnError(InvalidRequestError, KeyError): + """A nonexistent column is requested from a ``Row``.""" + + +class NoResultFound(InvalidRequestError): + """A database result was required but none was found. + + + .. versionchanged:: 1.4 This exception is now part of the + ``sqlalchemy.exc`` module in Core, moved from the ORM. The symbol + remains importable from ``sqlalchemy.orm.exc``. + + + """ + + +class MultipleResultsFound(InvalidRequestError): + """A single database result was required but more than one were found. + + .. versionchanged:: 1.4 This exception is now part of the + ``sqlalchemy.exc`` module in Core, moved from the ORM. The symbol + remains importable from ``sqlalchemy.orm.exc``. + + + """ + + +class NoReferenceError(InvalidRequestError): + """Raised by ``ForeignKey`` to indicate a reference cannot be resolved.""" + + table_name: str + + +class AwaitRequired(InvalidRequestError): + """Error raised by the async greenlet spawn if no async operation + was awaited when it required one. + + """ + + code = "xd1r" + + +class MissingGreenlet(InvalidRequestError): + r"""Error raised by the async greenlet await\_ if called while not inside + the greenlet spawn context. + + """ + + code = "xd2s" + + +class NoReferencedTableError(NoReferenceError): + """Raised by ``ForeignKey`` when the referred ``Table`` cannot be + located. + + """ + + def __init__(self, message: str, tname: str): + NoReferenceError.__init__(self, message) + self.table_name = tname + + def __reduce__(self) -> Union[str, Tuple[Any, ...]]: + return self.__class__, (self.args[0], self.table_name) + + +class NoReferencedColumnError(NoReferenceError): + """Raised by ``ForeignKey`` when the referred ``Column`` cannot be + located. + + """ + + def __init__(self, message: str, tname: str, cname: str): + NoReferenceError.__init__(self, message) + self.table_name = tname + self.column_name = cname + + def __reduce__(self) -> Union[str, Tuple[Any, ...]]: + return ( + self.__class__, + (self.args[0], self.table_name, self.column_name), + ) + + +class NoSuchTableError(InvalidRequestError): + """Table does not exist or is not visible to a connection.""" + + +class UnreflectableTableError(InvalidRequestError): + """Table exists but can't be reflected for some reason. + + .. versionadded:: 1.2 + + """ + + +class UnboundExecutionError(InvalidRequestError): + """SQL was attempted without a database connection to execute it on.""" + + +class DontWrapMixin: + """A mixin class which, when applied to a user-defined Exception class, + will not be wrapped inside of :exc:`.StatementError` if the error is + emitted within the process of executing a statement. + + E.g.:: + + from sqlalchemy.exc import DontWrapMixin + + + class MyCustomException(Exception, DontWrapMixin): + pass + + + class MySpecialType(TypeDecorator): + impl = String + + def process_bind_param(self, value, dialect): + if value == "invalid": + raise MyCustomException("invalid!") + + """ + + +class StatementError(SQLAlchemyError): + """An error occurred during execution of a SQL statement. + + :class:`StatementError` wraps the exception raised + during execution, and features :attr:`.statement` + and :attr:`.params` attributes which supply context regarding + the specifics of the statement which had an issue. + + The wrapped exception object is available in + the :attr:`.orig` attribute. + + """ + + statement: Optional[str] = None + """The string SQL statement being invoked when this exception occurred.""" + + params: Optional[_AnyExecuteParams] = None + """The parameter list being used when this exception occurred.""" + + orig: Optional[BaseException] = None + """The original exception that was thrown. + + """ + + ismulti: Optional[bool] = None + """multi parameter passed to repr_params(). None is meaningful.""" + + connection_invalidated: bool = False + + def __init__( + self, + message: str, + statement: Optional[str], + params: Optional[_AnyExecuteParams], + orig: Optional[BaseException], + hide_parameters: bool = False, + code: Optional[str] = None, + ismulti: Optional[bool] = None, + ): + SQLAlchemyError.__init__(self, message, code=code) + self.statement = statement + self.params = params + self.orig = orig + self.ismulti = ismulti + self.hide_parameters = hide_parameters + self.detail: List[str] = [] + + def add_detail(self, msg: str) -> None: + self.detail.append(msg) + + def __reduce__(self) -> Union[str, Tuple[Any, ...]]: + return ( + self.__class__, + ( + self.args[0], + self.statement, + self.params, + self.orig, + self.hide_parameters, + self.__dict__.get("code"), + self.ismulti, + ), + {"detail": self.detail}, + ) + + @_preloaded.preload_module("sqlalchemy.sql.util") + def _sql_message(self) -> str: + util = _preloaded.sql_util + + details = [self._message()] + if self.statement: + stmt_detail = "[SQL: %s]" % self.statement + details.append(stmt_detail) + if self.params: + if self.hide_parameters: + details.append( + "[SQL parameters hidden due to hide_parameters=True]" + ) + else: + params_repr = util._repr_params( + self.params, 10, ismulti=self.ismulti + ) + details.append("[parameters: %r]" % params_repr) + code_str = self._code_str() + if code_str: + details.append(code_str) + return "\n".join(["(%s)" % det for det in self.detail] + details) + + +class DBAPIError(StatementError): + """Raised when the execution of a database operation fails. + + Wraps exceptions raised by the DB-API underlying the + database operation. Driver-specific implementations of the standard + DB-API exception types are wrapped by matching sub-types of SQLAlchemy's + :class:`DBAPIError` when possible. DB-API's ``Error`` type maps to + :class:`DBAPIError` in SQLAlchemy, otherwise the names are identical. Note + that there is no guarantee that different DB-API implementations will + raise the same exception type for any given error condition. + + :class:`DBAPIError` features :attr:`~.StatementError.statement` + and :attr:`~.StatementError.params` attributes which supply context + regarding the specifics of the statement which had an issue, for the + typical case when the error was raised within the context of + emitting a SQL statement. + + The wrapped exception object is available in the + :attr:`~.StatementError.orig` attribute. Its type and properties are + DB-API implementation specific. + + """ + + code = "dbapi" + + @overload + @classmethod + def instance( + cls, + statement: Optional[str], + params: Optional[_AnyExecuteParams], + orig: Exception, + dbapi_base_err: Type[Exception], + hide_parameters: bool = False, + connection_invalidated: bool = False, + dialect: Optional[Dialect] = None, + ismulti: Optional[bool] = None, + ) -> StatementError: ... + + @overload + @classmethod + def instance( + cls, + statement: Optional[str], + params: Optional[_AnyExecuteParams], + orig: DontWrapMixin, + dbapi_base_err: Type[Exception], + hide_parameters: bool = False, + connection_invalidated: bool = False, + dialect: Optional[Dialect] = None, + ismulti: Optional[bool] = None, + ) -> DontWrapMixin: ... + + @overload + @classmethod + def instance( + cls, + statement: Optional[str], + params: Optional[_AnyExecuteParams], + orig: BaseException, + dbapi_base_err: Type[Exception], + hide_parameters: bool = False, + connection_invalidated: bool = False, + dialect: Optional[Dialect] = None, + ismulti: Optional[bool] = None, + ) -> BaseException: ... + + @classmethod + def instance( + cls, + statement: Optional[str], + params: Optional[_AnyExecuteParams], + orig: Union[BaseException, DontWrapMixin], + dbapi_base_err: Type[Exception], + hide_parameters: bool = False, + connection_invalidated: bool = False, + dialect: Optional[Dialect] = None, + ismulti: Optional[bool] = None, + ) -> Union[BaseException, DontWrapMixin]: + # Don't ever wrap these, just return them directly as if + # DBAPIError didn't exist. + if ( + isinstance(orig, BaseException) and not isinstance(orig, Exception) + ) or isinstance(orig, DontWrapMixin): + return orig + + if orig is not None: + # not a DBAPI error, statement is present. + # raise a StatementError + if isinstance(orig, SQLAlchemyError) and statement: + return StatementError( + "(%s.%s) %s" + % ( + orig.__class__.__module__, + orig.__class__.__name__, + orig.args[0], + ), + statement, + params, + orig, + hide_parameters=hide_parameters, + code=orig.code, + ismulti=ismulti, + ) + elif not isinstance(orig, dbapi_base_err) and statement: + return StatementError( + "(%s.%s) %s" + % ( + orig.__class__.__module__, + orig.__class__.__name__, + orig, + ), + statement, + params, + orig, + hide_parameters=hide_parameters, + ismulti=ismulti, + ) + + glob = globals() + for super_ in orig.__class__.__mro__: + name = super_.__name__ + if dialect: + name = dialect.dbapi_exception_translation_map.get( + name, name + ) + if name in glob and issubclass(glob[name], DBAPIError): + cls = glob[name] + break + + return cls( + statement, + params, + orig, + connection_invalidated=connection_invalidated, + hide_parameters=hide_parameters, + code=cls.code, + ismulti=ismulti, + ) + + def __reduce__(self) -> Union[str, Tuple[Any, ...]]: + return ( + self.__class__, + ( + self.statement, + self.params, + self.orig, + self.hide_parameters, + self.connection_invalidated, + self.__dict__.get("code"), + self.ismulti, + ), + {"detail": self.detail}, + ) + + def __init__( + self, + statement: Optional[str], + params: Optional[_AnyExecuteParams], + orig: BaseException, + hide_parameters: bool = False, + connection_invalidated: bool = False, + code: Optional[str] = None, + ismulti: Optional[bool] = None, + ): + try: + text = str(orig) + except Exception as e: + text = "Error in str() of DB-API-generated exception: " + str(e) + StatementError.__init__( + self, + "(%s.%s) %s" + % (orig.__class__.__module__, orig.__class__.__name__, text), + statement, + params, + orig, + hide_parameters, + code=code, + ismulti=ismulti, + ) + self.connection_invalidated = connection_invalidated + + +class InterfaceError(DBAPIError): + """Wraps a DB-API InterfaceError.""" + + code = "rvf5" + + +class DatabaseError(DBAPIError): + """Wraps a DB-API DatabaseError.""" + + code = "4xp6" + + +class DataError(DatabaseError): + """Wraps a DB-API DataError.""" + + code = "9h9h" + + +class OperationalError(DatabaseError): + """Wraps a DB-API OperationalError.""" + + code = "e3q8" + + +class IntegrityError(DatabaseError): + """Wraps a DB-API IntegrityError.""" + + code = "gkpj" + + +class InternalError(DatabaseError): + """Wraps a DB-API InternalError.""" + + code = "2j85" + + +class ProgrammingError(DatabaseError): + """Wraps a DB-API ProgrammingError.""" + + code = "f405" + + +class NotSupportedError(DatabaseError): + """Wraps a DB-API NotSupportedError.""" + + code = "tw8g" + + +# Warnings + + +class SATestSuiteWarning(Warning): + """warning for a condition detected during tests that is non-fatal + + Currently outside of SAWarning so that we can work around tools like + Alembic doing the wrong thing with warnings. + + """ + + +class SADeprecationWarning(HasDescriptionCode, DeprecationWarning): + """Issued for usage of deprecated APIs.""" + + deprecated_since: Optional[str] = None + "Indicates the version that started raising this deprecation warning" + + +class Base20DeprecationWarning(SADeprecationWarning): + """Issued for usage of APIs specifically deprecated or legacy in + SQLAlchemy 2.0. + + .. seealso:: + + :ref:`error_b8d9`. + + :ref:`deprecation_20_mode` + + """ + + deprecated_since: Optional[str] = "1.4" + "Indicates the version that started raising this deprecation warning" + + def __str__(self) -> str: + return ( + super().__str__() + + " (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)" + ) + + +class LegacyAPIWarning(Base20DeprecationWarning): + """indicates an API that is in 'legacy' status, a long term deprecation.""" + + +class MovedIn20Warning(Base20DeprecationWarning): + """Subtype of RemovedIn20Warning to indicate an API that moved only.""" + + +class SAPendingDeprecationWarning(PendingDeprecationWarning): + """A similar warning as :class:`_exc.SADeprecationWarning`, this warning + is not used in modern versions of SQLAlchemy. + + """ + + deprecated_since: Optional[str] = None + "Indicates the version that started raising this deprecation warning" + + +class SAWarning(HasDescriptionCode, RuntimeWarning): + """Issued at runtime.""" + + _what_are_we = "warning" diff --git a/venv/Lib/site-packages/sqlalchemy/util/inspection.py b/venv/Lib/site-packages/sqlalchemy/util/inspection.py new file mode 100644 index 0000000..8932b1b --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/inspection.py @@ -0,0 +1,175 @@ +# inspection.py +# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors +# +# +# This module is part of SQLAlchemy and is released under +# the MIT License: https://www.opensource.org/licenses/mit-license.php + +"""The inspection module provides the :func:`_sa.inspect` function, +which delivers runtime information about a wide variety +of SQLAlchemy objects, both within the Core as well as the +ORM. + +The :func:`_sa.inspect` function is the entry point to SQLAlchemy's +public API for viewing the configuration and construction +of in-memory objects. Depending on the type of object +passed to :func:`_sa.inspect`, the return value will either be +a related object which provides a known interface, or in many +cases it will return the object itself. + +The rationale for :func:`_sa.inspect` is twofold. One is that +it replaces the need to be aware of a large variety of "information +getting" functions in SQLAlchemy, such as +:meth:`_reflection.Inspector.from_engine` (deprecated in 1.4), +:func:`.orm.attributes.instance_state`, :func:`_orm.class_mapper`, +and others. The other is that the return value of :func:`_sa.inspect` +is guaranteed to obey a documented API, thus allowing third party +tools which build on top of SQLAlchemy configurations to be constructed +in a forwards-compatible way. + +""" + +from __future__ import annotations + +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generic +from typing import Optional +from typing import overload +from typing import Type +from typing import TypeVar +from typing import Union + +from . import exc +from .util.typing import Literal +from .util.typing import Protocol + +_T = TypeVar("_T", bound=Any) +_TCov = TypeVar("_TCov", bound=Any, covariant=True) +_F = TypeVar("_F", bound=Callable[..., Any]) + +_IN = TypeVar("_IN", bound=Any) + +_registrars: Dict[type, Union[Literal[True], Callable[[Any], Any]]] = {} + + +class Inspectable(Generic[_T]): + """define a class as inspectable. + + This allows typing to set up a linkage between an object that + can be inspected and the type of inspection it returns. + + Unfortunately we cannot at the moment get all classes that are + returned by inspection to suit this interface as we get into + MRO issues. + + """ + + __slots__ = () + + +class _InspectableTypeProtocol(Protocol[_TCov]): + """a protocol defining a method that's used when a type (ie the class + itself) is passed to inspect(). + + """ + + def _sa_inspect_type(self) -> _TCov: ... + + +class _InspectableProtocol(Protocol[_TCov]): + """a protocol defining a method that's used when an instance is + passed to inspect(). + + """ + + def _sa_inspect_instance(self) -> _TCov: ... + + +@overload +def inspect( + subject: Type[_InspectableTypeProtocol[_IN]], raiseerr: bool = True +) -> _IN: ... + + +@overload +def inspect( + subject: _InspectableProtocol[_IN], raiseerr: bool = True +) -> _IN: ... + + +@overload +def inspect(subject: Inspectable[_IN], raiseerr: bool = True) -> _IN: ... + + +@overload +def inspect(subject: Any, raiseerr: Literal[False] = ...) -> Optional[Any]: ... + + +@overload +def inspect(subject: Any, raiseerr: bool = True) -> Any: ... + + +def inspect(subject: Any, raiseerr: bool = True) -> Any: + """Produce an inspection object for the given target. + + The returned value in some cases may be the + same object as the one given, such as if a + :class:`_orm.Mapper` object is passed. In other + cases, it will be an instance of the registered + inspection type for the given object, such as + if an :class:`_engine.Engine` is passed, an + :class:`_reflection.Inspector` object is returned. + + :param subject: the subject to be inspected. + :param raiseerr: When ``True``, if the given subject + does not + correspond to a known SQLAlchemy inspected type, + :class:`sqlalchemy.exc.NoInspectionAvailable` + is raised. If ``False``, ``None`` is returned. + + """ + type_ = type(subject) + for cls in type_.__mro__: + if cls in _registrars: + reg = _registrars.get(cls, None) + if reg is None: + continue + elif reg is True: + return subject + ret = reg(subject) + if ret is not None: + return ret + else: + reg = ret = None + + if raiseerr and (reg is None or ret is None): + raise exc.NoInspectionAvailable( + "No inspection system is " + "available for object of type %s" % type_ + ) + return ret + + +def _inspects( + *types: Type[Any], +) -> Callable[[_F], _F]: + def decorate(fn_or_cls: _F) -> _F: + for type_ in types: + if type_ in _registrars: + raise AssertionError("Type %s is already registered" % type_) + _registrars[type_] = fn_or_cls + return fn_or_cls + + return decorate + + +_TT = TypeVar("_TT", bound="Type[Any]") + + +def _self_inspects(cls: _TT) -> _TT: + if cls in _registrars: + raise AssertionError("Type %s is already registered" % cls) + _registrars[cls] = True + return cls diff --git a/venv/Lib/site-packages/sqlalchemy/util/log.py b/venv/Lib/site-packages/sqlalchemy/util/log.py new file mode 100644 index 0000000..d6b7644 --- /dev/null +++ b/venv/Lib/site-packages/sqlalchemy/util/log.py @@ -0,0 +1,288 @@ +# log.py +# Copyright (C) 2006-2026 the SQLAlchemy authors and contributors +# +# Includes alterations by Vinay Sajip vinay_sajip@yahoo.co.uk +# +# This module is part of SQLAlchemy and is released under +# the MIT License: https://www.opensource.org/licenses/mit-license.php + +"""Logging control and utilities. + +Control of logging for SA can be performed from the regular python logging +module. The regular dotted module namespace is used, starting at +'sqlalchemy'. For class-level logging, the class name is appended. + +The "echo" keyword parameter, available on SQLA :class:`_engine.Engine` +and :class:`_pool.Pool` objects, corresponds to a logger specific to that +instance only. + +""" + +from __future__ import annotations + +import logging +import sys +from typing import Any +from typing import Optional +from typing import overload +from typing import Set +from typing import Type +from typing import TypeVar +from typing import Union + +from .util import py311 +from .util import py38 +from .util.typing import Literal + +if py38: + STACKLEVEL = True + # needed as of py3.11.0b1 + # #8019 + STACKLEVEL_OFFSET = 2 if py311 else 1 +else: + STACKLEVEL = False + STACKLEVEL_OFFSET = 0 + +_IT = TypeVar("_IT", bound="Identified") + +_EchoFlagType = Union[None, bool, Literal["debug"]] + +# set initial level to WARN. This so that +# log statements don't occur in the absence of explicit +# logging being enabled for 'sqlalchemy'. +rootlogger = logging.getLogger("sqlalchemy") +if rootlogger.level == logging.NOTSET: + rootlogger.setLevel(logging.WARNING) + + +def _add_default_handler(logger: logging.Logger) -> None: + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter( + logging.Formatter("%(asctime)s %(levelname)s %(name)s %(message)s") + ) + logger.addHandler(handler) + + +_logged_classes: Set[Type[Identified]] = set() + + +def _qual_logger_name_for_cls(cls: Type[Identified]) -> str: + return ( + getattr(cls, "_sqla_logger_namespace", None) + or cls.__module__ + "." + cls.__name__ + ) + + +def class_logger(cls: Type[_IT]) -> Type[_IT]: + logger = logging.getLogger(_qual_logger_name_for_cls(cls)) + cls._should_log_debug = lambda self: logger.isEnabledFor( # type: ignore[method-assign] # noqa: E501 + logging.DEBUG + ) + cls._should_log_info = lambda self: logger.isEnabledFor( # type: ignore[method-assign] # noqa: E501 + logging.INFO + ) + cls.logger = logger + _logged_classes.add(cls) + return cls + + +_IdentifiedLoggerType = Union[logging.Logger, "InstanceLogger"] + + +class Identified: + __slots__ = () + + logging_name: Optional[str] = None + + logger: _IdentifiedLoggerType + + _echo: _EchoFlagType + + def _should_log_debug(self) -> bool: + return self.logger.isEnabledFor(logging.DEBUG) + + def _should_log_info(self) -> bool: + return self.logger.isEnabledFor(logging.INFO) + + +class InstanceLogger: + """A logger adapter (wrapper) for :class:`.Identified` subclasses. + + This allows multiple instances (e.g. Engine or Pool instances) + to share a logger, but have its verbosity controlled on a + per-instance basis. + + The basic functionality is to return a logging level + which is based on an instance's echo setting. + + Default implementation is: + + 'debug' -> logging.DEBUG + True -> logging.INFO + False -> Effective level of underlying logger ( + logging.WARNING by default) + None -> same as False + """ + + # Map echo settings to logger levels + _echo_map = { + None: logging.NOTSET, + False: logging.NOTSET, + True: logging.INFO, + "debug": logging.DEBUG, + } + + _echo: _EchoFlagType + + __slots__ = ("echo", "logger") + + def __init__(self, echo: _EchoFlagType, name: str): + self.echo = echo + self.logger = logging.getLogger(name) + + # if echo flag is enabled and no handlers, + # add a handler to the list + if self._echo_map[echo] <= logging.INFO and not self.logger.handlers: + _add_default_handler(self.logger) + + # + # Boilerplate convenience methods + # + def debug(self, msg: str, *args: Any, **kwargs: Any) -> None: + """Delegate a debug call to the underlying logger.""" + + self.log(logging.DEBUG, msg, *args, **kwargs) + + def info(self, msg: str, *args: Any, **kwargs: Any) -> None: + """Delegate an info call to the underlying logger.""" + + self.log(logging.INFO, msg, *args, **kwargs) + + def warning(self, msg: str, *args: Any, **kwargs: Any) -> None: + """Delegate a warning call to the underlying logger.""" + + self.log(logging.WARNING, msg, *args, **kwargs) + + warn = warning + + def error(self, msg: str, *args: Any, **kwargs: Any) -> None: + """ + Delegate an error call to the underlying logger. + """ + self.log(logging.ERROR, msg, *args, **kwargs) + + def exception(self, msg: str, *args: Any, **kwargs: Any) -> None: + """Delegate an exception call to the underlying logger.""" + + kwargs["exc_info"] = 1 + self.log(logging.ERROR, msg, *args, **kwargs) + + def critical(self, msg: str, *args: Any, **kwargs: Any) -> None: + """Delegate a critical call to the underlying logger.""" + + self.log(logging.CRITICAL, msg, *args, **kwargs) + + def log(self, level: int, msg: str, *args: Any, **kwargs: Any) -> None: + """Delegate a log call to the underlying logger. + + The level here is determined by the echo + flag as well as that of the underlying logger, and + logger._log() is called directly. + + """ + + # inline the logic from isEnabledFor(), + # getEffectiveLevel(), to avoid overhead. + + if self.logger.manager.disable >= level: + return + + selected_level = self._echo_map[self.echo] + if selected_level == logging.NOTSET: + selected_level = self.logger.getEffectiveLevel() + + if level >= selected_level: + if STACKLEVEL: + kwargs["stacklevel"] = ( + kwargs.get("stacklevel", 1) + STACKLEVEL_OFFSET + ) + + self.logger._log(level, msg, args, **kwargs) + + def isEnabledFor(self, level: int) -> bool: + """Is this logger enabled for level 'level'?""" + + if self.logger.manager.disable >= level: + return False + return level >= self.getEffectiveLevel() + + def getEffectiveLevel(self) -> int: + """What's the effective level for this logger?""" + + level = self._echo_map[self.echo] + if level == logging.NOTSET: + level = self.logger.getEffectiveLevel() + return level + + +def instance_logger( + instance: Identified, echoflag: _EchoFlagType = None +) -> None: + """create a logger for an instance that implements :class:`.Identified`.""" + + if instance.logging_name: + name = "%s.%s" % ( + _qual_logger_name_for_cls(instance.__class__), + instance.logging_name, + ) + else: + name = _qual_logger_name_for_cls(instance.__class__) + + instance._echo = echoflag # type: ignore + + logger: Union[logging.Logger, InstanceLogger] + + if echoflag in (False, None): + # if no echo setting or False, return a Logger directly, + # avoiding overhead of filtering + logger = logging.getLogger(name) + else: + # if a specified echo flag, return an EchoLogger, + # which checks the flag, overrides normal log + # levels by calling logger._log() + logger = InstanceLogger(echoflag, name) + + instance.logger = logger # type: ignore + + +class echo_property: + __doc__ = """\ + When ``True``, enable log output for this element. + + This has the effect of setting the Python logging level for the namespace + of this element's class and object reference. A value of boolean ``True`` + indicates that the loglevel ``logging.INFO`` will be set for the logger, + whereas the string value ``debug`` will set the loglevel to + ``logging.DEBUG``. + """ + + @overload + def __get__( + self, instance: Literal[None], owner: Type[Identified] + ) -> echo_property: ... + + @overload + def __get__( + self, instance: Identified, owner: Type[Identified] + ) -> _EchoFlagType: ... + + def __get__( + self, instance: Optional[Identified], owner: Type[Identified] + ) -> Union[echo_property, _EchoFlagType]: + if instance is None: + return self + else: + return instance._echo + + def __set__(self, instance: Identified, value: _EchoFlagType) -> None: + instance_logger(instance, echoflag=value)