diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index 5bad52273..225942234 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 ( @@ -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): @@ -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) @@ -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): @@ -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": @@ -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 diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index 2180e1f6e..03ff837c0 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -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): @@ -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",