diff --git a/src/prompt_toolkit/completion/base.py b/src/prompt_toolkit/completion/base.py index 6e73290ec..622a0537e 100644 --- a/src/prompt_toolkit/completion/base.py +++ b/src/prompt_toolkit/completion/base.py @@ -81,7 +81,9 @@ def __eq__(self, other: object) -> bool: ) def __hash__(self) -> int: - return hash((self.text, self.start_position, self.display, self._display_meta)) + # Display and metadata may contain unhashable formatted text. These + # two fields alone still guarantee equal completions have equal hashes. + return hash((self.text, self.start_position)) @property def display_text(self) -> str: diff --git a/tests/test_completion.py b/tests/test_completion.py index 8b3541af0..6e7f6dee5 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -8,6 +8,7 @@ from prompt_toolkit.completion import ( CompleteEvent, + Completion, FuzzyWordCompleter, NestedCompleter, PathCompleter, @@ -467,3 +468,39 @@ def create_completer(deduplicate: bool): ) ) assert len(completions) == 5 + + +def test_completion_hash_default_display(): + first = Completion("example", start_position=-2) + equal = Completion("example", start_position=-2) + different = Completion("other", start_position=-2) + assert first == equal + assert hash(first) == hash(equal) + assert {first, equal, different} == {first, different} + assert {first: "cached"}[equal] == "cached" + + +def test_completion_hash_formatted_display_and_metadata(): + first = Completion( + "example", display=[("class:name", "Example")], display_meta=[("", "meta")] + ) + equal = Completion( + "example", display=[("class:name", "Example")], display_meta=[("", "meta")] + ) + different = Completion( + "example", display=[("class:name", "Other")], display_meta=[("", "meta")] + ) + assert first == equal + assert hash(first) == hash(equal) + assert len({first, equal, different}) == 2 + + +def test_completion_hash_does_not_evaluate_lazy_metadata(): + def metadata(): + raise AssertionError("Metadata should remain lazy") + + first = Completion("example", display_meta=metadata) + equal = Completion("example", display_meta=metadata) + assert first == equal + assert hash(first) == hash(equal) + assert len({first, equal}) == 1