[tor-commits] [stem/master] Cache immutable class hash values
atagar at torproject.org
atagar at torproject.org
Wed Jul 18 21:37:49 UTC 2018
commit 99c4ad9db437dc528f1e7134001842d30a073a8a
Author: Damian Johnson <atagar at torproject.org>
Date: Wed Jul 18 04:00:34 2018 -0700
Cache immutable class hash values
Immutable classes have immutable cache values, and as frequntly used
equality/dictionary values it's useful to cache these.
---
stem/__init__.py | 12 ++++++++++--
stem/client/cell.py | 46 +++++++++++++++-------------------------------
stem/client/datatype.py | 14 ++++++++++----
stem/directory.py | 17 +++++++++++++----
stem/manual.py | 15 +++++++++++----
stem/util/__init__.py | 7 +++----
stem/version.py | 5 ++---
7 files changed, 64 insertions(+), 52 deletions(-)
diff --git a/stem/__init__.py b/stem/__init__.py
index 7d84423a..6da73320 100644
--- a/stem/__init__.py
+++ b/stem/__init__.py
@@ -572,9 +572,13 @@ class Endpoint(object):
self.address = address
self.port = int(port)
+ self._hash = None
def __hash__(self):
- return stem.util._hash_attr(self, 'address', 'port')
+ if self._hash is None:
+ self._hash = stem.util._hash_attr(self, 'address', 'port')
+
+ return self._hash
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Endpoint) else False
@@ -593,9 +597,13 @@ 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):
- return stem.util._hash_attr(self, 'link_protocols', parent = Endpoint)
+ if self._hash is None:
+ self._hash = stem.util._hash_attr(self, 'link_protocols', parent = True)
+
+ return self._hash
class DirPort(Endpoint):
diff --git a/stem/client/cell.py b/stem/client/cell.py
index 948201fd..51e2fbd3 100644
--- a/stem/client/cell.py
+++ b/stem/client/cell.py
@@ -46,7 +46,7 @@ import stem.util
from stem import UNDEFINED
from stem.client.datatype import HASH_LEN, ZERO, LinkProtocol, Address, Certificate, CloseReason, RelayCommand, Size, split
-from stem.util import _hash_attr, datetime_to_unix, str_tools
+from stem.util import datetime_to_unix, str_tools
FIXED_PAYLOAD_LEN = 509 # PAYLOAD_LEN, per tor-spec section 0.2
AUTH_CHALLENGE_SIZE = 32
@@ -95,6 +95,7 @@ class Cell(object):
def __init__(self, unused = b''):
super(Cell, self).__init__()
self.unused = unused
+ self._hash = None
@staticmethod
def by_name(name):
@@ -252,6 +253,9 @@ 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
@@ -290,6 +294,7 @@ 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)
@@ -298,9 +303,6 @@ class PaddingCell(Cell):
def _unpack(cls, content, circ_id, link_protocol):
return PaddingCell(content)
- def __hash__(self):
- return _hash_attr(self, 'payload')
-
class CreateCell(CircuitCell):
NAME = 'CREATE'
@@ -358,6 +360,7 @@ 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:
@@ -390,9 +393,6 @@ class RelayCell(CircuitCell):
return RelayCell(circ_id, command, data, digest, stream_id, recognized, unused)
- def __hash__(self):
- return _hash_attr(self, 'command_int', 'stream_id', 'digest', 'data')
-
class DestroyCell(CircuitCell):
"""
@@ -409,6 +409,7 @@ 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)
@@ -418,9 +419,6 @@ class DestroyCell(CircuitCell):
reason, unused = Size.CHAR.pop(content)
return DestroyCell(circ_id, reason, unused)
- def __hash__(self):
- return _hash_attr(self, 'circ_id', 'reason_int')
-
class CreateFastCell(CircuitCell):
"""
@@ -442,6 +440,7 @@ 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)
@@ -455,9 +454,6 @@ class CreateFastCell(CircuitCell):
return CreateFastCell(circ_id, key_material, unused)
- def __hash__(self):
- return _hash_attr(self, 'circ_id', 'key_material')
-
class CreatedFastCell(CircuitCell):
"""
@@ -483,6 +479,7 @@ 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)
@@ -497,9 +494,6 @@ class CreatedFastCell(CircuitCell):
return CreatedFastCell(circ_id, derivative_key, key_material, content)
- def __hash__(self):
- return _hash_attr(self, 'circ_id', 'derivative_key', 'key_material')
-
class VersionsCell(Cell):
"""
@@ -515,6 +509,7 @@ 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])
@@ -530,9 +525,6 @@ class VersionsCell(Cell):
return VersionsCell(link_protocols)
- def __hash__(self):
- return _hash_attr(self, 'versions')
-
class NetinfoCell(Cell):
"""
@@ -552,6 +544,7 @@ 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()
@@ -578,9 +571,6 @@ class NetinfoCell(Cell):
return NetinfoCell(receiver_address, sender_addresses, datetime.datetime.utcfromtimestamp(timestamp), unused = content)
- def __hash__(self):
- return _hash_attr(self, 'timestamp', 'receiver_address', 'sender_addresses')
-
class RelayEarlyCell(CircuitCell):
NAME = 'RELAY_EARLY'
@@ -639,6 +629,7 @@ 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)
@@ -647,9 +638,6 @@ class VPaddingCell(Cell):
def _unpack(cls, content, circ_id, link_protocol):
return VPaddingCell(payload = content)
- def __hash__(self):
- return _hash_attr(self, 'payload')
-
class CertsCell(Cell):
"""
@@ -665,6 +653,7 @@ 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)
@@ -683,9 +672,6 @@ class CertsCell(Cell):
return CertsCell(certs, unused = content)
- def __hash__(self):
- return _hash_attr(self, 'certificates')
-
class AuthChallengeCell(Cell):
"""
@@ -709,6 +695,7 @@ 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()
@@ -740,9 +727,6 @@ class AuthChallengeCell(Cell):
return AuthChallengeCell(methods, challenge, unused = content)
- def __hash__(self):
- return _hash_attr(self, 'challenge', 'methods')
-
class AuthenticateCell(Cell):
NAME = 'AUTHENTICATE'
diff --git a/stem/client/datatype.py b/stem/client/datatype.py
index 2ee624cb..22827245 100644
--- a/stem/client/datatype.py
+++ b/stem/client/datatype.py
@@ -121,8 +121,6 @@ import stem.util
import stem.util.connection
import stem.util.enum
-from stem.util import _hash_attr
-
ZERO = b'\x00'
HASH_LEN = 20
KEY_LEN = 16
@@ -382,6 +380,7 @@ 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):
@@ -431,7 +430,10 @@ class Address(Field):
return Address(addr_value, addr_type), content
def __hash__(self):
- return _hash_attr(self, 'type_int', 'value_bin')
+ if self._hash is None:
+ self._hash = stem.util._hash_attr(self, 'type_int', 'value_bin')
+
+ return self._hash
class Certificate(Field):
@@ -446,6 +448,7 @@ 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()
@@ -466,7 +469,10 @@ class Certificate(Field):
return Certificate(cert_type, cert_bytes), content
def __hash__(self):
- return _hash_attr(self, 'type_int', 'value')
+ if self._hash is None:
+ self._hash = stem.util._hash_attr(self, 'type_int', 'value')
+
+ return self._hash
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 efd37641..020ac696 100644
--- a/stem/directory.py
+++ b/stem/directory.py
@@ -41,9 +41,10 @@ as follows...
import os
import re
+import stem.util
import stem.util.conf
-from stem.util import _hash_attr, connection, str_tools, tor_tools
+from stem.util import connection, str_tools, tor_tools
try:
# added in python 2.7
@@ -217,7 +218,7 @@ class Directory(object):
raise NotImplementedError('Unsupported Operation: this should be implemented by the Directory subclass')
def __hash__(self):
- return _hash_attr(self, 'address', 'or_port', 'dir_port', 'fingerprint', 'nickname', 'orport_v6')
+ return stem.util._hash_attr(self, 'address', 'or_port', 'dir_port', 'fingerprint', 'nickname', 'orport_v6')
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Directory) else False
@@ -254,6 +255,7 @@ class Authority(Directory):
self.v3ident = v3ident
self.is_bandwidth_authority = is_bandwidth_authority
+ self._hash = None
@staticmethod
def from_cache():
@@ -313,7 +315,10 @@ class Authority(Directory):
return section_lines
def __hash__(self):
- return _hash_attr(self, 'v3ident', 'is_bandwidth_authority', parent = Directory)
+ if self._hash is None:
+ self._hash = stem.util._hash_attr(self, 'v3ident', 'is_bandwidth_authority', parent = True)
+
+ return self._hash
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Authority) else False
@@ -365,6 +370,7 @@ 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):
@@ -514,7 +520,10 @@ class Fallback(Directory):
conf.save(path)
def __hash__(self):
- return _hash_attr(self, 'has_extrainfo', 'header', parent = Directory)
+ if self._hash is None:
+ self._hash = stem.util._hash_attr(self, 'has_extrainfo', 'header', parent = True)
+
+ return self._hash
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Fallback) else False
diff --git a/stem/manual.py b/stem/manual.py
index 573542da..458014b0 100644
--- a/stem/manual.py
+++ b/stem/manual.py
@@ -54,13 +54,12 @@ import sys
import tempfile
import stem.prereq
+import stem.util
import stem.util.conf
import stem.util.enum
import stem.util.log
import stem.util.system
-from stem.util import _hash_attr
-
try:
# added in python 2.7
from collections import OrderedDict
@@ -189,9 +188,13 @@ class ConfigOption(object):
self.usage = usage
self.summary = summary
self.description = description
+ self._hash = None
def __hash__(self):
- return _hash_attr(self, 'name', 'category', 'usage', 'summary', 'description')
+ if self._hash is None:
+ self._hash = stem.util._hash_attr(self, 'name', 'category', 'usage', 'summary', 'description')
+
+ return self._hash
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, ConfigOption) else False
@@ -381,6 +384,7 @@ class Manual(object):
self.man_commit = None
self.stem_commit = None
self.schema = None
+ self._hash = None
@staticmethod
def from_cache(path = None):
@@ -654,7 +658,10 @@ class Manual(object):
conf.save(path)
def __hash__(self):
- return _hash_attr(self, 'name', 'synopsis', 'description', 'commandline_options', 'signals', 'files', 'config_options')
+ if self._hash is None:
+ self._hash = stem.util._hash_attr(self, 'name', 'synopsis', 'description', 'commandline_options', 'signals', 'files', 'config_options')
+
+ return self._hash
def __eq__(self, other):
return hash(self) == hash(other) if isinstance(other, Manual) else False
diff --git a/stem/util/__init__.py b/stem/util/__init__.py
index b812fc13..cc40bd0a 100644
--- a/stem/util/__init__.py
+++ b/stem/util/__init__.py
@@ -130,12 +130,11 @@ def _hash_attr(obj, *attributes, **kwargs):
:param Object obj: object to be hashed
:param list attributes: attribute names to take into account
- :param class parent: parent object to include in the hash value
+ :param bool parent: include parent's hash value
"""
- # TODO: deal with this parent thing
-
- my_hash = hash(str(type(obj))) if kwargs.get('parent') is None else kwargs.get('parent').__hash__(obj)
+ my_hash = super(type(obj), obj).__hash__() if kwargs.get('parent') else 0
+ my_hash = my_hash * 1024 + hash(str(type(obj)))
for attr in attributes:
val = getattr(obj, attr)
diff --git a/stem/version.py b/stem/version.py
index 9036effb..ed1a3a38 100644
--- a/stem/version.py
+++ b/stem/version.py
@@ -83,11 +83,10 @@ easily parsed and compared, for instance...
import os
import re
+import stem.util
import stem.util.enum
import stem.util.system
-from stem.util import _hash_attr
-
try:
# added in python 3.2
from functools import lru_cache
@@ -252,7 +251,7 @@ class Version(object):
def __hash__(self):
if self._hash is None:
- self._hash = _hash_attr(self, 'major', 'minor', 'micro', 'patch', 'status')
+ self._hash = stem.util._hash_attr(self, 'major', 'minor', 'micro', 'patch', 'status')
return self._hash
More information about the tor-commits
mailing list