magmax / python-inquirer

A collection of common interactive command line user interfaces, based on Inquirer.js (https://github.com/SBoudrias/Inquirer.js/)
MIT License
992 stars 98 forks source link

Tuples as list choices can't be hashed when containing a dict #525

Closed Zereges closed 6 months ago

Zereges commented 6 months ago

In 3.2.1, the following worked:

>>> import inquirer
>>> test = [('aa', {'a': 1}), ('bb', {'b':2})]
>>> inquirer.list_input('Which?', carousel=True, choices=test)
[?] Which?: bb
   aa
 > bb

{'b': 2}

Whereas in 3.2.2 (and 3.2.3) this doesn't:

>>> import inquirer
>>> test = [('aa', {'a': 1}), ('bb', {'b':2})]
>>> inquirer.list_input('Which?', carousel=True, choices=test)
[?] Which?:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "App/.venv/lib/python3.11/site-packages/inquirer/shortcuts.py", line 32, in list_input
    return render.render(question)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "App/.venv/lib/python3.11/site-packages/inquirer/render/console/__init__.py", line 38, in render
    return self._event_loop(render)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "App/.venv/lib/python3.11/site-packages/inquirer/render/console/__init__.py", line 49, in _event_loop
    self._print_hint(render)
  File "App/.venv/lib/python3.11/site-packages/inquirer/render/console/__init__.py", line 96, in _print_hint
    hint = render.get_hint()
           ^^^^^^^^^^^^^^^^^
  File "App/.venv/lib/python3.11/site-packages/inquirer/render/console/_list.py", line 23, in get_hint
    hint = self.question.hints[choice]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^
  File "App/.venv/lib/python3.11/site-packages/inquirer/questions.py", line 37, in __hash__
    return hash(self.tuple)
           ^^^^^^^^^^^^^^^^
TypeError: unhashable type: 'dict'

The reason is that TaggedValue's hash function tries to hash whole tuple passed. Would it be enough to hash just the string representation (i.e. hash(self.tag)), instead of the whole tuple? I'd say the list of choices should provide unique string descriptions for every item (although it might technically not be necessary when passing hints= as well?)

Cube707 commented 6 months ago

Hm, this is a though one. The TaggedValue needs to hash to the same hash-value as the tuple it represents, so that they can be used interchangeably as dict-keys. This is need by the feature introduced in #433 to make tuple-pair and hint compatible.

I agree that it technically shouldn'd need to be hashed if no hints are provided, so I will check that if I find some time.

But as tuples in general repersend immutable data it can usually be assumed that they are hashable and this is not really a regression, instead your usecase only happend to work before due to pythons loose enforcment of such things

Cube707 commented 6 months ago

as you suggested, it is easy enough to just skip the whole hints part if its not needed, avoiding the issue.