bpo-37122: Make co->co_argcount represent the total number of positonal arguments in the code object (GH-13726)
This commit is contained in:
parent
059b9ea5ac
commit
cd74e66a8c
|
@ -42,6 +42,8 @@ bound into a function.
|
|||
|
||||
.. versionchanged:: 3.8
|
||||
An extra parameter is required (*posonlyargcount*) to support :PEP:`570`.
|
||||
The first parameter (*argcount*) now represents the total number of positional arguments,
|
||||
including positional-only.
|
||||
|
||||
.. audit-event:: code.__new__ "code filename name argcount kwonlyargcount nlocals stacksize flags"
|
||||
|
||||
|
|
|
@ -907,24 +907,26 @@ Internal types
|
|||
single: co_freevars (code object attribute)
|
||||
|
||||
Special read-only attributes: :attr:`co_name` gives the function name;
|
||||
:attr:`co_argcount` is the number of positional arguments (including arguments
|
||||
with default values); :attr:`co_posonlyargcount` is the number of
|
||||
positional-only arguments (including arguments with default values);
|
||||
:attr:`co_kwonlyargcount` is the number of keyword-only arguments (including
|
||||
arguments with default values); :attr:`co_nlocals` is the number of local
|
||||
variables used by the function (including arguments); :attr:`co_varnames` is a
|
||||
tuple containing the names of the local variables (starting with the argument
|
||||
names); :attr:`co_cellvars` is a tuple containing the names of local variables
|
||||
:attr:`co_argcount` is the total number of positional arguments
|
||||
(including positional-only arguments and arguments with default values);
|
||||
:attr:`co_posonlyargcount` is the number of positional-only arguments
|
||||
(including arguments with default values); :attr:`co_kwonlyargcount` is
|
||||
the number of keyword-only arguments (including arguments with default
|
||||
values); :attr:`co_nlocals` is the number of local variables used by the
|
||||
function (including arguments); :attr:`co_varnames` is a tuple containing
|
||||
the names of the local variables (starting with the argument names);
|
||||
:attr:`co_cellvars` is a tuple containing the names of local variables
|
||||
that are referenced by nested functions; :attr:`co_freevars` is a tuple
|
||||
containing the names of free variables; :attr:`co_code` is a string representing
|
||||
the sequence of bytecode instructions; :attr:`co_consts` is a tuple containing
|
||||
the literals used by the bytecode; :attr:`co_names` is a tuple containing the
|
||||
names used by the bytecode; :attr:`co_filename` is the filename from which the
|
||||
code was compiled; :attr:`co_firstlineno` is the first line number of the
|
||||
function; :attr:`co_lnotab` is a string encoding the mapping from bytecode
|
||||
offsets to line numbers (for details see the source code of the interpreter);
|
||||
:attr:`co_stacksize` is the required stack size (including local variables);
|
||||
:attr:`co_flags` is an integer encoding a number of flags for the interpreter.
|
||||
containing the names of free variables; :attr:`co_code` is a string
|
||||
representing the sequence of bytecode instructions; :attr:`co_consts` is
|
||||
a tuple containing the literals used by the bytecode; :attr:`co_names` is
|
||||
a tuple containing the names used by the bytecode; :attr:`co_filename` is
|
||||
the filename from which the code was compiled; :attr:`co_firstlineno` is
|
||||
the first line number of the function; :attr:`co_lnotab` is a string
|
||||
encoding the mapping from bytecode offsets to line numbers (for details
|
||||
see the source code of the interpreter); :attr:`co_stacksize` is the
|
||||
required stack size (including local variables); :attr:`co_flags` is an
|
||||
integer encoding a number of flags for the interpreter.
|
||||
|
||||
.. index:: object: generator
|
||||
|
||||
|
|
|
@ -1177,8 +1177,10 @@ Changes in the Python API
|
|||
|
||||
* :class:`types.CodeType` has a new parameter in the second position of the
|
||||
constructor (*posonlyargcount*) to support positional-only arguments defined
|
||||
in :pep:`570`. A new ``replace()`` method of :class:`types.CodeType` can be
|
||||
used to make the code future-proof.
|
||||
in :pep:`570`. The first argument (*argcount*) now represents the total
|
||||
number of positional arguments (including positional-only arguments). A new
|
||||
``replace()`` method of :class:`types.CodeType` can be used to make the code
|
||||
future-proof.
|
||||
|
||||
|
||||
Changes in the C API
|
||||
|
|
|
@ -1037,15 +1037,11 @@ def getargs(co):
|
|||
|
||||
names = co.co_varnames
|
||||
nargs = co.co_argcount
|
||||
nposonlyargs = co.co_posonlyargcount
|
||||
nkwargs = co.co_kwonlyargcount
|
||||
nposargs = nargs + nposonlyargs
|
||||
posonlyargs = list(names[:nposonlyargs])
|
||||
args = list(names[nposonlyargs:nposonlyargs+nargs])
|
||||
kwonlyargs = list(names[nposargs:nposargs+nkwargs])
|
||||
args = list(names[:nargs])
|
||||
kwonlyargs = list(names[nargs:nargs+nkwargs])
|
||||
step = 0
|
||||
|
||||
nargs += nposonlyargs
|
||||
nargs += nkwargs
|
||||
varargs = None
|
||||
if co.co_flags & CO_VARARGS:
|
||||
|
@ -1054,7 +1050,7 @@ def getargs(co):
|
|||
varkw = None
|
||||
if co.co_flags & CO_VARKEYWORDS:
|
||||
varkw = co.co_varnames[nargs]
|
||||
return Arguments(posonlyargs + args + kwonlyargs, varargs, varkw)
|
||||
return Arguments(args + kwonlyargs, varargs, varkw)
|
||||
|
||||
ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults')
|
||||
|
||||
|
@ -2136,11 +2132,9 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
|
|||
pos_count = func_code.co_argcount
|
||||
arg_names = func_code.co_varnames
|
||||
posonly_count = func_code.co_posonlyargcount
|
||||
positional_count = posonly_count + pos_count
|
||||
positional_only = tuple(arg_names[:posonly_count])
|
||||
positional = tuple(arg_names[posonly_count:positional_count])
|
||||
positional = arg_names[:pos_count]
|
||||
keyword_only_count = func_code.co_kwonlyargcount
|
||||
keyword_only = arg_names[positional_count:(positional_count + keyword_only_count)]
|
||||
keyword_only = arg_names[pos_count:pos_count + keyword_only_count]
|
||||
annotations = func.__annotations__
|
||||
defaults = func.__defaults__
|
||||
kwdefaults = func.__kwdefaults__
|
||||
|
@ -2152,13 +2146,11 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
|
|||
|
||||
parameters = []
|
||||
|
||||
non_default_count = positional_count - pos_default_count
|
||||
all_positional = positional_only + positional
|
||||
|
||||
non_default_count = pos_count - pos_default_count
|
||||
posonly_left = posonly_count
|
||||
|
||||
# Non-keyword-only parameters w/o defaults.
|
||||
for name in all_positional[:non_default_count]:
|
||||
for name in positional[:non_default_count]:
|
||||
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
|
||||
annotation = annotations.get(name, _empty)
|
||||
parameters.append(Parameter(name, annotation=annotation,
|
||||
|
@ -2167,7 +2159,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
|
|||
posonly_left -= 1
|
||||
|
||||
# ... w/ defaults.
|
||||
for offset, name in enumerate(all_positional[non_default_count:]):
|
||||
for offset, name in enumerate(positional[non_default_count:]):
|
||||
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
|
||||
annotation = annotations.get(name, _empty)
|
||||
parameters.append(Parameter(name, annotation=annotation,
|
||||
|
@ -2178,7 +2170,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
|
|||
|
||||
# *args
|
||||
if func_code.co_flags & CO_VARARGS:
|
||||
name = arg_names[positional_count + keyword_only_count]
|
||||
name = arg_names[pos_count + keyword_only_count]
|
||||
annotation = annotations.get(name, _empty)
|
||||
parameters.append(Parameter(name, annotation=annotation,
|
||||
kind=_VAR_POSITIONAL))
|
||||
|
@ -2195,7 +2187,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
|
|||
default=default))
|
||||
# **kwargs
|
||||
if func_code.co_flags & CO_VARKEYWORDS:
|
||||
index = positional_count + keyword_only_count
|
||||
index = pos_count + keyword_only_count
|
||||
if func_code.co_flags & CO_VARARGS:
|
||||
index += 1
|
||||
|
||||
|
|
|
@ -1132,7 +1132,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
"""
|
||||
co = self.curframe.f_code
|
||||
dict = self.curframe_locals
|
||||
n = co.co_argcount + co.co_posonlyargcount + co.co_kwonlyargcount
|
||||
n = co.co_argcount + co.co_kwonlyargcount
|
||||
if co.co_flags & inspect.CO_VARARGS: n = n+1
|
||||
if co.co_flags & inspect.CO_VARKEYWORDS: n = n+1
|
||||
for i in range(n):
|
||||
|
|
|
@ -112,7 +112,7 @@ consts: ('None',)
|
|||
|
||||
>>> dump(posonly_args.__code__)
|
||||
name: posonly_args
|
||||
argcount: 1
|
||||
argcount: 3
|
||||
posonlyargcount: 2
|
||||
kwonlyargcount: 0
|
||||
names: ()
|
||||
|
|
|
@ -640,7 +640,7 @@ def tricky(a, b, /, x, y, z=True, *args, c, d, e=[], **kwds):
|
|||
code_info_tricky = """\
|
||||
Name: tricky
|
||||
Filename: (.*)
|
||||
Argument count: 3
|
||||
Argument count: 5
|
||||
Positional-only arguments: 2
|
||||
Kw-only arguments: 3
|
||||
Number of locals: 10
|
||||
|
|
|
@ -100,14 +100,14 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
|||
def f(a, b, c, /, d, e=1, *, f, g=2):
|
||||
pass
|
||||
|
||||
self.assertEqual(2, f.__code__.co_argcount) # 2 "standard args"
|
||||
self.assertEqual(5, f.__code__.co_argcount) # 3 posonly + 2 "standard args"
|
||||
self.assertEqual(3, f.__code__.co_posonlyargcount)
|
||||
self.assertEqual((1,), f.__defaults__)
|
||||
|
||||
def f(a, b, c=1, /, d=2, e=3, *, f, g=4):
|
||||
pass
|
||||
|
||||
self.assertEqual(2, f.__code__.co_argcount) # 2 "standard args"
|
||||
self.assertEqual(5, f.__code__.co_argcount) # 3 posonly + 2 "standard args"
|
||||
self.assertEqual(3, f.__code__.co_posonlyargcount)
|
||||
self.assertEqual((1, 2, 3), f.__defaults__)
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Make the *co_argcount* attribute of code objects represent the total number
|
||||
of positional arguments (including positional-only arguments). The value of
|
||||
*co_posonlyargcount* can be used to distinguish which arguments are
|
||||
positional only, and the difference (*co_argcount* - *co_posonlyargcount*)
|
||||
is the number of positional-or-keyword arguments. Patch by Pablo Galindo.
|
|
@ -308,11 +308,11 @@ _PyFunction_FastCallDict(PyObject *func, PyObject *const *args, Py_ssize_t nargs
|
|||
(co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
|
||||
{
|
||||
/* Fast paths */
|
||||
if (argdefs == NULL && co->co_argcount + co->co_posonlyargcount == nargs) {
|
||||
if (argdefs == NULL && co->co_argcount == nargs) {
|
||||
return function_code_fastcall(co, args, nargs, globals);
|
||||
}
|
||||
else if (nargs == 0 && argdefs != NULL
|
||||
&& co->co_argcount + co->co_posonlyargcount == PyTuple_GET_SIZE(argdefs)) {
|
||||
&& co->co_argcount == PyTuple_GET_SIZE(argdefs)) {
|
||||
/* function called with no arguments, but all parameters have
|
||||
a default value: use default values as arguments .*/
|
||||
args = _PyTuple_ITEMS(argdefs);
|
||||
|
@ -396,11 +396,11 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
|
|||
if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
|
||||
(co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
|
||||
{
|
||||
if (argdefs == NULL && co->co_argcount + co->co_posonlyargcount== nargs) {
|
||||
if (argdefs == NULL && co->co_argcount == nargs) {
|
||||
return function_code_fastcall(co, stack, nargs, globals);
|
||||
}
|
||||
else if (nargs == 0 && argdefs != NULL
|
||||
&& co->co_argcount + co->co_posonlyargcount == PyTuple_GET_SIZE(argdefs)) {
|
||||
&& co->co_argcount == PyTuple_GET_SIZE(argdefs)) {
|
||||
/* function called with no arguments, but all parameters have
|
||||
a default value: use default values as arguments .*/
|
||||
stack = _PyTuple_ITEMS(argdefs);
|
||||
|
|
|
@ -114,8 +114,9 @@ PyCode_New(int argcount, int posonlyargcount, int kwonlyargcount,
|
|||
Py_ssize_t i, n_cellvars, n_varnames, total_args;
|
||||
|
||||
/* Check argument types */
|
||||
if (argcount < 0 || posonlyargcount < 0 || kwonlyargcount < 0 ||
|
||||
nlocals < 0 || stacksize < 0 || flags < 0 ||
|
||||
if (argcount < posonlyargcount || posonlyargcount < 0 ||
|
||||
kwonlyargcount < 0 || nlocals < 0 ||
|
||||
stacksize < 0 || flags < 0 ||
|
||||
code == NULL || !PyBytes_Check(code) ||
|
||||
consts == NULL || !PyTuple_Check(consts) ||
|
||||
names == NULL || !PyTuple_Check(names) ||
|
||||
|
@ -152,11 +153,9 @@ PyCode_New(int argcount, int posonlyargcount, int kwonlyargcount,
|
|||
}
|
||||
|
||||
n_varnames = PyTuple_GET_SIZE(varnames);
|
||||
if (posonlyargcount + argcount <= n_varnames
|
||||
&& kwonlyargcount <= n_varnames) {
|
||||
if (argcount <= n_varnames && kwonlyargcount <= n_varnames) {
|
||||
/* Never overflows. */
|
||||
total_args = (Py_ssize_t)posonlyargcount + (Py_ssize_t)argcount
|
||||
+ (Py_ssize_t)kwonlyargcount +
|
||||
total_args = (Py_ssize_t)argcount + (Py_ssize_t)kwonlyargcount +
|
||||
((flags & CO_VARARGS) != 0) + ((flags & CO_VARKEYWORDS) != 0);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -7807,7 +7807,7 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
"super(): no code object");
|
||||
return -1;
|
||||
}
|
||||
if (co->co_posonlyargcount + co->co_argcount == 0) {
|
||||
if (co->co_argcount == 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"super(): no arguments");
|
||||
return -1;
|
||||
|
|
|
@ -3757,10 +3757,10 @@ missing_arguments(PyThreadState *tstate, PyCodeObject *co,
|
|||
return;
|
||||
if (positional) {
|
||||
start = 0;
|
||||
end = co->co_posonlyargcount + co->co_argcount - defcount;
|
||||
end = co->co_argcount - defcount;
|
||||
}
|
||||
else {
|
||||
start = co->co_posonlyargcount + co->co_argcount;
|
||||
start = co->co_argcount;
|
||||
end = start + co->co_kwonlyargcount;
|
||||
}
|
||||
for (i = start; i < end; i++) {
|
||||
|
@ -3788,25 +3788,23 @@ too_many_positional(PyThreadState *tstate, PyCodeObject *co,
|
|||
Py_ssize_t kwonly_given = 0;
|
||||
Py_ssize_t i;
|
||||
PyObject *sig, *kwonly_sig;
|
||||
Py_ssize_t co_posonlyargcount = co->co_posonlyargcount;
|
||||
Py_ssize_t co_argcount = co->co_argcount;
|
||||
Py_ssize_t total_positional = co_argcount + co_posonlyargcount;
|
||||
|
||||
assert((co->co_flags & CO_VARARGS) == 0);
|
||||
/* Count missing keyword-only args. */
|
||||
for (i = total_positional; i < total_positional + co->co_kwonlyargcount; i++) {
|
||||
for (i = co_argcount; i < co_argcount + co->co_kwonlyargcount; i++) {
|
||||
if (GETLOCAL(i) != NULL) {
|
||||
kwonly_given++;
|
||||
}
|
||||
}
|
||||
if (defcount) {
|
||||
Py_ssize_t atleast = total_positional - defcount;
|
||||
Py_ssize_t atleast = co_argcount - defcount;
|
||||
plural = 1;
|
||||
sig = PyUnicode_FromFormat("from %zd to %zd", atleast, total_positional);
|
||||
sig = PyUnicode_FromFormat("from %zd to %zd", atleast, co_argcount);
|
||||
}
|
||||
else {
|
||||
plural = (total_positional != 1);
|
||||
sig = PyUnicode_FromFormat("%zd", total_positional);
|
||||
plural = (co_argcount != 1);
|
||||
sig = PyUnicode_FromFormat("%zd", co_argcount);
|
||||
}
|
||||
if (sig == NULL)
|
||||
return;
|
||||
|
@ -3917,7 +3915,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
|||
PyObject *retval = NULL;
|
||||
PyObject **fastlocals, **freevars;
|
||||
PyObject *x, *u;
|
||||
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount + co->co_posonlyargcount;
|
||||
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
|
||||
Py_ssize_t i, j, n;
|
||||
PyObject *kwdict;
|
||||
|
||||
|
@ -3953,9 +3951,9 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
|||
kwdict = NULL;
|
||||
}
|
||||
|
||||
/* Copy positional only arguments into local variables */
|
||||
if (argcount > co->co_argcount + co->co_posonlyargcount) {
|
||||
n = co->co_posonlyargcount;
|
||||
/* Copy all positional arguments into local variables */
|
||||
if (argcount > co->co_argcount) {
|
||||
n = co->co_argcount;
|
||||
}
|
||||
else {
|
||||
n = argcount;
|
||||
|
@ -3966,20 +3964,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
|||
SETLOCAL(j, x);
|
||||
}
|
||||
|
||||
|
||||
/* Copy positional arguments into local variables */
|
||||
if (argcount > co->co_argcount + co->co_posonlyargcount) {
|
||||
n += co->co_argcount;
|
||||
}
|
||||
else {
|
||||
n = argcount;
|
||||
}
|
||||
for (i = j; i < n; i++) {
|
||||
x = args[i];
|
||||
Py_INCREF(x);
|
||||
SETLOCAL(i, x);
|
||||
}
|
||||
|
||||
/* Pack other positional arguments into the *args argument */
|
||||
if (co->co_flags & CO_VARARGS) {
|
||||
u = _PyTuple_FromArray(args + n, argcount - n);
|
||||
|
@ -4059,14 +4043,14 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
|||
}
|
||||
|
||||
/* Check the number of positional arguments */
|
||||
if ((argcount > co->co_argcount + co->co_posonlyargcount) && !(co->co_flags & CO_VARARGS)) {
|
||||
if ((argcount > co->co_argcount) && !(co->co_flags & CO_VARARGS)) {
|
||||
too_many_positional(tstate, co, argcount, defcount, fastlocals);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Add missing positional arguments (copy default values from defs) */
|
||||
if (argcount < co->co_posonlyargcount + co->co_argcount) {
|
||||
Py_ssize_t m = co->co_posonlyargcount + co->co_argcount - defcount;
|
||||
if (argcount < co->co_argcount) {
|
||||
Py_ssize_t m = co->co_argcount - defcount;
|
||||
Py_ssize_t missing = 0;
|
||||
for (i = argcount; i < m; i++) {
|
||||
if (GETLOCAL(i) == NULL) {
|
||||
|
@ -4093,7 +4077,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
|||
/* Add missing keyword arguments (copy default values from kwdefs) */
|
||||
if (co->co_kwonlyargcount > 0) {
|
||||
Py_ssize_t missing = 0;
|
||||
for (i = co->co_posonlyargcount + co->co_argcount; i < total_args; i++) {
|
||||
for (i = co->co_argcount; i < total_args; i++) {
|
||||
PyObject *name;
|
||||
if (GETLOCAL(i) != NULL)
|
||||
continue;
|
||||
|
|
|
@ -5764,7 +5764,7 @@ makecode(struct compiler *c, struct assembler *a)
|
|||
Py_ssize_t nlocals;
|
||||
int nlocals_int;
|
||||
int flags;
|
||||
int argcount, posonlyargcount, kwonlyargcount, maxdepth;
|
||||
int posorkeywordargcount, posonlyargcount, kwonlyargcount, maxdepth;
|
||||
|
||||
consts = consts_dict_keys_inorder(c->u->u_consts);
|
||||
names = dict_keys_inorder(c->u->u_names, 0);
|
||||
|
@ -5808,15 +5808,15 @@ makecode(struct compiler *c, struct assembler *a)
|
|||
goto error;
|
||||
}
|
||||
|
||||
argcount = Py_SAFE_DOWNCAST(c->u->u_argcount, Py_ssize_t, int);
|
||||
posonlyargcount = Py_SAFE_DOWNCAST(c->u->u_posonlyargcount, Py_ssize_t, int);
|
||||
posorkeywordargcount = Py_SAFE_DOWNCAST(c->u->u_argcount, Py_ssize_t, int);
|
||||
kwonlyargcount = Py_SAFE_DOWNCAST(c->u->u_kwonlyargcount, Py_ssize_t, int);
|
||||
maxdepth = stackdepth(c);
|
||||
if (maxdepth < 0) {
|
||||
goto error;
|
||||
}
|
||||
co = PyCode_New(argcount, posonlyargcount, kwonlyargcount,
|
||||
nlocals_int, maxdepth, flags,
|
||||
co = PyCode_New(posonlyargcount+posorkeywordargcount, posonlyargcount,
|
||||
kwonlyargcount, nlocals_int, maxdepth, flags,
|
||||
bytecode, consts, names, varnames,
|
||||
freevars, cellvars,
|
||||
c->c_filename, c->u->u_name,
|
||||
|
|
Loading…
Reference in New Issue