From 3d41ea15b13fbd81027a88dd9565ef33a1fdf29e Mon Sep 17 00:00:00 2001 From: Cleber Rosa Date: Tue, 13 Mar 2018 09:32:04 -0400 Subject: [PATCH] avocado/core/tree.py: allow MuxTreeNode to be hashed under Python 3 Under Python 3, as in Python 2, most objects are automatically hashable. Example: >>> class Foo(object): >>> pass >>> hash(Foo()) >>> 8760583602380 But if an object that implements __eq__ also has to implement __hash__. Under Python 3: >>> class Foo(object): >>> def __eq__(self, other): >>> return False Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: 'Foo' Since TreeNode implements __eq__, under Python 3, it *must* implement __hash__. There are a few tricky points, though. One of them is that "The only required property is that objects which compare equal have the same hash value". TreeNodes have a very flexible equality comparison, which cannot be taken into account in a hash implementation which doesn't have access to the "other" object. To the best of my knowledge, the way to go with the TreeNode __hash__ implementation is to take into account the comparison of all attributes that are used on __eq__ (name, values, children) and use those as the composition for __hash__, as suggested by the Python docs. Unfortunately, some of the content of "values" and "children" may themselves be unshashable values (such as dicts coming from YAML test suite loader). My coward's way out here was to use a string representation of such unhashable types to compose the TreeNode hash. This fixes 5 unittests of the varianter_yaml_to_mux plugin when run under Python 3, such as: ====================================================================== ERROR: test_get_rel_path (tests.test_mux.TestAvocadoParams) ---------------------------------------------------------------------- .... if len(set([_[1] for _ in ret])) == 1: # single source of results TypeError: unhashable type: 'MuxTreeNode' Reference: https://docs.python.org/3/reference/datamodel.html#object.__hash__ Signed-off-by: Cleber Rosa --- avocado/core/tree.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/avocado/core/tree.py b/avocado/core/tree.py index a84b0b94..079c39f9 100644 --- a/avocado/core/tree.py +++ b/avocado/core/tree.py @@ -200,6 +200,21 @@ class TreeNode(object): """ Inverted eq """ return not self == other + def __hash__(self): + values = [] + for item in self.value: + try: + values.append(hash(item)) + except TypeError: + values.append(hash(str(item))) + children = [] + for item in self.children: + try: + children.append(hash(item)) + except TypeError: + children.append(hash(str(item))) + return hash((self.name, ) + tuple(values) + tuple(children)) + def fingerprint(self): """ Reports string which represents the value of this node. -- GitLab