quaint-telephone-89068
02/14/2023, 8:42 PM$ git grep "__hash__ = "
src/python/pants/engine/collection.py: __hash__ = Tuple.__hash__
src/python/pants/engine/target.py: __hash__ = Tuple.__hash__
With some instrumentation:
diff --git a/src/python/pants/engine/collection.py b/src/python/pants/engine/collection.py
index c340fa2e6..cceaae611 100644
--- a/src/python/pants/engine/collection.py
+++ b/src/python/pants/engine/collection.py
@@ -52,7 +52,16 @@ class Collection(Tuple[T, ...]):
# Unlike in Python 2 we must explicitly implement __hash__ since we explicitly implement __eq__
# per the Python 3 data model.
# See: <https://docs.python.org/3/reference/datamodel.html#object.__hash__>
- __hash__ = Tuple.__hash__
+ def __hash__(self):
+ import inspect
+ import sys
+ print(f">>> Tuple.__hash__ code:\n{inspect.getsource(Tuple.__hash__)}", file=sys.stderr)
+ print(f">>> Tuple.__origin__: {Tuple.__origin__}", file=sys.stderr)
+ print(f">>> Tuple.__args__: {Tuple.__args__}", file=sys.stderr)
+ print(f">>> {self} delegating __hash__ to super ...", file=sys.stderr)
+ print(f">>> Tuple.__hash__ value: {Tuple.__hash__()}", file=sys.stderr)
+ # return super().__hash__()
+ return Tuple.__hash__()
def __repr__(self) -> str:
return f"{self.__class__.__name__}({list(self)})"
That reveals:
>>> Tuple.__hash__ code:
def __hash__(self):
if self.__origin__ is Union:
return hash((Union, frozenset(self.__args__)))
return hash((self.__origin__, self.__args__))
>>> Tuple.__origin__: <class 'tuple'>
>>> Tuple.__args__: ()
>>> Tuple.__hash__ value: -9020823266885220566
...
>>> Tuple.__hash__ value: -9020823266885220566
>>> Tuple.__origin__: <class 'tuple'>
>>> Tuple.__args__: ()
>>> Tuple.__hash__ code:
def __hash__(self):
if self.__origin__ is Union:
return hash((Union, frozenset(self.__args__)))
return hash((self.__origin__, self.__args__))
...
So the same hash value for every instance of pants.engine.collection.Collection. Not good if we have any large collection of these.
Adding in a real call of `tuple.__hash__`:
$ git diff
diff --git a/src/python/pants/engine/collection.py b/src/python/pants/engine/collection.py
index c340fa2e6..b58e13069 100644
--- a/src/python/pants/engine/collection.py
+++ b/src/python/pants/engine/collection.py
@@ -52,7 +52,8 @@ class Collection(Tuple[T, ...]):
# Unlike in Python 2 we must explicitly implement __hash__ since we explicitly implement __eq__
# per the Python 3 data model.
# See: <https://docs.python.org/3/reference/datamodel.html#object.__hash__>
- __hash__ = Tuple.__hash__
+ def __hash__(self):
+ return super().__hash__()
Worse, we're attempting to hash unhashable contained things:
```
$ ./pants check src/python/pants/engine::
124508.69 [ERROR] 1 Exception encountered:
Engine traceback:
in select
..
in pants.core.util_rules.environments.determine_local_environment
..
in pants.core.util_rules.environments.determine_all_environments
..
Traceback (most recent call last):
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/util/frozendict.py", line 91, in _calculate_hash
return hash(tuple(self._data.items()))
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/collection.py", line 56, in hash
return super().__hash__()
File "<string>", line 3, in hash
TypeError: unhashable type: 'dict'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
res = rule.send(arg) if err is None else rule.throw(throw or err)
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/core/util_rules/environments.py", line 592, in determine_all_environments
Get(EnvironmentTarget, EnvironmentName(name)) for name in environments_subsystem.names
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 358, in MultiGet
return await _MultiGet(tuple(__arg0))
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 165, in await
result = yield self.gets
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
res = rule.send(arg) if err is None else rule.throw(throw or err)
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/core/util_rules/environments.py", line 827, in get_target_for_environment_name
WrappedTargetRequest(address, description_of_origin=_description_of_origin),
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 118, in await
result = yield self
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
res = rule.send(arg) if err is None else rule.throw(throw or err)
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/graph.py", line 401, in resolve_target_for_bootstrapping
description_of_origin=request.description_of_origin,
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/graph.py", line 180, in _determine_target_adaptor_and_type
TargetAdaptorRequest(address, description_of_origin=description_of_origin),
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 118, in await
result = yield self
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
res = rule.send(arg) if err is None else rule.throw(throw or err)
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/build_files.py", line 395, in find_target_adaptor
address_family = await Get(AddressFamily, AddressFamilyDir(address.spec_path))
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 118, in await
result = yield self
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
res = rule.send(arg) if err is None else rule.throw(throw or err)
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/build_files.py", line 395, in find_target_adaptor
address_family = await Get(AddressFamily, AddressFamilyDir(address.spec_path))
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 118, in await
result = yield self
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
res = rule.send(arg) if err is None else rule.throw(throw or err)
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/build_files.py", line 260, in parse_address_family
Get(SyntheticAddressMaps, SyntheticAddressMapsRequest(directory.path)),
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 509, in MultiGet
return await _MultiGet((__arg0, __arg1))
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 165, in await
result = yield self.gets
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
res = rule.send(arg) if err is None else rule.throw(throw or err)
File "/home/jsirois/dev/pantsbuild/pants/src/python/pants/engine/internals/synthetic_targets.py", line 314, in all_synthetic_targets
spec_pat…
pantsbuild/pants