[pypy-svn] r68436 - pypy/trunk/pypy/interpreter

cfbolz at codespeak.net cfbolz at codespeak.net
Wed Oct 14 14:55:34 CEST 2009


Author: cfbolz
Date: Wed Oct 14 14:55:33 2009
New Revision: 68436

Added:
   pypy/trunk/pypy/interpreter/argument.py.merge.tmp
      - copied, changed from r68434, pypy/trunk/pypy/interpreter/argument.py
Log:
merging of svn+ssh://codespeak.net/svn/pypy/branch/improve-kwd-args/pypy/interpreter/argument.py
revisions 68277 to 68434:

    ------------------------------------------------------------------------
    r68403 | cfbolz | 2009-10-13 23:22:02 +0200 (Tue, 13 Oct 2009) | 2 lines
    
    fix annotation
    
    ------------------------------------------------------------------------
    r68401 | cfbolz | 2009-10-13 22:46:56 +0200 (Tue, 13 Oct 2009) | 3 lines
    
    (cfbolz, pedronis around): invent a beautiful Signature object instead of a
    measly tuple.
    
    ------------------------------------------------------------------------
    r68378 | pedronis | 2009-10-13 15:25:09 +0200 (Tue, 13 Oct 2009) | 2 lines
    
    (cfbolz, pedronis): write unit tests for the error productions. aren't we nice.
    
    ------------------------------------------------------------------------
    r68377 | pedronis | 2009-10-13 14:39:06 +0200 (Tue, 13 Oct 2009) | 2 lines
    
    (cfbolz, pedronis): improve the silly guy
    
    ------------------------------------------------------------------------
    r68376 | pedronis | 2009-10-13 14:36:52 +0200 (Tue, 13 Oct 2009) | 3 lines
    
    (cfbolz, pedronis) improve test coverage, kill some rarely used methods
    
    
    ------------------------------------------------------------------------
    r68366 | cfbolz | 2009-10-13 12:19:10 +0200 (Tue, 13 Oct 2009) | 2 lines
    
    fix translation
    
    ------------------------------------------------------------------------
    r68365 | pedronis | 2009-10-13 12:06:57 +0200 (Tue, 13 Oct 2009) | 3 lines
    
    (cfbolz, pedronis) fixes and cleanups
    
    
    ------------------------------------------------------------------------
    r68364 | cfbolz | 2009-10-13 12:01:43 +0200 (Tue, 13 Oct 2009) | 2 lines
    
    fix repr
    
    ------------------------------------------------------------------------
    r68354 | cfbolz | 2009-10-12 22:49:53 +0200 (Mon, 12 Oct 2009) | 2 lines
    
    fix geninterp to no longer use fromshape
    
    ------------------------------------------------------------------------
    r68353 | cfbolz | 2009-10-12 22:23:56 +0200 (Mon, 12 Oct 2009) | 2 lines
    
    make Arguments immutable again and fix variaous breakages
    
    ------------------------------------------------------------------------
    r68349 | pedronis | 2009-10-12 19:29:24 +0200 (Mon, 12 Oct 2009) | 5 lines
    
    (pedronis, cfbolz): progress and regress all mixed up in a blob of confusion:
    - make the argument parsing not rely on dicts, which is JIT-friendlier
    - at the moment Arguments objects are not immutable any more, which is a bad
      idea
    
    ------------------------------------------------------------------------
    r68332 | pedronis | 2009-10-12 14:57:53 +0200 (Mon, 12 Oct 2009) | 2 lines
    
    (pedronis, cfbolz): more tests, this time for the interfaces that the translation toolchain uses
    
    ------------------------------------------------------------------------
    r68330 | pedronis | 2009-10-12 10:54:14 +0200 (Mon, 12 Oct 2009) | 3 lines
    
    (cfbolz, pedronis) fix failures
    
    
    ------------------------------------------------------------------------
    r68288 | pedronis | 2009-10-09 18:34:40 +0200 (Fri, 09 Oct 2009) | 3 lines
    
    (cfbolz, pedronis) fix some breakage, time to go home
    
    
    ------------------------------------------------------------------------
    r68284 | pedronis | 2009-10-09 17:52:29 +0200 (Fri, 09 Oct 2009) | 4 lines
    
    (pedronis, cfbolz): make a subclass of Arguments, ArgumentsForTranslation,
    which is used only in the translation toolchain (flow space, annotator, rtyper
    to be precise).
    
    ------------------------------------------------------------------------
    r68283 | pedronis | 2009-10-09 17:13:59 +0200 (Fri, 09 Oct 2009) | 4 lines
    
    (pedronis, cfbolz): reorder methods a bit to group those used only by the
    translation toolchain. annotate with use places a bit all methods. kill
    has_keywords.
    
    ------------------------------------------------------------------------
    r68280 | pedronis | 2009-10-09 16:50:58 +0200 (Fri, 09 Oct 2009) | 2 lines
    
    (pedronis, cfbolz): kill the now useless abstract base class
    
    ------------------------------------------------------------------------
    r68278 | pedronis | 2009-10-09 16:43:11 +0200 (Fri, 09 Oct 2009) | 2 lines
    
    (cfbolz, pedronis): a branch where we want to refactor the Arguments class for fun and profit.
    
    ------------------------------------------------------------------------


Copied: pypy/trunk/pypy/interpreter/argument.py.merge.tmp (from r68434, pypy/trunk/pypy/interpreter/argument.py)
==============================================================================
--- pypy/trunk/pypy/interpreter/argument.py	(original)
+++ pypy/trunk/pypy/interpreter/argument.py.merge.tmp	Wed Oct 14 14:55:33 2009
@@ -3,190 +3,80 @@
 """
 
 from pypy.interpreter.error import OperationError
+from pypy.rlib.debug import make_sure_not_resized
+from pypy.rlib.jit import purefunction
 
-class AbstractArguments:
 
-    def parse_into_scope(self, w_firstarg,
-                         scope_w, fnname, signature, defaults_w=[]):
-        """Parse args and kwargs to initialize a frame
-        according to the signature of code object.
-        Store the argumentvalues into scope_w.
-        scope_w must be big enough for signature.
-        """
-        argnames, varargname, kwargname = signature
-        has_vararg = varargname is not None
-        has_kwarg = kwargname is not None
-        try:
-            return self._match_signature(w_firstarg,
-                                         scope_w, argnames, has_vararg,
-                                         has_kwarg, defaults_w, 0)
-        except ArgErr, e:
-            raise OperationError(self.space.w_TypeError,
-                                 self.space.wrap(e.getmsg(fnname)))
+class Signature(object):
+    _immutable_ = True
+    _immutable_fields_ = ["argnames[*]"]
+    __slots__ = ("argnames", "varargname", "kwargname")
+
+    def __init__(self, argnames, varargname=None, kwargname=None):
+        self.argnames = argnames
+        self.varargname = varargname
+        self.kwargname = kwargname
 
-    def _parse(self, w_firstarg, signature, defaults_w, blindargs=0):
-        """Parse args and kwargs according to the signature of a code object,
-        or raise an ArgErr in case of failure.
-        """
-        argnames, varargname, kwargname = signature
-        scopelen = len(argnames)
-        has_vararg = varargname is not None
-        has_kwarg = kwargname is not None
-        if has_vararg:
-            scopelen += 1
-        if has_kwarg:
-            scopelen += 1
-        scope_w = [None] * scopelen
-        self._match_signature(w_firstarg, scope_w, argnames, has_vararg, has_kwarg, defaults_w, blindargs)
-        return scope_w    
-
-    def parse(self, fnname, signature, defaults_w=[], blindargs=0):
-        """Parse args and kwargs to initialize a frame
-        according to the signature of code object.
-        """
-        try:
-            return self._parse(None, signature, defaults_w, blindargs)
-        except ArgErr, e:
-            raise OperationError(self.space.w_TypeError,
-                                 self.space.wrap(e.getmsg(fnname)))
-
-    # xxx have only this one
-    def parse_obj(self, w_firstarg,
-                  fnname, signature, defaults_w=[], blindargs=0):
-        """Parse args and kwargs to initialize a frame
-        according to the signature of code object.
-        """
+    @purefunction
+    def find_argname(self, name):
         try:
-            return self._parse(w_firstarg, signature, defaults_w, blindargs)
-        except ArgErr, e:
-            raise OperationError(self.space.w_TypeError,
-                                 self.space.wrap(e.getmsg(fnname)))        
-
-    def frompacked(space, w_args=None, w_kwds=None):
-        """Convenience static method to build an Arguments
-           from a wrapped sequence and a wrapped dictionary."""
-        return Arguments(space, [], w_stararg=w_args, w_starstararg=w_kwds)
-    frompacked = staticmethod(frompacked)
-
-    def topacked(self):
-        """Express the Argument object as a pair of wrapped w_args, w_kwds."""
-        space = self.space
-        args_w, kwds_w = self.unpack()
-        w_args = space.newtuple(args_w)
-        w_kwds = space.newdict()
-        for key, w_value in kwds_w.items():
-            space.setitem(w_kwds, space.wrap(key), w_value)
-        return w_args, w_kwds
-
-    def fromshape(space, (shape_cnt,shape_keys,shape_star,shape_stst), data_w):
-        args_w = data_w[:shape_cnt]
-        p = shape_cnt
-        kwds_w = {}
-        for i in range(len(shape_keys)):
-            kwds_w[shape_keys[i]] = data_w[p]
-            p += 1
-        if shape_star:
-            w_star = data_w[p]
-            p += 1
-        else:
-            w_star = None
-        if shape_stst:
-            w_starstar = data_w[p]
-            p += 1
-        else:
-            w_starstar = None
-        return Arguments(space, args_w, kwds_w, w_star, w_starstar)
-    fromshape = staticmethod(fromshape)
-
-    def match_signature(self, signature, defaults_w):
-        """Parse args and kwargs according to the signature of a code object,
-        or raise an ArgErr in case of failure.
-        """
-        return self._parse(None, signature, defaults_w)
+            return self.argnames.index(name)
+        except ValueError:
+            return -1
+
+    def num_argnames(self):
+        return len(self.argnames)
+
+    def has_vararg(self):
+        return self.varargname is not None
+
+    def has_kwarg(self):
+        return self.kwargname is not None
+
+    def scope_length(self):
+        scopelen = len(self.argnames)
+        scopelen += self.has_vararg()
+        scopelen += self.has_kwarg()
+        return scopelen
+
+    def getallvarnames(self):
+        argnames = self.argnames
+        if self.varargname is not None:
+            argnames = argnames + [self.varargname]
+        if self.kwargname is not None:
+            argnames = argnames + [self.kwargname]
+        return argnames
 
-    def unmatch_signature(self, signature, data_w):
-        """kind of inverse of match_signature"""
-        args_w, kwds_w = self.unpack()
-        need_cnt = len(args_w)
-        need_kwds = kwds_w.keys()
-        space = self.space
-        argnames, varargname, kwargname = signature
-        cnt = len(argnames)
-        data_args_w = data_w[:cnt]
-        if varargname:
-            data_w_stararg = data_w[cnt]
-            cnt += 1
-        else:
-            data_w_stararg = space.newtuple([])
+    def __repr__(self):
+        return "Signature(%r, %r, %r)" % (
+                self.argnames, self.varargname, self.kwargname)
 
-        unfiltered_kwds_w = {}
-        if kwargname:
-            data_w_starargarg = data_w[cnt]
-            for w_key in space.unpackiterable(data_w_starargarg):
-                key = space.str_w(w_key)
-                w_value = space.getitem(data_w_starargarg, w_key)
-                unfiltered_kwds_w[key] = w_value            
-            cnt += 1
-        assert len(data_w) == cnt
+    def __eq__(self, other):
+        return (self.argnames == other.argnames and
+                self.varargname == other.varargname and
+                self.kwargname == other.kwargname)
+
+    def __ne__(self, other):
+        return not self == other
+
+
+    # make it look tuply for the annotator
+
+    def __len__(self):
+        return 3
+
+    def __getitem__(self, i):
+        if i == 0:
+            return self.argnames
+        if i == 1:
+            return self.varargname
+        if i == 2:
+            return self.kwargname
+        raise IndexError
         
-        ndata_args_w = len(data_args_w)
-        if ndata_args_w >= need_cnt:
-            args_w = data_args_w[:need_cnt]
-            for argname, w_arg in zip(argnames[need_cnt:], data_args_w[need_cnt:]):
-                unfiltered_kwds_w[argname] = w_arg
-            assert not space.is_true(data_w_stararg)
-        else:
-            stararg_w = space.unpackiterable(data_w_stararg)
-            datalen = len(data_args_w)
-            args_w = [None] * (datalen + len(stararg_w))
-            for i in range(0, datalen):
-                args_w[i] = data_args_w[i]
-            for i in range(0, len(stararg_w)):
-                args_w[i + datalen] = stararg_w[i]
-            assert len(args_w) == need_cnt
-            
-        kwds_w = {}
-        for key in need_kwds:
-            kwds_w[key] = unfiltered_kwds_w[key]
-                    
-        return Arguments(self.space, args_w, kwds_w)
-
-    def normalize(self):
-        """Return an instance of the Arguments class.  (Instances of other
-        classes may not be suitable for long-term storage or multiple
-        usage.)  Also force the type and validity of the * and ** arguments
-        to be checked now.
-        """
-        args_w, kwds_w = self.unpack()
-        return Arguments(self.space, args_w, kwds_w)
-
-    def unpack(self):
-        """ Purely abstract
-        """
-        raise NotImplementedError()
-
-    def firstarg(self):
-        """ Purely abstract
-        """
-        raise NotImplementedError()
-
-    def prepend(self, w_firstarg):
-        """ Purely abstract
-        """
-        raise NotImplementedError()    
 
-    def _match_signature(self, w_firstarg, scope_w, argnames, has_vararg=False, has_kwarg=False, defaults_w=[], blindargs=0):
-        """ Purely abstract
-        """
-        raise NotImplementedError()
-    
-    def fixedunpack(self, argcount):
-        """ Purely abstract
-        """
-        raise NotImplementedError()
 
-
-class Arguments(AbstractArguments):
+class Arguments(object):
     """
     Collects the arguments of a function call.
     
@@ -195,74 +85,66 @@
 
     ###  Construction  ###
 
-    def __init__(self, space, args_w, kwds_w=None,
+    def __init__(self, space, args_w, keywords=None, keywords_w=None,
                  w_stararg=None, w_starstararg=None):
         self.space = space
         assert isinstance(args_w, list)
         self.arguments_w = args_w
-        from pypy.rlib.debug import make_sure_not_resized
-        make_sure_not_resized(self.arguments_w)
-        self.kwds_w = kwds_w
-        self.w_stararg = w_stararg
-        self.w_starstararg = w_starstararg
+        self.keywords = keywords
+        self.keywords_w = keywords_w
+        if keywords is not None:
+            assert keywords_w is not None
+            assert len(keywords_w) == len(keywords)
+            make_sure_not_resized(self.keywords)
+            make_sure_not_resized(self.keywords_w)
 
-    def num_args(self):
-        self._unpack()
-        return len(self.arguments_w)
-
-    def num_kwds(self):
-        self._unpack()
-        return len(self.kwds_w)
+        make_sure_not_resized(self.arguments_w)
+        self._combine_wrapped(w_stararg, w_starstararg)
         
     def __repr__(self):
-        if self.w_starstararg is not None:
-            return 'Arguments(%s, %s, %s, %s)' % (self.arguments_w,
-                                                  self.kwds_w,
-                                                  self.w_stararg,
-                                                  self.w_starstararg)
-        if self.w_stararg is None:
-            if not self.kwds_w:
-                return 'Arguments(%s)' % (self.arguments_w,)
-            else:
-                return 'Arguments(%s, %s)' % (self.arguments_w, self.kwds_w)
+        """ NOT_RPYTHON """
+        name = self.__class__.__name__
+        if not self.keywords:
+            return '%s(%s)' % (name, self.arguments_w,)
         else:
-            return 'Arguments(%s, %s, %s)' % (self.arguments_w,
-                                              self.kwds_w,
-                                              self.w_stararg)
+            return '%s(%s, %s, %s)' % (name, self.arguments_w,
+                                       self.keywords, self.keywords_w)
+
 
     ###  Manipulation  ###
 
-    def unpack(self):
+    def unpack(self): # slowish
         "Return a ([w1,w2...], {'kw':w3...}) pair."
-        self._unpack()
-        return self.arguments_w, self.kwds_w
+        kwds_w = {}
+        if self.keywords:
+            for i in range(len(self.keywords)):
+                kwds_w[self.keywords[i]] = self.keywords_w[i]
+        return self.arguments_w, kwds_w
+
+    def replace_arguments(self, args_w):
+        "Return a new Arguments with a args_w as positional arguments."
+        return Arguments(self.space, args_w, self.keywords, self.keywords_w)
 
     def prepend(self, w_firstarg):
         "Return a new Arguments with a new argument inserted first."
-        return Arguments(self.space, [w_firstarg] + self.arguments_w,
-                         self.kwds_w, self.w_stararg, self.w_starstararg)
-            
-    def _unpack(self):
-        "unpack the *arg and **kwd into w_arguments and kwds_w"
-        # --- unpack the * argument now ---
-        if self.w_stararg is not None:
+        return self.replace_arguments([w_firstarg] + self.arguments_w)
+
+    def _combine_wrapped(self, w_stararg, w_starstararg):
+        "unpack the *arg and **kwd into arguments_w and keywords_w"
+        # unpack the * arguments 
+        if w_stararg is not None:
             self.arguments_w = (self.arguments_w +
-                                self.space.unpackiterable(self.w_stararg))
-            self.w_stararg = None
-        # --- unpack the ** argument now ---
-        if self.kwds_w is None:
-            self.kwds_w = {}
-        if self.w_starstararg is not None:
+                                self.space.viewiterable(w_stararg))
+        # unpack the ** arguments
+        if w_starstararg is not None:
             space = self.space
-            w_starstararg = self.w_starstararg
-            # maybe we could allow general mappings?
             if not space.is_true(space.isinstance(w_starstararg, space.w_dict)):
                 raise OperationError(space.w_TypeError,
                                      space.wrap("argument after ** must be "
                                                 "a dictionary"))
-            # don't change the original yet,
-            # in case something goes wrong               
-            d = self.kwds_w.copy()
+            keywords_w = [None] * space.int_w(space.len(w_starstararg))
+            keywords = [None] * space.int_w(space.len(w_starstararg))
+            i = 0
             for w_key in space.unpackiterable(w_starstararg):
                 try:
                     key = space.str_w(w_key)
@@ -271,31 +153,28 @@
                         raise
                     raise OperationError(space.w_TypeError,
                                          space.wrap("keywords must be strings"))
-                if key in d:
+                if self.keywords and key in self.keywords:
                     raise OperationError(self.space.w_TypeError,
                                          self.space.wrap("got multiple values "
                                                          "for keyword argument "
                                                          "'%s'" % key))
-                d[key] = space.getitem(w_starstararg, w_key)
-            self.kwds_w = d
-            self.w_starstararg = None
-
-    def has_keywords(self):
-        return bool(self.kwds_w) or (self.w_starstararg is not None and
-                                     self.space.is_true(self.w_starstararg))
+                keywords[i] = key
+                keywords_w[i] = space.getitem(w_starstararg, w_key)
+                i += 1
+            if self.keywords is None:
+                self.keywords = keywords
+                self.keywords_w = keywords_w
+            else:
+                self.keywords = self.keywords + keywords
+                self.keywords_w = self.keywords_w + keywords_w
 
     def fixedunpack(self, argcount):
         """The simplest argument parsing: get the 'argcount' arguments,
         or raise a real ValueError if the length is wrong."""
-        if self.has_keywords():
+        if self.keywords:
             raise ValueError, "no keyword arguments expected"
         if len(self.arguments_w) > argcount:
             raise ValueError, "too many arguments (%d expected)" % argcount
-        if self.w_stararg is not None:
-            self.arguments_w = (self.arguments_w +
-                                self.space.viewiterable(self.w_stararg,
-                                         argcount - len(self.arguments_w)))
-            self.w_stararg = None
         elif len(self.arguments_w) < argcount:
             raise ValueError, "not enough arguments (%d expected)" % argcount
         return self.arguments_w
@@ -304,20 +183,12 @@
         "Return the first argument for inspection."
         if self.arguments_w:
             return self.arguments_w[0]
-        if self.w_stararg is None:
-            return None
-        w_iter = self.space.iter(self.w_stararg)
-        try:
-            return self.space.next(w_iter)
-        except OperationError, e:
-            if not e.match(self.space, self.space.w_StopIteration):
-                raise
-            return None
+        return None
         
     ###  Parsing for function calls  ###
 
-    def _match_signature(self, w_firstarg, scope_w, argnames, has_vararg=False,
-                         has_kwarg=False, defaults_w=[], blindargs=0):
+    def _match_signature(self, w_firstarg, scope_w, signature, defaults_w=[],
+                         blindargs=0):
         """Parse args and kwargs according to the signature of a code object,
         or raise an ArgErr in case of failure.
         Return the number of arguments filled in.
@@ -328,7 +199,9 @@
         #   argnames = list of formal parameter names
         #   scope_w = resulting list of wrapped values
         #
-        co_argcount = len(argnames) # expected formal arguments, without */**
+        co_argcount = signature.num_argnames() # expected formal arguments, without */**
+        has_vararg = signature.has_vararg()
+        has_kwarg = signature.has_kwarg()
         extravarargs = None
         input_argcount =  0
 
@@ -342,27 +215,14 @@
         else:
             upfront = 0
         
-        if self.w_stararg is not None:
-            # There is a case where we don't have to unpack() a w_stararg:
-            # if it matches exactly a *arg in the signature.
-            if (len(self.arguments_w) + upfront == co_argcount and
-                has_vararg and
-                self.space.is_w(self.space.type(self.w_stararg),
-                                self.space.w_tuple)):
-                pass
-            else:
-                self._unpack()   # sets self.w_stararg to None
-        # always unpack the ** arguments
-        if self.w_starstararg is not None:
-            self._unpack()
-
         args_w = self.arguments_w
         num_args = len(args_w)
 
-        kwds_w = self.kwds_w
+        keywords = self.keywords
+        keywords_w = self.keywords_w
         num_kwds = 0
-        if kwds_w is not None:
-            num_kwds = len(kwds_w)
+        if keywords is not None:
+            num_kwds = len(keywords)
 
         avail = num_args + upfront
 
@@ -377,29 +237,36 @@
                 scope_w[i + input_argcount] = args_w[i]
             input_argcount += take
 
-        # 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, keywords do
-        # not conflict with the hidden extra argument bound by methods.
-        if kwds_w and input_argcount > blindargs:
-            for name in argnames[blindargs:input_argcount]: # XXX
-                if name in kwds_w:
-                    raise ArgErrMultipleValues(name)
-
-        remainingkwds_w = self.kwds_w
+        # the code assumes that keywords can potentially be large, but that
+        # argnames is typically not too large
+        num_remainingkwds = num_kwds
+        used_keywords = None
+        if keywords:
+            used_keywords = [False] * num_kwds
+            for i in range(num_kwds):
+                name = keywords[i]
+                j = signature.find_argname(name)
+                if j < 0:
+                    continue
+                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,
+                    # keywords do not conflict with the hidden extra argument
+                    # bound by methods.
+                    if blindargs <= j:
+                        raise ArgErrMultipleValues(name)
+                else:
+                    assert scope_w[j] is None
+                    scope_w[j] = keywords_w[i]
+                    used_keywords[i] = True # mark as used
+                    num_remainingkwds -= 1
         missing = 0
         if input_argcount < co_argcount:
-            if remainingkwds_w is None:
-                remainingkwds_w = {}
-            else:
-                remainingkwds_w = remainingkwds_w.copy()            
-            # not enough args, fill in kwargs or defaults if exists
             def_first = co_argcount - len(defaults_w)
             for i in range(input_argcount, co_argcount):
-                name = argnames[i]
-                if name in remainingkwds_w:
-                    scope_w[i] = remainingkwds_w[name]
-                    del remainingkwds_w[name]
+                if scope_w[i] is not None:
+                    pass
                 elif i >= def_first:
                     scope_w[i] = defaults_w[i-def_first]
                 else:
@@ -408,67 +275,236 @@
                     # keyword arguments, which will be checked for below.
                     missing += 1
 
-        
         # collect extra positional arguments into the *vararg
         if has_vararg:
-            if self.w_stararg is None:   # common case
-                args_left = co_argcount - upfront
-                if args_left < 0:  # check required by rpython
-                    assert extravarargs is not None
-                    starargs_w = extravarargs
-                    if num_args:
-                        starargs_w = starargs_w + args_w
-                elif num_args > args_left:
-                    starargs_w = args_w[args_left:]
-                else:
-                    starargs_w = []
-                scope_w[co_argcount] = self.space.newtuple(starargs_w)
-            else:      # shortcut for the non-unpack() case above
-                scope_w[co_argcount] = self.w_stararg
+            args_left = co_argcount - upfront
+            if args_left < 0:  # check required by rpython
+                assert extravarargs is not None
+                starargs_w = extravarargs
+                if num_args:
+                    starargs_w = starargs_w + args_w
+            elif num_args > args_left:
+                starargs_w = args_w[args_left:]
+            else:
+                starargs_w = []
+            scope_w[co_argcount] = self.space.newtuple(starargs_w)
         elif avail > co_argcount:
             raise ArgErrCount(avail, num_kwds,
-                              (co_argcount, has_vararg, has_kwarg),
+                              co_argcount, has_vararg, has_kwarg,
                               defaults_w, 0)
 
         # collect extra keyword arguments into the **kwarg
         if has_kwarg:
             w_kwds = self.space.newdict()
-            if remainingkwds_w:
-                for key, w_value in remainingkwds_w.items():
-                    self.space.setitem(w_kwds, self.space.wrap(key), w_value)
+            if num_remainingkwds:
+                for i in range(len(keywords)):
+                    if not used_keywords[i]:
+                        key = keywords[i]
+                        self.space.setitem(w_kwds, self.space.wrap(key), keywords_w[i])
             scope_w[co_argcount + has_vararg] = w_kwds
-        elif remainingkwds_w:
-            raise ArgErrUnknownKwds(remainingkwds_w)
+        elif num_remainingkwds:
+            raise ArgErrUnknownKwds(num_remainingkwds, keywords, used_keywords)
 
         if missing:
             raise ArgErrCount(avail, num_kwds,
-                              (co_argcount, has_vararg, has_kwarg),
+                              co_argcount, has_vararg, has_kwarg,
                               defaults_w, missing)
 
         return co_argcount + has_vararg + has_kwarg
     
-    ### Argument <-> list of w_objects together with "shape" information
 
-    def _rawshape(self, nextra=0):
-        shape_cnt  = len(self.arguments_w)+nextra        # Number of positional args
-        if self.kwds_w:
-            shape_keys = self.kwds_w.keys()           # List of keywords (strings)
+
+    def parse_into_scope(self, w_firstarg,
+                         scope_w, fnname, signature, defaults_w=[]):
+        """Parse args and kwargs to initialize a frame
+        according to the signature of code object.
+        Store the argumentvalues into scope_w.
+        scope_w must be big enough for signature.
+        """
+        try:
+            return self._match_signature(w_firstarg,
+                                         scope_w, signature, defaults_w, 0)
+        except ArgErr, e:
+            raise OperationError(self.space.w_TypeError,
+                                 self.space.wrap(e.getmsg(fnname)))
+
+    def _parse(self, w_firstarg, signature, defaults_w, blindargs=0):
+        """Parse args and kwargs according to the signature of a code object,
+        or raise an ArgErr in case of failure.
+        """
+        scopelen = signature.scope_length()
+        scope_w = [None] * scopelen
+        self._match_signature(w_firstarg, scope_w, signature, defaults_w,
+                              blindargs)
+        return scope_w    
+
+
+    def parse_obj(self, w_firstarg,
+                  fnname, signature, defaults_w=[], blindargs=0):
+        """Parse args and kwargs to initialize a frame
+        according to the signature of code object.
+        """
+        try:
+            return self._parse(w_firstarg, signature, defaults_w, blindargs)
+        except ArgErr, e:
+            raise OperationError(self.space.w_TypeError,
+                                 self.space.wrap(e.getmsg(fnname)))        
+
+    @staticmethod
+    def frompacked(space, w_args=None, w_kwds=None):
+        """Convenience static method to build an Arguments
+           from a wrapped sequence and a wrapped dictionary."""
+        return Arguments(space, [], w_stararg=w_args, w_starstararg=w_kwds)
+
+    def topacked(self):
+        """Express the Argument object as a pair of wrapped w_args, w_kwds."""
+        space = self.space
+        w_args = space.newtuple(self.arguments_w)
+        w_kwds = space.newdict()
+        for i in range(len(self.keywords)):
+            space.setitem(w_kwds, space.wrap(self.keywords[i]),
+                                  self.keywords_w[i])
+        return w_args, w_kwds
+
+class ArgumentsForTranslation(Arguments):
+    def __init__(self, space, args_w, keywords=None, keywords_w=None,
+                 w_stararg=None, w_starstararg=None):
+        self.w_stararg = w_stararg
+        self.w_starstararg = w_starstararg
+        self.combine_has_happened = False
+        Arguments.__init__(self, space, args_w, keywords, keywords_w)
+
+    def combine_if_necessary(self):
+        if self.combine_has_happened:
+            return
+        self._combine_wrapped(self.w_stararg, self.w_starstararg)
+        self.combine_has_happened = True
+
+    def prepend(self, w_firstarg): # used often
+        "Return a new Arguments with a new argument inserted first."
+        return ArgumentsForTranslation(self.space, [w_firstarg] + self.arguments_w,
+                                       self.keywords, self.keywords_w, self.w_stararg,
+                                       self.w_starstararg)
+
+    def copy(self):
+        return ArgumentsForTranslation(self.space, self.arguments_w,
+                                       self.keywords, self.keywords_w, self.w_stararg,
+                                       self.w_starstararg)
+
+
+            
+    def _match_signature(self, w_firstarg, scope_w, signature, defaults_w=[],
+                         blindargs=0):
+        self.combine_if_necessary()
+        # _match_signature is destructive
+        return Arguments._match_signature(
+               self, w_firstarg, scope_w, signature,
+               defaults_w, blindargs)
+
+    def unpack(self):
+        self.combine_if_necessary()
+        return Arguments.unpack(self)
+
+    def match_signature(self, signature, defaults_w):
+        """Parse args and kwargs according to the signature of a code object,
+        or raise an ArgErr in case of failure.
+        """
+        return self._parse(None, signature, defaults_w)
+
+    def unmatch_signature(self, signature, data_w):
+        """kind of inverse of match_signature"""
+        args_w, kwds_w = self.unpack()
+        need_cnt = len(args_w)
+        need_kwds = kwds_w.keys()
+        space = self.space
+        argnames, varargname, kwargname = signature
+        cnt = len(argnames)
+        data_args_w = data_w[:cnt]
+        if varargname:
+            data_w_stararg = data_w[cnt]
+            cnt += 1
         else:
-            shape_keys = []
-        shape_star = self.w_stararg is not None   # Flag: presence of *arg
-        shape_stst = self.w_starstararg is not None # Flag: presence of **kwds
-        shape_keys.sort()
-        return shape_cnt, tuple(shape_keys), shape_star, shape_stst # shape_keys are sorted
+            data_w_stararg = space.newtuple([])
+
+        unfiltered_kwds_w = {}
+        if kwargname:
+            data_w_starargarg = data_w[cnt]
+            for w_key in space.unpackiterable(data_w_starargarg):
+                key = space.str_w(w_key)
+                w_value = space.getitem(data_w_starargarg, w_key)
+                unfiltered_kwds_w[key] = w_value            
+            cnt += 1
+        assert len(data_w) == cnt
+        
+        ndata_args_w = len(data_args_w)
+        if ndata_args_w >= need_cnt:
+            args_w = data_args_w[:need_cnt]
+            for argname, w_arg in zip(argnames[need_cnt:], data_args_w[need_cnt:]):
+                unfiltered_kwds_w[argname] = w_arg
+            assert not space.is_true(data_w_stararg)
+        else:
+            stararg_w = space.unpackiterable(data_w_stararg)
+            datalen = len(data_args_w)
+            args_w = [None] * (datalen + len(stararg_w))
+            for i in range(0, datalen):
+                args_w[i] = data_args_w[i]
+            for i in range(0, len(stararg_w)):
+                args_w[i + datalen] = stararg_w[i]
+            assert len(args_w) == need_cnt
+            
+        keywords = []
+        keywords_w = []
+        for key in need_kwds:
+            keywords.append(key)
+            keywords_w.append(unfiltered_kwds_w[key])
+                    
+        return ArgumentsForTranslation(self.space, args_w, keywords, keywords_w)
+
+    @staticmethod
+    def frompacked(space, w_args=None, w_kwds=None):
+        raise NotImplementedError("go away")
+    
+    @staticmethod
+    def fromshape(space, (shape_cnt,shape_keys,shape_star,shape_stst), data_w):
+        args_w = data_w[:shape_cnt]
+        p = end_keys = shape_cnt + len(shape_keys)
+        if shape_star:
+            w_star = data_w[p]
+            p += 1
+        else:
+            w_star = None
+        if shape_stst:
+            w_starstar = data_w[p]
+            p += 1
+        else:
+            w_starstar = None
+        return ArgumentsForTranslation(space, args_w, list(shape_keys),
+                                       data_w[shape_cnt:end_keys], w_star,
+                                       w_starstar)
 
     def flatten(self):
+        """ Argument <-> list of w_objects together with "shape" information """
         shape_cnt, shape_keys, shape_star, shape_stst = self._rawshape()
-        data_w = self.arguments_w + [self.kwds_w[key] for key in shape_keys]
+        data_w = self.arguments_w + [self.keywords_w[self.keywords.index(key)]
+                                         for key in shape_keys]
         if shape_star:
             data_w.append(self.w_stararg)
         if shape_stst:
             data_w.append(self.w_starstararg)
         return (shape_cnt, shape_keys, shape_star, shape_stst), data_w
 
+    def _rawshape(self, nextra=0):
+        assert not self.combine_has_happened
+        shape_cnt  = len(self.arguments_w)+nextra        # Number of positional args
+        if self.keywords:
+            shape_keys = self.keywords[:]                # List of keywords (strings)
+            shape_keys.sort()
+        else:
+            shape_keys = []
+        shape_star = self.w_stararg is not None   # Flag: presence of *arg
+        shape_stst = self.w_starstararg is not None # Flag: presence of **kwds
+        return shape_cnt, tuple(shape_keys), shape_star, shape_stst # shape_keys are sorted
+
 def rawshape(args, nextra=0):
     return args._rawshape(nextra)
 
@@ -485,28 +521,31 @@
 
 class ArgErrCount(ArgErr):
 
-    def __init__(self, nargs, nkwds, signature, defaults_w, missing_args):
-        self.signature    = signature
+    def __init__(self, got_nargs, nkwds, expected_nargs, has_vararg, has_kwarg,
+                 defaults_w, missing_args):
+        self.expected_nargs = expected_nargs
+        self.has_vararg = has_vararg
+        self.has_kwarg = has_kwarg
+        
         self.num_defaults = len(defaults_w)
         self.missing_args = missing_args
-        self.num_args = nargs
+        self.num_args = got_nargs
         self.num_kwds = nkwds
         
     def getmsg(self, fnname):
         args = None
-        num_args, has_vararg, has_kwarg = self.signature
         #args_w, kwds_w = args.unpack()
-        if has_kwarg or (self.num_kwds and self.num_defaults):
+        if self.has_kwarg or (self.num_kwds and self.num_defaults):
             msg2 = "non-keyword "
             if self.missing_args:
-                required_args = num_args - self.num_defaults
+                required_args = self.expected_nargs - self.num_defaults
                 nargs = required_args - self.missing_args
             else:
                 nargs = self.num_args
         else:
             msg2 = ""
             nargs = self.num_args + self.num_kwds
-        n = num_args
+        n = self.expected_nargs
         if n == 0:
             msg = "%s() takes no %sargument (%d given)" % (
                 fnname, 
@@ -514,7 +553,7 @@
                 nargs)
         else:
             defcount = self.num_defaults
-            if defcount == 0 and not has_vararg:
+            if defcount == 0 and not self.has_vararg:
                 msg1 = "exactly"
             elif not self.missing_args:
                 msg1 = "at most"
@@ -549,11 +588,14 @@
 
 class ArgErrUnknownKwds(ArgErr):
 
-    def __init__(self, kwds_w):
+    def __init__(self, num_remainingkwds, keywords, used_keywords):
         self.kwd_name = ''
-        self.num_kwds = len(kwds_w)
-        if self.num_kwds == 1:
-            self.kwd_name = kwds_w.keys()[0]
+        self.num_kwds = num_remainingkwds
+        if num_remainingkwds == 1:
+            for i in range(len(keywords)):
+                if not used_keywords[i]:
+                    self.kwd_name = keywords[i]
+                    break
 
     def getmsg(self, fnname):
         if self.num_kwds == 1:



More information about the Pypy-commit mailing list