Repository: googleapis/python-bigquery
Pull request #2182 (2025-05-13 18:28:06)

fix: ensure AccessEntry equality and repr uses the correct `entity_type`
Discovered while updating the internal version of google-cloud-bigquery. Without this fix, it was causing this test to fail: https://github.com/dbt-labs/dbt-bigquery/blob/0995665e490cdee9c408d26aac8e1c19fefaebe0/tests/unit/test_dataset.py#L25

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/python-bigquery/issues/new/choose) before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> 🦕



Patch:
diff --git a/google/cloud/bigquery/dataset.py b/google/cloud/bigquery/dataset.py
index d225b710..f788275c 100644
--- a/google/cloud/bigquery/dataset.py
+++ b/google/cloud/bigquery/dataset.py
@@ -512,7 +512,7 @@ class AccessEntry(object):
         return not self == other
 
     def __repr__(self):
-        return f"<AccessEntry: role={self.role}, {self._entity_type}={self.entity_id}>"
+        return f"<AccessEntry: role={self.role}, {self.entity_type}={self.entity_id}>"
 
     def _key(self):
         """A tuple key that uniquely describes this field.
@@ -531,7 +531,7 @@ class AccessEntry(object):
             properties["condition"] = condition_key
 
         prop_tup = tuple(sorted(properties.items()))
-        return (self.role, self._entity_type, self.entity_id, prop_tup)
+        return (self.role, self.entity_type, self.entity_id, prop_tup)
 
     def __hash__(self):
         return hash(self._key())


Test patch:
diff --git a/tests/unit/test_dataset.py b/tests/unit/test_dataset.py
index 94143082..5cce2a9a 100644
--- a/tests/unit/test_dataset.py
+++ b/tests/unit/test_dataset.py
@@ -613,6 +613,15 @@ class TestAccessEntryAndCondition:
         assert hash(entry1) == hash(entry2)
         assert hash(entry1) != hash(entry3)  # Usually true
 
+    def test_equality_and_hash_from_api_repr(self):
+        """Compare equal entries where one was created via from_api_repr."""
+        entry1 = AccessEntry("OWNER", "specialGroup", "projectOwners")
+        entry2 = AccessEntry.from_api_repr(
+            {"role": "OWNER", "specialGroup": "projectOwners"}
+        )
+        assert entry1 == entry2
+        assert hash(entry1) == hash(entry2)
+
     def test_equality_and_hash_with_condition(self, condition_1, condition_2):
         cond1a = Condition(
             condition_1.expression, condition_1.title, condition_1.description
@@ -746,6 +755,13 @@ class TestAccessEntryAndCondition:
         assert "dataset" in entry._properties
         assert "condition" in entry._properties
 
+    def test_repr_from_api_repr(self):
+        """Check that repr() includes the correct entity_type when the object is initialized from a dictionary."""
+        api_repr = {"role": "OWNER", "userByEmail": "owner@example.com"}
+        entry = AccessEntry.from_api_repr(api_repr)
+        entry_str = repr(entry)
+        assert entry_str == "<AccessEntry: role=OWNER, userByEmail=owner@example.com>"
+
 
 class TestDatasetReference(unittest.TestCase):
     @staticmethod
@@ -1097,6 +1113,34 @@ class TestDataset(unittest.TestCase):
         self.assertIsNone(dataset.location)
         self.assertEqual(dataset.is_case_insensitive, False)
 
+    def test_access_entries_getter_from_api_repr(self):
+        """Check that `in` works correctly when Dataset is made via from_api_repr()."""
+        from google.cloud.bigquery.dataset import AccessEntry
+
+        dataset = self._get_target_class().from_api_repr(
+            {
+                "datasetReference": {"projectId": "my-proj", "datasetId": "my_dset"},
+                "access": [
+                    {
+                        "role": "OWNER",
+                        "userByEmail": "uilma@example.com",
+                    },
+                    {
+                        "role": "READER",
+                        "groupByEmail": "rhubbles@example.com",
+                    },
+                ],
+            }
+        )
+        assert (
+            AccessEntry("OWNER", "userByEmail", "uilma@example.com")
+            in dataset.access_entries
+        )
+        assert (
+            AccessEntry("READER", "groupByEmail", "rhubbles@example.com")
+            in dataset.access_entries
+        )
+
     def test_access_entries_setter_non_list(self):
         dataset = self._make_one(self.DS_REF)
         with self.assertRaises(TypeError):