Skip to content

Commit

Permalink
fix: error if eval()-ing repr(SchemaField) (googleapis#1046)
Browse files Browse the repository at this point in the history
* fix: error if eval()-ing repr(SchemaField)

* Make repr(PolicyTagList) evaluable

* Fix SchemaField repr with policy tags

The repr() should be evaluable.

Co-authored-by: Tres Seaver <tseaver@palladion.com>
  • Loading branch information
2 people authored and abdelmegahedgoogle committed Apr 17, 2023
1 parent a50fcfd commit 8dc6699
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 7 deletions.
16 changes: 10 additions & 6 deletions google/cloud/bigquery/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -407,7 +411,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)

Expand All @@ -425,7 +429,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):
Expand All @@ -439,7 +443,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":
Expand Down Expand Up @@ -478,5 +482,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
51 changes: 50 additions & 1 deletion tests/unit/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,9 +510,30 @@ 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_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

evaled_field = eval(field_repr)

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):
Expand Down Expand Up @@ -786,6 +807,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",
Expand Down

0 comments on commit 8dc6699

Please sign in to comment.