From bc8644fe0559d7c73dd2391966c2fc0120d01e5b Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Mon, 26 Apr 2021 15:12:31 -0600 Subject: [PATCH 1/8] fix: The DB API Binary function accepts bytes data --- google/cloud/bigquery/dbapi/types.py | 8 ++++---- tests/unit/test_dbapi_types.py | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/google/cloud/bigquery/dbapi/types.py b/google/cloud/bigquery/dbapi/types.py index 20eca9b00..3bc6b77f8 100644 --- a/google/cloud/bigquery/dbapi/types.py +++ b/google/cloud/bigquery/dbapi/types.py @@ -30,16 +30,16 @@ TimestampFromTicks = datetime.datetime.fromtimestamp -def Binary(string): +def Binary(data): """Contruct a DB-API binary value. Args: - string (str): A string to encode as a binary value. + data (bytes): A bytes object containing binary data. Returns: - bytes: The UTF-8 encoded bytes representing the string. + bytes: The data passed in. """ - return string.encode("utf-8") + return data if isinstance(data, bytes) else data.encode("utf-8") def TimeFromTicks(ticks, tz=None): diff --git a/tests/unit/test_dbapi_types.py b/tests/unit/test_dbapi_types.py index e05660ffe..ff8308d02 100644 --- a/tests/unit/test_dbapi_types.py +++ b/tests/unit/test_dbapi_types.py @@ -29,6 +29,7 @@ def test_binary_type(self): def test_binary_constructor(self): self.assertEqual(types.Binary(u"hello"), b"hello") self.assertEqual(types.Binary(u"\u1f60"), u"\u1f60".encode("utf-8")) + self.assertEqual(types.Binary(b"hello"), b"hello") def test_timefromticks(self): somedatetime = datetime.datetime( From 40e34b5f885490e7ce5d2d271a286c66dbd92c03 Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Mon, 26 Apr 2021 16:00:01 -0600 Subject: [PATCH 2/8] Binary should accept bytes-like objects. --- google/cloud/bigquery/dbapi/types.py | 18 +++++++++++++++--- tests/unit/test_dbapi_types.py | 13 +++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/google/cloud/bigquery/dbapi/types.py b/google/cloud/bigquery/dbapi/types.py index 3bc6b77f8..3bb7b70b9 100644 --- a/google/cloud/bigquery/dbapi/types.py +++ b/google/cloud/bigquery/dbapi/types.py @@ -34,12 +34,24 @@ def Binary(data): """Contruct a DB-API binary value. Args: - data (bytes): A bytes object containing binary data. + data (bytes-like): An object containing binary data and that + can be converted to bytes with the `bytes` builtin. Returns: - bytes: The data passed in. + bytes: The binary data as a bytes object. """ - return data if isinstance(data, bytes) else data.encode("utf-8") + try: + result = bytes(data) + if len(result) == data: + # Data is an integer. This is not the conversion we're looking for, + raise TypeError("cannot convert 'decimal.Decimal' object to binary") + else: + return result + except TypeError: + if isinstance(data, str): + return data.encode("utf-8") + else: + raise def TimeFromTicks(ticks, tz=None): diff --git a/tests/unit/test_dbapi_types.py b/tests/unit/test_dbapi_types.py index ff8308d02..6de897cb8 100644 --- a/tests/unit/test_dbapi_types.py +++ b/tests/unit/test_dbapi_types.py @@ -13,6 +13,7 @@ # limitations under the License. import datetime +import pytest import unittest import google.cloud._helpers @@ -30,6 +31,18 @@ def test_binary_constructor(self): self.assertEqual(types.Binary(u"hello"), b"hello") self.assertEqual(types.Binary(u"\u1f60"), u"\u1f60".encode("utf-8")) self.assertEqual(types.Binary(b"hello"), b"hello") + self.assertEqual(types.Binary(bytearray(b"hello")), b"hello") + self.assertEqual(types.Binary(memoryview(b"hello")), b"hello") + + class C: + def __bytes__(self): return b'Google' + + self.assertEqual(types.Binary(C()), b"Google") + + for bad in 42, 42.0, None: + with pytest.raises(TypeError): + types.Binary(bad) + def test_timefromticks(self): somedatetime = datetime.datetime( From c50686f2b7a24d72c270e9bd9c05d06312b4fa88 Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Tue, 27 Apr 2021 07:19:23 -0600 Subject: [PATCH 3/8] check for an integer before converting to bytes. Because we don't want to accidentally create a giant bytes. --- google/cloud/bigquery/dbapi/types.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/google/cloud/bigquery/dbapi/types.py b/google/cloud/bigquery/dbapi/types.py index 3bb7b70b9..96df9f849 100644 --- a/google/cloud/bigquery/dbapi/types.py +++ b/google/cloud/bigquery/dbapi/types.py @@ -40,13 +40,12 @@ def Binary(data): Returns: bytes: The binary data as a bytes object. """ + if isinstance(data, int): + # This is not the conversion we're looking for, because it + # will simply create a bytes object of the given size. + raise TypeError("cannot convert 'decimal.Decimal' object to binary") try: - result = bytes(data) - if len(result) == data: - # Data is an integer. This is not the conversion we're looking for, - raise TypeError("cannot convert 'decimal.Decimal' object to binary") - else: - return result + return bytes(data) except TypeError: if isinstance(data, str): return data.encode("utf-8") From 8d18d943291e4aa127316b81ee6f7174cc50e6f3 Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Tue, 27 Apr 2021 07:20:32 -0600 Subject: [PATCH 4/8] blackened. --- tests/unit/test_dbapi_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_dbapi_types.py b/tests/unit/test_dbapi_types.py index 6de897cb8..50ace5183 100644 --- a/tests/unit/test_dbapi_types.py +++ b/tests/unit/test_dbapi_types.py @@ -35,7 +35,8 @@ def test_binary_constructor(self): self.assertEqual(types.Binary(memoryview(b"hello")), b"hello") class C: - def __bytes__(self): return b'Google' + def __bytes__(self): + return b"Google" self.assertEqual(types.Binary(C()), b"Google") @@ -43,7 +44,6 @@ def __bytes__(self): return b'Google' with pytest.raises(TypeError): types.Binary(bad) - def test_timefromticks(self): somedatetime = datetime.datetime( 2017, 2, 18, 12, 47, 26, tzinfo=google.cloud._helpers.UTC From 433712fdb094a9f8c299033dd8071dbe62943b56 Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Tue, 27 Apr 2021 09:04:41 -0600 Subject: [PATCH 5/8] Fixed exception string. --- google/cloud/bigquery/dbapi/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/google/cloud/bigquery/dbapi/types.py b/google/cloud/bigquery/dbapi/types.py index 96df9f849..717593ae1 100644 --- a/google/cloud/bigquery/dbapi/types.py +++ b/google/cloud/bigquery/dbapi/types.py @@ -43,7 +43,8 @@ def Binary(data): if isinstance(data, int): # This is not the conversion we're looking for, because it # will simply create a bytes object of the given size. - raise TypeError("cannot convert 'decimal.Decimal' object to binary") + raise TypeError("cannot convert `int` object to binary") + try: return bytes(data) except TypeError: From 082a7d02247b7087bae0bbf5a365989286dcd4fc Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Tue, 27 Apr 2021 09:29:17 -0600 Subject: [PATCH 6/8] parameterized binary tests and rearranged imports. --- tests/unit/test_dbapi_types.py | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/tests/unit/test_dbapi_types.py b/tests/unit/test_dbapi_types.py index 50ace5183..cb13e5e63 100644 --- a/tests/unit/test_dbapi_types.py +++ b/tests/unit/test_dbapi_types.py @@ -13,9 +13,10 @@ # limitations under the License. import datetime -import pytest import unittest +import pytest + import google.cloud._helpers from google.cloud.bigquery.dbapi import types @@ -27,23 +28,6 @@ def test_binary_type(self): self.assertEqual("STRUCT", types.BINARY) self.assertNotEqual("STRING", types.BINARY) - def test_binary_constructor(self): - self.assertEqual(types.Binary(u"hello"), b"hello") - self.assertEqual(types.Binary(u"\u1f60"), u"\u1f60".encode("utf-8")) - self.assertEqual(types.Binary(b"hello"), b"hello") - self.assertEqual(types.Binary(bytearray(b"hello")), b"hello") - self.assertEqual(types.Binary(memoryview(b"hello")), b"hello") - - class C: - def __bytes__(self): - return b"Google" - - self.assertEqual(types.Binary(C()), b"Google") - - for bad in 42, 42.0, None: - with pytest.raises(TypeError): - types.Binary(bad) - def test_timefromticks(self): somedatetime = datetime.datetime( 2017, 2, 18, 12, 47, 26, tzinfo=google.cloud._helpers.UTC @@ -54,3 +38,27 @@ def test_timefromticks(self): types.TimeFromTicks(ticks, google.cloud._helpers.UTC), datetime.time(12, 47, 26, tzinfo=google.cloud._helpers.UTC), ) + + +class CustomBinary: + def __bytes__(self): + return b"Google" + + +@pytest.mark.parametrize( + "raw,expected", + [(u"hello", b"hello"), + (u"\u1f60", u"\u1f60".encode("utf-8")), + (b"hello", b"hello"), + (bytearray(b"hello"), b"hello"), + (memoryview(b"hello"), b"hello"), + (CustomBinary(), b"Google"), + ]) +def test_binary_constructor(raw, expected): + assert types.Binary(raw) == expected + + +@pytest.mark.parametrize("bad", (42, 42,0, None)) +def test_invalid_binary_constructor(bad): + with pytest.raises(TypeError): + types.Binary(bad) From 64a02eb602bccefa22a46a609a62e51dea73a8e3 Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Tue, 27 Apr 2021 09:32:32 -0600 Subject: [PATCH 7/8] typo --- tests/unit/test_dbapi_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_dbapi_types.py b/tests/unit/test_dbapi_types.py index cb13e5e63..dbc2179c8 100644 --- a/tests/unit/test_dbapi_types.py +++ b/tests/unit/test_dbapi_types.py @@ -58,7 +58,7 @@ def test_binary_constructor(raw, expected): assert types.Binary(raw) == expected -@pytest.mark.parametrize("bad", (42, 42,0, None)) +@pytest.mark.parametrize("bad", (42, 42.0, None)) def test_invalid_binary_constructor(bad): with pytest.raises(TypeError): types.Binary(bad) From 209fa5ff663321aec3edf142de57c2c008e9398f Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Tue, 27 Apr 2021 11:40:10 -0600 Subject: [PATCH 8/8] Blackened --- tests/unit/test_dbapi_types.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/unit/test_dbapi_types.py b/tests/unit/test_dbapi_types.py index dbc2179c8..cf282c68b 100644 --- a/tests/unit/test_dbapi_types.py +++ b/tests/unit/test_dbapi_types.py @@ -47,13 +47,15 @@ def __bytes__(self): @pytest.mark.parametrize( "raw,expected", - [(u"hello", b"hello"), - (u"\u1f60", u"\u1f60".encode("utf-8")), - (b"hello", b"hello"), - (bytearray(b"hello"), b"hello"), - (memoryview(b"hello"), b"hello"), - (CustomBinary(), b"Google"), - ]) + [ + (u"hello", b"hello"), + (u"\u1f60", u"\u1f60".encode("utf-8")), + (b"hello", b"hello"), + (bytearray(b"hello"), b"hello"), + (memoryview(b"hello"), b"hello"), + (CustomBinary(), b"Google"), + ], +) def test_binary_constructor(raw, expected): assert types.Binary(raw) == expected