[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Simplify hash caching
commit fb98a2c044aea25d776002300f72871e408b4394
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Wed Aug 8 15:21:34 2018 -0700
Simplify hash caching
Ahh. On reflection there's no need for each class to handle its own hash
caching. There's a couple spots where they should due to being special
snowflakes, but generally our hash_attr() can do this.
---
stem/__init__.py | 12 ++----------
stem/client/cell.py | 44 ++++++++++++++++++++++++++++++--------------
stem/client/datatype.py | 18 +++---------------
stem/directory.py | 12 ++----------
stem/exit_policy.py | 6 +-----
stem/manual.py | 12 ++----------
stem/response/__init__.py | 3 ++-
stem/response/events.py | 6 +-----
stem/util/__init__.py | 11 ++++++++++-
stem/version.py | 6 +-----
10 files changed, 54 insertions(+), 76 deletions(-)
diff --git a/stem/__init__.py b/stem/__init__.py
index 1679b0a0..f95d1b5e 100644
--- a/stem/__init__.py
+++ b/stem/__init__.py
@@ -572,13 +572,9 @@ class Endpoint(object):
self.address = address
self.port = int(port)
- self._hash = None
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'address', 'port')
-
- return self._hash
+ return stem.util._hash_attr(self, 'address', 'port', cache = True)
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Endpoint) else False
@@ -597,13 +593,9 @@ class ORPort(Endpoint):
def __init__(self, address, port, link_protocols = None):
super(ORPort, self).__init__(address, port)
self.link_protocols = link_protocols
- self._hash = None
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'link_protocols', parent = Endpoint)
-
- return self._hash
+ return stem.util._hash_attr(self, 'link_protocols', parent = Endpoint, cache = True)
class DirPort(Endpoint):
diff --git a/stem/client/cell.py b/stem/client/cell.py
index ec214018..12fa994c 100644
--- a/stem/client/cell.py
+++ b/stem/client/cell.py
@@ -95,7 +95,6 @@ class Cell(object):
def __init__(self, unused = b''):
super(Cell, self).__init__()
self.unused = unused
- self._hash = None
@staticmethod
def by_name(name):
@@ -253,9 +252,6 @@ class Cell(object):
raise NotImplementedError('Unpacking not yet implemented for %s cells' % cls.NAME)
- def __hash__(self):
- return self._hash if self._hash else super(type(self), self).__hash__()
-
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Cell) else False
@@ -294,7 +290,6 @@ class PaddingCell(Cell):
super(PaddingCell, self).__init__()
self.payload = payload
- self._hash = stem.util._hash_attr(self, 'payload')
def pack(self, link_protocol):
return PaddingCell._pack(link_protocol, self.payload)
@@ -303,6 +298,9 @@ class PaddingCell(Cell):
def _unpack(cls, content, circ_id, link_protocol):
return PaddingCell(content)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'payload', cache = True)
+
class CreateCell(CircuitCell):
NAME = 'CREATE'
@@ -360,7 +358,6 @@ class RelayCell(CircuitCell):
self.stream_id = stream_id
self.digest = digest
self.data = str_tools._to_bytes(data)
- self._hash = stem.util._hash_attr(self, 'command_int', 'stream_id', 'digest', 'data')
if digest == 0:
if not stream_id and self.command in STREAM_ID_REQUIRED:
@@ -393,6 +390,9 @@ class RelayCell(CircuitCell):
return RelayCell(circ_id, command, data, digest, stream_id, recognized, unused)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'command_int', 'stream_id', 'digest', 'data', cache = True)
+
class DestroyCell(CircuitCell):
"""
@@ -409,7 +409,6 @@ class DestroyCell(CircuitCell):
def __init__(self, circ_id, reason = CloseReason.NONE, unused = b''):
super(DestroyCell, self).__init__(circ_id, unused)
self.reason, self.reason_int = CloseReason.get(reason)
- self._hash = stem.util._hash_attr(self, 'circ_id', 'reason_int')
def pack(self, link_protocol):
return DestroyCell._pack(link_protocol, Size.CHAR.pack(self.reason_int), self.unused, self.circ_id)
@@ -419,6 +418,9 @@ class DestroyCell(CircuitCell):
reason, unused = Size.CHAR.pop(content)
return DestroyCell(circ_id, reason, unused)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'circ_id', 'reason_int', cache = True)
+
class CreateFastCell(CircuitCell):
"""
@@ -440,7 +442,6 @@ class CreateFastCell(CircuitCell):
super(CreateFastCell, self).__init__(circ_id, unused)
self.key_material = key_material
- self._hash = stem.util._hash_attr(self, 'circ_id', 'key_material')
def pack(self, link_protocol):
return CreateFastCell._pack(link_protocol, self.key_material, self.unused, self.circ_id)
@@ -454,6 +455,9 @@ class CreateFastCell(CircuitCell):
return CreateFastCell(circ_id, key_material, unused)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'circ_id', 'key_material', cache = True)
+
class CreatedFastCell(CircuitCell):
"""
@@ -479,7 +483,6 @@ class CreatedFastCell(CircuitCell):
super(CreatedFastCell, self).__init__(circ_id, unused)
self.key_material = key_material
self.derivative_key = derivative_key
- self._hash = stem.util._hash_attr(self, 'circ_id', 'derivative_key', 'key_material')
def pack(self, link_protocol):
return CreatedFastCell._pack(link_protocol, self.key_material + self.derivative_key, self.unused, self.circ_id)
@@ -494,6 +497,9 @@ class CreatedFastCell(CircuitCell):
return CreatedFastCell(circ_id, derivative_key, key_material, content)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'circ_id', 'derivative_key', 'key_material', cache = True)
+
class VersionsCell(Cell):
"""
@@ -509,7 +515,6 @@ class VersionsCell(Cell):
def __init__(self, versions):
super(VersionsCell, self).__init__()
self.versions = versions
- self._hash = stem.util._hash_attr(self, 'versions')
def pack(self, link_protocol):
payload = b''.join([Size.SHORT.pack(v) for v in self.versions])
@@ -525,6 +530,9 @@ class VersionsCell(Cell):
return VersionsCell(link_protocols)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'versions', cache = True)
+
class NetinfoCell(Cell):
"""
@@ -544,7 +552,6 @@ class NetinfoCell(Cell):
self.timestamp = timestamp if timestamp else datetime.datetime.now()
self.receiver_address = receiver_address
self.sender_addresses = sender_addresses
- self._hash = stem.util._hash_attr(self, 'timestamp', 'receiver_address', 'sender_addresses')
def pack(self, link_protocol):
payload = bytearray()
@@ -571,6 +578,9 @@ class NetinfoCell(Cell):
return NetinfoCell(receiver_address, sender_addresses, datetime.datetime.utcfromtimestamp(timestamp), unused = content)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'timestamp', 'receiver_address', 'sender_addresses', cache = True)
+
class RelayEarlyCell(CircuitCell):
NAME = 'RELAY_EARLY'
@@ -629,7 +639,6 @@ class VPaddingCell(Cell):
super(VPaddingCell, self).__init__()
self.payload = payload if payload is not None else os.urandom(size)
- self._hash = stem.util._hash_attr(self, 'payload')
def pack(self, link_protocol):
return VPaddingCell._pack(link_protocol, self.payload)
@@ -638,6 +647,9 @@ class VPaddingCell(Cell):
def _unpack(cls, content, circ_id, link_protocol):
return VPaddingCell(payload = content)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'payload', cache = True)
+
class CertsCell(Cell):
"""
@@ -653,7 +665,6 @@ class CertsCell(Cell):
def __init__(self, certs, unused = b''):
super(CertsCell, self).__init__(unused)
self.certificates = certs
- self._hash = stem.util._hash_attr(self, 'certificates')
def pack(self, link_protocol):
return CertsCell._pack(link_protocol, Size.CHAR.pack(len(self.certificates)) + b''.join([cert.pack() for cert in self.certificates]), self.unused)
@@ -672,6 +683,9 @@ class CertsCell(Cell):
return CertsCell(certs, unused = content)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'certificates', cache = True)
+
class AuthChallengeCell(Cell):
"""
@@ -695,7 +709,6 @@ class AuthChallengeCell(Cell):
super(AuthChallengeCell, self).__init__(unused)
self.challenge = challenge
self.methods = methods
- self._hash = stem.util._hash_attr(self, 'challenge', 'methods')
def pack(self, link_protocol):
payload = bytearray()
@@ -727,6 +740,9 @@ class AuthChallengeCell(Cell):
return AuthChallengeCell(methods, challenge, unused = content)
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'challenge', 'methods', cache = True)
+
class AuthenticateCell(Cell):
NAME = 'AUTHENTICATE'
diff --git a/stem/client/datatype.py b/stem/client/datatype.py
index 32295317..5ca4e820 100644
--- a/stem/client/datatype.py
+++ b/stem/client/datatype.py
@@ -349,7 +349,6 @@ class Size(Field):
self.name = name
self.size = size
self.format = pack_format
- self._hash = None
@staticmethod
def pop(packed):
@@ -378,10 +377,7 @@ class Size(Field):
return self.unpack(to_unpack), remainder
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'name', 'size', 'format')
-
- return self._hash
+ return stem.util._hash_attr(self, 'name', 'size', 'format', cache = True)
class Address(Field):
@@ -404,7 +400,6 @@ class Address(Field):
raise ValueError("'%s' isn't an IPv4 or IPv6 address" % value)
self.type, self.type_int = AddrType.get(addr_type)
- self._hash = None
if self.type == AddrType.IPv4:
if stem.util.connection.is_valid_ipv4_address(value):
@@ -454,10 +449,7 @@ class Address(Field):
return Address(addr_value, addr_type), content
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'type_int', 'value_bin')
-
- return self._hash
+ return stem.util._hash_attr(self, 'type_int', 'value_bin', cache = True)
class Certificate(Field):
@@ -472,7 +464,6 @@ class Certificate(Field):
def __init__(self, cert_type, value):
self.type, self.type_int = CertType.get(cert_type)
self.value = value
- self._hash = None
def pack(self):
cell = bytearray()
@@ -493,10 +484,7 @@ class Certificate(Field):
return Certificate(cert_type, cert_bytes), content
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'type_int', 'value')
-
- return self._hash
+ return stem.util._hash_attr(self, 'type_int', 'value')
class KDF(collections.namedtuple('KDF', ['key_hash', 'forward_digest', 'backward_digest', 'forward_key', 'backward_key'])):
diff --git a/stem/directory.py b/stem/directory.py
index 5d243707..1782fbf1 100644
--- a/stem/directory.py
+++ b/stem/directory.py
@@ -255,7 +255,6 @@ class Authority(Directory):
self.v3ident = v3ident
self.is_bandwidth_authority = is_bandwidth_authority
- self._hash = None
@staticmethod
def from_cache():
@@ -315,10 +314,7 @@ class Authority(Directory):
return section_lines
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'v3ident', 'is_bandwidth_authority', parent = Directory)
-
- return self._hash
+ return stem.util._hash_attr(self, 'v3ident', 'is_bandwidth_authority', parent = Directory, cache = True)
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Authority) else False
@@ -370,7 +366,6 @@ class Fallback(Directory):
super(Fallback, self).__init__(address, or_port, dir_port, fingerprint, nickname, orport_v6)
self.has_extrainfo = has_extrainfo
self.header = OrderedDict(header) if header else OrderedDict()
- self._hash = None
@staticmethod
def from_cache(path = FALLBACK_CACHE_PATH):
@@ -520,10 +515,7 @@ class Fallback(Directory):
conf.save(path)
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'has_extrainfo', 'header', parent = Directory)
-
- return self._hash
+ return stem.util._hash_attr(self, 'has_extrainfo', 'header', parent = Directory, cache = True)
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Fallback) else False
diff --git a/stem/exit_policy.py b/stem/exit_policy.py
index 0912546b..0ed5e581 100644
--- a/stem/exit_policy.py
+++ b/stem/exit_policy.py
@@ -1057,7 +1057,6 @@ class MicroExitPolicyRule(ExitPolicyRule):
self.address = None # wildcard address
self.min_port = min_port
self.max_port = max_port
- self._hash = None
self._skip_rule = False
def is_address_wildcard(self):
@@ -1073,10 +1072,7 @@ class MicroExitPolicyRule(ExitPolicyRule):
return None
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'is_accept', 'min_port', 'max_port')
-
- return self._hash
+ return stem.util._hash_attr(self, 'is_accept', 'min_port', 'max_port', cache = True)
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, MicroExitPolicyRule) else False
diff --git a/stem/manual.py b/stem/manual.py
index 3e3d317f..7b956aa5 100644
--- a/stem/manual.py
+++ b/stem/manual.py
@@ -187,13 +187,9 @@ class ConfigOption(object):
self.usage = usage
self.summary = summary
self.description = description
- self._hash = None
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'name', 'category', 'usage', 'summary', 'description')
-
- return self._hash
+ return stem.util._hash_attr(self, 'name', 'category', 'usage', 'summary', 'description', cache = True)
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, ConfigOption) else False
@@ -383,7 +379,6 @@ class Manual(object):
self.man_commit = None
self.stem_commit = None
self.schema = None
- self._hash = None
@staticmethod
def from_cache(path = None):
@@ -657,10 +652,7 @@ class Manual(object):
conf.save(path)
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'name', 'synopsis', 'description', 'commandline_options', 'signals', 'files', 'config_options')
-
- return self._hash
+ return stem.util._hash_attr(self, 'name', 'synopsis', 'description', 'commandline_options', 'signals', 'files', 'config_options', cache = True)
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Manual) else False
diff --git a/stem/response/__init__.py b/stem/response/__init__.py
index 54eb9c44..96436b4d 100644
--- a/stem/response/__init__.py
+++ b/stem/response/__init__.py
@@ -172,6 +172,7 @@ class ControlMessage(object):
self._parsed_content = parsed_content
self._raw_content = raw_content
self._str = None
+ self._hash = stem.util._hash_attr(self, '_raw_content')
def is_ok(self):
"""
@@ -302,7 +303,7 @@ class ControlMessage(object):
return ControlLine(content)
def __hash__(self):
- return stem.util._hash_attr(self, '_raw_content')
+ return self._hash
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, ControlMessage) else False
diff --git a/stem/response/events.py b/stem/response/events.py
index 37b60b38..64ed250d 100644
--- a/stem/response/events.py
+++ b/stem/response/events.py
@@ -57,7 +57,6 @@ class Event(stem.response.ControlMessage):
self.type = str(self).split()[0]
self.arrived_at = arrived_at
- self._hash = None
# if we're a recognized event type then translate ourselves into that subclass
@@ -73,10 +72,7 @@ class Event(stem.response.ControlMessage):
self._parse()
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'arrived_at', parent = stem.response.ControlMessage)
-
- return self._hash
+ return stem.util._hash_attr(self, 'arrived_at', parent = stem.response.ControlMessage, cache = True)
def _parse_standard_attr(self):
"""
diff --git a/stem/util/__init__.py b/stem/util/__init__.py
index f0c2397b..91d96bcf 100644
--- a/stem/util/__init__.py
+++ b/stem/util/__init__.py
@@ -130,10 +130,16 @@ def _hash_attr(obj, *attributes, **kwargs):
:param Object obj: object to be hashed
:param list attributes: attribute names to take into account
+ :param bool cache: persists hash in a '_cached_hash' object attribute
:param class parent: include parent's hash value
"""
- parent_class = kwargs.get('parent')
+ is_cached = kwargs.get('cache', False)
+ parent_class = kwargs.get('parent', None)
+ cached_hash = getattr(obj, '_cached_hash', None)
+
+ if is_cached and cached_hash is not None:
+ return cached_hash
my_hash = parent_class.__hash__(obj) if parent_class else 0
my_hash = my_hash * 1024 + hash(str(type(obj)))
@@ -142,4 +148,7 @@ def _hash_attr(obj, *attributes, **kwargs):
val = getattr(obj, attr)
my_hash = my_hash * 1024 + _hash_value(val)
+ if is_cached:
+ setattr(obj, '_cached_hash', my_hash)
+
return my_hash
diff --git a/stem/version.py b/stem/version.py
index b6a866f0..979bcf95 100644
--- a/stem/version.py
+++ b/stem/version.py
@@ -182,7 +182,6 @@ class Version(object):
def __init__(self, version_str):
self.version_str = version_str
version_parts = VERSION_PATTERN.match(version_str)
- self._hash = None
if version_parts:
major, minor, micro, patch, status, extra_str, _ = version_parts.groups()
@@ -251,10 +250,7 @@ class Version(object):
return method(my_status, other_status)
def __hash__(self):
- if self._hash is None:
- self._hash = stem.util._hash_attr(self, 'major', 'minor', 'micro', 'patch', 'status')
-
- return self._hash
+ return stem.util._hash_attr(self, 'major', 'minor', 'micro', 'patch', 'status', cache = True)
def __eq__(self, other):
return self._compare(other, lambda s, o: s == o)
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits