diff options
author | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:17:55 -0400 |
---|---|---|
committer | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:17:55 -0400 |
commit | 12cf076118570eebbff08c6b3090e0d4798447a1 (patch) | |
tree | 3ba25e17e3c3a5e82316558ba3864b955919ff72 /venv/lib/python3.11/site-packages/sqlalchemy/testing | |
parent | c45662ff3923b34614ddcc8feb9195541166dcc5 (diff) |
no venv
Diffstat (limited to 'venv/lib/python3.11/site-packages/sqlalchemy/testing')
76 files changed, 0 insertions, 20469 deletions
diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__init__.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__init__.py deleted file mode 100644 index d3a6f32..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__init__.py +++ /dev/null @@ -1,95 +0,0 @@ -# testing/__init__.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from unittest import mock - -from . import config -from .assertions import assert_raises -from .assertions import assert_raises_context_ok -from .assertions import assert_raises_message -from .assertions import assert_raises_message_context_ok -from .assertions import assert_warns -from .assertions import assert_warns_message -from .assertions import AssertsCompiledSQL -from .assertions import AssertsExecutionResults -from .assertions import ComparesIndexes -from .assertions import ComparesTables -from .assertions import emits_warning -from .assertions import emits_warning_on -from .assertions import eq_ -from .assertions import eq_ignore_whitespace -from .assertions import eq_regex -from .assertions import expect_deprecated -from .assertions import expect_deprecated_20 -from .assertions import expect_raises -from .assertions import expect_raises_message -from .assertions import expect_warnings -from .assertions import in_ -from .assertions import int_within_variance -from .assertions import is_ -from .assertions import is_false -from .assertions import is_instance_of -from .assertions import is_none -from .assertions import is_not -from .assertions import is_not_ -from .assertions import is_not_none -from .assertions import is_true -from .assertions import le_ -from .assertions import ne_ -from .assertions import not_in -from .assertions import not_in_ -from .assertions import startswith_ -from .assertions import uses_deprecated -from .config import add_to_marker -from .config import async_test -from .config import combinations -from .config import combinations_list -from .config import db -from .config import fixture -from .config import requirements as requires -from .config import skip_test -from .config import Variation -from .config import variation -from .config import variation_fixture -from .exclusions import _is_excluded -from .exclusions import _server_version -from .exclusions import against as _against -from .exclusions import db_spec -from .exclusions import exclude -from .exclusions import fails -from .exclusions import fails_if -from .exclusions import fails_on -from .exclusions import fails_on_everything_except -from .exclusions import future -from .exclusions import only_if -from .exclusions import only_on -from .exclusions import skip -from .exclusions import skip_if -from .schema import eq_clause_element -from .schema import eq_type_affinity -from .util import adict -from .util import fail -from .util import flag_combinations -from .util import force_drop_names -from .util import lambda_combinations -from .util import metadata_fixture -from .util import provide_metadata -from .util import resolve_lambda -from .util import rowset -from .util import run_as_contextmanager -from .util import teardown_events -from .warnings import assert_warnings -from .warnings import warn_test_suite - - -def against(*queries): - return _against(config._current, *queries) - - -crashes = skip diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/__init__.cpython-311.pyc Binary files differdeleted file mode 100644 index 7f6a336..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/__init__.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/assertions.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/assertions.cpython-311.pyc Binary files differdeleted file mode 100644 index 22f91d1..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/assertions.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/assertsql.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/assertsql.cpython-311.pyc Binary files differdeleted file mode 100644 index c435ea0..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/assertsql.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/asyncio.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/asyncio.cpython-311.pyc Binary files differdeleted file mode 100644 index f460eeb..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/asyncio.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/config.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/config.cpython-311.pyc Binary files differdeleted file mode 100644 index d420c76..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/config.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/engines.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/engines.cpython-311.pyc Binary files differdeleted file mode 100644 index e21f79d..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/engines.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/entities.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/entities.cpython-311.pyc Binary files differdeleted file mode 100644 index 611fd62..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/entities.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/exclusions.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/exclusions.cpython-311.pyc Binary files differdeleted file mode 100644 index cfa8c09..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/exclusions.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/pickleable.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/pickleable.cpython-311.pyc Binary files differdeleted file mode 100644 index ee832ea..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/pickleable.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/profiling.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/profiling.cpython-311.pyc Binary files differdeleted file mode 100644 index cabd6ee..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/profiling.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/provision.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/provision.cpython-311.pyc Binary files differdeleted file mode 100644 index 5da4c30..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/provision.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/requirements.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/requirements.cpython-311.pyc Binary files differdeleted file mode 100644 index b87b700..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/requirements.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/schema.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/schema.cpython-311.pyc Binary files differdeleted file mode 100644 index b64caf4..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/schema.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/util.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/util.cpython-311.pyc Binary files differdeleted file mode 100644 index 33f6afa..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/util.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/warnings.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/warnings.cpython-311.pyc Binary files differdeleted file mode 100644 index 67682c8..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/__pycache__/warnings.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/assertions.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/assertions.py deleted file mode 100644 index baef79d..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/assertions.py +++ /dev/null @@ -1,989 +0,0 @@ -# testing/assertions.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from __future__ import annotations - -from collections import defaultdict -import contextlib -from copy import copy -from itertools import filterfalse -import re -import sys -import warnings - -from . import assertsql -from . import config -from . import engines -from . import mock -from .exclusions import db_spec -from .util import fail -from .. import exc as sa_exc -from .. import schema -from .. import sql -from .. import types as sqltypes -from .. import util -from ..engine import default -from ..engine import url -from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL -from ..util import decorator - - -def expect_warnings(*messages, **kw): - """Context manager which expects one or more warnings. - - With no arguments, squelches all SAWarning emitted via - sqlalchemy.util.warn and sqlalchemy.util.warn_limited. Otherwise - pass string expressions that will match selected warnings via regex; - all non-matching warnings are sent through. - - The expect version **asserts** that the warnings were in fact seen. - - Note that the test suite sets SAWarning warnings to raise exceptions. - - """ # noqa - return _expect_warnings_sqla_only(sa_exc.SAWarning, messages, **kw) - - -@contextlib.contextmanager -def expect_warnings_on(db, *messages, **kw): - """Context manager which expects one or more warnings on specific - dialects. - - The expect version **asserts** that the warnings were in fact seen. - - """ - spec = db_spec(db) - - if isinstance(db, str) and not spec(config._current): - yield - else: - with expect_warnings(*messages, **kw): - yield - - -def emits_warning(*messages): - """Decorator form of expect_warnings(). - - Note that emits_warning does **not** assert that the warnings - were in fact seen. - - """ - - @decorator - def decorate(fn, *args, **kw): - with expect_warnings(assert_=False, *messages): - return fn(*args, **kw) - - return decorate - - -def expect_deprecated(*messages, **kw): - return _expect_warnings_sqla_only( - sa_exc.SADeprecationWarning, messages, **kw - ) - - -def expect_deprecated_20(*messages, **kw): - return _expect_warnings_sqla_only( - sa_exc.Base20DeprecationWarning, messages, **kw - ) - - -def emits_warning_on(db, *messages): - """Mark a test as emitting a warning on a specific dialect. - - With no arguments, squelches all SAWarning failures. Or pass one or more - strings; these will be matched to the root of the warning description by - warnings.filterwarnings(). - - Note that emits_warning_on does **not** assert that the warnings - were in fact seen. - - """ - - @decorator - def decorate(fn, *args, **kw): - with expect_warnings_on(db, assert_=False, *messages): - return fn(*args, **kw) - - return decorate - - -def uses_deprecated(*messages): - """Mark a test as immune from fatal deprecation warnings. - - With no arguments, squelches all SADeprecationWarning failures. - Or pass one or more strings; these will be matched to the root - of the warning description by warnings.filterwarnings(). - - As a special case, you may pass a function name prefixed with // - and it will be re-written as needed to match the standard warning - verbiage emitted by the sqlalchemy.util.deprecated decorator. - - Note that uses_deprecated does **not** assert that the warnings - were in fact seen. - - """ - - @decorator - def decorate(fn, *args, **kw): - with expect_deprecated(*messages, assert_=False): - return fn(*args, **kw) - - return decorate - - -_FILTERS = None -_SEEN = None -_EXC_CLS = None - - -def _expect_warnings_sqla_only( - exc_cls, - messages, - regex=True, - search_msg=False, - assert_=True, -): - """SQLAlchemy internal use only _expect_warnings(). - - Alembic is using _expect_warnings() directly, and should be updated - to use this new interface. - - """ - return _expect_warnings( - exc_cls, - messages, - regex=regex, - search_msg=search_msg, - assert_=assert_, - raise_on_any_unexpected=True, - ) - - -@contextlib.contextmanager -def _expect_warnings( - exc_cls, - messages, - regex=True, - search_msg=False, - assert_=True, - raise_on_any_unexpected=False, - squelch_other_warnings=False, -): - global _FILTERS, _SEEN, _EXC_CLS - - if regex or search_msg: - filters = [re.compile(msg, re.I | re.S) for msg in messages] - else: - filters = list(messages) - - if _FILTERS is not None: - # nested call; update _FILTERS and _SEEN, return. outer - # block will assert our messages - assert _SEEN is not None - assert _EXC_CLS is not None - _FILTERS.extend(filters) - _SEEN.update(filters) - _EXC_CLS += (exc_cls,) - yield - else: - seen = _SEEN = set(filters) - _FILTERS = filters - _EXC_CLS = (exc_cls,) - - if raise_on_any_unexpected: - - def real_warn(msg, *arg, **kw): - raise AssertionError("Got unexpected warning: %r" % msg) - - else: - real_warn = warnings.warn - - def our_warn(msg, *arg, **kw): - if isinstance(msg, _EXC_CLS): - exception = type(msg) - msg = str(msg) - elif arg: - exception = arg[0] - else: - exception = None - - if not exception or not issubclass(exception, _EXC_CLS): - if not squelch_other_warnings: - return real_warn(msg, *arg, **kw) - else: - return - - if not filters and not raise_on_any_unexpected: - return - - for filter_ in filters: - if ( - (search_msg and filter_.search(msg)) - or (regex and filter_.match(msg)) - or (not regex and filter_ == msg) - ): - seen.discard(filter_) - break - else: - if not squelch_other_warnings: - real_warn(msg, *arg, **kw) - - with mock.patch("warnings.warn", our_warn): - try: - yield - finally: - _SEEN = _FILTERS = _EXC_CLS = None - - if assert_: - assert not seen, "Warnings were not seen: %s" % ", ".join( - "%r" % (s.pattern if regex else s) for s in seen - ) - - -def global_cleanup_assertions(): - """Check things that have to be finalized at the end of a test suite. - - Hardcoded at the moment, a modular system can be built here - to support things like PG prepared transactions, tables all - dropped, etc. - - """ - _assert_no_stray_pool_connections() - - -def _assert_no_stray_pool_connections(): - engines.testing_reaper.assert_all_closed() - - -def int_within_variance(expected, received, variance): - deviance = int(expected * variance) - assert ( - abs(received - expected) < deviance - ), "Given int value %s is not within %d%% of expected value %s" % ( - received, - variance * 100, - expected, - ) - - -def eq_regex(a, b, msg=None): - assert re.match(b, a), msg or "%r !~ %r" % (a, b) - - -def eq_(a, b, msg=None): - """Assert a == b, with repr messaging on failure.""" - assert a == b, msg or "%r != %r" % (a, b) - - -def ne_(a, b, msg=None): - """Assert a != b, with repr messaging on failure.""" - assert a != b, msg or "%r == %r" % (a, b) - - -def le_(a, b, msg=None): - """Assert a <= b, with repr messaging on failure.""" - assert a <= b, msg or "%r != %r" % (a, b) - - -def is_instance_of(a, b, msg=None): - assert isinstance(a, b), msg or "%r is not an instance of %r" % (a, b) - - -def is_none(a, msg=None): - is_(a, None, msg=msg) - - -def is_not_none(a, msg=None): - is_not(a, None, msg=msg) - - -def is_true(a, msg=None): - is_(bool(a), True, msg=msg) - - -def is_false(a, msg=None): - is_(bool(a), False, msg=msg) - - -def is_(a, b, msg=None): - """Assert a is b, with repr messaging on failure.""" - assert a is b, msg or "%r is not %r" % (a, b) - - -def is_not(a, b, msg=None): - """Assert a is not b, with repr messaging on failure.""" - assert a is not b, msg or "%r is %r" % (a, b) - - -# deprecated. See #5429 -is_not_ = is_not - - -def in_(a, b, msg=None): - """Assert a in b, with repr messaging on failure.""" - assert a in b, msg or "%r not in %r" % (a, b) - - -def not_in(a, b, msg=None): - """Assert a in not b, with repr messaging on failure.""" - assert a not in b, msg or "%r is in %r" % (a, b) - - -# deprecated. See #5429 -not_in_ = not_in - - -def startswith_(a, fragment, msg=None): - """Assert a.startswith(fragment), with repr messaging on failure.""" - assert a.startswith(fragment), msg or "%r does not start with %r" % ( - a, - fragment, - ) - - -def eq_ignore_whitespace(a, b, msg=None): - a = re.sub(r"^\s+?|\n", "", a) - a = re.sub(r" {2,}", " ", a) - a = re.sub(r"\t", "", a) - b = re.sub(r"^\s+?|\n", "", b) - b = re.sub(r" {2,}", " ", b) - b = re.sub(r"\t", "", b) - - assert a == b, msg or "%r != %r" % (a, b) - - -def _assert_proper_exception_context(exception): - """assert that any exception we're catching does not have a __context__ - without a __cause__, and that __suppress_context__ is never set. - - Python 3 will report nested as exceptions as "during the handling of - error X, error Y occurred". That's not what we want to do. we want - these exceptions in a cause chain. - - """ - - if ( - exception.__context__ is not exception.__cause__ - and not exception.__suppress_context__ - ): - assert False, ( - "Exception %r was correctly raised but did not set a cause, " - "within context %r as its cause." - % (exception, exception.__context__) - ) - - -def assert_raises(except_cls, callable_, *args, **kw): - return _assert_raises(except_cls, callable_, args, kw, check_context=True) - - -def assert_raises_context_ok(except_cls, callable_, *args, **kw): - return _assert_raises(except_cls, callable_, args, kw) - - -def assert_raises_message(except_cls, msg, callable_, *args, **kwargs): - return _assert_raises( - except_cls, callable_, args, kwargs, msg=msg, check_context=True - ) - - -def assert_warns(except_cls, callable_, *args, **kwargs): - """legacy adapter function for functions that were previously using - assert_raises with SAWarning or similar. - - has some workarounds to accommodate the fact that the callable completes - with this approach rather than stopping at the exception raise. - - - """ - with _expect_warnings_sqla_only(except_cls, [".*"]): - return callable_(*args, **kwargs) - - -def assert_warns_message(except_cls, msg, callable_, *args, **kwargs): - """legacy adapter function for functions that were previously using - assert_raises with SAWarning or similar. - - has some workarounds to accommodate the fact that the callable completes - with this approach rather than stopping at the exception raise. - - Also uses regex.search() to match the given message to the error string - rather than regex.match(). - - """ - with _expect_warnings_sqla_only( - except_cls, - [msg], - search_msg=True, - regex=False, - ): - return callable_(*args, **kwargs) - - -def assert_raises_message_context_ok( - except_cls, msg, callable_, *args, **kwargs -): - return _assert_raises(except_cls, callable_, args, kwargs, msg=msg) - - -def _assert_raises( - except_cls, callable_, args, kwargs, msg=None, check_context=False -): - with _expect_raises(except_cls, msg, check_context) as ec: - callable_(*args, **kwargs) - return ec.error - - -class _ErrorContainer: - error = None - - -@contextlib.contextmanager -def _expect_raises(except_cls, msg=None, check_context=False): - if ( - isinstance(except_cls, type) - and issubclass(except_cls, Warning) - or isinstance(except_cls, Warning) - ): - raise TypeError( - "Use expect_warnings for warnings, not " - "expect_raises / assert_raises" - ) - ec = _ErrorContainer() - if check_context: - are_we_already_in_a_traceback = sys.exc_info()[0] - try: - yield ec - success = False - except except_cls as err: - ec.error = err - success = True - if msg is not None: - # I'm often pdbing here, and "err" above isn't - # in scope, so assign the string explicitly - error_as_string = str(err) - assert re.search(msg, error_as_string, re.UNICODE), "%r !~ %s" % ( - msg, - error_as_string, - ) - if check_context and not are_we_already_in_a_traceback: - _assert_proper_exception_context(err) - print(str(err).encode("utf-8")) - - # it's generally a good idea to not carry traceback objects outside - # of the except: block, but in this case especially we seem to have - # hit some bug in either python 3.10.0b2 or greenlet or both which - # this seems to fix: - # https://github.com/python-greenlet/greenlet/issues/242 - del ec - - # assert outside the block so it works for AssertionError too ! - assert success, "Callable did not raise an exception" - - -def expect_raises(except_cls, check_context=True): - return _expect_raises(except_cls, check_context=check_context) - - -def expect_raises_message(except_cls, msg, check_context=True): - return _expect_raises(except_cls, msg=msg, check_context=check_context) - - -class AssertsCompiledSQL: - def assert_compile( - self, - clause, - result, - params=None, - checkparams=None, - for_executemany=False, - check_literal_execute=None, - check_post_param=None, - dialect=None, - checkpositional=None, - check_prefetch=None, - use_default_dialect=False, - allow_dialect_select=False, - supports_default_values=True, - supports_default_metavalue=True, - literal_binds=False, - render_postcompile=False, - schema_translate_map=None, - render_schema_translate=False, - default_schema_name=None, - from_linting=False, - check_param_order=True, - use_literal_execute_for_simple_int=False, - ): - if use_default_dialect: - dialect = default.DefaultDialect() - dialect.supports_default_values = supports_default_values - dialect.supports_default_metavalue = supports_default_metavalue - elif allow_dialect_select: - dialect = None - else: - if dialect is None: - dialect = getattr(self, "__dialect__", None) - - if dialect is None: - dialect = config.db.dialect - elif dialect == "default" or dialect == "default_qmark": - if dialect == "default": - dialect = default.DefaultDialect() - else: - dialect = default.DefaultDialect("qmark") - dialect.supports_default_values = supports_default_values - dialect.supports_default_metavalue = supports_default_metavalue - elif dialect == "default_enhanced": - dialect = default.StrCompileDialect() - elif isinstance(dialect, str): - dialect = url.URL.create(dialect).get_dialect()() - - if default_schema_name: - dialect.default_schema_name = default_schema_name - - kw = {} - compile_kwargs = {} - - if schema_translate_map: - kw["schema_translate_map"] = schema_translate_map - - if params is not None: - kw["column_keys"] = list(params) - - if literal_binds: - compile_kwargs["literal_binds"] = True - - if render_postcompile: - compile_kwargs["render_postcompile"] = True - - if use_literal_execute_for_simple_int: - compile_kwargs["use_literal_execute_for_simple_int"] = True - - if for_executemany: - kw["for_executemany"] = True - - if render_schema_translate: - kw["render_schema_translate"] = True - - if from_linting or getattr(self, "assert_from_linting", False): - kw["linting"] = sql.FROM_LINTING - - from sqlalchemy import orm - - if isinstance(clause, orm.Query): - stmt = clause._statement_20() - stmt._label_style = LABEL_STYLE_TABLENAME_PLUS_COL - clause = stmt - - if compile_kwargs: - kw["compile_kwargs"] = compile_kwargs - - class DontAccess: - def __getattribute__(self, key): - raise NotImplementedError( - "compiler accessed .statement; use " - "compiler.current_executable" - ) - - class CheckCompilerAccess: - def __init__(self, test_statement): - self.test_statement = test_statement - self._annotations = {} - self.supports_execution = getattr( - test_statement, "supports_execution", False - ) - - if self.supports_execution: - self._execution_options = test_statement._execution_options - - if hasattr(test_statement, "_returning"): - self._returning = test_statement._returning - if hasattr(test_statement, "_inline"): - self._inline = test_statement._inline - if hasattr(test_statement, "_return_defaults"): - self._return_defaults = test_statement._return_defaults - - @property - def _variant_mapping(self): - return self.test_statement._variant_mapping - - def _default_dialect(self): - return self.test_statement._default_dialect() - - def compile(self, dialect, **kw): - return self.test_statement.compile.__func__( - self, dialect=dialect, **kw - ) - - def _compiler(self, dialect, **kw): - return self.test_statement._compiler.__func__( - self, dialect, **kw - ) - - def _compiler_dispatch(self, compiler, **kwargs): - if hasattr(compiler, "statement"): - with mock.patch.object( - compiler, "statement", DontAccess() - ): - return self.test_statement._compiler_dispatch( - compiler, **kwargs - ) - else: - return self.test_statement._compiler_dispatch( - compiler, **kwargs - ) - - # no construct can assume it's the "top level" construct in all cases - # as anything can be nested. ensure constructs don't assume they - # are the "self.statement" element - c = CheckCompilerAccess(clause).compile(dialect=dialect, **kw) - - if isinstance(clause, sqltypes.TypeEngine): - cache_key_no_warnings = clause._static_cache_key - if cache_key_no_warnings: - hash(cache_key_no_warnings) - else: - cache_key_no_warnings = clause._generate_cache_key() - if cache_key_no_warnings: - hash(cache_key_no_warnings[0]) - - param_str = repr(getattr(c, "params", {})) - param_str = param_str.encode("utf-8").decode("ascii", "ignore") - print(("\nSQL String:\n" + str(c) + param_str).encode("utf-8")) - - cc = re.sub(r"[\n\t]", "", str(c)) - - eq_(cc, result, "%r != %r on dialect %r" % (cc, result, dialect)) - - if checkparams is not None: - if render_postcompile: - expanded_state = c.construct_expanded_state( - params, escape_names=False - ) - eq_(expanded_state.parameters, checkparams) - else: - eq_(c.construct_params(params), checkparams) - if checkpositional is not None: - if render_postcompile: - expanded_state = c.construct_expanded_state( - params, escape_names=False - ) - eq_( - tuple( - [ - expanded_state.parameters[x] - for x in expanded_state.positiontup - ] - ), - checkpositional, - ) - else: - p = c.construct_params(params, escape_names=False) - eq_(tuple([p[x] for x in c.positiontup]), checkpositional) - if check_prefetch is not None: - eq_(c.prefetch, check_prefetch) - if check_literal_execute is not None: - eq_( - { - c.bind_names[b]: b.effective_value - for b in c.literal_execute_params - }, - check_literal_execute, - ) - if check_post_param is not None: - eq_( - { - c.bind_names[b]: b.effective_value - for b in c.post_compile_params - }, - check_post_param, - ) - if check_param_order and getattr(c, "params", None): - - def get_dialect(paramstyle, positional): - cp = copy(dialect) - cp.paramstyle = paramstyle - cp.positional = positional - return cp - - pyformat_dialect = get_dialect("pyformat", False) - pyformat_c = clause.compile(dialect=pyformat_dialect, **kw) - stmt = re.sub(r"[\n\t]", "", str(pyformat_c)) - - qmark_dialect = get_dialect("qmark", True) - qmark_c = clause.compile(dialect=qmark_dialect, **kw) - values = list(qmark_c.positiontup) - escaped = qmark_c.escaped_bind_names - - for post_param in ( - qmark_c.post_compile_params | qmark_c.literal_execute_params - ): - name = qmark_c.bind_names[post_param] - if name in values: - values = [v for v in values if v != name] - positions = [] - pos_by_value = defaultdict(list) - for v in values: - try: - if v in pos_by_value: - start = pos_by_value[v][-1] - else: - start = 0 - esc = escaped.get(v, v) - pos = stmt.index("%%(%s)s" % (esc,), start) + 2 - positions.append(pos) - pos_by_value[v].append(pos) - except ValueError: - msg = "Expected to find bindparam %r in %r" % (v, stmt) - assert False, msg - - ordered = all( - positions[i - 1] < positions[i] - for i in range(1, len(positions)) - ) - - expected = [v for _, v in sorted(zip(positions, values))] - - msg = ( - "Order of parameters %s does not match the order " - "in the statement %s. Statement %r" % (values, expected, stmt) - ) - - is_true(ordered, msg) - - -class ComparesTables: - def assert_tables_equal( - self, - table, - reflected_table, - strict_types=False, - strict_constraints=True, - ): - assert len(table.c) == len(reflected_table.c) - for c, reflected_c in zip(table.c, reflected_table.c): - eq_(c.name, reflected_c.name) - assert reflected_c is reflected_table.c[c.name] - - if strict_constraints: - eq_(c.primary_key, reflected_c.primary_key) - eq_(c.nullable, reflected_c.nullable) - - if strict_types: - msg = "Type '%s' doesn't correspond to type '%s'" - assert isinstance(reflected_c.type, type(c.type)), msg % ( - reflected_c.type, - c.type, - ) - else: - self.assert_types_base(reflected_c, c) - - if isinstance(c.type, sqltypes.String): - eq_(c.type.length, reflected_c.type.length) - - if strict_constraints: - eq_( - {f.column.name for f in c.foreign_keys}, - {f.column.name for f in reflected_c.foreign_keys}, - ) - if c.server_default: - assert isinstance( - reflected_c.server_default, schema.FetchedValue - ) - - if strict_constraints: - assert len(table.primary_key) == len(reflected_table.primary_key) - for c in table.primary_key: - assert reflected_table.primary_key.columns[c.name] is not None - - def assert_types_base(self, c1, c2): - assert c1.type._compare_type_affinity( - c2.type - ), "On column %r, type '%s' doesn't correspond to type '%s'" % ( - c1.name, - c1.type, - c2.type, - ) - - -class AssertsExecutionResults: - def assert_result(self, result, class_, *objects): - result = list(result) - print(repr(result)) - self.assert_list(result, class_, objects) - - def assert_list(self, result, class_, list_): - self.assert_( - len(result) == len(list_), - "result list is not the same size as test list, " - + "for class " - + class_.__name__, - ) - for i in range(0, len(list_)): - self.assert_row(class_, result[i], list_[i]) - - def assert_row(self, class_, rowobj, desc): - self.assert_( - rowobj.__class__ is class_, "item class is not " + repr(class_) - ) - for key, value in desc.items(): - if isinstance(value, tuple): - if isinstance(value[1], list): - self.assert_list(getattr(rowobj, key), value[0], value[1]) - else: - self.assert_row(value[0], getattr(rowobj, key), value[1]) - else: - self.assert_( - getattr(rowobj, key) == value, - "attribute %s value %s does not match %s" - % (key, getattr(rowobj, key), value), - ) - - def assert_unordered_result(self, result, cls, *expected): - """As assert_result, but the order of objects is not considered. - - The algorithm is very expensive but not a big deal for the small - numbers of rows that the test suite manipulates. - """ - - class immutabledict(dict): - def __hash__(self): - return id(self) - - found = util.IdentitySet(result) - expected = {immutabledict(e) for e in expected} - - for wrong in filterfalse(lambda o: isinstance(o, cls), found): - fail( - 'Unexpected type "%s", expected "%s"' - % (type(wrong).__name__, cls.__name__) - ) - - if len(found) != len(expected): - fail( - 'Unexpected object count "%s", expected "%s"' - % (len(found), len(expected)) - ) - - NOVALUE = object() - - def _compare_item(obj, spec): - for key, value in spec.items(): - if isinstance(value, tuple): - try: - self.assert_unordered_result( - getattr(obj, key), value[0], *value[1] - ) - except AssertionError: - return False - else: - if getattr(obj, key, NOVALUE) != value: - return False - return True - - for expected_item in expected: - for found_item in found: - if _compare_item(found_item, expected_item): - found.remove(found_item) - break - else: - fail( - "Expected %s instance with attributes %s not found." - % (cls.__name__, repr(expected_item)) - ) - return True - - def sql_execution_asserter(self, db=None): - if db is None: - from . import db as db - - return assertsql.assert_engine(db) - - def assert_sql_execution(self, db, callable_, *rules): - with self.sql_execution_asserter(db) as asserter: - result = callable_() - asserter.assert_(*rules) - return result - - def assert_sql(self, db, callable_, rules): - newrules = [] - for rule in rules: - if isinstance(rule, dict): - newrule = assertsql.AllOf( - *[assertsql.CompiledSQL(k, v) for k, v in rule.items()] - ) - else: - newrule = assertsql.CompiledSQL(*rule) - newrules.append(newrule) - - return self.assert_sql_execution(db, callable_, *newrules) - - def assert_sql_count(self, db, callable_, count): - return self.assert_sql_execution( - db, callable_, assertsql.CountStatements(count) - ) - - @contextlib.contextmanager - def assert_execution(self, db, *rules): - with self.sql_execution_asserter(db) as asserter: - yield - asserter.assert_(*rules) - - def assert_statement_count(self, db, count): - return self.assert_execution(db, assertsql.CountStatements(count)) - - @contextlib.contextmanager - def assert_statement_count_multi_db(self, dbs, counts): - recs = [ - (self.sql_execution_asserter(db), db, count) - for (db, count) in zip(dbs, counts) - ] - asserters = [] - for ctx, db, count in recs: - asserters.append(ctx.__enter__()) - try: - yield - finally: - for asserter, (ctx, db, count) in zip(asserters, recs): - ctx.__exit__(None, None, None) - asserter.assert_(assertsql.CountStatements(count)) - - -class ComparesIndexes: - def compare_table_index_with_expected( - self, table: schema.Table, expected: list, dialect_name: str - ): - eq_(len(table.indexes), len(expected)) - idx_dict = {idx.name: idx for idx in table.indexes} - for exp in expected: - idx = idx_dict[exp["name"]] - eq_(idx.unique, exp["unique"]) - cols = [c for c in exp["column_names"] if c is not None] - eq_(len(idx.columns), len(cols)) - for c in cols: - is_true(c in idx.columns) - exprs = exp.get("expressions") - if exprs: - eq_(len(idx.expressions), len(exprs)) - for idx_exp, expr, col in zip( - idx.expressions, exprs, exp["column_names"] - ): - if col is None: - eq_(idx_exp.text, expr) - if ( - exp.get("dialect_options") - and f"{dialect_name}_include" in exp["dialect_options"] - ): - eq_( - idx.dialect_options[dialect_name]["include"], - exp["dialect_options"][f"{dialect_name}_include"], - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/assertsql.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/assertsql.py deleted file mode 100644 index ae4d335..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/assertsql.py +++ /dev/null @@ -1,516 +0,0 @@ -# testing/assertsql.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from __future__ import annotations - -import collections -import contextlib -import itertools -import re - -from .. import event -from ..engine import url -from ..engine.default import DefaultDialect -from ..schema import BaseDDLElement - - -class AssertRule: - is_consumed = False - errormessage = None - consume_statement = True - - def process_statement(self, execute_observed): - pass - - def no_more_statements(self): - assert False, ( - "All statements are complete, but pending " - "assertion rules remain" - ) - - -class SQLMatchRule(AssertRule): - pass - - -class CursorSQL(SQLMatchRule): - def __init__(self, statement, params=None, consume_statement=True): - self.statement = statement - self.params = params - self.consume_statement = consume_statement - - def process_statement(self, execute_observed): - stmt = execute_observed.statements[0] - if self.statement != stmt.statement or ( - self.params is not None and self.params != stmt.parameters - ): - self.consume_statement = True - self.errormessage = ( - "Testing for exact SQL %s parameters %s received %s %s" - % ( - self.statement, - self.params, - stmt.statement, - stmt.parameters, - ) - ) - else: - execute_observed.statements.pop(0) - self.is_consumed = True - if not execute_observed.statements: - self.consume_statement = True - - -class CompiledSQL(SQLMatchRule): - def __init__( - self, statement, params=None, dialect="default", enable_returning=True - ): - self.statement = statement - self.params = params - self.dialect = dialect - self.enable_returning = enable_returning - - def _compare_sql(self, execute_observed, received_statement): - stmt = re.sub(r"[\n\t]", "", self.statement) - return received_statement == stmt - - def _compile_dialect(self, execute_observed): - if self.dialect == "default": - dialect = DefaultDialect() - # this is currently what tests are expecting - # dialect.supports_default_values = True - dialect.supports_default_metavalue = True - - if self.enable_returning: - dialect.insert_returning = dialect.update_returning = ( - dialect.delete_returning - ) = True - dialect.use_insertmanyvalues = True - dialect.supports_multivalues_insert = True - dialect.update_returning_multifrom = True - dialect.delete_returning_multifrom = True - # dialect.favor_returning_over_lastrowid = True - # dialect.insert_null_pk_still_autoincrements = True - - # this is calculated but we need it to be True for this - # to look like all the current RETURNING dialects - assert dialect.insert_executemany_returning - - return dialect - else: - return url.URL.create(self.dialect).get_dialect()() - - def _received_statement(self, execute_observed): - """reconstruct the statement and params in terms - of a target dialect, which for CompiledSQL is just DefaultDialect.""" - - context = execute_observed.context - compare_dialect = self._compile_dialect(execute_observed) - - # received_statement runs a full compile(). we should not need to - # consider extracted_parameters; if we do this indicates some state - # is being sent from a previous cached query, which some misbehaviors - # in the ORM can cause, see #6881 - cache_key = None # execute_observed.context.compiled.cache_key - extracted_parameters = ( - None # execute_observed.context.extracted_parameters - ) - - if "schema_translate_map" in context.execution_options: - map_ = context.execution_options["schema_translate_map"] - else: - map_ = None - - if isinstance(execute_observed.clauseelement, BaseDDLElement): - compiled = execute_observed.clauseelement.compile( - dialect=compare_dialect, - schema_translate_map=map_, - ) - else: - compiled = execute_observed.clauseelement.compile( - cache_key=cache_key, - dialect=compare_dialect, - column_keys=context.compiled.column_keys, - for_executemany=context.compiled.for_executemany, - schema_translate_map=map_, - ) - _received_statement = re.sub(r"[\n\t]", "", str(compiled)) - parameters = execute_observed.parameters - - if not parameters: - _received_parameters = [ - compiled.construct_params( - extracted_parameters=extracted_parameters - ) - ] - else: - _received_parameters = [ - compiled.construct_params( - m, extracted_parameters=extracted_parameters - ) - for m in parameters - ] - - return _received_statement, _received_parameters - - def process_statement(self, execute_observed): - context = execute_observed.context - - _received_statement, _received_parameters = self._received_statement( - execute_observed - ) - params = self._all_params(context) - - equivalent = self._compare_sql(execute_observed, _received_statement) - - if equivalent: - if params is not None: - all_params = list(params) - all_received = list(_received_parameters) - while all_params and all_received: - param = dict(all_params.pop(0)) - - for idx, received in enumerate(list(all_received)): - # do a positive compare only - for param_key in param: - # a key in param did not match current - # 'received' - if ( - param_key not in received - or received[param_key] != param[param_key] - ): - break - else: - # all keys in param matched 'received'; - # onto next param - del all_received[idx] - break - else: - # param did not match any entry - # in all_received - equivalent = False - break - if all_params or all_received: - equivalent = False - - if equivalent: - self.is_consumed = True - self.errormessage = None - else: - self.errormessage = self._failure_message( - execute_observed, params - ) % { - "received_statement": _received_statement, - "received_parameters": _received_parameters, - } - - def _all_params(self, context): - if self.params: - if callable(self.params): - params = self.params(context) - else: - params = self.params - if not isinstance(params, list): - params = [params] - return params - else: - return None - - def _failure_message(self, execute_observed, expected_params): - return ( - "Testing for compiled statement\n%r partial params %s, " - "received\n%%(received_statement)r with params " - "%%(received_parameters)r" - % ( - self.statement.replace("%", "%%"), - repr(expected_params).replace("%", "%%"), - ) - ) - - -class RegexSQL(CompiledSQL): - def __init__( - self, regex, params=None, dialect="default", enable_returning=False - ): - SQLMatchRule.__init__(self) - self.regex = re.compile(regex) - self.orig_regex = regex - self.params = params - self.dialect = dialect - self.enable_returning = enable_returning - - def _failure_message(self, execute_observed, expected_params): - return ( - "Testing for compiled statement ~%r partial params %s, " - "received %%(received_statement)r with params " - "%%(received_parameters)r" - % ( - self.orig_regex.replace("%", "%%"), - repr(expected_params).replace("%", "%%"), - ) - ) - - def _compare_sql(self, execute_observed, received_statement): - return bool(self.regex.match(received_statement)) - - -class DialectSQL(CompiledSQL): - def _compile_dialect(self, execute_observed): - return execute_observed.context.dialect - - def _compare_no_space(self, real_stmt, received_stmt): - stmt = re.sub(r"[\n\t]", "", real_stmt) - return received_stmt == stmt - - def _received_statement(self, execute_observed): - received_stmt, received_params = super()._received_statement( - execute_observed - ) - - # TODO: why do we need this part? - for real_stmt in execute_observed.statements: - if self._compare_no_space(real_stmt.statement, received_stmt): - break - else: - raise AssertionError( - "Can't locate compiled statement %r in list of " - "statements actually invoked" % received_stmt - ) - - return received_stmt, execute_observed.context.compiled_parameters - - def _dialect_adjusted_statement(self, dialect): - paramstyle = dialect.paramstyle - stmt = re.sub(r"[\n\t]", "", self.statement) - - # temporarily escape out PG double colons - stmt = stmt.replace("::", "!!") - - if paramstyle == "pyformat": - stmt = re.sub(r":([\w_]+)", r"%(\1)s", stmt) - else: - # positional params - repl = None - if paramstyle == "qmark": - repl = "?" - elif paramstyle == "format": - repl = r"%s" - elif paramstyle.startswith("numeric"): - counter = itertools.count(1) - - num_identifier = "$" if paramstyle == "numeric_dollar" else ":" - - def repl(m): - return f"{num_identifier}{next(counter)}" - - stmt = re.sub(r":([\w_]+)", repl, stmt) - - # put them back - stmt = stmt.replace("!!", "::") - - return stmt - - def _compare_sql(self, execute_observed, received_statement): - stmt = self._dialect_adjusted_statement( - execute_observed.context.dialect - ) - return received_statement == stmt - - def _failure_message(self, execute_observed, expected_params): - return ( - "Testing for compiled statement\n%r partial params %s, " - "received\n%%(received_statement)r with params " - "%%(received_parameters)r" - % ( - self._dialect_adjusted_statement( - execute_observed.context.dialect - ).replace("%", "%%"), - repr(expected_params).replace("%", "%%"), - ) - ) - - -class CountStatements(AssertRule): - def __init__(self, count): - self.count = count - self._statement_count = 0 - - def process_statement(self, execute_observed): - self._statement_count += 1 - - def no_more_statements(self): - if self.count != self._statement_count: - assert False, "desired statement count %d does not match %d" % ( - self.count, - self._statement_count, - ) - - -class AllOf(AssertRule): - def __init__(self, *rules): - self.rules = set(rules) - - def process_statement(self, execute_observed): - for rule in list(self.rules): - rule.errormessage = None - rule.process_statement(execute_observed) - if rule.is_consumed: - self.rules.discard(rule) - if not self.rules: - self.is_consumed = True - break - elif not rule.errormessage: - # rule is not done yet - self.errormessage = None - break - else: - self.errormessage = list(self.rules)[0].errormessage - - -class EachOf(AssertRule): - def __init__(self, *rules): - self.rules = list(rules) - - def process_statement(self, execute_observed): - if not self.rules: - self.is_consumed = True - self.consume_statement = False - - while self.rules: - rule = self.rules[0] - rule.process_statement(execute_observed) - if rule.is_consumed: - self.rules.pop(0) - elif rule.errormessage: - self.errormessage = rule.errormessage - if rule.consume_statement: - break - - if not self.rules: - self.is_consumed = True - - def no_more_statements(self): - if self.rules and not self.rules[0].is_consumed: - self.rules[0].no_more_statements() - elif self.rules: - super().no_more_statements() - - -class Conditional(EachOf): - def __init__(self, condition, rules, else_rules): - if condition: - super().__init__(*rules) - else: - super().__init__(*else_rules) - - -class Or(AllOf): - def process_statement(self, execute_observed): - for rule in self.rules: - rule.process_statement(execute_observed) - if rule.is_consumed: - self.is_consumed = True - break - else: - self.errormessage = list(self.rules)[0].errormessage - - -class SQLExecuteObserved: - def __init__(self, context, clauseelement, multiparams, params): - self.context = context - self.clauseelement = clauseelement - - if multiparams: - self.parameters = multiparams - elif params: - self.parameters = [params] - else: - self.parameters = [] - self.statements = [] - - def __repr__(self): - return str(self.statements) - - -class SQLCursorExecuteObserved( - collections.namedtuple( - "SQLCursorExecuteObserved", - ["statement", "parameters", "context", "executemany"], - ) -): - pass - - -class SQLAsserter: - def __init__(self): - self.accumulated = [] - - def _close(self): - self._final = self.accumulated - del self.accumulated - - def assert_(self, *rules): - rule = EachOf(*rules) - - observed = list(self._final) - while observed: - statement = observed.pop(0) - rule.process_statement(statement) - if rule.is_consumed: - break - elif rule.errormessage: - assert False, rule.errormessage - if observed: - assert False, "Additional SQL statements remain:\n%s" % observed - elif not rule.is_consumed: - rule.no_more_statements() - - -@contextlib.contextmanager -def assert_engine(engine): - asserter = SQLAsserter() - - orig = [] - - @event.listens_for(engine, "before_execute") - def connection_execute( - conn, clauseelement, multiparams, params, execution_options - ): - # grab the original statement + params before any cursor - # execution - orig[:] = clauseelement, multiparams, params - - @event.listens_for(engine, "after_cursor_execute") - def cursor_execute( - conn, cursor, statement, parameters, context, executemany - ): - if not context: - return - # then grab real cursor statements and associate them all - # around a single context - if ( - asserter.accumulated - and asserter.accumulated[-1].context is context - ): - obs = asserter.accumulated[-1] - else: - obs = SQLExecuteObserved(context, orig[0], orig[1], orig[2]) - asserter.accumulated.append(obs) - obs.statements.append( - SQLCursorExecuteObserved( - statement, parameters, context, executemany - ) - ) - - try: - yield asserter - finally: - event.remove(engine, "after_cursor_execute", cursor_execute) - event.remove(engine, "before_execute", connection_execute) - asserter._close() diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/asyncio.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/asyncio.py deleted file mode 100644 index f71ca57..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/asyncio.py +++ /dev/null @@ -1,135 +0,0 @@ -# testing/asyncio.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -# functions and wrappers to run tests, fixtures, provisioning and -# setup/teardown in an asyncio event loop, conditionally based on the -# current DB driver being used for a test. - -# note that SQLAlchemy's asyncio integration also supports a method -# of running individual asyncio functions inside of separate event loops -# using "async_fallback" mode; however running whole functions in the event -# loop is a more accurate test for how SQLAlchemy's asyncio features -# would run in the real world. - - -from __future__ import annotations - -from functools import wraps -import inspect - -from . import config -from ..util.concurrency import _AsyncUtil - -# may be set to False if the -# --disable-asyncio flag is passed to the test runner. -ENABLE_ASYNCIO = True -_async_util = _AsyncUtil() # it has lazy init so just always create one - - -def _shutdown(): - """called when the test finishes""" - _async_util.close() - - -def _run_coroutine_function(fn, *args, **kwargs): - return _async_util.run(fn, *args, **kwargs) - - -def _assume_async(fn, *args, **kwargs): - """Run a function in an asyncio loop unconditionally. - - This function is used for provisioning features like - testing a database connection for server info. - - Note that for blocking IO database drivers, this means they block the - event loop. - - """ - - if not ENABLE_ASYNCIO: - return fn(*args, **kwargs) - - return _async_util.run_in_greenlet(fn, *args, **kwargs) - - -def _maybe_async_provisioning(fn, *args, **kwargs): - """Run a function in an asyncio loop if any current drivers might need it. - - This function is used for provisioning features that take - place outside of a specific database driver being selected, so if the - current driver that happens to be used for the provisioning operation - is an async driver, it will run in asyncio and not fail. - - Note that for blocking IO database drivers, this means they block the - event loop. - - """ - if not ENABLE_ASYNCIO: - return fn(*args, **kwargs) - - if config.any_async: - return _async_util.run_in_greenlet(fn, *args, **kwargs) - else: - return fn(*args, **kwargs) - - -def _maybe_async(fn, *args, **kwargs): - """Run a function in an asyncio loop if the current selected driver is - async. - - This function is used for test setup/teardown and tests themselves - where the current DB driver is known. - - - """ - if not ENABLE_ASYNCIO: - return fn(*args, **kwargs) - - is_async = config._current.is_async - - if is_async: - return _async_util.run_in_greenlet(fn, *args, **kwargs) - else: - return fn(*args, **kwargs) - - -def _maybe_async_wrapper(fn): - """Apply the _maybe_async function to an existing function and return - as a wrapped callable, supporting generator functions as well. - - This is currently used for pytest fixtures that support generator use. - - """ - - if inspect.isgeneratorfunction(fn): - _stop = object() - - def call_next(gen): - try: - return next(gen) - # can't raise StopIteration in an awaitable. - except StopIteration: - return _stop - - @wraps(fn) - def wrap_fixture(*args, **kwargs): - gen = fn(*args, **kwargs) - while True: - value = _maybe_async(call_next, gen) - if value is _stop: - break - yield value - - else: - - @wraps(fn) - def wrap_fixture(*args, **kwargs): - return _maybe_async(fn, *args, **kwargs) - - return wrap_fixture diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/config.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/config.py deleted file mode 100644 index e2623ea..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/config.py +++ /dev/null @@ -1,427 +0,0 @@ -# testing/config.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from __future__ import annotations - -from argparse import Namespace -import collections -import inspect -import typing -from typing import Any -from typing import Callable -from typing import Iterable -from typing import NoReturn -from typing import Optional -from typing import Tuple -from typing import TypeVar -from typing import Union - -from . import mock -from . import requirements as _requirements -from .util import fail -from .. import util - -# default requirements; this is replaced by plugin_base when pytest -# is run -requirements = _requirements.SuiteRequirements() - -db = None -db_url = None -db_opts = None -file_config = None -test_schema = None -test_schema_2 = None -any_async = False -_current = None -ident = "main" -options: Namespace = None # type: ignore - -if typing.TYPE_CHECKING: - from .plugin.plugin_base import FixtureFunctions - - _fixture_functions: FixtureFunctions -else: - - class _NullFixtureFunctions: - def _null_decorator(self): - def go(fn): - return fn - - return go - - def skip_test_exception(self, *arg, **kw): - return Exception() - - @property - def add_to_marker(self): - return mock.Mock() - - def mark_base_test_class(self): - return self._null_decorator() - - def combinations(self, *arg_sets, **kw): - return self._null_decorator() - - def param_ident(self, *parameters): - return self._null_decorator() - - def fixture(self, *arg, **kw): - return self._null_decorator() - - def get_current_test_name(self): - return None - - def async_test(self, fn): - return fn - - # default fixture functions; these are replaced by plugin_base when - # pytest runs - _fixture_functions = _NullFixtureFunctions() - - -_FN = TypeVar("_FN", bound=Callable[..., Any]) - - -def combinations( - *comb: Union[Any, Tuple[Any, ...]], - argnames: Optional[str] = None, - id_: Optional[str] = None, - **kw: str, -) -> Callable[[_FN], _FN]: - r"""Deliver multiple versions of a test based on positional combinations. - - This is a facade over pytest.mark.parametrize. - - - :param \*comb: argument combinations. These are tuples that will be passed - positionally to the decorated function. - - :param argnames: optional list of argument names. These are the names - of the arguments in the test function that correspond to the entries - in each argument tuple. pytest.mark.parametrize requires this, however - the combinations function will derive it automatically if not present - by using ``inspect.getfullargspec(fn).args[1:]``. Note this assumes the - first argument is "self" which is discarded. - - :param id\_: optional id template. This is a string template that - describes how the "id" for each parameter set should be defined, if any. - The number of characters in the template should match the number of - entries in each argument tuple. Each character describes how the - corresponding entry in the argument tuple should be handled, as far as - whether or not it is included in the arguments passed to the function, as - well as if it is included in the tokens used to create the id of the - parameter set. - - If omitted, the argument combinations are passed to parametrize as is. If - passed, each argument combination is turned into a pytest.param() object, - mapping the elements of the argument tuple to produce an id based on a - character value in the same position within the string template using the - following scheme:: - - i - the given argument is a string that is part of the id only, don't - pass it as an argument - - n - the given argument should be passed and it should be added to the - id by calling the .__name__ attribute - - r - the given argument should be passed and it should be added to the - id by calling repr() - - s - the given argument should be passed and it should be added to the - id by calling str() - - a - (argument) the given argument should be passed and it should not - be used to generated the id - - e.g.:: - - @testing.combinations( - (operator.eq, "eq"), - (operator.ne, "ne"), - (operator.gt, "gt"), - (operator.lt, "lt"), - id_="na" - ) - def test_operator(self, opfunc, name): - pass - - The above combination will call ``.__name__`` on the first member of - each tuple and use that as the "id" to pytest.param(). - - - """ - return _fixture_functions.combinations( - *comb, id_=id_, argnames=argnames, **kw - ) - - -def combinations_list(arg_iterable: Iterable[Tuple[Any, ...]], **kw): - "As combination, but takes a single iterable" - return combinations(*arg_iterable, **kw) - - -class Variation: - __slots__ = ("_name", "_argname") - - def __init__(self, case, argname, case_names): - self._name = case - self._argname = argname - for casename in case_names: - setattr(self, casename, casename == case) - - if typing.TYPE_CHECKING: - - def __getattr__(self, key: str) -> bool: ... - - @property - def name(self): - return self._name - - def __bool__(self): - return self._name == self._argname - - def __nonzero__(self): - return not self.__bool__() - - def __str__(self): - return f"{self._argname}={self._name!r}" - - def __repr__(self): - return str(self) - - def fail(self) -> NoReturn: - fail(f"Unknown {self}") - - @classmethod - def idfn(cls, variation): - return variation.name - - @classmethod - def generate_cases(cls, argname, cases): - case_names = [ - argname if c is True else "not_" + argname if c is False else c - for c in cases - ] - - typ = type( - argname, - (Variation,), - { - "__slots__": tuple(case_names), - }, - ) - - return [typ(casename, argname, case_names) for casename in case_names] - - -def variation(argname_or_fn, cases=None): - """a helper around testing.combinations that provides a single namespace - that can be used as a switch. - - e.g.:: - - @testing.variation("querytyp", ["select", "subquery", "legacy_query"]) - @testing.variation("lazy", ["select", "raise", "raise_on_sql"]) - def test_thing( - self, - querytyp, - lazy, - decl_base - ): - class Thing(decl_base): - __tablename__ = 'thing' - - # use name directly - rel = relationship("Rel", lazy=lazy.name) - - # use as a switch - if querytyp.select: - stmt = select(Thing) - elif querytyp.subquery: - stmt = select(Thing).subquery() - elif querytyp.legacy_query: - stmt = Session.query(Thing) - else: - querytyp.fail() - - - The variable provided is a slots object of boolean variables, as well - as the name of the case itself under the attribute ".name" - - """ - - if inspect.isfunction(argname_or_fn): - argname = argname_or_fn.__name__ - cases = argname_or_fn(None) - - @variation_fixture(argname, cases) - def go(self, request): - yield request.param - - return go - else: - argname = argname_or_fn - cases_plus_limitations = [ - ( - entry - if (isinstance(entry, tuple) and len(entry) == 2) - else (entry, None) - ) - for entry in cases - ] - - variations = Variation.generate_cases( - argname, [c for c, l in cases_plus_limitations] - ) - return combinations( - *[ - ( - (variation._name, variation, limitation) - if limitation is not None - else (variation._name, variation) - ) - for variation, (case, limitation) in zip( - variations, cases_plus_limitations - ) - ], - id_="ia", - argnames=argname, - ) - - -def variation_fixture(argname, cases, scope="function"): - return fixture( - params=Variation.generate_cases(argname, cases), - ids=Variation.idfn, - scope=scope, - ) - - -def fixture(*arg: Any, **kw: Any) -> Any: - return _fixture_functions.fixture(*arg, **kw) - - -def get_current_test_name() -> str: - return _fixture_functions.get_current_test_name() - - -def mark_base_test_class() -> Any: - return _fixture_functions.mark_base_test_class() - - -class _AddToMarker: - def __getattr__(self, attr: str) -> Any: - return getattr(_fixture_functions.add_to_marker, attr) - - -add_to_marker = _AddToMarker() - - -class Config: - def __init__(self, db, db_opts, options, file_config): - self._set_name(db) - self.db = db - self.db_opts = db_opts - self.options = options - self.file_config = file_config - self.test_schema = "test_schema" - self.test_schema_2 = "test_schema_2" - - self.is_async = db.dialect.is_async and not util.asbool( - db.url.query.get("async_fallback", False) - ) - - _stack = collections.deque() - _configs = set() - - def _set_name(self, db): - suffix = "_async" if db.dialect.is_async else "" - if db.dialect.server_version_info: - svi = ".".join(str(tok) for tok in db.dialect.server_version_info) - self.name = "%s+%s%s_[%s]" % (db.name, db.driver, suffix, svi) - else: - self.name = "%s+%s%s" % (db.name, db.driver, suffix) - - @classmethod - def register(cls, db, db_opts, options, file_config): - """add a config as one of the global configs. - - If there are no configs set up yet, this config also - gets set as the "_current". - """ - global any_async - - cfg = Config(db, db_opts, options, file_config) - - # if any backends include an async driver, then ensure - # all setup/teardown and tests are wrapped in the maybe_async() - # decorator that will set up a greenlet context for async drivers. - any_async = any_async or cfg.is_async - - cls._configs.add(cfg) - return cfg - - @classmethod - def set_as_current(cls, config, namespace): - global db, _current, db_url, test_schema, test_schema_2, db_opts - _current = config - db_url = config.db.url - db_opts = config.db_opts - test_schema = config.test_schema - test_schema_2 = config.test_schema_2 - namespace.db = db = config.db - - @classmethod - def push_engine(cls, db, namespace): - assert _current, "Can't push without a default Config set up" - cls.push( - Config( - db, _current.db_opts, _current.options, _current.file_config - ), - namespace, - ) - - @classmethod - def push(cls, config, namespace): - cls._stack.append(_current) - cls.set_as_current(config, namespace) - - @classmethod - def pop(cls, namespace): - if cls._stack: - # a failed test w/ -x option can call reset() ahead of time - _current = cls._stack[-1] - del cls._stack[-1] - cls.set_as_current(_current, namespace) - - @classmethod - def reset(cls, namespace): - if cls._stack: - cls.set_as_current(cls._stack[0], namespace) - cls._stack.clear() - - @classmethod - def all_configs(cls): - return cls._configs - - @classmethod - def all_dbs(cls): - for cfg in cls.all_configs(): - yield cfg.db - - def skip_test(self, msg): - skip_test(msg) - - -def skip_test(msg): - raise _fixture_functions.skip_test_exception(msg) - - -def async_test(fn): - return _fixture_functions.async_test(fn) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/engines.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/engines.py deleted file mode 100644 index 7cae807..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/engines.py +++ /dev/null @@ -1,472 +0,0 @@ -# testing/engines.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from __future__ import annotations - -import collections -import re -import typing -from typing import Any -from typing import Dict -from typing import Optional -import warnings -import weakref - -from . import config -from .util import decorator -from .util import gc_collect -from .. import event -from .. import pool -from ..util import await_only -from ..util.typing import Literal - - -if typing.TYPE_CHECKING: - from ..engine import Engine - from ..engine.url import URL - from ..ext.asyncio import AsyncEngine - - -class ConnectionKiller: - def __init__(self): - self.proxy_refs = weakref.WeakKeyDictionary() - self.testing_engines = collections.defaultdict(set) - self.dbapi_connections = set() - - def add_pool(self, pool): - event.listen(pool, "checkout", self._add_conn) - event.listen(pool, "checkin", self._remove_conn) - event.listen(pool, "close", self._remove_conn) - event.listen(pool, "close_detached", self._remove_conn) - # note we are keeping "invalidated" here, as those are still - # opened connections we would like to roll back - - def _add_conn(self, dbapi_con, con_record, con_proxy): - self.dbapi_connections.add(dbapi_con) - self.proxy_refs[con_proxy] = True - - def _remove_conn(self, dbapi_conn, *arg): - self.dbapi_connections.discard(dbapi_conn) - - def add_engine(self, engine, scope): - self.add_pool(engine.pool) - - assert scope in ("class", "global", "function", "fixture") - self.testing_engines[scope].add(engine) - - def _safe(self, fn): - try: - fn() - except Exception as e: - warnings.warn( - "testing_reaper couldn't rollback/close connection: %s" % e - ) - - def rollback_all(self): - for rec in list(self.proxy_refs): - if rec is not None and rec.is_valid: - self._safe(rec.rollback) - - def checkin_all(self): - # run pool.checkin() for all ConnectionFairy instances we have - # tracked. - - for rec in list(self.proxy_refs): - if rec is not None and rec.is_valid: - self.dbapi_connections.discard(rec.dbapi_connection) - self._safe(rec._checkin) - - # for fairy refs that were GCed and could not close the connection, - # such as asyncio, roll back those remaining connections - for con in self.dbapi_connections: - self._safe(con.rollback) - self.dbapi_connections.clear() - - def close_all(self): - self.checkin_all() - - def prepare_for_drop_tables(self, connection): - # don't do aggressive checks for third party test suites - if not config.bootstrapped_as_sqlalchemy: - return - - from . import provision - - provision.prepare_for_drop_tables(connection.engine.url, connection) - - def _drop_testing_engines(self, scope): - eng = self.testing_engines[scope] - for rec in list(eng): - for proxy_ref in list(self.proxy_refs): - if proxy_ref is not None and proxy_ref.is_valid: - if ( - proxy_ref._pool is not None - and proxy_ref._pool is rec.pool - ): - self._safe(proxy_ref._checkin) - - if hasattr(rec, "sync_engine"): - await_only(rec.dispose()) - else: - rec.dispose() - eng.clear() - - def after_test(self): - self._drop_testing_engines("function") - - def after_test_outside_fixtures(self, test): - # don't do aggressive checks for third party test suites - if not config.bootstrapped_as_sqlalchemy: - return - - if test.__class__.__leave_connections_for_teardown__: - return - - self.checkin_all() - - # on PostgreSQL, this will test for any "idle in transaction" - # connections. useful to identify tests with unusual patterns - # that can't be cleaned up correctly. - from . import provision - - with config.db.connect() as conn: - provision.prepare_for_drop_tables(conn.engine.url, conn) - - def stop_test_class_inside_fixtures(self): - self.checkin_all() - self._drop_testing_engines("function") - self._drop_testing_engines("class") - - def stop_test_class_outside_fixtures(self): - # ensure no refs to checked out connections at all. - - if pool.base._strong_ref_connection_records: - gc_collect() - - if pool.base._strong_ref_connection_records: - ln = len(pool.base._strong_ref_connection_records) - pool.base._strong_ref_connection_records.clear() - assert ( - False - ), "%d connection recs not cleared after test suite" % (ln) - - def final_cleanup(self): - self.checkin_all() - for scope in self.testing_engines: - self._drop_testing_engines(scope) - - def assert_all_closed(self): - for rec in self.proxy_refs: - if rec.is_valid: - assert False - - -testing_reaper = ConnectionKiller() - - -@decorator -def assert_conns_closed(fn, *args, **kw): - try: - fn(*args, **kw) - finally: - testing_reaper.assert_all_closed() - - -@decorator -def rollback_open_connections(fn, *args, **kw): - """Decorator that rolls back all open connections after fn execution.""" - - try: - fn(*args, **kw) - finally: - testing_reaper.rollback_all() - - -@decorator -def close_first(fn, *args, **kw): - """Decorator that closes all connections before fn execution.""" - - testing_reaper.checkin_all() - fn(*args, **kw) - - -@decorator -def close_open_connections(fn, *args, **kw): - """Decorator that closes all connections after fn execution.""" - try: - fn(*args, **kw) - finally: - testing_reaper.checkin_all() - - -def all_dialects(exclude=None): - import sqlalchemy.dialects as d - - for name in d.__all__: - # TEMPORARY - if exclude and name in exclude: - continue - mod = getattr(d, name, None) - if not mod: - mod = getattr( - __import__("sqlalchemy.dialects.%s" % name).dialects, name - ) - yield mod.dialect() - - -class ReconnectFixture: - def __init__(self, dbapi): - self.dbapi = dbapi - self.connections = [] - self.is_stopped = False - - def __getattr__(self, key): - return getattr(self.dbapi, key) - - def connect(self, *args, **kwargs): - conn = self.dbapi.connect(*args, **kwargs) - if self.is_stopped: - self._safe(conn.close) - curs = conn.cursor() # should fail on Oracle etc. - # should fail for everything that didn't fail - # above, connection is closed - curs.execute("select 1") - assert False, "simulated connect failure didn't work" - else: - self.connections.append(conn) - return conn - - def _safe(self, fn): - try: - fn() - except Exception as e: - warnings.warn("ReconnectFixture couldn't close connection: %s" % e) - - def shutdown(self, stop=False): - # TODO: this doesn't cover all cases - # as nicely as we'd like, namely MySQLdb. - # would need to implement R. Brewer's - # proxy server idea to get better - # coverage. - self.is_stopped = stop - for c in list(self.connections): - self._safe(c.close) - self.connections = [] - - def restart(self): - self.is_stopped = False - - -def reconnecting_engine(url=None, options=None): - url = url or config.db.url - dbapi = config.db.dialect.dbapi - if not options: - options = {} - options["module"] = ReconnectFixture(dbapi) - engine = testing_engine(url, options) - _dispose = engine.dispose - - def dispose(): - engine.dialect.dbapi.shutdown() - engine.dialect.dbapi.is_stopped = False - _dispose() - - engine.test_shutdown = engine.dialect.dbapi.shutdown - engine.test_restart = engine.dialect.dbapi.restart - engine.dispose = dispose - return engine - - -@typing.overload -def testing_engine( - url: Optional[URL] = None, - options: Optional[Dict[str, Any]] = None, - asyncio: Literal[False] = False, - transfer_staticpool: bool = False, -) -> Engine: ... - - -@typing.overload -def testing_engine( - url: Optional[URL] = None, - options: Optional[Dict[str, Any]] = None, - asyncio: Literal[True] = True, - transfer_staticpool: bool = False, -) -> AsyncEngine: ... - - -def testing_engine( - url=None, - options=None, - asyncio=False, - transfer_staticpool=False, - share_pool=False, - _sqlite_savepoint=False, -): - if asyncio: - assert not _sqlite_savepoint - from sqlalchemy.ext.asyncio import ( - create_async_engine as create_engine, - ) - else: - from sqlalchemy import create_engine - from sqlalchemy.engine.url import make_url - - if not options: - use_reaper = True - scope = "function" - sqlite_savepoint = False - else: - use_reaper = options.pop("use_reaper", True) - scope = options.pop("scope", "function") - sqlite_savepoint = options.pop("sqlite_savepoint", False) - - url = url or config.db.url - - url = make_url(url) - if options is None: - if config.db is None or url.drivername == config.db.url.drivername: - options = config.db_opts - else: - options = {} - elif config.db is not None and url.drivername == config.db.url.drivername: - default_opt = config.db_opts.copy() - default_opt.update(options) - - engine = create_engine(url, **options) - - if sqlite_savepoint and engine.name == "sqlite": - # apply SQLite savepoint workaround - @event.listens_for(engine, "connect") - def do_connect(dbapi_connection, connection_record): - dbapi_connection.isolation_level = None - - @event.listens_for(engine, "begin") - def do_begin(conn): - conn.exec_driver_sql("BEGIN") - - if transfer_staticpool: - from sqlalchemy.pool import StaticPool - - if config.db is not None and isinstance(config.db.pool, StaticPool): - use_reaper = False - engine.pool._transfer_from(config.db.pool) - elif share_pool: - engine.pool = config.db.pool - - if scope == "global": - if asyncio: - engine.sync_engine._has_events = True - else: - engine._has_events = ( - True # enable event blocks, helps with profiling - ) - - if ( - isinstance(engine.pool, pool.QueuePool) - and "pool" not in options - and "pool_timeout" not in options - and "max_overflow" not in options - ): - engine.pool._timeout = 0 - engine.pool._max_overflow = 0 - if use_reaper: - testing_reaper.add_engine(engine, scope) - - return engine - - -def mock_engine(dialect_name=None): - """Provides a mocking engine based on the current testing.db. - - This is normally used to test DDL generation flow as emitted - by an Engine. - - It should not be used in other cases, as assert_compile() and - assert_sql_execution() are much better choices with fewer - moving parts. - - """ - - from sqlalchemy import create_mock_engine - - if not dialect_name: - dialect_name = config.db.name - - buffer = [] - - def executor(sql, *a, **kw): - buffer.append(sql) - - def assert_sql(stmts): - recv = [re.sub(r"[\n\t]", "", str(s)) for s in buffer] - assert recv == stmts, recv - - def print_sql(): - d = engine.dialect - return "\n".join(str(s.compile(dialect=d)) for s in engine.mock) - - engine = create_mock_engine(dialect_name + "://", executor) - assert not hasattr(engine, "mock") - engine.mock = buffer - engine.assert_sql = assert_sql - engine.print_sql = print_sql - return engine - - -class DBAPIProxyCursor: - """Proxy a DBAPI cursor. - - Tests can provide subclasses of this to intercept - DBAPI-level cursor operations. - - """ - - def __init__(self, engine, conn, *args, **kwargs): - self.engine = engine - self.connection = conn - self.cursor = conn.cursor(*args, **kwargs) - - def execute(self, stmt, parameters=None, **kw): - if parameters: - return self.cursor.execute(stmt, parameters, **kw) - else: - return self.cursor.execute(stmt, **kw) - - def executemany(self, stmt, params, **kw): - return self.cursor.executemany(stmt, params, **kw) - - def __iter__(self): - return iter(self.cursor) - - def __getattr__(self, key): - return getattr(self.cursor, key) - - -class DBAPIProxyConnection: - """Proxy a DBAPI connection. - - Tests can provide subclasses of this to intercept - DBAPI-level connection operations. - - """ - - def __init__(self, engine, conn, cursor_cls): - self.conn = conn - self.engine = engine - self.cursor_cls = cursor_cls - - def cursor(self, *args, **kwargs): - return self.cursor_cls(self.engine, self.conn, *args, **kwargs) - - def close(self): - self.conn.close() - - def __getattr__(self, key): - return getattr(self.conn, key) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/entities.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/entities.py deleted file mode 100644 index 8f0f36b..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/entities.py +++ /dev/null @@ -1,117 +0,0 @@ -# testing/entities.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from __future__ import annotations - -import sqlalchemy as sa -from .. import exc as sa_exc -from ..orm.writeonly import WriteOnlyCollection - -_repr_stack = set() - - -class BasicEntity: - def __init__(self, **kw): - for key, value in kw.items(): - setattr(self, key, value) - - def __repr__(self): - if id(self) in _repr_stack: - return object.__repr__(self) - _repr_stack.add(id(self)) - try: - return "%s(%s)" % ( - (self.__class__.__name__), - ", ".join( - [ - "%s=%r" % (key, getattr(self, key)) - for key in sorted(self.__dict__.keys()) - if not key.startswith("_") - ] - ), - ) - finally: - _repr_stack.remove(id(self)) - - -_recursion_stack = set() - - -class ComparableMixin: - def __ne__(self, other): - return not self.__eq__(other) - - def __eq__(self, other): - """'Deep, sparse compare. - - Deeply compare two entities, following the non-None attributes of the - non-persisted object, if possible. - - """ - if other is self: - return True - elif not self.__class__ == other.__class__: - return False - - if id(self) in _recursion_stack: - return True - _recursion_stack.add(id(self)) - - try: - # pick the entity that's not SA persisted as the source - try: - self_key = sa.orm.attributes.instance_state(self).key - except sa.orm.exc.NO_STATE: - self_key = None - - if other is None: - a = self - b = other - elif self_key is not None: - a = other - b = self - else: - a = self - b = other - - for attr in list(a.__dict__): - if attr.startswith("_"): - continue - - value = getattr(a, attr) - - if isinstance(value, WriteOnlyCollection): - continue - - try: - # handle lazy loader errors - battr = getattr(b, attr) - except (AttributeError, sa_exc.UnboundExecutionError): - return False - - if hasattr(value, "__iter__") and not isinstance(value, str): - if hasattr(value, "__getitem__") and not hasattr( - value, "keys" - ): - if list(value) != list(battr): - return False - else: - if set(value) != set(battr): - return False - else: - if value is not None and value != battr: - return False - return True - finally: - _recursion_stack.remove(id(self)) - - -class ComparableEntity(ComparableMixin, BasicEntity): - def __hash__(self): - return hash(self.__class__) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/exclusions.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/exclusions.py deleted file mode 100644 index addc4b7..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/exclusions.py +++ /dev/null @@ -1,435 +0,0 @@ -# testing/exclusions.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -import contextlib -import operator -import re -import sys - -from . import config -from .. import util -from ..util import decorator -from ..util.compat import inspect_getfullargspec - - -def skip_if(predicate, reason=None): - rule = compound() - pred = _as_predicate(predicate, reason) - rule.skips.add(pred) - return rule - - -def fails_if(predicate, reason=None): - rule = compound() - pred = _as_predicate(predicate, reason) - rule.fails.add(pred) - return rule - - -class compound: - def __init__(self): - self.fails = set() - self.skips = set() - - def __add__(self, other): - return self.add(other) - - def as_skips(self): - rule = compound() - rule.skips.update(self.skips) - rule.skips.update(self.fails) - return rule - - def add(self, *others): - copy = compound() - copy.fails.update(self.fails) - copy.skips.update(self.skips) - - for other in others: - copy.fails.update(other.fails) - copy.skips.update(other.skips) - return copy - - def not_(self): - copy = compound() - copy.fails.update(NotPredicate(fail) for fail in self.fails) - copy.skips.update(NotPredicate(skip) for skip in self.skips) - return copy - - @property - def enabled(self): - return self.enabled_for_config(config._current) - - def enabled_for_config(self, config): - for predicate in self.skips.union(self.fails): - if predicate(config): - return False - else: - return True - - def matching_config_reasons(self, config): - return [ - predicate._as_string(config) - for predicate in self.skips.union(self.fails) - if predicate(config) - ] - - def _extend(self, other): - self.skips.update(other.skips) - self.fails.update(other.fails) - - def __call__(self, fn): - if hasattr(fn, "_sa_exclusion_extend"): - fn._sa_exclusion_extend._extend(self) - return fn - - @decorator - def decorate(fn, *args, **kw): - return self._do(config._current, fn, *args, **kw) - - decorated = decorate(fn) - decorated._sa_exclusion_extend = self - return decorated - - @contextlib.contextmanager - def fail_if(self): - all_fails = compound() - all_fails.fails.update(self.skips.union(self.fails)) - - try: - yield - except Exception as ex: - all_fails._expect_failure(config._current, ex) - else: - all_fails._expect_success(config._current) - - def _do(self, cfg, fn, *args, **kw): - for skip in self.skips: - if skip(cfg): - msg = "'%s' : %s" % ( - config.get_current_test_name(), - skip._as_string(cfg), - ) - config.skip_test(msg) - - try: - return_value = fn(*args, **kw) - except Exception as ex: - self._expect_failure(cfg, ex, name=fn.__name__) - else: - self._expect_success(cfg, name=fn.__name__) - return return_value - - def _expect_failure(self, config, ex, name="block"): - for fail in self.fails: - if fail(config): - print( - "%s failed as expected (%s): %s " - % (name, fail._as_string(config), ex) - ) - break - else: - raise ex.with_traceback(sys.exc_info()[2]) - - def _expect_success(self, config, name="block"): - if not self.fails: - return - - for fail in self.fails: - if fail(config): - raise AssertionError( - "Unexpected success for '%s' (%s)" - % ( - name, - " and ".join( - fail._as_string(config) for fail in self.fails - ), - ) - ) - - -def only_if(predicate, reason=None): - predicate = _as_predicate(predicate) - return skip_if(NotPredicate(predicate), reason) - - -def succeeds_if(predicate, reason=None): - predicate = _as_predicate(predicate) - return fails_if(NotPredicate(predicate), reason) - - -class Predicate: - @classmethod - def as_predicate(cls, predicate, description=None): - if isinstance(predicate, compound): - return cls.as_predicate(predicate.enabled_for_config, description) - elif isinstance(predicate, Predicate): - if description and predicate.description is None: - predicate.description = description - return predicate - elif isinstance(predicate, (list, set)): - return OrPredicate( - [cls.as_predicate(pred) for pred in predicate], description - ) - elif isinstance(predicate, tuple): - return SpecPredicate(*predicate) - elif isinstance(predicate, str): - tokens = re.match( - r"([\+\w]+)\s*(?:(>=|==|!=|<=|<|>)\s*([\d\.]+))?", predicate - ) - if not tokens: - raise ValueError( - "Couldn't locate DB name in predicate: %r" % predicate - ) - db = tokens.group(1) - op = tokens.group(2) - spec = ( - tuple(int(d) for d in tokens.group(3).split(".")) - if tokens.group(3) - else None - ) - - return SpecPredicate(db, op, spec, description=description) - elif callable(predicate): - return LambdaPredicate(predicate, description) - else: - assert False, "unknown predicate type: %s" % predicate - - def _format_description(self, config, negate=False): - bool_ = self(config) - if negate: - bool_ = not negate - return self.description % { - "driver": ( - config.db.url.get_driver_name() if config else "<no driver>" - ), - "database": ( - config.db.url.get_backend_name() if config else "<no database>" - ), - "doesnt_support": "doesn't support" if bool_ else "does support", - "does_support": "does support" if bool_ else "doesn't support", - } - - def _as_string(self, config=None, negate=False): - raise NotImplementedError() - - -class BooleanPredicate(Predicate): - def __init__(self, value, description=None): - self.value = value - self.description = description or "boolean %s" % value - - def __call__(self, config): - return self.value - - def _as_string(self, config, negate=False): - return self._format_description(config, negate=negate) - - -class SpecPredicate(Predicate): - def __init__(self, db, op=None, spec=None, description=None): - self.db = db - self.op = op - self.spec = spec - self.description = description - - _ops = { - "<": operator.lt, - ">": operator.gt, - "==": operator.eq, - "!=": operator.ne, - "<=": operator.le, - ">=": operator.ge, - "in": operator.contains, - "between": lambda val, pair: val >= pair[0] and val <= pair[1], - } - - def __call__(self, config): - if config is None: - return False - - engine = config.db - - if "+" in self.db: - dialect, driver = self.db.split("+") - else: - dialect, driver = self.db, None - - if dialect and engine.name != dialect: - return False - if driver is not None and engine.driver != driver: - return False - - if self.op is not None: - assert driver is None, "DBAPI version specs not supported yet" - - version = _server_version(engine) - oper = ( - hasattr(self.op, "__call__") and self.op or self._ops[self.op] - ) - return oper(version, self.spec) - else: - return True - - def _as_string(self, config, negate=False): - if self.description is not None: - return self._format_description(config) - elif self.op is None: - if negate: - return "not %s" % self.db - else: - return "%s" % self.db - else: - if negate: - return "not %s %s %s" % (self.db, self.op, self.spec) - else: - return "%s %s %s" % (self.db, self.op, self.spec) - - -class LambdaPredicate(Predicate): - def __init__(self, lambda_, description=None, args=None, kw=None): - spec = inspect_getfullargspec(lambda_) - if not spec[0]: - self.lambda_ = lambda db: lambda_() - else: - self.lambda_ = lambda_ - self.args = args or () - self.kw = kw or {} - if description: - self.description = description - elif lambda_.__doc__: - self.description = lambda_.__doc__ - else: - self.description = "custom function" - - def __call__(self, config): - return self.lambda_(config) - - def _as_string(self, config, negate=False): - return self._format_description(config) - - -class NotPredicate(Predicate): - def __init__(self, predicate, description=None): - self.predicate = predicate - self.description = description - - def __call__(self, config): - return not self.predicate(config) - - def _as_string(self, config, negate=False): - if self.description: - return self._format_description(config, not negate) - else: - return self.predicate._as_string(config, not negate) - - -class OrPredicate(Predicate): - def __init__(self, predicates, description=None): - self.predicates = predicates - self.description = description - - def __call__(self, config): - for pred in self.predicates: - if pred(config): - return True - return False - - def _eval_str(self, config, negate=False): - if negate: - conjunction = " and " - else: - conjunction = " or " - return conjunction.join( - p._as_string(config, negate=negate) for p in self.predicates - ) - - def _negation_str(self, config): - if self.description is not None: - return "Not " + self._format_description(config) - else: - return self._eval_str(config, negate=True) - - def _as_string(self, config, negate=False): - if negate: - return self._negation_str(config) - else: - if self.description is not None: - return self._format_description(config) - else: - return self._eval_str(config) - - -_as_predicate = Predicate.as_predicate - - -def _is_excluded(db, op, spec): - return SpecPredicate(db, op, spec)(config._current) - - -def _server_version(engine): - """Return a server_version_info tuple.""" - - # force metadata to be retrieved - conn = engine.connect() - version = getattr(engine.dialect, "server_version_info", None) - if version is None: - version = () - conn.close() - return version - - -def db_spec(*dbs): - return OrPredicate([Predicate.as_predicate(db) for db in dbs]) - - -def open(): # noqa - return skip_if(BooleanPredicate(False, "mark as execute")) - - -def closed(): - return skip_if(BooleanPredicate(True, "marked as skip")) - - -def fails(reason=None): - return fails_if(BooleanPredicate(True, reason or "expected to fail")) - - -def future(): - return fails_if(BooleanPredicate(True, "Future feature")) - - -def fails_on(db, reason=None): - return fails_if(db, reason) - - -def fails_on_everything_except(*dbs): - return succeeds_if(OrPredicate([Predicate.as_predicate(db) for db in dbs])) - - -def skip(db, reason=None): - return skip_if(db, reason) - - -def only_on(dbs, reason=None): - return only_if( - OrPredicate( - [Predicate.as_predicate(db, reason) for db in util.to_list(dbs)] - ) - ) - - -def exclude(db, op, spec, reason=None): - return skip_if(SpecPredicate(db, op, spec), reason) - - -def against(config, *queries): - assert queries, "no queries sent!" - return OrPredicate([Predicate.as_predicate(query) for query in queries])( - config - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__init__.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__init__.py deleted file mode 100644 index 5981fb5..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# testing/fixtures/__init__.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors -from .base import FutureEngineMixin as FutureEngineMixin -from .base import TestBase as TestBase -from .mypy import MypyTest as MypyTest -from .orm import after_test as after_test -from .orm import close_all_sessions as close_all_sessions -from .orm import DeclarativeMappedTest as DeclarativeMappedTest -from .orm import fixture_session as fixture_session -from .orm import MappedTest as MappedTest -from .orm import ORMTest as ORMTest -from .orm import RemoveORMEventsGlobally as RemoveORMEventsGlobally -from .orm import ( - stop_test_class_inside_fixtures as stop_test_class_inside_fixtures, -) -from .sql import CacheKeyFixture as CacheKeyFixture -from .sql import ( - ComputedReflectionFixtureTest as ComputedReflectionFixtureTest, -) -from .sql import insertmanyvalues_fixture as insertmanyvalues_fixture -from .sql import NoCache as NoCache -from .sql import RemovesEvents as RemovesEvents -from .sql import TablesTest as TablesTest diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-311.pyc Binary files differdeleted file mode 100644 index 0f95b6a..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/base.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/base.cpython-311.pyc Binary files differdeleted file mode 100644 index ccff61f..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/base.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-311.pyc Binary files differdeleted file mode 100644 index 278a5f6..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/orm.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/orm.cpython-311.pyc Binary files differdeleted file mode 100644 index 71d2d9a..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/orm.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/sql.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/sql.cpython-311.pyc Binary files differdeleted file mode 100644 index bc870cb..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/__pycache__/sql.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/base.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/base.py deleted file mode 100644 index 0697f49..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/base.py +++ /dev/null @@ -1,366 +0,0 @@ -# testing/fixtures/base.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from __future__ import annotations - -import sqlalchemy as sa -from .. import assertions -from .. import config -from ..assertions import eq_ -from ..util import drop_all_tables_from_metadata -from ... import Column -from ... import func -from ... import Integer -from ... import select -from ... import Table -from ...orm import DeclarativeBase -from ...orm import MappedAsDataclass -from ...orm import registry - - -@config.mark_base_test_class() -class TestBase: - # A sequence of requirement names matching testing.requires decorators - __requires__ = () - - # A sequence of dialect names to exclude from the test class. - __unsupported_on__ = () - - # If present, test class is only runnable for the *single* specified - # dialect. If you need multiple, use __unsupported_on__ and invert. - __only_on__ = None - - # A sequence of no-arg callables. If any are True, the entire testcase is - # skipped. - __skip_if__ = None - - # if True, the testing reaper will not attempt to touch connection - # state after a test is completed and before the outer teardown - # starts - __leave_connections_for_teardown__ = False - - def assert_(self, val, msg=None): - assert val, msg - - @config.fixture() - def nocache(self): - _cache = config.db._compiled_cache - config.db._compiled_cache = None - yield - config.db._compiled_cache = _cache - - @config.fixture() - def connection_no_trans(self): - eng = getattr(self, "bind", None) or config.db - - with eng.connect() as conn: - yield conn - - @config.fixture() - def connection(self): - global _connection_fixture_connection - - eng = getattr(self, "bind", None) or config.db - - conn = eng.connect() - trans = conn.begin() - - _connection_fixture_connection = conn - yield conn - - _connection_fixture_connection = None - - if trans.is_active: - trans.rollback() - # trans would not be active here if the test is using - # the legacy @provide_metadata decorator still, as it will - # run a close all connections. - conn.close() - - @config.fixture() - def close_result_when_finished(self): - to_close = [] - to_consume = [] - - def go(result, consume=False): - to_close.append(result) - if consume: - to_consume.append(result) - - yield go - for r in to_consume: - try: - r.all() - except: - pass - for r in to_close: - try: - r.close() - except: - pass - - @config.fixture() - def registry(self, metadata): - reg = registry( - metadata=metadata, - type_annotation_map={ - str: sa.String().with_variant( - sa.String(50), "mysql", "mariadb", "oracle" - ) - }, - ) - yield reg - reg.dispose() - - @config.fixture - def decl_base(self, metadata): - _md = metadata - - class Base(DeclarativeBase): - metadata = _md - type_annotation_map = { - str: sa.String().with_variant( - sa.String(50), "mysql", "mariadb", "oracle" - ) - } - - yield Base - Base.registry.dispose() - - @config.fixture - def dc_decl_base(self, metadata): - _md = metadata - - class Base(MappedAsDataclass, DeclarativeBase): - metadata = _md - type_annotation_map = { - str: sa.String().with_variant( - sa.String(50), "mysql", "mariadb" - ) - } - - yield Base - Base.registry.dispose() - - @config.fixture() - def future_connection(self, future_engine, connection): - # integrate the future_engine and connection fixtures so - # that users of the "connection" fixture will get at the - # "future" connection - yield connection - - @config.fixture() - def future_engine(self): - yield - - @config.fixture() - def testing_engine(self): - from .. import engines - - def gen_testing_engine( - url=None, - options=None, - future=None, - asyncio=False, - transfer_staticpool=False, - share_pool=False, - ): - if options is None: - options = {} - options["scope"] = "fixture" - return engines.testing_engine( - url=url, - options=options, - asyncio=asyncio, - transfer_staticpool=transfer_staticpool, - share_pool=share_pool, - ) - - yield gen_testing_engine - - engines.testing_reaper._drop_testing_engines("fixture") - - @config.fixture() - def async_testing_engine(self, testing_engine): - def go(**kw): - kw["asyncio"] = True - return testing_engine(**kw) - - return go - - @config.fixture() - def metadata(self, request): - """Provide bound MetaData for a single test, dropping afterwards.""" - - from ...sql import schema - - metadata = schema.MetaData() - request.instance.metadata = metadata - yield metadata - del request.instance.metadata - - if ( - _connection_fixture_connection - and _connection_fixture_connection.in_transaction() - ): - trans = _connection_fixture_connection.get_transaction() - trans.rollback() - with _connection_fixture_connection.begin(): - drop_all_tables_from_metadata( - metadata, _connection_fixture_connection - ) - else: - drop_all_tables_from_metadata(metadata, config.db) - - @config.fixture( - params=[ - (rollback, second_operation, begin_nested) - for rollback in (True, False) - for second_operation in ("none", "execute", "begin") - for begin_nested in ( - True, - False, - ) - ] - ) - def trans_ctx_manager_fixture(self, request, metadata): - rollback, second_operation, begin_nested = request.param - - t = Table("test", metadata, Column("data", Integer)) - eng = getattr(self, "bind", None) or config.db - - t.create(eng) - - def run_test(subject, trans_on_subject, execute_on_subject): - with subject.begin() as trans: - if begin_nested: - if not config.requirements.savepoints.enabled: - config.skip_test("savepoints not enabled") - if execute_on_subject: - nested_trans = subject.begin_nested() - else: - nested_trans = trans.begin_nested() - - with nested_trans: - if execute_on_subject: - subject.execute(t.insert(), {"data": 10}) - else: - trans.execute(t.insert(), {"data": 10}) - - # for nested trans, we always commit/rollback on the - # "nested trans" object itself. - # only Session(future=False) will affect savepoint - # transaction for session.commit/rollback - - if rollback: - nested_trans.rollback() - else: - nested_trans.commit() - - if second_operation != "none": - with assertions.expect_raises_message( - sa.exc.InvalidRequestError, - "Can't operate on closed transaction " - "inside context " - "manager. Please complete the context " - "manager " - "before emitting further commands.", - ): - if second_operation == "execute": - if execute_on_subject: - subject.execute( - t.insert(), {"data": 12} - ) - else: - trans.execute(t.insert(), {"data": 12}) - elif second_operation == "begin": - if execute_on_subject: - subject.begin_nested() - else: - trans.begin_nested() - - # outside the nested trans block, but still inside the - # transaction block, we can run SQL, and it will be - # committed - if execute_on_subject: - subject.execute(t.insert(), {"data": 14}) - else: - trans.execute(t.insert(), {"data": 14}) - - else: - if execute_on_subject: - subject.execute(t.insert(), {"data": 10}) - else: - trans.execute(t.insert(), {"data": 10}) - - if trans_on_subject: - if rollback: - subject.rollback() - else: - subject.commit() - else: - if rollback: - trans.rollback() - else: - trans.commit() - - if second_operation != "none": - with assertions.expect_raises_message( - sa.exc.InvalidRequestError, - "Can't operate on closed transaction inside " - "context " - "manager. Please complete the context manager " - "before emitting further commands.", - ): - if second_operation == "execute": - if execute_on_subject: - subject.execute(t.insert(), {"data": 12}) - else: - trans.execute(t.insert(), {"data": 12}) - elif second_operation == "begin": - if hasattr(trans, "begin"): - trans.begin() - else: - subject.begin() - elif second_operation == "begin_nested": - if execute_on_subject: - subject.begin_nested() - else: - trans.begin_nested() - - expected_committed = 0 - if begin_nested: - # begin_nested variant, we inserted a row after the nested - # block - expected_committed += 1 - if not rollback: - # not rollback variant, our row inserted in the target - # block itself would be committed - expected_committed += 1 - - if execute_on_subject: - eq_( - subject.scalar(select(func.count()).select_from(t)), - expected_committed, - ) - else: - with subject.connect() as conn: - eq_( - conn.scalar(select(func.count()).select_from(t)), - expected_committed, - ) - - return run_test - - -_connection_fixture_connection = None - - -class FutureEngineMixin: - """alembic's suite still using this""" diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/mypy.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/mypy.py deleted file mode 100644 index 149df9f..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/mypy.py +++ /dev/null @@ -1,312 +0,0 @@ -# testing/fixtures/mypy.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from __future__ import annotations - -import inspect -import os -from pathlib import Path -import re -import shutil -import sys -import tempfile - -from .base import TestBase -from .. import config -from ..assertions import eq_ -from ... import util - - -@config.add_to_marker.mypy -class MypyTest(TestBase): - __requires__ = ("no_sqlalchemy2_stubs",) - - @config.fixture(scope="function") - def per_func_cachedir(self): - yield from self._cachedir() - - @config.fixture(scope="class") - def cachedir(self): - yield from self._cachedir() - - def _cachedir(self): - # as of mypy 0.971 i think we need to keep mypy_path empty - mypy_path = "" - - with tempfile.TemporaryDirectory() as cachedir: - with open( - Path(cachedir) / "sqla_mypy_config.cfg", "w" - ) as config_file: - config_file.write( - f""" - [mypy]\n - plugins = sqlalchemy.ext.mypy.plugin\n - show_error_codes = True\n - {mypy_path} - disable_error_code = no-untyped-call - - [mypy-sqlalchemy.*] - ignore_errors = True - - """ - ) - with open( - Path(cachedir) / "plain_mypy_config.cfg", "w" - ) as config_file: - config_file.write( - f""" - [mypy]\n - show_error_codes = True\n - {mypy_path} - disable_error_code = var-annotated,no-untyped-call - [mypy-sqlalchemy.*] - ignore_errors = True - - """ - ) - yield cachedir - - @config.fixture() - def mypy_runner(self, cachedir): - from mypy import api - - def run(path, use_plugin=False, use_cachedir=None): - if use_cachedir is None: - use_cachedir = cachedir - args = [ - "--strict", - "--raise-exceptions", - "--cache-dir", - use_cachedir, - "--config-file", - os.path.join( - use_cachedir, - ( - "sqla_mypy_config.cfg" - if use_plugin - else "plain_mypy_config.cfg" - ), - ), - ] - - # mypy as of 0.990 is more aggressively blocking messaging - # for paths that are in sys.path, and as pytest puts currdir, - # test/ etc in sys.path, just copy the source file to the - # tempdir we are working in so that we don't have to try to - # manipulate sys.path and/or guess what mypy is doing - filename = os.path.basename(path) - test_program = os.path.join(use_cachedir, filename) - if path != test_program: - shutil.copyfile(path, test_program) - args.append(test_program) - - # I set this locally but for the suite here needs to be - # disabled - os.environ.pop("MYPY_FORCE_COLOR", None) - - stdout, stderr, exitcode = api.run(args) - return stdout, stderr, exitcode - - return run - - @config.fixture - def mypy_typecheck_file(self, mypy_runner): - def run(path, use_plugin=False): - expected_messages = self._collect_messages(path) - stdout, stderr, exitcode = mypy_runner(path, use_plugin=use_plugin) - self._check_output( - path, expected_messages, stdout, stderr, exitcode - ) - - return run - - @staticmethod - def file_combinations(dirname): - if os.path.isabs(dirname): - path = dirname - else: - caller_path = inspect.stack()[1].filename - path = os.path.join(os.path.dirname(caller_path), dirname) - files = list(Path(path).glob("**/*.py")) - - for extra_dir in config.options.mypy_extra_test_paths: - if extra_dir and os.path.isdir(extra_dir): - files.extend((Path(extra_dir) / dirname).glob("**/*.py")) - return files - - def _collect_messages(self, path): - from sqlalchemy.ext.mypy.util import mypy_14 - - expected_messages = [] - expected_re = re.compile(r"\s*# EXPECTED(_MYPY)?(_RE)?(_TYPE)?: (.+)") - py_ver_re = re.compile(r"^#\s*PYTHON_VERSION\s?>=\s?(\d+\.\d+)") - with open(path) as file_: - current_assert_messages = [] - for num, line in enumerate(file_, 1): - m = py_ver_re.match(line) - if m: - major, _, minor = m.group(1).partition(".") - if sys.version_info < (int(major), int(minor)): - config.skip_test( - "Requires python >= %s" % (m.group(1)) - ) - continue - - m = expected_re.match(line) - if m: - is_mypy = bool(m.group(1)) - is_re = bool(m.group(2)) - is_type = bool(m.group(3)) - - expected_msg = re.sub(r"# noqa[:]? ?.*", "", m.group(4)) - if is_type: - if not is_re: - # the goal here is that we can cut-and-paste - # from vscode -> pylance into the - # EXPECTED_TYPE: line, then the test suite will - # validate that line against what mypy produces - expected_msg = re.sub( - r"([\[\]])", - lambda m: rf"\{m.group(0)}", - expected_msg, - ) - - # note making sure preceding text matches - # with a dot, so that an expect for "Select" - # does not match "TypedSelect" - expected_msg = re.sub( - r"([\w_]+)", - lambda m: rf"(?:.*\.)?{m.group(1)}\*?", - expected_msg, - ) - - expected_msg = re.sub( - "List", "builtins.list", expected_msg - ) - - expected_msg = re.sub( - r"\b(int|str|float|bool)\b", - lambda m: rf"builtins.{m.group(0)}\*?", - expected_msg, - ) - # expected_msg = re.sub( - # r"(Sequence|Tuple|List|Union)", - # lambda m: fr"typing.{m.group(0)}\*?", - # expected_msg, - # ) - - is_mypy = is_re = True - expected_msg = f'Revealed type is "{expected_msg}"' - - if mypy_14 and util.py39: - # use_lowercase_names, py39 and above - # https://github.com/python/mypy/blob/304997bfb85200fb521ac727ee0ce3e6085e5278/mypy/options.py#L363 # noqa: E501 - - # skip first character which could be capitalized - # "List item x not found" type of message - expected_msg = expected_msg[0] + re.sub( - ( - r"\b(List|Tuple|Dict|Set)\b" - if is_type - else r"\b(List|Tuple|Dict|Set|Type)\b" - ), - lambda m: m.group(1).lower(), - expected_msg[1:], - ) - - if mypy_14 and util.py310: - # use_or_syntax, py310 and above - # https://github.com/python/mypy/blob/304997bfb85200fb521ac727ee0ce3e6085e5278/mypy/options.py#L368 # noqa: E501 - expected_msg = re.sub( - r"Optional\[(.*?)\]", - lambda m: f"{m.group(1)} | None", - expected_msg, - ) - current_assert_messages.append( - (is_mypy, is_re, expected_msg.strip()) - ) - elif current_assert_messages: - expected_messages.extend( - (num, is_mypy, is_re, expected_msg) - for ( - is_mypy, - is_re, - expected_msg, - ) in current_assert_messages - ) - current_assert_messages[:] = [] - - return expected_messages - - def _check_output(self, path, expected_messages, stdout, stderr, exitcode): - not_located = [] - filename = os.path.basename(path) - if expected_messages: - # mypy 0.990 changed how return codes work, so don't assume a - # 1 or a 0 return code here, could be either depending on if - # errors were generated or not - - output = [] - - raw_lines = stdout.split("\n") - while raw_lines: - e = raw_lines.pop(0) - if re.match(r".+\.py:\d+: error: .*", e): - output.append(("error", e)) - elif re.match( - r".+\.py:\d+: note: +(?:Possible overload|def ).*", e - ): - while raw_lines: - ol = raw_lines.pop(0) - if not re.match(r".+\.py:\d+: note: +def \[.*", ol): - break - elif re.match( - r".+\.py:\d+: note: .*(?:perhaps|suggestion)", e, re.I - ): - pass - elif re.match(r".+\.py:\d+: note: .*", e): - output.append(("note", e)) - - for num, is_mypy, is_re, msg in expected_messages: - msg = msg.replace("'", '"') - prefix = "[SQLAlchemy Mypy plugin] " if not is_mypy else "" - for idx, (typ, errmsg) in enumerate(output): - if is_re: - if re.match( - rf".*{filename}\:{num}\: {typ}\: {prefix}{msg}", - errmsg, - ): - break - elif ( - f"{filename}:{num}: {typ}: {prefix}{msg}" - in errmsg.replace("'", '"') - ): - break - else: - not_located.append(msg) - continue - del output[idx] - - if not_located: - missing = "\n".join(not_located) - print("Couldn't locate expected messages:", missing, sep="\n") - if output: - extra = "\n".join(msg for _, msg in output) - print("Remaining messages:", extra, sep="\n") - assert False, "expected messages not found, see stdout" - - if output: - print(f"{len(output)} messages from mypy were not consumed:") - print("\n".join(msg for _, msg in output)) - assert False, "errors and/or notes remain, see stdout" - - else: - if exitcode != 0: - print(stdout, stderr, sep="\n") - - eq_(exitcode, 0, msg=stdout) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/orm.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/orm.py deleted file mode 100644 index 5ddd21e..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/orm.py +++ /dev/null @@ -1,227 +0,0 @@ -# testing/fixtures/orm.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors -from __future__ import annotations - -from typing import Any - -import sqlalchemy as sa -from .base import TestBase -from .sql import TablesTest -from .. import assertions -from .. import config -from .. import schema -from ..entities import BasicEntity -from ..entities import ComparableEntity -from ..util import adict -from ... import orm -from ...orm import DeclarativeBase -from ...orm import events as orm_events -from ...orm import registry - - -class ORMTest(TestBase): - @config.fixture - def fixture_session(self): - return fixture_session() - - -class MappedTest(ORMTest, TablesTest, assertions.AssertsExecutionResults): - # 'once', 'each', None - run_setup_classes = "once" - - # 'once', 'each', None - run_setup_mappers = "each" - - classes: Any = None - - @config.fixture(autouse=True, scope="class") - def _setup_tables_test_class(self): - cls = self.__class__ - cls._init_class() - - if cls.classes is None: - cls.classes = adict() - - cls._setup_once_tables() - cls._setup_once_classes() - cls._setup_once_mappers() - cls._setup_once_inserts() - - yield - - cls._teardown_once_class() - cls._teardown_once_metadata_bind() - - @config.fixture(autouse=True, scope="function") - def _setup_tables_test_instance(self): - self._setup_each_tables() - self._setup_each_classes() - self._setup_each_mappers() - self._setup_each_inserts() - - yield - - orm.session.close_all_sessions() - self._teardown_each_mappers() - self._teardown_each_classes() - self._teardown_each_tables() - - @classmethod - def _teardown_once_class(cls): - cls.classes.clear() - - @classmethod - def _setup_once_classes(cls): - if cls.run_setup_classes == "once": - cls._with_register_classes(cls.setup_classes) - - @classmethod - def _setup_once_mappers(cls): - if cls.run_setup_mappers == "once": - cls.mapper_registry, cls.mapper = cls._generate_registry() - cls._with_register_classes(cls.setup_mappers) - - def _setup_each_mappers(self): - if self.run_setup_mappers != "once": - ( - self.__class__.mapper_registry, - self.__class__.mapper, - ) = self._generate_registry() - - if self.run_setup_mappers == "each": - self._with_register_classes(self.setup_mappers) - - def _setup_each_classes(self): - if self.run_setup_classes == "each": - self._with_register_classes(self.setup_classes) - - @classmethod - def _generate_registry(cls): - decl = registry(metadata=cls._tables_metadata) - return decl, decl.map_imperatively - - @classmethod - def _with_register_classes(cls, fn): - """Run a setup method, framing the operation with a Base class - that will catch new subclasses to be established within - the "classes" registry. - - """ - cls_registry = cls.classes - - class _Base: - def __init_subclass__(cls) -> None: - assert cls_registry is not None - cls_registry[cls.__name__] = cls - super().__init_subclass__() - - class Basic(BasicEntity, _Base): - pass - - class Comparable(ComparableEntity, _Base): - pass - - cls.Basic = Basic - cls.Comparable = Comparable - fn() - - def _teardown_each_mappers(self): - # some tests create mappers in the test bodies - # and will define setup_mappers as None - - # clear mappers in any case - if self.run_setup_mappers != "once": - orm.clear_mappers() - - def _teardown_each_classes(self): - if self.run_setup_classes != "once": - self.classes.clear() - - @classmethod - def setup_classes(cls): - pass - - @classmethod - def setup_mappers(cls): - pass - - -class DeclarativeMappedTest(MappedTest): - run_setup_classes = "once" - run_setup_mappers = "once" - - @classmethod - def _setup_once_tables(cls): - pass - - @classmethod - def _with_register_classes(cls, fn): - cls_registry = cls.classes - - class _DeclBase(DeclarativeBase): - __table_cls__ = schema.Table - metadata = cls._tables_metadata - type_annotation_map = { - str: sa.String().with_variant( - sa.String(50), "mysql", "mariadb", "oracle" - ) - } - - def __init_subclass__(cls, **kw) -> None: - assert cls_registry is not None - cls_registry[cls.__name__] = cls - super().__init_subclass__(**kw) - - cls.DeclarativeBasic = _DeclBase - - # sets up cls.Basic which is helpful for things like composite - # classes - super()._with_register_classes(fn) - - if cls._tables_metadata.tables and cls.run_create_tables: - cls._tables_metadata.create_all(config.db) - - -class RemoveORMEventsGlobally: - @config.fixture(autouse=True) - def _remove_listeners(self): - yield - orm_events.MapperEvents._clear() - orm_events.InstanceEvents._clear() - orm_events.SessionEvents._clear() - orm_events.InstrumentationEvents._clear() - orm_events.QueryEvents._clear() - - -_fixture_sessions = set() - - -def fixture_session(**kw): - kw.setdefault("autoflush", True) - kw.setdefault("expire_on_commit", True) - - bind = kw.pop("bind", config.db) - - sess = orm.Session(bind, **kw) - _fixture_sessions.add(sess) - return sess - - -def close_all_sessions(): - # will close all still-referenced sessions - orm.close_all_sessions() - _fixture_sessions.clear() - - -def stop_test_class_inside_fixtures(cls): - close_all_sessions() - orm.clear_mappers() - - -def after_test(): - if _fixture_sessions: - close_all_sessions() diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/sql.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/sql.py deleted file mode 100644 index 830fa27..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/fixtures/sql.py +++ /dev/null @@ -1,493 +0,0 @@ -# testing/fixtures/sql.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors -from __future__ import annotations - -import itertools -import random -import re -import sys - -import sqlalchemy as sa -from .base import TestBase -from .. import config -from .. import mock -from ..assertions import eq_ -from ..assertions import ne_ -from ..util import adict -from ..util import drop_all_tables_from_metadata -from ... import event -from ... import util -from ...schema import sort_tables_and_constraints -from ...sql import visitors -from ...sql.elements import ClauseElement - - -class TablesTest(TestBase): - # 'once', None - run_setup_bind = "once" - - # 'once', 'each', None - run_define_tables = "once" - - # 'once', 'each', None - run_create_tables = "once" - - # 'once', 'each', None - run_inserts = "each" - - # 'each', None - run_deletes = "each" - - # 'once', None - run_dispose_bind = None - - bind = None - _tables_metadata = None - tables = None - other = None - sequences = None - - @config.fixture(autouse=True, scope="class") - def _setup_tables_test_class(self): - cls = self.__class__ - cls._init_class() - - cls._setup_once_tables() - - cls._setup_once_inserts() - - yield - - cls._teardown_once_metadata_bind() - - @config.fixture(autouse=True, scope="function") - def _setup_tables_test_instance(self): - self._setup_each_tables() - self._setup_each_inserts() - - yield - - self._teardown_each_tables() - - @property - def tables_test_metadata(self): - return self._tables_metadata - - @classmethod - def _init_class(cls): - if cls.run_define_tables == "each": - if cls.run_create_tables == "once": - cls.run_create_tables = "each" - assert cls.run_inserts in ("each", None) - - cls.other = adict() - cls.tables = adict() - cls.sequences = adict() - - cls.bind = cls.setup_bind() - cls._tables_metadata = sa.MetaData() - - @classmethod - def _setup_once_inserts(cls): - if cls.run_inserts == "once": - cls._load_fixtures() - with cls.bind.begin() as conn: - cls.insert_data(conn) - - @classmethod - def _setup_once_tables(cls): - if cls.run_define_tables == "once": - cls.define_tables(cls._tables_metadata) - if cls.run_create_tables == "once": - cls._tables_metadata.create_all(cls.bind) - cls.tables.update(cls._tables_metadata.tables) - cls.sequences.update(cls._tables_metadata._sequences) - - def _setup_each_tables(self): - if self.run_define_tables == "each": - self.define_tables(self._tables_metadata) - if self.run_create_tables == "each": - self._tables_metadata.create_all(self.bind) - self.tables.update(self._tables_metadata.tables) - self.sequences.update(self._tables_metadata._sequences) - elif self.run_create_tables == "each": - self._tables_metadata.create_all(self.bind) - - def _setup_each_inserts(self): - if self.run_inserts == "each": - self._load_fixtures() - with self.bind.begin() as conn: - self.insert_data(conn) - - def _teardown_each_tables(self): - if self.run_define_tables == "each": - self.tables.clear() - if self.run_create_tables == "each": - drop_all_tables_from_metadata(self._tables_metadata, self.bind) - self._tables_metadata.clear() - elif self.run_create_tables == "each": - drop_all_tables_from_metadata(self._tables_metadata, self.bind) - - savepoints = getattr(config.requirements, "savepoints", False) - if savepoints: - savepoints = savepoints.enabled - - # no need to run deletes if tables are recreated on setup - if ( - self.run_define_tables != "each" - and self.run_create_tables != "each" - and self.run_deletes == "each" - ): - with self.bind.begin() as conn: - for table in reversed( - [ - t - for (t, fks) in sort_tables_and_constraints( - self._tables_metadata.tables.values() - ) - if t is not None - ] - ): - try: - if savepoints: - with conn.begin_nested(): - conn.execute(table.delete()) - else: - conn.execute(table.delete()) - except sa.exc.DBAPIError as ex: - print( - ("Error emptying table %s: %r" % (table, ex)), - file=sys.stderr, - ) - - @classmethod - def _teardown_once_metadata_bind(cls): - if cls.run_create_tables: - drop_all_tables_from_metadata(cls._tables_metadata, cls.bind) - - if cls.run_dispose_bind == "once": - cls.dispose_bind(cls.bind) - - cls._tables_metadata.bind = None - - if cls.run_setup_bind is not None: - cls.bind = None - - @classmethod - def setup_bind(cls): - return config.db - - @classmethod - def dispose_bind(cls, bind): - if hasattr(bind, "dispose"): - bind.dispose() - elif hasattr(bind, "close"): - bind.close() - - @classmethod - def define_tables(cls, metadata): - pass - - @classmethod - def fixtures(cls): - return {} - - @classmethod - def insert_data(cls, connection): - pass - - def sql_count_(self, count, fn): - self.assert_sql_count(self.bind, fn, count) - - def sql_eq_(self, callable_, statements): - self.assert_sql(self.bind, callable_, statements) - - @classmethod - def _load_fixtures(cls): - """Insert rows as represented by the fixtures() method.""" - headers, rows = {}, {} - for table, data in cls.fixtures().items(): - if len(data) < 2: - continue - if isinstance(table, str): - table = cls.tables[table] - headers[table] = data[0] - rows[table] = data[1:] - for table, fks in sort_tables_and_constraints( - cls._tables_metadata.tables.values() - ): - if table is None: - continue - if table not in headers: - continue - with cls.bind.begin() as conn: - conn.execute( - table.insert(), - [ - dict(zip(headers[table], column_values)) - for column_values in rows[table] - ], - ) - - -class NoCache: - @config.fixture(autouse=True, scope="function") - def _disable_cache(self): - _cache = config.db._compiled_cache - config.db._compiled_cache = None - yield - config.db._compiled_cache = _cache - - -class RemovesEvents: - @util.memoized_property - def _event_fns(self): - return set() - - def event_listen(self, target, name, fn, **kw): - self._event_fns.add((target, name, fn)) - event.listen(target, name, fn, **kw) - - @config.fixture(autouse=True, scope="function") - def _remove_events(self): - yield - for key in self._event_fns: - event.remove(*key) - - -class ComputedReflectionFixtureTest(TablesTest): - run_inserts = run_deletes = None - - __backend__ = True - __requires__ = ("computed_columns", "table_reflection") - - regexp = re.compile(r"[\[\]\(\)\s`'\"]*") - - def normalize(self, text): - return self.regexp.sub("", text).lower() - - @classmethod - def define_tables(cls, metadata): - from ... import Integer - from ... import testing - from ...schema import Column - from ...schema import Computed - from ...schema import Table - - Table( - "computed_default_table", - metadata, - Column("id", Integer, primary_key=True), - Column("normal", Integer), - Column("computed_col", Integer, Computed("normal + 42")), - Column("with_default", Integer, server_default="42"), - ) - - t = Table( - "computed_column_table", - metadata, - Column("id", Integer, primary_key=True), - Column("normal", Integer), - Column("computed_no_flag", Integer, Computed("normal + 42")), - ) - - if testing.requires.schemas.enabled: - t2 = Table( - "computed_column_table", - metadata, - Column("id", Integer, primary_key=True), - Column("normal", Integer), - Column("computed_no_flag", Integer, Computed("normal / 42")), - schema=config.test_schema, - ) - - if testing.requires.computed_columns_virtual.enabled: - t.append_column( - Column( - "computed_virtual", - Integer, - Computed("normal + 2", persisted=False), - ) - ) - if testing.requires.schemas.enabled: - t2.append_column( - Column( - "computed_virtual", - Integer, - Computed("normal / 2", persisted=False), - ) - ) - if testing.requires.computed_columns_stored.enabled: - t.append_column( - Column( - "computed_stored", - Integer, - Computed("normal - 42", persisted=True), - ) - ) - if testing.requires.schemas.enabled: - t2.append_column( - Column( - "computed_stored", - Integer, - Computed("normal * 42", persisted=True), - ) - ) - - -class CacheKeyFixture: - def _compare_equal(self, a, b, compare_values): - a_key = a._generate_cache_key() - b_key = b._generate_cache_key() - - if a_key is None: - assert a._annotations.get("nocache") - - assert b_key is None - else: - eq_(a_key.key, b_key.key) - eq_(hash(a_key.key), hash(b_key.key)) - - for a_param, b_param in zip(a_key.bindparams, b_key.bindparams): - assert a_param.compare(b_param, compare_values=compare_values) - return a_key, b_key - - def _run_cache_key_fixture(self, fixture, compare_values): - case_a = fixture() - case_b = fixture() - - for a, b in itertools.combinations_with_replacement( - range(len(case_a)), 2 - ): - if a == b: - a_key, b_key = self._compare_equal( - case_a[a], case_b[b], compare_values - ) - if a_key is None: - continue - else: - a_key = case_a[a]._generate_cache_key() - b_key = case_b[b]._generate_cache_key() - - if a_key is None or b_key is None: - if a_key is None: - assert case_a[a]._annotations.get("nocache") - if b_key is None: - assert case_b[b]._annotations.get("nocache") - continue - - if a_key.key == b_key.key: - for a_param, b_param in zip( - a_key.bindparams, b_key.bindparams - ): - if not a_param.compare( - b_param, compare_values=compare_values - ): - break - else: - # this fails unconditionally since we could not - # find bound parameter values that differed. - # Usually we intended to get two distinct keys here - # so the failure will be more descriptive using the - # ne_() assertion. - ne_(a_key.key, b_key.key) - else: - ne_(a_key.key, b_key.key) - - # ClauseElement-specific test to ensure the cache key - # collected all the bound parameters that aren't marked - # as "literal execute" - if isinstance(case_a[a], ClauseElement) and isinstance( - case_b[b], ClauseElement - ): - assert_a_params = [] - assert_b_params = [] - - for elem in visitors.iterate(case_a[a]): - if elem.__visit_name__ == "bindparam": - assert_a_params.append(elem) - - for elem in visitors.iterate(case_b[b]): - if elem.__visit_name__ == "bindparam": - assert_b_params.append(elem) - - # note we're asserting the order of the params as well as - # if there are dupes or not. ordering has to be - # deterministic and matches what a traversal would provide. - eq_( - sorted(a_key.bindparams, key=lambda b: b.key), - sorted( - util.unique_list(assert_a_params), key=lambda b: b.key - ), - ) - eq_( - sorted(b_key.bindparams, key=lambda b: b.key), - sorted( - util.unique_list(assert_b_params), key=lambda b: b.key - ), - ) - - def _run_cache_key_equal_fixture(self, fixture, compare_values): - case_a = fixture() - case_b = fixture() - - for a, b in itertools.combinations_with_replacement( - range(len(case_a)), 2 - ): - self._compare_equal(case_a[a], case_b[b], compare_values) - - -def insertmanyvalues_fixture( - connection, randomize_rows=False, warn_on_downgraded=False -): - dialect = connection.dialect - orig_dialect = dialect._deliver_insertmanyvalues_batches - orig_conn = connection._exec_insertmany_context - - class RandomCursor: - __slots__ = ("cursor",) - - def __init__(self, cursor): - self.cursor = cursor - - # only this method is called by the deliver method. - # by not having the other methods we assert that those aren't being - # used - - @property - def description(self): - return self.cursor.description - - def fetchall(self): - rows = self.cursor.fetchall() - rows = list(rows) - random.shuffle(rows) - return rows - - def _deliver_insertmanyvalues_batches( - cursor, statement, parameters, generic_setinputsizes, context - ): - if randomize_rows: - cursor = RandomCursor(cursor) - for batch in orig_dialect( - cursor, statement, parameters, generic_setinputsizes, context - ): - if warn_on_downgraded and batch.is_downgraded: - util.warn("Batches were downgraded for sorted INSERT") - - yield batch - - def _exec_insertmany_context(dialect, context): - with mock.patch.object( - dialect, - "_deliver_insertmanyvalues_batches", - new=_deliver_insertmanyvalues_batches, - ): - return orig_conn(dialect, context) - - connection._exec_insertmany_context = _exec_insertmany_context diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/pickleable.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/pickleable.py deleted file mode 100644 index 761891a..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/pickleable.py +++ /dev/null @@ -1,155 +0,0 @@ -# testing/pickleable.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -"""Classes used in pickling tests, need to be at the module level for -unpickling. -""" - -from __future__ import annotations - -from .entities import ComparableEntity -from ..schema import Column -from ..types import String - - -class User(ComparableEntity): - pass - - -class Order(ComparableEntity): - pass - - -class Dingaling(ComparableEntity): - pass - - -class EmailUser(User): - pass - - -class Address(ComparableEntity): - pass - - -# TODO: these are kind of arbitrary.... -class Child1(ComparableEntity): - pass - - -class Child2(ComparableEntity): - pass - - -class Parent(ComparableEntity): - pass - - -class Screen: - def __init__(self, obj, parent=None): - self.obj = obj - self.parent = parent - - -class Mixin: - email_address = Column(String) - - -class AddressWMixin(Mixin, ComparableEntity): - pass - - -class Foo: - def __init__(self, moredata, stuff="im stuff"): - self.data = "im data" - self.stuff = stuff - self.moredata = moredata - - __hash__ = object.__hash__ - - def __eq__(self, other): - return ( - other.data == self.data - and other.stuff == self.stuff - and other.moredata == self.moredata - ) - - -class Bar: - def __init__(self, x, y): - self.x = x - self.y = y - - __hash__ = object.__hash__ - - def __eq__(self, other): - return ( - other.__class__ is self.__class__ - and other.x == self.x - and other.y == self.y - ) - - def __str__(self): - return "Bar(%d, %d)" % (self.x, self.y) - - -class OldSchool: - def __init__(self, x, y): - self.x = x - self.y = y - - def __eq__(self, other): - return ( - other.__class__ is self.__class__ - and other.x == self.x - and other.y == self.y - ) - - -class OldSchoolWithoutCompare: - def __init__(self, x, y): - self.x = x - self.y = y - - -class BarWithoutCompare: - def __init__(self, x, y): - self.x = x - self.y = y - - def __str__(self): - return "Bar(%d, %d)" % (self.x, self.y) - - -class NotComparable: - def __init__(self, data): - self.data = data - - def __hash__(self): - return id(self) - - def __eq__(self, other): - return NotImplemented - - def __ne__(self, other): - return NotImplemented - - -class BrokenComparable: - def __init__(self, data): - self.data = data - - def __hash__(self): - return id(self) - - def __eq__(self, other): - raise NotImplementedError - - def __ne__(self, other): - raise NotImplementedError diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__init__.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__init__.py deleted file mode 100644 index 0f98777..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# testing/plugin/__init__.py -# Copyright (C) 2005-2024 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 diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/__init__.cpython-311.pyc Binary files differdeleted file mode 100644 index 163dc80..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/__init__.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-311.pyc Binary files differdeleted file mode 100644 index 9b70281..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-311.pyc Binary files differdeleted file mode 100644 index c65e39e..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-311.pyc Binary files differdeleted file mode 100644 index 6eb39ab..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/bootstrap.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/bootstrap.py deleted file mode 100644 index d0d3754..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/bootstrap.py +++ /dev/null @@ -1,51 +0,0 @@ -# testing/plugin/bootstrap.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -""" -Bootstrapper for test framework plugins. - -The entire rationale for this system is to get the modules in plugin/ -imported without importing all of the supporting library, so that we can -set up things for testing before coverage starts. - -The rationale for all of plugin/ being *in* the supporting library in the -first place is so that the testing and plugin suite is available to other -libraries, mainly external SQLAlchemy and Alembic dialects, to make use -of the same test environment and standard suites available to -SQLAlchemy/Alembic themselves without the need to ship/install a separate -package outside of SQLAlchemy. - - -""" - -import importlib.util -import os -import sys - - -bootstrap_file = locals()["bootstrap_file"] -to_bootstrap = locals()["to_bootstrap"] - - -def load_file_as_module(name): - path = os.path.join(os.path.dirname(bootstrap_file), "%s.py" % name) - - spec = importlib.util.spec_from_file_location(name, path) - assert spec is not None - assert spec.loader is not None - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - return mod - - -if to_bootstrap == "pytest": - sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base") - sys.modules["sqla_plugin_base"].bootstrapped_as_sqlalchemy = True - sys.modules["sqla_pytestplugin"] = load_file_as_module("pytestplugin") -else: - raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/plugin_base.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/plugin_base.py deleted file mode 100644 index a642668..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/plugin_base.py +++ /dev/null @@ -1,779 +0,0 @@ -# testing/plugin/plugin_base.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from __future__ import annotations - -import abc -from argparse import Namespace -import configparser -import logging -import os -from pathlib import Path -import re -import sys -from typing import Any - -from sqlalchemy.testing import asyncio - -"""Testing extensions. - -this module is designed to work as a testing-framework-agnostic library, -created so that multiple test frameworks can be supported at once -(mostly so that we can migrate to new ones). The current target -is pytest. - -""" - -# flag which indicates we are in the SQLAlchemy testing suite, -# and not that of Alembic or a third party dialect. -bootstrapped_as_sqlalchemy = False - -log = logging.getLogger("sqlalchemy.testing.plugin_base") - -# late imports -fixtures = None -engines = None -exclusions = None -warnings = None -profiling = None -provision = None -assertions = None -requirements = None -config = None -testing = None -util = None -file_config = None - -logging = None -include_tags = set() -exclude_tags = set() -options: Namespace = None # type: ignore - - -def setup_options(make_option): - make_option( - "--log-info", - action="callback", - type=str, - callback=_log, - help="turn on info logging for <LOG> (multiple OK)", - ) - make_option( - "--log-debug", - action="callback", - type=str, - callback=_log, - help="turn on debug logging for <LOG> (multiple OK)", - ) - make_option( - "--db", - action="append", - type=str, - dest="db", - help="Use prefab database uri. Multiple OK, " - "first one is run by default.", - ) - make_option( - "--dbs", - action="callback", - zeroarg_callback=_list_dbs, - help="List available prefab dbs", - ) - make_option( - "--dburi", - action="append", - type=str, - dest="dburi", - help="Database uri. Multiple OK, first one is run by default.", - ) - make_option( - "--dbdriver", - action="append", - type=str, - dest="dbdriver", - help="Additional database drivers to include in tests. " - "These are linked to the existing database URLs by the " - "provisioning system.", - ) - make_option( - "--dropfirst", - action="store_true", - dest="dropfirst", - help="Drop all tables in the target database first", - ) - make_option( - "--disable-asyncio", - action="store_true", - help="disable test / fixtures / provisoning running in asyncio", - ) - make_option( - "--backend-only", - action="callback", - zeroarg_callback=_set_tag_include("backend"), - help=( - "Run only tests marked with __backend__ or __sparse_backend__; " - "this is now equivalent to the pytest -m backend mark expression" - ), - ) - make_option( - "--nomemory", - action="callback", - zeroarg_callback=_set_tag_exclude("memory_intensive"), - help="Don't run memory profiling tests; " - "this is now equivalent to the pytest -m 'not memory_intensive' " - "mark expression", - ) - make_option( - "--notimingintensive", - action="callback", - zeroarg_callback=_set_tag_exclude("timing_intensive"), - help="Don't run timing intensive tests; " - "this is now equivalent to the pytest -m 'not timing_intensive' " - "mark expression", - ) - make_option( - "--nomypy", - action="callback", - zeroarg_callback=_set_tag_exclude("mypy"), - help="Don't run mypy typing tests; " - "this is now equivalent to the pytest -m 'not mypy' mark expression", - ) - make_option( - "--profile-sort", - type=str, - default="cumulative", - dest="profilesort", - help="Type of sort for profiling standard output", - ) - make_option( - "--profile-dump", - type=str, - dest="profiledump", - help="Filename where a single profile run will be dumped", - ) - make_option( - "--low-connections", - action="store_true", - dest="low_connections", - help="Use a low number of distinct connections - " - "i.e. for Oracle TNS", - ) - make_option( - "--write-idents", - type=str, - dest="write_idents", - help="write out generated follower idents to <file>, " - "when -n<num> is used", - ) - make_option( - "--requirements", - action="callback", - type=str, - callback=_requirements_opt, - help="requirements class for testing, overrides setup.cfg", - ) - make_option( - "--include-tag", - action="callback", - callback=_include_tag, - type=str, - help="Include tests with tag <tag>; " - "legacy, use pytest -m 'tag' instead", - ) - make_option( - "--exclude-tag", - action="callback", - callback=_exclude_tag, - type=str, - help="Exclude tests with tag <tag>; " - "legacy, use pytest -m 'not tag' instead", - ) - make_option( - "--write-profiles", - action="store_true", - dest="write_profiles", - default=False, - help="Write/update failing profiling data.", - ) - make_option( - "--force-write-profiles", - action="store_true", - dest="force_write_profiles", - default=False, - help="Unconditionally write/update profiling data.", - ) - make_option( - "--dump-pyannotate", - type=str, - dest="dump_pyannotate", - help="Run pyannotate and dump json info to given file", - ) - make_option( - "--mypy-extra-test-path", - type=str, - action="append", - default=[], - dest="mypy_extra_test_paths", - help="Additional test directories to add to the mypy tests. " - "This is used only when running mypy tests. Multiple OK", - ) - # db specific options - make_option( - "--postgresql-templatedb", - type=str, - help="name of template database to use for PostgreSQL " - "CREATE DATABASE (defaults to current database)", - ) - make_option( - "--oracledb-thick-mode", - action="store_true", - help="enables the 'thick mode' when testing with oracle+oracledb", - ) - - -def configure_follower(follower_ident): - """Configure required state for a follower. - - This invokes in the parent process and typically includes - database creation. - - """ - from sqlalchemy.testing import provision - - provision.FOLLOWER_IDENT = follower_ident - - -def memoize_important_follower_config(dict_): - """Store important configuration we will need to send to a follower. - - This invokes in the parent process after normal config is set up. - - Hook is currently not used. - - """ - - -def restore_important_follower_config(dict_): - """Restore important configuration needed by a follower. - - This invokes in the follower process. - - Hook is currently not used. - - """ - - -def read_config(root_path): - global file_config - file_config = configparser.ConfigParser() - file_config.read( - [str(root_path / "setup.cfg"), str(root_path / "test.cfg")] - ) - - -def pre_begin(opt): - """things to set up early, before coverage might be setup.""" - global options - options = opt - for fn in pre_configure: - fn(options, file_config) - - -def set_coverage_flag(value): - options.has_coverage = value - - -def post_begin(): - """things to set up later, once we know coverage is running.""" - # Lazy setup of other options (post coverage) - for fn in post_configure: - fn(options, file_config) - - # late imports, has to happen after config. - global util, fixtures, engines, exclusions, assertions, provision - global warnings, profiling, config, testing - from sqlalchemy import testing # noqa - from sqlalchemy.testing import fixtures, engines, exclusions # noqa - from sqlalchemy.testing import assertions, warnings, profiling # noqa - from sqlalchemy.testing import config, provision # noqa - from sqlalchemy import util # noqa - - warnings.setup_filters() - - -def _log(opt_str, value, parser): - global logging - if not logging: - import logging - - logging.basicConfig() - - if opt_str.endswith("-info"): - logging.getLogger(value).setLevel(logging.INFO) - elif opt_str.endswith("-debug"): - logging.getLogger(value).setLevel(logging.DEBUG) - - -def _list_dbs(*args): - if file_config is None: - # assume the current working directory is the one containing the - # setup file - read_config(Path.cwd()) - print("Available --db options (use --dburi to override)") - for macro in sorted(file_config.options("db")): - print("%20s\t%s" % (macro, file_config.get("db", macro))) - sys.exit(0) - - -def _requirements_opt(opt_str, value, parser): - _setup_requirements(value) - - -def _set_tag_include(tag): - def _do_include_tag(opt_str, value, parser): - _include_tag(opt_str, tag, parser) - - return _do_include_tag - - -def _set_tag_exclude(tag): - def _do_exclude_tag(opt_str, value, parser): - _exclude_tag(opt_str, tag, parser) - - return _do_exclude_tag - - -def _exclude_tag(opt_str, value, parser): - exclude_tags.add(value.replace("-", "_")) - - -def _include_tag(opt_str, value, parser): - include_tags.add(value.replace("-", "_")) - - -pre_configure = [] -post_configure = [] - - -def pre(fn): - pre_configure.append(fn) - return fn - - -def post(fn): - post_configure.append(fn) - return fn - - -@pre -def _setup_options(opt, file_config): - global options - options = opt - - -@pre -def _register_sqlite_numeric_dialect(opt, file_config): - from sqlalchemy.dialects import registry - - registry.register( - "sqlite.pysqlite_numeric", - "sqlalchemy.dialects.sqlite.pysqlite", - "_SQLiteDialect_pysqlite_numeric", - ) - registry.register( - "sqlite.pysqlite_dollar", - "sqlalchemy.dialects.sqlite.pysqlite", - "_SQLiteDialect_pysqlite_dollar", - ) - - -@post -def __ensure_cext(opt, file_config): - if os.environ.get("REQUIRE_SQLALCHEMY_CEXT", "0") == "1": - from sqlalchemy.util import has_compiled_ext - - try: - has_compiled_ext(raise_=True) - except ImportError as err: - raise AssertionError( - "REQUIRE_SQLALCHEMY_CEXT is set but can't import the " - "cython extensions" - ) from err - - -@post -def _init_symbols(options, file_config): - from sqlalchemy.testing import config - - config._fixture_functions = _fixture_fn_class() - - -@pre -def _set_disable_asyncio(opt, file_config): - if opt.disable_asyncio: - asyncio.ENABLE_ASYNCIO = False - - -@post -def _engine_uri(options, file_config): - from sqlalchemy import testing - from sqlalchemy.testing import config - from sqlalchemy.testing import provision - from sqlalchemy.engine import url as sa_url - - if options.dburi: - db_urls = list(options.dburi) - else: - db_urls = [] - - extra_drivers = options.dbdriver or [] - - if options.db: - for db_token in options.db: - for db in re.split(r"[,\s]+", db_token): - if db not in file_config.options("db"): - raise RuntimeError( - "Unknown URI specifier '%s'. " - "Specify --dbs for known uris." % db - ) - else: - db_urls.append(file_config.get("db", db)) - - if not db_urls: - db_urls.append(file_config.get("db", "default")) - - config._current = None - - if options.write_idents and provision.FOLLOWER_IDENT: - for db_url in [sa_url.make_url(db_url) for db_url in db_urls]: - with open(options.write_idents, "a") as file_: - file_.write( - f"{provision.FOLLOWER_IDENT} " - f"{db_url.render_as_string(hide_password=False)}\n" - ) - - expanded_urls = list(provision.generate_db_urls(db_urls, extra_drivers)) - - for db_url in expanded_urls: - log.info("Adding database URL: %s", db_url) - - cfg = provision.setup_config( - db_url, options, file_config, provision.FOLLOWER_IDENT - ) - if not config._current: - cfg.set_as_current(cfg, testing) - - -@post -def _requirements(options, file_config): - requirement_cls = file_config.get("sqla_testing", "requirement_cls") - _setup_requirements(requirement_cls) - - -def _setup_requirements(argument): - from sqlalchemy.testing import config - from sqlalchemy import testing - - modname, clsname = argument.split(":") - - # importlib.import_module() only introduced in 2.7, a little - # late - mod = __import__(modname) - for component in modname.split(".")[1:]: - mod = getattr(mod, component) - req_cls = getattr(mod, clsname) - - config.requirements = testing.requires = req_cls() - - config.bootstrapped_as_sqlalchemy = bootstrapped_as_sqlalchemy - - -@post -def _prep_testing_database(options, file_config): - from sqlalchemy.testing import config - - if options.dropfirst: - from sqlalchemy.testing import provision - - for cfg in config.Config.all_configs(): - provision.drop_all_schema_objects(cfg, cfg.db) - - -@post -def _post_setup_options(opt, file_config): - from sqlalchemy.testing import config - - config.options = options - config.file_config = file_config - - -@post -def _setup_profiling(options, file_config): - from sqlalchemy.testing import profiling - - profiling._profile_stats = profiling.ProfileStatsFile( - file_config.get("sqla_testing", "profile_file"), - sort=options.profilesort, - dump=options.profiledump, - ) - - -def want_class(name, cls): - if not issubclass(cls, fixtures.TestBase): - return False - elif name.startswith("_"): - return False - else: - return True - - -def want_method(cls, fn): - if not fn.__name__.startswith("test_"): - return False - elif fn.__module__ is None: - return False - else: - return True - - -def generate_sub_tests(cls, module, markers): - if "backend" in markers or "sparse_backend" in markers: - sparse = "sparse_backend" in markers - for cfg in _possible_configs_for_cls(cls, sparse=sparse): - orig_name = cls.__name__ - - # we can have special chars in these names except for the - # pytest junit plugin, which is tripped up by the brackets - # and periods, so sanitize - - alpha_name = re.sub(r"[_\[\]\.]+", "_", cfg.name) - alpha_name = re.sub(r"_+$", "", alpha_name) - name = "%s_%s" % (cls.__name__, alpha_name) - subcls = type( - name, - (cls,), - {"_sa_orig_cls_name": orig_name, "__only_on_config__": cfg}, - ) - setattr(module, name, subcls) - yield subcls - else: - yield cls - - -def start_test_class_outside_fixtures(cls): - _do_skips(cls) - _setup_engine(cls) - - -def stop_test_class(cls): - # close sessions, immediate connections, etc. - fixtures.stop_test_class_inside_fixtures(cls) - - # close outstanding connection pool connections, dispose of - # additional engines - engines.testing_reaper.stop_test_class_inside_fixtures() - - -def stop_test_class_outside_fixtures(cls): - engines.testing_reaper.stop_test_class_outside_fixtures() - provision.stop_test_class_outside_fixtures(config, config.db, cls) - try: - if not options.low_connections: - assertions.global_cleanup_assertions() - finally: - _restore_engine() - - -def _restore_engine(): - if config._current: - config._current.reset(testing) - - -def final_process_cleanup(): - engines.testing_reaper.final_cleanup() - assertions.global_cleanup_assertions() - _restore_engine() - - -def _setup_engine(cls): - if getattr(cls, "__engine_options__", None): - opts = dict(cls.__engine_options__) - opts["scope"] = "class" - eng = engines.testing_engine(options=opts) - config._current.push_engine(eng, testing) - - -def before_test(test, test_module_name, test_class, test_name): - # format looks like: - # "test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause" - - name = getattr(test_class, "_sa_orig_cls_name", test_class.__name__) - - id_ = "%s.%s.%s" % (test_module_name, name, test_name) - - profiling._start_current_test(id_) - - -def after_test(test): - fixtures.after_test() - engines.testing_reaper.after_test() - - -def after_test_fixtures(test): - engines.testing_reaper.after_test_outside_fixtures(test) - - -def _possible_configs_for_cls(cls, reasons=None, sparse=False): - all_configs = set(config.Config.all_configs()) - - if cls.__unsupported_on__: - spec = exclusions.db_spec(*cls.__unsupported_on__) - for config_obj in list(all_configs): - if spec(config_obj): - all_configs.remove(config_obj) - - if getattr(cls, "__only_on__", None): - spec = exclusions.db_spec(*util.to_list(cls.__only_on__)) - for config_obj in list(all_configs): - if not spec(config_obj): - all_configs.remove(config_obj) - - if getattr(cls, "__only_on_config__", None): - all_configs.intersection_update([cls.__only_on_config__]) - - if hasattr(cls, "__requires__"): - requirements = config.requirements - for config_obj in list(all_configs): - for requirement in cls.__requires__: - check = getattr(requirements, requirement) - - skip_reasons = check.matching_config_reasons(config_obj) - if skip_reasons: - all_configs.remove(config_obj) - if reasons is not None: - reasons.extend(skip_reasons) - break - - if hasattr(cls, "__prefer_requires__"): - non_preferred = set() - requirements = config.requirements - for config_obj in list(all_configs): - for requirement in cls.__prefer_requires__: - check = getattr(requirements, requirement) - - if not check.enabled_for_config(config_obj): - non_preferred.add(config_obj) - if all_configs.difference(non_preferred): - all_configs.difference_update(non_preferred) - - if sparse: - # pick only one config from each base dialect - # sorted so we get the same backend each time selecting the highest - # server version info. - per_dialect = {} - for cfg in reversed( - sorted( - all_configs, - key=lambda cfg: ( - cfg.db.name, - cfg.db.driver, - cfg.db.dialect.server_version_info, - ), - ) - ): - db = cfg.db.name - if db not in per_dialect: - per_dialect[db] = cfg - return per_dialect.values() - - return all_configs - - -def _do_skips(cls): - reasons = [] - all_configs = _possible_configs_for_cls(cls, reasons) - - if getattr(cls, "__skip_if__", False): - for c in getattr(cls, "__skip_if__"): - if c(): - config.skip_test( - "'%s' skipped by %s" % (cls.__name__, c.__name__) - ) - - if not all_configs: - msg = "'%s.%s' unsupported on any DB implementation %s%s" % ( - cls.__module__, - cls.__name__, - ", ".join( - "'%s(%s)+%s'" - % ( - config_obj.db.name, - ".".join( - str(dig) - for dig in exclusions._server_version(config_obj.db) - ), - config_obj.db.driver, - ) - for config_obj in config.Config.all_configs() - ), - ", ".join(reasons), - ) - config.skip_test(msg) - elif hasattr(cls, "__prefer_backends__"): - non_preferred = set() - spec = exclusions.db_spec(*util.to_list(cls.__prefer_backends__)) - for config_obj in all_configs: - if not spec(config_obj): - non_preferred.add(config_obj) - if all_configs.difference(non_preferred): - all_configs.difference_update(non_preferred) - - if config._current not in all_configs: - _setup_config(all_configs.pop(), cls) - - -def _setup_config(config_obj, ctx): - config._current.push(config_obj, testing) - - -class FixtureFunctions(abc.ABC): - @abc.abstractmethod - def skip_test_exception(self, *arg, **kw): - raise NotImplementedError() - - @abc.abstractmethod - def combinations(self, *args, **kw): - raise NotImplementedError() - - @abc.abstractmethod - def param_ident(self, *args, **kw): - raise NotImplementedError() - - @abc.abstractmethod - def fixture(self, *arg, **kw): - raise NotImplementedError() - - def get_current_test_name(self): - raise NotImplementedError() - - @abc.abstractmethod - def mark_base_test_class(self) -> Any: - raise NotImplementedError() - - @abc.abstractproperty - def add_to_marker(self): - raise NotImplementedError() - - -_fixture_fn_class = None - - -def set_fixture_functions(fixture_fn_class): - global _fixture_fn_class - _fixture_fn_class = fixture_fn_class diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/pytestplugin.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/pytestplugin.py deleted file mode 100644 index 1a4d4bb..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/plugin/pytestplugin.py +++ /dev/null @@ -1,868 +0,0 @@ -# testing/plugin/pytestplugin.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from __future__ import annotations - -import argparse -import collections -from functools import update_wrapper -import inspect -import itertools -import operator -import os -import re -import sys -from typing import TYPE_CHECKING -import uuid - -import pytest - -try: - # installed by bootstrap.py - if not TYPE_CHECKING: - import sqla_plugin_base as plugin_base -except ImportError: - # assume we're a package, use traditional import - from . import plugin_base - - -def pytest_addoption(parser): - group = parser.getgroup("sqlalchemy") - - def make_option(name, **kw): - callback_ = kw.pop("callback", None) - if callback_: - - class CallableAction(argparse.Action): - def __call__( - self, parser, namespace, values, option_string=None - ): - callback_(option_string, values, parser) - - kw["action"] = CallableAction - - zeroarg_callback = kw.pop("zeroarg_callback", None) - if zeroarg_callback: - - class CallableAction(argparse.Action): - def __init__( - self, - option_strings, - dest, - default=False, - required=False, - help=None, # noqa - ): - super().__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=True, - default=default, - required=required, - help=help, - ) - - def __call__( - self, parser, namespace, values, option_string=None - ): - zeroarg_callback(option_string, values, parser) - - kw["action"] = CallableAction - - group.addoption(name, **kw) - - plugin_base.setup_options(make_option) - - -def pytest_configure(config: pytest.Config): - plugin_base.read_config(config.rootpath) - if plugin_base.exclude_tags or plugin_base.include_tags: - new_expr = " and ".join( - list(plugin_base.include_tags) - + [f"not {tag}" for tag in plugin_base.exclude_tags] - ) - - if config.option.markexpr: - config.option.markexpr += f" and {new_expr}" - else: - config.option.markexpr = new_expr - - if config.pluginmanager.hasplugin("xdist"): - config.pluginmanager.register(XDistHooks()) - - if hasattr(config, "workerinput"): - plugin_base.restore_important_follower_config(config.workerinput) - plugin_base.configure_follower(config.workerinput["follower_ident"]) - else: - if config.option.write_idents and os.path.exists( - config.option.write_idents - ): - os.remove(config.option.write_idents) - - plugin_base.pre_begin(config.option) - - plugin_base.set_coverage_flag( - bool(getattr(config.option, "cov_source", False)) - ) - - plugin_base.set_fixture_functions(PytestFixtureFunctions) - - if config.option.dump_pyannotate: - global DUMP_PYANNOTATE - DUMP_PYANNOTATE = True - - -DUMP_PYANNOTATE = False - - -@pytest.fixture(autouse=True) -def collect_types_fixture(): - if DUMP_PYANNOTATE: - from pyannotate_runtime import collect_types - - collect_types.start() - yield - if DUMP_PYANNOTATE: - collect_types.stop() - - -def _log_sqlalchemy_info(session): - import sqlalchemy - from sqlalchemy import __version__ - from sqlalchemy.util import has_compiled_ext - from sqlalchemy.util._has_cy import _CYEXTENSION_MSG - - greet = "sqlalchemy installation" - site = "no user site" if sys.flags.no_user_site else "user site loaded" - msgs = [ - f"SQLAlchemy {__version__} ({site})", - f"Path: {sqlalchemy.__file__}", - ] - - if has_compiled_ext(): - from sqlalchemy.cyextension import util - - msgs.append(f"compiled extension enabled, e.g. {util.__file__} ") - else: - msgs.append(f"compiled extension not enabled; {_CYEXTENSION_MSG}") - - pm = session.config.pluginmanager.get_plugin("terminalreporter") - if pm: - pm.write_sep("=", greet) - for m in msgs: - pm.write_line(m) - else: - # fancy pants reporter not found, fallback to plain print - print("=" * 25, greet, "=" * 25) - for m in msgs: - print(m) - - -def pytest_sessionstart(session): - from sqlalchemy.testing import asyncio - - _log_sqlalchemy_info(session) - asyncio._assume_async(plugin_base.post_begin) - - -def pytest_sessionfinish(session): - from sqlalchemy.testing import asyncio - - asyncio._maybe_async_provisioning(plugin_base.final_process_cleanup) - - if session.config.option.dump_pyannotate: - from pyannotate_runtime import collect_types - - collect_types.dump_stats(session.config.option.dump_pyannotate) - - -def pytest_unconfigure(config): - from sqlalchemy.testing import asyncio - - asyncio._shutdown() - - -def pytest_collection_finish(session): - if session.config.option.dump_pyannotate: - from pyannotate_runtime import collect_types - - lib_sqlalchemy = os.path.abspath("lib/sqlalchemy") - - def _filter(filename): - filename = os.path.normpath(os.path.abspath(filename)) - if "lib/sqlalchemy" not in os.path.commonpath( - [filename, lib_sqlalchemy] - ): - return None - if "testing" in filename: - return None - - return filename - - collect_types.init_types_collection(filter_filename=_filter) - - -class XDistHooks: - def pytest_configure_node(self, node): - from sqlalchemy.testing import provision - from sqlalchemy.testing import asyncio - - # the master for each node fills workerinput dictionary - # which pytest-xdist will transfer to the subprocess - - plugin_base.memoize_important_follower_config(node.workerinput) - - node.workerinput["follower_ident"] = "test_%s" % uuid.uuid4().hex[0:12] - - asyncio._maybe_async_provisioning( - provision.create_follower_db, node.workerinput["follower_ident"] - ) - - def pytest_testnodedown(self, node, error): - from sqlalchemy.testing import provision - from sqlalchemy.testing import asyncio - - asyncio._maybe_async_provisioning( - provision.drop_follower_db, node.workerinput["follower_ident"] - ) - - -def pytest_collection_modifyitems(session, config, items): - # look for all those classes that specify __backend__ and - # expand them out into per-database test cases. - - # this is much easier to do within pytest_pycollect_makeitem, however - # pytest is iterating through cls.__dict__ as makeitem is - # called which causes a "dictionary changed size" error on py3k. - # I'd submit a pullreq for them to turn it into a list first, but - # it's to suit the rather odd use case here which is that we are adding - # new classes to a module on the fly. - - from sqlalchemy.testing import asyncio - - rebuilt_items = collections.defaultdict( - lambda: collections.defaultdict(list) - ) - - items[:] = [ - item - for item in items - if item.getparent(pytest.Class) is not None - and not item.getparent(pytest.Class).name.startswith("_") - ] - - test_classes = {item.getparent(pytest.Class) for item in items} - - def collect(element): - for inst_or_fn in element.collect(): - if isinstance(inst_or_fn, pytest.Collector): - yield from collect(inst_or_fn) - else: - yield inst_or_fn - - def setup_test_classes(): - for test_class in test_classes: - # transfer legacy __backend__ and __sparse_backend__ symbols - # to be markers - add_markers = set() - if getattr(test_class.cls, "__backend__", False) or getattr( - test_class.cls, "__only_on__", False - ): - add_markers = {"backend"} - elif getattr(test_class.cls, "__sparse_backend__", False): - add_markers = {"sparse_backend"} - else: - add_markers = frozenset() - - existing_markers = { - mark.name for mark in test_class.iter_markers() - } - add_markers = add_markers - existing_markers - all_markers = existing_markers.union(add_markers) - - for marker in add_markers: - test_class.add_marker(marker) - - for sub_cls in plugin_base.generate_sub_tests( - test_class.cls, test_class.module, all_markers - ): - if sub_cls is not test_class.cls: - per_cls_dict = rebuilt_items[test_class.cls] - - module = test_class.getparent(pytest.Module) - - new_cls = pytest.Class.from_parent( - name=sub_cls.__name__, parent=module - ) - for marker in add_markers: - new_cls.add_marker(marker) - - for fn in collect(new_cls): - per_cls_dict[fn.name].append(fn) - - # class requirements will sometimes need to access the DB to check - # capabilities, so need to do this for async - asyncio._maybe_async_provisioning(setup_test_classes) - - newitems = [] - for item in items: - cls_ = item.cls - if cls_ in rebuilt_items: - newitems.extend(rebuilt_items[cls_][item.name]) - else: - newitems.append(item) - - # seems like the functions attached to a test class aren't sorted already? - # is that true and why's that? (when using unittest, they're sorted) - items[:] = sorted( - newitems, - key=lambda item: ( - item.getparent(pytest.Module).name, - item.getparent(pytest.Class).name, - item.name, - ), - ) - - -def pytest_pycollect_makeitem(collector, name, obj): - if inspect.isclass(obj) and plugin_base.want_class(name, obj): - from sqlalchemy.testing import config - - if config.any_async: - obj = _apply_maybe_async(obj) - - return [ - pytest.Class.from_parent( - name=parametrize_cls.__name__, parent=collector - ) - for parametrize_cls in _parametrize_cls(collector.module, obj) - ] - elif ( - inspect.isfunction(obj) - and collector.cls is not None - and plugin_base.want_method(collector.cls, obj) - ): - # None means, fall back to default logic, which includes - # method-level parametrize - return None - else: - # empty list means skip this item - return [] - - -def _is_wrapped_coroutine_function(fn): - while hasattr(fn, "__wrapped__"): - fn = fn.__wrapped__ - - return inspect.iscoroutinefunction(fn) - - -def _apply_maybe_async(obj, recurse=True): - from sqlalchemy.testing import asyncio - - for name, value in vars(obj).items(): - if ( - (callable(value) or isinstance(value, classmethod)) - and not getattr(value, "_maybe_async_applied", False) - and (name.startswith("test_")) - and not _is_wrapped_coroutine_function(value) - ): - is_classmethod = False - if isinstance(value, classmethod): - value = value.__func__ - is_classmethod = True - - @_pytest_fn_decorator - def make_async(fn, *args, **kwargs): - return asyncio._maybe_async(fn, *args, **kwargs) - - do_async = make_async(value) - if is_classmethod: - do_async = classmethod(do_async) - do_async._maybe_async_applied = True - - setattr(obj, name, do_async) - if recurse: - for cls in obj.mro()[1:]: - if cls != object: - _apply_maybe_async(cls, False) - return obj - - -def _parametrize_cls(module, cls): - """implement a class-based version of pytest parametrize.""" - - if "_sa_parametrize" not in cls.__dict__: - return [cls] - - _sa_parametrize = cls._sa_parametrize - classes = [] - for full_param_set in itertools.product( - *[params for argname, params in _sa_parametrize] - ): - cls_variables = {} - - for argname, param in zip( - [_sa_param[0] for _sa_param in _sa_parametrize], full_param_set - ): - if not argname: - raise TypeError("need argnames for class-based combinations") - argname_split = re.split(r",\s*", argname) - for arg, val in zip(argname_split, param.values): - cls_variables[arg] = val - parametrized_name = "_".join( - re.sub(r"\W", "", token) - for param in full_param_set - for token in param.id.split("-") - ) - name = "%s_%s" % (cls.__name__, parametrized_name) - newcls = type.__new__(type, name, (cls,), cls_variables) - setattr(module, name, newcls) - classes.append(newcls) - return classes - - -_current_class = None - - -def pytest_runtest_setup(item): - from sqlalchemy.testing import asyncio - - # pytest_runtest_setup runs *before* pytest fixtures with scope="class". - # plugin_base.start_test_class_outside_fixtures may opt to raise SkipTest - # for the whole class and has to run things that are across all current - # databases, so we run this outside of the pytest fixture system altogether - # and ensure asyncio greenlet if any engines are async - - global _current_class - - if isinstance(item, pytest.Function) and _current_class is None: - asyncio._maybe_async_provisioning( - plugin_base.start_test_class_outside_fixtures, - item.cls, - ) - _current_class = item.getparent(pytest.Class) - - -@pytest.hookimpl(hookwrapper=True) -def pytest_runtest_teardown(item, nextitem): - # runs inside of pytest function fixture scope - # after test function runs - - from sqlalchemy.testing import asyncio - - asyncio._maybe_async(plugin_base.after_test, item) - - yield - # this is now after all the fixture teardown have run, the class can be - # finalized. Since pytest v7 this finalizer can no longer be added in - # pytest_runtest_setup since the class has not yet been setup at that - # time. - # See https://github.com/pytest-dev/pytest/issues/9343 - global _current_class, _current_report - - if _current_class is not None and ( - # last test or a new class - nextitem is None - or nextitem.getparent(pytest.Class) is not _current_class - ): - _current_class = None - - try: - asyncio._maybe_async_provisioning( - plugin_base.stop_test_class_outside_fixtures, item.cls - ) - except Exception as e: - # in case of an exception during teardown attach the original - # error to the exception message, otherwise it will get lost - if _current_report.failed: - if not e.args: - e.args = ( - "__Original test failure__:\n" - + _current_report.longreprtext, - ) - elif e.args[-1] and isinstance(e.args[-1], str): - args = list(e.args) - args[-1] += ( - "\n__Original test failure__:\n" - + _current_report.longreprtext - ) - e.args = tuple(args) - else: - e.args += ( - "__Original test failure__", - _current_report.longreprtext, - ) - raise - finally: - _current_report = None - - -def pytest_runtest_call(item): - # runs inside of pytest function fixture scope - # before test function runs - - from sqlalchemy.testing import asyncio - - asyncio._maybe_async( - plugin_base.before_test, - item, - item.module.__name__, - item.cls, - item.name, - ) - - -_current_report = None - - -def pytest_runtest_logreport(report): - global _current_report - if report.when == "call": - _current_report = report - - -@pytest.fixture(scope="class") -def setup_class_methods(request): - from sqlalchemy.testing import asyncio - - cls = request.cls - - if hasattr(cls, "setup_test_class"): - asyncio._maybe_async(cls.setup_test_class) - - yield - - if hasattr(cls, "teardown_test_class"): - asyncio._maybe_async(cls.teardown_test_class) - - asyncio._maybe_async(plugin_base.stop_test_class, cls) - - -@pytest.fixture(scope="function") -def setup_test_methods(request): - from sqlalchemy.testing import asyncio - - # called for each test - - self = request.instance - - # before this fixture runs: - - # 1. function level "autouse" fixtures under py3k (examples: TablesTest - # define tables / data, MappedTest define tables / mappers / data) - - # 2. was for p2k. no longer applies - - # 3. run outer xdist-style setup - if hasattr(self, "setup_test"): - asyncio._maybe_async(self.setup_test) - - # alembic test suite is using setUp and tearDown - # xdist methods; support these in the test suite - # for the near term - if hasattr(self, "setUp"): - asyncio._maybe_async(self.setUp) - - # inside the yield: - # 4. function level fixtures defined on test functions themselves, - # e.g. "connection", "metadata" run next - - # 5. pytest hook pytest_runtest_call then runs - - # 6. test itself runs - - yield - - # yield finishes: - - # 7. function level fixtures defined on test functions - # themselves, e.g. "connection" rolls back the transaction, "metadata" - # emits drop all - - # 8. pytest hook pytest_runtest_teardown hook runs, this is associated - # with fixtures close all sessions, provisioning.stop_test_class(), - # engines.testing_reaper -> ensure all connection pool connections - # are returned, engines created by testing_engine that aren't the - # config engine are disposed - - asyncio._maybe_async(plugin_base.after_test_fixtures, self) - - # 10. run xdist-style teardown - if hasattr(self, "tearDown"): - asyncio._maybe_async(self.tearDown) - - if hasattr(self, "teardown_test"): - asyncio._maybe_async(self.teardown_test) - - # 11. was for p2k. no longer applies - - # 12. function level "autouse" fixtures under py3k (examples: TablesTest / - # MappedTest delete table data, possibly drop tables and clear mappers - # depending on the flags defined by the test class) - - -def _pytest_fn_decorator(target): - """Port of langhelpers.decorator with pytest-specific tricks.""" - - from sqlalchemy.util.langhelpers import format_argspec_plus - from sqlalchemy.util.compat import inspect_getfullargspec - - def _exec_code_in_env(code, env, fn_name): - # note this is affected by "from __future__ import annotations" at - # the top; exec'ed code will use non-evaluated annotations - # which allows us to be more flexible with code rendering - # in format_argpsec_plus() - exec(code, env) - return env[fn_name] - - def decorate(fn, add_positional_parameters=()): - spec = inspect_getfullargspec(fn) - if add_positional_parameters: - spec.args.extend(add_positional_parameters) - - metadata = dict( - __target_fn="__target_fn", __orig_fn="__orig_fn", name=fn.__name__ - ) - metadata.update(format_argspec_plus(spec, grouped=False)) - code = ( - """\ -def %(name)s%(grouped_args)s: - return %(__target_fn)s(%(__orig_fn)s, %(apply_kw)s) -""" - % metadata - ) - decorated = _exec_code_in_env( - code, {"__target_fn": target, "__orig_fn": fn}, fn.__name__ - ) - if not add_positional_parameters: - decorated.__defaults__ = getattr(fn, "__func__", fn).__defaults__ - decorated.__wrapped__ = fn - return update_wrapper(decorated, fn) - else: - # this is the pytest hacky part. don't do a full update wrapper - # because pytest is really being sneaky about finding the args - # for the wrapped function - decorated.__module__ = fn.__module__ - decorated.__name__ = fn.__name__ - if hasattr(fn, "pytestmark"): - decorated.pytestmark = fn.pytestmark - return decorated - - return decorate - - -class PytestFixtureFunctions(plugin_base.FixtureFunctions): - def skip_test_exception(self, *arg, **kw): - return pytest.skip.Exception(*arg, **kw) - - @property - def add_to_marker(self): - return pytest.mark - - def mark_base_test_class(self): - return pytest.mark.usefixtures( - "setup_class_methods", "setup_test_methods" - ) - - _combination_id_fns = { - "i": lambda obj: obj, - "r": repr, - "s": str, - "n": lambda obj: ( - obj.__name__ if hasattr(obj, "__name__") else type(obj).__name__ - ), - } - - def combinations(self, *arg_sets, **kw): - """Facade for pytest.mark.parametrize. - - Automatically derives argument names from the callable which in our - case is always a method on a class with positional arguments. - - ids for parameter sets are derived using an optional template. - - """ - from sqlalchemy.testing import exclusions - - if len(arg_sets) == 1 and hasattr(arg_sets[0], "__next__"): - arg_sets = list(arg_sets[0]) - - argnames = kw.pop("argnames", None) - - def _filter_exclusions(args): - result = [] - gathered_exclusions = [] - for a in args: - if isinstance(a, exclusions.compound): - gathered_exclusions.append(a) - else: - result.append(a) - - return result, gathered_exclusions - - id_ = kw.pop("id_", None) - - tobuild_pytest_params = [] - has_exclusions = False - if id_: - _combination_id_fns = self._combination_id_fns - - # because itemgetter is not consistent for one argument vs. - # multiple, make it multiple in all cases and use a slice - # to omit the first argument - _arg_getter = operator.itemgetter( - 0, - *[ - idx - for idx, char in enumerate(id_) - if char in ("n", "r", "s", "a") - ], - ) - fns = [ - (operator.itemgetter(idx), _combination_id_fns[char]) - for idx, char in enumerate(id_) - if char in _combination_id_fns - ] - - for arg in arg_sets: - if not isinstance(arg, tuple): - arg = (arg,) - - fn_params, param_exclusions = _filter_exclusions(arg) - - parameters = _arg_getter(fn_params)[1:] - - if param_exclusions: - has_exclusions = True - - tobuild_pytest_params.append( - ( - parameters, - param_exclusions, - "-".join( - comb_fn(getter(arg)) for getter, comb_fn in fns - ), - ) - ) - - else: - for arg in arg_sets: - if not isinstance(arg, tuple): - arg = (arg,) - - fn_params, param_exclusions = _filter_exclusions(arg) - - if param_exclusions: - has_exclusions = True - - tobuild_pytest_params.append( - (fn_params, param_exclusions, None) - ) - - pytest_params = [] - for parameters, param_exclusions, id_ in tobuild_pytest_params: - if has_exclusions: - parameters += (param_exclusions,) - - param = pytest.param(*parameters, id=id_) - pytest_params.append(param) - - def decorate(fn): - if inspect.isclass(fn): - if has_exclusions: - raise NotImplementedError( - "exclusions not supported for class level combinations" - ) - if "_sa_parametrize" not in fn.__dict__: - fn._sa_parametrize = [] - fn._sa_parametrize.append((argnames, pytest_params)) - return fn - else: - _fn_argnames = inspect.getfullargspec(fn).args[1:] - if argnames is None: - _argnames = _fn_argnames - else: - _argnames = re.split(r", *", argnames) - - if has_exclusions: - existing_exl = sum( - 1 for n in _fn_argnames if n.startswith("_exclusions") - ) - current_exclusion_name = f"_exclusions_{existing_exl}" - _argnames += [current_exclusion_name] - - @_pytest_fn_decorator - def check_exclusions(fn, *args, **kw): - _exclusions = args[-1] - if _exclusions: - exlu = exclusions.compound().add(*_exclusions) - fn = exlu(fn) - return fn(*args[:-1], **kw) - - fn = check_exclusions( - fn, add_positional_parameters=(current_exclusion_name,) - ) - - return pytest.mark.parametrize(_argnames, pytest_params)(fn) - - return decorate - - def param_ident(self, *parameters): - ident = parameters[0] - return pytest.param(*parameters[1:], id=ident) - - def fixture(self, *arg, **kw): - from sqlalchemy.testing import config - from sqlalchemy.testing import asyncio - - # wrapping pytest.fixture function. determine if - # decorator was called as @fixture or @fixture(). - if len(arg) > 0 and callable(arg[0]): - # was called as @fixture(), we have the function to wrap. - fn = arg[0] - arg = arg[1:] - else: - # was called as @fixture, don't have the function yet. - fn = None - - # create a pytest.fixture marker. because the fn is not being - # passed, this is always a pytest.FixtureFunctionMarker() - # object (or whatever pytest is calling it when you read this) - # that is waiting for a function. - fixture = pytest.fixture(*arg, **kw) - - # now apply wrappers to the function, including fixture itself - - def wrap(fn): - if config.any_async: - fn = asyncio._maybe_async_wrapper(fn) - # other wrappers may be added here - - # now apply FixtureFunctionMarker - fn = fixture(fn) - - return fn - - if fn: - return wrap(fn) - else: - return wrap - - def get_current_test_name(self): - return os.environ.get("PYTEST_CURRENT_TEST") - - def async_test(self, fn): - from sqlalchemy.testing import asyncio - - @_pytest_fn_decorator - def decorate(fn, *args, **kwargs): - asyncio._run_coroutine_function(fn, *args, **kwargs) - - return decorate(fn) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/profiling.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/profiling.py deleted file mode 100644 index b9093c9..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/profiling.py +++ /dev/null @@ -1,324 +0,0 @@ -# testing/profiling.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -"""Profiling support for unit and performance tests. - -These are special purpose profiling methods which operate -in a more fine-grained way than nose's profiling plugin. - -""" - -from __future__ import annotations - -import collections -import contextlib -import os -import platform -import pstats -import re -import sys - -from . import config -from .util import gc_collect -from ..util import has_compiled_ext - - -try: - import cProfile -except ImportError: - cProfile = None - -_profile_stats = None -"""global ProfileStatsFileInstance. - -plugin_base assigns this at the start of all tests. - -""" - - -_current_test = None -"""String id of current test. - -plugin_base assigns this at the start of each test using -_start_current_test. - -""" - - -def _start_current_test(id_): - global _current_test - _current_test = id_ - - if _profile_stats.force_write: - _profile_stats.reset_count() - - -class ProfileStatsFile: - """Store per-platform/fn profiling results in a file. - - There was no json module available when this was written, but now - the file format which is very deterministically line oriented is kind of - handy in any case for diffs and merges. - - """ - - def __init__(self, filename, sort="cumulative", dump=None): - self.force_write = ( - config.options is not None and config.options.force_write_profiles - ) - self.write = self.force_write or ( - config.options is not None and config.options.write_profiles - ) - self.fname = os.path.abspath(filename) - self.short_fname = os.path.split(self.fname)[-1] - self.data = collections.defaultdict( - lambda: collections.defaultdict(dict) - ) - self.dump = dump - self.sort = sort - self._read() - if self.write: - # rewrite for the case where features changed, - # etc. - self._write() - - @property - def platform_key(self): - dbapi_key = config.db.name + "_" + config.db.driver - - if config.db.name == "sqlite" and config.db.dialect._is_url_file_db( - config.db.url - ): - dbapi_key += "_file" - - # keep it at 2.7, 3.1, 3.2, etc. for now. - py_version = ".".join([str(v) for v in sys.version_info[0:2]]) - - platform_tokens = [ - platform.machine(), - platform.system().lower(), - platform.python_implementation().lower(), - py_version, - dbapi_key, - ] - - platform_tokens.append("dbapiunicode") - _has_cext = has_compiled_ext() - platform_tokens.append(_has_cext and "cextensions" or "nocextensions") - return "_".join(platform_tokens) - - def has_stats(self): - test_key = _current_test - return ( - test_key in self.data and self.platform_key in self.data[test_key] - ) - - def result(self, callcount): - test_key = _current_test - per_fn = self.data[test_key] - per_platform = per_fn[self.platform_key] - - if "counts" not in per_platform: - per_platform["counts"] = counts = [] - else: - counts = per_platform["counts"] - - if "current_count" not in per_platform: - per_platform["current_count"] = current_count = 0 - else: - current_count = per_platform["current_count"] - - has_count = len(counts) > current_count - - if not has_count: - counts.append(callcount) - if self.write: - self._write() - result = None - else: - result = per_platform["lineno"], counts[current_count] - per_platform["current_count"] += 1 - return result - - def reset_count(self): - test_key = _current_test - # since self.data is a defaultdict, don't access a key - # if we don't know it's there first. - if test_key not in self.data: - return - per_fn = self.data[test_key] - if self.platform_key not in per_fn: - return - per_platform = per_fn[self.platform_key] - if "counts" in per_platform: - per_platform["counts"][:] = [] - - def replace(self, callcount): - test_key = _current_test - per_fn = self.data[test_key] - per_platform = per_fn[self.platform_key] - counts = per_platform["counts"] - current_count = per_platform["current_count"] - if current_count < len(counts): - counts[current_count - 1] = callcount - else: - counts[-1] = callcount - if self.write: - self._write() - - def _header(self): - return ( - "# %s\n" - "# This file is written out on a per-environment basis.\n" - "# For each test in aaa_profiling, the corresponding " - "function and \n" - "# environment is located within this file. " - "If it doesn't exist,\n" - "# the test is skipped.\n" - "# If a callcount does exist, it is compared " - "to what we received. \n" - "# assertions are raised if the counts do not match.\n" - "# \n" - "# To add a new callcount test, apply the function_call_count \n" - "# decorator and re-run the tests using the --write-profiles \n" - "# option - this file will be rewritten including the new count.\n" - "# \n" - ) % (self.fname) - - def _read(self): - try: - profile_f = open(self.fname) - except OSError: - return - for lineno, line in enumerate(profile_f): - line = line.strip() - if not line or line.startswith("#"): - continue - - test_key, platform_key, counts = line.split() - per_fn = self.data[test_key] - per_platform = per_fn[platform_key] - c = [int(count) for count in counts.split(",")] - per_platform["counts"] = c - per_platform["lineno"] = lineno + 1 - per_platform["current_count"] = 0 - profile_f.close() - - def _write(self): - print("Writing profile file %s" % self.fname) - profile_f = open(self.fname, "w") - profile_f.write(self._header()) - for test_key in sorted(self.data): - per_fn = self.data[test_key] - profile_f.write("\n# TEST: %s\n\n" % test_key) - for platform_key in sorted(per_fn): - per_platform = per_fn[platform_key] - c = ",".join(str(count) for count in per_platform["counts"]) - profile_f.write("%s %s %s\n" % (test_key, platform_key, c)) - profile_f.close() - - -def function_call_count(variance=0.05, times=1, warmup=0): - """Assert a target for a test case's function call count. - - The main purpose of this assertion is to detect changes in - callcounts for various functions - the actual number is not as important. - Callcounts are stored in a file keyed to Python version and OS platform - information. This file is generated automatically for new tests, - and versioned so that unexpected changes in callcounts will be detected. - - """ - - # use signature-rewriting decorator function so that pytest fixtures - # still work on py27. In Py3, update_wrapper() alone is good enough, - # likely due to the introduction of __signature__. - - from sqlalchemy.util import decorator - - @decorator - def wrap(fn, *args, **kw): - for warm in range(warmup): - fn(*args, **kw) - - timerange = range(times) - with count_functions(variance=variance): - for time in timerange: - rv = fn(*args, **kw) - return rv - - return wrap - - -@contextlib.contextmanager -def count_functions(variance=0.05): - if cProfile is None: - raise config._skip_test_exception("cProfile is not installed") - - if not _profile_stats.has_stats() and not _profile_stats.write: - config.skip_test( - "No profiling stats available on this " - "platform for this function. Run tests with " - "--write-profiles to add statistics to %s for " - "this platform." % _profile_stats.short_fname - ) - - gc_collect() - - pr = cProfile.Profile() - pr.enable() - # began = time.time() - yield - # ended = time.time() - pr.disable() - - # s = StringIO() - stats = pstats.Stats(pr, stream=sys.stdout) - - # timespent = ended - began - callcount = stats.total_calls - - expected = _profile_stats.result(callcount) - - if expected is None: - expected_count = None - else: - line_no, expected_count = expected - - print("Pstats calls: %d Expected %s" % (callcount, expected_count)) - stats.sort_stats(*re.split(r"[, ]", _profile_stats.sort)) - stats.print_stats() - if _profile_stats.dump: - base, ext = os.path.splitext(_profile_stats.dump) - test_name = _current_test.split(".")[-1] - dumpfile = "%s_%s%s" % (base, test_name, ext or ".profile") - stats.dump_stats(dumpfile) - print("Dumped stats to file %s" % dumpfile) - # stats.print_callers() - if _profile_stats.force_write: - _profile_stats.replace(callcount) - elif expected_count: - deviance = int(callcount * variance) - failed = abs(callcount - expected_count) > deviance - - if failed: - if _profile_stats.write: - _profile_stats.replace(callcount) - else: - raise AssertionError( - "Adjusted function call count %s not within %s%% " - "of expected %s, platform %s. Rerun with " - "--write-profiles to " - "regenerate this callcount." - % ( - callcount, - (variance * 100), - expected_count, - _profile_stats.platform_key, - ) - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/provision.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/provision.py deleted file mode 100644 index e50c6eb..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/provision.py +++ /dev/null @@ -1,496 +0,0 @@ -# testing/provision.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from __future__ import annotations - -import collections -import logging - -from . import config -from . import engines -from . import util -from .. import exc -from .. import inspect -from ..engine import url as sa_url -from ..sql import ddl -from ..sql import schema - - -log = logging.getLogger(__name__) - -FOLLOWER_IDENT = None - - -class register: - def __init__(self, decorator=None): - self.fns = {} - self.decorator = decorator - - @classmethod - def init(cls, fn): - return register().for_db("*")(fn) - - @classmethod - def init_decorator(cls, decorator): - return register(decorator).for_db("*") - - def for_db(self, *dbnames): - def decorate(fn): - if self.decorator: - fn = self.decorator(fn) - for dbname in dbnames: - self.fns[dbname] = fn - return self - - return decorate - - def __call__(self, cfg, *arg, **kw): - if isinstance(cfg, str): - url = sa_url.make_url(cfg) - elif isinstance(cfg, sa_url.URL): - url = cfg - else: - url = cfg.db.url - backend = url.get_backend_name() - if backend in self.fns: - return self.fns[backend](cfg, *arg, **kw) - else: - return self.fns["*"](cfg, *arg, **kw) - - -def create_follower_db(follower_ident): - for cfg in _configs_for_db_operation(): - log.info("CREATE database %s, URI %r", follower_ident, cfg.db.url) - create_db(cfg, cfg.db, follower_ident) - - -def setup_config(db_url, options, file_config, follower_ident): - # load the dialect, which should also have it set up its provision - # hooks - - dialect = sa_url.make_url(db_url).get_dialect() - - dialect.load_provisioning() - - if follower_ident: - db_url = follower_url_from_main(db_url, follower_ident) - db_opts = {} - update_db_opts(db_url, db_opts, options) - db_opts["scope"] = "global" - eng = engines.testing_engine(db_url, db_opts) - post_configure_engine(db_url, eng, follower_ident) - eng.connect().close() - - cfg = config.Config.register(eng, db_opts, options, file_config) - - # a symbolic name that tests can use if they need to disambiguate - # names across databases - if follower_ident: - config.ident = follower_ident - - if follower_ident: - configure_follower(cfg, follower_ident) - return cfg - - -def drop_follower_db(follower_ident): - for cfg in _configs_for_db_operation(): - log.info("DROP database %s, URI %r", follower_ident, cfg.db.url) - drop_db(cfg, cfg.db, follower_ident) - - -def generate_db_urls(db_urls, extra_drivers): - """Generate a set of URLs to test given configured URLs plus additional - driver names. - - Given:: - - --dburi postgresql://db1 \ - --dburi postgresql://db2 \ - --dburi postgresql://db2 \ - --dbdriver=psycopg2 --dbdriver=asyncpg?async_fallback=true - - Noting that the default postgresql driver is psycopg2, the output - would be:: - - postgresql+psycopg2://db1 - postgresql+asyncpg://db1 - postgresql+psycopg2://db2 - postgresql+psycopg2://db3 - - That is, for the driver in a --dburi, we want to keep that and use that - driver for each URL it's part of . For a driver that is only - in --dbdrivers, we want to use it just once for one of the URLs. - for a driver that is both coming from --dburi as well as --dbdrivers, - we want to keep it in that dburi. - - Driver specific query options can be specified by added them to the - driver name. For example, to enable the async fallback option for - asyncpg:: - - --dburi postgresql://db1 \ - --dbdriver=asyncpg?async_fallback=true - - """ - urls = set() - - backend_to_driver_we_already_have = collections.defaultdict(set) - - urls_plus_dialects = [ - (url_obj, url_obj.get_dialect()) - for url_obj in [sa_url.make_url(db_url) for db_url in db_urls] - ] - - for url_obj, dialect in urls_plus_dialects: - # use get_driver_name instead of dialect.driver to account for - # "_async" virtual drivers like oracledb and psycopg - driver_name = url_obj.get_driver_name() - backend_to_driver_we_already_have[dialect.name].add(driver_name) - - backend_to_driver_we_need = {} - - for url_obj, dialect in urls_plus_dialects: - backend = dialect.name - dialect.load_provisioning() - - if backend not in backend_to_driver_we_need: - backend_to_driver_we_need[backend] = extra_per_backend = set( - extra_drivers - ).difference(backend_to_driver_we_already_have[backend]) - else: - extra_per_backend = backend_to_driver_we_need[backend] - - for driver_url in _generate_driver_urls(url_obj, extra_per_backend): - if driver_url in urls: - continue - urls.add(driver_url) - yield driver_url - - -def _generate_driver_urls(url, extra_drivers): - main_driver = url.get_driver_name() - extra_drivers.discard(main_driver) - - url = generate_driver_url(url, main_driver, "") - yield url - - for drv in list(extra_drivers): - if "?" in drv: - driver_only, query_str = drv.split("?", 1) - - else: - driver_only = drv - query_str = None - - new_url = generate_driver_url(url, driver_only, query_str) - if new_url: - extra_drivers.remove(drv) - - yield new_url - - -@register.init -def generate_driver_url(url, driver, query_str): - backend = url.get_backend_name() - - new_url = url.set( - drivername="%s+%s" % (backend, driver), - ) - if query_str: - new_url = new_url.update_query_string(query_str) - - try: - new_url.get_dialect() - except exc.NoSuchModuleError: - return None - else: - return new_url - - -def _configs_for_db_operation(): - hosts = set() - - for cfg in config.Config.all_configs(): - cfg.db.dispose() - - for cfg in config.Config.all_configs(): - url = cfg.db.url - backend = url.get_backend_name() - host_conf = (backend, url.username, url.host, url.database) - - if host_conf not in hosts: - yield cfg - hosts.add(host_conf) - - for cfg in config.Config.all_configs(): - cfg.db.dispose() - - -@register.init -def drop_all_schema_objects_pre_tables(cfg, eng): - pass - - -@register.init -def drop_all_schema_objects_post_tables(cfg, eng): - pass - - -def drop_all_schema_objects(cfg, eng): - drop_all_schema_objects_pre_tables(cfg, eng) - - drop_views(cfg, eng) - - if config.requirements.materialized_views.enabled: - drop_materialized_views(cfg, eng) - - inspector = inspect(eng) - - consider_schemas = (None,) - if config.requirements.schemas.enabled_for_config(cfg): - consider_schemas += (cfg.test_schema, cfg.test_schema_2) - util.drop_all_tables(eng, inspector, consider_schemas=consider_schemas) - - drop_all_schema_objects_post_tables(cfg, eng) - - if config.requirements.sequences.enabled_for_config(cfg): - with eng.begin() as conn: - for seq in inspector.get_sequence_names(): - conn.execute(ddl.DropSequence(schema.Sequence(seq))) - if config.requirements.schemas.enabled_for_config(cfg): - for schema_name in [cfg.test_schema, cfg.test_schema_2]: - for seq in inspector.get_sequence_names( - schema=schema_name - ): - conn.execute( - ddl.DropSequence( - schema.Sequence(seq, schema=schema_name) - ) - ) - - -def drop_views(cfg, eng): - inspector = inspect(eng) - - try: - view_names = inspector.get_view_names() - except NotImplementedError: - pass - else: - with eng.begin() as conn: - for vname in view_names: - conn.execute( - ddl._DropView(schema.Table(vname, schema.MetaData())) - ) - - if config.requirements.schemas.enabled_for_config(cfg): - try: - view_names = inspector.get_view_names(schema=cfg.test_schema) - except NotImplementedError: - pass - else: - with eng.begin() as conn: - for vname in view_names: - conn.execute( - ddl._DropView( - schema.Table( - vname, - schema.MetaData(), - schema=cfg.test_schema, - ) - ) - ) - - -def drop_materialized_views(cfg, eng): - inspector = inspect(eng) - - mview_names = inspector.get_materialized_view_names() - - with eng.begin() as conn: - for vname in mview_names: - conn.exec_driver_sql(f"DROP MATERIALIZED VIEW {vname}") - - if config.requirements.schemas.enabled_for_config(cfg): - mview_names = inspector.get_materialized_view_names( - schema=cfg.test_schema - ) - with eng.begin() as conn: - for vname in mview_names: - conn.exec_driver_sql( - f"DROP MATERIALIZED VIEW {cfg.test_schema}.{vname}" - ) - - -@register.init -def create_db(cfg, eng, ident): - """Dynamically create a database for testing. - - Used when a test run will employ multiple processes, e.g., when run - via `tox` or `pytest -n4`. - """ - raise NotImplementedError( - "no DB creation routine for cfg: %s" % (eng.url,) - ) - - -@register.init -def drop_db(cfg, eng, ident): - """Drop a database that we dynamically created for testing.""" - raise NotImplementedError("no DB drop routine for cfg: %s" % (eng.url,)) - - -def _adapt_update_db_opts(fn): - insp = util.inspect_getfullargspec(fn) - if len(insp.args) == 3: - return fn - else: - return lambda db_url, db_opts, _options: fn(db_url, db_opts) - - -@register.init_decorator(_adapt_update_db_opts) -def update_db_opts(db_url, db_opts, options): - """Set database options (db_opts) for a test database that we created.""" - - -@register.init -def post_configure_engine(url, engine, follower_ident): - """Perform extra steps after configuring an engine for testing. - - (For the internal dialects, currently only used by sqlite, oracle) - """ - - -@register.init -def follower_url_from_main(url, ident): - """Create a connection URL for a dynamically-created test database. - - :param url: the connection URL specified when the test run was invoked - :param ident: the pytest-xdist "worker identifier" to be used as the - database name - """ - url = sa_url.make_url(url) - return url.set(database=ident) - - -@register.init -def configure_follower(cfg, ident): - """Create dialect-specific config settings for a follower database.""" - pass - - -@register.init -def run_reap_dbs(url, ident): - """Remove databases that were created during the test process, after the - process has ended. - - This is an optional step that is invoked for certain backends that do not - reliably release locks on the database as long as a process is still in - use. For the internal dialects, this is currently only necessary for - mssql and oracle. - """ - - -def reap_dbs(idents_file): - log.info("Reaping databases...") - - urls = collections.defaultdict(set) - idents = collections.defaultdict(set) - dialects = {} - - with open(idents_file) as file_: - for line in file_: - line = line.strip() - db_name, db_url = line.split(" ") - url_obj = sa_url.make_url(db_url) - if db_name not in dialects: - dialects[db_name] = url_obj.get_dialect() - dialects[db_name].load_provisioning() - url_key = (url_obj.get_backend_name(), url_obj.host) - urls[url_key].add(db_url) - idents[url_key].add(db_name) - - for url_key in urls: - url = list(urls[url_key])[0] - ident = idents[url_key] - run_reap_dbs(url, ident) - - -@register.init -def temp_table_keyword_args(cfg, eng): - """Specify keyword arguments for creating a temporary Table. - - Dialect-specific implementations of this method will return the - kwargs that are passed to the Table method when creating a temporary - table for testing, e.g., in the define_temp_tables method of the - ComponentReflectionTest class in suite/test_reflection.py - """ - raise NotImplementedError( - "no temp table keyword args routine for cfg: %s" % (eng.url,) - ) - - -@register.init -def prepare_for_drop_tables(config, connection): - pass - - -@register.init -def stop_test_class_outside_fixtures(config, db, testcls): - pass - - -@register.init -def get_temp_table_name(cfg, eng, base_name): - """Specify table name for creating a temporary Table. - - Dialect-specific implementations of this method will return the - name to use when creating a temporary table for testing, - e.g., in the define_temp_tables method of the - ComponentReflectionTest class in suite/test_reflection.py - - Default to just the base name since that's what most dialects will - use. The mssql dialect's implementation will need a "#" prepended. - """ - return base_name - - -@register.init -def set_default_schema_on_connection(cfg, dbapi_connection, schema_name): - raise NotImplementedError( - "backend does not implement a schema name set function: %s" - % (cfg.db.url,) - ) - - -@register.init -def upsert( - cfg, table, returning, *, set_lambda=None, sort_by_parameter_order=False -): - """return the backends insert..on conflict / on dupe etc. construct. - - while we should add a backend-neutral upsert construct as well, such as - insert().upsert(), it's important that we continue to test the - backend-specific insert() constructs since if we do implement - insert().upsert(), that would be using a different codepath for the things - we need to test like insertmanyvalues, etc. - - """ - raise NotImplementedError( - f"backend does not include an upsert implementation: {cfg.db.url}" - ) - - -@register.init -def normalize_sequence(cfg, sequence): - """Normalize sequence parameters for dialect that don't start with 1 - by default. - - The default implementation does nothing - """ - return sequence diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/requirements.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/requirements.py deleted file mode 100644 index 31aac74..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/requirements.py +++ /dev/null @@ -1,1783 +0,0 @@ -# testing/requirements.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -"""Global database feature support policy. - -Provides decorators to mark tests requiring specific feature support from the -target database. - -External dialect test suites should subclass SuiteRequirements -to provide specific inclusion/exclusions. - -""" - -from __future__ import annotations - -import platform - -from . import asyncio as _test_asyncio -from . import exclusions -from .exclusions import only_on -from .. import create_engine -from .. import util -from ..pool import QueuePool - - -class Requirements: - pass - - -class SuiteRequirements(Requirements): - @property - def create_table(self): - """target platform can emit basic CreateTable DDL.""" - - return exclusions.open() - - @property - def drop_table(self): - """target platform can emit basic DropTable DDL.""" - - return exclusions.open() - - @property - def table_ddl_if_exists(self): - """target platform supports IF NOT EXISTS / IF EXISTS for tables.""" - - return exclusions.closed() - - @property - def index_ddl_if_exists(self): - """target platform supports IF NOT EXISTS / IF EXISTS for indexes.""" - - return exclusions.closed() - - @property - def uuid_data_type(self): - """Return databases that support the UUID datatype.""" - - return exclusions.closed() - - @property - def foreign_keys(self): - """Target database must support foreign keys.""" - - return exclusions.open() - - @property - def foreign_keys_reflect_as_index(self): - """Target database creates an index that's reflected for - foreign keys.""" - - return exclusions.closed() - - @property - def unique_index_reflect_as_unique_constraints(self): - """Target database reflects unique indexes as unique constrains.""" - - return exclusions.closed() - - @property - def unique_constraints_reflect_as_index(self): - """Target database reflects unique constraints as indexes.""" - - return exclusions.closed() - - @property - def table_value_constructor(self): - """Database / dialect supports a query like:: - - SELECT * FROM VALUES ( (c1, c2), (c1, c2), ...) - AS some_table(col1, col2) - - SQLAlchemy generates this with the :func:`_sql.values` function. - - """ - return exclusions.closed() - - @property - def standard_cursor_sql(self): - """Target database passes SQL-92 style statements to cursor.execute() - when a statement like select() or insert() is run. - - A very small portion of dialect-level tests will ensure that certain - conditions are present in SQL strings, and these tests use very basic - SQL that will work on any SQL-like platform in order to assert results. - - It's normally a given for any pep-249 DBAPI that a statement like - "SELECT id, name FROM table WHERE some_table.id=5" will work. - However, there are dialects that don't actually produce SQL Strings - and instead may work with symbolic objects instead, or dialects that - aren't working with SQL, so for those this requirement can be marked - as excluded. - - """ - - return exclusions.open() - - @property - def on_update_cascade(self): - """target database must support ON UPDATE..CASCADE behavior in - foreign keys.""" - - return exclusions.open() - - @property - def non_updating_cascade(self): - """target database must *not* support ON UPDATE..CASCADE behavior in - foreign keys.""" - return exclusions.closed() - - @property - def deferrable_fks(self): - return exclusions.closed() - - @property - def on_update_or_deferrable_fks(self): - # TODO: exclusions should be composable, - # somehow only_if([x, y]) isn't working here, negation/conjunctions - # getting confused. - return exclusions.only_if( - lambda: self.on_update_cascade.enabled - or self.deferrable_fks.enabled - ) - - @property - def queue_pool(self): - """target database is using QueuePool""" - - def go(config): - return isinstance(config.db.pool, QueuePool) - - return exclusions.only_if(go) - - @property - def self_referential_foreign_keys(self): - """Target database must support self-referential foreign keys.""" - - return exclusions.open() - - @property - def foreign_key_ddl(self): - """Target database must support the DDL phrases for FOREIGN KEY.""" - - return exclusions.open() - - @property - def named_constraints(self): - """target database must support names for constraints.""" - - return exclusions.open() - - @property - def implicitly_named_constraints(self): - """target database must apply names to unnamed constraints.""" - - return exclusions.open() - - @property - def unusual_column_name_characters(self): - """target database allows column names that have unusual characters - in them, such as dots, spaces, slashes, or percent signs. - - The column names are as always in such a case quoted, however the - DB still needs to support those characters in the name somehow. - - """ - return exclusions.open() - - @property - def subqueries(self): - """Target database must support subqueries.""" - - return exclusions.open() - - @property - def offset(self): - """target database can render OFFSET, or an equivalent, in a - SELECT. - """ - - return exclusions.open() - - @property - def bound_limit_offset(self): - """target database can render LIMIT and/or OFFSET using a bound - parameter - """ - - return exclusions.open() - - @property - def sql_expression_limit_offset(self): - """target database can render LIMIT and/or OFFSET with a complete - SQL expression, such as one that uses the addition operator. - parameter - """ - - return exclusions.open() - - @property - def parens_in_union_contained_select_w_limit_offset(self): - """Target database must support parenthesized SELECT in UNION - when LIMIT/OFFSET is specifically present. - - E.g. (SELECT ...) UNION (SELECT ..) - - This is known to fail on SQLite. - - """ - return exclusions.open() - - @property - def parens_in_union_contained_select_wo_limit_offset(self): - """Target database must support parenthesized SELECT in UNION - when OFFSET/LIMIT is specifically not present. - - E.g. (SELECT ... LIMIT ..) UNION (SELECT .. OFFSET ..) - - This is known to fail on SQLite. It also fails on Oracle - because without LIMIT/OFFSET, there is currently no step that - creates an additional subquery. - - """ - return exclusions.open() - - @property - def boolean_col_expressions(self): - """Target database must support boolean expressions as columns""" - - return exclusions.closed() - - @property - def nullable_booleans(self): - """Target database allows boolean columns to store NULL.""" - - return exclusions.open() - - @property - def nullsordering(self): - """Target backends that support nulls ordering.""" - - return exclusions.closed() - - @property - def standalone_binds(self): - """target database/driver supports bound parameters as column - expressions without being in the context of a typed column. - """ - return exclusions.open() - - @property - def standalone_null_binds_whereclause(self): - """target database/driver supports bound parameters with NULL in the - WHERE clause, in situations where it has to be typed. - - """ - return exclusions.open() - - @property - def intersect(self): - """Target database must support INTERSECT or equivalent.""" - return exclusions.closed() - - @property - def except_(self): - """Target database must support EXCEPT or equivalent (i.e. MINUS).""" - return exclusions.closed() - - @property - def window_functions(self): - """Target database must support window functions.""" - return exclusions.closed() - - @property - def ctes(self): - """Target database supports CTEs""" - - return exclusions.closed() - - @property - def ctes_with_update_delete(self): - """target database supports CTES that ride on top of a normal UPDATE - or DELETE statement which refers to the CTE in a correlated subquery. - - """ - - return exclusions.closed() - - @property - def ctes_on_dml(self): - """target database supports CTES which consist of INSERT, UPDATE - or DELETE *within* the CTE, e.g. WITH x AS (UPDATE....)""" - - return exclusions.closed() - - @property - def autoincrement_insert(self): - """target platform generates new surrogate integer primary key values - when insert() is executed, excluding the pk column.""" - - return exclusions.open() - - @property - def fetch_rows_post_commit(self): - """target platform will allow cursor.fetchone() to proceed after a - COMMIT. - - Typically this refers to an INSERT statement with RETURNING which - is invoked within "autocommit". If the row can be returned - after the autocommit, then this rule can be open. - - """ - - return exclusions.open() - - @property - def group_by_complex_expression(self): - """target platform supports SQL expressions in GROUP BY - - e.g. - - SELECT x + y AS somelabel FROM table GROUP BY x + y - - """ - - return exclusions.open() - - @property - def sane_rowcount(self): - return exclusions.skip_if( - lambda config: not config.db.dialect.supports_sane_rowcount, - "driver doesn't support 'sane' rowcount", - ) - - @property - def sane_multi_rowcount(self): - return exclusions.fails_if( - lambda config: not config.db.dialect.supports_sane_multi_rowcount, - "driver %(driver)s %(doesnt_support)s 'sane' multi row count", - ) - - @property - def sane_rowcount_w_returning(self): - return exclusions.fails_if( - lambda config: not ( - config.db.dialect.supports_sane_rowcount_returning - ), - "driver doesn't support 'sane' rowcount when returning is on", - ) - - @property - def empty_inserts(self): - """target platform supports INSERT with no values, i.e. - INSERT DEFAULT VALUES or equivalent.""" - - return exclusions.only_if( - lambda config: config.db.dialect.supports_empty_insert - or config.db.dialect.supports_default_values - or config.db.dialect.supports_default_metavalue, - "empty inserts not supported", - ) - - @property - def empty_inserts_executemany(self): - """target platform supports INSERT with no values, i.e. - INSERT DEFAULT VALUES or equivalent, within executemany()""" - - return self.empty_inserts - - @property - def insert_from_select(self): - """target platform supports INSERT from a SELECT.""" - - return exclusions.open() - - @property - def delete_returning(self): - """target platform supports DELETE ... RETURNING.""" - - return exclusions.only_if( - lambda config: config.db.dialect.delete_returning, - "%(database)s %(does_support)s 'DELETE ... RETURNING'", - ) - - @property - def insert_returning(self): - """target platform supports INSERT ... RETURNING.""" - - return exclusions.only_if( - lambda config: config.db.dialect.insert_returning, - "%(database)s %(does_support)s 'INSERT ... RETURNING'", - ) - - @property - def update_returning(self): - """target platform supports UPDATE ... RETURNING.""" - - return exclusions.only_if( - lambda config: config.db.dialect.update_returning, - "%(database)s %(does_support)s 'UPDATE ... RETURNING'", - ) - - @property - def insert_executemany_returning(self): - """target platform supports RETURNING when INSERT is used with - executemany(), e.g. multiple parameter sets, indicating - as many rows come back as do parameter sets were passed. - - """ - - return exclusions.only_if( - lambda config: config.db.dialect.insert_executemany_returning, - "%(database)s %(does_support)s 'RETURNING of " - "multiple rows with INSERT executemany'", - ) - - @property - def insertmanyvalues(self): - return exclusions.only_if( - lambda config: config.db.dialect.supports_multivalues_insert - and config.db.dialect.insert_returning - and config.db.dialect.use_insertmanyvalues, - "%(database)s %(does_support)s 'insertmanyvalues functionality", - ) - - @property - def tuple_in(self): - """Target platform supports the syntax - "(x, y) IN ((x1, y1), (x2, y2), ...)" - """ - - return exclusions.closed() - - @property - def tuple_in_w_empty(self): - """Target platform tuple IN w/ empty set""" - return self.tuple_in - - @property - def duplicate_names_in_cursor_description(self): - """target platform supports a SELECT statement that has - the same name repeated more than once in the columns list.""" - - return exclusions.open() - - @property - def denormalized_names(self): - """Target database must have 'denormalized', i.e. - UPPERCASE as case insensitive names.""" - - return exclusions.skip_if( - lambda config: not config.db.dialect.requires_name_normalize, - "Backend does not require denormalized names.", - ) - - @property - def multivalues_inserts(self): - """target database must support multiple VALUES clauses in an - INSERT statement.""" - - return exclusions.skip_if( - lambda config: not config.db.dialect.supports_multivalues_insert, - "Backend does not support multirow inserts.", - ) - - @property - def implements_get_lastrowid(self): - """target dialect implements the executioncontext.get_lastrowid() - method without reliance on RETURNING. - - """ - return exclusions.open() - - @property - def arraysize(self): - """dialect includes the required pep-249 attribute - ``cursor.arraysize``""" - - return exclusions.open() - - @property - def emulated_lastrowid(self): - """target dialect retrieves cursor.lastrowid, or fetches - from a database-side function after an insert() construct executes, - within the get_lastrowid() method. - - Only dialects that "pre-execute", or need RETURNING to get last - inserted id, would return closed/fail/skip for this. - - """ - return exclusions.closed() - - @property - def emulated_lastrowid_even_with_sequences(self): - """target dialect retrieves cursor.lastrowid or an equivalent - after an insert() construct executes, even if the table has a - Sequence on it. - - """ - return exclusions.closed() - - @property - def dbapi_lastrowid(self): - """target platform includes a 'lastrowid' accessor on the DBAPI - cursor object. - - """ - return exclusions.closed() - - @property - def views(self): - """Target database must support VIEWs.""" - - return exclusions.closed() - - @property - def schemas(self): - """Target database must support external schemas, and have one - named 'test_schema'.""" - - return only_on(lambda config: config.db.dialect.supports_schemas) - - @property - def cross_schema_fk_reflection(self): - """target system must support reflection of inter-schema - foreign keys""" - return exclusions.closed() - - @property - def foreign_key_constraint_name_reflection(self): - """Target supports reflection of FOREIGN KEY constraints and - will return the name of the constraint that was used in the - "CONSTRAINT <name> FOREIGN KEY" DDL. - - MySQL prior to version 8 and MariaDB prior to version 10.5 - don't support this. - - """ - return exclusions.closed() - - @property - def implicit_default_schema(self): - """target system has a strong concept of 'default' schema that can - be referred to implicitly. - - basically, PostgreSQL. - - """ - return exclusions.closed() - - @property - def default_schema_name_switch(self): - """target dialect implements provisioning module including - set_default_schema_on_connection""" - - return exclusions.closed() - - @property - def server_side_cursors(self): - """Target dialect must support server side cursors.""" - - return exclusions.only_if( - [lambda config: config.db.dialect.supports_server_side_cursors], - "no server side cursors support", - ) - - @property - def sequences(self): - """Target database must support SEQUENCEs.""" - - return exclusions.only_if( - [lambda config: config.db.dialect.supports_sequences], - "no sequence support", - ) - - @property - def no_sequences(self): - """the opposite of "sequences", DB does not support sequences at - all.""" - - return exclusions.NotPredicate(self.sequences) - - @property - def sequences_optional(self): - """Target database supports sequences, but also optionally - as a means of generating new PK values.""" - - return exclusions.only_if( - [ - lambda config: config.db.dialect.supports_sequences - and config.db.dialect.sequences_optional - ], - "no sequence support, or sequences not optional", - ) - - @property - def supports_lastrowid(self): - """target database / driver supports cursor.lastrowid as a means - of retrieving the last inserted primary key value. - - note that if the target DB supports sequences also, this is still - assumed to work. This is a new use case brought on by MariaDB 10.3. - - """ - return exclusions.only_if( - [lambda config: config.db.dialect.postfetch_lastrowid] - ) - - @property - def no_lastrowid_support(self): - """the opposite of supports_lastrowid""" - return exclusions.only_if( - [lambda config: not config.db.dialect.postfetch_lastrowid] - ) - - @property - def reflects_pk_names(self): - return exclusions.closed() - - @property - def table_reflection(self): - """target database has general support for table reflection""" - return exclusions.open() - - @property - def reflect_tables_no_columns(self): - """target database supports creation and reflection of tables with no - columns, or at least tables that seem to have no columns.""" - - return exclusions.closed() - - @property - def comment_reflection(self): - """Indicates if the database support table comment reflection""" - return exclusions.closed() - - @property - def comment_reflection_full_unicode(self): - """Indicates if the database support table comment reflection in the - full unicode range, including emoji etc. - """ - return exclusions.closed() - - @property - def constraint_comment_reflection(self): - """indicates if the database support comments on constraints - and their reflection""" - return exclusions.closed() - - @property - def view_column_reflection(self): - """target database must support retrieval of the columns in a view, - similarly to how a table is inspected. - - This does not include the full CREATE VIEW definition. - - """ - return self.views - - @property - def view_reflection(self): - """target database must support inspection of the full CREATE VIEW - definition.""" - return self.views - - @property - def schema_reflection(self): - return self.schemas - - @property - def schema_create_delete(self): - """target database supports schema create and dropped with - 'CREATE SCHEMA' and 'DROP SCHEMA'""" - return exclusions.closed() - - @property - def primary_key_constraint_reflection(self): - return exclusions.open() - - @property - def foreign_key_constraint_reflection(self): - return exclusions.open() - - @property - def foreign_key_constraint_option_reflection_ondelete(self): - return exclusions.closed() - - @property - def fk_constraint_option_reflection_ondelete_restrict(self): - return exclusions.closed() - - @property - def fk_constraint_option_reflection_ondelete_noaction(self): - return exclusions.closed() - - @property - def foreign_key_constraint_option_reflection_onupdate(self): - return exclusions.closed() - - @property - def fk_constraint_option_reflection_onupdate_restrict(self): - return exclusions.closed() - - @property - def temp_table_reflection(self): - return exclusions.open() - - @property - def temp_table_reflect_indexes(self): - return self.temp_table_reflection - - @property - def temp_table_names(self): - """target dialect supports listing of temporary table names""" - return exclusions.closed() - - @property - def has_temp_table(self): - """target dialect supports checking a single temp table name""" - return exclusions.closed() - - @property - def temporary_tables(self): - """target database supports temporary tables""" - return exclusions.open() - - @property - def temporary_views(self): - """target database supports temporary views""" - return exclusions.closed() - - @property - def index_reflection(self): - return exclusions.open() - - @property - def index_reflects_included_columns(self): - return exclusions.closed() - - @property - def indexes_with_ascdesc(self): - """target database supports CREATE INDEX with per-column ASC/DESC.""" - return exclusions.open() - - @property - def reflect_indexes_with_ascdesc(self): - """target database supports reflecting INDEX with per-column - ASC/DESC.""" - return exclusions.open() - - @property - def reflect_indexes_with_ascdesc_as_expression(self): - """target database supports reflecting INDEX with per-column - ASC/DESC but reflects them as expressions (like oracle).""" - return exclusions.closed() - - @property - def indexes_with_expressions(self): - """target database supports CREATE INDEX against SQL expressions.""" - return exclusions.closed() - - @property - def reflect_indexes_with_expressions(self): - """target database supports reflection of indexes with - SQL expressions.""" - return exclusions.closed() - - @property - def unique_constraint_reflection(self): - """target dialect supports reflection of unique constraints""" - return exclusions.open() - - @property - def check_constraint_reflection(self): - """target dialect supports reflection of check constraints""" - return exclusions.closed() - - @property - def duplicate_key_raises_integrity_error(self): - """target dialect raises IntegrityError when reporting an INSERT - with a primary key violation. (hint: it should) - - """ - return exclusions.open() - - @property - def unbounded_varchar(self): - """Target database must support VARCHAR with no length""" - - return exclusions.open() - - @property - def unicode_data_no_special_types(self): - """Target database/dialect can receive / deliver / compare data with - non-ASCII characters in plain VARCHAR, TEXT columns, without the need - for special "national" datatypes like NVARCHAR or similar. - - """ - return exclusions.open() - - @property - def unicode_data(self): - """Target database/dialect must support Python unicode objects with - non-ASCII characters represented, delivered as bound parameters - as well as in result rows. - - """ - return exclusions.open() - - @property - def unicode_ddl(self): - """Target driver must support some degree of non-ascii symbol - names. - """ - return exclusions.closed() - - @property - def symbol_names_w_double_quote(self): - """Target driver can create tables with a name like 'some " table'""" - return exclusions.open() - - @property - def datetime_interval(self): - """target dialect supports rendering of a datetime.timedelta as a - literal string, e.g. via the TypeEngine.literal_processor() method. - - """ - return exclusions.closed() - - @property - def datetime_literals(self): - """target dialect supports rendering of a date, time, or datetime as a - literal string, e.g. via the TypeEngine.literal_processor() method. - - """ - - return exclusions.closed() - - @property - def datetime(self): - """target dialect supports representation of Python - datetime.datetime() objects.""" - - return exclusions.open() - - @property - def datetime_timezone(self): - """target dialect supports representation of Python - datetime.datetime() with tzinfo with DateTime(timezone=True).""" - - return exclusions.closed() - - @property - def time_timezone(self): - """target dialect supports representation of Python - datetime.time() with tzinfo with Time(timezone=True).""" - - return exclusions.closed() - - @property - def date_implicit_bound(self): - """target dialect when given a date object will bind it such - that the database server knows the object is a date, and not - a plain string. - - """ - return exclusions.open() - - @property - def time_implicit_bound(self): - """target dialect when given a time object will bind it such - that the database server knows the object is a time, and not - a plain string. - - """ - return exclusions.open() - - @property - def datetime_implicit_bound(self): - """target dialect when given a datetime object will bind it such - that the database server knows the object is a datetime, and not - a plain string. - - """ - return exclusions.open() - - @property - def datetime_microseconds(self): - """target dialect supports representation of Python - datetime.datetime() with microsecond objects.""" - - return exclusions.open() - - @property - def timestamp_microseconds(self): - """target dialect supports representation of Python - datetime.datetime() with microsecond objects but only - if TIMESTAMP is used.""" - return exclusions.closed() - - @property - def timestamp_microseconds_implicit_bound(self): - """target dialect when given a datetime object which also includes - a microseconds portion when using the TIMESTAMP data type - will bind it such that the database server knows - the object is a datetime with microseconds, and not a plain string. - - """ - return self.timestamp_microseconds - - @property - def datetime_historic(self): - """target dialect supports representation of Python - datetime.datetime() objects with historic (pre 1970) values.""" - - return exclusions.closed() - - @property - def date(self): - """target dialect supports representation of Python - datetime.date() objects.""" - - return exclusions.open() - - @property - def date_coerces_from_datetime(self): - """target dialect accepts a datetime object as the target - of a date column.""" - - return exclusions.open() - - @property - def date_historic(self): - """target dialect supports representation of Python - datetime.datetime() objects with historic (pre 1970) values.""" - - return exclusions.closed() - - @property - def time(self): - """target dialect supports representation of Python - datetime.time() objects.""" - - return exclusions.open() - - @property - def time_microseconds(self): - """target dialect supports representation of Python - datetime.time() with microsecond objects.""" - - return exclusions.open() - - @property - def binary_comparisons(self): - """target database/driver can allow BLOB/BINARY fields to be compared - against a bound parameter value. - """ - - return exclusions.open() - - @property - def binary_literals(self): - """target backend supports simple binary literals, e.g. an - expression like:: - - SELECT CAST('foo' AS BINARY) - - Where ``BINARY`` is the type emitted from :class:`.LargeBinary`, - e.g. it could be ``BLOB`` or similar. - - Basically fails on Oracle. - - """ - - return exclusions.open() - - @property - def autocommit(self): - """target dialect supports 'AUTOCOMMIT' as an isolation_level""" - return exclusions.closed() - - @property - def isolation_level(self): - """target dialect supports general isolation level settings. - - Note that this requirement, when enabled, also requires that - the get_isolation_levels() method be implemented. - - """ - return exclusions.closed() - - def get_isolation_levels(self, config): - """Return a structure of supported isolation levels for the current - testing dialect. - - The structure indicates to the testing suite what the expected - "default" isolation should be, as well as the other values that - are accepted. The dictionary has two keys, "default" and "supported". - The "supported" key refers to a list of all supported levels and - it should include AUTOCOMMIT if the dialect supports it. - - If the :meth:`.DefaultRequirements.isolation_level` requirement is - not open, then this method has no return value. - - E.g.:: - - >>> testing.requirements.get_isolation_levels() - { - "default": "READ_COMMITTED", - "supported": [ - "SERIALIZABLE", "READ UNCOMMITTED", - "READ COMMITTED", "REPEATABLE READ", - "AUTOCOMMIT" - ] - } - """ - with config.db.connect() as conn: - try: - supported = conn.dialect.get_isolation_level_values( - conn.connection.dbapi_connection - ) - except NotImplementedError: - return None - else: - return { - "default": conn.dialect.default_isolation_level, - "supported": supported, - } - - @property - def get_isolation_level_values(self): - """target dialect supports the - :meth:`_engine.Dialect.get_isolation_level_values` - method added in SQLAlchemy 2.0. - - """ - - def go(config): - with config.db.connect() as conn: - try: - conn.dialect.get_isolation_level_values( - conn.connection.dbapi_connection - ) - except NotImplementedError: - return False - else: - return True - - return exclusions.only_if(go) - - @property - def dialect_level_isolation_level_param(self): - """test that the dialect allows the 'isolation_level' argument - to be handled by DefaultDialect""" - - def go(config): - try: - e = create_engine( - config.db.url, isolation_level="READ COMMITTED" - ) - except: - return False - else: - return ( - e.dialect._on_connect_isolation_level == "READ COMMITTED" - ) - - return exclusions.only_if(go) - - @property - def json_type(self): - """target platform implements a native JSON type.""" - - return exclusions.closed() - - @property - def json_array_indexes(self): - """target platform supports numeric array indexes - within a JSON structure""" - - return self.json_type - - @property - def json_index_supplementary_unicode_element(self): - return exclusions.open() - - @property - def legacy_unconditional_json_extract(self): - """Backend has a JSON_EXTRACT or similar function that returns a - valid JSON string in all cases. - - Used to test a legacy feature and is not needed. - - """ - return exclusions.closed() - - @property - def precision_numerics_general(self): - """target backend has general support for moderately high-precision - numerics.""" - return exclusions.open() - - @property - def precision_numerics_enotation_small(self): - """target backend supports Decimal() objects using E notation - to represent very small values.""" - return exclusions.closed() - - @property - def precision_numerics_enotation_large(self): - """target backend supports Decimal() objects using E notation - to represent very large values.""" - return exclusions.open() - - @property - def precision_numerics_many_significant_digits(self): - """target backend supports values with many digits on both sides, - such as 319438950232418390.273596, 87673.594069654243 - - """ - return exclusions.closed() - - @property - def cast_precision_numerics_many_significant_digits(self): - """same as precision_numerics_many_significant_digits but within the - context of a CAST statement (hello MySQL) - - """ - return self.precision_numerics_many_significant_digits - - @property - def implicit_decimal_binds(self): - """target backend will return a selected Decimal as a Decimal, not - a string. - - e.g.:: - - expr = decimal.Decimal("15.7563") - - value = e.scalar( - select(literal(expr)) - ) - - assert value == expr - - See :ticket:`4036` - - """ - - return exclusions.open() - - @property - def numeric_received_as_decimal_untyped(self): - """target backend will return result columns that are explicitly - against NUMERIC or similar precision-numeric datatypes (not including - FLOAT or INT types) as Python Decimal objects, and not as floats - or ints, including when no SQLAlchemy-side typing information is - associated with the statement (e.g. such as a raw SQL string). - - This should be enabled if either the DBAPI itself returns Decimal - objects, or if the dialect has set up DBAPI-specific return type - handlers such that Decimal objects come back automatically. - - """ - return exclusions.open() - - @property - def nested_aggregates(self): - """target database can select an aggregate from a subquery that's - also using an aggregate - - """ - return exclusions.open() - - @property - def recursive_fk_cascade(self): - """target database must support ON DELETE CASCADE on a self-referential - foreign key - - """ - return exclusions.open() - - @property - def precision_numerics_retains_significant_digits(self): - """A precision numeric type will return empty significant digits, - i.e. a value such as 10.000 will come back in Decimal form with - the .000 maintained.""" - - return exclusions.closed() - - @property - def infinity_floats(self): - """The Float type can persist and load float('inf'), float('-inf').""" - - return exclusions.closed() - - @property - def float_or_double_precision_behaves_generically(self): - return exclusions.closed() - - @property - def precision_generic_float_type(self): - """target backend will return native floating point numbers with at - least seven decimal places when using the generic Float type. - - """ - return exclusions.open() - - @property - def literal_float_coercion(self): - """target backend will return the exact float value 15.7563 - with only four significant digits from this statement: - - SELECT :param - - where :param is the Python float 15.7563 - - i.e. it does not return 15.75629997253418 - - """ - return exclusions.open() - - @property - def floats_to_four_decimals(self): - """target backend can return a floating-point number with four - significant digits (such as 15.7563) accurately - (i.e. without FP inaccuracies, such as 15.75629997253418). - - """ - return exclusions.open() - - @property - def fetch_null_from_numeric(self): - """target backend doesn't crash when you try to select a NUMERIC - value that has a value of NULL. - - Added to support Pyodbc bug #351. - """ - - return exclusions.open() - - @property - def float_is_numeric(self): - """target backend uses Numeric for Float/Dual""" - - return exclusions.open() - - @property - def text_type(self): - """Target database must support an unbounded Text() " - "type such as TEXT or CLOB""" - - return exclusions.open() - - @property - def empty_strings_varchar(self): - """target database can persist/return an empty string with a - varchar. - - """ - return exclusions.open() - - @property - def empty_strings_text(self): - """target database can persist/return an empty string with an - unbounded text.""" - - return exclusions.open() - - @property - def expressions_against_unbounded_text(self): - """target database supports use of an unbounded textual field in a - WHERE clause.""" - - return exclusions.open() - - @property - def selectone(self): - """target driver must support the literal statement 'select 1'""" - return exclusions.open() - - @property - def savepoints(self): - """Target database must support savepoints.""" - - return exclusions.closed() - - @property - def two_phase_transactions(self): - """Target database must support two-phase transactions.""" - - return exclusions.closed() - - @property - def update_from(self): - """Target must support UPDATE..FROM syntax""" - return exclusions.closed() - - @property - def delete_from(self): - """Target must support DELETE FROM..FROM or DELETE..USING syntax""" - return exclusions.closed() - - @property - def update_where_target_in_subquery(self): - """Target must support UPDATE (or DELETE) where the same table is - present in a subquery in the WHERE clause. - - This is an ANSI-standard syntax that apparently MySQL can't handle, - such as:: - - UPDATE documents SET flag=1 WHERE documents.title IN - (SELECT max(documents.title) AS title - FROM documents GROUP BY documents.user_id - ) - - """ - return exclusions.open() - - @property - def mod_operator_as_percent_sign(self): - """target database must use a plain percent '%' as the 'modulus' - operator.""" - return exclusions.closed() - - @property - def percent_schema_names(self): - """target backend supports weird identifiers with percent signs - in them, e.g. 'some % column'. - - this is a very weird use case but often has problems because of - DBAPIs that use python formatting. It's not a critical use - case either. - - """ - return exclusions.closed() - - @property - def order_by_col_from_union(self): - """target database supports ordering by a column from a SELECT - inside of a UNION - - E.g. (SELECT id, ...) UNION (SELECT id, ...) ORDER BY id - - """ - return exclusions.open() - - @property - def order_by_label_with_expression(self): - """target backend supports ORDER BY a column label within an - expression. - - Basically this:: - - select data as foo from test order by foo || 'bar' - - Lots of databases including PostgreSQL don't support this, - so this is off by default. - - """ - return exclusions.closed() - - @property - def order_by_collation(self): - def check(config): - try: - self.get_order_by_collation(config) - return False - except NotImplementedError: - return True - - return exclusions.skip_if(check) - - def get_order_by_collation(self, config): - raise NotImplementedError() - - @property - def unicode_connections(self): - """Target driver must support non-ASCII characters being passed at - all. - """ - return exclusions.open() - - @property - def graceful_disconnects(self): - """Target driver must raise a DBAPI-level exception, such as - InterfaceError, when the underlying connection has been closed - and the execute() method is called. - """ - return exclusions.open() - - @property - def independent_connections(self): - """ - Target must support simultaneous, independent database connections. - """ - return exclusions.open() - - @property - def independent_readonly_connections(self): - """ - Target must support simultaneous, independent database connections - that will be used in a readonly fashion. - - """ - return exclusions.open() - - @property - def skip_mysql_on_windows(self): - """Catchall for a large variety of MySQL on Windows failures""" - return exclusions.open() - - @property - def ad_hoc_engines(self): - """Test environment must allow ad-hoc engine/connection creation. - - DBs that scale poorly for many connections, even when closed, i.e. - Oracle, may use the "--low-connections" option which flags this - requirement as not present. - - """ - return exclusions.skip_if( - lambda config: config.options.low_connections - ) - - @property - def no_windows(self): - return exclusions.skip_if(self._running_on_windows()) - - def _running_on_windows(self): - return exclusions.LambdaPredicate( - lambda: platform.system() == "Windows", - description="running on Windows", - ) - - @property - def timing_intensive(self): - from . import config - - return config.add_to_marker.timing_intensive - - @property - def memory_intensive(self): - from . import config - - return config.add_to_marker.memory_intensive - - @property - def threading_with_mock(self): - """Mark tests that use threading and mock at the same time - stability - issues have been observed with coverage - - """ - return exclusions.skip_if( - lambda config: config.options.has_coverage, - "Stability issues with coverage", - ) - - @property - def sqlalchemy2_stubs(self): - def check(config): - try: - __import__("sqlalchemy-stubs.ext.mypy") - except ImportError: - return False - else: - return True - - return exclusions.only_if(check) - - @property - def no_sqlalchemy2_stubs(self): - def check(config): - try: - __import__("sqlalchemy-stubs.ext.mypy") - except ImportError: - return False - else: - return True - - return exclusions.skip_if(check) - - @property - def python38(self): - return exclusions.only_if( - lambda: util.py38, "Python 3.8 or above required" - ) - - @property - def python39(self): - return exclusions.only_if( - lambda: util.py39, "Python 3.9 or above required" - ) - - @property - def python310(self): - return exclusions.only_if( - lambda: util.py310, "Python 3.10 or above required" - ) - - @property - def python311(self): - return exclusions.only_if( - lambda: util.py311, "Python 3.11 or above required" - ) - - @property - def python312(self): - return exclusions.only_if( - lambda: util.py312, "Python 3.12 or above required" - ) - - @property - def cpython(self): - return exclusions.only_if( - lambda: util.cpython, "cPython interpreter needed" - ) - - @property - def is64bit(self): - return exclusions.only_if(lambda: util.is64bit, "64bit required") - - @property - def patch_library(self): - def check_lib(): - try: - __import__("patch") - except ImportError: - return False - else: - return True - - return exclusions.only_if(check_lib, "patch library needed") - - @property - def predictable_gc(self): - """target platform must remove all cycles unconditionally when - gc.collect() is called, as well as clean out unreferenced subclasses. - - """ - return self.cpython - - @property - def no_coverage(self): - """Test should be skipped if coverage is enabled. - - This is to block tests that exercise libraries that seem to be - sensitive to coverage, such as PostgreSQL notice logging. - - """ - return exclusions.skip_if( - lambda config: config.options.has_coverage, - "Issues observed when coverage is enabled", - ) - - def _has_mysql_on_windows(self, config): - return False - - def _has_mysql_fully_case_sensitive(self, config): - return False - - @property - def sqlite(self): - return exclusions.skip_if(lambda: not self._has_sqlite()) - - @property - def cextensions(self): - return exclusions.skip_if( - lambda: not util.has_compiled_ext(), - "Cython extensions not installed", - ) - - def _has_sqlite(self): - from sqlalchemy import create_engine - - try: - create_engine("sqlite://") - return True - except ImportError: - return False - - @property - def async_dialect(self): - """dialect makes use of await_() to invoke operations on the DBAPI.""" - - return exclusions.closed() - - @property - def asyncio(self): - return self.greenlet - - @property - def no_greenlet(self): - def go(config): - try: - import greenlet # noqa: F401 - except ImportError: - return True - else: - return False - - return exclusions.only_if(go) - - @property - def greenlet(self): - def go(config): - if not _test_asyncio.ENABLE_ASYNCIO: - return False - - try: - import greenlet # noqa: F401 - except ImportError: - return False - else: - return True - - return exclusions.only_if(go) - - @property - def computed_columns(self): - "Supports computed columns" - return exclusions.closed() - - @property - def computed_columns_stored(self): - "Supports computed columns with `persisted=True`" - return exclusions.closed() - - @property - def computed_columns_virtual(self): - "Supports computed columns with `persisted=False`" - return exclusions.closed() - - @property - def computed_columns_default_persisted(self): - """If the default persistence is virtual or stored when `persisted` - is omitted""" - return exclusions.closed() - - @property - def computed_columns_reflect_persisted(self): - """If persistence information is returned by the reflection of - computed columns""" - return exclusions.closed() - - @property - def supports_distinct_on(self): - """If a backend supports the DISTINCT ON in a select""" - return exclusions.closed() - - @property - def supports_is_distinct_from(self): - """Supports some form of "x IS [NOT] DISTINCT FROM y" construct. - Different dialects will implement their own flavour, e.g., - sqlite will emit "x IS NOT y" instead of "x IS DISTINCT FROM y". - - .. seealso:: - - :meth:`.ColumnOperators.is_distinct_from` - - """ - return exclusions.skip_if( - lambda config: not config.db.dialect.supports_is_distinct_from, - "driver doesn't support an IS DISTINCT FROM construct", - ) - - @property - def identity_columns(self): - """If a backend supports GENERATED { ALWAYS | BY DEFAULT } - AS IDENTITY""" - return exclusions.closed() - - @property - def identity_columns_standard(self): - """If a backend supports GENERATED { ALWAYS | BY DEFAULT } - AS IDENTITY with a standard syntax. - This is mainly to exclude MSSql. - """ - return exclusions.closed() - - @property - def regexp_match(self): - """backend supports the regexp_match operator.""" - return exclusions.closed() - - @property - def regexp_replace(self): - """backend supports the regexp_replace operator.""" - return exclusions.closed() - - @property - def fetch_first(self): - """backend supports the fetch first clause.""" - return exclusions.closed() - - @property - def fetch_percent(self): - """backend supports the fetch first clause with percent.""" - return exclusions.closed() - - @property - def fetch_ties(self): - """backend supports the fetch first clause with ties.""" - return exclusions.closed() - - @property - def fetch_no_order_by(self): - """backend supports the fetch first without order by""" - return exclusions.closed() - - @property - def fetch_offset_with_options(self): - """backend supports the offset when using fetch first with percent - or ties. basically this is "not mssql" - """ - return exclusions.closed() - - @property - def fetch_expression(self): - """backend supports fetch / offset with expression in them, like - - SELECT * FROM some_table - OFFSET 1 + 1 ROWS FETCH FIRST 1 + 1 ROWS ONLY - """ - return exclusions.closed() - - @property - def autoincrement_without_sequence(self): - """If autoincrement=True on a column does not require an explicit - sequence. This should be false only for oracle. - """ - return exclusions.open() - - @property - def generic_classes(self): - "If X[Y] can be implemented with ``__class_getitem__``. py3.7+" - return exclusions.open() - - @property - def json_deserializer_binary(self): - "indicates if the json_deserializer function is called with bytes" - return exclusions.closed() - - @property - def reflect_table_options(self): - """Target database must support reflecting table_options.""" - return exclusions.closed() - - @property - def materialized_views(self): - """Target database must support MATERIALIZED VIEWs.""" - return exclusions.closed() - - @property - def materialized_views_reflect_pk(self): - """Target database reflect MATERIALIZED VIEWs pks.""" - return exclusions.closed() diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/schema.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/schema.py deleted file mode 100644 index 7dfd33d..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/schema.py +++ /dev/null @@ -1,224 +0,0 @@ -# testing/schema.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from __future__ import annotations - -import sys - -from . import config -from . import exclusions -from .. import event -from .. import schema -from .. import types as sqltypes -from ..orm import mapped_column as _orm_mapped_column -from ..util import OrderedDict - -__all__ = ["Table", "Column"] - -table_options = {} - - -def Table(*args, **kw) -> schema.Table: - """A schema.Table wrapper/hook for dialect-specific tweaks.""" - - test_opts = {k: kw.pop(k) for k in list(kw) if k.startswith("test_")} - - kw.update(table_options) - - if exclusions.against(config._current, "mysql"): - if ( - "mysql_engine" not in kw - and "mysql_type" not in kw - and "autoload_with" not in kw - ): - if "test_needs_fk" in test_opts or "test_needs_acid" in test_opts: - kw["mysql_engine"] = "InnoDB" - else: - # there are in fact test fixtures that rely upon MyISAM, - # due to MySQL / MariaDB having poor FK behavior under innodb, - # such as a self-referential table can't be deleted from at - # once without attending to per-row dependencies. We'd need to - # add special steps to some fixtures if we want to not - # explicitly state MyISAM here - kw["mysql_engine"] = "MyISAM" - elif exclusions.against(config._current, "mariadb"): - if ( - "mariadb_engine" not in kw - and "mariadb_type" not in kw - and "autoload_with" not in kw - ): - if "test_needs_fk" in test_opts or "test_needs_acid" in test_opts: - kw["mariadb_engine"] = "InnoDB" - else: - kw["mariadb_engine"] = "MyISAM" - - return schema.Table(*args, **kw) - - -def mapped_column(*args, **kw): - """An orm.mapped_column wrapper/hook for dialect-specific tweaks.""" - - return _schema_column(_orm_mapped_column, args, kw) - - -def Column(*args, **kw): - """A schema.Column wrapper/hook for dialect-specific tweaks.""" - - return _schema_column(schema.Column, args, kw) - - -def _schema_column(factory, args, kw): - test_opts = {k: kw.pop(k) for k in list(kw) if k.startswith("test_")} - - if not config.requirements.foreign_key_ddl.enabled_for_config(config): - args = [arg for arg in args if not isinstance(arg, schema.ForeignKey)] - - construct = factory(*args, **kw) - - if factory is schema.Column: - col = construct - else: - col = construct.column - - if test_opts.get("test_needs_autoincrement", False) and kw.get( - "primary_key", False - ): - if col.default is None and col.server_default is None: - col.autoincrement = True - - # allow any test suite to pick up on this - col.info["test_needs_autoincrement"] = True - - # hardcoded rule for oracle; this should - # be moved out - if exclusions.against(config._current, "oracle"): - - def add_seq(c, tbl): - c._init_items( - schema.Sequence( - _truncate_name( - config.db.dialect, tbl.name + "_" + c.name + "_seq" - ), - optional=True, - ) - ) - - event.listen(col, "after_parent_attach", add_seq, propagate=True) - return construct - - -class eq_type_affinity: - """Helper to compare types inside of datastructures based on affinity. - - E.g.:: - - eq_( - inspect(connection).get_columns("foo"), - [ - { - "name": "id", - "type": testing.eq_type_affinity(sqltypes.INTEGER), - "nullable": False, - "default": None, - "autoincrement": False, - }, - { - "name": "data", - "type": testing.eq_type_affinity(sqltypes.NullType), - "nullable": True, - "default": None, - "autoincrement": False, - }, - ], - ) - - """ - - def __init__(self, target): - self.target = sqltypes.to_instance(target) - - def __eq__(self, other): - return self.target._type_affinity is other._type_affinity - - def __ne__(self, other): - return self.target._type_affinity is not other._type_affinity - - -class eq_compile_type: - """similar to eq_type_affinity but uses compile""" - - def __init__(self, target): - self.target = target - - def __eq__(self, other): - return self.target == other.compile() - - def __ne__(self, other): - return self.target != other.compile() - - -class eq_clause_element: - """Helper to compare SQL structures based on compare()""" - - def __init__(self, target): - self.target = target - - def __eq__(self, other): - return self.target.compare(other) - - def __ne__(self, other): - return not self.target.compare(other) - - -def _truncate_name(dialect, name): - if len(name) > dialect.max_identifier_length: - return ( - name[0 : max(dialect.max_identifier_length - 6, 0)] - + "_" - + hex(hash(name) % 64)[2:] - ) - else: - return name - - -def pep435_enum(name): - # Implements PEP 435 in the minimal fashion needed by SQLAlchemy - __members__ = OrderedDict() - - def __init__(self, name, value, alias=None): - self.name = name - self.value = value - self.__members__[name] = self - value_to_member[value] = self - setattr(self.__class__, name, self) - if alias: - self.__members__[alias] = self - setattr(self.__class__, alias, self) - - value_to_member = {} - - @classmethod - def get(cls, value): - return value_to_member[value] - - someenum = type( - name, - (object,), - {"__members__": __members__, "__init__": __init__, "get": get}, - ) - - # getframe() trick for pickling I don't understand courtesy - # Python namedtuple() - try: - module = sys._getframe(1).f_globals.get("__name__", "__main__") - except (AttributeError, ValueError): - pass - if module is not None: - someenum.__module__ = module - - return someenum diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__init__.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__init__.py deleted file mode 100644 index a146cb3..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# testing/suite/__init__.py -# Copyright (C) 2005-2024 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 -from .test_cte import * # noqa -from .test_ddl import * # noqa -from .test_deprecations import * # noqa -from .test_dialect import * # noqa -from .test_insert import * # noqa -from .test_reflection import * # noqa -from .test_results import * # noqa -from .test_rowcount import * # noqa -from .test_select import * # noqa -from .test_sequence import * # noqa -from .test_types import * # noqa -from .test_unicode_ddl import * # noqa -from .test_update_delete import * # noqa diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/__init__.cpython-311.pyc Binary files differdeleted file mode 100644 index b8ae9f7..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/__init__.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_cte.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_cte.cpython-311.pyc Binary files differdeleted file mode 100644 index 6bf56dd..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_cte.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-311.pyc Binary files differdeleted file mode 100644 index 99ddd6a..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-311.pyc Binary files differdeleted file mode 100644 index a114600..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-311.pyc Binary files differdeleted file mode 100644 index 325c9a1..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_insert.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_insert.cpython-311.pyc Binary files differdeleted file mode 100644 index c212fa4..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_insert.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-311.pyc Binary files differdeleted file mode 100644 index 858574b..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_results.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_results.cpython-311.pyc Binary files differdeleted file mode 100644 index 624d1f2..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_results.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-311.pyc Binary files differdeleted file mode 100644 index 4ebccba..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_select.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_select.cpython-311.pyc Binary files differdeleted file mode 100644 index 5edde44..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_select.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-311.pyc Binary files differdeleted file mode 100644 index 576fdcf..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_types.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_types.cpython-311.pyc Binary files differdeleted file mode 100644 index 79cb268..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_types.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-311.pyc Binary files differdeleted file mode 100644 index 1c8d3ff..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-311.pyc Binary files differdeleted file mode 100644 index 1be9d55..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_cte.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_cte.py deleted file mode 100644 index 5d37880..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_cte.py +++ /dev/null @@ -1,211 +0,0 @@ -# testing/suite/test_cte.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from .. import fixtures -from ..assertions import eq_ -from ..schema import Column -from ..schema import Table -from ... import ForeignKey -from ... import Integer -from ... import select -from ... import String -from ... import testing - - -class CTETest(fixtures.TablesTest): - __backend__ = True - __requires__ = ("ctes",) - - run_inserts = "each" - run_deletes = "each" - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - Column("parent_id", ForeignKey("some_table.id")), - ) - - Table( - "some_other_table", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - Column("parent_id", Integer), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "data": "d1", "parent_id": None}, - {"id": 2, "data": "d2", "parent_id": 1}, - {"id": 3, "data": "d3", "parent_id": 1}, - {"id": 4, "data": "d4", "parent_id": 3}, - {"id": 5, "data": "d5", "parent_id": 3}, - ], - ) - - def test_select_nonrecursive_round_trip(self, connection): - some_table = self.tables.some_table - - cte = ( - select(some_table) - .where(some_table.c.data.in_(["d2", "d3", "d4"])) - .cte("some_cte") - ) - result = connection.execute( - select(cte.c.data).where(cte.c.data.in_(["d4", "d5"])) - ) - eq_(result.fetchall(), [("d4",)]) - - def test_select_recursive_round_trip(self, connection): - some_table = self.tables.some_table - - cte = ( - select(some_table) - .where(some_table.c.data.in_(["d2", "d3", "d4"])) - .cte("some_cte", recursive=True) - ) - - cte_alias = cte.alias("c1") - st1 = some_table.alias() - # note that SQL Server requires this to be UNION ALL, - # can't be UNION - cte = cte.union_all( - select(st1).where(st1.c.id == cte_alias.c.parent_id) - ) - result = connection.execute( - select(cte.c.data) - .where(cte.c.data != "d2") - .order_by(cte.c.data.desc()) - ) - eq_( - result.fetchall(), - [("d4",), ("d3",), ("d3",), ("d1",), ("d1",), ("d1",)], - ) - - def test_insert_from_select_round_trip(self, connection): - some_table = self.tables.some_table - some_other_table = self.tables.some_other_table - - cte = ( - select(some_table) - .where(some_table.c.data.in_(["d2", "d3", "d4"])) - .cte("some_cte") - ) - connection.execute( - some_other_table.insert().from_select( - ["id", "data", "parent_id"], select(cte) - ) - ) - eq_( - connection.execute( - select(some_other_table).order_by(some_other_table.c.id) - ).fetchall(), - [(2, "d2", 1), (3, "d3", 1), (4, "d4", 3)], - ) - - @testing.requires.ctes_with_update_delete - @testing.requires.update_from - def test_update_from_round_trip(self, connection): - some_table = self.tables.some_table - some_other_table = self.tables.some_other_table - - connection.execute( - some_other_table.insert().from_select( - ["id", "data", "parent_id"], select(some_table) - ) - ) - - cte = ( - select(some_table) - .where(some_table.c.data.in_(["d2", "d3", "d4"])) - .cte("some_cte") - ) - connection.execute( - some_other_table.update() - .values(parent_id=5) - .where(some_other_table.c.data == cte.c.data) - ) - eq_( - connection.execute( - select(some_other_table).order_by(some_other_table.c.id) - ).fetchall(), - [ - (1, "d1", None), - (2, "d2", 5), - (3, "d3", 5), - (4, "d4", 5), - (5, "d5", 3), - ], - ) - - @testing.requires.ctes_with_update_delete - @testing.requires.delete_from - def test_delete_from_round_trip(self, connection): - some_table = self.tables.some_table - some_other_table = self.tables.some_other_table - - connection.execute( - some_other_table.insert().from_select( - ["id", "data", "parent_id"], select(some_table) - ) - ) - - cte = ( - select(some_table) - .where(some_table.c.data.in_(["d2", "d3", "d4"])) - .cte("some_cte") - ) - connection.execute( - some_other_table.delete().where( - some_other_table.c.data == cte.c.data - ) - ) - eq_( - connection.execute( - select(some_other_table).order_by(some_other_table.c.id) - ).fetchall(), - [(1, "d1", None), (5, "d5", 3)], - ) - - @testing.requires.ctes_with_update_delete - def test_delete_scalar_subq_round_trip(self, connection): - some_table = self.tables.some_table - some_other_table = self.tables.some_other_table - - connection.execute( - some_other_table.insert().from_select( - ["id", "data", "parent_id"], select(some_table) - ) - ) - - cte = ( - select(some_table) - .where(some_table.c.data.in_(["d2", "d3", "d4"])) - .cte("some_cte") - ) - connection.execute( - some_other_table.delete().where( - some_other_table.c.data - == select(cte.c.data) - .where(cte.c.id == some_other_table.c.id) - .scalar_subquery() - ) - ) - eq_( - connection.execute( - select(some_other_table).order_by(some_other_table.c.id) - ).fetchall(), - [(1, "d1", None), (5, "d5", 3)], - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_ddl.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_ddl.py deleted file mode 100644 index 3d9b8ec..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_ddl.py +++ /dev/null @@ -1,389 +0,0 @@ -# testing/suite/test_ddl.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -import random - -from . import testing -from .. import config -from .. import fixtures -from .. import util -from ..assertions import eq_ -from ..assertions import is_false -from ..assertions import is_true -from ..config import requirements -from ..schema import Table -from ... import CheckConstraint -from ... import Column -from ... import ForeignKeyConstraint -from ... import Index -from ... import inspect -from ... import Integer -from ... import schema -from ... import String -from ... import UniqueConstraint - - -class TableDDLTest(fixtures.TestBase): - __backend__ = True - - def _simple_fixture(self, schema=None): - return Table( - "test_table", - self.metadata, - Column("id", Integer, primary_key=True, autoincrement=False), - Column("data", String(50)), - schema=schema, - ) - - def _underscore_fixture(self): - return Table( - "_test_table", - self.metadata, - Column("id", Integer, primary_key=True, autoincrement=False), - Column("_data", String(50)), - ) - - def _table_index_fixture(self, schema=None): - table = self._simple_fixture(schema=schema) - idx = Index("test_index", table.c.data) - return table, idx - - def _simple_roundtrip(self, table): - with config.db.begin() as conn: - conn.execute(table.insert().values((1, "some data"))) - result = conn.execute(table.select()) - eq_(result.first(), (1, "some data")) - - @requirements.create_table - @util.provide_metadata - def test_create_table(self): - table = self._simple_fixture() - table.create(config.db, checkfirst=False) - self._simple_roundtrip(table) - - @requirements.create_table - @requirements.schemas - @util.provide_metadata - def test_create_table_schema(self): - table = self._simple_fixture(schema=config.test_schema) - table.create(config.db, checkfirst=False) - self._simple_roundtrip(table) - - @requirements.drop_table - @util.provide_metadata - def test_drop_table(self): - table = self._simple_fixture() - table.create(config.db, checkfirst=False) - table.drop(config.db, checkfirst=False) - - @requirements.create_table - @util.provide_metadata - def test_underscore_names(self): - table = self._underscore_fixture() - table.create(config.db, checkfirst=False) - self._simple_roundtrip(table) - - @requirements.comment_reflection - @util.provide_metadata - def test_add_table_comment(self, connection): - table = self._simple_fixture() - table.create(connection, checkfirst=False) - table.comment = "a comment" - connection.execute(schema.SetTableComment(table)) - eq_( - inspect(connection).get_table_comment("test_table"), - {"text": "a comment"}, - ) - - @requirements.comment_reflection - @util.provide_metadata - def test_drop_table_comment(self, connection): - table = self._simple_fixture() - table.create(connection, checkfirst=False) - table.comment = "a comment" - connection.execute(schema.SetTableComment(table)) - connection.execute(schema.DropTableComment(table)) - eq_( - inspect(connection).get_table_comment("test_table"), {"text": None} - ) - - @requirements.table_ddl_if_exists - @util.provide_metadata - def test_create_table_if_not_exists(self, connection): - table = self._simple_fixture() - - connection.execute(schema.CreateTable(table, if_not_exists=True)) - - is_true(inspect(connection).has_table("test_table")) - connection.execute(schema.CreateTable(table, if_not_exists=True)) - - @requirements.index_ddl_if_exists - @util.provide_metadata - def test_create_index_if_not_exists(self, connection): - table, idx = self._table_index_fixture() - - connection.execute(schema.CreateTable(table, if_not_exists=True)) - is_true(inspect(connection).has_table("test_table")) - is_false( - "test_index" - in [ - ix["name"] - for ix in inspect(connection).get_indexes("test_table") - ] - ) - - connection.execute(schema.CreateIndex(idx, if_not_exists=True)) - - is_true( - "test_index" - in [ - ix["name"] - for ix in inspect(connection).get_indexes("test_table") - ] - ) - - connection.execute(schema.CreateIndex(idx, if_not_exists=True)) - - @requirements.table_ddl_if_exists - @util.provide_metadata - def test_drop_table_if_exists(self, connection): - table = self._simple_fixture() - - table.create(connection) - - is_true(inspect(connection).has_table("test_table")) - - connection.execute(schema.DropTable(table, if_exists=True)) - - is_false(inspect(connection).has_table("test_table")) - - connection.execute(schema.DropTable(table, if_exists=True)) - - @requirements.index_ddl_if_exists - @util.provide_metadata - def test_drop_index_if_exists(self, connection): - table, idx = self._table_index_fixture() - - table.create(connection) - - is_true( - "test_index" - in [ - ix["name"] - for ix in inspect(connection).get_indexes("test_table") - ] - ) - - connection.execute(schema.DropIndex(idx, if_exists=True)) - - is_false( - "test_index" - in [ - ix["name"] - for ix in inspect(connection).get_indexes("test_table") - ] - ) - - connection.execute(schema.DropIndex(idx, if_exists=True)) - - -class FutureTableDDLTest(fixtures.FutureEngineMixin, TableDDLTest): - pass - - -class LongNameBlowoutTest(fixtures.TestBase): - """test the creation of a variety of DDL structures and ensure - label length limits pass on backends - - """ - - __backend__ = True - - def fk(self, metadata, connection): - convention = { - "fk": "foreign_key_%(table_name)s_" - "%(column_0_N_name)s_" - "%(referred_table_name)s_" - + ( - "_".join( - "".join(random.choice("abcdef") for j in range(20)) - for i in range(10) - ) - ), - } - metadata.naming_convention = convention - - Table( - "a_things_with_stuff", - metadata, - Column("id_long_column_name", Integer, primary_key=True), - test_needs_fk=True, - ) - - cons = ForeignKeyConstraint( - ["aid"], ["a_things_with_stuff.id_long_column_name"] - ) - Table( - "b_related_things_of_value", - metadata, - Column( - "aid", - ), - cons, - test_needs_fk=True, - ) - actual_name = cons.name - - metadata.create_all(connection) - - if testing.requires.foreign_key_constraint_name_reflection.enabled: - insp = inspect(connection) - fks = insp.get_foreign_keys("b_related_things_of_value") - reflected_name = fks[0]["name"] - - return actual_name, reflected_name - else: - return actual_name, None - - def pk(self, metadata, connection): - convention = { - "pk": "primary_key_%(table_name)s_" - "%(column_0_N_name)s" - + ( - "_".join( - "".join(random.choice("abcdef") for j in range(30)) - for i in range(10) - ) - ), - } - metadata.naming_convention = convention - - a = Table( - "a_things_with_stuff", - metadata, - Column("id_long_column_name", Integer, primary_key=True), - Column("id_another_long_name", Integer, primary_key=True), - ) - cons = a.primary_key - actual_name = cons.name - - metadata.create_all(connection) - insp = inspect(connection) - pk = insp.get_pk_constraint("a_things_with_stuff") - reflected_name = pk["name"] - return actual_name, reflected_name - - def ix(self, metadata, connection): - convention = { - "ix": "index_%(table_name)s_" - "%(column_0_N_name)s" - + ( - "_".join( - "".join(random.choice("abcdef") for j in range(30)) - for i in range(10) - ) - ), - } - metadata.naming_convention = convention - - a = Table( - "a_things_with_stuff", - metadata, - Column("id_long_column_name", Integer, primary_key=True), - Column("id_another_long_name", Integer), - ) - cons = Index(None, a.c.id_long_column_name, a.c.id_another_long_name) - actual_name = cons.name - - metadata.create_all(connection) - insp = inspect(connection) - ix = insp.get_indexes("a_things_with_stuff") - reflected_name = ix[0]["name"] - return actual_name, reflected_name - - def uq(self, metadata, connection): - convention = { - "uq": "unique_constraint_%(table_name)s_" - "%(column_0_N_name)s" - + ( - "_".join( - "".join(random.choice("abcdef") for j in range(30)) - for i in range(10) - ) - ), - } - metadata.naming_convention = convention - - cons = UniqueConstraint("id_long_column_name", "id_another_long_name") - Table( - "a_things_with_stuff", - metadata, - Column("id_long_column_name", Integer, primary_key=True), - Column("id_another_long_name", Integer), - cons, - ) - actual_name = cons.name - - metadata.create_all(connection) - insp = inspect(connection) - uq = insp.get_unique_constraints("a_things_with_stuff") - reflected_name = uq[0]["name"] - return actual_name, reflected_name - - def ck(self, metadata, connection): - convention = { - "ck": "check_constraint_%(table_name)s" - + ( - "_".join( - "".join(random.choice("abcdef") for j in range(30)) - for i in range(10) - ) - ), - } - metadata.naming_convention = convention - - cons = CheckConstraint("some_long_column_name > 5") - Table( - "a_things_with_stuff", - metadata, - Column("id_long_column_name", Integer, primary_key=True), - Column("some_long_column_name", Integer), - cons, - ) - actual_name = cons.name - - metadata.create_all(connection) - insp = inspect(connection) - ck = insp.get_check_constraints("a_things_with_stuff") - reflected_name = ck[0]["name"] - return actual_name, reflected_name - - @testing.combinations( - ("fk",), - ("pk",), - ("ix",), - ("ck", testing.requires.check_constraint_reflection.as_skips()), - ("uq", testing.requires.unique_constraint_reflection.as_skips()), - argnames="type_", - ) - def test_long_convention_name(self, type_, metadata, connection): - actual_name, reflected_name = getattr(self, type_)( - metadata, connection - ) - - assert len(actual_name) > 255 - - if reflected_name is not None: - overlap = actual_name[0 : len(reflected_name)] - if len(overlap) < len(actual_name): - eq_(overlap[0:-5], reflected_name[0 : len(overlap) - 5]) - else: - eq_(overlap, reflected_name) - - -__all__ = ("TableDDLTest", "FutureTableDDLTest", "LongNameBlowoutTest") diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_deprecations.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_deprecations.py deleted file mode 100644 index 07970c0..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_deprecations.py +++ /dev/null @@ -1,153 +0,0 @@ -# testing/suite/test_deprecations.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from .. import fixtures -from ..assertions import eq_ -from ..schema import Column -from ..schema import Table -from ... import Integer -from ... import select -from ... import testing -from ... import union - - -class DeprecatedCompoundSelectTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("x", Integer), - Column("y", Integer), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "x": 1, "y": 2}, - {"id": 2, "x": 2, "y": 3}, - {"id": 3, "x": 3, "y": 4}, - {"id": 4, "x": 4, "y": 5}, - ], - ) - - def _assert_result(self, conn, select, result, params=()): - eq_(conn.execute(select, params).fetchall(), result) - - def test_plain_union(self, connection): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2) - s2 = select(table).where(table.c.id == 3) - - u1 = union(s1, s2) - with testing.expect_deprecated( - "The SelectBase.c and SelectBase.columns " - "attributes are deprecated" - ): - self._assert_result( - connection, u1.order_by(u1.c.id), [(2, 2, 3), (3, 3, 4)] - ) - - # note we've had to remove one use case entirely, which is this - # one. the Select gets its FROMS from the WHERE clause and the - # columns clause, but not the ORDER BY, which means the old ".c" system - # allowed you to "order_by(s.c.foo)" to get an unnamed column in the - # ORDER BY without adding the SELECT into the FROM and breaking the - # query. Users will have to adjust for this use case if they were doing - # it before. - def _dont_test_select_from_plain_union(self, connection): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2) - s2 = select(table).where(table.c.id == 3) - - u1 = union(s1, s2).alias().select() - with testing.expect_deprecated( - "The SelectBase.c and SelectBase.columns " - "attributes are deprecated" - ): - self._assert_result( - connection, u1.order_by(u1.c.id), [(2, 2, 3), (3, 3, 4)] - ) - - @testing.requires.order_by_col_from_union - @testing.requires.parens_in_union_contained_select_w_limit_offset - def test_limit_offset_selectable_in_unions(self, connection): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2).limit(1).order_by(table.c.id) - s2 = select(table).where(table.c.id == 3).limit(1).order_by(table.c.id) - - u1 = union(s1, s2).limit(2) - with testing.expect_deprecated( - "The SelectBase.c and SelectBase.columns " - "attributes are deprecated" - ): - self._assert_result( - connection, u1.order_by(u1.c.id), [(2, 2, 3), (3, 3, 4)] - ) - - @testing.requires.parens_in_union_contained_select_wo_limit_offset - def test_order_by_selectable_in_unions(self, connection): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2).order_by(table.c.id) - s2 = select(table).where(table.c.id == 3).order_by(table.c.id) - - u1 = union(s1, s2).limit(2) - with testing.expect_deprecated( - "The SelectBase.c and SelectBase.columns " - "attributes are deprecated" - ): - self._assert_result( - connection, u1.order_by(u1.c.id), [(2, 2, 3), (3, 3, 4)] - ) - - def test_distinct_selectable_in_unions(self, connection): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2).distinct() - s2 = select(table).where(table.c.id == 3).distinct() - - u1 = union(s1, s2).limit(2) - with testing.expect_deprecated( - "The SelectBase.c and SelectBase.columns " - "attributes are deprecated" - ): - self._assert_result( - connection, u1.order_by(u1.c.id), [(2, 2, 3), (3, 3, 4)] - ) - - def test_limit_offset_aliased_selectable_in_unions(self, connection): - table = self.tables.some_table - s1 = ( - select(table) - .where(table.c.id == 2) - .limit(1) - .order_by(table.c.id) - .alias() - .select() - ) - s2 = ( - select(table) - .where(table.c.id == 3) - .limit(1) - .order_by(table.c.id) - .alias() - .select() - ) - - u1 = union(s1, s2).limit(2) - with testing.expect_deprecated( - "The SelectBase.c and SelectBase.columns " - "attributes are deprecated" - ): - self._assert_result( - connection, u1.order_by(u1.c.id), [(2, 2, 3), (3, 3, 4)] - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_dialect.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_dialect.py deleted file mode 100644 index 6964720..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_dialect.py +++ /dev/null @@ -1,740 +0,0 @@ -# testing/suite/test_dialect.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -import importlib - -from . import testing -from .. import assert_raises -from .. import config -from .. import engines -from .. import eq_ -from .. import fixtures -from .. import is_not_none -from .. import is_true -from .. import ne_ -from .. import provide_metadata -from ..assertions import expect_raises -from ..assertions import expect_raises_message -from ..config import requirements -from ..provision import set_default_schema_on_connection -from ..schema import Column -from ..schema import Table -from ... import bindparam -from ... import dialects -from ... import event -from ... import exc -from ... import Integer -from ... import literal_column -from ... import select -from ... import String -from ...sql.compiler import Compiled -from ...util import inspect_getfullargspec - - -class PingTest(fixtures.TestBase): - __backend__ = True - - def test_do_ping(self): - with testing.db.connect() as conn: - is_true( - testing.db.dialect.do_ping(conn.connection.dbapi_connection) - ) - - -class ArgSignatureTest(fixtures.TestBase): - """test that all visit_XYZ() in :class:`_sql.Compiler` subclasses have - ``**kw``, for #8988. - - This test uses runtime code inspection. Does not need to be a - ``__backend__`` test as it only needs to run once provided all target - dialects have been imported. - - For third party dialects, the suite would be run with that third - party as a "--dburi", which means its compiler classes will have been - imported by the time this test runs. - - """ - - def _all_subclasses(): # type: ignore # noqa - for d in dialects.__all__: - if not d.startswith("_"): - importlib.import_module("sqlalchemy.dialects.%s" % d) - - stack = [Compiled] - - while stack: - cls = stack.pop(0) - stack.extend(cls.__subclasses__()) - yield cls - - @testing.fixture(params=list(_all_subclasses())) - def all_subclasses(self, request): - yield request.param - - def test_all_visit_methods_accept_kw(self, all_subclasses): - cls = all_subclasses - - for k in cls.__dict__: - if k.startswith("visit_"): - meth = getattr(cls, k) - - insp = inspect_getfullargspec(meth) - is_not_none( - insp.varkw, - f"Compiler visit method {cls.__name__}.{k}() does " - "not accommodate for **kw in its argument signature", - ) - - -class ExceptionTest(fixtures.TablesTest): - """Test basic exception wrapping. - - DBAPIs vary a lot in exception behavior so to actually anticipate - specific exceptions from real round trips, we need to be conservative. - - """ - - run_deletes = "each" - - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "manual_pk", - metadata, - Column("id", Integer, primary_key=True, autoincrement=False), - Column("data", String(50)), - ) - - @requirements.duplicate_key_raises_integrity_error - def test_integrity_error(self): - with config.db.connect() as conn: - trans = conn.begin() - conn.execute( - self.tables.manual_pk.insert(), {"id": 1, "data": "d1"} - ) - - assert_raises( - exc.IntegrityError, - conn.execute, - self.tables.manual_pk.insert(), - {"id": 1, "data": "d1"}, - ) - - trans.rollback() - - def test_exception_with_non_ascii(self): - with config.db.connect() as conn: - try: - # try to create an error message that likely has non-ascii - # characters in the DBAPI's message string. unfortunately - # there's no way to make this happen with some drivers like - # mysqlclient, pymysql. this at least does produce a non- - # ascii error message for cx_oracle, psycopg2 - conn.execute(select(literal_column("méil"))) - assert False - except exc.DBAPIError as err: - err_str = str(err) - - assert str(err.orig) in str(err) - - assert isinstance(err_str, str) - - -class IsolationLevelTest(fixtures.TestBase): - __backend__ = True - - __requires__ = ("isolation_level",) - - def _get_non_default_isolation_level(self): - levels = requirements.get_isolation_levels(config) - - default = levels["default"] - supported = levels["supported"] - - s = set(supported).difference(["AUTOCOMMIT", default]) - if s: - return s.pop() - else: - config.skip_test("no non-default isolation level available") - - def test_default_isolation_level(self): - eq_( - config.db.dialect.default_isolation_level, - requirements.get_isolation_levels(config)["default"], - ) - - def test_non_default_isolation_level(self): - non_default = self._get_non_default_isolation_level() - - with config.db.connect() as conn: - existing = conn.get_isolation_level() - - ne_(existing, non_default) - - conn.execution_options(isolation_level=non_default) - - eq_(conn.get_isolation_level(), non_default) - - conn.dialect.reset_isolation_level( - conn.connection.dbapi_connection - ) - - eq_(conn.get_isolation_level(), existing) - - def test_all_levels(self): - levels = requirements.get_isolation_levels(config) - - all_levels = levels["supported"] - - for level in set(all_levels).difference(["AUTOCOMMIT"]): - with config.db.connect() as conn: - conn.execution_options(isolation_level=level) - - eq_(conn.get_isolation_level(), level) - - trans = conn.begin() - trans.rollback() - - eq_(conn.get_isolation_level(), level) - - with config.db.connect() as conn: - eq_( - conn.get_isolation_level(), - levels["default"], - ) - - @testing.requires.get_isolation_level_values - def test_invalid_level_execution_option(self, connection_no_trans): - """test for the new get_isolation_level_values() method""" - - connection = connection_no_trans - with expect_raises_message( - exc.ArgumentError, - "Invalid value '%s' for isolation_level. " - "Valid isolation levels for '%s' are %s" - % ( - "FOO", - connection.dialect.name, - ", ".join( - requirements.get_isolation_levels(config)["supported"] - ), - ), - ): - connection.execution_options(isolation_level="FOO") - - @testing.requires.get_isolation_level_values - @testing.requires.dialect_level_isolation_level_param - def test_invalid_level_engine_param(self, testing_engine): - """test for the new get_isolation_level_values() method - and support for the dialect-level 'isolation_level' parameter. - - """ - - eng = testing_engine(options=dict(isolation_level="FOO")) - with expect_raises_message( - exc.ArgumentError, - "Invalid value '%s' for isolation_level. " - "Valid isolation levels for '%s' are %s" - % ( - "FOO", - eng.dialect.name, - ", ".join( - requirements.get_isolation_levels(config)["supported"] - ), - ), - ): - eng.connect() - - @testing.requires.independent_readonly_connections - def test_dialect_user_setting_is_restored(self, testing_engine): - levels = requirements.get_isolation_levels(config) - default = levels["default"] - supported = ( - sorted( - set(levels["supported"]).difference([default, "AUTOCOMMIT"]) - ) - )[0] - - e = testing_engine(options={"isolation_level": supported}) - - with e.connect() as conn: - eq_(conn.get_isolation_level(), supported) - - with e.connect() as conn: - conn.execution_options(isolation_level=default) - eq_(conn.get_isolation_level(), default) - - with e.connect() as conn: - eq_(conn.get_isolation_level(), supported) - - -class AutocommitIsolationTest(fixtures.TablesTest): - run_deletes = "each" - - __requires__ = ("autocommit",) - - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True, autoincrement=False), - Column("data", String(50)), - test_needs_acid=True, - ) - - def _test_conn_autocommits(self, conn, autocommit): - trans = conn.begin() - conn.execute( - self.tables.some_table.insert(), {"id": 1, "data": "some data"} - ) - trans.rollback() - - eq_( - conn.scalar(select(self.tables.some_table.c.id)), - 1 if autocommit else None, - ) - conn.rollback() - - with conn.begin(): - conn.execute(self.tables.some_table.delete()) - - def test_autocommit_on(self, connection_no_trans): - conn = connection_no_trans - c2 = conn.execution_options(isolation_level="AUTOCOMMIT") - self._test_conn_autocommits(c2, True) - - c2.dialect.reset_isolation_level(c2.connection.dbapi_connection) - - self._test_conn_autocommits(conn, False) - - def test_autocommit_off(self, connection_no_trans): - conn = connection_no_trans - self._test_conn_autocommits(conn, False) - - def test_turn_autocommit_off_via_default_iso_level( - self, connection_no_trans - ): - conn = connection_no_trans - conn = conn.execution_options(isolation_level="AUTOCOMMIT") - self._test_conn_autocommits(conn, True) - - conn.execution_options( - isolation_level=requirements.get_isolation_levels(config)[ - "default" - ] - ) - self._test_conn_autocommits(conn, False) - - @testing.requires.independent_readonly_connections - @testing.variation("use_dialect_setting", [True, False]) - def test_dialect_autocommit_is_restored( - self, testing_engine, use_dialect_setting - ): - """test #10147""" - - if use_dialect_setting: - e = testing_engine(options={"isolation_level": "AUTOCOMMIT"}) - else: - e = testing_engine().execution_options( - isolation_level="AUTOCOMMIT" - ) - - levels = requirements.get_isolation_levels(config) - - default = levels["default"] - - with e.connect() as conn: - self._test_conn_autocommits(conn, True) - - with e.connect() as conn: - conn.execution_options(isolation_level=default) - self._test_conn_autocommits(conn, False) - - with e.connect() as conn: - self._test_conn_autocommits(conn, True) - - -class EscapingTest(fixtures.TestBase): - @provide_metadata - def test_percent_sign_round_trip(self): - """test that the DBAPI accommodates for escaped / nonescaped - percent signs in a way that matches the compiler - - """ - m = self.metadata - t = Table("t", m, Column("data", String(50))) - t.create(config.db) - with config.db.begin() as conn: - conn.execute(t.insert(), dict(data="some % value")) - conn.execute(t.insert(), dict(data="some %% other value")) - - eq_( - conn.scalar( - select(t.c.data).where( - t.c.data == literal_column("'some % value'") - ) - ), - "some % value", - ) - - eq_( - conn.scalar( - select(t.c.data).where( - t.c.data == literal_column("'some %% other value'") - ) - ), - "some %% other value", - ) - - -class WeCanSetDefaultSchemaWEventsTest(fixtures.TestBase): - __backend__ = True - - __requires__ = ("default_schema_name_switch",) - - def test_control_case(self): - default_schema_name = config.db.dialect.default_schema_name - - eng = engines.testing_engine() - with eng.connect(): - pass - - eq_(eng.dialect.default_schema_name, default_schema_name) - - def test_wont_work_wo_insert(self): - default_schema_name = config.db.dialect.default_schema_name - - eng = engines.testing_engine() - - @event.listens_for(eng, "connect") - def on_connect(dbapi_connection, connection_record): - set_default_schema_on_connection( - config, dbapi_connection, config.test_schema - ) - - with eng.connect() as conn: - what_it_should_be = eng.dialect._get_default_schema_name(conn) - eq_(what_it_should_be, config.test_schema) - - eq_(eng.dialect.default_schema_name, default_schema_name) - - def test_schema_change_on_connect(self): - eng = engines.testing_engine() - - @event.listens_for(eng, "connect", insert=True) - def on_connect(dbapi_connection, connection_record): - set_default_schema_on_connection( - config, dbapi_connection, config.test_schema - ) - - with eng.connect() as conn: - what_it_should_be = eng.dialect._get_default_schema_name(conn) - eq_(what_it_should_be, config.test_schema) - - eq_(eng.dialect.default_schema_name, config.test_schema) - - def test_schema_change_works_w_transactions(self): - eng = engines.testing_engine() - - @event.listens_for(eng, "connect", insert=True) - def on_connect(dbapi_connection, *arg): - set_default_schema_on_connection( - config, dbapi_connection, config.test_schema - ) - - with eng.connect() as conn: - trans = conn.begin() - what_it_should_be = eng.dialect._get_default_schema_name(conn) - eq_(what_it_should_be, config.test_schema) - trans.rollback() - - what_it_should_be = eng.dialect._get_default_schema_name(conn) - eq_(what_it_should_be, config.test_schema) - - eq_(eng.dialect.default_schema_name, config.test_schema) - - -class FutureWeCanSetDefaultSchemaWEventsTest( - fixtures.FutureEngineMixin, WeCanSetDefaultSchemaWEventsTest -): - pass - - -class DifficultParametersTest(fixtures.TestBase): - __backend__ = True - - tough_parameters = testing.combinations( - ("boring",), - ("per cent",), - ("per % cent",), - ("%percent",), - ("par(ens)",), - ("percent%(ens)yah",), - ("col:ons",), - ("_starts_with_underscore",), - ("dot.s",), - ("more :: %colons%",), - ("_name",), - ("___name",), - ("[BracketsAndCase]",), - ("42numbers",), - ("percent%signs",), - ("has spaces",), - ("/slashes/",), - ("more/slashes",), - ("q?marks",), - ("1param",), - ("1col:on",), - argnames="paramname", - ) - - @tough_parameters - @config.requirements.unusual_column_name_characters - def test_round_trip_same_named_column( - self, paramname, connection, metadata - ): - name = paramname - - t = Table( - "t", - metadata, - Column("id", Integer, primary_key=True), - Column(name, String(50), nullable=False), - ) - - # table is created - t.create(connection) - - # automatic param generated by insert - connection.execute(t.insert().values({"id": 1, name: "some name"})) - - # automatic param generated by criteria, plus selecting the column - stmt = select(t.c[name]).where(t.c[name] == "some name") - - eq_(connection.scalar(stmt), "some name") - - # use the name in a param explicitly - stmt = select(t.c[name]).where(t.c[name] == bindparam(name)) - - row = connection.execute(stmt, {name: "some name"}).first() - - # name works as the key from cursor.description - eq_(row._mapping[name], "some name") - - # use expanding IN - stmt = select(t.c[name]).where( - t.c[name].in_(["some name", "some other_name"]) - ) - - row = connection.execute(stmt).first() - - @testing.fixture - def multirow_fixture(self, metadata, connection): - mytable = Table( - "mytable", - metadata, - Column("myid", Integer), - Column("name", String(50)), - Column("desc", String(50)), - ) - - mytable.create(connection) - - connection.execute( - mytable.insert(), - [ - {"myid": 1, "name": "a", "desc": "a_desc"}, - {"myid": 2, "name": "b", "desc": "b_desc"}, - {"myid": 3, "name": "c", "desc": "c_desc"}, - {"myid": 4, "name": "d", "desc": "d_desc"}, - ], - ) - yield mytable - - @tough_parameters - def test_standalone_bindparam_escape( - self, paramname, connection, multirow_fixture - ): - tbl1 = multirow_fixture - stmt = select(tbl1.c.myid).where( - tbl1.c.name == bindparam(paramname, value="x") - ) - res = connection.scalar(stmt, {paramname: "c"}) - eq_(res, 3) - - @tough_parameters - def test_standalone_bindparam_escape_expanding( - self, paramname, connection, multirow_fixture - ): - tbl1 = multirow_fixture - stmt = ( - select(tbl1.c.myid) - .where(tbl1.c.name.in_(bindparam(paramname, value=["a", "b"]))) - .order_by(tbl1.c.myid) - ) - - res = connection.scalars(stmt, {paramname: ["d", "a"]}).all() - eq_(res, [1, 4]) - - -class ReturningGuardsTest(fixtures.TablesTest): - """test that the various 'returning' flags are set appropriately""" - - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "t", - metadata, - Column("id", Integer, primary_key=True, autoincrement=False), - Column("data", String(50)), - ) - - @testing.fixture - def run_stmt(self, connection): - t = self.tables.t - - def go(stmt, executemany, id_param_name, expect_success): - stmt = stmt.returning(t.c.id) - - if executemany: - if not expect_success: - # for RETURNING executemany(), we raise our own - # error as this is independent of general RETURNING - # support - with expect_raises_message( - exc.StatementError, - rf"Dialect {connection.dialect.name}\+" - f"{connection.dialect.driver} with " - f"current server capabilities does not support " - f".*RETURNING when executemany is used", - ): - result = connection.execute( - stmt, - [ - {id_param_name: 1, "data": "d1"}, - {id_param_name: 2, "data": "d2"}, - {id_param_name: 3, "data": "d3"}, - ], - ) - else: - result = connection.execute( - stmt, - [ - {id_param_name: 1, "data": "d1"}, - {id_param_name: 2, "data": "d2"}, - {id_param_name: 3, "data": "d3"}, - ], - ) - eq_(result.all(), [(1,), (2,), (3,)]) - else: - if not expect_success: - # for RETURNING execute(), we pass all the way to the DB - # and let it fail - with expect_raises(exc.DBAPIError): - connection.execute( - stmt, {id_param_name: 1, "data": "d1"} - ) - else: - result = connection.execute( - stmt, {id_param_name: 1, "data": "d1"} - ) - eq_(result.all(), [(1,)]) - - return go - - def test_insert_single(self, connection, run_stmt): - t = self.tables.t - - stmt = t.insert() - - run_stmt(stmt, False, "id", connection.dialect.insert_returning) - - def test_insert_many(self, connection, run_stmt): - t = self.tables.t - - stmt = t.insert() - - run_stmt( - stmt, True, "id", connection.dialect.insert_executemany_returning - ) - - def test_update_single(self, connection, run_stmt): - t = self.tables.t - - connection.execute( - t.insert(), - [ - {"id": 1, "data": "d1"}, - {"id": 2, "data": "d2"}, - {"id": 3, "data": "d3"}, - ], - ) - - stmt = t.update().where(t.c.id == bindparam("b_id")) - - run_stmt(stmt, False, "b_id", connection.dialect.update_returning) - - def test_update_many(self, connection, run_stmt): - t = self.tables.t - - connection.execute( - t.insert(), - [ - {"id": 1, "data": "d1"}, - {"id": 2, "data": "d2"}, - {"id": 3, "data": "d3"}, - ], - ) - - stmt = t.update().where(t.c.id == bindparam("b_id")) - - run_stmt( - stmt, True, "b_id", connection.dialect.update_executemany_returning - ) - - def test_delete_single(self, connection, run_stmt): - t = self.tables.t - - connection.execute( - t.insert(), - [ - {"id": 1, "data": "d1"}, - {"id": 2, "data": "d2"}, - {"id": 3, "data": "d3"}, - ], - ) - - stmt = t.delete().where(t.c.id == bindparam("b_id")) - - run_stmt(stmt, False, "b_id", connection.dialect.delete_returning) - - def test_delete_many(self, connection, run_stmt): - t = self.tables.t - - connection.execute( - t.insert(), - [ - {"id": 1, "data": "d1"}, - {"id": 2, "data": "d2"}, - {"id": 3, "data": "d3"}, - ], - ) - - stmt = t.delete().where(t.c.id == bindparam("b_id")) - - run_stmt( - stmt, True, "b_id", connection.dialect.delete_executemany_returning - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_insert.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_insert.py deleted file mode 100644 index 1cff044..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_insert.py +++ /dev/null @@ -1,630 +0,0 @@ -# testing/suite/test_insert.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from decimal import Decimal -import uuid - -from . import testing -from .. import fixtures -from ..assertions import eq_ -from ..config import requirements -from ..schema import Column -from ..schema import Table -from ... import Double -from ... import Float -from ... import Identity -from ... import Integer -from ... import literal -from ... import literal_column -from ... import Numeric -from ... import select -from ... import String -from ...types import LargeBinary -from ...types import UUID -from ...types import Uuid - - -class LastrowidTest(fixtures.TablesTest): - run_deletes = "each" - - __backend__ = True - - __requires__ = "implements_get_lastrowid", "autoincrement_insert" - - @classmethod - def define_tables(cls, metadata): - Table( - "autoinc_pk", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("data", String(50)), - implicit_returning=False, - ) - - Table( - "manual_pk", - metadata, - Column("id", Integer, primary_key=True, autoincrement=False), - Column("data", String(50)), - implicit_returning=False, - ) - - def _assert_round_trip(self, table, conn): - row = conn.execute(table.select()).first() - eq_( - row, - ( - conn.dialect.default_sequence_base, - "some data", - ), - ) - - def test_autoincrement_on_insert(self, connection): - connection.execute( - self.tables.autoinc_pk.insert(), dict(data="some data") - ) - self._assert_round_trip(self.tables.autoinc_pk, connection) - - def test_last_inserted_id(self, connection): - r = connection.execute( - self.tables.autoinc_pk.insert(), dict(data="some data") - ) - pk = connection.scalar(select(self.tables.autoinc_pk.c.id)) - eq_(r.inserted_primary_key, (pk,)) - - @requirements.dbapi_lastrowid - def test_native_lastrowid_autoinc(self, connection): - r = connection.execute( - self.tables.autoinc_pk.insert(), dict(data="some data") - ) - lastrowid = r.lastrowid - pk = connection.scalar(select(self.tables.autoinc_pk.c.id)) - eq_(lastrowid, pk) - - -class InsertBehaviorTest(fixtures.TablesTest): - run_deletes = "each" - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "autoinc_pk", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("data", String(50)), - ) - Table( - "manual_pk", - metadata, - Column("id", Integer, primary_key=True, autoincrement=False), - Column("data", String(50)), - ) - Table( - "no_implicit_returning", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("data", String(50)), - implicit_returning=False, - ) - Table( - "includes_defaults", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("data", String(50)), - Column("x", Integer, default=5), - Column( - "y", - Integer, - default=literal_column("2", type_=Integer) + literal(2), - ), - ) - - @testing.variation("style", ["plain", "return_defaults"]) - @testing.variation("executemany", [True, False]) - def test_no_results_for_non_returning_insert( - self, connection, style, executemany - ): - """test another INSERT issue found during #10453""" - - table = self.tables.no_implicit_returning - - stmt = table.insert() - if style.return_defaults: - stmt = stmt.return_defaults() - - if executemany: - data = [ - {"data": "d1"}, - {"data": "d2"}, - {"data": "d3"}, - {"data": "d4"}, - {"data": "d5"}, - ] - else: - data = {"data": "d1"} - - r = connection.execute(stmt, data) - assert not r.returns_rows - - @requirements.autoincrement_insert - def test_autoclose_on_insert(self, connection): - r = connection.execute( - self.tables.autoinc_pk.insert(), dict(data="some data") - ) - assert r._soft_closed - assert not r.closed - assert r.is_insert - - # new as of I8091919d45421e3f53029b8660427f844fee0228; for the moment - # an insert where the PK was taken from a row that the dialect - # selected, as is the case for mssql/pyodbc, will still report - # returns_rows as true because there's a cursor description. in that - # case, the row had to have been consumed at least. - assert not r.returns_rows or r.fetchone() is None - - @requirements.insert_returning - def test_autoclose_on_insert_implicit_returning(self, connection): - r = connection.execute( - # return_defaults() ensures RETURNING will be used, - # new in 2.0 as sqlite/mariadb offer both RETURNING and - # cursor.lastrowid - self.tables.autoinc_pk.insert().return_defaults(), - dict(data="some data"), - ) - assert r._soft_closed - assert not r.closed - assert r.is_insert - - # note we are experimenting with having this be True - # as of I8091919d45421e3f53029b8660427f844fee0228 . - # implicit returning has fetched the row, but it still is a - # "returns rows" - assert r.returns_rows - - # and we should be able to fetchone() on it, we just get no row - eq_(r.fetchone(), None) - - # and the keys, etc. - eq_(r.keys(), ["id"]) - - # but the dialect took in the row already. not really sure - # what the best behavior is. - - @requirements.empty_inserts - def test_empty_insert(self, connection): - r = connection.execute(self.tables.autoinc_pk.insert()) - assert r._soft_closed - assert not r.closed - - r = connection.execute( - self.tables.autoinc_pk.select().where( - self.tables.autoinc_pk.c.id != None - ) - ) - eq_(len(r.all()), 1) - - @requirements.empty_inserts_executemany - def test_empty_insert_multiple(self, connection): - r = connection.execute(self.tables.autoinc_pk.insert(), [{}, {}, {}]) - assert r._soft_closed - assert not r.closed - - r = connection.execute( - self.tables.autoinc_pk.select().where( - self.tables.autoinc_pk.c.id != None - ) - ) - - eq_(len(r.all()), 3) - - @requirements.insert_from_select - def test_insert_from_select_autoinc(self, connection): - src_table = self.tables.manual_pk - dest_table = self.tables.autoinc_pk - connection.execute( - src_table.insert(), - [ - dict(id=1, data="data1"), - dict(id=2, data="data2"), - dict(id=3, data="data3"), - ], - ) - - result = connection.execute( - dest_table.insert().from_select( - ("data",), - select(src_table.c.data).where( - src_table.c.data.in_(["data2", "data3"]) - ), - ) - ) - - eq_(result.inserted_primary_key, (None,)) - - result = connection.execute( - select(dest_table.c.data).order_by(dest_table.c.data) - ) - eq_(result.fetchall(), [("data2",), ("data3",)]) - - @requirements.insert_from_select - def test_insert_from_select_autoinc_no_rows(self, connection): - src_table = self.tables.manual_pk - dest_table = self.tables.autoinc_pk - - result = connection.execute( - dest_table.insert().from_select( - ("data",), - select(src_table.c.data).where( - src_table.c.data.in_(["data2", "data3"]) - ), - ) - ) - eq_(result.inserted_primary_key, (None,)) - - result = connection.execute( - select(dest_table.c.data).order_by(dest_table.c.data) - ) - - eq_(result.fetchall(), []) - - @requirements.insert_from_select - def test_insert_from_select(self, connection): - table = self.tables.manual_pk - connection.execute( - table.insert(), - [ - dict(id=1, data="data1"), - dict(id=2, data="data2"), - dict(id=3, data="data3"), - ], - ) - - connection.execute( - table.insert() - .inline() - .from_select( - ("id", "data"), - select(table.c.id + 5, table.c.data).where( - table.c.data.in_(["data2", "data3"]) - ), - ) - ) - - eq_( - connection.execute( - select(table.c.data).order_by(table.c.data) - ).fetchall(), - [("data1",), ("data2",), ("data2",), ("data3",), ("data3",)], - ) - - @requirements.insert_from_select - def test_insert_from_select_with_defaults(self, connection): - table = self.tables.includes_defaults - connection.execute( - table.insert(), - [ - dict(id=1, data="data1"), - dict(id=2, data="data2"), - dict(id=3, data="data3"), - ], - ) - - connection.execute( - table.insert() - .inline() - .from_select( - ("id", "data"), - select(table.c.id + 5, table.c.data).where( - table.c.data.in_(["data2", "data3"]) - ), - ) - ) - - eq_( - connection.execute( - select(table).order_by(table.c.data, table.c.id) - ).fetchall(), - [ - (1, "data1", 5, 4), - (2, "data2", 5, 4), - (7, "data2", 5, 4), - (3, "data3", 5, 4), - (8, "data3", 5, 4), - ], - ) - - -class ReturningTest(fixtures.TablesTest): - run_create_tables = "each" - __requires__ = "insert_returning", "autoincrement_insert" - __backend__ = True - - def _assert_round_trip(self, table, conn): - row = conn.execute(table.select()).first() - eq_( - row, - ( - conn.dialect.default_sequence_base, - "some data", - ), - ) - - @classmethod - def define_tables(cls, metadata): - Table( - "autoinc_pk", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("data", String(50)), - ) - - @requirements.fetch_rows_post_commit - def test_explicit_returning_pk_autocommit(self, connection): - table = self.tables.autoinc_pk - r = connection.execute( - table.insert().returning(table.c.id), dict(data="some data") - ) - pk = r.first()[0] - fetched_pk = connection.scalar(select(table.c.id)) - eq_(fetched_pk, pk) - - def test_explicit_returning_pk_no_autocommit(self, connection): - table = self.tables.autoinc_pk - r = connection.execute( - table.insert().returning(table.c.id), dict(data="some data") - ) - - pk = r.first()[0] - fetched_pk = connection.scalar(select(table.c.id)) - eq_(fetched_pk, pk) - - def test_autoincrement_on_insert_implicit_returning(self, connection): - connection.execute( - self.tables.autoinc_pk.insert(), dict(data="some data") - ) - self._assert_round_trip(self.tables.autoinc_pk, connection) - - def test_last_inserted_id_implicit_returning(self, connection): - r = connection.execute( - self.tables.autoinc_pk.insert(), dict(data="some data") - ) - pk = connection.scalar(select(self.tables.autoinc_pk.c.id)) - eq_(r.inserted_primary_key, (pk,)) - - @requirements.insert_executemany_returning - def test_insertmanyvalues_returning(self, connection): - r = connection.execute( - self.tables.autoinc_pk.insert().returning( - self.tables.autoinc_pk.c.id - ), - [ - {"data": "d1"}, - {"data": "d2"}, - {"data": "d3"}, - {"data": "d4"}, - {"data": "d5"}, - ], - ) - rall = r.all() - - pks = connection.execute(select(self.tables.autoinc_pk.c.id)) - - eq_(rall, pks.all()) - - @testing.combinations( - (Double(), 8.5514716, True), - ( - Double(53), - 8.5514716, - True, - testing.requires.float_or_double_precision_behaves_generically, - ), - (Float(), 8.5514, True), - ( - Float(8), - 8.5514, - True, - testing.requires.float_or_double_precision_behaves_generically, - ), - ( - Numeric(precision=15, scale=12, asdecimal=False), - 8.5514716, - True, - testing.requires.literal_float_coercion, - ), - ( - Numeric(precision=15, scale=12, asdecimal=True), - Decimal("8.5514716"), - False, - ), - argnames="type_,value,do_rounding", - ) - @testing.variation("sort_by_parameter_order", [True, False]) - @testing.variation("multiple_rows", [True, False]) - def test_insert_w_floats( - self, - connection, - metadata, - sort_by_parameter_order, - type_, - value, - do_rounding, - multiple_rows, - ): - """test #9701. - - this tests insertmanyvalues as well as decimal / floating point - RETURNING types - - """ - - t = Table( - # Oracle backends seems to be getting confused if - # this table is named the same as the one - # in test_imv_returning_datatypes. use a different name - "f_t", - metadata, - Column("id", Integer, Identity(), primary_key=True), - Column("value", type_), - ) - - t.create(connection) - - result = connection.execute( - t.insert().returning( - t.c.id, - t.c.value, - sort_by_parameter_order=bool(sort_by_parameter_order), - ), - ( - [{"value": value} for i in range(10)] - if multiple_rows - else {"value": value} - ), - ) - - if multiple_rows: - i_range = range(1, 11) - else: - i_range = range(1, 2) - - # we want to test only that we are getting floating points back - # with some degree of the original value maintained, that it is not - # being truncated to an integer. there's too much variation in how - # drivers return floats, which should not be relied upon to be - # exact, for us to just compare as is (works for PG drivers but not - # others) so we use rounding here. There's precedent for this - # in suite/test_types.py::NumericTest as well - - if do_rounding: - eq_( - {(id_, round(val_, 5)) for id_, val_ in result}, - {(id_, round(value, 5)) for id_ in i_range}, - ) - - eq_( - { - round(val_, 5) - for val_ in connection.scalars(select(t.c.value)) - }, - {round(value, 5)}, - ) - else: - eq_( - set(result), - {(id_, value) for id_ in i_range}, - ) - - eq_( - set(connection.scalars(select(t.c.value))), - {value}, - ) - - @testing.combinations( - ( - "non_native_uuid", - Uuid(native_uuid=False), - uuid.uuid4(), - ), - ( - "non_native_uuid_str", - Uuid(as_uuid=False, native_uuid=False), - str(uuid.uuid4()), - ), - ( - "generic_native_uuid", - Uuid(native_uuid=True), - uuid.uuid4(), - testing.requires.uuid_data_type, - ), - ( - "generic_native_uuid_str", - Uuid(as_uuid=False, native_uuid=True), - str(uuid.uuid4()), - testing.requires.uuid_data_type, - ), - ("UUID", UUID(), uuid.uuid4(), testing.requires.uuid_data_type), - ( - "LargeBinary1", - LargeBinary(), - b"this is binary", - ), - ("LargeBinary2", LargeBinary(), b"7\xe7\x9f"), - argnames="type_,value", - id_="iaa", - ) - @testing.variation("sort_by_parameter_order", [True, False]) - @testing.variation("multiple_rows", [True, False]) - @testing.requires.insert_returning - def test_imv_returning_datatypes( - self, - connection, - metadata, - sort_by_parameter_order, - type_, - value, - multiple_rows, - ): - """test #9739, #9808 (similar to #9701). - - this tests insertmanyvalues in conjunction with various datatypes. - - These tests are particularly for the asyncpg driver which needs - most types to be explicitly cast for the new IMV format - - """ - t = Table( - "d_t", - metadata, - Column("id", Integer, Identity(), primary_key=True), - Column("value", type_), - ) - - t.create(connection) - - result = connection.execute( - t.insert().returning( - t.c.id, - t.c.value, - sort_by_parameter_order=bool(sort_by_parameter_order), - ), - ( - [{"value": value} for i in range(10)] - if multiple_rows - else {"value": value} - ), - ) - - if multiple_rows: - i_range = range(1, 11) - else: - i_range = range(1, 2) - - eq_( - set(result), - {(id_, value) for id_ in i_range}, - ) - - eq_( - set(connection.scalars(select(t.c.value))), - {value}, - ) - - -__all__ = ("LastrowidTest", "InsertBehaviorTest", "ReturningTest") diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_reflection.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_reflection.py deleted file mode 100644 index f257d2f..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_reflection.py +++ /dev/null @@ -1,3128 +0,0 @@ -# testing/suite/test_reflection.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -import operator -import re - -import sqlalchemy as sa -from .. import config -from .. import engines -from .. import eq_ -from .. import expect_raises -from .. import expect_raises_message -from .. import expect_warnings -from .. import fixtures -from .. import is_ -from ..provision import get_temp_table_name -from ..provision import temp_table_keyword_args -from ..schema import Column -from ..schema import Table -from ... import event -from ... import ForeignKey -from ... import func -from ... import Identity -from ... import inspect -from ... import Integer -from ... import MetaData -from ... import String -from ... import testing -from ... import types as sql_types -from ...engine import Inspector -from ...engine import ObjectKind -from ...engine import ObjectScope -from ...exc import NoSuchTableError -from ...exc import UnreflectableTableError -from ...schema import DDL -from ...schema import Index -from ...sql.elements import quoted_name -from ...sql.schema import BLANK_SCHEMA -from ...testing import ComparesIndexes -from ...testing import ComparesTables -from ...testing import is_false -from ...testing import is_true -from ...testing import mock - - -metadata, users = None, None - - -class OneConnectionTablesTest(fixtures.TablesTest): - @classmethod - def setup_bind(cls): - # TODO: when temp tables are subject to server reset, - # this will also have to disable that server reset from - # happening - if config.requirements.independent_connections.enabled: - from sqlalchemy import pool - - return engines.testing_engine( - options=dict(poolclass=pool.StaticPool, scope="class"), - ) - else: - return config.db - - -class HasTableTest(OneConnectionTablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "test_table", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - ) - if testing.requires.schemas.enabled: - Table( - "test_table_s", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - schema=config.test_schema, - ) - - if testing.requires.view_reflection: - cls.define_views(metadata) - if testing.requires.has_temp_table.enabled: - cls.define_temp_tables(metadata) - - @classmethod - def define_views(cls, metadata): - query = "CREATE VIEW vv AS SELECT id, data FROM test_table" - - event.listen(metadata, "after_create", DDL(query)) - event.listen(metadata, "before_drop", DDL("DROP VIEW vv")) - - if testing.requires.schemas.enabled: - query = ( - "CREATE VIEW %s.vv AS SELECT id, data FROM %s.test_table_s" - % ( - config.test_schema, - config.test_schema, - ) - ) - event.listen(metadata, "after_create", DDL(query)) - event.listen( - metadata, - "before_drop", - DDL("DROP VIEW %s.vv" % (config.test_schema)), - ) - - @classmethod - def temp_table_name(cls): - return get_temp_table_name( - config, config.db, f"user_tmp_{config.ident}" - ) - - @classmethod - def define_temp_tables(cls, metadata): - kw = temp_table_keyword_args(config, config.db) - table_name = cls.temp_table_name() - user_tmp = Table( - table_name, - metadata, - Column("id", sa.INT, primary_key=True), - Column("name", sa.VARCHAR(50)), - **kw, - ) - if ( - testing.requires.view_reflection.enabled - and testing.requires.temporary_views.enabled - ): - event.listen( - user_tmp, - "after_create", - DDL( - "create temporary view user_tmp_v as " - "select * from user_tmp_%s" % config.ident - ), - ) - event.listen(user_tmp, "before_drop", DDL("drop view user_tmp_v")) - - def test_has_table(self): - with config.db.begin() as conn: - is_true(config.db.dialect.has_table(conn, "test_table")) - is_false(config.db.dialect.has_table(conn, "test_table_s")) - is_false(config.db.dialect.has_table(conn, "nonexistent_table")) - - def test_has_table_cache(self, metadata): - insp = inspect(config.db) - is_true(insp.has_table("test_table")) - nt = Table("new_table", metadata, Column("col", Integer)) - is_false(insp.has_table("new_table")) - nt.create(config.db) - try: - is_false(insp.has_table("new_table")) - insp.clear_cache() - is_true(insp.has_table("new_table")) - finally: - nt.drop(config.db) - - @testing.requires.schemas - def test_has_table_schema(self): - with config.db.begin() as conn: - is_false( - config.db.dialect.has_table( - conn, "test_table", schema=config.test_schema - ) - ) - is_true( - config.db.dialect.has_table( - conn, "test_table_s", schema=config.test_schema - ) - ) - is_false( - config.db.dialect.has_table( - conn, "nonexistent_table", schema=config.test_schema - ) - ) - - @testing.requires.schemas - def test_has_table_nonexistent_schema(self): - with config.db.begin() as conn: - is_false( - config.db.dialect.has_table( - conn, "test_table", schema="nonexistent_schema" - ) - ) - - @testing.requires.views - def test_has_table_view(self, connection): - insp = inspect(connection) - is_true(insp.has_table("vv")) - - @testing.requires.has_temp_table - def test_has_table_temp_table(self, connection): - insp = inspect(connection) - temp_table_name = self.temp_table_name() - is_true(insp.has_table(temp_table_name)) - - @testing.requires.has_temp_table - @testing.requires.view_reflection - @testing.requires.temporary_views - def test_has_table_temp_view(self, connection): - insp = inspect(connection) - is_true(insp.has_table("user_tmp_v")) - - @testing.requires.views - @testing.requires.schemas - def test_has_table_view_schema(self, connection): - insp = inspect(connection) - is_true(insp.has_table("vv", config.test_schema)) - - -class HasIndexTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - tt = Table( - "test_table", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - Column("data2", String(50)), - ) - Index("my_idx", tt.c.data) - - if testing.requires.schemas.enabled: - tt = Table( - "test_table", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - schema=config.test_schema, - ) - Index("my_idx_s", tt.c.data) - - kind = testing.combinations("dialect", "inspector", argnames="kind") - - def _has_index(self, kind, conn): - if kind == "dialect": - return lambda *a, **k: config.db.dialect.has_index(conn, *a, **k) - else: - return inspect(conn).has_index - - @kind - def test_has_index(self, kind, connection, metadata): - meth = self._has_index(kind, connection) - assert meth("test_table", "my_idx") - assert not meth("test_table", "my_idx_s") - assert not meth("nonexistent_table", "my_idx") - assert not meth("test_table", "nonexistent_idx") - - assert not meth("test_table", "my_idx_2") - assert not meth("test_table_2", "my_idx_3") - idx = Index("my_idx_2", self.tables.test_table.c.data2) - tbl = Table( - "test_table_2", - metadata, - Column("foo", Integer), - Index("my_idx_3", "foo"), - ) - idx.create(connection) - tbl.create(connection) - try: - if kind == "inspector": - assert not meth("test_table", "my_idx_2") - assert not meth("test_table_2", "my_idx_3") - meth.__self__.clear_cache() - assert meth("test_table", "my_idx_2") is True - assert meth("test_table_2", "my_idx_3") is True - finally: - tbl.drop(connection) - idx.drop(connection) - - @testing.requires.schemas - @kind - def test_has_index_schema(self, kind, connection): - meth = self._has_index(kind, connection) - assert meth("test_table", "my_idx_s", schema=config.test_schema) - assert not meth("test_table", "my_idx", schema=config.test_schema) - assert not meth( - "nonexistent_table", "my_idx_s", schema=config.test_schema - ) - assert not meth( - "test_table", "nonexistent_idx_s", schema=config.test_schema - ) - - -class BizarroCharacterFKResolutionTest(fixtures.TestBase): - """tests for #10275""" - - __backend__ = True - - @testing.combinations( - ("id",), ("(3)",), ("col%p",), ("[brack]",), argnames="columnname" - ) - @testing.variation("use_composite", [True, False]) - @testing.combinations( - ("plain",), - ("(2)",), - ("per % cent",), - ("[brackets]",), - argnames="tablename", - ) - def test_fk_ref( - self, connection, metadata, use_composite, tablename, columnname - ): - tt = Table( - tablename, - metadata, - Column(columnname, Integer, key="id", primary_key=True), - test_needs_fk=True, - ) - if use_composite: - tt.append_column(Column("id2", Integer, primary_key=True)) - - if use_composite: - Table( - "other", - metadata, - Column("id", Integer, primary_key=True), - Column("ref", Integer), - Column("ref2", Integer), - sa.ForeignKeyConstraint(["ref", "ref2"], [tt.c.id, tt.c.id2]), - test_needs_fk=True, - ) - else: - Table( - "other", - metadata, - Column("id", Integer, primary_key=True), - Column("ref", ForeignKey(tt.c.id)), - test_needs_fk=True, - ) - - metadata.create_all(connection) - - m2 = MetaData() - - o2 = Table("other", m2, autoload_with=connection) - t1 = m2.tables[tablename] - - assert o2.c.ref.references(t1.c[0]) - if use_composite: - assert o2.c.ref2.references(t1.c[1]) - - -class QuotedNameArgumentTest(fixtures.TablesTest): - run_create_tables = "once" - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "quote ' one", - metadata, - Column("id", Integer), - Column("name", String(50)), - Column("data", String(50)), - Column("related_id", Integer), - sa.PrimaryKeyConstraint("id", name="pk quote ' one"), - sa.Index("ix quote ' one", "name"), - sa.UniqueConstraint( - "data", - name="uq quote' one", - ), - sa.ForeignKeyConstraint( - ["id"], ["related.id"], name="fk quote ' one" - ), - sa.CheckConstraint("name != 'foo'", name="ck quote ' one"), - comment=r"""quote ' one comment""", - test_needs_fk=True, - ) - - if testing.requires.symbol_names_w_double_quote.enabled: - Table( - 'quote " two', - metadata, - Column("id", Integer), - Column("name", String(50)), - Column("data", String(50)), - Column("related_id", Integer), - sa.PrimaryKeyConstraint("id", name='pk quote " two'), - sa.Index('ix quote " two', "name"), - sa.UniqueConstraint( - "data", - name='uq quote" two', - ), - sa.ForeignKeyConstraint( - ["id"], ["related.id"], name='fk quote " two' - ), - sa.CheckConstraint("name != 'foo'", name='ck quote " two '), - comment=r"""quote " two comment""", - test_needs_fk=True, - ) - - Table( - "related", - metadata, - Column("id", Integer, primary_key=True), - Column("related", Integer), - test_needs_fk=True, - ) - - if testing.requires.view_column_reflection.enabled: - if testing.requires.symbol_names_w_double_quote.enabled: - names = [ - "quote ' one", - 'quote " two', - ] - else: - names = [ - "quote ' one", - ] - for name in names: - query = "CREATE VIEW %s AS SELECT * FROM %s" % ( - config.db.dialect.identifier_preparer.quote( - "view %s" % name - ), - config.db.dialect.identifier_preparer.quote(name), - ) - - event.listen(metadata, "after_create", DDL(query)) - event.listen( - metadata, - "before_drop", - DDL( - "DROP VIEW %s" - % config.db.dialect.identifier_preparer.quote( - "view %s" % name - ) - ), - ) - - def quote_fixtures(fn): - return testing.combinations( - ("quote ' one",), - ('quote " two', testing.requires.symbol_names_w_double_quote), - )(fn) - - @quote_fixtures - def test_get_table_options(self, name): - insp = inspect(config.db) - - if testing.requires.reflect_table_options.enabled: - res = insp.get_table_options(name) - is_true(isinstance(res, dict)) - else: - with expect_raises(NotImplementedError): - res = insp.get_table_options(name) - - @quote_fixtures - @testing.requires.view_column_reflection - def test_get_view_definition(self, name): - insp = inspect(config.db) - assert insp.get_view_definition("view %s" % name) - - @quote_fixtures - def test_get_columns(self, name): - insp = inspect(config.db) - assert insp.get_columns(name) - - @quote_fixtures - def test_get_pk_constraint(self, name): - insp = inspect(config.db) - assert insp.get_pk_constraint(name) - - @quote_fixtures - def test_get_foreign_keys(self, name): - insp = inspect(config.db) - assert insp.get_foreign_keys(name) - - @quote_fixtures - def test_get_indexes(self, name): - insp = inspect(config.db) - assert insp.get_indexes(name) - - @quote_fixtures - @testing.requires.unique_constraint_reflection - def test_get_unique_constraints(self, name): - insp = inspect(config.db) - assert insp.get_unique_constraints(name) - - @quote_fixtures - @testing.requires.comment_reflection - def test_get_table_comment(self, name): - insp = inspect(config.db) - assert insp.get_table_comment(name) - - @quote_fixtures - @testing.requires.check_constraint_reflection - def test_get_check_constraints(self, name): - insp = inspect(config.db) - assert insp.get_check_constraints(name) - - -def _multi_combination(fn): - schema = testing.combinations( - None, - ( - lambda: config.test_schema, - testing.requires.schemas, - ), - argnames="schema", - ) - scope = testing.combinations( - ObjectScope.DEFAULT, - ObjectScope.TEMPORARY, - ObjectScope.ANY, - argnames="scope", - ) - kind = testing.combinations( - ObjectKind.TABLE, - ObjectKind.VIEW, - ObjectKind.MATERIALIZED_VIEW, - ObjectKind.ANY, - ObjectKind.ANY_VIEW, - ObjectKind.TABLE | ObjectKind.VIEW, - ObjectKind.TABLE | ObjectKind.MATERIALIZED_VIEW, - argnames="kind", - ) - filter_names = testing.combinations(True, False, argnames="use_filter") - - return schema(scope(kind(filter_names(fn)))) - - -class ComponentReflectionTest(ComparesTables, OneConnectionTablesTest): - run_inserts = run_deletes = None - - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - cls.define_reflected_tables(metadata, None) - if testing.requires.schemas.enabled: - cls.define_reflected_tables(metadata, testing.config.test_schema) - - @classmethod - def define_reflected_tables(cls, metadata, schema): - if schema: - schema_prefix = schema + "." - else: - schema_prefix = "" - - if testing.requires.self_referential_foreign_keys.enabled: - parent_id_args = ( - ForeignKey( - "%susers.user_id" % schema_prefix, name="user_id_fk" - ), - ) - else: - parent_id_args = () - users = Table( - "users", - metadata, - Column("user_id", sa.INT, primary_key=True), - Column("test1", sa.CHAR(5), nullable=False), - Column("test2", sa.Float(), nullable=False), - Column("parent_user_id", sa.Integer, *parent_id_args), - sa.CheckConstraint( - "test2 > 0", - name="zz_test2_gt_zero", - comment="users check constraint", - ), - sa.CheckConstraint("test2 <= 1000"), - schema=schema, - test_needs_fk=True, - ) - - Table( - "dingalings", - metadata, - Column("dingaling_id", sa.Integer, primary_key=True), - Column( - "address_id", - sa.Integer, - ForeignKey( - "%semail_addresses.address_id" % schema_prefix, - name="zz_email_add_id_fg", - comment="di fk comment", - ), - ), - Column( - "id_user", - sa.Integer, - ForeignKey("%susers.user_id" % schema_prefix), - ), - Column("data", sa.String(30), unique=True), - sa.CheckConstraint( - "address_id > 0 AND address_id < 1000", - name="address_id_gt_zero", - ), - sa.UniqueConstraint( - "address_id", - "dingaling_id", - name="zz_dingalings_multiple", - comment="di unique comment", - ), - schema=schema, - test_needs_fk=True, - ) - Table( - "email_addresses", - metadata, - Column("address_id", sa.Integer), - Column("remote_user_id", sa.Integer, ForeignKey(users.c.user_id)), - Column("email_address", sa.String(20), index=True), - sa.PrimaryKeyConstraint( - "address_id", name="email_ad_pk", comment="ea pk comment" - ), - schema=schema, - test_needs_fk=True, - ) - Table( - "comment_test", - metadata, - Column("id", sa.Integer, primary_key=True, comment="id comment"), - Column("data", sa.String(20), comment="data % comment"), - Column( - "d2", - sa.String(20), - comment=r"""Comment types type speedily ' " \ '' Fun!""", - ), - Column("d3", sa.String(42), comment="Comment\nwith\rescapes"), - schema=schema, - comment=r"""the test % ' " \ table comment""", - ) - Table( - "no_constraints", - metadata, - Column("data", sa.String(20)), - schema=schema, - comment="no\nconstraints\rhas\fescaped\vcomment", - ) - - if testing.requires.cross_schema_fk_reflection.enabled: - if schema is None: - Table( - "local_table", - metadata, - Column("id", sa.Integer, primary_key=True), - Column("data", sa.String(20)), - Column( - "remote_id", - ForeignKey( - "%s.remote_table_2.id" % testing.config.test_schema - ), - ), - test_needs_fk=True, - schema=config.db.dialect.default_schema_name, - ) - else: - Table( - "remote_table", - metadata, - Column("id", sa.Integer, primary_key=True), - Column( - "local_id", - ForeignKey( - "%s.local_table.id" - % config.db.dialect.default_schema_name - ), - ), - Column("data", sa.String(20)), - schema=schema, - test_needs_fk=True, - ) - Table( - "remote_table_2", - metadata, - Column("id", sa.Integer, primary_key=True), - Column("data", sa.String(20)), - schema=schema, - test_needs_fk=True, - ) - - if testing.requires.index_reflection.enabled: - Index("users_t_idx", users.c.test1, users.c.test2, unique=True) - Index( - "users_all_idx", users.c.user_id, users.c.test2, users.c.test1 - ) - - if not schema: - # test_needs_fk is at the moment to force MySQL InnoDB - noncol_idx_test_nopk = Table( - "noncol_idx_test_nopk", - metadata, - Column("q", sa.String(5)), - test_needs_fk=True, - ) - - noncol_idx_test_pk = Table( - "noncol_idx_test_pk", - metadata, - Column("id", sa.Integer, primary_key=True), - Column("q", sa.String(5)), - test_needs_fk=True, - ) - - if ( - testing.requires.indexes_with_ascdesc.enabled - and testing.requires.reflect_indexes_with_ascdesc.enabled - ): - Index("noncol_idx_nopk", noncol_idx_test_nopk.c.q.desc()) - Index("noncol_idx_pk", noncol_idx_test_pk.c.q.desc()) - - if testing.requires.view_column_reflection.enabled: - cls.define_views(metadata, schema) - if not schema and testing.requires.temp_table_reflection.enabled: - cls.define_temp_tables(metadata) - - @classmethod - def temp_table_name(cls): - return get_temp_table_name( - config, config.db, f"user_tmp_{config.ident}" - ) - - @classmethod - def define_temp_tables(cls, metadata): - kw = temp_table_keyword_args(config, config.db) - table_name = cls.temp_table_name() - user_tmp = Table( - table_name, - metadata, - Column("id", sa.INT, primary_key=True), - Column("name", sa.VARCHAR(50)), - Column("foo", sa.INT), - # disambiguate temp table unique constraint names. this is - # pretty arbitrary for a generic dialect however we are doing - # it to suit SQL Server which will produce name conflicts for - # unique constraints created against temp tables in different - # databases. - # https://www.arbinada.com/en/node/1645 - sa.UniqueConstraint("name", name=f"user_tmp_uq_{config.ident}"), - sa.Index("user_tmp_ix", "foo"), - **kw, - ) - if ( - testing.requires.view_reflection.enabled - and testing.requires.temporary_views.enabled - ): - event.listen( - user_tmp, - "after_create", - DDL( - "create temporary view user_tmp_v as " - "select * from user_tmp_%s" % config.ident - ), - ) - event.listen(user_tmp, "before_drop", DDL("drop view user_tmp_v")) - - @classmethod - def define_views(cls, metadata, schema): - if testing.requires.materialized_views.enabled: - materialized = {"dingalings"} - else: - materialized = set() - for table_name in ("users", "email_addresses", "dingalings"): - fullname = table_name - if schema: - fullname = f"{schema}.{table_name}" - view_name = fullname + "_v" - prefix = "MATERIALIZED " if table_name in materialized else "" - query = ( - f"CREATE {prefix}VIEW {view_name} AS SELECT * FROM {fullname}" - ) - - event.listen(metadata, "after_create", DDL(query)) - if table_name in materialized: - index_name = "mat_index" - if schema and testing.against("oracle"): - index_name = f"{schema}.{index_name}" - idx = f"CREATE INDEX {index_name} ON {view_name}(data)" - event.listen(metadata, "after_create", DDL(idx)) - event.listen( - metadata, "before_drop", DDL(f"DROP {prefix}VIEW {view_name}") - ) - - def _resolve_kind(self, kind, tables, views, materialized): - res = {} - if ObjectKind.TABLE in kind: - res.update(tables) - if ObjectKind.VIEW in kind: - res.update(views) - if ObjectKind.MATERIALIZED_VIEW in kind: - res.update(materialized) - return res - - def _resolve_views(self, views, materialized): - if not testing.requires.view_column_reflection.enabled: - materialized.clear() - views.clear() - elif not testing.requires.materialized_views.enabled: - views.update(materialized) - materialized.clear() - - def _resolve_names(self, schema, scope, filter_names, values): - scope_filter = lambda _: True # noqa: E731 - if scope is ObjectScope.DEFAULT: - scope_filter = lambda k: "tmp" not in k[1] # noqa: E731 - if scope is ObjectScope.TEMPORARY: - scope_filter = lambda k: "tmp" in k[1] # noqa: E731 - - removed = { - None: {"remote_table", "remote_table_2"}, - testing.config.test_schema: { - "local_table", - "noncol_idx_test_nopk", - "noncol_idx_test_pk", - "user_tmp_v", - self.temp_table_name(), - }, - } - if not testing.requires.cross_schema_fk_reflection.enabled: - removed[None].add("local_table") - removed[testing.config.test_schema].update( - ["remote_table", "remote_table_2"] - ) - if not testing.requires.index_reflection.enabled: - removed[None].update( - ["noncol_idx_test_nopk", "noncol_idx_test_pk"] - ) - if ( - not testing.requires.temp_table_reflection.enabled - or not testing.requires.temp_table_names.enabled - ): - removed[None].update(["user_tmp_v", self.temp_table_name()]) - if not testing.requires.temporary_views.enabled: - removed[None].update(["user_tmp_v"]) - - res = { - k: v - for k, v in values.items() - if scope_filter(k) - and k[1] not in removed[schema] - and (not filter_names or k[1] in filter_names) - } - return res - - def exp_options( - self, - schema=None, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - filter_names=None, - ): - materialized = {(schema, "dingalings_v"): mock.ANY} - views = { - (schema, "email_addresses_v"): mock.ANY, - (schema, "users_v"): mock.ANY, - (schema, "user_tmp_v"): mock.ANY, - } - self._resolve_views(views, materialized) - tables = { - (schema, "users"): mock.ANY, - (schema, "dingalings"): mock.ANY, - (schema, "email_addresses"): mock.ANY, - (schema, "comment_test"): mock.ANY, - (schema, "no_constraints"): mock.ANY, - (schema, "local_table"): mock.ANY, - (schema, "remote_table"): mock.ANY, - (schema, "remote_table_2"): mock.ANY, - (schema, "noncol_idx_test_nopk"): mock.ANY, - (schema, "noncol_idx_test_pk"): mock.ANY, - (schema, self.temp_table_name()): mock.ANY, - } - res = self._resolve_kind(kind, tables, views, materialized) - res = self._resolve_names(schema, scope, filter_names, res) - return res - - def exp_comments( - self, - schema=None, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - filter_names=None, - ): - empty = {"text": None} - materialized = {(schema, "dingalings_v"): empty} - views = { - (schema, "email_addresses_v"): empty, - (schema, "users_v"): empty, - (schema, "user_tmp_v"): empty, - } - self._resolve_views(views, materialized) - tables = { - (schema, "users"): empty, - (schema, "dingalings"): empty, - (schema, "email_addresses"): empty, - (schema, "comment_test"): { - "text": r"""the test % ' " \ table comment""" - }, - (schema, "no_constraints"): { - "text": "no\nconstraints\rhas\fescaped\vcomment" - }, - (schema, "local_table"): empty, - (schema, "remote_table"): empty, - (schema, "remote_table_2"): empty, - (schema, "noncol_idx_test_nopk"): empty, - (schema, "noncol_idx_test_pk"): empty, - (schema, self.temp_table_name()): empty, - } - res = self._resolve_kind(kind, tables, views, materialized) - res = self._resolve_names(schema, scope, filter_names, res) - return res - - def exp_columns( - self, - schema=None, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - filter_names=None, - ): - def col( - name, auto=False, default=mock.ANY, comment=None, nullable=True - ): - res = { - "name": name, - "autoincrement": auto, - "type": mock.ANY, - "default": default, - "comment": comment, - "nullable": nullable, - } - if auto == "omit": - res.pop("autoincrement") - return res - - def pk(name, **kw): - kw = {"auto": True, "default": mock.ANY, "nullable": False, **kw} - return col(name, **kw) - - materialized = { - (schema, "dingalings_v"): [ - col("dingaling_id", auto="omit", nullable=mock.ANY), - col("address_id"), - col("id_user"), - col("data"), - ] - } - views = { - (schema, "email_addresses_v"): [ - col("address_id", auto="omit", nullable=mock.ANY), - col("remote_user_id"), - col("email_address"), - ], - (schema, "users_v"): [ - col("user_id", auto="omit", nullable=mock.ANY), - col("test1", nullable=mock.ANY), - col("test2", nullable=mock.ANY), - col("parent_user_id"), - ], - (schema, "user_tmp_v"): [ - col("id", auto="omit", nullable=mock.ANY), - col("name"), - col("foo"), - ], - } - self._resolve_views(views, materialized) - tables = { - (schema, "users"): [ - pk("user_id"), - col("test1", nullable=False), - col("test2", nullable=False), - col("parent_user_id"), - ], - (schema, "dingalings"): [ - pk("dingaling_id"), - col("address_id"), - col("id_user"), - col("data"), - ], - (schema, "email_addresses"): [ - pk("address_id"), - col("remote_user_id"), - col("email_address"), - ], - (schema, "comment_test"): [ - pk("id", comment="id comment"), - col("data", comment="data % comment"), - col( - "d2", - comment=r"""Comment types type speedily ' " \ '' Fun!""", - ), - col("d3", comment="Comment\nwith\rescapes"), - ], - (schema, "no_constraints"): [col("data")], - (schema, "local_table"): [pk("id"), col("data"), col("remote_id")], - (schema, "remote_table"): [pk("id"), col("local_id"), col("data")], - (schema, "remote_table_2"): [pk("id"), col("data")], - (schema, "noncol_idx_test_nopk"): [col("q")], - (schema, "noncol_idx_test_pk"): [pk("id"), col("q")], - (schema, self.temp_table_name()): [ - pk("id"), - col("name"), - col("foo"), - ], - } - res = self._resolve_kind(kind, tables, views, materialized) - res = self._resolve_names(schema, scope, filter_names, res) - return res - - @property - def _required_column_keys(self): - return {"name", "type", "nullable", "default"} - - def exp_pks( - self, - schema=None, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - filter_names=None, - ): - def pk(*cols, name=mock.ANY, comment=None): - return { - "constrained_columns": list(cols), - "name": name, - "comment": comment, - } - - empty = pk(name=None) - if testing.requires.materialized_views_reflect_pk.enabled: - materialized = {(schema, "dingalings_v"): pk("dingaling_id")} - else: - materialized = {(schema, "dingalings_v"): empty} - views = { - (schema, "email_addresses_v"): empty, - (schema, "users_v"): empty, - (schema, "user_tmp_v"): empty, - } - self._resolve_views(views, materialized) - tables = { - (schema, "users"): pk("user_id"), - (schema, "dingalings"): pk("dingaling_id"), - (schema, "email_addresses"): pk( - "address_id", name="email_ad_pk", comment="ea pk comment" - ), - (schema, "comment_test"): pk("id"), - (schema, "no_constraints"): empty, - (schema, "local_table"): pk("id"), - (schema, "remote_table"): pk("id"), - (schema, "remote_table_2"): pk("id"), - (schema, "noncol_idx_test_nopk"): empty, - (schema, "noncol_idx_test_pk"): pk("id"), - (schema, self.temp_table_name()): pk("id"), - } - if not testing.requires.reflects_pk_names.enabled: - for val in tables.values(): - if val["name"] is not None: - val["name"] = mock.ANY - res = self._resolve_kind(kind, tables, views, materialized) - res = self._resolve_names(schema, scope, filter_names, res) - return res - - @property - def _required_pk_keys(self): - return {"name", "constrained_columns"} - - def exp_fks( - self, - schema=None, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - filter_names=None, - ): - class tt: - def __eq__(self, other): - return ( - other is None - or config.db.dialect.default_schema_name == other - ) - - def fk( - cols, - ref_col, - ref_table, - ref_schema=schema, - name=mock.ANY, - comment=None, - ): - return { - "constrained_columns": cols, - "referred_columns": ref_col, - "name": name, - "options": mock.ANY, - "referred_schema": ( - ref_schema if ref_schema is not None else tt() - ), - "referred_table": ref_table, - "comment": comment, - } - - materialized = {(schema, "dingalings_v"): []} - views = { - (schema, "email_addresses_v"): [], - (schema, "users_v"): [], - (schema, "user_tmp_v"): [], - } - self._resolve_views(views, materialized) - tables = { - (schema, "users"): [ - fk(["parent_user_id"], ["user_id"], "users", name="user_id_fk") - ], - (schema, "dingalings"): [ - fk(["id_user"], ["user_id"], "users"), - fk( - ["address_id"], - ["address_id"], - "email_addresses", - name="zz_email_add_id_fg", - comment="di fk comment", - ), - ], - (schema, "email_addresses"): [ - fk(["remote_user_id"], ["user_id"], "users") - ], - (schema, "comment_test"): [], - (schema, "no_constraints"): [], - (schema, "local_table"): [ - fk( - ["remote_id"], - ["id"], - "remote_table_2", - ref_schema=config.test_schema, - ) - ], - (schema, "remote_table"): [ - fk(["local_id"], ["id"], "local_table", ref_schema=None) - ], - (schema, "remote_table_2"): [], - (schema, "noncol_idx_test_nopk"): [], - (schema, "noncol_idx_test_pk"): [], - (schema, self.temp_table_name()): [], - } - if not testing.requires.self_referential_foreign_keys.enabled: - tables[(schema, "users")].clear() - if not testing.requires.named_constraints.enabled: - for vals in tables.values(): - for val in vals: - if val["name"] is not mock.ANY: - val["name"] = mock.ANY - - res = self._resolve_kind(kind, tables, views, materialized) - res = self._resolve_names(schema, scope, filter_names, res) - return res - - @property - def _required_fk_keys(self): - return { - "name", - "constrained_columns", - "referred_schema", - "referred_table", - "referred_columns", - } - - def exp_indexes( - self, - schema=None, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - filter_names=None, - ): - def idx( - *cols, - name, - unique=False, - column_sorting=None, - duplicates=False, - fk=False, - ): - fk_req = testing.requires.foreign_keys_reflect_as_index - dup_req = testing.requires.unique_constraints_reflect_as_index - sorting_expression = ( - testing.requires.reflect_indexes_with_ascdesc_as_expression - ) - - if (fk and not fk_req.enabled) or ( - duplicates and not dup_req.enabled - ): - return () - res = { - "unique": unique, - "column_names": list(cols), - "name": name, - "dialect_options": mock.ANY, - "include_columns": [], - } - if column_sorting: - res["column_sorting"] = column_sorting - if sorting_expression.enabled: - res["expressions"] = orig = res["column_names"] - res["column_names"] = [ - None if c in column_sorting else c for c in orig - ] - - if duplicates: - res["duplicates_constraint"] = name - return [res] - - materialized = {(schema, "dingalings_v"): []} - views = { - (schema, "email_addresses_v"): [], - (schema, "users_v"): [], - (schema, "user_tmp_v"): [], - } - self._resolve_views(views, materialized) - if materialized: - materialized[(schema, "dingalings_v")].extend( - idx("data", name="mat_index") - ) - tables = { - (schema, "users"): [ - *idx("parent_user_id", name="user_id_fk", fk=True), - *idx("user_id", "test2", "test1", name="users_all_idx"), - *idx("test1", "test2", name="users_t_idx", unique=True), - ], - (schema, "dingalings"): [ - *idx("data", name=mock.ANY, unique=True, duplicates=True), - *idx("id_user", name=mock.ANY, fk=True), - *idx( - "address_id", - "dingaling_id", - name="zz_dingalings_multiple", - unique=True, - duplicates=True, - ), - ], - (schema, "email_addresses"): [ - *idx("email_address", name=mock.ANY), - *idx("remote_user_id", name=mock.ANY, fk=True), - ], - (schema, "comment_test"): [], - (schema, "no_constraints"): [], - (schema, "local_table"): [ - *idx("remote_id", name=mock.ANY, fk=True) - ], - (schema, "remote_table"): [ - *idx("local_id", name=mock.ANY, fk=True) - ], - (schema, "remote_table_2"): [], - (schema, "noncol_idx_test_nopk"): [ - *idx( - "q", - name="noncol_idx_nopk", - column_sorting={"q": ("desc",)}, - ) - ], - (schema, "noncol_idx_test_pk"): [ - *idx( - "q", name="noncol_idx_pk", column_sorting={"q": ("desc",)} - ) - ], - (schema, self.temp_table_name()): [ - *idx("foo", name="user_tmp_ix"), - *idx( - "name", - name=f"user_tmp_uq_{config.ident}", - duplicates=True, - unique=True, - ), - ], - } - if ( - not testing.requires.indexes_with_ascdesc.enabled - or not testing.requires.reflect_indexes_with_ascdesc.enabled - ): - tables[(schema, "noncol_idx_test_nopk")].clear() - tables[(schema, "noncol_idx_test_pk")].clear() - res = self._resolve_kind(kind, tables, views, materialized) - res = self._resolve_names(schema, scope, filter_names, res) - return res - - @property - def _required_index_keys(self): - return {"name", "column_names", "unique"} - - def exp_ucs( - self, - schema=None, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - filter_names=None, - all_=False, - ): - def uc( - *cols, name, duplicates_index=None, is_index=False, comment=None - ): - req = testing.requires.unique_index_reflect_as_unique_constraints - if is_index and not req.enabled: - return () - res = { - "column_names": list(cols), - "name": name, - "comment": comment, - } - if duplicates_index: - res["duplicates_index"] = duplicates_index - return [res] - - materialized = {(schema, "dingalings_v"): []} - views = { - (schema, "email_addresses_v"): [], - (schema, "users_v"): [], - (schema, "user_tmp_v"): [], - } - self._resolve_views(views, materialized) - tables = { - (schema, "users"): [ - *uc( - "test1", - "test2", - name="users_t_idx", - duplicates_index="users_t_idx", - is_index=True, - ) - ], - (schema, "dingalings"): [ - *uc("data", name=mock.ANY, duplicates_index=mock.ANY), - *uc( - "address_id", - "dingaling_id", - name="zz_dingalings_multiple", - duplicates_index="zz_dingalings_multiple", - comment="di unique comment", - ), - ], - (schema, "email_addresses"): [], - (schema, "comment_test"): [], - (schema, "no_constraints"): [], - (schema, "local_table"): [], - (schema, "remote_table"): [], - (schema, "remote_table_2"): [], - (schema, "noncol_idx_test_nopk"): [], - (schema, "noncol_idx_test_pk"): [], - (schema, self.temp_table_name()): [ - *uc("name", name=f"user_tmp_uq_{config.ident}") - ], - } - if all_: - return {**materialized, **views, **tables} - else: - res = self._resolve_kind(kind, tables, views, materialized) - res = self._resolve_names(schema, scope, filter_names, res) - return res - - @property - def _required_unique_cst_keys(self): - return {"name", "column_names"} - - def exp_ccs( - self, - schema=None, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - filter_names=None, - ): - class tt(str): - def __eq__(self, other): - res = ( - other.lower() - .replace("(", "") - .replace(")", "") - .replace("`", "") - ) - return self in res - - def cc(text, name, comment=None): - return {"sqltext": tt(text), "name": name, "comment": comment} - - # print({1: "test2 > (0)::double precision"} == {1: tt("test2 > 0")}) - # assert 0 - materialized = {(schema, "dingalings_v"): []} - views = { - (schema, "email_addresses_v"): [], - (schema, "users_v"): [], - (schema, "user_tmp_v"): [], - } - self._resolve_views(views, materialized) - tables = { - (schema, "users"): [ - cc("test2 <= 1000", mock.ANY), - cc( - "test2 > 0", - "zz_test2_gt_zero", - comment="users check constraint", - ), - ], - (schema, "dingalings"): [ - cc( - "address_id > 0 and address_id < 1000", - name="address_id_gt_zero", - ), - ], - (schema, "email_addresses"): [], - (schema, "comment_test"): [], - (schema, "no_constraints"): [], - (schema, "local_table"): [], - (schema, "remote_table"): [], - (schema, "remote_table_2"): [], - (schema, "noncol_idx_test_nopk"): [], - (schema, "noncol_idx_test_pk"): [], - (schema, self.temp_table_name()): [], - } - res = self._resolve_kind(kind, tables, views, materialized) - res = self._resolve_names(schema, scope, filter_names, res) - return res - - @property - def _required_cc_keys(self): - return {"name", "sqltext"} - - @testing.requires.schema_reflection - def test_get_schema_names(self, connection): - insp = inspect(connection) - - is_true(testing.config.test_schema in insp.get_schema_names()) - - @testing.requires.schema_reflection - def test_has_schema(self, connection): - insp = inspect(connection) - - is_true(insp.has_schema(testing.config.test_schema)) - is_false(insp.has_schema("sa_fake_schema_foo")) - - @testing.requires.schema_reflection - def test_get_schema_names_w_translate_map(self, connection): - """test #7300""" - - connection = connection.execution_options( - schema_translate_map={ - "foo": "bar", - BLANK_SCHEMA: testing.config.test_schema, - } - ) - insp = inspect(connection) - - is_true(testing.config.test_schema in insp.get_schema_names()) - - @testing.requires.schema_reflection - def test_has_schema_w_translate_map(self, connection): - connection = connection.execution_options( - schema_translate_map={ - "foo": "bar", - BLANK_SCHEMA: testing.config.test_schema, - } - ) - insp = inspect(connection) - - is_true(insp.has_schema(testing.config.test_schema)) - is_false(insp.has_schema("sa_fake_schema_foo")) - - @testing.requires.schema_reflection - @testing.requires.schema_create_delete - def test_schema_cache(self, connection): - insp = inspect(connection) - - is_false("foo_bar" in insp.get_schema_names()) - is_false(insp.has_schema("foo_bar")) - connection.execute(DDL("CREATE SCHEMA foo_bar")) - try: - is_false("foo_bar" in insp.get_schema_names()) - is_false(insp.has_schema("foo_bar")) - insp.clear_cache() - is_true("foo_bar" in insp.get_schema_names()) - is_true(insp.has_schema("foo_bar")) - finally: - connection.execute(DDL("DROP SCHEMA foo_bar")) - - @testing.requires.schema_reflection - def test_dialect_initialize(self): - engine = engines.testing_engine() - inspect(engine) - assert hasattr(engine.dialect, "default_schema_name") - - @testing.requires.schema_reflection - def test_get_default_schema_name(self, connection): - insp = inspect(connection) - eq_(insp.default_schema_name, connection.dialect.default_schema_name) - - @testing.combinations( - None, - ("foreign_key", testing.requires.foreign_key_constraint_reflection), - argnames="order_by", - ) - @testing.combinations( - (True, testing.requires.schemas), False, argnames="use_schema" - ) - def test_get_table_names(self, connection, order_by, use_schema): - if use_schema: - schema = config.test_schema - else: - schema = None - - _ignore_tables = { - "comment_test", - "noncol_idx_test_pk", - "noncol_idx_test_nopk", - "local_table", - "remote_table", - "remote_table_2", - "no_constraints", - } - - insp = inspect(connection) - - if order_by: - tables = [ - rec[0] - for rec in insp.get_sorted_table_and_fkc_names(schema) - if rec[0] - ] - else: - tables = insp.get_table_names(schema) - table_names = [t for t in tables if t not in _ignore_tables] - - if order_by == "foreign_key": - answer = ["users", "email_addresses", "dingalings"] - eq_(table_names, answer) - else: - answer = ["dingalings", "email_addresses", "users"] - eq_(sorted(table_names), answer) - - @testing.combinations( - (True, testing.requires.schemas), False, argnames="use_schema" - ) - def test_get_view_names(self, connection, use_schema): - insp = inspect(connection) - if use_schema: - schema = config.test_schema - else: - schema = None - table_names = insp.get_view_names(schema) - if testing.requires.materialized_views.enabled: - eq_(sorted(table_names), ["email_addresses_v", "users_v"]) - eq_(insp.get_materialized_view_names(schema), ["dingalings_v"]) - else: - answer = ["dingalings_v", "email_addresses_v", "users_v"] - eq_(sorted(table_names), answer) - - @testing.requires.temp_table_names - def test_get_temp_table_names(self, connection): - insp = inspect(connection) - temp_table_names = insp.get_temp_table_names() - eq_(sorted(temp_table_names), [f"user_tmp_{config.ident}"]) - - @testing.requires.view_reflection - @testing.requires.temporary_views - def test_get_temp_view_names(self, connection): - insp = inspect(connection) - temp_table_names = insp.get_temp_view_names() - eq_(sorted(temp_table_names), ["user_tmp_v"]) - - @testing.requires.comment_reflection - def test_get_comments(self, connection): - self._test_get_comments(connection) - - @testing.requires.comment_reflection - @testing.requires.schemas - def test_get_comments_with_schema(self, connection): - self._test_get_comments(connection, testing.config.test_schema) - - def _test_get_comments(self, connection, schema=None): - insp = inspect(connection) - exp = self.exp_comments(schema=schema) - eq_( - insp.get_table_comment("comment_test", schema=schema), - exp[(schema, "comment_test")], - ) - - eq_( - insp.get_table_comment("users", schema=schema), - exp[(schema, "users")], - ) - - eq_( - insp.get_table_comment("comment_test", schema=schema), - exp[(schema, "comment_test")], - ) - - no_cst = self.tables.no_constraints.name - eq_( - insp.get_table_comment(no_cst, schema=schema), - exp[(schema, no_cst)], - ) - - @testing.combinations( - (False, False), - (False, True, testing.requires.schemas), - (True, False, testing.requires.view_reflection), - ( - True, - True, - testing.requires.schemas + testing.requires.view_reflection, - ), - argnames="use_views,use_schema", - ) - def test_get_columns(self, connection, use_views, use_schema): - if use_schema: - schema = config.test_schema - else: - schema = None - - users, addresses = (self.tables.users, self.tables.email_addresses) - if use_views: - table_names = ["users_v", "email_addresses_v", "dingalings_v"] - else: - table_names = ["users", "email_addresses"] - - insp = inspect(connection) - for table_name, table in zip(table_names, (users, addresses)): - schema_name = schema - cols = insp.get_columns(table_name, schema=schema_name) - is_true(len(cols) > 0, len(cols)) - - # should be in order - - for i, col in enumerate(table.columns): - eq_(col.name, cols[i]["name"]) - ctype = cols[i]["type"].__class__ - ctype_def = col.type - if isinstance(ctype_def, sa.types.TypeEngine): - ctype_def = ctype_def.__class__ - - # Oracle returns Date for DateTime. - - if testing.against("oracle") and ctype_def in ( - sql_types.Date, - sql_types.DateTime, - ): - ctype_def = sql_types.Date - - # assert that the desired type and return type share - # a base within one of the generic types. - - is_true( - len( - set(ctype.__mro__) - .intersection(ctype_def.__mro__) - .intersection( - [ - sql_types.Integer, - sql_types.Numeric, - sql_types.DateTime, - sql_types.Date, - sql_types.Time, - sql_types.String, - sql_types._Binary, - ] - ) - ) - > 0, - "%s(%s), %s(%s)" - % (col.name, col.type, cols[i]["name"], ctype), - ) - - if not col.primary_key: - assert cols[i]["default"] is None - - # The case of a table with no column - # is tested below in TableNoColumnsTest - - @testing.requires.temp_table_reflection - def test_reflect_table_temp_table(self, connection): - table_name = self.temp_table_name() - user_tmp = self.tables[table_name] - - reflected_user_tmp = Table( - table_name, MetaData(), autoload_with=connection - ) - self.assert_tables_equal( - user_tmp, reflected_user_tmp, strict_constraints=False - ) - - @testing.requires.temp_table_reflection - def test_get_temp_table_columns(self, connection): - table_name = self.temp_table_name() - user_tmp = self.tables[table_name] - insp = inspect(connection) - cols = insp.get_columns(table_name) - is_true(len(cols) > 0, len(cols)) - - for i, col in enumerate(user_tmp.columns): - eq_(col.name, cols[i]["name"]) - - @testing.requires.temp_table_reflection - @testing.requires.view_column_reflection - @testing.requires.temporary_views - def test_get_temp_view_columns(self, connection): - insp = inspect(connection) - cols = insp.get_columns("user_tmp_v") - eq_([col["name"] for col in cols], ["id", "name", "foo"]) - - @testing.combinations( - (False,), (True, testing.requires.schemas), argnames="use_schema" - ) - @testing.requires.primary_key_constraint_reflection - def test_get_pk_constraint(self, connection, use_schema): - if use_schema: - schema = testing.config.test_schema - else: - schema = None - - users, addresses = self.tables.users, self.tables.email_addresses - insp = inspect(connection) - exp = self.exp_pks(schema=schema) - - users_cons = insp.get_pk_constraint(users.name, schema=schema) - self._check_list( - [users_cons], [exp[(schema, users.name)]], self._required_pk_keys - ) - - addr_cons = insp.get_pk_constraint(addresses.name, schema=schema) - exp_cols = exp[(schema, addresses.name)]["constrained_columns"] - eq_(addr_cons["constrained_columns"], exp_cols) - - with testing.requires.reflects_pk_names.fail_if(): - eq_(addr_cons["name"], "email_ad_pk") - - no_cst = self.tables.no_constraints.name - self._check_list( - [insp.get_pk_constraint(no_cst, schema=schema)], - [exp[(schema, no_cst)]], - self._required_pk_keys, - ) - - @testing.combinations( - (False,), (True, testing.requires.schemas), argnames="use_schema" - ) - @testing.requires.foreign_key_constraint_reflection - def test_get_foreign_keys(self, connection, use_schema): - if use_schema: - schema = config.test_schema - else: - schema = None - - users, addresses = (self.tables.users, self.tables.email_addresses) - insp = inspect(connection) - expected_schema = schema - # users - - if testing.requires.self_referential_foreign_keys.enabled: - users_fkeys = insp.get_foreign_keys(users.name, schema=schema) - fkey1 = users_fkeys[0] - - with testing.requires.named_constraints.fail_if(): - eq_(fkey1["name"], "user_id_fk") - - eq_(fkey1["referred_schema"], expected_schema) - eq_(fkey1["referred_table"], users.name) - eq_(fkey1["referred_columns"], ["user_id"]) - eq_(fkey1["constrained_columns"], ["parent_user_id"]) - - # addresses - addr_fkeys = insp.get_foreign_keys(addresses.name, schema=schema) - fkey1 = addr_fkeys[0] - - with testing.requires.implicitly_named_constraints.fail_if(): - is_true(fkey1["name"] is not None) - - eq_(fkey1["referred_schema"], expected_schema) - eq_(fkey1["referred_table"], users.name) - eq_(fkey1["referred_columns"], ["user_id"]) - eq_(fkey1["constrained_columns"], ["remote_user_id"]) - - no_cst = self.tables.no_constraints.name - eq_(insp.get_foreign_keys(no_cst, schema=schema), []) - - @testing.requires.cross_schema_fk_reflection - @testing.requires.schemas - def test_get_inter_schema_foreign_keys(self, connection): - local_table, remote_table, remote_table_2 = self.tables( - "%s.local_table" % connection.dialect.default_schema_name, - "%s.remote_table" % testing.config.test_schema, - "%s.remote_table_2" % testing.config.test_schema, - ) - - insp = inspect(connection) - - local_fkeys = insp.get_foreign_keys(local_table.name) - eq_(len(local_fkeys), 1) - - fkey1 = local_fkeys[0] - eq_(fkey1["referred_schema"], testing.config.test_schema) - eq_(fkey1["referred_table"], remote_table_2.name) - eq_(fkey1["referred_columns"], ["id"]) - eq_(fkey1["constrained_columns"], ["remote_id"]) - - remote_fkeys = insp.get_foreign_keys( - remote_table.name, schema=testing.config.test_schema - ) - eq_(len(remote_fkeys), 1) - - fkey2 = remote_fkeys[0] - - is_true( - fkey2["referred_schema"] - in ( - None, - connection.dialect.default_schema_name, - ) - ) - eq_(fkey2["referred_table"], local_table.name) - eq_(fkey2["referred_columns"], ["id"]) - eq_(fkey2["constrained_columns"], ["local_id"]) - - @testing.combinations( - (False,), (True, testing.requires.schemas), argnames="use_schema" - ) - @testing.requires.index_reflection - def test_get_indexes(self, connection, use_schema): - if use_schema: - schema = config.test_schema - else: - schema = None - - # The database may decide to create indexes for foreign keys, etc. - # so there may be more indexes than expected. - insp = inspect(connection) - indexes = insp.get_indexes("users", schema=schema) - exp = self.exp_indexes(schema=schema) - self._check_list( - indexes, exp[(schema, "users")], self._required_index_keys - ) - - no_cst = self.tables.no_constraints.name - self._check_list( - insp.get_indexes(no_cst, schema=schema), - exp[(schema, no_cst)], - self._required_index_keys, - ) - - @testing.combinations( - ("noncol_idx_test_nopk", "noncol_idx_nopk"), - ("noncol_idx_test_pk", "noncol_idx_pk"), - argnames="tname,ixname", - ) - @testing.requires.index_reflection - @testing.requires.indexes_with_ascdesc - @testing.requires.reflect_indexes_with_ascdesc - def test_get_noncol_index(self, connection, tname, ixname): - insp = inspect(connection) - indexes = insp.get_indexes(tname) - # reflecting an index that has "x DESC" in it as the column. - # the DB may or may not give us "x", but make sure we get the index - # back, it has a name, it's connected to the table. - expected_indexes = self.exp_indexes()[(None, tname)] - self._check_list(indexes, expected_indexes, self._required_index_keys) - - t = Table(tname, MetaData(), autoload_with=connection) - eq_(len(t.indexes), 1) - is_(list(t.indexes)[0].table, t) - eq_(list(t.indexes)[0].name, ixname) - - @testing.requires.temp_table_reflection - @testing.requires.unique_constraint_reflection - def test_get_temp_table_unique_constraints(self, connection): - insp = inspect(connection) - name = self.temp_table_name() - reflected = insp.get_unique_constraints(name) - exp = self.exp_ucs(all_=True)[(None, name)] - self._check_list(reflected, exp, self._required_index_keys) - - @testing.requires.temp_table_reflect_indexes - def test_get_temp_table_indexes(self, connection): - insp = inspect(connection) - table_name = self.temp_table_name() - indexes = insp.get_indexes(table_name) - for ind in indexes: - ind.pop("dialect_options", None) - expected = [ - {"unique": False, "column_names": ["foo"], "name": "user_tmp_ix"} - ] - if testing.requires.index_reflects_included_columns.enabled: - expected[0]["include_columns"] = [] - eq_( - [idx for idx in indexes if idx["name"] == "user_tmp_ix"], - expected, - ) - - @testing.combinations( - (True, testing.requires.schemas), (False,), argnames="use_schema" - ) - @testing.requires.unique_constraint_reflection - def test_get_unique_constraints(self, metadata, connection, use_schema): - # SQLite dialect needs to parse the names of the constraints - # separately from what it gets from PRAGMA index_list(), and - # then matches them up. so same set of column_names in two - # constraints will confuse it. Perhaps we should no longer - # bother with index_list() here since we have the whole - # CREATE TABLE? - - if use_schema: - schema = config.test_schema - else: - schema = None - uniques = sorted( - [ - {"name": "unique_a", "column_names": ["a"]}, - {"name": "unique_a_b_c", "column_names": ["a", "b", "c"]}, - {"name": "unique_c_a_b", "column_names": ["c", "a", "b"]}, - {"name": "unique_asc_key", "column_names": ["asc", "key"]}, - {"name": "i.have.dots", "column_names": ["b"]}, - {"name": "i have spaces", "column_names": ["c"]}, - ], - key=operator.itemgetter("name"), - ) - table = Table( - "testtbl", - metadata, - Column("a", sa.String(20)), - Column("b", sa.String(30)), - Column("c", sa.Integer), - # reserved identifiers - Column("asc", sa.String(30)), - Column("key", sa.String(30)), - schema=schema, - ) - for uc in uniques: - table.append_constraint( - sa.UniqueConstraint(*uc["column_names"], name=uc["name"]) - ) - table.create(connection) - - insp = inspect(connection) - reflected = sorted( - insp.get_unique_constraints("testtbl", schema=schema), - key=operator.itemgetter("name"), - ) - - names_that_duplicate_index = set() - - eq_(len(uniques), len(reflected)) - - for orig, refl in zip(uniques, reflected): - # Different dialects handle duplicate index and constraints - # differently, so ignore this flag - dupe = refl.pop("duplicates_index", None) - if dupe: - names_that_duplicate_index.add(dupe) - eq_(refl.pop("comment", None), None) - eq_(orig, refl) - - reflected_metadata = MetaData() - reflected = Table( - "testtbl", - reflected_metadata, - autoload_with=connection, - schema=schema, - ) - - # test "deduplicates for index" logic. MySQL and Oracle - # "unique constraints" are actually unique indexes (with possible - # exception of a unique that is a dupe of another one in the case - # of Oracle). make sure # they aren't duplicated. - idx_names = {idx.name for idx in reflected.indexes} - uq_names = { - uq.name - for uq in reflected.constraints - if isinstance(uq, sa.UniqueConstraint) - }.difference(["unique_c_a_b"]) - - assert not idx_names.intersection(uq_names) - if names_that_duplicate_index: - eq_(names_that_duplicate_index, idx_names) - eq_(uq_names, set()) - - no_cst = self.tables.no_constraints.name - eq_(insp.get_unique_constraints(no_cst, schema=schema), []) - - @testing.requires.view_reflection - @testing.combinations( - (False,), (True, testing.requires.schemas), argnames="use_schema" - ) - def test_get_view_definition(self, connection, use_schema): - if use_schema: - schema = config.test_schema - else: - schema = None - insp = inspect(connection) - for view in ["users_v", "email_addresses_v", "dingalings_v"]: - v = insp.get_view_definition(view, schema=schema) - is_true(bool(v)) - - @testing.requires.view_reflection - def test_get_view_definition_does_not_exist(self, connection): - insp = inspect(connection) - with expect_raises(NoSuchTableError): - insp.get_view_definition("view_does_not_exist") - with expect_raises(NoSuchTableError): - insp.get_view_definition("users") # a table - - @testing.requires.table_reflection - def test_autoincrement_col(self, connection): - """test that 'autoincrement' is reflected according to sqla's policy. - - Don't mark this test as unsupported for any backend ! - - (technically it fails with MySQL InnoDB since "id" comes before "id2") - - A backend is better off not returning "autoincrement" at all, - instead of potentially returning "False" for an auto-incrementing - primary key column. - - """ - - insp = inspect(connection) - - for tname, cname in [ - ("users", "user_id"), - ("email_addresses", "address_id"), - ("dingalings", "dingaling_id"), - ]: - cols = insp.get_columns(tname) - id_ = {c["name"]: c for c in cols}[cname] - assert id_.get("autoincrement", True) - - @testing.combinations( - (True, testing.requires.schemas), (False,), argnames="use_schema" - ) - def test_get_table_options(self, use_schema): - insp = inspect(config.db) - schema = config.test_schema if use_schema else None - - if testing.requires.reflect_table_options.enabled: - res = insp.get_table_options("users", schema=schema) - is_true(isinstance(res, dict)) - # NOTE: can't really create a table with no option - res = insp.get_table_options("no_constraints", schema=schema) - is_true(isinstance(res, dict)) - else: - with expect_raises(NotImplementedError): - res = insp.get_table_options("users", schema=schema) - - @testing.combinations((True, testing.requires.schemas), False) - def test_multi_get_table_options(self, use_schema): - insp = inspect(config.db) - if testing.requires.reflect_table_options.enabled: - schema = config.test_schema if use_schema else None - res = insp.get_multi_table_options(schema=schema) - - exp = { - (schema, table): insp.get_table_options(table, schema=schema) - for table in insp.get_table_names(schema=schema) - } - eq_(res, exp) - else: - with expect_raises(NotImplementedError): - res = insp.get_multi_table_options() - - @testing.fixture - def get_multi_exp(self, connection): - def provide_fixture( - schema, scope, kind, use_filter, single_reflect_fn, exp_method - ): - insp = inspect(connection) - # call the reflection function at least once to avoid - # "Unexpected success" errors if the result is actually empty - # and NotImplementedError is not raised - single_reflect_fn(insp, "email_addresses") - kw = {"scope": scope, "kind": kind} - if schema: - schema = schema() - - filter_names = [] - - if ObjectKind.TABLE in kind: - filter_names.extend( - ["comment_test", "users", "does-not-exist"] - ) - if ObjectKind.VIEW in kind: - filter_names.extend(["email_addresses_v", "does-not-exist"]) - if ObjectKind.MATERIALIZED_VIEW in kind: - filter_names.extend(["dingalings_v", "does-not-exist"]) - - if schema: - kw["schema"] = schema - if use_filter: - kw["filter_names"] = filter_names - - exp = exp_method( - schema=schema, - scope=scope, - kind=kind, - filter_names=kw.get("filter_names"), - ) - kws = [kw] - if scope == ObjectScope.DEFAULT: - nkw = kw.copy() - nkw.pop("scope") - kws.append(nkw) - if kind == ObjectKind.TABLE: - nkw = kw.copy() - nkw.pop("kind") - kws.append(nkw) - - return inspect(connection), kws, exp - - return provide_fixture - - @testing.requires.reflect_table_options - @_multi_combination - def test_multi_get_table_options_tables( - self, get_multi_exp, schema, scope, kind, use_filter - ): - insp, kws, exp = get_multi_exp( - schema, - scope, - kind, - use_filter, - Inspector.get_table_options, - self.exp_options, - ) - for kw in kws: - insp.clear_cache() - result = insp.get_multi_table_options(**kw) - eq_(result, exp) - - @testing.requires.comment_reflection - @_multi_combination - def test_get_multi_table_comment( - self, get_multi_exp, schema, scope, kind, use_filter - ): - insp, kws, exp = get_multi_exp( - schema, - scope, - kind, - use_filter, - Inspector.get_table_comment, - self.exp_comments, - ) - for kw in kws: - insp.clear_cache() - eq_(insp.get_multi_table_comment(**kw), exp) - - def _check_expressions(self, result, exp, err_msg): - def _clean(text: str): - return re.sub(r"['\" ]", "", text).lower() - - if isinstance(exp, dict): - eq_({_clean(e): v for e, v in result.items()}, exp, err_msg) - else: - eq_([_clean(e) for e in result], exp, err_msg) - - def _check_list(self, result, exp, req_keys=None, msg=None): - if req_keys is None: - eq_(result, exp, msg) - else: - eq_(len(result), len(exp), msg) - for r, e in zip(result, exp): - for k in set(r) | set(e): - if k in req_keys or (k in r and k in e): - err_msg = f"{msg} - {k} - {r}" - if k in ("expressions", "column_sorting"): - self._check_expressions(r[k], e[k], err_msg) - else: - eq_(r[k], e[k], err_msg) - - def _check_table_dict(self, result, exp, req_keys=None, make_lists=False): - eq_(set(result.keys()), set(exp.keys())) - for k in result: - r, e = result[k], exp[k] - if make_lists: - r, e = [r], [e] - self._check_list(r, e, req_keys, k) - - @_multi_combination - def test_get_multi_columns( - self, get_multi_exp, schema, scope, kind, use_filter - ): - insp, kws, exp = get_multi_exp( - schema, - scope, - kind, - use_filter, - Inspector.get_columns, - self.exp_columns, - ) - - for kw in kws: - insp.clear_cache() - result = insp.get_multi_columns(**kw) - self._check_table_dict(result, exp, self._required_column_keys) - - @testing.requires.primary_key_constraint_reflection - @_multi_combination - def test_get_multi_pk_constraint( - self, get_multi_exp, schema, scope, kind, use_filter - ): - insp, kws, exp = get_multi_exp( - schema, - scope, - kind, - use_filter, - Inspector.get_pk_constraint, - self.exp_pks, - ) - for kw in kws: - insp.clear_cache() - result = insp.get_multi_pk_constraint(**kw) - self._check_table_dict( - result, exp, self._required_pk_keys, make_lists=True - ) - - def _adjust_sort(self, result, expected, key): - if not testing.requires.implicitly_named_constraints.enabled: - for obj in [result, expected]: - for val in obj.values(): - if len(val) > 1 and any( - v.get("name") in (None, mock.ANY) for v in val - ): - val.sort(key=key) - - @testing.requires.foreign_key_constraint_reflection - @_multi_combination - def test_get_multi_foreign_keys( - self, get_multi_exp, schema, scope, kind, use_filter - ): - insp, kws, exp = get_multi_exp( - schema, - scope, - kind, - use_filter, - Inspector.get_foreign_keys, - self.exp_fks, - ) - for kw in kws: - insp.clear_cache() - result = insp.get_multi_foreign_keys(**kw) - self._adjust_sort( - result, exp, lambda d: tuple(d["constrained_columns"]) - ) - self._check_table_dict(result, exp, self._required_fk_keys) - - @testing.requires.index_reflection - @_multi_combination - def test_get_multi_indexes( - self, get_multi_exp, schema, scope, kind, use_filter - ): - insp, kws, exp = get_multi_exp( - schema, - scope, - kind, - use_filter, - Inspector.get_indexes, - self.exp_indexes, - ) - for kw in kws: - insp.clear_cache() - result = insp.get_multi_indexes(**kw) - self._check_table_dict(result, exp, self._required_index_keys) - - @testing.requires.unique_constraint_reflection - @_multi_combination - def test_get_multi_unique_constraints( - self, get_multi_exp, schema, scope, kind, use_filter - ): - insp, kws, exp = get_multi_exp( - schema, - scope, - kind, - use_filter, - Inspector.get_unique_constraints, - self.exp_ucs, - ) - for kw in kws: - insp.clear_cache() - result = insp.get_multi_unique_constraints(**kw) - self._adjust_sort(result, exp, lambda d: tuple(d["column_names"])) - self._check_table_dict(result, exp, self._required_unique_cst_keys) - - @testing.requires.check_constraint_reflection - @_multi_combination - def test_get_multi_check_constraints( - self, get_multi_exp, schema, scope, kind, use_filter - ): - insp, kws, exp = get_multi_exp( - schema, - scope, - kind, - use_filter, - Inspector.get_check_constraints, - self.exp_ccs, - ) - for kw in kws: - insp.clear_cache() - result = insp.get_multi_check_constraints(**kw) - self._adjust_sort(result, exp, lambda d: tuple(d["sqltext"])) - self._check_table_dict(result, exp, self._required_cc_keys) - - @testing.combinations( - ("get_table_options", testing.requires.reflect_table_options), - "get_columns", - ( - "get_pk_constraint", - testing.requires.primary_key_constraint_reflection, - ), - ( - "get_foreign_keys", - testing.requires.foreign_key_constraint_reflection, - ), - ("get_indexes", testing.requires.index_reflection), - ( - "get_unique_constraints", - testing.requires.unique_constraint_reflection, - ), - ( - "get_check_constraints", - testing.requires.check_constraint_reflection, - ), - ("get_table_comment", testing.requires.comment_reflection), - argnames="method", - ) - def test_not_existing_table(self, method, connection): - insp = inspect(connection) - meth = getattr(insp, method) - with expect_raises(NoSuchTableError): - meth("table_does_not_exists") - - def test_unreflectable(self, connection): - mc = Inspector.get_multi_columns - - def patched(*a, **k): - ur = k.setdefault("unreflectable", {}) - ur[(None, "some_table")] = UnreflectableTableError("err") - return mc(*a, **k) - - with mock.patch.object(Inspector, "get_multi_columns", patched): - with expect_raises_message(UnreflectableTableError, "err"): - inspect(connection).reflect_table( - Table("some_table", MetaData()), None - ) - - @testing.combinations(True, False, argnames="use_schema") - @testing.combinations( - (True, testing.requires.views), False, argnames="views" - ) - def test_metadata(self, connection, use_schema, views): - m = MetaData() - schema = config.test_schema if use_schema else None - m.reflect(connection, schema=schema, views=views, resolve_fks=False) - - insp = inspect(connection) - tables = insp.get_table_names(schema) - if views: - tables += insp.get_view_names(schema) - try: - tables += insp.get_materialized_view_names(schema) - except NotImplementedError: - pass - if schema: - tables = [f"{schema}.{t}" for t in tables] - eq_(sorted(m.tables), sorted(tables)) - - @testing.requires.comment_reflection - def test_comments_unicode(self, connection, metadata): - Table( - "unicode_comments", - metadata, - Column("unicode", Integer, comment="é試蛇ẟΩ"), - Column("emoji", Integer, comment="☁️✨"), - comment="試蛇ẟΩ✨", - ) - - metadata.create_all(connection) - - insp = inspect(connection) - tc = insp.get_table_comment("unicode_comments") - eq_(tc, {"text": "試蛇ẟΩ✨"}) - - cols = insp.get_columns("unicode_comments") - value = {c["name"]: c["comment"] for c in cols} - exp = {"unicode": "é試蛇ẟΩ", "emoji": "☁️✨"} - eq_(value, exp) - - @testing.requires.comment_reflection_full_unicode - def test_comments_unicode_full(self, connection, metadata): - Table( - "unicode_comments", - metadata, - Column("emoji", Integer, comment="🐍🧙🝝🧙♂️🧙♀️"), - comment="🎩🁰🝑🤷♀️🤷♂️", - ) - - metadata.create_all(connection) - - insp = inspect(connection) - tc = insp.get_table_comment("unicode_comments") - eq_(tc, {"text": "🎩🁰🝑🤷♀️🤷♂️"}) - c = insp.get_columns("unicode_comments")[0] - eq_({c["name"]: c["comment"]}, {"emoji": "🐍🧙🝝🧙♂️🧙♀️"}) - - -class TableNoColumnsTest(fixtures.TestBase): - __requires__ = ("reflect_tables_no_columns",) - __backend__ = True - - @testing.fixture - def table_no_columns(self, connection, metadata): - Table("empty", metadata) - metadata.create_all(connection) - - @testing.fixture - def view_no_columns(self, connection, metadata): - Table("empty", metadata) - event.listen( - metadata, - "after_create", - DDL("CREATE VIEW empty_v AS SELECT * FROM empty"), - ) - - # for transactional DDL the transaction is rolled back before this - # drop statement is invoked - event.listen( - metadata, "before_drop", DDL("DROP VIEW IF EXISTS empty_v") - ) - metadata.create_all(connection) - - def test_reflect_table_no_columns(self, connection, table_no_columns): - t2 = Table("empty", MetaData(), autoload_with=connection) - eq_(list(t2.c), []) - - def test_get_columns_table_no_columns(self, connection, table_no_columns): - insp = inspect(connection) - eq_(insp.get_columns("empty"), []) - multi = insp.get_multi_columns() - eq_(multi, {(None, "empty"): []}) - - def test_reflect_incl_table_no_columns(self, connection, table_no_columns): - m = MetaData() - m.reflect(connection) - assert set(m.tables).intersection(["empty"]) - - @testing.requires.views - def test_reflect_view_no_columns(self, connection, view_no_columns): - t2 = Table("empty_v", MetaData(), autoload_with=connection) - eq_(list(t2.c), []) - - @testing.requires.views - def test_get_columns_view_no_columns(self, connection, view_no_columns): - insp = inspect(connection) - eq_(insp.get_columns("empty_v"), []) - multi = insp.get_multi_columns(kind=ObjectKind.VIEW) - eq_(multi, {(None, "empty_v"): []}) - - -class ComponentReflectionTestExtra(ComparesIndexes, fixtures.TestBase): - __backend__ = True - - @testing.combinations( - (True, testing.requires.schemas), (False,), argnames="use_schema" - ) - @testing.requires.check_constraint_reflection - def test_get_check_constraints(self, metadata, connection, use_schema): - if use_schema: - schema = config.test_schema - else: - schema = None - - Table( - "sa_cc", - metadata, - Column("a", Integer()), - sa.CheckConstraint("a > 1 AND a < 5", name="cc1"), - sa.CheckConstraint( - "a = 1 OR (a > 2 AND a < 5)", name="UsesCasing" - ), - schema=schema, - ) - Table( - "no_constraints", - metadata, - Column("data", sa.String(20)), - schema=schema, - ) - - metadata.create_all(connection) - - insp = inspect(connection) - reflected = sorted( - insp.get_check_constraints("sa_cc", schema=schema), - key=operator.itemgetter("name"), - ) - - # trying to minimize effect of quoting, parenthesis, etc. - # may need to add more to this as new dialects get CHECK - # constraint reflection support - def normalize(sqltext): - return " ".join( - re.findall(r"and|\d|=|a|or|<|>", sqltext.lower(), re.I) - ) - - reflected = [ - {"name": item["name"], "sqltext": normalize(item["sqltext"])} - for item in reflected - ] - eq_( - reflected, - [ - {"name": "UsesCasing", "sqltext": "a = 1 or a > 2 and a < 5"}, - {"name": "cc1", "sqltext": "a > 1 and a < 5"}, - ], - ) - no_cst = "no_constraints" - eq_(insp.get_check_constraints(no_cst, schema=schema), []) - - @testing.requires.indexes_with_expressions - def test_reflect_expression_based_indexes(self, metadata, connection): - t = Table( - "t", - metadata, - Column("x", String(30)), - Column("y", String(30)), - Column("z", String(30)), - ) - - Index("t_idx", func.lower(t.c.x), t.c.z, func.lower(t.c.y)) - long_str = "long string " * 100 - Index("t_idx_long", func.coalesce(t.c.x, long_str)) - Index("t_idx_2", t.c.x) - - metadata.create_all(connection) - - insp = inspect(connection) - - expected = [ - { - "name": "t_idx_2", - "column_names": ["x"], - "unique": False, - "dialect_options": {}, - } - ] - - def completeIndex(entry): - if testing.requires.index_reflects_included_columns.enabled: - entry["include_columns"] = [] - entry["dialect_options"] = { - f"{connection.engine.name}_include": [] - } - else: - entry.setdefault("dialect_options", {}) - - completeIndex(expected[0]) - - class lower_index_str(str): - def __eq__(self, other): - ol = other.lower() - # test that lower and x or y are in the string - return "lower" in ol and ("x" in ol or "y" in ol) - - class coalesce_index_str(str): - def __eq__(self, other): - # test that coalesce and the string is in other - return "coalesce" in other.lower() and long_str in other - - if testing.requires.reflect_indexes_with_expressions.enabled: - expr_index = { - "name": "t_idx", - "column_names": [None, "z", None], - "expressions": [ - lower_index_str("lower(x)"), - "z", - lower_index_str("lower(y)"), - ], - "unique": False, - } - completeIndex(expr_index) - expected.insert(0, expr_index) - - expr_index_long = { - "name": "t_idx_long", - "column_names": [None], - "expressions": [ - coalesce_index_str(f"coalesce(x, '{long_str}')") - ], - "unique": False, - } - completeIndex(expr_index_long) - expected.append(expr_index_long) - - eq_(insp.get_indexes("t"), expected) - m2 = MetaData() - t2 = Table("t", m2, autoload_with=connection) - else: - with expect_warnings( - "Skipped unsupported reflection of expression-based " - "index t_idx" - ): - eq_(insp.get_indexes("t"), expected) - m2 = MetaData() - t2 = Table("t", m2, autoload_with=connection) - - self.compare_table_index_with_expected( - t2, expected, connection.engine.name - ) - - @testing.requires.index_reflects_included_columns - def test_reflect_covering_index(self, metadata, connection): - t = Table( - "t", - metadata, - Column("x", String(30)), - Column("y", String(30)), - ) - idx = Index("t_idx", t.c.x) - idx.dialect_options[connection.engine.name]["include"] = ["y"] - - metadata.create_all(connection) - - insp = inspect(connection) - - get_indexes = insp.get_indexes("t") - eq_( - get_indexes, - [ - { - "name": "t_idx", - "column_names": ["x"], - "include_columns": ["y"], - "unique": False, - "dialect_options": mock.ANY, - } - ], - ) - eq_( - get_indexes[0]["dialect_options"][ - "%s_include" % connection.engine.name - ], - ["y"], - ) - - t2 = Table("t", MetaData(), autoload_with=connection) - eq_( - list(t2.indexes)[0].dialect_options[connection.engine.name][ - "include" - ], - ["y"], - ) - - def _type_round_trip(self, connection, metadata, *types): - t = Table( - "t", - metadata, - *[Column("t%d" % i, type_) for i, type_ in enumerate(types)], - ) - t.create(connection) - - return [c["type"] for c in inspect(connection).get_columns("t")] - - @testing.requires.table_reflection - def test_numeric_reflection(self, connection, metadata): - for typ in self._type_round_trip( - connection, metadata, sql_types.Numeric(18, 5) - ): - assert isinstance(typ, sql_types.Numeric) - eq_(typ.precision, 18) - eq_(typ.scale, 5) - - @testing.requires.table_reflection - def test_varchar_reflection(self, connection, metadata): - typ = self._type_round_trip( - connection, metadata, sql_types.String(52) - )[0] - assert isinstance(typ, sql_types.String) - eq_(typ.length, 52) - - @testing.requires.table_reflection - def test_nullable_reflection(self, connection, metadata): - t = Table( - "t", - metadata, - Column("a", Integer, nullable=True), - Column("b", Integer, nullable=False), - ) - t.create(connection) - eq_( - { - col["name"]: col["nullable"] - for col in inspect(connection).get_columns("t") - }, - {"a": True, "b": False}, - ) - - @testing.combinations( - ( - None, - "CASCADE", - None, - testing.requires.foreign_key_constraint_option_reflection_ondelete, - ), - ( - None, - None, - "SET NULL", - testing.requires.foreign_key_constraint_option_reflection_onupdate, - ), - ( - {}, - None, - "NO ACTION", - testing.requires.foreign_key_constraint_option_reflection_onupdate, - ), - ( - {}, - "NO ACTION", - None, - testing.requires.fk_constraint_option_reflection_ondelete_noaction, - ), - ( - None, - None, - "RESTRICT", - testing.requires.fk_constraint_option_reflection_onupdate_restrict, - ), - ( - None, - "RESTRICT", - None, - testing.requires.fk_constraint_option_reflection_ondelete_restrict, - ), - argnames="expected,ondelete,onupdate", - ) - def test_get_foreign_key_options( - self, connection, metadata, expected, ondelete, onupdate - ): - options = {} - if ondelete: - options["ondelete"] = ondelete - if onupdate: - options["onupdate"] = onupdate - - if expected is None: - expected = options - - Table( - "x", - metadata, - Column("id", Integer, primary_key=True), - test_needs_fk=True, - ) - - Table( - "table", - metadata, - Column("id", Integer, primary_key=True), - Column("x_id", Integer, ForeignKey("x.id", name="xid")), - Column("test", String(10)), - test_needs_fk=True, - ) - - Table( - "user", - metadata, - Column("id", Integer, primary_key=True), - Column("name", String(50), nullable=False), - Column("tid", Integer), - sa.ForeignKeyConstraint( - ["tid"], ["table.id"], name="myfk", **options - ), - test_needs_fk=True, - ) - - metadata.create_all(connection) - - insp = inspect(connection) - - # test 'options' is always present for a backend - # that can reflect these, since alembic looks for this - opts = insp.get_foreign_keys("table")[0]["options"] - - eq_({k: opts[k] for k in opts if opts[k]}, {}) - - opts = insp.get_foreign_keys("user")[0]["options"] - eq_(opts, expected) - # eq_(dict((k, opts[k]) for k in opts if opts[k]), expected) - - -class NormalizedNameTest(fixtures.TablesTest): - __requires__ = ("denormalized_names",) - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - quoted_name("t1", quote=True), - metadata, - Column("id", Integer, primary_key=True), - ) - Table( - quoted_name("t2", quote=True), - metadata, - Column("id", Integer, primary_key=True), - Column("t1id", ForeignKey("t1.id")), - ) - - def test_reflect_lowercase_forced_tables(self): - m2 = MetaData() - t2_ref = Table( - quoted_name("t2", quote=True), m2, autoload_with=config.db - ) - t1_ref = m2.tables["t1"] - assert t2_ref.c.t1id.references(t1_ref.c.id) - - m3 = MetaData() - m3.reflect( - config.db, only=lambda name, m: name.lower() in ("t1", "t2") - ) - assert m3.tables["t2"].c.t1id.references(m3.tables["t1"].c.id) - - def test_get_table_names(self): - tablenames = [ - t - for t in inspect(config.db).get_table_names() - if t.lower() in ("t1", "t2") - ] - - eq_(tablenames[0].upper(), tablenames[0].lower()) - eq_(tablenames[1].upper(), tablenames[1].lower()) - - -class ComputedReflectionTest(fixtures.ComputedReflectionFixtureTest): - def test_computed_col_default_not_set(self): - insp = inspect(config.db) - - cols = insp.get_columns("computed_default_table") - col_data = {c["name"]: c for c in cols} - is_true("42" in col_data["with_default"]["default"]) - is_(col_data["normal"]["default"], None) - is_(col_data["computed_col"]["default"], None) - - def test_get_column_returns_computed(self): - insp = inspect(config.db) - - cols = insp.get_columns("computed_default_table") - data = {c["name"]: c for c in cols} - for key in ("id", "normal", "with_default"): - is_true("computed" not in data[key]) - compData = data["computed_col"] - is_true("computed" in compData) - is_true("sqltext" in compData["computed"]) - eq_(self.normalize(compData["computed"]["sqltext"]), "normal+42") - eq_( - "persisted" in compData["computed"], - testing.requires.computed_columns_reflect_persisted.enabled, - ) - if testing.requires.computed_columns_reflect_persisted.enabled: - eq_( - compData["computed"]["persisted"], - testing.requires.computed_columns_default_persisted.enabled, - ) - - def check_column(self, data, column, sqltext, persisted): - is_true("computed" in data[column]) - compData = data[column]["computed"] - eq_(self.normalize(compData["sqltext"]), sqltext) - if testing.requires.computed_columns_reflect_persisted.enabled: - is_true("persisted" in compData) - is_(compData["persisted"], persisted) - - def test_get_column_returns_persisted(self): - insp = inspect(config.db) - - cols = insp.get_columns("computed_column_table") - data = {c["name"]: c for c in cols} - - self.check_column( - data, - "computed_no_flag", - "normal+42", - testing.requires.computed_columns_default_persisted.enabled, - ) - if testing.requires.computed_columns_virtual.enabled: - self.check_column( - data, - "computed_virtual", - "normal+2", - False, - ) - if testing.requires.computed_columns_stored.enabled: - self.check_column( - data, - "computed_stored", - "normal-42", - True, - ) - - @testing.requires.schemas - def test_get_column_returns_persisted_with_schema(self): - insp = inspect(config.db) - - cols = insp.get_columns( - "computed_column_table", schema=config.test_schema - ) - data = {c["name"]: c for c in cols} - - self.check_column( - data, - "computed_no_flag", - "normal/42", - testing.requires.computed_columns_default_persisted.enabled, - ) - if testing.requires.computed_columns_virtual.enabled: - self.check_column( - data, - "computed_virtual", - "normal/2", - False, - ) - if testing.requires.computed_columns_stored.enabled: - self.check_column( - data, - "computed_stored", - "normal*42", - True, - ) - - -class IdentityReflectionTest(fixtures.TablesTest): - run_inserts = run_deletes = None - - __backend__ = True - __requires__ = ("identity_columns", "table_reflection") - - @classmethod - def define_tables(cls, metadata): - Table( - "t1", - metadata, - Column("normal", Integer), - Column("id1", Integer, Identity()), - ) - Table( - "t2", - metadata, - Column( - "id2", - Integer, - Identity( - always=True, - start=2, - increment=3, - minvalue=-2, - maxvalue=42, - cycle=True, - cache=4, - ), - ), - ) - if testing.requires.schemas.enabled: - Table( - "t1", - metadata, - Column("normal", Integer), - Column("id1", Integer, Identity(always=True, start=20)), - schema=config.test_schema, - ) - - def check(self, value, exp, approx): - if testing.requires.identity_columns_standard.enabled: - common_keys = ( - "always", - "start", - "increment", - "minvalue", - "maxvalue", - "cycle", - "cache", - ) - for k in list(value): - if k not in common_keys: - value.pop(k) - if approx: - eq_(len(value), len(exp)) - for k in value: - if k == "minvalue": - is_true(value[k] <= exp[k]) - elif k in {"maxvalue", "cache"}: - is_true(value[k] >= exp[k]) - else: - eq_(value[k], exp[k], k) - else: - eq_(value, exp) - else: - eq_(value["start"], exp["start"]) - eq_(value["increment"], exp["increment"]) - - def test_reflect_identity(self): - insp = inspect(config.db) - - cols = insp.get_columns("t1") + insp.get_columns("t2") - for col in cols: - if col["name"] == "normal": - is_false("identity" in col) - elif col["name"] == "id1": - if "autoincrement" in col: - is_true(col["autoincrement"]) - eq_(col["default"], None) - is_true("identity" in col) - self.check( - col["identity"], - dict( - always=False, - start=1, - increment=1, - minvalue=1, - maxvalue=2147483647, - cycle=False, - cache=1, - ), - approx=True, - ) - elif col["name"] == "id2": - if "autoincrement" in col: - is_true(col["autoincrement"]) - eq_(col["default"], None) - is_true("identity" in col) - self.check( - col["identity"], - dict( - always=True, - start=2, - increment=3, - minvalue=-2, - maxvalue=42, - cycle=True, - cache=4, - ), - approx=False, - ) - - @testing.requires.schemas - def test_reflect_identity_schema(self): - insp = inspect(config.db) - - cols = insp.get_columns("t1", schema=config.test_schema) - for col in cols: - if col["name"] == "normal": - is_false("identity" in col) - elif col["name"] == "id1": - if "autoincrement" in col: - is_true(col["autoincrement"]) - eq_(col["default"], None) - is_true("identity" in col) - self.check( - col["identity"], - dict( - always=True, - start=20, - increment=1, - minvalue=1, - maxvalue=2147483647, - cycle=False, - cache=1, - ), - approx=True, - ) - - -class CompositeKeyReflectionTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - tb1 = Table( - "tb1", - metadata, - Column("id", Integer), - Column("attr", Integer), - Column("name", sql_types.VARCHAR(20)), - sa.PrimaryKeyConstraint("name", "id", "attr", name="pk_tb1"), - schema=None, - test_needs_fk=True, - ) - Table( - "tb2", - metadata, - Column("id", Integer, primary_key=True), - Column("pid", Integer), - Column("pattr", Integer), - Column("pname", sql_types.VARCHAR(20)), - sa.ForeignKeyConstraint( - ["pname", "pid", "pattr"], - [tb1.c.name, tb1.c.id, tb1.c.attr], - name="fk_tb1_name_id_attr", - ), - schema=None, - test_needs_fk=True, - ) - - @testing.requires.primary_key_constraint_reflection - def test_pk_column_order(self, connection): - # test for issue #5661 - insp = inspect(connection) - primary_key = insp.get_pk_constraint(self.tables.tb1.name) - eq_(primary_key.get("constrained_columns"), ["name", "id", "attr"]) - - @testing.requires.foreign_key_constraint_reflection - def test_fk_column_order(self, connection): - # test for issue #5661 - insp = inspect(connection) - foreign_keys = insp.get_foreign_keys(self.tables.tb2.name) - eq_(len(foreign_keys), 1) - fkey1 = foreign_keys[0] - eq_(fkey1.get("referred_columns"), ["name", "id", "attr"]) - eq_(fkey1.get("constrained_columns"), ["pname", "pid", "pattr"]) - - -__all__ = ( - "ComponentReflectionTest", - "ComponentReflectionTestExtra", - "TableNoColumnsTest", - "QuotedNameArgumentTest", - "BizarroCharacterFKResolutionTest", - "HasTableTest", - "HasIndexTest", - "NormalizedNameTest", - "ComputedReflectionTest", - "IdentityReflectionTest", - "CompositeKeyReflectionTest", -) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_results.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_results.py deleted file mode 100644 index b3f432f..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_results.py +++ /dev/null @@ -1,468 +0,0 @@ -# testing/suite/test_results.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -import datetime - -from .. import engines -from .. import fixtures -from ..assertions import eq_ -from ..config import requirements -from ..schema import Column -from ..schema import Table -from ... import DateTime -from ... import func -from ... import Integer -from ... import select -from ... import sql -from ... import String -from ... import testing -from ... import text - - -class RowFetchTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "plain_pk", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - ) - Table( - "has_dates", - metadata, - Column("id", Integer, primary_key=True), - Column("today", DateTime), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.plain_pk.insert(), - [ - {"id": 1, "data": "d1"}, - {"id": 2, "data": "d2"}, - {"id": 3, "data": "d3"}, - ], - ) - - connection.execute( - cls.tables.has_dates.insert(), - [{"id": 1, "today": datetime.datetime(2006, 5, 12, 12, 0, 0)}], - ) - - def test_via_attr(self, connection): - row = connection.execute( - self.tables.plain_pk.select().order_by(self.tables.plain_pk.c.id) - ).first() - - eq_(row.id, 1) - eq_(row.data, "d1") - - def test_via_string(self, connection): - row = connection.execute( - self.tables.plain_pk.select().order_by(self.tables.plain_pk.c.id) - ).first() - - eq_(row._mapping["id"], 1) - eq_(row._mapping["data"], "d1") - - def test_via_int(self, connection): - row = connection.execute( - self.tables.plain_pk.select().order_by(self.tables.plain_pk.c.id) - ).first() - - eq_(row[0], 1) - eq_(row[1], "d1") - - def test_via_col_object(self, connection): - row = connection.execute( - self.tables.plain_pk.select().order_by(self.tables.plain_pk.c.id) - ).first() - - eq_(row._mapping[self.tables.plain_pk.c.id], 1) - eq_(row._mapping[self.tables.plain_pk.c.data], "d1") - - @requirements.duplicate_names_in_cursor_description - def test_row_with_dupe_names(self, connection): - result = connection.execute( - select( - self.tables.plain_pk.c.data, - self.tables.plain_pk.c.data.label("data"), - ).order_by(self.tables.plain_pk.c.id) - ) - row = result.first() - eq_(result.keys(), ["data", "data"]) - eq_(row, ("d1", "d1")) - - def test_row_w_scalar_select(self, connection): - """test that a scalar select as a column is returned as such - and that type conversion works OK. - - (this is half a SQLAlchemy Core test and half to catch database - backends that may have unusual behavior with scalar selects.) - - """ - datetable = self.tables.has_dates - s = select(datetable.alias("x").c.today).scalar_subquery() - s2 = select(datetable.c.id, s.label("somelabel")) - row = connection.execute(s2).first() - - eq_(row.somelabel, datetime.datetime(2006, 5, 12, 12, 0, 0)) - - -class PercentSchemaNamesTest(fixtures.TablesTest): - """tests using percent signs, spaces in table and column names. - - This didn't work for PostgreSQL / MySQL drivers for a long time - but is now supported. - - """ - - __requires__ = ("percent_schema_names",) - - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - cls.tables.percent_table = Table( - "percent%table", - metadata, - Column("percent%", Integer), - Column("spaces % more spaces", Integer), - ) - cls.tables.lightweight_percent_table = sql.table( - "percent%table", - sql.column("percent%"), - sql.column("spaces % more spaces"), - ) - - def test_single_roundtrip(self, connection): - percent_table = self.tables.percent_table - for params in [ - {"percent%": 5, "spaces % more spaces": 12}, - {"percent%": 7, "spaces % more spaces": 11}, - {"percent%": 9, "spaces % more spaces": 10}, - {"percent%": 11, "spaces % more spaces": 9}, - ]: - connection.execute(percent_table.insert(), params) - self._assert_table(connection) - - def test_executemany_roundtrip(self, connection): - percent_table = self.tables.percent_table - connection.execute( - percent_table.insert(), {"percent%": 5, "spaces % more spaces": 12} - ) - connection.execute( - percent_table.insert(), - [ - {"percent%": 7, "spaces % more spaces": 11}, - {"percent%": 9, "spaces % more spaces": 10}, - {"percent%": 11, "spaces % more spaces": 9}, - ], - ) - self._assert_table(connection) - - @requirements.insert_executemany_returning - def test_executemany_returning_roundtrip(self, connection): - percent_table = self.tables.percent_table - connection.execute( - percent_table.insert(), {"percent%": 5, "spaces % more spaces": 12} - ) - result = connection.execute( - percent_table.insert().returning( - percent_table.c["percent%"], - percent_table.c["spaces % more spaces"], - ), - [ - {"percent%": 7, "spaces % more spaces": 11}, - {"percent%": 9, "spaces % more spaces": 10}, - {"percent%": 11, "spaces % more spaces": 9}, - ], - ) - eq_(result.all(), [(7, 11), (9, 10), (11, 9)]) - self._assert_table(connection) - - def _assert_table(self, conn): - percent_table = self.tables.percent_table - lightweight_percent_table = self.tables.lightweight_percent_table - - for table in ( - percent_table, - percent_table.alias(), - lightweight_percent_table, - lightweight_percent_table.alias(), - ): - eq_( - list( - conn.execute(table.select().order_by(table.c["percent%"])) - ), - [(5, 12), (7, 11), (9, 10), (11, 9)], - ) - - eq_( - list( - conn.execute( - table.select() - .where(table.c["spaces % more spaces"].in_([9, 10])) - .order_by(table.c["percent%"]) - ) - ), - [(9, 10), (11, 9)], - ) - - row = conn.execute( - table.select().order_by(table.c["percent%"]) - ).first() - eq_(row._mapping["percent%"], 5) - eq_(row._mapping["spaces % more spaces"], 12) - - eq_(row._mapping[table.c["percent%"]], 5) - eq_(row._mapping[table.c["spaces % more spaces"]], 12) - - conn.execute( - percent_table.update().values( - {percent_table.c["spaces % more spaces"]: 15} - ) - ) - - eq_( - list( - conn.execute( - percent_table.select().order_by( - percent_table.c["percent%"] - ) - ) - ), - [(5, 15), (7, 15), (9, 15), (11, 15)], - ) - - -class ServerSideCursorsTest( - fixtures.TestBase, testing.AssertsExecutionResults -): - __requires__ = ("server_side_cursors",) - - __backend__ = True - - def _is_server_side(self, cursor): - # TODO: this is a huge issue as it prevents these tests from being - # usable by third party dialects. - if self.engine.dialect.driver == "psycopg2": - return bool(cursor.name) - elif self.engine.dialect.driver == "pymysql": - sscursor = __import__("pymysql.cursors").cursors.SSCursor - return isinstance(cursor, sscursor) - elif self.engine.dialect.driver in ("aiomysql", "asyncmy", "aioodbc"): - return cursor.server_side - elif self.engine.dialect.driver == "mysqldb": - sscursor = __import__("MySQLdb.cursors").cursors.SSCursor - return isinstance(cursor, sscursor) - elif self.engine.dialect.driver == "mariadbconnector": - return not cursor.buffered - elif self.engine.dialect.driver in ("asyncpg", "aiosqlite"): - return cursor.server_side - elif self.engine.dialect.driver == "pg8000": - return getattr(cursor, "server_side", False) - elif self.engine.dialect.driver == "psycopg": - return bool(getattr(cursor, "name", False)) - else: - return False - - def _fixture(self, server_side_cursors): - if server_side_cursors: - with testing.expect_deprecated( - "The create_engine.server_side_cursors parameter is " - "deprecated and will be removed in a future release. " - "Please use the Connection.execution_options.stream_results " - "parameter." - ): - self.engine = engines.testing_engine( - options={"server_side_cursors": server_side_cursors} - ) - else: - self.engine = engines.testing_engine( - options={"server_side_cursors": server_side_cursors} - ) - return self.engine - - @testing.combinations( - ("global_string", True, "select 1", True), - ("global_text", True, text("select 1"), True), - ("global_expr", True, select(1), True), - ("global_off_explicit", False, text("select 1"), False), - ( - "stmt_option", - False, - select(1).execution_options(stream_results=True), - True, - ), - ( - "stmt_option_disabled", - True, - select(1).execution_options(stream_results=False), - False, - ), - ("for_update_expr", True, select(1).with_for_update(), True), - # TODO: need a real requirement for this, or dont use this test - ( - "for_update_string", - True, - "SELECT 1 FOR UPDATE", - True, - testing.skip_if(["sqlite", "mssql"]), - ), - ("text_no_ss", False, text("select 42"), False), - ( - "text_ss_option", - False, - text("select 42").execution_options(stream_results=True), - True, - ), - id_="iaaa", - argnames="engine_ss_arg, statement, cursor_ss_status", - ) - def test_ss_cursor_status( - self, engine_ss_arg, statement, cursor_ss_status - ): - engine = self._fixture(engine_ss_arg) - with engine.begin() as conn: - if isinstance(statement, str): - result = conn.exec_driver_sql(statement) - else: - result = conn.execute(statement) - eq_(self._is_server_side(result.cursor), cursor_ss_status) - result.close() - - def test_conn_option(self): - engine = self._fixture(False) - - with engine.connect() as conn: - # should be enabled for this one - result = conn.execution_options( - stream_results=True - ).exec_driver_sql("select 1") - assert self._is_server_side(result.cursor) - - # the connection has autobegun, which means at the end of the - # block, we will roll back, which on MySQL at least will fail - # with "Commands out of sync" if the result set - # is not closed, so we close it first. - # - # fun fact! why did we not have this result.close() in this test - # before 2.0? don't we roll back in the connection pool - # unconditionally? yes! and in fact if you run this test in 1.4 - # with stdout shown, there is in fact "Exception during reset or - # similar" with "Commands out sync" emitted a warning! 2.0's - # architecture finds and fixes what was previously an expensive - # silent error condition. - result.close() - - def test_stmt_enabled_conn_option_disabled(self): - engine = self._fixture(False) - - s = select(1).execution_options(stream_results=True) - - with engine.connect() as conn: - # not this one - result = conn.execution_options(stream_results=False).execute(s) - assert not self._is_server_side(result.cursor) - - def test_aliases_and_ss(self): - engine = self._fixture(False) - s1 = ( - select(sql.literal_column("1").label("x")) - .execution_options(stream_results=True) - .subquery() - ) - - # options don't propagate out when subquery is used as a FROM clause - with engine.begin() as conn: - result = conn.execute(s1.select()) - assert not self._is_server_side(result.cursor) - result.close() - - s2 = select(1).select_from(s1) - with engine.begin() as conn: - result = conn.execute(s2) - assert not self._is_server_side(result.cursor) - result.close() - - def test_roundtrip_fetchall(self, metadata): - md = self.metadata - - engine = self._fixture(True) - test_table = Table( - "test_table", - md, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - ) - - with engine.begin() as connection: - test_table.create(connection, checkfirst=True) - connection.execute(test_table.insert(), dict(data="data1")) - connection.execute(test_table.insert(), dict(data="data2")) - eq_( - connection.execute( - test_table.select().order_by(test_table.c.id) - ).fetchall(), - [(1, "data1"), (2, "data2")], - ) - connection.execute( - test_table.update() - .where(test_table.c.id == 2) - .values(data=test_table.c.data + " updated") - ) - eq_( - connection.execute( - test_table.select().order_by(test_table.c.id) - ).fetchall(), - [(1, "data1"), (2, "data2 updated")], - ) - connection.execute(test_table.delete()) - eq_( - connection.scalar( - select(func.count("*")).select_from(test_table) - ), - 0, - ) - - def test_roundtrip_fetchmany(self, metadata): - md = self.metadata - - engine = self._fixture(True) - test_table = Table( - "test_table", - md, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - ) - - with engine.begin() as connection: - test_table.create(connection, checkfirst=True) - connection.execute( - test_table.insert(), - [dict(data="data%d" % i) for i in range(1, 20)], - ) - - result = connection.execute( - test_table.select().order_by(test_table.c.id) - ) - - eq_( - result.fetchmany(5), - [(i, "data%d" % i) for i in range(1, 6)], - ) - eq_( - result.fetchmany(10), - [(i, "data%d" % i) for i in range(6, 16)], - ) - eq_(result.fetchall(), [(i, "data%d" % i) for i in range(16, 20)]) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_rowcount.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_rowcount.py deleted file mode 100644 index a7dbd36..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_rowcount.py +++ /dev/null @@ -1,258 +0,0 @@ -# testing/suite/test_rowcount.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from sqlalchemy import bindparam -from sqlalchemy import Column -from sqlalchemy import Integer -from sqlalchemy import MetaData -from sqlalchemy import select -from sqlalchemy import String -from sqlalchemy import Table -from sqlalchemy import testing -from sqlalchemy import text -from sqlalchemy.testing import eq_ -from sqlalchemy.testing import fixtures - - -class RowCountTest(fixtures.TablesTest): - """test rowcount functionality""" - - __requires__ = ("sane_rowcount",) - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "employees", - metadata, - Column( - "employee_id", - Integer, - autoincrement=False, - primary_key=True, - ), - Column("name", String(50)), - Column("department", String(1)), - ) - - @classmethod - def insert_data(cls, connection): - cls.data = data = [ - ("Angela", "A"), - ("Andrew", "A"), - ("Anand", "A"), - ("Bob", "B"), - ("Bobette", "B"), - ("Buffy", "B"), - ("Charlie", "C"), - ("Cynthia", "C"), - ("Chris", "C"), - ] - - employees_table = cls.tables.employees - connection.execute( - employees_table.insert(), - [ - {"employee_id": i, "name": n, "department": d} - for i, (n, d) in enumerate(data) - ], - ) - - def test_basic(self, connection): - employees_table = self.tables.employees - s = select( - employees_table.c.name, employees_table.c.department - ).order_by(employees_table.c.employee_id) - rows = connection.execute(s).fetchall() - - eq_(rows, self.data) - - @testing.variation("statement", ["update", "delete", "insert", "select"]) - @testing.variation("close_first", [True, False]) - def test_non_rowcount_scenarios_no_raise( - self, connection, statement, close_first - ): - employees_table = self.tables.employees - - # WHERE matches 3, 3 rows changed - department = employees_table.c.department - - if statement.update: - r = connection.execute( - employees_table.update().where(department == "C"), - {"department": "Z"}, - ) - elif statement.delete: - r = connection.execute( - employees_table.delete().where(department == "C"), - {"department": "Z"}, - ) - elif statement.insert: - r = connection.execute( - employees_table.insert(), - [ - {"employee_id": 25, "name": "none 1", "department": "X"}, - {"employee_id": 26, "name": "none 2", "department": "Z"}, - {"employee_id": 27, "name": "none 3", "department": "Z"}, - ], - ) - elif statement.select: - s = select( - employees_table.c.name, employees_table.c.department - ).where(employees_table.c.department == "C") - r = connection.execute(s) - r.all() - else: - statement.fail() - - if close_first: - r.close() - - assert r.rowcount in (-1, 3) - - def test_update_rowcount1(self, connection): - employees_table = self.tables.employees - - # WHERE matches 3, 3 rows changed - department = employees_table.c.department - r = connection.execute( - employees_table.update().where(department == "C"), - {"department": "Z"}, - ) - assert r.rowcount == 3 - - def test_update_rowcount2(self, connection): - employees_table = self.tables.employees - - # WHERE matches 3, 0 rows changed - department = employees_table.c.department - - r = connection.execute( - employees_table.update().where(department == "C"), - {"department": "C"}, - ) - eq_(r.rowcount, 3) - - @testing.variation("implicit_returning", [True, False]) - @testing.variation( - "dml", - [ - ("update", testing.requires.update_returning), - ("delete", testing.requires.delete_returning), - ], - ) - def test_update_delete_rowcount_return_defaults( - self, connection, implicit_returning, dml - ): - """note this test should succeed for all RETURNING backends - as of 2.0. In - Idf28379f8705e403a3c6a937f6a798a042ef2540 we changed rowcount to use - len(rows) when we have implicit returning - - """ - - if implicit_returning: - employees_table = self.tables.employees - else: - employees_table = Table( - "employees", - MetaData(), - Column( - "employee_id", - Integer, - autoincrement=False, - primary_key=True, - ), - Column("name", String(50)), - Column("department", String(1)), - implicit_returning=False, - ) - - department = employees_table.c.department - - if dml.update: - stmt = ( - employees_table.update() - .where(department == "C") - .values(name=employees_table.c.department + "Z") - .return_defaults() - ) - elif dml.delete: - stmt = ( - employees_table.delete() - .where(department == "C") - .return_defaults() - ) - else: - dml.fail() - - r = connection.execute(stmt) - eq_(r.rowcount, 3) - - def test_raw_sql_rowcount(self, connection): - # test issue #3622, make sure eager rowcount is called for text - result = connection.exec_driver_sql( - "update employees set department='Z' where department='C'" - ) - eq_(result.rowcount, 3) - - def test_text_rowcount(self, connection): - # test issue #3622, make sure eager rowcount is called for text - result = connection.execute( - text("update employees set department='Z' where department='C'") - ) - eq_(result.rowcount, 3) - - def test_delete_rowcount(self, connection): - employees_table = self.tables.employees - - # WHERE matches 3, 3 rows deleted - department = employees_table.c.department - r = connection.execute( - employees_table.delete().where(department == "C") - ) - eq_(r.rowcount, 3) - - @testing.requires.sane_multi_rowcount - def test_multi_update_rowcount(self, connection): - employees_table = self.tables.employees - stmt = ( - employees_table.update() - .where(employees_table.c.name == bindparam("emp_name")) - .values(department="C") - ) - - r = connection.execute( - stmt, - [ - {"emp_name": "Bob"}, - {"emp_name": "Cynthia"}, - {"emp_name": "nonexistent"}, - ], - ) - - eq_(r.rowcount, 2) - - @testing.requires.sane_multi_rowcount - def test_multi_delete_rowcount(self, connection): - employees_table = self.tables.employees - - stmt = employees_table.delete().where( - employees_table.c.name == bindparam("emp_name") - ) - - r = connection.execute( - stmt, - [ - {"emp_name": "Bob"}, - {"emp_name": "Cynthia"}, - {"emp_name": "nonexistent"}, - ], - ) - - eq_(r.rowcount, 2) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_select.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_select.py deleted file mode 100644 index 866bf09..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_select.py +++ /dev/null @@ -1,1888 +0,0 @@ -# testing/suite/test_select.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -import collections.abc as collections_abc -import itertools - -from .. import AssertsCompiledSQL -from .. import AssertsExecutionResults -from .. import config -from .. import fixtures -from ..assertions import assert_raises -from ..assertions import eq_ -from ..assertions import in_ -from ..assertsql import CursorSQL -from ..schema import Column -from ..schema import Table -from ... import bindparam -from ... import case -from ... import column -from ... import Computed -from ... import exists -from ... import false -from ... import ForeignKey -from ... import func -from ... import Identity -from ... import Integer -from ... import literal -from ... import literal_column -from ... import null -from ... import select -from ... import String -from ... import table -from ... import testing -from ... import text -from ... import true -from ... import tuple_ -from ... import TupleType -from ... import union -from ... import values -from ...exc import DatabaseError -from ...exc import ProgrammingError - - -class CollateTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(100)), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "data": "collate data1"}, - {"id": 2, "data": "collate data2"}, - ], - ) - - def _assert_result(self, select, result): - with config.db.connect() as conn: - eq_(conn.execute(select).fetchall(), result) - - @testing.requires.order_by_collation - def test_collate_order_by(self): - collation = testing.requires.get_order_by_collation(testing.config) - - self._assert_result( - select(self.tables.some_table).order_by( - self.tables.some_table.c.data.collate(collation).asc() - ), - [(1, "collate data1"), (2, "collate data2")], - ) - - -class OrderByLabelTest(fixtures.TablesTest): - """Test the dialect sends appropriate ORDER BY expressions when - labels are used. - - This essentially exercises the "supports_simple_order_by_label" - setting. - - """ - - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("x", Integer), - Column("y", Integer), - Column("q", String(50)), - Column("p", String(50)), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "x": 1, "y": 2, "q": "q1", "p": "p3"}, - {"id": 2, "x": 2, "y": 3, "q": "q2", "p": "p2"}, - {"id": 3, "x": 3, "y": 4, "q": "q3", "p": "p1"}, - ], - ) - - def _assert_result(self, select, result): - with config.db.connect() as conn: - eq_(conn.execute(select).fetchall(), result) - - def test_plain(self): - table = self.tables.some_table - lx = table.c.x.label("lx") - self._assert_result(select(lx).order_by(lx), [(1,), (2,), (3,)]) - - def test_composed_int(self): - table = self.tables.some_table - lx = (table.c.x + table.c.y).label("lx") - self._assert_result(select(lx).order_by(lx), [(3,), (5,), (7,)]) - - def test_composed_multiple(self): - table = self.tables.some_table - lx = (table.c.x + table.c.y).label("lx") - ly = (func.lower(table.c.q) + table.c.p).label("ly") - self._assert_result( - select(lx, ly).order_by(lx, ly.desc()), - [(3, "q1p3"), (5, "q2p2"), (7, "q3p1")], - ) - - def test_plain_desc(self): - table = self.tables.some_table - lx = table.c.x.label("lx") - self._assert_result(select(lx).order_by(lx.desc()), [(3,), (2,), (1,)]) - - def test_composed_int_desc(self): - table = self.tables.some_table - lx = (table.c.x + table.c.y).label("lx") - self._assert_result(select(lx).order_by(lx.desc()), [(7,), (5,), (3,)]) - - @testing.requires.group_by_complex_expression - def test_group_by_composed(self): - table = self.tables.some_table - expr = (table.c.x + table.c.y).label("lx") - stmt = ( - select(func.count(table.c.id), expr).group_by(expr).order_by(expr) - ) - self._assert_result(stmt, [(1, 3), (1, 5), (1, 7)]) - - -class ValuesExpressionTest(fixtures.TestBase): - __requires__ = ("table_value_constructor",) - - __backend__ = True - - def test_tuples(self, connection): - value_expr = values( - column("id", Integer), column("name", String), name="my_values" - ).data([(1, "name1"), (2, "name2"), (3, "name3")]) - - eq_( - connection.execute(select(value_expr)).all(), - [(1, "name1"), (2, "name2"), (3, "name3")], - ) - - -class FetchLimitOffsetTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("x", Integer), - Column("y", Integer), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "x": 1, "y": 2}, - {"id": 2, "x": 2, "y": 3}, - {"id": 3, "x": 3, "y": 4}, - {"id": 4, "x": 4, "y": 5}, - {"id": 5, "x": 4, "y": 6}, - ], - ) - - def _assert_result( - self, connection, select, result, params=(), set_=False - ): - if set_: - query_res = connection.execute(select, params).fetchall() - eq_(len(query_res), len(result)) - eq_(set(query_res), set(result)) - - else: - eq_(connection.execute(select, params).fetchall(), result) - - def _assert_result_str(self, select, result, params=()): - with config.db.connect() as conn: - eq_(conn.exec_driver_sql(select, params).fetchall(), result) - - def test_simple_limit(self, connection): - table = self.tables.some_table - stmt = select(table).order_by(table.c.id) - self._assert_result( - connection, - stmt.limit(2), - [(1, 1, 2), (2, 2, 3)], - ) - self._assert_result( - connection, - stmt.limit(3), - [(1, 1, 2), (2, 2, 3), (3, 3, 4)], - ) - - def test_limit_render_multiple_times(self, connection): - table = self.tables.some_table - stmt = select(table.c.id).limit(1).scalar_subquery() - - u = union(select(stmt), select(stmt)).subquery().select() - - self._assert_result( - connection, - u, - [ - (1,), - ], - ) - - @testing.requires.fetch_first - def test_simple_fetch(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).order_by(table.c.id).fetch(2), - [(1, 1, 2), (2, 2, 3)], - ) - self._assert_result( - connection, - select(table).order_by(table.c.id).fetch(3), - [(1, 1, 2), (2, 2, 3), (3, 3, 4)], - ) - - @testing.requires.offset - def test_simple_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).order_by(table.c.id).offset(2), - [(3, 3, 4), (4, 4, 5), (5, 4, 6)], - ) - self._assert_result( - connection, - select(table).order_by(table.c.id).offset(3), - [(4, 4, 5), (5, 4, 6)], - ) - - @testing.combinations( - ([(2, 0), (2, 1), (3, 2)]), - ([(2, 1), (2, 0), (3, 2)]), - ([(3, 1), (2, 1), (3, 1)]), - argnames="cases", - ) - @testing.requires.offset - def test_simple_limit_offset(self, connection, cases): - table = self.tables.some_table - connection = connection.execution_options(compiled_cache={}) - - assert_data = [(1, 1, 2), (2, 2, 3), (3, 3, 4), (4, 4, 5), (5, 4, 6)] - - for limit, offset in cases: - expected = assert_data[offset : offset + limit] - self._assert_result( - connection, - select(table).order_by(table.c.id).limit(limit).offset(offset), - expected, - ) - - @testing.requires.fetch_first - def test_simple_fetch_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).order_by(table.c.id).fetch(2).offset(1), - [(2, 2, 3), (3, 3, 4)], - ) - - self._assert_result( - connection, - select(table).order_by(table.c.id).fetch(3).offset(2), - [(3, 3, 4), (4, 4, 5), (5, 4, 6)], - ) - - @testing.requires.fetch_no_order_by - def test_fetch_offset_no_order(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).fetch(10), - [(1, 1, 2), (2, 2, 3), (3, 3, 4), (4, 4, 5), (5, 4, 6)], - set_=True, - ) - - @testing.requires.offset - def test_simple_offset_zero(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).order_by(table.c.id).offset(0), - [(1, 1, 2), (2, 2, 3), (3, 3, 4), (4, 4, 5), (5, 4, 6)], - ) - - self._assert_result( - connection, - select(table).order_by(table.c.id).offset(1), - [(2, 2, 3), (3, 3, 4), (4, 4, 5), (5, 4, 6)], - ) - - @testing.requires.offset - def test_limit_offset_nobinds(self): - """test that 'literal binds' mode works - no bound params.""" - - table = self.tables.some_table - stmt = select(table).order_by(table.c.id).limit(2).offset(1) - sql = stmt.compile( - dialect=config.db.dialect, compile_kwargs={"literal_binds": True} - ) - sql = str(sql) - - self._assert_result_str(sql, [(2, 2, 3), (3, 3, 4)]) - - @testing.requires.fetch_first - def test_fetch_offset_nobinds(self): - """test that 'literal binds' mode works - no bound params.""" - - table = self.tables.some_table - stmt = select(table).order_by(table.c.id).fetch(2).offset(1) - sql = stmt.compile( - dialect=config.db.dialect, compile_kwargs={"literal_binds": True} - ) - sql = str(sql) - - self._assert_result_str(sql, [(2, 2, 3), (3, 3, 4)]) - - @testing.requires.bound_limit_offset - def test_bound_limit(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).order_by(table.c.id).limit(bindparam("l")), - [(1, 1, 2), (2, 2, 3)], - params={"l": 2}, - ) - - self._assert_result( - connection, - select(table).order_by(table.c.id).limit(bindparam("l")), - [(1, 1, 2), (2, 2, 3), (3, 3, 4)], - params={"l": 3}, - ) - - @testing.requires.bound_limit_offset - def test_bound_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).order_by(table.c.id).offset(bindparam("o")), - [(3, 3, 4), (4, 4, 5), (5, 4, 6)], - params={"o": 2}, - ) - - self._assert_result( - connection, - select(table).order_by(table.c.id).offset(bindparam("o")), - [(2, 2, 3), (3, 3, 4), (4, 4, 5), (5, 4, 6)], - params={"o": 1}, - ) - - @testing.requires.bound_limit_offset - def test_bound_limit_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .limit(bindparam("l")) - .offset(bindparam("o")), - [(2, 2, 3), (3, 3, 4)], - params={"l": 2, "o": 1}, - ) - - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .limit(bindparam("l")) - .offset(bindparam("o")), - [(3, 3, 4), (4, 4, 5), (5, 4, 6)], - params={"l": 3, "o": 2}, - ) - - @testing.requires.fetch_first - def test_bound_fetch_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .fetch(bindparam("f")) - .offset(bindparam("o")), - [(2, 2, 3), (3, 3, 4)], - params={"f": 2, "o": 1}, - ) - - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .fetch(bindparam("f")) - .offset(bindparam("o")), - [(3, 3, 4), (4, 4, 5), (5, 4, 6)], - params={"f": 3, "o": 2}, - ) - - @testing.requires.sql_expression_limit_offset - def test_expr_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .offset(literal_column("1") + literal_column("2")), - [(4, 4, 5), (5, 4, 6)], - ) - - @testing.requires.sql_expression_limit_offset - def test_expr_limit(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .limit(literal_column("1") + literal_column("2")), - [(1, 1, 2), (2, 2, 3), (3, 3, 4)], - ) - - @testing.requires.sql_expression_limit_offset - def test_expr_limit_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .limit(literal_column("1") + literal_column("1")) - .offset(literal_column("1") + literal_column("1")), - [(3, 3, 4), (4, 4, 5)], - ) - - @testing.requires.fetch_first - @testing.requires.fetch_expression - def test_expr_fetch_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .fetch(literal_column("1") + literal_column("1")) - .offset(literal_column("1") + literal_column("1")), - [(3, 3, 4), (4, 4, 5)], - ) - - @testing.requires.sql_expression_limit_offset - def test_simple_limit_expr_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .limit(2) - .offset(literal_column("1") + literal_column("1")), - [(3, 3, 4), (4, 4, 5)], - ) - - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .limit(3) - .offset(literal_column("1") + literal_column("1")), - [(3, 3, 4), (4, 4, 5), (5, 4, 6)], - ) - - @testing.requires.sql_expression_limit_offset - def test_expr_limit_simple_offset(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .limit(literal_column("1") + literal_column("1")) - .offset(2), - [(3, 3, 4), (4, 4, 5)], - ) - - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .limit(literal_column("1") + literal_column("1")) - .offset(1), - [(2, 2, 3), (3, 3, 4)], - ) - - @testing.requires.fetch_ties - def test_simple_fetch_ties(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).order_by(table.c.x.desc()).fetch(1, with_ties=True), - [(4, 4, 5), (5, 4, 6)], - set_=True, - ) - - self._assert_result( - connection, - select(table).order_by(table.c.x.desc()).fetch(3, with_ties=True), - [(3, 3, 4), (4, 4, 5), (5, 4, 6)], - set_=True, - ) - - @testing.requires.fetch_ties - @testing.requires.fetch_offset_with_options - def test_fetch_offset_ties(self, connection): - table = self.tables.some_table - fa = connection.execute( - select(table) - .order_by(table.c.x) - .fetch(2, with_ties=True) - .offset(2) - ).fetchall() - eq_(fa[0], (3, 3, 4)) - eq_(set(fa), {(3, 3, 4), (4, 4, 5), (5, 4, 6)}) - - @testing.requires.fetch_ties - @testing.requires.fetch_offset_with_options - def test_fetch_offset_ties_exact_number(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.x) - .fetch(2, with_ties=True) - .offset(1), - [(2, 2, 3), (3, 3, 4)], - ) - - self._assert_result( - connection, - select(table) - .order_by(table.c.x) - .fetch(3, with_ties=True) - .offset(3), - [(4, 4, 5), (5, 4, 6)], - ) - - @testing.requires.fetch_percent - def test_simple_fetch_percent(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table).order_by(table.c.id).fetch(20, percent=True), - [(1, 1, 2)], - ) - - @testing.requires.fetch_percent - @testing.requires.fetch_offset_with_options - def test_fetch_offset_percent(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.id) - .fetch(40, percent=True) - .offset(1), - [(2, 2, 3), (3, 3, 4)], - ) - - @testing.requires.fetch_ties - @testing.requires.fetch_percent - def test_simple_fetch_percent_ties(self, connection): - table = self.tables.some_table - self._assert_result( - connection, - select(table) - .order_by(table.c.x.desc()) - .fetch(20, percent=True, with_ties=True), - [(4, 4, 5), (5, 4, 6)], - set_=True, - ) - - @testing.requires.fetch_ties - @testing.requires.fetch_percent - @testing.requires.fetch_offset_with_options - def test_fetch_offset_percent_ties(self, connection): - table = self.tables.some_table - fa = connection.execute( - select(table) - .order_by(table.c.x) - .fetch(40, percent=True, with_ties=True) - .offset(2) - ).fetchall() - eq_(fa[0], (3, 3, 4)) - eq_(set(fa), {(3, 3, 4), (4, 4, 5), (5, 4, 6)}) - - -class SameNamedSchemaTableTest(fixtures.TablesTest): - """tests for #7471""" - - __backend__ = True - - __requires__ = ("schemas",) - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - schema=config.test_schema, - ) - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column( - "some_table_id", - Integer, - # ForeignKey("%s.some_table.id" % config.test_schema), - nullable=False, - ), - ) - - @classmethod - def insert_data(cls, connection): - some_table, some_table_schema = cls.tables( - "some_table", "%s.some_table" % config.test_schema - ) - connection.execute(some_table_schema.insert(), {"id": 1}) - connection.execute(some_table.insert(), {"id": 1, "some_table_id": 1}) - - def test_simple_join_both_tables(self, connection): - some_table, some_table_schema = self.tables( - "some_table", "%s.some_table" % config.test_schema - ) - - eq_( - connection.execute( - select(some_table, some_table_schema).join_from( - some_table, - some_table_schema, - some_table.c.some_table_id == some_table_schema.c.id, - ) - ).first(), - (1, 1, 1), - ) - - def test_simple_join_whereclause_only(self, connection): - some_table, some_table_schema = self.tables( - "some_table", "%s.some_table" % config.test_schema - ) - - eq_( - connection.execute( - select(some_table) - .join_from( - some_table, - some_table_schema, - some_table.c.some_table_id == some_table_schema.c.id, - ) - .where(some_table.c.id == 1) - ).first(), - (1, 1), - ) - - def test_subquery(self, connection): - some_table, some_table_schema = self.tables( - "some_table", "%s.some_table" % config.test_schema - ) - - subq = ( - select(some_table) - .join_from( - some_table, - some_table_schema, - some_table.c.some_table_id == some_table_schema.c.id, - ) - .where(some_table.c.id == 1) - .subquery() - ) - - eq_( - connection.execute( - select(some_table, subq.c.id) - .join_from( - some_table, - subq, - some_table.c.some_table_id == subq.c.id, - ) - .where(some_table.c.id == 1) - ).first(), - (1, 1, 1), - ) - - -class JoinTest(fixtures.TablesTest): - __backend__ = True - - def _assert_result(self, select, result, params=()): - with config.db.connect() as conn: - eq_(conn.execute(select, params).fetchall(), result) - - @classmethod - def define_tables(cls, metadata): - Table("a", metadata, Column("id", Integer, primary_key=True)) - Table( - "b", - metadata, - Column("id", Integer, primary_key=True), - Column("a_id", ForeignKey("a.id"), nullable=False), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.a.insert(), - [{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}], - ) - - connection.execute( - cls.tables.b.insert(), - [ - {"id": 1, "a_id": 1}, - {"id": 2, "a_id": 1}, - {"id": 4, "a_id": 2}, - {"id": 5, "a_id": 3}, - ], - ) - - def test_inner_join_fk(self): - a, b = self.tables("a", "b") - - stmt = select(a, b).select_from(a.join(b)).order_by(a.c.id, b.c.id) - - self._assert_result(stmt, [(1, 1, 1), (1, 2, 1), (2, 4, 2), (3, 5, 3)]) - - def test_inner_join_true(self): - a, b = self.tables("a", "b") - - stmt = ( - select(a, b) - .select_from(a.join(b, true())) - .order_by(a.c.id, b.c.id) - ) - - self._assert_result( - stmt, - [ - (a, b, c) - for (a,), (b, c) in itertools.product( - [(1,), (2,), (3,), (4,), (5,)], - [(1, 1), (2, 1), (4, 2), (5, 3)], - ) - ], - ) - - def test_inner_join_false(self): - a, b = self.tables("a", "b") - - stmt = ( - select(a, b) - .select_from(a.join(b, false())) - .order_by(a.c.id, b.c.id) - ) - - self._assert_result(stmt, []) - - def test_outer_join_false(self): - a, b = self.tables("a", "b") - - stmt = ( - select(a, b) - .select_from(a.outerjoin(b, false())) - .order_by(a.c.id, b.c.id) - ) - - self._assert_result( - stmt, - [ - (1, None, None), - (2, None, None), - (3, None, None), - (4, None, None), - (5, None, None), - ], - ) - - def test_outer_join_fk(self): - a, b = self.tables("a", "b") - - stmt = select(a, b).select_from(a.join(b)).order_by(a.c.id, b.c.id) - - self._assert_result(stmt, [(1, 1, 1), (1, 2, 1), (2, 4, 2), (3, 5, 3)]) - - -class CompoundSelectTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("x", Integer), - Column("y", Integer), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "x": 1, "y": 2}, - {"id": 2, "x": 2, "y": 3}, - {"id": 3, "x": 3, "y": 4}, - {"id": 4, "x": 4, "y": 5}, - ], - ) - - def _assert_result(self, select, result, params=()): - with config.db.connect() as conn: - eq_(conn.execute(select, params).fetchall(), result) - - def test_plain_union(self): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2) - s2 = select(table).where(table.c.id == 3) - - u1 = union(s1, s2) - self._assert_result( - u1.order_by(u1.selected_columns.id), [(2, 2, 3), (3, 3, 4)] - ) - - def test_select_from_plain_union(self): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2) - s2 = select(table).where(table.c.id == 3) - - u1 = union(s1, s2).alias().select() - self._assert_result( - u1.order_by(u1.selected_columns.id), [(2, 2, 3), (3, 3, 4)] - ) - - @testing.requires.order_by_col_from_union - @testing.requires.parens_in_union_contained_select_w_limit_offset - def test_limit_offset_selectable_in_unions(self): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2).limit(1).order_by(table.c.id) - s2 = select(table).where(table.c.id == 3).limit(1).order_by(table.c.id) - - u1 = union(s1, s2).limit(2) - self._assert_result( - u1.order_by(u1.selected_columns.id), [(2, 2, 3), (3, 3, 4)] - ) - - @testing.requires.parens_in_union_contained_select_wo_limit_offset - def test_order_by_selectable_in_unions(self): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2).order_by(table.c.id) - s2 = select(table).where(table.c.id == 3).order_by(table.c.id) - - u1 = union(s1, s2).limit(2) - self._assert_result( - u1.order_by(u1.selected_columns.id), [(2, 2, 3), (3, 3, 4)] - ) - - def test_distinct_selectable_in_unions(self): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2).distinct() - s2 = select(table).where(table.c.id == 3).distinct() - - u1 = union(s1, s2).limit(2) - self._assert_result( - u1.order_by(u1.selected_columns.id), [(2, 2, 3), (3, 3, 4)] - ) - - @testing.requires.parens_in_union_contained_select_w_limit_offset - def test_limit_offset_in_unions_from_alias(self): - table = self.tables.some_table - s1 = select(table).where(table.c.id == 2).limit(1).order_by(table.c.id) - s2 = select(table).where(table.c.id == 3).limit(1).order_by(table.c.id) - - # this necessarily has double parens - u1 = union(s1, s2).alias() - self._assert_result( - u1.select().limit(2).order_by(u1.c.id), [(2, 2, 3), (3, 3, 4)] - ) - - def test_limit_offset_aliased_selectable_in_unions(self): - table = self.tables.some_table - s1 = ( - select(table) - .where(table.c.id == 2) - .limit(1) - .order_by(table.c.id) - .alias() - .select() - ) - s2 = ( - select(table) - .where(table.c.id == 3) - .limit(1) - .order_by(table.c.id) - .alias() - .select() - ) - - u1 = union(s1, s2).limit(2) - self._assert_result( - u1.order_by(u1.selected_columns.id), [(2, 2, 3), (3, 3, 4)] - ) - - -class PostCompileParamsTest( - AssertsExecutionResults, AssertsCompiledSQL, fixtures.TablesTest -): - __backend__ = True - - __requires__ = ("standard_cursor_sql",) - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("x", Integer), - Column("y", Integer), - Column("z", String(50)), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "x": 1, "y": 2, "z": "z1"}, - {"id": 2, "x": 2, "y": 3, "z": "z2"}, - {"id": 3, "x": 3, "y": 4, "z": "z3"}, - {"id": 4, "x": 4, "y": 5, "z": "z4"}, - ], - ) - - def test_compile(self): - table = self.tables.some_table - - stmt = select(table.c.id).where( - table.c.x == bindparam("q", literal_execute=True) - ) - - self.assert_compile( - stmt, - "SELECT some_table.id FROM some_table " - "WHERE some_table.x = __[POSTCOMPILE_q]", - {}, - ) - - def test_compile_literal_binds(self): - table = self.tables.some_table - - stmt = select(table.c.id).where( - table.c.x == bindparam("q", 10, literal_execute=True) - ) - - self.assert_compile( - stmt, - "SELECT some_table.id FROM some_table WHERE some_table.x = 10", - {}, - literal_binds=True, - ) - - def test_execute(self): - table = self.tables.some_table - - stmt = select(table.c.id).where( - table.c.x == bindparam("q", literal_execute=True) - ) - - with self.sql_execution_asserter() as asserter: - with config.db.connect() as conn: - conn.execute(stmt, dict(q=10)) - - asserter.assert_( - CursorSQL( - "SELECT some_table.id \nFROM some_table " - "\nWHERE some_table.x = 10", - () if config.db.dialect.positional else {}, - ) - ) - - def test_execute_expanding_plus_literal_execute(self): - table = self.tables.some_table - - stmt = select(table.c.id).where( - table.c.x.in_(bindparam("q", expanding=True, literal_execute=True)) - ) - - with self.sql_execution_asserter() as asserter: - with config.db.connect() as conn: - conn.execute(stmt, dict(q=[5, 6, 7])) - - asserter.assert_( - CursorSQL( - "SELECT some_table.id \nFROM some_table " - "\nWHERE some_table.x IN (5, 6, 7)", - () if config.db.dialect.positional else {}, - ) - ) - - @testing.requires.tuple_in - def test_execute_tuple_expanding_plus_literal_execute(self): - table = self.tables.some_table - - stmt = select(table.c.id).where( - tuple_(table.c.x, table.c.y).in_( - bindparam("q", expanding=True, literal_execute=True) - ) - ) - - with self.sql_execution_asserter() as asserter: - with config.db.connect() as conn: - conn.execute(stmt, dict(q=[(5, 10), (12, 18)])) - - asserter.assert_( - CursorSQL( - "SELECT some_table.id \nFROM some_table " - "\nWHERE (some_table.x, some_table.y) " - "IN (%s(5, 10), (12, 18))" - % ("VALUES " if config.db.dialect.tuple_in_values else ""), - () if config.db.dialect.positional else {}, - ) - ) - - @testing.requires.tuple_in - def test_execute_tuple_expanding_plus_literal_heterogeneous_execute(self): - table = self.tables.some_table - - stmt = select(table.c.id).where( - tuple_(table.c.x, table.c.z).in_( - bindparam("q", expanding=True, literal_execute=True) - ) - ) - - with self.sql_execution_asserter() as asserter: - with config.db.connect() as conn: - conn.execute(stmt, dict(q=[(5, "z1"), (12, "z3")])) - - asserter.assert_( - CursorSQL( - "SELECT some_table.id \nFROM some_table " - "\nWHERE (some_table.x, some_table.z) " - "IN (%s(5, 'z1'), (12, 'z3'))" - % ("VALUES " if config.db.dialect.tuple_in_values else ""), - () if config.db.dialect.positional else {}, - ) - ) - - -class ExpandingBoundInTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("x", Integer), - Column("y", Integer), - Column("z", String(50)), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "x": 1, "y": 2, "z": "z1"}, - {"id": 2, "x": 2, "y": 3, "z": "z2"}, - {"id": 3, "x": 3, "y": 4, "z": "z3"}, - {"id": 4, "x": 4, "y": 5, "z": "z4"}, - ], - ) - - def _assert_result(self, select, result, params=()): - with config.db.connect() as conn: - eq_(conn.execute(select, params).fetchall(), result) - - def test_multiple_empty_sets_bindparam(self): - # test that any anonymous aliasing used by the dialect - # is fine with duplicates - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.x.in_(bindparam("q"))) - .where(table.c.y.in_(bindparam("p"))) - .order_by(table.c.id) - ) - self._assert_result(stmt, [], params={"q": [], "p": []}) - - def test_multiple_empty_sets_direct(self): - # test that any anonymous aliasing used by the dialect - # is fine with duplicates - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.x.in_([])) - .where(table.c.y.in_([])) - .order_by(table.c.id) - ) - self._assert_result(stmt, []) - - @testing.requires.tuple_in_w_empty - def test_empty_heterogeneous_tuples_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(tuple_(table.c.x, table.c.z).in_(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result(stmt, [], params={"q": []}) - - @testing.requires.tuple_in_w_empty - def test_empty_heterogeneous_tuples_direct(self): - table = self.tables.some_table - - def go(val, expected): - stmt = ( - select(table.c.id) - .where(tuple_(table.c.x, table.c.z).in_(val)) - .order_by(table.c.id) - ) - self._assert_result(stmt, expected) - - go([], []) - go([(2, "z2"), (3, "z3"), (4, "z4")], [(2,), (3,), (4,)]) - go([], []) - - @testing.requires.tuple_in_w_empty - def test_empty_homogeneous_tuples_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(tuple_(table.c.x, table.c.y).in_(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result(stmt, [], params={"q": []}) - - @testing.requires.tuple_in_w_empty - def test_empty_homogeneous_tuples_direct(self): - table = self.tables.some_table - - def go(val, expected): - stmt = ( - select(table.c.id) - .where(tuple_(table.c.x, table.c.y).in_(val)) - .order_by(table.c.id) - ) - self._assert_result(stmt, expected) - - go([], []) - go([(1, 2), (2, 3), (3, 4)], [(1,), (2,), (3,)]) - go([], []) - - def test_bound_in_scalar_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.x.in_(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result(stmt, [(2,), (3,), (4,)], params={"q": [2, 3, 4]}) - - def test_bound_in_scalar_direct(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.x.in_([2, 3, 4])) - .order_by(table.c.id) - ) - self._assert_result(stmt, [(2,), (3,), (4,)]) - - def test_nonempty_in_plus_empty_notin(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.x.in_([2, 3])) - .where(table.c.id.not_in([])) - .order_by(table.c.id) - ) - self._assert_result(stmt, [(2,), (3,)]) - - def test_empty_in_plus_notempty_notin(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.x.in_([])) - .where(table.c.id.not_in([2, 3])) - .order_by(table.c.id) - ) - self._assert_result(stmt, []) - - def test_typed_str_in(self): - """test related to #7292. - - as a type is given to the bound param, there is no ambiguity - to the type of element. - - """ - - stmt = text( - "select id FROM some_table WHERE z IN :q ORDER BY id" - ).bindparams(bindparam("q", type_=String, expanding=True)) - self._assert_result( - stmt, - [(2,), (3,), (4,)], - params={"q": ["z2", "z3", "z4"]}, - ) - - def test_untyped_str_in(self): - """test related to #7292. - - for untyped expression, we look at the types of elements. - Test for Sequence to detect tuple in. but not strings or bytes! - as always.... - - """ - - stmt = text( - "select id FROM some_table WHERE z IN :q ORDER BY id" - ).bindparams(bindparam("q", expanding=True)) - self._assert_result( - stmt, - [(2,), (3,), (4,)], - params={"q": ["z2", "z3", "z4"]}, - ) - - @testing.requires.tuple_in - def test_bound_in_two_tuple_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(tuple_(table.c.x, table.c.y).in_(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result( - stmt, [(2,), (3,), (4,)], params={"q": [(2, 3), (3, 4), (4, 5)]} - ) - - @testing.requires.tuple_in - def test_bound_in_two_tuple_direct(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(tuple_(table.c.x, table.c.y).in_([(2, 3), (3, 4), (4, 5)])) - .order_by(table.c.id) - ) - self._assert_result(stmt, [(2,), (3,), (4,)]) - - @testing.requires.tuple_in - def test_bound_in_heterogeneous_two_tuple_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(tuple_(table.c.x, table.c.z).in_(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result( - stmt, - [(2,), (3,), (4,)], - params={"q": [(2, "z2"), (3, "z3"), (4, "z4")]}, - ) - - @testing.requires.tuple_in - def test_bound_in_heterogeneous_two_tuple_direct(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where( - tuple_(table.c.x, table.c.z).in_( - [(2, "z2"), (3, "z3"), (4, "z4")] - ) - ) - .order_by(table.c.id) - ) - self._assert_result( - stmt, - [(2,), (3,), (4,)], - ) - - @testing.requires.tuple_in - def test_bound_in_heterogeneous_two_tuple_text_bindparam(self): - # note this becomes ARRAY if we dont use expanding - # explicitly right now - stmt = text( - "select id FROM some_table WHERE (x, z) IN :q ORDER BY id" - ).bindparams(bindparam("q", expanding=True)) - self._assert_result( - stmt, - [(2,), (3,), (4,)], - params={"q": [(2, "z2"), (3, "z3"), (4, "z4")]}, - ) - - @testing.requires.tuple_in - def test_bound_in_heterogeneous_two_tuple_typed_bindparam_non_tuple(self): - class LikeATuple(collections_abc.Sequence): - def __init__(self, *data): - self._data = data - - def __iter__(self): - return iter(self._data) - - def __getitem__(self, idx): - return self._data[idx] - - def __len__(self): - return len(self._data) - - stmt = text( - "select id FROM some_table WHERE (x, z) IN :q ORDER BY id" - ).bindparams( - bindparam( - "q", type_=TupleType(Integer(), String()), expanding=True - ) - ) - self._assert_result( - stmt, - [(2,), (3,), (4,)], - params={ - "q": [ - LikeATuple(2, "z2"), - LikeATuple(3, "z3"), - LikeATuple(4, "z4"), - ] - }, - ) - - @testing.requires.tuple_in - def test_bound_in_heterogeneous_two_tuple_text_bindparam_non_tuple(self): - # note this becomes ARRAY if we dont use expanding - # explicitly right now - - class LikeATuple(collections_abc.Sequence): - def __init__(self, *data): - self._data = data - - def __iter__(self): - return iter(self._data) - - def __getitem__(self, idx): - return self._data[idx] - - def __len__(self): - return len(self._data) - - stmt = text( - "select id FROM some_table WHERE (x, z) IN :q ORDER BY id" - ).bindparams(bindparam("q", expanding=True)) - self._assert_result( - stmt, - [(2,), (3,), (4,)], - params={ - "q": [ - LikeATuple(2, "z2"), - LikeATuple(3, "z3"), - LikeATuple(4, "z4"), - ] - }, - ) - - def test_empty_set_against_integer_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.x.in_(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result(stmt, [], params={"q": []}) - - def test_empty_set_against_integer_direct(self): - table = self.tables.some_table - stmt = select(table.c.id).where(table.c.x.in_([])).order_by(table.c.id) - self._assert_result(stmt, []) - - def test_empty_set_against_integer_negation_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.x.not_in(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result(stmt, [(1,), (2,), (3,), (4,)], params={"q": []}) - - def test_empty_set_against_integer_negation_direct(self): - table = self.tables.some_table - stmt = ( - select(table.c.id).where(table.c.x.not_in([])).order_by(table.c.id) - ) - self._assert_result(stmt, [(1,), (2,), (3,), (4,)]) - - def test_empty_set_against_string_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.z.in_(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result(stmt, [], params={"q": []}) - - def test_empty_set_against_string_direct(self): - table = self.tables.some_table - stmt = select(table.c.id).where(table.c.z.in_([])).order_by(table.c.id) - self._assert_result(stmt, []) - - def test_empty_set_against_string_negation_bindparam(self): - table = self.tables.some_table - stmt = ( - select(table.c.id) - .where(table.c.z.not_in(bindparam("q"))) - .order_by(table.c.id) - ) - self._assert_result(stmt, [(1,), (2,), (3,), (4,)], params={"q": []}) - - def test_empty_set_against_string_negation_direct(self): - table = self.tables.some_table - stmt = ( - select(table.c.id).where(table.c.z.not_in([])).order_by(table.c.id) - ) - self._assert_result(stmt, [(1,), (2,), (3,), (4,)]) - - def test_null_in_empty_set_is_false_bindparam(self, connection): - stmt = select( - case( - ( - null().in_(bindparam("foo", value=())), - true(), - ), - else_=false(), - ) - ) - in_(connection.execute(stmt).fetchone()[0], (False, 0)) - - def test_null_in_empty_set_is_false_direct(self, connection): - stmt = select( - case( - ( - null().in_([]), - true(), - ), - else_=false(), - ) - ) - in_(connection.execute(stmt).fetchone()[0], (False, 0)) - - -class LikeFunctionsTest(fixtures.TablesTest): - __backend__ = True - - run_inserts = "once" - run_deletes = None - - @classmethod - def define_tables(cls, metadata): - Table( - "some_table", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.some_table.insert(), - [ - {"id": 1, "data": "abcdefg"}, - {"id": 2, "data": "ab/cdefg"}, - {"id": 3, "data": "ab%cdefg"}, - {"id": 4, "data": "ab_cdefg"}, - {"id": 5, "data": "abcde/fg"}, - {"id": 6, "data": "abcde%fg"}, - {"id": 7, "data": "ab#cdefg"}, - {"id": 8, "data": "ab9cdefg"}, - {"id": 9, "data": "abcde#fg"}, - {"id": 10, "data": "abcd9fg"}, - {"id": 11, "data": None}, - ], - ) - - def _test(self, expr, expected): - some_table = self.tables.some_table - - with config.db.connect() as conn: - rows = { - value - for value, in conn.execute(select(some_table.c.id).where(expr)) - } - - eq_(rows, expected) - - def test_startswith_unescaped(self): - col = self.tables.some_table.c.data - self._test(col.startswith("ab%c"), {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) - - def test_startswith_autoescape(self): - col = self.tables.some_table.c.data - self._test(col.startswith("ab%c", autoescape=True), {3}) - - def test_startswith_sqlexpr(self): - col = self.tables.some_table.c.data - self._test( - col.startswith(literal_column("'ab%c'")), - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - ) - - def test_startswith_escape(self): - col = self.tables.some_table.c.data - self._test(col.startswith("ab##c", escape="#"), {7}) - - def test_startswith_autoescape_escape(self): - col = self.tables.some_table.c.data - self._test(col.startswith("ab%c", autoescape=True, escape="#"), {3}) - self._test(col.startswith("ab#c", autoescape=True, escape="#"), {7}) - - def test_endswith_unescaped(self): - col = self.tables.some_table.c.data - self._test(col.endswith("e%fg"), {1, 2, 3, 4, 5, 6, 7, 8, 9}) - - def test_endswith_sqlexpr(self): - col = self.tables.some_table.c.data - self._test( - col.endswith(literal_column("'e%fg'")), {1, 2, 3, 4, 5, 6, 7, 8, 9} - ) - - def test_endswith_autoescape(self): - col = self.tables.some_table.c.data - self._test(col.endswith("e%fg", autoescape=True), {6}) - - def test_endswith_escape(self): - col = self.tables.some_table.c.data - self._test(col.endswith("e##fg", escape="#"), {9}) - - def test_endswith_autoescape_escape(self): - col = self.tables.some_table.c.data - self._test(col.endswith("e%fg", autoescape=True, escape="#"), {6}) - self._test(col.endswith("e#fg", autoescape=True, escape="#"), {9}) - - def test_contains_unescaped(self): - col = self.tables.some_table.c.data - self._test(col.contains("b%cde"), {1, 2, 3, 4, 5, 6, 7, 8, 9}) - - def test_contains_autoescape(self): - col = self.tables.some_table.c.data - self._test(col.contains("b%cde", autoescape=True), {3}) - - def test_contains_escape(self): - col = self.tables.some_table.c.data - self._test(col.contains("b##cde", escape="#"), {7}) - - def test_contains_autoescape_escape(self): - col = self.tables.some_table.c.data - self._test(col.contains("b%cd", autoescape=True, escape="#"), {3}) - self._test(col.contains("b#cd", autoescape=True, escape="#"), {7}) - - @testing.requires.regexp_match - def test_not_regexp_match(self): - col = self.tables.some_table.c.data - self._test(~col.regexp_match("a.cde"), {2, 3, 4, 7, 8, 10}) - - @testing.requires.regexp_replace - def test_regexp_replace(self): - col = self.tables.some_table.c.data - self._test( - col.regexp_replace("a.cde", "FOO").contains("FOO"), {1, 5, 6, 9} - ) - - @testing.requires.regexp_match - @testing.combinations( - ("a.cde", {1, 5, 6, 9}), - ("abc", {1, 5, 6, 9, 10}), - ("^abc", {1, 5, 6, 9, 10}), - ("9cde", {8}), - ("^a", set(range(1, 11))), - ("(b|c)", set(range(1, 11))), - ("^(b|c)", set()), - ) - def test_regexp_match(self, text, expected): - col = self.tables.some_table.c.data - self._test(col.regexp_match(text), expected) - - -class ComputedColumnTest(fixtures.TablesTest): - __backend__ = True - __requires__ = ("computed_columns",) - - @classmethod - def define_tables(cls, metadata): - Table( - "square", - metadata, - Column("id", Integer, primary_key=True), - Column("side", Integer), - Column("area", Integer, Computed("side * side")), - Column("perimeter", Integer, Computed("4 * side")), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.square.insert(), - [{"id": 1, "side": 10}, {"id": 10, "side": 42}], - ) - - def test_select_all(self): - with config.db.connect() as conn: - res = conn.execute( - select(text("*")) - .select_from(self.tables.square) - .order_by(self.tables.square.c.id) - ).fetchall() - eq_(res, [(1, 10, 100, 40), (10, 42, 1764, 168)]) - - def test_select_columns(self): - with config.db.connect() as conn: - res = conn.execute( - select( - self.tables.square.c.area, self.tables.square.c.perimeter - ) - .select_from(self.tables.square) - .order_by(self.tables.square.c.id) - ).fetchall() - eq_(res, [(100, 40), (1764, 168)]) - - -class IdentityColumnTest(fixtures.TablesTest): - __backend__ = True - __requires__ = ("identity_columns",) - run_inserts = "once" - run_deletes = "once" - - @classmethod - def define_tables(cls, metadata): - Table( - "tbl_a", - metadata, - Column( - "id", - Integer, - Identity( - always=True, start=42, nominvalue=True, nomaxvalue=True - ), - primary_key=True, - ), - Column("desc", String(100)), - ) - Table( - "tbl_b", - metadata, - Column( - "id", - Integer, - Identity(increment=-5, start=0, minvalue=-1000, maxvalue=0), - primary_key=True, - ), - Column("desc", String(100)), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.tbl_a.insert(), - [{"desc": "a"}, {"desc": "b"}], - ) - connection.execute( - cls.tables.tbl_b.insert(), - [{"desc": "a"}, {"desc": "b"}], - ) - connection.execute( - cls.tables.tbl_b.insert(), - [{"id": 42, "desc": "c"}], - ) - - def test_select_all(self, connection): - res = connection.execute( - select(text("*")) - .select_from(self.tables.tbl_a) - .order_by(self.tables.tbl_a.c.id) - ).fetchall() - eq_(res, [(42, "a"), (43, "b")]) - - res = connection.execute( - select(text("*")) - .select_from(self.tables.tbl_b) - .order_by(self.tables.tbl_b.c.id) - ).fetchall() - eq_(res, [(-5, "b"), (0, "a"), (42, "c")]) - - def test_select_columns(self, connection): - res = connection.execute( - select(self.tables.tbl_a.c.id).order_by(self.tables.tbl_a.c.id) - ).fetchall() - eq_(res, [(42,), (43,)]) - - @testing.requires.identity_columns_standard - def test_insert_always_error(self, connection): - def fn(): - connection.execute( - self.tables.tbl_a.insert(), - [{"id": 200, "desc": "a"}], - ) - - assert_raises((DatabaseError, ProgrammingError), fn) - - -class IdentityAutoincrementTest(fixtures.TablesTest): - __backend__ = True - __requires__ = ("autoincrement_without_sequence",) - - @classmethod - def define_tables(cls, metadata): - Table( - "tbl", - metadata, - Column( - "id", - Integer, - Identity(), - primary_key=True, - autoincrement=True, - ), - Column("desc", String(100)), - ) - - def test_autoincrement_with_identity(self, connection): - res = connection.execute(self.tables.tbl.insert(), {"desc": "row"}) - res = connection.execute(self.tables.tbl.select()).first() - eq_(res, (1, "row")) - - -class ExistsTest(fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "stuff", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.stuff.insert(), - [ - {"id": 1, "data": "some data"}, - {"id": 2, "data": "some data"}, - {"id": 3, "data": "some data"}, - {"id": 4, "data": "some other data"}, - ], - ) - - def test_select_exists(self, connection): - stuff = self.tables.stuff - eq_( - connection.execute( - select(literal(1)).where( - exists().where(stuff.c.data == "some data") - ) - ).fetchall(), - [(1,)], - ) - - def test_select_exists_false(self, connection): - stuff = self.tables.stuff - eq_( - connection.execute( - select(literal(1)).where( - exists().where(stuff.c.data == "no data") - ) - ).fetchall(), - [], - ) - - -class DistinctOnTest(AssertsCompiledSQL, fixtures.TablesTest): - __backend__ = True - - @testing.fails_if(testing.requires.supports_distinct_on) - def test_distinct_on(self): - stm = select("*").distinct(column("q")).select_from(table("foo")) - with testing.expect_deprecated( - "DISTINCT ON is currently supported only by the PostgreSQL " - ): - self.assert_compile(stm, "SELECT DISTINCT * FROM foo") - - -class IsOrIsNotDistinctFromTest(fixtures.TablesTest): - __backend__ = True - __requires__ = ("supports_is_distinct_from",) - - @classmethod - def define_tables(cls, metadata): - Table( - "is_distinct_test", - metadata, - Column("id", Integer, primary_key=True), - Column("col_a", Integer, nullable=True), - Column("col_b", Integer, nullable=True), - ) - - @testing.combinations( - ("both_int_different", 0, 1, 1), - ("both_int_same", 1, 1, 0), - ("one_null_first", None, 1, 1), - ("one_null_second", 0, None, 1), - ("both_null", None, None, 0), - id_="iaaa", - argnames="col_a_value, col_b_value, expected_row_count_for_is", - ) - def test_is_or_is_not_distinct_from( - self, col_a_value, col_b_value, expected_row_count_for_is, connection - ): - tbl = self.tables.is_distinct_test - - connection.execute( - tbl.insert(), - [{"id": 1, "col_a": col_a_value, "col_b": col_b_value}], - ) - - result = connection.execute( - tbl.select().where(tbl.c.col_a.is_distinct_from(tbl.c.col_b)) - ).fetchall() - eq_( - len(result), - expected_row_count_for_is, - ) - - expected_row_count_for_is_not = ( - 1 if expected_row_count_for_is == 0 else 0 - ) - result = connection.execute( - tbl.select().where(tbl.c.col_a.is_not_distinct_from(tbl.c.col_b)) - ).fetchall() - eq_( - len(result), - expected_row_count_for_is_not, - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_sequence.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_sequence.py deleted file mode 100644 index 138616f..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_sequence.py +++ /dev/null @@ -1,317 +0,0 @@ -# testing/suite/test_sequence.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from .. import config -from .. import fixtures -from ..assertions import eq_ -from ..assertions import is_true -from ..config import requirements -from ..provision import normalize_sequence -from ..schema import Column -from ..schema import Table -from ... import inspect -from ... import Integer -from ... import MetaData -from ... import Sequence -from ... import String -from ... import testing - - -class SequenceTest(fixtures.TablesTest): - __requires__ = ("sequences",) - __backend__ = True - - run_create_tables = "each" - - @classmethod - def define_tables(cls, metadata): - Table( - "seq_pk", - metadata, - Column( - "id", - Integer, - normalize_sequence(config, Sequence("tab_id_seq")), - primary_key=True, - ), - Column("data", String(50)), - ) - - Table( - "seq_opt_pk", - metadata, - Column( - "id", - Integer, - normalize_sequence( - config, - Sequence("tab_id_seq", data_type=Integer, optional=True), - ), - primary_key=True, - ), - Column("data", String(50)), - ) - - Table( - "seq_no_returning", - metadata, - Column( - "id", - Integer, - normalize_sequence(config, Sequence("noret_id_seq")), - primary_key=True, - ), - Column("data", String(50)), - implicit_returning=False, - ) - - if testing.requires.schemas.enabled: - Table( - "seq_no_returning_sch", - metadata, - Column( - "id", - Integer, - normalize_sequence( - config, - Sequence( - "noret_sch_id_seq", schema=config.test_schema - ), - ), - primary_key=True, - ), - Column("data", String(50)), - implicit_returning=False, - schema=config.test_schema, - ) - - def test_insert_roundtrip(self, connection): - connection.execute(self.tables.seq_pk.insert(), dict(data="some data")) - self._assert_round_trip(self.tables.seq_pk, connection) - - def test_insert_lastrowid(self, connection): - r = connection.execute( - self.tables.seq_pk.insert(), dict(data="some data") - ) - eq_( - r.inserted_primary_key, (testing.db.dialect.default_sequence_base,) - ) - - def test_nextval_direct(self, connection): - r = connection.scalar(self.tables.seq_pk.c.id.default) - eq_(r, testing.db.dialect.default_sequence_base) - - @requirements.sequences_optional - def test_optional_seq(self, connection): - r = connection.execute( - self.tables.seq_opt_pk.insert(), dict(data="some data") - ) - eq_(r.inserted_primary_key, (1,)) - - def _assert_round_trip(self, table, conn): - row = conn.execute(table.select()).first() - eq_(row, (testing.db.dialect.default_sequence_base, "some data")) - - def test_insert_roundtrip_no_implicit_returning(self, connection): - connection.execute( - self.tables.seq_no_returning.insert(), dict(data="some data") - ) - self._assert_round_trip(self.tables.seq_no_returning, connection) - - @testing.combinations((True,), (False,), argnames="implicit_returning") - @testing.requires.schemas - def test_insert_roundtrip_translate(self, connection, implicit_returning): - seq_no_returning = Table( - "seq_no_returning_sch", - MetaData(), - Column( - "id", - Integer, - normalize_sequence( - config, Sequence("noret_sch_id_seq", schema="alt_schema") - ), - primary_key=True, - ), - Column("data", String(50)), - implicit_returning=implicit_returning, - schema="alt_schema", - ) - - connection = connection.execution_options( - schema_translate_map={"alt_schema": config.test_schema} - ) - connection.execute(seq_no_returning.insert(), dict(data="some data")) - self._assert_round_trip(seq_no_returning, connection) - - @testing.requires.schemas - def test_nextval_direct_schema_translate(self, connection): - seq = normalize_sequence( - config, Sequence("noret_sch_id_seq", schema="alt_schema") - ) - connection = connection.execution_options( - schema_translate_map={"alt_schema": config.test_schema} - ) - - r = connection.scalar(seq) - eq_(r, testing.db.dialect.default_sequence_base) - - -class SequenceCompilerTest(testing.AssertsCompiledSQL, fixtures.TestBase): - __requires__ = ("sequences",) - __backend__ = True - - def test_literal_binds_inline_compile(self, connection): - table = Table( - "x", - MetaData(), - Column( - "y", Integer, normalize_sequence(config, Sequence("y_seq")) - ), - Column("q", Integer), - ) - - stmt = table.insert().values(q=5) - - seq_nextval = connection.dialect.statement_compiler( - statement=None, dialect=connection.dialect - ).visit_sequence(normalize_sequence(config, Sequence("y_seq"))) - self.assert_compile( - stmt, - "INSERT INTO x (y, q) VALUES (%s, 5)" % (seq_nextval,), - literal_binds=True, - dialect=connection.dialect, - ) - - -class HasSequenceTest(fixtures.TablesTest): - run_deletes = None - - __requires__ = ("sequences",) - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - normalize_sequence(config, Sequence("user_id_seq", metadata=metadata)) - normalize_sequence( - config, - Sequence( - "other_seq", - metadata=metadata, - nomaxvalue=True, - nominvalue=True, - ), - ) - if testing.requires.schemas.enabled: - normalize_sequence( - config, - Sequence( - "user_id_seq", schema=config.test_schema, metadata=metadata - ), - ) - normalize_sequence( - config, - Sequence( - "schema_seq", schema=config.test_schema, metadata=metadata - ), - ) - Table( - "user_id_table", - metadata, - Column("id", Integer, primary_key=True), - ) - - def test_has_sequence(self, connection): - eq_(inspect(connection).has_sequence("user_id_seq"), True) - - def test_has_sequence_cache(self, connection, metadata): - insp = inspect(connection) - eq_(insp.has_sequence("user_id_seq"), True) - ss = normalize_sequence(config, Sequence("new_seq", metadata=metadata)) - eq_(insp.has_sequence("new_seq"), False) - ss.create(connection) - try: - eq_(insp.has_sequence("new_seq"), False) - insp.clear_cache() - eq_(insp.has_sequence("new_seq"), True) - finally: - ss.drop(connection) - - def test_has_sequence_other_object(self, connection): - eq_(inspect(connection).has_sequence("user_id_table"), False) - - @testing.requires.schemas - def test_has_sequence_schema(self, connection): - eq_( - inspect(connection).has_sequence( - "user_id_seq", schema=config.test_schema - ), - True, - ) - - def test_has_sequence_neg(self, connection): - eq_(inspect(connection).has_sequence("some_sequence"), False) - - @testing.requires.schemas - def test_has_sequence_schemas_neg(self, connection): - eq_( - inspect(connection).has_sequence( - "some_sequence", schema=config.test_schema - ), - False, - ) - - @testing.requires.schemas - def test_has_sequence_default_not_in_remote(self, connection): - eq_( - inspect(connection).has_sequence( - "other_sequence", schema=config.test_schema - ), - False, - ) - - @testing.requires.schemas - def test_has_sequence_remote_not_in_default(self, connection): - eq_(inspect(connection).has_sequence("schema_seq"), False) - - def test_get_sequence_names(self, connection): - exp = {"other_seq", "user_id_seq"} - - res = set(inspect(connection).get_sequence_names()) - is_true(res.intersection(exp) == exp) - is_true("schema_seq" not in res) - - @testing.requires.schemas - def test_get_sequence_names_no_sequence_schema(self, connection): - eq_( - inspect(connection).get_sequence_names( - schema=config.test_schema_2 - ), - [], - ) - - @testing.requires.schemas - def test_get_sequence_names_sequences_schema(self, connection): - eq_( - sorted( - inspect(connection).get_sequence_names( - schema=config.test_schema - ) - ), - ["schema_seq", "user_id_seq"], - ) - - -class HasSequenceTestEmpty(fixtures.TestBase): - __requires__ = ("sequences",) - __backend__ = True - - def test_get_sequence_names_no_sequence(self, connection): - eq_( - inspect(connection).get_sequence_names(), - [], - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_types.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_types.py deleted file mode 100644 index 4a7c1f1..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_types.py +++ /dev/null @@ -1,2071 +0,0 @@ -# testing/suite/test_types.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -import datetime -import decimal -import json -import re -import uuid - -from .. import config -from .. import engines -from .. import fixtures -from .. import mock -from ..assertions import eq_ -from ..assertions import is_ -from ..assertions import ne_ -from ..config import requirements -from ..schema import Column -from ..schema import Table -from ... import and_ -from ... import ARRAY -from ... import BigInteger -from ... import bindparam -from ... import Boolean -from ... import case -from ... import cast -from ... import Date -from ... import DateTime -from ... import Float -from ... import Integer -from ... import Interval -from ... import JSON -from ... import literal -from ... import literal_column -from ... import MetaData -from ... import null -from ... import Numeric -from ... import select -from ... import String -from ... import testing -from ... import Text -from ... import Time -from ... import TIMESTAMP -from ... import type_coerce -from ... import TypeDecorator -from ... import Unicode -from ... import UnicodeText -from ... import UUID -from ... import Uuid -from ...orm import declarative_base -from ...orm import Session -from ...sql import sqltypes -from ...sql.sqltypes import LargeBinary -from ...sql.sqltypes import PickleType - - -class _LiteralRoundTripFixture: - supports_whereclause = True - - @testing.fixture - def literal_round_trip(self, metadata, connection): - """test literal rendering""" - - # for literal, we test the literal render in an INSERT - # into a typed column. we can then SELECT it back as its - # official type; ideally we'd be able to use CAST here - # but MySQL in particular can't CAST fully - - def run( - type_, - input_, - output, - filter_=None, - compare=None, - support_whereclause=True, - ): - t = Table("t", metadata, Column("x", type_)) - t.create(connection) - - for value in input_: - ins = t.insert().values( - x=literal(value, type_, literal_execute=True) - ) - connection.execute(ins) - - ins = t.insert().values( - x=literal(None, type_, literal_execute=True) - ) - connection.execute(ins) - - if support_whereclause and self.supports_whereclause: - if compare: - stmt = t.select().where( - t.c.x - == literal( - compare, - type_, - literal_execute=True, - ), - t.c.x - == literal( - input_[0], - type_, - literal_execute=True, - ), - ) - else: - stmt = t.select().where( - t.c.x - == literal( - compare if compare is not None else input_[0], - type_, - literal_execute=True, - ) - ) - else: - stmt = t.select().where(t.c.x.is_not(None)) - - rows = connection.execute(stmt).all() - assert rows, "No rows returned" - for row in rows: - value = row[0] - if filter_ is not None: - value = filter_(value) - assert value in output - - stmt = t.select().where(t.c.x.is_(None)) - rows = connection.execute(stmt).all() - eq_(rows, [(None,)]) - - return run - - -class _UnicodeFixture(_LiteralRoundTripFixture, fixtures.TestBase): - __requires__ = ("unicode_data",) - - data = ( - "Alors vous imaginez ma 🐍 surprise, au lever du jour, " - "quand une drôle de petite 🐍 voix m’a réveillé. Elle " - "disait: « S’il vous plaît… dessine-moi 🐍 un mouton! »" - ) - - @property - def supports_whereclause(self): - return config.requirements.expressions_against_unbounded_text.enabled - - @classmethod - def define_tables(cls, metadata): - Table( - "unicode_table", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("unicode_data", cls.datatype), - ) - - def test_round_trip(self, connection): - unicode_table = self.tables.unicode_table - - connection.execute( - unicode_table.insert(), {"id": 1, "unicode_data": self.data} - ) - - row = connection.execute(select(unicode_table.c.unicode_data)).first() - - eq_(row, (self.data,)) - assert isinstance(row[0], str) - - def test_round_trip_executemany(self, connection): - unicode_table = self.tables.unicode_table - - connection.execute( - unicode_table.insert(), - [{"id": i, "unicode_data": self.data} for i in range(1, 4)], - ) - - rows = connection.execute( - select(unicode_table.c.unicode_data) - ).fetchall() - eq_(rows, [(self.data,) for i in range(1, 4)]) - for row in rows: - assert isinstance(row[0], str) - - def _test_null_strings(self, connection): - unicode_table = self.tables.unicode_table - - connection.execute( - unicode_table.insert(), {"id": 1, "unicode_data": None} - ) - row = connection.execute(select(unicode_table.c.unicode_data)).first() - eq_(row, (None,)) - - def _test_empty_strings(self, connection): - unicode_table = self.tables.unicode_table - - connection.execute( - unicode_table.insert(), {"id": 1, "unicode_data": ""} - ) - row = connection.execute(select(unicode_table.c.unicode_data)).first() - eq_(row, ("",)) - - def test_literal(self, literal_round_trip): - literal_round_trip(self.datatype, [self.data], [self.data]) - - def test_literal_non_ascii(self, literal_round_trip): - literal_round_trip(self.datatype, ["réve🐍 illé"], ["réve🐍 illé"]) - - -class UnicodeVarcharTest(_UnicodeFixture, fixtures.TablesTest): - __requires__ = ("unicode_data",) - __backend__ = True - - datatype = Unicode(255) - - @requirements.empty_strings_varchar - def test_empty_strings_varchar(self, connection): - self._test_empty_strings(connection) - - def test_null_strings_varchar(self, connection): - self._test_null_strings(connection) - - -class UnicodeTextTest(_UnicodeFixture, fixtures.TablesTest): - __requires__ = "unicode_data", "text_type" - __backend__ = True - - datatype = UnicodeText() - - @requirements.empty_strings_text - def test_empty_strings_text(self, connection): - self._test_empty_strings(connection) - - def test_null_strings_text(self, connection): - self._test_null_strings(connection) - - -class ArrayTest(_LiteralRoundTripFixture, fixtures.TablesTest): - """Add ARRAY test suite, #8138. - - This only works on PostgreSQL right now. - - """ - - __requires__ = ("array_type",) - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "array_table", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("single_dim", ARRAY(Integer)), - Column("multi_dim", ARRAY(String, dimensions=2)), - ) - - def test_array_roundtrip(self, connection): - array_table = self.tables.array_table - - connection.execute( - array_table.insert(), - { - "id": 1, - "single_dim": [1, 2, 3], - "multi_dim": [["one", "two"], ["thr'ee", "réve🐍 illé"]], - }, - ) - row = connection.execute( - select(array_table.c.single_dim, array_table.c.multi_dim) - ).first() - eq_(row, ([1, 2, 3], [["one", "two"], ["thr'ee", "réve🐍 illé"]])) - - def test_literal_simple(self, literal_round_trip): - literal_round_trip( - ARRAY(Integer), - ([1, 2, 3],), - ([1, 2, 3],), - support_whereclause=False, - ) - - def test_literal_complex(self, literal_round_trip): - literal_round_trip( - ARRAY(String, dimensions=2), - ([["one", "two"], ["thr'ee", "réve🐍 illé"]],), - ([["one", "two"], ["thr'ee", "réve🐍 illé"]],), - support_whereclause=False, - ) - - -class BinaryTest(_LiteralRoundTripFixture, fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "binary_table", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("binary_data", LargeBinary), - Column("pickle_data", PickleType), - ) - - @testing.combinations(b"this is binary", b"7\xe7\x9f", argnames="data") - def test_binary_roundtrip(self, connection, data): - binary_table = self.tables.binary_table - - connection.execute( - binary_table.insert(), {"id": 1, "binary_data": data} - ) - row = connection.execute(select(binary_table.c.binary_data)).first() - eq_(row, (data,)) - - def test_pickle_roundtrip(self, connection): - binary_table = self.tables.binary_table - - connection.execute( - binary_table.insert(), - {"id": 1, "pickle_data": {"foo": [1, 2, 3], "bar": "bat"}}, - ) - row = connection.execute(select(binary_table.c.pickle_data)).first() - eq_(row, ({"foo": [1, 2, 3], "bar": "bat"},)) - - -class TextTest(_LiteralRoundTripFixture, fixtures.TablesTest): - __requires__ = ("text_type",) - __backend__ = True - - @property - def supports_whereclause(self): - return config.requirements.expressions_against_unbounded_text.enabled - - @classmethod - def define_tables(cls, metadata): - Table( - "text_table", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("text_data", Text), - ) - - def test_text_roundtrip(self, connection): - text_table = self.tables.text_table - - connection.execute( - text_table.insert(), {"id": 1, "text_data": "some text"} - ) - row = connection.execute(select(text_table.c.text_data)).first() - eq_(row, ("some text",)) - - @testing.requires.empty_strings_text - def test_text_empty_strings(self, connection): - text_table = self.tables.text_table - - connection.execute(text_table.insert(), {"id": 1, "text_data": ""}) - row = connection.execute(select(text_table.c.text_data)).first() - eq_(row, ("",)) - - def test_text_null_strings(self, connection): - text_table = self.tables.text_table - - connection.execute(text_table.insert(), {"id": 1, "text_data": None}) - row = connection.execute(select(text_table.c.text_data)).first() - eq_(row, (None,)) - - def test_literal(self, literal_round_trip): - literal_round_trip(Text, ["some text"], ["some text"]) - - @requirements.unicode_data_no_special_types - def test_literal_non_ascii(self, literal_round_trip): - literal_round_trip(Text, ["réve🐍 illé"], ["réve🐍 illé"]) - - def test_literal_quoting(self, literal_round_trip): - data = """some 'text' hey "hi there" that's text""" - literal_round_trip(Text, [data], [data]) - - def test_literal_backslashes(self, literal_round_trip): - data = r"backslash one \ backslash two \\ end" - literal_round_trip(Text, [data], [data]) - - def test_literal_percentsigns(self, literal_round_trip): - data = r"percent % signs %% percent" - literal_round_trip(Text, [data], [data]) - - -class StringTest(_LiteralRoundTripFixture, fixtures.TestBase): - __backend__ = True - - @requirements.unbounded_varchar - def test_nolength_string(self): - metadata = MetaData() - foo = Table("foo", metadata, Column("one", String)) - - foo.create(config.db) - foo.drop(config.db) - - def test_literal(self, literal_round_trip): - # note that in Python 3, this invokes the Unicode - # datatype for the literal part because all strings are unicode - literal_round_trip(String(40), ["some text"], ["some text"]) - - @requirements.unicode_data_no_special_types - def test_literal_non_ascii(self, literal_round_trip): - literal_round_trip(String(40), ["réve🐍 illé"], ["réve🐍 illé"]) - - @testing.combinations( - ("%B%", ["AB", "BC"]), - ("A%C", ["AC"]), - ("A%C%Z", []), - argnames="expr, expected", - ) - def test_dont_truncate_rightside( - self, metadata, connection, expr, expected - ): - t = Table("t", metadata, Column("x", String(2))) - t.create(connection) - - connection.execute(t.insert(), [{"x": "AB"}, {"x": "BC"}, {"x": "AC"}]) - - eq_( - connection.scalars(select(t.c.x).where(t.c.x.like(expr))).all(), - expected, - ) - - def test_literal_quoting(self, literal_round_trip): - data = """some 'text' hey "hi there" that's text""" - literal_round_trip(String(40), [data], [data]) - - def test_literal_backslashes(self, literal_round_trip): - data = r"backslash one \ backslash two \\ end" - literal_round_trip(String(40), [data], [data]) - - def test_concatenate_binary(self, connection): - """dialects with special string concatenation operators should - implement visit_concat_op_binary() and visit_concat_op_clauselist() - in their compiler. - - .. versionchanged:: 2.0 visit_concat_op_clauselist() is also needed - for dialects to override the string concatenation operator. - - """ - eq_(connection.scalar(select(literal("a") + "b")), "ab") - - def test_concatenate_clauselist(self, connection): - """dialects with special string concatenation operators should - implement visit_concat_op_binary() and visit_concat_op_clauselist() - in their compiler. - - .. versionchanged:: 2.0 visit_concat_op_clauselist() is also needed - for dialects to override the string concatenation operator. - - """ - eq_( - connection.scalar(select(literal("a") + "b" + "c" + "d" + "e")), - "abcde", - ) - - -class IntervalTest(_LiteralRoundTripFixture, fixtures.TestBase): - __requires__ = ("datetime_interval",) - __backend__ = True - - datatype = Interval - data = datetime.timedelta(days=1, seconds=4) - - def test_literal(self, literal_round_trip): - literal_round_trip(self.datatype, [self.data], [self.data]) - - def test_select_direct_literal_interval(self, connection): - row = connection.execute(select(literal(self.data))).first() - eq_(row, (self.data,)) - - def test_arithmetic_operation_literal_interval(self, connection): - now = datetime.datetime.now().replace(microsecond=0) - # Able to subtract - row = connection.execute( - select(literal(now) - literal(self.data)) - ).scalar() - eq_(row, now - self.data) - - # Able to Add - row = connection.execute( - select(literal(now) + literal(self.data)) - ).scalar() - eq_(row, now + self.data) - - @testing.fixture - def arithmetic_table_fixture(cls, metadata, connection): - class Decorated(TypeDecorator): - impl = cls.datatype - cache_ok = True - - it = Table( - "interval_table", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("interval_data", cls.datatype), - Column("date_data", DateTime), - Column("decorated_interval_data", Decorated), - ) - it.create(connection) - return it - - def test_arithmetic_operation_table_interval_and_literal_interval( - self, connection, arithmetic_table_fixture - ): - interval_table = arithmetic_table_fixture - data = datetime.timedelta(days=2, seconds=5) - connection.execute( - interval_table.insert(), {"id": 1, "interval_data": data} - ) - # Subtraction Operation - value = connection.execute( - select(interval_table.c.interval_data - literal(self.data)) - ).scalar() - eq_(value, data - self.data) - - # Addition Operation - value = connection.execute( - select(interval_table.c.interval_data + literal(self.data)) - ).scalar() - eq_(value, data + self.data) - - def test_arithmetic_operation_table_date_and_literal_interval( - self, connection, arithmetic_table_fixture - ): - interval_table = arithmetic_table_fixture - now = datetime.datetime.now().replace(microsecond=0) - connection.execute( - interval_table.insert(), {"id": 1, "date_data": now} - ) - # Subtraction Operation - value = connection.execute( - select(interval_table.c.date_data - literal(self.data)) - ).scalar() - eq_(value, (now - self.data)) - - # Addition Operation - value = connection.execute( - select(interval_table.c.date_data + literal(self.data)) - ).scalar() - eq_(value, (now + self.data)) - - -class PrecisionIntervalTest(IntervalTest): - __requires__ = ("datetime_interval",) - __backend__ = True - - datatype = Interval(day_precision=9, second_precision=9) - data = datetime.timedelta(days=103, seconds=4) - - -class _DateFixture(_LiteralRoundTripFixture, fixtures.TestBase): - compare = None - - @classmethod - def define_tables(cls, metadata): - class Decorated(TypeDecorator): - impl = cls.datatype - cache_ok = True - - Table( - "date_table", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("date_data", cls.datatype), - Column("decorated_date_data", Decorated), - ) - - def test_round_trip(self, connection): - date_table = self.tables.date_table - - connection.execute( - date_table.insert(), {"id": 1, "date_data": self.data} - ) - - row = connection.execute(select(date_table.c.date_data)).first() - - compare = self.compare or self.data - eq_(row, (compare,)) - assert isinstance(row[0], type(compare)) - - def test_round_trip_decorated(self, connection): - date_table = self.tables.date_table - - connection.execute( - date_table.insert(), {"id": 1, "decorated_date_data": self.data} - ) - - row = connection.execute( - select(date_table.c.decorated_date_data) - ).first() - - compare = self.compare or self.data - eq_(row, (compare,)) - assert isinstance(row[0], type(compare)) - - def test_null(self, connection): - date_table = self.tables.date_table - - connection.execute(date_table.insert(), {"id": 1, "date_data": None}) - - row = connection.execute(select(date_table.c.date_data)).first() - eq_(row, (None,)) - - @testing.requires.datetime_literals - def test_literal(self, literal_round_trip): - compare = self.compare or self.data - - literal_round_trip( - self.datatype, [self.data], [compare], compare=compare - ) - - @testing.requires.standalone_null_binds_whereclause - def test_null_bound_comparison(self): - # this test is based on an Oracle issue observed in #4886. - # passing NULL for an expression that needs to be interpreted as - # a certain type, does the DBAPI have the info it needs to do this. - date_table = self.tables.date_table - with config.db.begin() as conn: - result = conn.execute( - date_table.insert(), {"id": 1, "date_data": self.data} - ) - id_ = result.inserted_primary_key[0] - stmt = select(date_table.c.id).where( - case( - ( - bindparam("foo", type_=self.datatype) != None, - bindparam("foo", type_=self.datatype), - ), - else_=date_table.c.date_data, - ) - == date_table.c.date_data - ) - - row = conn.execute(stmt, {"foo": None}).first() - eq_(row[0], id_) - - -class DateTimeTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("datetime",) - __backend__ = True - datatype = DateTime - data = datetime.datetime(2012, 10, 15, 12, 57, 18) - - @testing.requires.datetime_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class DateTimeTZTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("datetime_timezone",) - __backend__ = True - datatype = DateTime(timezone=True) - data = datetime.datetime( - 2012, 10, 15, 12, 57, 18, tzinfo=datetime.timezone.utc - ) - - @testing.requires.datetime_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class DateTimeMicrosecondsTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("datetime_microseconds",) - __backend__ = True - datatype = DateTime - data = datetime.datetime(2012, 10, 15, 12, 57, 18, 39642) - - -class TimestampMicrosecondsTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("timestamp_microseconds",) - __backend__ = True - datatype = TIMESTAMP - data = datetime.datetime(2012, 10, 15, 12, 57, 18, 396) - - @testing.requires.timestamp_microseconds_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class TimeTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("time",) - __backend__ = True - datatype = Time - data = datetime.time(12, 57, 18) - - @testing.requires.time_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class TimeTZTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("time_timezone",) - __backend__ = True - datatype = Time(timezone=True) - data = datetime.time(12, 57, 18, tzinfo=datetime.timezone.utc) - - @testing.requires.time_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class TimeMicrosecondsTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("time_microseconds",) - __backend__ = True - datatype = Time - data = datetime.time(12, 57, 18, 396) - - @testing.requires.time_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class DateTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("date",) - __backend__ = True - datatype = Date - data = datetime.date(2012, 10, 15) - - @testing.requires.date_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class DateTimeCoercedToDateTimeTest(_DateFixture, fixtures.TablesTest): - """this particular suite is testing that datetime parameters get - coerced to dates, which tends to be something DBAPIs do. - - """ - - __requires__ = "date", "date_coerces_from_datetime" - __backend__ = True - datatype = Date - data = datetime.datetime(2012, 10, 15, 12, 57, 18) - compare = datetime.date(2012, 10, 15) - - @testing.requires.datetime_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class DateTimeHistoricTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("datetime_historic",) - __backend__ = True - datatype = DateTime - data = datetime.datetime(1850, 11, 10, 11, 52, 35) - - @testing.requires.date_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class DateHistoricTest(_DateFixture, fixtures.TablesTest): - __requires__ = ("date_historic",) - __backend__ = True - datatype = Date - data = datetime.date(1727, 4, 1) - - @testing.requires.date_implicit_bound - def test_select_direct(self, connection): - result = connection.scalar(select(literal(self.data))) - eq_(result, self.data) - - -class IntegerTest(_LiteralRoundTripFixture, fixtures.TestBase): - __backend__ = True - - def test_literal(self, literal_round_trip): - literal_round_trip(Integer, [5], [5]) - - def _huge_ints(): - return testing.combinations( - 2147483649, # 32 bits - 2147483648, # 32 bits - 2147483647, # 31 bits - 2147483646, # 31 bits - -2147483649, # 32 bits - -2147483648, # 32 interestingly, asyncpg accepts this one as int32 - -2147483647, # 31 - -2147483646, # 31 - 0, - 1376537018368127, - -1376537018368127, - argnames="intvalue", - ) - - @_huge_ints() - def test_huge_int_auto_accommodation(self, connection, intvalue): - """test #7909""" - - eq_( - connection.scalar( - select(intvalue).where(literal(intvalue) == intvalue) - ), - intvalue, - ) - - @_huge_ints() - def test_huge_int(self, integer_round_trip, intvalue): - integer_round_trip(BigInteger, intvalue) - - @testing.fixture - def integer_round_trip(self, metadata, connection): - def run(datatype, data): - int_table = Table( - "integer_table", - metadata, - Column( - "id", - Integer, - primary_key=True, - test_needs_autoincrement=True, - ), - Column("integer_data", datatype), - ) - - metadata.create_all(config.db) - - connection.execute( - int_table.insert(), {"id": 1, "integer_data": data} - ) - - row = connection.execute(select(int_table.c.integer_data)).first() - - eq_(row, (data,)) - - assert isinstance(row[0], int) - - return run - - -class CastTypeDecoratorTest(_LiteralRoundTripFixture, fixtures.TestBase): - __backend__ = True - - @testing.fixture - def string_as_int(self): - class StringAsInt(TypeDecorator): - impl = String(50) - cache_ok = True - - def column_expression(self, col): - return cast(col, Integer) - - def bind_expression(self, col): - return cast(type_coerce(col, Integer), String(50)) - - return StringAsInt() - - def test_special_type(self, metadata, connection, string_as_int): - type_ = string_as_int - - t = Table("t", metadata, Column("x", type_)) - t.create(connection) - - connection.execute(t.insert(), [{"x": x} for x in [1, 2, 3]]) - - result = {row[0] for row in connection.execute(t.select())} - eq_(result, {1, 2, 3}) - - result = { - row[0] for row in connection.execute(t.select().where(t.c.x == 2)) - } - eq_(result, {2}) - - -class TrueDivTest(fixtures.TestBase): - __backend__ = True - - @testing.combinations( - ("15", "10", 1.5), - ("-15", "10", -1.5), - argnames="left, right, expected", - ) - def test_truediv_integer(self, connection, left, right, expected): - """test #4926""" - - eq_( - connection.scalar( - select( - literal_column(left, type_=Integer()) - / literal_column(right, type_=Integer()) - ) - ), - expected, - ) - - @testing.combinations( - ("15", "10", 1), ("-15", "5", -3), argnames="left, right, expected" - ) - def test_floordiv_integer(self, connection, left, right, expected): - """test #4926""" - - eq_( - connection.scalar( - select( - literal_column(left, type_=Integer()) - // literal_column(right, type_=Integer()) - ) - ), - expected, - ) - - @testing.combinations( - ("5.52", "2.4", "2.3"), argnames="left, right, expected" - ) - def test_truediv_numeric(self, connection, left, right, expected): - """test #4926""" - - eq_( - connection.scalar( - select( - literal_column(left, type_=Numeric(10, 2)) - / literal_column(right, type_=Numeric(10, 2)) - ) - ), - decimal.Decimal(expected), - ) - - @testing.combinations( - ("5.52", "2.4", 2.3), argnames="left, right, expected" - ) - def test_truediv_float(self, connection, left, right, expected): - """test #4926""" - - eq_( - connection.scalar( - select( - literal_column(left, type_=Float()) - / literal_column(right, type_=Float()) - ) - ), - expected, - ) - - @testing.combinations( - ("5.52", "2.4", "2.0"), argnames="left, right, expected" - ) - def test_floordiv_numeric(self, connection, left, right, expected): - """test #4926""" - - eq_( - connection.scalar( - select( - literal_column(left, type_=Numeric()) - // literal_column(right, type_=Numeric()) - ) - ), - decimal.Decimal(expected), - ) - - def test_truediv_integer_bound(self, connection): - """test #4926""" - - eq_( - connection.scalar(select(literal(15) / literal(10))), - 1.5, - ) - - def test_floordiv_integer_bound(self, connection): - """test #4926""" - - eq_( - connection.scalar(select(literal(15) // literal(10))), - 1, - ) - - -class NumericTest(_LiteralRoundTripFixture, fixtures.TestBase): - __backend__ = True - - @testing.fixture - def do_numeric_test(self, metadata, connection): - def run(type_, input_, output, filter_=None, check_scale=False): - t = Table("t", metadata, Column("x", type_)) - t.create(connection) - connection.execute(t.insert(), [{"x": x} for x in input_]) - - result = {row[0] for row in connection.execute(t.select())} - output = set(output) - if filter_: - result = {filter_(x) for x in result} - output = {filter_(x) for x in output} - eq_(result, output) - if check_scale: - eq_([str(x) for x in result], [str(x) for x in output]) - - connection.execute(t.delete()) - - # test that this is actually a number! - # note we have tiny scale here as we have tests with very - # small scale Numeric types. PostgreSQL will raise an error - # if you use values outside the available scale. - if type_.asdecimal: - test_value = decimal.Decimal("2.9") - add_value = decimal.Decimal("37.12") - else: - test_value = 2.9 - add_value = 37.12 - - connection.execute(t.insert(), {"x": test_value}) - assert_we_are_a_number = connection.scalar( - select(type_coerce(t.c.x + add_value, type_)) - ) - eq_( - round(assert_we_are_a_number, 3), - round(test_value + add_value, 3), - ) - - return run - - def test_render_literal_numeric(self, literal_round_trip): - literal_round_trip( - Numeric(precision=8, scale=4), - [15.7563, decimal.Decimal("15.7563")], - [decimal.Decimal("15.7563")], - ) - - def test_render_literal_numeric_asfloat(self, literal_round_trip): - literal_round_trip( - Numeric(precision=8, scale=4, asdecimal=False), - [15.7563, decimal.Decimal("15.7563")], - [15.7563], - ) - - def test_render_literal_float(self, literal_round_trip): - literal_round_trip( - Float(), - [15.7563, decimal.Decimal("15.7563")], - [15.7563], - filter_=lambda n: n is not None and round(n, 5) or None, - support_whereclause=False, - ) - - @testing.requires.precision_generic_float_type - def test_float_custom_scale(self, do_numeric_test): - do_numeric_test( - Float(None, decimal_return_scale=7, asdecimal=True), - [15.7563827, decimal.Decimal("15.7563827")], - [decimal.Decimal("15.7563827")], - check_scale=True, - ) - - def test_numeric_as_decimal(self, do_numeric_test): - do_numeric_test( - Numeric(precision=8, scale=4), - [15.7563, decimal.Decimal("15.7563")], - [decimal.Decimal("15.7563")], - ) - - def test_numeric_as_float(self, do_numeric_test): - do_numeric_test( - Numeric(precision=8, scale=4, asdecimal=False), - [15.7563, decimal.Decimal("15.7563")], - [15.7563], - ) - - @testing.requires.infinity_floats - def test_infinity_floats(self, do_numeric_test): - """test for #977, #7283""" - - do_numeric_test( - Float(None), - [float("inf")], - [float("inf")], - ) - - @testing.requires.fetch_null_from_numeric - def test_numeric_null_as_decimal(self, do_numeric_test): - do_numeric_test(Numeric(precision=8, scale=4), [None], [None]) - - @testing.requires.fetch_null_from_numeric - def test_numeric_null_as_float(self, do_numeric_test): - do_numeric_test( - Numeric(precision=8, scale=4, asdecimal=False), [None], [None] - ) - - @testing.requires.floats_to_four_decimals - def test_float_as_decimal(self, do_numeric_test): - do_numeric_test( - Float(asdecimal=True), - [15.756, decimal.Decimal("15.756"), None], - [decimal.Decimal("15.756"), None], - filter_=lambda n: n is not None and round(n, 4) or None, - ) - - def test_float_as_float(self, do_numeric_test): - do_numeric_test( - Float(), - [15.756, decimal.Decimal("15.756")], - [15.756], - filter_=lambda n: n is not None and round(n, 5) or None, - ) - - @testing.requires.literal_float_coercion - def test_float_coerce_round_trip(self, connection): - expr = 15.7563 - - val = connection.scalar(select(literal(expr))) - eq_(val, expr) - - # this does not work in MySQL, see #4036, however we choose not - # to render CAST unconditionally since this is kind of an edge case. - - @testing.requires.implicit_decimal_binds - def test_decimal_coerce_round_trip(self, connection): - expr = decimal.Decimal("15.7563") - - val = connection.scalar(select(literal(expr))) - eq_(val, expr) - - def test_decimal_coerce_round_trip_w_cast(self, connection): - expr = decimal.Decimal("15.7563") - - val = connection.scalar(select(cast(expr, Numeric(10, 4)))) - eq_(val, expr) - - @testing.requires.precision_numerics_general - def test_precision_decimal(self, do_numeric_test): - numbers = { - decimal.Decimal("54.234246451650"), - decimal.Decimal("0.004354"), - decimal.Decimal("900.0"), - } - - do_numeric_test(Numeric(precision=18, scale=12), numbers, numbers) - - @testing.requires.precision_numerics_enotation_large - def test_enotation_decimal(self, do_numeric_test): - """test exceedingly small decimals. - - Decimal reports values with E notation when the exponent - is greater than 6. - - """ - - numbers = { - decimal.Decimal("1E-2"), - decimal.Decimal("1E-3"), - decimal.Decimal("1E-4"), - decimal.Decimal("1E-5"), - decimal.Decimal("1E-6"), - decimal.Decimal("1E-7"), - decimal.Decimal("1E-8"), - decimal.Decimal("0.01000005940696"), - decimal.Decimal("0.00000005940696"), - decimal.Decimal("0.00000000000696"), - decimal.Decimal("0.70000000000696"), - decimal.Decimal("696E-12"), - } - do_numeric_test(Numeric(precision=18, scale=14), numbers, numbers) - - @testing.requires.precision_numerics_enotation_large - def test_enotation_decimal_large(self, do_numeric_test): - """test exceedingly large decimals.""" - - numbers = { - decimal.Decimal("4E+8"), - decimal.Decimal("5748E+15"), - decimal.Decimal("1.521E+15"), - decimal.Decimal("00000000000000.1E+12"), - } - do_numeric_test(Numeric(precision=25, scale=2), numbers, numbers) - - @testing.requires.precision_numerics_many_significant_digits - def test_many_significant_digits(self, do_numeric_test): - numbers = { - decimal.Decimal("31943874831932418390.01"), - decimal.Decimal("319438950232418390.273596"), - decimal.Decimal("87673.594069654243"), - } - do_numeric_test(Numeric(precision=38, scale=12), numbers, numbers) - - @testing.requires.precision_numerics_retains_significant_digits - def test_numeric_no_decimal(self, do_numeric_test): - numbers = {decimal.Decimal("1.000")} - do_numeric_test( - Numeric(precision=5, scale=3), numbers, numbers, check_scale=True - ) - - @testing.combinations(sqltypes.Float, sqltypes.Double, argnames="cls_") - @testing.requires.float_is_numeric - def test_float_is_not_numeric(self, connection, cls_): - target_type = cls_().dialect_impl(connection.dialect) - numeric_type = sqltypes.Numeric().dialect_impl(connection.dialect) - - ne_(target_type.__visit_name__, numeric_type.__visit_name__) - ne_(target_type.__class__, numeric_type.__class__) - - -class BooleanTest(_LiteralRoundTripFixture, fixtures.TablesTest): - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "boolean_table", - metadata, - Column("id", Integer, primary_key=True, autoincrement=False), - Column("value", Boolean), - Column("unconstrained_value", Boolean(create_constraint=False)), - ) - - def test_render_literal_bool(self, literal_round_trip): - literal_round_trip(Boolean(), [True, False], [True, False]) - - def test_round_trip(self, connection): - boolean_table = self.tables.boolean_table - - connection.execute( - boolean_table.insert(), - {"id": 1, "value": True, "unconstrained_value": False}, - ) - - row = connection.execute( - select(boolean_table.c.value, boolean_table.c.unconstrained_value) - ).first() - - eq_(row, (True, False)) - assert isinstance(row[0], bool) - - @testing.requires.nullable_booleans - def test_null(self, connection): - boolean_table = self.tables.boolean_table - - connection.execute( - boolean_table.insert(), - {"id": 1, "value": None, "unconstrained_value": None}, - ) - - row = connection.execute( - select(boolean_table.c.value, boolean_table.c.unconstrained_value) - ).first() - - eq_(row, (None, None)) - - def test_whereclause(self): - # testing "WHERE <column>" renders a compatible expression - boolean_table = self.tables.boolean_table - - with config.db.begin() as conn: - conn.execute( - boolean_table.insert(), - [ - {"id": 1, "value": True, "unconstrained_value": True}, - {"id": 2, "value": False, "unconstrained_value": False}, - ], - ) - - eq_( - conn.scalar( - select(boolean_table.c.id).where(boolean_table.c.value) - ), - 1, - ) - eq_( - conn.scalar( - select(boolean_table.c.id).where( - boolean_table.c.unconstrained_value - ) - ), - 1, - ) - eq_( - conn.scalar( - select(boolean_table.c.id).where(~boolean_table.c.value) - ), - 2, - ) - eq_( - conn.scalar( - select(boolean_table.c.id).where( - ~boolean_table.c.unconstrained_value - ) - ), - 2, - ) - - -class JSONTest(_LiteralRoundTripFixture, fixtures.TablesTest): - __requires__ = ("json_type",) - __backend__ = True - - datatype = JSON - - @classmethod - def define_tables(cls, metadata): - Table( - "data_table", - metadata, - Column("id", Integer, primary_key=True), - Column("name", String(30), nullable=False), - Column("data", cls.datatype, nullable=False), - Column("nulldata", cls.datatype(none_as_null=True)), - ) - - def test_round_trip_data1(self, connection): - self._test_round_trip({"key1": "value1", "key2": "value2"}, connection) - - @testing.combinations( - ("unicode", True), ("ascii", False), argnames="unicode_", id_="ia" - ) - @testing.combinations(100, 1999, 3000, 4000, 5000, 9000, argnames="length") - def test_round_trip_pretty_large_data(self, connection, unicode_, length): - if unicode_: - data = "réve🐍illé" * ((length // 9) + 1) - data = data[0 : (length // 2)] - else: - data = "abcdefg" * ((length // 7) + 1) - data = data[0:length] - - self._test_round_trip({"key1": data, "key2": data}, connection) - - def _test_round_trip(self, data_element, connection): - data_table = self.tables.data_table - - connection.execute( - data_table.insert(), - {"id": 1, "name": "row1", "data": data_element}, - ) - - row = connection.execute(select(data_table.c.data)).first() - - eq_(row, (data_element,)) - - def _index_fixtures(include_comparison): - if include_comparison: - # basically SQL Server and MariaDB can kind of do json - # comparison, MySQL, PG and SQLite can't. not worth it. - json_elements = [] - else: - json_elements = [ - ("json", {"foo": "bar"}), - ("json", ["one", "two", "three"]), - (None, {"foo": "bar"}), - (None, ["one", "two", "three"]), - ] - - elements = [ - ("boolean", True), - ("boolean", False), - ("boolean", None), - ("string", "some string"), - ("string", None), - ("string", "réve illé"), - ( - "string", - "réve🐍 illé", - testing.requires.json_index_supplementary_unicode_element, - ), - ("integer", 15), - ("integer", 1), - ("integer", 0), - ("integer", None), - ("float", 28.5), - ("float", None), - ("float", 1234567.89, testing.requires.literal_float_coercion), - ("numeric", 1234567.89), - # this one "works" because the float value you see here is - # lost immediately to floating point stuff - ( - "numeric", - 99998969694839.983485848, - ), - ("numeric", 99939.983485848), - ("_decimal", decimal.Decimal("1234567.89")), - ( - "_decimal", - decimal.Decimal("99998969694839.983485848"), - # fails on SQLite and MySQL (non-mariadb) - requirements.cast_precision_numerics_many_significant_digits, - ), - ( - "_decimal", - decimal.Decimal("99939.983485848"), - ), - ] + json_elements - - def decorate(fn): - fn = testing.combinations(id_="sa", *elements)(fn) - - return fn - - return decorate - - def _json_value_insert(self, connection, datatype, value, data_element): - data_table = self.tables.data_table - if datatype == "_decimal": - # Python's builtin json serializer basically doesn't support - # Decimal objects without implicit float conversion period. - # users can otherwise use simplejson which supports - # precision decimals - - # https://bugs.python.org/issue16535 - - # inserting as strings to avoid a new fixture around the - # dialect which would have idiosyncrasies for different - # backends. - - class DecimalEncoder(json.JSONEncoder): - def default(self, o): - if isinstance(o, decimal.Decimal): - return str(o) - return super().default(o) - - json_data = json.dumps(data_element, cls=DecimalEncoder) - - # take the quotes out. yup, there is *literally* no other - # way to get Python's json.dumps() to put all the digits in - # the string - json_data = re.sub(r'"(%s)"' % str(value), str(value), json_data) - - datatype = "numeric" - - connection.execute( - data_table.insert().values( - name="row1", - # to pass the string directly to every backend, including - # PostgreSQL which needs the value to be CAST as JSON - # both in the SQL as well as at the prepared statement - # level for asyncpg, while at the same time MySQL - # doesn't even support CAST for JSON, here we are - # sending the string embedded in the SQL without using - # a parameter. - data=bindparam(None, json_data, literal_execute=True), - nulldata=bindparam(None, json_data, literal_execute=True), - ), - ) - else: - connection.execute( - data_table.insert(), - { - "name": "row1", - "data": data_element, - "nulldata": data_element, - }, - ) - - p_s = None - - if datatype: - if datatype == "numeric": - a, b = str(value).split(".") - s = len(b) - p = len(a) + s - - if isinstance(value, decimal.Decimal): - compare_value = value - else: - compare_value = decimal.Decimal(str(value)) - - p_s = (p, s) - else: - compare_value = value - else: - compare_value = value - - return datatype, compare_value, p_s - - @_index_fixtures(False) - def test_index_typed_access(self, datatype, value): - data_table = self.tables.data_table - data_element = {"key1": value} - - with config.db.begin() as conn: - datatype, compare_value, p_s = self._json_value_insert( - conn, datatype, value, data_element - ) - - expr = data_table.c.data["key1"] - if datatype: - if datatype == "numeric" and p_s: - expr = expr.as_numeric(*p_s) - else: - expr = getattr(expr, "as_%s" % datatype)() - - roundtrip = conn.scalar(select(expr)) - eq_(roundtrip, compare_value) - is_(type(roundtrip), type(compare_value)) - - @_index_fixtures(True) - def test_index_typed_comparison(self, datatype, value): - data_table = self.tables.data_table - data_element = {"key1": value} - - with config.db.begin() as conn: - datatype, compare_value, p_s = self._json_value_insert( - conn, datatype, value, data_element - ) - - expr = data_table.c.data["key1"] - if datatype: - if datatype == "numeric" and p_s: - expr = expr.as_numeric(*p_s) - else: - expr = getattr(expr, "as_%s" % datatype)() - - row = conn.execute( - select(expr).where(expr == compare_value) - ).first() - - # make sure we get a row even if value is None - eq_(row, (compare_value,)) - - @_index_fixtures(True) - def test_path_typed_comparison(self, datatype, value): - data_table = self.tables.data_table - data_element = {"key1": {"subkey1": value}} - with config.db.begin() as conn: - datatype, compare_value, p_s = self._json_value_insert( - conn, datatype, value, data_element - ) - - expr = data_table.c.data[("key1", "subkey1")] - - if datatype: - if datatype == "numeric" and p_s: - expr = expr.as_numeric(*p_s) - else: - expr = getattr(expr, "as_%s" % datatype)() - - row = conn.execute( - select(expr).where(expr == compare_value) - ).first() - - # make sure we get a row even if value is None - eq_(row, (compare_value,)) - - @testing.combinations( - (True,), - (False,), - (None,), - (15,), - (0,), - (-1,), - (-1.0,), - (15.052,), - ("a string",), - ("réve illé",), - ("réve🐍 illé",), - ) - def test_single_element_round_trip(self, element): - data_table = self.tables.data_table - data_element = element - with config.db.begin() as conn: - conn.execute( - data_table.insert(), - { - "name": "row1", - "data": data_element, - "nulldata": data_element, - }, - ) - - row = conn.execute( - select(data_table.c.data, data_table.c.nulldata) - ).first() - - eq_(row, (data_element, data_element)) - - def test_round_trip_custom_json(self): - data_table = self.tables.data_table - data_element = {"key1": "data1"} - - js = mock.Mock(side_effect=json.dumps) - jd = mock.Mock(side_effect=json.loads) - engine = engines.testing_engine( - options=dict(json_serializer=js, json_deserializer=jd) - ) - - # support sqlite :memory: database... - data_table.create(engine, checkfirst=True) - with engine.begin() as conn: - conn.execute( - data_table.insert(), {"name": "row1", "data": data_element} - ) - row = conn.execute(select(data_table.c.data)).first() - - eq_(row, (data_element,)) - eq_(js.mock_calls, [mock.call(data_element)]) - if testing.requires.json_deserializer_binary.enabled: - eq_( - jd.mock_calls, - [mock.call(json.dumps(data_element).encode())], - ) - else: - eq_(jd.mock_calls, [mock.call(json.dumps(data_element))]) - - @testing.combinations( - ("parameters",), - ("multiparameters",), - ("values",), - ("omit",), - argnames="insert_type", - ) - def test_round_trip_none_as_sql_null(self, connection, insert_type): - col = self.tables.data_table.c["nulldata"] - - conn = connection - - if insert_type == "parameters": - stmt, params = self.tables.data_table.insert(), { - "name": "r1", - "nulldata": None, - "data": None, - } - elif insert_type == "multiparameters": - stmt, params = self.tables.data_table.insert(), [ - {"name": "r1", "nulldata": None, "data": None} - ] - elif insert_type == "values": - stmt, params = ( - self.tables.data_table.insert().values( - name="r1", - nulldata=None, - data=None, - ), - {}, - ) - elif insert_type == "omit": - stmt, params = ( - self.tables.data_table.insert(), - {"name": "r1", "data": None}, - ) - - else: - assert False - - conn.execute(stmt, params) - - eq_( - conn.scalar( - select(self.tables.data_table.c.name).where(col.is_(null())) - ), - "r1", - ) - - eq_(conn.scalar(select(col)), None) - - def test_round_trip_json_null_as_json_null(self, connection): - col = self.tables.data_table.c["data"] - - conn = connection - conn.execute( - self.tables.data_table.insert(), - {"name": "r1", "data": JSON.NULL}, - ) - - eq_( - conn.scalar( - select(self.tables.data_table.c.name).where( - cast(col, String) == "null" - ) - ), - "r1", - ) - - eq_(conn.scalar(select(col)), None) - - @testing.combinations( - ("parameters",), - ("multiparameters",), - ("values",), - argnames="insert_type", - ) - def test_round_trip_none_as_json_null(self, connection, insert_type): - col = self.tables.data_table.c["data"] - - if insert_type == "parameters": - stmt, params = self.tables.data_table.insert(), { - "name": "r1", - "data": None, - } - elif insert_type == "multiparameters": - stmt, params = self.tables.data_table.insert(), [ - {"name": "r1", "data": None} - ] - elif insert_type == "values": - stmt, params = ( - self.tables.data_table.insert().values(name="r1", data=None), - {}, - ) - else: - assert False - - conn = connection - conn.execute(stmt, params) - - eq_( - conn.scalar( - select(self.tables.data_table.c.name).where( - cast(col, String) == "null" - ) - ), - "r1", - ) - - eq_(conn.scalar(select(col)), None) - - def test_unicode_round_trip(self): - # note we include Unicode supplementary characters as well - with config.db.begin() as conn: - conn.execute( - self.tables.data_table.insert(), - { - "name": "r1", - "data": { - "réve🐍 illé": "réve🐍 illé", - "data": {"k1": "drôl🐍e"}, - }, - }, - ) - - eq_( - conn.scalar(select(self.tables.data_table.c.data)), - { - "réve🐍 illé": "réve🐍 illé", - "data": {"k1": "drôl🐍e"}, - }, - ) - - def test_eval_none_flag_orm(self, connection): - Base = declarative_base() - - class Data(Base): - __table__ = self.tables.data_table - - with Session(connection) as s: - d1 = Data(name="d1", data=None, nulldata=None) - s.add(d1) - s.commit() - - s.bulk_insert_mappings( - Data, [{"name": "d2", "data": None, "nulldata": None}] - ) - eq_( - s.query( - cast(self.tables.data_table.c.data, String()), - cast(self.tables.data_table.c.nulldata, String), - ) - .filter(self.tables.data_table.c.name == "d1") - .first(), - ("null", None), - ) - eq_( - s.query( - cast(self.tables.data_table.c.data, String()), - cast(self.tables.data_table.c.nulldata, String), - ) - .filter(self.tables.data_table.c.name == "d2") - .first(), - ("null", None), - ) - - -class JSONLegacyStringCastIndexTest( - _LiteralRoundTripFixture, fixtures.TablesTest -): - """test JSON index access with "cast to string", which we have documented - for a long time as how to compare JSON values, but is ultimately not - reliable in all cases. The "as_XYZ()" comparators should be used - instead. - - """ - - __requires__ = ("json_type", "legacy_unconditional_json_extract") - __backend__ = True - - datatype = JSON - - data1 = {"key1": "value1", "key2": "value2"} - - data2 = { - "Key 'One'": "value1", - "key two": "value2", - "key three": "value ' three '", - } - - data3 = { - "key1": [1, 2, 3], - "key2": ["one", "two", "three"], - "key3": [{"four": "five"}, {"six": "seven"}], - } - - data4 = ["one", "two", "three"] - - data5 = { - "nested": { - "elem1": [{"a": "b", "c": "d"}, {"e": "f", "g": "h"}], - "elem2": {"elem3": {"elem4": "elem5"}}, - } - } - - data6 = {"a": 5, "b": "some value", "c": {"foo": "bar"}} - - @classmethod - def define_tables(cls, metadata): - Table( - "data_table", - metadata, - Column("id", Integer, primary_key=True), - Column("name", String(30), nullable=False), - Column("data", cls.datatype), - Column("nulldata", cls.datatype(none_as_null=True)), - ) - - def _criteria_fixture(self): - with config.db.begin() as conn: - conn.execute( - self.tables.data_table.insert(), - [ - {"name": "r1", "data": self.data1}, - {"name": "r2", "data": self.data2}, - {"name": "r3", "data": self.data3}, - {"name": "r4", "data": self.data4}, - {"name": "r5", "data": self.data5}, - {"name": "r6", "data": self.data6}, - ], - ) - - def _test_index_criteria(self, crit, expected, test_literal=True): - self._criteria_fixture() - with config.db.connect() as conn: - stmt = select(self.tables.data_table.c.name).where(crit) - - eq_(conn.scalar(stmt), expected) - - if test_literal: - literal_sql = str( - stmt.compile( - config.db, compile_kwargs={"literal_binds": True} - ) - ) - - eq_(conn.exec_driver_sql(literal_sql).scalar(), expected) - - def test_string_cast_crit_spaces_in_key(self): - name = self.tables.data_table.c.name - col = self.tables.data_table.c["data"] - - # limit the rows here to avoid PG error - # "cannot extract field from a non-object", which is - # fixed in 9.4 but may exist in 9.3 - self._test_index_criteria( - and_( - name.in_(["r1", "r2", "r3"]), - cast(col["key two"], String) == '"value2"', - ), - "r2", - ) - - @config.requirements.json_array_indexes - def test_string_cast_crit_simple_int(self): - name = self.tables.data_table.c.name - col = self.tables.data_table.c["data"] - - # limit the rows here to avoid PG error - # "cannot extract array element from a non-array", which is - # fixed in 9.4 but may exist in 9.3 - self._test_index_criteria( - and_( - name == "r4", - cast(col[1], String) == '"two"', - ), - "r4", - ) - - def test_string_cast_crit_mixed_path(self): - col = self.tables.data_table.c["data"] - self._test_index_criteria( - cast(col[("key3", 1, "six")], String) == '"seven"', - "r3", - ) - - def test_string_cast_crit_string_path(self): - col = self.tables.data_table.c["data"] - self._test_index_criteria( - cast(col[("nested", "elem2", "elem3", "elem4")], String) - == '"elem5"', - "r5", - ) - - def test_string_cast_crit_against_string_basic(self): - name = self.tables.data_table.c.name - col = self.tables.data_table.c["data"] - - self._test_index_criteria( - and_( - name == "r6", - cast(col["b"], String) == '"some value"', - ), - "r6", - ) - - -class UuidTest(_LiteralRoundTripFixture, fixtures.TablesTest): - __backend__ = True - - datatype = Uuid - - @classmethod - def define_tables(cls, metadata): - Table( - "uuid_table", - metadata, - Column( - "id", Integer, primary_key=True, test_needs_autoincrement=True - ), - Column("uuid_data", cls.datatype), - Column("uuid_text_data", cls.datatype(as_uuid=False)), - Column("uuid_data_nonnative", Uuid(native_uuid=False)), - Column( - "uuid_text_data_nonnative", - Uuid(as_uuid=False, native_uuid=False), - ), - ) - - def test_uuid_round_trip(self, connection): - data = uuid.uuid4() - uuid_table = self.tables.uuid_table - - connection.execute( - uuid_table.insert(), - {"id": 1, "uuid_data": data, "uuid_data_nonnative": data}, - ) - row = connection.execute( - select( - uuid_table.c.uuid_data, uuid_table.c.uuid_data_nonnative - ).where( - uuid_table.c.uuid_data == data, - uuid_table.c.uuid_data_nonnative == data, - ) - ).first() - eq_(row, (data, data)) - - def test_uuid_text_round_trip(self, connection): - data = str(uuid.uuid4()) - uuid_table = self.tables.uuid_table - - connection.execute( - uuid_table.insert(), - { - "id": 1, - "uuid_text_data": data, - "uuid_text_data_nonnative": data, - }, - ) - row = connection.execute( - select( - uuid_table.c.uuid_text_data, - uuid_table.c.uuid_text_data_nonnative, - ).where( - uuid_table.c.uuid_text_data == data, - uuid_table.c.uuid_text_data_nonnative == data, - ) - ).first() - eq_((row[0].lower(), row[1].lower()), (data, data)) - - def test_literal_uuid(self, literal_round_trip): - data = uuid.uuid4() - literal_round_trip(self.datatype, [data], [data]) - - def test_literal_text(self, literal_round_trip): - data = str(uuid.uuid4()) - literal_round_trip( - self.datatype(as_uuid=False), - [data], - [data], - filter_=lambda x: x.lower(), - ) - - def test_literal_nonnative_uuid(self, literal_round_trip): - data = uuid.uuid4() - literal_round_trip(Uuid(native_uuid=False), [data], [data]) - - def test_literal_nonnative_text(self, literal_round_trip): - data = str(uuid.uuid4()) - literal_round_trip( - Uuid(as_uuid=False, native_uuid=False), - [data], - [data], - filter_=lambda x: x.lower(), - ) - - @testing.requires.insert_returning - def test_uuid_returning(self, connection): - data = uuid.uuid4() - str_data = str(data) - uuid_table = self.tables.uuid_table - - result = connection.execute( - uuid_table.insert().returning( - uuid_table.c.uuid_data, - uuid_table.c.uuid_text_data, - uuid_table.c.uuid_data_nonnative, - uuid_table.c.uuid_text_data_nonnative, - ), - { - "id": 1, - "uuid_data": data, - "uuid_text_data": str_data, - "uuid_data_nonnative": data, - "uuid_text_data_nonnative": str_data, - }, - ) - row = result.first() - - eq_(row, (data, str_data, data, str_data)) - - -class NativeUUIDTest(UuidTest): - __requires__ = ("uuid_data_type",) - - datatype = UUID - - -__all__ = ( - "ArrayTest", - "BinaryTest", - "UnicodeVarcharTest", - "UnicodeTextTest", - "JSONTest", - "JSONLegacyStringCastIndexTest", - "DateTest", - "DateTimeTest", - "DateTimeTZTest", - "TextTest", - "NumericTest", - "IntegerTest", - "IntervalTest", - "PrecisionIntervalTest", - "CastTypeDecoratorTest", - "DateTimeHistoricTest", - "DateTimeCoercedToDateTimeTest", - "TimeMicrosecondsTest", - "TimestampMicrosecondsTest", - "TimeTest", - "TimeTZTest", - "TrueDivTest", - "DateTimeMicrosecondsTest", - "DateHistoricTest", - "StringTest", - "BooleanTest", - "UuidTest", - "NativeUUIDTest", -) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py deleted file mode 100644 index 1f15ab5..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py +++ /dev/null @@ -1,189 +0,0 @@ -# testing/suite/test_unicode_ddl.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from sqlalchemy import desc -from sqlalchemy import ForeignKey -from sqlalchemy import Integer -from sqlalchemy import MetaData -from sqlalchemy import testing -from sqlalchemy.testing import eq_ -from sqlalchemy.testing import fixtures -from sqlalchemy.testing.schema import Column -from sqlalchemy.testing.schema import Table - - -class UnicodeSchemaTest(fixtures.TablesTest): - __requires__ = ("unicode_ddl",) - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - global t1, t2, t3 - - t1 = Table( - "unitable1", - metadata, - Column("méil", Integer, primary_key=True), - Column("\u6e2c\u8a66", Integer), - test_needs_fk=True, - ) - t2 = Table( - "Unitéble2", - metadata, - Column("méil", Integer, primary_key=True, key="a"), - Column( - "\u6e2c\u8a66", - Integer, - ForeignKey("unitable1.méil"), - key="b", - ), - test_needs_fk=True, - ) - - # Few DBs support Unicode foreign keys - if testing.against("sqlite"): - t3 = Table( - "\u6e2c\u8a66", - metadata, - Column( - "\u6e2c\u8a66_id", - Integer, - primary_key=True, - autoincrement=False, - ), - Column( - "unitable1_\u6e2c\u8a66", - Integer, - ForeignKey("unitable1.\u6e2c\u8a66"), - ), - Column("Unitéble2_b", Integer, ForeignKey("Unitéble2.b")), - Column( - "\u6e2c\u8a66_self", - Integer, - ForeignKey("\u6e2c\u8a66.\u6e2c\u8a66_id"), - ), - test_needs_fk=True, - ) - else: - t3 = Table( - "\u6e2c\u8a66", - metadata, - Column( - "\u6e2c\u8a66_id", - Integer, - primary_key=True, - autoincrement=False, - ), - Column("unitable1_\u6e2c\u8a66", Integer), - Column("Unitéble2_b", Integer), - Column("\u6e2c\u8a66_self", Integer), - test_needs_fk=True, - ) - - def test_insert(self, connection): - connection.execute(t1.insert(), {"méil": 1, "\u6e2c\u8a66": 5}) - connection.execute(t2.insert(), {"a": 1, "b": 1}) - connection.execute( - t3.insert(), - { - "\u6e2c\u8a66_id": 1, - "unitable1_\u6e2c\u8a66": 5, - "Unitéble2_b": 1, - "\u6e2c\u8a66_self": 1, - }, - ) - - eq_(connection.execute(t1.select()).fetchall(), [(1, 5)]) - eq_(connection.execute(t2.select()).fetchall(), [(1, 1)]) - eq_(connection.execute(t3.select()).fetchall(), [(1, 5, 1, 1)]) - - def test_col_targeting(self, connection): - connection.execute(t1.insert(), {"méil": 1, "\u6e2c\u8a66": 5}) - connection.execute(t2.insert(), {"a": 1, "b": 1}) - connection.execute( - t3.insert(), - { - "\u6e2c\u8a66_id": 1, - "unitable1_\u6e2c\u8a66": 5, - "Unitéble2_b": 1, - "\u6e2c\u8a66_self": 1, - }, - ) - - row = connection.execute(t1.select()).first() - eq_(row._mapping[t1.c["méil"]], 1) - eq_(row._mapping[t1.c["\u6e2c\u8a66"]], 5) - - row = connection.execute(t2.select()).first() - eq_(row._mapping[t2.c["a"]], 1) - eq_(row._mapping[t2.c["b"]], 1) - - row = connection.execute(t3.select()).first() - eq_(row._mapping[t3.c["\u6e2c\u8a66_id"]], 1) - eq_(row._mapping[t3.c["unitable1_\u6e2c\u8a66"]], 5) - eq_(row._mapping[t3.c["Unitéble2_b"]], 1) - eq_(row._mapping[t3.c["\u6e2c\u8a66_self"]], 1) - - def test_reflect(self, connection): - connection.execute(t1.insert(), {"méil": 2, "\u6e2c\u8a66": 7}) - connection.execute(t2.insert(), {"a": 2, "b": 2}) - connection.execute( - t3.insert(), - { - "\u6e2c\u8a66_id": 2, - "unitable1_\u6e2c\u8a66": 7, - "Unitéble2_b": 2, - "\u6e2c\u8a66_self": 2, - }, - ) - - meta = MetaData() - tt1 = Table(t1.name, meta, autoload_with=connection) - tt2 = Table(t2.name, meta, autoload_with=connection) - tt3 = Table(t3.name, meta, autoload_with=connection) - - connection.execute(tt1.insert(), {"méil": 1, "\u6e2c\u8a66": 5}) - connection.execute(tt2.insert(), {"méil": 1, "\u6e2c\u8a66": 1}) - connection.execute( - tt3.insert(), - { - "\u6e2c\u8a66_id": 1, - "unitable1_\u6e2c\u8a66": 5, - "Unitéble2_b": 1, - "\u6e2c\u8a66_self": 1, - }, - ) - - eq_( - connection.execute(tt1.select().order_by(desc("méil"))).fetchall(), - [(2, 7), (1, 5)], - ) - eq_( - connection.execute(tt2.select().order_by(desc("méil"))).fetchall(), - [(2, 2), (1, 1)], - ) - eq_( - connection.execute( - tt3.select().order_by(desc("\u6e2c\u8a66_id")) - ).fetchall(), - [(2, 7, 2, 2), (1, 5, 1, 1)], - ) - - def test_repr(self): - meta = MetaData() - t = Table("\u6e2c\u8a66", meta, Column("\u6e2c\u8a66_id", Integer)) - eq_( - repr(t), - ( - "Table('測試', MetaData(), " - "Column('測試_id', Integer(), " - "table=<測試>), " - "schema=None)" - ), - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_update_delete.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_update_delete.py deleted file mode 100644 index fd4757f..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/suite/test_update_delete.py +++ /dev/null @@ -1,139 +0,0 @@ -# testing/suite/test_update_delete.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from .. import fixtures -from ..assertions import eq_ -from ..schema import Column -from ..schema import Table -from ... import Integer -from ... import String -from ... import testing - - -class SimpleUpdateDeleteTest(fixtures.TablesTest): - run_deletes = "each" - __requires__ = ("sane_rowcount",) - __backend__ = True - - @classmethod - def define_tables(cls, metadata): - Table( - "plain_pk", - metadata, - Column("id", Integer, primary_key=True), - Column("data", String(50)), - ) - - @classmethod - def insert_data(cls, connection): - connection.execute( - cls.tables.plain_pk.insert(), - [ - {"id": 1, "data": "d1"}, - {"id": 2, "data": "d2"}, - {"id": 3, "data": "d3"}, - ], - ) - - def test_update(self, connection): - t = self.tables.plain_pk - r = connection.execute( - t.update().where(t.c.id == 2), dict(data="d2_new") - ) - assert not r.is_insert - assert not r.returns_rows - assert r.rowcount == 1 - - eq_( - connection.execute(t.select().order_by(t.c.id)).fetchall(), - [(1, "d1"), (2, "d2_new"), (3, "d3")], - ) - - def test_delete(self, connection): - t = self.tables.plain_pk - r = connection.execute(t.delete().where(t.c.id == 2)) - assert not r.is_insert - assert not r.returns_rows - assert r.rowcount == 1 - eq_( - connection.execute(t.select().order_by(t.c.id)).fetchall(), - [(1, "d1"), (3, "d3")], - ) - - @testing.variation("criteria", ["rows", "norows", "emptyin"]) - @testing.requires.update_returning - def test_update_returning(self, connection, criteria): - t = self.tables.plain_pk - - stmt = t.update().returning(t.c.id, t.c.data) - - if criteria.norows: - stmt = stmt.where(t.c.id == 10) - elif criteria.rows: - stmt = stmt.where(t.c.id == 2) - elif criteria.emptyin: - stmt = stmt.where(t.c.id.in_([])) - else: - criteria.fail() - - r = connection.execute(stmt, dict(data="d2_new")) - assert not r.is_insert - assert r.returns_rows - eq_(r.keys(), ["id", "data"]) - - if criteria.rows: - eq_(r.all(), [(2, "d2_new")]) - else: - eq_(r.all(), []) - - eq_( - connection.execute(t.select().order_by(t.c.id)).fetchall(), - ( - [(1, "d1"), (2, "d2_new"), (3, "d3")] - if criteria.rows - else [(1, "d1"), (2, "d2"), (3, "d3")] - ), - ) - - @testing.variation("criteria", ["rows", "norows", "emptyin"]) - @testing.requires.delete_returning - def test_delete_returning(self, connection, criteria): - t = self.tables.plain_pk - - stmt = t.delete().returning(t.c.id, t.c.data) - - if criteria.norows: - stmt = stmt.where(t.c.id == 10) - elif criteria.rows: - stmt = stmt.where(t.c.id == 2) - elif criteria.emptyin: - stmt = stmt.where(t.c.id.in_([])) - else: - criteria.fail() - - r = connection.execute(stmt) - assert not r.is_insert - assert r.returns_rows - eq_(r.keys(), ["id", "data"]) - - if criteria.rows: - eq_(r.all(), [(2, "d2")]) - else: - eq_(r.all(), []) - - eq_( - connection.execute(t.select().order_by(t.c.id)).fetchall(), - ( - [(1, "d1"), (3, "d3")] - if criteria.rows - else [(1, "d1"), (2, "d2"), (3, "d3")] - ), - ) - - -__all__ = ("SimpleUpdateDeleteTest",) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/util.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/util.py deleted file mode 100644 index a6ce6ca..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/util.py +++ /dev/null @@ -1,519 +0,0 @@ -# testing/util.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - - -from __future__ import annotations - -from collections import deque -import decimal -import gc -from itertools import chain -import random -import sys -from sys import getsizeof -import types - -from . import config -from . import mock -from .. import inspect -from ..engine import Connection -from ..schema import Column -from ..schema import DropConstraint -from ..schema import DropTable -from ..schema import ForeignKeyConstraint -from ..schema import MetaData -from ..schema import Table -from ..sql import schema -from ..sql.sqltypes import Integer -from ..util import decorator -from ..util import defaultdict -from ..util import has_refcount_gc -from ..util import inspect_getfullargspec - - -if not has_refcount_gc: - - def non_refcount_gc_collect(*args): - gc.collect() - gc.collect() - - gc_collect = lazy_gc = non_refcount_gc_collect -else: - # assume CPython - straight gc.collect, lazy_gc() is a pass - gc_collect = gc.collect - - def lazy_gc(): - pass - - -def picklers(): - picklers = set() - import pickle - - picklers.add(pickle) - - # yes, this thing needs this much testing - for pickle_ in picklers: - for protocol in range(-2, pickle.HIGHEST_PROTOCOL + 1): - yield pickle_.loads, lambda d: pickle_.dumps(d, protocol) - - -def random_choices(population, k=1): - return random.choices(population, k=k) - - -def round_decimal(value, prec): - if isinstance(value, float): - return round(value, prec) - - # can also use shift() here but that is 2.6 only - return (value * decimal.Decimal("1" + "0" * prec)).to_integral( - decimal.ROUND_FLOOR - ) / pow(10, prec) - - -class RandomSet(set): - def __iter__(self): - l = list(set.__iter__(self)) - random.shuffle(l) - return iter(l) - - def pop(self): - index = random.randint(0, len(self) - 1) - item = list(set.__iter__(self))[index] - self.remove(item) - return item - - def union(self, other): - return RandomSet(set.union(self, other)) - - def difference(self, other): - return RandomSet(set.difference(self, other)) - - def intersection(self, other): - return RandomSet(set.intersection(self, other)) - - def copy(self): - return RandomSet(self) - - -def conforms_partial_ordering(tuples, sorted_elements): - """True if the given sorting conforms to the given partial ordering.""" - - deps = defaultdict(set) - for parent, child in tuples: - deps[parent].add(child) - for i, node in enumerate(sorted_elements): - for n in sorted_elements[i:]: - if node in deps[n]: - return False - else: - return True - - -def all_partial_orderings(tuples, elements): - edges = defaultdict(set) - for parent, child in tuples: - edges[child].add(parent) - - def _all_orderings(elements): - if len(elements) == 1: - yield list(elements) - else: - for elem in elements: - subset = set(elements).difference([elem]) - if not subset.intersection(edges[elem]): - for sub_ordering in _all_orderings(subset): - yield [elem] + sub_ordering - - return iter(_all_orderings(elements)) - - -def function_named(fn, name): - """Return a function with a given __name__. - - Will assign to __name__ and return the original function if possible on - the Python implementation, otherwise a new function will be constructed. - - This function should be phased out as much as possible - in favor of @decorator. Tests that "generate" many named tests - should be modernized. - - """ - try: - fn.__name__ = name - except TypeError: - fn = types.FunctionType( - fn.__code__, fn.__globals__, name, fn.__defaults__, fn.__closure__ - ) - return fn - - -def run_as_contextmanager(ctx, fn, *arg, **kw): - """Run the given function under the given contextmanager, - simulating the behavior of 'with' to support older - Python versions. - - This is not necessary anymore as we have placed 2.6 - as minimum Python version, however some tests are still using - this structure. - - """ - - obj = ctx.__enter__() - try: - result = fn(obj, *arg, **kw) - ctx.__exit__(None, None, None) - return result - except: - exc_info = sys.exc_info() - raise_ = ctx.__exit__(*exc_info) - if not raise_: - raise - else: - return raise_ - - -def rowset(results): - """Converts the results of sql execution into a plain set of column tuples. - - Useful for asserting the results of an unordered query. - """ - - return {tuple(row) for row in results} - - -def fail(msg): - assert False, msg - - -@decorator -def provide_metadata(fn, *args, **kw): - """Provide bound MetaData for a single test, dropping afterwards. - - Legacy; use the "metadata" pytest fixture. - - """ - - from . import fixtures - - metadata = schema.MetaData() - self = args[0] - prev_meta = getattr(self, "metadata", None) - self.metadata = metadata - try: - return fn(*args, **kw) - finally: - # close out some things that get in the way of dropping tables. - # when using the "metadata" fixture, there is a set ordering - # of things that makes sure things are cleaned up in order, however - # the simple "decorator" nature of this legacy function means - # we have to hardcode some of that cleanup ahead of time. - - # close ORM sessions - fixtures.close_all_sessions() - - # integrate with the "connection" fixture as there are many - # tests where it is used along with provide_metadata - cfc = fixtures.base._connection_fixture_connection - if cfc: - # TODO: this warning can be used to find all the places - # this is used with connection fixture - # warn("mixing legacy provide metadata with connection fixture") - drop_all_tables_from_metadata(metadata, cfc) - # as the provide_metadata fixture is often used with "testing.db", - # when we do the drop we have to commit the transaction so that - # the DB is actually updated as the CREATE would have been - # committed - cfc.get_transaction().commit() - else: - drop_all_tables_from_metadata(metadata, config.db) - self.metadata = prev_meta - - -def flag_combinations(*combinations): - """A facade around @testing.combinations() oriented towards boolean - keyword-based arguments. - - Basically generates a nice looking identifier based on the keywords - and also sets up the argument names. - - E.g.:: - - @testing.flag_combinations( - dict(lazy=False, passive=False), - dict(lazy=True, passive=False), - dict(lazy=False, passive=True), - dict(lazy=False, passive=True, raiseload=True), - ) - - - would result in:: - - @testing.combinations( - ('', False, False, False), - ('lazy', True, False, False), - ('lazy_passive', True, True, False), - ('lazy_passive', True, True, True), - id_='iaaa', - argnames='lazy,passive,raiseload' - ) - - """ - - keys = set() - - for d in combinations: - keys.update(d) - - keys = sorted(keys) - - return config.combinations( - *[ - ("_".join(k for k in keys if d.get(k, False)),) - + tuple(d.get(k, False) for k in keys) - for d in combinations - ], - id_="i" + ("a" * len(keys)), - argnames=",".join(keys), - ) - - -def lambda_combinations(lambda_arg_sets, **kw): - args = inspect_getfullargspec(lambda_arg_sets) - - arg_sets = lambda_arg_sets(*[mock.Mock() for arg in args[0]]) - - def create_fixture(pos): - def fixture(**kw): - return lambda_arg_sets(**kw)[pos] - - fixture.__name__ = "fixture_%3.3d" % pos - return fixture - - return config.combinations( - *[(create_fixture(i),) for i in range(len(arg_sets))], **kw - ) - - -def resolve_lambda(__fn, **kw): - """Given a no-arg lambda and a namespace, return a new lambda that - has all the values filled in. - - This is used so that we can have module-level fixtures that - refer to instance-level variables using lambdas. - - """ - - pos_args = inspect_getfullargspec(__fn)[0] - pass_pos_args = {arg: kw.pop(arg) for arg in pos_args} - glb = dict(__fn.__globals__) - glb.update(kw) - new_fn = types.FunctionType(__fn.__code__, glb) - return new_fn(**pass_pos_args) - - -def metadata_fixture(ddl="function"): - """Provide MetaData for a pytest fixture.""" - - def decorate(fn): - def run_ddl(self): - metadata = self.metadata = schema.MetaData() - try: - result = fn(self, metadata) - metadata.create_all(config.db) - # TODO: - # somehow get a per-function dml erase fixture here - yield result - finally: - metadata.drop_all(config.db) - - return config.fixture(scope=ddl)(run_ddl) - - return decorate - - -def force_drop_names(*names): - """Force the given table names to be dropped after test complete, - isolating for foreign key cycles - - """ - - @decorator - def go(fn, *args, **kw): - try: - return fn(*args, **kw) - finally: - drop_all_tables(config.db, inspect(config.db), include_names=names) - - return go - - -class adict(dict): - """Dict keys available as attributes. Shadows.""" - - def __getattribute__(self, key): - try: - return self[key] - except KeyError: - return dict.__getattribute__(self, key) - - def __call__(self, *keys): - return tuple([self[key] for key in keys]) - - get_all = __call__ - - -def drop_all_tables_from_metadata(metadata, engine_or_connection): - from . import engines - - def go(connection): - engines.testing_reaper.prepare_for_drop_tables(connection) - - if not connection.dialect.supports_alter: - from . import assertions - - with assertions.expect_warnings( - "Can't sort tables", assert_=False - ): - metadata.drop_all(connection) - else: - metadata.drop_all(connection) - - if not isinstance(engine_or_connection, Connection): - with engine_or_connection.begin() as connection: - go(connection) - else: - go(engine_or_connection) - - -def drop_all_tables( - engine, - inspector, - schema=None, - consider_schemas=(None,), - include_names=None, -): - if include_names is not None: - include_names = set(include_names) - - if schema is not None: - assert consider_schemas == ( - None, - ), "consider_schemas and schema are mutually exclusive" - consider_schemas = (schema,) - - with engine.begin() as conn: - for table_key, fkcs in reversed( - inspector.sort_tables_on_foreign_key_dependency( - consider_schemas=consider_schemas - ) - ): - if table_key: - if ( - include_names is not None - and table_key[1] not in include_names - ): - continue - conn.execute( - DropTable( - Table(table_key[1], MetaData(), schema=table_key[0]) - ) - ) - elif fkcs: - if not engine.dialect.supports_alter: - continue - for t_key, fkc in fkcs: - if ( - include_names is not None - and t_key[1] not in include_names - ): - continue - tb = Table( - t_key[1], - MetaData(), - Column("x", Integer), - Column("y", Integer), - schema=t_key[0], - ) - conn.execute( - DropConstraint( - ForeignKeyConstraint([tb.c.x], [tb.c.y], name=fkc) - ) - ) - - -def teardown_events(event_cls): - @decorator - def decorate(fn, *arg, **kw): - try: - return fn(*arg, **kw) - finally: - event_cls._clear() - - return decorate - - -def total_size(o): - """Returns the approximate memory footprint an object and all of its - contents. - - source: https://code.activestate.com/recipes/577504/ - - - """ - - def dict_handler(d): - return chain.from_iterable(d.items()) - - all_handlers = { - tuple: iter, - list: iter, - deque: iter, - dict: dict_handler, - set: iter, - frozenset: iter, - } - seen = set() # track which object id's have already been seen - default_size = getsizeof(0) # estimate sizeof object without __sizeof__ - - def sizeof(o): - if id(o) in seen: # do not double count the same object - return 0 - seen.add(id(o)) - s = getsizeof(o, default_size) - - for typ, handler in all_handlers.items(): - if isinstance(o, typ): - s += sum(map(sizeof, handler(o))) - break - return s - - return sizeof(o) - - -def count_cache_key_tuples(tup): - """given a cache key tuple, counts how many instances of actual - tuples are found. - - used to alert large jumps in cache key complexity. - - """ - stack = [tup] - - sentinel = object() - num_elements = 0 - - while stack: - elem = stack.pop(0) - if elem is sentinel: - num_elements += 1 - elif isinstance(elem, tuple): - if elem: - stack = list(elem) + [sentinel] + stack - return num_elements diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/testing/warnings.py b/venv/lib/python3.11/site-packages/sqlalchemy/testing/warnings.py deleted file mode 100644 index baef037..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/testing/warnings.py +++ /dev/null @@ -1,52 +0,0 @@ -# testing/warnings.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -from __future__ import annotations - -import warnings - -from . import assertions -from .. import exc -from .. import exc as sa_exc -from ..exc import SATestSuiteWarning -from ..util.langhelpers import _warnings_warn - - -def warn_test_suite(message): - _warnings_warn(message, category=SATestSuiteWarning) - - -def setup_filters(): - """hook for setting up warnings filters. - - SQLAlchemy-specific classes must only be here and not in pytest config, - as we need to delay importing SQLAlchemy until conftest.py has been - processed. - - NOTE: filters on subclasses of DeprecationWarning or - PendingDeprecationWarning have no effect if added here, since pytest - will add at each test the following filters - ``always::PendingDeprecationWarning`` and ``always::DeprecationWarning`` - that will take precedence over any added here. - - """ - warnings.filterwarnings("error", category=exc.SAWarning) - warnings.filterwarnings("always", category=exc.SATestSuiteWarning) - - -def assert_warnings(fn, warning_msgs, regex=False): - """Assert that each of the given warnings are emitted by fn. - - Deprecated. Please use assertions.expect_warnings(). - - """ - - with assertions._expect_warnings( - sa_exc.SAWarning, warning_msgs, regex=regex - ): - return fn() |