Skip to content

chore: fix coverage tests sqlalchemy 2.0 migration #987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 10 additions & 32 deletions sqlalchemy_bigquery/_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,14 @@
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import packaging.version
import sqlalchemy.sql.default_comparator
import sqlalchemy.sql.sqltypes
import sqlalchemy.types

from . import base

sqlalchemy_1_4_or_more = packaging.version.parse(
sqlalchemy.__version__
) >= packaging.version.parse("1.4")

if sqlalchemy_1_4_or_more:
import sqlalchemy.sql.coercions
import sqlalchemy.sql.roles
import sqlalchemy.sql.coercions
import sqlalchemy.sql.roles


def _get_subtype_col_spec(type_):
Expand Down Expand Up @@ -109,30 +103,14 @@ def __getattr__(self, name):
comparator_factory = Comparator


# In the implementations of _field_index below, we're stealing from
# the JSON type implementation, but the code to steal changed in
# 1.4. :/

if sqlalchemy_1_4_or_more:

def _field_index(self, name, operator):
return sqlalchemy.sql.coercions.expect(
sqlalchemy.sql.roles.BinaryElementRole,
name,
expr=self.expr,
operator=operator,
bindparam_type=sqlalchemy.types.String(),
)

else:

def _field_index(self, name, operator):
return sqlalchemy.sql.default_comparator._check_literal(
self.expr,
operator,
name,
bindparam_type=sqlalchemy.types.String(),
)
def _field_index(self, name, operator):
return sqlalchemy.sql.coercions.expect(
sqlalchemy.sql.roles.BinaryElementRole,
name,
expr=self.expr,
operator=operator,
bindparam_type=sqlalchemy.types.String(),
)


def struct_getitem_op(a, b):
Expand Down
32 changes: 12 additions & 20 deletions sqlalchemy_bigquery/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def get_insert_default(self, column): # pragma: NO COVER
""",
flags=re.IGNORECASE | re.VERBOSE,
)
def __distribute_types_to_expanded_placeholders(self, m):
def __distribute_types_to_expanded_placeholders(self, m): # pragma: NO COVER
# If we have an in parameter, it sometimes gets expaned to 0 or more
# parameters and we need to move the type marker to each
# parameter.
Expand All @@ -174,6 +174,8 @@ def __distribute_types_to_expanded_placeholders(self, m):
# suffixes refect that when an array parameter is expanded,
# numeric suffixes are added. For example, a placeholder like
# `%(foo)s` gets expaneded to `%(foo_0)s, `%(foo_1)s, ...`.

# Coverage: despite our best efforts, never recognized this segment of code as being tested.
placeholders, type_ = m.groups()
if placeholders:
placeholders = placeholders.replace(")", f":{type_})")
Expand Down Expand Up @@ -356,11 +358,7 @@ def group_by_clause(self, select, **kw):

__sqlalchemy_version_info = packaging.version.parse(sqlalchemy.__version__)

__expanding_text = (
"EXPANDING"
if __sqlalchemy_version_info < packaging.version.parse("1.4")
else "POSTCOMPILE"
)
__expanding_text = "POSTCOMPILE"

# https://github.com/sqlalchemy/sqlalchemy/commit/f79df12bd6d99b8f6f09d4bf07722638c4b4c159
__expanding_conflict = (
Expand Down Expand Up @@ -388,9 +386,6 @@ def visit_in_op_binary(self, binary, operator_, **kw):
self._generate_generic_binary(binary, " IN ", **kw)
)

def visit_empty_set_expr(self, element_types, **kw):
return ""

def visit_not_in_op_binary(self, binary, operator, **kw):
return (
"("
Expand Down Expand Up @@ -424,8 +419,8 @@ def visit_contains_op_binary(self, binary, operator, **kw):
self._maybe_reescape(binary), operator, **kw
)

def visit_notcontains_op_binary(self, binary, operator, **kw):
return super(BigQueryCompiler, self).visit_notcontains_op_binary(
def visit_not_contains_op_binary(self, binary, operator, **kw):
return super(BigQueryCompiler, self).visit_not_contains_op_binary(
self._maybe_reescape(binary), operator, **kw
)

Expand All @@ -434,8 +429,8 @@ def visit_startswith_op_binary(self, binary, operator, **kw):
self._maybe_reescape(binary), operator, **kw
)

def visit_notstartswith_op_binary(self, binary, operator, **kw):
return super(BigQueryCompiler, self).visit_notstartswith_op_binary(
def visit_not_startswith_op_binary(self, binary, operator, **kw):
return super(BigQueryCompiler, self).visit_not_startswith_op_binary(
self._maybe_reescape(binary), operator, **kw
)

Expand All @@ -444,8 +439,8 @@ def visit_endswith_op_binary(self, binary, operator, **kw):
self._maybe_reescape(binary), operator, **kw
)

def visit_notendswith_op_binary(self, binary, operator, **kw):
return super(BigQueryCompiler, self).visit_notendswith_op_binary(
def visit_not_endswith_op_binary(self, binary, operator, **kw):
return super(BigQueryCompiler, self).visit_not_endswith_op_binary(
self._maybe_reescape(binary), operator, **kw
)

Expand Down Expand Up @@ -510,7 +505,8 @@ def visit_bindparam(
# here, because then we can't do a recompile later (e.g., first
# print the statment, then execute it). See issue #357.
#
if getattr(bindparam, "expand_op", None) is not None:
# Coverage: despite our best efforts, never recognized this segment of code as being tested.
if getattr(bindparam, "expand_op", None) is not None: # pragma: NO COVER
assert bindparam.expand_op.__name__.endswith("in_op") # in in
bindparam = bindparam._clone(maintain_key=True)
bindparam.expanding = False
Expand Down Expand Up @@ -1278,10 +1274,6 @@ def do_rollback(self, dbapi_connection):
# BigQuery has no support for transactions.
pass

def _check_unicode_returns(self, connection, additional_tests=None):
# requests gives back Unicode strings
return True

def get_view_definition(self, connection, view_name, schema=None, **kw):
if isinstance(connection, Engine):
connection = connection.connect()
Expand Down
1 change: 0 additions & 1 deletion sqlalchemy_bigquery/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

import sqlalchemy.testing.requirements
import sqlalchemy.testing.exclusions
from sqlalchemy.testing.exclusions import against, only_on

supported = sqlalchemy.testing.exclusions.open
unsupported = sqlalchemy.testing.exclusions.closed
Expand Down
78 changes: 2 additions & 76 deletions tests/sqlalchemy_dialect_compliance/test_dialect_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,11 @@
import sqlalchemy.testing.suite.test_types
import sqlalchemy.sql.sqltypes
from sqlalchemy.testing import util, config
from sqlalchemy.testing import is_false
from sqlalchemy.testing import is_true
from sqlalchemy.testing import is_
from sqlalchemy.testing.assertions import eq_
from sqlalchemy.testing.suite import config, select, exists
from sqlalchemy.testing.suite import select, exists
from sqlalchemy.testing.suite import * # noqa
from sqlalchemy.testing.suite import Integer, Table, Column, String, bindparam, testing
from sqlalchemy.testing.suite import (
ComponentReflectionTest as _ComponentReflectionTest,
CTETest as _CTETest,
ExistsTest as _ExistsTest,
InsertBehaviorTest as _InsertBehaviorTest,
Expand All @@ -53,21 +50,18 @@
from sqlalchemy.testing.suite.test_reflection import (
BizarroCharacterFKResolutionTest,
ComponentReflectionTest,
OneConnectionTablesTest,
HasTableTest,
)

if packaging.version.parse(sqlalchemy.__version__) >= packaging.version.parse("2.0"):
import uuid
from sqlalchemy.sql import type_coerce
from sqlalchemy import Uuid
from sqlalchemy.testing.suite import (
TrueDivTest as _TrueDivTest,
IntegerTest as _IntegerTest,
NumericTest as _NumericTest,
DifficultParametersTest as _DifficultParametersTest,
FetchLimitOffsetTest as _FetchLimitOffsetTest,
PostCompileParamsTest,
StringTest as _StringTest,
UuidTest as _UuidTest,
)
Expand Down Expand Up @@ -469,74 +463,6 @@ def test_dont_truncate_rightside(
del IdentityAutoincrementTest # BQ doesn't do autoincrement
del PostCompileParamsTest # BQ adds backticks to bind parameters, causing failure of tests TODO: fix this?

elif packaging.version.parse(sqlalchemy.__version__) < packaging.version.parse("1.4"):
from sqlalchemy.testing.suite import LimitOffsetTest as _LimitOffsetTest

class LimitOffsetTest(_LimitOffsetTest):
@pytest.mark.skip("BigQuery doesn't allow an offset without a limit.")
def test_simple_offset(self):
pass

test_bound_offset = test_simple_offset

class TimestampMicrosecondsTest(_TimestampMicrosecondsTest):
data = datetime.datetime(2012, 10, 15, 12, 57, 18, 396, tzinfo=pytz.UTC)

def test_literal(self):
# The base tests doesn't set up the literal properly, because
# it doesn't pass its datatype to `literal`.

def literal(value):
assert value == self.data
return sqlalchemy.sql.elements.literal(value, self.datatype)

with mock.patch("sqlalchemy.testing.suite.test_types.literal", literal):
super(TimestampMicrosecondsTest, self).test_literal()

def test_select_direct(self, connection):
# This func added because this test was failing when passed the
# UTC timezone.

def literal(value, type_=None):
assert value == self.data

if type_ is not None:
assert type_ is self.datatype

return sqlalchemy.sql.elements.literal(value, self.datatype)

with mock.patch("sqlalchemy.testing.suite.test_types.literal", literal):
super(TimestampMicrosecondsTest, self).test_select_direct(connection)

class InsertBehaviorTest(_InsertBehaviorTest):
@pytest.mark.skip(
"BQ has no autoinc and client-side defaults can't work for select."
)
def test_insert_from_select_autoinc(cls):
pass

class SimpleUpdateDeleteTest(_SimpleUpdateDeleteTest):
"""The base tests fail if operations return rows for some reason."""

def test_update(self):
t = self.tables.plain_pk
r = config.db.execute(t.update().where(t.c.id == 2), data="d2_new")
assert not r.is_insert

eq_(
config.db.execute(t.select().order_by(t.c.id)).fetchall(),
[(1, "d1"), (2, "d2_new"), (3, "d3")],
)

def test_delete(self):
t = self.tables.plain_pk
r = config.db.execute(t.delete().where(t.c.id == 2))
assert not r.is_insert
eq_(
config.db.execute(t.select().order_by(t.c.id)).fetchall(),
[(1, "d1"), (3, "d3")],
)

else:
from sqlalchemy.testing.suite import (
FetchLimitOffsetTest as _FetchLimitOffsetTest,
Expand Down
12 changes: 0 additions & 12 deletions tests/unit/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,6 @@
from . import fauxdbi

sqlalchemy_version = packaging.version.parse(sqlalchemy.__version__)
sqlalchemy_1_3_or_higher = pytest.mark.skipif(
sqlalchemy_version < packaging.version.parse("1.3"),
reason="requires sqlalchemy 1.3 or higher",
)
sqlalchemy_1_4_or_higher = pytest.mark.skipif(
sqlalchemy_version < packaging.version.parse("1.4"),
reason="requires sqlalchemy 1.4 or higher",
)
sqlalchemy_before_1_4 = pytest.mark.skipif(
sqlalchemy_version >= packaging.version.parse("1.4"),
reason="requires sqlalchemy 1.3 or lower",
)
sqlalchemy_before_2_0 = pytest.mark.skipif(
sqlalchemy_version >= packaging.version.parse("2.0"),
reason="requires sqlalchemy 1.3 or lower",
Expand Down
6 changes: 0 additions & 6 deletions tests/unit/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@

from .conftest import setup_table
from .conftest import (
sqlalchemy_1_4_or_higher,
sqlalchemy_before_1_4,
sqlalchemy_2_0_or_higher,
sqlalchemy_before_2_0,
)
Expand Down Expand Up @@ -63,7 +61,6 @@ def test_cant_compile_unnamed_column(faux_conn, metadata):
sqlalchemy.Column(sqlalchemy.Integer).compile(faux_conn)


@sqlalchemy_1_4_or_higher
def test_no_alias_for_known_tables(faux_conn, metadata):
# See: https://github.com/googleapis/python-bigquery-sqlalchemy/issues/353
table = setup_table(
Expand All @@ -85,7 +82,6 @@ def test_no_alias_for_known_tables(faux_conn, metadata):
assert found_sql == expected_sql


@sqlalchemy_1_4_or_higher
def test_no_alias_for_known_tables_cte(faux_conn, metadata):
# See: https://github.com/googleapis/python-bigquery-sqlalchemy/issues/368
table = setup_table(
Expand Down Expand Up @@ -239,7 +235,6 @@ def test_no_implicit_join_for_inner_unnest(faux_conn, metadata):
assert found_outer_sql == expected_outer_sql


@sqlalchemy_1_4_or_higher
def test_no_implicit_join_asterix_for_inner_unnest_no_table2_column(
faux_conn, metadata
):
Expand All @@ -264,7 +259,6 @@ def test_no_implicit_join_asterix_for_inner_unnest_no_table2_column(
assert found_outer_sql == expected_outer_sql


@sqlalchemy_1_4_or_higher
def test_no_implicit_join_for_inner_unnest_no_table2_column(faux_conn, metadata):
# See: https://github.com/googleapis/python-bigquery-sqlalchemy/issues/368
q = prepare_implicit_join_base_query(faux_conn, metadata, False, False)
Expand Down
4 changes: 1 addition & 3 deletions tests/unit/test_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from sqlalchemy import Column, Integer, literal_column, select, String, Table, union
from sqlalchemy.testing.assertions import eq_, in_

from .conftest import setup_table, sqlalchemy_1_3_or_higher
from .conftest import setup_table


def assert_result(connection, sel, expected, params=()):
Expand Down Expand Up @@ -106,7 +106,6 @@ def test_percent_sign_round_trip(faux_conn, metadata):
)


@sqlalchemy_1_3_or_higher
def test_empty_set_against_integer(faux_conn):
table = some_table(faux_conn)

Expand All @@ -119,7 +118,6 @@ def test_empty_set_against_integer(faux_conn):
assert_result(faux_conn, stmt, [], params={"q": []})


@sqlalchemy_1_3_or_higher
def test_null_in_empty_set_is_false(faux_conn):
stmt = select(
sqlalchemy.case(
Expand Down
Loading