Repository: ibis-project/ibis

Issue:
DEPR: Deprecate verify
We've got a method `verify` that simply compiles a expression, and returns if the compilation raised an exception. Doesn't seem very useful (users can compile themselves, in the very rare cases this is really needed). The public API is already huge, so removing and simplifying things seems a good idea.

This is what will be deprecated:
```python
ibis.sqlite.verify(expr)
```


Discussion:

Resolving patch:
diff --git a/ibis/backends/base/__init__.py b/ibis/backends/base/__init__.py
--- a/ibis/backends/base/__init__.py
+++ b/ibis/backends/base/__init__.py
@@ -1,4 +1,5 @@
 import abc
+import warnings
 
 from ibis.common.exceptions import TranslationError
 
@@ -64,6 +65,11 @@ def verify(self, expr, params=None):
         """
         Verify `expr` is an expression that can be compiled.
         """
+        warnings.warn(
+            '`verify` is deprecated, use `compile` and capture the '
+            '`TranslationError` exception instead',
+            FutureWarning,
+        )
         try:
             self.compile(expr, params=params)
             return True
diff --git a/ibis/expr/types.py b/ibis/expr/types.py
--- a/ibis/expr/types.py
+++ b/ibis/expr/types.py
@@ -1,5 +1,6 @@
 import itertools
 import os
+import warnings
 import webbrowser
 from typing import TYPE_CHECKING, Optional
 
@@ -295,6 +296,11 @@ def verify(self):
         """
         Returns True if expression can be compiled to its attached client
         """
+        warnings.warn(
+            '`verify` is deprecated, use `compile` and capture the '
+            '`TranslationError` exception instead',
+            FutureWarning,
+        )
         try:
             self.compile()
         except Exception:


Test patch:
diff --git a/ibis/backends/clickhouse/tests/test_client.py b/ibis/backends/clickhouse/tests/test_client.py
--- a/ibis/backends/clickhouse/tests/test_client.py
+++ b/ibis/backends/clickhouse/tests/test_client.py
@@ -114,23 +114,6 @@ def test_sql_query_limits(alltypes):
         assert table.count().execute(limit=10) == 7300
 
 
-def test_expr_compile_verify(alltypes):
-    expr = alltypes.double_col.sum()
-
-    assert isinstance(expr.compile(), str)
-    assert expr.verify()
-
-
-def test_api_compile_verify(alltypes):
-    t = alltypes.timestamp_col
-
-    supported = t.year()
-    unsupported = t.rank()
-
-    assert ibis.clickhouse.verify(supported)
-    assert not ibis.clickhouse.verify(unsupported)
-
-
 def test_database_repr(db):
     assert db.name in repr(db)
 
diff --git a/ibis/backends/impala/tests/test_client.py b/ibis/backends/impala/tests/test_client.py
--- a/ibis/backends/impala/tests/test_client.py
+++ b/ibis/backends/impala/tests/test_client.py
@@ -239,26 +239,6 @@ def test_sql_query_limits(con, test_data_db):
         assert table.count().execute(limit=10) == 25
 
 
-def test_expr_compile_verify(db):
-    table = db.functional_alltypes
-    expr = table.double_col.sum()
-
-    assert isinstance(expr.compile(), str)
-    assert expr.verify()
-
-
-def test_api_compile_verify(db):
-    t = db.functional_alltypes
-
-    s = t.string_col
-
-    supported = s.lower()
-    unsupported = s.replace('foo', 'bar')
-
-    assert ibis.impala.verify(supported)
-    assert not ibis.impala.verify(unsupported)
-
-
 def test_database_repr(db, test_data_db):
     assert test_data_db in repr(db)
 
diff --git a/ibis/backends/postgres/tests/test_client.py b/ibis/backends/postgres/tests/test_client.py
--- a/ibis/backends/postgres/tests/test_client.py
+++ b/ibis/backends/postgres/tests/test_client.py
@@ -64,14 +64,6 @@ def test_list_tables(con):
     assert len(con.list_tables(like='functional')) == 1
 
 
-def test_compile_verify(alltypes):
-    unsupported_expr = alltypes.double_col.approx_median()
-    assert not unsupported_expr.verify()
-
-    supported_expr = alltypes.double_col.sum()
-    assert supported_expr.verify()
-
-
 def test_database_layer(con, alltypes):
     db = con.database()
     t = db.functional_alltypes
diff --git a/ibis/backends/spark/tests/test_api.py b/ibis/backends/spark/tests/test_api.py
--- a/ibis/backends/spark/tests/test_api.py
+++ b/ibis/backends/spark/tests/test_api.py
@@ -16,7 +16,3 @@ def expr():
 def test_compile(client, expr):
     expected = 'SELECT `c2`, `c1` + 1 AS `c3`, length(`c2`) AS `c4`\nFROM tbl'
     assert client.compile(expr) == ibis.spark.compile(expr) == expected
-
-
-def test_verify(expr):
-    assert ibis.spark.verify(expr)
diff --git a/ibis/backends/sqlite/tests/test_client.py b/ibis/backends/sqlite/tests/test_client.py
--- a/ibis/backends/sqlite/tests/test_client.py
+++ b/ibis/backends/sqlite/tests/test_client.py
@@ -57,14 +57,6 @@ def test_list_tables(con):
     assert len(con.list_tables(like='functional')) == 1
 
 
-def test_compile_verify(alltypes):
-    unsupported_expr = alltypes.string_col.approx_nunique()
-    assert not unsupported_expr.verify()
-
-    supported_expr = alltypes.double_col.sum()
-    assert supported_expr.verify()
-
-
 def test_attach_file(dbpath):
     client = ibis.sqlite.connect()
 
diff --git a/ibis/backends/tests/test_client.py b/ibis/backends/tests/test_client.py
--- a/ibis/backends/tests/test_client.py
+++ b/ibis/backends/tests/test_client.py
@@ -295,3 +295,29 @@ def test_insert_overwrite_from_expr(
     con.insert(temp_table, obj=from_table, overwrite=True)
     assert len(temporary.execute()) == 3
     tm.assert_frame_equal(temporary.execute(), from_table.execute())
+
+
+def test_verify(con, backend):
+    if not hasattr(con, 'compiler'):
+        pytest.skip(
+            '{} backend has no `compiler` attribute'.format(
+                type(backend).__name__
+            )
+        )
+    expr = con.table('functional_alltypes').double_col.sum()
+
+    with pytest.warns(FutureWarning):
+        assert expr.verify()
+
+    with pytest.warns(FutureWarning):
+        assert backend.api.verify(expr)
+
+    # There is no expression that can't be compiled to any backend
+    # Testing `not verify()` only for an expression not supported in postgres
+    if backend.api.name == 'postgres':
+        expr = con.table('functional_alltypes').double_col.approx_median()
+        with pytest.warns(FutureWarning):
+            assert not expr.verify()
+
+        with pytest.warns(FutureWarning):
+            assert not backend.api.verify(expr)