]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Apply underscore naming to several more operators
authorjonathan vanasco <jonathan@2xlp.com>
Wed, 28 Oct 2020 18:35:39 +0000 (14:35 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 30 Oct 2020 14:02:29 +0000 (10:02 -0400)
The operator changes are:

* `isfalse` is now `is_false`
* `isnot_distinct_from` is now `is_not_distinct_from`
* `istrue` is now `is_true`
* `notbetween` is now `not_between`
* `notcontains` is now `not_contains`
* `notendswith` is now `not_endswith`
* `notilike` is now `not_ilike`
* `notlike` is now `not_like`
* `notmatch` is now `not_match`
* `notstartswith` is now `not_startswith`
* `nullsfirst` is now `nulls_first`
* `nullslast` is now `nulls_last`

Because these are core operators, the internal migration strategy for this
change is to support legacy terms for an extended period of time -- if not
indefinitely -- but update all documentation, tutorials, and internal usage
to the new terms.  The new terms are used to define the functions, and
the legacy terms have been deprecated into aliases of the new terms.

Fixes: #5435
Change-Id: Ifbd7cb1cdda5981990243c4fc4b4ff467dc132ac

25 files changed:
doc/build/changelog/unreleased_14/5435.rst [new file with mode: 0644]
doc/build/core/custom_types.rst
doc/build/core/sqlelement.rst
lib/sqlalchemy/__init__.py
lib/sqlalchemy/dialects/firebird/base.py
lib/sqlalchemy/dialects/mssql/base.py
lib/sqlalchemy/dialects/mysql/base.py
lib/sqlalchemy/dialects/oracle/base.py
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/dialects/sqlite/base.py
lib/sqlalchemy/engine/reflection.py
lib/sqlalchemy/orm/evaluator.py
lib/sqlalchemy/sql/__init__.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/default_comparator.py
lib/sqlalchemy/sql/elements.py
lib/sqlalchemy/sql/expression.py
lib/sqlalchemy/sql/operators.py
lib/sqlalchemy/testing/suite/test_select.py
test/dialect/postgresql/test_reflection.py
test/dialect/test_sqlite.py
test/sql/test_compiler.py
test/sql/test_deprecations.py
test/sql/test_operators.py
test/sql/test_query.py

diff --git a/doc/build/changelog/unreleased_14/5435.rst b/doc/build/changelog/unreleased_14/5435.rst
new file mode 100644 (file)
index 0000000..618926e
--- /dev/null
@@ -0,0 +1,27 @@
+.. change::
+    :tags: change, sql
+    :tickets: 5435
+
+    Several operators are renamed to achieve more consistent naming across
+    SQLAlchemy.
+
+    The operator changes are:
+
+    * `isfalse` is now `is_false`
+    * `isnot_distinct_from` is now `is_not_distinct_from`
+    * `istrue` is now `is_true`
+    * `notbetween` is now `not_between`
+    * `notcontains` is now `not_contains`
+    * `notendswith` is now `not_endswith`
+    * `notilike` is now `not_ilike`
+    * `notlike` is now `not_like`
+    * `notmatch` is now `not_match`
+    * `notstartswith` is now `not_startswith`
+    * `nullsfirst` is now `nulls_first`
+    * `nullslast` is now `nulls_last`
+
+    Because these are core operators, the internal migration strategy for this
+    change is to support legacy terms for an extended period of time -- if not
+    indefinitely -- but update all documentation, tutorials, and internal usage
+    to the new terms.  The new terms are used to define the functions, and
+    the legacy terms have been deprecated into aliases of the new terms.
index 3d65ceeb6566451a8d2e80211addf3b5227521c1..31c66b1e6dc8c1f6e108ba81f2455b86e5dbe6b0 100644 (file)
@@ -307,7 +307,7 @@ method::
         impl = VARCHAR
 
         def coerce_compared_value(self, op, value):
-            if op in (operators.like_op, operators.notlike_op):
+            if op in (operators.like_op, operators.not_like_op):
                 return String()
             else:
                 return self
index 29019eb18b895e5b69f77a241685c139af30ee95..8e65993624d9ac586f7197218d3bd8157634f747 100644 (file)
@@ -98,9 +98,9 @@ Functions listed here are more commonly available as methods from any
 
 .. autofunction:: label
 
-.. autofunction:: nullsfirst
+.. autofunction:: nulls_first
 
-.. autofunction:: nullslast
+.. autofunction:: nulls_last
 
 .. autofunction:: over
 
index d2c99b5b2598613eb97e9aa223e4104e22d04079..1146803f536f73d009b639b8021d081a57dcecff 100644 (file)
@@ -61,8 +61,10 @@ from .sql import literal_column  # noqa
 from .sql import modifier  # noqa
 from .sql import not_  # noqa
 from .sql import null  # noqa
-from .sql import nullsfirst  # noqa
-from .sql import nullslast  # noqa
+from .sql import nullsfirst  # noqa; deprecated 1.4; see #5435
+from .sql import nullslast  # noqa; deprecated 1.4; see #5435
+from .sql import nulls_first  # noqa
+from .sql import nulls_last  # noqa
 from .sql import or_  # noqa
 from .sql import outerjoin  # noqa
 from .sql import outparam  # noqa
index 680968b9edf6ab8f7f764be784120820e6b69555..8a110fbd36412274754d707a70de7bfbd5678f1e 100644 (file)
@@ -439,7 +439,7 @@ class FBCompiler(sql.compiler.SQLCompiler):
     # def visit_contains_op_binary(self, binary, operator, **kw):
     # cant use CONTAINING b.c. it's case insensitive.
 
-    # def visit_notcontains_op_binary(self, binary, operator, **kw):
+    # def visit_not_contains_op_binary(self, binary, operator, **kw):
     # cant use NOT CONTAINING b.c. it's case insensitive.
 
     def visit_now_func(self, fn, **kw):
@@ -451,7 +451,7 @@ class FBCompiler(sql.compiler.SQLCompiler):
             binary.right._compiler_dispatch(self, **kw),
         )
 
-    def visit_notstartswith_op_binary(self, binary, operator, **kw):
+    def visit_not_startswith_op_binary(self, binary, operator, **kw):
         return "%s NOT STARTING WITH %s" % (
             binary.left._compiler_dispatch(self, **kw),
             binary.right._compiler_dispatch(self, **kw),
index a224c00bbd14973698bdfa5cc7bf2026087679b2..bfe7a00baa554df4d57ed801ba97b1c8a73548d1 100644 (file)
@@ -2077,7 +2077,7 @@ class MSSQLCompiler(compiler.SQLCompiler):
             self.process(binary.right),
         )
 
-    def visit_isnot_distinct_from_binary(self, binary, operator, **kw):
+    def visit_is_not_distinct_from_binary(self, binary, operator, **kw):
         return "EXISTS (SELECT %s INTERSECT SELECT %s)" % (
             self.process(binary.left),
             self.process(binary.right),
@@ -2165,7 +2165,7 @@ class MSSQLStrictCompiler(MSSQLCompiler):
             self.process(binary.right, **kw),
         )
 
-    def visit_notin_op_binary(self, binary, operator, **kw):
+    def visit_not_in_op_binary(self, binary, operator, **kw):
         kw["literal_execute"] = True
         return "%s NOT IN %s" % (
             self.process(binary.left, **kw),
index 77f65799c2ea47bb63cfece35d9b8088c25249c2..04175dedff51057c39c5db22b7f1797b3b7c4cd5 100644 (file)
@@ -1773,7 +1773,7 @@ class MySQLCompiler(compiler.SQLCompiler):
             self.process(binary.right),
         )
 
-    def visit_isnot_distinct_from_binary(self, binary, operator, **kw):
+    def visit_is_not_distinct_from_binary(self, binary, operator, **kw):
         return "%s <=> %s" % (
             self.process(binary.left),
             self.process(binary.right),
index 1f1f7501bd378def0aa9d24d9994d1040637c410..7bdaafd82d310f9be01eb57c86582a2d02a83c4c 100644 (file)
@@ -1213,7 +1213,7 @@ class OracleCompiler(compiler.SQLCompiler):
             self.process(binary.right),
         )
 
-    def visit_isnot_distinct_from_binary(self, binary, operator, **kw):
+    def visit_is_not_distinct_from_binary(self, binary, operator, **kw):
         return "DECODE(%s, %s, 0, 1) = 0" % (
             self.process(binary.left),
             self.process(binary.right),
index ea6921b2db21864551b586eb0143865d275da442..391361e2379407b66465adc67cca13a05a1108d8 100644 (file)
@@ -1865,7 +1865,7 @@ class PGCompiler(compiler.SQLCompiler):
             else ""
         )
 
-    def visit_notilike_op_binary(self, binary, operator, **kw):
+    def visit_not_ilike_op_binary(self, binary, operator, **kw):
         escape = binary.modifiers.get("escape", None)
         return "%s NOT ILIKE %s" % (
             self.process(binary.left, **kw),
@@ -3850,10 +3850,10 @@ class PGDialect(default.DefaultDialect):
                     if col_flags & 0x01:
                         col_sorting += ("desc",)
                         if not (col_flags & 0x02):
-                            col_sorting += ("nullslast",)
+                            col_sorting += ("nulls_last",)
                     else:
                         if col_flags & 0x02:
-                            col_sorting += ("nullsfirst",)
+                            col_sorting += ("nulls_first",)
                     if col_sorting:
                         sorting[col_idx] = col_sorting
                 if sorting:
index 8a4fbe8e55d3a17df7063288d72f9c8100514dca..5efd0d9c991edc3fd7dc32afd1ab24f65da2fd4f 100644 (file)
@@ -1043,7 +1043,7 @@ class SQLiteCompiler(compiler.SQLCompiler):
             self.process(binary.right),
         )
 
-    def visit_isnot_distinct_from_binary(self, binary, operator, **kw):
+    def visit_is_not_distinct_from_binary(self, binary, operator, **kw):
         return "%s IS %s" % (
             self.process(binary.left),
             self.process(binary.right),
index 6af74d05276170c62b4f903594391f28e3b5f20f..daebfe26358abe33b6e94548b039219c67340a65 100644 (file)
@@ -581,7 +581,7 @@ class Inspector(object):
 
         * ``column_sorting`` -
           optional dict mapping column names to tuple of sort keywords,
-          which may include ``asc``, ``desc``, ``nullsfirst``, ``nullslast``.
+          which may include ``asc``, ``desc``, ``nulls_first``, ``nulls_last``.
 
           .. versionadded:: 1.3.5
 
@@ -1006,8 +1006,8 @@ class Inspector(object):
     _index_sort_exprs = [
         ("asc", operators.asc_op),
         ("desc", operators.desc_op),
-        ("nullsfirst", operators.nullsfirst_op),
-        ("nullslast", operators.nullslast_op),
+        ("nulls_first", operators.nulls_first_op),
+        ("nulls_last", operators.nulls_last_op),
     ]
 
     def _reflect_indexes(
index 23c48329da8ebb22f3bfa10839d6c777df9f8d7e..8763f0a3d309260499204b665265b4e984872e4a 100644 (file)
@@ -56,9 +56,9 @@ _notimplemented_ops = set(
     getattr(operators, op)
     for op in (
         "like_op",
-        "notlike_op",
+        "not_like_op",
         "ilike_op",
-        "notilike_op",
+        "not_ilike_op",
         "startswith_op",
         "between_op",
         "endswith_op",
index 8f6dc8e72d99fadeb3b0a3684971bf7e81d7240f..8cfd20054e4a3d3e36dca5c8698bd506218a27a5 100644 (file)
@@ -55,8 +55,10 @@ from .expression import literal_column  # noqa
 from .expression import modifier  # noqa
 from .expression import not_  # noqa
 from .expression import null  # noqa
-from .expression import nullsfirst  # noqa
-from .expression import nullslast  # noqa
+from .expression import nullsfirst  # noqa; deprecated 1.4; see #5435
+from .expression import nullslast  # noqa; deprecated 1.4; see #5435
+from .expression import nulls_first  # noqa
+from .expression import nulls_last  # noqa
 from .expression import or_  # noqa
 from .expression import outerjoin  # noqa
 from .expression import outparam  # noqa
index 10499975c7b26edd0c2fcd406a0a5840250e06eb..23c9d5c0a3ad87f99249d348b623a9866e00fbb4 100644 (file)
@@ -185,10 +185,10 @@ OPERATORS = {
     operators.ge: " >= ",
     operators.eq: " = ",
     operators.is_distinct_from: " IS DISTINCT FROM ",
-    operators.isnot_distinct_from: " IS NOT DISTINCT FROM ",
+    operators.is_not_distinct_from: " IS NOT DISTINCT FROM ",
     operators.concat_op: " || ",
     operators.match_op: " MATCH ",
-    operators.notmatch_op: " NOT MATCH ",
+    operators.not_match_op: " NOT MATCH ",
     operators.in_op: " IN ",
     operators.not_in_op: " NOT IN ",
     operators.comma_op: ", ",
@@ -206,8 +206,8 @@ OPERATORS = {
     # modifiers
     operators.desc_op: " DESC",
     operators.asc_op: " ASC",
-    operators.nullsfirst_op: " NULLS FIRST",
-    operators.nullslast_op: " NULLS LAST",
+    operators.nulls_first_op: " NULLS FIRST",
+    operators.nulls_last_op: " NULLS LAST",
 }
 
 FUNCTIONS = {
@@ -1860,7 +1860,7 @@ class SQLCompiler(Compiled):
                 "Unary expression has no operator or modifier"
             )
 
-    def visit_istrue_unary_operator(self, element, operator, **kw):
+    def visit_is_true_unary_operator(self, element, operator, **kw):
         if (
             element._is_implicitly_boolean
             or self.dialect.supports_native_boolean
@@ -1869,7 +1869,7 @@ class SQLCompiler(Compiled):
         else:
             return "%s = 1" % self.process(element.element, **kw)
 
-    def visit_isfalse_unary_operator(self, element, operator, **kw):
+    def visit_is_false_unary_operator(self, element, operator, **kw):
         if (
             element._is_implicitly_boolean
             or self.dialect.supports_native_boolean
@@ -1878,7 +1878,7 @@ class SQLCompiler(Compiled):
         else:
             return "%s = 0" % self.process(element.element, **kw)
 
-    def visit_notmatch_op_binary(self, binary, operator, **kw):
+    def visit_not_match_op_binary(self, binary, operator, **kw):
         return "NOT %s" % self.visit_binary(
             binary, override_operator=operators.match_op
         )
@@ -2083,11 +2083,11 @@ class SQLCompiler(Compiled):
         binary.right = percent.__add__(binary.right).__add__(percent)
         return self.visit_like_op_binary(binary, operator, **kw)
 
-    def visit_notcontains_op_binary(self, binary, operator, **kw):
+    def visit_not_contains_op_binary(self, binary, operator, **kw):
         binary = binary._clone()
         percent = self._like_percent_literal
         binary.right = percent.__add__(binary.right).__add__(percent)
-        return self.visit_notlike_op_binary(binary, operator, **kw)
+        return self.visit_not_like_op_binary(binary, operator, **kw)
 
     def visit_startswith_op_binary(self, binary, operator, **kw):
         binary = binary._clone()
@@ -2095,11 +2095,11 @@ class SQLCompiler(Compiled):
         binary.right = percent.__radd__(binary.right)
         return self.visit_like_op_binary(binary, operator, **kw)
 
-    def visit_notstartswith_op_binary(self, binary, operator, **kw):
+    def visit_not_startswith_op_binary(self, binary, operator, **kw):
         binary = binary._clone()
         percent = self._like_percent_literal
         binary.right = percent.__radd__(binary.right)
-        return self.visit_notlike_op_binary(binary, operator, **kw)
+        return self.visit_not_like_op_binary(binary, operator, **kw)
 
     def visit_endswith_op_binary(self, binary, operator, **kw):
         binary = binary._clone()
@@ -2107,11 +2107,11 @@ class SQLCompiler(Compiled):
         binary.right = percent.__add__(binary.right)
         return self.visit_like_op_binary(binary, operator, **kw)
 
-    def visit_notendswith_op_binary(self, binary, operator, **kw):
+    def visit_not_endswith_op_binary(self, binary, operator, **kw):
         binary = binary._clone()
         percent = self._like_percent_literal
         binary.right = percent.__add__(binary.right)
-        return self.visit_notlike_op_binary(binary, operator, **kw)
+        return self.visit_not_like_op_binary(binary, operator, **kw)
 
     def visit_like_op_binary(self, binary, operator, **kw):
         escape = binary.modifiers.get("escape", None)
@@ -2126,7 +2126,7 @@ class SQLCompiler(Compiled):
             else ""
         )
 
-    def visit_notlike_op_binary(self, binary, operator, **kw):
+    def visit_not_like_op_binary(self, binary, operator, **kw):
         escape = binary.modifiers.get("escape", None)
         return "%s NOT LIKE %s" % (
             binary.left._compiler_dispatch(self, **kw),
@@ -2148,7 +2148,7 @@ class SQLCompiler(Compiled):
             else ""
         )
 
-    def visit_notilike_op_binary(self, binary, operator, **kw):
+    def visit_not_ilike_op_binary(self, binary, operator, **kw):
         escape = binary.modifiers.get("escape", None)
         return "lower(%s) NOT LIKE lower(%s)" % (
             binary.left._compiler_dispatch(self, **kw),
@@ -2165,7 +2165,7 @@ class SQLCompiler(Compiled):
             binary, " BETWEEN SYMMETRIC " if symmetric else " BETWEEN ", **kw
         )
 
-    def visit_notbetween_op_binary(self, binary, operator, **kw):
+    def visit_not_between_op_binary(self, binary, operator, **kw):
         symmetric = binary.modifiers.get("symmetric", False)
         return self._generate_generic_binary(
             binary,
index d5762ff1f3e236c41fc85df345fd25726bf0c848..be6d0787b1bc94dc68def869dbd0226dfb6db9dc 100644 (file)
@@ -57,7 +57,10 @@ def _boolean_compare(
                 negate=negate,
                 modifiers=kwargs,
             )
-        elif op in (operators.is_distinct_from, operators.isnot_distinct_from):
+        elif op in (
+            operators.is_distinct_from,
+            operators.is_not_distinct_from,
+        ):
             return BinaryExpression(
                 expr,
                 coercions.expect(roles.ConstExprRole, obj),
@@ -87,7 +90,7 @@ def _boolean_compare(
             else:
                 raise exc.ArgumentError(
                     "Only '=', '!=', 'is_()', 'is_not()', "
-                    "'is_distinct_from()', 'isnot_distinct_from()' "
+                    "'is_distinct_from()', 'is_not_distinct_from()' "
                     "operators can be used with None/True/False"
                 )
     else:
@@ -205,7 +208,7 @@ def _match_impl(expr, op, other, **kw):
             operator=operators.match_op,
         ),
         result_type=type_api.MATCHTYPE,
-        negate=operators.notmatch_op
+        negate=operators.not_match_op
         if op is operators.match_op
         else operators.match_op,
         **kw
@@ -241,7 +244,7 @@ def _between_impl(expr, op, cleft, cright, **kw):
             group_contents=False,
         ),
         op,
-        negate=operators.notbetween_op
+        negate=operators.not_between_op
         if op is operators.between_op
         else operators.between_op,
         modifiers=kw,
@@ -315,29 +318,29 @@ operator_lookup = {
     "gt": (_boolean_compare, operators.le),
     "ge": (_boolean_compare, operators.lt),
     "eq": (_boolean_compare, operators.ne),
-    "is_distinct_from": (_boolean_compare, operators.isnot_distinct_from),
-    "isnot_distinct_from": (_boolean_compare, operators.is_distinct_from),
-    "like_op": (_boolean_compare, operators.notlike_op),
-    "ilike_op": (_boolean_compare, operators.notilike_op),
-    "notlike_op": (_boolean_compare, operators.like_op),
-    "notilike_op": (_boolean_compare, operators.ilike_op),
-    "contains_op": (_boolean_compare, operators.notcontains_op),
-    "startswith_op": (_boolean_compare, operators.notstartswith_op),
-    "endswith_op": (_boolean_compare, operators.notendswith_op),
+    "is_distinct_from": (_boolean_compare, operators.is_not_distinct_from),
+    "is_not_distinct_from": (_boolean_compare, operators.is_distinct_from),
+    "like_op": (_boolean_compare, operators.not_like_op),
+    "ilike_op": (_boolean_compare, operators.not_ilike_op),
+    "not_like_op": (_boolean_compare, operators.like_op),
+    "not_ilike_op": (_boolean_compare, operators.ilike_op),
+    "contains_op": (_boolean_compare, operators.not_contains_op),
+    "startswith_op": (_boolean_compare, operators.not_startswith_op),
+    "endswith_op": (_boolean_compare, operators.not_endswith_op),
     "desc_op": (_scalar, UnaryExpression._create_desc),
     "asc_op": (_scalar, UnaryExpression._create_asc),
-    "nullsfirst_op": (_scalar, UnaryExpression._create_nullsfirst),
-    "nullslast_op": (_scalar, UnaryExpression._create_nullslast),
+    "nulls_first_op": (_scalar, UnaryExpression._create_nulls_first),
+    "nulls_last_op": (_scalar, UnaryExpression._create_nulls_last),
     "in_op": (_in_impl, operators.not_in_op),
     "not_in_op": (_in_impl, operators.in_op),
     "is_": (_boolean_compare, operators.is_),
     "is_not": (_boolean_compare, operators.is_not),
     "collate": (_collate_impl,),
     "match_op": (_match_impl,),
-    "notmatch_op": (_match_impl,),
+    "not_match_op": (_match_impl,),
     "distinct_op": (_distinct_impl,),
     "between_op": (_between_impl,),
-    "notbetween_op": (_between_impl,),
+    "not_between_op": (_between_impl,),
     "neg": (_neg_impl,),
     "getitem": (_getitem_impl,),
     "lshift": (_unsupported_impl,),
index 00e28ac2072ea8734a7df632be70f59f764e019f..e268abc8aa7f5895b1a215a6835c0b1729bbe9c7 100644 (file)
@@ -766,7 +766,7 @@ class ColumnElement(
             against in (operators.and_, operators.or_, operators._asbool)
             and self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity
         ):
-            return AsBoolean(self, operators.istrue, operators.isfalse)
+            return AsBoolean(self, operators.is_true, operators.is_false)
         elif against in (operators.any_op, operators.all_op):
             return Grouping(self)
         else:
@@ -774,7 +774,7 @@ class ColumnElement(
 
     def _negate(self):
         if self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity:
-            return AsBoolean(self, operators.isfalse, operators.istrue)
+            return AsBoolean(self, operators.is_false, operators.is_true)
         else:
             return super(ColumnElement, self)._negate()
 
@@ -3096,7 +3096,7 @@ class UnaryExpression(ColumnElement):
 
     :class:`.UnaryExpression` is the basis for several unary operators
     including those used by :func:`.desc`, :func:`.asc`, :func:`.distinct`,
-    :func:`.nullsfirst` and :func:`.nullslast`.
+    :func:`.nulls_first` and :func:`.nulls_last`.
 
     """
 
@@ -3126,31 +3126,35 @@ class UnaryExpression(ColumnElement):
         self.wraps_column_expression = wraps_column_expression
 
     @classmethod
-    def _create_nullsfirst(cls, column):
+    def _create_nulls_first(cls, column):
         """Produce the ``NULLS FIRST`` modifier for an ``ORDER BY`` expression.
 
-        :func:`.nullsfirst` is intended to modify the expression produced
+        :func:`.nulls_first` is intended to modify the expression produced
         by :func:`.asc` or :func:`.desc`, and indicates how NULL values
         should be handled when they are encountered during ordering::
 
 
-            from sqlalchemy import desc, nullsfirst
+            from sqlalchemy import desc, nulls_first
 
             stmt = select(users_table).order_by(
-                nullsfirst(desc(users_table.c.name)))
+                nulls_first(desc(users_table.c.name)))
 
         The SQL expression from the above would resemble::
 
             SELECT id, name FROM user ORDER BY name DESC NULLS FIRST
 
-        Like :func:`.asc` and :func:`.desc`, :func:`.nullsfirst` is typically
+        Like :func:`.asc` and :func:`.desc`, :func:`.nulls_first` is typically
         invoked from the column expression itself using
-        :meth:`_expression.ColumnElement.nullsfirst`,
+        :meth:`_expression.ColumnElement.nulls_first`,
         rather than as its standalone
         function version, as in::
 
             stmt = select(users_table).order_by(
-                users_table.c.name.desc().nullsfirst())
+                users_table.c.name.desc().nulls_first())
+
+        .. versionchanged:: 1.4 :func:`.nulls_first` is renamed from
+            :func:`.nullsfirst` in previous releases.
+            The previous name remains available for backwards compatibility.
 
         .. seealso::
 
@@ -3158,43 +3162,47 @@ class UnaryExpression(ColumnElement):
 
             :func:`.desc`
 
-            :func:`.nullslast`
+            :func:`.nulls_last`
 
             :meth:`_expression.Select.order_by`
 
         """
         return UnaryExpression(
             coercions.expect(roles.ByOfRole, column),
-            modifier=operators.nullsfirst_op,
+            modifier=operators.nulls_first_op,
             wraps_column_expression=False,
         )
 
     @classmethod
-    def _create_nullslast(cls, column):
+    def _create_nulls_last(cls, column):
         """Produce the ``NULLS LAST`` modifier for an ``ORDER BY`` expression.
 
-        :func:`.nullslast` is intended to modify the expression produced
+        :func:`.nulls_last` is intended to modify the expression produced
         by :func:`.asc` or :func:`.desc`, and indicates how NULL values
         should be handled when they are encountered during ordering::
 
 
-            from sqlalchemy import desc, nullslast
+            from sqlalchemy import desc, nulls_last
 
             stmt = select(users_table).order_by(
-                nullslast(desc(users_table.c.name)))
+                nulls_last(desc(users_table.c.name)))
 
         The SQL expression from the above would resemble::
 
             SELECT id, name FROM user ORDER BY name DESC NULLS LAST
 
-        Like :func:`.asc` and :func:`.desc`, :func:`.nullslast` is typically
+        Like :func:`.asc` and :func:`.desc`, :func:`.nulls_last` is typically
         invoked from the column expression itself using
-        :meth:`_expression.ColumnElement.nullslast`,
+        :meth:`_expression.ColumnElement.nulls_last`,
         rather than as its standalone
         function version, as in::
 
             stmt = select(users_table).order_by(
-                users_table.c.name.desc().nullslast())
+                users_table.c.name.desc().nulls_last())
+
+        .. versionchanged:: 1.4 :func:`.nulls_last` is renamed from
+            :func:`.nullslast` in previous releases.
+            The previous name remains available for backwards compatibility.
 
         .. seealso::
 
@@ -3202,14 +3210,14 @@ class UnaryExpression(ColumnElement):
 
             :func:`.desc`
 
-            :func:`.nullsfirst`
+            :func:`.nulls_first`
 
             :meth:`_expression.Select.order_by`
 
         """
         return UnaryExpression(
             coercions.expect(roles.ByOfRole, column),
-            modifier=operators.nullslast_op,
+            modifier=operators.nulls_last_op,
             wraps_column_expression=False,
         )
 
@@ -3243,9 +3251,9 @@ class UnaryExpression(ColumnElement):
 
             :func:`.asc`
 
-            :func:`.nullsfirst`
+            :func:`.nulls_first`
 
-            :func:`.nullslast`
+            :func:`.nulls_last`
 
             :meth:`_expression.Select.order_by`
 
@@ -3285,9 +3293,9 @@ class UnaryExpression(ColumnElement):
 
             :func:`.desc`
 
-            :func:`.nullsfirst`
+            :func:`.nulls_first`
 
-            :func:`.nullslast`
+            :func:`.nulls_last`
 
             :meth:`_expression.Select.order_by`
 
index 31584f072935a2ef2c6385e20a4a54cb770faaa2..a3a4ec35164493ae207b7323ee21ce1fb1353b66 100644 (file)
@@ -67,8 +67,8 @@ __all__ = [
     "literal_column",
     "not_",
     "null",
-    "nullsfirst",
-    "nullslast",
+    "nulls_first",
+    "nulls_last",
     "or_",
     "outparam",
     "outerjoin",
@@ -220,12 +220,14 @@ union_all = public_factory(
     CompoundSelect._create_union_all, ".sql.expression.union_all"
 )
 exists = public_factory(Exists, ".sql.expression.exists")
-nullsfirst = public_factory(
-    UnaryExpression._create_nullsfirst, ".sql.expression.nullsfirst"
+nulls_first = public_factory(
+    UnaryExpression._create_nulls_first, ".sql.expression.nulls_first"
 )
-nullslast = public_factory(
-    UnaryExpression._create_nullslast, ".sql.expression.nullslast"
+nullsfirst = nulls_first  # deprecated 1.4; see #5435
+nulls_last = public_factory(
+    UnaryExpression._create_nulls_last, ".sql.expression.nulls_last"
 )
+nullslast = nulls_last  # deprecated 1.4; see #5435
 asc = public_factory(UnaryExpression._create_asc, ".sql.expression.asc")
 desc = public_factory(UnaryExpression._create_desc, ".sql.expression.desc")
 distinct = public_factory(
index 5f5052c2862b65c4eccf460cc65421ec7f79c880..29a2f191e85c878694ab3e37c88a92c82a3504ec 100644 (file)
@@ -379,16 +379,23 @@ class ColumnOperators(Operators):
         """
         return self.operate(is_distinct_from, other)
 
-    def isnot_distinct_from(self, other):
+    def is_not_distinct_from(self, other):
         """Implement the ``IS NOT DISTINCT FROM`` operator.
 
         Renders "a IS NOT DISTINCT FROM b" on most platforms;
         on some such as SQLite may render "a IS b".
 
+        .. versionchanged:: 1.4 The ``is_not_distinct_from()`` operator is
+           renamed from ``isnot_distinct_from()`` in previous releases.
+           The previous name remains available for backwards compatibility.
+
         .. versionadded:: 1.1
 
         """
-        return self.operate(isnot_distinct_from, other)
+        return self.operate(is_not_distinct_from, other)
+
+    # deprecated 1.4; see #5435
+    isnot_distinct_from = is_not_distinct_from
 
     def __gt__(self, other):
         """Implement the ``>`` operator.
@@ -627,12 +634,16 @@ class ColumnOperators(Operators):
     # deprecated 1.4; see #5429
     notin_ = not_in
 
-    def notlike(self, other, escape=None):
+    def not_like(self, other, escape=None):
         """implement the ``NOT LIKE`` operator.
 
         This is equivalent to using negation with
         :meth:`.ColumnOperators.like`, i.e. ``~x.like(y)``.
 
+        .. versionchanged:: 1.4 The ``not_like()`` operator is renamed from
+           ``notlike()`` in previous releases.  The previous name remains
+           available for backwards compatibility.
+
         .. seealso::
 
             :meth:`.ColumnOperators.like`
@@ -640,12 +651,19 @@ class ColumnOperators(Operators):
         """
         return self.operate(notlike_op, other, escape=escape)
 
-    def notilike(self, other, escape=None):
+    # deprecated 1.4; see #5435
+    notlike = not_like
+
+    def not_ilike(self, other, escape=None):
         """implement the ``NOT ILIKE`` operator.
 
         This is equivalent to using negation with
         :meth:`.ColumnOperators.ilike`, i.e. ``~x.ilike(y)``.
 
+        .. versionchanged:: 1.4 The ``not_ilike()`` operator is renamed from
+           ``notilike()`` in previous releases.  The previous name remains
+           available for backwards compatibility.
+
         .. seealso::
 
             :meth:`.ColumnOperators.ilike`
@@ -653,6 +671,9 @@ class ColumnOperators(Operators):
         """
         return self.operate(notilike_op, other, escape=escape)
 
+    # deprecated 1.4; see #5435
+    notilike = not_ilike
+
     def is_(self, other):
         """Implement the ``IS`` operator.
 
@@ -678,7 +699,6 @@ class ColumnOperators(Operators):
            ``isnot()`` in previous releases.  The previous name remains
            available for backwards compatibility.
 
-
         .. seealso:: :meth:`.ColumnOperators.is_`
 
         """
@@ -1043,15 +1063,31 @@ class ColumnOperators(Operators):
         parent object."""
         return self.operate(asc_op)
 
-    def nullsfirst(self):
-        """Produce a :func:`_expression.nullsfirst` clause against the
-        parent object."""
-        return self.operate(nullsfirst_op)
+    def nulls_first(self):
+        """Produce a :func:`_expression.nulls_first` clause against the
+        parent object.
 
-    def nullslast(self):
-        """Produce a :func:`_expression.nullslast` clause against the
-        parent object."""
-        return self.operate(nullslast_op)
+        .. versionchanged:: 1.4 The ``nulls_first()`` operator is
+           renamed from ``nullsfirst()`` in previous releases.
+           The previous name remains available for backwards compatibility.
+        """
+        return self.operate(nulls_first_op)
+
+    # deprecated 1.4; see #5435
+    nullsfirst = nulls_first
+
+    def nulls_last(self):
+        """Produce a :func:`_expression.nulls_last` clause against the
+        parent object.
+
+        .. versionchanged:: 1.4 The ``nulls_last()`` operator is
+           renamed from ``nullslast()`` in previous releases.
+           The previous name remains available for backwards compatibility.
+        """
+        return self.operate(nulls_last_op)
+
+    # deprecated 1.4; see #5429
+    nullslast = nulls_last
 
     def collate(self, collation):
         """Produce a :func:`_expression.collate` clause against
@@ -1260,22 +1296,34 @@ def exists():
     raise NotImplementedError()
 
 
-def istrue(a):
+def is_true(a):
     raise NotImplementedError()
 
 
-def isfalse(a):
+# 1.4 deprecated; see #5435
+istrue = is_true
+
+
+def is_false(a):
     raise NotImplementedError()
 
 
+# 1.4 deprecated; see #5435
+isfalse = is_false
+
+
 @comparison_op
 def is_distinct_from(a, b):
     return a.is_distinct_from(b)
 
 
 @comparison_op
-def isnot_distinct_from(a, b):
-    return a.isnot_distinct_from(b)
+def is_not_distinct_from(a, b):
+    return a.is_not_distinct_from(b)
+
+
+# deprecated 1.4; see #5435
+isnot_distinct_from = is_not_distinct_from
 
 
 @comparison_op
@@ -1306,18 +1354,26 @@ def like_op(a, b, escape=None):
 
 
 @comparison_op
-def notlike_op(a, b, escape=None):
+def not_like_op(a, b, escape=None):
     return a.notlike(b, escape=escape)
 
 
+# 1.4 deprecated; see #5435
+notlike_op = not_like_op
+
+
 @comparison_op
 def ilike_op(a, b, escape=None):
     return a.ilike(b, escape=escape)
 
 
 @comparison_op
-def notilike_op(a, b, escape=None):
-    return a.notilike(b, escape=escape)
+def not_ilike_op(a, b, escape=None):
+    return a.not_ilike(b, escape=escape)
+
+
+# 1.4 deprecated; see #5435
+notilike_op = not_ilike_op
 
 
 @comparison_op
@@ -1326,8 +1382,12 @@ def between_op(a, b, c, symmetric=False):
 
 
 @comparison_op
-def notbetween_op(a, b, c, symmetric=False):
-    return a.notbetween(b, c, symmetric=symmetric)
+def not_between_op(a, b, c, symmetric=False):
+    return ~a.between(b, c, symmetric=symmetric)
+
+
+# 1.4 deprecated; see #5435
+notbetween_op = not_between_op
 
 
 @comparison_op
@@ -1382,30 +1442,42 @@ def startswith_op(a, b, escape=None, autoescape=False):
 
 
 @comparison_op
-def notstartswith_op(a, b, escape=None, autoescape=False):
+def not_startswith_op(a, b, escape=None, autoescape=False):
     return ~_escaped_like_impl(a.startswith, b, escape, autoescape)
 
 
+# 1.4 deprecated; see #5435
+notstartswith_op = not_startswith_op
+
+
 @comparison_op
 def endswith_op(a, b, escape=None, autoescape=False):
     return _escaped_like_impl(a.endswith, b, escape, autoescape)
 
 
 @comparison_op
-def notendswith_op(a, b, escape=None, autoescape=False):
+def not_endswith_op(a, b, escape=None, autoescape=False):
     return ~_escaped_like_impl(a.endswith, b, escape, autoescape)
 
 
+# 1.4 deprecated; see #5435
+notendswith_op = not_endswith_op
+
+
 @comparison_op
 def contains_op(a, b, escape=None, autoescape=False):
     return _escaped_like_impl(a.contains, b, escape, autoescape)
 
 
 @comparison_op
-def notcontains_op(a, b, escape=None, autoescape=False):
+def not_contains_op(a, b, escape=None, autoescape=False):
     return ~_escaped_like_impl(a.contains, b, escape, autoescape)
 
 
+# 1.4 deprecated; see #5435
+notcontains_op = not_contains_op
+
+
 @comparison_op
 def match_op(a, b, **kw):
     return a.match(b, **kw)
@@ -1426,8 +1498,12 @@ def regexp_replace_op(a, b, replacement, flags=None):
 
 
 @comparison_op
-def notmatch_op(a, b, **kw):
-    return a.notmatch(b, **kw)
+def not_match_op(a, b, **kw):
+    return ~a.match(b, **kw)
+
+
+# 1.4 deprecated; see #5429
+notmatch_op = not_match_op
 
 
 def comma_op(a, b):
@@ -1450,12 +1526,20 @@ def asc_op(a):
     return a.asc()
 
 
-def nullsfirst_op(a):
-    return a.nullsfirst()
+def nulls_first_op(a):
+    return a.nulls_first()
+
+
+# 1.4 deprecated; see #5435
+nullsfirst_op = nulls_first_op
+
+
+def nulls_last_op(a):
+    return a.nulls_last()
 
 
-def nullslast_op(a):
-    return a.nullslast()
+# 1.4 deprecated; see #5435
+nullslast_op = nulls_last_op
 
 
 def json_getitem_op(a, b):
@@ -1475,7 +1559,7 @@ def is_commutative(op):
 
 
 def is_ordering_modifier(op):
-    return op in (asc_op, desc_op, nullsfirst_op, nullslast_op)
+    return op in (asc_op, desc_op, nulls_first_op, nulls_last_op)
 
 
 def is_natural_self_precedent(op):
@@ -1486,7 +1570,7 @@ def is_natural_self_precedent(op):
     )
 
 
-_booleans = (inv, istrue, isfalse, and_, or_)
+_booleans = (inv, is_true, is_false, and_, or_)
 
 
 def is_boolean(op):
@@ -1543,14 +1627,14 @@ _PRECEDENCE = {
     concat_op: 6,
     filter_op: 6,
     match_op: 5,
-    notmatch_op: 5,
+    not_match_op: 5,
     regexp_match_op: 5,
     not_regexp_match_op: 5,
     regexp_replace_op: 5,
     ilike_op: 5,
-    notilike_op: 5,
+    not_ilike_op: 5,
     like_op: 5,
-    notlike_op: 5,
+    not_like_op: 5,
     in_op: 5,
     not_in_op: 5,
     is_: 5,
@@ -1558,17 +1642,17 @@ _PRECEDENCE = {
     eq: 5,
     ne: 5,
     is_distinct_from: 5,
-    isnot_distinct_from: 5,
+    is_not_distinct_from: 5,
     gt: 5,
     lt: 5,
     ge: 5,
     le: 5,
     between_op: 5,
-    notbetween_op: 5,
+    not_between_op: 5,
     distinct_op: 5,
     inv: 5,
-    istrue: 5,
-    isfalse: 5,
+    is_true: 5,
+    is_false: 5,
     and_: 3,
     or_: 2,
     comma_op: -1,
index ee9db9111584aaf77fa611fc57c04b3061ddba5f..d15fae3c4773a42a0cfc660fe81dbbddb1fb5aa1 100644 (file)
@@ -1349,7 +1349,7 @@ class IsOrIsNotDistinctFromTest(fixtures.TablesTest):
         id_="iaaa",
         argnames="col_a_value, col_b_value, expected_row_count_for_is",
     )
-    def test_is_or_isnot_distinct_from(
+    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
@@ -1371,7 +1371,7 @@ class IsOrIsNotDistinctFromTest(fixtures.TablesTest):
             1 if expected_row_count_for_is == 0 else 0
         )
         result = connection.execute(
-            tbl.select(tbl.c.col_a.isnot_distinct_from(tbl.c.col_b))
+            tbl.select(tbl.c.col_a.is_not_distinct_from(tbl.c.col_b))
         ).fetchall()
         eq_(
             len(result),
index d85bdff77fba19c7a10cd4a66555b44155da9595..2e79d071c43b5d4bc0d2075becb9ab4b0994bdaa 100644 (file)
@@ -1063,12 +1063,12 @@ class ReflectionTest(AssertsCompiledSQL, fixtures.TestBase):
         )
 
         eq_(
-            compile_exprs([t2.c.name.desc(), t2.c.aname.desc().nullslast()]),
+            compile_exprs([t2.c.name.desc(), t2.c.aname.desc().nulls_last()]),
             compile_exprs(r2.expressions),
         )
 
         eq_(
-            compile_exprs([t2.c.name.nullsfirst(), t2.c.aname]),
+            compile_exprs([t2.c.name.nulls_first(), t2.c.aname]),
             compile_exprs(r3.expressions),
         )
 
index 4a8f6fd788c7bf8186858f9a0cb4a18a424e5449..4f581433c5d9d35fad6bfea27047f016cf102eaf 100644 (file)
@@ -993,7 +993,7 @@ class SQLTest(fixtures.TestBase, AssertsCompiledSQL):
         )
 
         self.assert_compile(
-            sql.column("x").isnot_distinct_from(False), "x IS 0"
+            sql.column("x").is_not_distinct_from(False), "x IS 0"
         )
 
     def test_localtime(self):
index c9e1d9ab419c30a9f7cce10de66f641731274868..d3f8b6a9fb08d8a3feae684cb6ce545087efc390 100644 (file)
@@ -1697,7 +1697,7 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
         self.assert_compile(
             table2.select().order_by(
                 table2.c.otherid,
-                table2.c.othername.desc().nullsfirst(),
+                table2.c.othername.desc().nulls_first(),
             ),
             "SELECT myothertable.otherid, myothertable.othername FROM "
             "myothertable ORDER BY myothertable.otherid, "
@@ -1707,7 +1707,7 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
         self.assert_compile(
             table2.select().order_by(
                 table2.c.otherid,
-                table2.c.othername.desc().nullslast(),
+                table2.c.othername.desc().nulls_last(),
             ),
             "SELECT myothertable.otherid, myothertable.othername FROM "
             "myothertable ORDER BY myothertable.otherid, "
@@ -1716,8 +1716,8 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
 
         self.assert_compile(
             table2.select().order_by(
-                table2.c.otherid.nullslast(),
-                table2.c.othername.desc().nullsfirst(),
+                table2.c.otherid.nulls_last(),
+                table2.c.othername.desc().nulls_first(),
             ),
             "SELECT myothertable.otherid, myothertable.othername FROM "
             "myothertable ORDER BY myothertable.otherid NULLS LAST, "
@@ -1726,7 +1726,7 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
 
         self.assert_compile(
             table2.select().order_by(
-                table2.c.otherid.nullsfirst(),
+                table2.c.otherid.nulls_first(),
                 table2.c.othername.desc(),
             ),
             "SELECT myothertable.otherid, myothertable.othername FROM "
@@ -1736,8 +1736,8 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
 
         self.assert_compile(
             table2.select().order_by(
-                table2.c.otherid.nullsfirst(),
-                table2.c.othername.desc().nullslast(),
+                table2.c.otherid.nulls_first(),
+                table2.c.othername.desc().nulls_last(),
             ),
             "SELECT myothertable.otherid, myothertable.othername FROM "
             "myothertable ORDER BY myothertable.otherid NULLS FIRST, "
index d566a1f6b7385df97e3554154722ecaf1468dae7..5f86b0b89f295b4c1e8f6269fc2ea29a720f5fab 100644 (file)
@@ -47,6 +47,7 @@ from sqlalchemy.testing import engines
 from sqlalchemy.testing import eq_
 from sqlalchemy.testing import fixtures
 from sqlalchemy.testing import in_
+from sqlalchemy.testing import is_
 from sqlalchemy.testing import is_true
 from sqlalchemy.testing import mock
 from sqlalchemy.testing import not_in
@@ -2109,6 +2110,24 @@ class TableDeprecationTest(fixtures.TestBase):
 
 
 class LegacyOperatorTest(AssertsCompiledSQL, fixtures.TestBase):
+    """
+    Several operators were renamed for SqlAlchemy 2.0 in #5429 and #5435
+
+    This test class is designed to ensure the deprecated legacy operators
+    are still available and equivalent to their modern replacements.
+
+    These tests should be removed when the legacy operators are removed.
+
+    Note: Although several of these tests simply check to see if two functions
+    are the same, some platforms in the test matrix require an `==` comparison
+    and will fail on an `is` comparison.
+
+    .. seealso::
+
+        :ref:`change_5429`
+        :ref:`change_5435`
+    """
+
     __dialect__ = "default"
 
     def test_issue_5429_compile(self):
@@ -2123,27 +2142,19 @@ class LegacyOperatorTest(AssertsCompiledSQL, fixtures.TestBase):
         # is_not
         assert hasattr(operators, "is_not")  # modern
         assert hasattr(operators, "isnot")  # legacy
-        assert operators.is_not is operators.isnot
+        is_(operators.is_not, operators.isnot)
         # not_in
         assert hasattr(operators, "not_in_op")  # modern
         assert hasattr(operators, "notin_op")  # legacy
-        assert operators.not_in_op is operators.notin_op
+        is_(operators.not_in_op, operators.notin_op)
 
         # precedence mapping
+        # since they are the same item, only 1 precedence check needed
         # is_not
-        assert operators.is_not in operators._PRECEDENCE  # modern
         assert operators.isnot in operators._PRECEDENCE  # legacy
-        assert (
-            operators._PRECEDENCE[operators.is_not]
-            == operators._PRECEDENCE[operators.isnot]
-        )
+
         # not_in_op
-        assert operators.not_in_op in operators._PRECEDENCE  # modern
         assert operators.notin_op in operators._PRECEDENCE  # legacy
-        assert (
-            operators._PRECEDENCE[operators.not_in_op]
-            == operators._PRECEDENCE[operators.notin_op]
-        )
 
         # ColumnOperators
         # is_not
@@ -2168,8 +2179,101 @@ class LegacyOperatorTest(AssertsCompiledSQL, fixtures.TestBase):
         # is_not
         assert hasattr(assertions, "is_not")  # modern
         assert hasattr(assertions, "is_not_")  # legacy
-        assert assertions.is_not is assertions.is_not_
+        assert assertions.is_not == assertions.is_not_
         # not_in
         assert hasattr(assertions, "not_in")  # modern
         assert hasattr(assertions, "not_in_")  # legacy
-        assert assertions.not_in is assertions.not_in_
+        assert assertions.not_in == assertions.not_in_
+
+    @testing.combinations(
+        (
+            "is_not_distinct_from",
+            "isnot_distinct_from",
+            "a IS NOT DISTINCT FROM b",
+        ),
+        ("not_contains_op", "notcontains_op", "a NOT LIKE '%' || b || '%'"),
+        ("not_endswith_op", "notendswith_op", "a NOT LIKE '%' || b"),
+        ("not_ilike_op", "notilike_op", "lower(a) NOT LIKE lower(b)"),
+        ("not_like_op", "notlike_op", "a NOT LIKE b"),
+        ("not_match_op", "notmatch_op", "NOT a MATCH b"),
+        ("not_startswith_op", "notstartswith_op", "a NOT LIKE b || '%'"),
+    )
+    def test_issue_5435_binary_operators(self, modern, legacy, txt):
+        a, b = column("a"), column("b")
+        _op_modern = getattr(operators, modern)
+        _op_legacy = getattr(operators, legacy)
+
+        eq_(str(_op_modern(a, b)), txt)
+
+        eq_(str(_op_modern(a, b)), str(_op_legacy(a, b)))
+
+    @testing.combinations(
+        ("nulls_first_op", "nullsfirst_op", "a NULLS FIRST"),
+        ("nulls_last_op", "nullslast_op", "a NULLS LAST"),
+    )
+    def test_issue_5435_unary_operators(self, modern, legacy, txt):
+        a = column("a")
+        _op_modern = getattr(operators, modern)
+        _op_legacy = getattr(operators, legacy)
+
+        eq_(str(_op_modern(a)), txt)
+
+        eq_(str(_op_modern(a)), str(_op_legacy(a)))
+
+    @testing.combinations(
+        ("not_between_op", "notbetween_op", "a NOT BETWEEN b AND c")
+    )
+    def test_issue_5435_between_operators(self, modern, legacy, txt):
+        a, b, c = column("a"), column("b"), column("c")
+        _op_modern = getattr(operators, modern)
+        _op_legacy = getattr(operators, legacy)
+
+        eq_(str(_op_modern(a, b, c)), txt)
+
+        eq_(str(_op_modern(a, b, c)), str(_op_legacy(a, b, c)))
+
+    @testing.combinations(
+        ("is_false", "isfalse", True),
+        ("is_true", "istrue", True),
+        ("is_not_distinct_from", "isnot_distinct_from", True),
+        ("not_between_op", "notbetween_op", True),
+        ("not_contains_op", "notcontains_op", False),
+        ("not_endswith_op", "notendswith_op", False),
+        ("not_ilike_op", "notilike_op", True),
+        ("not_like_op", "notlike_op", True),
+        ("not_match_op", "notmatch_op", True),
+        ("not_startswith_op", "notstartswith_op", False),
+        ("nulls_first_op", "nullsfirst_op", False),
+        ("nulls_last_op", "nullslast_op", False),
+    )
+    def test_issue_5435_operators_precedence(
+        self, _modern, _legacy, _in_precedence
+    ):
+        # (modern, legacy, in_precendence)
+        # core operators
+        assert hasattr(operators, _modern)
+        assert hasattr(operators, _legacy)
+        _op_modern = getattr(operators, _modern)
+        _op_legacy = getattr(operators, _legacy)
+        assert _op_modern == _op_legacy
+        # since they are the same item, only 1 precedence check needed
+        if _in_precedence:
+            assert _op_legacy in operators._PRECEDENCE
+        else:
+            assert _op_legacy not in operators._PRECEDENCE
+
+    @testing.combinations(
+        ("is_not_distinct_from", "isnot_distinct_from"),
+        ("not_ilike", "notilike"),
+        ("not_like", "notlike"),
+        ("nulls_first", "nullsfirst"),
+        ("nulls_last", "nullslast"),
+    )
+    def test_issue_5435_operators_column(self, _modern, _legacy):
+        # (modern, legacy)
+        # Column operators
+        assert hasattr(operators.ColumnOperators, _modern)
+        assert hasattr(operators.ColumnOperators, _legacy)
+        _op_modern = getattr(operators.ColumnOperators, _modern)
+        _op_legacy = getattr(operators.ColumnOperators, _legacy)
+        assert _op_modern == _op_legacy
index 2f92738594248424efdd792a2e15a67836d39a1f..aaeed68ddb81fcee97c01831123e6a43b6daf401 100644 (file)
@@ -97,7 +97,8 @@ class DefaultColumnComparatorTest(fixtures.TestBase):
         (operators.is_distinct_from, True),
         (operators.is_distinct_from, False),
         (operators.is_distinct_from, None),
-        (operators.isnot_distinct_from, True),
+        (operators.is_not_distinct_from, True),
+        (operators.isnot_distinct_from, True),  # deprecated 1.4; See #5429
         (operators.is_, True),
         (operators.is_not, True),
         (operators.isnot, True),  # deprecated 1.4; See #5429
@@ -105,9 +106,11 @@ class DefaultColumnComparatorTest(fixtures.TestBase):
         (operators.is_not, False),
         (operators.isnot, False),  # deprecated 1.4; See #5429
         (operators.like_op, right_column),
-        (operators.notlike_op, right_column),
+        (operators.not_like_op, right_column),
+        (operators.notlike_op, right_column),  # deprecated 1.4; See #5435
         (operators.ilike_op, right_column),
-        (operators.notilike_op, right_column),
+        (operators.not_ilike_op, right_column),
+        (operators.notilike_op, right_column),  # deprecated 1.4; See #5435
         (operators.is_, right_column),
         (operators.is_not, right_column),
         (operators.isnot, right_column),  # deprecated 1.4; See #5429
@@ -1422,7 +1425,7 @@ class OperatorPrecedenceTest(fixtures.TestBase, testing.AssertsCompiledSQL):
     def test_operator_precedence_collate_6(self):
         self.assert_compile(
             select(self.table1.c.name).order_by(
-                self.table1.c.name.collate("utf-8").desc().nullslast()
+                self.table1.c.name.collate("utf-8").desc().nulls_last()
             ),
             "SELECT mytable.name FROM mytable "
             'ORDER BY mytable.name COLLATE "utf-8" DESC NULLS LAST',
@@ -1616,35 +1619,35 @@ class IsDistinctFromTest(fixtures.TestBase, testing.AssertsCompiledSQL):
             dialect=postgresql.dialect(),
         )
 
-    def test_isnot_distinct_from(self):
+    def test_is_not_distinct_from(self):
         self.assert_compile(
-            self.table1.c.myid.isnot_distinct_from(1),
+            self.table1.c.myid.is_not_distinct_from(1),
             "mytable.myid IS NOT DISTINCT FROM :myid_1",
         )
 
-    def test_isnot_distinct_from_sqlite(self):
+    def test_is_not_distinct_from_sqlite(self):
         self.assert_compile(
-            self.table1.c.myid.isnot_distinct_from(1),
+            self.table1.c.myid.is_not_distinct_from(1),
             "mytable.myid IS ?",
             dialect=sqlite.dialect(),
         )
 
-    def test_isnot_distinct_from_postgresql(self):
+    def test_is_not_distinct_from_postgresql(self):
         self.assert_compile(
-            self.table1.c.myid.isnot_distinct_from(1),
+            self.table1.c.myid.is_not_distinct_from(1),
             "mytable.myid IS NOT DISTINCT FROM %(myid_1)s",
             dialect=postgresql.dialect(),
         )
 
-    def test_not_isnot_distinct_from(self):
+    def test_not_is_not_distinct_from(self):
         self.assert_compile(
-            ~self.table1.c.myid.isnot_distinct_from(1),
+            ~self.table1.c.myid.is_not_distinct_from(1),
             "mytable.myid IS DISTINCT FROM :myid_1",
         )
 
-    def test_not_isnot_distinct_from_postgresql(self):
+    def test_not_is_not_distinct_from_postgresql(self):
         self.assert_compile(
-            ~self.table1.c.myid.isnot_distinct_from(1),
+            ~self.table1.c.myid.is_not_distinct_from(1),
             "mytable.myid IS DISTINCT FROM %(myid_1)s",
             dialect=postgresql.dialect(),
         )
@@ -2656,30 +2659,30 @@ class ComposedLikeOperatorsTest(fixtures.TestBase, testing.AssertsCompiledSQL):
             checkparams={"x_1": "a%b_c"},
         )
 
-    def test_notlike(self):
+    def test_not_like(self):
         self.assert_compile(
-            column("x").notlike("y"),
+            column("x").not_like("y"),
             "x NOT LIKE :x_1",
             checkparams={"x_1": "y"},
         )
 
-    def test_notlike_escape(self):
+    def test_not_like_escape(self):
         self.assert_compile(
-            column("x").notlike("a%b_c", escape="\\"),
+            column("x").not_like("a%b_c", escape="\\"),
             "x NOT LIKE :x_1 ESCAPE '\\'",
             checkparams={"x_1": "a%b_c"},
         )
 
-    def test_notilike(self):
+    def test_not_ilike(self):
         self.assert_compile(
-            column("x").notilike("y"),
+            column("x").not_ilike("y"),
             "lower(x) NOT LIKE lower(:x_1)",
             checkparams={"x_1": "y"},
         )
 
-    def test_notilike_escape(self):
+    def test_not_ilike_escape(self):
         self.assert_compile(
-            column("x").notilike("a%b_c", escape="\\"),
+            column("x").not_ilike("a%b_c", escape="\\"),
             "lower(x) NOT LIKE lower(:x_1) ESCAPE '\\'",
             checkparams={"x_1": "a%b_c"},
         )
index 9f66a2ef594c11fb28c49a0c292979cc12b1fc2c..7d05462abb2fae04af4622023502a7c74bbd7832 100644 (file)
@@ -480,7 +480,7 @@ class QueryTest(fixtures.TestBase):
         for labels in False, True:
             a_eq(
                 users.select(
-                    order_by=[users.c.user_name.nullsfirst()],
+                    order_by=[users.c.user_name.nulls_first()],
                     use_labels=labels,
                 ),
                 [(1, None), (3, "a"), (2, "b")],
@@ -488,14 +488,15 @@ class QueryTest(fixtures.TestBase):
 
             a_eq(
                 users.select(
-                    order_by=[users.c.user_name.nullslast()], use_labels=labels
+                    order_by=[users.c.user_name.nulls_last()],
+                    use_labels=labels,
                 ),
                 [(3, "a"), (2, "b"), (1, None)],
             )
 
             a_eq(
                 users.select(
-                    order_by=[asc(users.c.user_name).nullsfirst()],
+                    order_by=[asc(users.c.user_name).nulls_first()],
                     use_labels=labels,
                 ),
                 [(1, None), (3, "a"), (2, "b")],
@@ -503,7 +504,7 @@ class QueryTest(fixtures.TestBase):
 
             a_eq(
                 users.select(
-                    order_by=[asc(users.c.user_name).nullslast()],
+                    order_by=[asc(users.c.user_name).nulls_last()],
                     use_labels=labels,
                 ),
                 [(3, "a"), (2, "b"), (1, None)],
@@ -511,7 +512,7 @@ class QueryTest(fixtures.TestBase):
 
             a_eq(
                 users.select(
-                    order_by=[users.c.user_name.desc().nullsfirst()],
+                    order_by=[users.c.user_name.desc().nulls_first()],
                     use_labels=labels,
                 ),
                 [(1, None), (2, "b"), (3, "a")],
@@ -519,7 +520,7 @@ class QueryTest(fixtures.TestBase):
 
             a_eq(
                 users.select(
-                    order_by=[users.c.user_name.desc().nullslast()],
+                    order_by=[users.c.user_name.desc().nulls_last()],
                     use_labels=labels,
                 ),
                 [(2, "b"), (3, "a"), (1, None)],
@@ -527,7 +528,7 @@ class QueryTest(fixtures.TestBase):
 
             a_eq(
                 users.select(
-                    order_by=[desc(users.c.user_name).nullsfirst()],
+                    order_by=[desc(users.c.user_name).nulls_first()],
                     use_labels=labels,
                 ),
                 [(1, None), (2, "b"), (3, "a")],
@@ -535,7 +536,7 @@ class QueryTest(fixtures.TestBase):
 
             a_eq(
                 users.select(
-                    order_by=[desc(users.c.user_name).nullslast()],
+                    order_by=[desc(users.c.user_name).nulls_last()],
                     use_labels=labels,
                 ),
                 [(2, "b"), (3, "a"), (1, None)],
@@ -543,7 +544,10 @@ class QueryTest(fixtures.TestBase):
 
             a_eq(
                 users.select(
-                    order_by=[users.c.user_name.nullsfirst(), users.c.user_id],
+                    order_by=[
+                        users.c.user_name.nulls_first(),
+                        users.c.user_id,
+                    ],
                     use_labels=labels,
                 ),
                 [(1, None), (3, "a"), (2, "b")],
@@ -551,7 +555,7 @@ class QueryTest(fixtures.TestBase):
 
             a_eq(
                 users.select(
-                    order_by=[users.c.user_name.nullslast(), users.c.user_id],
+                    order_by=[users.c.user_name.nulls_last(), users.c.user_id],
                     use_labels=labels,
                 ),
                 [(3, "a"), (2, "b"), (1, None)],