bpo-36540: PEP 570 -- Implementation (GH-12701)

This commit contains the implementation of PEP570: Python positional-only parameters.

* Update Grammar/Grammar with new typedarglist and varargslist

* Regenerate grammar files

* Update and regenerate AST related files

* Update code object

* Update marshal.c

* Update compiler and symtable

* Regenerate importlib files

* Update callable objects

* Implement positional-only args logic in ceval.c

* Regenerate frozen data

* Update standard library to account for positional-only args

* Add test file for positional-only args

* Update other test files to account for positional-only args

* Add News entry

* Update inspect module and related tests
This commit is contained in:
Pablo Galindo 2019-04-29 13:36:57 +01:00 committed by GitHub
parent 99fcc616d4
commit 8c77b8cb91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 5771 additions and 4710 deletions

View file

@ -22,13 +22,55 @@ async_funcdef: ASYNC funcdef
funcdef: 'def' NAME parameters ['->' test] ':' [TYPE_COMMENT] func_body_suite
parameters: '(' [typedargslist] ')'
typedargslist: (tfpdef ['=' test] (',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] [
# The following definition for typedarglist is equivalent to this set of rules:
#
# arguments = argument (',' [TYPE_COMMENT] argument)*
# argument = tfpdef ['=' test]
# kwargs = '**' tfpdef [','] [TYPE_COMMENT]
# args = '*' [tfpdef]
# kwonly_kwargs = (',' [TYPE_COMMENT] argument)* (TYPE_COMMENT | [',' [TYPE_COMMENT] [kwargs]])
# args_kwonly_kwargs = args kwonly_kwargs | kwargs
# poskeyword_args_kwonly_kwargs = arguments ( TYPE_COMMENT | [',' [TYPE_COMMENT] [args_kwonly_kwargs]])
# typedargslist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs
# typedarglist = (arguments ',' [TYPE_COMMENT] '/' [',' [[TYPE_COMMENT] typedargslist_no_posonly]])|(typedargslist_no_posonly)"
#
# It needs to be fully expanded to allow our LL(1) parser to work on it.
typedargslist: (
(tfpdef ['=' test] (',' [TYPE_COMMENT] tfpdef ['=' test])* ',' [TYPE_COMMENT] '/' [',' [ [TYPE_COMMENT] tfpdef ['=' test] (
',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] [
'*' [tfpdef] (',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] ['**' tfpdef [','] [TYPE_COMMENT]]])
| '**' tfpdef [','] [TYPE_COMMENT]]])
| '*' [tfpdef] (',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] ['**' tfpdef [','] [TYPE_COMMENT]]])
| '**' tfpdef [','] [TYPE_COMMENT]]] )
| (tfpdef ['=' test] (',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] [
'*' [tfpdef] (',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] ['**' tfpdef [','] [TYPE_COMMENT]]])
| '**' tfpdef [','] [TYPE_COMMENT]]])
| '*' [tfpdef] (',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] ['**' tfpdef [','] [TYPE_COMMENT]]])
| '**' tfpdef [','] [TYPE_COMMENT])
)
tfpdef: NAME [':' test]
varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
# The following definition for varargslist is equivalent to this set of rules:
#
# arguments = argument (',' argument )*
# argument = vfpdef ['=' test]
# kwargs = '**' vfpdef [',']
# args = '*' [vfpdef]
# kwonly_kwargs = (',' argument )* [',' [kwargs]]
# args_kwonly_kwargs = args kwonly_kwargs | kwargs
# poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]]
# vararglist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs
# varargslist = arguments ',' '/' [','[(vararglist_no_posonly)]] | (vararglist_no_posonly)
#
# It needs to be fully expanded to allow our LL(1) parser to work on it.
varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
'*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
| '**' vfpdef [',']]]
| '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
| '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
'*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
| '**' vfpdef [',']]]
| '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]

10
Include/Python-ast.h generated
View file

@ -427,6 +427,7 @@ struct _excepthandler {
struct _arguments {
asdl_seq *args;
asdl_seq *posonlyargs;
arg_ty vararg;
asdl_seq *kwonlyargs;
asdl_seq *kw_defaults;
@ -684,10 +685,11 @@ excepthandler_ty _Py_ExceptHandler(expr_ty type, identifier name, asdl_seq *
body, int lineno, int col_offset, int
end_lineno, int end_col_offset, PyArena
*arena);
#define arguments(a0, a1, a2, a3, a4, a5, a6) _Py_arguments(a0, a1, a2, a3, a4, a5, a6)
arguments_ty _Py_arguments(asdl_seq * args, arg_ty vararg, asdl_seq *
kwonlyargs, asdl_seq * kw_defaults, arg_ty kwarg,
asdl_seq * defaults, PyArena *arena);
#define arguments(a0, a1, a2, a3, a4, a5, a6, a7) _Py_arguments(a0, a1, a2, a3, a4, a5, a6, a7)
arguments_ty _Py_arguments(asdl_seq * args, asdl_seq * posonlyargs, arg_ty
vararg, asdl_seq * kwonlyargs, asdl_seq *
kw_defaults, arg_ty kwarg, asdl_seq * defaults,
PyArena *arena);
#define arg(a0, a1, a2, a3, a4, a5, a6, a7) _Py_arg(a0, a1, a2, a3, a4, a5, a6, a7)
arg_ty _Py_arg(identifier arg, expr_ty annotation, string type_comment, int
lineno, int col_offset, int end_lineno, int end_col_offset,

View file

@ -21,6 +21,7 @@ typedef uint16_t _Py_CODEUNIT;
typedef struct {
PyObject_HEAD
int co_argcount; /* #arguments, except *args */
int co_posonlyargcount; /* #positional only arguments */
int co_kwonlyargcount; /* #keyword only arguments */
int co_nlocals; /* #local variables */
int co_stacksize; /* #entries needed for evaluation stack */
@ -102,7 +103,7 @@ PyAPI_DATA(PyTypeObject) PyCode_Type;
/* Public interface */
PyAPI_FUNC(PyCodeObject *) PyCode_New(
int, int, int, int, int, PyObject *, PyObject *,
int, int, int, int, int, int, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *, int, PyObject *);
/* same as struct above */

View file

@ -80,9 +80,9 @@ class struct_frozen(Structure):
continue
items.append((entry.name.decode("ascii"), entry.size))
expected = [("__hello__", 139),
("__phello__", -139),
("__phello__.spam", 139),
expected = [("__hello__", 141),
("__phello__", -141),
("__phello__.spam", 141),
]
self.assertEqual(items, expected, "PyImport_FrozenModules example "
"in Doc/library/ctypes.rst may be out of date")

View file

@ -157,6 +157,7 @@ def _format_code_info(co):
lines.append("Name: %s" % co.co_name)
lines.append("Filename: %s" % co.co_filename)
lines.append("Argument count: %s" % co.co_argcount)
lines.append("Positional-only arguments: %s" % co.co_posonlyargcount)
lines.append("Kw-only arguments: %s" % co.co_kwonlyargcount)
lines.append("Number of locals: %s" % co.co_nlocals)
lines.append("Stack size: %s" % co.co_stacksize)

View file

@ -265,6 +265,7 @@ def _write_atomic(path, data, mode=0o666):
# this might affected the first line number #32911)
# Python 3.8a1 3400 (move frame block handling to compiler #17611)
# Python 3.8a1 3401 (add END_ASYNC_FOR #33041)
# Python 3.8a1 3410 (PEP570 Python Positional-Only Parameters #36540)
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually
@ -273,7 +274,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
MAGIC_NUMBER = (3401).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3410).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__'

View file

@ -272,6 +272,7 @@ def iscode(object):
| 16=nested | 32=generator | 64=nofree | 128=coroutine
| 256=iterable_coroutine | 512=async_generator
co_freevars tuple of names of free variables
co_posonlyargcount number of positional only arguments
co_kwonlyargcount number of keyword only arguments (not including ** arg)
co_lnotab encoded mapping of line numbers to bytecode indices
co_name name with which this code object was defined
@ -1031,26 +1032,20 @@ def getargs(co):
'args' is the list of argument names. Keyword-only arguments are
appended. 'varargs' and 'varkw' are the names of the * and **
arguments or None."""
args, varargs, kwonlyargs, varkw = _getfullargs(co)
return Arguments(args + kwonlyargs, varargs, varkw)
def _getfullargs(co):
"""Get information about the arguments accepted by a code object.
Four things are returned: (args, varargs, kwonlyargs, varkw), where
'args' and 'kwonlyargs' are lists of argument names, and 'varargs'
and 'varkw' are the names of the * and ** arguments or None."""
if not iscode(co):
raise TypeError('{!r} is not a code object'.format(co))
nargs = co.co_argcount
names = co.co_varnames
nargs = co.co_argcount
nposonlyargs = co.co_posonlyargcount
nkwargs = co.co_kwonlyargcount
args = list(names[:nargs])
kwonlyargs = list(names[nargs:nargs+nkwargs])
nposargs = nargs + nposonlyargs
posonlyargs = list(names[:nposonlyargs])
args = list(names[nposonlyargs:nposonlyargs+nargs])
kwonlyargs = list(names[nposargs:nposargs+nkwargs])
step = 0
nargs += nposonlyargs
nargs += nkwargs
varargs = None
if co.co_flags & CO_VARARGS:
@ -1059,8 +1054,7 @@ def _getfullargs(co):
varkw = None
if co.co_flags & CO_VARKEYWORDS:
varkw = co.co_varnames[nargs]
return args, varargs, kwonlyargs, varkw
return Arguments(posonlyargs + args + kwonlyargs, varargs, varkw)
ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults')
@ -1087,15 +1081,16 @@ def getargspec(func):
warnings.warn("inspect.getargspec() is deprecated since Python 3.0, "
"use inspect.signature() or inspect.getfullargspec()",
DeprecationWarning, stacklevel=2)
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
getfullargspec(func)
if kwonlyargs or ann:
raise ValueError("Function has keyword-only parameters or annotations"
", use getfullargspec() API which can support them")
args, varargs, varkw, defaults, posonlyargs, kwonlyargs, \
kwonlydefaults, ann = getfullargspec(func)
if posonlyargs or kwonlyargs or ann:
raise ValueError("Function has positional-only, keyword-only parameters"
" or annotations, use getfullargspec() API which can"
" support them")
return ArgSpec(args, varargs, varkw, defaults)
FullArgSpec = namedtuple('FullArgSpec',
'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations')
'args, varargs, varkw, defaults, posonlyargs, kwonlyargs, kwonlydefaults, annotations')
def getfullargspec(func):
"""Get the names and default values of a callable object's parameters.
@ -1145,6 +1140,7 @@ def getfullargspec(func):
args = []
varargs = None
varkw = None
posonlyargs = []
kwonlyargs = []
defaults = ()
annotations = {}
@ -1159,7 +1155,9 @@ def getfullargspec(func):
name = param.name
if kind is _POSITIONAL_ONLY:
args.append(name)
posonlyargs.append(name)
if param.default is not param.empty:
defaults += (param.default,)
elif kind is _POSITIONAL_OR_KEYWORD:
args.append(name)
if param.default is not param.empty:
@ -1185,7 +1183,7 @@ def getfullargspec(func):
defaults = None
return FullArgSpec(args, varargs, varkw, defaults,
kwonlyargs, kwdefaults, annotations)
posonlyargs, kwonlyargs, kwdefaults, annotations)
ArgInfo = namedtuple('ArgInfo', 'args varargs keywords locals')
@ -1216,7 +1214,8 @@ def _formatannotation(annotation):
return _formatannotation
def formatargspec(args, varargs=None, varkw=None, defaults=None,
kwonlyargs=(), kwonlydefaults={}, annotations={},
posonlyargs=(), kwonlyargs=(), kwonlydefaults={},
annotations={},
formatarg=str,
formatvarargs=lambda name: '*' + name,
formatvarkw=lambda name: '**' + name,
@ -1249,12 +1248,17 @@ def formatargandannotation(arg):
return result
specs = []
if defaults:
firstdefault = len(args) - len(defaults)
for i, arg in enumerate(args):
firstdefault = len(posonlyargs) + len(args) - len(defaults)
posonly_left = len(posonlyargs)
for i, arg in enumerate([*posonlyargs, *args]):
spec = formatargandannotation(arg)
if defaults and i >= firstdefault:
spec = spec + formatvalue(defaults[i - firstdefault])
specs.append(spec)
posonly_left -= 1
if posonlyargs and posonly_left == 0:
specs.append('/')
if varargs is not None:
specs.append(formatvarargs(formatargandannotation(varargs)))
else:
@ -1342,7 +1346,8 @@ def getcallargs(*func_and_positional, **named):
func = func_and_positional[0]
positional = func_and_positional[1:]
spec = getfullargspec(func)
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = spec
(args, varargs, varkw, defaults, posonlyargs,
kwonlyargs, kwonlydefaults, ann) = spec
f_name = func.__name__
arg2value = {}
@ -1351,12 +1356,16 @@ def getcallargs(*func_and_positional, **named):
# implicit 'self' (or 'cls' for classmethods) argument
positional = (func.__self__,) + positional
num_pos = len(positional)
num_posonlyargs = len(posonlyargs)
num_args = len(args)
num_defaults = len(defaults) if defaults else 0
n = min(num_pos, num_posonlyargs)
for i in range(num_posonlyargs):
arg2value[posonlyargs[i]] = positional[i]
n = min(num_pos, num_args)
for i in range(n):
arg2value[args[i]] = positional[i]
arg2value[args[i]] = positional[num_posonlyargs+i]
if varargs:
arg2value[varargs] = tuple(positional[n:])
possible_kwargs = set(args + kwonlyargs)
@ -2137,9 +2146,12 @@ def _signature_from_function(cls, func):
func_code = func.__code__
pos_count = func_code.co_argcount
arg_names = func_code.co_varnames
positional = tuple(arg_names[:pos_count])
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])
keyword_only_count = func_code.co_kwonlyargcount
keyword_only = arg_names[pos_count:(pos_count + keyword_only_count)]
keyword_only = arg_names[positional_count:(positional_count + keyword_only_count)]
annotations = func.__annotations__
defaults = func.__defaults__
kwdefaults = func.__kwdefaults__
@ -2151,23 +2163,33 @@ def _signature_from_function(cls, func):
parameters = []
non_default_count = positional_count - pos_default_count
all_positional = positional_only + positional
posonly_left = posonly_count
# Non-keyword-only parameters w/o defaults.
non_default_count = pos_count - pos_default_count
for name in positional[:non_default_count]:
for name in all_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,
kind=_POSITIONAL_OR_KEYWORD))
kind=kind))
if posonly_left:
posonly_left -= 1
# ... w/ defaults.
for offset, name in enumerate(positional[non_default_count:]):
for offset, name in enumerate(all_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,
kind=_POSITIONAL_OR_KEYWORD,
kind=kind,
default=defaults[offset]))
if posonly_left:
posonly_left -= 1
# *args
if func_code.co_flags & CO_VARARGS:
name = arg_names[pos_count + keyword_only_count]
name = arg_names[positional_count + keyword_only_count]
annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
kind=_VAR_POSITIONAL))
@ -2184,7 +2206,7 @@ def _signature_from_function(cls, func):
default=default))
# **kwargs
if func_code.co_flags & CO_VARKEYWORDS:
index = pos_count + keyword_only_count
index = positional_count + keyword_only_count
if func_code.co_flags & CO_VARARGS:
index += 1

View file

@ -619,8 +619,9 @@ def replace_paths_in_code(self, co):
if isinstance(consts[i], type(co)):
consts[i] = self.replace_paths_in_code(consts[i])
return types.CodeType(co.co_argcount, co.co_kwonlyargcount,
co.co_nlocals, co.co_stacksize, co.co_flags,
return types.CodeType(co.co_argcount, co.co_posonlyargcount,
co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize, co.co_flags,
co.co_code, tuple(consts), co.co_names,
co.co_varnames, new_filename, co.co_name,
co.co_firstlineno, co.co_lnotab, co.co_freevars,

View file

@ -5,7 +5,7 @@
# line 5
# line 7
def spam(a, b, c, d=3, e=4, f=5, *g, **h):
def spam(a, /, b, c, d=3, e=4, f=5, *g, **h):
eggs(b + d, c + f)
# line 11

View file

@ -137,3 +137,19 @@ def func136():
def func137():
never_reached1
never_reached2
#line 141
def positional_only_arg(a, /):
pass
#line 145
def all_markers(a, b, /, c, d, *, e, f):
pass
# line 149
def all_markers_with_args_and_kwargs(a, b, /, c, d, *args, e, f, **kwargs):
pass
#line 153
def all_markers_with_defaults(a, b=1, /, c=2, d=3, *, e=4, f=5):
pass

View file

@ -319,14 +319,14 @@ def test_field_attr_existence(self):
def test_arguments(self):
x = ast.arguments()
self.assertEqual(x._fields, ('args', 'vararg', 'kwonlyargs',
self.assertEqual(x._fields, ('args', 'posonlyargs', 'vararg', 'kwonlyargs',
'kw_defaults', 'kwarg', 'defaults'))
with self.assertRaises(AttributeError):
x.vararg
x = ast.arguments(*range(1, 7))
self.assertEqual(x.vararg, 2)
x = ast.arguments(*range(1, 8))
self.assertEqual(x.vararg, 3)
def test_field_attr_writable(self):
x = ast.Num()
@ -816,22 +816,25 @@ def test_module(self):
self.mod(m, "must have Load context", "eval")
def _check_arguments(self, fac, check):
def arguments(args=None, vararg=None,
def arguments(args=None, posonlyargs=None, vararg=None,
kwonlyargs=None, kwarg=None,
defaults=None, kw_defaults=None):
if args is None:
args = []
if posonlyargs is None:
posonlyargs = []
if kwonlyargs is None:
kwonlyargs = []
if defaults is None:
defaults = []
if kw_defaults is None:
kw_defaults = []
args = ast.arguments(args, vararg, kwonlyargs, kw_defaults,
kwarg, defaults)
args = ast.arguments(args, posonlyargs, vararg, kwonlyargs,
kw_defaults, kwarg, defaults)
return fac(args)
args = [ast.arg("x", ast.Name("x", ast.Store()))]
check(arguments(args=args), "must have Load context")
check(arguments(posonlyargs=args), "must have Load context")
check(arguments(kwonlyargs=args), "must have Load context")
check(arguments(defaults=[ast.Num(3)]),
"more positional defaults than args")
@ -847,7 +850,7 @@ def arguments(args=None, vararg=None,
"must have Load context")
def test_funcdef(self):
a = ast.arguments([], None, [], [], None, [])
a = ast.arguments([], [], None, [], [], None, [])
f = ast.FunctionDef("x", a, [], [], None)
self.stmt(f, "empty body on FunctionDef")
f = ast.FunctionDef("x", a, [ast.Pass()], [ast.Name("x", ast.Store())],
@ -1013,7 +1016,7 @@ def test_unaryop(self):
self.expr(u, "must have Load context")
def test_lambda(self):
a = ast.arguments([], None, [], [], None, [])
a = ast.arguments([], [], None, [], [], None, [])
self.expr(ast.Lambda(a, ast.Name("x", ast.Store())),
"must have Load context")
def fac(args):
@ -1636,17 +1639,17 @@ def main():
exec_results = [
('Module', [('Expr', (1, 0), ('Constant', (1, 0), None, None))], []),
('Module', [('Expr', (1, 0), ('Constant', (1, 0), 'module docstring', None))], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Pass', (1, 9))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (1, 9), ('Constant', (1, 9), 'function docstring', None))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 10))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], None, [], [], None, [('Constant', (1, 8), 0, None)]), [('Pass', (1, 12))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], ('arg', (1, 7), 'args', None, None), [], [], None, []), [('Pass', (1, 14))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], ('arg', (1, 8), 'kwargs', None, None), []), [('Pass', (1, 17))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None), ('arg', (1, 9), 'b', None, None), ('arg', (1, 14), 'c', None, None), ('arg', (1, 22), 'd', None, None), ('arg', (1, 28), 'e', None, None)], ('arg', (1, 35), 'args', None, None), [('arg', (1, 41), 'f', None, None)], [('Constant', (1, 43), 42, None)], ('arg', (1, 49), 'kwargs', None, None), [('Constant', (1, 11), 1, None), ('Constant', (1, 16), None, None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Expr', (1, 58), ('Constant', (1, 58), 'doc for f()', None))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 9))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9), ('Constant', (1, 9), 'function docstring', None))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 10))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8), 0, None)]), [('Pass', (1, 12))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], ('arg', (1, 7), 'args', None, None), [], [], None, []), [('Pass', (1, 14))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], ('arg', (1, 8), 'kwargs', None, None), []), [('Pass', (1, 17))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None), ('arg', (1, 9), 'b', None, None), ('arg', (1, 14), 'c', None, None), ('arg', (1, 22), 'd', None, None), ('arg', (1, 28), 'e', None, None)], [], ('arg', (1, 35), 'args', None, None), [('arg', (1, 41), 'f', None, None)], [('Constant', (1, 43), 42, None)], ('arg', (1, 49), 'kwargs', None, None), [('Constant', (1, 11), 1, None), ('Constant', (1, 16), None, None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Expr', (1, 58), ('Constant', (1, 58), 'doc for f()', None))], [], None, None)], []),
('Module', [('ClassDef', (1, 0), 'C', [], [], [('Pass', (1, 8))], [])], []),
('Module', [('ClassDef', (1, 0), 'C', [], [], [('Expr', (1, 9), ('Constant', (1, 9), 'docstring for class C', None))], [])], []),
('Module', [('ClassDef', (1, 0), 'C', [('Name', (1, 8), 'object', ('Load',))], [], [('Pass', (1, 17))], [])], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Return', (1, 8), ('Constant', (1, 15), 1, None))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Return', (1, 8), ('Constant', (1, 15), 1, None))], [], None, None)], []),
('Module', [('Delete', (1, 0), [('Name', (1, 4), 'v', ('Del',))])], []),
('Module', [('Assign', (1, 0), [('Name', (1, 0), 'v', ('Store',))], ('Constant', (1, 4), 1, None), None)], []),
('Module', [('Assign', (1, 0), [('Tuple', (1, 0), [('Name', (1, 0), 'a', ('Store',)), ('Name', (1, 2), 'b', ('Store',))], ('Store',))], ('Name', (1, 6), 'c', ('Load',)), None)], []),
@ -1677,16 +1680,16 @@ def main():
('Module', [('Expr', (1, 0), ('DictComp', (1, 0), ('Name', (1, 1), 'a', ('Load',)), ('Name', (1, 5), 'b', ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'v', ('Store',)), ('Name', (1, 13), 'w', ('Store',))], ('Store',)), ('Name', (1, 18), 'x', ('Load',)), [], 0)]))], []),
('Module', [('Expr', (1, 0), ('SetComp', (1, 0), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 12), 'x', ('Load',)), [('Name', (1, 17), 'g', ('Load',))], 0)]))], []),
('Module', [('Expr', (1, 0), ('SetComp', (1, 0), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Tuple', (1, 7), [('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 9), 'm', ('Store',))], ('Store',)), ('Name', (1, 14), 'x', ('Load',)), [], 0)]))], []),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('Constant', (2, 1), 'async function', None)), ('Expr', (3, 1), ('Await', (3, 1), ('Call', (3, 7), ('Name', (3, 7), 'something', ('Load',)), [], [])))], [], None, None)], []),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('AsyncFor', (2, 1), ('Name', (2, 11), 'e', ('Store',)), ('Name', (2, 16), 'i', ('Load',)), [('Expr', (2, 19), ('Constant', (2, 19), 1, None))], [('Expr', (3, 7), ('Constant', (3, 7), 2, None))], None)], [], None, None)], []),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('AsyncWith', (2, 1), [('withitem', ('Name', (2, 12), 'a', ('Load',)), ('Name', (2, 17), 'b', ('Store',)))], [('Expr', (2, 20), ('Constant', (2, 20), 1, None))], None)], [], None, None)], []),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1), ('Constant', (2, 1), 'async function', None)), ('Expr', (3, 1), ('Await', (3, 1), ('Call', (3, 7), ('Name', (3, 7), 'something', ('Load',)), [], [])))], [], None, None)], []),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('AsyncFor', (2, 1), ('Name', (2, 11), 'e', ('Store',)), ('Name', (2, 16), 'i', ('Load',)), [('Expr', (2, 19), ('Constant', (2, 19), 1, None))], [('Expr', (3, 7), ('Constant', (3, 7), 2, None))], None)], [], None, None)], []),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('AsyncWith', (2, 1), [('withitem', ('Name', (2, 12), 'a', ('Load',)), ('Name', (2, 17), 'b', ('Store',)))], [('Expr', (2, 20), ('Constant', (2, 20), 1, None))], None)], [], None, None)], []),
('Module', [('Expr', (1, 0), ('Dict', (1, 0), [None, ('Constant', (1, 10), 2, None)], [('Dict', (1, 3), [('Constant', (1, 4), 1, None)], [('Constant', (1, 6), 2, None)]), ('Constant', (1, 12), 3, None)]))], []),
('Module', [('Expr', (1, 0), ('Set', (1, 0), [('Starred', (1, 1), ('Set', (1, 2), [('Constant', (1, 3), 1, None), ('Constant', (1, 6), 2, None)]), ('Load',)), ('Constant', (1, 10), 3, None)]))], []),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('ListComp', (2, 1), ('Name', (2, 2), 'i', ('Load',)), [('comprehension', ('Name', (2, 14), 'b', ('Store',)), ('Name', (2, 19), 'c', ('Load',)), [], 1)]))], [], None, None)], []),
('Module', [('FunctionDef', (3, 0), 'f', ('arguments', [], None, [], [], None, []), [('Pass', (3, 9))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])], None, None)], []),
('Module', [('AsyncFunctionDef', (3, 0), 'f', ('arguments', [], None, [], [], None, []), [('Pass', (3, 15))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])], None, None)], []),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (2, 1), ('ListComp', (2, 1), ('Name', (2, 2), 'i', ('Load',)), [('comprehension', ('Name', (2, 14), 'b', ('Store',)), ('Name', (2, 19), 'c', ('Load',)), [], 1)]))], [], None, None)], []),
('Module', [('FunctionDef', (3, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (3, 9))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])], None, None)], []),
('Module', [('AsyncFunctionDef', (3, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (3, 15))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])], None, None)], []),
('Module', [('ClassDef', (3, 0), 'C', [], [], [('Pass', (3, 9))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])])], []),
('Module', [('FunctionDef', (2, 0), 'f', ('arguments', [], None, [], [], None, []), [('Pass', (2, 9))], [('Call', (1, 1), ('Name', (1, 1), 'deco', ('Load',)), [('GeneratorExp', (1, 5), ('Name', (1, 6), 'a', ('Load',)), [('comprehension', ('Name', (1, 12), 'a', ('Store',)), ('Name', (1, 17), 'b', ('Load',)), [], 0)])], [])], None, None)], []),
('Module', [('FunctionDef', (2, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9))], [('Call', (1, 1), ('Name', (1, 1), 'deco', ('Load',)), [('GeneratorExp', (1, 5), ('Name', (1, 6), 'a', ('Load',)), [('comprehension', ('Name', (1, 12), 'a', ('Store',)), ('Name', (1, 17), 'b', ('Load',)), [], 0)])], [])], None, None)], []),
('Module', [('Expr', (1, 0), ('NamedExpr', (1, 1), ('Name', (1, 1), 'a', ('Store',)), ('Constant', (1, 6), 1, None)))], []),
]
single_results = [
@ -1697,7 +1700,7 @@ def main():
('Expression', ('BoolOp', (1, 0), ('And',), [('Name', (1, 0), 'a', ('Load',)), ('Name', (1, 6), 'b', ('Load',))])),
('Expression', ('BinOp', (1, 0), ('Name', (1, 0), 'a', ('Load',)), ('Add',), ('Name', (1, 4), 'b', ('Load',)))),
('Expression', ('UnaryOp', (1, 0), ('Not',), ('Name', (1, 4), 'v', ('Load',)))),
('Expression', ('Lambda', (1, 0), ('arguments', [], None, [], [], None, []), ('Constant', (1, 7), None, None))),
('Expression', ('Lambda', (1, 0), ('arguments', [], [], None, [], [], None, []), ('Constant', (1, 7), None, None))),
('Expression', ('Dict', (1, 0), [('Constant', (1, 2), 1, None)], [('Constant', (1, 4), 2, None)])),
('Expression', ('Dict', (1, 0), [], [])),
('Expression', ('Set', (1, 0), [('Constant', (1, 1), None, None)])),

View file

@ -9,6 +9,7 @@
>>> dump(f.__code__)
name: f
argcount: 1
posonlyargcount: 0
kwonlyargcount: 0
names: ()
varnames: ('x', 'g')
@ -21,6 +22,7 @@
>>> dump(f(4).__code__)
name: g
argcount: 1
posonlyargcount: 0
kwonlyargcount: 0
names: ()
varnames: ('y',)
@ -40,6 +42,7 @@
>>> dump(h.__code__)
name: h
argcount: 2
posonlyargcount: 0
kwonlyargcount: 0
names: ()
varnames: ('x', 'y', 'a', 'b', 'c')
@ -57,6 +60,7 @@
>>> dump(attrs.__code__)
name: attrs
argcount: 1
posonlyargcount: 0
kwonlyargcount: 0
names: ('print', 'attr1', 'attr2', 'attr3')
varnames: ('obj',)
@ -75,6 +79,7 @@
>>> dump(optimize_away.__code__)
name: optimize_away
argcount: 0
posonlyargcount: 0
kwonlyargcount: 0
names: ()
varnames: ()
@ -91,6 +96,7 @@
>>> dump(keywordonly_args.__code__)
name: keywordonly_args
argcount: 2
posonlyargcount: 0
kwonlyargcount: 1
names: ()
varnames: ('a', 'b', 'k1')
@ -100,6 +106,23 @@
flags: 67
consts: ('None',)
>>> def posonly_args(a,b,/,c):
... return a,b,c
...
>>> dump(posonly_args.__code__)
name: posonly_args
argcount: 1
posonlyargcount: 2
kwonlyargcount: 0
names: ()
varnames: ('a', 'b', 'c')
cellvars: ()
freevars: ()
nlocals: 3
flags: 67
consts: ('None',)
"""
import inspect
@ -126,7 +149,8 @@ def consts(t):
def dump(co):
"""Print out a text representation of a code object."""
for attr in ["name", "argcount", "kwonlyargcount", "names", "varnames",
for attr in ["name", "argcount", "posonlyargcount",
"kwonlyargcount", "names", "varnames",
"cellvars", "freevars", "nlocals", "flags"]:
print("%s: %s" % (attr, getattr(co, "co_" + attr)))
print("consts:", tuple(consts(co.co_consts)))
@ -157,7 +181,7 @@ def create_closure(__class__):
def new_code(c):
'''A new code object with a __class__ cell added to freevars'''
return CodeType(
c.co_argcount, c.co_kwonlyargcount, c.co_nlocals,
c.co_argcount, c.co_posonlyargcount, c.co_kwonlyargcount, c.co_nlocals,
c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names,
c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
c.co_lnotab, c.co_freevars + ('__class__',), c.co_cellvars)

View file

@ -617,6 +617,7 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs):
Name: code_info
Filename: (.*)
Argument count: 1
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 3
@ -631,50 +632,53 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs):
if sys.flags.optimize < 2 else (None,))
@staticmethod
def tricky(x, y, z=True, *args, c, d, e=[], **kwds):
def tricky(a, b, /, x, y, z=True, *args, c, d, e=[], **kwds):
def f(c=c):
print(x, y, z, c, d, e, f)
yield x, y, z, c, d, e, f
print(a, b, x, y, z, c, d, e, f)
yield a, b, x, y, z, c, d, e, f
code_info_tricky = """\
Name: tricky
Filename: (.*)
Argument count: 3
Positional-only arguments: 2
Kw-only arguments: 3
Number of locals: 8
Stack size: 7
Number of locals: 10
Stack size: 9
Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR
Constants:
0: None
1: <code object f at (.*), file "(.*)", line (.*)>
2: 'tricky.<locals>.f'
Variable names:
0: x
1: y
2: z
3: c
4: d
5: e
6: args
7: kwds
0: a
1: b
2: x
3: y
4: z
5: c
6: d
7: e
8: args
9: kwds
Cell variables:
0: [edfxyz]
1: [edfxyz]
2: [edfxyz]
3: [edfxyz]
4: [edfxyz]
5: [edfxyz]"""
0: [abedfxyz]
1: [abedfxyz]
2: [abedfxyz]
3: [abedfxyz]
4: [abedfxyz]
5: [abedfxyz]"""
# NOTE: the order of the cell variables above depends on dictionary order!
co_tricky_nested_f = tricky.__func__.__code__.co_consts[1]
code_info_tricky_nested_f = """\
Name: f
Filename: (.*)
Argument count: 1
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 8
Stack size: 10
Flags: OPTIMIZED, NEWLOCALS, NESTED
Constants:
0: None
@ -683,17 +687,18 @@ def f(c=c):
Variable names:
0: c
Free variables:
0: [edfxyz]
1: [edfxyz]
2: [edfxyz]
3: [edfxyz]
4: [edfxyz]
5: [edfxyz]"""
0: [abedfxyz]
1: [abedfxyz]
2: [abedfxyz]
3: [abedfxyz]
4: [abedfxyz]
5: [abedfxyz]"""
code_info_expr_str = """\
Name: <module>
Filename: <disassembly>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 2
@ -707,6 +712,7 @@ def f(c=c):
Name: <module>
Filename: <disassembly>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 2
@ -721,6 +727,7 @@ def f(c=c):
Name: <module>
Filename: <disassembly>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 2
@ -742,6 +749,7 @@ async def async_def():
Name: async_def
Filename: (.*)
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 2
Stack size: 10

View file

@ -581,6 +581,13 @@ def add(self, x, y):
for func in [self.A.static, self.A.cls, self.A.over_partial, self.A.nested, self.A.both]:
self.assertFalse(getattr(func, '__isabstractmethod__', False))
def test_positional_only(self):
def f(a, b, /):
return a + b
p = functools.partial(f, 1)
self.assertEqual(p(2), f(1, 2))
class TestUpdateWrapper(unittest.TestCase):

View file

@ -674,7 +674,8 @@ def test_foreign_code(self):
foreign_code = importlib.import_module.__code__
pos = constants.index(1)
constants[pos] = foreign_code
code = type(code)(code.co_argcount, code.co_kwonlyargcount,
code = type(code)(code.co_argcount, code.co_posonlyargcount,
code.co_kwonlyargcount,
code.co_nlocals, code.co_stacksize,
code.co_flags, code.co_code, tuple(constants),
code.co_names, code.co_varnames, code.co_filename,

View file

@ -862,7 +862,7 @@ def test_magic_number(self):
in advance. Such exceptional releases will then require an
adjustment to this test case.
"""
EXPECTED_MAGIC_NUMBER = 3400
EXPECTED_MAGIC_NUMBER = 3410
actual = int.from_bytes(importlib.util.MAGIC_NUMBER[:2], 'little')
msg = (

View file

@ -763,30 +763,31 @@ def assertArgSpecEquals(self, routine, args_e, varargs_e=None,
def assertFullArgSpecEquals(self, routine, args_e, varargs_e=None,
varkw_e=None, defaults_e=None,
kwonlyargs_e=[], kwonlydefaults_e=None,
posonlyargs_e=[], kwonlyargs_e=[],
kwonlydefaults_e=None,
ann_e={}, formatted=None):
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
args, varargs, varkw, defaults, posonlyargs, kwonlyargs, kwonlydefaults, ann = \
inspect.getfullargspec(routine)
self.assertEqual(args, args_e)
self.assertEqual(varargs, varargs_e)
self.assertEqual(varkw, varkw_e)
self.assertEqual(defaults, defaults_e)
self.assertEqual(posonlyargs, posonlyargs_e)
self.assertEqual(kwonlyargs, kwonlyargs_e)
self.assertEqual(kwonlydefaults, kwonlydefaults_e)
self.assertEqual(ann, ann_e)
if formatted is not None:
with self.assertWarns(DeprecationWarning):
self.assertEqual(inspect.formatargspec(args, varargs, varkw, defaults,
kwonlyargs, kwonlydefaults, ann),
posonlyargs, kwonlyargs,
kwonlydefaults, ann),
formatted)
def test_getargspec(self):
self.assertArgSpecEquals(mod.eggs, ['x', 'y'], formatted='(x, y)')
self.assertArgSpecEquals(mod.spam,
['a', 'b', 'c', 'd', 'e', 'f'],
'g', 'h', (3, 4, 5),
'(a, b, c, d=3, e=4, f=5, *g, **h)')
self.assertRaises(ValueError, self.assertArgSpecEquals,
mod.spam, [])
self.assertRaises(ValueError, self.assertArgSpecEquals,
mod2.keyworded, [])
@ -810,6 +811,26 @@ def test_getfullargspec(self):
kwonlyargs_e=['arg'],
formatted='(*, arg)')
self.assertFullArgSpecEquals(mod2.all_markers, ['c', 'd'],
posonlyargs_e=['a', 'b'],
kwonlyargs_e=['e', 'f'],
formatted='(a, b, /, c, d, *, e, f)')
self.assertFullArgSpecEquals(mod2.all_markers_with_args_and_kwargs,
['c', 'd'],
posonlyargs_e=['a', 'b'],
varargs_e='args',
varkw_e='kwargs',
kwonlyargs_e=['e', 'f'],
formatted='(a, b, /, c, d, *args, e, f, **kwargs)')
self.assertFullArgSpecEquals(mod2.all_markers_with_defaults, ['c', 'd'],
defaults_e=(1,2,3),
posonlyargs_e=['a', 'b'],
kwonlyargs_e=['e', 'f'],
kwonlydefaults_e={'e': 4, 'f': 5},
formatted='(a, b=1, /, c=2, d=3, *, e=4, f=5)')
def test_argspec_api_ignores_wrapped(self):
# Issue 20684: low level introspection API must ignore __wrapped__
@functools.wraps(mod.spam)
@ -856,7 +877,7 @@ def test():
spam_param = inspect.Parameter('spam', inspect.Parameter.POSITIONAL_ONLY)
test.__signature__ = inspect.Signature(parameters=(spam_param,))
self.assertFullArgSpecEquals(test, args_e=['spam'], formatted='(spam)')
self.assertFullArgSpecEquals(test, [], posonlyargs_e=['spam'], formatted='(spam, /)')
def test_getfullargspec_signature_annos(self):
def test(a:'spam') -> 'ham': pass
@ -870,11 +891,11 @@ def test(): pass
@unittest.skipIf(MISSING_C_DOCSTRINGS,
"Signature information for builtins requires docstrings")
def test_getfullargspec_builtin_methods(self):
self.assertFullArgSpecEquals(_pickle.Pickler.dump,
args_e=['self', 'obj'], formatted='(self, obj)')
self.assertFullArgSpecEquals(_pickle.Pickler.dump, [],
posonlyargs_e=['self', 'obj'], formatted='(self, obj, /)')
self.assertFullArgSpecEquals(_pickle.Pickler(io.BytesIO()).dump,
args_e=['self', 'obj'], formatted='(self, obj)')
self.assertFullArgSpecEquals(_pickle.Pickler(io.BytesIO()).dump, [],
posonlyargs_e=['self', 'obj'], formatted='(self, obj, /)')
self.assertFullArgSpecEquals(
os.stat,

View file

@ -233,6 +233,18 @@ def test_function_defs(self):
self.check_suite("def f(*args, a, b = 5): pass")
self.check_suite("def f(*args, a, b = 5, **kwds): pass")
# positional-only arguments
self.check_suite("def f(a, /): pass")
self.check_suite("def f(a, /,): pass")
self.check_suite("def f(a, b, /): pass")
self.check_suite("def f(a, b, /, c): pass")
self.check_suite("def f(a, b, /, c = 6): pass")
self.check_suite("def f(a, b, /, c, *, d): pass")
self.check_suite("def f(a, b, /, c = 1, *, d): pass")
self.check_suite("def f(a, b, /, c, *, d = 1): pass")
self.check_suite("def f(a, b=1, /, c=2, *, d = 3): pass")
self.check_suite("def f(a=0, b=1, /, c=2, *, d = 3): pass")
# function annotations
self.check_suite("def f(a: int): pass")
self.check_suite("def f(a: int = 5): pass")

View file

@ -0,0 +1,403 @@
"""Unit tests for the positional only argument syntax specified in PEP 570."""
import pickle
import unittest
from test.support import check_syntax_error
def global_pos_only_f(a, b, /):
return a, b
def global_pos_only_and_normal(a, /, b):
return a, b
def global_pos_only_defaults(a=1, /, b=2):
return a, b
class PositionalOnlyTestCase(unittest.TestCase):
def assertRaisesSyntaxError(self, codestr, regex="invalid syntax"):
with self.assertRaisesRegex(SyntaxError, regex):
compile(codestr + "\n", "<test>", "single")
def test_invalid_syntax_errors(self):
check_syntax_error(self, "def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
check_syntax_error(self, "def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
check_syntax_error(self, "def f(a = 5, b=1, /, c, *, d=2): pass", "non-default argument follows default argument")
check_syntax_error(self, "def f(a = 5, b, /): pass", "non-default argument follows default argument")
check_syntax_error(self, "def f(*args, /): pass")
check_syntax_error(self, "def f(*args, a, /): pass")
check_syntax_error(self, "def f(**kwargs, /): pass")
check_syntax_error(self, "def f(/, a = 1): pass")
check_syntax_error(self, "def f(/, a): pass")
check_syntax_error(self, "def f(/): pass")
check_syntax_error(self, "def f(*, a, /): pass")
check_syntax_error(self, "def f(*, /, a): pass")
check_syntax_error(self, "def f(a, /, a): pass", "duplicate argument 'a' in function definition")
check_syntax_error(self, "def f(a, /, *, a): pass", "duplicate argument 'a' in function definition")
check_syntax_error(self, "def f(a, b/2, c): pass")
check_syntax_error(self, "def f(a, /, c, /): pass")
check_syntax_error(self, "def f(a, /, c, /, d): pass")
check_syntax_error(self, "def f(a, /, c, /, d, *, e): pass")
check_syntax_error(self, "def f(a, *, c, /, d, e): pass")
def test_invalid_syntax_errors_async(self):
check_syntax_error(self, "async def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
check_syntax_error(self, "async def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
check_syntax_error(self, "async def f(a = 5, b=1, /, c, d=2): pass", "non-default argument follows default argument")
check_syntax_error(self, "async def f(a = 5, b, /): pass", "non-default argument follows default argument")
check_syntax_error(self, "async def f(*args, /): pass")
check_syntax_error(self, "async def f(*args, a, /): pass")
check_syntax_error(self, "async def f(**kwargs, /): pass")
check_syntax_error(self, "async def f(/, a = 1): pass")
check_syntax_error(self, "async def f(/, a): pass")
check_syntax_error(self, "async def f(/): pass")
check_syntax_error(self, "async def f(*, a, /): pass")
check_syntax_error(self, "async def f(*, /, a): pass")
check_syntax_error(self, "async def f(a, /, a): pass", "duplicate argument 'a' in function definition")
check_syntax_error(self, "async def f(a, /, *, a): pass", "duplicate argument 'a' in function definition")
check_syntax_error(self, "async def f(a, b/2, c): pass")
check_syntax_error(self, "async def f(a, /, c, /): pass")
check_syntax_error(self, "async def f(a, /, c, /, d): pass")
check_syntax_error(self, "async def f(a, /, c, /, d, *, e): pass")
check_syntax_error(self, "async def f(a, *, c, /, d, e): pass")
def test_optional_positional_only_args(self):
def f(a, b=10, /, c=100):
return a + b + c
self.assertEqual(f(1, 2, 3), 6)
self.assertEqual(f(1, 2, c=3), 6)
with self.assertRaisesRegex(TypeError, r"f\(\) got some positional-only arguments passed as keyword arguments: 'b'"):
f(1, b=2, c=3)
self.assertEqual(f(1, 2), 103)
with self.assertRaisesRegex(TypeError, r"f\(\) got some positional-only arguments passed as keyword arguments: 'b'"):
f(1, b=2)
self.assertEqual(f(1, c=2), 13)
def f(a=1, b=10, /, c=100):
return a + b + c
self.assertEqual(f(1, 2, 3), 6)
self.assertEqual(f(1, 2, c=3), 6)
with self.assertRaisesRegex(TypeError, r"f\(\) got some positional-only arguments passed as keyword arguments: 'b'"):
f(1, b=2, c=3)
self.assertEqual(f(1, 2), 103)
with self.assertRaisesRegex(TypeError, r"f\(\) got some positional-only arguments passed as keyword arguments: 'b'"):
f(1, b=2)
self.assertEqual(f(1, c=2), 13)
def test_syntax_for_many_positional_only(self):
# more than 255 positional only arguments, should compile ok
fundef = "def f(%s, /):\n pass\n" % ', '.join('i%d' % i for i in range(300))
compile(fundef, "<test>", "single")
def test_pos_only_definition(self):
def f(a, b, c, /, d, e=1, *, f, g=2):
pass
self.assertEqual(2, f.__code__.co_argcount) # 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(3, f.__code__.co_posonlyargcount)
self.assertEqual((1, 2, 3), f.__defaults__)
def test_pos_only_call_via_unpacking(self):
def f(a, b, /):
return a + b
self.assertEqual(f(*[1, 2]), 3)
def test_use_positional_as_keyword(self):
def f(a, /):
pass
expected = r"f\(\) got some positional-only arguments passed as keyword arguments: 'a'"
with self.assertRaisesRegex(TypeError, expected):
f(a=1)
def f(a, /, b):
pass
expected = r"f\(\) got some positional-only arguments passed as keyword arguments: 'a'"
with self.assertRaisesRegex(TypeError, expected):
f(a=1, b=2)
def f(a, b, /):
pass
expected = r"f\(\) got some positional-only arguments passed as keyword arguments: 'a, b'"
with self.assertRaisesRegex(TypeError, expected):
f(a=1, b=2)
def test_positional_only_and_arg_invalid_calls(self):
def f(a, b, /, c):
pass
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required positional argument: 'c'"):
f(1, 2)
with self.assertRaisesRegex(TypeError, r"f\(\) missing 2 required positional arguments: 'b' and 'c'"):
f(1)
with self.assertRaisesRegex(TypeError, r"f\(\) missing 3 required positional arguments: 'a', 'b', and 'c'"):
f()
with self.assertRaisesRegex(TypeError, r"f\(\) takes 3 positional arguments but 4 were given"):
f(1, 2, 3, 4)
def test_positional_only_and_optional_arg_invalid_calls(self):
def f(a, b, /, c=3):
pass
f(1, 2) # does not raise
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required positional argument: 'b'"):
f(1)
with self.assertRaisesRegex(TypeError, r"f\(\) missing 2 required positional arguments: 'a' and 'b'"):
f()
with self.assertRaisesRegex(TypeError, r"f\(\) takes from 2 to 3 positional arguments but 4 were given"):
f(1, 2, 3, 4)
def test_positional_only_and_kwonlyargs_invalid_calls(self):
def f(a, b, /, c, *, d, e):
pass
f(1, 2, 3, d=1, e=2) # does not raise
with self.assertRaisesRegex(TypeError, r"missing 1 required keyword-only argument: 'd'"):
f(1, 2, 3, e=2)
with self.assertRaisesRegex(TypeError, r"missing 2 required keyword-only arguments: 'd' and 'e'"):
f(1, 2, 3)
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required positional argument: 'c'"):
f(1, 2)
with self.assertRaisesRegex(TypeError, r"f\(\) missing 2 required positional arguments: 'b' and 'c'"):
f(1)
with self.assertRaisesRegex(TypeError, r" missing 3 required positional arguments: 'a', 'b', and 'c'"):
f()
with self.assertRaisesRegex(TypeError, r"f\(\) takes 3 positional arguments but 6 positional arguments "
r"\(and 2 keyword-only arguments\) were given"):
f(1, 2, 3, 4, 5, 6, d=7, e=8)
with self.assertRaisesRegex(TypeError, r"f\(\) got an unexpected keyword argument 'f'"):
f(1, 2, 3, d=1, e=4, f=56)
def test_positional_only_invalid_calls(self):
def f(a, b, /):
pass
f(1, 2) # does not raise
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required positional argument: 'b'"):
f(1)
with self.assertRaisesRegex(TypeError, r"f\(\) missing 2 required positional arguments: 'a' and 'b'"):
f()
with self.assertRaisesRegex(TypeError, r"f\(\) takes 2 positional arguments but 3 were given"):
f(1, 2, 3)
def test_positional_only_with_optional_invalid_calls(self):
def f(a, b=2, /):
pass
f(1) # does not raise
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required positional argument: 'a'"):
f()
with self.assertRaisesRegex(TypeError, r"f\(\) takes from 1 to 2 positional arguments but 3 were given"):
f(1, 2, 3)
def test_no_standard_args_usage(self):
def f(a, b, /, *, c):
pass
f(1, 2, c=3)
with self.assertRaises(TypeError):
f(1, b=2, c=3)
def test_change_default_pos_only(self):
def f(a, b=2, /, c=3):
return a + b + c
self.assertEqual((2,3), f.__defaults__)
f.__defaults__ = (1, 2, 3)
self.assertEqual(f(1, 2, 3), 6)
def test_lambdas(self):
x = lambda a, /, b: a + b
self.assertEqual(x(1,2), 3)
self.assertEqual(x(1,b=2), 3)
x = lambda a, /, b=2: a + b
self.assertEqual(x(1), 3)
x = lambda a, b, /: a + b
self.assertEqual(x(1, 2), 3)
x = lambda a, b, /, : a + b
self.assertEqual(x(1, 2), 3)
def test_invalid_syntax_lambda(self):
check_syntax_error(self, "lambda a, b = 5, /, c: None", "non-default argument follows default argument")
check_syntax_error(self, "lambda a = 5, b, /, c: None", "non-default argument follows default argument")
check_syntax_error(self, "lambda a = 5, b, /: None", "non-default argument follows default argument")
check_syntax_error(self, "lambda *args, /: None")
check_syntax_error(self, "lambda *args, a, /: None")
check_syntax_error(self, "lambda **kwargs, /: None")
check_syntax_error(self, "lambda /, a = 1: None")
check_syntax_error(self, "lambda /, a: None")
check_syntax_error(self, "lambda /: None")
check_syntax_error(self, "lambda *, a, /: None")
check_syntax_error(self, "lambda *, /, a: None")
check_syntax_error(self, "lambda a, /, a: None", "duplicate argument 'a' in function definition")
check_syntax_error(self, "lambda a, /, *, a: None", "duplicate argument 'a' in function definition")
check_syntax_error(self, "lambda a, /, b, /: None")
check_syntax_error(self, "lambda a, /, b, /, c: None")
check_syntax_error(self, "lambda a, /, b, /, c, *, d: None")
check_syntax_error(self, "lambda a, *, b, /, c: None")
def test_posonly_methods(self):
class Example:
def f(self, a, b, /):
return a, b
self.assertEqual(Example().f(1, 2), (1, 2))
self.assertEqual(Example.f(Example(), 1, 2), (1, 2))
self.assertRaises(TypeError, Example.f, 1, 2)
expected = r"f\(\) got some positional-only arguments passed as keyword arguments: 'b'"
with self.assertRaisesRegex(TypeError, expected):
Example().f(1, b=2)
def test_mangling(self):
class X:
def f(self, *, __a=42):
return __a
self.assertEqual(X().f(), 42)
def test_module_function(self):
with self.assertRaisesRegex(TypeError, r"f\(\) missing 2 required positional arguments: 'a' and 'b'"):
global_pos_only_f()
def test_closures(self):
def f(x,y):
def g(x2,/,y2):
return x + y + x2 + y2
return g
self.assertEqual(f(1,2)(3,4), 10)
with self.assertRaisesRegex(TypeError, r"g\(\) missing 1 required positional argument: 'y2'"):
f(1,2)(3)
with self.assertRaisesRegex(TypeError, r"g\(\) takes 2 positional arguments but 3 were given"):
f(1,2)(3,4,5)
def f(x,/,y):
def g(x2,y2):
return x + y + x2 + y2
return g
self.assertEqual(f(1,2)(3,4), 10)
def f(x,/,y):
def g(x2,/,y2):
return x + y + x2 + y2
return g
self.assertEqual(f(1,2)(3,4), 10)
with self.assertRaisesRegex(TypeError, r"g\(\) missing 1 required positional argument: 'y2'"):
f(1,2)(3)
with self.assertRaisesRegex(TypeError, r"g\(\) takes 2 positional arguments but 3 were given"):
f(1,2)(3,4,5)
def test_same_keyword_as_positional_with_kwargs(self):
def f(something,/,**kwargs):
return (something, kwargs)
self.assertEqual(f(42, something=42), (42, {'something': 42}))
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required positional argument: 'something'"):
f(something=42)
self.assertEqual(f(42), (42, {}))
def test_mangling(self):
class X:
def f(self, __a=42, /):
return __a
def f2(self, __a=42, /, __b=43):
return (__a, __b)
def f3(self, __a=42, /, __b=43, *, __c=44):
return (__a, __b, __c)
self.assertEqual(X().f(), 42)
self.assertEqual(X().f2(), (42, 43))
self.assertEqual(X().f3(), (42, 43, 44))
def test_too_many_arguments(self):
# more than 255 positional-only arguments, should compile ok
fundef = "def f(%s, /):\n pass\n" % ', '.join('i%d' % i for i in range(300))
compile(fundef, "<test>", "single")
def test_serialization(self):
pickled_posonly = pickle.dumps(global_pos_only_f)
pickled_optional = pickle.dumps(global_pos_only_and_normal)
pickled_defaults = pickle.dumps(global_pos_only_defaults)
unpickled_posonly = pickle.loads(pickled_posonly)
unpickled_optional = pickle.loads(pickled_optional)
unpickled_defaults = pickle.loads(pickled_defaults)
self.assertEqual(unpickled_posonly(1,2), (1,2))
expected = r"global_pos_only_f\(\) got some positional-only arguments "\
r"passed as keyword arguments: 'a, b'"
with self.assertRaisesRegex(TypeError, expected):
unpickled_posonly(a=1,b=2)
self.assertEqual(unpickled_optional(1,2), (1,2))
expected = r"global_pos_only_and_normal\(\) got some positional-only arguments "\
r"passed as keyword arguments: 'a'"
with self.assertRaisesRegex(TypeError, expected):
unpickled_optional(a=1,b=2)
self.assertEqual(unpickled_defaults(), (1,2))
expected = r"global_pos_only_defaults\(\) got some positional-only arguments "\
r"passed as keyword arguments: 'a'"
with self.assertRaisesRegex(TypeError, expected):
unpickled_defaults(a=1,b=2)
def test_async(self):
async def f(a=1, /, b=2):
return a, b
with self.assertRaisesRegex(TypeError, r"f\(\) got some positional-only arguments passed as keyword arguments: 'a'"):
f(a=1, b=2)
def _check_call(*args, **kwargs):
try:
coro = f(*args, **kwargs)
coro.send(None)
except StopIteration as e:
result = e.value
self.assertEqual(result, (1, 2))
_check_call(1, 2)
_check_call(1, b=2)
_check_call(1)
_check_call()
def test_generator(self):
def f(a=1, /, b=2):
yield a, b
with self.assertRaisesRegex(TypeError, r"f\(\) got some positional-only arguments passed as keyword arguments: 'a'"):
f(a=1, b=2)
gen = f(1, 2)
self.assertEqual(next(gen), (1, 2))
gen = f(1, b=2)
self.assertEqual(next(gen), (1, 2))
gen = f(1)
self.assertEqual(next(gen), (1, 2))
gen = f()
self.assertEqual(next(gen), (1, 2))
if __name__ == "__main__":
unittest.main()

View file

@ -101,7 +101,7 @@ def fab(
def fab(
a, # type: A
b # type: B
b # type: B
):
pass

View file

@ -263,7 +263,7 @@ def coroutine(func):
# TODO: Implement this in C.
co = func.__code__
func.__code__ = CodeType(
co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_argcount, co.co_posonlyargcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize,
co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE
co.co_code,

View file

@ -0,0 +1,2 @@
Implement :pep:`570` (Python positional-only parameters). Patch by Pablo
Galindo.

View file

@ -320,11 +320,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 == nargs) {
if (argdefs == NULL && co->co_argcount + co->co_posonlyargcount == nargs) {
return function_code_fastcall(co, args, nargs, globals);
}
else if (nargs == 0 && argdefs != NULL
&& co->co_argcount == PyTuple_GET_SIZE(argdefs)) {
&& co->co_argcount + co->co_posonlyargcount == 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);
@ -406,11 +406,11 @@ _PyFunction_FastCallKeywords(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 == nargs) {
if (argdefs == NULL && co->co_argcount + co->co_posonlyargcount== nargs) {
return function_code_fastcall(co, stack, nargs, globals);
}
else if (nargs == 0 && argdefs != NULL
&& co->co_argcount == PyTuple_GET_SIZE(argdefs)) {
&& co->co_argcount + co->co_posonlyargcount == 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);

View file

@ -96,7 +96,7 @@ intern_string_constants(PyObject *tuple)
PyCodeObject *
PyCode_New(int argcount, int kwonlyargcount,
PyCode_New(int argcount, int posonlyargcount, int kwonlyargcount,
int nlocals, int stacksize, int flags,
PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *freevars, PyObject *cellvars,
@ -108,8 +108,8 @@ PyCode_New(int argcount, int kwonlyargcount,
Py_ssize_t i, n_cellvars, n_varnames, total_args;
/* Check argument types */
if (argcount < 0 || kwonlyargcount < 0 || nlocals < 0 ||
code == NULL || !PyBytes_Check(code) ||
if (argcount < 0 || posonlyargcount < 0 || kwonlyargcount < 0 ||
nlocals < 0 || code == NULL || !PyBytes_Check(code) ||
consts == NULL || !PyTuple_Check(consts) ||
names == NULL || !PyTuple_Check(names) ||
varnames == NULL || !PyTuple_Check(varnames) ||
@ -141,10 +141,12 @@ PyCode_New(int argcount, int kwonlyargcount,
}
n_varnames = PyTuple_GET_SIZE(varnames);
if (argcount <= n_varnames && kwonlyargcount <= n_varnames) {
if (posonlyargcount + argcount <= n_varnames
&& kwonlyargcount <= n_varnames) {
/* Never overflows. */
total_args = (Py_ssize_t)argcount + (Py_ssize_t)kwonlyargcount +
((flags & CO_VARARGS) != 0) + ((flags & CO_VARKEYWORDS) != 0);
total_args = (Py_ssize_t)posonlyargcount + (Py_ssize_t)argcount
+ (Py_ssize_t)kwonlyargcount +
((flags & CO_VARARGS) != 0) + ((flags & CO_VARKEYWORDS) != 0);
}
else {
total_args = n_varnames + 1;
@ -193,6 +195,7 @@ PyCode_New(int argcount, int kwonlyargcount,
return NULL;
}
co->co_argcount = argcount;
co->co_posonlyargcount = posonlyargcount;
co->co_kwonlyargcount = kwonlyargcount;
co->co_nlocals = nlocals;
co->co_stacksize = stacksize;
@ -249,6 +252,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
goto failed;
result = PyCode_New(0, /* argcount */
0, /* posonlyargcount */
0, /* kwonlyargcount */
0, /* nlocals */
0, /* stacksize */
@ -274,21 +278,22 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
#define OFF(x) offsetof(PyCodeObject, x)
static PyMemberDef code_memberlist[] = {
{"co_argcount", T_INT, OFF(co_argcount), READONLY},
{"co_kwonlyargcount", T_INT, OFF(co_kwonlyargcount), READONLY},
{"co_nlocals", T_INT, OFF(co_nlocals), READONLY},
{"co_stacksize",T_INT, OFF(co_stacksize), READONLY},
{"co_flags", T_INT, OFF(co_flags), READONLY},
{"co_code", T_OBJECT, OFF(co_code), READONLY},
{"co_consts", T_OBJECT, OFF(co_consts), READONLY},
{"co_names", T_OBJECT, OFF(co_names), READONLY},
{"co_varnames", T_OBJECT, OFF(co_varnames), READONLY},
{"co_freevars", T_OBJECT, OFF(co_freevars), READONLY},
{"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY},
{"co_filename", T_OBJECT, OFF(co_filename), READONLY},
{"co_name", T_OBJECT, OFF(co_name), READONLY},
{"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY},
{"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY},
{"co_argcount", T_INT, OFF(co_argcount), READONLY},
{"co_posonlyargcount", T_INT, OFF(co_posonlyargcount), READONLY},
{"co_kwonlyargcount", T_INT, OFF(co_kwonlyargcount), READONLY},
{"co_nlocals", T_INT, OFF(co_nlocals), READONLY},
{"co_stacksize",T_INT, OFF(co_stacksize), READONLY},
{"co_flags", T_INT, OFF(co_flags), READONLY},
{"co_code", T_OBJECT, OFF(co_code), READONLY},
{"co_consts", T_OBJECT, OFF(co_consts), READONLY},
{"co_names", T_OBJECT, OFF(co_names), READONLY},
{"co_varnames", T_OBJECT, OFF(co_varnames), READONLY},
{"co_freevars", T_OBJECT, OFF(co_freevars), READONLY},
{"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY},
{"co_filename", T_OBJECT, OFF(co_filename), READONLY},
{"co_name", T_OBJECT, OFF(co_name), READONLY},
{"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY},
{"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY},
{NULL} /* Sentinel */
};
@ -335,9 +340,9 @@ validate_and_copy_tuple(PyObject *tup)
}
PyDoc_STRVAR(code_doc,
"code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,\n\
constants, names, varnames, filename, name, firstlineno,\n\
lnotab[, freevars[, cellvars]])\n\
"code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n\
flags, codestring, constants, names, varnames, filename, name,\n\
firstlineno, lnotab[, freevars[, cellvars]])\n\
\n\
Create a code object. Not for the faint of heart.");
@ -345,6 +350,7 @@ static PyObject *
code_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
int argcount;
int posonlyargcount;
int kwonlyargcount;
int nlocals;
int stacksize;
@ -361,8 +367,8 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw)
int firstlineno;
PyObject *lnotab;
if (!PyArg_ParseTuple(args, "iiiiiSO!O!O!UUiS|O!O!:code",
&argcount, &kwonlyargcount,
if (!PyArg_ParseTuple(args, "iiiiiiSO!O!O!UUiS|O!O!:code",
&argcount, &posonlyargcount, &kwonlyargcount,
&nlocals, &stacksize, &flags,
&code,
&PyTuple_Type, &consts,
@ -381,6 +387,13 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw)
goto cleanup;
}
if (posonlyargcount < 0) {
PyErr_SetString(
PyExc_ValueError,
"code: posonlyargcount must not be negative");
goto cleanup;
}
if (kwonlyargcount < 0) {
PyErr_SetString(
PyExc_ValueError,
@ -413,7 +426,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw)
if (ourcellvars == NULL)
goto cleanup;
co = (PyObject *)PyCode_New(argcount, kwonlyargcount,
co = (PyObject *)PyCode_New(argcount, posonlyargcount, kwonlyargcount,
nlocals, stacksize, flags,
code, consts, ournames, ourvarnames,
ourfreevars, ourcellvars, filename,
@ -645,9 +658,11 @@ code_richcompare(PyObject *self, PyObject *other, int op)
cp = (PyCodeObject *)other;
eq = PyObject_RichCompareBool(co->co_name, cp->co_name, Py_EQ);
if (eq <= 0) goto unequal;
if (!eq) goto unequal;
eq = co->co_argcount == cp->co_argcount;
if (!eq) goto unequal;
eq = co->co_posonlyargcount == cp->co_posonlyargcount;
if (!eq) goto unequal;
eq = co->co_kwonlyargcount == cp->co_kwonlyargcount;
if (!eq) goto unequal;
eq = co->co_nlocals == cp->co_nlocals;
@ -720,7 +735,7 @@ code_hash(PyCodeObject *co)
h6 = PyObject_Hash(co->co_cellvars);
if (h6 == -1) return -1;
h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^
co->co_argcount ^ co->co_kwonlyargcount ^
co->co_argcount ^ co->co_posonlyargcount ^ co->co_kwonlyargcount ^
co->co_nlocals ^ co->co_flags;
if (h == -1) h = -2;
return h;

View file

@ -1139,7 +1139,7 @@ static PYC_MAGIC magic_values[] = {
{ 3320, 3351, L"3.5" },
{ 3360, 3379, L"3.6" },
{ 3390, 3399, L"3.7" },
{ 3400, 3409, L"3.8" },
{ 3400, 3410, L"3.8" },
{ 0 }
};

View file

@ -111,8 +111,8 @@ module Python
excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults,
arg? kwarg, expr* defaults)
arguments = (arg* args, arg* posonlyargs, arg? vararg, arg* kwonlyargs,
expr* kw_defaults, arg? kwarg, expr* defaults)
arg = (identifier arg, expr? annotation, string? type_comment)
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

50
Python/Python-ast.c generated
View file

@ -469,6 +469,7 @@ static char *ExceptHandler_fields[]={
};
static PyTypeObject *arguments_type;
static PyObject* ast2obj_arguments(void*);
_Py_IDENTIFIER(posonlyargs);
_Py_IDENTIFIER(vararg);
_Py_IDENTIFIER(kwonlyargs);
_Py_IDENTIFIER(kw_defaults);
@ -476,6 +477,7 @@ _Py_IDENTIFIER(kwarg);
_Py_IDENTIFIER(defaults);
static char *arguments_fields[]={
"args",
"posonlyargs",
"vararg",
"kwonlyargs",
"kw_defaults",
@ -1141,7 +1143,7 @@ static int init_types(void)
ExceptHandler_type = make_type("ExceptHandler", excepthandler_type,
ExceptHandler_fields, 3);
if (!ExceptHandler_type) return 0;
arguments_type = make_type("arguments", &AST_type, arguments_fields, 6);
arguments_type = make_type("arguments", &AST_type, arguments_fields, 7);
if (!arguments_type) return 0;
if (!add_attributes(arguments_type, NULL, 0)) return 0;
arg_type = make_type("arg", &AST_type, arg_fields, 3);
@ -2569,14 +2571,16 @@ ExceptHandler(expr_ty type, identifier name, asdl_seq * body, int lineno, int
}
arguments_ty
arguments(asdl_seq * args, arg_ty vararg, asdl_seq * kwonlyargs, asdl_seq *
kw_defaults, arg_ty kwarg, asdl_seq * defaults, PyArena *arena)
arguments(asdl_seq * args, asdl_seq * posonlyargs, arg_ty vararg, asdl_seq *
kwonlyargs, asdl_seq * kw_defaults, arg_ty kwarg, asdl_seq *
defaults, PyArena *arena)
{
arguments_ty p;
p = (arguments_ty)PyArena_Malloc(arena, sizeof(*p));
if (!p)
return NULL;
p->args = args;
p->posonlyargs = posonlyargs;
p->vararg = vararg;
p->kwonlyargs = kwonlyargs;
p->kw_defaults = kw_defaults;
@ -3954,6 +3958,11 @@ ast2obj_arguments(void* _o)
if (_PyObject_SetAttrId(result, &PyId_args, value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_list(o->posonlyargs, ast2obj_arg);
if (!value) goto failed;
if (_PyObject_SetAttrId(result, &PyId_posonlyargs, value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_arg(o->vararg);
if (!value) goto failed;
if (_PyObject_SetAttrId(result, &PyId_vararg, value) == -1)
@ -8267,6 +8276,7 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
{
PyObject* tmp = NULL;
asdl_seq* args;
asdl_seq* posonlyargs;
arg_ty vararg;
asdl_seq* kwonlyargs;
asdl_seq* kw_defaults;
@ -8303,6 +8313,36 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
}
Py_CLEAR(tmp);
}
if (_PyObject_LookupAttrId(obj, &PyId_posonlyargs, &tmp) < 0) {
return 1;
}
if (tmp == NULL) {
PyErr_SetString(PyExc_TypeError, "required field \"posonlyargs\" missing from arguments");
return 1;
}
else {
int res;
Py_ssize_t len;
Py_ssize_t i;
if (!PyList_Check(tmp)) {
PyErr_Format(PyExc_TypeError, "arguments field \"posonlyargs\" must be a list, not a %.200s", tmp->ob_type->tp_name);
goto failed;
}
len = PyList_GET_SIZE(tmp);
posonlyargs = _Py_asdl_seq_new(len, arena);
if (posonlyargs == NULL) goto failed;
for (i = 0; i < len; i++) {
arg_ty val;
res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &val, arena);
if (res != 0) goto failed;
if (len != PyList_GET_SIZE(tmp)) {
PyErr_SetString(PyExc_RuntimeError, "arguments field \"posonlyargs\" changed size during iteration");
goto failed;
}
asdl_seq_SET(posonlyargs, i, val);
}
Py_CLEAR(tmp);
}
if (_PyObject_LookupAttrId(obj, &PyId_vararg, &tmp) < 0) {
return 1;
}
@ -8419,8 +8459,8 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
}
Py_CLEAR(tmp);
}
*out = arguments(args, vararg, kwonlyargs, kw_defaults, kwarg, defaults,
arena);
*out = arguments(args, posonlyargs, vararg, kwonlyargs, kw_defaults, kwarg,
defaults, arena);
return 0;
failed:
Py_XDECREF(tmp);

View file

@ -110,8 +110,9 @@ expr_context_name(expr_context_ty ctx)
static int
validate_arguments(arguments_ty args)
{
if (!validate_args(args->args))
if (!validate_args(args->posonlyargs) || !validate_args(args->args)) {
return 0;
}
if (args->vararg && args->vararg->annotation
&& !validate_expr(args->vararg->annotation, Load)) {
return 0;
@ -1431,31 +1432,73 @@ ast_for_arguments(struct compiling *c, const node *n)
and varargslist (lambda definition).
parameters: '(' [typedargslist] ')'
typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
'*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
| '**' tfpdef [',']]]
| '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
| '**' tfpdef [','])
The following definition for typedarglist is equivalent to this set of rules:
arguments = argument (',' [TYPE_COMMENT] argument)*
argument = tfpdef ['=' test]
kwargs = '**' tfpdef [','] [TYPE_COMMENT]
args = '*' [tfpdef]
kwonly_kwargs = (',' [TYPE_COMMENT] argument)* (TYPE_COMMENT | [','
[TYPE_COMMENT] [kwargs]])
args_kwonly_kwargs = args kwonly_kwargs | kwargs
poskeyword_args_kwonly_kwargs = arguments ( TYPE_COMMENT | [','
[TYPE_COMMENT] [args_kwonly_kwargs]])
typedargslist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs
typedarglist = (arguments ',' [TYPE_COMMENT] '/' [',' [[TYPE_COMMENT]
typedargslist_no_posonly]])|(typedargslist_no_posonly)"
typedargslist: ( (tfpdef ['=' test] (',' [TYPE_COMMENT] tfpdef ['=' test])*
',' [TYPE_COMMENT] '/' [',' [ [TYPE_COMMENT] tfpdef ['=' test] ( ','
[TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] [ '*'
[tfpdef] (',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [','
[TYPE_COMMENT] ['**' tfpdef [','] [TYPE_COMMENT]]]) | '**' tfpdef [',']
[TYPE_COMMENT]]]) | '*' [tfpdef] (',' [TYPE_COMMENT] tfpdef ['=' test])*
(TYPE_COMMENT | [',' [TYPE_COMMENT] ['**' tfpdef [','] [TYPE_COMMENT]]]) |
'**' tfpdef [','] [TYPE_COMMENT]]] ) | (tfpdef ['=' test] (','
[TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [',' [TYPE_COMMENT] [ '*'
[tfpdef] (',' [TYPE_COMMENT] tfpdef ['=' test])* (TYPE_COMMENT | [','
[TYPE_COMMENT] ['**' tfpdef [','] [TYPE_COMMENT]]]) | '**' tfpdef [',']
[TYPE_COMMENT]]]) | '*' [tfpdef] (',' [TYPE_COMMENT] tfpdef ['=' test])*
(TYPE_COMMENT | [',' [TYPE_COMMENT] ['**' tfpdef [','] [TYPE_COMMENT]]]) |
'**' tfpdef [','] [TYPE_COMMENT]))
tfpdef: NAME [':' test]
varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
'*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
| '**' vfpdef [',']]]
| '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
| '**' vfpdef [',']
)
The following definition for varargslist is equivalent to this set of rules:
arguments = argument (',' argument )*
argument = vfpdef ['=' test]
kwargs = '**' vfpdef [',']
args = '*' [vfpdef]
kwonly_kwargs = (',' argument )* [',' [kwargs]]
args_kwonly_kwargs = args kwonly_kwargs | kwargs
poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]]
vararglist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs
varargslist = arguments ',' '/' [','[(vararglist_no_posonly)]] |
(vararglist_no_posonly)
varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['='
test] (',' vfpdef ['=' test])* [',' [ '*' [vfpdef] (',' vfpdef ['=' test])* [','
['**' vfpdef [',']]] | '**' vfpdef [',']]] | '*' [vfpdef] (',' vfpdef ['=' test])*
[',' ['**' vfpdef [',']]] | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef
['=' test])* [',' [ '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
| '**' vfpdef [',']]] | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef
[',']]] | '**' vfpdef [','])
vfpdef: NAME
*/
int i, j, k, nposargs = 0, nkwonlyargs = 0;
int i, j, k, l, nposonlyargs=0, nposargs = 0, nkwonlyargs = 0;
int nposdefaults = 0, found_default = 0;
asdl_seq *posargs, *posdefaults, *kwonlyargs, *kwdefaults;
asdl_seq *posonlyargs, *posargs, *posdefaults, *kwonlyargs, *kwdefaults;
arg_ty vararg = NULL, kwarg = NULL;
arg_ty arg = NULL;
node *ch;
if (TYPE(n) == parameters) {
if (NCH(n) == 2) /* () as argument list */
return arguments(NULL, NULL, NULL, NULL, NULL, NULL, c->c_arena);
return arguments(NULL, NULL, NULL, NULL, NULL, NULL, NULL, c->c_arena);
n = CHILD(n, 1);
}
assert(TYPE(n) == typedargslist || TYPE(n) == varargslist);
@ -1479,6 +1522,10 @@ ast_for_arguments(struct compiling *c, const node *n)
if (TYPE(ch) == DOUBLESTAR) break;
if (TYPE(ch) == vfpdef || TYPE(ch) == tfpdef) nposargs++;
if (TYPE(ch) == EQUAL) nposdefaults++;
if (TYPE(ch) == SLASH ) {
nposonlyargs = nposargs;
nposargs = 0;
}
}
/* count the number of keyword only args &
defaults for keyword only args */
@ -1487,6 +1534,10 @@ ast_for_arguments(struct compiling *c, const node *n)
if (TYPE(ch) == DOUBLESTAR) break;
if (TYPE(ch) == tfpdef || TYPE(ch) == vfpdef) nkwonlyargs++;
}
posonlyargs = (nposonlyargs ? _Py_asdl_seq_new(nposonlyargs, c->c_arena) : NULL);
if (!posonlyargs && nposonlyargs) {
return NULL;
}
posargs = (nposargs ? _Py_asdl_seq_new(nposargs, c->c_arena) : NULL);
if (!posargs && nposargs)
return NULL;
@ -1512,6 +1563,7 @@ ast_for_arguments(struct compiling *c, const node *n)
i = 0;
j = 0; /* index for defaults */
k = 0; /* index for args */
l = 0; /* index for posonlyargs */
while (i < NCH(n)) {
ch = CHILD(n, i);
switch (TYPE(ch)) {
@ -1537,11 +1589,23 @@ ast_for_arguments(struct compiling *c, const node *n)
arg = ast_for_arg(c, ch);
if (!arg)
return NULL;
asdl_seq_SET(posargs, k++, arg);
if (l < nposonlyargs) {
asdl_seq_SET(posonlyargs, l++, arg);
} else {
asdl_seq_SET(posargs, k++, arg);
}
i += 1; /* the name */
if (i < NCH(n) && TYPE(CHILD(n, i)) == COMMA)
i += 1; /* the comma, if present */
break;
case SLASH:
/* Advance the slash and the comma. If there are more names
* after the slash there will be a comma so we are advancing
* the correct number of nodes. If the slash is the last item,
* we will be advancing an extra token but then * i > NCH(n)
* and the enclosing while will finish correctly. */
i += 2;
break;
case STAR:
if (i+1 >= NCH(n) ||
(i+2 == NCH(n) && (TYPE(CHILD(n, i+1)) == COMMA
@ -1621,7 +1685,7 @@ ast_for_arguments(struct compiling *c, const node *n)
return NULL;
}
}
return arguments(posargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults, c->c_arena);
return arguments(posargs, posonlyargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults, c->c_arena);
}
static expr_ty
@ -1909,7 +1973,7 @@ ast_for_lambdef(struct compiling *c, const node *n)
expr_ty expression;
if (NCH(n) == 3) {
args = arguments(NULL, NULL, NULL, NULL, NULL, NULL, c->c_arena);
args = arguments(NULL, NULL, NULL, NULL, NULL, NULL, NULL, c->c_arena);
if (!args)
return NULL;
expression = ast_for_expr(c, CHILD(n, 2));

View file

@ -3694,10 +3694,10 @@ missing_arguments(PyCodeObject *co, Py_ssize_t missing, Py_ssize_t defcount,
return;
if (positional) {
start = 0;
end = co->co_argcount - defcount;
end = co->co_posonlyargcount + co->co_argcount - defcount;
}
else {
start = co->co_argcount;
start = co->co_posonlyargcount + co->co_argcount;
end = start + co->co_kwonlyargcount;
}
for (i = start; i < end; i++) {
@ -3724,23 +3724,25 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount,
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 = co_argcount; i < co_argcount + co->co_kwonlyargcount; i++) {
for (i = total_positional; i < total_positional + co->co_kwonlyargcount; i++) {
if (GETLOCAL(i) != NULL) {
kwonly_given++;
}
}
if (defcount) {
Py_ssize_t atleast = co_argcount - defcount;
Py_ssize_t atleast = total_positional - defcount;
plural = 1;
sig = PyUnicode_FromFormat("from %zd to %zd", atleast, co_argcount);
sig = PyUnicode_FromFormat("from %zd to %zd", atleast, total_positional);
}
else {
plural = (co_argcount != 1);
sig = PyUnicode_FromFormat("%zd", co_argcount);
plural = (total_positional != 1);
sig = PyUnicode_FromFormat("%zd", total_positional);
}
if (sig == NULL)
return;
@ -3772,6 +3774,67 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount,
Py_DECREF(kwonly_sig);
}
static int
positional_only_passed_as_keyword(PyCodeObject *co, Py_ssize_t kwcount,
PyObject* const* kwnames)
{
int posonly_conflicts = 0;
PyObject* posonly_names = PyList_New(0);
for(int k=0; k < co->co_posonlyargcount; k++){
PyObject* posonly_name = PyTuple_GET_ITEM(co->co_varnames, k);
for (int k2=0; k2<kwcount; k2++){
/* Compare the pointers first and fallback to PyObject_RichCompareBool*/
PyObject* kwname = kwnames[k2];
if (kwname == posonly_name){
if(PyList_Append(posonly_names, kwname) != 0) {
goto fail;
}
posonly_conflicts++;
continue;
}
int cmp = PyObject_RichCompareBool(posonly_name, kwname, Py_EQ);
if ( cmp > 0) {
if(PyList_Append(posonly_names, kwname) != 0) {
goto fail;
}
posonly_conflicts++;
} else if (cmp < 0) {
goto fail;
}
}
}
if (posonly_conflicts) {
PyObject* comma = PyUnicode_FromString(", ");
if (comma == NULL) {
goto fail;
}
PyObject* error_names = PyUnicode_Join(comma, posonly_names);
Py_DECREF(comma);
if (error_names == NULL) {
goto fail;
}
PyErr_Format(PyExc_TypeError,
"%U() got some positional-only arguments passed"
" as keyword arguments: '%U'",
co->co_name, error_names);
Py_DECREF(error_names);
goto fail;
}
Py_DECREF(posonly_names);
return 0;
fail:
Py_XDECREF(posonly_names);
return 1;
}
/* This is gonna seem *real weird*, but if you put some other code between
PyEval_EvalFrame() and _PyEval_EvalFrameDefault() you will need to adjust
the test in the if statements in Misc/gdbinit (pystack and pystackv). */
@ -3791,8 +3854,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject **fastlocals, **freevars;
PyThreadState *tstate;
PyObject *x, *u;
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
Py_ssize_t i, n;
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount + co->co_posonlyargcount;
Py_ssize_t i, j, n;
PyObject *kwdict;
if (globals == NULL) {
@ -3826,14 +3889,28 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
kwdict = NULL;
}
/* Copy positional arguments into local variables */
if (argcount > co->co_argcount) {
n = co->co_argcount;
/* Copy positional only arguments into local variables */
if (argcount > co->co_argcount + co->co_posonlyargcount) {
n = co->co_posonlyargcount;
}
else {
n = argcount;
}
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
x = args[j];
Py_INCREF(x);
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);
@ -3866,7 +3943,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
/* Speed hack: do raw pointer compares. As names are
normally interned this should almost always hit. */
co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
for (j = 0; j < total_args; j++) {
for (j = co->co_posonlyargcount; j < total_args; j++) {
PyObject *name = co_varnames[j];
if (name == keyword) {
goto kw_found;
@ -3874,7 +3951,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
}
/* Slow fallback, just in case */
for (j = 0; j < total_args; j++) {
for (j = co->co_posonlyargcount; j < total_args; j++) {
PyObject *name = co_varnames[j];
int cmp = PyObject_RichCompareBool( keyword, name, Py_EQ);
if (cmp > 0) {
@ -3887,6 +3964,11 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
assert(j >= total_args);
if (kwdict == NULL) {
if (co->co_posonlyargcount && positional_only_passed_as_keyword(co, kwcount, kwnames)) {
goto fail;
}
PyErr_Format(PyExc_TypeError,
"%U() got an unexpected keyword argument '%S'",
co->co_name, keyword);
@ -3910,14 +3992,14 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
}
/* Check the number of positional arguments */
if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) {
if ((argcount > co->co_argcount + co->co_posonlyargcount) && !(co->co_flags & CO_VARARGS)) {
too_many_positional(co, argcount, defcount, fastlocals);
goto fail;
}
/* Add missing positional arguments (copy default values from defs) */
if (argcount < co->co_argcount) {
Py_ssize_t m = co->co_argcount - defcount;
if (argcount < co->co_posonlyargcount + co->co_argcount) {
Py_ssize_t m = co->co_posonlyargcount + co->co_argcount - defcount;
Py_ssize_t missing = 0;
for (i = argcount; i < m; i++) {
if (GETLOCAL(i) == NULL) {
@ -3944,7 +4026,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_argcount; i < total_args; i++) {
for (i = co->co_posonlyargcount + co->co_argcount; i < total_args; i++) {
PyObject *name;
if (GETLOCAL(i) != NULL)
continue;

View file

@ -122,6 +122,7 @@ struct compiler_unit {
PyObject *u_private; /* for private name mangling */
Py_ssize_t u_argcount; /* number of arguments for block */
Py_ssize_t u_posonlyargcount; /* number of positional only arguments for block */
Py_ssize_t u_kwonlyargcount; /* number of keyword only arguments for block */
/* Pointer to the most recently allocated block. By following b_list
members, you can reach all early allocated blocks. */
@ -552,6 +553,7 @@ compiler_enter_scope(struct compiler *c, identifier name,
memset(u, 0, sizeof(struct compiler_unit));
u->u_scope_type = scope_type;
u->u_argcount = 0;
u->u_posonlyargcount = 0;
u->u_kwonlyargcount = 0;
u->u_ste = PySymtable_Lookup(c->c_st, key);
if (!u->u_ste) {
@ -2127,6 +2129,7 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async)
}
c->u->u_argcount = asdl_seq_LEN(args->args);
c->u->u_posonlyargcount = asdl_seq_LEN(args->posonlyargs);
c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs);
VISIT_SEQ_IN_SCOPE(c, stmt, body);
co = assemble(c, 1);
@ -2507,6 +2510,7 @@ compiler_lambda(struct compiler *c, expr_ty e)
return 0;
c->u->u_argcount = asdl_seq_LEN(args->args);
c->u->u_posonlyargcount = asdl_seq_LEN(args->posonlyargs);
c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs);
VISIT_IN_SCOPE(c, expr, e->v.Lambda.body);
if (c->u->u_ste->ste_generator) {
@ -5742,7 +5746,7 @@ makecode(struct compiler *c, struct assembler *a)
Py_ssize_t nlocals;
int nlocals_int;
int flags;
int argcount, kwonlyargcount, maxdepth;
int argcount, posonlyargcount, kwonlyargcount, maxdepth;
consts = consts_dict_keys_inorder(c->u->u_consts);
names = dict_keys_inorder(c->u->u_names, 0);
@ -5787,12 +5791,13 @@ makecode(struct compiler *c, struct assembler *a)
}
argcount = Py_SAFE_DOWNCAST(c->u->u_argcount, Py_ssize_t, int);
posonlyargcount = Py_SAFE_DOWNCAST(c->u->u_posonlyargcount, 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, kwonlyargcount,
co = PyCode_New(argcount, posonlyargcount, kwonlyargcount,
nlocals_int, maxdepth, flags,
bytecode, consts, names, varnames,
freevars, cellvars,

View file

@ -15,15 +15,15 @@
the appropriate bytes from M___main__.c. */
static unsigned char M___hello__[] = {
227,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,
0,64,0,0,0,115,16,0,0,0,100,0,90,0,101,1,
100,1,131,1,1,0,100,2,83,0,41,3,84,122,12,72,
101,108,108,111,32,119,111,114,108,100,33,78,41,2,218,11,
105,110,105,116,105,97,108,105,122,101,100,218,5,112,114,105,
110,116,169,0,114,3,0,0,0,114,3,0,0,0,250,22,
46,47,84,111,111,108,115,47,102,114,101,101,122,101,47,102,
108,97,103,46,112,121,218,8,60,109,111,100,117,108,101,62,
1,0,0,0,115,2,0,0,0,4,1,
227,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,2,0,0,0,64,0,0,0,115,16,0,0,0,100,0,
90,0,101,1,100,1,131,1,1,0,100,2,83,0,41,3,
84,122,12,72,101,108,108,111,32,119,111,114,108,100,33,78,
41,2,218,11,105,110,105,116,105,97,108,105,122,101,100,218,
5,112,114,105,110,116,169,0,114,3,0,0,0,114,3,0,
0,0,250,20,84,111,111,108,115,47,102,114,101,101,122,101,
47,102,108,97,103,46,112,121,218,8,60,109,111,100,117,108,
101,62,1,0,0,0,115,2,0,0,0,4,1,
};
#define SIZE (int)sizeof(M___hello__)

File diff suppressed because it is too large Load diff

2781
Python/importlib.h generated

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -530,6 +530,7 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
PyCodeObject *co = (PyCodeObject *)v;
W_TYPE(TYPE_CODE, p);
w_long(co->co_argcount, p);
w_long(co->co_posonlyargcount, p);
w_long(co->co_kwonlyargcount, p);
w_long(co->co_nlocals, p);
w_long(co->co_stacksize, p);
@ -1322,6 +1323,7 @@ r_object(RFILE *p)
case TYPE_CODE:
{
int argcount;
int posonlyargcount;
int kwonlyargcount;
int nlocals;
int stacksize;
@ -1347,6 +1349,10 @@ r_object(RFILE *p)
argcount = (int)r_long(p);
if (PyErr_Occurred())
goto code_error;
posonlyargcount = (int)r_long(p);
if (PyErr_Occurred()) {
goto code_error;
}
kwonlyargcount = (int)r_long(p);
if (PyErr_Occurred())
goto code_error;
@ -1391,7 +1397,7 @@ r_object(RFILE *p)
goto code_error;
v = (PyObject *) PyCode_New(
argcount, kwonlyargcount,
argcount, posonlyargcount, kwonlyargcount,
nlocals, stacksize, flags,
code, consts, names, varnames,
freevars, cellvars, filename, name,

View file

@ -1653,6 +1653,8 @@ symtable_visit_arguments(struct symtable *st, arguments_ty a)
/* skip default arguments inside function block
XXX should ast be different?
*/
if (a->posonlyargs && !symtable_visit_params(st, a->posonlyargs))
return 0;
if (a->args && !symtable_visit_params(st, a->args))
return 0;
if (a->kwonlyargs && !symtable_visit_params(st, a->kwonlyargs))

View file

@ -592,14 +592,18 @@ def _arg(self, t):
def _arguments(self, t):
first = True
# normal arguments
defaults = [None] * (len(t.args) - len(t.defaults)) + t.defaults
for a, d in zip(t.args, defaults):
all_args = t.posonlyargs + t.args
defaults = [None] * (len(all_args) - len(t.defaults)) + t.defaults
for index, elements in enumerate(zip(all_args, defaults), 1):
a, d = elements
if first:first = False
else: self.write(", ")
self.dispatch(a)
if d:
self.write("=")
self.dispatch(d)
if index == len(t.posonlyargs):
self.write(", /")
# varargs, or bare '*' if no varargs but keyword-only arguments present
if t.vararg or t.kwonlyargs: