Author: Yannick Jadoul <[email protected]>
Branch: posonly-params
Changeset: r98420:b09184b998ef
Date: 2019-12-31 02:39 +0100
http://bitbucket.org/pypy/pypy/changeset/b09184b998ef/
Log: First try at positional-only parameters
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -181,6 +181,7 @@
# some comments about the JIT: it assumes that signature is a constant,
# so all values coming from there can be assumed constant. It assumes
# that the length of the defaults_w does not vary too much.
+ co_posonlyargcount = signature.num_posonlyargnames()
co_argcount = signature.num_argnames() # expected formal arguments,
without */**
co_kwonlyargcount = signature.num_kwonlyargnames()
too_many_args = False
@@ -189,7 +190,7 @@
upfront = 0
args_w = self.arguments_w
if w_firstarg is not None:
- if co_argcount > 0:
+ if co_posonlyargcount + co_argcount > 0:
scope_w[0] = w_firstarg
upfront = 1
else:
@@ -207,33 +208,33 @@
# put as many positional input arguments into place as available
input_argcount = upfront
- if input_argcount < co_argcount:
- take = min(num_args, co_argcount - upfront)
+ if input_argcount < co_posonlyargcount + co_argcount:
+ take = min(num_args, co_posonlyargcount + co_argcount - upfront)
# letting the JIT unroll this loop is safe, because take is always
- # smaller than co_argcount
+ # smaller than co_posonlyargcount + co_argcount
for i in range(take):
scope_w[i + input_argcount] = args_w[i]
input_argcount += take
# collect extra positional arguments into the *vararg
if signature.has_vararg():
- args_left = co_argcount - upfront
+ args_left = co_posonlyargcount + co_argcount - upfront
assert args_left >= 0 # check required by rpython
if num_args > args_left:
starargs_w = args_w[args_left:]
else:
starargs_w = []
- loc = co_argcount + co_kwonlyargcount
+ loc = co_posonlyargcount + co_argcount + co_kwonlyargcount
scope_w[loc] = self.space.newtuple(starargs_w)
- elif avail > co_argcount:
+ elif avail > co_posonlyargcount + co_argcount:
too_many_args = True
# if a **kwargs argument is needed, create the dict
w_kwds = None
if signature.has_kwarg():
w_kwds = self.space.newdict(kwargs=True)
- scope_w[co_argcount + co_kwonlyargcount + signature.has_vararg()]
= w_kwds
+ scope_w[co_posonlyargcount + co_argcount + co_kwonlyargcount +
signature.has_vararg()] = w_kwds
# handle keyword arguments
num_remainingkwds = 0
@@ -242,7 +243,7 @@
if num_kwds:
# kwds_mapping maps target indexes in the scope (minus
input_argcount)
# to positions in the keywords_w list
- kwds_mapping = [0] * (co_argcount + co_kwonlyargcount -
input_argcount)
+ kwds_mapping = [0] * (co_posonlyargcount + co_argcount +
co_kwonlyargcount - input_argcount)
# initialize manually, for the JIT :-(
for i in range(len(kwds_mapping)):
kwds_mapping[i] = -1
@@ -250,15 +251,17 @@
# the called function takes
# this function must not take a scope_w, to make the scope not
# escape
- num_remainingkwds = _match_keywords(
- signature, blindargs, input_argcount, keywords,
- kwds_mapping, self._jit_few_keywords)
+ num_remainingkwds, posonly_conflicts = _match_keywords(
+ signature, blindargs, co_posonlyargcount, input_argcount,
+ keywords, kwds_mapping, self._jit_few_keywords)
if num_remainingkwds:
if w_kwds is not None:
# collect extra keyword arguments into the **kwarg
_collect_keyword_args(
self.space, keywords, keywords_w, w_kwds,
kwds_mapping, self.keyword_names_w,
self._jit_few_keywords)
+ elif posonly_conflicts:
+ raise ArgErrPosonlyAsKwds(posonly_conflicts)
else:
raise ArgErrUnknownKwds(self.space, num_remainingkwds,
keywords,
kwds_mapping, self.keyword_names_w)
@@ -267,14 +270,14 @@
# or with defaults, if available
missing_positional = []
missing_kwonly = []
- more_filling = (input_argcount < co_argcount + co_kwonlyargcount)
+ more_filling = (input_argcount < co_posonlyargcount + co_argcount +
co_kwonlyargcount)
def_first = 0
if more_filling:
- def_first = co_argcount - (0 if defaults_w is None else
len(defaults_w))
+ def_first = co_posonlyargcount + co_argcount - (0 if defaults_w is
None else len(defaults_w))
j = 0
kwds_index = -1
# first, fill the arguments from the kwds
- for i in range(input_argcount, co_argcount + co_kwonlyargcount):
+ for i in range(input_argcount, co_posonlyargcount + co_argcount +
co_kwonlyargcount):
if kwds_mapping is not None:
kwds_index = kwds_mapping[j]
j += 1
@@ -283,7 +286,7 @@
if too_many_args:
kwonly_given = 0
- for i in range(co_argcount, co_argcount + co_kwonlyargcount):
+ for i in range(co_posonlyargcount + co_argcount,
co_posonlyargcount + co_argcount + co_kwonlyargcount):
if scope_w[i] is not None:
kwonly_given += 1
if self.methodcall:
@@ -295,21 +298,23 @@
avail, kwonly_given)
if more_filling:
- # then, fill the normal arguments with defaults_w (if needed)
- for i in range(input_argcount, co_argcount):
+ # then, fill the posonly arguments with defaults_w (if needed)
+ for i in range(input_argcount, co_posonlyargcount + co_argcount):
if scope_w[i] is not None:
continue
defnum = i - def_first
if defnum >= 0:
scope_w[i] = defaults_w[defnum]
+ elif i < co_posonlyargcount:
+ missing_positional.append(signature.posonlyargnames[i])
else:
- missing_positional.append(signature.argnames[i])
+ missing_positional.append(signature.argnames[i -
co_posonlyargcount])
# finally, fill kwonly arguments with w_kw_defs (if needed)
- for i in range(co_argcount, co_argcount + co_kwonlyargcount):
+ for i in range(co_posonlyargcount + co_argcount,
co_posonlyargcount + co_argcount + co_kwonlyargcount):
if scope_w[i] is not None:
continue
- name = signature.kwonlyargnames[i - co_argcount]
+ name = signature.kwonlyargnames[i - (co_posonlyargcount +
co_argcount)]
if w_kw_defs is None:
missing_kwonly.append(name)
continue
@@ -441,13 +446,14 @@
i += 1
@jit.look_inside_iff(
- lambda signature, blindargs, input_argcount,
+ lambda signature, blindargs, co_posonlyargcount, input_argcount,
keywords, kwds_mapping, jiton: jiton)
-def _match_keywords(signature, blindargs, input_argcount,
- keywords, kwds_mapping, _):
+def _match_keywords(signature, blindargs, co_posonlyargcount,
+ input_argcount, keywords, kwds_mapping, _):
# letting JIT unroll the loop is *only* safe if the callsite didn't
# use **args because num_kwds can be arbitrarily large otherwise.
num_kwds = num_remainingkwds = len(keywords)
+ posonly_conflicts = []
for i in range(num_kwds):
name = keywords[i]
# If name was not encoded as a string, it could be None. In that
@@ -457,7 +463,9 @@
j = signature.find_argname(name)
# if j == -1 nothing happens, because j < input_argcount and
# blindargs > j
- if j < input_argcount:
+ if j < co_posonlyargcount:
+ posonly_conflicts.append(name)
+ elif j < input_argcount:
# check that no keyword argument conflicts with these. note
# that for this purpose we ignore the first blindargs,
# which were put into place by prepend(). This way,
@@ -468,7 +476,7 @@
else:
kwds_mapping[j - input_argcount] = i # map to the right index
num_remainingkwds -= 1
- return num_remainingkwds
+ return num_remainingkwds, posonly_conflicts
@jit.look_inside_iff(
lambda space, keywords, keywords_w, w_kwds, kwds_mapping,
@@ -536,7 +544,7 @@
self.kwonly_given = kwonly_given
def getmsg(self):
- num_args = self.signature.num_argnames()
+ num_args = self.signature.num_posonlyargnames() +
self.signature.num_argnames()
num_defaults = self.num_defaults
if num_defaults:
takes_str = "from %d to %d positional arguments" % (
@@ -564,7 +572,7 @@
def getmsg(self):
msg = ArgErrTooMany.getmsg(self)
- n = self.signature.num_argnames()
+ n = self.signature.num_posonlyargnames() +
self.signature.num_argnames()
if (self.given == n + 1 and
(n == 0 or self.signature.argnames[0] != "self")):
msg += ". Did you forget 'self' in the function definition?"
@@ -618,3 +626,13 @@
msg = "got %d unexpected keyword arguments" % (
self.num_kwds)
return msg
+
+class ArgErrPosonlyAsKwds(ArgErr):
+
+ def __init__(self, posonly_kwds):
+ self.posonly_kwds = posonly_kwds
+
+ def getmsg(self):
+ msg = ("got some positional-only arguments passed "
+ "as keyword arguments: '%s'") % ', '.join(self.posonly_kwds)
+ return msg
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -48,6 +48,7 @@
self.argnames = argnames
self.varargname = varargname
self.kwargname = kwargname
+ self.posonlyargnames = None
self.kwonlyargnames = None
def append(self, argname):
@@ -56,13 +57,19 @@
else:
self.kwonlyargnames.append(argname)
+ def marker_posonly(self):
+ assert self.posonlyargnames is None
+ assert self.kwonlyargnames is None
+ self.posonlyargnames = self.argnames
+ self.argnames = []
+
def marker_kwonly(self):
assert self.kwonlyargnames is None
self.kwonlyargnames = []
def signature(self):
return Signature(self.argnames, self.varargname, self.kwargname,
- self.kwonlyargnames)
+ self.posonlyargnames, self.kwonlyargnames)
#________________________________________________________________
@@ -253,6 +260,11 @@
name = int_unwrapping_space_method(typ)
self.checked_space_method(name, app_sig)
+ def visit_posonly(self, _, app_sig):
+ argname = self.orig_arg()
+ assert argname == '__posonly__'
+ app_sig.marker_posonly()
+
def visit_kwonly(self, _, app_sig):
argname = self.orig_arg()
assert argname == '__kwonly__'
@@ -363,6 +375,9 @@
def visit_truncatedint_w(self, typ):
self.run_args.append("space.truncatedint_w(%s)" % (self.scopenext(),))
+ def visit_posonly(self, typ):
+ self.run_args.append("None")
+
def visit_kwonly(self, typ):
self.run_args.append("None")
@@ -536,6 +551,9 @@
def visit_truncatedint_w(self, typ):
self.unwrap.append("space.truncatedint_w(%s)" % (self.nextarg(),))
+ def visit_posonly(self, typ):
+ raise FastFuncNotSupported
+
def visit_kwonly(self, typ):
raise FastFuncNotSupported
@@ -645,6 +663,8 @@
unwrap_spec.append('args_w')
elif argname.startswith('w_'):
unwrap_spec.append(W_Root)
+ elif argname == '__posonly__':
+ unwrap_spec.append('posonly')
elif argname == '__kwonly__':
unwrap_spec.append('kwonly')
else:
@@ -702,6 +722,8 @@
argnames = sig.argnames
varargname = sig.varargname
kwargname = sig.kwargname
+ if sig.posonlyargnames:
+ import pdb; pdb.set_trace()
if sig.kwonlyargnames:
import pdb; pdb.set_trace()
self._argnames = argnames
@@ -1065,7 +1087,7 @@
alldefs_w = {}
assert len(self._code._argnames) == len(self._code._unwrap_spec)
for name, spec in zip(self._code._argnames, self._code._unwrap_spec):
- if name == '__kwonly__':
+ if name in ('__posonly__', '__kwonly__'):
continue
defaultval = self._staticdefs.get(name, NO_DEFAULT)
@@ -1107,10 +1129,19 @@
#
sig = self._code.sig
first_defined = 0
- while (first_defined < len(sig.argnames) and
- sig.argnames[first_defined] not in alldefs_w):
+ n_posonlyargnames = len(sig.posonlyargnames)
+ while (first_defined < n_posonlyargnames and
+ sig.posonlyargnames[first_defined] not in alldefs_w):
first_defined += 1
- defs_w = [alldefs_w.pop(name) for name in sig.argnames[first_defined:]]
+ n_argnames = len(sig.argnames)
+ while (first_defined < n_posonlyargnames + n_argnames and
+ sig.argnames[first_defined-n_posonlyargnames] not in alldefs_w):
+ first_defined += 1
+ if first_defined < n_posonlyargnames:
+ defs_w = [alldefs_w.pop(name) for name in
sig.posonlyargnames[first_defined:]]
+ defs_w.extend([alldefs_w.pop(name) for name in sig.argnames])
+ else:
+ defs_w = [alldefs_w.pop(name) for name in
sig.argnames[first_defined-n_posonlyargnames:]]
kw_defs_w = None
if alldefs_w:
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -71,7 +71,7 @@
kwargname = code.co_varnames[argcount]
else:
kwargname = None
- return Signature(argnames, varargname, kwargname, kwonlyargs)
+ return Signature(argnames, varargname, kwargname, None, kwonlyargs)
class CodeHookCache(object):
def __init__(self, space):
diff --git a/pypy/interpreter/signature.py b/pypy/interpreter/signature.py
--- a/pypy/interpreter/signature.py
+++ b/pypy/interpreter/signature.py
@@ -2,13 +2,16 @@
class Signature(object):
_immutable_ = True
- _immutable_fields_ = ["argnames[*]", "kwonlyargnames[*]"]
- __slots__ = ("argnames", "kwonlyargnames", "varargname", "kwargname")
+ _immutable_fields_ = ["argnames[*]", "posonlyargnames[*]",
"kwonlyargnames[*]"]
+ __slots__ = ("argnames", "posonlyargnames", "kwonlyargnames",
"varargname", "kwargname")
- def __init__(self, argnames, varargname=None, kwargname=None,
kwonlyargnames=None):
+ def __init__(self, argnames, varargname=None, kwargname=None,
posonlyargnames=None, kwonlyargnames=None):
self.argnames = argnames
self.varargname = varargname
self.kwargname = kwargname
+ if posonlyargnames is None:
+ posonlyargnames = []
+ self.posonlyargnames = posonlyargnames
if kwonlyargnames is None:
kwonlyargnames = []
self.kwonlyargnames = kwonlyargnames
@@ -16,11 +19,15 @@
@jit.elidable
def find_argname(self, name):
try:
- return self.argnames.index(name)
+ return self.posonlyargnames.index(name)
except ValueError:
pass
try:
- return len(self.argnames) + self.kwonlyargnames.index(name)
+ return len(self.posonlyargnames) + self.argnames.index(name)
+ except ValueError:
+ pass
+ try:
+ return len(self.posonlyargnames) + len(self.argnames) +
self.kwonlyargnames.index(name)
except ValueError:
pass
return -1
@@ -28,6 +35,9 @@
def num_argnames(self):
return len(self.argnames)
+ def num_posonlyargnames(self):
+ return len(self.posonlyargnames)
+
def num_kwonlyargnames(self):
return len(self.kwonlyargnames)
@@ -39,13 +49,15 @@
def scope_length(self):
scopelen = len(self.argnames)
+ scopelen += len(self.posonlyargnames)
scopelen += len(self.kwonlyargnames)
scopelen += self.has_vararg()
scopelen += self.has_kwarg()
return scopelen
def getallvarnames(self):
- argnames = self.argnames
+ argnames = self.posonlyargnames
+ argnames = argnames + self.argnames
if self.varargname is not None:
argnames = argnames + [self.varargname]
argnames = argnames + self.kwonlyargnames
@@ -54,8 +66,8 @@
return argnames
def __repr__(self):
- return "Signature(%r, %r, %r, %r)" % (
- self.argnames, self.varargname, self.kwargname,
self.kwonlyargnames)
+ return "Signature(%r, %r, %r, %r, %r)" % (
+ self.argnames, self.varargname, self.kwargname,
self.posonlyargnames, self.kwonlyargnames)
def __eq__(self, other):
if not isinstance(other, Signature):
@@ -63,6 +75,7 @@
return (self.argnames == other.argnames and
self.varargname == other.varargname and
self.kwargname == other.kwargname and
+ self.posonlyargnames == other.posonlyargnames and
self.kwonlyargnames == other.kwonlyargnames)
def __ne__(self, other):
diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py
--- a/pypy/objspace/std/intobject.py
+++ b/pypy/objspace/std/intobject.py
@@ -550,7 +550,7 @@
@staticmethod
@unwrap_spec(w_x=WrappedDefault(0))
- def descr_new(space, w_inttype, w_x, w_base=None):
+ def descr_new(space, w_inttype, w_x, __posonly__, w_base=None):
"Create and return a new object. See help(type) for accurate
signature."
return _new_int(space, w_inttype, w_x, w_base)
@@ -979,7 +979,7 @@
W_AbstractIntObject.typedef = TypeDef("int",
- __doc__ = """int(x=0) -> integer
+ __doc__ = """int([x]) -> integer
int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit