[tor-commits] [stem/master] Adding hash functions to exit policy classes
atagar at torproject.org
atagar at torproject.org
Mon Oct 14 00:42:16 UTC 2013
commit 4ef5ee1ee8fbecabeaee872f2681cdda1fb86584
Author: Damian Johnson <atagar at torproject.org>
Date: Sat Oct 12 20:18:12 2013 -0700
Adding hash functions to exit policy classes
Huh, didn't expect that. To work the @lru_cache requires that all arguments
(including the class in the ase of methods) are hashable. This makes perfectly
sense, after all the cache is a 'argument => cached value' dictionary.
Under python 2.x all seemed to be well, but under python 3.x the @lru_cache
complained that our classes weren't hashable (which... well, they aren't). Why,
then, did it work under python 2.x? Turns out there's a subtle difference where
our object parent provides a __hash__ method for our id, but in python 3.x it's
a little different (object still does, but for reasons I haven't been able to
figure out it doesn't for our classes).
The ExitPolicyRule can certainly be hashable. The ExitPolicy, however, is a lot
trickier due to its lazy loading of rules. For now just opting for the same
behavior as python 2.x and using the address.
---
stem/exit_policy.py | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/stem/exit_policy.py b/stem/exit_policy.py
index 9366a54..a358908 100644
--- a/stem/exit_policy.py
+++ b/stem/exit_policy.py
@@ -349,6 +349,14 @@ class ExitPolicy(object):
def __str__(self):
return ', '.join([str(rule) for rule in self._get_rules()])
+ def __hash__(self):
+ # TODO: It would be nice to provide a real hash function, but doing so is
+ # tricky due to how we lazily load the rules. Like equality checks a proper
+ # hash function would need to call _get_rules(), but that's behind
+ # @lru_cache which calls hash() forming a circular dependency.
+
+ return id(self)
+
def __eq__(self, other):
if isinstance(other, ExitPolicy):
return self._get_rules() == list(other)
@@ -429,6 +437,9 @@ class MicroExitPolicy(ExitPolicy):
def __str__(self):
return self._policy
+ def __hash__(self):
+ return id(self)
+
def __eq__(self, other):
if isinstance(other, MicroExitPolicy):
return str(self) == str(other)
@@ -682,6 +693,22 @@ class ExitPolicyRule(object):
return label
+ def __hash__(self):
+ my_hash = 0
+
+ for attr in ("is_accept", "address", "min_port", "max_port"):
+ my_hash *= 1024
+
+ attr_value = getattr(self, attr)
+
+ if attr_value is not None:
+ my_hash += hash(attr_value)
+
+ my_hash *= 1024
+ my_hash += hash(self.get_mask(False))
+
+ return my_hash
+
@lru_cache()
def _get_mask_bin(self):
# provides an integer representation of our mask
@@ -792,7 +819,7 @@ class ExitPolicyRule(object):
# 0.0.0.0/0" == "accept 0.0.0.0/0.0.0.0" will be True), but these
# policies are effectively equivalent.
- return str(self) == str(other)
+ return hash(self) == hash(other)
else:
return False
@@ -827,3 +854,16 @@ class MicroExitPolicyRule(ExitPolicyRule):
def get_masked_bits(self):
return None
+
+ def __hash__(self):
+ my_hash = 0
+
+ for attr in ("is_accept", "min_port", "max_port"):
+ my_hash *= 1024
+
+ attr_value = getattr(self, attr)
+
+ if attr_value is not None:
+ my_hash += hash(attr_value)
+
+ return my_hash
More information about the tor-commits
mailing list