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

Reply via email to