Skip to content

AttributeDict objects not always hashable #2572

Closed
@rikublock

Description

@rikublock
  • Version: 5.29.2
  • Python: 3.8.10
  • OS: Linux

What was wrong?

Depending on the items, AttributeDict might not always be hashable.

For example, log entries returned by eth_getLogs() can contain list objects (in this case 'topics'), which are not hashable.

AttributeDict({
	'address': '0x2C846CF666f2D427ec86ca4BADe204fFD5CbC421', 
	'topics': [HexBytes('0xdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496'), HexBytes('0x000000000000000000000000e54ca86531e17ef3616d22ca28b0d458b6c89106'), HexBytes('0x000000000000000000000000d2f59be407080abfd97a00a7071d67f93b526733')], 
	'data': '0x000000000000000000000000000000000000000000000000000000000018921600000000000000000000000000000000000000000000000033bd1424be462f14', 
	'blockNumber': 16955438, 
	'transactionHash': HexBytes('0x932a26038bcf3d22bd981543b7ed6aae653a8a565a2a78bd644162c7e1bff6e4'), 
	'transactionIndex': 2, 
	'blockHash': HexBytes('0xd5b1c3a7704a26b1b7b47b12d1a929ecba82bb0cbf9b97f41d227ff104612f7b'), 
	'logIndex': 16, 
	'removed': False
 })

How can it be fixed?

a) Adjust the __hash__ function. Recursively look for mutable types like lists, etc. and temporary replace them before hashing.
This would likely also require changing __eq__ as two object can now have the same hash without being "equal".

source

def __hash__(self) -> int:
    return hash(tuple(sorted(self.items())))

b) Recursively replace mutable types (e.g. list -> tuple) when creating the AttributeDict object.

source

def __init__(
    self, dictionary: Dict[TKey, TValue], *args: Any, **kwargs: Any
) -> None:
    # type ignored on 46/50 b/c dict() expects str index type not TKey
    self.__dict__ = dict(dictionary)  # type: ignore
    self.__dict__.update(dict(*args, **kwargs))

c) Handle it earlier, for instance when parsing response results.

d) Add a more restrictive TValue type (e.g. allow only hashable values). That seems a much larger change though as it likely affects many parts/sections of the code base.

The AttributeDict type is supposed to be immutable, this would suggest going with something like b).

I can prepare a PR, just need some directions on how you would like to have it solved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions