Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
e109c708
Commit
e109c708
authored
Jun 24, 2011
by
Benjamin Peterson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
give the names of missing positional or keyword-only arguments (closes #12356)
parent
947d6b04
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
199 additions
and
94 deletions
+199
-94
Lib/inspect.py
Lib/inspect.py
+29
-12
Lib/test/test_extcall.py
Lib/test/test_extcall.py
+48
-59
Misc/NEWS
Misc/NEWS
+4
-0
Python/ceval.c
Python/ceval.c
+118
-23
No files found.
Lib/inspect.py
View file @
e109c708
...
...
@@ -918,10 +918,24 @@ def formatargvalues(args, varargs, varkw, locals,
specs
.
append
(
formatvarkw
(
varkw
)
+
formatvalue
(
locals
[
varkw
]))
return
'('
+
', '
.
join
(
specs
)
+
')'
def
_positional_error
(
f_name
,
args
,
kwonly
,
varargs
,
defcount
,
given
,
values
):
def
_missing_arguments
(
f_name
,
argnames
,
pos
,
values
):
names
=
[
repr
(
name
)
for
name
in
argnames
if
name
not
in
values
]
missing
=
len
(
names
)
if
missing
==
1
:
s
=
names
[
0
]
elif
missing
==
2
:
s
=
"{} and {}"
.
format
(
*
names
)
else
:
tail
=
", {} and {}"
.
format
(
names
[
-
2
:])
del
names
[
-
2
:]
s
=
", "
.
join
(
names
)
+
tail
raise
TypeError
(
"%s() missing %i required %s argument%s: %s"
%
(
f_name
,
missing
,
"positional"
if
pos
else
"keyword-only"
,
""
if
missing
==
1
else
"s"
,
s
))
def
_too_many
(
f_name
,
args
,
kwonly
,
varargs
,
defcount
,
given
,
values
):
atleast
=
len
(
args
)
-
defcount
if
given
is
None
:
given
=
len
([
arg
for
arg
in
args
if
arg
in
values
])
kwonly_given
=
len
([
arg
for
arg
in
kwonly
if
arg
in
values
])
if
varargs
:
plural
=
atleast
!=
1
...
...
@@ -980,22 +994,25 @@ def getcallargs(func, *positional, **named):
(
f_name
,
kw
))
arg2value
[
kw
]
=
value
if
num_pos
>
num_args
and
not
varargs
:
_
positional_error
(
f_name
,
args
,
kwonlyargs
,
varargs
,
num_defaults
,
num_pos
,
arg2value
)
_
too_many
(
f_name
,
args
,
kwonlyargs
,
varargs
,
num_defaults
,
num_pos
,
arg2value
)
if
num_pos
<
num_args
:
for
arg
in
args
[:
num_args
-
num_defaults
]:
req
=
args
[:
num_args
-
num_defaults
]
for
arg
in
req
:
if
arg
not
in
arg2value
:
_positional_error
(
f_name
,
args
,
kwonlyargs
,
varargs
,
num_defaults
,
None
,
arg2value
)
_missing_arguments
(
f_name
,
req
,
True
,
arg2value
)
for
i
,
arg
in
enumerate
(
args
[
num_args
-
num_defaults
:]):
if
arg
not
in
arg2value
:
arg2value
[
arg
]
=
defaults
[
i
]
missing
=
0
for
kwarg
in
kwonlyargs
:
if
kwarg
not
in
arg2value
:
if
kwarg
not
in
kwonlydefaults
:
raise
TypeError
(
"%s() requires keyword-only argument %r"
%
(
f_name
,
kwarg
))
arg2value
[
kwarg
]
=
kwonlydefaults
[
kwarg
]
if
kwarg
in
kwonlydefaults
:
arg2value
[
kwarg
]
=
kwonlydefaults
[
kwarg
]
else
:
missing
+=
1
if
missing
:
_missing_arguments
(
f_name
,
kwonlyargs
,
False
,
arg2value
)
return
arg2value
# -------------------------------------------------- stack frame extraction
...
...
Lib/test/test_extcall.py
View file @
e109c708
...
...
@@ -66,17 +66,17 @@ Verify clearing of SF bug #733667
>>> g()
Traceback (most recent call last):
...
TypeError: g()
takes at least 1 positional argument but 0 were given
TypeError: g()
missing 1 required positional argument: 'x'
>>> g(*())
Traceback (most recent call last):
...
TypeError: g()
takes at least 1 positional argument but 0 were given
TypeError: g()
missing 1 required positional argument: 'x'
>>> g(*(), **{})
Traceback (most recent call last):
...
TypeError: g()
takes at least 1 positional argument but 0 were given
TypeError: g()
missing 1 required positional argument: 'x'
>>> g(1)
1 () {}
...
...
@@ -263,91 +263,80 @@ the function call setup. See <http://bugs.python.org/issue2016>.
>>> f(**x)
1 2
Some additional tests about positional argument error
s:
Too many argument
s:
>>> def f(a, b):
... pass
>>> f(b=1)
>>> def f(): pass
>>> f(1)
Traceback (most recent call last):
...
TypeError: f() takes 2 positional arguments but 1 was given
>>> def f(a):
... pass
>>> f(6, a=4, *(1, 2, 3))
TypeError: f() takes 0 positional arguments but 1 was given
>>> def f(a): pass
>>> f(1, 2)
Traceback (most recent call last):
...
TypeError: f() got multiple values for argument 'a'
>>> def f(a, *, kw):
... pass
>>> f(6, 4, kw=4)
TypeError: f() takes 1 positional argument but 2 were given
>>> def f(a, b=1): pass
>>> f(1, 2, 3)
Traceback (most recent call last):
...
TypeError: f() takes 1 positional argument but 2 positional arguments (and 1 keyword-only argument) were given
>>> def f(a):
... pass
>>> f()
TypeError: f() takes from 1 to 2 positional arguments but 3 were given
>>> def f(*, kw): pass
>>> f(1, kw=3)
Traceback (most recent call last):
...
TypeError: f() takes 1 positional argument but 0 were given
>>> def f(a, b):
... pass
>>> f(1)
TypeError: f() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given
>>> def f(*, kw, b): pass
>>> f(1, 2, 3, b=3, kw=3)
Traceback (most recent call last):
...
TypeError: f() takes 0 positional arguments but 3 positional arguments (and 2 keyword-only arguments) were given
>>> def f(a, b=2, *, kw): pass
>>> f(2, 3, 4, kw=4)
Traceback (most recent call last):
...
TypeError: f() takes
2 positional arguments but 1 was
given
TypeError: f() takes
from 1 to 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were
given
>>> def f(a, *b):
... pass
Too few and missing arguments:
>>> def f(a): pass
>>> f()
Traceback (most recent call last):
...
TypeError: f() takes at least 1 positional argument but 0 were given
>>> def f(a, *, kw=4):
... pass
>>> f(kw=4)
TypeError: f() missing 1 required positional argument: 'a'
>>> def f(a, b): pass
>>> f()
Traceback (most recent call last):
...
TypeError: f() takes 1 positional argument but 0 positional arguments (and 1 keyword-only argument) were given
>>> def f(a, b=2):
... pass
TypeError: f() missing 2 required positional arguments: 'a' and 'b'
>>> def f(a, b, c): pass
>>> f()
Traceback (most recent call last):
...
TypeError: f() takes from 1 to 2 positional arguments but 0 were given
>>> def f(a, *b):
... pass
TypeError: f() missing 3 required positional arguments: 'a', 'b', and 'c'
>>> def f(a, b, c, d, e): pass
>>> f()
Traceback (most recent call last):
...
TypeError: f() takes at least 1 positional argument but 0 were given
>>> def f(*, kw):
... pass
>>> f(3, kw=4)
TypeError: f() missing 5 required positional arguments: 'a', 'b', 'c', 'd', and 'e'
>>> def f(a, b=4, c=5, d=5): pass
>>> f(c=12, b=9)
Traceback (most recent call last):
...
TypeError: f()
takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given
TypeError: f()
missing 1 required positional argument: 'a'
>>> def f(a, c=3, *b, kw):
... pass
Same with keyword only args:
>>> def f(*, w): pass
>>> f()
Traceback (most recent call last):
...
TypeError: f() takes at least 1 positional argument but 0 were given
>>> f(kw=3)
Traceback (most recent call last):
...
TypeError: f() takes at least 1 positional argument but 0 positional arguments (and 1 keyword-only argument) were given
>>> f(kw=3, c=4)
...
TypeError: f() missing 1 required keyword-only argument: 'w'
>>> def f(*, a, b, c, d, e): pass
>>> f()
Traceback (most recent call last):
...
TypeError: f() takes at least 1 positional argument but 1 positional argument (and 1 keyword-only argument) were given
...
TypeError: f() missing 5 required keyword-only arguments: 'a', 'b', 'c', 'd', and 'e'
"""
import
sys
...
...
Misc/NEWS
View file @
e109c708
...
...
@@ -10,6 +10,10 @@ What's New in Python 3.3 Alpha 1?
Core and Builtins
-----------------
- Issue #12356: When required positional or keyword-only arguments are not
given, produce a informative error message which includes the name(s) of the
missing arguments.
- Issue #12370: Fix super with not arguments when __class__ is overriden in the
class body.
...
...
Python/ceval.c
View file @
e109c708
...
...
@@ -3046,28 +3046,118 @@ exit_eval_frame:
}
static
void
positional_argument_error
(
PyCodeObject
*
co
,
int
given
,
int
defcount
,
PyObject
**
fastlocals
)
format_missing
(
const
char
*
kind
,
PyCodeObject
*
co
,
PyObject
*
names
)
{
int
err
;
Py_ssize_t
len
=
PyList_GET_SIZE
(
names
);
PyObject
*
name_str
,
*
comma
,
*
tail
,
*
tmp
;
assert
(
PyList_CheckExact
(
names
));
assert
(
len
>=
1
);
/* Deal with the joys of natural language. */
switch
(
len
)
{
case
1
:
name_str
=
PyList_GET_ITEM
(
names
,
0
);
Py_INCREF
(
name_str
);
break
;
case
2
:
name_str
=
PyUnicode_FromFormat
(
"%U and %U"
,
PyList_GET_ITEM
(
names
,
len
-
2
),
PyList_GET_ITEM
(
names
,
len
-
1
));
break
;
default:
tail
=
PyUnicode_FromFormat
(
", %U, and %U"
,
PyList_GET_ITEM
(
names
,
len
-
2
),
PyList_GET_ITEM
(
names
,
len
-
1
));
/* Chop off the last two objects in the list. This shouldn't actually
fail, but we can't be too careful. */
err
=
PyList_SetSlice
(
names
,
len
-
2
,
len
,
NULL
);
if
(
err
==
-
1
)
{
Py_DECREF
(
tail
);
return
;
}
/* Stitch everything up into a nice comma-separated list. */
comma
=
PyUnicode_FromString
(
", "
);
if
(
comma
==
NULL
)
{
Py_DECREF
(
tail
);
return
;
}
tmp
=
PyUnicode_Join
(
comma
,
names
);
Py_DECREF
(
comma
);
if
(
tmp
==
NULL
)
{
Py_DECREF
(
tail
);
return
;
}
name_str
=
PyUnicode_Concat
(
tmp
,
tail
);
Py_DECREF
(
tmp
);
Py_DECREF
(
tail
);
break
;
}
if
(
name_str
==
NULL
)
return
;
PyErr_Format
(
PyExc_TypeError
,
"%U() missing %i required %s argument%s: %U"
,
co
->
co_name
,
len
,
kind
,
len
==
1
?
""
:
"s"
,
name_str
);
Py_DECREF
(
name_str
);
}
static
void
missing_arguments
(
PyCodeObject
*
co
,
int
missing
,
int
defcount
,
PyObject
**
fastlocals
)
{
int
i
,
j
=
0
;
int
start
,
end
;
int
positional
=
defcount
!=
-
1
;
const
char
*
kind
=
positional
?
"positional"
:
"keyword-only"
;
PyObject
*
missing_names
;
/* Compute the names of the arguments that are missing. */
missing_names
=
PyList_New
(
missing
);
if
(
missing_names
==
NULL
)
return
;
if
(
positional
)
{
start
=
0
;
end
=
co
->
co_argcount
-
defcount
;
}
else
{
start
=
co
->
co_argcount
;
end
=
start
+
co
->
co_kwonlyargcount
;
}
for
(
i
=
start
;
i
<
end
;
i
++
)
{
if
(
GETLOCAL
(
i
)
==
NULL
)
{
PyObject
*
raw
=
PyTuple_GET_ITEM
(
co
->
co_varnames
,
i
);
PyObject
*
name
=
PyObject_Repr
(
raw
);
if
(
name
==
NULL
)
{
Py_DECREF
(
missing_names
);
return
;
}
PyList_SET_ITEM
(
missing_names
,
j
++
,
name
);
}
}
assert
(
j
==
missing
);
format_missing
(
kind
,
co
,
missing_names
);
Py_DECREF
(
missing_names
);
}
static
void
too_many_positional
(
PyCodeObject
*
co
,
int
given
,
int
defcount
,
PyObject
**
fastlocals
)
{
int
plural
;
int
kwonly_given
=
0
;
int
atleast
=
co
->
co_argcount
-
defcount
;
int
i
;
PyObject
*
sig
,
*
kwonly_sig
;
if
(
given
==
-
1
)
{
given
=
0
;
for
(
i
=
0
;
i
<
co
->
co_argcount
;
i
++
)
if
(
GETLOCAL
(
i
))
given
++
;
}
assert
((
co
->
co_flags
&
CO_VARARGS
)
==
0
);
/* Count missing keyword-only args. */
for
(
i
=
co
->
co_argcount
;
i
<
co
->
co_argcount
+
co
->
co_kwonlyargcount
;
i
++
)
if
(
GETLOCAL
(
i
))
if
(
GETLOCAL
(
i
)
!=
NULL
)
kwonly_given
++
;
if
(
co
->
co_flags
&
CO_VARARGS
)
{
plural
=
atleast
!=
1
;
sig
=
PyUnicode_FromFormat
(
"at least %d"
,
atleast
);
}
else
if
(
defcount
)
{
if
(
defcount
)
{
int
atleast
=
co
->
co_argcount
-
defcount
;
plural
=
1
;
sig
=
PyUnicode_FromFormat
(
"from %d to %d"
,
atleast
,
co
->
co_argcount
);
}
...
...
@@ -3089,6 +3179,7 @@ positional_argument_error(PyCodeObject *co, int given, int defcount, PyObject **
else
{
/* This will not fail. */
kwonly_sig
=
PyUnicode_FromString
(
""
);
assert
(
kwonly_sig
!=
NULL
);
}
PyErr_Format
(
PyExc_TypeError
,
"%U() takes %U positional argument%s but %d%U %s given"
,
...
...
@@ -3217,16 +3308,18 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
SETLOCAL
(
j
,
value
);
}
if
(
argcount
>
co
->
co_argcount
&&
!
(
co
->
co_flags
&
CO_VARARGS
))
{
positional_argument_error
(
co
,
argcount
,
defcount
,
fastlocals
);
too_many_positional
(
co
,
argcount
,
defcount
,
fastlocals
);
goto
fail
;
}
if
(
argcount
<
co
->
co_argcount
)
{
int
m
=
co
->
co_argcount
-
defcount
;
for
(
i
=
argcount
;
i
<
m
;
i
++
)
{
if
(
GETLOCAL
(
i
)
==
NULL
)
{
positional_argument_error
(
co
,
-
1
,
defcount
,
fastlocals
);
goto
fail
;
}
int
missing
=
0
;
for
(
i
=
argcount
;
i
<
m
;
i
++
)
if
(
GETLOCAL
(
i
)
==
NULL
)
missing
++
;
if
(
missing
)
{
missing_arguments
(
co
,
missing
,
defcount
,
fastlocals
);
goto
fail
;
}
if
(
n
>
m
)
i
=
n
-
m
;
...
...
@@ -3241,6 +3334,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
}
}
if
(
co
->
co_kwonlyargcount
>
0
)
{
int
missing
=
0
;
for
(
i
=
co
->
co_argcount
;
i
<
total_args
;
i
++
)
{
PyObject
*
name
;
if
(
GETLOCAL
(
i
)
!=
NULL
)
...
...
@@ -3254,9 +3348,10 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
continue
;
}
}
PyErr_Format
(
PyExc_TypeError
,
"%U() requires keyword-only argument '%S'"
,
co
->
co_name
,
name
);
missing
++
;
}
if
(
missing
)
{
missing_arguments
(
co
,
missing
,
-
1
,
fastlocals
);
goto
fail
;
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment