]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Enable F821
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 1 Jan 2020 23:24:03 +0000 (18:24 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 4 Jan 2020 17:56:26 +0000 (12:56 -0500)
In Ia63a510f9c1d08b055eef62cf047f1f427f0450c we introduced
"lambda combinations" which use a bit of function closure inspection
in order to allow for testing combinations that make use of symbols that
come from test fixtures, or from the test itself.

Two problems.  One is that we can't use F821 flake8 rule without either
adding lots of noqas, skipping the file, or adding arguments to the
lambdas themselves that are then populated, which makes for a very
verbose system.  The other is that the system is already verbose
with all those lambdas and the magic in use is a non-explicit kind,
hence F821 reminds us that if we can improve upon this, we should.

So let's improve upon it by making it so that the "lambda" is just
once and up front for the whole thing, and let it accept the arguments
directly.   This still requires magic, because these test cases need
to resolve at test collection time, not test runtime.  But we will
instead substitute a namespace up front that can be coerced into
its desired form within the tests.

Additionally, there's a little bit of py2k compatible type annotations
present; f821 is checking these, so we have to add those imports
also using the TYPE_CHECKING boolean so they don't take place in
py2k.

Change-Id: Idb7e7a0c8af86d9ab133f548511306ef68cdba14

17 files changed:
lib/sqlalchemy/dialects/mysql/pyodbc.py
lib/sqlalchemy/engine/mock.py
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/relationships.py
lib/sqlalchemy/sql/base.py
lib/sqlalchemy/sql/coercions.py
lib/sqlalchemy/sql/elements.py
lib/sqlalchemy/sql/selectable.py
lib/sqlalchemy/testing/__init__.py
lib/sqlalchemy/testing/exclusions.py
lib/sqlalchemy/testing/util.py
lib/sqlalchemy/util/__init__.py
lib/sqlalchemy/util/compat.py
setup.cfg
test/dialect/mssql/test_compiler.py
test/orm/test_query.py

index 6a411b98448e78fa9ae9f6c2bde02204eaadf96f..eb62e64258595e99a2ce4cf870f22ace98ccc17c 100644 (file)
@@ -126,7 +126,7 @@ class MySQLDialect_pyodbc(PyODBCConnector, MySQLDialect):
                 conn.setdecoding(pyodbc_SQL_CHAR, encoding="utf-8")
                 conn.setdecoding(pyodbc_SQL_WCHAR, encoding="utf-8")
                 conn.setencoding(str, encoding="utf-8")
-                conn.setencoding(unicode, encoding="utf-8")
+                conn.setencoding(unicode, encoding="utf-8")  # noqa: F821
 
         return on_connect
 
index d98d0ee3a17de4cd0e54c359ecb1a0a64040a3e8..570ee2d0430bbfd3a4cea2e16f810e9077edd413 100644 (file)
@@ -108,7 +108,7 @@ def create_mock_engine(url, executor, **kw):
     # consume dialect arguments from kwargs
     for k in util.get_cls_kwargs(dialect_cls):
         if k in kw:
-            dialect_args[k] = kwargs.pop(k)
+            dialect_args[k] = kw.pop(k)
 
     # create dialect
     dialect = dialect_cls(**dialect_args)
index 1805bf8db811cc0b67d0098d2e47b00f2e09eba1..66a18da9923de39f94da849a714cf6dbc2abd7a9 100644 (file)
@@ -1586,10 +1586,8 @@ def backref_listeners(attribute, key, uselist):
 _NO_HISTORY = util.symbol("NO_HISTORY")
 _NO_STATE_SYMBOLS = frozenset([id(PASSIVE_NO_RESULT), id(NO_VALUE)])
 
-History = util.namedtuple("History", ["added", "unchanged", "deleted"])
 
-
-class History(History):
+class History(util.namedtuple("History", ["added", "unchanged", "deleted"])):
     """A 3-tuple of added, unchanged and deleted values,
     representing the changes which have occurred on an instrumented
     attribute.
index 21479c08bd7cd26db785630eea52608e6859d617..c7b059fda18551d7870c3d7b316202d833f67360 100644 (file)
@@ -39,6 +39,12 @@ from ..sql import operators
 from ..sql import visitors
 from ..sql.traversals import HasCacheKey
 
+if util.TYPE_CHECKING:
+    from typing import Any
+    from typing import List
+    from typing import Optional
+    from .mapper import Mapper
+    from .util import AliasedInsp
 
 __all__ = (
     "EXT_CONTINUE",
@@ -363,8 +369,12 @@ class PropComparator(operators.ColumnOperators):
 
     __slots__ = "prop", "property", "_parententity", "_adapt_to_entity"
 
-    def __init__(self, prop, parentmapper, adapt_to_entity=None):
-        # type: (MapperProperty, Mapper, Optional(AliasedInsp))
+    def __init__(
+        self,
+        prop,  # type: MapperProperty
+        parentmapper,  # type: Mapper
+        adapt_to_entity=None,  # type: Optional[AliasedInsp]
+    ):
         self.prop = self.property = prop
         self._parententity = adapt_to_entity or parentmapper
         self._adapt_to_entity = adapt_to_entity
@@ -372,8 +382,10 @@ class PropComparator(operators.ColumnOperators):
     def __clause_element__(self):
         raise NotImplementedError("%r" % self)
 
-    def _bulk_update_tuples(self, value):
-        # type: (ColumnOperators) -> List[tuple[ColumnOperators, Any]]
+    def _bulk_update_tuples(
+        self, value  # type: (operators.ColumnOperators)
+    ):
+        # type: (...) -> List[tuple[operators.ColumnOperators, Any]]
         """Receive a SQL expression that represents a value in the SET
         clause of an UPDATE statement.
 
index f1764672ce990f4f306f75d599d7a18a58658da8..d745500c15d4262afb173b7c7e22451af5033453 100644 (file)
@@ -50,6 +50,11 @@ from ..sql.util import selectables_overlap
 from ..sql.util import visit_binary_product
 
 
+if util.TYPE_CHECKING:
+    from .util import AliasedInsp
+    from typing import Union
+
+
 def remote(expr):
     """Annotate a portion of a primaryjoin expression
     with a 'remote' annotation.
@@ -1859,9 +1864,9 @@ class RelationshipProperty(StrategizedProperty):
             )
 
     @util.memoized_property
-    def entity(self):  # type: () -> Union[AliasedInsp, Mapper]
+    def entity(self):  # type: () -> Union[AliasedInsp, mapperlib.Mapper]
         """Return the target mapped entity, which is an inspect() of the
-        class or aliased class tha is referred towards.
+        class or aliased class that is referred towards.
 
         """
         if callable(self.argument) and not isinstance(
index 60456223f00ede7957dedcc3f5b554222eae2fbd..a7324c45fa7c053fdd61335f46302170f72681dd 100644 (file)
@@ -19,9 +19,12 @@ from .visitors import ClauseVisitor
 from .. import exc
 from .. import util
 
-coercions = None  # type: types.ModuleType
-elements = None  # type: types.ModuleType
-type_api = None  # type: types.ModuleType
+if util.TYPE_CHECKING:
+    from types import ModuleType
+
+coercions = None  # type: ModuleType
+elements = None  # type: ModuleType
+type_api = None  # type: ModuleType
 
 PARSE_AUTOCOMMIT = util.symbol("PARSE_AUTOCOMMIT")
 NO_ARG = util.symbol("NO_ARG")
index 12ec7c7501a419a70339b4379e64d2e70624f58d..b3bf4e93b99b320f37faef0dc47ad1012c2c102b 100644 (file)
@@ -17,10 +17,13 @@ from .. import inspection
 from .. import util
 from ..util import collections_abc
 
-elements = None  # type: types.ModuleType
-schema = None  # type: types.ModuleType
-selectable = None  # type: types.ModuleType
-sqltypes = None  # type: types.ModuleType
+if util.TYPE_CHECKING:
+    from types import ModuleType
+
+elements = None  # type: ModuleType
+schema = None  # type: ModuleType
+selectable = None  # type: ModuleType
+sqltypes = None  # type: ModuleType
 
 
 def _is_literal(element):
index d94d91b1653d0be250da6417e1ef6f3fb4c95585..422eb62209526a14d07f7d97b1e20a47d8dc59b9 100644 (file)
@@ -43,6 +43,11 @@ from .. import exc
 from .. import inspection
 from .. import util
 
+if util.TYPE_CHECKING:
+    from typing import Any
+    from typing import Optional
+    from typing import Union
+
 
 def collate(expression, collation):
     """Return the clause ``expression COLLATE collation``.
@@ -709,7 +714,7 @@ class ColumnElement(
     _alt_names = ()
 
     def self_group(self, against=None):
-        # type: (Module, Module, Optional[Any]) -> ClauseEleent
+        # type: (Optional[Any]) -> ClauseElement
         if (
             against in (operators.and_, operators.or_, operators._asbool)
             and self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity
index 8f5503db085797899f855a90d57cb16d9e6b9305..136c9f868c9ff7c81cc667d341783c405929a333 100644 (file)
@@ -50,6 +50,10 @@ from .visitors import InternalTraversal
 from .. import exc
 from .. import util
 
+if util.TYPE_CHECKING:
+    from typing import Any
+    from typing import Optional
+
 
 class _OffsetLimitParam(BindParameter):
     @property
@@ -2096,7 +2100,7 @@ class SelectBase(
     _memoized_property = util.group_expirable_memoized_property()
 
     def _generate_fromclause_column_proxies(self, fromclause):
-        # type: (FromClause)
+        # type: (FromClause) -> None
         raise NotImplementedError()
 
     def _refresh_for_new_column(self, column):
@@ -2344,7 +2348,7 @@ class SelectStatementGrouping(GroupedElement, SelectBase):
     _is_select_container = True
 
     def __init__(self, element):
-        # type: (SelectBase)
+        # type: (SelectBase) -> None
         self.element = coercions.expect(roles.SelectStatementRole, element)
 
     @property
index e5425dd81f00ce6dd003f3f9acade8fa562511bd..ab1198da8990066e19a5cb62b121c6bb9f2224f2 100644 (file)
@@ -54,6 +54,7 @@ from .util import adict  # noqa
 from .util import fail  # noqa
 from .util import flag_combinations  # noqa
 from .util import force_drop_names  # noqa
+from .util import lambda_combinations  # noqa
 from .util import metadata_fixture  # noqa
 from .util import provide_metadata  # noqa
 from .util import resolve_lambda  # noqa
index 8b17f64c7c4dfdef67d89074a33bbc213ef211c3..b2828b107b53e530a7ea10fc779197a279ff39fb 100644 (file)
@@ -159,7 +159,9 @@ class compound(object):
         for fail in self.fails:
             if self._check_combinations(combination, fail) and fail(config):
                 if util.py2k:
-                    str_ex = unicode(ex).encode("utf-8", errors="ignore")
+                    str_ex = unicode(ex).encode(  # noqa: F821
+                        "utf-8", errors="ignore"
+                    )
                 else:
                     str_ex = str(ex)
                 print(
index 74c9b1aebc823534a9b62463b2a809f02a6e28bf..de20bb794fad9eaac4fc1beedf6698104a671cae 100644 (file)
@@ -12,13 +12,14 @@ import sys
 import time
 import types
 
+from . import mock
 from ..util import decorator
 from ..util import defaultdict
+from ..util import inspect_getfullargspec
 from ..util import jython
 from ..util import py2k
 from ..util import pypy
 
-
 if jython:
 
     def jython_gc_collect(*args):
@@ -276,6 +277,25 @@ def flag_combinations(*combinations):
     )
 
 
+def lambda_combinations(lambda_arg_sets, **kw):
+    from . import config
+
+    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.
@@ -285,10 +305,12 @@ def resolve_lambda(__fn, **kw):
 
     """
 
+    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()
+    return new_fn(**pass_pos_args)
 
 
 def metadata_fixture(ddl="function"):
index c1790439c8bd7228e0d811c4358ecf86ed4f50ef..a19636f623641dc6fd559cc7bee6bc4eb5830922 100644 (file)
@@ -80,6 +80,7 @@ from .compat import StringIO  # noqa
 from .compat import text_type  # noqa
 from .compat import threading  # noqa
 from .compat import timezone  # noqa
+from .compat import TYPE_CHECKING  # noqa
 from .compat import u  # noqa
 from .compat import ue  # noqa
 from .compat import unquote  # noqa
index 82bf68d2cd17c5b36e09cbeba65a2cf79bb94c0a..2cb5db5d42053e9368b4905a918f57055fbc4bd6 100644 (file)
@@ -158,6 +158,8 @@ if py3k:
     def ue(s):
         return s
 
+    from typing import TYPE_CHECKING
+
     # Unused. Kept for backwards compatibility.
     callable = callable  # noqa
 else:
@@ -244,8 +246,10 @@ else:
     def safe_bytestring(text):
         # py2k only
         if not isinstance(text, string_types):
-            return unicode(text).encode("ascii", errors="backslashreplace")
-        elif isinstance(text, unicode):
+            return unicode(text).encode(  # noqa: F821
+                "ascii", errors="backslashreplace"
+            )
+        elif isinstance(text, unicode):  # noqa: F821
             return text.encode("ascii", errors="backslashreplace")
         else:
             return text
@@ -259,6 +263,8 @@ else:
         "    raise tp, value, tb\n"
     )
 
+    TYPE_CHECKING = False
+
 
 if py35:
     from inspect import formatannotation
index 9943bab01dc321624eee1019a07b9a560a48ae00..2ee0abbe07af8846790188a28249a6255fbeee6f 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -20,7 +20,6 @@ ignore =
     A003,
     D,
     E203,E305,E711,E712,E721,E722,E741,
-    F821
     N801,N802,N806,
     RST304,RST303,RST299,RST399,
     W503,W504
index 9d46c3f3553432234bccd393f9f3049905e2b482..bb5199b00d65a7a647c6380d92286a35eeab531c 100644 (file)
@@ -315,7 +315,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
             },
         ),
         (
-            lambda: select([t]).where(t.c.foo.in_(["x", "y", "z"])),
+            lambda t: select([t]).where(t.c.foo.in_(["x", "y", "z"])),
             "SELECT sometable.foo FROM sometable WHERE sometable.foo "
             "IN ([POSTCOMPILE_foo_1])",
             {
@@ -323,7 +323,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
                 "check_post_param": {},
             },
         ),
-        (lambda: t.c.foo.in_([None]), "sometable.foo IN (NULL)", {}),
+        (lambda t: t.c.foo.in_([None]), "sometable.foo IN (NULL)", {}),
     )
     def test_strict_binds(self, expr, compiled, kw):
         """test the 'strict' compiler binds."""
index 558b4d91c7ac7096a9a0f67c1da30a36a899886b..271d85dd6968d54f3bbe445fba38ab4974c3e64e 100644 (file)
@@ -161,7 +161,7 @@ class RowTupleTest(QueryTest):
         is_(sess.query(ex)._deep_entity_zero(), inspect(User))
 
     @testing.combinations(
-        lambda: (
+        lambda sess, User: (
             sess.query(User),
             [
                 {
@@ -173,7 +173,7 @@ class RowTupleTest(QueryTest):
                 }
             ],
         ),
-        lambda: (
+        lambda sess, User, users: (
             sess.query(User.id, User),
             [
                 {
@@ -192,7 +192,7 @@ class RowTupleTest(QueryTest):
                 },
             ],
         ),
-        lambda: (
+        lambda sess, User, user_alias, users: (
             sess.query(User.id, user_alias),
             [
                 {
@@ -211,7 +211,7 @@ class RowTupleTest(QueryTest):
                 },
             ],
         ),
-        lambda: (
+        lambda sess, user_alias, users: (
             sess.query(user_alias.id),
             [
                 {
@@ -223,7 +223,7 @@ class RowTupleTest(QueryTest):
                 }
             ],
         ),
-        lambda: (
+        lambda sess, user_alias_id_label, users, user_alias: (
             sess.query(user_alias_id_label),
             [
                 {
@@ -235,7 +235,7 @@ class RowTupleTest(QueryTest):
                 }
             ],
         ),
-        lambda: (
+        lambda sess, address_alias, Address: (
             sess.query(address_alias),
             [
                 {
@@ -247,7 +247,7 @@ class RowTupleTest(QueryTest):
                 }
             ],
         ),
-        lambda: (
+        lambda sess, name_label, fn, users, User: (
             sess.query(name_label, fn),
             [
                 {
@@ -266,7 +266,7 @@ class RowTupleTest(QueryTest):
                 },
             ],
         ),
-        lambda: (
+        lambda sess, cte: (
             sess.query(cte),
             [
                 {
@@ -278,7 +278,7 @@ class RowTupleTest(QueryTest):
                 }
             ],
         ),
-        lambda: (
+        lambda sess, subq1: (
             sess.query(subq1.c.id),
             [
                 {
@@ -290,7 +290,7 @@ class RowTupleTest(QueryTest):
                 }
             ],
         ),
-        lambda: (
+        lambda sess, subq2: (
             sess.query(subq2.c.id),
             [
                 {
@@ -302,7 +302,7 @@ class RowTupleTest(QueryTest):
                 }
             ],
         ),
-        lambda: (
+        lambda sess, users: (
             sess.query(users),
             [
                 {
@@ -321,7 +321,7 @@ class RowTupleTest(QueryTest):
                 },
             ],
         ),
-        lambda: (
+        lambda sess, users: (
             sess.query(users.c.name),
             [
                 {
@@ -333,7 +333,7 @@ class RowTupleTest(QueryTest):
                 }
             ],
         ),
-        lambda: (
+        lambda sess, bundle, User: (
             sess.query(bundle),
             [
                 {
@@ -968,9 +968,9 @@ class GetTest(QueryTest):
 
 class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
     @testing.combinations(
-        lambda: s.query(User).limit(2),
-        lambda: s.query(User).filter(User.id == 1).offset(2),
-        lambda: s.query(User).limit(2).offset(2),
+        lambda s, User: s.query(User).limit(2),
+        lambda s, User: s.query(User).filter(User.id == 1).offset(2),
+        lambda s, User: s.query(User).limit(2).offset(2),
     )
     def test_no_limit_offset(self, test_case):
         User = self.classes.User
@@ -1128,11 +1128,11 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
         is_(q1._entity_zero(), inspect(User))
 
     @testing.combinations(
-        lambda: s.query(User).filter(User.id == 5),
-        lambda: s.query(User).filter_by(id=5),
-        lambda: s.query(User).limit(5),
-        lambda: s.query(User).group_by(User.name),
-        lambda: s.query(User).order_by(User.name),
+        lambda s, User: s.query(User).filter(User.id == 5),
+        lambda s, User: s.query(User).filter_by(id=5),
+        lambda s, User: s.query(User).limit(5),
+        lambda s, User: s.query(User).group_by(User.name),
+        lambda s, User: s.query(User).order_by(User.name),
     )
     def test_from_statement(self, test_case):
         User = self.classes.User
@@ -1144,11 +1144,11 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
         assert_raises(sa_exc.InvalidRequestError, q.from_statement, text("x"))
 
     @testing.combinations(
-        (Query.filter, lambda: meth(User.id == 5)),
-        (Query.filter_by, lambda: meth(id=5)),
-        (Query.limit, lambda: meth(5)),
-        (Query.group_by, lambda: meth(User.name)),
-        (Query.order_by, lambda: meth(User.name)),
+        (Query.filter, lambda meth, User: meth(User.id == 5)),
+        (Query.filter_by, lambda meth: meth(id=5)),
+        (Query.limit, lambda meth: meth(5)),
+        (Query.group_by, lambda meth, User: meth(User.name)),
+        (Query.order_by, lambda meth, User: meth(User.name)),
     )
     def test_from_statement_text(self, meth, test_case):
 
@@ -1251,13 +1251,13 @@ class OperatorTest(QueryTest, AssertsCompiledSQL):
         id_="ar",
     )
     @testing.combinations(
-        (lambda: 5, lambda: User.id, ":id_1 %s users.id"),
+        (lambda User: 5, lambda User: User.id, ":id_1 %s users.id"),
         (lambda: 5, lambda: literal(6), ":param_1 %s :param_2"),
-        (lambda: User.id, lambda: 5, "users.id %s :id_1"),
-        (lambda: User.id, lambda: literal("b"), "users.id %s :param_1"),
-        (lambda: User.id, lambda: User.id, "users.id %s users.id"),
+        (lambda User: User.id, lambda: 5, "users.id %s :id_1"),
+        (lambda User: User.id, lambda: literal("b"), "users.id %s :param_1"),
+        (lambda User: User.id, lambda User: User.id, "users.id %s users.id"),
         (lambda: literal(5), lambda: "b", ":param_1 %s :param_2"),
-        (lambda: literal(5), lambda: User.id, ":param_1 %s users.id"),
+        (lambda: literal(5), lambda User: User.id, ":param_1 %s users.id"),
         (lambda: literal(5), lambda: literal(6), ":param_1 %s :param_2"),
         argnames="lhs, rhs, res",
         id_="aar",
@@ -1280,35 +1280,30 @@ class OperatorTest(QueryTest, AssertsCompiledSQL):
         id_="arr",
         argnames="py_op, fwd_op, rev_op",
     )
-    @testing.combinations(
-        (lambda: "a", lambda: User.id, ":id_1", "users.id"),
-        (
-            lambda: "a",
-            lambda: literal("b"),
-            ":param_2",
-            ":param_1",
-        ),  # note swap!
-        (lambda: User.id, lambda: "b", "users.id", ":id_1"),
-        (lambda: User.id, lambda: literal("b"), "users.id", ":param_1"),
-        (lambda: User.id, lambda: User.id, "users.id", "users.id"),
-        (lambda: literal("a"), lambda: "b", ":param_1", ":param_2"),
-        (lambda: literal("a"), lambda: User.id, ":param_1", "users.id"),
-        (lambda: literal("a"), lambda: literal("b"), ":param_1", ":param_2"),
-        (lambda: ualias.id, lambda: literal("b"), "users_1.id", ":param_1"),
-        (lambda: User.id, lambda: ualias.name, "users.id", "users_1.name"),
-        (lambda: User.name, lambda: ualias.name, "users.name", "users_1.name"),
-        (lambda: ualias.name, lambda: User.name, "users_1.name", "users.name"),
-        argnames="lhs, rhs, l_sql, r_sql",
-        id_="aarr",
+    @testing.lambda_combinations(
+        lambda User, ualias: (
+            ("a", User.id, ":id_1", "users.id"),
+            ("a", literal("b"), ":param_2", ":param_1"),  # note swap!
+            (User.id, "b", "users.id", ":id_1"),
+            (User.id, literal("b"), "users.id", ":param_1"),
+            (User.id, User.id, "users.id", "users.id"),
+            (literal("a"), "b", ":param_1", ":param_2"),
+            (literal("a"), User.id, ":param_1", "users.id"),
+            (literal("a"), literal("b"), ":param_1", ":param_2"),
+            (ualias.id, literal("b"), "users_1.id", ":param_1"),
+            (User.id, ualias.name, "users.id", "users_1.name"),
+            (User.name, ualias.name, "users.name", "users_1.name"),
+            (ualias.name, User.name, "users_1.name", "users.name"),
+        ),
+        argnames="fixture",
     )
-    def test_comparison(self, py_op, fwd_op, rev_op, lhs, rhs, l_sql, r_sql):
+    def test_comparison(self, py_op, fwd_op, rev_op, fixture):
         User = self.classes.User
 
         create_session().query(User)
         ualias = aliased(User)
 
-        lhs = testing.resolve_lambda(lhs, User=User, ualias=ualias)
-        rhs = testing.resolve_lambda(rhs, User=User, ualias=ualias)
+        lhs, rhs, l_sql, r_sql = fixture(User=User, ualias=ualias)
 
         # the compiled clause should match either (e.g.):
         # 'a' < 'b' -or- 'b' > 'a'.