classification
Title: argparse types (and actions) must be hashable
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.6, Python 3.4, Python 3.5, Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: BNMetrics, bethard, gvanrossum, jnothman, krassowski, paul.j3
Priority: normal Keywords: patch

Created on 2012-11-21 03:28 by jnothman, last changed 2018-12-16 18:36 by gvanrossum. This issue is now closed.

Files
File name Uploaded Description Edit
patch.diff paul.j3, 2014-07-12 22:22 review
Pull Requests
URL Status Linked Edit
PR 10205 closed bradengroom, 2018-10-28 23:19
Messages (8)
msg176040 - (view) Author: Joel Nothman (jnothman) Date: 2012-11-21 03:28
The argparse documentation states that "type= can take any callable that takes a single string argument and returns the converted value". The following is an exception:

    >>> import argparse
    >>> ap = argparse.ArgumentParser()
    >>> ap.add_argument('foo', type={}.get)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib/python3/argparse.py", line 1294, in add_argument
        type_func = self._registry_get('type', action.type, action.type)
      File "/usr/lib/python3/argparse.py", line 1236, in _registry_get
        return self._registries[registry_name].get(value, default)
    TypeError: unhashable type: 'dict'

Sadly, a method bound to an unhashable type is unhashable! (Of course, is trivial to use partial or lambda to make any function hashable.)

The offending line in _registry_get is intended to look up named types (or actions), so it perhaps only relevant for string type= and action= values, which could be handled explicitly instead of using dict.get(x, x) to handle both cases. Alternatively, the TypeError could be caught and default returned.
msg222780 - (view) Author: Mark Lawrence (BreamoreBoy) * Date: 2014-07-11 19:17
@Paul can you take a look at this please.
msg222890 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-07-12 22:22
This is a straight forward patch, modifying '_registry_get' to return 'default' if it gets this 'TypeError'.  'test_argparse.py' has a testcase based on jnothman's example.  Temporarily it includes a skipped test that would pass the original code.

The alternative would be to add a note to the documentation to the effect that the 'type' must also be hashable.  But for an edge condition like this, that may be more confusing than enlightening.

While 'dict(one=1, two=20).get' now works as a 'type' callable, it is not ideal.  If there is no match it returns None.  The alternative '.__getitem__' raises a KeyError.  But '_get_values' handles TypeError and ValueErrors, the kinds returned by 'int' and 'float'.  It also handles an ArgumentTypeError, giving a custom type more control over the error message.  Is it worth noting something about the type errors in the documentation?

I've seen users make a different error with the type parameter - 'type=boolean', thinking this means the input should be converted to boolean.  They are thinking of 'type' as a datatype, rather than a function that converts a string into something else.  I don't know of a solution other than explaining that 'boolean()' does not act like 'int()'.

This patch also handles the 'action' case.  However I can't think of a way write a Action subclass that would be unhashable.  It would have to inherit from both Action and dict.
msg303907 - (view) Author: Joel Nothman (jnothman) Date: 2017-10-08 10:43
Clearly this is not in high demand
msg307864 - (view) Author: Michał (krassowski) Date: 2017-12-08 19:23
I've got confused with the "TypeError: unhashable type: 'dict'" error creating a choose-from-dictionary argument in Python 3.6. I used lambda as a workaround, but IMO it would be great to have this patch merged.
msg330863 - (view) Author: Luna Chen (BNMetrics) * Date: 2018-12-01 23:52
Hi bradengroom,
I have reviewed your PR, it's just a small thing! If you could update your PR, it would be amazing!

Thank you!
msg331658 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2018-12-11 22:21
Luna and I talked a bit about this offline and we decided not to merge the PR (nor the original patch, which is the same). Instead Luna will add a note to the docs explaining the caveat.
msg331932 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2018-12-16 18:36
Luna discovered that has actually been fixed in 3.8 (i.e. the master branch), by making {}.get hashable. So I'm closing this as fixed.
History
Date User Action Args
2018-12-16 18:36:05gvanrossumsetstatus: open -> closed
resolution: fixed
messages: + msg331932

stage: patch review -> resolved
2018-12-11 22:21:13gvanrossumsetnosy: + gvanrossum
messages: + msg331658
2018-12-01 23:52:49BNMetricssetnosy: + BNMetrics
messages: + msg330863
2018-10-28 23:19:55bradengroomsetstage: patch review
pull_requests: + pull_request9523
2017-12-08 19:23:12krassowskisetnosy: + krassowski

messages: + msg307864
versions: + Python 3.6
2017-10-08 11:16:11BreamoreBoysetnosy: - BreamoreBoy
2017-10-08 10:43:05jnothmansetmessages: + msg303907
2014-07-12 22:22:35paul.j3setfiles: + patch.diff
keywords: + patch
messages: + msg222890
2014-07-11 19:17:21BreamoreBoysetnosy: + BreamoreBoy, paul.j3

messages: + msg222780
versions: + Python 3.5
2012-11-21 03:28:27jnothmancreate