Skip to content
Browse files
feat: make AccessEntry objects hashable (#93)
* feat: add a __hash__ implementation to AccessEntry

* use internal

* feat: unit tests for hashable AccessEntry

* fix: black lint issue

Co-authored-by: Peter Lamut <>
  • Loading branch information
spennymac and plamut committed May 15, 2020
1 parent 0dd90b9 commit 23a173bc5a25c0c8200adc5af62eb05624c9099e
Showing with 51 additions and 14 deletions.
  1. +37 −14 google/cloud/bigquery/
  2. +14 −0 tests/unit/
@@ -20,6 +20,7 @@
import copy


from import _helpers
from import ModelReference
from import RoutineReference
@@ -145,38 +146,60 @@ def __init__(self, role, entity_type, entity_id):
"Role must be set for entity " "type %r" % (entity_type,)

self.role = role
self.entity_type = entity_type
self.entity_id = entity_id
self._role = role
self._entity_type = entity_type
self._entity_id = entity_id

def role(self):
"""str: The role of the entry."""
return self._role

def entity_type(self):
"""str: The entity_type of the entry."""
return self._entity_type

def entity_id(self):
"""str: The entity_id of the entry."""
return self._entity_id

def __eq__(self, other):
if not isinstance(other, AccessEntry):
return NotImplemented
return (
self.role == other.role
and self.entity_type == other.entity_type
and self.entity_id == other.entity_id
return self._key() == other._key()

def __ne__(self, other):
return not self == other

def __repr__(self):
return "<AccessEntry: role=%s, %s=%s>" % (

def _key(self):
""" A tuple key that uniquely describes this field.
Used to compute this instance's hashcode and evaluate equality.
Tuple: The contents of this :class:``.
return (self._role, self._entity_type, self._entity_id)

def __hash__(self):
return hash(self._key())

def to_api_repr(self):
"""Construct the API resource representation of this access entry
Dict[str, object]: Access entry represented as an API resource
resource = {self.entity_type: self.entity_id}
if self.role is not None:
resource["role"] = self.role
resource = {self._entity_type: self._entity_id}
if self._role is not None:
resource["role"] = self._role
return resource

@@ -84,6 +84,20 @@ def test__eq___type_mismatch(self):
self.assertNotEqual(entry, object())
self.assertEqual(entry, mock.ANY)

def test___hash__set_equality(self):
entry1 = self._make_one("OWNER", "userByEmail", "")
entry2 = self._make_one("OWNER", "userByEmail", "")
set_one = {entry1, entry2}
set_two = {entry1, entry2}
self.assertEqual(set_one, set_two)

def test___hash__not_equals(self):
entry1 = self._make_one("OWNER", "userByEmail", "")
entry2 = self._make_one("OWNER", "userByEmail", "")
set_one = {entry1}
set_two = {entry2}
self.assertNotEqual(set_one, set_two)

def test_to_api_repr(self):
entry = self._make_one("OWNER", "userByEmail", "")
resource = entry.to_api_repr()

0 comments on commit 23a173b

Please sign in to comment.