[issue25299] TypeError: __init__() takes at least 4 arguments (4 given)
New submission from A. Skrobov: Python 2.7.3 (default, Dec 18 2014, 19:10:20) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information.
from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument("--foo", help="foo", action='store_const') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/argparse.py", line 1281, in add_argument action = action_class(**kwargs) TypeError: __init__() takes at least 4 arguments (4 given)
First, the exception message is entirely unhelpful (you wanted at least 4, you got 4, what's your problem?) Second, adding "const=None" to the invocation makes it succeed; but, according to https://docs.python.org/2/library/argparse.html#const this argument defaults to "None", so it shouldn't be necessary to specify it explicitly. Thus, either the documentation or the implementation is wrong. ---------- assignee: docs@python components: Documentation, Extension Modules messages: 252106 nosy: A. Skrobov, docs@python priority: normal severity: normal status: open title: TypeError: __init__() takes at least 4 arguments (4 given) type: behavior versions: Python 2.7 _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
R. David Murray added the comment: Yes, the python2 TypeErrors for mimatched arguments are sub-optimal. This has been fixed in python3:
parser.add_argument('--foo', help="foo", action='store_const') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/rdmurray/python/p36/Lib/argparse.py", line 1336, in add_argument action = action_class(**kwargs) TypeError: __init__() missing 1 required positional argument: 'const'
---------- nosy: +r.david.murray resolution: -> out of date stage: -> resolved status: open -> closed _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
R. David Murray added the comment: Oops, sorry, I missed the fact that you were pointing out a doc issue. ---------- resolution: out of date -> stage: resolved -> needs patch status: closed -> open _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
R. David Murray added the comment: Hmm. After looking at the code, it may also be desirable to improve that error message at the argparse level, since there's a bit of not-immediately-obvious indirection going on there. ---------- _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
A. Skrobov added the comment: Thank you for confirming that the mismatch between the documentation and the behaviour is preserved in Python 3! Adding it to the list of affected versions. ---------- versions: +Python 3.5 _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
R. David Murray added the comment: To clarify this for other readers: the issue here is that the arguments to add_argument are passed through the action routine, which in this case is store_const, and store_const is the one that requires 'const' be specified...which isn't made clear by either the docs or the error message. ---------- _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
paul j3 added the comment: A related issue is http://bugs.python.org/issue24754 argparse add_argument with action="store_true", type=bool should not crash In this case the user specified a parameter that the 'store_true' subclass did not accept. In both cases, 'parser.add_argument' does minimal error checking (or catching), so the user ends up seeing the '__init__' argument error. This line in the 'store_const' documentation is clearly wrong. There's no default. (Note that the const keyword argument defaults to the rather unhelpful None.) Possible fixes to the bigger issue: - major addition to the documentation, documenting allowable subclass parameters (but the subclasses aren't part of the API). - major addition to add_argument that filters parameters before passing them to the subclasses. But can that be done without replicating information that is implicit in the subclass __init__? Can we use advanced inspection? What about user defined Action classes? - minor addition to add_argument that catches the __init__ parameter errors and adds a mollifing explanation. - somehow make Action (and its subclasses) responsible for a nice error message. But how do you override the normal behavior of Python regarding function parameters? ---------- nosy: +paul.j3 _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
R. David Murray added the comment: I think your second to last choice (catch the error) is the only practical solution. It is also probably the best, since we need to support user-written action routines. ---------- _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
paul j3 added the comment: A fix that I am exploring would wrap the Action instantiation call in add_argument with a try/except block That is replace: def add_argument(...) .... action = action_class(**kwargs) ... with .... try: action = action_class(**kwargs) except TypeError: msg = str(_sys.exc_info()[1]) msg = msg.replace('__init__','add_argument') msg = [msg] msg.append('Wrong argument(s) for Action subclass: %s'%action_class.__name__) # action_class is now a class, rather than the input string msg.append('kwargs: %s'%kwargs) sig = getattr(action_class, 'action_sig',None) if sig is not None: msg = sig(msg) raise ValueError(msg) .... This collects information on the error, the action class and arguments, and passes them to a class method of the Action. That customizes the message, and returns it for reraising. In 'class Action' I define: @classmethod def action_sig(cls, msg=[]): # return the signature # subclasses may return custom version import inspect try: name = cls.__name__ sig = inspect.signature(cls.__init__) sig = str(sig) except AttributeError: spec = inspect.getfullargspec(cls.__init__) sig = inspect.formatargspec(*spec) # remove self, option_strings, dest dstr='dest,' ind = sig.find(dstr) if ind>=0: sig = '(...'+sig[(ind+len(dstr)+1):] sig = 'class args: %s'%sig msg.append(sig) return '\n'.join(msg) This adds inspect.signature information from the subclass __init__ method to the message from add_argument. Subclasses (including the user defined ones) could customize this. So a missing 'const' error would display as: ValueError: add_argument() missing 1 required positional argument: 'const' Wrong argument(s) for Action subclass: _StoreConstAction kwargs: {'option_strings': ['--bar'], 'dest': 'bar'} class args: (...const, default=None, required=False, help=None, metavar=None) This may be overkill, but it gives an idea of the information that we can add to the original TypeError. Done right this action_sig() could also serve as a usage function for an Action class. ---------- keywords: +3.5regression _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
paul j3 added the comment: The unittest file test_argparse.py tests add_argument parameters in class TestInvalidArgumentConstructors(TestCase) It looks at a dozen different categories of errors, mostly checking that they return the correct TypeError or ValueError. It does not check the content of error messages. If I change the code I described yesterday to return a TypeError again (but with an enhanced message), it passes this unittest. Most of the test_argparse.py tests just check for error type. There just a couple of late additions that check error message content: TestMessageContentError TestAddArgumentMetavar ---------- _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue25299> _______________________________________
Joannah Nanjekye <nanjekyejoannah@gmail.com> added the comment: paul.j3, The fix looks promising. Do you want to open a PR with this fix? ---------- nosy: +nanjekyejoannah _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue25299> _______________________________________
paul j3 <ajipanca@gmail.com> added the comment: I'm not set up to work with the current development distribution (via github). All my proposed patches are diffs for earlier repos. Paul ---------- _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue25299> _______________________________________
Joannah Nanjekye <nanjekyejoannah@gmail.com> added the comment:
this argument defaults to "None"
Am leaning to the fact that this works as required. The documentation does not just say defaults to none. It says: With the 'store_const' and 'append_const' actions, the const keyword argument must be given. For other actions, it defaults to None. In the scenario, you have given, you are using 'store_const' so the const keyword argument must be given. This should be closed If you agree with what I have just stated. ---------- _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue25299> _______________________________________
A. Skrobov <tyomitch@gmail.com> added the comment: Joannah, I see that under #25314, the docs were updated to match the implementation: https://github.com/python/cpython/commit/b4912b8ed367e540ee060fe912f841cc764... On the other hand, the discussion here (from 2015) and on #25314 (from 2016) includes suggestions for improvements in the implementation as well. ---------- _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue25299> _______________________________________
Joannah Nanjekye <nanjekyejoannah@gmail.com> added the comment: Since both the docs and implementation now match, I suggest we close this and open a new issue suggesting to change the implementation back to make const=None when action='store_const'. ---------- stage: needs patch -> resolved _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue25299> _______________________________________
Joannah Nanjekye <nanjekyejoannah@gmail.com> added the comment: Opened issue37880 to track this change. ---------- _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue25299> _______________________________________
Change by STINNER Victor <vstinner@redhat.com>: ---------- title: TypeError: __init__() takes at least 4 arguments (4 given) -> argparse: TypeError: __init__() takes at least 4 arguments (4 given) _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue25299> _______________________________________
Change by Phil Connell <pconnell@gmail.com>: ---------- nosy: +pconnell _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue25299> _______________________________________
participants (6)
-
A. Skrobov -
Joannah Nanjekye -
paul j3 -
Phil Connell -
R. David Murray -
STINNER Victor