From 046768d81b23bc3a86d18afb53069e972ab7d259 Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Thu, 4 Nov 2021 12:09:29 +0200 Subject: [PATCH 1/3] fix: error if eval()-ing repr(SchemaField) --- google/cloud/bigquery/schema.py | 2 +- tests/unit/test_schema.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index 5bad52273..0375f2762 100644 --- a/google/cloud/bigquery/schema.py +++ b/google/cloud/bigquery/schema.py @@ -268,7 +268,7 @@ def _key(self): field_type = f"{field_type}({self.precision})" policy_tags = ( - () if self.policy_tags is None else tuple(sorted(self.policy_tags.names)) + None if self.policy_tags is None else tuple(sorted(self.policy_tags.names)) ) return ( diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index 2180e1f6e..77daf6bb6 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -510,9 +510,18 @@ def test___hash__not_equals(self): def test___repr__(self): field1 = self._make_one("field1", "STRING") - expected = "SchemaField('field1', 'STRING', 'NULLABLE', None, (), ())" + expected = "SchemaField('field1', 'STRING', 'NULLABLE', None, (), None)" self.assertEqual(repr(field1), expected) + def test___repr__evaluable(self): + field = self._make_one("field1", "STRING", "REQUIRED", "Description") + field_repr = repr(field) + SchemaField = self._get_target_class() # needed for eval # noqa + + evaled_field = eval(field_repr) + + assert field == evaled_field + # TODO: dedup with the same class in test_table.py. class _SchemaBase(object): From 711dc4a72b5bde14c3879367830352b5ba0b0f12 Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Thu, 4 Nov 2021 16:49:55 +0200 Subject: [PATCH 2/3] Make repr(PolicyTagList) evaluable --- google/cloud/bigquery/schema.py | 8 ++++---- tests/unit/test_schema.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index 0375f2762..61e2f19d0 100644 --- a/google/cloud/bigquery/schema.py +++ b/google/cloud/bigquery/schema.py @@ -407,7 +407,7 @@ class PolicyTagList(object): `projects/*/locations/*/taxonomies/*/policyTags/*`. """ - def __init__(self, names=()): + def __init__(self, names: Iterable[str] = ()): self._properties = {} self._properties["names"] = tuple(names) @@ -425,7 +425,7 @@ def _key(self): Returns: Tuple: The contents of this :class:`~google.cloud.bigquery.schema.PolicyTagList`. """ - return tuple(sorted(self._properties.items())) + return tuple(sorted(self._properties.get("names", ()))) def __eq__(self, other): if not isinstance(other, PolicyTagList): @@ -439,7 +439,7 @@ def __hash__(self): return hash(self._key()) def __repr__(self): - return "PolicyTagList{}".format(self._key()) + return f"{self.__class__.__name__}(names={self._key()})" @classmethod def from_api_repr(cls, api_repr: dict) -> "PolicyTagList": @@ -478,5 +478,5 @@ def to_api_repr(self) -> dict: A dictionary representing the PolicyTagList object in serialized form. """ - answer = {"names": [name for name in self.names]} + answer = {"names": list(self.names)} return answer diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index 77daf6bb6..d05000e46 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -795,6 +795,34 @@ def test___hash__not_equals(self): set_two = {policy2} self.assertNotEqual(set_one, set_two) + def test___repr__no_tags(self): + policy = self._make_one() + assert repr(policy) == "PolicyTagList(names=())" + + def test___repr__with_tags(self): + policy1 = self._make_one(["foo", "bar", "baz"]) + policy2 = self._make_one(["baz", "bar", "foo"]) + expected_repr = "PolicyTagList(names=('bar', 'baz', 'foo'))" # alphabetical + + assert repr(policy1) == expected_repr + assert repr(policy2) == expected_repr + + def test___repr__evaluable_no_tags(self): + policy = self._make_one(names=[]) + policy_repr = repr(policy) + + evaled_policy = eval(policy_repr) + + assert policy == evaled_policy + + def test___repr__evaluable_with_tags(self): + policy = self._make_one(names=["foo", "bar"]) + policy_repr = repr(policy) + + evaled_policy = eval(policy_repr) + + assert policy == evaled_policy + @pytest.mark.parametrize( "api,expect,key2", From e735b6b164a16ad1360fcb35273bf11105d5ecbd Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Thu, 4 Nov 2021 17:08:18 +0200 Subject: [PATCH 3/3] Fix SchemaField repr with policy tags The repr() should be evaluable. --- google/cloud/bigquery/schema.py | 6 +++++- tests/unit/test_schema.py | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index 61e2f19d0..225942234 100644 --- a/google/cloud/bigquery/schema.py +++ b/google/cloud/bigquery/schema.py @@ -336,7 +336,11 @@ def __hash__(self): return hash(self._key()) def __repr__(self): - return "SchemaField{}".format(self._key()) + key = self._key() + policy_tags = key[-1] + policy_tags_inst = None if policy_tags is None else PolicyTagList(policy_tags) + adjusted_key = key[:-1] + (policy_tags_inst,) + return f"{self.__class__.__name__}{adjusted_key}" def _parse_schema_resource(info): diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index d05000e46..03ff837c0 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -513,7 +513,7 @@ def test___repr__(self): expected = "SchemaField('field1', 'STRING', 'NULLABLE', None, (), None)" self.assertEqual(repr(field1), expected) - def test___repr__evaluable(self): + def test___repr__evaluable_no_policy_tags(self): field = self._make_one("field1", "STRING", "REQUIRED", "Description") field_repr = repr(field) SchemaField = self._get_target_class() # needed for eval # noqa @@ -522,6 +522,18 @@ def test___repr__evaluable(self): assert field == evaled_field + def test___repr__evaluable_with_policy_tags(self): + policy_tags = PolicyTagList(names=["foo", "bar"]) + field = self._make_one( + "field1", "STRING", "REQUIRED", "Description", policy_tags=policy_tags, + ) + field_repr = repr(field) + SchemaField = self._get_target_class() # needed for eval # noqa + + evaled_field = eval(field_repr) + + assert field == evaled_field + # TODO: dedup with the same class in test_table.py. class _SchemaBase(object):