summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/testing
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2020-04-21 12:51:13 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-05-01 16:09:24 -0400
commitaded39f68c29e44a50c85be1ddb370d3d1affe9d (patch)
tree0855ecfe2ecf5092f1e350c33f460571f495f1b8 /lib/sqlalchemy/testing
parent18ce4f9937c2d6753acbb054b4990c7da298a5d7 (diff)
downloadsqlalchemy-aded39f68c29e44a50c85be1ddb370d3d1affe9d.tar.gz
Propose Result as immediate replacement for ResultProxy
As progress is made on the _future.Result, including breaking it out such that DBAPI behaviors are local to specific implementations, it becomes apparent that the Result object is a functional superset of ResultProxy and that basic operations like fetchone(), fetchall(), and fetchmany() behave pretty much exactly the same way on the new object. Reorganize things so that ResultProxy is now referred to as LegacyCursorResult, which subclasses CursorResult that represents the DBAPI-cursor version of Result, making use of a multiple inheritance pattern so that the functionality of Result is also available in non-DBAPI contexts, as will be necessary for some ORM patterns. Additionally propose the composition system for Result that will form the basis for ORM-alternative result systems such as horizontal sharding and dogpile cache. As ORM results will soon be coming directly from instances of Result, these extensions will instead build their own ResultFetchStrategies that perform the special steps to create composed or cached result sets. Also considering at the moment not emitting deprecation warnings for fetchXYZ() methods; the immediate issue is Keystone tests are calling upon it, but as the implementations here are proving to be not in any kind of conflict with how Result works, there's not too much issue leaving them around and deprecating at some later point. References: #5087 References: #4395 Fixes: #4959 Change-Id: I8091919d45421e3f53029b8660427f844fee0228
Diffstat (limited to 'lib/sqlalchemy/testing')
-rw-r--r--lib/sqlalchemy/testing/fixtures.py23
-rw-r--r--lib/sqlalchemy/testing/suite/test_insert.py24
2 files changed, 35 insertions, 12 deletions
diff --git a/lib/sqlalchemy/testing/fixtures.py b/lib/sqlalchemy/testing/fixtures.py
index a4399830e..1fa7daed6 100644
--- a/lib/sqlalchemy/testing/fixtures.py
+++ b/lib/sqlalchemy/testing/fixtures.py
@@ -59,7 +59,8 @@ class TestBase(object):
@config.fixture()
def connection(self):
- conn = config.db.connect()
+ eng = getattr(self, "bind", config.db)
+ conn = eng.connect()
trans = conn.begin()
try:
yield conn
@@ -87,25 +88,27 @@ class TestBase(object):
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)
+ facade = Engine._future_facade(config.db)
+ config._current.push_engine(facade, testing)
+
+ super_ = super(FutureEngineMixin, cls)
+ if hasattr(super_, "setup_class"):
+ super_.setup_class()
@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()
+ from sqlalchemy import testing
+
+ config._current.pop(testing)
+
class TablesTest(TestBase):
@@ -195,7 +198,7 @@ class TablesTest(TestBase):
# no need to run deletes if tables are recreated on setup
if self.run_define_tables != "each" and self.run_deletes == "each":
- with self.bind.connect() as conn:
+ with self.bind.begin() as conn:
for table in reversed(self.metadata.sorted_tables):
try:
conn.execute(table.delete())
diff --git a/lib/sqlalchemy/testing/suite/test_insert.py b/lib/sqlalchemy/testing/suite/test_insert.py
index f449b2fe6..92e38ab20 100644
--- a/lib/sqlalchemy/testing/suite/test_insert.py
+++ b/lib/sqlalchemy/testing/suite/test_insert.py
@@ -114,7 +114,13 @@ class InsertBehaviorTest(fixtures.TablesTest):
assert r._soft_closed
assert not r.closed
assert r.is_insert
- assert not r.returns_rows
+
+ # 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.returning
def test_autoclose_on_insert_implicit_returning(self, connection):
@@ -124,7 +130,21 @@ class InsertBehaviorTest(fixtures.TablesTest):
assert r._soft_closed
assert not r.closed
assert r.is_insert
- assert not r.returns_rows
+
+ # 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):