From 2f617f56f2acdce00b88f746c403cf5ed66d4d27 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 7 Apr 2020 14:15:43 -0400 Subject: Create initial 2.0 engine implementation Implemented the SQLAlchemy 2 :func:`.future.create_engine` function which is used for forwards compatibility with SQLAlchemy 2. This engine features always-transactional behavior with autobegin. Allow execution options per statement execution. This includes that the before_execute() and after_execute() events now accept an additional dictionary with these options, empty if not passed; a legacy event decorator is added for backwards compatibility which now also emits a deprecation warning. Add some basic tests for execution, transactions, and the new result object. Build out on a new testing fixture that swaps in the future engine completely to start with. Change-Id: I70e7338bb3f0ce22d2f702537d94bb249bd9fb0a Fixes: #4644 --- lib/sqlalchemy/testing/assertsql.py | 4 +++- lib/sqlalchemy/testing/config.py | 8 ++++++++ lib/sqlalchemy/testing/engines.py | 7 +++++-- lib/sqlalchemy/testing/fixtures.py | 23 +++++++++++++++++++++++ lib/sqlalchemy/testing/suite/test_ddl.py | 6 +++++- lib/sqlalchemy/testing/warnings.py | 7 +++++++ 6 files changed, 51 insertions(+), 4 deletions(-) (limited to 'lib/sqlalchemy/testing') diff --git a/lib/sqlalchemy/testing/assertsql.py b/lib/sqlalchemy/testing/assertsql.py index 8876c2304..7988b4ec9 100644 --- a/lib/sqlalchemy/testing/assertsql.py +++ b/lib/sqlalchemy/testing/assertsql.py @@ -388,7 +388,9 @@ def assert_engine(engine): orig = [] @event.listens_for(engine, "before_execute") - def connection_execute(conn, clauseelement, multiparams, params): + def connection_execute( + conn, clauseelement, multiparams, params, execution_options + ): # grab the original statement + params before any cursor # execution orig[:] = clauseelement, multiparams, params diff --git a/lib/sqlalchemy/testing/config.py b/lib/sqlalchemy/testing/config.py index 140f5f782..e97821d72 100644 --- a/lib/sqlalchemy/testing/config.py +++ b/lib/sqlalchemy/testing/config.py @@ -149,6 +149,14 @@ class Config(object): 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: diff --git a/lib/sqlalchemy/testing/engines.py b/lib/sqlalchemy/testing/engines.py index 910af5876..280e6901e 100644 --- a/lib/sqlalchemy/testing/engines.py +++ b/lib/sqlalchemy/testing/engines.py @@ -238,10 +238,13 @@ def reconnecting_engine(url=None, options=None): return engine -def testing_engine(url=None, options=None): +def testing_engine(url=None, options=None, future=False): """Produce an engine configured by --options with optional overrides.""" - from sqlalchemy import create_engine + if future or config.db and config.db._is_future: + from sqlalchemy.future import create_engine + else: + from sqlalchemy import create_engine from sqlalchemy.engine.url import make_url if not options: diff --git a/lib/sqlalchemy/testing/fixtures.py b/lib/sqlalchemy/testing/fixtures.py index e5e6c42fc..26ae221b8 100644 --- a/lib/sqlalchemy/testing/fixtures.py +++ b/lib/sqlalchemy/testing/fixtures.py @@ -84,6 +84,29 @@ class TestBase(object): # engines.drop_all_tables(metadata, config.db) +class FutureEngineMixin(object): + @classmethod + def setup_class(cls): + super_ = super(FutureEngineMixin, cls) + if hasattr(super_, "setup_class"): + super_.setup_class() + + from ..future.engine import Engine + from sqlalchemy import testing + + config._current.push_engine(Engine._future_facade(config.db), testing) + + @classmethod + def teardown_class(cls): + from sqlalchemy import testing + + config._current.pop(testing) + + super_ = super(FutureEngineMixin, cls) + if hasattr(super_, "teardown_class"): + super_.teardown_class() + + class TablesTest(TestBase): # 'once', None diff --git a/lib/sqlalchemy/testing/suite/test_ddl.py b/lib/sqlalchemy/testing/suite/test_ddl.py index 1f49106fb..93706338c 100644 --- a/lib/sqlalchemy/testing/suite/test_ddl.py +++ b/lib/sqlalchemy/testing/suite/test_ddl.py @@ -90,4 +90,8 @@ class TableDDLTest(fixtures.TestBase): ) -__all__ = ("TableDDLTest",) +class FutureTableDDLTest(fixtures.FutureEngineMixin, TableDDLTest): + pass + + +__all__ = ("TableDDLTest", "FutureTableDDLTest") diff --git a/lib/sqlalchemy/testing/warnings.py b/lib/sqlalchemy/testing/warnings.py index 6b42c98cb..39cffbf15 100644 --- a/lib/sqlalchemy/testing/warnings.py +++ b/lib/sqlalchemy/testing/warnings.py @@ -34,6 +34,13 @@ def setup_filters(): # ignore 2.0 warnings unless we are explicitly testing for them warnings.filterwarnings("ignore", category=sa_exc.RemovedIn20Warning) + # ignore things that are deprecated *as of* 2.0 :) + warnings.filterwarnings( + "ignore", + category=sa_exc.SADeprecationWarning, + message=r".*\(deprecated since: 2.0\)$", + ) + try: import pytest except ImportError: -- cgit v1.2.1